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 /* check-sources:disable-copyright-check */
33 
34 /* OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert */
35 
36 /*
37  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
38  * Compares a filename or pathname to a pattern.
39  */
40 
41 /* Define SYS to use the system fnmatch() rather than ours */
42 /* #define SYS 1 */
43 
44 #include "include/bareos.h"
45 #ifdef SYS
46 #  include <fnmatch.h>
47 #else
48 #  include "fnmatch.h"
49 #endif
50 
51 #undef EOS
52 #define EOS '\0'
53 
54 #define RANGE_MATCH 1
55 #define RANGE_NOMATCH 0
56 #define RANGE_ERROR (-1)
57 
58 /* Limit of recursion during matching attempts. */
59 #define FNM_MAX_RECUR 64
60 
61 #define ISSET(x, y) ((x) & (y))
62 #define FOLD(c) ((flags & FNM_CASEFOLD) && B_ISUPPER(c) ? tolower(c) : (c))
63 
64 static int Rangematch(const char*, char, int, char**);
65 static int r_fnmatch(const char*, const char*, int, int);
66 
67 #ifdef SYS
xfnmatch(const char * pattern,const char * string,int flags)68 int xfnmatch(const char* pattern, const char* string, int flags)
69 #else
70 int fnmatch(const char* pattern, const char* string, int flags)
71 #endif
72 {
73   int e;
74 
75   e = r_fnmatch(pattern, string, flags, FNM_MAX_RECUR);
76   if (e == -1) { /* Too much recursion */
77     e = FNM_NOMATCH;
78   }
79   return (e);
80 }
81 
r_fnmatch(const char * pattern,const char * string,int flags,int recur)82 static int r_fnmatch(const char* pattern,
83                      const char* string,
84                      int flags,
85                      int recur)
86 {
87   const char* stringstart;
88   char* newp;
89   char c, test;
90   int e;
91 
92   if (recur-- <= 0) { return (-1); }
93 
94   stringstart = string;
95   for (;;) {
96     switch (c = *pattern++) {
97       case EOS:
98         if (ISSET(flags, FNM_LEADING_DIR) && IsPathSeparator(*string))
99           return (0);
100         return (*string == EOS ? 0 : FNM_NOMATCH);
101       case '?':
102         if (*string == EOS) 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)
108                     && IsPathSeparator(*(string - 1)))))
109           return (FNM_NOMATCH);
110         ++string;
111         break;
112       case '*':
113         c = *pattern;
114         /* Collapse multiple stars. */
115         while (c == '*') { c = *++pattern; }
116 
117         if (*string == '.' && ISSET(flags, FNM_PERIOD)
118             && (string == stringstart
119                 || (ISSET(flags, FNM_PATHNAME)
120                     && 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) || strchr(string, '/') == NULL
128                         ? 0
129                         : FNM_NOMATCH);
130           } else {
131             return (0);
132           }
133         } else if (IsPathSeparator(c) && ISSET(flags, FNM_PATHNAME)) {
134           if ((string = strchr(string, '/')) == NULL) 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)) { break; }
145           ++string;
146         }
147         return (FNM_NOMATCH);
148       case '[':
149         if (*string == EOS) return (FNM_NOMATCH);
150         if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
151           return (FNM_NOMATCH);
152         if (*string == '.' && ISSET(flags, FNM_PERIOD)
153             && (string == stringstart
154                 || (ISSET(flags, FNM_PATHNAME)
155                     && IsPathSeparator(*(string - 1)))))
156           return (FNM_NOMATCH);
157 
158         switch (Rangematch(pattern, *string, flags, &newp)) {
159           case RANGE_ERROR:
160             /* not a good range, treat as normal text */
161             goto normal;
162           case RANGE_MATCH:
163             pattern = newp;
164             break;
165           case RANGE_NOMATCH:
166             return (FNM_NOMATCH);
167         }
168         ++string;
169         break;
170 
171       case '\\':
172         if (!ISSET(flags, FNM_NOESCAPE)) {
173           if ((c = *pattern++) == EOS) {
174             c = '\\';
175             --pattern;
176           }
177         }
178         /* FALLTHROUGH */
179       default:
180       normal:
181         if (FOLD(c) != FOLD(*string)) { return (FNM_NOMATCH); }
182         ++string;
183         break;
184     }
185   }
186   /* NOTREACHED */
187 }
188 
Rangematch(const char * pattern,char test,int flags,char ** newp)189 static int Rangematch(const char* pattern, char test, int flags, char** newp)
190 {
191   int negate, ok;
192   char c, c2;
193 
194   /*
195    * A bracket expression starting with an unquoted circumflex
196    * character produces unspecified results (IEEE 1003.2-1992,
197    * 3.13.2).  This implementation treats it like '!', for
198    * consistency with the regular expression syntax.
199    * J.T. Conklin (conklin@ngai.kaleida.com)
200    */
201   if ((negate = (*pattern == '!' || *pattern == '^'))) ++pattern;
202 
203   test = FOLD(test);
204 
205   /*
206    * A right bracket shall lose its special meaning and represent
207    * itself in a bracket expression if it occurs first in the list.
208    * -- POSIX.2 2.8.3.2
209    */
210   ok = 0;
211   c = *pattern++;
212   do {
213     if (c == '\\' && !ISSET(flags, FNM_NOESCAPE)) c = *pattern++;
214     if (c == EOS) return (RANGE_ERROR);
215     if (c == '/' && ISSET(flags, FNM_PATHNAME)) return (RANGE_NOMATCH);
216     c = FOLD(c);
217     if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
218       pattern += 2;
219       if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE)) c2 = *pattern++;
220       if (c2 == EOS) return (RANGE_ERROR);
221       c2 = FOLD(c2);
222       if (c <= test && test <= c2) ok = 1;
223     } else if (c == test)
224       ok = 1;
225   } while ((c = *pattern++) != ']');
226 
227   *newp = (char*)pattern;
228   return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
229 }
230