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 "fnmatch.h"
44 #include <string.h>
45
46 #define EOS '\0'
47
foldcase(int ch,int flags)48 static inline int foldcase(int ch, int flags)
49 {
50
51 if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
52 return tolower(ch);
53 return ch;
54 }
55
56 #define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags))
57
rangematch(const char * pattern,int test,int flags)58 static const char * rangematch(const char *pattern, int test, int flags)
59 {
60 int negate, ok, need;
61 char c, c2;
62
63 if (pattern == NULL)
64 {
65 return NULL;
66 }
67
68 /*
69 * A bracket expression starting with an unquoted circumflex
70 * character produces unspecified results (IEEE 1003.2-1992,
71 * 3.13.2). This implementation treats it like '!', for
72 * consistency with the regular expression syntax.
73 * J.T. Conklin (conklin@ngai.kaleida.com)
74 */
75 if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
76 ++pattern;
77
78 need = 1;
79 for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) {
80 need = 0;
81 if (c == '/')
82 return (void *)-1;
83 if (c == '\\' && !(flags & FNM_NOESCAPE))
84 c = FOLDCASE(*pattern++, flags);
85 if (c == EOS)
86 return NULL;
87 if (*pattern == '-'
88 && (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS &&
89 c2 != ']') {
90 pattern += 2;
91 if (c2 == '\\' && !(flags & FNM_NOESCAPE))
92 c2 = FOLDCASE(*pattern++, flags);
93 if (c2 == EOS)
94 return NULL;
95 if (c <= test && test <= c2)
96 ok = 1;
97 } else if (c == test)
98 ok = 1;
99 }
100 return ok == negate ? NULL : pattern;
101 }
102
103
fnmatchx(const char * pattern,const char * string,int flags,size_t recursion)104 static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
105 {
106 const char *stringstart, *r;
107 char c, test;
108
109 if ((pattern == NULL) || (string == NULL))
110 {
111 return FNM_NOMATCH;
112 }
113
114 if (recursion-- == 0)
115 return FNM_NORES;
116
117 for (stringstart = string;;) {
118 switch (c = FOLDCASE(*pattern++, flags)) {
119 case EOS:
120 if ((flags & FNM_LEADING_DIR) && *string == '/')
121 return 0;
122 return *string == EOS ? 0 : FNM_NOMATCH;
123 case '?':
124 if (*string == EOS)
125 return FNM_NOMATCH;
126 if (*string == '/' && (flags & FNM_PATHNAME))
127 return FNM_NOMATCH;
128 if (*string == '.' && (flags & FNM_PERIOD) &&
129 (string == stringstart ||
130 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
131 return FNM_NOMATCH;
132 ++string;
133 break;
134 case '*':
135 c = FOLDCASE(*pattern, flags);
136 /* Collapse multiple stars. */
137 while (c == '*')
138 c = FOLDCASE(*++pattern, flags);
139
140 if (*string == '.' && (flags & FNM_PERIOD) &&
141 (string == stringstart ||
142 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
143 return FNM_NOMATCH;
144
145 /* Optimize for pattern with * at end or before /. */
146 if (c == EOS) {
147 if (flags & FNM_PATHNAME)
148 return (flags & FNM_LEADING_DIR) ||
149 strchr(string, '/') == NULL ?
150 0 : FNM_NOMATCH;
151 else
152 return 0;
153 } else if (c == '/' && flags & FNM_PATHNAME) {
154 if ((string = strchr(string, '/')) == NULL)
155 return FNM_NOMATCH;
156 break;
157 }
158
159 /* General case, use recursion. */
160 while ((test = FOLDCASE(*string, flags)) != EOS) {
161 int e;
162 switch ((e = fnmatchx(pattern, string,
163 flags & ~FNM_PERIOD, recursion))) {
164 case FNM_NOMATCH:
165 break;
166 default:
167 return e;
168 }
169 if (test == '/' && flags & FNM_PATHNAME)
170 break;
171 ++string;
172 }
173 return FNM_NOMATCH;
174 case '[':
175 if (*string == EOS)
176 return FNM_NOMATCH;
177 if (*string == '/' && flags & FNM_PATHNAME)
178 return FNM_NOMATCH;
179 if ((r = rangematch(pattern,
180 FOLDCASE(*string, flags), flags)) == NULL)
181 return FNM_NOMATCH;
182 if (r == (void *)-1) {
183 if (*string != '[')
184 return FNM_NOMATCH;
185 } else
186 pattern = r;
187 ++string;
188 break;
189 case '\\':
190 if (!(flags & FNM_NOESCAPE)) {
191 if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
192 c = '\0';
193 --pattern;
194 }
195 }
196 /* FALLTHROUGH */
197 default:
198 if (c != FOLDCASE(*string++, flags))
199 return FNM_NOMATCH;
200 break;
201 }
202 }
203 /* NOTREACHED */
204 }
205
fnmatch(const char * pattern,const char * string,int flags)206 int fnmatch(const char *pattern, const char *string, int flags)
207 {
208 return fnmatchx(pattern, string, flags, 64);
209 }
210