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