summaryrefslogtreecommitdiff
path: root/lib/glob/fnmatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/glob/fnmatch.c')
-rw-r--r--lib/glob/fnmatch.c816
1 files changed, 658 insertions, 158 deletions
diff --git a/lib/glob/fnmatch.c b/lib/glob/fnmatch.c
index 2f8eb240..b5fdcc16 100644
--- a/lib/glob/fnmatch.c
+++ b/lib/glob/fnmatch.c
@@ -1,238 +1,738 @@
-/* Copyright (C) 1991 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* fnmatch.c -- ksh-like extended pattern matching for the shell and filename
+ globbing. */
+
+/* Copyright (C) 1991, 1997 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Bash; see the file COPYING. If not, write to the Free Software
+ Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <config.h>
+
+#include "fnmatch.h"
+#include "collsyms.h"
+#include <ctype.h>
-The GNU C Library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+static int gmatch ();
+static char *brackmatch ();
+#ifdef EXTENDED_GLOB
+static int extmatch ();
+#endif
+
+#if !defined (isascii)
+# define isascii(c) ((unsigned int)(c) <= 0177)
+#endif
-The GNU C Library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
+/* Note that these evaluate C many times. */
-You should have received a copy of the GNU Library General Public
-License along with the GNU C Library; see the file COPYING.LIB. If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA. */
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
-#include <errno.h>
-#include "fnmatch.h"
+#ifndef isblank
+# define isblank(c) ((c) == ' ' || (c) == '\t')
+#endif
-#if !defined (__GNU_LIBRARY__) && !defined (STDC_HEADERS)
-# if !defined (errno)
-extern int errno;
-# endif /* !errno */
+#ifndef isgraph
+# define isgraph(c) ((c) != ' ' && isprint((c)))
#endif
-/* Match STRING against the filename pattern PATTERN, returning zero if
- it matches, FNM_NOMATCH if not. */
+#ifndef isxdigit
+# define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
+#endif
+
+# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
+
+#ifndef STREQ
+#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
+#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
+#endif
+
+#if defined (HAVE_STRCOLL)
+static int rangecmp (c1, c2)
+ int c1, c2;
+{
+ static char s1[2] = { ' ', '\0' };
+ static char s2[2] = { ' ', '\0' };
+ int ret;
+
+ /* Eight bits only. Period. */
+ c1 &= 0xFF;
+ c2 &= 0xFF;
+
+ if (c1 == c2)
+ return (0);
+
+ s1[0] = c1;
+ s2[0] = c2;
+
+ if ((ret = strcoll (s1, s2)) != 0)
+ return ret;
+ return (c1 - c2);
+}
+#else /* !HAVE_STRCOLL */
+# define rangecmp(c1, c2) ((c1) - (c2))
+#endif /* !HAVE_STRCOLL */
+
+#if defined (HAVE_STRCOLL)
+static int collequiv (c1, c2)
+ int c1, c2;
+{
+ return (rangecmp (c1, c2) == 0);
+}
+#else
+# define collequiv(c1, c2) ((c1) == (c2))
+#endif
+
+static int
+collsym (s, len)
+ char *s;
+ int len;
+{
+ register struct _collsym *csp;
+
+ for (csp = posix_collsyms; csp->name; csp++)
+ {
+ if (STREQN(csp->name, s, len) && csp->name[len] == '\0')
+ return (csp->code);
+ }
+ if (len == 1)
+ return s[0];
+ return -1;
+}
+
int
fnmatch (pattern, string, flags)
char *pattern;
char *string;
int flags;
{
- register char *p = pattern, *n = string;
- register char c;
+ char *se, *pe;
- if ((flags & ~__FNM_FLAGS) != 0)
- {
- errno = EINVAL;
- return (-1);
- }
+ if (string == 0 || pattern == 0)
+ return FNM_NOMATCH;
+
+ se = string + strlen (string);
+ pe = pattern + strlen (pattern);
+
+ return (gmatch (string, se, pattern, pe, flags));
+}
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, FNM_NOMATCH if not. */
+static int
+gmatch (string, se, pattern, pe, flags)
+ char *string, *se;
+ char *pattern, *pe;
+ int flags;
+{
+ register char *p, *n; /* pattern, string */
+ register char c; /* current pattern character */
+ register char sc; /* current string character */
- while ((c = *p++) != '\0')
+ p = pattern;
+ n = string;
+
+ if (string == 0 || pattern == 0)
+ return FNM_NOMATCH;
+
+ while (p < pe)
{
+ c = *p++;
+ c = FOLD (c);
+
+ sc = n < se ? *n : '\0';
+
+#ifdef EXTENDED_GLOB
+ if ((flags & FNM_EXTMATCH) && *p == '(' &&
+ (c == '+' || c == '*' || c == '?' || c == '@' || c == '!')) /* ) */
+ /* extmatch () will handle recursively calling gmatch, so we can
+ just return what extmatch() returns. */
+ return (extmatch (c, n, se, p, pe, flags));
+#endif
+
switch (c)
{
- case '?':
- if (*n == '\0')
- return (FNM_NOMATCH);
- else if ((flags & FNM_PATHNAME) && *n == '/')
+ case '?': /* Match single character */
+ if (sc == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PATHNAME) && sc == '/')
/* If we are matching a pathname, `?' can never match a `/'. */
- return (FNM_NOMATCH);
- else if ((flags & FNM_PERIOD) && *n == '.' &&
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
/* `?' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
- return (FNM_NOMATCH);
+ return FNM_NOMATCH;
break;
- case '\\':
- if (!(flags & FNM_NOESCAPE))
+ case '\\': /* backslash escape removes special meaning */
+ if (p == pe)
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_NOESCAPE) == 0)
{
c = *p++;
- if (c == '\0')
- return (FNM_NOMATCH);
+ /* A trailing `\' cannot match. */
+ if (p > pe)
+ return FNM_NOMATCH;
+ c = FOLD (c);
}
- if (*n != c)
- return (FNM_NOMATCH);
+ if (FOLD (sc) != c)
+ return FNM_NOMATCH;
break;
- case '*':
- if ((flags & FNM_PERIOD) && *n == '.' &&
+ case '*': /* Match zero or more characters */
+ if (p == pe)
+ return 0;
+
+ if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
/* `*' cannot match a `.' if it is the first character of the
string or if it is the first character following a slash and
we are matching a pathname. */
- return (FNM_NOMATCH);
+ return FNM_NOMATCH;
/* Collapse multiple consecutive, `*' and `?', but make sure that
one character of the string is consumed for each `?'. */
- for (c = *p++; c == '?' || c == '*'; c = *p++)
+ for (c = *p++; (c == '?' || c == '*'); c = *p++)
{
- if ((flags & FNM_PATHNAME) && *n == '/')
+ if ((flags & FNM_PATHNAME) && sc == '/')
/* A slash does not match a wildcard under FNM_PATHNAME. */
- return (FNM_NOMATCH);
+ return FNM_NOMATCH;
else if (c == '?')
{
- if (*n == '\0')
- return (FNM_NOMATCH);
+ if (sc == '\0')
+ return FNM_NOMATCH;
/* One character of the string is consumed in matching
this ? wildcard, so *??? won't match if there are
fewer than three characters. */
n++;
+ sc = n < se ? *n : '\0';
}
+
+#ifdef EXTENDED_GLOB
+ /* Handle ******(patlist) */
+ if ((flags & FNM_EXTMATCH) && c == '*' && *p == '(') /*)*/
+ return (extmatch (c, n, se, p, pe, flags));
+#endif
+ if (p == pe)
+ break;
}
- if (c == '\0')
+ /* If we've hit the end of the pattern and the last character of
+ the pattern was handled by the loop above, we've succeeded.
+ Otherwise, we need to match that last character. */
+ if (p == pe && (c == '?' || c == '*'))
return (0);
/* General case, use recursion. */
{
- char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
- for (--p; *n != '\0'; ++n)
+ char c1;
+
+ c1 = ((flags & FNM_NOESCAPE) == 0 && c == '\\') ? *p : c;
+ c1 = FOLD (c1);
+ for (--p; n < se; ++n)
/* Only call fnmatch if the first character indicates a
possible match. */
- if ((c == '[' || *n == c1) &&
- fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ if ((c == '[' || FOLD (*n) == c1) &&
+ gmatch (n, se, p, pe, flags & ~FNM_PERIOD) == 0)
return (0);
- return (FNM_NOMATCH);
+ return FNM_NOMATCH;
}
case '[':
{
- /* Nonzero if the sense of the character class is inverted. */
- register int not;
-
- if (*n == '\0')
- return (FNM_NOMATCH);
+ if (sc == '\0' || n == se)
+ return FNM_NOMATCH;
/* A character class cannot match a `.' if it is the first
character of the string or if it is the first character
following a slash and we are matching a pathname. */
- if ((flags & FNM_PERIOD) && *n == '.' &&
+ if ((flags & FNM_PERIOD) && sc == '.' &&
(n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
return (FNM_NOMATCH);
- /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
- is not preceded by a backslash and is not part of a bracket
- expression produces undefined results.' This implementation
- treats the `[' as just a character to be matched if there is
- not a closing `]'. This code will have to be changed when
- POSIX.2 character classes are implemented. */
- {
- register char *np;
-
- for (np = p; np && *np && *np != ']'; np++)
- ;
-
- if (np && !*np)
- {
- if (*n != '[')
- return (FNM_NOMATCH);
- break;
- }
- }
-
- not = (*p == '!' || *p == '^');
- if (not)
- ++p;
-
- c = *p++;
- for (;;)
- {
- register char cstart, cend;
-
- /* Initialize cstart and cend in case `-' is the last
- character of the pattern. */
- cstart = cend = c;
-
- if (!(flags & FNM_NOESCAPE) && c == '\\')
- {
- if (*p == '\0')
- return FNM_NOMATCH;
- cstart = cend = *p++;
- }
-
- if (c == '\0')
- /* [ (unterminated) loses. */
- return (FNM_NOMATCH);
-
- c = *p++;
-
- if ((flags & FNM_PATHNAME) && c == '/')
- /* [/] can never match. */
- return (FNM_NOMATCH);
-
- /* This introduces a range, unless the `-' is the last
- character of the class. Find the end of the range
- and move past it. */
- if (c == '-' && *p != ']')
- {
- cend = *p++;
- if (!(flags & FNM_NOESCAPE) && cend == '\\')
- cend = *p++;
- if (cend == '\0')
- return (FNM_NOMATCH);
-
- c = *p++;
- }
-
- if (*n >= cstart && *n <= cend)
- goto matched;
-
- if (c == ']')
- break;
- }
- if (!not)
- return (FNM_NOMATCH);
- break;
-
- matched:
- /* Skip the rest of the [...] that already matched. */
- while (c != ']')
- {
- if (c == '\0')
- /* [... (unterminated) loses. */
- return (FNM_NOMATCH);
-
- c = *p++;
- if (!(flags & FNM_NOESCAPE) && c == '\\')
- {
- if (*p == '\0')
- return FNM_NOMATCH;
- /* XXX 1003.2d11 is unclear if this is right. */
- ++p;
- }
- }
- if (not)
- return (FNM_NOMATCH);
+ p = brackmatch (p, sc, flags);
+ if (p == 0)
+ return FNM_NOMATCH;
}
break;
default:
- if (c != *n)
+ if (c != FOLD (sc))
return (FNM_NOMATCH);
}
++n;
}
- if (*n == '\0')
+ if (n == se)
return (0);
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return (FNM_NOMATCH);
+}
+
+/* Parse a bracket expression collating symbol ([.sym.]) starting at P, find
+ the value of the symbol, and move P past the collating symbol expression.
+ The value is returned in *VP, if VP is not null. */
+static char *
+parse_collsym (p, vp)
+ char *p;
+ int *vp;
+{
+ register int pc;
+ int val;
+
+ p++; /* move past the `.' */
+
+ for (pc = 0; p[pc]; pc++)
+ if (p[pc] == '.' && p[pc+1] == ']')
+ break;
+ val = collsym (p, pc);
+ if (vp)
+ *vp = val;
+ return (p + pc + 2);
+}
+
+static char *
+brackmatch (p, test, flags)
+ char *p;
+ unsigned char test;
+ int flags;
+{
+ register char cstart, cend, c;
+ register int not; /* Nonzero if the sense of the character class is inverted. */
+ int pc, brcnt;
+ char *savep;
+
+ test = FOLD (test);
+
+ savep = p;
+
+ /* POSIX.2 3.13.1 says that an exclamation mark (`!') shall replace the
+ circumflex (`^') in its role in a `nonmatching list'. A bracket
+ expression starging with an unquoted circumflex character produces
+ unspecified results. This implementation treats the two identically. */
+ if (not = (*p == '!' || *p == '^'))
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ /* Initialize cstart and cend in case `-' is the last
+ character of the pattern. */
+ cstart = cend = c;
+
+ /* POSIX.2 equivalence class: [=c=]. See POSIX.2 2.8.3.2. Find
+ the end of the equivalence class, move the pattern pointer past
+ it, and check for equivalence. XXX - this handles only
+ single-character equivalence classes, which is wrong, or at
+ least incomplete. */
+ if (c == '[' && *p == '=' && p[2] == '=' && p[3] == ']')
+ {
+ pc = FOLD (p[1]);
+ p += 4;
+ if (collequiv (test, pc))
+ goto matched;
+ else
+ {
+ c = *p++;
+ if (c == '\0')
+ return ((test == '[') ? savep : (char *)0);
+ c = FOLD (c);
+ continue;
+ }
+ }
+
+ /* POSIX.2 character class expression. See POSIX.2 2.8.3.2. */
+ if (c == '[' && *p == ':')
+ {
+ pc = 0; /* make sure invalid char classes don't match. */
+ if (STREQN (p+1, "alnum:]", 7))
+ { pc = isalnum (test); p += 8; }
+ else if (STREQN (p+1, "alpha:]", 7))
+ { pc = isalpha (test); p += 8; }
+ else if (STREQN (p+1, "blank:]", 7))
+ { pc = isblank (test); p += 8; }
+ else if (STREQN (p+1, "cntrl:]", 7))
+ { pc = iscntrl (test); p += 8; }
+ else if (STREQN (p+1, "digit:]", 7))
+ { pc = isdigit (test); p += 8; }
+ else if (STREQN (p+1, "graph:]", 7))
+ { pc = isgraph (test); p += 8; }
+ else if (STREQN (p+1, "lower:]", 7))
+ { pc = ISLOWER (test); p += 8; }
+ else if (STREQN (p+1, "print:]", 7))
+ { pc = isprint (test); p += 8; }
+ else if (STREQN (p+1, "punct:]", 7))
+ { pc = ispunct (test); p += 8; }
+ else if (STREQN (p+1, "space:]", 7))
+ { pc = isspace (test); p += 8; }
+ else if (STREQN (p+1, "upper:]", 7))
+ { pc = ISUPPER (test); p += 8; }
+ else if (STREQN (p+1, "xdigit:]", 8))
+ { pc = isxdigit (test); p += 9; }
+ else if (STREQN (p+1, "ascii:]", 7))
+ { pc = isascii (test); p += 8; }
+ if (pc)
+ goto matched;
+ else
+ {
+ /* continue the loop here, since this expression can't be
+ the first part of a range expression. */
+ c = *p++;
+ if (c == '\0')
+ return ((test == '[') ? savep : (char *)0);
+ else if (c == ']')
+ break;
+ c = FOLD (c);
+ continue;
+ }
+ }
+
+ /* POSIX.2 collating symbols. See POSIX.2 2.8.3.2. Find the end of
+ the symbol name, make sure it is terminated by `.]', translate
+ the name to a character using the external table, and do the
+ comparison. */
+ if (c == '[' && *p == '.')
+ {
+ p = parse_collsym (p, &pc);
+ /* An invalid collating symbol cannot be the first point of a
+ range. If it is, we set cstart to one greater than `test',
+ so any comparisons later will fail. */
+ cstart = (pc == -1) ? test + 1 : pc;
+ }
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ {
+ if (*p == '\0')
+ return (char *)0;
+ cstart = cend = *p++;
+ }
+
+ cstart = cend = FOLD (cstart);
+
+ /* POSIX.2 2.8.3.1.2 says: `An expression containing a `[' that
+ is not preceded by a backslash and is not part of a bracket
+ expression produces undefined results.' This implementation
+ treats the `[' as just a character to be matched if there is
+ not a closing `]'. */
+ if (c == '\0')
+ return ((test == '[') ? savep : (char *)0);
+
+ c = *p++;
+ c = FOLD (c);
+
+ if ((flags & FNM_PATHNAME) && c == '/')
+ /* [/] can never match when matching a pathname. */
+ return (char *)0;
+
+ /* This introduces a range, unless the `-' is the last
+ character of the class. Find the end of the range
+ and move past it. */
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return (char *)0;
+ if (cend == '[' && *p == '.')
+ {
+ p = parse_collsym (p, &pc);
+ /* An invalid collating symbol cannot be the second part of a
+ range expression. If we get one, we set cend to one fewer
+ than the test character to make sure the range test fails. */
+ cend = (pc == -1) ? test - 1 : pc;
+ }
+ cend = FOLD (cend);
+
+ c = *p++;
+
+ /* POSIX.2 2.8.3.2: ``The ending range point shall collate
+ equal to or higher than the starting range point; otherwise
+ the expression shall be treated as invalid.'' Note that this
+ applies to only the range expression; the rest of the bracket
+ expression is still checked for matches. */
+ if (rangecmp (cstart, cend) > 0)
+ {
+ if (c == ']')
+ break;
+ c = FOLD (c);
+ continue;
+ }
+ }
+
+ if (rangecmp (test, cstart) >= 0 && rangecmp (test, cend) <= 0)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ /* No match. */
+ return (!not ? (char *)0 : p);
+
+matched:
+ /* Skip the rest of the [...] that already matched. */
+ brcnt = (c != ']') + (c == '[' && (*p == '=' || *p == ':' || *p == '.'));
+ while (brcnt > 0)
+ {
+ /* A `[' without a matching `]' is just another character to match. */
+ if (c == '\0')
+ return ((test == '[') ? savep : (char *)0);
+
+ c = *p++;
+ if (c == '[' && (*p == '=' || *p == ':' || *p == '.'))
+ brcnt++;
+ else if (c == ']')
+ brcnt--;
+ else if (!(flags & FNM_NOESCAPE) && c == '\\')
+ {
+ if (*p == '\0')
+ return (char *)0;
+ /* XXX 1003.2d11 is unclear if this is right. */
+ ++p;
+ }
+ }
+ return (not ? (char *)0 : p);
+}
+
+#if defined (EXTENDED_GLOB)
+/* ksh-like extended pattern matching:
+
+ [?*+@!](pat-list)
+
+ where pat-list is a list of one or patterns separated by `|'. Operation
+ is as follows:
+
+ ?(patlist) match zero or one of the given patterns
+ *(patlist) match zero or more of the given patterns
+ +(patlist) match one or more of the given patterns
+ @(patlist) match exactly one of the given patterns
+ !(patlist) match anything except one of the given patterns
+*/
+
+/* Scan a pattern starting at STRING and ending at END, keeping track of
+ embedded () and []. If DELIM is 0, we scan until a matching `)'
+ because we're scanning a `patlist'. Otherwise, we scan until we see
+ DELIM. In all cases, we never scan past END. The return value is the
+ first character after the matching DELIM. */
+static char *
+patscan (string, end, delim)
+ char *string, *end;
+ int delim;
+{
+ int pnest, bnest;
+ char *s, c;
+
+ pnest = bnest = 0;
+ for (s = string; c = *s; s++)
+ {
+ switch (c)
+ {
+ case '\0':
+ return ((char *)0);
+ case '[':
+ bnest++;
+ break;
+ case ']':
+ if (bnest)
+ bnest--;
+ break;
+ case '(':
+ if (bnest == 0)
+ pnest++;
+ break;
+ case ')':
+ if (bnest == 0)
+ pnest--;
+ if (pnest <= 0)
+ return ++s;
+ break;
+ case '|':
+ if (bnest == 0 && pnest == 0 && delim == '|')
+ return ++s;
+ break;
+ }
+ }
+ return (char *)0;
+}
+
+/* Return 0 if dequoted pattern matches S in the current locale. */
+static int
+strcompare (p, pe, s, se)
+ char *p, *pe, *s, *se;
+{
+ int ret;
+ char c1, c2;
+
+ c1 = *pe;
+ c2 = *se;
+
+ *pe = *se = '\0';
+#if defined (HAVE_STRCOLL)
+ ret = strcoll (p, s);
+#else
+ ret = strcmp (p, s);
+#endif
+
+ *pe = c1;
+ *se = c2;
+
+ return (ret == 0 ? ret : FNM_NOMATCH);
+}
+
+/* Match a ksh extended pattern specifier. Return FNM_NOMATCH on failure or
+ 0 on success. This is handed the entire rest of the pattern and string
+ the first time an extended pattern specifier is encountered, so it calls
+ gmatch recursively. */
+static int
+extmatch (xc, s, se, p, pe, flags)
+ int xc; /* select which operation */
+ char *s, *se;
+ char *p, *pe;
+ int flags;
+{
+ char *prest; /* pointer to rest of pattern */
+ char *psub; /* pointer to sub-pattern */
+ char *pnext; /* pointer to next sub-pattern */
+ char *srest; /* pointer to rest of string */
+ int m1, m2;
+
+ switch (xc)
+ {
+ case '+': /* match one or more occurrences */
+ case '*': /* match zero or more occurrences */
+ prest = patscan (p, pe, 0);
+ if (prest == 0)
+ /* If PREST is 0, we failed to scan a valid pattern. In this
+ case, we just want to compare the two as strings. */
+ return (strcompare (p - 1, pe, s, se));
+
+ /* If we can get away with no matches, don't even bother. Just
+ call gmatch on the rest of the pattern and return success if
+ it succeeds. */
+ if (xc == '*' && (gmatch (s, se, prest, pe, flags) == 0))
+ return 0;
+
+ /* OK, we have to do this the hard way. First, we make sure one of
+ the subpatterns matches, then we try to match the rest of the
+ string. */
+ for (psub = p + 1; ; psub = pnext)
+ {
+ pnext = patscan (psub, pe, '|');
+ for (srest = s; srest <= se; srest++)
+ {
+ /* Match this substring (S -> SREST) against this
+ subpattern (psub -> pnext - 1) */
+ m1 = gmatch (s, srest, psub, pnext - 1, flags) == 0;
+ /* OK, we matched a subpattern, so make sure the rest of the
+ string matches the rest of the pattern. Also handle
+ multiple matches of the pattern. */
+ if (m1)
+ m2 = (gmatch (srest, se, prest, pe, flags) == 0) ||
+ (s != srest && gmatch (srest, se, p - 1, pe, flags) == 0);
+ if (m1 && m2)
+ return (0);
+ }
+ if (pnext == prest)
+ break;
+ }
+ return (FNM_NOMATCH);
+
+ case '?': /* match zero or one of the patterns */
+ case '@': /* match exactly one of the patterns */
+ prest = patscan (p, pe, 0);
+ if (prest == 0)
+ return (strcompare (p - 1, pe, s, se));
+
+ /* If we can get away with no matches, don't even bother. Just
+ call gmatch on the rest of the pattern and return success if
+ it succeeds. */
+ if (xc == '?' && (gmatch (s, se, prest, pe, flags) == 0))
+ return 0;
+
+ /* OK, we have to do this the hard way. First, we see if one of
+ the subpatterns matches, then, if it does, we try to match the
+ rest of the string. */
+ for (psub = p + 1; ; psub = pnext)
+ {
+ pnext = patscan (psub, pe, '|');
+ srest = (prest == pe) ? se : s;
+ for ( ; srest <= se; srest++)
+ {
+ if (gmatch (s, srest, psub, pnext - 1, flags) == 0 &&
+ gmatch (srest, se, prest, pe, flags) == 0)
+ return (0);
+ }
+ if (pnext == prest)
+ break;
+ }
+ return (FNM_NOMATCH);
+
+ case '!': /* match anything *except* one of the patterns */
+ prest = patscan (p, pe, 0);
+ if (prest == 0)
+ return (strcompare (p - 1, pe, s, se));
+
+ for (srest = s; srest <= se; srest++)
+ {
+ m1 = 0;
+ for (psub = p + 1; ; psub = pnext)
+ {
+ pnext = patscan (psub, pe, '|');
+ /* If one of the patterns matches, just bail immediately. */
+ if (m1 = (gmatch (s, srest, psub, pnext - 1, flags) == 0))
+ break;
+ if (pnext == prest)
+ break;
+ }
+ if (m1 == 0 && gmatch (srest, se, prest, pe, flags) == 0)
+ return (0);
+ }
+ return (FNM_NOMATCH);
+ }
+
return (FNM_NOMATCH);
}
+#endif /* EXTENDED_GLOB */
+
+#ifdef TEST
+main (c, v)
+ int c;
+ char **v;
+{
+ char *string, *pat;
+
+ string = v[1];
+ pat = v[2];
+
+ if (fnmatch (pat, string, 0) == 0)
+ {
+ printf ("%s matches %s\n", string, pat);
+ exit (0);
+ }
+ else
+ {
+ printf ("%s does not match %s\n", string, pat);
+ exit (1);
+ }
+}
+#endif