glibc/posix/tst-fnmatch.c

407 lines
8.1 KiB
C

/* Tests for fnmatch function.
Copyright (C) 2000-2023 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
<https://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 if (*cp >= '0' && *cp <= '7')
{
int ndigits = 0;
int cval = 0;
while (ndigits < 3 && *cp >= '0' && *cp <= '7')
{
cval *= 8;
cval += (*cp++) - '0';
ndigits ++;
}
*wp++ = cval;
--cp;
}
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"