-2005-05-25 Reed Loden <reed@reedloden.com>
+2005-05-30 Michael Poole <mdpoole@troilus.org>
+
+ * ircd/match.c (match): Rewrite to handle globs that end in an
+ escaped wildcard (and hopefully clarify the code).
+
+ * ircd/test/Makefile.in: Add new ircd_match_t test program.
+
+ * ircd/test/ircd_match_t.c: New file.
+
+ * ircd/test/test_stub.c: Emite newlines after log and debug
+ messages.
+
+2005-05-25 Reed Loden <reed@reedloden.com>
* ircd/s_err.c (replyTable): Allow for the specification of 'O' or
'o' in RPL_STATSOLINE.
{
const char *m = mask, *n = name;
const char *m_tmp = mask, *n_tmp = name;
- int wild = 0;
-
- for (;;)
- {
- if (*m == '*') {
- while (*m == '*') /* clean up any additional wildcards */
- m++;
-
- m_tmp = m;
- n_tmp = n;
- wild = 1;
- }
- if (*m == '\\') /* next wildcard is disregarded */
- m++;
-
- if (!*m) {
- if (!*n)
- return 0; /* match */
-
- for (m--; (m > mask) && (*m == '?'); m--);
- ;
-
- if (*m == '*' && (m > mask))
- return 0; /* match */
-
- if (!wild)
- return 1;
-
- m = m_tmp;
- n = ++n_tmp;
- }
- else if (!*n) {
- while (*m == '*') /* clean up any additional wildcards */
- m++;
+ int star_p;
- return (*m != 0);
- }
- if (ToLower(*m) != ToLower(*n) && *m != '?') {
- if (!wild)
- return 1; /* failure! */
-
- m = m_tmp;
- n = ++n_tmp;
+ for (;;) switch (*m) {
+ case '\0':
+ if (!*n)
+ return 0;
+ backtrack:
+ if (m_tmp == mask)
+ return 1;
+ m = m_tmp;
+ n = ++n_tmp;
+ break;
+ case '\\':
+ m++;
+ /* allow escaping to force capitalization */
+ if (*m++ != *n++)
+ return 1;
+ break;
+ case '*': case '?':
+ for (star_p = 0; ; m++) {
+ if (*m == '*')
+ star_p = 1;
+ else if (*m == '?') {
+ if (!*n++)
+ goto backtrack;
+ } else break;
}
- else {
- if (*m)
- m++;
- if (*n)
- n++;
+ if (star_p) {
+ if (!*m)
+ return 0;
+ else if (*m == '\\') {
+ m_tmp = ++m;
+ if (!*m)
+ return 1;
+ for (n_tmp = n; *n && *n != *m; n++) ;
+ } else {
+ m_tmp = m;
+ for (n_tmp = n; *n && ToLower(*n) != ToLower(*m); n++) ;
+ }
}
+ /* and fall through */
+ default:
+ if (!*n)
+ return *m != '\0';
+ if (ToLower(*m) != ToLower(*n))
+ goto backtrack;
+ m++;
+ n++;
+ break;
}
-
- return 1; /* no match! */
}
/*
TESTPROGS = \
ircd_chattr_t \
- ircd_string_t \
- ircd_in_addr_t
+ ircd_in_addr_t \
+ ircd_match_t \
+ ircd_string_t
DEP_SRC = \
ircd_chattr_t.c \
ircd_in_addr_t.c \
+ ircd_match_t.c \
ircd_string_t.c \
test_stub.c
ircd_chattr_t: $(IRCD_CHATTR_T_OBJS)
${CC} -o $@ $(IRCD_CHATTR_T_OBJS)
-IRCD_STRING_T_OBJS = ircd_string_t.o test_stub.o ../ircd_string.o
-ircd_string_t: $(IRCD_STRING_T_OBJS)
- ${CC} -o $@ $(IRCD_STRING_T_OBJS)
-
IRCD_IN_ADDR_T_OBJS = ircd_in_addr_t.o test_stub.o ../ircd_alloc.o ../ircd_string.o ../match.o ../numnicks.o
ircd_in_addr_t: $(IRCD_IN_ADDR_T_OBJS)
${CC} -o $@ $(IRCD_IN_ADDR_T_OBJS)
+IRCD_MATCH_T_OBJS = ircd_match_t.o test_stub.o ../ircd_string.o ../match.o
+ircd_match_t: $(IRCD_MATCH_T_OBJS)
+ ${CC} -o $@ $(IRCD_MATCH_T_OBJS)
+
+IRCD_STRING_T_OBJS = ircd_string_t.o test_stub.o ../ircd_string.o
+ircd_string_t: $(IRCD_STRING_T_OBJS)
+ ${CC} -o $@ $(IRCD_STRING_T_OBJS)
+
.c.o:
${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
--- /dev/null
+/*
+ * ircd_match_t.c - test cases for irc glob matching
+ */
+
+#include "ircd_log.h"
+#include "match.h"
+#include <stdio.h>
+#include <string.h>
+
+struct match_test {
+ const char *glob;
+ const char *should_match;
+ const char *shouldnt_match;
+};
+
+const struct match_test match_tests[] = {
+ { "\\*",
+ "*\0",
+ "a\0*PeacefuL*\0" },
+ { "*a*",
+ "a\0pizza\0abe\0brack\0",
+ "b\0" },
+ { "?",
+ "*\0a\0?\0",
+ "*PeacefuL*\0pizza\0???\0" },
+ { "abc",
+ "abc\0",
+ "abcd\0cabc\0" },
+ { "*abc",
+ "abc\0fooabc\0",
+ "abra\0abcd\0" },
+ { "\\?",
+ "?\0",
+ "a\0" },
+ { NULL, NULL, NULL }
+};
+
+void do_match_test(const struct match_test *test)
+{
+ const char *candidate;
+ unsigned int matched, not_matched;
+ int res;
+
+ for (candidate = test->should_match, matched = 0;
+ *candidate;
+ candidate += strlen(candidate) + 1, ++matched) {
+ res = match(test->glob, candidate);
+ if (res != 0) {
+ fprintf(stderr, "\"%s\" failed to match \"%s\".\n", test->glob, candidate);
+ assert(0);
+ }
+ }
+
+ for (candidate = test->shouldnt_match, not_matched = 0;
+ *candidate;
+ candidate += strlen(candidate) + 1, ++not_matched) {
+ res = match(test->glob, candidate);
+ if (res == 0) {
+ fprintf(stderr, "\"%s\" incorrectly matched \"%s\".\n", test->glob, candidate);
+ assert(0);
+ }
+ }
+
+ printf("Passed: %s (%u matches, %u non-matches)\n",
+ test->glob, matched, not_matched);
+}
+
+int main(int argc, char *argv[])
+{
+ const struct match_test *match;
+ for (match = match_tests; match->glob; ++match)
+ do_match_test(match);
+ return 0;
+}
va_start(args, fmt);
vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
va_end(args);
}
va_start(args, form);
vfprintf(stdout, form, args);
+ fputc('\n', stdout);
va_end(args);
}