From: "H.Yamamoto" Date: 2004-03-11T20:45:43+09:00 Subject: [ruby-dev:23184] Re: File.fnmatch のリファクタリング 山本です。 リファクタリングが終わりました。動作の変更点は、 ・FNM_PATHNAME のあるときの **/ をサポート ・'/' のみをセパレータとして扱う です。パッチを添付します。また、完全な dir.c を http://www.ccsnet.ne.jp/~ocean/23184/dir.c に置いておきます。 一通りテストしましたが、念のため明日またテストし、深夜(3/12 24:00)にコミットする予定です。 問題があれば報告お願いします。 # これは別件なのですが、拡張ライブラリの makefile 作り直しが、前より遅くなってる気がします。 # ext/socket/mkmf.log を http://www.ccsnet.ne.jp/~ocean/23184/mkmf.log に置いておきますが、 # makefile の作成のときコンパイルが行われているようです。こんなものなのでしょうか? 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.111 diff -u -w -b -p -r1.111 dir.c --- dir.c 8 Mar 2004 12:14:49 -0000 1.111 +++ dir.c 11 Mar 2004 11:28:36 -0000 @@ -164,150 +164,181 @@ CompareImpl(p1, p2, nocase) } #endif /* environment */ -#if defined DOSISH -#define isdirsep(c) ((c) == '/' || (c) == '\\') -#else -#define isdirsep(c) ((c) == '/') -#endif - -static const char * -range( - const char *p, /* pattern */ - const char *test, - int flags) +static char * +bracket(p, s, flags) + const char *p; /* pattern (next to '[') */ + const char *s; /* string */ + int flags; { - int not = 0, ok = 0; - const char *t1, *t2; const int nocase = flags & FNM_CASEFOLD; const int escape = !(flags & FNM_NOESCAPE); + int ok = 0, not = 0; + if (*p == '!' || *p == '^') { not = 1; p++; } - while (*p) { - if (*p == ']') - return ok == not ? 0 : p + 1; - t1 = p; + while (*p != ']') { + const char *t1 = p; if (escape && *t1 == '\\') t1++; if (!*t1) - break; + return 0; p = Next(t1); - if (*p == '-' && p[1] != ']') { - t2 = p + 1; + if (p[0] == '-' && p[1] != ']') { + const char *t2 = p + 1; if (escape && *t2 == '\\') t2++; if (!*t2) - break; + return 0; p = Next(t2); - if (!ok && Compare(t1, test) <= 0 && Compare(test, t2) <= 0) + if (!ok && Compare(t1, s) <= 0 && Compare(s, t2) <= 0) ok = 1; } - else { - if (!ok && Compare(t1, test) == 0) + else + if (!ok && Compare(t1, s) == 0) ok = 1; } - } - return 0; + return ok == not ? 0 : (char *)p + 1; } -#define ISDIRSEP(c) (pathname && isdirsep(c)) -#define PERIOD_S() (period && *s == '.' && \ - (!s_prev || ISDIRSEP(*s_prev))) -#define INC_S() (s = Next(s_prev = s)) +/* If FNM_PATHNAME is set, only path element will be matched. (upto '/' or '\0') + Otherwise, entire string will be matched. + End marker itself won't be compared. +*/ +#define ISEND(c) (!(c) || (pathname && (c) == '/')) +#define RETURN(val) return *pcur = p, *scur = s, (val); + static int -fnmatch(pat, string, flags) - const char *pat; - const char *string; +fnmatch_helper(p, pcur, s, scur, flags) + const char *p; /* pattern */ + const char **pcur; + const char *s; /* string */ + const char **scur; int flags; { - int c; - const char *test; - const char *s = string, *s_prev = 0; - int escape = !(flags & FNM_NOESCAPE); - int pathname = flags & FNM_PATHNAME; - int period = !(flags & FNM_DOTMATCH); - int nocase = flags & FNM_CASEFOLD; + const int period = !(flags & FNM_DOTMATCH); + const int escape = !(flags & FNM_NOESCAPE); + const int nocase = flags & FNM_CASEFOLD; + const int pathname = flags & FNM_PATHNAME; - while (c = *pat) { - switch (c) { - case '?': - if (!*s || ISDIRSEP(*s) || PERIOD_S()) - return FNM_NOMATCH; - INC_S(); - ++pat; - break; - - case '*': - while ((c = *++pat) == '*') - ; + const char *ptmp = 0; + const char *stmp = 0; - if (PERIOD_S()) - return FNM_NOMATCH; + if (period && *s == '.' && *p != '.') /* leading period */ + RETURN(FNM_NOMATCH); - if (!c) { - if (pathname && *rb_path_next(s)) - return FNM_NOMATCH; - else - return 0; + while (1) { + if (*p == '*') { + do { p++; } while (*p == '*'); + if (ISEND(*p)) + RETURN(0); + ptmp = p; + stmp = s; } - else if (ISDIRSEP(c)) { - s = rb_path_next(s); - if (*s) { - INC_S(); - ++pat; - break; + if (ISEND(*s)) { + RETURN(ISEND(*p) ? 0 : FNM_NOMATCH); } - return FNM_NOMATCH; + if (ISEND(*p)) { + goto failed; } + switch (*p) { + case '?': + p++; + Inc(s); + continue; - test = escape && c == '\\' ? pat+1 : pat; - while (*s) { - if ((c == '?' || c == '[' || Compare(s, test) == 0) && - !fnmatch(pat, s, flags | FNM_DOTMATCH)) - return 0; - else if (ISDIRSEP(*s)) - break; - INC_S(); + case '[': { + const char *t = bracket(p + 1, s, flags); + if (t) { + p = t; + Inc(s); + continue; + } + goto failed; } - return FNM_NOMATCH; - - case '[': - if (!*s || ISDIRSEP(*s) || PERIOD_S()) - return FNM_NOMATCH; - pat = range(pat+1, s, flags); - if (!pat) - return FNM_NOMATCH; - INC_S(); - break; case '\\': - if (escape && pat[1] -#if defined DOSISH - && strchr("*?[]\\", pat[1]) -#endif - ) { - c = *++pat; - } - /* FALLTHROUGH */ - - default: -#if defined DOSISH - if (ISDIRSEP(c) && isdirsep(*s)) - ; - else -#endif - if (Compare(pat, s) != 0) + if (escape && p[1]) + p++; + break; /* goto ordinary */ + } + + /* ordinary */ + if (Compare(p, s) != 0) { + goto failed; + } + Inc(p); + Inc(s); + continue; + + failed: /* try next '*' position */ + if (ptmp && stmp) { + if (ISEND(*stmp)) rb_bug("ISEND(*stmp)"); + p = ptmp; + Inc(stmp); + s = stmp; + continue; + } + RETURN(FNM_NOMATCH); + } +} + +static int +fnmatch(p, s, flags) + const char *p; /* pattern */ + const char *s; /* string */ + int flags; +{ + const int period = !(flags & FNM_DOTMATCH); + const int pathname = flags & FNM_PATHNAME; + + const char *pcur; + const char *scur; + + const char *ptmp = 0; + const char *stmp = 0; + + if (pathname) { + while (1) { + if (p[0] == '*' && p[1] == '*' && p[2] == '/') { + do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); + if (period && *s == '.' && *p != '.') { /* leading period */ + ptmp = 0; + stmp = 0; + } + else { + ptmp = p; + stmp = s; + } + } + if (fnmatch_helper(p, &pcur, s, &scur, flags) == 0) { + while (*scur && *scur != '/') Inc(scur); + if (*pcur && *scur) { + p = pcur + 1; + s = scur + 1; + continue; + } + if (!*pcur && !*scur) + return 0; + } + if (ptmp && stmp) { /* failed : try next recursion */ + while (*stmp && *stmp != '/') Inc(stmp); + if (*stmp) { + p = ptmp; + stmp++; + s = stmp; + continue; + } + } return FNM_NOMATCH; - INC_S(); - Inc(pat); - break; } } - return !*s ? 0 : FNM_NOMATCH; + else + return fnmatch_helper(p, &pcur, s, &scur, flags); } VALUE rb_cDir;