From 584b9fa94f9020c2b14b074002a9caf9bc5c8043 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 Sep 2014 00:35:06 +0200 Subject: [PATCH] Allow to match multiple patterns (#100). Change the matching functions to not end at the NUL of the pattern. In stead we give a pattern length parameter. This allows to process multiple patterns without the need to put NUL-bytes into it or to allocate memory for the parts to match. --- src/autocmd.c | 35 ++----------------- src/util.c | 86 +++++++++++++++++++++++++++++++++++++++++------ src/util.h | 1 + tests/test-util.c | 18 ++++++++++ 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/src/autocmd.c b/src/autocmd.c index ddd8b23..d2741d6 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -57,7 +57,6 @@ static GSList *get_group(const char *name); static guint get_event_bits(const char *name); static void rebuild_used_bits(void); static char *get_next_word(char **line); -static gboolean wildmatch(char *patterns, const char *uri); static AuGroup *new_group(const char *name); static void free_group(AuGroup *group); static AutoCmd *new_autocmd(const char *excmd, const char *pattern); @@ -201,7 +200,7 @@ gboolean autocmd_add(char *name, gboolean delete) } /* skip if pattern does not match - we check the pattern against * another pattern */ - if (!wildmatch(pattern, cmd->pattern)) { + if (!util_wildmatch_multi(pattern, cmd->pattern)) { continue; } /* remove the bits from the item */ @@ -271,7 +270,7 @@ gboolean autocmd_run(const char *group, AuEvent event, const char *uri) } /* check pattern only if uri was given */ /* skip if pattern does not match */ - if (uri && !wildmatch(cmd->pattern, uri)) { + if (uri && !util_wildmatch_multi(cmd->pattern, uri)) { continue; } /* run the command */ @@ -398,36 +397,6 @@ static char *get_next_word(char **line) return word; } -/** - * Check if given uri matches one of the patterns given as comma separated - * string. - */ -static gboolean wildmatch(char *patterns, const char *uri) -{ - char *cur, *start; - gboolean matched; - - for (cur = start = patterns; *cur; cur++) { - /* iterate through the patterns until the next ',' */ - if (*cur == ',') { - *cur = '\0'; - matched = util_wildmatch(start, uri); - *cur = ','; - - /* return if the first match is found */ - if (matched) { - return true; - } - - /* set the start right after the ',' */ - start = cur + 1; - } - } - - /* still no match found - check the last pattern */ - return util_wildmatch(start, uri); -} - static AuGroup *new_group(const char *name) { AuGroup *new = g_slice_new(AuGroup); diff --git a/src/util.c b/src/util.c index 7101bca..92d5d26 100644 --- a/src/util.c +++ b/src/util.c @@ -28,7 +28,8 @@ extern VbCore vb; -static int match_list(const char *pattern, const char *subject); +static gboolean match(const char *pattern, int patlen, const char *subject); +static gboolean match_list(const char *pattern, int patlen, const char *subject); char *util_get_config_dir(void) @@ -510,6 +511,42 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags, return expanded; } +/** + * Like util_wildmatch, but allow to use a list of patterns, + */ +gboolean util_wildmatch_multi(const char *pattern, const char *subject) +{ + const char *end; + int braces, patlen; + + /* loop through all pattens */ + for (; *pattern; pattern = (*end == ',' ? end + 1 : end)) { + /* find end of the pattern - but be careful with comma in curly braces */ + braces = 0; + for (end = pattern; *end && (*end != ',' || braces || *(end - 1) == '\\'); ++end) { + if (*end == '{') { + braces++; + } else if (*end == '}') { + braces--; + } + } + /* ignore single comma */ + if (*pattern == *end) { + continue; + } + /* calculate the length of the pattern */ + patlen = end - pattern; + + /* if this pattern matches - return */ + if (match(pattern, patlen, subject)) { + return true; + } + } + + /* empty pattern matches only on empty subject */ + return !*subject; +} + /** * Compares given string against also given pattern. * @@ -521,11 +558,20 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags, * *?{} these chars must always be escaped by '\' to match them literally */ gboolean util_wildmatch(const char *pattern, const char *subject) +{ + return match(pattern, strlen(pattern), subject); +} + +/** + * Compares given subject string against the given pattern. + * The pattern needs not to bee NUL terminated. + */ +static gboolean match(const char *pattern, int patlen, const char *subject) { int i; char sl, pl; - while (*pattern) { + while (patlen > 0) { switch (*pattern) { case '?': /* '?' matches a single char except of / and subject end */ @@ -537,14 +583,14 @@ gboolean util_wildmatch(const char *pattern, const char *subject) case '*': /* easiest case - the '*' ist the last char in pattern - this * will always match */ - if (!pattern[1]) { + if (patlen == 1) { return true; } /* Try to match as much as possible. Try to match the complete * uri, if that fails move forward in uri and check for a * match. */ i = strlen(subject); - while (i >= 0 && !util_wildmatch(pattern + 1, subject + i)) { + while (i >= 0 && !match(pattern + 1, patlen - 1, subject + i)) { i--; } return i >= 0; @@ -555,12 +601,13 @@ gboolean util_wildmatch(const char *pattern, const char *subject) case '{': /* possible {foo,bar} pattern */ - return match_list(pattern, subject); + return match_list(pattern, patlen, subject); case '\\': /* '\' escapes next special char */ if (strchr("*?{}", pattern[1])) { pattern++; + patlen--; if (*pattern != *subject) { return false; } @@ -584,6 +631,7 @@ gboolean util_wildmatch(const char *pattern, const char *subject) } /* do another loop run with next pattern and subject char */ pattern++; + patlen--; subject++; } @@ -591,16 +639,26 @@ gboolean util_wildmatch(const char *pattern, const char *subject) return !*subject; } -static int match_list(const char *pattern, const char *subject) +/** + * Matches pattern starting with '{'. + * This function can process also on none null terminated pattern. + */ +static gboolean match_list(const char *pattern, int patlen, const char *subject) { + int endlen; const char *end, *s; + end = pattern; + endlen = patlen; /* finde the next none escaped '}' */ - for (end = pattern; *end && *end != '}'; end++) { + while (endlen > 0 && *end != '}') { /* if escape char - move pointer one additional step */ if (*end == '\\') { end++; + endlen--; } + end++; + endlen--; } if (!*end) { @@ -610,29 +668,34 @@ static int match_list(const char *pattern, const char *subject) s = subject; end++; /* skip over } */ + endlen--; pattern++; /* skip over { */ + patlen--; while (true) { switch (*pattern) { case ',': - if (util_wildmatch(end, s)) { + if (match(end, endlen, s)) { return true; } s = subject; pattern++; + patlen--; break; case '}': - return util_wildmatch(end, s); + return match(end, endlen, s); case '\\': if (pattern[1] == ',' || pattern[1] == '}' || pattern[1] == '{') { - pattern += 1; + pattern++; + patlen--; } /* fall through */ default: if (*pattern == *s) { pattern++; + patlen--; s++; } else { /* this item of the list does not match - move forward to @@ -642,12 +705,15 @@ static int match_list(const char *pattern, const char *subject) /* if escape char is found - skip next char */ if (*pattern == '\\') { pattern++; + patlen--; } pattern++; + patlen--; } /* found ',' skip over it to check the next list item */ if (*pattern == ',') { pattern++; + patlen--; } } } diff --git a/src/util.h b/src/util.h index ad6a770..938d789 100644 --- a/src/util.h +++ b/src/util.h @@ -49,6 +49,7 @@ char *util_build_path(const char *path, const char *dir); char *util_expand(const char *src, int expflags); gboolean util_parse_expansion(const char **input, GString *str, int flags, const char *quoteable); +gboolean util_wildmatch_multi(const char *pattern, const char *subject); gboolean util_wildmatch(const char *pattern, const char *string); gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src); diff --git a/tests/test-util.c b/tests/test-util.c index 38b7bf2..b89d620 100644 --- a/tests/test-util.c +++ b/tests/test-util.c @@ -248,6 +248,23 @@ static void test_wildmatch_complete(void) g_assert_true(util_wildmatch("http{s,}://{fanglingsu.,}github.{io,com}/*vimb/", "https://github.com/fanglingsu/vimb/")); } +static void test_wildmatch_multi(void) +{ + /* check if sinlge pattern matching works */ + g_assert_true(util_wildmatch_multi("", "")); + g_assert_true(util_wildmatch_multi("single", "single")); + g_assert_true(util_wildmatch_multi("s*e", "single")); + + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "foo")); + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "bar")); + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "bor")); + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "br")); + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "baz")); + g_assert_true(util_wildmatch_multi("foo,b{a,o,}r,ba?", "bat")); + + g_assert_false(util_wildmatch_multi("foo,b{a,o,}r,ba?", "foo,")); +} + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); @@ -264,6 +281,7 @@ int main(int argc, char *argv[]) g_test_add_func("/test-util/wildmatch-wildcard", test_wildmatch_wildcard); g_test_add_func("/test-util/wildmatch-curlybraces", test_wildmatch_curlybraces); g_test_add_func("/test-util/wildmatch-complete", test_wildmatch_complete); + g_test_add_func("/test-util/wildmatch-multi", test_wildmatch_multi); return g_test_run(); } -- 2.20.1