Home | History | Annotate | Download | only in unistd
      1 /*      $OpenBSD: fnmatch.c,v 1.13 2006/03/31 05:34:14 deraadt Exp $        */
      2 
      3 /*
      4  * Copyright (c) 1989, 1993, 1994
      5  *      The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Guido van Rossum.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*
     36  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
     37  * Compares a filename or pathname to a pattern.
     38  */
     39 
     40 #include <ctype.h>
     41 #include <stdio.h>
     42 #include <string.h>
     43 #include <fnmatch.h>
     44 
     45 #define EOS        '\0'
     46 
     47 #define RANGE_MATCH        1
     48 #define RANGE_NOMATCH        0
     49 #define RANGE_ERROR        (-1)
     50 
     51 static int rangematch(const char *, char, int, char **);
     52 
     53 int
     54 fnmatch(const char *pattern, const char *string, int flags)
     55 {
     56         const char *stringstart;
     57         char *newp;
     58         char c, test;
     59 
     60         for (stringstart = string;;)
     61                 switch (c = *pattern++) {
     62                 case EOS:
     63                         if ((flags & FNM_LEADING_DIR) && *string == '/')
     64                                 return (0);
     65                         return (*string == EOS ? 0 : FNM_NOMATCH);
     66                 case '?':
     67                         if (*string == EOS)
     68                                 return (FNM_NOMATCH);
     69                         if (*string == '/' && (flags & FNM_PATHNAME))
     70                                 return (FNM_NOMATCH);
     71                         if (*string == '.' && (flags & FNM_PERIOD) &&
     72                             (string == stringstart ||
     73                             ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
     74                                 return (FNM_NOMATCH);
     75                         ++string;
     76                         break;
     77                 case '*':
     78                         c = *pattern;
     79                         /* Collapse multiple stars. */
     80                         while (c == '*')
     81                                 c = *++pattern;
     82 
     83                         if (*string == '.' && (flags & FNM_PERIOD) &&
     84                             (string == stringstart ||
     85                             ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
     86                                 return (FNM_NOMATCH);
     87 
     88                         /* Optimize for pattern with * at end or before /. */
     89                         if (c == EOS) {
     90                                 if (flags & FNM_PATHNAME)
     91                                         return ((flags & FNM_LEADING_DIR) ||
     92                                             strchr(string, '/') == NULL ?
     93                                             0 : FNM_NOMATCH);
     94                                 else
     95                                         return (0);
     96                         } else if (c == '/' && (flags & FNM_PATHNAME)) {
     97                                 if ((string = strchr(string, '/')) == NULL)
     98                                         return (FNM_NOMATCH);
     99                                 break;
    100                         }
    101 
    102                         /* General case, use recursion. */
    103                         while ((test = *string) != EOS) {
    104                                 if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
    105                                         return (0);
    106                                 if (test == '/' && (flags & FNM_PATHNAME))
    107                                         break;
    108                                 ++string;
    109                         }
    110                         return (FNM_NOMATCH);
    111                 case '[':
    112                         if (*string == EOS)
    113                                 return (FNM_NOMATCH);
    114                         if (*string == '/' && (flags & FNM_PATHNAME))
    115                                 return (FNM_NOMATCH);
    116                         if (*string == '.' && (flags & FNM_PERIOD) &&
    117                             (string == stringstart ||
    118                             ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
    119                                 return (FNM_NOMATCH);
    120 
    121                         switch (rangematch(pattern, *string, flags, &newp)) {
    122                         case RANGE_ERROR:
    123                                 /* not a good range, treat as normal text */
    124                                 goto normal;
    125                         case RANGE_MATCH:
    126                                 pattern = newp;
    127                                 break;
    128                         case RANGE_NOMATCH:
    129                                 return (FNM_NOMATCH);
    130                         }
    131                         ++string;
    132                         break;
    133                 case '\\':
    134                         if (!(flags & FNM_NOESCAPE)) {
    135                                 if ((c = *pattern++) == EOS) {
    136                                         c = '\\';
    137                                         --pattern;
    138                                 }
    139                         }
    140                         /* FALLTHROUGH */
    141                 default:
    142                 normal:
    143                         if (c != *string && !((flags & FNM_CASEFOLD) &&
    144                                  (tolower((unsigned char)c) ==
    145                                  tolower((unsigned char)*string))))
    146                                 return (FNM_NOMATCH);
    147                         ++string;
    148                         break;
    149                 }
    150         /* NOTREACHED */
    151 }
    152 
    153 static int
    154 rangematch(const char *pattern, char test, int flags, char **newp)
    155 {
    156         int negate, ok;
    157         char c, c2;
    158 
    159         /*
    160          * A bracket expression starting with an unquoted circumflex
    161          * character produces unspecified results (IEEE 1003.2-1992,
    162          * 3.13.2).  This implementation treats it like '!', for
    163          * consistency with the regular expression syntax.
    164          * J.T. Conklin (conklin (at) ngai.kaleida.com)
    165          */
    166         if ((negate = (*pattern == '!' || *pattern == '^')))
    167                 ++pattern;
    168 
    169         if (flags & FNM_CASEFOLD)
    170                 test = (char)tolower((unsigned char)test);
    171 
    172         /*
    173          * A right bracket shall lose its special meaning and represent
    174          * itself in a bracket expression if it occurs first in the list.
    175          * -- POSIX.2 2.8.3.2
    176          */
    177         ok = 0;
    178         c = *pattern++;
    179         do {
    180                 if (c == '\\' && !(flags & FNM_NOESCAPE))
    181                         c = *pattern++;
    182                 if (c == EOS)
    183                         return (RANGE_ERROR);
    184                 if (c == '/' && (flags & FNM_PATHNAME))
    185                         return (RANGE_NOMATCH);
    186                 if ((flags & FNM_CASEFOLD))
    187                         c = (char)tolower((unsigned char)c);
    188                 if (*pattern == '-'
    189                     && (c2 = *(pattern+1)) != EOS && c2 != ']') {
    190                         pattern += 2;
    191                         if (c2 == '\\' && !(flags & FNM_NOESCAPE))
    192                                 c2 = *pattern++;
    193                         if (c2 == EOS)
    194                                 return (RANGE_ERROR);
    195                         if (flags & FNM_CASEFOLD)
    196                                 c2 = (char)tolower((unsigned char)c2);
    197                         if (c <= test && test <= c2)
    198                                 ok = 1;
    199                 } else if (c == test)
    200                         ok = 1;
    201         } while ((c = *pattern++) != ']');
    202 
    203         *newp = (char *)pattern;
    204         return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
    205 }
    206