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