combine unreleased changelog entries
[debian/sudo] / fnmatch.c
index 2ccea5974a390093dfbd655f458f4168ca0c79df..625d759cbbc055d1ef20a8c31f06a4567859cfb8 100644 (file)
--- a/fnmatch.c
+++ b/fnmatch.c
@@ -1,4 +1,5 @@
 /*
+ * Copyright (c) 2008 Todd C. Miller <Todd.Miller@courtesan.com>
  * Copyright (c) 1989, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
  *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * SUCH DAMAGE.
  */
 
-#if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
-#endif /* LIBC_SCCS and not lint */
-
 /*
  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
  * Compares a filename or pathname to a pattern.
  */
 
-#include "config.h"
+#include <config.h>
 
 #include <stdio.h>
 #include <ctype.h>
@@ -55,8 +48,9 @@ static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp
 # endif
 #endif /* HAVE_STRING_H */
 
-#include "compat.h"
+#include <compat.h>
 #include "emul/fnmatch.h"
+#include "emul/charclass.h"
 
 #undef EOS
 #define        EOS     '\0'
@@ -65,7 +59,12 @@ static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp
 #define        RANGE_NOMATCH   0
 #define        RANGE_ERROR     (-1)
 
-static int rangematch __P((const char *, char, int, char **));
+#if defined(LIBC_SCCS) && !defined(lint)
+__unused static const char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+static int rangematch __P((const char *, int, int, char **));
+static int classmatch __P((const char *, int, int, const char **));
 
 int
 fnmatch(pattern, string, flags)
