mirror of
https://github.com/fallout2-ce/fpattern.git
synced 2026-07-27 16:47:36 -07:00
821 lines
20 KiB
C
821 lines
20 KiB
C
/*******************************************************************************
|
|||
|
|
* fpattern.c
|
||
|
|
* Functions for matching filename patterns to filenames.
|
||
|
|
*
|
||
|
|
* Usage
|
||
|
|
* (See "fpattern.h".)
|
||
|
|
*
|
||
|
|
* Notes
|
||
|
|
* These pattern matching capabilities are modeled after those found in
|
||
|
|
* the UNIX command shells.
|
||
|
|
*
|
||
|
|
* `DELIM' must be defined to 1 if pathname separators are to be handled
|
||
|
|
* explicitly.
|
||
|
|
*
|
||
|
|
* History
|
||
|
|
* 1.0, 1997-01-03, David Tribble.
|
||
|
|
* First cut.
|
||
|
|
*
|
||
|
|
* 1.1, 1997-01-03, David Tribble.
|
||
|
|
* Added the SUB pattern character.
|
||
|
|
* Added function fpattern_matchn().
|
||
|
|
*
|
||
|
|
* 1.2, 1997-01-12, David Tribble.
|
||
|
|
* Fixed missing lowercase matching cases.
|
||
|
|
*
|
||
|
|
* 1.3, 1997-01-13, David Tribble.
|
||
|
|
* Pathname separator code is now controlled by DELIM macro.
|
||
|
|
*
|
||
|
|
* 1.4, 1997-01-14, David Tribble.
|
||
|
|
* Added the QUOTE macro.
|
||
|
|
*
|
||
|
|
* 1.5, 1997-01-15, David Tribble.
|
||
|
|
* Handles the special case of empty pattern and empty filename.
|
||
|
|
*
|
||
|
|
* 1.6, 1997-01-26, David Tribble.
|
||
|
|
* Changed the range negation character from '^' to '!', ala Unix.
|
||
|
|
*
|
||
|
|
* 1.7, 1997-08-02, David Tribble.
|
||
|
|
* Uses the 'FPAT_XXX' constants.
|
||
|
|
*
|
||
|
|
* 1.8, 1998-06-28, David Tribble.
|
||
|
|
* Minor fixes for MS-VC++ (5.0).
|
||
|
|
*
|
||
|
|
* 1.9, 2001-11-21, David Tribble.
|
||
|
|
* Minor fixes for Win32 compilations.
|
||
|
|
*
|
||
|
|
* Limitations
|
||
|
|
* This code is copyrighted by the author, but permission is hereby granted
|
||
|
|
* for its unlimited use provided that the original copyright and
|
||
|
|
* authorship notices are retained intact.
|
||
|
|
*
|
||
|
|
* Queries about this source code can be sent to: <david@tribble.com>
|
||
|
|
*
|
||
|
|
* Copyright ©1997-2001 by David R. Tribble, all rights reserved.
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
/* Identification */
|
||
|
|
|
||
|
|
static const char id[] =
|
||
|
|
"@(#)drt/src/lib/fpattern.c $Revision: 1.9 $ $Date: 2001/11/21 06:00:00 $";
|
||
|
|
|
||
|
|
static const char copyright[] =
|
||
|
|
"@(#)Portions are Copyright \2511997-2001 David R. Tribble, "
|
||
|
|
"all rights reserved.\n";
|
||
|
|
|
||
|
|
|
||
|
|
/* System includes */
|
||
|
|
|
||
|
|
#include <ctype.h>
|
||
|
|
#include <stddef.h>
|
||
|
|
|
||
|
|
#if TEST
|
||
|
|
#include <locale.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <string.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(unix) || defined(_unix) || defined(__unix)
|
||
|
|
#define UNIX 1
|
||
|
|
#define DOS 0
|
||
|
|
#elif defined(__MSDOS__) || defined(_WIN32)
|
||
|
|
#define UNIX 0
|
||
|
|
#define DOS 1
|
||
|
|
#else
|
||
|
|
#error Cannot ascertain the O/S from predefined macros
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
/* Local includes */
|
||
|
|
|
||
|
|
#include "debug.h"
|
||
|
|
|
||
|
|
#include "fpattern.h"
|
||
|
|
|
||
|
|
|
||
|
|
/* Local constants */
|
||
|
|
|
||
|
|
#ifndef NULL
|
||
|
|
#define NULL ((void *) 0)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef false
|
||
|
|
#define false 0
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef true
|
||
|
|
#define true 1
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if TEST
|
||
|
|
#define SUB '~'
|
||
|
|
#else
|
||
|
|
#define SUB FPAT_CLOSP
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifndef DELIM
|
||
|
|
#define DELIM 0
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define DEL FPAT_DEL
|
||
|
|
|
||
|
|
#if UNIX
|
||
|
|
#define DEL2 FPAT_DEL
|
||
|
|
#else /*DOS*/
|
||
|
|
#define DEL2 FPAT_DEL2
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if UNIX
|
||
|
|
#define QUOTE FPAT_QUOTE
|
||
|
|
#else /*DOS*/
|
||
|
|
#define QUOTE FPAT_QUOTE2
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
/* Local function macros */
|
||
|
|
|
||
|
|
#if UNIX
|
||
|
|
#define lowercase(c) (c)
|
||
|
|
#else /*DOS*/
|
||
|
|
#define lowercase(c) tolower(c)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* fpattern_isvalid()
|
||
|
|
* Checks that filename pattern 'pat' is a well-formed pattern.
|
||
|
|
*
|
||
|
|
* Returns
|
||
|
|
* True (1) if 'pat' is a valid filename pattern, otherwise false (0).
|
||
|
|
*
|
||
|
|
* Caveats
|
||
|
|
* If 'pat' is null, false (0) is returned.
|
||
|
|
*
|
||
|
|
* If 'pat' is empty (""), true (1) is returned, and it is considered a
|
||
|
|
* valid (but degenerate) pattern (the only filename it matches is the
|
||
|
|
* empty ("") string).
|
||
|
|
*/
|
||
|
|
|
||
|
|
int fpattern_isvalid(const char *pat)
|
||
|
|
{
|
||
|
|
int len;
|
||
|
|
|
||
|
|
DL(printf("fpattern_isvalid: pat=%04p:\"%s\"\n", pat, pat ? pat : ""));
|
||
|
|
|
||
|
|
/* Check args */
|
||
|
|
if (pat == NULL)
|
||
|
|
{
|
||
|
|
DL(printf("Null pattern\n"));
|
||
|
|
return (false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Verify that the pattern is valid */
|
||
|
|
for (len = 0; pat[len] != '\0'; len++)
|
||
|
|
{
|
||
|
|
switch (pat[len])
|
||
|
|
{
|
||
|
|
case FPAT_SET_L:
|
||
|
|
/* Char set */
|
||
|
|
len++;
|
||
|
|
if (pat[len] == FPAT_SET_NOT)
|
||
|
|
len++; /* Set negation */
|
||
|
|
|
||
|
|
while (pat[len] != FPAT_SET_R)
|
||
|
|
{
|
||
|
|
if (pat[len] == QUOTE)
|
||
|
|
len++; /* Quoted char */
|
||
|
|
if (pat[len] == '\0')
|
||
|
|
{
|
||
|
|
DL(printf("Missing '%c'\n", FPAT_SET_R));
|
||
|
|
return (false); /* Missing closing bracket */
|
||
|
|
}
|
||
|
|
len++;
|
||
|
|
|
||
|
|
if (pat[len] == FPAT_SET_THRU)
|
||
|
|
{
|
||
|
|
/* Char range */
|
||
|
|
len++;
|
||
|
|
if (pat[len] == QUOTE)
|
||
|
|
len++; /* Quoted char */
|
||
|
|
if (pat[len] == '\0')
|
||
|
|
{
|
||
|
|
DL(printf("Missing '%c%c'\n",
|
||
|
|
FPAT_SET_THRU, FPAT_SET_R));
|
||
|
|
return (false); /* Missing closing bracket */
|
||
|
|
}
|
||
|
|
len++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pat[len] == '\0')
|
||
|
|
{
|
||
|
|
DL(printf("Missing '%c'\n", FPAT_SET_R));
|
||
|
|
return (false); /* Missing closing bracket */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case QUOTE:
|
||
|
|
/* Quoted char */
|
||
|
|
len++;
|
||
|
|
if (pat[len] == '\0')
|
||
|
|
{
|
||
|
|
DL(printf("Missing quoted char\n"));
|
||
|
|
return (false); /* Missing quoted char */
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case FPAT_NOT:
|
||
|
|
/* Negated pattern */
|
||
|
|
len++;
|
||
|
|
if (pat[len] == '\0')
|
||
|
|
{
|
||
|
|
DL(printf("Missing negated subpattern\n"));
|
||
|
|
return (false); /* Missing subpattern */
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
/* Valid character */
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
DL(printf("fpattern_isvalid: return %d\n", len));
|
||
|
|
return (true);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* fpattern_submatch()
|
||
|
|
* Attempts to match subpattern 'pat' to subfilename 'fname'.
|
||
|
|
*
|
||
|
|
* Returns
|
||
|
|
* True (1) if the subfilename matches, otherwise false (0).
|
||
|
|
*
|
||
|
|
* Caveats
|
||
|
|
* This does not assume that 'pat' is well-formed.
|
||
|
|
*
|
||
|
|
* If 'pat' is empty (""), the only filename it matches is the empty ("")
|
||
|
|
* string.
|
||
|
|
*
|
||
|
|
* Some non-empty patterns (e.g., "") will match an empty filename ("").
|
||
|
|
*/
|
||
|
|
|
||
|
|
static int fpattern_submatch(const char *pat, const char *fname)
|
||
|
|
{
|
||
|
|
int fch;
|
||
|
|
int pch;
|
||
|
|
int i;
|
||
|
|
int yes, match;
|
||
|
|
int lo, hi;
|
||
|
|
|
||
|
|
DL(printf("fpattern_submatch: fname=\"%s\", pat=\"%s\"\n", fname, pat));
|
||
|
|
|
||
|
|
/* Attempt to match subpattern against subfilename */
|
||
|
|
while (*pat != '\0')
|
||
|
|
{
|
||
|
|
fch = *fname;
|
||
|
|
pch = *pat;
|
||
|
|
pat++;
|
||
|
|
|
||
|
|
switch (pch)
|
||
|
|
{
|
||
|
|
case FPAT_ANY:
|
||
|
|
/* Match a single char */
|
||
|
|
#if DELIM
|
||
|
|
if (fch == DEL || fch == DEL2 || fch == '\0')
|
||
|
|
#else
|
||
|
|
if (fch == '\0')
|
||
|
|
#endif
|
||
|
|
{
|
||
|
|
DL(printf("match=F\n"));
|
||
|
|
return (false);
|
||
|
|
}
|
||
|
|
fname++;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case FPAT_CLOS:
|
||
|
|
/* Match zero or more chars */
|
||
|
|
i = 0;
|
||
|
|
#if DELIM
|
||
|
|
while (fname[i] != '\0' &&
|
||
|
|
fname[i] != DEL && fname[i] != DEL2)
|
||
|
|
i++;
|
||
|
|
#else
|
||
|
|
while (fname[i] != '\0')
|
||
|
|
i++;
|
||
|
|
#endif
|
||
|
|
while (i >= 0)
|
||
|
|
{
|
||
|
|
if (fpattern_submatch(pat, fname+i))
|
||
|
|
{
|
||
|
|
DL(printf("submatch=T for +%d\n", i));
|
||
|
|
return (true);
|
||
|
|
}
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
case SUB:
|
||
|
|
/* Match zero or more chars */
|
||
|
|
i = 0;
|
||
|
|
while (fname[i] != '\0' &&
|
||
|
|
#if DELIM
|
||
|
|
fname[i] != DEL && fname[i] != DEL2 &&
|
||
|
|
#endif
|
||
|
|
fname[i] != '.')
|
||
|
|
i++;
|
||
|
|
while (i >= 0)
|
||
|
|
{
|
||
|
|
if (fpattern_submatch(pat, fname+i))
|
||
|
|
return (true);
|
||
|
|
i--;
|
||
|
|
}
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
case QUOTE:
|
||
|
|
/* Match a quoted char */
|
||
|
|
pch = *pat;
|
||
|
|
if (lowercase(fch) != lowercase(pch) || pch == '\0')
|
||
|
|
return (false);
|
||
|
|
fname++;
|
||
|
|
pat++;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case FPAT_SET_L:
|
||
|
|
/* Match char set/range */
|
||
|
|
yes = true;
|
||
|
|
if (*pat == FPAT_SET_NOT)
|
||
|
|
{
|
||
|
|
pat++;
|
||
|
|
yes = false; /* Set negation */
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Look for [s], [-], [abc], [a-c] */
|
||
|
|
match = !yes;
|
||
|
|
while (*pat != FPAT_SET_R && *pat != '\0')
|
||
|
|
{
|
||
|
|
if (*pat == QUOTE)
|
||
|
|
pat++; /* Quoted char */
|
||
|
|
|
||
|
|
if (*pat == '\0')
|
||
|
|
break;
|
||
|
|
lo = *pat++;
|
||
|
|
hi = lo;
|
||
|
|
|
||
|
|
if (*pat == FPAT_SET_THRU)
|
||
|
|
{
|
||
|
|
/* Range */
|
||
|
|
pat++;
|
||
|
|
|
||
|
|
if (*pat == QUOTE)
|
||
|
|
pat++; /* Quoted char */
|
||
|
|
|
||
|
|
if (*pat == '\0')
|
||
|
|
break;
|
||
|
|
hi = *pat++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (*pat == '\0')
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* Compare character to set range */
|
||
|
|
if (lowercase(fch) >= lowercase(lo) &&
|
||
|
|
lowercase(fch) <= lowercase(hi))
|
||
|
|
match = yes;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!match)
|
||
|
|
{
|
||
|
|
DL(printf("match=F\n"));
|
||
|
|
return (false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (*pat == '\0')
|
||
|
|
return (false); /* Missing closing bracket */
|
||
|
|
|
||
|
|
fname++;
|
||
|
|
pat++;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case FPAT_NOT:
|
||
|
|
/* Match only if rest of pattern does not match */
|
||
|
|
if (*pat == '\0')
|
||
|
|
return (false); /* Missing subpattern */
|
||
|
|
i = fpattern_submatch(pat, fname);
|
||
|
|
DL(printf("submatch=%c\n", "FT"[!!i]));
|
||
|
|
return !i;
|
||
|
|
|
||
|
|
#if DELIM
|
||
|
|
case DEL:
|
||
|
|
#if DEL2 != DEL
|
||
|
|
case DEL2:
|
||
|
|
#endif
|
||
|
|
/* Match path delimiter char */
|
||
|
|
if (fch != DEL && fch != DEL2)
|
||
|
|
return (false);
|
||
|
|
fname++;
|
||
|
|
break;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
default:
|
||
|
|
/* Match a (non-null) char exactly */
|
||
|
|
if (lowercase(fch) != lowercase(pch))
|
||
|
|
{
|
||
|
|
DL(printf("fch:'%c' != pch:'%c'\n", fch, pch));
|
||
|
|
return (false);
|
||
|
|
}
|
||
|
|
fname++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Check for complete match */
|
||
|
|
if (*fname != '\0')
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
/* Successful match */
|
||
|
|
DL(printf("fpattern_submatch: pass\n"));
|
||
|
|
return (true);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* fpattern_match()
|
||
|
|
* Attempts to match pattern 'pat' to filename 'fname'.
|
||
|
|
*
|
||
|
|
* Returns
|
||
|
|
* True (1) if the filename matches, otherwise false (0).
|
||
|
|
*
|
||
|
|
* Caveats
|
||
|
|
* If 'fname' is null, false (0) is returned.
|
||
|
|
*
|
||
|
|
* If 'pat' is null, false (0) is returned.
|
||
|
|
*
|
||
|
|
* If 'pat' is empty (""), the only filename it matches is the empty string
|
||
|
|
* ("").
|
||
|
|
*
|
||
|
|
* If 'fname' is empty, the only pattern that will match it is the empty
|
||
|
|
* string ("").
|
||
|
|
*
|
||
|
|
* If 'pat' is not a well-formed pattern, false (0) is returned.
|
||
|
|
*
|
||
|
|
* Upper and lower case letters are treated the same; alphabetic characters
|
||
|
|
* are converted to lower case before matching occurs. Conversion to lower
|
||
|
|
* case is dependent upon the current locale setting.
|
||
|
|
*/
|
||
|
|
|
||
|
|
int fpattern_match(const char *pat, const char *fname)
|
||
|
|
{
|
||
|
|
int rc;
|
||
|
|
|
||
|
|
DL(printf("fpattern_match: fname=%04p:\"%s\", pat=%04p:\"%s\"\n",
|
||
|
|
fname, fname ? fname : "", pat, pat ? pat : ""));
|
||
|
|
|
||
|
|
/* Check args */
|
||
|
|
if (fname == NULL)
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
if (pat == NULL)
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
/* Verify that the pattern is valid, and get its length */
|
||
|
|
if (!fpattern_isvalid(pat))
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
/* Attempt to match pattern against filename */
|
||
|
|
if (fname[0] == '\0')
|
||
|
|
return (pat[0] == '\0'); /* Special case */
|
||
|
|
rc = fpattern_submatch(pat, fname);
|
||
|
|
|
||
|
|
DL(printf("fpattern_match: return %c\n", "FT"[!!rc]));
|
||
|
|
return (rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* fpattern_matchn()
|
||
|
|
* Attempts to match pattern 'pat' to filename 'fname'.
|
||
|
|
* This operates like fpattern_match() except that it does not verify that
|
||
|
|
* pattern 'pat' is well-formed, assuming that it has been checked by a
|
||
|
|
* prior call to fpattern_isvalid().
|
||
|
|
*
|
||
|
|
* Returns
|
||
|
|
* True (1) if the filename matches, otherwise false (0).
|
||
|
|
*
|
||
|
|
* Caveats
|
||
|
|
* If 'fname' is null, false (0) is returned.
|
||
|
|
*
|
||
|
|
* If 'pat' is null, false (0) is returned.
|
||
|
|
*
|
||
|
|
* If 'pat' is empty (""), the only filename it matches is the empty ("")
|
||
|
|
* string.
|
||
|
|
*
|
||
|
|
* If 'pat' is not a well-formed pattern, unpredictable results may occur.
|
||
|
|
*
|
||
|
|
* Upper and lower case letters are treated the same; alphabetic characters
|
||
|
|
* are converted to lower case before matching occurs. Conversion to lower
|
||
|
|
* case is dependent upon the current locale setting.
|
||
|
|
*
|
||
|
|
* See also
|
||
|
|
* fpattern_match().
|
||
|
|
*/
|
||
|
|
|
||
|
|
int fpattern_matchn(const char *pat, const char *fname)
|
||
|
|
{
|
||
|
|
int rc;
|
||
|
|
|
||
|
|
DL(printf("fpattern_matchn: fname=%04p:\"%s\", pat=%04p:\"%s\"\n",
|
||
|
|
fname, fname ? fname : "", pat, pat ? pat : ""));
|
||
|
|
|
||
|
|
/* Check args */
|
||
|
|
if (fname == NULL)
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
if (pat == NULL)
|
||
|
|
return (false);
|
||
|
|
|
||
|
|
/* Assume that pattern is well-formed */
|
||
|
|
|
||
|
|
/* Attempt to match pattern against filename */
|
||
|
|
rc = fpattern_submatch(pat, fname);
|
||
|
|
|
||
|
|
DL(printf("fpattern_matchn: return %c\n", "FT"[!!rc]));
|
||
|
|
return (rc);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
/*----------------------------------------------------------------------------*/
|
||
|
|
|
||
|
|
#if TEST
|
||
|
|
|
||
|
|
|
||
|
|
/* Local variables */
|
||
|
|
|
||
|
|
static int count = 0;
|
||
|
|
static int fails = 0;
|
||
|
|
static int stop_on_fail = false;
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* test()
|
||
|
|
*/
|
||
|
|
|
||
|
|
static void test(int expect, const char *fname, const char *pat)
|
||
|
|
{
|
||
|
|
int failed;
|
||
|
|
int result;
|
||
|
|
char fbuf[80+1];
|
||
|
|
char pbuf[80+1];
|
||
|
|
|
||
|
|
count++;
|
||
|
|
printf("%3d. ", count);
|
||
|
|
|
||
|
|
if (fname == NULL)
|
||
|
|
{
|
||
|
|
printf("<null>\n");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
strcpy(fbuf, fname);
|
||
|
|
printf("\"%s\"\n", fbuf);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (pat == NULL)
|
||
|
|
{
|
||
|
|
printf(" <null>\n");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
strcpy(pbuf, pat);
|
||
|
|
printf(" \"%s\"\n", pbuf);
|
||
|
|
}
|
||
|
|
|
||
|
|
result = fpattern_match(pat == NULL ? NULL : pbuf,
|
||
|
|
fname == NULL ? NULL : fbuf);
|
||
|
|
|
||
|
|
failed = (result != expect);
|
||
|
|
printf(" -> %c, expected %c: %s\n",
|
||
|
|
"FT"[!!result], "FT"[!!expect], failed ? "FAIL ***" : "pass");
|
||
|
|
|
||
|
|
if (failed)
|
||
|
|
{
|
||
|
|
fails++;
|
||
|
|
|
||
|
|
if (stop_on_fail)
|
||
|
|
exit(1);
|
||
|
|
sleep(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/*------------------------------------------------------------------------------
|
||
|
|
* main()
|
||
|
|
* Test driver.
|
||
|
|
*/
|
||
|
|
|
||
|
|
int main(int argc, char **argv)
|
||
|
|
{
|
||
|
|
(void) argc; /* Shut up lint */
|
||
|
|
(void) argv; /* Shut up lint */
|
||
|
|
|
||
|
|
#if DEBUG
|
||
|
|
dbg_f = stdout;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
printf("==========================================\n");
|
||
|
|
|
||
|
|
setlocale(LC_CTYPE, "");
|
||
|
|
|
||
|
|
#if UNIX
|
||
|
|
printf("[O/S is UNIX]\n");
|
||
|
|
#elif DOS
|
||
|
|
printf("[O/S is DOS]\n");
|
||
|
|
#else
|
||
|
|
printf("[O/S is unknown]\n");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if 1 /* Set to nonzero to stop on first failure */
|
||
|
|
stop_on_fail = true;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
test(0, NULL, NULL);
|
||
|
|
test(0, NULL, "");
|
||
|
|
test(0, NULL, "abc");
|
||
|
|
test(0, "", NULL);
|
||
|
|
test(0, "abc", NULL);
|
||
|
|
|
||
|
|
test(1, "abc", "abc");
|
||
|
|
test(0, "ab", "abc");
|
||
|
|
test(0, "abcd", "abc");
|
||
|
|
test(0, "Foo.txt", "Foo.x");
|
||
|
|
test(1, "Foo.txt", "Foo.txt");
|
||
|
|
test(1, "Foo.txt", "foo.txt");
|
||
|
|
test(1, "FOO.txt", "foo.TXT");
|
||
|
|
|
||
|
|
test(1, "a", "?");
|
||
|
|
test(1, "foo.txt", "f??.txt");
|
||
|
|
test(1, "foo.txt", "???????");
|
||
|
|
test(0, "foo.txt", "??????");
|
||
|
|
test(0, "foo.txt", "????????");
|
||
|
|
|
||
|
|
test(1, "a", "`a");
|
||
|
|
test(1, "AB", "a`b");
|
||
|
|
test(0, "aa", "a`b");
|
||
|
|
test(1, "a`x", "a``x");
|
||
|
|
test(1, "a`x", "`a```x");
|
||
|
|
test(1, "a*x", "a`*x");
|
||
|
|
|
||
|
|
#if DELIM
|
||
|
|
test(0, "", "/");
|
||
|
|
test(0, "", "\\");
|
||
|
|
test(1, "/", "/");
|
||
|
|
test(1, "/", "\\");
|
||
|
|
test(1, "\\", "/");
|
||
|
|
test(1, "\\", "\\");
|
||
|
|
|
||
|
|
test(1, "a/b", "a/b");
|
||
|
|
test(1, "a/b", "a\\b");
|
||
|
|
|
||
|
|
test(1, "/", "*/*");
|
||
|
|
test(1, "foo/a.c", "f*/*.?");
|
||
|
|
test(1, "foo/a.c", "*/*");
|
||
|
|
test(0, "foo/a.c", "/*/*");
|
||
|
|
test(0, "foo/a.c", "*/*/");
|
||
|
|
|
||
|
|
test(1, "/", "~/~");
|
||
|
|
test(1, "foo/a.c", "f~/~.?");
|
||
|
|
test(0, "foo/a.c", "~/~");
|
||
|
|
test(1, "foo/abc", "~/~");
|
||
|
|
test(0, "foo/a.c", "/~/~");
|
||
|
|
test(0, "foo/a.c", "~/~/");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
test(0, "", "*");
|
||
|
|
test(1, "a", "*");
|
||
|
|
test(1, "ab", "*");
|
||
|
|
test(1, "abc", "**");
|
||
|
|
test(1, "ab.c", "*.?");
|
||
|
|
test(1, "ab.c", "*.*");
|
||
|
|
test(1, "ab.c", "*?");
|
||
|
|
test(1, "ab.c", "?*");
|
||
|
|
test(1, "ab.c", "?*?");
|
||
|
|
test(1, "ab.c", "?*?*");
|
||
|
|
test(1, "ac", "a*c");
|
||
|
|
test(1, "axc", "a*c");
|
||
|
|
test(1, "ax-yyy.c", "a*c");
|
||
|
|
test(1, "ax-yyy.c", "a*x-yyy.c");
|
||
|
|
test(1, "axx/yyy.c", "a*x/*c");
|
||
|
|
|
||
|
|
test(0, "", "~");
|
||
|
|
test(1, "a", "~");
|
||
|
|
test(1, "ab", "~");
|
||
|
|
test(1, "abc", "~~");
|
||
|
|
test(1, "ab.c", "~.?");
|
||
|
|
test(1, "ab.c", "~.~");
|
||
|
|
test(0, "ab.c", "~?");
|
||
|
|
test(0, "ab.c", "?~");
|
||
|
|
test(0, "ab.c", "?~?");
|
||
|
|
test(1, "ab.c", "?~.?");
|
||
|
|
test(1, "ab.c", "?~?~");
|
||
|
|
test(1, "ac", "a~c");
|
||
|
|
test(1, "axc", "a~c");
|
||
|
|
test(0, "ax-yyy.c", "a~c");
|
||
|
|
test(1, "ax-yyyvc", "a~c");
|
||
|
|
test(1, "ax-yyy.c", "a~x-yyy.c");
|
||
|
|
test(0, "axx/yyy.c", "a~x/~c");
|
||
|
|
test(1, "axx/yyyvc", "a~x/~c");
|
||
|
|
|
||
|
|
test(0, "a", "!");
|
||
|
|
test(0, "a", "!a");
|
||
|
|
test(1, "a", "!b");
|
||
|
|
test(1, "abc", "!abb");
|
||
|
|
test(0, "a", "!*");
|
||
|
|
test(1, "abc", "!*.?");
|
||
|
|
test(1, "abc", "!*.*");
|
||
|
|
test(0, "", "!*"); /*!*/
|
||
|
|
test(0, "", "!*?"); /*!*/
|
||
|
|
test(0, "a", "!*?");
|
||
|
|
test(0, "a", "a!*");
|
||
|
|
test(1, "a", "a!?");
|
||
|
|
test(1, "a", "a!*?");
|
||
|
|
test(1, "ab", "*!?");
|
||
|
|
test(1, "abc", "*!?");
|
||
|
|
test(0, "ab", "?!?");
|
||
|
|
test(1, "abc", "?!?");
|
||
|
|
test(0, "a-b", "!a[-]b");
|
||
|
|
test(0, "a-b", "!a[x-]b");
|
||
|
|
test(0, "a=b", "!a[x-]b");
|
||
|
|
test(0, "a-b", "!a[x`-]b");
|
||
|
|
test(1, "a=b", "!a[x`-]b");
|
||
|
|
test(0, "a-b", "!a[x---]b");
|
||
|
|
test(1, "a=b", "!a[x---]b");
|
||
|
|
|
||
|
|
test(1, "abc", "a[b]c");
|
||
|
|
test(1, "aBc", "a[b]c");
|
||
|
|
test(1, "abc", "a[bB]c");
|
||
|
|
test(1, "abc", "a[bcz]c");
|
||
|
|
test(1, "azc", "a[bcz]c");
|
||
|
|
test(0, "ab", "a[b]c");
|
||
|
|
test(0, "ac", "a[b]c");
|
||
|
|
test(0, "axc", "a[b]c");
|
||
|
|
|
||
|
|
test(0, "abc", "a[!b]c");
|
||
|
|
test(0, "abc", "a[!bcz]c");
|
||
|
|
test(0, "azc", "a[!bcz]c");
|
||
|
|
test(0, "ab", "a[!b]c");
|
||
|
|
test(0, "ac", "a[!b]c");
|
||
|
|
test(1, "axc", "a[!b]c");
|
||
|
|
test(1, "axc", "a[!bcz]c");
|
||
|
|
|
||
|
|
test(1, "a1z", "a[0-9]z");
|
||
|
|
test(0, "a1", "a[0-9]z");
|
||
|
|
test(0, "az", "a[0-9]z");
|
||
|
|
test(0, "axz", "a[0-9]z");
|
||
|
|
test(1, "a2z", "a[-0-9]z");
|
||
|
|
test(1, "a-z", "a[-0-9]z");
|
||
|
|
test(1, "a-b", "a[-]b");
|
||
|
|
test(0, "a-b", "a[x-]b");
|
||
|
|
test(0, "a=b", "a[x-]b");
|
||
|
|
test(1, "a-b", "a[x`-]b");
|
||
|
|
test(0, "a=b", "a[x`-]b");
|
||
|
|
test(1, "a-b", "a[x---]b");
|
||
|
|
test(0, "a=b", "a[x---]b");
|
||
|
|
|
||
|
|
test(0, "a0z", "a[!0-9]z");
|
||
|
|
test(1, "aoz", "a[!0-9]z");
|
||
|
|
test(0, "a1", "a[!0-9]z");
|
||
|
|
test(0, "az", "a[!0-9]z");
|
||
|
|
test(0, "a9Z", "a[!0-9]z");
|
||
|
|
test(1, "acz", "a[!-0-9]z");
|
||
|
|
test(0, "a7z", "a[!-0-9]z");
|
||
|
|
test(0, "a-z", "a[!-0-9]z");
|
||
|
|
test(0, "a-b", "a[!-]b");
|
||
|
|
test(0, "a-b", "a[!x-]b");
|
||
|
|
test(0, "a=b", "a[!x-]b");
|
||
|
|
test(0, "a-b", "a[!x`-]b");
|
||
|
|
test(1, "a=b", "a[!x`-]b");
|
||
|
|
test(0, "a-b", "a[!x---]b");
|
||
|
|
test(1, "a=b", "a[!x---]b");
|
||
|
|
|
||
|
|
test(1, "a!z", "a[`!0-9]z");
|
||
|
|
test(1, "a3Z", "a[`!0-9]z");
|
||
|
|
test(0, "A3Z", "a[`!0`-9]z");
|
||
|
|
test(1, "a9z", "a[`!0`-9]z");
|
||
|
|
test(1, "a-z", "a[`!0`-9]z");
|
||
|
|
|
||
|
|
done:
|
||
|
|
printf("%d tests, %d failures\n", count, fails);
|
||
|
|
return (fails == 0 ? 0 : 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#endif /* TEST */
|
||
|
|
|
||
|
|
/* End fpattern.c */
|