Implement and test multi-matching on strings.
[miniexpect.git] / miniexpect.c
index f525b68..9e9cc0e 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <poll.h>
@@ -50,8 +51,10 @@ create_handle (void)
   h->pid = 0;
   h->timeout = 60000;
   h->read_size = 1024;
+  h->pcre_error = 0;
   h->buffer = NULL;
   h->len = h->alloc = 0;
+  h->next_match = -1;
   h->user1 = h->user2 = h->user3 = NULL;
 
   return h;
@@ -63,6 +66,7 @@ clear_buffer (mexp_h *h)
   free (h->buffer);
   h->buffer = NULL;
   h->alloc = h->len = 0;
+  h->next_match = -1;
 }
 
 int
@@ -79,6 +83,8 @@ mexp_close (mexp_h *h)
       return -1;
   }
 
+  free (h);
+
   return status;
 }
 
@@ -196,10 +202,7 @@ mexp_spawnv (const char *file, char **argv)
 }
 
 enum mexp_status
-mexp_expect (mexp_h *h, const pcre *code,
-             const pcre_extra *extra,
-             int options, int *ovector, int ovecsize,
-             int *pcre_ret)
+mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize)
 {
   time_t start_t, now_t;
   int timeout;
@@ -209,10 +212,19 @@ mexp_expect (mexp_h *h, const pcre *code,
 
   time (&start_t);
 
-  options |= PCRE_PARTIAL_SOFT;
-
-  /* Clear the read buffer. */
-  clear_buffer (h);
+  if (h->next_match == -1) {
+    /* Fully clear the buffer, then read. */
+    clear_buffer (h);
+  } else {
+    /* See the comment in the manual about h->next_match.  We have
+     * some data remaining in the buffer, so begin by matching that.
+     */
+    memmove (&h->buffer[0], &h->buffer[h->next_match], h->len - h->next_match);
+    h->len -= h->next_match;
+    h->buffer[h->len] = '\0';
+    h->next_match = -1;
+    goto try_match;
+  }
 
   for (;;) {
     /* If we've got a timeout then work out how many seconds are left.
@@ -277,34 +289,55 @@ mexp_expect (mexp_h *h, const pcre *code,
     fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer);
 #endif
 
-    /* See if there is a full or partial match against the regular expression. */
-    if (code) {
+  try_match:
+    /* See if there is a full or partial match against any regexp. */
+    if (regexps) {
+      size_t i;
+      int can_clear_buffer = 1;
+
       assert (h->buffer != NULL);
-      r = pcre_exec (code, extra, h->buffer, (int)h->len, 0,
-                     options, ovector, ovecsize);
-      if (pcre_ret)
-        *pcre_ret = r;
-
-      if (r >= 0) {
-        /* A full match. */
-        return MEXP_MATCHED;
-      }
 
-      else if (r == PCRE_ERROR_NOMATCH) {
-        /* No match at all, so we can dump the input buffer. */
-        clear_buffer (h);
+      for (i = 0; regexps[i].r > 0; ++i) {
+        int options = regexps[i].options | PCRE_PARTIAL_SOFT;
+
+        r = pcre_exec (regexps[i].re, regexps[i].extra,
+                       h->buffer, (int)h->len, 0,
+                       options,
+                       ovector, ovecsize);
+        h->pcre_error = r;
+
+        if (r >= 0) {
+          /* A full match. */
+          if (ovector != NULL && ovecsize >= 1 && ovector[1] >= 0)
+            h->next_match = ovector[1];
+          else
+            h->next_match = -1;
+          return regexps[i].r;
+        }
+
+        else if (r == PCRE_ERROR_NOMATCH) {
+          /* No match at all. */
+          /* (nothing here) */
+        }
+
+        else if (r == PCRE_ERROR_PARTIAL) {
+          /* Partial match.  Keep the buffer and keep reading. */
+          can_clear_buffer = 0;
+        }
+
+        else {
+          /* An actual PCRE error. */
+          return MEXP_PCRE_ERROR;
+        }
       }
 
-      else if (r == PCRE_ERROR_PARTIAL) {
-        /* Partial match.  Keep the buffer and keep reading. */
-        /* (nothing here) */
-      }
+      /* If none of the regular expressions matched (not partially)
+       * then we can clear the buffer.  This is an optimization.
+       */
+      if (can_clear_buffer)
+        clear_buffer (h);
 
-      else {
-        /* An actual PCRE error. */
-        return MEXP_PCRE_ERROR;
-      }
-    } /* if (code) */
+    } /* if (regexps) */
   }
 }