1*19261079SEd Maste /* $OpenBSD: fnmatch.c,v 1.22 2020/03/13 03:25:45 djm Exp $ */
2*19261079SEd Maste
3*19261079SEd Maste /* Copyright (c) 2011, VMware, Inc.
4*19261079SEd Maste * All rights reserved.
5*19261079SEd Maste *
6*19261079SEd Maste * Redistribution and use in source and binary forms, with or without
7*19261079SEd Maste * modification, are permitted provided that the following conditions are met:
8*19261079SEd Maste * * Redistributions of source code must retain the above copyright
9*19261079SEd Maste * notice, this list of conditions and the following disclaimer.
10*19261079SEd Maste * * Redistributions in binary form must reproduce the above copyright
11*19261079SEd Maste * notice, this list of conditions and the following disclaimer in the
12*19261079SEd Maste * documentation and/or other materials provided with the distribution.
13*19261079SEd Maste * * Neither the name of the VMware, Inc. nor the names of its contributors
14*19261079SEd Maste * may be used to endorse or promote products derived from this software
15*19261079SEd Maste * without specific prior written permission.
16*19261079SEd Maste *
17*19261079SEd Maste * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*19261079SEd Maste * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*19261079SEd Maste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*19261079SEd Maste * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR
21*19261079SEd Maste * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22*19261079SEd Maste * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23*19261079SEd Maste * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24*19261079SEd Maste * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25*19261079SEd Maste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26*19261079SEd Maste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*19261079SEd Maste */
28*19261079SEd Maste
29*19261079SEd Maste /*
30*19261079SEd Maste * Copyright (c) 2008, 2016 Todd C. Miller <millert@openbsd.org>
31*19261079SEd Maste *
32*19261079SEd Maste * Permission to use, copy, modify, and distribute this software for any
33*19261079SEd Maste * purpose with or without fee is hereby granted, provided that the above
34*19261079SEd Maste * copyright notice and this permission notice appear in all copies.
35*19261079SEd Maste *
36*19261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
37*19261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
38*19261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
39*19261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40*19261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
41*19261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
42*19261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43*19261079SEd Maste */
44*19261079SEd Maste
45*19261079SEd Maste /* Authored by William A. Rowe Jr. <wrowe; apache.org, vmware.com>, April 2011
46*19261079SEd Maste *
47*19261079SEd Maste * Derived from The Open Group Base Specifications Issue 7, IEEE Std 1003.1-2008
48*19261079SEd Maste * as described in;
49*19261079SEd Maste * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
50*19261079SEd Maste *
51*19261079SEd Maste * Filename pattern matches defined in section 2.13, "Pattern Matching Notation"
52*19261079SEd Maste * from chapter 2. "Shell Command Language"
53*19261079SEd Maste * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
54*19261079SEd Maste * where; 1. A bracket expression starting with an unquoted <circumflex> '^'
55*19261079SEd Maste * character CONTINUES to specify a non-matching list; 2. an explicit <period> '.'
56*19261079SEd Maste * in a bracket expression matching list, e.g. "[.abc]" does NOT match a leading
57*19261079SEd Maste * <period> in a filename; 3. a <left-square-bracket> '[' which does not introduce
58*19261079SEd Maste * a valid bracket expression is treated as an ordinary character; 4. a differing
59*19261079SEd Maste * number of consecutive slashes within pattern and string will NOT match;
60*19261079SEd Maste * 5. a trailing '\' in FNM_ESCAPE mode is treated as an ordinary '\' character.
61*19261079SEd Maste *
62*19261079SEd Maste * Bracket expansion defined in section 9.3.5, "RE Bracket Expression",
63*19261079SEd Maste * from chapter 9, "Regular Expressions"
64*19261079SEd Maste * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
65*19261079SEd Maste * with no support for collating symbols, equivalence class expressions or
66*19261079SEd Maste * character class expressions. A partial range expression with a leading
67*19261079SEd Maste * hyphen following a valid range expression will match only the ordinary
68*19261079SEd Maste * <hyphen> and the ending character (e.g. "[a-m-z]" will match characters
69*19261079SEd Maste * 'a' through 'm', a <hyphen> '-', or a 'z').
70*19261079SEd Maste *
71*19261079SEd Maste * Supports BSD extensions FNM_LEADING_DIR to match pattern to the end of one
72*19261079SEd Maste * path segment of string, and FNM_CASEFOLD to ignore alpha case.
73*19261079SEd Maste *
74*19261079SEd Maste * NOTE: Only POSIX/C single byte locales are correctly supported at this time.
75*19261079SEd Maste * Notably, non-POSIX locales with FNM_CASEFOLD produce undefined results,
76*19261079SEd Maste * particularly in ranges of mixed case (e.g. "[A-z]") or spanning alpha and
77*19261079SEd Maste * nonalpha characters within a range.
78*19261079SEd Maste *
79*19261079SEd Maste * XXX comments below indicate porting required for multi-byte character sets
80*19261079SEd Maste * and non-POSIX locale collation orders; requires mbr* APIs to track shift
81*19261079SEd Maste * state of pattern and string (rewinding pattern and string repeatedly).
82*19261079SEd Maste *
83*19261079SEd Maste * Certain parts of the code assume 0x00-0x3F are unique with any MBCS (e.g.
84*19261079SEd Maste * UTF-8, SHIFT-JIS, etc). Any implementation allowing '\' as an alternate
85*19261079SEd Maste * path delimiter must be aware that 0x5C is NOT unique within SHIFT-JIS.
86*19261079SEd Maste */
87*19261079SEd Maste
88*19261079SEd Maste /* OPENBSD ORIGINAL: lib/libc/gen/fnmatch.c */
89*19261079SEd Maste
90*19261079SEd Maste #include "includes.h"
91*19261079SEd Maste #ifndef HAVE_FNMATCH
92*19261079SEd Maste
93*19261079SEd Maste #include <fnmatch.h>
94*19261079SEd Maste #include <string.h>
95*19261079SEd Maste #include <ctype.h>
96*19261079SEd Maste
97*19261079SEd Maste #include "charclass.h"
98*19261079SEd Maste
99*19261079SEd Maste #define RANGE_MATCH 1
100*19261079SEd Maste #define RANGE_NOMATCH 0
101*19261079SEd Maste #define RANGE_ERROR (-1)
102*19261079SEd Maste
103*19261079SEd Maste static int
classmatch(const char * pattern,char test,int foldcase,const char ** ep)104*19261079SEd Maste classmatch(const char *pattern, char test, int foldcase, const char **ep)
105*19261079SEd Maste {
106*19261079SEd Maste const char * const mismatch = pattern;
107*19261079SEd Maste const char *colon;
108*19261079SEd Maste struct cclass *cc;
109*19261079SEd Maste int rval = RANGE_NOMATCH;
110*19261079SEd Maste size_t len;
111*19261079SEd Maste
112*19261079SEd Maste if (pattern[0] != '[' || pattern[1] != ':') {
113*19261079SEd Maste *ep = mismatch;
114*19261079SEd Maste return RANGE_ERROR;
115*19261079SEd Maste }
116*19261079SEd Maste pattern += 2;
117*19261079SEd Maste
118*19261079SEd Maste if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') {
119*19261079SEd Maste *ep = mismatch;
120*19261079SEd Maste return RANGE_ERROR;
121*19261079SEd Maste }
122*19261079SEd Maste *ep = colon + 2;
123*19261079SEd Maste len = (size_t)(colon - pattern);
124*19261079SEd Maste
125*19261079SEd Maste if (foldcase && strncmp(pattern, "upper:]", 7) == 0)
126*19261079SEd Maste pattern = "lower:]";
127*19261079SEd Maste for (cc = cclasses; cc->name != NULL; cc++) {
128*19261079SEd Maste if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') {
129*19261079SEd Maste if (cc->isctype((unsigned char)test))
130*19261079SEd Maste rval = RANGE_MATCH;
131*19261079SEd Maste break;
132*19261079SEd Maste }
133*19261079SEd Maste }
134*19261079SEd Maste if (cc->name == NULL) {
135*19261079SEd Maste /* invalid character class, treat as normal text */
136*19261079SEd Maste *ep = mismatch;
137*19261079SEd Maste rval = RANGE_ERROR;
138*19261079SEd Maste }
139*19261079SEd Maste return rval;
140*19261079SEd Maste }
141*19261079SEd Maste
142*19261079SEd Maste /* Most MBCS/collation/case issues handled here. Wildcard '*' is not handled.
143*19261079SEd Maste * EOS '\0' and the FNM_PATHNAME '/' delimiters are not advanced over,
144*19261079SEd Maste * however the "\/" sequence is advanced to '/'.
145*19261079SEd Maste *
146*19261079SEd Maste * Both pattern and string are **char to support pointer increment of arbitrary
147*19261079SEd Maste * multibyte characters for the given locale, in a later iteration of this code
148*19261079SEd Maste */
fnmatch_ch(const char ** pattern,const char ** string,int flags)149*19261079SEd Maste static int fnmatch_ch(const char **pattern, const char **string, int flags)
150*19261079SEd Maste {
151*19261079SEd Maste const char * const mismatch = *pattern;
152*19261079SEd Maste const int nocase = !!(flags & FNM_CASEFOLD);
153*19261079SEd Maste const int escape = !(flags & FNM_NOESCAPE);
154*19261079SEd Maste const int slash = !!(flags & FNM_PATHNAME);
155*19261079SEd Maste int result = FNM_NOMATCH;
156*19261079SEd Maste const char *startch;
157*19261079SEd Maste int negate;
158*19261079SEd Maste
159*19261079SEd Maste if (**pattern == '[') {
160*19261079SEd Maste ++*pattern;
161*19261079SEd Maste
162*19261079SEd Maste /* Handle negation, either leading ! or ^ operators */
163*19261079SEd Maste negate = (**pattern == '!') || (**pattern == '^');
164*19261079SEd Maste if (negate)
165*19261079SEd Maste ++*pattern;
166*19261079SEd Maste
167*19261079SEd Maste /* ']' is an ordinary char at the start of the range pattern */
168*19261079SEd Maste if (**pattern == ']')
169*19261079SEd Maste goto leadingclosebrace;
170*19261079SEd Maste
171*19261079SEd Maste while (**pattern) {
172*19261079SEd Maste if (**pattern == ']') {
173*19261079SEd Maste ++*pattern;
174*19261079SEd Maste /* XXX: Fix for MBCS character width */
175*19261079SEd Maste ++*string;
176*19261079SEd Maste return (result ^ negate);
177*19261079SEd Maste }
178*19261079SEd Maste
179*19261079SEd Maste if (escape && (**pattern == '\\')) {
180*19261079SEd Maste ++*pattern;
181*19261079SEd Maste
182*19261079SEd Maste /* Patterns must terminate with ']', not EOS */
183*19261079SEd Maste if (!**pattern)
184*19261079SEd Maste break;
185*19261079SEd Maste }
186*19261079SEd Maste
187*19261079SEd Maste /* Patterns must terminate with ']' not '/' */
188*19261079SEd Maste if (slash && (**pattern == '/'))
189*19261079SEd Maste break;
190*19261079SEd Maste
191*19261079SEd Maste /* Match character classes. */
192*19261079SEd Maste switch (classmatch(*pattern, **string, nocase, pattern)) {
193*19261079SEd Maste case RANGE_MATCH:
194*19261079SEd Maste result = 0;
195*19261079SEd Maste continue;
196*19261079SEd Maste case RANGE_NOMATCH:
197*19261079SEd Maste /* Valid character class but no match. */
198*19261079SEd Maste continue;
199*19261079SEd Maste default:
200*19261079SEd Maste /* Not a valid character class. */
201*19261079SEd Maste break;
202*19261079SEd Maste }
203*19261079SEd Maste if (!**pattern)
204*19261079SEd Maste break;
205*19261079SEd Maste
206*19261079SEd Maste leadingclosebrace:
207*19261079SEd Maste /* Look at only well-formed range patterns;
208*19261079SEd Maste * "x-]" is not allowed unless escaped ("x-\]")
209*19261079SEd Maste * XXX: Fix for locale/MBCS character width
210*19261079SEd Maste */
211*19261079SEd Maste if (((*pattern)[1] == '-') && ((*pattern)[2] != ']')) {
212*19261079SEd Maste startch = *pattern;
213*19261079SEd Maste *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
214*19261079SEd Maste
215*19261079SEd Maste /*
216*19261079SEd Maste * NOT a properly balanced [expr] pattern, EOS
217*19261079SEd Maste * terminated or ranges containing a slash in
218*19261079SEd Maste * FNM_PATHNAME mode pattern fall out to to the
219*19261079SEd Maste * rewind and test '[' literal code path.
220*19261079SEd Maste */
221*19261079SEd Maste if (!**pattern || (slash && (**pattern == '/')))
222*19261079SEd Maste break;
223*19261079SEd Maste
224*19261079SEd Maste /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
225*19261079SEd Maste if ((**string >= *startch) && (**string <= **pattern))
226*19261079SEd Maste result = 0;
227*19261079SEd Maste else if (nocase &&
228*19261079SEd Maste (isupper((unsigned char)**string) ||
229*19261079SEd Maste isupper((unsigned char)*startch) ||
230*19261079SEd Maste isupper((unsigned char)**pattern)) &&
231*19261079SEd Maste (tolower((unsigned char)**string) >=
232*19261079SEd Maste tolower((unsigned char)*startch)) &&
233*19261079SEd Maste (tolower((unsigned char)**string) <=
234*19261079SEd Maste tolower((unsigned char)**pattern)))
235*19261079SEd Maste result = 0;
236*19261079SEd Maste
237*19261079SEd Maste ++*pattern;
238*19261079SEd Maste continue;
239*19261079SEd Maste }
240*19261079SEd Maste
241*19261079SEd Maste /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
242*19261079SEd Maste if ((**string == **pattern))
243*19261079SEd Maste result = 0;
244*19261079SEd Maste else if (nocase && (isupper((unsigned char)**string) ||
245*19261079SEd Maste isupper((unsigned char)**pattern)) &&
246*19261079SEd Maste (tolower((unsigned char)**string) ==
247*19261079SEd Maste tolower((unsigned char)**pattern)))
248*19261079SEd Maste result = 0;
249*19261079SEd Maste
250*19261079SEd Maste ++*pattern;
251*19261079SEd Maste }
252*19261079SEd Maste /*
253*19261079SEd Maste * NOT a properly balanced [expr] pattern;
254*19261079SEd Maste * Rewind and reset result to test '[' literal
255*19261079SEd Maste */
256*19261079SEd Maste *pattern = mismatch;
257*19261079SEd Maste result = FNM_NOMATCH;
258*19261079SEd Maste } else if (**pattern == '?') {
259*19261079SEd Maste /* Optimize '?' match before unescaping **pattern */
260*19261079SEd Maste if (!**string || (slash && (**string == '/')))
261*19261079SEd Maste return FNM_NOMATCH;
262*19261079SEd Maste result = 0;
263*19261079SEd Maste goto fnmatch_ch_success;
264*19261079SEd Maste } else if (escape && (**pattern == '\\') && (*pattern)[1]) {
265*19261079SEd Maste ++*pattern;
266*19261079SEd Maste }
267*19261079SEd Maste
268*19261079SEd Maste /* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
269*19261079SEd Maste if (**string == **pattern)
270*19261079SEd Maste result = 0;
271*19261079SEd Maste else if (nocase && (isupper((unsigned char)**string) ||
272*19261079SEd Maste isupper((unsigned char)**pattern)) &&
273*19261079SEd Maste (tolower((unsigned char)**string) ==
274*19261079SEd Maste tolower((unsigned char)**pattern)))
275*19261079SEd Maste result = 0;
276*19261079SEd Maste
277*19261079SEd Maste /* Refuse to advance over trailing slash or NULs */
278*19261079SEd Maste if (**string == '\0' || **pattern == '\0' ||
279*19261079SEd Maste (slash && ((**string == '/') || (**pattern == '/'))))
280*19261079SEd Maste return result;
281*19261079SEd Maste
282*19261079SEd Maste fnmatch_ch_success:
283*19261079SEd Maste ++*pattern;
284*19261079SEd Maste ++*string;
285*19261079SEd Maste return result;
286*19261079SEd Maste }
287*19261079SEd Maste
288*19261079SEd Maste
fnmatch(const char * pattern,const char * string,int flags)289*19261079SEd Maste int fnmatch(const char *pattern, const char *string, int flags)
290*19261079SEd Maste {
291*19261079SEd Maste static const char dummystring[2] = {' ', 0};
292*19261079SEd Maste const int escape = !(flags & FNM_NOESCAPE);
293*19261079SEd Maste const int slash = !!(flags & FNM_PATHNAME);
294*19261079SEd Maste const int leading_dir = !!(flags & FNM_LEADING_DIR);
295*19261079SEd Maste const char *dummyptr, *matchptr, *strendseg;
296*19261079SEd Maste int wild;
297*19261079SEd Maste /* For '*' wild processing only; suppress 'used before initialization'
298*19261079SEd Maste * warnings with dummy initialization values;
299*19261079SEd Maste */
300*19261079SEd Maste const char *strstartseg = NULL;
301*19261079SEd Maste const char *mismatch = NULL;
302*19261079SEd Maste int matchlen = 0;
303*19261079SEd Maste
304*19261079SEd Maste if (*pattern == '*')
305*19261079SEd Maste goto firstsegment;
306*19261079SEd Maste
307*19261079SEd Maste while (*pattern && *string) {
308*19261079SEd Maste /*
309*19261079SEd Maste * Pre-decode "\/" which has no special significance, and
310*19261079SEd Maste * match balanced slashes, starting a new segment pattern.
311*19261079SEd Maste */
312*19261079SEd Maste if (slash && escape && (*pattern == '\\') && (pattern[1] == '/'))
313*19261079SEd Maste ++pattern;
314*19261079SEd Maste if (slash && (*pattern == '/') && (*string == '/')) {
315*19261079SEd Maste ++pattern;
316*19261079SEd Maste ++string;
317*19261079SEd Maste }
318*19261079SEd Maste
319*19261079SEd Maste firstsegment:
320*19261079SEd Maste /*
321*19261079SEd Maste * At the beginning of each segment, validate leading period
322*19261079SEd Maste * behavior.
323*19261079SEd Maste */
324*19261079SEd Maste if ((flags & FNM_PERIOD) && (*string == '.')) {
325*19261079SEd Maste if (*pattern == '.')
326*19261079SEd Maste ++pattern;
327*19261079SEd Maste else if (escape && (*pattern == '\\') && (pattern[1] == '.'))
328*19261079SEd Maste pattern += 2;
329*19261079SEd Maste else
330*19261079SEd Maste return FNM_NOMATCH;
331*19261079SEd Maste ++string;
332*19261079SEd Maste }
333*19261079SEd Maste
334*19261079SEd Maste /*
335*19261079SEd Maste * Determine the end of string segment. Presumes '/'
336*19261079SEd Maste * character is unique, not composite in any MBCS encoding
337*19261079SEd Maste */
338*19261079SEd Maste if (slash) {
339*19261079SEd Maste strendseg = strchr(string, '/');
340*19261079SEd Maste if (!strendseg)
341*19261079SEd Maste strendseg = strchr(string, '\0');
342*19261079SEd Maste } else {
343*19261079SEd Maste strendseg = strchr(string, '\0');
344*19261079SEd Maste }
345*19261079SEd Maste
346*19261079SEd Maste /*
347*19261079SEd Maste * Allow pattern '*' to be consumed even with no remaining
348*19261079SEd Maste * string to match.
349*19261079SEd Maste */
350*19261079SEd Maste while (*pattern) {
351*19261079SEd Maste if ((string > strendseg) ||
352*19261079SEd Maste ((string == strendseg) && (*pattern != '*')))
353*19261079SEd Maste break;
354*19261079SEd Maste
355*19261079SEd Maste if (slash && ((*pattern == '/') ||
356*19261079SEd Maste (escape && (*pattern == '\\') && (pattern[1] == '/'))))
357*19261079SEd Maste break;
358*19261079SEd Maste
359*19261079SEd Maste /*
360*19261079SEd Maste * Reduce groups of '*' and '?' to n '?' matches
361*19261079SEd Maste * followed by one '*' test for simplicity.
362*19261079SEd Maste */
363*19261079SEd Maste for (wild = 0; (*pattern == '*') || (*pattern == '?'); ++pattern) {
364*19261079SEd Maste if (*pattern == '*') {
365*19261079SEd Maste wild = 1;
366*19261079SEd Maste } else if (string < strendseg) { /* && (*pattern == '?') */
367*19261079SEd Maste /* XXX: Advance 1 char for MBCS locale */
368*19261079SEd Maste ++string;
369*19261079SEd Maste }
370*19261079SEd Maste else { /* (string >= strendseg) && (*pattern == '?') */
371*19261079SEd Maste return FNM_NOMATCH;
372*19261079SEd Maste }
373*19261079SEd Maste }
374*19261079SEd Maste
375*19261079SEd Maste if (wild) {
376*19261079SEd Maste strstartseg = string;
377*19261079SEd Maste mismatch = pattern;
378*19261079SEd Maste
379*19261079SEd Maste /*
380*19261079SEd Maste * Count fixed (non '*') char matches remaining
381*19261079SEd Maste * in pattern * excluding '/' (or "\/") and '*'.
382*19261079SEd Maste */
383*19261079SEd Maste for (matchptr = pattern, matchlen = 0; 1; ++matchlen) {
384*19261079SEd Maste if ((*matchptr == '\0') ||
385*19261079SEd Maste (slash && ((*matchptr == '/') ||
386*19261079SEd Maste (escape && (*matchptr == '\\') &&
387*19261079SEd Maste (matchptr[1] == '/'))))) {
388*19261079SEd Maste /* Compare precisely this many
389*19261079SEd Maste * trailing string chars, the
390*19261079SEd Maste * resulting match needs no
391*19261079SEd Maste * wildcard loop.
392*19261079SEd Maste */
393*19261079SEd Maste /* XXX: Adjust for MBCS */
394*19261079SEd Maste if (string + matchlen > strendseg)
395*19261079SEd Maste return FNM_NOMATCH;
396*19261079SEd Maste
397*19261079SEd Maste string = strendseg - matchlen;
398*19261079SEd Maste wild = 0;
399*19261079SEd Maste break;
400*19261079SEd Maste }
401*19261079SEd Maste
402*19261079SEd Maste if (*matchptr == '*') {
403*19261079SEd Maste /*
404*19261079SEd Maste * Ensure at least this many
405*19261079SEd Maste * trailing string chars remain
406*19261079SEd Maste * for the first comparison.
407*19261079SEd Maste */
408*19261079SEd Maste /* XXX: Adjust for MBCS */
409*19261079SEd Maste if (string + matchlen > strendseg)
410*19261079SEd Maste return FNM_NOMATCH;
411*19261079SEd Maste
412*19261079SEd Maste /*
413*19261079SEd Maste * Begin first wild comparison
414*19261079SEd Maste * at the current position.
415*19261079SEd Maste */
416*19261079SEd Maste break;
417*19261079SEd Maste }
418*19261079SEd Maste
419*19261079SEd Maste /*
420*19261079SEd Maste * Skip forward in pattern by a single
421*19261079SEd Maste * character match Use a dummy
422*19261079SEd Maste * fnmatch_ch() test to count one
423*19261079SEd Maste * "[range]" escape.
424*19261079SEd Maste */
425*19261079SEd Maste /* XXX: Adjust for MBCS */
426*19261079SEd Maste if (escape && (*matchptr == '\\') &&
427*19261079SEd Maste matchptr[1]) {
428*19261079SEd Maste matchptr += 2;
429*19261079SEd Maste } else if (*matchptr == '[') {
430*19261079SEd Maste dummyptr = dummystring;
431*19261079SEd Maste fnmatch_ch(&matchptr, &dummyptr,
432*19261079SEd Maste flags);
433*19261079SEd Maste } else {
434*19261079SEd Maste ++matchptr;
435*19261079SEd Maste }
436*19261079SEd Maste }
437*19261079SEd Maste }
438*19261079SEd Maste
439*19261079SEd Maste /* Incrementally match string against the pattern. */
440*19261079SEd Maste while (*pattern && (string < strendseg)) {
441*19261079SEd Maste /* Success; begin a new wild pattern search. */
442*19261079SEd Maste if (*pattern == '*')
443*19261079SEd Maste break;
444*19261079SEd Maste
445*19261079SEd Maste if (slash && ((*string == '/') ||
446*19261079SEd Maste (*pattern == '/') || (escape &&
447*19261079SEd Maste (*pattern == '\\') && (pattern[1] == '/'))))
448*19261079SEd Maste break;
449*19261079SEd Maste
450*19261079SEd Maste /*
451*19261079SEd Maste * Compare ch's (the pattern is advanced over
452*19261079SEd Maste * "\/" to the '/', but slashes will mismatch,
453*19261079SEd Maste * and are not consumed).
454*19261079SEd Maste */
455*19261079SEd Maste if (!fnmatch_ch(&pattern, &string, flags))
456*19261079SEd Maste continue;
457*19261079SEd Maste
458*19261079SEd Maste /*
459*19261079SEd Maste * Failed to match, loop against next char
460*19261079SEd Maste * offset of string segment until not enough
461*19261079SEd Maste * string chars remain to match the fixed
462*19261079SEd Maste * pattern.
463*19261079SEd Maste */
464*19261079SEd Maste if (wild) {
465*19261079SEd Maste /* XXX: Advance 1 char for MBCS locale */
466*19261079SEd Maste string = ++strstartseg;
467*19261079SEd Maste if (string + matchlen > strendseg)
468*19261079SEd Maste return FNM_NOMATCH;
469*19261079SEd Maste
470*19261079SEd Maste pattern = mismatch;
471*19261079SEd Maste continue;
472*19261079SEd Maste } else
473*19261079SEd Maste return FNM_NOMATCH;
474*19261079SEd Maste }
475*19261079SEd Maste }
476*19261079SEd Maste
477*19261079SEd Maste if (*string && !((slash || leading_dir) && (*string == '/')))
478*19261079SEd Maste return FNM_NOMATCH;
479*19261079SEd Maste
480*19261079SEd Maste if (*pattern && !(slash && ((*pattern == '/') ||
481*19261079SEd Maste (escape && (*pattern == '\\') && (pattern[1] == '/')))))
482*19261079SEd Maste return FNM_NOMATCH;
483*19261079SEd Maste
484*19261079SEd Maste if (leading_dir && !*pattern && *string == '/')
485*19261079SEd Maste return 0;
486*19261079SEd Maste }
487*19261079SEd Maste
488*19261079SEd Maste /* Where both pattern and string are at EOS, declare success. */
489*19261079SEd Maste if (!*string && !*pattern)
490*19261079SEd Maste return 0;
491*19261079SEd Maste
492*19261079SEd Maste /* Pattern didn't match to the end of string. */
493*19261079SEd Maste return FNM_NOMATCH;
494*19261079SEd Maste }
495*19261079SEd Maste #endif /* HAVE_FNMATCH */
496