|
|
/*
|
|
|
from http://home.flash.net/~dtribble/text/fpattern.htm
|
|
|
2000/12/12
|
|
|
by Perry
|
|
|
License statement is in comments below
|
|
|
*/
|
|
|
|
|
|
/******************************************************************************
|
|
|
* 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.
|
|
|
*
|
|
|
* 'FPAT_DELIM' must be #define'd if pathname separators are to be handled
|
|
|
* explicitly.
|
|
|
*
|
|
|
* History
|
|
|
* 1.00 1997-01-03 David Tribble.
|
|
|
* First cut.
|
|
|
* 1.01 1997-01-03 David Tribble.
|
|
|
* Added SUB pattern character.
|
|
|
* Added fpattern_matchn().
|
|
|
* 1.02 1997-01-12 David Tribble.
|
|
|
* Fixed missing lowercase matching cases.
|
|
|
* 1.03 1997-01-13 David Tribble.
|
|
|
* Pathname separator code is now controlled by FPAT_DELIM macro.
|
|
|
* 1.04 1997-01-14 David Tribble.
|
|
|
* Added QUOTE macro.
|
|
|
* 1.05 1997-01-15 David Tribble.
|
|
|
* Handles special case of empty pattern and empty filename.
|
|
|
* 1.06 1997-01-26 David Tribble.
|
|
|
* Changed range negation character from '^' to '!', ala Unix.
|
|
|
* 1.07 1997-08-02 David Tribble.
|
|
|
* Uses the 'FPAT_XXX' constants.
|
|
|
* 1.08 1998-06-28 David Tribble.
|
|
|
* Minor fixed for MS-VC++ (5.0).
|
|
|
*
|
|
|
* Modifications by Thiadmer Riemersma (optional case-sensitive matching,
|
|
|
* optional explicit array length, repeated set syntax), 2005-11-27.
|
|
|
*
|
|
|
* 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.
|
|
|
*
|
|
|
* Other queries can be sent to:
|
|
|
* dtribble@technologist.com
|
|
|
* david.tribble@beasys.com
|
|
|
* dtribble@flash.net
|
|
|
*
|
|
|
* Copyright 1997-1998 by David R. Tribble, all rights reserved.
|
|
|
* Copyright (c) 2005-2015, ITB CompuPhase, all rights reserved.
|
|
|
* www.compuphase.com
|
|
|
*
|
|
|
* Version: $Id: fpattern.c 5181 2015-01-21 09:44:28Z thiadmer $
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* System includes */
|
|
|
#if defined TEST
|
|
|
#include <assert.h>
|
|
|
#elif !defined assert
|
|
|
#define assert(e)
|
|
|
#endif
|
|
|
#include <string.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
|
|
|
|
|
|
#ifdef FPAT_SUBCLOS
|
|
|
# if TEST
|
|
|
# undef FPAT_CLOSP
|
|
|
# define FPAT_CLOSP '~'
|
|
|
# endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
#define ishexdigit(c) ( ((c)>='0' && (c)<='9') || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F') )
|
|
|
#define hexdigit(c) ( ((c)>='0' && (c)<='9') ? (c) - '0' : 10 + \
|
|
|
( ((c)>='a' && (c)<='f') ? (c) - 'a' : (c) - 'A' ) )
|
|
|
#if !defined tolower
|
|
|
# define tolower(c) ( (c)>='A' && (c)<='Z' ? (c) - 'A' + 'a' : (c) )
|
|
|
#endif
|
|
|
|
|
|
#define BITSET(set,idx) ( (set)[(idx)/8] |= (unsigned char)(1 << ((idx) & 7)) )
|
|
|
#define BITGET(set,idx) ( (set)[(idx)/8] & (1 << ((idx) & 7)) )
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
* fpattern_isvalid()
|
|
|
* Checks that filename pattern 'pat' is a well-formed pattern.
|
|
|
*
|
|
|
* Returns
|
|
|
* 0 (FPAT_INVALID) if 'pat' is an INVALID pattern, 1 or 2 for VALID patterns
|
|
|
* (FPAT_CLOSED and FPAT_OPEN respectively).
|
|
|
* A valid pattern may be "closed" (with a non-repeating terminator) or
|
|
|
* "open-ended" (where the last character in the pattern may be repeated 0 or
|
|
|
* more times).
|
|
|
*
|
|
|
* Caveats
|
|
|
* If 'pat' is NULL, 0 (FPAT_INVALID) is returned.
|
|
|
*
|
|
|
* If 'pat' is empty (""), 1 (FPAT_CLOSED) 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)
|
|
|
{
|
|
|
static const char specialchars[] = { FPAT_QUOTE, FPAT_SET_THRU, FPAT_SET_L,
|
|
|
FPAT_SET_R, FPAT_MSET_L, FPAT_MSET_R,
|
|
|
FPAT_ANY, FPAT_NOT, FPAT_CLOS, FPAT_CLOSP,
|
|
|
'\0' };
|
|
|
int len, open;
|
|
|
char close, pch, pch2;
|
|
|
unsigned char mask[256/sizeof(unsigned char)];
|
|
|
|
|
|
/* Check args */
|
|
|
if (pat == NULL)
|
|
|
return (FPAT_INVALID);
|
|
|
|
|
|
open = FALSE; /* pattern is not open-ended */
|
|
|
|
|
|
/* Verify that the pattern is valid */
|
|
|
for (len = 0; pat[len] != '\0'; len++)
|
|
|
{
|
|
|
switch (pat[len])
|
|
|
{
|
|
|
case FPAT_SET_L:
|
|
|
#if defined FPAT_MSET_ENABLED
|
|
|
case FPAT_MSET_L:
|
|
|
#endif
|
|
|
/* Char set */
|
|
|
if (pat[len] == FPAT_SET_L)
|
|
|
{
|
|
|
open = FALSE;
|
|
|
close = FPAT_SET_R;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
open = TRUE;
|
|
|
close = FPAT_MSET_R;
|
|
|
}
|
|
|
len++;
|
|
|
if (pat[len] == FPAT_NOT)
|
|
|
len++; /* Set negation */
|
|
|
|
|
|
memset(mask,0,sizeof mask);
|
|
|
while (pat[len] != close)
|
|
|
{
|
|
|
pch = pat[len];
|
|
|
if (pat[len] == FPAT_QUOTE) {
|
|
|
len++; /* Quoted char */
|
|
|
pch = pat[len];
|
|
|
if (ishexdigit(pch)) {
|
|
|
len++; /* Hex digits should come in pairs */
|
|
|
if (!ishexdigit(pat[len]))
|
|
|
return (FPAT_INVALID);
|
|
|
pch2 = pat[len];
|
|
|
pch = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else if (strchr(specialchars,pat[len])==NULL) {
|
|
|
return (FPAT_INVALID); /* escaped char should be special char */
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
if (pat[len] == '\0')
|
|
|
return (FPAT_INVALID); /* Missing closing bracket */
|
|
|
if (BITGET(mask,pch))
|
|
|
return (FPAT_INVALID);
|
|
|
BITSET(mask,pch);
|
|
|
len++;
|
|
|
|
|
|
if (pat[len] == FPAT_SET_THRU && (pat[len+1] != FPAT_SET_THRU || pch == FPAT_SET_THRU))
|
|
|
{
|
|
|
/* Char range */
|
|
|
len++;
|
|
|
if (pat[len] == FPAT_QUOTE) {
|
|
|
len++; /* Quoted char */
|
|
|
if (ishexdigit(pat[len])) {
|
|
|
len++; /* Hex digits should come in pairs */
|
|
|
if (!ishexdigit(pat[len]))
|
|
|
return (FPAT_INVALID);
|
|
|
} else if (strchr(specialchars,pat[len])==NULL) {
|
|
|
return (FPAT_INVALID); /* escaped char should be special char */
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
|
if (pat[len] == '\0')
|
|
|
return (FPAT_INVALID); /* Missing closing bracket */
|
|
|
if (len<2 || (unsigned char)pat[len-2]>(unsigned char)pat[len]
|
|
|
|| (pat[len-2]==pat[len] && pat[len]!=FPAT_SET_THRU))
|
|
|
return (FPAT_INVALID); /* invalid range (decrementing) */
|
|
|
//??? also set bits of characters in range?
|
|
|
len++;
|
|
|
}
|
|
|
|
|
|
if (pat[len] == '\0')
|
|
|
return (FPAT_INVALID); /* Missing closing bracket */
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case FPAT_QUOTE:
|
|
|
/* Quoted char */
|
|
|
len++;
|
|
|
open = FALSE;
|
|
|
if (ishexdigit(pat[len])) {
|
|
|
len++; /* Hex digits should come in pairs */
|
|
|
if (!ishexdigit(pat[len]))
|
|
|
return (FPAT_INVALID);
|
|
|
} else if (strchr(specialchars,pat[len])==NULL) {
|
|
|
return (FPAT_INVALID); /* escaped char should be special char */
|
|
|
} /* if */
|
|
|
if (pat[len] == '\0')
|
|
|
return (FPAT_INVALID); /* Missing quoted char */
|
|
|
break;
|
|
|
|
|
|
#if defined FPAT_NOT_ENABLED
|
|
|
case FPAT_NOT:
|
|
|
/* Negated pattern */
|
|
|
len++;
|
|
|
open = FALSE;
|
|
|
if (pat[len] == '\0')
|
|
|
return (FPAT_INVALID); /* Missing subpattern */
|
|
|
break;
|
|
|
#endif
|
|
|
|
|
|
case FPAT_CLOS:
|
|
|
open = TRUE;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
/* Valid character */
|
|
|
open = FALSE;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return (open ? FPAT_OPEN : FPAT_CLOSED);
|
|
|
}
|
|
|
|
|
|
|
|
|
/* NOTA BENE
|
|
|
* =========
|
|
|
* To modify the pattern matcher, modify the case-insensitive version and
|
|
|
* copy it to the case-sensitive version. In steps:
|
|
|
* - modify the function fpatter_submatch_tolower(), here below
|
|
|
* - make a copy of the entire function
|
|
|
* - erase all references to "tolower" in the copy
|
|
|
*/
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
* fpattern_submatch_tolower()
|
|
|
* Attempts to match subpattern 'pat' to subfilename 'fname'.
|
|
|
*
|
|
|
* Returns
|
|
|
* 1 (true) if the subfilename matches, otherwise 0 (false).
|
|
|
*
|
|
|
* 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_tolower(const char *pat, const char *fname, int flength)
|
|
|
{
|
|
|
int fch;
|
|
|
int pch, pch2;
|
|
|
int i;
|
|
|
int yes, match;
|
|
|
int lo, hi;
|
|
|
|
|
|
/* Attempt to match subpattern against subfilename */
|
|
|
while (*pat != '\0')
|
|
|
{
|
|
|
fch = (unsigned char)*fname;
|
|
|
pch = (unsigned char)*pat;
|
|
|
pat++;
|
|
|
|
|
|
switch (pch)
|
|
|
{
|
|
|
case FPAT_ANY:
|
|
|
/* Match a single char */
|
|
|
#if defined FPAT_DELIM
|
|
|
if (fch == FPAT_DEL || fch == FPAT_DEL2 || fch == '\0')
|
|
|
return (FALSE);
|
|
|
#else
|
|
|
if (flength == 0)
|
|
|
return (FALSE);
|
|
|
#endif
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
|
|
|
case FPAT_CLOS:
|
|
|
/* Match zero or more chars */
|
|
|
#if defined FPAT_DELIM
|
|
|
i = 0;
|
|
|
while (fname[i] != '\0' &&
|
|
|
fname[i] != FPAT_DEL && fname[i] != FPAT_DEL2)
|
|
|
i++;
|
|
|
#else
|
|
|
i = flength;
|
|
|
#endif
|
|
|
while (i >= 0)
|
|
|
{
|
|
|
if (fpattern_submatch_tolower(pat, fname+i, flength-i))
|
|
|
return (TRUE);
|
|
|
i--;
|
|
|
}
|
|
|
return (FALSE);
|
|
|
|
|
|
#ifdef FPAT_SUBCLOS
|
|
|
case FPAT_CLOSP:
|
|
|
/* Match zero or more chars */
|
|
|
i = 0;
|
|
|
while (i < flength &&
|
|
|
#if defined FPAT_DELIM
|
|
|
fname[i] != FPAT_DEL && fname[i] != FPAT_DEL2 &&
|
|
|
#endif
|
|
|
fname[i] != '.')
|
|
|
i++;
|
|
|
while (i >= 0)
|
|
|
{
|
|
|
if (fpattern_submatch_tolower(pat, fname+i, flength-i))
|
|
|
return (TRUE);
|
|
|
i--;
|
|
|
}
|
|
|
return (FALSE);
|
|
|
#endif
|
|
|
|
|
|
case FPAT_QUOTE:
|
|
|
/* Match a quoted char */
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *++pat;
|
|
|
pch = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} /* if */
|
|
|
if (tolower(fch) != tolower(pch))
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
pat++;
|
|
|
break;
|
|
|
|
|
|
case FPAT_SET_L:
|
|
|
/* Match char set/range */
|
|
|
yes = TRUE;
|
|
|
if (*pat == FPAT_NOT) {
|
|
|
pat++;
|
|
|
yes = FALSE; /* Set negation */
|
|
|
} /* if */
|
|
|
|
|
|
/* Look for [s], [-], [abc], [a-c] */
|
|
|
match = !yes;
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
lo = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
|
|
|
if (*pat == FPAT_SET_THRU) {
|
|
|
/* Range */
|
|
|
pat++;
|
|
|
pch = (unsigned char)*pat++;
|
|
|
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
hi = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
|
|
|
/* Compare character to set range */
|
|
|
if (tolower(fch) >= tolower(lo) && tolower(fch) <= tolower(hi)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no need to
|
|
|
* search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
/* Compare character to single char from set */
|
|
|
if (tolower(fch) == tolower(lo)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no need to
|
|
|
* search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
|
assert(*pat != '\0');
|
|
|
} /* while */
|
|
|
|
|
|
if (!match)
|
|
|
return (FALSE);
|
|
|
|
|
|
fname++;
|
|
|
flength--;
|
|
|
assert(*pat == FPAT_SET_R);
|
|
|
pat++;
|
|
|
break;
|
|
|
|
|
|
#if defined FPAT_MSET_ENABLED
|
|
|
case FPAT_MSET_L:
|
|
|
/* Match zero or more characters in a char set/range */
|
|
|
yes = TRUE;
|
|
|
if (*pat == FPAT_NOT) {
|
|
|
pat++;
|
|
|
yes = FALSE; /* Set negation */
|
|
|
} /* if */
|
|
|
|
|
|
do {
|
|
|
const char *org_pat = pat;
|
|
|
/* Look for [s], [-], [abc], [a-c] */
|
|
|
match = !yes;
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
lo = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
|
|
|
if (*pat == FPAT_SET_THRU) {
|
|
|
/* Range */
|
|
|
pat++;
|
|
|
pch = (unsigned char)*pat++;
|
|
|
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
hi = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
|
|
|
/* Compare character to set range */
|
|
|
if (tolower(fch) >= tolower(lo) && tolower(fch) <= tolower(hi)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no
|
|
|
* need to search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
/* Compare character to single char from the set */
|
|
|
if (tolower(fch) == tolower(lo)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no
|
|
|
* need to search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
|
assert(*pat != '\0');
|
|
|
} /* while */
|
|
|
|
|
|
assert(*pat == FPAT_MSET_R);
|
|
|
|
|
|
if (match) {
|
|
|
fname++;
|
|
|
flength--;
|
|
|
fch = *fname;
|
|
|
if (flength > 0)
|
|
|
pat = org_pat;
|
|
|
} /* if */
|
|
|
|
|
|
} while (match && flength > 0);
|
|
|
|
|
|
pat++;
|
|
|
break;
|
|
|
#endif /* FPAT_MSET_ENABLED */
|
|
|
|
|
|
#if defined FPAT_NOT_ENABLED
|
|
|
case FPAT_NOT:
|
|
|
/* Match only if rest of pattern does not match */
|
|
|
assert(*pat != '\0');
|
|
|
i = fpattern_submatch_tolower(pat, fname, flength);
|
|
|
return !i;
|
|
|
#endif
|
|
|
|
|
|
#if defined FPAT_DELIM
|
|
|
case FPAT_DEL:
|
|
|
#if FPAT_DEL2 != FPAT_DEL
|
|
|
case FPAT_DEL2:
|
|
|
#endif
|
|
|
/* Match path delimiter char */
|
|
|
if (fch != FPAT_DEL && fch != FPAT_DEL2)
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
#endif
|
|
|
|
|
|
default:
|
|
|
/* Match a (non-null) char exactly */
|
|
|
if (tolower(fch) != tolower(pch))
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Check for complete match */
|
|
|
if (flength != 0)
|
|
|
return (FALSE);
|
|
|
|
|
|
/* Successful match */
|
|
|
return (TRUE);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* fpattern_submatch()
|
|
|
* See fpattern_submatch_tolower()
|
|
|
*/
|
|
|
|
|
|
static int fpattern_submatch(const char *pat, const char *fname, int flength)
|
|
|
{
|
|
|
int fch;
|
|
|
int pch, pch2;
|
|
|
int i;
|
|
|
int yes, match;
|
|
|
int lo, hi;
|
|
|
|
|
|
/* Attempt to match subpattern against subfilename */
|
|
|
while (*pat != '\0')
|
|
|
{
|
|
|
fch = (unsigned char)*fname;
|
|
|
pch = (unsigned char)*pat;
|
|
|
pat++;
|
|
|
|
|
|
switch (pch)
|
|
|
{
|
|
|
case FPAT_ANY:
|
|
|
/* Match a single char */
|
|
|
#if defined FPAT_DELIM
|
|
|
if (fch == FPAT_DEL || fch == FPAT_DEL2 || fch == '\0')
|
|
|
return (FALSE);
|
|
|
#else
|
|
|
if (flength == 0)
|
|
|
return (FALSE);
|
|
|
#endif
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
|
|
|
case FPAT_CLOS:
|
|
|
/* Match zero or more chars */
|
|
|
#if defined FPAT_DELIM
|
|
|
i = 0;
|
|
|
while (fname[i] != '\0' &&
|
|
|
fname[i] != FPAT_DEL && fname[i] != FPAT_DEL2)
|
|
|
i++;
|
|
|
#else
|
|
|
i = flength;
|
|
|
#endif
|
|
|
while (i >= 0)
|
|
|
{
|
|
|
if (fpattern_submatch(pat, fname+i, flength-i))
|
|
|
return (TRUE);
|
|
|
i--;
|
|
|
}
|
|
|
return (FALSE);
|
|
|
|
|
|
#ifdef FPAT_SUBCLOS
|
|
|
case FPAT_CLOSP:
|
|
|
/* Match zero or more chars */
|
|
|
i = 0;
|
|
|
while (i < flength &&
|
|
|
#if defined FPAT_DELIM
|
|
|
fname[i] != FPAT_DEL && fname[i] != FPAT_DEL2 &&
|
|
|
#endif
|
|
|
fname[i] != '.')
|
|
|
i++;
|
|
|
while (i >= 0)
|
|
|
{
|
|
|
if (fpattern_submatch(pat, fname+i, flength-i))
|
|
|
return (TRUE);
|
|
|
i--;
|
|
|
}
|
|
|
return (FALSE);
|
|
|
#endif
|
|
|
|
|
|
case FPAT_QUOTE:
|
|
|
/* Match a quoted char */
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *++pat;
|
|
|
pch = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} /* if */
|
|
|
if ((fch) != (pch))
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
pat++;
|
|
|
break;
|
|
|
|
|
|
case FPAT_SET_L:
|
|
|
/* Match char set/range */
|
|
|
yes = TRUE;
|
|
|
if (*pat == FPAT_NOT) {
|
|
|
pat++;
|
|
|
yes = FALSE; /* Set negation */
|
|
|
} /* if */
|
|
|
|
|
|
/* Look for [s], [-], [abc], [a-c] */
|
|
|
match = !yes;
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
lo = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
|
|
|
if (*pat == FPAT_SET_THRU) {
|
|
|
/* Range */
|
|
|
pat++;
|
|
|
pch = (unsigned char)*pat++;
|
|
|
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
hi = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
|
|
|
/* Compare character to set range */
|
|
|
if ((fch) >= (lo) && (fch) <= (hi)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no need to
|
|
|
* search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
/* Compare character to single char from set */
|
|
|
if ((fch) == (lo)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no need to
|
|
|
* search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_SET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
|
assert(*pat != '\0');
|
|
|
} /* while */
|
|
|
|
|
|
if (!match)
|
|
|
return (FALSE);
|
|
|
|
|
|
fname++;
|
|
|
flength--;
|
|
|
assert(*pat == FPAT_SET_R);
|
|
|
pat++;
|
|
|
break;
|
|
|
|
|
|
#if defined FPAT_MSET_ENABLED
|
|
|
case FPAT_MSET_L:
|
|
|
/* Match zero or more characters in a char set/range */
|
|
|
yes = TRUE;
|
|
|
if (*pat == FPAT_NOT) {
|
|
|
pat++;
|
|
|
yes = FALSE; /* Set negation */
|
|
|
} /* if */
|
|
|
|
|
|
do {
|
|
|
const char *org_pat = pat;
|
|
|
/* Look for [s], [-], [abc], [a-c] */
|
|
|
match = !yes;
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
lo = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
lo = pch;
|
|
|
} /* if */
|
|
|
|
|
|
if (*pat == FPAT_SET_THRU) {
|
|
|
/* Range */
|
|
|
pat++;
|
|
|
pch = (unsigned char)*pat++;
|
|
|
|
|
|
if (pch == FPAT_QUOTE) {
|
|
|
assert(*pat != '\0');
|
|
|
pch = (unsigned char)*pat++;
|
|
|
if (ishexdigit(pch)) {
|
|
|
pch2 = *pat++;
|
|
|
hi = (hexdigit(pch) << 4) | hexdigit(pch2);
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
hi = pch;
|
|
|
} /* if */
|
|
|
|
|
|
/* Compare character to set range */
|
|
|
if ((fch) >= (lo) && (fch) <= (hi)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no
|
|
|
* need to search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} else {
|
|
|
/* Compare character to single char from the set */
|
|
|
if ((fch) == (lo)) {
|
|
|
match = yes;
|
|
|
/* skip to the end of the set in the pattern (no
|
|
|
* need to search further once a match is found)
|
|
|
*/
|
|
|
while (*pat != FPAT_MSET_R) {
|
|
|
assert(*pat != '\0');
|
|
|
pat++;
|
|
|
} /* while */
|
|
|
break;
|
|
|
} /* if */
|
|
|
} /* if */
|
|
|
|
|
|
assert(*pat != '\0');
|
|
|
} /* while */
|
|
|
|
|
|
assert(*pat == FPAT_MSET_R);
|
|
|
|
|
|
if (match) {
|
|
|
fname++;
|
|
|
flength--;
|
|
|
fch = *fname;
|
|
|
if (flength > 0)
|
|
|
pat = org_pat;
|
|
|
} /* if */
|
|
|
|
|
|
} while (match && flength > 0);
|
|
|
|
|
|
pat++;
|
|
|
break;
|
|
|
#endif /* FPAT_MSET_ENABLED */
|
|
|
|
|
|
#if defined FPAT_NOT_ENABLED
|
|
|
case FPAT_NOT:
|
|
|
/* Match only if rest of pattern does not match */
|
|
|
assert(*pat != '\0');
|
|
|
i = fpattern_submatch(pat, fname, flength);
|
|
|
return !i;
|
|
|
#endif
|
|
|
|
|
|
#if defined FPAT_DELIM
|
|
|
case FPAT_DEL:
|
|
|
#if FPAT_DEL2 != FPAT_DEL
|
|
|
case FPAT_DEL2:
|
|
|
#endif
|
|
|
/* Match path delimiter char */
|
|
|
if (fch != FPAT_DEL && fch != FPAT_DEL2)
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
#endif
|
|
|
|
|
|
default:
|
|
|
/* Match a (non-null) char exactly */
|
|
|
if ((fch) != (pch))
|
|
|
return (FALSE);
|
|
|
fname++;
|
|
|
flength--;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Check for complete match */
|
|
|
if (flength != 0)
|
|
|
return (FALSE);
|
|
|
|
|
|
/* Successful match */
|
|
|
return (TRUE);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
* fpattern_match()
|
|
|
* Attempts to match pattern 'pat' to filename 'fname'. The comparison is case
|
|
|
* sensitive if 'keepcase' is true, and case insensitive otherwise. The 'flength'
|
|
|
* parameter allows to check partial strings, or to check strings with embedded
|
|
|
* zero bytes. When 'flength' is -1, it is set to the string length.
|
|
|
*
|
|
|
* Returns
|
|
|
* 1 (true) if the filename matches, otherwise 0 (false).
|
|
|
*
|
|
|
* Caveats
|
|
|
* If 'fname' is null, zero (false) is returned.
|
|
|
*
|
|
|
* If 'pat' is null, zero (false) 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, zero (false) 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 flength, int keepcase)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
/* 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 (flength < 0)
|
|
|
flength = (int)strlen(fname);
|
|
|
if (flength == 0)
|
|
|
return (pat[0] == '\0'); /* Special case */
|
|
|
if (keepcase)
|
|
|
rc = fpattern_submatch(pat, fname, flength);
|
|
|
else
|
|
|
rc = fpattern_submatch_tolower(pat, fname, flength);
|
|
|
|
|
|
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().
|
|
|
*
|
|
|
* NOTA BENE: this function may crash on invalid patterns!
|
|
|
* =======================================================
|
|
|
*
|
|
|
* Returns
|
|
|
* 1 (true) if the filename matches, otherwise 0 (false).
|
|
|
*
|
|
|
* Caveats
|
|
|
* If 'fname' is null, zero (false) is returned.
|
|
|
*
|
|
|
* If 'pat' is null, zero (false) 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 flength, int keepcase)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
/* Check args */
|
|
|
if (fname == NULL)
|
|
|
return (FALSE);
|
|
|
|
|
|
if (pat == NULL)
|
|
|
return (FALSE);
|
|
|
|
|
|
/* Assume that pattern is well-formed
|
|
|
* NOTA BENE: this function may crash on invalid patterns!
|
|
|
*/
|
|
|
assert(fpattern_isvalid(pat));
|
|
|
|
|
|
/* Attempt to match pattern against filename */
|
|
|
if (flength < 0)
|
|
|
flength = (int)strlen(fname);
|
|
|
if (keepcase)
|
|
|
rc = fpattern_submatch(pat, fname, flength);
|
|
|
else
|
|
|
rc = fpattern_submatch_tolower(pat, fname, flength);
|
|
|
|
|
|
return (rc);
|
|
|
}
|
|
|
|
|
|
/* returns the largest packet that matches the pattern */
|
|
|
int fpattern_matchcount(const char *pat, const char *fname, int flength, int minlength, int keepcase)
|
|
|
{
|
|
|
int len;
|
|
|
|
|
|
if (fname == NULL)
|
|
|
return (FALSE);
|
|
|
|
|
|
if (pat == NULL)
|
|
|
return (FALSE);
|
|
|
|
|
|
/* Assume that pattern is well-formed */
|
|
|
|
|
|
/* Attempt to match pattern against filename */
|
|
|
if (flength < 0)
|
|
|
flength = (int)strlen(fname);
|
|
|
|
|
|
if (keepcase) {
|
|
|
for (len = flength; len > minlength; len--)
|
|
|
if (fpattern_submatch(pat, fname, len))
|
|
|
break;
|
|
|
} else {
|
|
|
for (len = flength; len > minlength; len--)
|
|
|
if (fpattern_submatch_tolower(pat, fname, len))
|
|
|
break;
|
|
|
} /* if */
|
|
|
|
|
|
return (len > minlength) ? len : 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
|
|
#include <locale.h>
|
|
|
#include <stdio.h>
|
|
|
#if defined _WIN32 || defined __WIN32__ || defined WIN32
|
|
|
# include <stdlib.h>
|
|
|
# define sleep(q) Sleep(q*1000)
|
|
|
# include <windows.h>
|
|
|
#endif
|
|
|
|
|
|
/* 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,
|
|
|
-1, FALSE);
|
|
|
|
|
|
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 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, "*", "`*");
|
|
|
test(1, "A[", "a`[");
|
|
|
test(1, "a`x", "a``x");
|
|
|
test(1, "*`?", "`*```?");
|
|
|
test(1, "a*x", "a`*x");
|
|
|
test(1, "a<EFBFBD>", "a`80");
|
|
|
test(0, "a<EFBFBD>", "a`8");
|
|
|
|
|
|
#if defined FPAT_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");
|
|
|
|
|
|
#ifdef FPAT_SUBCLOS
|
|
|
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");
|
|
|
#endif
|
|
|
|
|
|
#if defined FPAT_NOT_ENABLED
|
|
|
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");
|
|
|
#endif
|
|
|
|
|
|
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");
|
|
|
|
|
|
test(1, "ac", "a{b}c");
|
|
|
test(1, "abc", "a{b}c");
|
|
|
test(1, "abbc", "a{b}c");
|
|
|
test(1, "aBbBc", "a{b}c");
|
|
|
test(1, "abc", "a{bB}c");
|
|
|
test(1, "abc", "a{bpz}c");
|
|
|
test(1, "azc", "a{bcz}");
|
|
|
test(0, "ab", "a{b}c");
|
|
|
test(0, "axc", "a{b}c");
|
|
|
|
|
|
assert(fpattern_isvalid("a[`[`]]") == 1);
|
|
|
assert(fpattern_isvalid("a{`{`}}") == 2);
|
|
|
assert(fpattern_isvalid("a[b-z]") == 1);
|
|
|
assert(fpattern_isvalid("a?") == 1);
|
|
|
assert(fpattern_isvalid("a{b-z}") == 2);
|
|
|
assert(fpattern_isvalid("a*") == 2);
|
|
|
assert(fpattern_isvalid("a[aba]") == FPAT_INVALID);
|
|
|
assert(fpattern_isvalid("a[z-b]") == FPAT_INVALID);
|
|
|
assert(fpattern_isvalid("a[b c ]") == FPAT_INVALID);
|
|
|
assert(fpattern_isvalid("a[`a]") == FPAT_INVALID);
|
|
|
|
|
|
assert(fpattern_isvalid("[`80-`ff]") == FPAT_CLOSED);
|
|
|
assert(fpattern_isvalid("[`80-`ff]{`00-`7f}") == FPAT_OPEN);
|
|
|
assert(fpattern_isvalid("???") == FPAT_CLOSED);
|
|
|
test(1, "\x90\x40\x4f", "[`80-`ff]{`00-`7f}");
|
|
|
test(0, "\x90\x40\x4f\x90", "[`80-`ff]{`00-`7f}");
|
|
|
test(1, "\x90\x40\x4f", "[`80`90]*");
|
|
|
|
|
|
printf("%d tests, %d failures\n", count, fails);
|
|
|
return (fails == 0 ? 0 : 1);
|
|
|
}
|
|
|
|
|
|
|
|
|
#endif /* TEST */
|
|
|
|
|
|
/* End fpattern.c */
|