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.
This commit is contained in:
parent
bc6e29e3b0
commit
57fbf05388
2 changed files with 54 additions and 2 deletions
33
ircd/match.c
33
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.
|
/** Check a mask against a mask.
|
||||||
* This test checks using traditional IRC wildcards only: '*' means
|
* This test checks using traditional IRC wildcards only: '*' means
|
||||||
* match zero or more characters of any type; '?' means match exactly
|
* 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.
|
* @param[in] name New wildcard-containing mask.
|
||||||
* @return 1 if \a name is equal to or more specific than \a mask, 0 otherwise.
|
* @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 = mask, *n = name;
|
||||||
const char *m_tmp = mask, *n_tmp = name;
|
const char *m_tmp = mask, *n_tmp = name;
|
||||||
int star_p;
|
int star_p;
|
||||||
|
|
||||||
s_assert(mask != NULL);
|
s_assert(mask_ != NULL);
|
||||||
s_assert(name != NULL);
|
s_assert(name != NULL);
|
||||||
|
|
||||||
|
rb_strlcpy(mask, mask_, sizeof mask);
|
||||||
|
match_arrange_stars(mask);
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
switch (*m)
|
switch (*m)
|
||||||
|
@ -146,6 +174,7 @@ int mask_match(const char *mask, const char *name)
|
||||||
else if (*m == '?')
|
else if (*m == '?')
|
||||||
{
|
{
|
||||||
/* changed for mask_match() */
|
/* changed for mask_match() */
|
||||||
|
while (star_p && *n == '*') n++;
|
||||||
if (*n == '*' || !*n)
|
if (*n == '*' || !*n)
|
||||||
goto backtrack;
|
goto backtrack;
|
||||||
n++;
|
n++;
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
struct Client me;
|
struct Client me;
|
||||||
|
|
||||||
|
void match_arrange_stars(char *);
|
||||||
|
|
||||||
static void test_match(void)
|
static void test_match(void)
|
||||||
{
|
{
|
||||||
is_int(0, match("*foo*", "bar"), MSG);
|
is_int(0, match("*foo*", "bar"), MSG);
|
||||||
|
@ -40,6 +42,7 @@ static void test_match(void)
|
||||||
|
|
||||||
static void test_mask_match(void)
|
static void test_mask_match(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
is_int(0, mask_match("*foo*", "bar"), MSG);
|
is_int(0, mask_match("*foo*", "bar"), MSG);
|
||||||
is_int(1, mask_match("*foo*", "foo"), 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);
|
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[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
plan_lazy();
|
plan_lazy();
|
||||||
|
|
||||||
test_match();
|
test_match();
|
||||||
test_mask_match();
|
test_mask_match();
|
||||||
|
test_arrange_stars();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue