121aa27c5Sbostic /*
2*14744ecfSbostic * Copyright (c) 1989, 1993, 1994
3bac379f5Sbostic * The Regents of the University of California. All rights reserved.
421aa27c5Sbostic *
521aa27c5Sbostic * This code is derived from software contributed to Berkeley by
621aa27c5Sbostic * Guido van Rossum.
721aa27c5Sbostic *
8bcb80f39Sbostic * %sccs.include.redist.c%
921aa27c5Sbostic */
1021aa27c5Sbostic
1121aa27c5Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*14744ecfSbostic static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 04/16/94";
1321aa27c5Sbostic #endif /* LIBC_SCCS and not lint */
1421aa27c5Sbostic
1521aa27c5Sbostic /*
16*14744ecfSbostic * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
1721aa27c5Sbostic * Compares a filename or pathname to a pattern.
1821aa27c5Sbostic */
1921aa27c5Sbostic
20c5d7c245Sbostic #include <fnmatch.h>
2121aa27c5Sbostic #include <string.h>
2221aa27c5Sbostic
2321aa27c5Sbostic #define EOS '\0'
2421aa27c5Sbostic
25*14744ecfSbostic static const char *rangematch __P((const char *, int, int));
26c5d7c245Sbostic
27*14744ecfSbostic int
fnmatch(pattern,string,flags)28c5d7c245Sbostic fnmatch(pattern, string, flags)
29*14744ecfSbostic const char *pattern, *string;
30c5d7c245Sbostic int flags;
31c5d7c245Sbostic {
32*14744ecfSbostic const char *stringstart;
33*14744ecfSbostic char c, test;
34c5d7c245Sbostic
35*14744ecfSbostic for (stringstart = string;;)
36c5d7c245Sbostic switch (c = *pattern++) {
37c5d7c245Sbostic case EOS:
38c5d7c245Sbostic return (*string == EOS ? 0 : FNM_NOMATCH);
39c5d7c245Sbostic case '?':
40*14744ecfSbostic if (*string == EOS)
41c5d7c245Sbostic return (FNM_NOMATCH);
42*14744ecfSbostic if (*string == '/' && (flags & FNM_PATHNAME))
43*14744ecfSbostic return (FNM_NOMATCH);
44*14744ecfSbostic if (*string == '.' && (flags & FNM_PERIOD) &&
45*14744ecfSbostic (string == stringstart ||
46*14744ecfSbostic ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
47*14744ecfSbostic return (FNM_NOMATCH);
48*14744ecfSbostic ++string;
49c5d7c245Sbostic break;
50c5d7c245Sbostic case '*':
51c5d7c245Sbostic c = *pattern;
52c5d7c245Sbostic /* Collapse multiple stars. */
53c5d7c245Sbostic while (c == '*')
54c5d7c245Sbostic c = *++pattern;
55c5d7c245Sbostic
56*14744ecfSbostic if (*string == '.' && (flags & FNM_PERIOD) &&
57*14744ecfSbostic (string == stringstart ||
58*14744ecfSbostic ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
59*14744ecfSbostic return (FNM_NOMATCH);
60*14744ecfSbostic
61c5d7c245Sbostic /* Optimize for pattern with * at end or before /. */
62c5d7c245Sbostic if (c == EOS)
63c5d7c245Sbostic if (flags & FNM_PATHNAME)
64*14744ecfSbostic return (strchr(string, '/') == NULL ?
65c5d7c245Sbostic 0 : FNM_NOMATCH);
66c5d7c245Sbostic else
67c5d7c245Sbostic return (0);
68c5d7c245Sbostic else if (c == '/' && flags & FNM_PATHNAME) {
69*14744ecfSbostic if ((string = strchr(string, '/')) == NULL)
70c5d7c245Sbostic return (FNM_NOMATCH);
71c5d7c245Sbostic break;
72c5d7c245Sbostic }
73c5d7c245Sbostic
74c5d7c245Sbostic /* General case, use recursion. */
75c5d7c245Sbostic while ((test = *string) != EOS) {
76*14744ecfSbostic if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
77c5d7c245Sbostic return (0);
78c5d7c245Sbostic if (test == '/' && flags & FNM_PATHNAME)
79c5d7c245Sbostic break;
80c5d7c245Sbostic ++string;
81c5d7c245Sbostic }
82c5d7c245Sbostic return (FNM_NOMATCH);
83c5d7c245Sbostic case '[':
84*14744ecfSbostic if (*string == EOS)
85c5d7c245Sbostic return (FNM_NOMATCH);
86*14744ecfSbostic if (*string == '/' && flags & FNM_PATHNAME)
87c5d7c245Sbostic return (FNM_NOMATCH);
88*14744ecfSbostic if ((pattern =
89*14744ecfSbostic rangematch(pattern, *string, flags)) == NULL)
90*14744ecfSbostic return (FNM_NOMATCH);
91*14744ecfSbostic ++string;
92c5d7c245Sbostic break;
93c5d7c245Sbostic case '\\':
94c5d7c245Sbostic if (!(flags & FNM_NOESCAPE)) {
95c5d7c245Sbostic if ((c = *pattern++) == EOS) {
96c5d7c245Sbostic c = '\\';
97c5d7c245Sbostic --pattern;
98c5d7c245Sbostic }
99c5d7c245Sbostic }
100c5d7c245Sbostic /* FALLTHROUGH */
101c5d7c245Sbostic default:
102c5d7c245Sbostic if (c != *string++)
103c5d7c245Sbostic return (FNM_NOMATCH);
104c5d7c245Sbostic break;
105c5d7c245Sbostic }
106c5d7c245Sbostic /* NOTREACHED */
107c5d7c245Sbostic }
108c5d7c245Sbostic
1091444a3beSbostic static const char *
rangematch(pattern,test,flags)110*14744ecfSbostic rangematch(pattern, test, flags)
111*14744ecfSbostic const char *pattern;
112*14744ecfSbostic int test, flags;
113a3293107Sbostic {
114a3293107Sbostic int negate, ok;
115*14744ecfSbostic char c, c2;
116a3293107Sbostic
117a3293107Sbostic /*
118*14744ecfSbostic * A bracket expression starting with an unquoted circumflex
119*14744ecfSbostic * character produces unspecified results (IEEE 1003.2-1992,
120*14744ecfSbostic * 3.13.2). This implementation treats it like '!', for
121*14744ecfSbostic * consistency with the regular expression syntax.
122*14744ecfSbostic * J.T. Conklin (conklin@ngai.kaleida.com)
123a3293107Sbostic */
124*14744ecfSbostic if (negate = (*pattern == '!' || *pattern == '^'))
125*14744ecfSbostic ++pattern;
126*14744ecfSbostic
127a3293107Sbostic for (ok = 0; (c = *pattern++) != ']';) {
128*14744ecfSbostic if (c == '\\' && !(flags & FNM_NOESCAPE))
129*14744ecfSbostic c = *pattern++;
130a3293107Sbostic if (c == EOS)
131*14744ecfSbostic return (NULL);
132*14744ecfSbostic if (*pattern == '-'
133*14744ecfSbostic && (c2 = *(pattern+1)) != EOS && c2 != ']') {
134*14744ecfSbostic pattern += 2;
135*14744ecfSbostic if (c2 == '\\' && !(flags & FNM_NOESCAPE))
136*14744ecfSbostic c2 = *pattern++;
137*14744ecfSbostic if (c2 == EOS)
138*14744ecfSbostic return (NULL);
139a3293107Sbostic if (c <= test && test <= c2)
140a3293107Sbostic ok = 1;
141*14744ecfSbostic } else if (c == test)
142a3293107Sbostic ok = 1;
143a3293107Sbostic }
144a3293107Sbostic return (ok == negate ? NULL : pattern);
145a3293107Sbostic }
146