Update.
authorUlrich Drepper <drepper@redhat.com>
Thu, 15 Mar 2001 09:36:19 +0000 (09:36 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 15 Mar 2001 09:36:19 +0000 (09:36 +0000)
2001-03-15  Ulrich Drepper  <drepper@redhat.com>

* posix/fnmatch.h (FNM_EXTMATCH): Define.
* posix/fnmatch.c (NO_LEADING_PERIOD): Define.
(posixly_correct): Move global variable here.
(INT, EXT, END): Name new functions defined in fnmatch_loop.c.
(fnmatch): Pretty printing.
* posix/fnmatch_loop.c: Add code to handle FNM_EXTMATCH.
* posix/tst-fnmatch.c: Recognize EXTMATCH flag.
* posix/tst-fnmatch.input: Add tests for extended matching.

* posix/testfnm.c: Add test for patterns with multiple ** before /.
* posix/fnmatch_loop.c: Fix problem with the test above.

ChangeLog
posix/fnmatch.c
posix/fnmatch.h
posix/fnmatch_loop.c
posix/testfnm.c
posix/tst-fnmatch.c
posix/tst-fnmatch.input

index 23bc92a..da0bcbb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2001-03-15  Ulrich Drepper  <drepper@redhat.com>
+
+       * posix/fnmatch.h (FNM_EXTMATCH): Define.
+       * posix/fnmatch.c (NO_LEADING_PERIOD): Define.
+       (posixly_correct): Move global variable here.
+       (INT, EXT, END): Name new functions defined in fnmatch_loop.c.
+       (fnmatch): Pretty printing.
+       * posix/fnmatch_loop.c: Add code to handle FNM_EXTMATCH.
+       * posix/tst-fnmatch.c: Recognize EXTMATCH flag.
+       * posix/tst-fnmatch.input: Add tests for extended matching.
+
+       * posix/testfnm.c: Add test for patterns with multiple ** before /.
+       * posix/fnmatch_loop.c: Fix problem with the test above.
+
 2001-03-14  Ulrich Drepper  <drepper@redhat.com>
 
        * sysdeps/ieee754/dbl-64/e_sqrt.c (__ieee754_sqrt): Remove
index d0d7002..98c5ffc 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-1993, 1996-1999, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1991-1993,1996-1999,2000,2001 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    This library is free software; you can redistribute it and/or
 # define mbsrtowcs __mbsrtowcs
 #endif
 
+/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set.  */
+#define NO_LEADING_PERIOD(flags) \
+  ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))
+
 /* Comment out all this code if we are using the GNU C Library, and are not
    actually compiling the library itself.  This code is part of the GNU C
    Library, but also included in many other GNU distributions.  Compiling
@@ -153,6 +157,9 @@ extern char *getenv ();
 extern int errno;
 # endif
 
+/* Global variable.  */
+static int posixly_correct;
+
 /* This function doesn't exist on most systems.  */
 
 # if !defined HAVE___STRCHRNUL && !defined _LIBC
@@ -195,15 +202,18 @@ __wcschrnul (s, c)
 # endif
 # define CHAR  char
 # define UCHAR unsigned char
+# define INT   int
 # define FCT   internal_fnmatch
+# define EXT   ext_match
+# define END   end_pattern
 # define L(CS) CS
 # ifdef _LIBC
 #  define BTOWC(C)     __btowc (C)
 # else
 #  define BTOWC(C)     btowc (C)
 # endif
-# define STRCHR(S, C)  strchr (S, C)
-# define STRCHRNUL(S, C) __strchrnul (S, C)
+# define MEMPCPY(D, S, N) __mempcpy (D, S, N)
+# define MEMCHR(S, C, N) memchr (S, C, N)
 # define STRCOLL(S1, S2) strcoll (S1, S2)
 # include "fnmatch_loop.c"
 
@@ -217,11 +227,14 @@ __wcschrnul (s, c)
 #  endif
 #  define CHAR wchar_t
 #  define UCHAR        wint_t
+#  define INT  wint_t
 #  define FCT  internal_fnwmatch
+#  define EXT  ext_wmatch
+# define END   end_wpattern
 #  define L(CS)        L##CS
 #  define BTOWC(C)     (C)
-#  define STRCHR(S, C) wcschr (S, C)
-#  define STRCHRNUL(S, C) __wcschrnul (S, C)
+#  define MEMPCPY(D, S, N) __wmempcpy (D, S, N)
+#  define MEMCHR(S, C, N) wmemchr (S, C, N)
 #  define STRCOLL(S1, S2) wcscoll (S1, S2)
 #  define WIDE_CHAR_VERSION 1
 
