about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--posix/fnmatch.c96
-rw-r--r--posix/testfnm.c33
3 files changed, 108 insertions, 30 deletions
diff --git a/ChangeLog b/ChangeLog
index 95f538ef20..feee8234bd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+1999-04-26  Ulrich Drepper  <drepper@cygnus.com>
+
+	* posix/fnmatch.c (internal_fnmatch): Renamed from fnmatch.  Take
+	extra parameter.  Fix several more bugs involving wildcard and
+	ranges.
+	(fnmatch): New function.  Call internal_fnmatch.
+
+	* posix/testfnm.c: More test cases.
+
 1999-04-03  Andreas Jaeger  <aj@arthur.rhein-neckar.de>
 
 	* manual/install.texi (Configuring and compiling): Explain that
diff --git a/posix/fnmatch.c b/posix/fnmatch.c
index e0ff2c34d2..d31c375bd2 100644
--- a/posix/fnmatch.c
+++ b/posix/fnmatch.c
@@ -129,11 +129,10 @@ extern int errno;
 
 /* Match STRING against the filename pattern PATTERN, returning zero if
    it matches, nonzero if not.  */
-int
-fnmatch (pattern, string, flags)
-     const char *pattern;
-     const char *string;
-     int flags;
+static int
+internal_function
+internal_fnmatch (const char *pattern, const char *string,
+		  int no_leading_period, int flags)
 {
   register const char *p = pattern, *n = string;
   register unsigned char c;
@@ -156,8 +155,7 @@ fnmatch (pattern, string, flags)
 	    return FNM_NOMATCH;
 	  else if (*n == '/' && (flags & FNM_FILE_NAME))
 	    return FNM_NOMATCH;
-	  else if (*n == '.' && (flags & FNM_PERIOD) &&
-		   (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	  else if (*n == '.' && no_leading_period)
 	    return FNM_NOMATCH;
 	  break;
 
@@ -175,8 +173,7 @@ fnmatch (pattern, string, flags)
 	  break;
 
 	case '*':
-	  if (*n == '.' && (flags & FNM_PERIOD) &&
-	      (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	  if (*n == '.' && no_leading_period)
 	    return FNM_NOMATCH;
 
 	  for (c = *p++; c == '?' || c == '*'; c = *p++)
@@ -214,9 +211,22 @@ fnmatch (pattern, string, flags)
 	      if (c == '[')
 		{
 		  for (--p; n < endp; ++n)
-		    if (fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+		    if (internal_fnmatch (p, n,
+					  (n == string) && no_leading_period,
+					  ((flags & FNM_FILE_NAME)
+					   ? flags : (flags & ~FNM_PERIOD)))
+			== 0)
 		      return 0;
 		}
+	      else if (c == '/' && (flags & FNM_FILE_NAME))
+		{
+		  while (*n != '\0' && *n != '/')
+		    ++n;
+		  if (*n == '/'
+		      && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
+					    flags) == 0))
+		    return 0;
+		}
 	      else
 		{
 		  if (c == '\\' && !(flags & FNM_NOESCAPE))
@@ -224,7 +234,13 @@ fnmatch (pattern, string, flags)
 		  c = FOLD (c);
 		  for (--p; n < endp; ++n)
 		    if (FOLD ((unsigned char) *n) == c
-			&& fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+			&& (internal_fnmatch (p, n,
+					      ((n == string)
+					       && no_leading_period),
+					      ((flags & FNM_FILE_NAME)
+					       ? flags
+					       : (flags & ~FNM_PERIOD)))
+			    == 0))
 		      return 0;
 		}
 	    }
@@ -245,8 +261,7 @@ fnmatch (pattern, string, flags)
 	    if (*n == '\0')
 	      return FNM_NOMATCH;
 
-	    if (*n == '.' && (flags & FNM_PERIOD) &&
-		(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+	    if (*n == '.' && no_leading_period)
 	      return FNM_NOMATCH;
 
 	    if (*n == '/' && (flags & FNM_FILE_NAME))
@@ -294,7 +309,15 @@ fnmatch (pattern, string, flags)
 			    p += 2;
 			    break;
 			  }
-			str[c1++] = 'c';
+			if (c < 'a' || c >= 'z')
+			  {
+			    /* This cannot possibly be a character class name.
+			       Match it as a normal range.  */
+			    --p;
+			    c = '[';
+			    goto normal_bracket;
+			  }
+			str[c1++] = c;
 		      }
 		    str[c1] = '\0';
 
@@ -325,26 +348,31 @@ fnmatch (pattern, string, flags)
 		else if (c == '\0')
 		  /* [ (unterminated) loses.  */
 		  return FNM_NOMATCH;
-		else if (FOLD (c) == fn)
-		  goto matched;
-
-		cold = c;
-		c = *p++;
-
-		if (c == '-' && *p != ']')
+		else
 		  {
-		    /* It is a range.  */
-		    unsigned char cend = *p++;
-		    if (!(flags & FNM_NOESCAPE) && cend == '\\')
-		      cend = *p++;
-		    if (cend == '\0')
-		      return FNM_NOMATCH;
-
-		    if (cold <= fn && fn <= FOLD (cend))
+		  normal_bracket:
+		    if (FOLD (c) == fn)
 		      goto matched;
 
+		    cold = c;
 		    c = *p++;
+
+		    if (c == '-' && *p != ']')
+		      {
+			/* It is a range.  */
+			unsigned char cend = *p++;
+			if (!(flags & FNM_NOESCAPE) && cend == '\\')
+			  cend = *p++;
+			if (cend == '\0')
+			  return FNM_NOMATCH;
+
+			if (cold <= fn && fn <= FOLD (cend))
+			  goto matched;
+
+			c = *p++;
+		      }
 		  }
+
 		if (c == ']')
 		  break;
 	      }
@@ -404,4 +432,14 @@ fnmatch (pattern, string, flags)
 # undef FOLD
 }
 
+
+int
+fnmatch (pattern, string, flags)
+     const char *pattern;
+     const char *string;
+     int flags;
+{
+  return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
+}
+
 #endif	/* _LIBC or not __GNU_LIBRARY__.  */
diff --git a/posix/testfnm.c b/posix/testfnm.c
index b43f9531fb..5a6c7e12ea 100644
--- a/posix/testfnm.c
+++ b/posix/testfnm.c
@@ -15,7 +15,38 @@ struct {
   { "a/b", "[a-z]/[a-z]", 0, 0 },
   { "a/b", "*", FNM_FILE_NAME, FNM_NOMATCH },
   { "a/b", "*[/]b", FNM_FILE_NAME, FNM_NOMATCH },
-  { "a/b", "*[b]", FNM_FILE_NAME, FNM_NOMATCH }
+  { "a/b", "*[b]", FNM_FILE_NAME, FNM_NOMATCH },
+  { "a/b", "[*]/b", 0, FNM_NOMATCH },
+  { "*/b", "[*]/b", 0, 0 },
+  { "a/b", "[?]/b", 0, FNM_NOMATCH },
+  { "?/b", "[?]/b", 0, 0 },
+  { "a/b", "[[a]/b", 0, 0 },
+  { "[/b", "[[a]/b", 0, 0 },
+  { "a/b", "\\*/b", 0, FNM_NOMATCH },
+  { "*/b", "\\*/b", 0, 0 },
+  { "a/b", "\\?/b", 0, FNM_NOMATCH },
+  { "?/b", "\\?/b", 0, 0 },
+  { "[/b", "[/b", 0, FNM_NOMATCH },
+  { "[/b", "\\[/b", 0, 0 },
+  { "aa/b", "??/b", 0, 0 },
+  { "aa/b", "???b", 0, 0 },
+  { "aa/b", "???b", FNM_PATHNAME, FNM_NOMATCH },
+  { ".a/b", "?a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { "a/.b", "a/?b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { ".a/b", "*a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { "a/.b", "a/*b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { ".a/b", "[.]a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { "a/.b", "a/[.]b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { "a/b", "*/?", FNM_PATHNAME|FNM_PERIOD, 0 },
+  { "a/b", "?/*", FNM_PATHNAME|FNM_PERIOD, 0 },
+  { ".a/b", ".*/?", FNM_PATHNAME|FNM_PERIOD, 0 },
+  { "a/.b", "*/.?", FNM_PATHNAME|FNM_PERIOD, 0 },
+  { "a/.b", "*/*", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
+  { "a/.b", "*?*/*", FNM_PERIOD, 0 },
+  { "a./b", "*[.]/b", FNM_PATHNAME|FNM_PERIOD, 0 },
+  { "a/b", "*[[:alpha:]]/*[[:alnum:]]", FNM_PATHNAME, 0 },
+  { "a/b", "*[![:digit:]]*/[![:d-d]", FNM_PATHNAME, 0 },
+  { "a/[", "*[![:digit:]]*/[![:d-d]", FNM_PATHNAME, FNM_NOMATCH },
 };
 
 int