Correctly match globs that end in escaped wildcards.
authorMichael Poole <mdpoole@troilus.org>
Mon, 30 May 2005 15:11:39 +0000 (15:11 +0000)
committerMichael Poole <mdpoole@troilus.org>
Mon, 30 May 2005 15:11:39 +0000 (15:11 +0000)
git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1414 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ChangeLog
ircd/match.c
ircd/test/Makefile.in
ircd/test/ircd_match_t.c [new file with mode: 0644]
ircd/test/test_stub.c

index d87124eae37bb6eccb46a667ad743ae617713e01..623f41b99602b8855d2506067fe2aed9a7de1df8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,16 @@
-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.
index cd754231e0137e4f2af62705435f881400d53799..530201871d96d9e3f0c3e1750d70ab93e81f6b81 100644 (file)
@@ -190,59 +190,56 @@ int match(const char *mask, const char *name)
 {
   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! */
 }
 
 /*
index aa9ecfcd386440a3d3fc503a45d1718c62a9fb2a..8449b09dd14c02d1c67957f47e03768b2695b1e2 100644 (file)
@@ -7,12 +7,14 @@ CC = @CC@
 
 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
 
@@ -36,14 +38,18 @@ IRCD_CHATTR_T_OBJS = ircd_chattr_t.o test_stub.o ../ircd_string.o
 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 $@
 
diff --git a/ircd/test/ircd_match_t.c b/ircd/test/ircd_match_t.c
new file mode 100644 (file)
index 0000000..e051e5f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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;
+}
index aa9227bb000abecd23a146ed7ff7baf11728cee7..e5fe4a46c6a7d226e8c8523d96f0b59b709da971 100644 (file)
@@ -17,6 +17,7 @@ log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
 
     va_start(args, fmt);
     vfprintf(stderr, fmt, args);
+    fputc('\n', stderr);
     va_end(args);
 }
 
@@ -27,6 +28,7 @@ debug(int level, const char *form, ...)
 
     va_start(args, form);
     vfprintf(stdout, form, args);
+    fputc('\n', stdout);
     va_end(args);
 }