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