/* 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 #elif !defined assert #define assert(e) #endif #include #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 #include #if defined _WIN32 || defined __WIN32__ || defined WIN32 # include # define sleep(q) Sleep(q*1000) # include #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("\n"); } else { strcpy(fbuf, fname); printf("\"%s\"\n", fbuf); } if (pat == NULL) { printf(" \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�", "a`80"); test(0, "a�", "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 */