1*185becb1SMartin Matuska /*-
2*185becb1SMartin Matuska * SPDX-License-Identifier: BSD-3-Clause
3*185becb1SMartin Matuska *
4*185becb1SMartin Matuska * Copyright (c) 1989, 1993, 1994
5*185becb1SMartin Matuska * The Regents of the University of California. All rights reserved.
6*185becb1SMartin Matuska *
7*185becb1SMartin Matuska * This code is derived from software contributed to Berkeley by
8*185becb1SMartin Matuska * Guido van Rossum.
9*185becb1SMartin Matuska *
10*185becb1SMartin Matuska * Copyright (c) 2011 The FreeBSD Foundation
11*185becb1SMartin Matuska *
12*185becb1SMartin Matuska * Portions of this software were developed by David Chisnall
13*185becb1SMartin Matuska * under sponsorship from the FreeBSD Foundation.
14*185becb1SMartin Matuska *
15*185becb1SMartin Matuska * Redistribution and use in source and binary forms, with or without
16*185becb1SMartin Matuska * modification, are permitted provided that the following conditions
17*185becb1SMartin Matuska * are met:
18*185becb1SMartin Matuska * 1. Redistributions of source code must retain the above copyright
19*185becb1SMartin Matuska * notice, this list of conditions and the following disclaimer.
20*185becb1SMartin Matuska * 2. Redistributions in binary form must reproduce the above copyright
21*185becb1SMartin Matuska * notice, this list of conditions and the following disclaimer in the
22*185becb1SMartin Matuska * documentation and/or other materials provided with the distribution.
23*185becb1SMartin Matuska * 3. Neither the name of the University nor the names of its contributors
24*185becb1SMartin Matuska * may be used to endorse or promote products derived from this software
25*185becb1SMartin Matuska * without specific prior written permission.
26*185becb1SMartin Matuska *
27*185becb1SMartin Matuska * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28*185becb1SMartin Matuska * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29*185becb1SMartin Matuska * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30*185becb1SMartin Matuska * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31*185becb1SMartin Matuska * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32*185becb1SMartin Matuska * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33*185becb1SMartin Matuska * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34*185becb1SMartin Matuska * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35*185becb1SMartin Matuska * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36*185becb1SMartin Matuska * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37*185becb1SMartin Matuska * SUCH DAMAGE.
38*185becb1SMartin Matuska */
39*185becb1SMartin Matuska
40*185becb1SMartin Matuska #include "lafe_platform.h"
41*185becb1SMartin Matuska
42*185becb1SMartin Matuska #ifndef HAVE_FNMATCH
43*185becb1SMartin Matuska /*
44*185becb1SMartin Matuska * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
45*185becb1SMartin Matuska * Compares a filename or pathname to a pattern.
46*185becb1SMartin Matuska */
47*185becb1SMartin Matuska
48*185becb1SMartin Matuska /*
49*185becb1SMartin Matuska * Some notes on multibyte character support:
50*185becb1SMartin Matuska * 1. Patterns with illegal byte sequences match nothing.
51*185becb1SMartin Matuska * 2. Illegal byte sequences in the "string" argument are handled by treating
52*185becb1SMartin Matuska * them as single-byte characters with a value of the first byte of the
53*185becb1SMartin Matuska * sequence cast to wchar_t.
54*185becb1SMartin Matuska * 3. Multibyte conversion state objects (mbstate_t) are passed around and
55*185becb1SMartin Matuska * used for most, but not all, conversions. Further work will be required
56*185becb1SMartin Matuska * to support state-dependent encodings.
57*185becb1SMartin Matuska */
58*185becb1SMartin Matuska
59*185becb1SMartin Matuska #ifdef HAVE_LIMITS_H
60*185becb1SMartin Matuska #include <limits.h>
61*185becb1SMartin Matuska #endif
62*185becb1SMartin Matuska #ifdef HAVE_STRING_H
63*185becb1SMartin Matuska #include <string.h>
64*185becb1SMartin Matuska #endif
65*185becb1SMartin Matuska #ifdef HAVE_WCHAR_H
66*185becb1SMartin Matuska #include <wchar.h>
67*185becb1SMartin Matuska #endif
68*185becb1SMartin Matuska #ifdef HAVE_WCTYPE_H
69*185becb1SMartin Matuska #include <wctype.h>
70*185becb1SMartin Matuska #endif
71*185becb1SMartin Matuska
72*185becb1SMartin Matuska #include "lafe_fnmatch.h"
73*185becb1SMartin Matuska
74*185becb1SMartin Matuska #define EOS '\0'
75*185becb1SMartin Matuska
76*185becb1SMartin Matuska #define RANGE_MATCH 1
77*185becb1SMartin Matuska #define RANGE_NOMATCH 0
78*185becb1SMartin Matuska #define RANGE_ERROR (-1)
79*185becb1SMartin Matuska
80*185becb1SMartin Matuska static int rangematch(const char *, wchar_t, int, const char **, mbstate_t *);
81*185becb1SMartin Matuska static int fnmatch1(const char *, const char *, const char *, int, mbstate_t,
82*185becb1SMartin Matuska mbstate_t);
83*185becb1SMartin Matuska
84*185becb1SMartin Matuska int
fnmatch(const char * pattern,const char * string,int flags)85*185becb1SMartin Matuska fnmatch(const char *pattern, const char *string, int flags)
86*185becb1SMartin Matuska {
87*185becb1SMartin Matuska static const mbstate_t initial;
88*185becb1SMartin Matuska
89*185becb1SMartin Matuska return (fnmatch1(pattern, string, string, flags, initial, initial));
90*185becb1SMartin Matuska }
91*185becb1SMartin Matuska
92*185becb1SMartin Matuska static int
fnmatch1(const char * pattern,const char * string,const char * stringstart,int flags,mbstate_t patmbs,mbstate_t strmbs)93*185becb1SMartin Matuska fnmatch1(const char *pattern, const char *string, const char *stringstart,
94*185becb1SMartin Matuska int flags, mbstate_t patmbs, mbstate_t strmbs)
95*185becb1SMartin Matuska {
96*185becb1SMartin Matuska const char *bt_pattern, *bt_string;
97*185becb1SMartin Matuska mbstate_t bt_patmbs, bt_strmbs;
98*185becb1SMartin Matuska const char *newp;
99*185becb1SMartin Matuska char c;
100*185becb1SMartin Matuska wchar_t pc, sc;
101*185becb1SMartin Matuska size_t pclen, sclen;
102*185becb1SMartin Matuska
103*185becb1SMartin Matuska bt_pattern = bt_string = NULL;
104*185becb1SMartin Matuska for (;;) {
105*185becb1SMartin Matuska pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs);
106*185becb1SMartin Matuska if (pclen == (size_t)-1 || pclen == (size_t)-2)
107*185becb1SMartin Matuska return (FNM_NOMATCH);
108*185becb1SMartin Matuska pattern += pclen;
109*185becb1SMartin Matuska sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs);
110*185becb1SMartin Matuska if (sclen == (size_t)-1 || sclen == (size_t)-2) {
111*185becb1SMartin Matuska sc = (unsigned char)*string;
112*185becb1SMartin Matuska sclen = 1;
113*185becb1SMartin Matuska memset(&strmbs, 0, sizeof(strmbs));
114*185becb1SMartin Matuska }
115*185becb1SMartin Matuska switch (pc) {
116*185becb1SMartin Matuska case EOS:
117*185becb1SMartin Matuska if ((flags & FNM_LEADING_DIR) && sc == '/')
118*185becb1SMartin Matuska return (0);
119*185becb1SMartin Matuska if (sc == EOS)
120*185becb1SMartin Matuska return (0);
121*185becb1SMartin Matuska goto backtrack;
122*185becb1SMartin Matuska case '?':
123*185becb1SMartin Matuska if (sc == EOS)
124*185becb1SMartin Matuska return (FNM_NOMATCH);
125*185becb1SMartin Matuska if (sc == '/' && (flags & FNM_PATHNAME))
126*185becb1SMartin Matuska goto backtrack;
127*185becb1SMartin Matuska if (sc == '.' && (flags & FNM_PERIOD) &&
128*185becb1SMartin Matuska (string == stringstart ||
129*185becb1SMartin Matuska ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
130*185becb1SMartin Matuska goto backtrack;
131*185becb1SMartin Matuska string += sclen;
132*185becb1SMartin Matuska break;
133*185becb1SMartin Matuska case '*':
134*185becb1SMartin Matuska c = *pattern;
135*185becb1SMartin Matuska /* Collapse multiple stars. */
136*185becb1SMartin Matuska while (c == '*')
137*185becb1SMartin Matuska c = *++pattern;
138*185becb1SMartin Matuska
139*185becb1SMartin Matuska if (sc == '.' && (flags & FNM_PERIOD) &&
140*185becb1SMartin Matuska (string == stringstart ||
141*185becb1SMartin Matuska ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
142*185becb1SMartin Matuska goto backtrack;
143*185becb1SMartin Matuska
144*185becb1SMartin Matuska /* Optimize for pattern with * at end or before /. */
145*185becb1SMartin Matuska if (c == EOS)
146*185becb1SMartin Matuska if (flags & FNM_PATHNAME)
147*185becb1SMartin Matuska return ((flags & FNM_LEADING_DIR) ||
148*185becb1SMartin Matuska strchr(string, '/') == NULL ?
149*185becb1SMartin Matuska 0 : FNM_NOMATCH);
150*185becb1SMartin Matuska else
151*185becb1SMartin Matuska return (0);
152*185becb1SMartin Matuska else if (c == '/' && flags & FNM_PATHNAME) {
153*185becb1SMartin Matuska if ((string = strchr(string, '/')) == NULL)
154*185becb1SMartin Matuska return (FNM_NOMATCH);
155*185becb1SMartin Matuska break;
156*185becb1SMartin Matuska }
157*185becb1SMartin Matuska
158*185becb1SMartin Matuska /*
159*185becb1SMartin Matuska * First try the shortest match for the '*' that
160*185becb1SMartin Matuska * could work. We can forget any earlier '*' since
161*185becb1SMartin Matuska * there is no way having it match more characters
162*185becb1SMartin Matuska * can help us, given that we are already here.
163*185becb1SMartin Matuska */
164*185becb1SMartin Matuska bt_pattern = pattern, bt_patmbs = patmbs;
165*185becb1SMartin Matuska bt_string = string, bt_strmbs = strmbs;
166*185becb1SMartin Matuska break;
167*185becb1SMartin Matuska case '[':
168*185becb1SMartin Matuska if (sc == EOS)
169*185becb1SMartin Matuska return (FNM_NOMATCH);
170*185becb1SMartin Matuska if (sc == '/' && (flags & FNM_PATHNAME))
171*185becb1SMartin Matuska goto backtrack;
172*185becb1SMartin Matuska if (sc == '.' && (flags & FNM_PERIOD) &&
173*185becb1SMartin Matuska (string == stringstart ||
174*185becb1SMartin Matuska ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
175*185becb1SMartin Matuska goto backtrack;
176*185becb1SMartin Matuska
177*185becb1SMartin Matuska switch (rangematch(pattern, sc, flags, &newp,
178*185becb1SMartin Matuska &patmbs)) {
179*185becb1SMartin Matuska case RANGE_ERROR:
180*185becb1SMartin Matuska goto norm;
181*185becb1SMartin Matuska case RANGE_MATCH:
182*185becb1SMartin Matuska pattern = newp;
183*185becb1SMartin Matuska break;
184*185becb1SMartin Matuska case RANGE_NOMATCH:
185*185becb1SMartin Matuska goto backtrack;
186*185becb1SMartin Matuska }
187*185becb1SMartin Matuska string += sclen;
188*185becb1SMartin Matuska break;
189*185becb1SMartin Matuska case '\\':
190*185becb1SMartin Matuska if (!(flags & FNM_NOESCAPE)) {
191*185becb1SMartin Matuska pclen = mbrtowc(&pc, pattern, MB_LEN_MAX,
192*185becb1SMartin Matuska &patmbs);
193*185becb1SMartin Matuska if (pclen == 0 || pclen == (size_t)-1 ||
194*185becb1SMartin Matuska pclen == (size_t)-2)
195*185becb1SMartin Matuska return (FNM_NOMATCH);
196*185becb1SMartin Matuska pattern += pclen;
197*185becb1SMartin Matuska }
198*185becb1SMartin Matuska /* FALLTHROUGH */
199*185becb1SMartin Matuska default:
200*185becb1SMartin Matuska norm:
201*185becb1SMartin Matuska string += sclen;
202*185becb1SMartin Matuska if (pc == sc)
203*185becb1SMartin Matuska ;
204*185becb1SMartin Matuska else if ((flags & FNM_CASEFOLD) &&
205*185becb1SMartin Matuska (towlower(pc) == towlower(sc)))
206*185becb1SMartin Matuska ;
207*185becb1SMartin Matuska else {
208*185becb1SMartin Matuska backtrack:
209*185becb1SMartin Matuska /*
210*185becb1SMartin Matuska * If we have a mismatch (other than hitting
211*185becb1SMartin Matuska * the end of the string), go back to the last
212*185becb1SMartin Matuska * '*' seen and have it match one additional
213*185becb1SMartin Matuska * character.
214*185becb1SMartin Matuska */
215*185becb1SMartin Matuska if (bt_pattern == NULL)
216*185becb1SMartin Matuska return (FNM_NOMATCH);
217*185becb1SMartin Matuska sclen = mbrtowc(&sc, bt_string, MB_LEN_MAX,
218*185becb1SMartin Matuska &bt_strmbs);
219*185becb1SMartin Matuska if (sclen == (size_t)-1 ||
220*185becb1SMartin Matuska sclen == (size_t)-2) {
221*185becb1SMartin Matuska sc = (unsigned char)*bt_string;
222*185becb1SMartin Matuska sclen = 1;
223*185becb1SMartin Matuska memset(&bt_strmbs, 0,
224*185becb1SMartin Matuska sizeof(bt_strmbs));
225*185becb1SMartin Matuska }
226*185becb1SMartin Matuska if (sc == EOS)
227*185becb1SMartin Matuska return (FNM_NOMATCH);
228*185becb1SMartin Matuska if (sc == '/' && flags & FNM_PATHNAME)
229*185becb1SMartin Matuska return (FNM_NOMATCH);
230*185becb1SMartin Matuska bt_string += sclen;
231*185becb1SMartin Matuska pattern = bt_pattern, patmbs = bt_patmbs;
232*185becb1SMartin Matuska string = bt_string, strmbs = bt_strmbs;
233*185becb1SMartin Matuska }
234*185becb1SMartin Matuska break;
235*185becb1SMartin Matuska }
236*185becb1SMartin Matuska }
237*185becb1SMartin Matuska /* NOTREACHED */
238*185becb1SMartin Matuska }
239*185becb1SMartin Matuska
240*185becb1SMartin Matuska static int
rangematch(const char * pattern,wchar_t test,int flags,const char ** newp,mbstate_t * patmbs)241*185becb1SMartin Matuska rangematch(const char *pattern, wchar_t test, int flags, const char **newp,
242*185becb1SMartin Matuska mbstate_t *patmbs)
243*185becb1SMartin Matuska {
244*185becb1SMartin Matuska int negate, ok;
245*185becb1SMartin Matuska wchar_t s1[2], s2[2], t[2];
246*185becb1SMartin Matuska size_t pclen;
247*185becb1SMartin Matuska const char *origpat;
248*185becb1SMartin Matuska
249*185becb1SMartin Matuska s1[1] = L'\0';
250*185becb1SMartin Matuska s2[1] = L'\0';
251*185becb1SMartin Matuska
252*185becb1SMartin Matuska t[0] = test;
253*185becb1SMartin Matuska t[1] = L'\0';
254*185becb1SMartin Matuska
255*185becb1SMartin Matuska /*
256*185becb1SMartin Matuska * A bracket expression starting with an unquoted circumflex
257*185becb1SMartin Matuska * character produces unspecified results (IEEE 1003.2-1992,
258*185becb1SMartin Matuska * 3.13.2). This implementation treats it like '!', for
259*185becb1SMartin Matuska * consistency with the regular expression syntax.
260*185becb1SMartin Matuska * J.T. Conklin (conklin@ngai.kaleida.com)
261*185becb1SMartin Matuska */
262*185becb1SMartin Matuska if ((negate = (*pattern == '!' || *pattern == '^')))
263*185becb1SMartin Matuska ++pattern;
264*185becb1SMartin Matuska
265*185becb1SMartin Matuska if (flags & FNM_CASEFOLD)
266*185becb1SMartin Matuska t[0] = towlower(t[0]);
267*185becb1SMartin Matuska
268*185becb1SMartin Matuska /*
269*185becb1SMartin Matuska * A right bracket shall lose its special meaning and represent
270*185becb1SMartin Matuska * itself in a bracket expression if it occurs first in the list.
271*185becb1SMartin Matuska * -- POSIX.2 2.8.3.2
272*185becb1SMartin Matuska */
273*185becb1SMartin Matuska ok = 0;
274*185becb1SMartin Matuska origpat = pattern;
275*185becb1SMartin Matuska for (;;) {
276*185becb1SMartin Matuska if (*pattern == ']' && pattern > origpat) {
277*185becb1SMartin Matuska pattern++;
278*185becb1SMartin Matuska break;
279*185becb1SMartin Matuska } else if (*pattern == '\0') {
280*185becb1SMartin Matuska return (RANGE_ERROR);
281*185becb1SMartin Matuska } else if (*pattern == '/' && (flags & FNM_PATHNAME)) {
282*185becb1SMartin Matuska return (RANGE_NOMATCH);
283*185becb1SMartin Matuska } else if (*pattern == '\\' && !(flags & FNM_NOESCAPE))
284*185becb1SMartin Matuska pattern++;
285*185becb1SMartin Matuska pclen = mbrtowc(s1, pattern, MB_LEN_MAX, patmbs);
286*185becb1SMartin Matuska if (pclen == (size_t)-1 || pclen == (size_t)-2)
287*185becb1SMartin Matuska return (RANGE_NOMATCH);
288*185becb1SMartin Matuska pattern += pclen;
289*185becb1SMartin Matuska
290*185becb1SMartin Matuska if (flags & FNM_CASEFOLD)
291*185becb1SMartin Matuska s1[0] = towlower(s1[0]);
292*185becb1SMartin Matuska
293*185becb1SMartin Matuska if (*pattern == '-' && *(pattern + 1) != EOS &&
294*185becb1SMartin Matuska *(pattern + 1) != ']') {
295*185becb1SMartin Matuska if (*++pattern == '\\' && !(flags & FNM_NOESCAPE))
296*185becb1SMartin Matuska if (*pattern != EOS)
297*185becb1SMartin Matuska pattern++;
298*185becb1SMartin Matuska pclen = mbrtowc(s2, pattern, MB_LEN_MAX, patmbs);
299*185becb1SMartin Matuska if (pclen == (size_t)-1 || pclen == (size_t)-2)
300*185becb1SMartin Matuska return (RANGE_NOMATCH);
301*185becb1SMartin Matuska pattern += pclen;
302*185becb1SMartin Matuska if (s2[0] == EOS)
303*185becb1SMartin Matuska return (RANGE_ERROR);
304*185becb1SMartin Matuska
305*185becb1SMartin Matuska if (flags & FNM_CASEFOLD)
306*185becb1SMartin Matuska s2[0] = towlower(s2[0]);
307*185becb1SMartin Matuska
308*185becb1SMartin Matuska if (wcscoll(s1, t) && wcscoll(t, s2) <= 0)
309*185becb1SMartin Matuska ok = 1;
310*185becb1SMartin Matuska } else if (s1[0] == t[0])
311*185becb1SMartin Matuska ok = 1;
312*185becb1SMartin Matuska }
313*185becb1SMartin Matuska
314*185becb1SMartin Matuska *newp = pattern;
315*185becb1SMartin Matuska return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
316*185becb1SMartin Matuska }
317*185becb1SMartin Matuska #endif
318