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 "include/bareos.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
xfnmatch(const char * pattern,const char * string,int flags)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 
r_fnmatch(const char * pattern,const char * string,int flags,int recur)81 static int r_fnmatch(const char* pattern,
82                      const char* string,
83                      int flags,
84                      int recur)
85 {
86   const char* stringstart;
87   char* newp;
88   char c, test;
89   int e;
90 
91   if (recur-- <= 0) { return (-1); }
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) return (FNM_NOMATCH);
102         if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
103           return (FNM_NOMATCH);
104         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
105             (string == stringstart ||
106              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
107           return (FNM_NOMATCH);
108         ++string;
109         break;
110       case '*':
111         c = *pattern;
112         /* Collapse multiple stars. */
113         while (c == '*') { c = *++pattern; }
114 
115         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
116             (string == stringstart ||
117              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1))))) {
118           return (FNM_NOMATCH);
119         }
120 
121         /* Optimize for pattern with * at end or before /. */
122         if (c == EOS) {
123           if (ISSET(flags, FNM_PATHNAME)) {
124             return (ISSET(flags, FNM_LEADING_DIR) || strchr(string, '/') == NULL
125                         ? 0
126                         : FNM_NOMATCH);
127           } else {
128             return (0);
129           }
130         } else if (IsPathSeparator(c) && ISSET(flags, FNM_PATHNAME)) {
131           if ((string = strchr(string, '/')) == NULL) return (FNM_NOMATCH);
132           break;
133         }
134 
135         /* General case, use recursion. */
136         while ((test = *string) != EOS) {
137           e = r_fnmatch(pattern, string, flags & ~FNM_PERIOD, recur);
138           if (e != FNM_NOMATCH) { /* can be NOMATCH, -1 or MATCH */
139             return (e);
140           }
141           if (test == '/' && ISSET(flags, FNM_PATHNAME)) { break; }
142           ++string;
143         }
144         return (FNM_NOMATCH);
145       case '[':
146         if (*string == EOS) return (FNM_NOMATCH);
147         if (IsPathSeparator(*string) && ISSET(flags, FNM_PATHNAME))
148           return (FNM_NOMATCH);
149         if (*string == '.' && ISSET(flags, FNM_PERIOD) &&
150             (string == stringstart ||
151              (ISSET(flags, FNM_PATHNAME) && IsPathSeparator(*(string - 1)))))
152           return (FNM_NOMATCH);
153 
154         switch (Rangematch(pattern, *string, flags, &newp)) {
155           case RANGE_ERROR:
156             /* not a good range, treat as normal text */
157             goto normal;
158           case RANGE_MATCH:
159             pattern = newp;
160             break;
161           case RANGE_NOMATCH:
162             return (FNM_NOMATCH);
163         }
164         ++string;
165         break;
166 
167       case '\\':
168         if (!ISSET(flags, FNM_NOESCAPE)) {
169           if ((c = *pattern++) == EOS) {
170             c = '\\';
171             --pattern;
172           }
173         }
174         /* FALLTHROUGH */
175       default:
176       normal:
177         if (FOLD(c) != FOLD(*string)) { return (FNM_NOMATCH); }
178         ++string;
179         break;
180     }
181   }
182   /* NOTREACHED */
183 }
184 
Rangematch(const char * pattern,char test,int flags,char ** newp)185 static int Rangematch(const char* pattern, char test, int flags, char** newp)
186 {
187   int negate, ok;
188   char c, c2;
189 
190   /*
191    * A bracket expression starting with an unquoted circumflex
192    * character produces unspecified results (IEEE 1003.2-1992,
193    * 3.13.2).  This implementation treats it like '!', for
194    * consistency with the regular expression syntax.
195    * J.T. Conklin (conklin@ngai.kaleida.com)
196    */
197   if ((negate = (*pattern == '!' || *pattern == '^'))) ++pattern;
198 
199   test = FOLD(test);
200 
201   /*
202    * A right bracket shall lose its special meaning and represent
203    * itself in a bracket expression if it occurs first in the list.
204    * -- POSIX.2 2.8.3.2
205    */
206   ok = 0;
207   c = *pattern++;
208   do {
209     if (c == '\\' && !ISSET(flags, FNM_NOESCAPE)) c = *pattern++;
210     if (c == EOS) return (RANGE_ERROR);
211     if (c == '/' && ISSET(flags, FNM_PATHNAME)) return (RANGE_NOMATCH);
212     c = FOLD(c);
213     if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
214       pattern += 2;
215       if (c2 == '\\' && !ISSET(flags, FNM_NOESCAPE)) c2 = *pattern++;
216       if (c2 == EOS) return (RANGE_ERROR);
217       c2 = FOLD(c2);
218       if (c <= test && test <= c2) ok = 1;
219     } else if (c == test)
220       ok = 1;
221   } while ((c = *pattern++) != ']');
222 
223   *newp = (char*)pattern;
224   return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
225 }
226