@@ -79,17 +78,17 @@ fnmatch(pattern, string, flags)
        for (stringstart = string;;)
                switch (c = *pattern++) {
                case EOS:
-                       if ((flags & FNM_LEADING_DIR) && *string == '/')
+                       if (ISSET(flags, FNM_LEADING_DIR) && *string == '/')
                                return (0);
                        return (*string == EOS ? 0 : FNM_NOMATCH);
                case '?':
                        if (*string == EOS)
                                return (FNM_NOMATCH);
-                       if (*string == '/' && (flags & FNM_PATHNAME))
+                       if (*string == '/' && ISSET(flags, FNM_PATHNAME))
                                return (FNM_NOMATCH);
-                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                       if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                            (string == stringstart ||
-                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                           (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                                return (FNM_NOMATCH);
                        ++string;
                        break;
@@ -99,20 +98,20 @@ fnmatch(pattern, string, flags)
                        while (c == '*')
                                c = *++pattern;
 
-                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                       if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                            (string == stringstart ||
-                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                           (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                                return (FNM_NOMATCH);
 
                        /* Optimize for pattern with * at end or before /. */
                        if (c == EOS) {
-                               if (flags & FNM_PATHNAME)
-                                       return ((flags & FNM_LEADING_DIR) ||
+                               if (ISSET(flags, FNM_PATHNAME))
+                                       return (ISSET(flags, FNM_LEADING_DIR) ||
                                            strchr(string, '/') == NULL ?
                                            0 : FNM_NOMATCH);
                                else
                                        return (0);
-                       } else if (c == '/' && (flags & FNM_PATHNAME)) {
+                       } else if (c == '/' && ISSET(flags, FNM_PATHNAME)) {
                                if ((string = strchr(string, '/')) == NULL)
                                        return (FNM_NOMATCH);
                                break;
@@ -122,7 +121,7 @@ fnmatch(pattern, string, flags)
                        while ((test = *string) != EOS) {
                                if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
                                        return (0);
-                               if (test == '/' && (flags & FNM_PATHNAME))
+                               if (test == '/' && ISSET(flags, FNM_PATHNAME))
                                        break;
                                ++string;
                        }
@@ -130,11 +129,11 @@ fnmatch(pattern, string, flags)
                case '[':
                        if (*string == EOS)
                                return (FNM_NOMATCH);
-                       if (*string == '/' && (flags & FNM_PATHNAME))
+                       if (*string == '/' && ISSET(flags, FNM_PATHNAME))
                                return (FNM_NOMATCH);
-                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                       if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
                            (string == stringstart ||
-                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                           (ISSET(flags, FNM_PATHNAME) && *(string - 1) == '/')))
                                return (FNM_NOMATCH);
 
                        switch (rangematch(pattern, *string, flags, &newp)) {
@@ -150,7 +149,7 @@ fnmatch(pattern, string, flags)
                        ++string;
                        break;
                case '\\':
-                       if (!(flags & FNM_NOESCAPE)) {
+                       if (!ISSET(flags, FNM_NOESCAPE)) {
                                if ((c = *pattern++) == EOS) {
                                        c = '\\';
                                        --pattern;
@@ -159,7 +158,7 @@ fnmatch(pattern, string, flags)
                        /* FALLTHROUGH */
                default:
                normal:
-                       if (c != *string && !((flags & FNM_CASEFOLD) &&
+                       if (c != *string && !(ISSET(flags, FNM_CASEFOLD) &&
                                 (tolower((unsigned char)c) ==
                                 tolower((unsigned char)*string))))
                                return (FNM_NOMATCH);
@@ -171,16 +170,16 @@ fnmatch(pattern, string, flags)
 
 static int
 #ifdef __STDC__
-rangematch(const char *pattern, char test, int flags, char **newp)
+rangematch(const char *pattern, int test, int flags, char **newp)
 #else
 rangematch(pattern, test, flags, newp)
        const char *pattern;
-       char test;
+       int test;
        int flags;
        char **newp;
 #endif
 {
-       int negate, ok;
+       int negate, ok, rv;
        char c, c2;
 
        /*
@@ -193,8 +192,8 @@ rangematch(pattern, test, flags, newp)
        if ((negate = (*pattern == '!' || *pattern == '^')))
                ++pattern;
 
-       if (flags & FNM_CASEFOLD)
-               test = tolower((unsigned char)test);
+       if (ISSET(flags, FNM_CASEFOLD))
+               test = tolower(test);
 
        /*
         * A right bracket shall lose its special meaning and represent
@@ -204,22 +203,33 @@ rangematch(pattern, test, flags, newp)
        ok = 0;
        c = *pattern++;
        do {
-               if (c == '\\' && !(flags & FNM_NOESCAPE))
+               if (c == '[' && *pattern == ':') {
+                       do {
+                               rv = classmatch(pattern + 1, test,
+                                   (flags & FNM_CASEFOLD), &pattern);
+                               if (rv == RANGE_MATCH)
+                                       ok = 1;
+                               c = *pattern++;
+                       } while (rv != RANGE_ERROR && c == '[' && *pattern == ':');
+                       if (c == ']')
+                       break;
+               }
+               if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
                        c = *pattern++;
                if (c == EOS)
                        return (RANGE_ERROR);
-               if (c == '/' && (flags & FNM_PATHNAME))
+               if (c == '/' && ISSET(flags, FNM_PATHNAME))
                        return (RANGE_NOMATCH);
-               if ((flags & FNM_CASEFOLD))
+               if (ISSET(flags, FNM_CASEFOLD))
                        c = tolower((unsigned char)c);
                if (*pattern == '-'
                    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
                        pattern += 2;
-                       if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+                       if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
                                c2 = *pattern++;
                        if (c2 == EOS)
                                return (RANGE_ERROR);
-                       if (flags & FNM_CASEFOLD)
+                       if (ISSET(flags, FNM_CASEFOLD))
                                c2 = tolower((unsigned char)c2);
                        if (c <= test && test <= c2)
                                ok = 1;
@@ -230,3 +240,43 @@ rangematch(pattern, test, flags, newp)
        *newp = (char *)pattern;
        return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
 }
+
+static int
+#ifdef __STDC__
+classmatch(const char *pattern, int test, int foldcase, const char **ep)
+#else
+classmatch(pattern, test, foldcase, ep)
+       const char *pattern;
+       int test;
+       int foldcase;
+       const char **ep;
+#endif
+{
+       struct cclass *cc;
+       const char *colon;
+       size_t len;
+       int rval = RANGE_NOMATCH;
+
+       if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
+               *ep = pattern - 2;
+               return(RANGE_ERROR);
+       }
+       *ep = colon + 2;
+       len = (size_t)(colon - pattern);
+
+       if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
+               pattern = "lower:]";
+       for (cc = cclasses; cc->name != NULL; cc++) {
+               if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
+                       if (cc->isctype(test))
+                               rval = RANGE_MATCH;
+                       break;
+               }
+       }
+       if (cc->name == NULL) {
+               /* invalid character class, return EOS */
+               *ep = colon + strlen(colon);
+               rval = RANGE_ERROR;
+       }
+       return(rval);
+}