diff -rup2 ../ruby-1.8.1-base/dir.c ./dir.c
--- ../ruby-1.8.1-base/dir.c	Sat Dec 20 02:20:56 2003
+++ ./dir.c	Tue Dec 23 16:57:24 2003
@@ -3,6 +3,6 @@
   dir.c -
 
-  $Author: dave $
-  $Date: 2003/12/19 15:18:09 $
+  $Author: matz $
+  $Date: 2003/11/22 03:59:17 $
   created at: Wed Jan  5 09:51:01 JST 1994
 
@@ -77,17 +77,77 @@ char *strchr _((char*,char));
 
 #define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
+#define compare(c1, c2) (((unsigned char)(c1)) - ((unsigned char)(c2)))
 
-#ifndef CharNext		/* defined as CharNext[AW] on Windows. */
-# if defined(DJGPP)
-#   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
-# else
-#   define CharNext(p) ((p) + 1)
-# endif
+/* CAUTION: in case *p == '\0'
+   Next(p) == p + 1 in single byte environment
+   Next(p) == p     in multi byte environment
+*/
+#if defined(CharNext)
+# define Next(p) CharNext(p)
+#elif defined(DJGPP)
+# define Next(p) ((p) + mblen(p, MB_CUR_MAX))
+#elif defined(__EMX__)
+# define Next(p) ((p) + emx_mblen(p))
+static inline int
+emx_mblen(p)
+    const char *p;
+{
+    int n = mblen(p, INT_MAX);
+    return (n < 0) ? 1 : n;
+}
 #endif
 
+#ifndef Next /* single byte environment */
+# define Next(p) ((p) + 1)
+# define Inc(p) (++(p))
+# define Compare(p1, p2) (compare(downcase(*(p1)), downcase(*(p2))))
+#else /* multi byte environment */
+# define Inc(p) ((p) = Next(p))
+# define Compare(p1, p2) (CompareImpl(p1, p2, nocase))
+static int
+CompareImpl(p1, p2, nocase)
+    const char *p1;
+    const char *p2;
+    int nocase;
+{
+    const int len1 = Next(p1) - p1;
+    const int len2 = Next(p2) - p2;
+
+    if (len1 <= 1)
+	if (len2 <= 1) {
+	    return compare(downcase(*p1), downcase(*p2));
+	}
+	else {
+	    const int ret = compare(downcase(*p1), *p2);
+	    return ret ? ret : -1;
+	}
+    else
+	if (len2 <= 1) {
+	    const int ret = compare(*p1, downcase(*p2));
+	    return ret ? ret : 1;
+	}
+	else {
+	    const int ret = memcmp(p1, p2, len1 < len2 ? len1 : len2);
+	    return ret ? ret : len1 - len2;
+	}
+}
+#endif /* environment */
+
 #if defined DOSISH
 #define isdirsep(c) ((c) == '/' || (c) == '\\')
+static char *
+find_dirsep(s)
+    const char *s;
+{
+    while (*s) {
+	if (isdirsep(*s))
+	    return (char *)s;
+	Inc(s);
+    }
+    return 0;
+}
 #else
 #define isdirsep(c) ((c) == '/')
+#define find_dirsep(s) strchr(s, '/')
 #endif
 
@@ -95,5 +155,5 @@ static char *
 range(pat, test, flags)
     char *pat;
-    char test;
+    char *test;
     int flags;
 {
@@ -106,22 +166,21 @@ range(pat, test, flags)
 	pat++;
 
-    test = downcase(test);
-
     while (*pat) {
-	int cstart, cend;
-	cstart = cend = *pat++;
-	if (cstart == ']')
-	    return ok == not ? 0 : pat;
-        else if (escape && cstart == '\\')
-	    cstart = cend = *pat++;
+	char *pstart, *pend;
+	pstart = pend = pat;
+	if (*pstart == ']')
+	    return ok == not ? 0 : ++pat;
+	else if (escape && *pstart == '\\')
+	    pstart = pend = ++pat;
+		Inc(pat);
 	if (*pat == '-' && pat[1] != ']') {
 	    if (escape && pat[1] == '\\')
 		pat++;
-	    cend = pat[1];
-	    if (!cend)
+	    pend = pat+1;
+	    if (!*pend)
 		return 0;
-	    pat += 2;
+	    pat = Next(pend);
 	}
-	if (downcase(cstart) <= test && test <= downcase(cend))
+	if (Compare(pstart, test) <= 0 && Compare(test, pend) <= 0)
 	    ok = 1;
     }
@@ -130,6 +189,7 @@ range(pat, test, flags)
 
 #define ISDIRSEP(c) (pathname && isdirsep(c))
-#define PERIOD(s) (period && *(s) == '.' && \
-		  ((s) == string || ISDIRSEP((s)[-1])))
+#define PERIOD_S() (period && *s == '.' && \
+    (s == string || ISDIRSEP(*s_prev)))
+#define INC_S() (s = Next(s_prev = s))
 static int
 fnmatch(pat, string, flags)
