[#23168] File.fnmatch のリファクタリング — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>

山本です。

13 messages 2004/03/08

[#23192] File.fnmatch と Dir.glob の非互換部分 — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>

山本です。

19 messages 2004/03/13
[#23194] Re: File.fnmatch と Dir.glob の非互換部分 — matz@... (Yukihiro Matsumoto) 2004/03/13

まつもと ゆきひろです

[#23195] Re: File.fnmatch とDir.glob の非互換部分 — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2004/03/14

山本です。

[#23196] Re: File.fnmatch とDir.glob の非互換部分 — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2004/03/14

山本です。

[#23260] Re: File.fnmatch とDir.glob の非互換部分 — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2004/03/30

山本です。

[#23261] Re: File.fnmatch とDir.glob の非互換部分 — matz@... (Yukihiro Matsumoto) 2004/03/30

まつもと ゆきひろです

[#23265] Re: File.fnmatch とDir.glob の非互換部分 — "H.Yamamoto" <ocean@...2.ccsnet.ne.jp> 2004/03/30

山本です。

[#23238] Re: [ruby-cvs] ruby, ruby/lib, ruby/lib/rss, ruby/sample/openssl: * lib/logger.rb: trim tail space of each line. no user visible change. — Kouhei Sutou <kou@...>

須藤です.

10 messages 2004/03/27

[ruby-dev:23184] Re: File.fnmatch のリファクタリング

From: "H.Yamamoto" <ocean@...2.ccsnet.ne.jp>
Date: 2004-03-11 11:45:43 UTC
List: ruby-dev #23184
山本です。

リファクタリングが終わりました。動作の変更点は、

  ・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;

In This Thread