| /* Tests for fnmatch function. | 
 |    Copyright (C) 2000-2016 Free Software Foundation, Inc. | 
 |    This file is part of the GNU C Library. | 
 |  | 
 |    The GNU C Library is free software; you can redistribute it and/or | 
 |    modify it under the terms of the GNU Lesser General Public | 
 |    License as published by the Free Software Foundation; either | 
 |    version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |    The GNU C Library is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |    Lesser General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU Lesser General Public | 
 |    License along with the GNU C Library; if not, see | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <errno.h> | 
 | #include <error.h> | 
 | #include <fnmatch.h> | 
 | #include <locale.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/types.h> | 
 | #include <mcheck.h> | 
 |  | 
 |  | 
 | static char *next_input (char **line, int first, int last); | 
 | static int convert_flags (const char *str); | 
 | static char *flag_output (int flags); | 
 | static char *escape (const char *str, size_t *reslenp, char **resbuf); | 
 |  | 
 |  | 
 | static int | 
 | do_test (void) | 
 | { | 
 |   char *linebuf = NULL; | 
 |   size_t linebuflen = 0; | 
 |   int ntests = 0; | 
 |   int nfailed = 0; | 
 |   char *escinput = NULL; | 
 |   size_t escinputlen = 0; | 
 |   char *escpattern = NULL; | 
 |   size_t escpatternlen = 0; | 
 |   int nr = 0; | 
 |  | 
 |   mtrace (); | 
 |  | 
 |   /* Read lines from stdin with the following format: | 
 |  | 
 |        locale  input-string  match-string  flags  result | 
 |  | 
 |      where `result' is either 0 or 1.  If the first character of a | 
 |      string is '"' we read until the next '"' and handled escaped '"'.  */ | 
 |   while (! feof (stdin)) | 
 |     { | 
 |       ssize_t n = getline (&linebuf, &linebuflen, stdin); | 
 |       char *cp; | 
 |       const char *locale; | 
 |       const char *input; | 
 |       const char *pattern; | 
 |       const char *result_str; | 
 |       int result; | 
 |       const char *flags; | 
 |       int flags_val; | 
 |       int fnmres; | 
 |       char numbuf[24]; | 
 |  | 
 |       if (n == -1) | 
 | 	break; | 
 |  | 
 |       if (n == 0) | 
 | 	/* Maybe an empty line.  */ | 
 | 	continue; | 
 |  | 
 |       /* Skip over all leading white spaces.  */ | 
 |       cp = linebuf; | 
 |  | 
 |       locale = next_input (&cp, 1, 0); | 
 |       if (locale == NULL) | 
 | 	continue; | 
 |  | 
 |       input = next_input (&cp, 0, 0); | 
 |       if (input == NULL) | 
 | 	continue; | 
 |  | 
 |       pattern = next_input (&cp, 0, 0); | 
 |       if (pattern == NULL) | 
 | 	continue; | 
 |  | 
 |       result_str = next_input (&cp, 0, 0); | 
 |       if (result_str == NULL) | 
 | 	continue; | 
 |  | 
 |       if (strcmp (result_str, "0") == 0) | 
 | 	result = 0; | 
 |       else if  (strcasecmp (result_str, "NOMATCH") == 0) | 
 | 	result = FNM_NOMATCH; | 
 |       else | 
 | 	{ | 
 | 	  char *endp; | 
 | 	  result = strtol (result_str, &endp, 0); | 
 | 	  if (*endp != '\0') | 
 | 	    continue; | 
 | 	} | 
 |  | 
 |       flags = next_input (&cp, 0, 1); | 
 |       if (flags == NULL) | 
 | 	/* We allow the flags missing.  */ | 
 | 	flags = ""; | 
 |  | 
 |       /* Convert the text describing the flags in a numeric value.  */ | 
 |       flags_val = convert_flags (flags); | 
 |       if (flags_val == -1) | 
 | 	/* Something went wrong.  */ | 
 | 	continue; | 
 |  | 
 |       /* Now run the actual test.  */ | 
 |       ++ntests; | 
 |  | 
 |       if (setlocale (LC_COLLATE, locale) == NULL | 
 | 	  || setlocale (LC_CTYPE, locale) == NULL) | 
 | 	{ | 
 | 	  puts ("*** Cannot set locale"); | 
 | 	  ++nfailed; | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       fnmres = fnmatch (pattern, input, flags_val); | 
 |  | 
 |       printf ("%3d: fnmatch (\"%s\", \"%s\", %s) = %s%c", | 
 | 	      ++nr, | 
 | 	      escape (pattern, &escpatternlen, &escpattern), | 
 | 	      escape (input, &escinputlen, &escinput), | 
 | 	      flag_output (flags_val), | 
 | 	      (fnmres == 0 | 
 | 	       ? "0" : (fnmres == FNM_NOMATCH | 
 | 			? "FNM_NOMATCH" | 
 | 			: (sprintf (numbuf, "%d", fnmres), numbuf))), | 
 | 	      (fnmres != 0) != (result != 0) ? ' ' : '\n'); | 
 |  | 
 |       if ((fnmres != 0) != (result != 0)) | 
 | 	{ | 
 | 	  printf ("(FAIL, expected %s) ***\n", | 
 | 		  result == 0 | 
 | 		  ? "0" : (result == FNM_NOMATCH | 
 | 			   ? "FNM_NOMATCH" | 
 | 			   : (sprintf (numbuf, "%d", result), numbuf))); | 
 | 	  ++nfailed; | 
 | 	} | 
 |     } | 
 |  | 
 |   printf ("=====================\n%3d tests, %3d failed\n", ntests, nfailed); | 
 |  | 
 |   free (escpattern); | 
 |   free (escinput); | 
 |   free (linebuf); | 
 |  | 
 |   return nfailed != 0; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | next_input (char **line, int first, int last) | 
 | { | 
 |   char *cp = *line; | 
 |   char *result; | 
 |  | 
 |   while (*cp == ' ' || *cp == '\t') | 
 |     ++cp; | 
 |  | 
 |   /* We allow comment lines starting with '#'.  */ | 
 |   if (first && *cp == '#') | 
 |     return NULL; | 
 |  | 
 |   if (*cp == '"') | 
 |     { | 
 |       char *wp; | 
 |  | 
 |       result = ++cp; | 
 |       wp = cp; | 
 |  | 
 |       while (*cp != '"' && *cp != '\0' && *cp != '\n') | 
 | 	if (*cp == '\\') | 
 | 	  { | 
 | 	    if (cp[1] == '\n' || cp[1] == '\0') | 
 | 	      return NULL; | 
 |  | 
 | 	    ++cp; | 
 | 	    if (*cp == 't') | 
 | 	      *wp++ = '\t'; | 
 | 	    else if (*cp == 'n') | 
 | 	      *wp++ = '\n'; | 
 | 	    else | 
 | 	      *wp++ = *cp; | 
 |  | 
 | 	    ++cp; | 
 | 	  } | 
 | 	else | 
 | 	  *wp++ = *cp++; | 
 |  | 
 |       if (*cp != '"') | 
 | 	return NULL; | 
 |  | 
 |       if (wp != cp) | 
 | 	*wp = '\0'; | 
 |     } | 
 |   else | 
 |     { | 
 |       result = cp; | 
 |       while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t') | 
 | 	++cp; | 
 |  | 
 |       if (cp == result && ! last) | 
 | 	/* Premature end of line.  */ | 
 | 	return NULL; | 
 |     } | 
 |  | 
 |   /* Terminate and skip over the next white spaces.  */ | 
 |   *cp++ = '\0'; | 
 |  | 
 |   *line = cp; | 
 |   return result; | 
 | } | 
 |  | 
 |  | 
 | static int | 
 | convert_flags (const char *str) | 
 | { | 
 |   int result = 0; | 
 |  | 
 |   while (*str != '\0') | 
 |     { | 
 |       int len; | 
 |  | 
 |       if (strncasecmp (str, "PATHNAME", 8) == 0 | 
 | 	  && (str[8] == '|' || str[8] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_PATHNAME; | 
 | 	  len = 8; | 
 | 	} | 
 |       else if (strncasecmp (str, "NOESCAPE", 8) == 0 | 
 | 	       && (str[8] == '|' || str[8] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_NOESCAPE; | 
 | 	  len = 8; | 
 | 	} | 
 |       else if (strncasecmp (str, "PERIOD", 6) == 0 | 
 | 	       && (str[6] == '|' || str[6] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_PERIOD; | 
 | 	  len = 6; | 
 | 	} | 
 |       else if (strncasecmp (str, "LEADING_DIR", 11) == 0 | 
 | 	       && (str[11] == '|' || str[11] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_LEADING_DIR; | 
 | 	  len = 11; | 
 | 	} | 
 |       else if (strncasecmp (str, "CASEFOLD", 8) == 0 | 
 | 	       && (str[8] == '|' || str[8] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_CASEFOLD; | 
 | 	  len = 8; | 
 | 	} | 
 |       else if (strncasecmp (str, "EXTMATCH", 8) == 0 | 
 | 	       && (str[8] == '|' || str[8] == '\0')) | 
 | 	{ | 
 | 	  result |= FNM_EXTMATCH; | 
 | 	  len = 8; | 
 | 	} | 
 |       else | 
 | 	return -1; | 
 |  | 
 |       str += len; | 
 |       if (*str != '\0') | 
 | 	++str; | 
 |     } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | flag_output (int flags) | 
 | { | 
 |   static char buf[100]; | 
 |   int first = 1; | 
 |   char *cp = buf; | 
 |  | 
 |   if (flags & FNM_PATHNAME) | 
 |     { | 
 |       cp = stpcpy (cp, "FNM_PATHNAME"); | 
 |       first = 0; | 
 |     } | 
 |   if (flags & FNM_NOESCAPE) | 
 |     { | 
 |       if (! first) | 
 | 	*cp++ = '|'; | 
 |       cp = stpcpy (cp, "FNM_NOESCAPE"); | 
 |       first = 0; | 
 |     } | 
 |   if (flags & FNM_PERIOD) | 
 |     { | 
 |       if (! first) | 
 | 	*cp++ = '|'; | 
 |       cp = stpcpy (cp, "FNM_PERIOD"); | 
 |       first = 0; | 
 |     } | 
 |   if (flags & FNM_LEADING_DIR) | 
 |     { | 
 |       if (! first) | 
 | 	*cp++ = '|'; | 
 |       cp = stpcpy (cp, "FNM_LEADING_DIR"); | 
 |       first = 0; | 
 |     } | 
 |   if (flags & FNM_CASEFOLD) | 
 |     { | 
 |       if (! first) | 
 | 	*cp++ = '|'; | 
 |       cp = stpcpy (cp, "FNM_CASEFOLD"); | 
 |       first = 0; | 
 |     } | 
 |   if (flags & FNM_EXTMATCH) | 
 |     { | 
 |       if (! first) | 
 | 	*cp++ = '|'; | 
 |       cp = stpcpy (cp, "FNM_EXTMATCH"); | 
 |       first = 0; | 
 |     } | 
 |   if (cp == buf) | 
 |     *cp++ = '0'; | 
 |   *cp = '\0'; | 
 |  | 
 |   return buf; | 
 | } | 
 |  | 
 |  | 
 | static char * | 
 | escape (const char *str, size_t *reslenp, char **resbufp) | 
 | { | 
 |   size_t reslen = *reslenp; | 
 |   char *resbuf = *resbufp; | 
 |   size_t len = strlen (str); | 
 |   char *wp; | 
 |  | 
 |   if (2 * len + 1 > reslen) | 
 |     { | 
 |       resbuf = (char *) realloc (resbuf, 2 * len + 1); | 
 |       if (resbuf == NULL) | 
 | 	error (EXIT_FAILURE, errno, "while allocating buffer for printing"); | 
 |       *reslenp = 2 * len + 1; | 
 |       *resbufp = resbuf; | 
 |     } | 
 |  | 
 |   wp = resbuf; | 
 |   while (*str != '\0') | 
 |     if (*str == '\t') | 
 |       { | 
 | 	*wp++ = '\\'; | 
 | 	*wp++ = 't'; | 
 | 	++str; | 
 |       } | 
 |     else if (*str == '\n') | 
 |       { | 
 | 	*wp++ = '\\'; | 
 | 	*wp++ = 'n'; | 
 | 	++str; | 
 |       } | 
 |     else if (*str == '"') | 
 |       { | 
 | 	*wp++ = '\\'; | 
 | 	*wp++ = '"'; | 
 | 	++str; | 
 |       } | 
 |     else if (*str == '\\') | 
 |       { | 
 | 	*wp++ = '\\'; | 
 | 	*wp++ = '\\'; | 
 | 	++str; | 
 |       } | 
 |     else | 
 |       *wp++ = *str++; | 
 |  | 
 |   *wp = '\0'; | 
 |  | 
 |   return resbuf; | 
 | } | 
 |  | 
 | #define TEST_FUNCTION do_test () | 
 | #include "../test-skeleton.c" |