1 | /* Tests for fnmatch function. |
2 | Copyright (C) 2000-2022 Free Software Foundation, Inc. |
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 |
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. |
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 |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
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> |
27 | #include <mcheck.h> |
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 | |
36 | static int |
37 | do_test (void) |
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 | |
49 | mtrace (); |
50 | |
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 (lineptr: &linebuf, n: &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 (line: &cp, first: 1, last: 0); |
82 | if (locale == NULL) |
83 | continue; |
84 | |
85 | input = next_input (line: &cp, first: 0, last: 0); |
86 | if (input == NULL) |
87 | continue; |
88 | |
89 | pattern = next_input (line: &cp, first: 0, last: 0); |
90 | if (pattern == NULL) |
91 | continue; |
92 | |
93 | result_str = next_input (line: &cp, first: 0, last: 0); |
94 | if (result_str == NULL) |
95 | continue; |
96 | |
97 | if (strcmp (result_str, "0" ) == 0) |
98 | result = 0; |
99 | else if (strcasecmp (s1: result_str, s2: "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 (line: &cp, first: 0, last: 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 (str: flags); |
116 | if (flags_val == -1) |
117 | /* Something went wrong. */ |
118 | continue; |
119 | |
120 | /* Now run the actual test. */ |
121 | ++ntests; |
122 | |
123 | if (setlocale (LC_COLLATE, locale) == NULL |
124 | || setlocale (LC_CTYPE, locale) == NULL) |
125 | { |
126 | puts (s: "*** Cannot set locale" ); |
127 | ++nfailed; |
128 | continue; |
129 | } |
130 | |
131 | fnmres = fnmatch (pattern, input, flags_val); |
132 | |
133 | printf (format: "%3d: fnmatch (\"%s\", \"%s\", %s) = %s%c" , |
134 | ++nr, |
135 | escape (str: pattern, reslenp: &escpatternlen, resbuf: &escpattern), |
136 | escape (str: input, reslenp: &escinputlen, resbuf: &escinput), |
137 | flag_output (flags: 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 (format: "(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 (format: "=====================\n%3d tests, %3d failed\n" , ntests, nfailed); |
156 | |
157 | free (ptr: escpattern); |
158 | free (ptr: escinput); |
159 | free (ptr: 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 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 | } |
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 (s1: str, s2: "PATHNAME" , n: 8) == 0 |
252 | && (str[8] == '|' || str[8] == '\0')) |
253 | { |
254 | result |= FNM_PATHNAME; |
255 | len = 8; |
256 | } |
257 | else if (strncasecmp (s1: str, s2: "NOESCAPE" , n: 8) == 0 |
258 | && (str[8] == '|' || str[8] == '\0')) |
259 | { |
260 | result |= FNM_NOESCAPE; |
261 | len = 8; |
262 | } |
263 | else if (strncasecmp (s1: str, s2: "PERIOD" , n: 6) == 0 |
264 | && (str[6] == '|' || str[6] == '\0')) |
265 | { |
266 | result |= FNM_PERIOD; |
267 | len = 6; |
268 | } |
269 | else if (strncasecmp (s1: str, s2: "LEADING_DIR" , n: 11) == 0 |
270 | && (str[11] == '|' || str[11] == '\0')) |
271 | { |
272 | result |= FNM_LEADING_DIR; |
273 | len = 11; |
274 | } |
275 | else if (strncasecmp (s1: str, s2: "CASEFOLD" , n: 8) == 0 |
276 | && (str[8] == '|' || str[8] == '\0')) |
277 | { |
278 | result |= FNM_CASEFOLD; |
279 | len = 8; |
280 | } |
281 | else if (strncasecmp (s1: str, s2: "EXTMATCH" , n: 8) == 0 |
282 | && (str[8] == '|' || str[8] == '\0')) |
283 | { |
284 | result |= FNM_EXTMATCH; |
285 | len = 8; |
286 | } |
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 | } |
339 | if (flags & FNM_EXTMATCH) |
340 | { |
341 | if (! first) |
342 | *cp++ = '|'; |
343 | cp = stpcpy (cp, "FNM_EXTMATCH" ); |
344 | first = 0; |
345 | } |
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 (ptr: resbuf, size: 2 * len + 1); |
365 | if (resbuf == NULL) |
366 | error (EXIT_FAILURE, errno, format: "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 | } |
404 | |
405 | #define TEST_FUNCTION do_test () |
406 | #include "../test-skeleton.c" |
407 | |