@@ -139,6 +199,6 @@ fnmatch(pat, string, flags)
 {
     int c;
-    int test;
-    const char *s = string;
+    const char *test;
+    const char *s = string, *s_prev;
     int escape = !(flags & FNM_NOESCAPE);
     int pathname = flags & FNM_PATHNAME;
@@ -146,20 +206,21 @@ fnmatch(pat, string, flags)
     int nocase = flags & FNM_CASEFOLD;
 
-    while (c = *pat++) {
+    while (c = *pat) {
 	switch (c) {
 	case '?':
-	    if (!*s || ISDIRSEP(*s) || PERIOD(s))
+	    if (!*s || ISDIRSEP(*s) || PERIOD_S())
 		return FNM_NOMATCH;
-	    s++;
+	    INC_S();
+	    ++pat;
 	    break;
 	case '*':
-	    while ((c = *pat++) == '*')
+	    while ((c = *++pat) == '*')
 		;
 
-	    if (PERIOD(s))
+	    if (PERIOD_S())
 		return FNM_NOMATCH;
 
 	    if (!c) {
-		if (pathname && *rb_path_next(s))
+		if (pathname && find_dirsep(s))
 		    return FNM_NOMATCH;
 		else
@@ -167,7 +228,7 @@ fnmatch(pat, string, flags)
 	    }
 	    else if (ISDIRSEP(c)) {
-		s = rb_path_next(s);
-		if (*s) {
-                    s++;
+		s = find_dirsep(s);
+		if (s) {
+		    INC_S();
 		    break;
                 }
@@ -175,37 +236,31 @@ fnmatch(pat, string, flags)
 	    }
 
-	    test = escape && c == '\\' ? *pat : c;
-	    test = downcase(test);
-	    pat--;
+	    test = escape && c == '\\' ? pat+1 : pat;
 	    while (*s) {
-		if ((c == '[' || downcase(*s) == test) &&
+		if ((c == '[' || Compare(s, test) == 0) &&
 		    !fnmatch(pat, s, flags | FNM_DOTMATCH))
 		    return 0;
 		else if (ISDIRSEP(*s))
 		    break;
-		s++;
+		INC_S();
 	    }
 	    return FNM_NOMATCH;
       
 	case '[':
-	    if (!*s || ISDIRSEP(*s) || PERIOD(s))
+	    if (!*s || ISDIRSEP(*s) || PERIOD_S())
 		return FNM_NOMATCH;
-	    pat = range(pat, *s, flags);
+	    pat = range(pat+1, s, flags);
 	    if (!pat)
 		return FNM_NOMATCH;
-	    s++;
+	    INC_S();
 	    break;
 
 	case '\\':
-	    if (escape
+	    if (escape && pat[1]
 #if defined DOSISH
-		&& *pat && strchr("*?[\\", *pat)
+		&& strchr("*?[\\", pat[1])
 #endif
 		) {
-		c = *pat;
-		if (!c)
-		    c = '\\';
-		else
-		    pat++;
+		c = *++pat;
 	    }
 	    /* FALLTHROUGH */
@@ -217,7 +272,8 @@ fnmatch(pat, string, flags)
 	    else
 #endif
-	    if(downcase(c) != downcase(*s))
+	    if(Compare(pat, s) != 0)
 		return FNM_NOMATCH;
-	    s++;
+	    INC_S();
+	    Inc(pat);
 	    break;
 	}
@@ -256,10 +312,4 @@ dir_s_alloc(klass)
 }
 
-/*
- *  call-seq:
- *     Dir.new( string ) -> aDir
- *
- *  Returns a new directory object for the named directory.
- */
 static VALUE
 dir_initialize(dir, dirname)
@@ -289,15 +339,4 @@ dir_initialize(dir, dirname)
 }
 
-/*
- *  call-seq:
- *     Dir.open( string ) => aDir
- *     Dir.open( string ) {| aDir | block } => anObject
- *
- *  With no block, <code>open</code> is a synonym for
- *  <code>Dir::new</code>. If a block is present, it is passed
- *  <i>aDir</i> as a parameter. The directory is closed at the end of
- *  the block, and <code>Dir::open</code> returns the value of the
- *  block.
- */
 static VALUE
 dir_s_open(klass, dirname)
@@ -326,13 +365,4 @@ dir_closed()
 } while (0)
 
-/*
- *  call-seq:
- *     dir.path => string or nil
- *
- *  Returns the path parameter passed to <em>dir</em>'s constructor.
- *
- *     d = Dir.new("..")
- *     d.path   #=> ".."
- */
 static VALUE
 dir_path(dir)
@@ -346,16 +376,4 @@ dir_path(dir)
 }
 
-/*
- *  call-seq:
- *     dir.read => string or nil
- *
- *  Reads the next entry from <em>dir</em> and returns it as a string.
- *  Returns <code>nil</code> at the end of the stream.
- *
- *     d = Dir.new("testdir")
- *     d.read   #=> "."
- *     d.read   #=> ".."
- *     d.read   #=> "config.h"
- */
 static VALUE
 dir_read(dir)
@@ -380,21 +398,4 @@ dir_read(dir)
 }
 
-/*
- *  call-seq:
- *     dir.each { |filename| block }  => dir
- *
- *  Calls the block once for each entry in this directory, passing the
- *  filename of each entry as a parameter to the block.
- *
- *     d = Dir.new("testdir")
- *     d.each  {|x| puts "Got #{x}" }
- *
- *  <em>produces:</em>
- *
- *     Got .
- *     Got ..
- *     Got config.h
- *     Got main.rb
- */
 static VALUE
 dir_each(dir)
@@ -412,17 +413,4 @@ dir_each(dir)
 }
 
