1 /* $OpenBSD: fnmatch.c,v 1.14 2008/10/01 23:04:13 millert Exp $ */
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
37 * Compares a filename or pathname to a pattern.
45 #include "charclass.h"
50 #define RANGE_NOMATCH 0
51 #define RANGE_ERROR (-1)
53 static int rangematch(const char *, char, int, char **);
54 static int classmatch(const char *, char, int, const char **);
57 fnmatch(const char *pattern
, const char *string
, int flags
)
59 const char *stringstart
;
63 for (stringstart
= string
;;)
64 switch (c
= *pattern
++) {
66 if ((flags
& FNM_LEADING_DIR
) && *string
== '/')
68 return (*string
== EOS
? 0 : FNM_NOMATCH
);
72 if (*string
== '/' && (flags
& FNM_PATHNAME
))
74 if (*string
== '.' && (flags
& FNM_PERIOD
) &&
75 (string
== stringstart
||
76 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
82 /* Collapse multiple stars. */
86 if (*string
== '.' && (flags
& FNM_PERIOD
) &&
87 (string
== stringstart
||
88 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
91 /* Optimize for pattern with * at end or before /. */
93 if (flags
& FNM_PATHNAME
)
94 return ((flags
& FNM_LEADING_DIR
) ||
95 strchr(string
, '/') == NULL
?
99 } else if (c
== '/' && (flags
& FNM_PATHNAME
)) {
100 if ((string
= strchr(string
, '/')) == NULL
)
101 return (FNM_NOMATCH
);
105 /* General case, use recursion. */
106 while ((test
= *string
) != EOS
) {
107 if (!fnmatch(pattern
, string
, flags
& ~FNM_PERIOD
))
109 if (test
== '/' && (flags
& FNM_PATHNAME
))
113 return (FNM_NOMATCH
);
116 return (FNM_NOMATCH
);
117 if (*string
== '/' && (flags
& FNM_PATHNAME
))
118 return (FNM_NOMATCH
);
119 if (*string
== '.' && (flags
& FNM_PERIOD
) &&
120 (string
== stringstart
||
121 ((flags
& FNM_PATHNAME
) && *(string
- 1) == '/')))
122 return (FNM_NOMATCH
);
124 switch (rangematch(pattern
, *string
, flags
, &newp
)) {
126 /* not a good range, treat as normal text */
132 return (FNM_NOMATCH
);
137 if (!(flags
& FNM_NOESCAPE
)) {
138 if ((c
= *pattern
++) == EOS
) {
146 if (c
!= *string
&& !((flags
& FNM_CASEFOLD
) &&
147 (tolower((unsigned char)c
) ==
148 tolower((unsigned char)*string
))))
149 return (FNM_NOMATCH
);
157 rangematch(const char *pattern
, char test
, int flags
, char **newp
)
163 * A bracket expression starting with an unquoted circumflex
164 * character produces unspecified results (IEEE 1003.2-1992,
165 * 3.13.2). This implementation treats it like '!', for
166 * consistency with the regular expression syntax.
167 * J.T. Conklin (conklin@ngai.kaleida.com)
169 if ((negate
= (*pattern
== '!' || *pattern
== '^')))
172 if (flags
& FNM_CASEFOLD
)
173 test
= (char)tolower((unsigned char)test
);
176 * A right bracket shall lose its special meaning and represent
177 * itself in a bracket expression if it occurs first in the list.
183 if (c
== '[' && *pattern
== ':') {
185 rv
= classmatch(pattern
+ 1, test
,
186 (flags
& FNM_CASEFOLD
), &pattern
);
187 if (rv
== RANGE_MATCH
)
190 } while (rv
!= RANGE_ERROR
&& c
== '[' && *pattern
== ':');
194 if (c
== '\\' && !(flags
& FNM_NOESCAPE
))
197 return (RANGE_ERROR
);
198 if (c
== '/' && (flags
& FNM_PATHNAME
))
199 return (RANGE_NOMATCH
);
200 if ((flags
& FNM_CASEFOLD
))
201 c
= (char)tolower((unsigned char)c
);
203 && (c2
= *(pattern
+1)) != EOS
&& c2
!= ']') {
205 if (c2
== '\\' && !(flags
& FNM_NOESCAPE
))
208 return (RANGE_ERROR
);
209 if (flags
& FNM_CASEFOLD
)
210 c2
= (char)tolower((unsigned char)c2
);
211 if (c
<= test
&& test
<= c2
)
213 } else if (c
== test
)
215 } while ((c
= *pattern
++) != ']');
217 *newp
= (char *)pattern
;
218 return (ok
== negate
? RANGE_NOMATCH
: RANGE_MATCH
);
222 classmatch(const char *pattern
, char test
, int foldcase
, const char **ep
)
227 int rval
= RANGE_NOMATCH
;
229 if ((colon
= strchr(pattern
, ':')) == NULL
|| colon
[1] != ']') {
234 len
= (size_t)(colon
- pattern
);
236 if (foldcase
&& strncmp(pattern
, "upper:]", 7) == 0)
238 for (cc
= cclasses
; cc
->name
!= NULL
; cc
++) {
239 if (!strncmp(pattern
, cc
->name
, len
) && cc
->name
[len
] == '\0') {
240 if (cc
->isctype((unsigned char)test
))
245 if (cc
->name
== NULL
) {
246 /* invalid character class, return EOS */
247 *ep
= colon
+ strlen(colon
);