2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #if defined(LIBC_SCCS) && !defined(lint)
34 static char sccsid
[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
35 #endif /* LIBC_SCCS and not lint */
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: src/lib/libc/gen/fnmatch.c,v 1.19 2010/04/16 22:29:24 jilles Exp $");
39 #include "xlocale_private.h"
42 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
43 * Compares a filename or pathname to a pattern.
47 * Some notes on multibyte character support:
48 * 1. Patterns with illegal byte sequences match nothing.
49 * 2. Illegal byte sequences in the "string" argument are handled by treating
50 * them as single-byte characters with a value of the first byte of the
51 * sequence cast to wchar_t.
52 * 3. Multibyte conversion state objects (mbstate_t) are passed around and
53 * used for most, but not all, conversions. Further work will be required
54 * to support state-dependent encodings.
67 #define RETURN_ERROR 2 /* neither 0 or FNM_NOMATCH */
69 #define RANGE_NOMATCH 0
70 #define RANGE_ERROR (-1)
72 #define RECURSION_MAX 64
74 __private_extern__
int rangematch(const char *, wchar_t, const char *, int, char **, char **, mbstate_t *, mbstate_t *, locale_t
);
75 static int fnmatch1(const char *, const char *, const char *, int, mbstate_t,
76 mbstate_t, locale_t
, int);
79 fnmatch(pattern
, string
, flags
)
80 const char *pattern
, *string
;
83 static const mbstate_t initial
;
85 return (fnmatch1(pattern
, string
, string
, flags
, initial
, initial
, __current_locale(), RECURSION_MAX
));
86 #else /* !__DARWIN_UNIX03 */
87 return (fnmatch1(pattern
, string
, string
, flags
, initial
, initial
, __current_locale(), RECURSION_MAX
) != 0 ? FNM_NOMATCH
: 0);
88 #endif /* __DARWIN_UNIX03 */
92 fnmatch1(pattern
, string
, stringstart
, flags
, patmbs
, strmbs
, loc
, recursion
)
93 const char *pattern
, *string
, *stringstart
;
95 mbstate_t patmbs
, strmbs
;
104 if (recursion
-- <= 0)
107 pclen
= mbrtowc_l(&pc
, pattern
, MB_LEN_MAX
, &patmbs
, loc
);
108 if (pclen
== (size_t)-1 || pclen
== (size_t)-2)
110 return (RETURN_ERROR
);
111 #else /* !__DARWIN_UNIX03 */
112 return (FNM_NOMATCH
);
113 #endif /* __DARWIN_UNIX03 */
115 sclen
= mbrtowc_l(&sc
, string
, MB_LEN_MAX
, &strmbs
, loc
);
116 if (sclen
== (size_t)-1 || sclen
== (size_t)-2) {
117 sc
= (unsigned char)*string
;
119 memset(&strmbs
, 0, sizeof(strmbs
));
123 if ((flags
& FNM_LEADING_DIR
) && sc
== '/')
125 return (sc
== EOS
? 0 : FNM_NOMATCH
);
128 return (FNM_NOMATCH
);
129 if (sc
== '/' && (flags
& FNM_PATHNAME
))
130 return (FNM_NOMATCH
);
131 if (sc
== '.' && (flags
& FNM_PERIOD
) &&
132 (string
== stringstart
||
133 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
134 return (FNM_NOMATCH
);
139 /* Collapse multiple stars. */
143 if (sc
== '.' && (flags
& FNM_PERIOD
) &&
144 (string
== stringstart
||
145 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
146 return (FNM_NOMATCH
);
148 /* Optimize for pattern with * at end or before /. */
150 if (flags
& FNM_PATHNAME
)
151 return ((flags
& FNM_LEADING_DIR
) ||
152 strchr(string
, '/') == NULL
?
156 else if (c
== '/' && flags
& FNM_PATHNAME
) {
157 if ((string
= strchr(string
, '/')) == NULL
)
158 return (FNM_NOMATCH
);
162 /* General case, use recursion. */
165 if ((ret
= fnmatch1(pattern
, string
, stringstart
,
166 flags
, patmbs
, strmbs
, loc
, recursion
)) != FNM_NOMATCH
)
168 sclen
= mbrtowc_l(&sc
, string
, MB_LEN_MAX
,
170 if (sclen
== (size_t)-1 ||
171 sclen
== (size_t)-2) {
172 sc
= (unsigned char)*string
;
174 memset(&strmbs
, 0, sizeof(strmbs
));
176 if (sc
== '/' && flags
& FNM_PATHNAME
)
180 return (FNM_NOMATCH
);
183 return (FNM_NOMATCH
);
184 if (sc
== '/' && (flags
& FNM_PATHNAME
))
185 return (FNM_NOMATCH
);
186 if (sc
== '.' && (flags
& FNM_PERIOD
) &&
187 (string
== stringstart
||
188 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
189 return (FNM_NOMATCH
);
191 switch (rangematch(pattern
, sc
, string
+ sclen
, flags
,
192 &newp
, &news
, &patmbs
, &strmbs
, loc
)) {
195 return (RETURN_ERROR
);
196 #else /* !__DARWIN_UNIX03 */
198 #endif /* __DARWIN_UNIX03 */
204 return (FNM_NOMATCH
);
208 if (!(flags
& FNM_NOESCAPE
)) {
209 pclen
= mbrtowc_l(&pc
, pattern
, MB_LEN_MAX
,
211 if (pclen
== (size_t)-1 || pclen
== (size_t)-2)
213 return (RETURN_ERROR
);
214 #else /* !__DARWIN_UNIX03 */
215 return (FNM_NOMATCH
);
216 #endif /* __DARWIN_UNIX03 */
225 #endif /* !__DARWIN_UNIX03 */
228 else if ((flags
& FNM_CASEFOLD
) &&
229 (towlower_l(pc
, loc
) == towlower_l(sc
, loc
)))
232 return (FNM_NOMATCH
);
240 #ifndef BUILDING_VARIANT
241 __private_extern__
int
242 rangematch(pattern
, test
, string
, flags
, newp
, news
, patmbs
, strmbs
, loc
)
243 const char *pattern
, *string
;
247 mbstate_t *patmbs
, *strmbs
;
250 int negate
, ok
, special
;
252 wchar_t buf
[STR_LEN
]; /* STR_LEN defined in collate.h */
253 size_t pclen
, sclen
, len
;
254 const char *origpat
, *cp
, *savestring
;
258 * A bracket expression starting with an unquoted circumflex
259 * character produces unspecified results (IEEE 1003.2-1992,
260 * 3.13.2). This implementation treats it like '!', for
261 * consistency with the regular expression syntax.
262 * J.T. Conklin (conklin@ngai.kaleida.com)
264 if ( (negate
= (*pattern
== '!' || *pattern
== '^')) )
267 if (flags
& FNM_CASEFOLD
)
268 test
= towlower_l(test
, loc
);
271 * A right bracket shall lose its special meaning and represent
272 * itself in a bracket expression if it occurs first in the list.
279 if (*pattern
== ']' && pattern
> origpat
) {
281 } else if (*pattern
== '\0') {
282 return (RANGE_ERROR
);
283 } else if (*pattern
== '/' && (flags
& FNM_PATHNAME
)) {
284 return (RANGE_NOMATCH
);
285 } else if (*pattern
== '\\' && !(flags
& FNM_NOESCAPE
))
287 else if (*pattern
== '[' && ((special
= *(pattern
+ 1)) == '.' || special
== '=' || special
== ':')) {
289 while(cp
= strchr(cp
, special
)) {
290 if (*(cp
+ 1) == ']')
295 return (RANGE_ERROR
);
296 if (special
== '.') {
297 treat_like_collating_symbol
:
298 len
= __collate_collating_symbol(buf
, STR_LEN
, pattern
, cp
- pattern
, patmbs
, loc
);
299 if (len
== (size_t)-1 || len
== 0)
300 return (RANGE_ERROR
);
304 /* no multi-character collation symbols as start of range */
305 if (*(cp
+ 2) == '-' && *(cp
+ 3) != EOS
307 return (RANGE_ERROR
);
315 memcpy(&save
, strmbs
, sizeof(save
));
318 sclen
= mbrtowc_l(&sc
, string
, MB_LEN_MAX
, strmbs
, loc
);
319 if (sclen
== (size_t)-1 || sclen
== (size_t)-2) {
320 sc
= (unsigned char)*string
;
322 memset(&strmbs
, 0, sizeof(strmbs
));
325 memcpy(strmbs
, &save
, sizeof(save
));
335 continue; /* no match */
338 } else if (special
== '=') {
340 memcpy(&save
, patmbs
, sizeof(save
));
341 ec
= __collate_equiv_class(pattern
, cp
- pattern
, patmbs
, loc
);
343 return (RANGE_ERROR
);
345 memcpy(patmbs
, &save
, sizeof(save
));
346 goto treat_like_collating_symbol
;
349 /* no equivalence classes as start of range */
350 if (*(cp
+ 2) == '-' && *(cp
+ 3) != EOS
&&
352 return (RANGE_ERROR
);
353 len
= __collate_equiv_match(ec
, NULL
, 0, test
, string
, strlen(string
), strmbs
, &sclen
, loc
);
355 return (RANGE_ERROR
);
362 } else { /* special == ':' */
364 char name
[CHARCLASS_NAME_MAX
+ 1];
365 /* no character classes as start of range */
366 if (*(cp
+ 2) == '-' && *(cp
+ 3) != EOS
&&
368 return (RANGE_ERROR
);
369 /* assume character class names are ascii */
370 if (cp
- pattern
> CHARCLASS_NAME_MAX
)
371 return (RANGE_ERROR
);
372 strlcpy(name
, pattern
, cp
- pattern
+ 1);
374 if ((charclass
= wctype(name
)) == 0)
375 return (RANGE_ERROR
);
376 if (iswctype_l(test
, charclass
, loc
)) {
384 pclen
= mbrtowc_l(&c
, pattern
, MB_LEN_MAX
, patmbs
, loc
);
385 if (pclen
== (size_t)-1 || pclen
== (size_t)-2)
386 return (RANGE_ERROR
);
390 if (flags
& FNM_CASEFOLD
)
391 c
= towlower_l(c
, loc
);
393 if (*pattern
== '-' && *(pattern
+ 1) != EOS
&&
394 *(pattern
+ 1) != ']') {
395 if (*++pattern
== '\\' && !(flags
& FNM_NOESCAPE
))
398 pclen
= mbrtowc_l(&c2
, pattern
, MB_LEN_MAX
, patmbs
, loc
);
399 if (pclen
== (size_t)-1 || pclen
== (size_t)-2)
400 return (RANGE_ERROR
);
403 return (RANGE_ERROR
);
405 if (c2
== '[' && (special
= *pattern
) == '.' || special
== '=' || special
== ':') {
406 /* no equivalence classes or character classes as end of range */
407 if (special
== '=' || special
== ':')
408 return (RANGE_ERROR
);
410 while(cp
= strchr(cp
, special
)) {
411 if (*(cp
+ 1) == ']')
416 return (RANGE_ERROR
);
417 len
= __collate_collating_symbol(buf
, STR_LEN
, pattern
, cp
- pattern
, patmbs
, loc
);
418 /* no multi-character collation symbols as end of range */
420 return (RANGE_ERROR
);
425 if (flags
& FNM_CASEFOLD
)
426 c2
= towlower_l(c2
, loc
);
428 if (loc
->__collate_load_error
?
429 c
<= test
&& test
<= c2
:
430 __collate_range_cmp(c
, test
, loc
) <= 0
431 && __collate_range_cmp(test
, c2
, loc
) <= 0
436 } else if (c
== test
) {
441 /* go to end of bracket expression */
443 while(*pattern
!= ']') {
445 return (RANGE_ERROR
);
446 if (*pattern
== special
) {
447 if (*++pattern
== ']') {
453 if (!special
&& *pattern
== '[') {
454 special
= *++pattern
;
455 if (special
!= '.' && special
!= '=' && special
!= ':')
461 pclen
= mbrtowc_l(&c
, pattern
, MB_LEN_MAX
, patmbs
, loc
);
462 if (pclen
== (size_t)-1 || pclen
== (size_t)-2)
463 return (RANGE_ERROR
);
467 *newp
= (char *)++pattern
;
468 *news
= (char *)string
;
469 return (ok
== negate
? RANGE_NOMATCH
: RANGE_MATCH
);
471 #endif /* BUILDING_VARIANT */