-/*
- *  call-seq:
- *     dir.pos => integer
- *     dir.tell => integer
- *
- *  Returns the current position in <em>dir</em>. See also
- *  <code>Dir#seek</code>.
- *
- *     d = Dir.new("testdir")
- *     d.tell   #=> 0
- *     d.read   #=> "."
- *     d.tell   #=> 12
- */
 static VALUE
 dir_tell(dir)
@@ -441,18 +429,4 @@ dir_tell(dir)
 }
 
-/*
- *  call-seq:
- *     dir.seek( integer ) => dir
- *
- *  Seeks to a particular location in <em>dir</em>. <i>integer</i>
- *  must be a value returned by <code>Dir#tell</code>.
- *
- *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
- *     d.read                   #=> "."
- *     i = d.tell               #=> 12
- *     d.read                   #=> ".."
- *     d.seek(i)                #=> #<Dir:0x401b3c40>
- *     d.read                   #=> ".."
- */
 static VALUE
 dir_seek(dir, pos)
@@ -470,18 +444,4 @@ dir_seek(dir, pos)
 }
 
-/*
- *  call-seq:
- *     dir.pos( integer ) => integer
- *
- *  Synonym for <code>Dir#seek</code>, but returns the position
- *  parameter.
- *
- *     d = Dir.new("testdir")   #=> #<Dir:0x401b3c40>
- *     d.read                   #=> "."
- *     i = d.pos                #=> 12
- *     d.read                   #=> ".."
- *     d.pos = i                #=> 12
- *     d.read                   #=> ".."
- */
 static VALUE
 dir_set_pos(dir, pos)
@@ -492,15 +452,4 @@ dir_set_pos(dir, pos)
 }
 
-/*
- *  call-seq:
- *     dir.rewind => dir
- *
- *  Repositions <em>dir</em> to the first entry.
- *
- *     d = Dir.new("testdir")
- *     d.read     #=> "."
- *     d.rewind   #=> #<Dir:0x401b3fb0>
- *     d.read     #=> "."
- */
 static VALUE
 dir_rewind(dir)
@@ -514,14 +463,4 @@ dir_rewind(dir)
 }
 
-/*
- *  call-seq:
- *     dir.close => nil
- *
- *  Closes the directory stream. Any further attempts to access
- *  <em>dir</em> will raise an <code>IOError</code>.
- *
- *     d = Dir.new("testdir")
- *     d.close   #=> nil
- */
 static VALUE
 dir_close(dir)
@@ -560,43 +499,4 @@ chdir_restore(path)
 }
 
-/*
- *  call-seq:
- *     Dir.chdir( [ string] ) => 0
- *     Dir.chdir( [ string] ) {| path | block }  => anObject
- *
- *  Changes the current working directory of the process to the given
- *  string. When called without an argument, changes the directory to
- *  the value of the environment variable <code>HOME</code>, or
- *  <code>LOGDIR</code>. <code>SystemCallError</code> (probably
- *  <code>Errno::ENOENT</code>) if the target directory does not exist.
- *
- *  If a block is given, it is passed the name of the new current
- *  directory, and the block is executed with that as the current
- *  directory. The original working directory is restored when the block
- *  exits. The return value of <code>chdir</code> is the value of the
- *  block. <code>chdir</code> blocks can be nested, but in a
- *  multi-threaded program an error will be raised if a thread attempts
- *  to open a <code>chdir</code> block while another thread has one
- *  open.
- *
- *     Dir.chdir("/var/spool/mail")
- *     puts Dir.pwd
- *     Dir.chdir("/tmp") do
- *       puts Dir.pwd
- *       Dir.chdir("/usr") do
- *         puts Dir.pwd
- *       end
- *       puts Dir.pwd
- *     end
- *     puts Dir.pwd
- *
- *  <em>produces:</em>
- *
- *     /var/spool/mail
- *     /tmp
- *     /usr
- *     /tmp
- *     /var/spool/mail
- */
 static VALUE
 dir_s_chdir(argc, argv, obj)
