]>
Commit | Line | Data |
---|---|---|
cce855bc JA |
1 | /* fnmatch.c -- ksh-like extended pattern matching for the shell and filename |
2 | globbing. */ | |
3 | ||
4 | /* Copyright (C) 1991, 1997 Free Software Foundation, Inc. | |
5 | ||
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
10 | Software Foundation; either version 2, or (at your option) any later | |
11 | version. | |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
20 | Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
21 | ||
22 | #include <config.h> | |
23 | ||
24 | #include "fnmatch.h" | |
25 | #include "collsyms.h" | |
26 | #include <ctype.h> | |
726f6388 | 27 | |
cce855bc JA |
28 | static int gmatch (); |
29 | static char *brackmatch (); | |
30 | #ifdef EXTENDED_GLOB | |
31 | static int extmatch (); | |
32 | #endif | |
33 | ||
34 | #if !defined (isascii) | |
35 | # define isascii(c) ((unsigned int)(c) <= 0177) | |
36 | #endif | |
726f6388 | 37 | |
cce855bc | 38 | /* Note that these evaluate C many times. */ |
726f6388 | 39 | |
cce855bc JA |
40 | #ifndef isblank |
41 | # define isblank(c) ((c) == ' ' || (c) == '\t') | |
42 | #endif | |
726f6388 | 43 | |
cce855bc JA |
44 | #ifndef isgraph |
45 | # define isgraph(c) ((c) != ' ' && isprint((c))) | |
726f6388 JA |
46 | #endif |
47 | ||
cce855bc JA |
48 | #ifndef isxdigit |
49 | # define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) | |
50 | #endif | |
51 | ||
bc4cd23c JA |
52 | /* The result of FOLD is an `unsigned char' */ |
53 | # define FOLD(c) ((flags & FNM_CASEFOLD) && isupper ((unsigned char)c) \ | |
54 | ? tolower ((unsigned char)c) \ | |
55 | : ((unsigned char)c)) | |
cce855bc JA |
56 | |
57 | #ifndef STREQ | |
58 | #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) | |
59 | #define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0) | |
60 | #endif | |
61 | ||
62 | #if defined (HAVE_STRCOLL) | |
63 | static int rangecmp (c1, c2) | |
64 | int c1, c2; | |
65 | { | |
66 | static char s1[2] = { ' ', '\0' }; | |
67 | static char s2[2] = { ' ', '\0' }; | |
68 | int ret; | |
69 | ||
70 | /* Eight bits only. Period. */ | |
71 | c1 &= 0xFF; | |
72 | c2 &= 0xFF; | |
73 | ||
74 | if (c1 == c2) | |
75 | return (0); | |
76 | ||
77 | s1[0] = c1; | |
78 | s2[0] = c2; | |
79 | ||
80 | if ((ret = strcoll (s1, s2)) != 0) | |
81 | return ret; | |
82 | return (c1 - c2); | |
83 | } | |
84 | #else /* !HAVE_STRCOLL */ | |
85 | # define rangecmp(c1, c2) ((c1) - (c2)) | |
86 | #endif /* !HAVE_STRCOLL */ | |
87 | ||
88 | #if defined (HAVE_STRCOLL) | |
89 | static int collequiv (c1, c2) | |
90 | int c1, c2; | |
91 | { | |
92 | return (rangecmp (c1, c2) == 0); | |
93 | } | |
94 | #else | |
95 | # define collequiv(c1, c2) ((c1) == (c2)) | |
96 | #endif | |
97 | ||
98 | static int | |
99 | collsym (s, len) | |
100 | char *s; | |
101 | int len; | |
102 | { | |
103 | register struct _collsym *csp; | |
104 | ||
105 | for (csp = posix_collsyms; csp->name; csp++) | |
106 | { | |
107 | if (STREQN(csp->name, s, len) && csp->name[len] == '\0') | |
108 | return (csp->code); | |
109 | } | |
110 | if (len == 1) | |
111 | return s[0]; | |
112 | return -1; | |
113 | } | |
114 | ||
726f6388 JA |
115 | int |
116 | fnmatch (pattern, string, flags) | |
117 | char *pattern; | |
118 | char *string; | |
119 | int flags; | |
120 | { | |
cce855bc | 121 | char *se, *pe; |
726f6388 | 122 | |
cce855bc JA |
123 | if (string == 0 || pattern == 0) |
124 | return FNM_NOMATCH; | |
125 | ||
126 | se = string + strlen (string); | |
127 | pe = pattern + strlen (pattern); | |
128 | ||
129 | return (gmatch (string, se, pattern, pe, flags)); | |
130 | } | |
131 | ||
132 | /* Match STRING against the filename pattern PATTERN, returning zero if | |
133 | it matches, FNM_NOMATCH if not. */ | |
134 | static int | |
135 | gmatch (string, se, pattern, pe, flags) | |
136 | char *string, *se; | |
137 | char *pattern, *pe; | |
138 | int flags; | |
139 | { | |
140 | register char *p, *n; /* pattern, string */ | |
141 | register char c; /* current pattern character */ | |
142 | register char sc; /* current string character */ | |
726f6388 | 143 | |
cce855bc JA |
144 | p = pattern; |
145 | n = string; | |
146 | ||
147 | if (string == 0 || pattern == 0) | |
148 | return FNM_NOMATCH; | |
149 | ||
150 | while (p < pe) | |
726f6388 | 151 | { |
cce855bc JA |
152 | c = *p++; |
153 | c = FOLD (c); | |
154 | ||
155 | sc = n < se ? *n : '\0'; | |
156 | ||
157 | #ifdef EXTENDED_GLOB | |
158 | if ((flags & FNM_EXTMATCH) && *p == '(' && | |
159 | (c == '+' || c == '*' || c == '?' || c == '@' || c == '!')) /* ) */ | |
160 | /* extmatch () will handle recursively calling gmatch, so we can | |
161 | just return what extmatch() returns. */ | |
162 | return (extmatch (c, n, se, p, pe, flags)); | |
163 | #endif | |
164 | ||
726f6388 JA |
165 | switch (c) |
166 | { | |
cce855bc JA |
167 | case '?': /* Match single character */ |
168 | if (sc == '\0') | |
169 | return FNM_NOMATCH; | |
170 | else if ((flags & FNM_PATHNAME) && sc == '/') | |
d166f048 | 171 | /* If we are matching a pathname, `?' can never match a `/'. */ |
cce855bc JA |
172 | return FNM_NOMATCH; |
173 | else if ((flags & FNM_PERIOD) && sc == '.' && | |
726f6388 | 174 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) |
d166f048 JA |
175 | /* `?' cannot match a `.' if it is the first character of the |
176 | string or if it is the first character following a slash and | |
177 | we are matching a pathname. */ | |
cce855bc | 178 | return FNM_NOMATCH; |
726f6388 JA |
179 | break; |
180 | ||
cce855bc JA |
181 | case '\\': /* backslash escape removes special meaning */ |
182 | if (p == pe) | |
183 | return FNM_NOMATCH; | |
184 | ||
185 | if ((flags & FNM_NOESCAPE) == 0) | |
d166f048 JA |
186 | { |
187 | c = *p++; | |
cce855bc JA |
188 | /* A trailing `\' cannot match. */ |
189 | if (p > pe) | |
190 | return FNM_NOMATCH; | |
191 | c = FOLD (c); | |
d166f048 | 192 | } |
bc4cd23c | 193 | if (FOLD (sc) != (unsigned char)c) |
cce855bc | 194 | return FNM_NOMATCH; |
726f6388 JA |
195 | break; |
196 | ||
cce855bc JA |
197 | case '*': /* Match zero or more characters */ |
198 | if (p == pe) | |
199 | return 0; | |
200 | ||
201 | if ((flags & FNM_PERIOD) && sc == '.' && | |
726f6388 | 202 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) |
d166f048 JA |
203 | /* `*' cannot match a `.' if it is the first character of the |
204 | string or if it is the first character following a slash and | |
205 | we are matching a pathname. */ | |
cce855bc | 206 | return FNM_NOMATCH; |
726f6388 | 207 | |
d166f048 JA |
208 | /* Collapse multiple consecutive, `*' and `?', but make sure that |
209 | one character of the string is consumed for each `?'. */ | |
cce855bc | 210 | for (c = *p++; (c == '?' || c == '*'); c = *p++) |
ccc6cda3 | 211 | { |
cce855bc | 212 | if ((flags & FNM_PATHNAME) && sc == '/') |
d166f048 | 213 | /* A slash does not match a wildcard under FNM_PATHNAME. */ |
cce855bc | 214 | return FNM_NOMATCH; |
d166f048 JA |
215 | else if (c == '?') |
216 | { | |
cce855bc JA |
217 | if (sc == '\0') |
218 | return FNM_NOMATCH; | |
d166f048 JA |
219 | /* One character of the string is consumed in matching |
220 | this ? wildcard, so *??? won't match if there are | |
221 | fewer than three characters. */ | |
222 | n++; | |
cce855bc | 223 | sc = n < se ? *n : '\0'; |
d166f048 | 224 | } |
cce855bc JA |
225 | |
226 | #ifdef EXTENDED_GLOB | |
227 | /* Handle ******(patlist) */ | |
228 | if ((flags & FNM_EXTMATCH) && c == '*' && *p == '(') /*)*/ | |
229 | return (extmatch (c, n, se, p, pe, flags)); | |
230 | #endif | |
231 | if (p == pe) | |
232 | break; | |
ccc6cda3 | 233 | } |
726f6388 | 234 | |
cce855bc JA |
235 | /* If we've hit the end of the pattern and the last character of |
236 | the pattern was handled by the loop above, we've succeeded. | |
237 | Otherwise, we need to match that last character. */ | |
238 | if (p == pe && (c == '?' || c == '*')) | |
726f6388 JA |
239 | return (0); |
240 | ||
d166f048 | 241 | /* General case, use recursion. */ |
726f6388 | 242 | { |
bc4cd23c | 243 | unsigned char c1; |
cce855bc | 244 | |
bc4cd23c | 245 | c1 = (unsigned char)((flags & FNM_NOESCAPE) == 0 && c == '\\') ? *p : c; |
cce855bc JA |
246 | c1 = FOLD (c1); |
247 | for (--p; n < se; ++n) | |
d166f048 JA |
248 | /* Only call fnmatch if the first character indicates a |
249 | possible match. */ | |
cce855bc JA |
250 | if ((c == '[' || FOLD (*n) == c1) && |
251 | gmatch (n, se, p, pe, flags & ~FNM_PERIOD) == 0) | |
726f6388 | 252 | return (0); |
cce855bc | 253 | return FNM_NOMATCH; |
726f6388 JA |
254 | } |
255 | ||
256 | case '[': | |
257 | { | |
cce855bc JA |
258 | if (sc == '\0' || n == se) |
259 | return FNM_NOMATCH; | |
726f6388 | 260 | |
d166f048 JA |
261 | /* A character class cannot match a `.' if it is the first |
262 | character of the string or if it is the first character | |
263 | following a slash and we are matching a pathname. */ | |
cce855bc | 264 | if ((flags & FNM_PERIOD) && sc == '.' && |
726f6388 JA |
265 | (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) |
266 | return (FNM_NOMATCH); | |
267 | ||
cce855bc JA |
268 | p = brackmatch (p, sc, flags); |
269 | if (p == 0) | |
270 | return FNM_NOMATCH; | |
726f6388 JA |
271 | } |
272 | break; | |
273 | ||
274 | default: | |
bc4cd23c | 275 | if ((unsigned char)c != FOLD (sc)) |
726f6388 JA |
276 | return (FNM_NOMATCH); |
277 | } | |
278 | ||
279 | ++n; | |
280 | } | |
281 | ||
cce855bc | 282 | if (n == se) |
726f6388 JA |
283 | return (0); |
284 | ||
cce855bc JA |
285 | if ((flags & FNM_LEADING_DIR) && *n == '/') |
286 | /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ | |
287 | return 0; | |
288 | ||
289 | return (FNM_NOMATCH); | |
290 | } | |
291 | ||
292 | /* Parse a bracket expression collating symbol ([.sym.]) starting at P, find | |
293 | the value of the symbol, and move P past the collating symbol expression. | |
294 | The value is returned in *VP, if VP is not null. */ | |
295 | static char * | |
296 | parse_collsym (p, vp) | |
297 | char *p; | |
298 | int *vp; | |
299 | { | |
300 | register int pc; | |
301 | int val; | |
302 | ||
303 | p++; /* move past the `.' */ | |
304 | ||
305 | for (pc = 0; p[pc]; pc++) | |
306 | if (p[pc] == '.' && p[pc+1] == ']') | |
307 | break; | |
308 | val = collsym (p, pc); | |
309 | if (vp) | |
310 | *vp = val; | |
311 | return (p + pc + 2); | |
312 | } | |
313 | ||
314 | static char * | |
315 | brackmatch (p, test, flags) | |
316 | char *p; | |
317 | unsigned char test; | |
318 | int flags; | |
319 | { | |
320 | register char cstart, cend, c; | |
321 | register int not; /* Nonzero if the sense of the character class is inverted. */ | |
322 | int pc, brcnt; | |
323 | char *savep; | |
324 | ||
325 | test = FOLD (test); | |
326 | ||
327 | savep = p; | |
328 | ||
329 | /* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the | |
330 | circumflex (`^') in its role in a `nonmatching list'. A bracket | |
331 | expression starging with an unquoted circumflex character produces | |
332 | unspecified results. This implementation treats the two identically. */ | |
333 | if (not = (*p == '!' || *p == '^')) | |
334 | ++p; | |
335 | ||
336 | c = *p++; | |
337 | for (;;) | |
338 | { | |
339 | /* Initialize cstart and cend in case `-' is the last | |
340 | character of the pattern. */ | |
341 | cstart = cend = c; | |
342 | ||
343 | /* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find | |
344 | the end of the equivalence class, move the pattern pointer past | |
345 | it, and check for equivalence. XXX - this handles only | |
346 | single-character equivalence classes, which is wrong, or at | |
347 | least incomplete. */ | |
348 | if (c == '[' && *p == '=' && p[2] == '=' && p[3] == ']') | |
349 | { | |
350 | pc = FOLD (p[1]); | |
351 | p += 4; | |
352 | if (collequiv (test, pc)) | |
353 | goto matched; | |
354 | else | |
355 | { | |
356 | c = *p++; | |
357 | if (c == '\0') | |
358 | return ((test == '[') ? savep : (char *)0); | |
359 | c = FOLD (c); | |
360 | continue; | |
361 | } | |
362 | } | |
363 | ||
364 | /* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */ | |
365 | if (c == '[' && *p == ':') | |
366 | { | |
367 | pc = 0; /* make sure invalid char classes don't match. */ | |
368 | if (STREQN (p+1, "alnum:]", 7)) | |
369 | { pc = isalnum (test); p += 8; } | |
370 | else if (STREQN (p+1, "alpha:]", 7)) | |
371 | { pc = isalpha (test); p += 8; } | |
372 | else if (STREQN (p+1, "blank:]", 7)) | |
373 | { pc = isblank (test); p += 8; } | |
374 | else if (STREQN (p+1, "cntrl:]", 7)) | |
375 | { pc = iscntrl (test); p += 8; } | |
376 | else if (STREQN (p+1, "digit:]", 7)) | |
377 | { pc = isdigit (test); p += 8; } | |
378 | else if (STREQN (p+1, "graph:]", 7)) | |
379 | { pc = isgraph (test); p += 8; } | |
380 | else if (STREQN (p+1, "lower:]", 7)) | |
bc4cd23c | 381 | { pc = islower (test); p += 8; } |
cce855bc JA |
382 | else if (STREQN (p+1, "print:]", 7)) |
383 | { pc = isprint (test); p += 8; } | |
384 | else if (STREQN (p+1, "punct:]", 7)) | |
385 | { pc = ispunct (test); p += 8; } | |
386 | else if (STREQN (p+1, "space:]", 7)) | |
387 | { pc = isspace (test); p += 8; } | |
388 | else if (STREQN (p+1, "upper:]", 7)) | |
bc4cd23c | 389 | { pc = isupper (test); p += 8; } |
cce855bc JA |
390 | else if (STREQN (p+1, "xdigit:]", 8)) |
391 | { pc = isxdigit (test); p += 9; } | |
392 | else if (STREQN (p+1, "ascii:]", 7)) | |
393 | { pc = isascii (test); p += 8; } | |
394 | if (pc) | |
395 | goto matched; | |
396 | else | |
397 | { | |
398 | /* continue the loop here, since this expression can't be | |
399 | the first part of a range expression. */ | |
400 | c = *p++; | |
401 | if (c == '\0') | |
402 | return ((test == '[') ? savep : (char *)0); | |
403 | else if (c == ']') | |
404 | break; | |
405 | c = FOLD (c); | |
406 | continue; | |
407 | } | |
408 | } | |
409 | ||
410 | /* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of | |
411 | the symbol name, make sure it is terminated by `.]', translate | |
412 | the name to a character using the external table, and do the | |
413 | comparison. */ | |
414 | if (c == '[' && *p == '.') | |
415 | { | |
416 | p = parse_collsym (p, &pc); | |
417 | /* An invalid collating symbol cannot be the first point of a | |
418 | range. If it is, we set cstart to one greater than `test', | |
419 | so any comparisons later will fail. */ | |
420 | cstart = (pc == -1) ? test + 1 : pc; | |
421 | } | |
422 | ||
423 | if (!(flags & FNM_NOESCAPE) && c == '\\') | |
424 | { | |
425 | if (*p == '\0') | |
426 | return (char *)0; | |
427 | cstart = cend = *p++; | |
428 | } | |
429 | ||
430 | cstart = cend = FOLD (cstart); | |
431 | ||
432 | /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that | |
433 | is not preceded by a backslash and is not part of a bracket | |
434 | expression produces undefined results.' This implementation | |
435 | treats the `[' as just a character to be matched if there is | |
436 | not a closing `]'. */ | |
437 | if (c == '\0') | |
438 | return ((test == '[') ? savep : (char *)0); | |
439 | ||
440 | c = *p++; | |
441 | c = FOLD (c); | |
442 | ||
443 | if ((flags & FNM_PATHNAME) && c == '/') | |
444 | /* [/] can never match when matching a pathname. */ | |
445 | return (char *)0; | |
446 | ||
447 | /* This introduces a range, unless the `-' is the last | |
448 | character of the class. Find the end of the range | |
449 | and move past it. */ | |
450 | if (c == '-' && *p != ']') | |
451 | { | |
452 | cend = *p++; | |
453 | if (!(flags & FNM_NOESCAPE) && cend == '\\') | |
454 | cend = *p++; | |
455 | if (cend == '\0') | |
456 | return (char *)0; | |
457 | if (cend == '[' && *p == '.') | |
458 | { | |
459 | p = parse_collsym (p, &pc); | |
460 | /* An invalid collating symbol cannot be the second part of a | |
461 | range expression. If we get one, we set cend to one fewer | |
462 | than the test character to make sure the range test fails. */ | |
463 | cend = (pc == -1) ? test - 1 : pc; | |
464 | } | |
465 | cend = FOLD (cend); | |
466 | ||
467 | c = *p++; | |
468 | ||
469 | /* POSIX.2 2.8.3.2: ``The ending range point shall collate | |
470 | equal to or higher than the starting range point; otherwise | |
471 | the expression shall be treated as invalid.'' Note that this | |
472 | applies to only the range expression; the rest of the bracket | |
473 | expression is still checked for matches. */ | |
474 | if (rangecmp (cstart, cend) > 0) | |
475 | { | |
476 | if (c == ']') | |
477 | break; | |
478 | c = FOLD (c); | |
479 | continue; | |
480 | } | |
481 | } | |
482 | ||
483 | if (rangecmp (test, cstart) >= 0 && rangecmp (test, cend) <= 0) | |
484 | goto matched; | |
485 | ||
486 | if (c == ']') | |
487 | break; | |
488 | } | |
489 | /* No match. */ | |
490 | return (!not ? (char *)0 : p); | |
491 | ||
492 | matched: | |
493 | /* Skip the rest of the [...] that already matched. */ | |
494 | brcnt = (c != ']') + (c == '[' && (*p == '=' || *p == ':' || *p == '.')); | |
495 | while (brcnt > 0) | |
496 | { | |
497 | /* A `[' without a matching `]' is just another character to match. */ | |
498 | if (c == '\0') | |
499 | return ((test == '[') ? savep : (char *)0); | |
500 | ||
501 | c = *p++; | |
502 | if (c == '[' && (*p == '=' || *p == ':' || *p == '.')) | |
503 | brcnt++; | |
504 | else if (c == ']') | |
505 | brcnt--; | |
506 | else if (!(flags & FNM_NOESCAPE) && c == '\\') | |
507 | { | |
508 | if (*p == '\0') | |
509 | return (char *)0; | |
510 | /* XXX 1003.2d11 is unclear if this is right. */ | |
511 | ++p; | |
512 | } | |
513 | } | |
514 | return (not ? (char *)0 : p); | |
515 | } | |
516 | ||
517 | #if defined (EXTENDED_GLOB) | |
518 | /* ksh-like extended pattern matching: | |
519 | ||
520 | [?*+@!](pat-list) | |
521 | ||
522 | where pat-list is a list of one or patterns separated by `|'. Operation | |
523 | is as follows: | |
524 | ||
525 | ?(patlist) match zero or one of the given patterns | |
526 | *(patlist) match zero or more of the given patterns | |
527 | +(patlist) match one or more of the given patterns | |
528 | @(patlist) match exactly one of the given patterns | |
529 | !(patlist) match anything except one of the given patterns | |
530 | */ | |
531 | ||
532 | /* Scan a pattern starting at STRING and ending at END, keeping track of | |
533 | embedded () and []. If DELIM is 0, we scan until a matching `)' | |
534 | because we're scanning a `patlist'. Otherwise, we scan until we see | |
535 | DELIM. In all cases, we never scan past END. The return value is the | |
536 | first character after the matching DELIM. */ | |
537 | static char * | |
538 | patscan (string, end, delim) | |
539 | char *string, *end; | |
540 | int delim; | |
541 | { | |
542 | int pnest, bnest; | |
543 | char *s, c; | |
544 | ||
545 | pnest = bnest = 0; | |
546 | for (s = string; c = *s; s++) | |
547 | { | |
548 | switch (c) | |
549 | { | |
550 | case '\0': | |
551 | return ((char *)0); | |
552 | case '[': | |
553 | bnest++; | |
554 | break; | |
555 | case ']': | |
556 | if (bnest) | |
557 | bnest--; | |
558 | break; | |
559 | case '(': | |
560 | if (bnest == 0) | |
561 | pnest++; | |
562 | break; | |
563 | case ')': | |
564 | if (bnest == 0) | |
565 | pnest--; | |
566 | if (pnest <= 0) | |
567 | return ++s; | |
568 | break; | |
569 | case '|': | |
570 | if (bnest == 0 && pnest == 0 && delim == '|') | |
571 | return ++s; | |
572 | break; | |
573 | } | |
574 | } | |
575 | return (char *)0; | |
576 | } | |
577 | ||
578 | /* Return 0 if dequoted pattern matches S in the current locale. */ | |
579 | static int | |
580 | strcompare (p, pe, s, se) | |
581 | char *p, *pe, *s, *se; | |
582 | { | |
583 | int ret; | |
584 | char c1, c2; | |
585 | ||
586 | c1 = *pe; | |
587 | c2 = *se; | |
588 | ||
589 | *pe = *se = '\0'; | |
590 | #if defined (HAVE_STRCOLL) | |
591 | ret = strcoll (p, s); | |
592 | #else | |
593 | ret = strcmp (p, s); | |
594 | #endif | |
595 | ||
596 | *pe = c1; | |
597 | *se = c2; | |
598 | ||
599 | return (ret == 0 ? ret : FNM_NOMATCH); | |
600 | } | |
601 | ||
602 | /* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or | |
603 | 0 on success. This is handed the entire rest of the pattern and string | |
604 | the first time an extended pattern specifier is encountered, so it calls | |
605 | gmatch recursively. */ | |
606 | static int | |
607 | extmatch (xc, s, se, p, pe, flags) | |
608 | int xc; /* select which operation */ | |
609 | char *s, *se; | |
610 | char *p, *pe; | |
611 | int flags; | |
612 | { | |
613 | char *prest; /* pointer to rest of pattern */ | |
614 | char *psub; /* pointer to sub-pattern */ | |
615 | char *pnext; /* pointer to next sub-pattern */ | |
616 | char *srest; /* pointer to rest of string */ | |
617 | int m1, m2; | |
618 | ||
619 | switch (xc) | |
620 | { | |
621 | case '+': /* match one or more occurrences */ | |
622 | case '*': /* match zero or more occurrences */ | |
623 | prest = patscan (p, pe, 0); | |
624 | if (prest == 0) | |
625 | /* If PREST is 0, we failed to scan a valid pattern. In this | |
626 | case, we just want to compare the two as strings. */ | |
627 | return (strcompare (p - 1, pe, s, se)); | |
628 | ||
629 | /* If we can get away with no matches, don't even bother. Just | |
630 | call gmatch on the rest of the pattern and return success if | |
631 | it succeeds. */ | |
632 | if (xc == '*' && (gmatch (s, se, prest, pe, flags) == 0)) | |
633 | return 0; | |
634 | ||
635 | /* OK, we have to do this the hard way. First, we make sure one of | |
636 | the subpatterns matches, then we try to match the rest of the | |
637 | string. */ | |
638 | for (psub = p + 1; ; psub = pnext) | |
639 | { | |
640 | pnext = patscan (psub, pe, '|'); | |
641 | for (srest = s; srest <= se; srest++) | |
642 | { | |
643 | /* Match this substring (S -> SREST) against this | |
644 | subpattern (psub -> pnext - 1) */ | |
645 | m1 = gmatch (s, srest, psub, pnext - 1, flags) == 0; | |
646 | /* OK, we matched a subpattern, so make sure the rest of the | |
647 | string matches the rest of the pattern. Also handle | |
648 | multiple matches of the pattern. */ | |
649 | if (m1) | |
650 | m2 = (gmatch (srest, se, prest, pe, flags) == 0) || | |
651 | (s != srest && gmatch (srest, se, p - 1, pe, flags) == 0); | |
652 | if (m1 && m2) | |
653 | return (0); | |
654 | } | |
655 | if (pnext == prest) | |
656 | break; | |
657 | } | |
658 | return (FNM_NOMATCH); | |
659 | ||
660 | case '?': /* match zero or one of the patterns */ | |
661 | case '@': /* match exactly one of the patterns */ | |
662 | prest = patscan (p, pe, 0); | |
663 | if (prest == 0) | |
664 | return (strcompare (p - 1, pe, s, se)); | |
665 | ||
666 | /* If we can get away with no matches, don't even bother. Just | |
667 | call gmatch on the rest of the pattern and return success if | |
668 | it succeeds. */ | |
669 | if (xc == '?' && (gmatch (s, se, prest, pe, flags) == 0)) | |
670 | return 0; | |
671 | ||
672 | /* OK, we have to do this the hard way. First, we see if one of | |
673 | the subpatterns matches, then, if it does, we try to match the | |
674 | rest of the string. */ | |
675 | for (psub = p + 1; ; psub = pnext) | |
676 | { | |
677 | pnext = patscan (psub, pe, '|'); | |
678 | srest = (prest == pe) ? se : s; | |
679 | for ( ; srest <= se; srest++) | |
680 | { | |
681 | if (gmatch (s, srest, psub, pnext - 1, flags) == 0 && | |
682 | gmatch (srest, se, prest, pe, flags) == 0) | |
683 | return (0); | |
684 | } | |
685 | if (pnext == prest) | |
686 | break; | |
687 | } | |
688 | return (FNM_NOMATCH); | |
689 | ||
690 | case '!': /* match anything *except* one of the patterns */ | |
691 | prest = patscan (p, pe, 0); | |
692 | if (prest == 0) | |
693 | return (strcompare (p - 1, pe, s, se)); | |
694 | ||
695 | for (srest = s; srest <= se; srest++) | |
696 | { | |
697 | m1 = 0; | |
698 | for (psub = p + 1; ; psub = pnext) | |
699 | { | |
700 | pnext = patscan (psub, pe, '|'); | |
701 | /* If one of the patterns matches, just bail immediately. */ | |
702 | if (m1 = (gmatch (s, srest, psub, pnext - 1, flags) == 0)) | |
703 | break; | |
704 | if (pnext == prest) | |
705 | break; | |
706 | } | |
707 | if (m1 == 0 && gmatch (srest, se, prest, pe, flags) == 0) | |
708 | return (0); | |
709 | } | |
710 | return (FNM_NOMATCH); | |
711 | } | |
712 | ||
726f6388 JA |
713 | return (FNM_NOMATCH); |
714 | } | |
cce855bc JA |
715 | #endif /* EXTENDED_GLOB */ |
716 | ||
717 | #ifdef TEST | |
718 | main (c, v) | |
719 | int c; | |
720 | char **v; | |
721 | { | |
722 | char *string, *pat; | |
723 | ||
724 | string = v[1]; | |
725 | pat = v[2]; | |
726 | ||
727 | if (fnmatch (pat, string, 0) == 0) | |
728 | { | |
729 | printf ("%s matches %s\n", string, pat); | |
730 | exit (0); | |
731 | } | |
732 | else | |
733 | { | |
734 | printf ("%s does not match %s\n", string, pat); | |
735 | exit (1); | |
736 | } | |
737 | } | |
738 | #endif |