From: "H.Yamamoto" Date: 2004-02-23T15:04:08+09:00 Subject: [ruby-dev:23007] Re: File.fnmatchとDir.glob 山本です。 今まで迷いがありましたが、File.fnmatch は fnmatch なんだから、 100% SUSv3 に従うべきだと思うにいたりました。 そこで dir.c の形にしました。ビルドも確認してあります。 ・DOSISH対応はしない ・SUSv3 準拠 ・FNM_PATHNAME ではなく、逆の意味の FNM_SEPMATCH を定義 http://www.ccsnet.ne.jp/~ocean/23007/dir.c にも置いておきます。 cvs diff -u -wb dir.c (in directory E:\ruby-cvs\ruby\) Index: dir.c =================================================================== RCS file: /ruby/ruby/dir.c,v retrieving revision 1.107 diff -u -w -b -r1.107 dir.c --- dir.c 16 Feb 2004 01:33:12 -0000 1.107 +++ dir.c 23 Feb 2004 05:25:42 -0000 @@ -68,7 +68,7 @@ #endif #define FNM_NOESCAPE 0x01 -#define FNM_PATHNAME 0x02 +#define FNM_SEPMATCH 0x02 #define FNM_DOTMATCH 0x04 #define FNM_CASEFOLD 0x08 @@ -164,143 +164,151 @@ } #endif /* environment */ -#if defined DOSISH -#define isdirsep(c) ((c) == '/' || (c) == '\\') -#else -#define isdirsep(c) ((c) == '/') -#endif - -static char * -range(pat, test, flags) - char *pat; - char *test; - int flags; +static int +bracket( + const char *p, + const char *pend, + const char *test, + int flags) { - int not, ok = 0; + int ok = 1; int nocase = flags & FNM_CASEFOLD; - int escape = !(flags & FNM_NOESCAPE); - not = *pat == '!' || *pat == '^'; - if (not) - pat++; - - while (*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++; - pend = pat+1; - if (!*pend) - return 0; - pat = Next(pend); + if (p < pend && (*p == '!' || *p == '^')) { + ok = 0; + p++; } - if (Compare(pstart, test) <= 0 && Compare(test, pend) <= 0) - ok = 1; + + while (p < pend) { + const char *t1 = p; + Inc(p); + if (p < pend && *p == '-') { + const char *t2 = p + 1; + if (t2 < pend) /* range */ + if (Compare(t1, test) <= 0 && Compare(test, t2) <= 0) + return ok; + else { + p = Next(t2); + if (p < pend && *p == '-') /* behavoir is undefined in POSIX.2 */ + p = t2; + continue; } - return 0; + } + if (Compare(t1, test) == 0) + return ok; + } + return !ok; } -#define ISDIRSEP(c) (pathname && isdirsep(c)) -#define PERIOD_S() (period && *s == '.' && \ - (!s_prev || ISDIRSEP(*s_prev))) -#define INC_S() (s = Next(s_prev = s)) static int -fnmatch(pat, string, flags) - const char *pat; - const char *string; - int flags; +fnmatch_helper( + const char *p, /* pattern */ + const char *s, /* string */ + int leading, /* 's' is leading. */ + 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; + int pathname = !(flags & FNM_SEPMATCH); - while (c = *pat) { - switch (c) { + while (*p) { + switch (*p) { case '?': - if (!*s || ISDIRSEP(*s) || PERIOD_S()) + if (period && (leading && *s == '.')) /* leading period */ return FNM_NOMATCH; - INC_S(); - ++pat; - break; + if (!*s || (pathname && *s == '/')) + return FNM_NOMATCH; + p++; + leading = 0; + Inc(s); + continue; case '*': - while ((c = *++pat) == '*') - ; - - if (PERIOD_S()) + if (period && (leading && *s == '.')) /* leading period */ return FNM_NOMATCH; - if (!c) { - if (pathname && *rb_path_next(s)) - return FNM_NOMATCH; + do { p++; } while (*p == '*'); + + if (!*p) { + if (pathname) { + while (*s && *s != '/') Inc(s); + return !*s ? 0 : FNM_NOMATCH; + } else return 0; } - else if (ISDIRSEP(c)) { - s = rb_path_next(s); - if (*s) { - INC_S(); - break; + + if (pathname && *p == '/') { + while (*s && *s != '/') Inc(s); + if (*s == '/') { + p++; + leading = 1; + s++; + continue; } + else return FNM_NOMATCH; } - test = escape && c == '\\' ? pat+1 : pat; - while (*s) { - if ((c == '?' || c == '[' || Compare(s, test) == 0) && - !fnmatch(pat, s, flags | FNM_DOTMATCH)) + while (*s && !(pathname && *s == '/')) { + if (0 == fnmatch_helper(p, s, leading, flags)) return 0; - else if (ISDIRSEP(*s)) - break; - INC_S(); + leading = 0; + Inc(s); } return FNM_NOMATCH; - case '[': - if (!*s || ISDIRSEP(*s) || PERIOD_S()) + case '[': { + const char *pend = ++p; + if (period && (leading && *s == '.')) /* leading period */ return FNM_NOMATCH; - pat = range(pat+1, s, flags); - if (!pat) + if (!*s || (pathname && *s == '/')) return FNM_NOMATCH; - INC_S(); - break; + if (*pend == ']') /* bracket expression includes ']' */ + pend++; + while (*pend && !(pathname && *pend == '/') && *pend != ']') + Inc(pend); + if (*pend == ']') { /* bracket expression */ + if (bracket(p, pend, s, flags)) { + p = pend + 1; + leading = 0; + Inc(s); + continue; + } + else + return FNM_NOMATCH; + } + p--; /* treat '[' as ordinary character */ + break; /* goto ordinary */ + } case '\\': - if (escape && pat[1] -#if defined DOSISH - && strchr("*?[]\\", pat[1]) -#endif - ) { - c = *++pat; + if (escape && p[1]) + p++; + break; /* goto ordinary */ } - /* FALLTHROUGH */ - default: -#if defined DOSISH - if (ISDIRSEP(c) && isdirsep(*s)) - ; - else -#endif - if (Compare(pat, s) != 0) + /* ordinary */ + + if (Compare(p, s) != 0) return FNM_NOMATCH; - INC_S(); - Inc(pat); - break; - } + Inc(p); + leading = pathname && *s == '/'; + Inc(s); } return !*s ? 0 : FNM_NOMATCH; } +static int +fnmatch( + const char *p, /* pattern */ + const char *s, /* string */ + int flags) +{ + return fnmatch_helper(p, s, 1, flags); +} + VALUE rb_cDir; struct dir_data { @@ -919,15 +927,18 @@ return 1; case '[': /* Only accept an open brace if there is a close */ - open++; /* brace to match it. Bracket expressions must be */ - continue; /* complete, according to Posix.2 */ + open = 1; /* brace to match it. Bracket expressions must be */ + if (*p == ']') /* complete, according to Posix.2 */ + p++; + continue; + case ']': if (open) return 1; continue; case '\\': - if (escape && !(c = *p++)) + if (!open && escape && !(c = *p++)) /* escape is disabled inside bracket */ return 0; continue; } @@ -1272,17 +1283,15 @@ int n; int status; - if (flags & FNM_CASEFOLD) { - rb_warn("Dir.glob() ignores File::FNM_CASEFOLD"); - } + if (flags & FNM_CASEFOLD) + rb_raise(rb_eArgError, "FNM_CASEFOLD cannot be used. case sensitivities depends on system."); + if (flags & FNM_SEPMATCH) + rb_raise(rb_eArgError, "FNM_SEPMATCH cannot be used."); #if defined DOSISH flags |= FNM_CASEFOLD; root = rb_path_skip_prefix(root); -#else - flags &= ~FNM_CASEFOLD; #endif - if (*root == '/') root++; n = root - path; @@ -1652,7 +1661,7 @@ rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1); rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE)); - rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME)); + rb_file_const("FNM_SEPMATCH", INT2FIX(FNM_SEPMATCH)); rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH)); rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD)); }