山本です。

[ruby-dev:22303]での

|>これだと、readdir(a)を?magicの時と(child)の列挙のとき2度
|>行っているので、これを1度にできないか考えています。

|と書きましたが、recursiveなほうはlstat()で、そうでないほうはstat()なので、
|単純にまとめるわけにはいきませんでした。

を実装してみました。かなりいじったのでバグの可能性が高まってますが、
自分でテストした限りでは動いているようなのでパッチを送ります。
まずいところがありましたら、指摘してください。

パターンによっては、倍近く高速化しました。

# g: 4787files, 166dirs

# ruby -Ks -e "Dir.glob('g:/**/*'){}"
0:08.458 # [ruby-dev:22431]
0:07.850 # this patch

# ruby -Ks -e "Dir.glob('g:/**/*/'){}"
0:15.303 # [ruby-dev:22431]
0:07.722 # this patch


--- dir.c-22431	Sat Dec 27 23:35:54 2003
+++ dir.c	Sat Dec 27 23:30:54 2003
@@ -892,3 +892,3 @@ remove_backslashes(p, pend)
 static int
-fnmatch_for_substr(p, pend, string, flags)
+do_fnmatch(p, pend, string, flags)
     char *p;
@@ -908,2 +908,34 @@ fnmatch_for_substr(p, pend, string, flag
 
+static int
+do_stat(path, pst)
+    const char *path;
+    struct stat *pst;
+{
+    int ret = stat(path, pst);
+    if (ret < 0 && errno != ENOENT)
+	rb_sys_warning(path);
+    return ret;
+}
+
+static int
+do_lstat(path, pst)
+    const char *path;
+    struct stat *pst;
+{
+    int ret = lstat(path, pst);
+    if (ret < 0 && errno != ENOENT)
+	rb_sys_warning(path);
+    return ret;
+}
+
+static DIR *
+do_opendir(path)
+    const char *path;
+{
+    DIR *dirp = opendir(path);
+    if (dirp == NULL && errno != ENOENT && errno != ENOTDIR)
+	rb_sys_warning(path);
+    return dirp;
+}
+
 #ifndef S_ISDIR
@@ -912,2 +944,10 @@ fnmatch_for_substr(p, pend, string, flag
 
+#ifndef S_ISLNK
+#  ifndef S_IFLNK
+#    define S_ISLNK(m) (0)
+#  else
+#    define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+#  endif
+#endif
+
 struct glob_args {
@@ -961,5 +1001,6 @@ glob_helper(path, sub, separator, flags,
 	    int recursive = 0;
+    int magical = 1;
 
 	    struct d_link {
-		char *path;
+	char *name;
 		struct d_link *next;
@@ -985,25 +1026,14 @@ glob_helper(path, sub, separator, flags,
     if (*p == '\0') { /* magic not found */
-        if (!separator) {
-	    if (lstat(path, &st) < 0) {
-		/* In case stat error is other than ENOENT and
-		   we may want to know what is wrong. */
-		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);
+        if (separator) {
+	    char c = p[-1]; p[-1] = '\0';
+	    if (do_lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
 		p[-1] = c;
+		return glob_call_func(func, path, arg);
 	    }
-	    else if (S_ISDIR(st.st_mode)) {
-		p[-1] = c;
-		status = glob_call_func(func, path, arg);
 	    }
+	else {
+	    if (do_lstat(path, &st) == 0)
+		return glob_call_func(func, path, arg);
 	}
-	return status;
+	return 0;
     }
@@ -1011,73 +1041,60 @@ glob_helper(path, sub, separator, flags,
     if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
-	const int n = p - path;
 		    recursive = 1;
-	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);
+	memmove(p, p+3, strlen(p+3)+1); /* move '\0' too */
+	magical = has_magic(p, &m, flags); /* go to next element */
+	if (!magical) {
+	    status = glob_helper(path, p, separator, flags, func, arg);
 	if (status) return status;
     }
+    }
 
-    {
-	char *dir;
 	if (path == p) {
-	    dir = ALLOC_N(char, 2);
-	    dir[0] = '.';
-	    dir[1] = '\0';
+	dirp = do_opendir(".");
+	if (dirp == NULL) return 0;
 	}
 	else {
-	    const int n = separator ? (p - path) - 1 : (p - path);
-	    dir = ALLOC_N(char, n+1);
-	    memcpy(dir, path, n);
-	    dir[n] = '\0';
-		}
-		dirp = opendir(dir);
-		if (dirp == NULL) {
-	    if (errno != ENOENT && errno != ENOTDIR) rb_sys_warning(dir);
-	    free(dir);
-	    return 0;
-	    }
-	free(dir);
+	char *t = separator ? p-1 : p;
+	char c = *t; *t = '\0';
+	dirp = do_opendir(path);
+	*t = c;
+	if (dirp == NULL) return 0;
 	    }
 
-	    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+    for (dp = readdir(dirp); dp != NULL && status == 0; dp = readdir(dirp)) {
+	int notdir = 0;
+	if (recursive && strcmp(".", dp->d_name) != 0 && strcmp("..", dp->d_name) != 0) {
 	const int n = p - path;
-		if (recursive) {
-		    if (strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0)
-			continue;
-	    buf = ALLOC_N(char, n+NAMLEN(dp)+strlen(m)+3+1);
+	    buf = ALLOC_N(char, n+NAMLEN(dp)+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 (do_lstat(buf, &st) == 0)
 		    if (S_ISDIR(st.st_mode)) {
-			char *t = buf+strlen(buf);
-		memcpy(t, "/**", 3);
-			strcpy(t+3, m);
-		status = glob_helper(buf, t+1, 1, flags, func, arg);
-			free(buf);
-			if (status) break;
-			continue;
+		    tmp = ALLOC(struct d_link);
+		    tmp->name = ALLOC_N(char, NAMLEN(dp)+1);
+		    strcpy(tmp->name, dp->d_name);
+		    *tail = tmp;
+		    tail = &tmp->next;
 		    }
+		else if (!(S_ISLNK(st.st_mode) && do_stat(buf, &st) == 0 && S_ISDIR(st.st_mode)))
+		    notdir = 1;
+	    else
+		notdir = 1;
 		    free(buf);
-		    continue;
 		}
-	if (fnmatch_for_substr(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 (notdir && *m == '/')
+	    continue;
+	if (magical && do_fnmatch(p, m, dp->d_name, flags) == 0) {
+	    const int n1 = p - path;
+	    const int n2 = NAMLEN(dp);
+	    buf = ALLOC_N(char, n1+n2+strlen(m)+1);
+	    memcpy(buf, path, n1);
+	    strcpy(buf+n1, dp->d_name);
 	    if (*m == '\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;
+	    else {
+		strcpy(buf+n1+n2, m);
+		status = glob_helper(buf, buf+n1+n2+1, 1, flags, func, arg);
+	    }
+	    free(buf);
 		}
@@ -1088,19 +1105,15 @@ glob_helper(path, sub, separator, flags,
 		    if (status == 0) {
-			if (stat(link->path, &st) == 0) {
-			    if (S_ISDIR(st.st_mode)) {
-		    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);
+	    const int n1 = p - path;
+	    const int n2 = strlen(link->name);
+	    buf = ALLOC_N(char, n1+n2+4+strlen(p)+1);
+	    memcpy(buf, path, n1);
+	    strcpy(buf+n1, link->name);
+	    strcpy(buf+n1+n2, "/**/");
+	    strcpy(buf+n1+n2+4, p);
+	    status = glob_helper(buf, buf+n1+n2+1, 1, flags, func, arg);
 		    free(buf);
 			    }
-			}
-			else {
-			    rb_sys_warning(link->path);
-			}
-		    }
 		    tmp = link;
 		    link = link->next;
-		    free(tmp->path);
+	free(tmp->name);
 		    free(tmp);