]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/fnmatch.c
update version
[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 /* Limit of recursion during matching attempts. */
60 #define FNM_MAX_RECUR 64
61
62 #define ISSET(x, y) ((x) & (y))
63 #define FOLD(c) ((flags & FNM_CASEFOLD) && B_ISUPPER(c) ? tolower(c) : (c))
64
65 static int rangematch(const char *, char, int, char **);
66 static int r_fnmatch(const char *, const char *, int, int);
67
68 #ifdef SYS 
69 int xfnmatch(const char *pattern, const char *string, int flags)
70 #else
71 int fnmatch(const char *pattern, const char *string, int flags)
72 #endif
73 {
74    int e;
75
76    e = r_fnmatch(pattern, string, flags, FNM_MAX_RECUR);
77    if (e == -1) {               /* Too much recursion */
78       e = FNM_NOMATCH;
79    }
80    return (e);
81 }
82
83 static 
84 int r_fnmatch(const char *pattern, const char *string, int flags, int recur)
85 {
86    const char *stringstart;
87    char *newp;
88    char c, test;
89    int e;
90
91    if (recur-- <= 0) {
92       return (-1);
93    }
94
95    stringstart = string;
96    for ( ;; ) {
97       switch (c = *pattern++) {
98       case EOS:
99          if (ISSET(flags, FNM_LEADING_DIR) && IsPathSeparator(*string))
100             return (0);
101          return (*string == EOS ? 0 : FNM_NOMATCH);
102       case '?':
103          if (*string == EOS)
104             return (FNM_NOMATCH);
105          if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
106             return (FNM_NOMATCH);
107          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
108              (string == stringstart ||
109               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
110             return (FNM_NOMATCH);
111          ++string;
112          break;
113       case '*':
114          c = *pattern;
115          /* Collapse multiple stars. */
116          while (c == '*') {
117             c = *++pattern;
118          }
119
120          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
121              (string == stringstart ||
122               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1))))) {
123             return (FNM_NOMATCH);
124          }
125
126          /* Optimize for pattern with * at end or before /. */
127          if (c == EOS) {
128             if (ISSET(flags, FNM_PATHNAME)) {
129                return (ISSET(flags, FNM_LEADING_DIR) ||
130                        strchr(string, '/') == NULL ? 0 : FNM_NOMATCH);
131             } else {
132                return (0);
133             }
134          } else if (IsPathSeparator(c) && ISSET(flags, FNM_PATHNAME)) {
135             if ((string = strchr(string, '/')) == NULL)
136                return (FNM_NOMATCH);
137             break;
138          }
139
140          /* General case, use recursion. */
141          while ((test = *string) != EOS) {
142             e = r_fnmatch(pattern, string, flags & ~FNM_PERIOD, recur);
143             if (e != FNM_NOMATCH) { /* can be NOMATCH, -1 or MATCH */
144                return (e);
145             }
146             if (test == '/' && ISSET(flags, FNM_PATHNAME)) {
147                break;
148             }
149             ++string;
150          }
151          return (FNM_NOMATCH);
152       case '[':
153          if (*string == EOS)
154             return (FNM_NOMATCH);
155          if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
156             return (FNM_NOMATCH);
157          if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
158              (string == stringstart ||
159               (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
160             return (FNM_NOMATCH);
161
162          switch (rangematch(pattern, *string, flags, &newp)) {
163          case RANGE_ERROR:
164             /* not a good range, treat as normal text */
165             goto normal;
166          case RANGE_MATCH:
167             pattern = newp;
168             break;
169          case RANGE_NOMATCH:
170             return (FNM_NOMATCH);
171          }
172          ++string;
173          break;
174
175       case '\\':
176          if (!ISSET(flags, FNM_NOESCAPE)) {
177             if ((c = *pattern++) == EOS) {
178                c = '\\';
179                --pattern;
180             }
181          }
182          /* FALLTHROUGH */
183       default:
184 normal:
185          if (FOLD(c) != FOLD(*string)) {
186             return (FNM_NOMATCH);
187          }
188          ++string;
189          break;
190       }
191    }
192    /* NOTREACHED */
193 }
194
195 static int rangematch(const char *pattern, char test, int flags,
196                       char **newp)
197 {
198    int negate, ok;
199    char c, c2;
200
201    /*
202     * A bracket expression starting with an unquoted circumflex
203     * character produces unspecified results (IEEE 1003.2-1992,
204     * 3.13.2).  This implementation treats it like '!', for
205     * consistency with the regular expression syntax.
206     * J.T. Conklin (conklin@ngai.kaleida.com)
207     */
208    if ((negate = (*pattern == '!' || *pattern == '^')))
209       ++pattern;
210
211    test = FOLD(test);
212
213    /*
214     * A right bracket shall lose its special meaning and represent
215     * itself in a bracket expression if it occurs first in the list.
216     * -- POSIX.2 2.8.3.2
217     */
218    ok = 0;
219    c = *pattern++;
220    do {
221       if (c == '\\' && !ISSET(flags, FNM_NOESCAPE))
222          c = *pattern++;
223       if (c == EOS)
224          return (RANGE_ERROR);
225       if (c == '/' && ISSET(flags, FNM_PATHNAME))
226          return (RANGE_NOMATCH);
227       c = FOLD(c);
228       if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
229          pattern += 2;
230          if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE))
231             c2 = *pattern++;
232          if (c2 == EOS)
233             return (RANGE_ERROR);
234          c2 = FOLD(c2);
235          if (c <= test && test <= c2)
236             ok = 1;
237       } else if (c == test)
238          ok = 1;
239    } while ((c = *pattern++) != ']');
240
241    *newp = (char *) pattern;
242    return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
243 }
244
245 #ifdef TEST_PROGRAM
246 struct test {
247    const char *pattern;
248    const char *string;
249    const int options;
250    const int result; 
251 };
252
253 /*
254  * Note, some of these tests were duplicated from a patch file I found
255  *  in an email, so I am unsure what the license is.  Since this code is
256  *  never turned on in any release, it probably doesn't matter at all.
257  * If by some chance someone finds this to be a problem please let
258  *  me know.
259  */
260 static struct test tests[] = {
261 /*1*/  {"x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
262        {"x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
263        {"x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
264        {"*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
265 /*5*/  {"*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
266        {"*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
267        {"*x", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
268        {"*x", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
269        {"*x", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
270 /*10*/ {"x*", "x", FNM_PATHNAME | FNM_LEADING_DIR, 0},
271        {"x*", "x/y", FNM_PATHNAME | FNM_LEADING_DIR, 0},
272        {"x*", "x/y/z", FNM_PATHNAME | FNM_LEADING_DIR, 0},
273        {"a*b/*", "abbb/.x", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH},
274        {"a*b/*", "abbb/xy", FNM_PATHNAME|FNM_PERIOD, 0},
275 /*15*/ {"[A-[]", "A", 0, 0},
276        {"[A-[]", "a", 0, FNM_NOMATCH},
277        {"[a-{]", "A", 0, FNM_NOMATCH},
278        {"[a-{]", "a", 0, 0},
279        {"[A-[]", "A", FNM_CASEFOLD, FNM_NOMATCH},
280 /*20*/ {"[A-[]", "a", FNM_CASEFOLD, FNM_NOMATCH},
281        {"[a-{]", "A", FNM_CASEFOLD, 0},
282        {"[a-{]", "a", FNM_CASEFOLD, 0},
283        { "*LIB*", "lib", FNM_PERIOD, FNM_NOMATCH },
284        { "*LIB*", "lib", FNM_CASEFOLD, 0},
285 /*25*/ { "a[/]b", "a/b", 0, 0},
286        { "a[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
287        { "[a-z]/[a-z]", "a/b", 0, 0 },
288        { "a/b", "*", FNM_PATHNAME, FNM_NOMATCH },
289        { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
290        { "*[/]b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
291 /*30*/ { "\\[/b", "[/b", 0, 0 },
292        { "?\?/b", "aa/b", 0, 0 },
293        { "???b", "aa/b", 0, 0 },
294        { "???b", "aa/b", FNM_PATHNAME, FNM_NOMATCH },
295        { "?a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
296 /*35*/ { "a/?b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
297        { "*a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
298        { "a/*b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
299        { "[.]a/b", ".a/b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
300        { "a/[.]b", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
301 /*40*/ { "*/?", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
302        { "?/*", "a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
303        { ".*/?", ".a/b", FNM_PATHNAME|FNM_PERIOD, 0 },
304        { "*/.?", "a/.b", FNM_PATHNAME|FNM_PERIOD, 0 },
305        { "*/*", "a/.b", FNM_PATHNAME|FNM_PERIOD, FNM_NOMATCH },
306 /*45*/ { "*[.]/b", "a./b", FNM_PATHNAME|FNM_PERIOD, 0 },
307        { "a?b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
308        { "a*b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
309        { "a[.]b", "a.b", FNM_PATHNAME|FNM_PERIOD, 0 },
310 /*49*/ { "*a*", "a/b", FNM_PATHNAME|FNM_LEADING_DIR, 0 },
311        { "[/b", "[/b", 0, 0},
312 #ifdef FULL_TEST
313        /* This test takes a *long* time */
314        {"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*",
315           "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm"
316           "nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyy", 0, FNM_NOMATCH},
317 #endif
318
319        /* Keep dummy last to avoid compiler warnings */
320        {"dummy", "dummy", 0, 0}
321
322 };
323
324 #define ntests ((int)(sizeof(tests)/sizeof(struct test)))
325
326 int main()
327 {
328    bool fail = false;
329    for (int i=0; i<ntests; i++) {
330       if (fnmatch(tests[i].pattern, tests[i].string, tests[i].options) != tests[i].result) {
331          printf("Test %d failed: pat=%s str=%s expect=%s got=%s\n",
332             i+1, tests[i].pattern, tests[i].string, 
333             tests[i].result==0?"matches":"no match",
334             tests[i].result==0?"no match":"matches");
335          fail = true;
336       } else {
337          printf("Test %d succeeded\n", i+1);
338       }
339    }
340    return fail;
341 }
342 #endif /* TEST_PROGRAM */