From: Richard W.M. Jones Date: Thu, 15 May 2014 18:29:50 +0000 (+0100) Subject: Implement and test multi-matching on strings. X-Git-Tag: v1.1~18 X-Git-Url: http://git.annexia.org/?p=miniexpect.git;a=commitdiff_plain;h=329575032af72a3155bcc2640231e603604cd253 Implement and test multi-matching on strings. If the subprocess prints multiple matching strings, and you call mexp_expect repeatedly, the code now does the right thing. --- diff --git a/.gitignore b/.gitignore index 959fb48..959e32a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,5 @@ /stamp-h1 /test-driver /test-ls-version +/test-multi-match /test-spawn diff --git a/Makefile.am b/Makefile.am index 4d3f91f..98d21eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,7 +41,8 @@ example_sshpass_LDADD = libminiexpect.la TESTS = $(check_PROGRAMS) check_PROGRAMS = \ test-spawn \ - test-ls-version + test-ls-version \ + test-multi-match test_spawn_SOURCES = test-spawn.c tests.h miniexpect.h test_spawn_CFLAGS = $(PCRE_CFLAGS) -Wall @@ -51,6 +52,10 @@ test_ls_version_SOURCES = test-ls-version.c tests.h miniexpect.h test_ls_version_CFLAGS = $(PCRE_CFLAGS) -Wall test_ls_version_LDADD = libminiexpect.la +test_multi_match_SOURCES = test-multi-match.c tests.h miniexpect.h +test_multi_match_CFLAGS = $(PCRE_CFLAGS) -Wall +test_multi_match_LDADD = libminiexpect.la + # parallel-tests breaks the ability to put 'valgrind' into # TESTS_ENVIRONMENT. Hence we have to work around it: check-valgrind: diff --git a/miniexpect.c b/miniexpect.c index 7f02584..9e9cc0e 100644 --- a/miniexpect.c +++ b/miniexpect.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ create_handle (void) 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; @@ -64,6 +66,7 @@ clear_buffer (mexp_h *h) free (h->buffer); h->buffer = NULL; h->alloc = h->len = 0; + h->next_match = -1; } int @@ -209,12 +212,19 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize) time (&start_t); - /* Clear the read buffer. */ - /* XXX This is possibly incorrect because it throws away inputs that - * may not have been matched yet. A better idea is to record the - * end of the previous match and only throw that away. - */ - 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. @@ -279,6 +289,7 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize) fprintf (stderr, "DEBUG: buffer content: %s\n", h->buffer); #endif + try_match: /* See if there is a full or partial match against any regexp. */ if (regexps) { size_t i; @@ -297,6 +308,10 @@ mexp_expect (mexp_h *h, const mexp_regexp *regexps, int *ovector, int ovecsize) 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; } diff --git a/miniexpect.h b/miniexpect.h index 9a374b7..f987655 100644 --- a/miniexpect.h +++ b/miniexpect.h @@ -41,6 +41,7 @@ struct mexp_h { char *buffer; size_t len; size_t alloc; + ssize_t next_match; size_t read_size; int pcre_error; void *user1; diff --git a/miniexpect.pod b/miniexpect.pod index 0ab4a4a..66ba8c2 100644 --- a/miniexpect.pod +++ b/miniexpect.pod @@ -114,6 +114,19 @@ the process, but it will contain at least the part matched by the regular expression (and maybe some more). C is the read buffer and C is the number of bytes of data in the buffer. + ssize_t next_match; + +If C returns a match, then C points to the +first byte in the buffer I the fully matched expression. (It +may be C<-1> which means it is invalid). The next time that +C is called, it will start by consuming the data +C. Callers may also need to read from +that point in the buffer before calling L on the file +descriptor. Callers may also set this, for example setting it to +C<-1> in order to ignore the remainder of the buffer. In most cases +callers can ignore this field, and C will just do the +right thing when called repeatedly. + size_t read_size; Callers may set this to the natural size (in bytes) for reads from the diff --git a/test-multi-match.c b/test-multi-match.c new file mode 100644 index 0000000..7237202 --- /dev/null +++ b/test-multi-match.c @@ -0,0 +1,97 @@ +/* miniexpect test suite + * Copyright (C) 2014 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include +#include + +#include "miniexpect.h" +#include "tests.h" + +int +main (int argc, char *argv[]) +{ + mexp_h *h; + int status; + int r; + int rv[5]; + size_t i; + pcre *multi_re = test_compile_re ("multi"); + pcre *match_re = test_compile_re ("match"); + pcre *ing_re = test_compile_re ("ing"); + pcre *str_re = test_compile_re ("str"); + pcre *s_re = test_compile_re ("s"); + const int ovecsize = 12; + int ovector[ovecsize]; + + /* If the subprocess prints multiple things, we should be able to + * repeatedly call mexp_expect to match on each one. This didn't + * work correctly in earlier versions of the library. + */ + h = mexp_spawnl ("echo", "echo", "multimatchingstrs", NULL); + assert (h != NULL); + + for (i = 0; i < 5; ++i) { + r = mexp_expect (h, + (mexp_regexp[]) { + { 100, multi_re }, + { 101, match_re }, + { 102, ing_re }, + { 103, str_re }, + { 104, s_re }, + { 0 }, + }, ovector, ovecsize); + switch (r) { + case 100: case 101: case 102: case 103: case 104: + printf ("iteration %zu: matched %d\n", i, r); + rv[i] = r; + break; + case MEXP_EOF: + fprintf (stderr, "error: unexpected EOF in iteration %zu\n", i); + exit (EXIT_FAILURE); + case MEXP_TIMEOUT: + fprintf (stderr, "error: unexpected timeout in iteration %zu\n", i); + exit (EXIT_FAILURE); + case MEXP_ERROR: + perror ("mexp_expect"); + exit (EXIT_FAILURE); + case MEXP_PCRE_ERROR: + fprintf (stderr, "error: PCRE error: %d\n", h->pcre_error); + exit (EXIT_FAILURE); + } + } + + assert (rv[0] == 100); /* multi */ + assert (rv[1] == 101); /* match */ + assert (rv[2] == 102); /* ing */ + assert (rv[3] == 103); /* str */ + assert (rv[4] == 104); /* s */ + + status = mexp_close (h); + if (status != 0 && !test_is_sighup (status)) { + fprintf (stderr, "%s: non-zero exit status from subcommand: ", argv[0]); + test_diagnose (status); + fprintf (stderr, "\n"); + exit (EXIT_FAILURE); + } + + exit (EXIT_SUCCESS); +}