1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "curlcheck.h" |
23 | |
24 | #include "curl_fnmatch.h" |
25 | |
26 | static CURLcode unit_setup(void) |
27 | { |
28 | return CURLE_OK; |
29 | } |
30 | |
31 | static void unit_stop(void) |
32 | { |
33 | } |
34 | |
35 | #ifndef CURL_DISABLE_FTP |
36 | |
37 | /* |
38 | CURL_FNMATCH_MATCH 0 |
39 | CURL_FNMATCH_NOMATCH 1 |
40 | CURL_FNMATCH_FAIL 2 |
41 | */ |
42 | |
43 | #define MATCH CURL_FNMATCH_MATCH |
44 | #define NOMATCH CURL_FNMATCH_NOMATCH |
45 | |
46 | #define LINUX_DIFFER 0x80 |
47 | #define LINUX_SHIFT 8 |
48 | #define LINUX_MATCH ((CURL_FNMATCH_MATCH << LINUX_SHIFT) | LINUX_DIFFER) |
49 | #define LINUX_NOMATCH ((CURL_FNMATCH_NOMATCH << LINUX_SHIFT) | LINUX_DIFFER) |
50 | #define LINUX_FAIL ((CURL_FNMATCH_FAIL << LINUX_SHIFT) | LINUX_DIFFER) |
51 | |
52 | #define MAC_DIFFER 0x40 |
53 | #define MAC_SHIFT 16 |
54 | #define MAC_MATCH ((CURL_FNMATCH_MATCH << MAC_SHIFT) | MAC_DIFFER) |
55 | #define MAC_NOMATCH ((CURL_FNMATCH_NOMATCH << MAC_SHIFT) | MAC_DIFFER) |
56 | #define MAC_FAIL ((CURL_FNMATCH_FAIL << MAC_SHIFT) | MAC_DIFFER) |
57 | |
58 | struct testcase { |
59 | const char *pattern; |
60 | const char *string; |
61 | int result; |
62 | }; |
63 | |
64 | static const struct testcase tests[] = { |
65 | /* brackets syntax */ |
66 | {"*[*[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
67 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
68 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\001\177[[[[[[[[[[[[[[[[[[[[[" , |
69 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
70 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" |
71 | "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" , |
72 | NOMATCH|MAC_FAIL}, |
73 | |
74 | { "\\[" , "[" , MATCH }, |
75 | { "[" , "[" , NOMATCH|LINUX_MATCH|MAC_FAIL}, |
76 | { "[]" , "[]" , NOMATCH|LINUX_MATCH|MAC_FAIL}, |
77 | { "[][]" , "[" , MATCH }, |
78 | { "[][]" , "]" , MATCH }, |
79 | { "[[]" , "[" , MATCH }, |
80 | { "[[[]" , "[" , MATCH }, |
81 | { "[[[[]" , "[" , MATCH }, |
82 | { "[[[[]" , "[" , MATCH }, |
83 | |
84 | { "[][[]" , "]" , MATCH }, |
85 | { "[][[[]" , "[" , MATCH }, |
86 | { "[[]" , "]" , NOMATCH }, |
87 | |
88 | { "[a@]" , "a" , MATCH }, |
89 | |
90 | { "[a-z]" , "a" , MATCH }, |
91 | { "[a-z]" , "A" , NOMATCH }, |
92 | { "?[a-z]" , "?Z" , NOMATCH }, |
93 | { "[A-Z]" , "C" , MATCH }, |
94 | { "[A-Z]" , "c" , NOMATCH }, |
95 | { "[0-9]" , "7" , MATCH }, |
96 | { "[7-8]" , "7" , MATCH }, |
97 | { "[7-]" , "7" , MATCH }, |
98 | { "[7-]" , "-" , MATCH }, |
99 | { "[7-]" , "[" , NOMATCH }, |
100 | { "[a-bA-F]" , "F" , MATCH }, |
101 | { "[a-bA-B9]" , "9" , MATCH }, |
102 | { "[a-bA-B98]" , "8" , MATCH }, |
103 | { "[a-bA-B98]" , "C" , NOMATCH }, |
104 | { "[a-bA-Z9]" , "F" , MATCH }, |
105 | { "[a-bA-Z9]ero*" , "Zero chance." , MATCH }, |
106 | { "S[a-][x]opho*" , "Saxophone" , MATCH }, |
107 | { "S[a-][x]opho*" , "SaXophone" , NOMATCH }, |
108 | { "S[a-][x]*.txt" , "S-x.txt" , MATCH }, |
109 | { "[\\a-\\b]" , "a" , MATCH }, |
110 | { "[\\a-\\b]" , "b" , MATCH }, |
111 | { "[?*[][?*[][?*[]" , "?*[" , MATCH }, |
112 | { "[][?*-]" , "]" , MATCH }, |
113 | { "[][?*-]" , "[" , MATCH }, |
114 | { "[][?*-]" , "?" , MATCH }, |
115 | { "[][?*-]" , "*" , MATCH }, |
116 | { "[][?*-]" , "-" , MATCH }, |
117 | { "[]?*-]" , "-" , MATCH }, |
118 | { "[\xFF]" , "\xFF" , MATCH|LINUX_FAIL|MAC_FAIL}, |
119 | { "?/b/c" , "a/b/c" , MATCH }, |
120 | { "^_{}~" , "^_{}~" , MATCH }, |
121 | { "!#%+,-./01234567889" , "!#%+,-./01234567889" , MATCH }, |
122 | { "PQRSTUVWXYZ]abcdefg" , "PQRSTUVWXYZ]abcdefg" , MATCH }, |
123 | { ":;=@ABCDEFGHIJKLMNO" , ":;=@ABCDEFGHIJKLMNO" , MATCH }, |
124 | |
125 | /* negate */ |
126 | { "[!a]" , "b" , MATCH }, |
127 | { "[!a]" , "a" , NOMATCH }, |
128 | { "[^a]" , "b" , MATCH }, |
129 | { "[^a]" , "a" , NOMATCH }, |
130 | { "[^a-z0-9A-Z]" , "a" , NOMATCH }, |
131 | { "[^a-z0-9A-Z]" , "-" , MATCH }, |
132 | { "curl[!a-z]lib" , "curl lib" , MATCH }, |
133 | { "curl[! ]lib" , "curl lib" , NOMATCH }, |
134 | { "[! ][ ]" , " " , NOMATCH }, |
135 | { "[! ][ ]" , "a " , MATCH }, |
136 | { "*[^a].t?t" , "a.txt" , NOMATCH }, |
137 | { "*[^a].t?t" , "ba.txt" , NOMATCH }, |
138 | { "*[^a].t?t" , "ab.txt" , MATCH }, |
139 | { "*[^a]" , "" , NOMATCH }, |
140 | { "[!\xFF]" , "" , NOMATCH|LINUX_FAIL}, |
141 | { "[!\xFF]" , "\xFF" , NOMATCH|LINUX_FAIL|MAC_FAIL}, |
142 | { "[!\xFF]" , "a" , MATCH|LINUX_FAIL|MAC_FAIL}, |
143 | { "[!?*[]" , "?" , NOMATCH }, |
144 | { "[!!]" , "!" , NOMATCH }, |
145 | { "[!!]" , "x" , MATCH }, |
146 | |
147 | { "[[:alpha:]]" , "a" , MATCH }, |
148 | { "[[:alpha:]]" , "9" , NOMATCH }, |
149 | { "[[:alnum:]]" , "a" , MATCH }, |
150 | { "[[:alnum:]]" , "[" , NOMATCH }, |
151 | { "[[:alnum:]]" , "]" , NOMATCH }, |
152 | { "[[:alnum:]]" , "9" , MATCH }, |
153 | { "[[:digit:]]" , "9" , MATCH }, |
154 | { "[[:xdigit:]]" , "9" , MATCH }, |
155 | { "[[:xdigit:]]" , "F" , MATCH }, |
156 | { "[[:xdigit:]]" , "G" , NOMATCH }, |
157 | { "[[:upper:]]" , "U" , MATCH }, |
158 | { "[[:upper:]]" , "u" , NOMATCH }, |
159 | { "[[:lower:]]" , "l" , MATCH }, |
160 | { "[[:lower:]]" , "L" , NOMATCH }, |
161 | { "[[:print:]]" , "L" , MATCH }, |
162 | { "[[:print:]]" , "\10" , NOMATCH }, |
163 | { "[[:print:]]" , "\10" , NOMATCH }, |
164 | { "[[:space:]]" , " " , MATCH }, |
165 | { "[[:space:]]" , "x" , NOMATCH }, |
166 | { "[[:graph:]]" , " " , NOMATCH }, |
167 | { "[[:graph:]]" , "x" , MATCH }, |
168 | { "[[:blank:]]" , "\t" , MATCH }, |
169 | { "[[:blank:]]" , " " , MATCH }, |
170 | { "[[:blank:]]" , "\r" , NOMATCH }, |
171 | { "[^[:blank:]]" , "\t" , NOMATCH }, |
172 | { "[^[:print:]]" , "\10" , MATCH }, |
173 | { "[[:lower:]][[:lower:]]" , "ll" , MATCH }, |
174 | { "[[:foo:]]" , "bar" , NOMATCH|MAC_FAIL}, |
175 | { "[[:foo:]]" , "f]" , MATCH|LINUX_NOMATCH|MAC_FAIL}, |
176 | |
177 | { "Curl[[:blank:]];-)" , "Curl ;-)" , MATCH }, |
178 | { "*[[:blank:]]*" , " " , MATCH }, |
179 | { "*[[:blank:]]*" , "" , NOMATCH }, |
180 | { "*[[:blank:]]*" , "hi, im_Pavel" , MATCH }, |
181 | |
182 | /* common using */ |
183 | { "filename.dat" , "filename.dat" , MATCH }, |
184 | { "*curl*" , "lets use curl!!" , MATCH }, |
185 | { "filename.txt" , "filename.dat" , NOMATCH }, |
186 | { "*.txt" , "text.txt" , MATCH }, |
187 | { "*.txt" , "a.txt" , MATCH }, |
188 | { "*.txt" , ".txt" , MATCH }, |
189 | { "*.txt" , "txt" , NOMATCH }, |
190 | { "??.txt" , "99.txt" , MATCH }, |
191 | { "??.txt" , "a99.txt" , NOMATCH }, |
192 | { "?.???" , "a.txt" , MATCH }, |
193 | { "*.???" , "somefile.dat" , MATCH }, |
194 | { "*.???" , "photo.jpeg" , NOMATCH }, |
195 | { ".*" , ".htaccess" , MATCH }, |
196 | { ".*" , "." , MATCH }, |
197 | { ".*" , ".." , MATCH }, |
198 | |
199 | /* many stars => one star */ |
200 | { "**.txt" , "text.txt" , MATCH }, |
201 | { "***.txt" , "t.txt" , MATCH }, |
202 | { "****.txt" , ".txt" , MATCH }, |
203 | |
204 | /* empty string or pattern */ |
205 | { "" , "" , MATCH }, |
206 | { "" , "hello" , NOMATCH }, |
207 | { "file" , "" , NOMATCH }, |
208 | { "?" , "" , NOMATCH }, |
209 | { "*" , "" , MATCH }, |
210 | { "x" , "" , NOMATCH }, |
211 | |
212 | /* backslash */ |
213 | { "\\" , "\\" , MATCH|LINUX_NOMATCH}, |
214 | { "\\\\" , "\\" , MATCH }, |
215 | { "\\\\" , "\\\\" , NOMATCH }, |
216 | { "\\?" , "?" , MATCH }, |
217 | { "\\*" , "*" , MATCH }, |
218 | { "?.txt" , "?.txt" , MATCH }, |
219 | { "*.txt" , "*.txt" , MATCH }, |
220 | { "\\?.txt" , "?.txt" , MATCH }, |
221 | { "\\*.txt" , "*.txt" , MATCH }, |
222 | { "\\?.txt" , "x.txt" , NOMATCH }, |
223 | { "\\*.txt" , "x.txt" , NOMATCH }, |
224 | { "\\*\\\\.txt" , "*\\.txt" , MATCH }, |
225 | { "*\\**\\?*\\\\*" , "cc*cc?cccc" , NOMATCH }, |
226 | { "*\\?*\\**" , "cc?cc" , NOMATCH }, |
227 | { "\\\"\\$\\&\\'\\(\\)" , "\"$&'()" , MATCH }, |
228 | { "\\*\\?\\[\\\\\\`\\|" , "*?[\\`|" , MATCH }, |
229 | { "[\\a\\b]c" , "ac" , MATCH }, |
230 | { "[\\a\\b]c" , "bc" , MATCH }, |
231 | { "[\\a\\b]d" , "bc" , NOMATCH }, |
232 | { "[a-bA-B\\?]" , "?" , MATCH }, |
233 | { "cu[a-ab-b\\r]l" , "curl" , MATCH }, |
234 | { "[\\a-z]" , "c" , MATCH }, |
235 | |
236 | { "?*?*?.*?*" , "abc.c" , MATCH }, |
237 | { "?*?*?.*?*" , "abcc" , NOMATCH }, |
238 | { "?*?*?.*?*" , "abc." , NOMATCH }, |
239 | { "?*?*?.*?*" , "abc.c++" , MATCH }, |
240 | { "?*?*?.*?*" , "abcdef.c++" , MATCH }, |
241 | { "?*?*?.?" , "abcdef.c" , MATCH }, |
242 | { "?*?*?.?" , "abcdef.cd" , NOMATCH }, |
243 | |
244 | { "Lindmätarv" , "Lindmätarv" , MATCH }, |
245 | |
246 | { "" , "" , MATCH}, |
247 | {"**]*[*[\x13]**[*\x13)]*]*[**[*\x13~r-]*]**[.*]*[\xe3\xe3\xe3\xe3\xe3\xe3" |
248 | "\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3\xe3" |
249 | "\xe3\xe3\xe3\xe3\xe3*[\x13]**[*\x13)]*]*[*[\x13]*[~r]*]*\xba\x13\xa6~b-]*" , |
250 | "a" , NOMATCH|LINUX_FAIL} |
251 | }; |
252 | |
253 | static const char *ret2name(int i) |
254 | { |
255 | switch(i) { |
256 | case 0: |
257 | return "MATCH" ; |
258 | case 1: |
259 | return "NOMATCH" ; |
260 | case 2: |
261 | return "FAIL" ; |
262 | default: |
263 | return "unknown" ; |
264 | } |
265 | /* not reached */ |
266 | } |
267 | |
268 | enum system { |
269 | SYSTEM_CUSTOM, |
270 | SYSTEM_LINUX, |
271 | SYSTEM_MACOS |
272 | }; |
273 | |
274 | UNITTEST_START |
275 | { |
276 | int testnum = sizeof(tests) / sizeof(struct testcase); |
277 | int i; |
278 | enum system machine; |
279 | |
280 | #ifdef HAVE_FNMATCH |
281 | if(strstr(OS, "apple" ) || strstr(OS, "darwin" )) { |
282 | machine = SYSTEM_MACOS; |
283 | } |
284 | else |
285 | machine = SYSTEM_LINUX; |
286 | printf("Tested with system fnmatch(), %s-style\n" , |
287 | machine == SYSTEM_LINUX ? "linux" : "mac" ); |
288 | #else |
289 | printf("Tested with custom fnmatch()\n" ); |
290 | machine = SYSTEM_CUSTOM; |
291 | #endif |
292 | |
293 | for(i = 0; i < testnum; i++) { |
294 | int result = tests[i].result; |
295 | int rc = Curl_fnmatch(NULL, tests[i].pattern, tests[i].string); |
296 | if(result & (LINUX_DIFFER|MAC_DIFFER)) { |
297 | if((result & LINUX_DIFFER) && (machine == SYSTEM_LINUX)) |
298 | result >>= LINUX_SHIFT; |
299 | else if((result & MAC_DIFFER) && (machine == SYSTEM_MACOS)) |
300 | result >>= MAC_SHIFT; |
301 | result &= 0x03; /* filter off all high bits */ |
302 | } |
303 | if(rc != result) { |
304 | printf("Curl_fnmatch(\"%s\", \"%s\") should return %s (returns %s)" |
305 | " [%d]\n" , |
306 | tests[i].pattern, tests[i].string, ret2name(result), |
307 | ret2name(rc), i); |
308 | fail("pattern mismatch" ); |
309 | } |
310 | } |
311 | } |
312 | UNITTEST_STOP |
313 | |
314 | #else |
315 | |
316 | UNITTEST_START |
317 | { |
318 | /* nothing to do, just fail */ |
319 | return 1; |
320 | } |
321 | UNITTEST_STOP |
322 | |
323 | #endif |
324 | |