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