Index: dir.c
===================================================================
RCS file: /cvs/ruby/src/ruby/dir.c,v
retrieving revision 1.90
diff -u -2 -p -r1.90 dir.c
--- dir.c	15 Dec 2003 04:11:34 -0000	1.90
+++ dir.c	15 Dec 2003 04:28:22 -0000
@@ -77,12 +77,63 @@ 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)
+/* caution: MBLEN('\0')==0 and MBLEN(other SB)==1 */
+#if defined(CharNext)
+# define Next(p) CharNext(p)
+# define MBLEN(p) (CharNext(p)-(p))
+#elif defined(DJGPP)
+# define MBLEN(p) mblen(p, MB_CUR_MAX)
+#elif defined(__EMX__)
+static inline int
+MBLEN(p)
+    const char *p;
+{
+    int n = mblen(p, INT_MAX);
+    return (n < 0) ? 1 : n;
+}
 # endif
+
+/* caution: between these environments, Next(Inc) has difference in behavior for '\0' */
+#ifndef MBLEN /* 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 */
+#ifndef Next
+# define Next(p) ((p) + MBLEN(p))
 #endif
+# 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 = MBLEN(p1);
+    const int len2 = MBLEN(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
@@ -96,5 +147,5 @@ static char *
 range(pat, test, flags)
     char *pat;
-    char test;
+    char *test;
     int flags;
 {
@@ -107,22 +158,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;
     }
@@ -131,6 +181,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)
@@ -140,6 +191,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;
@@ -147,16 +198,17 @@ fnmatch(pat, string, flags)
     int nocase = flags & FNM_CASEFOLD;
 
-    while (c = *pat++) {
+    while (c = *pat) {
 	switch (c) {
-	case '?':
-	    if (!*s || ISDIRSEP(*s) || PERIOD(s))
+	  case '?':
+	    if (!*s || ISDIRSEP(*s) || PERIOD_S())
 		return FNM_NOMATCH;
-	    s++;
+	    INC_S();
+	    ++pat;
 	    break;
-	case '*':
-	    while ((c = *pat++) == '*')
+	  case '*':
+	    while ((c = *++pat) == '*')
 		;
 
-	    if (PERIOD(s))
+	    if (PERIOD_S())
 		return FNM_NOMATCH;
 
@@ -170,5 +222,5 @@ fnmatch(pat, string, flags)
 		s = find_dirsep(s);
 		if (s) {
-                    s++;
+		    INC_S();
 		    break;
                 }
@@ -176,41 +228,35 @@ 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))
+	  case '[':
+	    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
+	  case '\\':
+	    if (escape && pat[1]
 #if defined DOSISH
-		&& *pat && strchr("*?[\\", *pat)
+		&& strchr("*?[\\", pat[1])
 #endif
 		) {
-		c = *pat;
-		if (!c)
-		    c = '\\';
-		else
-		    pat++;
+		c = *++pat;
 	    }
 	    /* FALLTHROUGH */
 
-	default:
+	  default:
 #if defined DOSISH
 	    if (ISDIRSEP(c) && isdirsep(*s))
@@ -218,7 +264,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;
 	}
@@ -570,20 +617,27 @@ 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;
+/* difference from find_dirsep: if not found, return pointer to '\0' */
+static void
+find_magic(p, flags, pbeg, pend)
+     register char *p;
      int flags;
+     char **pbeg;
+     char **pend;
 {
-    register char *p = s;
     register char c;
     int open = 0;
     int escape = !(flags & FNM_NOESCAPE);
 
+    *pbeg = p;
+
     while ((c = *p++) != '\0') {
 	switch (c) {
+	  case '/':
+	    *pbeg = p;
+	    continue;
+
 	  case '?':
 	  case '*':
-	    return Qtrue;
+	    goto found;
 
 	  case '[':	/* Only accept an open brace if there is a close */
@@ -592,67 +646,73 @@ has_magic(s, send, flags)
 	  case ']':
 	    if (open)
-		return Qtrue;
+		goto found;
 	    continue;
 
 	  case '\\':
 	    if (escape && *p++ == '\0')
-		return Qfalse;
+		goto miss;
+	    continue;
 	}
 
-	if (send && p >= send) break;
+	p = Next(p - 1);
     }
-    return Qfalse;
-}
-
-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:
+    *pbeg = *pend = p-1;
+    return;
 
