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