From 57fbf053880b92e5832443672eaea3b092dff549 Mon Sep 17 00:00:00 2001 From: Ed Kellett Date: Fri, 30 Oct 2020 01:06:07 +0000 Subject: [PATCH] Fix a corner case of superset matching The algorithm we're using gets stuck if it has a ? and can only see a * to feed to it, even if it could skip over that * and consume a character following it. Remedy this by rearranging the input so * always precedes ? in runs of wildcards, so when we're matching ? we know we can skip things. --- ircd/match.c | 33 +++++++++++++++++++++++++++++++-- tests/match1.c | 23 +++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/ircd/match.c b/ircd/match.c index a8165ffb..417e2ccd 100644 --- a/ircd/match.c +++ b/ircd/match.c @@ -104,6 +104,30 @@ int match(const char *mask, const char *name) } } +void +match_arrange_stars(char *mask) +{ + char *swap = NULL; + + for (char *p = mask; *p != '\0'; p++) + { + switch (*p) + { + case '*': + if (swap == NULL) break; + *swap++ = '*'; + *p = '?'; + break; + case '?': + if (swap == NULL) swap = p; + break; + default: + swap = NULL; + break; + } + } +} + /** Check a mask against a mask. * This test checks using traditional IRC wildcards only: '*' means * match zero or more characters of any type; '?' means match exactly @@ -115,15 +139,19 @@ int match(const char *mask, const char *name) * @param[in] name New wildcard-containing mask. * @return 1 if \a name is equal to or more specific than \a mask, 0 otherwise. */ -int mask_match(const char *mask, const char *name) +int mask_match(const char *mask_, const char *name) { + static char mask[BUFSIZE]; const char *m = mask, *n = name; const char *m_tmp = mask, *n_tmp = name; int star_p; - s_assert(mask != NULL); + s_assert(mask_ != NULL); s_assert(name != NULL); + rb_strlcpy(mask, mask_, sizeof mask); + match_arrange_stars(mask); + for (;;) { switch (*m) @@ -146,6 +174,7 @@ int mask_match(const char *mask, const char *name) else if (*m == '?') { /* changed for mask_match() */ + while (star_p && *n == '*') n++; if (*n == '*' || !*n) goto backtrack; n++; diff --git a/tests/match1.c b/tests/match1.c index 9d118ed6..913f71d7 100644 --- a/tests/match1.c +++ b/tests/match1.c @@ -30,6 +30,8 @@ struct Client me; +void match_arrange_stars(char *); + static void test_match(void) { is_int(0, match("*foo*", "bar"), MSG); @@ -40,6 +42,7 @@ static void test_match(void) static void test_mask_match(void) { + is_int(0, mask_match("*foo*", "bar"), MSG); is_int(1, mask_match("*foo*", "foo"), MSG); @@ -63,12 +66,32 @@ static void test_mask_match(void) is_int(0, mask_match("??", "aaa"), MSG); } +static void test_arrange_stars(void) +{ + { + char rearrange[] = "quick brown fox"; + match_arrange_stars(rearrange); + is_string("quick brown fox", rearrange, MSG); + } + { + char rearrange[] = "?*?*?*"; + match_arrange_stars(rearrange); + is_string("***???", rearrange, MSG); + } + { + char rearrange[] = "?*? *?*"; + match_arrange_stars(rearrange); + is_string("*?? **?", rearrange, MSG); + } +} + int main(int argc, char *argv[]) { plan_lazy(); test_match(); test_mask_match(); + test_arrange_stars(); return 0; }