]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/fnmatch.c
kes Prepare to add JS_Warnings termination status.
[bacula/bacula] / bacula / src / lib / fnmatch.c
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Guido van Rossum.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /* OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert */
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 /* Version: $Id$ */
41
42 /* Define SYS to use the system fnmatch() rather than ours */
43 /* #define SYS 1 */
44
45 #include "bacula.h"
46 #ifdef SYS
47 #include <fnmatch.h>
48 #else
49 #include "fnmatch.h"
50 #endif
51
52 #undef  EOS
53 #define EOS     '\0'
54
55 #define RANGE_MATCH     1
56 #define RANGE_NOMATCH   0
57 #define RANGE_ERROR     (-1)
58
59 #define ISSET(x, y) ((x) & (y))
60 #define FOLD(c) ((flags & FNM_CASEFOLD) && B_ISUPPER(c) ? tolower(c) : (c))
61
62 static int rangematch(const char *, char, int, char **);
63
64 #ifdef SYS 
65 int xfnmatch(const char *pattern, const char *string, int flags)
66 #else
67 int fnmatch(const char *pattern, const char *string, int flags)
68 #endif
69 {
70    const char *stringstart;
71    char *newp;
72    char c, test;
73
74    stringstart = string;
75    for ( ;; ) {
76       switch (c = *pattern++) {
77       case EOS:
78          if (ISSET(flags, FNM_LEADING_DIR) && IsPathSeparator(*string))
79             return (0);
80          return (*string == EOS ? 0 : FNM_NOMATCH);
81       case '?':
82          if (*string == EOS)
83             return (FNM_NOMATCH);
84          if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
85             return (FNM_NOMATCH);
86          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
87              (string == stringstart ||
88               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
89             return (FNM_NOMATCH);
90          ++string;
91          break;
92       case '*':
93          c = *pattern;
94          /* Collapse multiple stars. */
95          while (c == '*') {
96             c = *++pattern;
97          }
98
99          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
100              (string == stringstart ||
101               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1))))) {
102             return (FNM_NOMATCH);
103          }
104
105          /* Optimize for pattern with * at end or before /. */
106          if (c == EOS) {
107             if (ISSET(flags, FNM_PATHNAME)) {
108                return (ISSET(flags, FNM_LEADING_DIR) ||
109                        strchr(string, '/') == NULL ? 0 : FNM_NOMATCH);
110             } else {
111                return (0);
112             }
113          } else if (IsPathSeparator(c) && ISSET(flags, FNM_PATHNAME)) {
114             if ((string = strchr(string, '/')) == NULL)
115                return (FNM_NOMATCH);
116             break;
117          }
118
119          /* General case, use recursion. */
120          while ((test = *string) != EOS) {
121             if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) {
122                return (0);
123             }
124             if (test == '/' && ISSET(flags, FNM_PATHNAME)) {
125                break;
126             }
127             ++string;
128          }
129          return (FNM_NOMATCH);
130       case '[':
131          if (*string == EOS)
132             return (FNM_NOMATCH);
133          if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
134             return (FNM_NOMATCH);
135          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
136              (string == stringstart ||
137               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
138             return (FNM_NOMATCH);
139
140          switch (rangematch(pattern, *string, flags, &newp)) {
141          case RANGE_ERROR:
142             /* not a good range, treat as normal text */
143             goto normal;
144          case RANGE_MATCH:
145             pattern = newp;
146             break;
147          case RANGE_NOMATCH:
148             return (FNM_NOMATCH);
149          }
150          ++string;
151          break;
152
153       case '\\':
154          if (!ISSET(flags, FNM_NOESCAPE)) {
155             if ((c = *pattern++) == EOS) {
156                c = '\\';
157                --pattern;
158             }
159          }
160          /* FALLTHROUGH */
161       default:
162 normal:
163          if (FOLD(c) != FOLD(*string)) {
164             return (FNM_NOMATCH);
165          }
166          ++string;
167          break;
168       }
169    }
170    /* NOTREACHED */
171 }
172
173 static int rangematch(const char *pattern, char test, int flags,
174                       char **newp)
175 {
176    int negate, ok;
177    char c, c2;
178
179    /*
180     * A bracket expression starting with an unquoted circumflex
181     * character produces unspecified results (IEEE 1003.2-1992,
182     * 3.13.2).  This implementation treats it like '!', for
183     * consistency with the regular expression syntax.
184     * J.T. Conklin (conklin@ngai.kaleida.com)
185     */
186    if ((negate = (*pattern == '!' || *pattern == '^')))
187       ++pattern;
188
189    test = FOLD(test);
190
191    /*
192     * A right bracket shall lose its special meaning and represent
193     * itself in a bracket expression if it occurs first in the list.
194     * -- POSIX.2 2.8.3.2
195     */
196    ok = 0;
197    c = *pattern++;
198    do {
199       if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
200          c = *pattern++;
201       if (c == EOS)
202          return (RANGE_ERROR);
203       if (c == '/' && ISSET(flags, FNM_PATHNAME))
204          return (RANGE_NOMATCH);
205       c = FOLD(c);
206       if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
207          pattern += 2;
208          if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
209             c2 = *pattern++;
210          if (c2 == EOS)
211             return (RANGE_ERROR);
212          c2 = FOLD(c2);
213          if (c <= test && test <= c2)
214             ok = 1;
215       } else if (c == test)
216          ok = 1;
217    } while ((c = *pattern++) != ']');
218
219    *newp = (char *) pattern;
220    return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
221 }
222
223 #ifdef TEST_PROGRAM
224 struct test {
225    const char *pattern;
226    const char *string;
227    const int options;
228    const int result; 
229 };
230
231 /*
232  * Note, some of these tests were duplicated from a patch file I found
233  *  in an email, so I am unsure what the license is.  Since this code is
234  *  never turned on in any release, it probably doesn't matter at all.
235  * If by some chance someone finds this to be a problem please let
236  *  me know.
237  */
238 static struct test tests[] = {
239 /*1*/  {"x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
240        {"x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
241        {"x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
242        {"*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
243 /*5*/  {"*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
244        {"*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
245        {"*x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
246        {"*x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
247        {"*x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
248 /*10*/ {"x*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
249        {"x*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
250        {"x*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
251        {"a*b/*", "abbb/.x", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH},
252        {"a*b/*", "abbb/xy", FNM_PATHNAME|FNM_PERIOD, 0},
253 /*15*/ {"[A-[]", "A", 0, 0},
254        {"[A-[]", "a", 0, FNM_NOMATCH},
255        {"[a-{]", "A", 0, FNM_NOMATCH},
256        {"[a-{]", "a", 0, 0},
257        {"[A-[]", "A", FNM_CASEFOLD, FNM_NOMATCH},
258 /*20*/ {"[A-[]", "a", FNM_CASEFOLD, FNM_NOMATCH},
259        {"[a-{]", "A", FNM_CASEFOLD, 0},
260        {"[a-{]", "a", FNM_CASEFOLD, 0},
261        { "*LIB*", "lib", FNM_PERIOD, FNM_NOMATCH },
262        { "*LIB*", "lib", FNM_CASEFOLD, 0},
263 /*25*/ { "a[/]b", "a/b", 0, 0},
264        { "a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
265        { "[a-z]/[a-z]", "a/b", 0, 0 },
266        { "a/b", "*", FNM_PATHNAME, FNM_NOMATCH },
267        { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
268        { "*[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
269 /*30*/ { "\\[/b", "[/b", 0, 0 },
270        { "?\?/b", "aa/b", 0, 0 },
271        { "???b", "aa/b", 0, 0 },
272        { "???b", "aa/b", FNM_PATHNAME, FNM_NOMATCH },
273        { "?a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
274 /*35*/ { "a/?b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
275        { "*a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
276        { "a/*b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
277        { "[.]a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
278        { "a/[.]b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
279 /*40*/ { "*/?", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
280        { "?/*", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
281        { ".*/?", ".a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
282        { "*/.?", "a/.b", FNM_PATHNAME|FNM_PERIOD, 0 },
283        { "*/*", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
284 /*45*/ { "*[.]/b", "a./b", FNM_PATHNAME|FNM_PERIOD, 0 },
285        { "a?b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
286        { "a*b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
287        { "a[.]b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
288 /*49*/ { "*a*", "a/b", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
289        { "[/b", "[/b", 0, 0},
290 #ifdef FULL_TEST
291        /* This test takes a *long* time */
292        {"a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*",
293           "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm"
294           "nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyy", 0, FNM_NOMATCH},
295 #endif
296
297        /* Keep dummy last to avoid compiler warnings */
298        {"dummy", "dummy", 0, 0}
299
300 };
301
302 #define ntests ((int)(sizeof(tests)/sizeof(struct test)))
303
304 int main()
305 {
306    bool fail = false;
307    for (int i=0; i<ntests; i++) {
308       if (fnmatch(tests[i].pattern, tests[i].string, tests[i].options) != tests[i].result) {
309          printf("Test %d failed: pat=%s str=%s expect=%s got=%s\n",
310             i+1, tests[i].pattern, tests[i].string, 
311             tests[i].result==0?"matches":"no match",
312             tests[i].result==0?"no match":"matches");
313          fail = true;
314       } else {
315          printf("Test %d succeeded\n", i+1);
316       }
317    }
318    return fail;
319 }
320 #endif /* TEST_PROGRAM */