1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
9 #if defined(LIBC_SCCS) && !defined(lint)
10 static const char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
11 #endif /* LIBC_SCCS and not lint */
14 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
15 * Compares a filename or pathname to a pattern.
26 static const char *rangematch(const char *, char, int);
29 fnmatch(const char *pattern, const char *string, int flags)
31 const char *stringstart;
34 for (stringstart = string;;)
35 switch (c = *pattern++) {
37 if ((flags & FNM_LEADING_DIR) && *string == '/')
39 return (*string == EOS ? 0 : FNM_NOMATCH);
43 if (*string == '/' && (flags & FNM_PATHNAME))
45 if (*string == '.' && (flags & FNM_PERIOD) &&
46 (string == stringstart ||
47 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
53 /* Collapse multiple stars. */
57 if (*string == '.' && (flags & FNM_PERIOD) &&
58 (string == stringstart ||
59 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
62 /* Optimize for pattern with * at end or before /. */
64 if (flags & FNM_PATHNAME)
65 return ((flags & FNM_LEADING_DIR) ||
66 strchr(string, '/') == NULL ?
70 else if (c == '/' && flags & FNM_PATHNAME) {
71 string = strchr(string, '/');
77 /* General case, use recursion. */
78 while ((test = *string) != EOS) {
79 if (!fnmatch(pattern, string,
82 if (test == '/' && flags & FNM_PATHNAME)
90 if (*string == '/' && flags & FNM_PATHNAME)
92 pattern = rangematch(pattern, *string, flags);
98 if (!(flags & FNM_NOESCAPE)) {
109 else if ((flags & FNM_CASEFOLD) &&
110 (tolower((unsigned char)c) ==
111 tolower((unsigned char)*string)))
113 else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
114 ((c == '/' && string != stringstart) ||
115 (string == stringstart+1 && *stringstart == '/')))
118 return (FNM_NOMATCH);
126 rangematch(const char *pattern, char test, int flags)
132 * A bracket expression starting with an unquoted circumflex
133 * character produces unspecified results (IEEE 1003.2-1992,
134 * 3.13.2). This implementation treats it like '!', for
135 * consistency with the regular expression syntax.
136 * J.T. Conklin (conklin@ngai.kaleida.com)
138 negate = (*pattern == '!' || *pattern == '^');
142 if (flags & FNM_CASEFOLD)
143 test = tolower((unsigned char)test);
145 for (ok = 0; (c = *pattern++) != ']';) {
146 if (c == '\\' && !(flags & FNM_NOESCAPE))
151 if (flags & FNM_CASEFOLD)
152 c = tolower((unsigned char)c);
155 if (*pattern == '-' && c2 != EOS && c2 != ']') {
157 if (c2 == '\\' && !(flags & FNM_NOESCAPE))
162 if (flags & FNM_CASEFOLD)
163 c2 = tolower((unsigned char)c2);
165 if ((unsigned char)c <= (unsigned char)test &&
166 (unsigned char)test <= (unsigned char)c2)
168 } else if (c == test)
171 return (ok == negate ? NULL : pattern);