Hi [2019-07-09 19:47] markus schnalke <mei...@marmaro.de> > [2019-07-09 16:04] Philipp Takacs <phil...@bureaucracy.de> > > > > So if you find a test case is to little bug me to add one ;-). > > Some suggestions: > > - `pick -subject foo' for lower, upper and mixed case ``foo'' in > the subject > > - `pick -subject übung' for encoded header > > - `pick -search übung' for encoded body (does fail currently) > > - `pick -search foo' for some messages have ``foo'' only in the > subject and some have ``foo'' only in the body (It doesn't so > much matter here where `-search' should search, but just that > we can detect regression bugs.)
Thanks for the testcases. With them I found some bugs in my implementation. A new version is attach, witch the testcases. > > [2019-07-08 16:15] markus schnalke <mei...@marmaro.de> > > Also I think about adding regex also to the header name search. > > What usecases do you have in mind for that? I'm not shoure, this was just an idea I have, because it's easy to implement. So if I find it realy worth to implement, I'll write an other mail with the reasons. > > Also the search switch could search in all field values. > > It would be nice if `-search' would search in about everywhere in > the messages, so that you don't have to think about it ... like > grepping through the files. > > If we want specific only body searching, we rather add a `-body' > switch. Sounds good. I'll look at it. > > But this isn't > > well-thought-out, so maybe my plans how to implement this change. > > See how it goes during implementation. See what makes sense from > the implementation view. This was more the view from the feature set. Do we realy need this features and in this exact form or a bit diffrent. Implementing looks like it is possible just straight forword. Philipp
From f5e37993d288c40b1d6a02d4ac0faed57d021561 Mon Sep 17 00:00:00 2001 From: Philipp Takacs <phil...@bureaucracy.de> Date: Mon, 10 Jun 2019 03:17:36 +0200 Subject: [PATCH 1/3] pick matching rework the last rewrite of the matching implementation was a bit overcomplex. Now the structures are better to read and without function pointers. Also now the -not switch is working again. The early return true in the last version causes this. Now the leafs of the matching tree remeber, if the message has matched. --- test/tests/pick/test-case | 48 +++ test/tests/pick/test-complex | 14 + test/tests/pick/test-encoded-body | 40 +++ test/tests/pick/test-normal-output | 15 +- test/tests/pick/test-output-on-error | 18 +- test/tests/pick/test-rfc2047 | 41 ++- test/tests/pick/test-search | 39 +++ test/tests/pick/test-stderr | 15 +- test/tests/pick/test-thread-without-msgid | 19 +- uip/pick.c | 558 +++++++++++++----------------- 10 files changed, 417 insertions(+), 390 deletions(-) create mode 100644 test/tests/pick/test-case create mode 100644 test/tests/pick/test-complex create mode 100644 test/tests/pick/test-encoded-body create mode 100644 test/tests/pick/test-search diff --git a/test/tests/pick/test-case b/test/tests/pick/test-case new file mode 100644 index 00000000..fc06ef2d --- /dev/null +++ b/test/tests/pick/test-case @@ -0,0 +1,48 @@ +#!/bin/sh +###################################################### +# +# Test that the -thread option works. +# +###################################################### + +. "$MH_TEST_COMMON" + +#lower +cat > `mhpath b` << '!' +From: al...@example.org +subject: fooooo +! +lower=$(pick l) + +#upper +cat > `mhpath b` << '!' +From: al...@example.org +SUBJECT: FOOOOO +! +upper=$(pick l) + + +#mixed +cat > `mhpath b` << '!' +From: al...@example.org +sUbJEcT: FOooOo +! +mixed=$(pick l) + +runandcheck 'pick --subject fooooo' <<! +$lower +$upper +$mixed +! + +runandcheck 'pick --SUBJECT FOOOOO' <<! +$lower +$upper +$mixed +! + +runandcheck 'pick --SuBjeCt foOOoO' <<! +$lower +$upper +$mixed +! diff --git a/test/tests/pick/test-complex b/test/tests/pick/test-complex new file mode 100644 index 00000000..6bdec11a --- /dev/null +++ b/test/tests/pick/test-complex @@ -0,0 +1,14 @@ +#!/bin/sh +###################################################### +# +# Test that all messages are picked and no ``0'' is giving out. +# +###################################################### + +. "$MH_TEST_COMMON" + +# All messages should be go to stdout +runandcheck "pick -lbrace -from test1 -or -subject 'Testing message 5' -rbrace -and -not -from test10" <<! +1 +5 +! diff --git a/test/tests/pick/test-encoded-body b/test/tests/pick/test-encoded-body new file mode 100644 index 00000000..cfcac61a --- /dev/null +++ b/test/tests/pick/test-encoded-body @@ -0,0 +1,40 @@ +#!/bin/sh +###################################################### +# +# Test pick -search with encoded body +# +###################################################### + +. "$MH_TEST_COMMON" + +test_skip "not implemented yet" +exit + +require_locale en_US.utf-8 en_US.utf8 +LC_ALL=en_US.UTF-8 +export LC_ALL + +cat > $msgfile <<EOF +To: recipi...@example.com +From: sen...@example.com +Subject: test encoded body +MIME-Version: 1.0 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +Sehr geehrte Damen und Herren + +Feuer! Feuer! Feuer! Dies ist keine =C3=BCbung. + +123 Carenden Road + +Ich freue mich auf ihre Antwort + +Mit freundlichen Gr=C3=BC=C3=9Fen +Maurice Moss +EOF +lm=$(pick l) + +runandcheck 'pick -search übung' <<! +$lm +! diff --git a/test/tests/pick/test-normal-output b/test/tests/pick/test-normal-output index bd1a2e70..292dd235 100644 --- a/test/tests/pick/test-normal-output +++ b/test/tests/pick/test-normal-output @@ -5,13 +5,10 @@ # ###################################################### -expected_err=$MH_TEST_DIR/$$.expected_err -expected_out=$MH_TEST_DIR/$$.expected_out -actual_err=$MH_TEST_DIR/$$.actual_err -actual_out=$MH_TEST_DIR/$$.actual_out +. "$MH_TEST_COMMON" # All messages should be go to stdout -cat > $expected_out <<EOF +runandcheck pick <<! 1 2 3 @@ -22,10 +19,4 @@ cat > $expected_out <<EOF 8 9 10 -EOF -# Nothing should to go stderr -cat /dev/null > $expected_err - -pick > $actual_out 2> $actual_err -diff -u $expected_err $actual_err -diff -u $expected_out $actual_out +! diff --git a/test/tests/pick/test-output-on-error b/test/tests/pick/test-output-on-error index 8d962240..07231f04 100644 --- a/test/tests/pick/test-output-on-error +++ b/test/tests/pick/test-output-on-error @@ -5,20 +5,10 @@ # ###################################################### -expected_err=$MH_TEST_DIR/$$.expected_err -expected_out=$MH_TEST_DIR/$$.expected_out -actual_err=$MH_TEST_DIR/$$.actual_err -actual_out=$MH_TEST_DIR/$$.actual_out +. "$MH_TEST_COMMON" # A zero should go to standard out to protect other programms -cat > $expected_out <<EOF -0 -EOF -# Error message should go to stderr. -cat > $expected_err <<EOF +runandcheck 'pick -after tomorrow' <<! pick: no messages match specification -EOF - -pick -after tomorrow > $actual_out 2> $actual_err -diff -u $expected_err $actual_err -diff -u $expected_out $actual_out +0 +! diff --git a/test/tests/pick/test-rfc2047 b/test/tests/pick/test-rfc2047 index b89a71ab..3eab2535 100644 --- a/test/tests/pick/test-rfc2047 +++ b/test/tests/pick/test-rfc2047 @@ -5,28 +5,39 @@ # ###################################################### -set -e - -expected_err=$MH_TEST_DIR/$$.expected_err -expected_out=$MH_TEST_DIR/$$.expected_out -actual_err=$MH_TEST_DIR/$$.actual_err -actual_out=$MH_TEST_DIR/$$.actual_out +. "$MH_TEST_COMMON" # Test MIME-encoded header. -cat >"$MH_TEST_DIR/Mail/inbox/13" <<EOF +cat > $(mhpath b) <<! From: Test13 <tes...@example.com> To: Some User <u...@example.com> Date: Fri, 29 Sep 2006 00:00:00 Message-Id: 1...@test.nmh -Subject: =?us-ascii?q?=66=6f=6f?= - =?utf-8?q?=62=61=72?= +Subject: =?utf-8?q?=66=6f=6f=62=61=72?= This is message number 13, with MIME-encoded Subject "foobar". -EOF +! +lm=$(pick l) + +runandcheck 'pick -subject foobar' <<! +$lm +! + + +require_locale en_US.utf-8 en_US.utf8 +LC_ALL=en_US.UTF-8 +export LC_ALL + +cat > $(mhpath b) <<! +From: Test13 <tes...@example.com> +To: Some User <u...@example.com> +Date: Fri, 29 Sep 2006 00:00:00 +Subject: =?utf-8?q?=C3=BCbung?= -echo 13 >"$expected_out" -cat /dev/null > $expected_err +This is a message, with MIME-encoded subject. +! +lm=$(pick l) -pick -subject foobar 13 > $actual_out 2> $actual_err -diff -u $expected_err $actual_err -diff -u $expected_out $actual_out +runandcheck 'pick -subject übung' <<! +$lm +! diff --git a/test/tests/pick/test-search b/test/tests/pick/test-search new file mode 100644 index 00000000..5fc6ca5d --- /dev/null +++ b/test/tests/pick/test-search @@ -0,0 +1,39 @@ +#!/bin/sh +###################################################### +# +# Test pick -search switch +# +###################################################### + +. "$MH_TEST_COMMON" + +# Test MIME-encoded header. +cat > $(mhpath b) <<! +From: Test13 <tes...@example.com> +To: Some User <u...@example.com> +Date: Fri, 29 Sep 2006 00:00:00 +Message-Id: 1...@test.nmh +Subject: fooo + +This is a test message +! +lm=$(pick l) + +runandcheck 'pick -search fooo' <<! 2>/dev/null +pick: no messages match specification +0 +! + +cat > $(mhpath b) <<! +From: Test13 <tes...@example.com> +To: Some User <u...@example.com> +Date: Fri, 29 Sep 2006 00:00:00 +Subject: bla + +This is a test message, with fooo in the body. +! +lm=$(pick l) + +runandcheck 'pick -search fooo' <<! +$lm +! diff --git a/test/tests/pick/test-stderr b/test/tests/pick/test-stderr index 35a64efb..6f46b2a1 100644 --- a/test/tests/pick/test-stderr +++ b/test/tests/pick/test-stderr @@ -5,20 +5,11 @@ # ###################################################### -expected_err=$MH_TEST_DIR/$$.expected_err -expected_out=$MH_TEST_DIR/$$.expected_out -actual_err=$MH_TEST_DIR/$$.actual_err -actual_out=$MH_TEST_DIR/$$.actual_out +. "$MH_TEST_COMMON" # Error message should go to stderr. -cat > $expected_err <<EOF +runandcheck 'pick -a' <<! pick: -a ambiguous. It matches -and -after date -EOF -# Nothing should to go stdout. -cat /dev/null > $expected_out - -pick -a > $actual_out 2> $actual_err -diff -u $expected_err $actual_err -diff -u $expected_out $actual_out +! diff --git a/test/tests/pick/test-thread-without-msgid b/test/tests/pick/test-thread-without-msgid index 60588151..154c9c03 100644 --- a/test/tests/pick/test-thread-without-msgid +++ b/test/tests/pick/test-thread-without-msgid @@ -5,20 +5,9 @@ # ###################################################### -expected_err=$MH_TEST_DIR/$$.expected_err -expected_out=$MH_TEST_DIR/$$.expected_out -actual_err=$MH_TEST_DIR/$$.actual_err -actual_out=$MH_TEST_DIR/$$.actual_out +. "$MH_TEST_COMMON" -# All messages should be go to stdout -cat > $expected_out <<EOF -0 -EOF -# Nothing should to go stderr -cat > $expected_err <<EOF +runandcheck 'pick -thread 5' <<! pick: message 5 is not part of a thread -EOF - -pick -thread 5 > $actual_out 2> $actual_err -diff -u $expected_err $actual_err || exit 1 -diff -u $expected_out $actual_out || exit 1 +0 +! diff --git a/uip/pick.c b/uip/pick.c index b1a946d6..e76b528b 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -85,12 +85,40 @@ static struct swit switches[] = { char *version=VERSION; -struct nexus { - boolean (*action)(struct field *, int, void *); - void (*free)(struct nexus **); - void (*debug)(void *, size_t); +enum nexus_type { + noop_t = 0, + not_t, + and_t, + or_t, + date_t, + grep_t +}; + +struct bin_data { + struct nexus *left; + struct nexus *right; +}; + +struct date_data { + char *datef; + boolean after; + struct tws tws; +}; - void *data; +struct grep_data { + char *header; + char *pattern; + regex_t *preg; +}; + +struct nexus { + enum nexus_type t; + boolean match; + union { + struct bin_data b; + struct date_data d; + struct grep_data g; + } data; }; static struct nexus *head; @@ -101,6 +129,12 @@ static boolean body = FALSE; */ static int pcompile(char **, char *); static int pmatches(FILE *, int); +static boolean nexus_match(struct field *, int, struct nexus *); +static void nexus_free(struct nexus **); +static void nexus_clear(struct nexus *); +static void nexus_debug(struct nexus *, size_t); +static void nexus_debug_grep(struct grep_data *); +static void print_debug_level(size_t); static struct nexus * createonethread(char *); static struct nexus * createpickthread(char *); static void scan_mbox(char *, char *, int); @@ -358,9 +392,7 @@ main(int argc, char **argv) } } - if (head) { - head->free(&head); - } + nexus_free(&head); mp->lowsel = lo; mp->hghsel = hi; @@ -415,7 +447,6 @@ scan_mbox(char *file, char *fmtstr, int width) fclose(in); } - void putzero_done() { @@ -448,7 +479,6 @@ printmsg(FILE *f, struct msgs *mp, int msgnum, char *fmtstr, int width) } } - static struct swit parswit[] = { #define PRAND 0 { "and", 0 }, @@ -519,44 +549,12 @@ static struct swit parswit[] = { #define padvise if (!talked++) advise - -enum nexus_type { - TYPE_GREP, - TYPE_DATE, - TYPE_OR, - TYPE_AND, - TYPE_NOT -}; - -struct bin_data { - struct nexus *left; - struct nexus *right; - enum nexus_type type; - int oldmsgnum; - boolean leftmatch; - boolean rightmatch; - boolean match; -}; - -struct date_data { - char *datef; - boolean after; - struct tws tws; -}; - -struct grep_data { - char *header; - char *pattern; - regex_t *preg; -}; - static int talked; static int pdebug = 0; static char *datesw; static char **argp; - /* ** prototypes for date routines */ @@ -575,19 +573,6 @@ static struct nexus *nexp2(void); static struct nexus *nexp3(void); static struct nexus *newnexus(enum nexus_type); -static boolean BINaction(struct field *, int, void *); -static boolean NOTaction(struct field *, int, void *); -static boolean GREPaction(struct field *, int, void *); -static boolean DATEaction(struct field *, int, void *); - -static void BINfree(struct nexus **); -static void GREPfree(struct nexus **); -static void DATEfree(struct nexus **); - -static void BINdebug(void *, size_t); -static void GREPdebug(void *, size_t); -static void DATEdebug(void *, size_t); - static int pcompile(char **vec, char *date) { @@ -641,8 +626,8 @@ parse(void) return NULL; case PROR: - o = newnexus(TYPE_OR); - bin = o->data; + o = newnexus(or_t); + bin = &o->data.b; bin->left = n; if ((bin->right = parse())) return o; @@ -684,8 +669,8 @@ nexp1(void) return NULL; case PRAND: - o = newnexus(TYPE_AND); - bin = o->data; + o = newnexus(and_t); + bin = &o->data.b; bin->left = n; if ((bin->right = nexp1())) return o; @@ -728,8 +713,8 @@ nexp2(void) return NULL; case PRNOT: - n = newnexus(TYPE_NOT); - bin = n->data; + n = newnexus(not_t); + bin = &n->data.b; if ((bin->left = nexp3())) return n; padvise(NULL, "missing negation"); @@ -810,16 +795,16 @@ header: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(TYPE_GREP); - gdata = n->data; + n = newnexus(grep_t); + gdata = &n->data.g; gdata->header = mh_xstrdup(dp); snprintf(buffer, sizeof(buffer), "%s", cp); dp = buffer; goto pattern; case PRSRCH: - n = newnexus(TYPE_GREP); - gdata = n->data; + n = newnexus(grep_t); + gdata = &n->data.g; gdata->header = NULL; body = TRUE; if (!(cp = nxtarg())) { /* allow -xyz arguments */ @@ -852,8 +837,8 @@ pattern: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(TYPE_DATE); - twsd = n->data; + n = newnexus(date_t); + twsd = &n->data.d; twsd->datef = datesw; if (!tcompile(cp, &twsd->tws, twsd->after = i == PRAFTR)) { padvise(NULL, "unable to parse %s %s", argp[-2], cp); @@ -868,44 +853,26 @@ static struct nexus * newnexus(enum nexus_type t) { struct nexus *p = NULL; - struct bin_data *bin; - p = mh_xcalloc(1, sizeof(struct nexus)); + p->t = t; + return p; - switch (t) { - case TYPE_NOT: - p->action = NOTaction; - p->debug = BINdebug; - p->free = BINfree; - p->data = bin = mh_xcalloc(1, sizeof(struct bin_data)); - bin->type = t; - break; - case TYPE_AND: - case TYPE_OR: - p->action = BINaction; - p->debug = BINdebug; - p->free = BINfree; - p->data = bin = mh_xcalloc(1, sizeof(struct bin_data)); - bin->type = t; - break; - case TYPE_GREP: - p->action = GREPaction; - p->debug = GREPdebug; - p->free = GREPfree; - p->data = mh_xcalloc(1, sizeof(struct grep_data)); - break; - case TYPE_DATE: - p->action = DATEaction; - p->debug = DATEdebug; - p->free = DATEfree; - p->data = mh_xcalloc(1, sizeof(struct date_data)); +} + +static void nexus_clear(struct nexus *n) +{ + n->match = FALSE; + switch(n->t) { + case and_t: + case or_t: + nexus_clear(n->data.b.right); + /* FALL */ + case not_t: + nexus_clear(n->data.b.left); break; default: - adios(EX_SOFTWARE, NULL, "unknown nexus type %d", t); + break; } - - return p; - } static int @@ -914,11 +881,13 @@ pmatches(FILE *fp, int msgnum) struct field f = {{0}}; enum state s = FLD2; + if (!head) return 1; - if (!talked++ && pdebug && head->debug) { - head->debug(head->data, 0); + nexus_clear(head); + if (!talked++ && pdebug) { + nexus_debug(head, 0); } while (s == FLD2 || s == BODY2) { @@ -927,167 +896,29 @@ pmatches(FILE *fp, int msgnum) s = FLD2; /* FALL */ case FLD2: - if (head->action(&f, msgnum, head->data)) { - return TRUE; - } + nexus_match(&f, msgnum, head); break; case BODY2: if (!body) { - return FALSE; - } - if (head->action(&f, msgnum, head->data)) { - return TRUE; + return head->match; } + nexus_match(&f, msgnum, head); break; case IOERR2: advise(NULL, "IOERR in message %d\n", msgnum); return FALSE; case FILEEOF2: - return FALSE; + break; default: adios(EX_SOFTWARE, "m_getfld2", "returned unknown state %d at message %d", s, msgnum); } } - return FALSE; -} - -void -print_debug_level(size_t level) -{ - size_t i; - - for (i = 0; i < level; i++) { - fputs("| ", stderr); - } -} - -void -BINdebug(void *data, size_t level) -{ - struct bin_data *bd = data; - - print_debug_level(level); - - switch (bd->type) { - case TYPE_OR: - fputs("OR\n", stderr); - break; - case TYPE_AND: - fputs("AND\n", stderr); - break; - case TYPE_NOT: - fputs("NOT\n", stderr); - break; - default: - advise(NULL, "binary nexus has unknown type: %d\n", bd->type); - return; - } - - if (bd->left && bd->left->debug) { - bd->left->debug(bd->left->data, level+1); - } else { - print_debug_level(level+1); - fputs("can't debug left child\n", stderr); - } - - if (bd->right && bd->right->debug) { - bd->right->debug(bd->right->data, level+1); - } else if (bd->type != TYPE_NOT) { - print_debug_level(level+1); - fputs("can't debug right child\n", stderr); - } -} - -static boolean -NOTaction(struct field *f, int msgnum, void *data) -{ - struct bin_data *bin = data; - return !bin->left->action(f, msgnum, bin->left->data); -} - -static boolean -BINaction(struct field *f, int msgnum, void *data) -{ - struct bin_data *bin = data; - - if (bin->oldmsgnum != msgnum) { - bin->oldmsgnum = msgnum; - bin->match = FALSE; - bin->leftmatch = FALSE; - bin->rightmatch = FALSE; - } - - if (bin->match) { - return bin->match; - } - - bin->leftmatch = bin->leftmatch || bin->left->action(f, msgnum, bin->left->data); - bin->rightmatch = bin->rightmatch || bin->right->action(f, msgnum, bin->right->data); - - switch (bin->type) { - case TYPE_OR: - bin->match = bin->leftmatch || bin->rightmatch; - break; - case TYPE_AND: - bin->match = bin->leftmatch && bin->rightmatch; - break; - default: - adios(EX_SOFTWARE, NULL, "unknown nexus type: %d\n", bin->type); - } - - return bin->match; -} - -static void -BINfree(struct nexus **n) -{ - struct bin_data *bd; - - if (!(*n)) { - return; - } - - bd = (*n)->data; - - if (bd->left && bd->left->free) { - bd->left->free(&bd->left); - } else { - advise(NULL, "BUG: can't free left child"); - } - - if (bd->right && bd->right->free) { - bd->right->free(&bd->right); - } else { - advise(NULL, "BUG: can't free right child"); - } - - mh_free0(n); -} - -static int -gcompile(struct grep_data *g, const char *astr) -{ - regex_t *preg = mh_xcalloc(1, sizeof(regex_t)); - char *buf; - int ret; - - g->preg = preg; - g->pattern = mh_xstrdup(astr); - ret = regcomp(preg, astr, REG_ICASE | REG_NOSUB); - if (ret != 0) { - buf = mh_xcalloc(BUFSIZ, sizeof(char)); - regerror(ret, g->preg, buf, BUFSIZ*sizeof(char)); - fprintf(stderr, "%s\n", buf); - return FALSE; - } - return TRUE; - + return head->match; } static boolean -GREPaction(struct field *f, int msgnum, void *data) +match_grep(struct field *f, struct grep_data *g) { - struct grep_data *g = data; int ret; char buf[BUFSIZ]; @@ -1095,16 +926,22 @@ GREPaction(struct field *f, int msgnum, void *data) return FALSE; } + if (!g->header) { + ret = regexec(g->preg, f->value, 0, NULL, 0); + goto out; + } + /* check for the right field */ if (!(g->header && *g->header && mh_strcasecmp(g->header, f->name)==0)) { return FALSE; } - if(decode_rfc2047(f->value, buf, sizeof(buf))) { + if (decode_rfc2047(f->value, buf, sizeof(buf))) { ret = regexec(g->preg, buf, 0, NULL, 0); } else { ret = regexec(g->preg, f->value, 0, NULL, 0); } +out: switch (ret) { case 0: return TRUE; @@ -1115,26 +952,99 @@ GREPaction(struct field *f, int msgnum, void *data) fprintf(stderr, "%s\n", buf); return FALSE; } +} + +static boolean +match_date(struct field *f, int msgnum, struct date_data *dd) +{ + struct tws *tw; + char *bp; + boolean ret = FALSE; + + if (mh_strcasecmp(f->name, dd->datef)!=0) { + return FALSE; + } + bp = mh_xstrdup(f->value); + if ((tw = dparsetime(bp)) == NULL) { + advise(NULL, "unable to parse %s field in message %d, not matching...", dd->datef, msgnum); + } else if (dd->after) { + ret = twsort(tw, &dd->tws) > 0; + } else { + ret = twsort(tw, &dd->tws) < 0; + } + + mh_free0(&bp); + return ret; +} +static boolean +nexus_match(struct field *f, int msgnum, struct nexus *n) +{ + switch (n->t) { + case and_t: + n->match = nexus_match(f, msgnum, n->data.b.left); + n->match = nexus_match(f, msgnum, n->data.b.right) && n->match; + break; + case or_t: + n->match = nexus_match(f, msgnum, n->data.b.left); + n->match = nexus_match(f, msgnum, n->data.b.right) || n->match; + break; + case not_t: + n->match = !nexus_match(f, msgnum, n->data.b.left); + break; + case date_t: + if (n->match) { + return n->match; + } + n->match = match_date(f, msgnum, &n->data.d); + break; + case grep_t: + if (n->match) { + return n->match; + } + n->match = match_grep(f, &n->data.g); + break; + default: + adios(EX_SOFTWARE, NULL, "nexus tree contains a unknown nexus_type (%d)", n->t); + } + return n->match; } static void -GREPfree(struct nexus **n) +nexus_debug(struct nexus *n, size_t level) { - struct grep_data *gd; - if (!(*n)) { - return; + struct date_data *dd; + print_debug_level(level); + switch (n->t) { + case and_t: + fputs("AND\n", stderr); + nexus_debug(n->data.b.left, level+1); + nexus_debug(n->data.b.right, level+1); + break; + case or_t: + fputs("OR\n", stderr); + nexus_debug(n->data.b.left, level+1); + nexus_debug(n->data.b.right, level+1); + break; + case not_t: + fputs("NOT\n", stderr); + nexus_debug(n->data.b.left, level+1); + break; + case grep_t: + nexus_debug_grep(&n->data.g); + break; + case date_t: + dd = &n->data.d; + fprintf(stderr, "TEMPORAL(%s) %s: %s\n",dd->after ? "after" : "before", dd->datef, dasctime(&dd->tws)); + break; + default: + adios(EX_SOFTWARE, NULL, "nexus tree contains a unknown nexus_type (%d)", n->t); } - gd = (*n)->data; - mh_free0(&gd->header); - regfree(gd->preg); - mh_free0(n); } static void -GREPdebug(void *data, size_t level) +nexus_debug_grep(struct grep_data *gd) { - struct grep_data *gd = data; char *buf, *buf2, *pbuf, *pbuf2; pbuf = pbuf2 = mh_xstrdup(gd->pattern); @@ -1143,8 +1053,6 @@ GREPdebug(void *data, size_t level) *pbuf2 = tolower(*pbuf2); } - print_debug_level(level); - if (gd->header) { buf = buf2 = mh_xstrdup(gd->header); for (;*buf2; buf2++) { @@ -1158,6 +1066,62 @@ GREPdebug(void *data, size_t level) mh_free0(&pbuf); } +static void +nexus_free(struct nexus **n) +{ + if (!(*n)) { + return; + } + switch((*n)->t) { + case and_t: + case or_t: + nexus_free(&(*n)->data.b.right); + /* FALL */ + case not_t: + nexus_free(&(*n)->data.b.left); + break; + case grep_t: + mh_free0(&(*n)->data.g.header); + mh_free0(&(*n)->data.g.pattern); + regfree((*n)->data.g.preg); + case date_t: + break; + default: + advise(NULL, "Unknown nexus_type (%d) to free", (*n)->t); + } + mh_free0(n); +} + +static void +print_debug_level(size_t level) +{ + size_t i; + + for (i = 0; i < level; i++) { + fputs("| ", stderr); + } +} + +static int +gcompile(struct grep_data *g, const char *astr) +{ + regex_t *preg = mh_xcalloc(1, sizeof(regex_t)); + char *buf; + int ret; + + g->preg = preg; + g->pattern = mh_xstrdup(astr); + ret = regcomp(preg, astr, REG_ICASE | REG_NOSUB); + if (ret != 0) { + buf = mh_xcalloc(BUFSIZ, sizeof(char)); + regerror(ret, g->preg, buf, BUFSIZ*sizeof(char)); + fprintf(stderr, "%s\n", buf); + return FALSE; + } + return TRUE; + +} + static int tcompile(char *ap, struct tws *tb, int isafter) { @@ -1251,52 +1215,6 @@ tws_special(char *ap) } -static boolean -DATEaction(struct field *f, int msgnum, void *data) -{ - struct date_data *dd = data; - boolean state = FALSE; - char *bp; - struct tws *tw; - - if (mh_strcasecmp(f->name, dd->datef)!=0) { - return FALSE; - } - bp = mh_xstrdup(f->value); - if ((tw = dparsetime(bp)) == NULL) { - advise(NULL, "unable to parse %s field in message %d, not matching...", dd->datef, msgnum); - state = FALSE; - } else if (dd->after) { - state = twsort(tw, &dd->tws) > 0; - } else { - state = twsort(tw, &dd->tws) < 0; - } - - mh_free0(&bp); - - return state; -} - -static void -DATEfree(struct nexus **n) -{ - struct date_data *dd; - if (!(*n)) { - return; - } - dd = (*n)->data; - - mh_free0(n); -} - -static void -DATEdebug(void *data, size_t level) -{ - struct date_data *dd = data; - print_debug_level(level); - fprintf(stderr, "TEMPORAL(%s) %s: %s\n",dd->after ? "after" : "before", dd->datef, dasctime(&dd->tws)); -} - static struct nexus * createpickthread(char *msgs) { @@ -1343,8 +1261,8 @@ createpickthread(char *msgs) } - or = newnexus(TYPE_OR); - bd = or->data; + or = newnexus(or_t); + bd = &or->data.b; bd->right = ret; bd->left = c; ret = or; @@ -1359,28 +1277,26 @@ createpickthread(char *msgs) static struct nexus * createonethread(char *c) { - struct nexus *ret = newnexus(TYPE_OR); - struct nexus *left = newnexus(TYPE_GREP); - struct nexus *right = newnexus(TYPE_GREP); - struct bin_data *bd = ret->data; - struct grep_data *gd = left->data; + struct nexus *ret = newnexus(or_t); + struct nexus *left = newnexus(grep_t); + struct nexus *right = newnexus(grep_t); char buf[BUFSIZ]; - bd->left = left; - bd->right = right; - gd->header = mh_xstrdup("message-id"); + ret->data.b.left = left; + ret->data.b.right = right; + left->data.g.header = mh_xstrdup("message-id"); + snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c); - if(!gcompile(gd, buf)) { + if(!gcompile(&left->data.g, buf)) { padvise(NULL, "pattern error %s", c); goto error; } - gd = right->data; - gd->header = mh_xstrdup("references"); + right->data.g.header = mh_xstrdup("references"); snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c); - if(!gcompile(gd, buf)) { + if(!gcompile(&right->data.g, buf)) { padvise(NULL, "pattern error in %s", c); goto error; } @@ -1388,9 +1304,7 @@ createonethread(char *c) return ret; error: - GREPfree(&left); - GREPfree(&right); - BINfree(&ret); + nexus_free(&ret); return NULL; } -- 2.11.0