/* SPDX-License-Identifier: BSD-3-Clause */ | |
/* $NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $ */ | |
/* | |
* Copyright (c) 1989, 1993, 1994 | |
* The Regents of the University of California. All rights reserved. | |
* | |
* This code is derived from software contributed to Berkeley by | |
* Guido van Rossum. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of the University nor the names of its contributors | |
* may be used to endorse or promote products derived from this software | |
* without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
* SUCH DAMAGE. | |
*/ | |
/* | |
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. | |
* Compares a filename or pathname to a pattern. | |
*/ | |
#include <ctype.h> | |
#include "fnmatch.h" | |
#include <string.h> | |
#define EOS '\0' | |
static inline int foldcase(int ch, int flags) | |
{ | |
if ((flags & FNM_CASEFOLD) != 0 && isupper(ch)) | |
return tolower(ch); | |
return ch; | |
} | |
#define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags)) | |
static const char * rangematch(const char *pattern, int test, int flags) | |
{ | |
int negate, ok, need; | |
char c, c2; | |
if (pattern == NULL) | |
{ | |
return NULL; | |
} | |
/* | |
* A bracket expression starting with an unquoted circumflex | |
* character produces unspecified results (IEEE 1003.2-1992, | |
* 3.13.2). This implementation treats it like '!', for | |
* consistency with the regular expression syntax. | |
* J.T. Conklin (conklin@ngai.kaleida.com) | |
*/ | |
if ((negate = (*pattern == '!' || *pattern == '^')) != 0) | |
++pattern; | |
need = 1; | |
for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) { | |
need = 0; | |
if (c == '/') | |
return (void *)-1; | |
if (c == '\\' && !(flags & FNM_NOESCAPE)) | |
c = FOLDCASE(*pattern++, flags); | |
if (c == EOS) | |
return NULL; | |
if (*pattern == '-' | |
&& (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS && | |
c2 != ']') { | |
pattern += 2; | |
if (c2 == '\\' && !(flags & FNM_NOESCAPE)) | |
c2 = FOLDCASE(*pattern++, flags); | |
if (c2 == EOS) | |
return NULL; | |
if (c <= test && test <= c2) | |
ok = 1; | |
} else if (c == test) | |
ok = 1; | |
} | |
return ok == negate ? NULL : pattern; | |
} | |
static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion) | |
{ | |
const char *stringstart, *r; | |
char c, test; | |
if ((pattern == NULL) || (string == NULL)) | |
{ | |
return FNM_NOMATCH; | |
} | |
if (recursion-- == 0) | |
return FNM_NORES; | |
for (stringstart = string;;) { | |
switch (c = FOLDCASE(*pattern++, flags)) { | |
case EOS: | |
if ((flags & FNM_LEADING_DIR) && *string == '/') | |
return 0; | |
return *string == EOS ? 0 : FNM_NOMATCH; | |
case '?': | |
if (*string == EOS) | |
return FNM_NOMATCH; | |
if (*string == '/' && (flags & FNM_PATHNAME)) | |
return FNM_NOMATCH; | |
if (*string == '.' && (flags & FNM_PERIOD) && | |
(string == stringstart || | |
((flags & FNM_PATHNAME) && *(string - 1) == '/'))) | |
return FNM_NOMATCH; | |
++string; | |
break; | |
case '*': | |
c = FOLDCASE(*pattern, flags); | |
/* Collapse multiple stars. */ | |
while (c == '*') | |
c = FOLDCASE(*++pattern, flags); | |
if (*string == '.' && (flags & FNM_PERIOD) && | |
(string == stringstart || | |
((flags & FNM_PATHNAME) && *(string - 1) == '/'))) | |
return FNM_NOMATCH; | |
/* Optimize for pattern with * at end or before /. */ | |
if (c == EOS) { | |
if (flags & FNM_PATHNAME) | |
return (flags & FNM_LEADING_DIR) || | |
strchr(string, '/') == NULL ? | |
0 : FNM_NOMATCH; | |
else | |
return 0; | |
} else if (c == '/' && flags & FNM_PATHNAME) { | |
if ((string = strchr(string, '/')) == NULL) | |
return FNM_NOMATCH; | |
break; | |
} | |
/* General case, use recursion. */ | |
while ((test = FOLDCASE(*string, flags)) != EOS) { | |
int e; | |
switch ((e = fnmatchx(pattern, string, | |
flags & ~FNM_PERIOD, recursion))) { | |
case FNM_NOMATCH: | |
break; | |
default: | |
return e; | |
} | |
if (test == '/' && flags & FNM_PATHNAME) | |
break; | |
++string; | |
} | |
return FNM_NOMATCH; | |
case '[': | |
if (*string == EOS) | |
return FNM_NOMATCH; | |
if (*string == '/' && flags & FNM_PATHNAME) | |
return FNM_NOMATCH; | |
if ((r = rangematch(pattern, | |
FOLDCASE(*string, flags), flags)) == NULL) | |
return FNM_NOMATCH; | |
if (r == (void *)-1) { | |
if (*string != '[') | |
return FNM_NOMATCH; | |
} else | |
pattern = r; | |
++string; | |
break; | |
case '\\': | |
if (!(flags & FNM_NOESCAPE)) { | |
if ((c = FOLDCASE(*pattern++, flags)) == EOS) { | |
c = '\0'; | |
--pattern; | |
} | |
} | |
/* FALLTHROUGH */ | |
default: | |
if (c != FOLDCASE(*string++, flags)) | |
return FNM_NOMATCH; | |
break; | |
} | |
} | |
/* NOTREACHED */ | |
} | |
int fnmatch(const char *pattern, const char *string, int flags) | |
{ | |
return fnmatchx(pattern, string, flags, 64); | |
} |