| /* Tests for fnmatch function. |
| Copyright (C) 2000-2018 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" |