]>
Commit | Line | Data |
---|---|---|
83b1b6d8 | 1 | /* Tests for fnmatch function. |
581c785b | 2 | Copyright (C) 2000-2022 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'; | |
69623c0d DD |
196 | else if (*cp >= '0' && *cp <= '7') |
197 | { | |
198 | int ndigits = 0; | |
199 | int cval = 0; | |
200 | while (ndigits < 3 && *cp >= '0' && *cp <= '7') | |
201 | { | |
202 | cval *= 8; | |
203 | cval += (*cp++) - '0'; | |
204 | ndigits ++; | |
205 | } | |
206 | *wp++ = cval; | |
207 | --cp; | |
208 | } | |
83b1b6d8 UD |
209 | else |
210 | *wp++ = *cp; | |
211 | ||
212 | ++cp; | |
213 | } | |
214 | else | |
215 | *wp++ = *cp++; | |
216 | ||
217 | if (*cp != '"') | |
218 | return NULL; | |
219 | ||
220 | if (wp != cp) | |
221 | *wp = '\0'; | |
222 | } | |
223 | else | |
224 | { | |
225 | result = cp; | |
226 | while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t') | |
227 | ++cp; | |
228 | ||
229 | if (cp == result && ! last) | |
230 | /* Premature end of line. */ | |
231 | return NULL; | |
232 | } | |
233 | ||
234 | /* Terminate and skip over the next white spaces. */ | |
235 | *cp++ = '\0'; | |
236 | ||
237 | *line = cp; | |
238 | return result; | |
239 | } | |
240 | ||
241 | ||
242 | static int | |
243 | convert_flags (const char *str) | |
244 | { | |
245 | int result = 0; | |
246 | ||
247 | while (*str != '\0') | |
248 | { | |
249 | int len; | |
250 | ||
251 | if (strncasecmp (str, "PATHNAME", 8) == 0 | |
252 | && (str[8] == '|' || str[8] == '\0')) | |
253 | { | |
254 | result |= FNM_PATHNAME; | |
255 | len = 8; | |
256 | } | |
257 | else if (strncasecmp (str, "NOESCAPE", 8) == 0 | |
258 | && (str[8] == '|' || str[8] == '\0')) | |
259 | { | |
260 | result |= FNM_NOESCAPE; | |
261 | len = 8; | |
262 | } | |
263 | else if (strncasecmp (str, "PERIOD", 6) == 0 | |
264 | && (str[6] == '|' || str[6] == '\0')) | |
265 | { | |
266 | result |= FNM_PERIOD; | |
267 | len = 6; | |
268 | } | |
269 | else if (strncasecmp (str, "LEADING_DIR", 11) == 0 | |
270 | && (str[11] == '|' || str[11] == '\0')) | |
271 | { | |
272 | result |= FNM_LEADING_DIR; | |
273 | len = 11; | |
274 | } | |
275 | else if (strncasecmp (str, "CASEFOLD", 8) == 0 | |
276 | && (str[8] == '|' || str[8] == '\0')) | |
277 | { | |
278 | result |= FNM_CASEFOLD; | |
279 | len = 8; | |
280 | } | |
955994e1 UD |
281 | else if (strncasecmp (str, "EXTMATCH", 8) == 0 |
282 | && (str[8] == '|' || str[8] == '\0')) | |
283 | { | |
284 | result |= FNM_EXTMATCH; | |
285 | len = 8; | |
286 | } | |
83b1b6d8 UD |
287 | else |
288 | return -1; | |
289 | ||
290 | str += len; | |
291 | if (*str != '\0') | |
292 | ++str; | |
293 | } | |
294 | ||
295 | return result; | |
296 | } | |
297 | ||
298 | ||
299 | static char * | |
300 | flag_output (int flags) | |
301 | { | |
302 | static char buf[100]; | |
303 | int first = 1; | |
304 | char *cp = buf; | |
305 | ||
306 | if (flags & FNM_PATHNAME) | |
307 | { | |
308 | cp = stpcpy (cp, "FNM_PATHNAME"); | |
309 | first = 0; | |
310 | } | |
311 | if (flags & FNM_NOESCAPE) | |
312 | { | |
313 | if (! first) | |
314 | *cp++ = '|'; | |
315 | cp = stpcpy (cp, "FNM_NOESCAPE"); | |
316 | first = 0; | |
317 | } | |
318 | if (flags & FNM_PERIOD) | |
319 | { | |
320 | if (! first) | |
321 | *cp++ = '|'; | |
322 | cp = stpcpy (cp, "FNM_PERIOD"); | |
323 | first = 0; | |
324 | } | |
325 | if (flags & FNM_LEADING_DIR) | |
326 | { | |
327 | if (! first) | |
328 | *cp++ = '|'; | |
329 | cp = stpcpy (cp, "FNM_LEADING_DIR"); | |
330 | first = 0; | |
331 | } | |
332 | if (flags & FNM_CASEFOLD) | |
333 | { | |
334 | if (! first) | |
335 | *cp++ = '|'; | |
336 | cp = stpcpy (cp, "FNM_CASEFOLD"); | |
337 | first = 0; | |
338 | } | |
955994e1 UD |
339 | if (flags & FNM_EXTMATCH) |
340 | { | |
341 | if (! first) | |
342 | *cp++ = '|'; | |
343 | cp = stpcpy (cp, "FNM_EXTMATCH"); | |
344 | first = 0; | |
345 | } | |
83b1b6d8 UD |
346 | if (cp == buf) |
347 | *cp++ = '0'; | |
348 | *cp = '\0'; | |
349 | ||
350 | return buf; | |
351 | } | |
352 | ||
353 | ||
354 | static char * | |
355 | escape (const char *str, size_t *reslenp, char **resbufp) | |
356 | { | |
357 | size_t reslen = *reslenp; | |
358 | char *resbuf = *resbufp; | |
359 | size_t len = strlen (str); | |
360 | char *wp; | |
361 | ||
362 | if (2 * len + 1 > reslen) | |
363 | { | |
364 | resbuf = (char *) realloc (resbuf, 2 * len + 1); | |
365 | if (resbuf == NULL) | |
366 | error (EXIT_FAILURE, errno, "while allocating buffer for printing"); | |
367 | *reslenp = 2 * len + 1; | |
368 | *resbufp = resbuf; | |
369 | } | |
370 | ||
371 | wp = resbuf; | |
372 | while (*str != '\0') | |
373 | if (*str == '\t') | |
374 | { | |
375 | *wp++ = '\\'; | |
376 | *wp++ = 't'; | |
377 | ++str; | |
378 | } | |
379 | else if (*str == '\n') | |
380 | { | |
381 | *wp++ = '\\'; | |
382 | *wp++ = 'n'; | |
383 | ++str; | |
384 | } | |
385 | else if (*str == '"') | |
386 | { | |
387 | *wp++ = '\\'; | |
388 | *wp++ = '"'; | |
389 | ++str; | |
390 | } | |
391 | else if (*str == '\\') | |
392 | { | |
393 | *wp++ = '\\'; | |
394 | *wp++ = '\\'; | |
395 | ++str; | |
396 | } | |
397 | else | |
398 | *wp++ = *str++; | |
399 | ||
400 | *wp = '\0'; | |
401 | ||
402 | return resbuf; | |
403 | } | |
29955b5d AS |
404 | |
405 | #define TEST_FUNCTION do_test () | |
406 | #include "../test-skeleton.c" |