• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
fnmatch(const char * pattern,const char * string,int flags)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
rangematch(const char * pattern,char test,int flags,char ** newp)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@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