山本です。 File.fnmatch で、* のあとに ? が続く場合のマッチが正しくないようなので、 修正してみました。File.fnmatch は把握しきれてないのですが、こんな感じで いいでしょうか? # 修正前 E:\ruby-cvs\ruby>..\miniruby -ve "puts File.fnmatch('*?', 'a')" ruby 1.9.0 (2004-01-31) [i386-bccwin32] false # 修正後 E:\ruby-cvs\ruby>miniruby -ve "puts File.fnmatch('*?', 'a')" ruby 1.9.0 (2004-01-31) [i386-bccwin32] true fnmatch 以外の部分は、コメントの修正とか、見栄えの修正とかです。 動作にはまったく影響ありません。 cvs diff -u -wb -p dir.c (in directory E:\ruby-cvs\ruby\) Index: dir.c =================================================================== RCS file: /ruby/ruby/dir.c,v retrieving revision 1.100 diff -u -w -b -p -r1.100 dir.c --- dir.c 29 Jan 2004 11:59:55 -0000 1.100 +++ dir.c 6 Feb 2004 15:30:10 -0000 @@ -267,7 +267,7 @@ fnmatch(pat, string, flags) test = escape && c == '\\' ? pat+1 : pat; while (*s) { - if ((c == '[' || Compare(s, test) == 0) && + if ((c == '?' || c == '[' || Compare(s, test) == 0) && !fnmatch(pat, s, flags | FNM_DOTMATCH)) return 0; else if (ISDIRSEP(*s)) @@ -884,6 +884,7 @@ do_stat(path, pst) int ret = stat(path, pst); if (ret < 0 && errno != ENOENT) rb_sys_warning(path); + return ret; } @@ -895,6 +896,7 @@ do_lstat(path, pst) int ret = lstat(path, pst); if (ret < 0 && errno != ENOENT) rb_sys_warning(path); + return ret; } @@ -905,6 +907,7 @@ do_opendir(path) DIR *dirp = opendir(path); if (dirp == NULL && errno != ENOENT && errno != ENOTDIR) rb_sys_warning(path); + return dirp; } @@ -971,7 +974,13 @@ remove_backslashes(p) } /* Globing pattern */ -enum glob_pattern_type { PLAIN, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR }; +enum glob_pattern_type { + PLAIN, + MAGICAL, + RECURSIVE, + MATCH_ALL, + MATCH_DIR +}; struct glob_pattern { char *str; @@ -985,13 +994,14 @@ glob_make_pattern(p, flags) int flags; { char *buf; - int dirsep = 0; /* pattern terminates with '/' */ + int dirsep = 0; struct glob_pattern *list, *tmp, **tail = &list; while (*p) { tmp = ALLOC(struct glob_pattern); if (p[0] == '*' && p[1] == '*' && p[2] == '/') { - do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); /* fold continuous RECURSIVEs */ + /* fold continuous recursive patterns */ + do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); tmp->type = RECURSIVE; tmp->str = 0; dirsep = 1; @@ -1046,7 +1056,6 @@ join_path(path, dirsep, name) const char *name; { const int len = strlen(path); - char *buf = ALLOC_N(char, len+1+strlen(name)+1); strcpy(buf, path); if (dirsep) { @@ -1056,11 +1065,14 @@ join_path(path, dirsep, name) else { strcpy(buf+len, name); } - return buf; } -enum answer { YES, NO, UNKNOWN }; +enum answer { + YES, + NO, + UNKNOWN +}; #ifndef S_ISDIR # define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) @@ -1111,9 +1123,9 @@ glob_call_func(func, path, arg) static int glob_helper(path, dirsep, exist, isdir, beg, end, flags, func, arg) const char *path; - int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */ - enum answer exist; /* exist? */ - enum answer isdir; /* a directory or a symlink to a directory? */ + int dirsep; /* '/' is needed before appending name to 'path' */ + enum answer exist; /* Does 'path' indicates an existing entry? */ + enum answer isdir; /* Does 'path' indicates a directory or a symlink to a directory? */ struct glob_pattern **beg; struct glob_pattern **end; int flags; @@ -1121,24 +1133,23 @@ glob_helper(path, dirsep, exist, isdir, VALUE arg; { struct stat st; - int status = 0; struct glob_pattern **cur, **new_beg, **new_end; - int recursive = 0, need_readdir = 0, need_plain = 0, match_all = 0, match_dir = 0; - const int escape = !(flags & FNM_NOESCAPE); + int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0; + int status = 0; + int escape = !(flags & FNM_NOESCAPE); for (cur = beg; cur < end; ++cur) { struct glob_pattern *p = *cur; if (p->type == RECURSIVE) { recursive = 1; - need_readdir = 1; p = p->next; } switch (p->type) { case PLAIN: - need_plain = 1; /* ignored if need_readdir is set */ + plain = 1; break; case MAGICAL: - need_readdir = 1; + magical = 1; break; case MATCH_ALL: match_all = 1; @@ -1173,29 +1184,25 @@ glob_helper(path, dirsep, exist, isdir, if (match_all && exist == YES) { status = glob_call_func(func, path, arg); + if (status) return status; } if (match_dir && isdir == YES) { char *buf = join_path(path, dirsep, ""); status = glob_call_func(func, buf, arg); free(buf); - } - if (status) return status; + } if (exist == NO || isdir == NO) return 0; - if (need_readdir) { - + if (magical || recursive) { struct dirent *dp; - DIR *dirp = do_opendir(*path ? path : "."); if (dirp == NULL) return 0; for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { - char *buf = join_path(path, dirsep, dp->d_name); - enum answer new_isdir = UNKNOWN; if (recursive && strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { @@ -1213,13 +1220,11 @@ glob_helper(path, dirsep, exist, isdir, for (cur = beg; cur < end; ++cur) { struct glob_pattern *p = *cur; - if (p->type == RECURSIVE) { if (new_isdir == YES) /* not symlink but real directory */ *new_end++ = p; /* append recursive pattern */ p = p->next; /* 0 times recursion */ } - if (p->type == PLAIN || p->type == MAGICAL) { if (fnmatch(p->str, dp->d_name, flags) == 0) *new_end++ = p->next; @@ -1236,8 +1241,7 @@ glob_helper(path, dirsep, exist, isdir, closedir(dirp); } - else if (need_plain) { - + else if (plain) { struct glob_pattern **copy_beg, **copy_end, **cur2; copy_beg = copy_end = ALLOC_N(struct glob_pattern *, end - beg); @@ -1246,11 +1250,8 @@ glob_helper(path, dirsep, exist, isdir, *copy_end++ = (*cur)->type == PLAIN ? *cur : 0; for (cur = copy_beg; cur < copy_end; ++cur) { - if (*cur) { - - char *buf, *name; - + char *name, *buf; name = ALLOC_N(char, strlen((*cur)->str) + 1); strcpy(name, (*cur)->str); if (escape) remove_backslashes(name); @@ -1258,7 +1259,6 @@ glob_helper(path, dirsep, exist, isdir, new_beg = new_end = ALLOC_N(struct glob_pattern *, end - beg); *new_end++ = (*cur)->next; - for (cur2 = cur + 1; cur2 < copy_end; ++cur2) { if (*cur2 && fnmatch((*cur2)->str, name, flags) == 0) { *new_end++ = (*cur2)->next; @@ -1276,7 +1276,6 @@ glob_helper(path, dirsep, exist, isdir, if (status) break; } - } free(copy_beg);