]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/glob/fnmatch.c
Imported from ../bash-2.01.tar.gz.
[thirdparty/bash.git] / lib / glob / fnmatch.c
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 == '/')
53 /* If we are matching a pathname, `?' can never match a `/'. */
54 return (FNM_NOMATCH);
55 else if ((flags & FNM_PERIOD) && *n == '.' &&
56 (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
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. */
60 return (FNM_NOMATCH);
61 break;
62
63 case '\\':
64 if (!(flags & FNM_NOESCAPE))
65 {
66 c = *p++;
67 if (c == '\0')
68 return (FNM_NOMATCH);
69 }
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] == '/')))
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. */
80 return (FNM_NOMATCH);
81
82 /* Collapse multiple consecutive, `*' and `?', but make sure that
83 one character of the string is consumed for each `?'. */
84 for (c = *p++; c == '?' || c == '*'; c = *p++)
85 {
86 if ((flags & FNM_PATHNAME) && *n == '/')
87 /* A slash does not match a wildcard under FNM_PATHNAME. */
88 return (FNM_NOMATCH);
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 }
98 }
99
100 if (c == '\0')
101 return (0);
102
103 /* General case, use recursion. */
104 {
105 char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
106 for (--p; *n != '\0'; ++n)
107 /* Only call fnmatch if the first character indicates a
108 possible match. */
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
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. */
126 if ((flags & FNM_PERIOD) && *n == '.' &&
127 (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
128 return (FNM_NOMATCH);
129
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. */
136 {
137 register char *np;
138
139 for (np = p; np && *np && *np != ']'; np++)
140 ;
141
142 if (np && !*np)
143 {
144 if (*n != '[')
145 return (FNM_NOMATCH);
146 break;
147 }
148 }
149
150 not = (*p == '!' || *p == '^');
151 if (not)
152 ++p;
153
154 c = *p++;
155 for (;;)
156 {
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;
162
163 if (!(flags & FNM_NOESCAPE) && c == '\\')
164 {
165 if (*p == '\0')
166 return FNM_NOMATCH;
167 cstart = cend = *p++;
168 }
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
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. */
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);
190
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);
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 == '\\')
214 {
215 if (*p == '\0')
216 return FNM_NOMATCH;
217 /* XXX 1003.2d11 is unclear if this is right. */
218 ++p;
219 }
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 }