1 /* SPDX-License-Identifier: BSD-3-Clause */
2
3 /* $NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $ */
4
5 /*
6 * Copyright (c) 1989, 1993, 1994
7 * The Regents of the University of California. All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * Guido van Rossum.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 /*
38 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
39 * Compares a filename or pathname to a pattern.
40 */
41
42 #include <ctype.h>
43 #include <stdbool.h>
44 #include <string.h>
45
46 #include <zephyr/posix/fnmatch.h>
47
48 #define EOS '\0'
49
foldcase(int ch,int flags)50 static inline int foldcase(int ch, int flags)
51 {
52
53 if ((flags & FNM_CASEFOLD) != 0 && isupper(ch)) {
54 return tolower(ch);
55 }
56
57 return ch;
58 }
59
60 #define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags))
61
rangematch(const char * pattern,int test,int flags)62 static const char *rangematch(const char *pattern, int test, int flags)
63 {
64 bool negate, ok, need;
65 char c, c2;
66
67 if (pattern == NULL) {
68 return NULL;
69 }
70
71 /*
72 * A bracket expression starting with an unquoted circumflex
73 * character produces unspecified results (IEEE 1003.2-1992,
74 * 3.13.2). This implementation treats it like '!', for
75 * consistency with the regular expression syntax.
76 * J.T. Conklin (conklin@ngai.kaleida.com)
77 */
78 negate = *pattern == '!' || *pattern == '^';
79 if (negate) {
80 ++pattern;
81 }
82
83 for (need = true, ok = false, c = FOLDCASE(*pattern++, flags); c != ']' || need;
84 c = FOLDCASE(*pattern++, flags)) {
85 need = false;
86 if (c == '/') {
87 return (void *)-1;
88 }
89
90 if (c == '\\' && !(flags & FNM_NOESCAPE)) {
91 c = FOLDCASE(*pattern++, flags);
92 }
93
94 if (c == EOS) {
95 return NULL;
96 }
97
98 if (*pattern == '-') {
99 c2 = FOLDCASE(*(pattern + 1), flags);
100 if (c2 != EOS && c2 != ']') {
101 pattern += 2;
102 if (c2 == '\\' && !(flags & FNM_NOESCAPE)) {
103 c2 = FOLDCASE(*pattern++, flags);
104 }
105
106 if (c2 == EOS) {
107 return NULL;
108 }
109
110 if (c <= test && test <= c2) {
111 ok = true;
112 }
113 }
114 } else if (c == test) {
115 ok = true;
116 }
117 }
118
119 return ok == negate ? NULL : pattern;
120 }
121
fnmatchx(const char * pattern,const char * string,int flags,size_t recursion)122 static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
123 {
124 const char *stringstart, *r;
125 char c, test;
126
127 if (pattern == NULL || string == NULL) {
128 return FNM_NOMATCH;
129 }
130
131 if (recursion-- == 0) {
132 return FNM_NORES;
133 }
134
135 for (stringstart = string;;) {
136 c = FOLDCASE(*pattern++, flags);
137 switch (c) {
138 case EOS:
139 if ((flags & FNM_LEADING_DIR) && *string == '/') {
140 return 0;
141 }
142
143 return *string == EOS ? 0 : FNM_NOMATCH;
144 case '?':
145 if (*string == EOS) {
146 return FNM_NOMATCH;
147 }
148
149 if (*string == '/' && (flags & FNM_PATHNAME)) {
150 return FNM_NOMATCH;
151 }
152
153 if (*string == '.' && (flags & FNM_PERIOD) &&
154 (string == stringstart ||
155 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
156 return FNM_NOMATCH;
157 }
158
159 ++string;
160 break;
161 case '*':
162 c = FOLDCASE(*pattern, flags);
163 /* Collapse multiple stars. */
164 while (c == '*') {
165 c = FOLDCASE(*++pattern, flags);
166 }
167
168 if (*string == '.' && (flags & FNM_PERIOD) &&
169 (string == stringstart ||
170 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
171 return FNM_NOMATCH;
172 }
173
174 /* Optimize for pattern with * at end or before /. */
175 if (c == EOS) {
176 if (flags & FNM_PATHNAME) {
177 return (flags & FNM_LEADING_DIR) ||
178 strchr(string, '/') == NULL
179 ? 0
180 : FNM_NOMATCH;
181 } else {
182 return 0;
183 }
184 } else if (c == '/' && flags & FNM_PATHNAME) {
185 string = strchr(string, '/');
186 if (string == NULL) {
187 return FNM_NOMATCH;
188 }
189
190 break;
191 }
192
193 /* General case, use recursion. */
194 do {
195 test = FOLDCASE(*string, flags);
196 if (test == EOS) {
197 break;
198 }
199
200 int e = fnmatchx(pattern, string, flags & ~FNM_PERIOD, recursion);
201
202 if (e != FNM_NOMATCH) {
203 return e;
204 }
205
206 if (test == '/' && flags & FNM_PATHNAME) {
207 break;
208 }
209
210 ++string;
211 } while (true);
212
213 return FNM_NOMATCH;
214 case '[':
215 if (*string == EOS) {
216 return FNM_NOMATCH;
217 }
218
219 if (*string == '/' && flags & FNM_PATHNAME) {
220 return FNM_NOMATCH;
221 }
222
223 r = rangematch(pattern, FOLDCASE(*string, flags), flags);
224 if (r == NULL) {
225 return FNM_NOMATCH;
226 }
227
228 if (r == (void *)-1) {
229 if (*string != '[') {
230 return FNM_NOMATCH;
231 }
232 } else {
233 pattern = r;
234 }
235
236 ++string;
237 break;
238 case '\\':
239 if (!(flags & FNM_NOESCAPE)) {
240 c = FOLDCASE(*pattern++, flags);
241 if (c == EOS) {
242 c = '\0';
243 --pattern;
244 }
245 }
246 /* FALLTHROUGH */
247 default:
248 if (c != FOLDCASE(*string++, flags)) {
249 return FNM_NOMATCH;
250 }
251
252 break;
253 }
254 }
255 /* NOTREACHED */
256 }
257
fnmatch(const char * pattern,const char * string,int flags)258 int fnmatch(const char *pattern, const char *string, int flags)
259 {
260 return fnmatchx(pattern, string, flags, 64);
261 }
262