#include <fnmatch.h>
#include <limits.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#include "collate.h"
#define EOS '\0'
#define RANGE_MATCH 1
#define RANGE_NOMATCH 0
#define RANGE_ERROR (-1)
static int rangematch(const char *, wchar_t, const char *, int, char **,
char **, mbstate_t *, mbstate_t *);
static int fnmatch1(const char *, const char *, const char *, int, mbstate_t,
mbstate_t);
int
fnmatch(const char *pattern, const char *string, int flags)
{
static const mbstate_t initial;
return (fnmatch1(pattern, string, string, flags, initial, initial));
}
static int
fnmatch1(const char *pattern, const char *string, const char *stringstart,
int flags, mbstate_t patmbs, mbstate_t strmbs)
{
const char *bt_pattern, *bt_string;
mbstate_t bt_patmbs, bt_strmbs;
char *newp, *news;
char c;
wchar_t pc, sc;
size_t pclen, sclen;
bt_pattern = bt_string = NULL;
for (;;) {
pclen = mbrtowc(&pc, pattern, MB_LEN_MAX, &patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (FNM_NOMATCH);
pattern += pclen;
sclen = mbrtowc(&sc, string, MB_LEN_MAX, &strmbs);
if (sclen == (size_t)-1 || sclen == (size_t)-2) {
sc = (unsigned char)*string;
sclen = 1;
memset(&strmbs, 0, sizeof(strmbs));
}
switch (pc) {
case EOS:
if ((flags & FNM_LEADING_DIR) && sc == '/')
return (0);
if (sc == EOS)
return (0);
goto backtrack;
case '?':
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && (flags & FNM_PATHNAME))
goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
string += sclen;
break;
case '*':
c = *pattern;
while (c == '*')
c = *++pattern;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
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;
}
bt_pattern = pattern;
bt_patmbs = patmbs;
bt_string = string;
bt_strmbs = strmbs;
break;
case '[':
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && (flags & FNM_PATHNAME))
goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
goto backtrack;
switch (rangematch(pattern, sc, string + sclen, flags,
&newp, &news, &patmbs, &strmbs)) {
case RANGE_ERROR:
goto norm;
case RANGE_MATCH:
pattern = newp;
string = news;
break;
case RANGE_NOMATCH:
goto backtrack;
}
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
pclen = mbrtowc(&pc, pattern, MB_LEN_MAX,
&patmbs);
if (pclen == 0 || pclen == (size_t)-1 ||
pclen == (size_t)-2)
return (FNM_NOMATCH);
pattern += pclen;
}
default:
norm:
string += sclen;
if (pc == sc)
;
else if ((flags & FNM_CASEFOLD) &&
(towlower(pc) == towlower(sc)))
;
else {
backtrack:
if (bt_pattern == NULL)
return (FNM_NOMATCH);
sclen = mbrtowc(&sc, bt_string, MB_LEN_MAX,
&bt_strmbs);
if (sclen == (size_t)-1 ||
sclen == (size_t)-2) {
sc = (unsigned char)*bt_string;
sclen = 1;
memset(&bt_strmbs, 0,
sizeof(bt_strmbs));
}
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && flags & FNM_PATHNAME)
return (FNM_NOMATCH);
bt_string += sclen;
pattern = bt_pattern;
patmbs = bt_patmbs;
string = bt_string;
strmbs = bt_strmbs;
}
break;
}
}
}
static int
rangematch(const char *pattern, wchar_t test, const char *string, int flags,
char **newp, char **news, mbstate_t *patmbs, mbstate_t *strmbs)
{
int negate, ok;
wchar_t c, c2;
size_t pclen;
const char *origpat;
struct xlocale_collate *table =
(struct xlocale_collate *)__get_locale()->components[XLC_COLLATE];
wchar_t buf[COLLATE_STR_LEN];
const char *cp, *savestring;
int special;
mbstate_t save;
size_t sclen, len;
if ((negate = (*pattern == '!' || *pattern == '^')))
++pattern;
if (flags & FNM_CASEFOLD)
test = towlower(test);
ok = 0;
origpat = pattern;
for (;;) {
c = 0;
if (*pattern == ']' && pattern > origpat) {
break;
} else if (*pattern == '\0') {
return (RANGE_ERROR);
} else if (*pattern == '/' && (flags & FNM_PATHNAME)) {
return (RANGE_NOMATCH);
} else if (*pattern == '\\' && !(flags & FNM_NOESCAPE)) {
pattern++;
} else if (*pattern == '[' &&
((special = *(pattern + 1)) == '.' ||
special == '=' || special == ':')) {
cp = (pattern += 2);
while ((cp = strchr(cp, special))) {
if (*(cp + 1) == ']')
break;
cp++;
}
if (!cp)
return (RANGE_ERROR);
if (special == '.') {
treat_like_collating_symbol:
len = __collate_collating_symbol(buf,
COLLATE_STR_LEN, pattern,
cp - pattern, patmbs);
if (len == (size_t)-1 || len == 0)
return (RANGE_ERROR);
pattern = cp + 2;
if (len > 1) {
wchar_t *wp, sc;
if (*(cp + 2) == '-' &&
*(cp + 3) != EOS &&
*(cp + 3) != ']')
return (RANGE_ERROR);
wp = buf;
if (test != *wp++)
continue;
if (len == 1) {
ok = 1;
break;
}
memcpy(&save, strmbs, sizeof(save));
savestring = string;
while (--len > 0) {
sclen = mbrtowc(&sc, string,
MB_LEN_MAX, strmbs);
if (sclen == (size_t)-1 ||
sclen == (size_t)-2) {
sc = (unsigned char)*string;
sclen = 1;
memset(&strmbs, 0,
sizeof(strmbs));
}
if (sc != *wp++) {
memcpy(strmbs, &save,
sizeof(save));
string = savestring;
break;
}
string += sclen;
}
if (len == 0) {
ok = 1;
break;
}
continue;
}
c = *buf;
} else if (special == '=') {
int ec;
memcpy(&save, patmbs, sizeof(save));
ec = __collate_equiv_class(pattern,
cp - pattern, patmbs);
if (ec < 0)
return (RANGE_ERROR);
if (ec == 0) {
memcpy(patmbs, &save, sizeof(save));
goto treat_like_collating_symbol;
}
pattern = cp + 2;
if (*(cp + 2) == '-' && *(cp + 3) != EOS &&
*(cp + 3) != ']')
return (RANGE_ERROR);
len = __collate_equiv_match(ec, NULL, 0, test,
string, strlen(string), strmbs, &sclen);
if (len < 0)
return (RANGE_ERROR);
if (len > 0) {
ok = 1;
string += sclen;
break;
}
continue;
} else {
wctype_t charclass;
char name[CHARCLASS_NAME_MAX + 1];
if (*(cp + 2) == '-' && *(cp + 3) != EOS &&
*(cp + 3) != ']')
return (RANGE_ERROR);
if (cp - pattern > CHARCLASS_NAME_MAX)
return (RANGE_ERROR);
strlcpy(name, pattern, cp - pattern + 1);
pattern = cp + 2;
if ((charclass = wctype(name)) == 0)
return (RANGE_ERROR);
if (iswctype(test, charclass)) {
ok = 1;
break;
}
continue;
}
}
if (!c) {
pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (RANGE_NOMATCH);
pattern += pclen;
}
if (flags & FNM_CASEFOLD)
c = towlower(c);
if (*pattern == '-' && *(pattern + 1) != EOS &&
*(pattern + 1) != ']') {
if (*++pattern == '\\' && !(flags & FNM_NOESCAPE))
if (*pattern != EOS)
pattern++;
pclen = mbrtowc(&c2, pattern, MB_LEN_MAX, patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (RANGE_NOMATCH);
pattern += pclen;
if (c2 == EOS)
return (RANGE_ERROR);
if ((c2 == '[' && (special = *pattern) == '.') ||
special == '=' || special == ':') {
if (special == '=' || special == ':')
return (RANGE_ERROR);
cp = ++pattern;
while ((cp = strchr(cp, special))) {
if (*(cp + 1) == ']')
break;
cp++;
}
if (!cp)
return (RANGE_ERROR);
len = __collate_collating_symbol(buf,
COLLATE_STR_LEN, pattern,
cp - pattern, patmbs);
if (len != 1)
return (RANGE_ERROR);
pattern = cp + 2;
c2 = *buf;
}
if (flags & FNM_CASEFOLD)
c2 = towlower(c2);
if (table->__collate_load_error ?
c <= test && test <= c2 :
__wcollate_range_cmp(c, test) <= 0
&& __wcollate_range_cmp(test, c2) <= 0
) {
ok = 1;
break;
}
} else if (c == test) {
ok = 1;
break;
}
}
special = 0;
while (*pattern != ']') {
if (*pattern == 0)
return (RANGE_ERROR);
if (*pattern == special) {
if (*++pattern == ']') {
special = 0;
pattern++;
}
continue;
}
if (!special && *pattern == '[') {
special = *++pattern;
if (special != '.' && special != '=' && special != ':')
special = 0;
else
pattern++;
continue;
}
pclen = mbrtowc(&c, pattern, MB_LEN_MAX, patmbs);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
return (RANGE_NOMATCH);
pattern += pclen;
}
*newp = (char *)++pattern;
*news = (char *)string;
return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
}