summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/locale/fnmatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc/port/locale/fnmatch.c')
-rw-r--r--usr/src/lib/libc/port/locale/fnmatch.c90
1 files changed, 53 insertions, 37 deletions
diff --git a/usr/src/lib/libc/port/locale/fnmatch.c b/usr/src/lib/libc/port/locale/fnmatch.c
index ebea076036..cf7b1c2372 100644
--- a/usr/src/lib/libc/port/locale/fnmatch.c
+++ b/usr/src/lib/libc/port/locale/fnmatch.c
@@ -32,8 +32,7 @@
/*
* Copyright 2013 Garrett D'Amore <garrett@damore.org>
- * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright 2017 Nexenta Systems, Inc.
*/
/*
@@ -74,9 +73,7 @@ static int fnmatch1(const char *, const char *, const char *, int, mbstate_t,
mbstate_t, locale_t);
int
-fnmatch(pattern, string, flags)
- const char *pattern, *string;
- int flags;
+fnmatch(const char *pattern, const char *string, int flags)
{
locale_t loc = uselocale(NULL);
static const mbstate_t initial = { 0 };
@@ -89,11 +86,14 @@ static int
fnmatch1(const char *pattern, const char *string, const char *stringstart,
int flags, mbstate_t patmbs, mbstate_t strmbs, locale_t loc)
{
+ const char *bt_pattern, *bt_string;
+ mbstate_t bt_patmbs, bt_strmbs;
char *newp;
char c;
wchar_t pc, sc;
size_t pclen, sclen;
+ bt_pattern = bt_string = NULL;
for (;;) {
pclen = mbrtowc_l(&pc, pattern, MB_LEN_MAX, &patmbs, loc);
if (pclen == (size_t)-1 || pclen == (size_t)-2)
@@ -111,16 +111,18 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart,
* Removed FNM_LEADING_DIR, as it is not present
* on Solaris.
*/
- return (sc == EOS ? 0 : FNM_NOMATCH);
+ if (sc == EOS)
+ return (0);
+ goto backtrack;
case '?':
if (sc == EOS)
return (FNM_NOMATCH);
if (sc == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
+ goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
+ goto backtrack;
string += sclen;
break;
case '*':
@@ -132,7 +134,7 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart,
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
+ goto backtrack;
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
@@ -147,34 +149,24 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart,
break;
}
- /* General case, use recursion. */
- while (sc != EOS) {
- if (!fnmatch1(pattern, string, stringstart,
- flags, patmbs, strmbs, loc))
- return (0);
- sclen = mbrtowc_l(&sc, string, MB_LEN_MAX,
- &strmbs, loc);
- if (sclen == (size_t)-1 ||
- sclen == (size_t)-2) {
- sc = (unsigned char)*string;
- sclen = 1;
- (void) memset(&strmbs, 0,
- sizeof (strmbs));
- }
- if (sc == '/' && flags & FNM_PATHNAME)
- break;
- string += sclen;
- }
- return (FNM_NOMATCH);
+ /*
+ * First try the shortest match for the '*' that
+ * could work. We can forget any earlier '*' since
+ * there is no way having it match more characters
+ * can help us, given that we are already here.
+ */
+ 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))
- return (FNM_NOMATCH);
+ goto backtrack;
if (sc == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
+ goto backtrack;
switch (rangematch(pattern, sc, flags, &newp,
&patmbs, loc)) {
@@ -184,7 +176,7 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart,
pattern = newp;
break;
case RANGE_NOMATCH:
- return (FNM_NOMATCH);
+ goto backtrack;
}
string += sclen;
break;
@@ -201,15 +193,39 @@ fnmatch1(const char *pattern, const char *string, const char *stringstart,
/* FALLTHROUGH */
default:
norm:
+ string += sclen;
if (pc == sc)
- string += sclen;
-
+ break;
else if ((flags & FNM_IGNORECASE) &&
(towlower_l(pc, loc) == towlower_l(sc, loc)))
- string += sclen;
- else
- return (FNM_NOMATCH);
-
+ break;
+ else {
+ backtrack:
+ /*
+ * If we have a mismatch (other than hitting
+ * the end of the string), go back to the last
+ * '*' seen and have it match one additional
+ * character.
+ */
+ if (bt_pattern == NULL)
+ return (FNM_NOMATCH);
+ sclen = mbrtowc_l(&sc, bt_string, MB_LEN_MAX,
+ &bt_strmbs, loc);
+ if (sclen == (size_t)-1 ||
+ sclen == (size_t)-2) {
+ sc = (unsigned char)*bt_string;
+ sclen = 1;
+ (void) 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;
}
}