@@ -639,15 +539,4 @@ dir_s_chdir(argc, argv, obj)
 }
 
-/*
- *  call-seq:
- *     Dir.getwd => string
- *     Dir.pwd => string
- *
- *  Returns the path to the current working directory of this process as
- *  a string.
- *
- *     Dir.chdir("/tmp")   #=> 0
- *     Dir.getwd           #=> "/tmp"
- */
 static VALUE
 dir_s_getwd(dir)
@@ -665,28 +554,4 @@ dir_s_getwd(dir)
 }
 
-static void check_dirname _((volatile VALUE *));
-static void
-check_dirname(dir)
-    volatile VALUE *dir;
-{
-    char *path, *pend;
-
-    SafeStringValue(*dir);
-    rb_secure(2);
-    path = RSTRING(*dir)->ptr;
-    if (path && *(pend = rb_path_end(rb_path_skip_prefix(path)))) {
-	*dir = rb_str_new(path, pend - path);
-    }
-}
-
-/*
- *  call-seq:
- *     Dir.chroot( string ) => 0
- *
- *  Changes this process's idea of the file system root. Only a
- *  privileged process may make this call. Not available on all
- *  platforms. On Unix systems, see <code>chroot(2)</code> for more
- *  information.
- */
 static VALUE
 dir_s_chroot(dir, path)
@@ -694,5 +559,6 @@ dir_s_chroot(dir, path)
 {
 #if defined(HAVE_CHROOT) && !defined(__CHECKER__)
-    check_dirname(&path);
+    rb_secure(2);
+    SafeStringValue(path);
 
     if (chroot(RSTRING(path)->ptr) == -1)
@@ -706,17 +572,4 @@ dir_s_chroot(dir, path)
 }
 
-/*
- *  call-seq:
- *     Dir.mkdir( string [, integer] ) => 0
- *
- *  Makes a new directory named by <i>string</i>, with permissions
- *  specified by the optional parameter <i>anInteger</i>. The
- *  permissions may be modified by the value of
- *  <code>File::umask</code>, and are ignored on NT. Raises a
- *  <code>SystemCallError</code> if the directory cannot be created. See
- *  also the discussion of permissions in the class documentation for
- *  <code>File</code>.
- *
- */
 static VALUE
 dir_s_mkdir(argc, argv, obj)
@@ -735,5 +588,6 @@ dir_s_mkdir(argc, argv, obj)
     }
 
-    check_dirname(&path);
+    SafeStringValue(path);
+    rb_secure(2);
 #ifndef _WIN32
     if (mkdir(RSTRING(path)->ptr, mode) == -1)
@@ -747,18 +601,10 @@ dir_s_mkdir(argc, argv, obj)
 }
 
-/*
- *  call-seq:
- *     Dir.delete( string ) => 0
- *     Dir.rmdir( string ) => 0
- *     Dir.unlink( string ) => 0
- *
- *  Deletes the named directory. Raises a subclass of
- *  <code>SystemCallError</code> if the directory isn't empty.
- */
 static VALUE
 dir_s_rmdir(obj, dir)
     VALUE obj, dir;
 {
-    check_dirname(&dir);
+    SafeStringValue(dir);
+    rb_secure(2);
     if (rmdir(RSTRING(dir)->ptr) < 0)
 	rb_sys_fail(RSTRING(dir)->ptr);
@@ -767,20 +613,19 @@ dir_s_rmdir(obj, dir)
 }
 
-/* Return nonzero if S has any special globbing chars in it.  */
 static int
