update fnmatch to POSIX 2008 semantics
[musl] / src / regex / fnmatch.c
1 #include <fnmatch.h>
2 #include <wctype.h>
3 #include <string.h>
4 #include <wchar.h>
5 #include <stdlib.h>
6 #include <limits.h>
7
8 static int next(const char **s)
9 {
10         wchar_t c;
11         int l = mbtowc(&c, *s, MB_LEN_MAX);
12         /* hack to allow literal matches of invalid byte sequences */
13         if (l < 0) return (unsigned char)*(*s)++ - 0x100;
14         *s += l;
15         return c;
16 }
17
18 #define BRACKET_ERROR  -0x100
19 #define BRACKET_NOCHAR -0x101
20
21 static int bracket_next(const char **s)
22 {
23         int c;
24         int type;
25         if (**s == '[') {
26                 type = *(*s+1);
27                 if (type == '.' || type == '=') {
28                         *s += 2;
29                         c = next(s);
30                         if (c <= 0) return BRACKET_ERROR;
31                         if (**s == type && *(*s+1) == ']') {
32                                 *s += 2;
33                                 return c;
34                         }
35                         for (; **s && (**s != type || *(*s+1) != ']'); (*s)++);
36                         if (!**s) return BRACKET_ERROR;
37                         *s += 2;
38                         return BRACKET_NOCHAR;
39                 }
40         }
41         c = next(s);
42         if (c <= 0) return BRACKET_ERROR;
43         return c;
44 }
45
46 #define __FNM_CONT 0x8000
47
48 int fnmatch(const char *p, const char *s, int flags)
49 {
50         int c, d, k;
51         int not;
52         int match;
53         int first;
54         int no_slash = (flags & FNM_PATHNAME) ? '/' : 0;
55         int no_period = (flags & FNM_PERIOD) && !(flags & __FNM_CONT) ? '.' : 0x100;
56         const char *p1;
57
58         flags |= __FNM_CONT;
59
60         while ((c = *p++)) {
61                 switch (c) {
62                 case '?':
63                         k = next(&s);
64                         if (!k || k == no_period || k == no_slash)
65                                 return FNM_NOMATCH;
66                         break;
67                 case '\\':
68                         if (!(flags & FNM_NOESCAPE)) {
69                                 c = *p++;
70                                 goto literal;
71                         }
72                         if (*s++ != c) return FNM_NOMATCH;
73                         break;
74                 case '*':
75                         for (; *p == '*'; p++);
76                         if (*p && !*s) return FNM_NOMATCH;
77                         if (*s == no_period)
78                                 return FNM_NOMATCH;
79                         if (!*p && (!no_slash || !strchr(s, no_slash)))
80                                 return 0;
81                         for (; *s; s++)
82                                 if (!fnmatch(p, s, flags))
83                                         return 0;
84                                 else if (*s == no_slash)
85                                         break;
86                         return FNM_NOMATCH;
87                 case '[':
88                         p1 = p-1;
89                         not = (*p == '!' || *p == '^');
90                         if (not) p++;
91                         k = next(&s);
92                         if (!k || k == no_slash || k == no_period)
93                                 return FNM_NOMATCH;
94                         match = 0;
95                         first = 1;
96                         for (;;) {
97                                 if (!*p) goto literal_bracket;
98                                 if (*p == ']' && !first) break;
99                                 first = 0;
100                                 if (*p == '[' && *(p+1) == ':') {
101                                         const char *z;
102                                         p += 2;
103                                         for (z=p; *z && (*z != ':' || *(z+1) != ']'); z++);
104                                         if (!*z || z-p > 32) { /* FIXME: symbolic const? */
105                                                 return FNM_NOMATCH;
106                                         } else {
107                                                 char class[33];
108                                                 memcpy(class, p, z-p);
109                                                 class[z-p] = 0;
110                                                 if (iswctype(k, wctype(class)))
111                                                         match = 1;
112                                         }
113                                         p = z+2;
114                                         continue;
115                                 }
116                                 c = bracket_next(&p);
117                                 if (c == BRACKET_ERROR) {
118 literal_bracket:
119                                         match = (k=='[');
120                                         p = p1;
121                                         not = 0;
122                                         break;
123                                 }
124                                 if (c == BRACKET_NOCHAR)
125                                         continue;
126                                 if (*p == '-' && *(p+1) != ']') {
127                                         p++;
128                                         d = bracket_next(&p);
129                                         if (d == BRACKET_ERROR)
130                                                 goto literal_bracket;
131                                         if (d == BRACKET_NOCHAR)
132                                                 continue;
133                                         if (k >= c && k <= d)
134                                                 match = 1;
135                                         continue;
136                                 }
137                                 if (k == c) match = 1;
138                         }
139                         p++;
140                         if (not == match)
141                                 return FNM_NOMATCH;
142                         break;
143                 default:
144                 literal:
145                         if (*s++ != c)
146                                 return FNM_NOMATCH;
147                         if (c == no_slash && (flags & FNM_PERIOD)) {
148                                 no_period = '.';
149                                 continue;
150                         }
151                         break;
152                 }
153                 no_period = 0x100;
154         }
155         if (*s) return FNM_NOMATCH;
156         return 0;
157 }