]> sjero.net Git - wget/blobdiff - src/cmpt.c
[svn] Move fnmatch() to cmpt.c and don't use it under GNU libc.
[wget] / src / cmpt.c
index 6edf8402b8e58220415c3a0e4a3c261b49e378b6..ea1f44ae69d5bbc7c2d55a85c4ff9626085f9928 100644 (file)
@@ -43,6 +43,11 @@ so, delete this exception statement from your version.  */
 #endif
 #include <limits.h>
 
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+
 #include "wget.h"
 
 #ifndef HAVE_STRERROR
@@ -1470,3 +1475,180 @@ memmove (char *dest, const char *source, unsigned length)
 #endif /* not HAVE_MEMMOVE */
 
 #endif /* 0 */
+
+/* fnmatch is a POSIX function, but we include an implementation for
+   the sake of systems that don't have it.  Furthermore, according to
+   anecdotal evidence, historical implementations of fnmatch are buggy
+   and unreliable.  So we use our version, except when compiling under
+   systems where fnmatch is known to work (currently glibc.)  */
+
+#ifndef SYSTEM_FNMATCH
+
+#define        __FNM_FLAGS     (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD)
+
+/* Match STRING against the filename pattern PATTERN, returning zero
+   if it matches, FNM_NOMATCH if not.  This implementation comes from
+   an earlier version of GNU Bash.  (It doesn't make sense to update
+   it with a newer version because it adds a lot of features Wget
+   doesn't use or care about.)  */
+
+int
+fnmatch (const char *pattern, const char *string, int flags)
+{
+  register const char *p = pattern, *n = string;
+  register char c;
+
+  if ((flags & ~__FNM_FLAGS) != 0)
+    {
+      errno = EINVAL;
+      return (-1);
+    }
+
+  while ((c = *p++) != '\0')
+    {
+      switch (c)
+       {
+       case '?':
+         if (*n == '\0')
+           return (FNM_NOMATCH);
+         else if ((flags & FNM_PATHNAME) && *n == '/')
+           return (FNM_NOMATCH);
+         else if ((flags & FNM_PERIOD) && *n == '.' &&
+                  (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+           return (FNM_NOMATCH);
+         break;
+
+       case '\\':
+         if (!(flags & FNM_NOESCAPE))
+           c = *p++;
+         if (*n != c)
+           return (FNM_NOMATCH);
+         break;
+
+       case '*':
+         if ((flags & FNM_PERIOD) && *n == '.' &&
+             (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+           return (FNM_NOMATCH);
+
+         for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+           if (((flags & FNM_PATHNAME) && *n == '/') ||
+               (c == '?' && *n == '\0'))
+             return (FNM_NOMATCH);
+
+         if (c == '\0')
+           return (0);
+
+         {
+           char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+           for (--p; *n != '\0'; ++n)
+             if ((c == '[' || *n == c1) &&
+                 fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+               return (0);
+           return (FNM_NOMATCH);
+         }
+
+       case '[':
+         {
+           /* Nonzero if the sense of the character class is
+              inverted.  */
+           register int not;
+
+           if (*n == '\0')
+             return (FNM_NOMATCH);
+
+           if ((flags & FNM_PERIOD) && *n == '.' &&
+               (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
+             return (FNM_NOMATCH);
+
+           /* Make sure there is a closing `]'.  If there isn't,
+              the `[' is just a character to be matched.  */
+           {
+             register const char *np;
+
+             for (np = p; np && *np && *np != ']'; np++);
+
+             if (np && !*np)
+               {
+                 if (*n != '[')
+                   return (FNM_NOMATCH);
+                 goto next_char;
+               }
+           }
+
+           not = (*p == '!' || *p == '^');
+           if (not)
+             ++p;
+
+           c = *p++;
+           while (1)
+             {
+               register char cstart = c, cend = c;
+
+               if (!(flags & FNM_NOESCAPE) && c == '\\')
+                 cstart = cend = *p++;
+
+               if (c == '\0')
+                 /* [ (unterminated) loses.  */
+                 return (FNM_NOMATCH);
+
+               c = *p++;
+
+               if ((flags & FNM_PATHNAME) && c == '/')
+                 /* [/] can never match.  */
+                 return (FNM_NOMATCH);
+
+               if (c == '-' && *p != ']')
+                 {
+                   cend = *p++;
+                   if (!(flags & FNM_NOESCAPE) && cend == '\\')
+                     cend = *p++;
+                   if (cend == '\0')
+                     return (FNM_NOMATCH);
+                   c = *p++;
+                 }
+
+               if (*n >= cstart && *n <= cend)
+                 goto matched;
+
+               if (c == ']')
+                 break;
+             }
+           if (!not)
+             return (FNM_NOMATCH);
+
+         next_char:
+           break;
+
+         matched:
+           /* Skip the rest of the [...] that already matched.  */
+           while (c != ']')
+             {
+               if (c == '\0')
+                 /* [... (unterminated) loses.  */
+                 return (FNM_NOMATCH);
+
+               c = *p++;
+               if (!(flags & FNM_NOESCAPE) && c == '\\')
+                 /* 1003.2d11 is unclear if this is right.  %%% */
+                 ++p;
+             }
+           if (not)
+             return (FNM_NOMATCH);
+         }
+         break;
+
+       default:
+         if (c != *n)
+           return (FNM_NOMATCH);
+       }
+
+      ++n;
+    }
+
+  if (*n == '\0')
+    return (0);
+
+  return (FNM_NOMATCH);
+}
+
+#endif /* not SYSTEM_FNMATCH */