-    return alloc;
+  found:
+    while (*p && *p != '/')
+	Inc(p);
+    *pend = p;
 }
 
-static char*
-extract_elem(path)
-    char *path;
+static int
+do_fnmatch(beg, end, string, flags)
+    char *beg;
+    char *end;
+    const char *string;
+    int flags;
 {
-    char *pend;
-
-    pend = strchr(path, '/');
-    if (!pend) pend = path + strlen(path);
+    int ret;
+    char c;
 
-    return extract_path(path, pend);
+    c = *end;
+    *end = '\0'; /* should I allocate new string? */
+    ret = fnmatch(beg, string, flags);
+    *end = 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;
 	}
-	*t++ = *p++;
+	Inc(p);
     }
-    *t = '\0';
+
+    while (*p++);
+
+    if (t != s) {
+	memmove(t, s, p - s); /* move '\0' too */
+	n++;
+    }
+
+    return n;
 }
 
@@ -704,14 +764,24 @@ glob_helper(path, sub, flags, func, arg)
 {
     struct stat st;
-    char *p, *m;
     int status = 0;
+    char *beg, *end, *buf;
+    DIR *dirp;
+    struct dirent *dp;
+    int recursive = 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
+    struct d_link {
+	char *path;
+	struct d_link *next;
+    } *tmp, *link, **tail = &link;
+
+    find_magic(sub, flags, &beg, &end);
+
+    if (!(flags & FNM_NOESCAPE)) {
+	int n = remove_backslashes(sub, beg);
+	beg -= n;
+	end -= n;
+    }
+
+    if (*beg == '\0') { /* magic not found */
 	if (lstat(path, &st) == 0) {
 	    status = glob_call_func(func, path, arg);
@@ -726,129 +796,121 @@ glob_helper(path, sub, flags, func, arg)
     }
 
-    while (p && !status) {
-	if (*p == '/') p++;
-	m = strchr(p, '/');
-	if (has_magic(p, m, flags)) {
-	    char *dir, *base, *magic, *buf;
-	    DIR *dirp;
-	    struct dirent *dp;
-	    int recursive = 0;
-
-	    struct d_link {
-		char *path;
-		struct d_link *next;
-	    } *tmp, *link, **tail = &link;
-
-	    base = extract_path(path, p);
-	    if (path == p) dir = ".";
-	    else dir = base;
-
-	    magic = extract_elem(p);
-	    if (stat(dir, &st) < 0) {
-	        if (errno != ENOENT) rb_sys_warning(dir);
-	        free(base);
-	        free(magic);
-	        break;
-	    }
-	    if (S_ISDIR(st.st_mode)) {
-		if (m && strcmp(magic, "**") == 0) {
-		    int n = strlen(base);
-		    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);
-		    free(buf);
-		    if (status) goto finalize;
-		}
-		dirp = opendir(dir);
-		if (dirp == NULL) {
-		    rb_sys_warning(dir);
-		    free(base);
-		    free(magic);
-		    break;
-		}
-	    }
-	    else {
-		free(base);
-		free(magic);
-		break;
-	    }
-
+    {
 #if defined DOSISH_DRIVE_LETTER
-#define BASE (*base && !((isdirsep(*base) && !base[1]) || (base[1] == ':' && isdirsep(base[2]) && !base[3])))
+#define NEED_DOT  ((beg-path==0) || (beg-path==2 && ISALPHA(*path) && path[1] == ':'))
+#define NEED_LAST ((NEED_DOT) || (beg-path==1 && *path=='/') || (beg-path==3 && ISALPHA(*path) && path[1]==':' && path[2]=='/'))
 #else
-#define BASE (*base && !(isdirsep(*base) && !base[1]))
+#define NEED_DOT  ((beg-path==0))
+#define NEED_LAST ((beg-path==0) || (beg-path==1 && *path=='/'))
 #endif
+	int n = (NEED_LAST) ? (beg - path) : (beg - path) - 1;
+	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(dir);
+	    return 0;
+	}
+	if (S_ISDIR(st.st_mode)) {
+	    if (beg[0] == '*' && beg[1] == '*' && beg[2] == '/') {
+		int n = beg - path;
+		recursive = 1;
+		buf = ALLOC_N(char, n+strlen(beg+3)+1);
+		memcpy(buf, path, n);
+		strcpy(buf+n, beg+3);
+		status = glob_helper(buf, buf+n, flags, func, arg);
+		free(buf);
+		if (status) {
+		    free(dir);
+		    return status;
+		}
+	    }
+	    dirp = opendir(dir);
+	    if (dirp == NULL) {
+		rb_sys_warning(dir);
+		free(dir);
+		return 0;
+	    }
+	}
+	else {
+	    free(dir);
+	    return 0;
+	}
+	free(dir);
+    }
 
-	    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
-		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);
-		    if (lstat(buf, &st) < 0) {
-			if (errno != ENOENT) rb_sys_warning(buf);
-			free(buf);
-			continue;
-		    }
+    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+	int n = beg - path;
+	if (recursive) {
+	    if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0)
+		continue;
+	    buf = ALLOC_N(char, n+NAMLEN(dp)+strlen(end)+3+1);
+	    memcpy(buf, path, n);
+	    strcpy(buf+n, dp->d_name);
+	    if (lstat(buf, &st) < 0) {
+		if (errno != ENOENT) rb_sys_warning(buf);
+		free(buf);
+		continue;
+	    }
+	    if (S_ISDIR(st.st_mode)) {
+		char *t = buf+strlen(buf);
+		memcpy(t, "/**", 3);
+		strcpy(t+3, end);
+		status = glob_helper(buf, t, flags, func, arg);
+		free(buf);
+		if (status) break;
+		continue;
+	    }
+	    free(buf);
+	    continue;
+	}
+	if (do_fnmatch(beg, end, dp->d_name, flags) == 0) {
+	    buf = ALLOC_N(char, n+NAMLEN(dp)+1);
+	    memcpy(buf, path, n);
+	    strcpy(buf+n, dp->d_name);
+	    if (*end == '\0') {
+		status = glob_call_func(func, buf, arg);
+		free(buf);
+		if (status) break;
+		continue;
+	    }
+	    tmp = ALLOC(struct d_link);
+	    tmp->path = buf;
+	    *tail = tmp;
+	    tail = &tmp->next;
+	}
+    }
+    closedir(dirp);
+  finalize:
+    *tail = 0;
+    if (link) {
+	while (link) {
+	    if (status == 0) {
+		if (stat(link->path, &st) == 0) {
 		    if (S_ISDIR(st.st_mode)) {
-			char *t = buf+strlen(buf);
-		        strcpy(t, "/**");
-			strcpy(t+3, m);
-			status = glob_helper(buf, t, flags, func, arg);
-			free(buf);
-			if (status) break;
-			continue;
-		    }
-		    free(buf);
-		    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) {
-			status = glob_call_func(func, buf, arg);
+			int len = strlen(link->path);
+			buf = ALLOC_N(char, len+strlen(end)+1);
+			memcpy(buf, link->path, len);
+			strcpy(buf+len, end);
+			status = glob_helper(buf, buf+len, flags, func, arg);
 			free(buf);
-			if (status) break;
-			continue;
 		    }
-		    tmp = ALLOC(struct d_link);
-		    tmp->path = buf;
-		    *tail = tmp;
-		    tail = &tmp->next;
 		}
-	    }
-	    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);
-			    }
-			}
-			else {
-			    rb_sys_warning(link->path);
-			}
-		    }
-		    tmp = link;
-		    link = link->next;
-		    free(tmp->path);
-		    free(tmp);
+		else {
+		    rb_sys_warning(link->path);
 		}
-		break;
 	    }
+	    tmp = link;
+	    link = link->next;
+	    free(tmp->path);
+	    free(tmp);
 	}
-	p = m;
     }
     return status;
@@ -862,5 +924,11 @@ 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
+    root = rb_path_skip_prefix(root);
+#endif
+    status = glob_helper(path, root, flags, func, arg);
     if (status) rb_jump_tag(status);
 }
@@ -926,5 +994,5 @@ push_braces(ary, s, flags)
 	    break;
 	}
-	p++;
+	Inc(p);
     }
     while (*p) {
@@ -934,5 +1002,5 @@ push_braces(ary, s, flags)
 	    break;
 	}
-	p++;
+	Inc(p);
     }
 
@@ -944,11 +1012,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);
 	}
@@ -986,17 +1054,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);
