]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* Copyright (C) 1991 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. | |
3 | ||
4 | The GNU C Library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Library General Public License as | |
6 | published by the Free Software Foundation; either version 2 of the | |
7 | License, or (at your option) any later version. | |
8 | ||
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Library General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Library General Public | |
15 | License along with the GNU C Library; see the file COPYING.LIB. If | |
16 | not, write to the Free Software Foundation, Inc., 675 Mass Ave, | |
17 | Cambridge, MA 02139, USA. */ | |
18 | ||
19 | #include <errno.h> | |
20 | #include "fnmatch.h" | |
21 | ||
22 | #if !defined (__GNU_LIBRARY__) && !defined (STDC_HEADERS) | |
23 | # if !defined (errno) | |
24 | extern int errno; | |
25 | # endif /* !errno */ | |
26 | #endif | |
27 | ||
28 | /* Match STRING against the filename pattern PATTERN, returning zero if | |
29 | it matches, FNM_NOMATCH if not. */ | |
30 | int | |
31 | fnmatch (pattern, string, flags) | |
32 | char *pattern; | |
33 | char *string; | |
34 | int flags; | |
35 | { | |
36 | register char *p = pattern, *n = string; | |
37 | register char c; | |
38 | ||
39 | if ((flags & ~__FNM_FLAGS) != 0) | |
40 | { | |
41 | errno = EINVAL; | |
42 | return (-1); | |
43 | } | |
44 | ||
45 | while ((c = *p++) != '\0') | |
46 | { | |
47 | switch (c) | |
48 | { | |
49 | case '?': | |
50 | if (*n == '\0') | |
51 | return (FNM_NOMATCH); | |
52 | else if ((flags & FNM_PATHNAME) && *n == '/') | |
d166f048 | 53 | /* If we are matching a pathname, `?' can never match a `/'. */ |
726f6388 JA |
54 | return (FNM_NOMATCH); |
55 | else if ((flags & FNM_PERIOD) && *n == '.' && | |
56 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) | |
d166f048 JA |
57 | /* `?' cannot match a `.' if it is the first character of the |
58 | string or if it is the first character following a slash and | |
59 | we are matching a pathname. */ | |
726f6388 JA |
60 | return (FNM_NOMATCH); |
61 | break; | |
62 | ||
63 | case '\\': | |
64 | if (!(flags & FNM_NOESCAPE)) | |
d166f048 JA |
65 | { |
66 | c = *p++; | |
67 | if (c == '\0') | |
68 | return (FNM_NOMATCH); | |
69 | } | |
726f6388 JA |
70 | if (*n != c) |
71 | return (FNM_NOMATCH); | |
72 | break; | |
73 | ||
74 | case '*': | |
75 | if ((flags & FNM_PERIOD) && *n == '.' && | |
76 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) | |
d166f048 JA |
77 | /* `*' cannot match a `.' if it is the first character of the |
78 | string or if it is the first character following a slash and | |
79 | we are matching a pathname. */ | |
726f6388 JA |
80 | return (FNM_NOMATCH); |
81 | ||
d166f048 JA |
82 | /* Collapse multiple consecutive, `*' and `?', but make sure that |
83 | one character of the string is consumed for each `?'. */ | |
ccc6cda3 JA |
84 | for (c = *p++; c == '?' || c == '*'; c = *p++) |
85 | { | |
d166f048 JA |
86 | if ((flags & FNM_PATHNAME) && *n == '/') |
87 | /* A slash does not match a wildcard under FNM_PATHNAME. */ | |
ccc6cda3 | 88 | return (FNM_NOMATCH); |
d166f048 JA |
89 | else if (c == '?') |
90 | { | |
91 | if (*n == '\0') | |
92 | return (FNM_NOMATCH); | |
93 | /* One character of the string is consumed in matching | |
94 | this ? wildcard, so *??? won't match if there are | |
95 | fewer than three characters. */ | |
96 | n++; | |
97 | } | |
ccc6cda3 | 98 | } |
726f6388 JA |
99 | |
100 | if (c == '\0') | |
101 | return (0); | |
102 | ||
d166f048 | 103 | /* General case, use recursion. */ |
726f6388 JA |
104 | { |
105 | char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; | |
106 | for (--p; *n != '\0'; ++n) | |
d166f048 JA |
107 | /* Only call fnmatch if the first character indicates a |
108 | possible match. */ | |
726f6388 JA |
109 | if ((c == '[' || *n == c1) && |
110 | fnmatch (p, n, flags & ~FNM_PERIOD) == 0) | |
111 | return (0); | |
112 | return (FNM_NOMATCH); | |
113 | } | |
114 | ||
115 | case '[': | |
116 | { | |
117 | /* Nonzero if the sense of the character class is inverted. */ | |
118 | register int not; | |
119 | ||
120 | if (*n == '\0') | |
121 | return (FNM_NOMATCH); | |
122 | ||
d166f048 JA |
123 | /* A character class cannot match a `.' if it is the first |
124 | character of the string or if it is the first character | |
125 | following a slash and we are matching a pathname. */ | |
726f6388 JA |
126 | if ((flags & FNM_PERIOD) && *n == '.' && |
127 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) | |
128 | return (FNM_NOMATCH); | |
129 | ||
d166f048 JA |
130 | /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that |
131 | is not preceded by a backslash and is not part of a bracket | |
132 | expression produces undefined results.' This implementation | |
133 | treats the `[' as just a character to be matched if there is | |
134 | not a closing `]'. This code will have to be changed when | |
135 | POSIX.2 character classes are implemented. */ | |
726f6388 JA |
136 | { |
137 | register char *np; | |
138 | ||
d166f048 JA |
139 | for (np = p; np && *np && *np != ']'; np++) |
140 | ; | |
726f6388 JA |
141 | |
142 | if (np && !*np) | |
143 | { | |
144 | if (*n != '[') | |
145 | return (FNM_NOMATCH); | |
d166f048 | 146 | break; |
726f6388 JA |
147 | } |
148 | } | |
149 | ||
150 | not = (*p == '!' || *p == '^'); | |
151 | if (not) | |
152 | ++p; | |
153 | ||
154 | c = *p++; | |
155 | for (;;) | |
156 | { | |
d166f048 JA |
157 | register char cstart, cend; |
158 | ||
159 | /* Initialize cstart and cend in case `-' is the last | |
160 | character of the pattern. */ | |
161 | cstart = cend = c; | |
726f6388 JA |
162 | |
163 | if (!(flags & FNM_NOESCAPE) && c == '\\') | |
d166f048 JA |
164 | { |
165 | if (*p == '\0') | |
166 | return FNM_NOMATCH; | |
167 | cstart = cend = *p++; | |
168 | } | |
726f6388 JA |
169 | |
170 | if (c == '\0') | |
171 | /* [ (unterminated) loses. */ | |
172 | return (FNM_NOMATCH); | |
173 | ||
174 | c = *p++; | |
175 | ||
176 | if ((flags & FNM_PATHNAME) && c == '/') | |
177 | /* [/] can never match. */ | |
178 | return (FNM_NOMATCH); | |
179 | ||
d166f048 JA |
180 | /* This introduces a range, unless the `-' is the last |
181 | character of the class. Find the end of the range | |
182 | and move past it. */ | |
726f6388 JA |
183 | if (c == '-' && *p != ']') |
184 | { | |
185 | cend = *p++; | |
186 | if (!(flags & FNM_NOESCAPE) && cend == '\\') | |
187 | cend = *p++; | |
188 | if (cend == '\0') | |
189 | return (FNM_NOMATCH); | |
d166f048 | 190 | |
726f6388 JA |
191 | c = *p++; |
192 | } | |
193 | ||
194 | if (*n >= cstart && *n <= cend) | |
195 | goto matched; | |
196 | ||
197 | if (c == ']') | |
198 | break; | |
199 | } | |
200 | if (!not) | |
201 | return (FNM_NOMATCH); | |
726f6388 JA |
202 | break; |
203 | ||
204 | matched: | |
205 | /* Skip the rest of the [...] that already matched. */ | |
206 | while (c != ']') | |
207 | { | |
208 | if (c == '\0') | |
209 | /* [... (unterminated) loses. */ | |
210 | return (FNM_NOMATCH); | |
211 | ||
212 | c = *p++; | |
213 | if (!(flags & FNM_NOESCAPE) && c == '\\') | |
d166f048 JA |
214 | { |
215 | if (*p == '\0') | |
216 | return FNM_NOMATCH; | |
217 | /* XXX 1003.2d11 is unclear if this is right. */ | |
218 | ++p; | |
219 | } | |
726f6388 JA |
220 | } |
221 | if (not) | |
222 | return (FNM_NOMATCH); | |
223 | } | |
224 | break; | |
225 | ||
226 | default: | |
227 | if (c != *n) | |
228 | return (FNM_NOMATCH); | |
229 | } | |
230 | ||
231 | ++n; | |
232 | } | |
233 | ||
234 | if (*n == '\0') | |
235 | return (0); | |
236 | ||
237 | return (FNM_NOMATCH); | |
238 | } |