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