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
do_test(void)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 (&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
123 if (setlocale (LC_COLLATE, locale) == NULL
124 || setlocale (LC_CTYPE, locale) == NULL)
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 *
next_input(char ** line,int first,int last)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
convert_flags(const char * str)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 }
281 else if (strncasecmp (str, "EXTMATCH", 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 *
flag_output(int flags)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 *
escape(const char * str,size_t * reslenp,char ** resbufp)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 }
404
405 #define TEST_FUNCTION do_test ()
406 #include "../test-skeleton.c"
407