diff options
Diffstat (limited to 'usr/src/lib/libc/port/locale/fnmatch.c')
| -rw-r--r-- | usr/src/lib/libc/port/locale/fnmatch.c | 90 |
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; } } |