-has_magic(s, send, flags)
-     char *s, *send;
+has_magic(p, m, flags)
+     register char *p;
+     char **m;
      int flags;
 {
-    register char *p = s;
     register char c;
     int open = 0;
     int escape = !(flags & FNM_NOESCAPE);
 
-    while ((c = *p++) != '\0') {
+    while (c = *p++, (c && c != '/')) {
 	switch (c) {
 	  case '?':
 	  case '*':
-	    return Qtrue;
+	    goto found;
 
 	  case '[':	/* Only accept an open brace if there is a close */
@@ -789,67 +634,74 @@ has_magic(s, send, flags)
 	  case ']':
 	    if (open)
-		return Qtrue;
+		goto found;
 	    continue;
 
 	  case '\\':
-	    if (escape && *p++ == '\0')
-		return Qfalse;
+	    if (escape && (c = *p++, !(c && c != '/')))
+		goto miss;
+	    continue;
 	}
 
-	if (send && p >= send) break;
-    }
-    return Qfalse;
+	p = Next(p - 1);
 }
 
-static char*
-extract_path(p, pend)
-    char *p, *pend;
-{
-    char *alloc;
-    int len;
-
-    len = pend - p;
-    alloc = ALLOC_N(char, len+1);
-    memcpy(alloc, p, len);
-    if (len > 1 && pend[-1] == '/'
-#if defined DOSISH_DRIVE_LETTER
-    && pend[-2] != ':'
-#endif
-    ) {
-	alloc[len-1] = 0;
-    }
-    else {
-	alloc[len] = 0;
-    }
+  miss:
+    *m = p-1;
+    return 0;
 
-    return alloc;
+  found:
+    while (*p && *p != '/')
+	Inc(p);
+    *m = p;
+    return 1;
 }
 
-static char*
-extract_elem(path)
-    char *path;
-{
+static int
+do_fnmatch(p, pend, string, flags)
+    char *p;
     char *pend;
+    const char *string;
+    int flags;
+{
+    int ret;
+    char c;
 
-    pend = strchr(path, '/');
-    if (!pend) pend = path + strlen(path);
-
-    return extract_path(path, pend);
+    c = *pend;
+    *pend = '\0'; /* should I allocate new string? */
+    ret = fnmatch(p, string, flags);
+    *pend = c;
+    return ret;
 }
 
-static void
-remove_backslashes(p)
+static int
+remove_backslashes(p, pend)
     char *p;
+    char *pend;
 {
-    char *pend = p + strlen(p);
     char *t = p;
+    char *s = p;
+    int n = 0;
 
-    while (p < pend) {
+    while (*p && p < pend) {
 	if (*p == '\\') {
-	    if (++p == pend) break;
+	    if (t != s) {
+		memmove(t, s, p - s);
+		n++;
+	    }
+	    t += (p - s);
+	    s = ++p;
+	    if (!(*p && p < pend)) break;
+	}
+	Inc(p);
 	}
-	*t++ = *p++;
+
+    while (*p++);
+
+    if (t != s) {
+	memmove(t, s, p - s); /* move '\0' too */
+	n++;
     }
-    *t = '\0';
+
+    return n;
 }
 
@@ -893,7 +745,8 @@ glob_call_func(func, path, arg)
 
 static int
-glob_helper(path, sub, flags, func, arg)
+glob_helper(path, sub, separator, flags, func, arg) /* if separator p[-1] is removable '/' */
     char *path;
     char *sub;
+    int separator;
     int flags;
     void (*func) _((const char*, VALUE));
@@ -901,31 +754,6 @@ glob_helper(path, sub, flags, func, arg)
 {
     struct stat st;
-    char *p, *m;
     int status = 0;
-
-    p = sub ? sub : path;
-    if (!has_magic(p, 0, flags)) {
-#if defined DOSISH
-	remove_backslashes(path);
-#else
-	if (!(flags & FNM_NOESCAPE)) remove_backslashes(p);
-#endif
-	if (lstat(path, &st) == 0) {
-	    status = glob_call_func(func, path, arg);
-	    if (status) return status;
-	}
-	else if (errno != ENOENT) {
-	    /* In case stat error is other than ENOENT and
-	       we may want to know what is wrong. */
-	    rb_sys_warning(path);
-	}
-	return 0;
-    }
-
-    while (p && !status) {
-	if (*p == '/') p++;
-	m = strchr(p, '/');
-	if (has_magic(p, m, flags)) {
-	    char *dir, *base, *magic, *buf;
+    char *p = sub, *m, *buf;
 	    DIR *dirp;
 	    struct dirent *dp;
@@ -937,51 +765,105 @@ glob_helper(path, sub, flags, func, arg)
 	    } *tmp, *link, **tail = &link;
 
-	    base = extract_path(path, p);
-	    if (path == p) dir = ".";
-	    else dir = base;
+    while (*p && !has_magic(p, &m, flags)) {
+	if (*m == '/') {
+	    separator = 1;
+	    p = m + 1;
+	}
+	else {
+	    separator = 0;
+	    p = m;
+	}
+    }
+
+    if (!(flags & FNM_NOESCAPE)) {
+	int n = remove_backslashes(sub, p);
+	p -= n;
+	m -= n;
+    }
+
+    /* In case stat error is other than ENOENT and
+       we may want to know what is wrong. */
+
+    if (*p == '\0') { /* magic not found */
+        if (!separator) {
+	    if (lstat(path, &st) < 0) {
+		if (errno != ENOENT) rb_sys_warning(path);
+	    }
+	    else {
+		status = glob_call_func(func, path, arg);
+	    }
+	}
+	else {
+	    const char c = p[-1];
+	    p[-1] = '\0';
+	    if (lstat(path, &st) < 0) {
+		if (errno != ENOENT) rb_sys_warning(path);
+		p[-1] = c;
+	    }
+	    else if (S_ISDIR(st.st_mode)) {
+		p[-1] = c;
+		status = glob_call_func(func, path, arg);
+	    }
+	}
+	return status;
+    }
 
-	    magic = extract_elem(p);
+    {
+#if defined DOSISH_DRIVE_LETTER
+#define NEED_DOT ((p-path==0) || (p-path==2 && ISALPHA(*path) && path[1] == ':'))
+#else
+#define NEED_DOT ((p-path==0))
+#endif
+	const int n = separator ? (p - path) - 1 : (p - path);
+	char *dir = ALLOC_N(char, n+1+1);
+	memcpy(dir, path, n);
+	if (NEED_DOT) {
+	    dir[n] = '.';
+	    dir[n+1] = '\0';
+	}
+	else {
+	    dir[n] = '\0';
+	}
 	    if (stat(dir, &st) < 0) {
 	        if (errno != ENOENT) rb_sys_warning(dir);
-	        free(base);
-	        free(magic);
-	        break;
+	    free(dir);
+	    return 0;
 	    }
 	    if (S_ISDIR(st.st_mode)) {
-		if (m && strcmp(magic, "**") == 0) {
-		    int n = strlen(base);
+	    if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+		const int n = p - path;
 		    recursive = 1;
-		    buf = ALLOC_N(char, n+strlen(m)+3);
-		    sprintf(buf, "%s%s", base, *base ? m : m+1);
-		    status = glob_helper(buf, buf+n, flags, func, arg);
+		buf = ALLOC_N(char, n+strlen(p+3)+1);
+		memcpy(buf, path, n);
+		strcpy(buf+n, p+3);
+		status = glob_helper(buf, buf+n, separator, flags, func, arg);
 		    free(buf);
-		    if (status) goto finalize;
+		if (status) {
+		    free(dir);
+		    return status;
+		}
 		}
 		dirp = opendir(dir);
 		if (dirp == NULL) {
 		    rb_sys_warning(dir);
-		    free(base);
-		    free(magic);
-		    break;
+		free(dir);
+		return 0;
 		}
 	    }
 	    else {
-		free(base);
-		free(magic);
-		break;
+	    free(dir);
+	    return 0;
+	}
+	free(dir);
 	    }
 
-#if defined DOSISH_DRIVE_LETTER
-#define BASE (*base && !((isdirsep(*base) && !base[1]) || (base[1] == ':' && isdirsep(base[2]) && !base[3])))
-#else
-#define BASE (*base && !(isdirsep(*base) && !base[1]))
-#endif
-
 	    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+	const int n = p - path;
 		if (recursive) {
 		    if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0)
 			continue;
-		    buf = ALLOC_N(char, strlen(base)+NAMLEN(dp)+strlen(m)+6);
-		    sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
+	    buf = ALLOC_N(char, n+NAMLEN(dp)+strlen(m)+3+1);
+	    memcpy(buf, path, n);
+	    strcpy(buf+n, dp->d_name);
 		    if (lstat(buf, &st) < 0) {
 			if (errno != ENOENT) rb_sys_warning(buf);
@@ -991,7 +873,7 @@ glob_helper(path, sub, flags, func, arg)
 		    if (S_ISDIR(st.st_mode)) {
 			char *t = buf+strlen(buf);
-		        strcpy(t, "/**");
+		memcpy(t, "/**", 3);
 			strcpy(t+3, m);
-			status = glob_helper(buf, t, flags, func, arg);
+		status = glob_helper(buf, t+1, 1, flags, func, arg);
 			free(buf);
 			if (status) break;
@@ -1001,8 +883,9 @@ glob_helper(path, sub, flags, func, arg)
 		    continue;
 		}
-		if (fnmatch(magic, dp->d_name, flags) == 0) {
-		    buf = ALLOC_N(char, strlen(base)+NAMLEN(dp)+2);
-		    sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
-		    if (!m) {
+	if (do_fnmatch(p, m, dp->d_name, flags) == 0) {
+	    buf = ALLOC_N(char, n+NAMLEN(dp)+1);
+	    memcpy(buf, path, n);
+	    strcpy(buf+n, dp->d_name);
+	    if (*m == '\0') {
 			status = glob_call_func(func, buf, arg);
 			free(buf);
@@ -1017,20 +900,15 @@ glob_helper(path, sub, flags, func, arg)
 	    }
 	    closedir(dirp);
-	  finalize:
 	    *tail = 0;
-	    free(base);
-	    free(magic);
-	    if (link) {
 		while (link) {
 		    if (status == 0) {
 			if (stat(link->path, &st) == 0) {
 			    if (S_ISDIR(st.st_mode)) {
-				int len = strlen(link->path);
-				int mlen = strlen(m);
-				char *t = ALLOC_N(char, len+mlen+1);
-
-				sprintf(t, "%s%s", link->path, m);
-				status = glob_helper(t, t+len, flags, func, arg);
-				free(t);
+		    const int len = strlen(link->path);
+		    buf = ALLOC_N(char, len+strlen(m)+1);
+		    memcpy(buf, link->path, len);
+		    strcpy(buf+len, m);
+		    status = glob_helper(buf, buf+len+1, 1, flags, func, arg);
+		    free(buf);
 			    }
 			}
@@ -1044,9 +922,4 @@ glob_helper(path, sub, flags, func, arg)
 		    free(tmp);
 		}
-		break;
-	    }
-	}
-	p = m;
-    }
     return status;
 }
@@ -1059,5 +932,19 @@ rb_glob2(path, flags, func, arg)
     VALUE arg;
 {
-    int status = glob_helper(path, 0, flags, func, arg);
+    int status;
+    char *root = path;
+
+#if defined DOSISH
+    flags |= FNM_CASEFOLD;
+
+    if (ISALPHA(*root) && root[1] == ':') {
+	root += 2;
+    }
+#endif
+    if (*root == '/') {
+	root += 1;
+    }
+
+    status = glob_helper(path, root, 0, flags, func, arg);
     if (status) rb_jump_tag(status);
 }
@@ -1123,5 +1010,5 @@ push_braces(ary, s, flags)
 	    break;
 	}
-	p++;
+	Inc(p);
     }
     while (*p) {
@@ -1131,5 +1018,5 @@ push_braces(ary, s, flags)
 	    break;
 	}
-	p++;
+	Inc(p);
     }
 
@@ -1141,11 +1028,11 @@ push_braces(ary, s, flags)
 	p = lbrace;
 	while (*p != '}') {
-	    t = p + 1;
-	    for (p = t; *p!='}' && *p!=','; p++) {
+	    t = Next(p);
+	    for (p = t; *p!='}' && *p!=','; Inc(p)) {
 		/* skip inner braces */
-		if (*p == '{') while (*p!='}') p++;
+		if (*p == '{') while (*p!='}') Inc(p);
 	    }
 	    memcpy(b, t, p-t);
-	    strcpy(b+(p-t), rbrace+1);
+	    strcpy(b+(p-t), Next(rbrace));
 	    push_braces(ary, buf, flags);
 	}
@@ -1183,17 +1070,18 @@ rb_push_glob(str, flags)
 
     while (p < pend) {
-	t = buf;
 	nest = maxnest = 0;
 	while (p < pend && isdelim(*p)) p++;
+	t = p;
 	while (p < pend && !isdelim(*p)) {
 	    if (*p == '{') nest++, maxnest++;
 	    if (*p == '}') nest--;
 	    if (!noescape && *p == '\\') {
-		*t++ = *p++;
-		if (p == pend) break;
+		p++;
+		if (p == pend || isdelim(*p)) break;
 	    }
-	    *t++ = *p++;
+	    p = Next(p);
 	}
-	*t = '\0';
+	memcpy(buf, t, p - t);
+	buf[p - t] = '\0';
 	if (maxnest == 0) {
 	    push_globs(ary, buf, flags);
@@ -1209,12 +1097,4 @@ rb_push_glob(str, flags)
 }
 
-/*
- *  call-seq:
- *     Dir[ string ] => array
- *
- *  Equivalent to calling
- *  <em>dir</em>.<code>glob(</code><i>string,</i><code>0)</code>.
- *
- */
 static VALUE
 dir_s_aref(obj, str)
@@ -1224,25 +1104,4 @@ dir_s_aref(obj, str)
 }
 
-/*
- *  call-seq:
- *     Dir.glob( string, [flags] ) => array
- *     Dir.glob( string, [flags] ) {| filename | block }  => false
- *
- *  Returns the filenames found by expanding the pattern given in
- *  <i>string</i>, either as an <i>array</i> or as parameters to the
- *  block. Note that this pattern is not a regexp (it's closer to a
- *  shell glob). See <code>File::fnmatch</code> for
- *  details of file name matching and the meaning of the <i>flags</i>
- *  parameter.
- *
- *     Dir["config.?"]                     #=> ["config.h"]
- *     Dir.glob("config.?")                #=> ["config.h"]
- *     Dir.glob("*.[a-z][a-z]")            #=> ["main.rb"]
- *     Dir.glob("*.[^r]*")                 #=> ["config.h"]
- *     Dir.glob("*.{rb,h}")                #=> ["main.rb", "config.h"]
- *     Dir.glob("*")                       #=> ["config.h", "main.rb"]
- *     Dir.glob("*", File::FNM_DOTMATCH)   #=> [".", "..", "config.h", "main.rb"]
- *
- */
 static VALUE
 dir_s_glob(argc, argv, obj)
@@ -1262,21 +1121,4 @@ dir_s_glob(argc, argv, obj)
 }
 
-/*
- *  call-seq:
- *     Dir.foreach( dirname ) {| filename | block }  => nil
- *
- *  Calls the block once for each entry in the named directory, passing
- *  the filename of each entry as a parameter to the block.
- *
- *     Dir.foreach("testdir") {|x| puts "Got #{x}" }
- *
- *  <em>produces:</em>
- *
- *     Got .
- *     Got ..
- *     Got config.h
- *     Got main.rb
- *
- */
 static VALUE
 dir_foreach(io, dirname)
@@ -1290,15 +1132,4 @@ dir_foreach(io, dirname)
 }
 
-/*
- *  call-seq:
- *     Dir.entries( dirname ) => array
- *
- *  Returns an array containing all of the filenames in the given
- *  directory. Will raise a <code>SystemCallError</code> if the named
- *  directory doesn't exist.
- *
- *     Dir.entries("testdir")   #=> [".", "..", "config.h", "main.rb"]
- *
- */
 static VALUE
 dir_entries(io, dirname)
@@ -1311,43 +1142,4 @@ dir_entries(io, dirname)
 }
 
-/*
- *  call-seq:
- *     File.fnmatch( pattern, path, [flags] ) => (true or false)
- *     File.fnmatch?( pattern, path, [flags] ) => (true or false)
- *
- *  Returns true if <i>path</i> matches against <i>pattern</i> The
- *  pattern is not a regular expression; instead it follows rules
- *  similar to shell filename globbing. It may contain the following
- *  metacharacters:
- *
- *  <i>flags</i> is a bitwise OR of the <code>FNM_xxx</code> parameters.
- *  The same glob pattern and flags are used by <code>Dir::glob</code>.
- *
- *     File.fnmatch('cat',       'cat')        #=> true
- *     File.fnmatch('cat',       'category')   #=> false
- *     File.fnmatch('c{at,ub}s', 'cats')       #=> false
- *     File.fnmatch('c{at,ub}s', 'cubs')       #=> false
- *     File.fnmatch('c{at,ub}s', 'cat')        #=> false
- *
- *     File.fnmatch('c?t',    'cat')                       #=> true
- *     File.fnmatch('c\?t',   'cat')                       #=> false
- *     File.fnmatch('c??t',   'cat')                       #=> false
- *     File.fnmatch('c*',     'cats')                      #=> true
- *     File.fnmatch('c/ * FIXME * /t', 'c/a/b/c/t')                 #=> true
- *     File.fnmatch('c*t',    'cat')                       #=> true
- *     File.fnmatch('c\at',   'cat')                       #=> true
- *     File.fnmatch('c\at',   'cat', File::FNM_NOESCAPE)   #=> false
- *     File.fnmatch('a?b',    'a/b')                       #=> true
- *     File.fnmatch('a?b',    'a/b', File::FNM_PATHNAME)   #=> false
- *
- *     File.fnmatch('*',   '.profile')                            #=> false
- *     File.fnmatch('*',   '.profile', File::FNM_DOTMATCH)        #=> true
- *     File.fnmatch('*',   'dave/.profile')                       #=> true
- *     File.fnmatch('*',   'dave/.profile', File::FNM_DOTMATCH)   #=> true
- *     File.fnmatch('*',   'dave/.profile', File::FNM_PATHNAME)   #=> false
- *     File.fnmatch('* / FIXME *', 'dave/.profile', File::FNM_PATHNAME)   #=> false
- *     STRICT = File::FNM_PATHNAME | File::FNM_DOTMATCH
- *     File.fnmatch('* / FIXME *', 'dave/.profile', STRICT)               #=> true
- */
 static VALUE
 file_s_fnmatch(argc, argv, obj)
@@ -1374,15 +1166,4 @@ file_s_fnmatch(argc, argv, obj)
 }
 
-/*
- *  Objects of class <code>Dir</code> are directory streams representing
- *  directories in the underlying file system. They provide a variety of
- *  ways to list directories and their contents. See also
- *  <code>File</code>.
- *
- *  The directory used in these examples contains the two regular files
- *  (<code>config.h</code> and <code>main.rb</code>), the parent
- *  directory (<code>..</code>), and the directory itself
- *  (<code>.</code>).
- */
 void
 Init_Dir()
