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 #include <zephyr/toolchain.h>
48
49 #define EOS '\0'
50
foldcase(int ch,int flags)51 static inline int foldcase(int ch, int flags)
52 {
53
54 if ((flags & FNM_CASEFOLD) != 0 && isupper(ch)) {
55 return tolower(ch);
56 }
57
58 return ch;
59 }
60
61 #define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags))
62
rangematch(const char * pattern,int test,int flags)63 static const char *rangematch(const char *pattern, int test, int flags)
64 {
65 bool negate, ok, need;
66 char c, c2;
67
68 if (pattern == NULL) {
69 return NULL;
70 }
71
72 /*
73 * A bracket expression starting with an unquoted circumflex
74 * character produces unspecified results (IEEE 1003.2-1992,
75 * 3.13.2). This implementation treats it like '!', for
76 * consistency with the regular expression syntax.
77 * J.T. Conklin (conklin@ngai.kaleida.com)
78 */
79 negate = *pattern == '!' || *pattern == '^';
80 if (negate) {
81 ++pattern;
82 }
83
84 for (need = true, ok = false, c = FOLDCASE(*pattern++, flags); c != ']' || need;
85 c = FOLDCASE(*pattern++, flags)) {
86 need = false;
87 if (c == '/') {
88 return (void *)-1;
89 }
90
91 if (c == '\\' && !(flags & FNM_NOESCAPE)) {
92 c = FOLDCASE(*pattern++, flags);
93 }
94
95 if (c == EOS) {
96 return NULL;
97 }
98
99 if (*pattern == '-') {
100 c2 = FOLDCASE(*(pattern + 1), flags);
101 if (c2 != EOS && c2 != ']') {
102 pattern += 2;
103 if (c2 == '\\' && !(flags & FNM_NOESCAPE)) {
104 c2 = FOLDCASE(*pattern++, flags);
105 }
106
107 if (c2 == EOS) {
108 return NULL;
109 }
110
111 if (c <= test && test <= c2) {
112 ok = true;
113 }
114 }
115 } else if (c == test) {
116 ok = true;
117 }
118 }
119
120 return ok == negate ? NULL : pattern;
121 }
122
fnmatchx(const char * pattern,const char * string,int flags,size_t recursion)123 static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
124 {
125 const char *stringstart, *r;
126 char c, test;
127
128 if (pattern == NULL || string == NULL) {
129 return FNM_NOMATCH;
130 }
131
132 if (recursion-- == 0) {
133 return FNM_NORES;
134 }
135
136 for (stringstart = string;;) {
137 c = FOLDCASE(*pattern++, flags);
138 switch (c) {
139 case EOS:
140 if ((flags & FNM_LEADING_DIR) && *string == '/') {
141 return 0;
142 }
143
144 return *string == EOS ? 0 : FNM_NOMATCH;
145 case '?':
146 if (*string == EOS) {
147 return FNM_NOMATCH;
148 }
149
150 if (*string == '/' && (flags & FNM_PATHNAME)) {
151 return FNM_NOMATCH;
152 }
153
154 if (*string == '.' && (flags & FNM_PERIOD) &&
155 (string == stringstart ||
156 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
157 return FNM_NOMATCH;
158 }
159
160 ++string;
161 break;
162 case '*':
163 c = FOLDCASE(*pattern, flags);
164 /* Collapse multiple stars. */
165 while (c == '*') {
166 c = FOLDCASE(*++pattern, flags);
167 }
168
169 if (*string == '.' && (flags & FNM_PERIOD) &&
170 (string == stringstart ||
171 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
172 return FNM_NOMATCH;
173 }
174
175 /* Optimize for pattern with * at end or before /. */
176 if (c == EOS) {
177 if (flags & FNM_PATHNAME) {
178 return (flags & FNM_LEADING_DIR) ||
179 strchr(string, '/') == NULL
180 ? 0
181 : FNM_NOMATCH;
182 } else {
183 return 0;
184 }
185 } else if (c == '/' && flags & FNM_PATHNAME) {
186 string = strchr(string, '/');
187 if (string == NULL) {
188 return FNM_NOMATCH;
189 }
190
191 break;
192 }
193
194 /* General case, use recursion. */
195 do {
196 test = FOLDCASE(*string, flags);
197 if (test == EOS) {
198 break;
199 }
200
201 int e = fnmatchx(pattern, string, flags & ~FNM_PERIOD, recursion);
202
203 if (e != FNM_NOMATCH) {
204 return e;
205 }
206
207 if (test == '/' && flags & FNM_PATHNAME) {
208 break;
209 }
210
211 ++string;
212 } while (true);
213
214 return FNM_NOMATCH;
215 case '[':
216 if (*string == EOS) {
217 return FNM_NOMATCH;
218 }
219
220 if (*string == '/' && flags & FNM_PATHNAME) {
221 return FNM_NOMATCH;
222 }
223
224 r = rangematch(pattern, FOLDCASE(*string, flags), flags);
225 if (r == NULL) {
226 return FNM_NOMATCH;
227 }
228
229 if (r == (void *)-1) {
230 if (*string != '[') {
231 return FNM_NOMATCH;
232 }
233 } else {
234 pattern = r;
235 }
236
237 ++string;
238 break;
239 case '\\':
240 if (!(flags & FNM_NOESCAPE)) {
241 c = FOLDCASE(*pattern++, flags);
242 if (c == EOS) {
243 c = '\0';
244 --pattern;
245 }
246 }
247 __fallthrough;
248 default:
249 if (c != FOLDCASE(*string++, flags)) {
250 return FNM_NOMATCH;
251 }
252
253 break;
254 }
255 }
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