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