199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
2*1fcd0b17SBruce Richardson * Copyright (c) 1989, 1993, 1994
3*1fcd0b17SBruce Richardson * The Regents of the University of California. All rights reserved.
4*1fcd0b17SBruce Richardson *
5*1fcd0b17SBruce Richardson * This code is derived from software contributed to Berkeley by
6*1fcd0b17SBruce Richardson * Guido van Rossum.
799a2dd95SBruce Richardson */
899a2dd95SBruce Richardson #ifndef _FNMATCH_H_
999a2dd95SBruce Richardson #define _FNMATCH_H_
1099a2dd95SBruce Richardson
11*1fcd0b17SBruce Richardson /*
12*1fcd0b17SBruce Richardson * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
13*1fcd0b17SBruce Richardson * Compares a filename or pathname to a pattern.
1499a2dd95SBruce Richardson */
1599a2dd95SBruce Richardson
16*1fcd0b17SBruce Richardson #include <ctype.h>
17*1fcd0b17SBruce Richardson #include <string.h>
18*1fcd0b17SBruce Richardson #include <stdio.h>
1999a2dd95SBruce Richardson
2099a2dd95SBruce Richardson #define FNM_NOMATCH 1
2199a2dd95SBruce Richardson
2299a2dd95SBruce Richardson #define FNM_NOESCAPE 0x01
2399a2dd95SBruce Richardson #define FNM_PATHNAME 0x02
2499a2dd95SBruce Richardson #define FNM_PERIOD 0x04
2599a2dd95SBruce Richardson #define FNM_LEADING_DIR 0x08
2699a2dd95SBruce Richardson #define FNM_CASEFOLD 0x10
2799a2dd95SBruce Richardson #define FNM_PREFIX_DIRS 0x20
2899a2dd95SBruce Richardson
29*1fcd0b17SBruce Richardson #define FNM_EOS '\0'
30*1fcd0b17SBruce Richardson
31*1fcd0b17SBruce Richardson static inline const char *
fnm_rangematch(const char * pattern,char test,int flags)32*1fcd0b17SBruce Richardson fnm_rangematch(const char *pattern, char test, int flags)
33*1fcd0b17SBruce Richardson {
34*1fcd0b17SBruce Richardson int negate, ok;
35*1fcd0b17SBruce Richardson char c, c2;
36*1fcd0b17SBruce Richardson
37*1fcd0b17SBruce Richardson /*
38*1fcd0b17SBruce Richardson * A bracket expression starting with an unquoted circumflex
39*1fcd0b17SBruce Richardson * character produces unspecified results (IEEE 1003.2-1992,
40*1fcd0b17SBruce Richardson * 3.13.2). This implementation treats it like '!', for
41*1fcd0b17SBruce Richardson * consistency with the regular expression syntax.
42*1fcd0b17SBruce Richardson * J.T. Conklin (conklin@ngai.kaleida.com)
43*1fcd0b17SBruce Richardson */
44*1fcd0b17SBruce Richardson negate = (*pattern == '!' || *pattern == '^');
45*1fcd0b17SBruce Richardson if (negate)
46*1fcd0b17SBruce Richardson ++pattern;
47*1fcd0b17SBruce Richardson
48*1fcd0b17SBruce Richardson if (flags & FNM_CASEFOLD)
49*1fcd0b17SBruce Richardson test = tolower((unsigned char)test);
50*1fcd0b17SBruce Richardson
51*1fcd0b17SBruce Richardson for (ok = 0; (c = *pattern++) != ']';) {
52*1fcd0b17SBruce Richardson if (c == '\\' && !(flags & FNM_NOESCAPE))
53*1fcd0b17SBruce Richardson c = *pattern++;
54*1fcd0b17SBruce Richardson if (c == FNM_EOS)
55*1fcd0b17SBruce Richardson return (NULL);
56*1fcd0b17SBruce Richardson
57*1fcd0b17SBruce Richardson if (flags & FNM_CASEFOLD)
58*1fcd0b17SBruce Richardson c = tolower((unsigned char)c);
59*1fcd0b17SBruce Richardson
60*1fcd0b17SBruce Richardson c2 = *(pattern + 1);
61*1fcd0b17SBruce Richardson if (*pattern == '-' && c2 != FNM_EOS && c2 != ']') {
62*1fcd0b17SBruce Richardson pattern += 2;
63*1fcd0b17SBruce Richardson if (c2 == '\\' && !(flags & FNM_NOESCAPE))
64*1fcd0b17SBruce Richardson c2 = *pattern++;
65*1fcd0b17SBruce Richardson if (c2 == FNM_EOS)
66*1fcd0b17SBruce Richardson return (NULL);
67*1fcd0b17SBruce Richardson
68*1fcd0b17SBruce Richardson if (flags & FNM_CASEFOLD)
69*1fcd0b17SBruce Richardson c2 = tolower((unsigned char)c2);
70*1fcd0b17SBruce Richardson
71*1fcd0b17SBruce Richardson if ((unsigned char)c <= (unsigned char)test &&
72*1fcd0b17SBruce Richardson (unsigned char)test <= (unsigned char)c2)
73*1fcd0b17SBruce Richardson ok = 1;
74*1fcd0b17SBruce Richardson } else if (c == test)
75*1fcd0b17SBruce Richardson ok = 1;
76*1fcd0b17SBruce Richardson }
77*1fcd0b17SBruce Richardson return (ok == negate ? NULL : pattern);
78*1fcd0b17SBruce Richardson }
79*1fcd0b17SBruce Richardson
8099a2dd95SBruce Richardson /**
817be78d02SJosh Soref * This function is used for searching a given string source
8299a2dd95SBruce Richardson * with the given regular expression pattern.
8399a2dd95SBruce Richardson *
8499a2dd95SBruce Richardson * @param pattern
854a6672c2SStephen Hemminger * regular expression notation describing the pattern to match
8699a2dd95SBruce Richardson *
8799a2dd95SBruce Richardson * @param string
887be78d02SJosh Soref * source string to search for the pattern
8999a2dd95SBruce Richardson *
9099a2dd95SBruce Richardson * @param flag
9199a2dd95SBruce Richardson * containing information about the pattern
9299a2dd95SBruce Richardson *
9399a2dd95SBruce Richardson * @return
9499a2dd95SBruce Richardson * if the pattern is found then return 0 or else FNM_NOMATCH
9599a2dd95SBruce Richardson */
96*1fcd0b17SBruce Richardson static inline int
fnmatch(const char * pattern,const char * string,int flags)97*1fcd0b17SBruce Richardson fnmatch(const char *pattern, const char *string, int flags)
98*1fcd0b17SBruce Richardson {
99*1fcd0b17SBruce Richardson const char *stringstart;
100*1fcd0b17SBruce Richardson char c, test;
10199a2dd95SBruce Richardson
102*1fcd0b17SBruce Richardson for (stringstart = string;;)
103*1fcd0b17SBruce Richardson switch (c = *pattern++) {
104*1fcd0b17SBruce Richardson case FNM_EOS:
105*1fcd0b17SBruce Richardson if ((flags & FNM_LEADING_DIR) && *string == '/')
106*1fcd0b17SBruce Richardson return (0);
107*1fcd0b17SBruce Richardson return (*string == FNM_EOS ? 0 : FNM_NOMATCH);
108*1fcd0b17SBruce Richardson case '?':
109*1fcd0b17SBruce Richardson if (*string == FNM_EOS)
110*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
111*1fcd0b17SBruce Richardson if (*string == '/' && (flags & FNM_PATHNAME))
112*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
113*1fcd0b17SBruce Richardson if (*string == '.' && (flags & FNM_PERIOD) &&
114*1fcd0b17SBruce Richardson (string == stringstart ||
115*1fcd0b17SBruce Richardson ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
116*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
117*1fcd0b17SBruce Richardson ++string;
118*1fcd0b17SBruce Richardson break;
119*1fcd0b17SBruce Richardson case '*':
120*1fcd0b17SBruce Richardson c = *pattern;
121*1fcd0b17SBruce Richardson /* Collapse multiple stars. */
122*1fcd0b17SBruce Richardson while (c == '*')
123*1fcd0b17SBruce Richardson c = *++pattern;
124*1fcd0b17SBruce Richardson
125*1fcd0b17SBruce Richardson if (*string == '.' && (flags & FNM_PERIOD) &&
126*1fcd0b17SBruce Richardson (string == stringstart ||
127*1fcd0b17SBruce Richardson ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
128*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
129*1fcd0b17SBruce Richardson
130*1fcd0b17SBruce Richardson /* Optimize for pattern with * at end or before /. */
131*1fcd0b17SBruce Richardson if (c == FNM_EOS)
132*1fcd0b17SBruce Richardson if (flags & FNM_PATHNAME)
133*1fcd0b17SBruce Richardson return ((flags & FNM_LEADING_DIR) ||
134*1fcd0b17SBruce Richardson strchr(string, '/') == NULL ?
135*1fcd0b17SBruce Richardson 0 : FNM_NOMATCH);
136*1fcd0b17SBruce Richardson else
137*1fcd0b17SBruce Richardson return (0);
138*1fcd0b17SBruce Richardson else if (c == '/' && flags & FNM_PATHNAME) {
139*1fcd0b17SBruce Richardson string = strchr(string, '/');
140*1fcd0b17SBruce Richardson if (string == NULL)
141*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
142*1fcd0b17SBruce Richardson break;
14399a2dd95SBruce Richardson }
144*1fcd0b17SBruce Richardson
145*1fcd0b17SBruce Richardson /* General case, use recursion. */
146*1fcd0b17SBruce Richardson while ((test = *string) != FNM_EOS) {
147*1fcd0b17SBruce Richardson if (!fnmatch(pattern, string,
148*1fcd0b17SBruce Richardson flags & ~FNM_PERIOD))
149*1fcd0b17SBruce Richardson return (0);
150*1fcd0b17SBruce Richardson if (test == '/' && flags & FNM_PATHNAME)
151*1fcd0b17SBruce Richardson break;
152*1fcd0b17SBruce Richardson ++string;
153*1fcd0b17SBruce Richardson }
154*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
155*1fcd0b17SBruce Richardson case '[':
156*1fcd0b17SBruce Richardson if (*string == FNM_EOS)
157*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
158*1fcd0b17SBruce Richardson if (*string == '/' && flags & FNM_PATHNAME)
159*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
160*1fcd0b17SBruce Richardson pattern = fnm_rangematch(pattern, *string, flags);
161*1fcd0b17SBruce Richardson if (pattern == NULL)
162*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
163*1fcd0b17SBruce Richardson ++string;
164*1fcd0b17SBruce Richardson break;
165*1fcd0b17SBruce Richardson case '\\':
166*1fcd0b17SBruce Richardson if (!(flags & FNM_NOESCAPE)) {
167*1fcd0b17SBruce Richardson c = *pattern++;
168*1fcd0b17SBruce Richardson if (c == FNM_EOS) {
169*1fcd0b17SBruce Richardson c = '\\';
170*1fcd0b17SBruce Richardson --pattern;
171*1fcd0b17SBruce Richardson }
172*1fcd0b17SBruce Richardson }
173*1fcd0b17SBruce Richardson /* FALLTHROUGH */
174*1fcd0b17SBruce Richardson default:
175*1fcd0b17SBruce Richardson if (c == *string)
176*1fcd0b17SBruce Richardson ;
177*1fcd0b17SBruce Richardson else if ((flags & FNM_CASEFOLD) &&
178*1fcd0b17SBruce Richardson (tolower((unsigned char)c) ==
179*1fcd0b17SBruce Richardson tolower((unsigned char)*string)))
180*1fcd0b17SBruce Richardson ;
181*1fcd0b17SBruce Richardson else if ((flags & FNM_PREFIX_DIRS) && *string == FNM_EOS &&
182*1fcd0b17SBruce Richardson ((c == '/' && string != stringstart) ||
183*1fcd0b17SBruce Richardson (string == stringstart+1 && *stringstart == '/')))
184*1fcd0b17SBruce Richardson return (0);
185*1fcd0b17SBruce Richardson else
186*1fcd0b17SBruce Richardson return (FNM_NOMATCH);
187*1fcd0b17SBruce Richardson string++;
188*1fcd0b17SBruce Richardson break;
189*1fcd0b17SBruce Richardson }
190*1fcd0b17SBruce Richardson /* NOTREACHED */
191*1fcd0b17SBruce Richardson }
19299a2dd95SBruce Richardson
19399a2dd95SBruce Richardson #endif /* _FNMATCH_H_ */
194