]>
Commit | Line | Data |
---|---|---|
83b1b6d8 | 1 | /* Tests for fnmatch function. |
d614a753 | 2 | Copyright (C) 2000-2020 Free Software Foundation, Inc. |
83b1b6d8 UD |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
83b1b6d8 UD |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
83b1b6d8 | 14 | |
41bdb6e2 | 15 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 16 | License along with the GNU C Library; if not, see |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
83b1b6d8 UD |
18 | |
19 | #include <errno.h> | |
20 | #include <error.h> | |
21 | #include <fnmatch.h> | |
22 | #include <locale.h> | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <sys/types.h> | |
3540d66b | 27 | #include <mcheck.h> |
83b1b6d8 UD |
28 | |
29 | ||
30 | static char *next_input (char **line, int first, int last); | |
31 | static int convert_flags (const char *str); | |
32 | static char *flag_output (int flags); | |
33 | static char *escape (const char *str, size_t *reslenp, char **resbuf); | |
34 | ||
35 | ||
29955b5d AS |
36 | static int |
37 | do_test (void) | |
83b1b6d8 UD |
38 | { |
39 | char *linebuf = NULL; | |
40 | size_t linebuflen = 0; | |
41 | int ntests = 0; | |
42 | int nfailed = 0; | |
43 | char *escinput = NULL; | |
44 | size_t escinputlen = 0; | |
45 | char *escpattern = NULL; | |
46 | size_t escpatternlen = 0; | |
47 | int nr = 0; | |
48 | ||
3540d66b AS |
49 | mtrace (); |
50 | ||
83b1b6d8 UD |
51 | /* Read lines from stdin with the following format: |
52 | ||
53 | locale input-string match-string flags result | |
54 | ||
55 | where `result' is either 0 or 1. If the first character of a | |
56 | string is '"' we read until the next '"' and handled escaped '"'. */ | |
57 | while (! feof (stdin)) | |
58 | { | |
59 | ssize_t n = getline (&linebuf, &linebuflen, stdin); | |
60 | char *cp; | |
61 | const char *locale; | |
62 | const char *input; | |
63 | const char *pattern; | |
64 | const char *result_str; | |
65 | int result; | |
66 | const char *flags; | |
67 | int flags_val; | |
68 | int fnmres; | |
69 | char numbuf[24]; | |
70 | ||
71 | if (n == -1) | |
72 | break; | |
73 | ||
74 | if (n == 0) | |
75 | /* Maybe an empty line. */ | |
76 | continue; | |
77 | ||
78 | /* Skip over all leading white spaces. */ | |
79 | cp = linebuf; | |
80 | ||
81 | locale = next_input (&cp, 1, 0); | |
82 | if (locale == NULL) | |
83 | continue; | |
84 | ||
85 | input = next_input (&cp, 0, 0); | |
86 | if (input == NULL) | |
87 | continue; | |
88 | ||
89 | pattern = next_input (&cp, 0, 0); | |
90 | if (pattern == NULL) | |
91 | continue; | |
92 | ||
93 | result_str = next_input (&cp, 0, 0); | |
94 | if (result_str == NULL) | |
95 | continue; | |
96 | ||
97 | if (strcmp (result_str, "0") == 0) | |
98 | result = 0; | |
99 | else if (strcasecmp (result_str, "NOMATCH") == 0) | |
100 | result = FNM_NOMATCH; | |
101 | else | |
102 | { | |
103 | char *endp; | |
104 | result = strtol (result_str, &endp, 0); | |
105 | if (*endp != '\0') | |
106 | continue; | |
107 | } | |
108 | ||
109 | flags = next_input (&cp, 0, 1); | |
110 | if (flags == NULL) | |
111 | /* We allow the flags missing. */ | |
112 | flags = ""; | |
113 | ||
114 | /* Convert the text describing the flags in a numeric value. */ | |
115 | flags_val = convert_flags (flags); | |
116 | if (flags_val == -1) | |
117 | /* Something went wrong. */ | |
118 | continue; | |
119 | ||
120 | /* Now run the actual test. */ | |
121 | ++ntests; | |
122 | ||
21d9e5cf UD |
123 | if (setlocale (LC_COLLATE, locale) == NULL |
124 | || setlocale (LC_CTYPE, locale) == NULL) | |
83b1b6d8 UD |
125 | { |
126 | puts ("*** Cannot set locale"); | |
127 | ++nfailed; | |
128 | continue; | |
129 | } | |
130 | ||
131 | fnmres = fnmatch (pattern, input, flags_val); | |
132 | ||
133 | printf ("%3d: fnmatch (\"%s\", \"%s\", %s) = %s%c", | |
134 | ++nr, | |
135 | escape (pattern, &escpatternlen, &escpattern), | |
136 | escape (input, &escinputlen, &escinput), | |
137 | flag_output (flags_val), | |
138 | (fnmres == 0 | |
139 | ? "0" : (fnmres == FNM_NOMATCH | |
140 | ? "FNM_NOMATCH" | |
141 | : (sprintf (numbuf, "%d", fnmres), numbuf))), | |
142 | (fnmres != 0) != (result != 0) ? ' ' : '\n'); | |
143 | ||
144 | if ((fnmres != 0) != (result != 0)) | |
145 | { | |
146 | printf ("(FAIL, expected %s) ***\n", | |
147 | result == 0 | |
148 | ? "0" : (result == FNM_NOMATCH | |
149 | ? "FNM_NOMATCH" | |
150 | : (sprintf (numbuf, "%d", result), numbuf))); | |
151 | ++nfailed; | |
152 | } | |
153 | } | |
154 | ||
155 | printf ("=====================\n%3d tests, %3d failed\n", ntests, nfailed); | |
156 | ||
157 | free (escpattern); | |
158 | free (escinput); | |
159 | free (linebuf); | |
160 | ||
161 | return nfailed != 0; | |
162 | } | |
163 | ||
164 | ||
165 | static char * | |
166 | next_input (char **line, int first, int last) | |
167 | { | |
168 | char *cp = *line; | |
169 | char *result; | |
170 | ||
171 | while (*cp == ' ' || *cp == '\t') | |
172 | ++cp; | |
173 | ||
174 | /* We allow comment lines starting with '#'. */ | |
175 | if (first && *cp == '#') | |
176 | return NULL; | |
177 | ||
178 | if (*cp == '"') | |
179 | { | |
180 | char *wp; | |
181 | ||
182 | result = ++cp; | |
183 | wp = cp; | |
184 | ||
185 | while (*cp != '"' && *cp != '\0' && *cp != '\n') | |
186 | if (*cp == '\\') | |
187 | { | |
188 | if (cp[1] == '\n' || cp[1] == '\0') | |
189 | return NULL; | |
190 | ||
191 | ++cp; | |
192 | if (*cp == 't') | |
193 | *wp++ = '\t'; | |
194 | else if (*cp == 'n') | |
195 | *wp++ = '\n'; | |
196 | else | |
197 | *wp++ = *cp; | |
198 | ||
199 | ++cp; | |
200 | } | |
201 | else | |
202 | *wp++ = *cp++; | |
203 | ||
204 | if (*cp != '"') | |
205 | return NULL; | |
206 | ||
207 | if (wp != cp) | |
208 | *wp = '\0'; | |
209 | } | |
210 | else | |
211 | { | |
212 | result = cp; | |
213 | while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t') | |
214 | ++cp; | |
215 | ||
216 | if (cp == result && ! last) | |
217 | /* Premature end of line. */ | |
218 | return NULL; | |
219 | } | |
220 | ||
221 | /* Terminate and skip over the next white spaces. */ | |
222 | *cp++ = '\0'; | |
223 | ||
224 | *line = cp; | |
225 | return result; | |
226 | } | |
227 | ||
228 | ||
229 | static int | |
230 | convert_flags (const char *str) | |
231 | { | |
232 | int result = 0; | |
233 | ||
234 | while (*str != '\0') | |
235 | { | |
236 | int len; | |
237 | ||
238 | if (strncasecmp (str, "PATHNAME", 8) == 0 | |
239 | && (str[8] == '|' || str[8] == '\0')) | |
240 | { | |
241 | result |= FNM_PATHNAME; | |
242 | len = 8; | |
243 | } | |
244 | else if (strncasecmp (str, "NOESCAPE", 8) == 0 | |
245 | && (str[8] == '|' || str[8] == '\0')) | |
246 | { | |
247 | result |= FNM_NOESCAPE; | |
248 | len = 8; | |
249 | } | |
250 | else if (strncasecmp (str, "PERIOD", 6) == 0 | |
251 | && (str[6] == '|' || str[6] == '\0')) | |
252 | { | |
253 | result |= FNM_PERIOD; | |
254 | len = 6; | |
255 | } | |
256 | else if (strncasecmp (str, "LEADING_DIR", 11) == 0 | |
257 | && (str[11] == '|' || str[11] == '\0')) | |
258 | { | |
259 | result |= FNM_LEADING_DIR; | |
260 | len = 11; | |
261 | } | |
262 | else if (strncasecmp (str, "CASEFOLD", 8) == 0 | |
263 | && (str[8] == '|' || str[8] == '\0')) | |
264 | { | |
265 | result |= FNM_CASEFOLD; | |
266 | len = 8; | |
267 | } | |
955994e1 UD |
268 | else if (strncasecmp (str, "EXTMATCH", 8) == 0 |
269 | && (str[8] == '|' || str[8] == '\0')) | |
270 | { | |
271 | result |= FNM_EXTMATCH; | |
272 | len = 8; | |
273 | } | |
83b1b6d8 UD |
274 | else |
275 | return -1; | |
276 | ||
277 | str += len; | |
278 | if (*str != '\0') | |
279 | ++str; | |
280 | } | |
281 | ||
282 | return result; | |
283 | } | |
284 | ||
285 | ||
286 | static char * | |
287 | flag_output (int flags) | |
288 | { | |
289 | static char buf[100]; | |
290 | int first = 1; | |
291 | char *cp = buf; | |
292 | ||
293 | if (flags & FNM_PATHNAME) | |
294 | { | |
295 | cp = stpcpy (cp, "FNM_PATHNAME"); | |
296 | first = 0; | |
297 | } | |
298 | if (flags & FNM_NOESCAPE) | |
299 | { | |
300 | if (! first) | |
301 | *cp++ = '|'; | |
302 | cp = stpcpy (cp, "FNM_NOESCAPE"); | |
303 | first = 0; | |
304 | } | |
305 | if (flags & FNM_PERIOD) | |
306 | { | |
307 | if (! first) | |
308 | *cp++ = '|'; | |
309 | cp = stpcpy (cp, "FNM_PERIOD"); | |
310 | first = 0; | |
311 | } | |
312 | if (flags & FNM_LEADING_DIR) | |
313 | { | |
314 | if (! first) | |
315 | *cp++ = '|'; | |
316 | cp = stpcpy (cp, "FNM_LEADING_DIR"); | |
317 | first = 0; | |
318 | } | |
319 | if (flags & FNM_CASEFOLD) | |
320 | { | |
321 | if (! first) | |
322 | *cp++ = '|'; | |
323 | cp = stpcpy (cp, "FNM_CASEFOLD"); | |
324 | first = 0; | |
325 | } | |
955994e1 UD |
326 | if (flags & FNM_EXTMATCH) |
327 | { | |
328 | if (! first) | |
329 | *cp++ = '|'; | |
330 | cp = stpcpy (cp, "FNM_EXTMATCH"); | |
331 | first = 0; | |
332 | } | |
83b1b6d8 UD |
333 | if (cp == buf) |
334 | *cp++ = '0'; | |
335 | *cp = '\0'; | |
336 | ||
337 | return buf; | |
338 | } | |
339 | ||
340 | ||
341 | static char * | |
342 | escape (const char *str, size_t *reslenp, char **resbufp) | |
343 | { | |
344 | size_t reslen = *reslenp; | |
345 | char *resbuf = *resbufp; | |
346 | size_t len = strlen (str); | |
347 | char *wp; | |
348 | ||
349 | if (2 * len + 1 > reslen) | |
350 | { | |
351 | resbuf = (char *) realloc (resbuf, 2 * len + 1); | |
352 | if (resbuf == NULL) | |
353 | error (EXIT_FAILURE, errno, "while allocating buffer for printing"); | |
354 | *reslenp = 2 * len + 1; | |
355 | *resbufp = resbuf; | |
356 | } | |
357 | ||
358 | wp = resbuf; | |
359 | while (*str != '\0') | |
360 | if (*str == '\t') | |
361 | { | |
362 | *wp++ = '\\'; | |
363 | *wp++ = 't'; | |
364 | ++str; | |
365 | } | |
366 | else if (*str == '\n') | |
367 | { | |
368 | *wp++ = '\\'; | |
369 | *wp++ = 'n'; | |
370 | ++str; | |
371 | } | |
372 | else if (*str == '"') | |
373 | { | |
374 | *wp++ = '\\'; | |
375 | *wp++ = '"'; | |
376 | ++str; | |
377 | } | |
378 | else if (*str == '\\') | |
379 | { | |
380 | *wp++ = '\\'; | |
381 | *wp++ = '\\'; | |
382 | ++str; | |
383 | } | |
384 | else | |
385 | *wp++ = *str++; | |
386 | ||
387 | *wp = '\0'; | |
388 | ||
389 | return resbuf; | |
390 | } | |
29955b5d AS |
391 | |
392 | #define TEST_FUNCTION do_test () | |
393 | #include "../test-skeleton.c" |