@@ -303,42 +316,43 @@ fnmatch (pattern, string, flags)
      int flags;
 {
 # if HANDLE_MULTIBYTE
-  mbstate_t ps;
-  size_t n;
-  wchar_t *wpattern;
-  wchar_t *wstring;
-
-  if (MB_CUR_MAX == 1)
-    /* This is an optimization for 8-bit character set.  */
-    return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
-
-  /* Convert the strings into wide characters.  */
-  memset (&ps, '\0', sizeof (ps));
-  n = mbsrtowcs (NULL, &pattern, 0, &ps);
-  if (n == (size_t) -1)
-    /* Something wrong.
-       XXX Do we have to set `errno' to something which mbsrtows hasn't
-       already done?  */
-    return -1;
-  wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-  assert (mbsinit (&ps));
-  (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
-
-  assert (mbsinit (&ps));
-  n = mbsrtowcs (NULL, &string, 0, &ps);
-  if (n == (size_t) -1)
-    /* Something wrong.
-       XXX Do we have to set `errno' to something which mbsrtows hasn't
-       already done?  */
-    return -1;
-  wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
-  assert (mbsinit (&ps));
-  (void) mbsrtowcs (wstring, &string, n + 1, &ps);
-
-  return internal_fnwmatch (wpattern, wstring, flags & FNM_PERIOD, flags);
-# else
-  return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
+  if (__builtin_expect (MB_CUR_MAX, 1) != 1)
+    {
+      mbstate_t ps;
+      size_t n;
+      wchar_t *wpattern;
+      wchar_t *wstring;
+
+      /* Convert the strings into wide characters.  */
+      memset (&ps, '\0', sizeof (ps));
+      n = mbsrtowcs (NULL, &pattern, 0, &ps);
+      if (__builtin_expect (n, 0) == (size_t) -1)
+       /* Something wrong.
+          XXX Do we have to set `errno' to something which mbsrtows hasn't
+          already done?  */
+       return -1;
+      wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
+      assert (mbsinit (&ps));
+      (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
+
+      assert (mbsinit (&ps));
+      n = mbsrtowcs (NULL, &string, 0, &ps);
+      if (__builtin_expect (n, 0) == (size_t) -1)
+       /* Something wrong.
+          XXX Do we have to set `errno' to something which mbsrtows hasn't
+          already done?  */
+       return -1;
+      wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
+      assert (mbsinit (&ps));
+      (void) mbsrtowcs (wstring, &string, n + 1, &ps);
+
+      return internal_fnwmatch (wpattern, wstring, wstring + n,
+                               flags & FNM_PERIOD, flags);
+    }
 # endif  /* mbstate_t and mbsrtowcs or _LIBC.  */
+
+  return internal_fnmatch (pattern, string, string + strlen (string),
+                          flags & FNM_PERIOD, flags);
 }
 
 #endif /* _LIBC or not __GNU_LIBRARY__.  */
index cc3ec37..56cb561 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
+/* Copyright (C) 1991,92,93,96,97,98,99,2001 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -59,6 +59,7 @@ extern "C" {
 # define FNM_FILE_NAME  FNM_PATHNAME   /* Preferred GNU name.  */
 # define FNM_LEADING_DIR (1 << 3)      /* Ignore `/...' after a match.  */
 # define FNM_CASEFOLD   (1 << 4)       /* Compare without regard to case.  */
+# define FNM_EXTMATCH   (1 << 5)       /* Use ksh-like extended matching. */
 #endif
 
 /* Value returned by `fnmatch' if STRING does not match PATTERN.  */
index a453aec..e9c7af2 100644 (file)
 /* Match STRING against the filename pattern PATTERN, returning zero if
    it matches, nonzero if not.  */
 static int FCT (const CHAR *pattern, const CHAR *string,
-               int no_leading_period, int flags) internal_function;
+               const CHAR *string_end, int no_leading_period, int flags)
+     internal_function;
+static int EXT (INT opt, const CHAR *pattern, const CHAR *string,
+               const CHAR *string_end, int no_leading_period, int flags)
+     internal_function;
+static const CHAR *END (const CHAR *patternp) internal_function;
 
 static int
 internal_function
-FCT (pattern, string, no_leading_period, flags)
+FCT (pattern, string, string_end, no_leading_period, flags)
      const CHAR *pattern;
      const CHAR *string;
+     const CHAR *string_end;
      int no_leading_period;
      int flags;
 {
@@ -43,18 +49,27 @@ FCT (pattern, string, no_leading_period, flags)
 
   while ((c = *p++) != L('\0'))
     {
+      int new_no_leading_period = 0;
       c = FOLD (c);
 
       switch (c)
        {
        case L('?'):
-         if (*n == L('\0'))
+         if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+           {
+             int res;
+
+             res = EXT (c, p, n, string_end, no_leading_period,
+                        flags);
+             if (res != -1)
+               return res;
+           }
+
+         if (n == string_end)
            return FNM_NOMATCH;
          else if (*n == L('/') && (flags & FNM_FILE_NAME))
            return FNM_NOMATCH;
-         else if (*n == L('.') && no_leading_period
-                  && (n == string
-                      || (n[-1] == L('/') && (flags & FNM_FILE_NAME))))
+         else if (*n == L('.') && no_leading_period)
            return FNM_NOMATCH;
          break;
 
@@ -67,27 +82,48 @@ FCT (pattern, string, no_leading_period, flags)
                return FNM_NOMATCH;
              c = FOLD (c);
            }
-         if (FOLD ((UCHAR) *n) != c)
+         if (n == string_end || FOLD ((UCHAR) *n) != c)
            return FNM_NOMATCH;
          break;
 
        case L('*'):
-         if (*n == L('.') && no_leading_period
-             && (n == string
-                 || (n[-1] == L('/') && (flags & FNM_FILE_NAME))))
+         if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+           {
+             int res;
+
+             res = EXT (c, p, n, string_end, no_leading_period,
+                        flags);
+             if (res != -1)
+               return res;
+           }
+
+         if (n != string_end && *n == L('.') && no_leading_period)
            return FNM_NOMATCH;
 
          for (c = *p++; c == L('?') || c == L('*'); c = *p++)
            {
-             if (*n == L('/') && (flags & FNM_FILE_NAME))
-               /* A slash does not match a wildcard under FNM_FILE_NAME.  */
-               return FNM_NOMATCH;
-             else if (c == L('?'))
+             if (*p == L('(') && (flags & FNM_EXTMATCH) != 0)
+               {
+                 const CHAR *endp = END (p);
+                 if (endp != p)
+                   {
+                     /* This is a pattern.  Skip over it.  */
+                     p = endp;
+                     continue;
+                   }
+               }
+
+             if (c == L('?'))
                {
                  /* A ? needs to match one character.  */
-                 if (*n == L('\0'))
+                 if (n == string_end)
                    /* There isn't another character; no match.  */
                    return FNM_NOMATCH;
+                 else if (*n == L('/')
+                          && __builtin_expect (flags & FNM_FILE_NAME, 0))
+                   /* A slash does not match a wildcard under
+                      FNM_FILE_NAME.  */
+                   return FNM_NOMATCH;
                  else
                    /* One character of the string is consumed in matching
                       this ? wildcard, so *??? won't match if there are
@@ -110,7 +146,7 @@ FCT (pattern, string, no_leading_period, flags)
                    result = 0;
                  else
                    {
-                     if (STRCHR (n, L('/')) == NULL)
+                     if (MEMCHR (n, L('/'), string_end - n) == NULL)
                        result = 0;
                    }
                }
@@ -121,44 +157,47 @@ FCT (pattern, string, no_leading_period, flags)
            {
              const CHAR *endp;
 
-             endp = STRCHRNUL (n, (flags & FNM_FILE_NAME) ? L('/') : L('\0'));
+             endp = MEMCHR (n, (flags & FNM_FILE_NAME) ? L('/') : L('\0'),
+                            string_end - n);
+             if (endp == NULL)
+               endp = string_end;
 
-             if (c == L('['))
+             if (c == L('[')
+                 || (__builtin_expect (flags & FNM_EXTMATCH, 0) != 0
+                     /* XXX Do we have to add '!'?  */
+                     && (c == L('@') || c == L('+')) && *p == L('(')))
                {
                  int flags2 = ((flags & FNM_FILE_NAME)
                                ? flags : (flags & ~FNM_PERIOD));
+                 int no_leading_period2 = no_leading_period;
 
-                 for (--p; n < endp; ++n)
-                   if (FCT (p, n, (no_leading_period
-                                   && (n == string
-                                       || (n[-1] == L('/')
-                                           && (flags & FNM_FILE_NAME)))),
-                            flags2) == 0)
+                 for (--p; n < endp; ++n, no_leading_period2 = 0)
+                   if (FCT (p, n, string_end, no_leading_period2, flags2)
+                       == 0)
                      return 0;
                }
              else if (c == L('/') && (flags & FNM_FILE_NAME))
                {
-                 while (*n != L('\0') && *n != L('/'))
+                 while (n < string_end && *n != L('/'))
                    ++n;
-                 if (*n == L('/')
-                     && (FCT (p, n + 1, flags & FNM_PERIOD, flags) == 0))
+                 if (n < string_end && *n == L('/')
+                     && (FCT (p, n + 1, string_end, flags & FNM_PERIOD, flags)
+                         == 0))
                    return 0;
                }
              else
                {
                  int flags2 = ((flags & FNM_FILE_NAME)
                                ? flags : (flags & ~FNM_PERIOD));
+                 int no_leading_period2 = no_leading_period;
 
                  if (c == L('\\') && !(flags & FNM_NOESCAPE))
                    c = *p;
                  c = FOLD (c);
-                 for (--p; n < endp; ++n)
+                 for (--p; n < endp; ++n, no_leading_period2 = 0)
                    if (FOLD ((UCHAR) *n) == c
-                       && (FCT (p, n, (no_leading_period
-                                       && (n == string
-                                           || (n[-1] == L('/')
-                                               && (flags & FNM_FILE_NAME)))),
-                                flags2) == 0))
+                       && (FCT (p, n, string_end, no_leading_period2, flags2)
+                           == 0))
                      return 0;
                }
            }
@@ -168,20 +207,18 @@ FCT (pattern, string, no_leading_period, flags)
 
        case L('['):
          {
-           static int posixly_correct;
            /* Nonzero if the sense of the character class is inverted.  */
            register int not;
            CHAR cold;
+           UCHAR fn;
 
            if (posixly_correct == 0)
              posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
 
-           if (*n == L('\0'))
+           if (n == string_end)
              return FNM_NOMATCH;
 
-           if (*n == L('.') && no_leading_period
-               && (n == string
-                   || (n[-1] == L('/') && (flags & FNM_FILE_NAME))))
+           if (*n == L('.') && no_leading_period)
              return FNM_NOMATCH;
 
            if (*n == L('/') && (flags & FNM_FILE_NAME))
@@ -192,11 +229,11 @@ FCT (pattern, string, no_leading_period, flags)
            if (not)
              ++p;
 
+           fn = FOLD ((UCHAR) *n);
+
            c = *p++;
            for (;;)
              {
-               UCHAR fn = FOLD ((UCHAR) *n);
-
                if (!(flags & FNM_NOESCAPE) && c == L('\\'))
                  {
                    if (*p == L('\0'))
@@ -878,30 +915,275 @@ FCT (pattern, string, no_leading_period, flags)
          }
          break;
 
+       case L('+'):
+       case L('@'):
+       case L('!'):
+         if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+           {
+             int res;
+
+             res = EXT (c, p, n, string_end, no_leading_period, flags);
+             if (res != -1)
+               return res;
+           }
+         goto normal_match;
+
+       case L('/'):
+         if (NO_LEADING_PERIOD (flags))
+           {
+             if (n == string_end || c != *n)
+               return FNM_NOMATCH;
+
+             new_no_leading_period = 1;
+             break;
+           }
+         /* FALLTHROUGH */
        default:
-         if (c != FOLD ((UCHAR) *n))
+       normal_match:
+         if (n == string_end || c != FOLD ((UCHAR) *n))
            return FNM_NOMATCH;
        }
 
+      no_leading_period = new_no_leading_period;
       ++n;
     }
 
-  if (*n == '\0')
+  if (n == string_end)
     return 0;
 
-  if ((flags & FNM_LEADING_DIR) && *n == L('/'))
+  if ((flags & FNM_LEADING_DIR) && n != string_end && *n == L('/'))
     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
     return 0;
 
   return FNM_NOMATCH;
 }
 
+
+static const CHAR *
+internal_function
+END (const CHAR *pattern)
+{
+  const CHAR *p = pattern;
+
+  while (1)
+    if (*++p == L('\0'))
+      /* This is an invalid pattern.  */
+      return pattern;
+    else if (*p == L('['))
+      {
+       /* Handle brackets special.  */
+       if (posixly_correct == 0)
+         posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
+
+       /* Skip the not sign.  We have to recognize it because of a possibly
+          following ']'.  */
+       if (*++p == L('!') || (posixly_correct < 0 && *p == L('^')))
+         ++p;
+       /* A leading ']' is recognized as such.  */
+       if (*p == L(']'))
+         ++p;
+       /* Skip over all characters of the list.  */
+       while (*p != L(']'))
+         if (*p++ == L('\0'))
+           /* This is no valid pattern.  */
+           return pattern;
+      }
+    else if ((*p == L('?') || *p == L('*') || *p == L('+') || *p == L('@')
+             || *p == L('!')) && p[1] == L('('))
+      p = END (p + 1);
+    else if (*p == L(')'))
+      break;
+
+  return p + 1;
+}
+
+
+static int
+internal_function
+EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end,
+     int no_leading_period, int flags)
+{
+  const CHAR *startp;
+  int level;
+  struct patternlist
+  {
+    struct patternlist *next;
+    CHAR str[0];
+  } *list = NULL;
+  struct patternlist **lastp = &list;
+  const CHAR *p;
+  const CHAR *rs;
+
+  /* Parse the pattern.  Store the individual parts in the list.  */
+  level = 0;
+  for (startp = p = pattern + 1; level >= 0; ++p)
+    if (*p == L('\0'))
+      /* This is an invalid pattern.  */
+      return -1;
+    else if (*p == L('['))
+      {
+       /* Handle brackets special.  */
+       if (posixly_correct == 0)
+         posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
+
+       /* Skip the not sign.  We have to recognize it because of a possibly
+          following ']'.  */
+       if (*++p == L('!') || (posixly_correct < 0 && *p == L('^')))
+         ++p;
+       /* A leading ']' is recognized as such.  */
+       if (*p == L(']'))
+         ++p;
+       /* Skip over all characters of the list.  */
+       while (*p != L(']'))
+         if (*p++ == L('\0'))
+           /* This is no valid pattern.  */
+           return -1;
+      }
+    else if ((*p == L('?') || *p == L('*') || *p == L('+') || *p == L('@')
+             || *p == L('!')) && p[1] == L('('))
+      /* Remember the nesting level.  */
+      ++level;
+    else if (*p == L(')'))
+      {
+       if (level-- == 0)
+         {
+           /* This means we found the end of the pattern.  */
+#define NEW_PATTERN \
+           struct patternlist *newp = alloca (sizeof (struct patternlist)    \
+                                              + ((p - startp + 1)            \
+                                                 * sizeof (CHAR)));          \
+           *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L('\0');    \
+           newp->next = NULL;                                                \
+           *lastp = newp;                                                    \
+           lastp = &newp->next
+           NEW_PATTERN;
+         }
+      }
+    else if (*p == L('|'))
+      {
+       if (level == 0)
+         {
+           NEW_PATTERN;
+           startp = p + 1;
+         }
+      }
+  assert (list != NULL);
+  assert (p[-1] == L(')'));
+
+  switch (opt)
+    {
+    case L('*'):
+      if (FCT (p, string, string_end, no_leading_period, flags) == 0)
+       return 0;
+      /* FALLTHROUGH */
+
+    case L('+'):
+      do
+       {
+         for (rs = string; rs <= string_end; ++rs)
+           /* First match the prefix with the current pattern with the
+              current pattern.  */
+           if (FCT (list->str, string, rs, no_leading_period,
+                    flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0
+               /* This was successful.  Now match the rest with the rest
+                  of the pattern.  */
+               && (FCT (p, rs, string_end,
+                        rs == string
+                        ? no_leading_period
+                        : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
+                        flags & FNM_FILE_NAME
+                        ? flags : flags & ~FNM_PERIOD) == 0
+                   /* This didn't work.  Try the whole pattern.  */
+                   || (rs != string
+                       && FCT (pattern - 1, rs, string_end,
+                               rs == string
+                               ? no_leading_period
+                               : (rs[-1] == '/' && NO_LEADING_PERIOD (flags)
+                                  ? 1 : 0),
+                               flags & FNM_FILE_NAME
+                               ? flags : flags & ~FNM_PERIOD) == 0)))
+             /* It worked.  Signal success.  */
+             return 0;
+       }
+      while ((list = list->next) != NULL);
+
+      /* None of the patterns lead to a match.  */
+      return FNM_NOMATCH;
+
+    case L('?'):
+      if (FCT (p, string, string_end, no_leading_period, flags) == 0)
+       return 0;
+      /* FALLTHROUGH */
+
+    case L('@'):
+      do
+       {
+         for (rs = string; rs <= string_end; ++rs)
+           /* First match the prefix with the current pattern with the
+              current pattern.  */
+           if (FCT (list->str, string, rs, no_leading_period,
+                    flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0
+               /* This was successful.  Now match the rest of the strings
+                  with the rest of the pattern.  */
+               && (FCT (p, rs, string_end,
+                        rs == string
+                        ? no_leading_period
+                        : (rs[-1] == '/' && NO_LEADING_PERIOD (flags)
+                           ? 1 : 0),
+                        flags & FNM_FILE_NAME
+                        ? flags : flags & ~FNM_PERIOD) == 0))
+             /* It worked.  Signal success.  */
+             return 0;
+       }
+      while ((list = list->next) != NULL);
+
+      /* None of the patterns lead to a match.  */
+      return FNM_NOMATCH;
+
+    case L('!'):
+      for (rs = string; rs <= string_end; ++rs)
+       {
+         struct patternlist *runp;
+
+         for (runp = list; runp != NULL; runp = runp->next)
+           if (FCT (runp->str, string, rs,  no_leading_period,
+                    flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0)
+             break;
+
+         /* If none of the patterns matched see whether the rest does.  */
+         if (runp == NULL
+             && (FCT (p, rs, string_end,
+                      rs == string
+                      ? no_leading_period
+                      : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
+                      flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD)
+                 == 0))
+           /* This is successful.  */
+           return 0;
+       }
+
+      /* None of the patterns together with the rest of the pattern
+        lead to a match.  */
+      return FNM_NOMATCH;
+
+    default:
+      assert (! "Invalid extended matching operator");
+      break;
+    }
+
+  return -1;
+}
+
+
 #undef FOLD
 #undef CHAR
 #undef UCHAR
+#undef INT
 #undef FCT
-#undef STRCHR
-#undef STRCHRNUL
+#undef EXT
+#undef END
+#undef MEMPCPY
+#undef MEMCHR
 #undef STRCOLL
 #undef L
 #undef BTOWC
index b735a26..f316532 100644 (file)
@@ -54,6 +54,9 @@ struct {
   { "a/b", "*a*", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
   { "ab/c", "*a?", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
   { "ab/c", "a?", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
+  { "a/b", "?*/?", FNM_PATHNAME, 0 },
+  { "/b", "*/?", FNM_PATHNAME, 0 },
+  { "/b", "**/?", FNM_PATHNAME, 0 },
 };
 
 int
index 7193615..eee50c6 100644 (file)
@@ -1,5 +1,5 @@
 /* Tests for fnmatch function.
-   Copyright (C) 2000 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -263,6 +263,12 @@ convert_flags (const char *str)
          result |= FNM_CASEFOLD;
          len = 8;
        }
+      else if (strncasecmp (str, "EXTMATCH", 8) == 0
+              && (str[8] == '|' || str[8] == '\0'))
+       {
+         result |= FNM_EXTMATCH;
+         len = 8;
+       }
       else
        return -1;
 
@@ -315,6 +321,13 @@ flag_output (int flags)
       cp = stpcpy (cp, "FNM_CASEFOLD");
       first = 0;
     }
+  if (flags & FNM_EXTMATCH)
+    {
+      if (! first)
+       *cp++ = '|';
+      cp = stpcpy (cp, "FNM_EXTMATCH");
+      first = 0;
+    }
   if (cp == buf)
     *cp++ = '0';
   *cp = '\0';
index 6add86c..6ae0a2a 100644 (file)
@@ -348,7 +348,7 @@ C            "\\/\\$"               "\\/\\$"               0       NOESCAPE
 
 # B.6 033(C)
 C               ".asd"                 ".*"                   0       PERIOD
-C               "/.asd"                        "*"                    0       PERIOD
+C               "/.asd"                "*"                    0       PERIOD
 C               "/as/.df"              "*/?*f"                0       PERIOD
 C               "..asd"                ".[!a-z]*"             0       PERIOD
 
@@ -370,6 +370,17 @@ C           "/."                   "/[!a-z]"              NOMATCH PATHNAME|PERIOD
 C               "/a./.b."              "/*/*"                 NOMATCH PATHNAME|PERIOD
 C               "/a./.b."              "/??/???"              NOMATCH PATHNAME|PERIOD
 
+# Some home-grown tests.
+C              "foobar"                "foo*[abc]z"           NOMATCH
+C              "foobaz"                "foo*[abc][xyz]"       0
+C              "foobaz"                "foo?*[abc][xyz]"      0
+C              "foobaz"                "foo?*[abc][x/yz]"     0
+C              "foobaz"                "foo?*[abc]/[xyz]"     NOMATCH PATHNAME
+C              "a"                     "a/"                   NOMATCH PATHNAME
+C              "a/"                    "a"                    NOMATCH PATHNAME
+C              "//a"                   "/a"                   NOMATCH PATHNAME
+C              "/a"                    "//a"                  NOMATCH PATHNAME
+
 # Following are tests outside the scope of IEEE 2003.2 since they are using
 # locales other than the C locale.  The main focus of the tests is on the
 # handling of ranges and the recognition of character (vs bytes).
@@ -578,3 +589,128 @@ C          "x/y/z"                "x/y"                  0       PATHNAME|LEADING_DIR
 C               "x"                    "x?y"                  NOMATCH PATHNAME|LEADING_DIR
 C               "x/y"                  "x?y"                  NOMATCH PATHNAME|LEADING_DIR
 C               "x/y/z"                "x?y"                  NOMATCH PATHNAME|LEADING_DIR
+
+# ksh style matching.
+C              "abcd"                  "?@(a|b)*@(c)d"        0       EXTMATCH
+C              "/dev/udp/129.22.8.102/45" "/dev/@(tcp|udp)/*/*" 0     PATHNAME|EXTMATCH
+C              "12"                    "[1-9]*([0-9])"        0       EXTMATCH
+C              "12abc"                 "[1-9]*([0-9])"        NOMATCH EXTMATCH
+C              "1"                     "[1-9]*([0-9])"        0       EXTMATCH
+C              "07"                    "+([0-7])"             0       EXTMATCH
+C              "0377"                  "+([0-7])"             0       EXTMATCH
+C              "09"                    "+([0-7])"             NOMATCH EXTMATCH
+C              "paragraph"             "para@(chute|graph)"   0       EXTMATCH
+C              "paramour"              "para@(chute|graph)"   NOMATCH EXTMATCH
+C              "para991"               "para?([345]|99)1"     0       EXTMATCH
+C              "para381"               "para?([345]|99)1"     NOMATCH EXTMATCH
+C              "paragraph"             "para*([0-9])"         NOMATCH EXTMATCH
+C              "para"                  "para*([0-9])"         0       EXTMATCH
+C              "para13829383746592"    "para*([0-9])"         0       EXTMATCH
+C              "paragraph"             "para+([0-9])"         NOMATCH EXTMATCH
+C              "para"                  "para+([0-9])"         NOMATCH EXTMATCH
+C              "para987346523"         "para+([0-9])"         0       EXTMATCH
+C              "paragraph"             "para!(*.[0-9])"       0       EXTMATCH
+C              "para.38"               "para!(*.[0-9])"       0       EXTMATCH
+C              "para.graph"            "para!(*.[0-9])"       0       EXTMATCH
+C              "para39"                "para!(*.[0-9])"       0       EXTMATCH
+C              ""                      "*(0|1|3|5|7|9)"       0       EXTMATCH
+C              "137577991"             "*(0|1|3|5|7|9)"       0       EXTMATCH
+C              "2468"                  "*(0|1|3|5|7|9)"       NOMATCH EXTMATCH
+C              "1358"                  "*(0|1|3|5|7|9)"       NOMATCH EXTMATCH
+C              "file.c"                "*.c?(c)"              0       EXTMATCH
+C              "file.C"                "*.c?(c)"              NOMATCH EXTMATCH
+C              "file.cc"               "*.c?(c)"              0       EXTMATCH
+C              "file.ccc"              "*.c?(c)"              NOMATCH EXTMATCH
+C              "parse.y"               "!(*.c|*.h|Makefile.in|config*|README)" 0 EXTMATCH
+C              "shell.c"               "!(*.c|*.h|Makefile.in|config*|README)" NOMATCH EXTMATCH
+C              "Makefile"              "!(*.c|*.h|Makefile.in|config*|README)" 0 EXTMATCH
+C              "VMS.FILE;1"            "*\;[1-9]*([0-9])"     0       EXTMATCH
+C              "VMS.FILE;0"            "*\;[1-9]*([0-9])"     NOMATCH EXTMATCH
+C              "VMS.FILE;"             "*\;[1-9]*([0-9])"     NOMATCH EXTMATCH
+C              "VMS.FILE;139"          "*\;[1-9]*([0-9])"     0       EXTMATCH
+C              "VMS.FILE;1N"           "*\;[1-9]*([0-9])"     NOMATCH EXTMATCH
+C              "abcfefg"               "ab**(e|f)"            0       EXTMATCH
+C              "abcfefg"               "ab**(e|f)g"           0       EXTMATCH
+C              "ab"                    "ab*+(e|f)"            NOMATCH EXTMATCH
+C              "abef"                  "ab***ef"              0       EXTMATCH
+C              "abef"                  "ab**"                 0       EXTMATCH
+C              "fofo"                  "*(f*(o))"             0       EXTMATCH
+C              "ffo"                   "*(f*(o))"             0       EXTMATCH
+C              "foooofo"               "*(f*(o))"             0       EXTMATCH
+C              "foooofof"              "*(f*(o))"             0       EXTMATCH
+C              "fooofoofofooo"         "*(f*(o))"             0       EXTMATCH
+C              "foooofof"              "*(f+(o))"             NOMATCH EXTMATCH
+C              "xfoooofof"             "*(f*(o))"             NOMATCH EXTMATCH
+C              "foooofofx"             "*(f*(o))"             NOMATCH EXTMATCH
+C              "ofxoofxo"              "*(*(of*(o)x)o)"       0       EXTMATCH
+C              "ofooofoofofooo"        "*(f*(o))"             NOMATCH EXTMATCH
+C              "foooxfooxfoxfooox"     "*(f*(o)x)"            0       EXTMATCH
+C              "foooxfooxofoxfooox"    "*(f*(o)x)"            NOMATCH EXTMATCH
+C              "foooxfooxfxfooox"      "*(f*(o)x)"            0       EXTMATCH
+C              "ofxoofxo"              "*(*(of*(o)x)o)"       0       EXTMATCH
+C              "ofoooxoofxo"           "*(*(of*(o)x)o)"       0       EXTMATCH
+C              "ofoooxoofxoofoooxoofxo" "*(*(of*(o)x)o)"      0       EXTMATCH
+C              "ofoooxoofxoofoooxoofxoo" "*(*(of*(o)x)o)"     0       EXTMATCH
+C              "ofoooxoofxoofoooxoofxofo" "*(*(of*(o)x)o)"    NOMATCH EXTMATCH
+C              "ofoooxoofxoofoooxoofxooofxofxo" "*(*(of*(o)x)o)" 0    EXTMATCH
+C              "aac"                   "*(@(a))a@(c)"         0       EXTMATCH
+C              "ac"                    "*(@(a))a@(c)"         0       EXTMATCH
+C              "c"                     "*(@(a))a@(c)"         NOMATCH EXTMATCH
+C              "aaac"                  "*(@(a))a@(c)"         0       EXTMATCH
+C              "baaac"                 "*(@(a))a@(c)"         NOMATCH EXTMATCH
+C              "abcd"                  "?@(a|b)*@(c)d"        0       EXTMATCH
+C              "abcd"                  "@(ab|a*@(b))*(c)d"    0       EXTMATCH
+C              "acd"                   "@(ab|a*(b))*(c)d"     0       EXTMATCH
+C              "abbcd"                 "@(ab|a*(b))*(c)d"     0       EXTMATCH
+C              "effgz"                 "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH
+C              "efgz"                  "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH
+C              "egz"                   "@(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH
+C              "egzefffgzbcdij"        "*(b+(c)d|e*(f)g?|?(h)i@(j|k))" 0 EXTMATCH
+C              "egz"                   "@(b+(c)d|e+(f)g?|?(h)i@(j|k))" NOMATCH EXTMATCH
+C              "ofoofo"                "*(of+(o))"            0       EXTMATCH
+C              "oxfoxoxfox"            "*(oxf+(ox))"          0       EXTMATCH
+C              "oxfoxfox"              "*(oxf+(ox))"          NOMATCH EXTMATCH
+C              "ofoofo"                "*(of+(o)|f)"          0       EXTMATCH
+C              "foofoofo"              "@(foo|f|fo)*(f|of+(o))" 0     EXTMATCH
+C              "oofooofo"              "*(of|oof+(o))"        0       EXTMATCH
+C              "fffooofoooooffoofffooofff" "*(*(f)*(o))"      0       EXTMATCH
+C              "fofoofoofofoo"         "*(fo|foo)"            0       EXTMATCH
+C              "foo"                   "!(x)"                 0       EXTMATCH
+C              "foo"                   "!(x)*"                0       EXTMATCH
+C              "foo"                   "!(foo)"               NOMATCH EXTMATCH
+C              "foo"                   "!(foo)*"              0       EXTMATCH
+C              "foobar"                "!(foo)"               0       EXTMATCH
+C              "foobar"                "!(foo)*"              0       EXTMATCH
+C              "moo.cow"               "!(*.*).!(*.*)"        0       EXTMATCH
+C              "mad.moo.cow"           "!(*.*).!(*.*)"        NOMATCH EXTMATCH
+C              "mucca.pazza"           "mu!(*(c))?.pa!(*(z))?" NOMATCH EXTMATCH
+C              "fff"                   "!(f)"                 0       EXTMATCH
+C              "fff"                   "*(!(f))"              0       EXTMATCH
+C              "fff"                   "+(!(f))"              0       EXTMATCH
+C              "ooo"                   "!(f)"                 0       EXTMATCH
+C              "ooo"                   "*(!(f))"              0       EXTMATCH
+C              "ooo"                   "+(!(f))"              0       EXTMATCH
+C              "foo"                   "!(f)"                 0       EXTMATCH
+C              "foo"                   "*(!(f))"              0       EXTMATCH
+C              "foo"                   "+(!(f))"              0       EXTMATCH
+C              "f"                     "!(f)"                 NOMATCH EXTMATCH
+C              "f"                     "*(!(f))"              NOMATCH EXTMATCH
+C              "f"                     "+(!(f))"              NOMATCH EXTMATCH
+C              "foot"                  "@(!(z*)|*x)"          0       EXTMATCH
+C              "zoot"                  "@(!(z*)|*x)"          NOMATCH EXTMATCH
+C              "foox"                  "@(!(z*)|*x)"          0       EXTMATCH
+C              "zoox"                  "@(!(z*)|*x)"          0       EXTMATCH
+C              "foo"                   "*(!(foo))             0       EXTMATCH
+C              "foob"                  "!(foo)b*"             NOMATCH EXTMATCH
+C              "foobb"                 "!(foo)b*"             0       EXTMATCH
+C              "["                     "*([a[])"              0       EXTMATCH
+C              "]"                     "*([]a[])"             0       EXTMATCH
+C              "a"                     "*([]a[])"             0       EXTMATCH
+C              "b"                     "*([!]a[])"            0       EXTMATCH
+C              "["                     "*([!]a[]|[[])"        0       EXTMATCH
+C              "]"                     "*([!]a[]|[]])"        0       EXTMATCH
+C              "["                     "!([!]a[])"            0       EXTMATCH
+C              "]"                     "!([!]a[])"            0       EXTMATCH
+C              ")"                     "*([)])"               0       EXTMATCH
+C              "*"                     "*([*(])"              0       EXTMATCH
+C              "abcd"                  "*!(|a)cd"             NOMATCH EXTMATCH