+static struct nexus *
+parse(void)
+{
+ char *cp;
+ struct nexus *n, *o;
+ struct bin_data *bin;
+
+ if ((n = nexp1()) == NULL || (cp = nxtarg()) == NULL)
+ return n;
+
+ if (*cp != '-') {
+ padvise(NULL, "%s unexpected", cp);
+ return NULL;
+ }
+
+ if (*++cp == '-')
+ goto header;
+ switch (smatch(cp, parswit)) {
+ case AMBIGSW:
+ ambigsw(cp, parswit);
+ talked++;
+ return NULL;
+ case UNKWNSW:
+ fprintf(stderr, "-%s unknown\n", cp);
+ talked++;
+ return NULL;
+
+ case PROR:
+ o = newnexus(or_t);
+ bin = &o->data.b;
+ bin->left = n;
+ if ((bin->right = parse()))
+ return o;
+ padvise(NULL, "missing disjunctive");
+ return NULL;
+
+header: ;
+ default:
+ prvarg();
+ return n;
+ }
+}
+
+static struct nexus *
+nexp1(void)
+{
+ char *cp;
+ struct nexus *n, *o;
+ struct bin_data *bin;
+
+ if ((n = nexp2()) == NULL || (cp = nxtarg()) == NULL)
+ return n;
+
+ if (*cp != '-') {
+ padvise(NULL, "%s unexpected", cp);
+ return NULL;
+ }
+
+ if (*++cp == '-')
+ goto header;
+ switch (smatch(cp, parswit)) {
+ case AMBIGSW:
+ ambigsw(cp, parswit);
+ talked++;
+ return NULL;
+ case UNKWNSW:
+ fprintf(stderr, "-%s unknown\n", cp);
+ talked++;
+ return NULL;
+
+ case PRAND:
+ o = newnexus(and_t);
+ bin = &o->data.b;
+ bin->left = n;
+ if ((bin->right = nexp1()))
+ return o;
+ padvise(NULL, "missing conjunctive");
+ return NULL;
+
+header: ;
+ default:
+ prvarg();
+ return n;
+ }
+}
+
+
+static struct nexus *
+nexp2(void)
+{
+ char *cp;
+ struct nexus *n;
+ struct bin_data *bin;
+
+ if ((cp = nxtarg()) == NULL)
+ return NULL;
+
+ if (*cp != '-') {
+ prvarg();
+ return nexp3();
+ }
+
+ if (*++cp == '-')
+ goto header;
+ switch (smatch(cp, parswit)) {
+ case AMBIGSW:
+ ambigsw(cp, parswit);
+ talked++;
+ return NULL;
+ case UNKWNSW:
+ fprintf(stderr, "-%s unknown\n", cp);
+ talked++;
+ return NULL;
+
+ case PRNOT:
+ n = newnexus(not_t);
+ bin = &n->data.b;
+ if ((bin->left = nexp3()))
+ return n;
+ padvise(NULL, "missing negation");
+ return NULL;
+
+header: ;
+ default:
+ prvarg();
+ return nexp3();
+ }
+}
+
+static struct nexus *
+nexp3(void)
+{
+ int i;
+ char *cp, *dp;
+ char buffer[BUFSIZ], temp[64];
+ struct nexus *n;
+ struct grep_data *gdata;
+ struct date_data *twsd;
+
+ if ((cp = nxtarg()) == NULL)
+ return NULL;
+
+ if (*cp != '-') {
+ padvise(NULL, "%s unexpected", cp);
+ return NULL;
+ }
+
+ if (*++cp == '-') {
+ dp = ++cp;
+ goto header;
+ }
+ switch (i = smatch(cp, parswit)) {
+ case AMBIGSW:
+ ambigsw(cp, parswit);
+ talked++;
+ return NULL;
+ case UNKWNSW:
+ fprintf(stderr, "-%s unknown\n", cp);
+ talked++;
+ return NULL;
+
+ case PRLBR:
+ if ((n = parse()) == NULL) {
+ padvise(NULL, "missing group");
+ return NULL;
+ }
+ if ((cp = nxtarg()) == NULL) {
+ padvise(NULL, "missing -rbrace");
+ return NULL;
+ }
+ if (*cp++ == '-' && smatch(cp, parswit) == PRRBR)
+ return n;
+ padvise(NULL, "%s unexpected", --cp);
+ return NULL;
+
+ default:
+ prvarg();
+ return NULL;
+
+ case PRTHREAD:
+ if (!(cp = nxtarg())) { /* allow -xyz arguments */
+ padvise(NULL, "missing argument to %s", argp[-2]);
+ }
+ return createpickthread(cp);
+ case PRCC:
+ case PRDATE:
+ case PRFROM:
+ case PRTO:
+ case PRSUBJ:
+ strncpy(temp, parswit[i].sw, sizeof(temp));
+ temp[sizeof(temp) - 1] = '\0';
+ dp = *brkstring(temp, " ", NULL);
+header: ;
+ if (!(cp = nxtarg())) { /* allow -xyz arguments */
+ padvise(NULL, "missing argument to %s", argp[-2]);
+ return NULL;
+ }
+ 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(grep_t);
+ gdata = &n->data.g;
+ gdata->header = NULL;
+ body = TRUE;
+ if (!(cp = nxtarg())) { /* allow -xyz arguments */
+ padvise(NULL, "missing argument to %s", argp[-2]);
+ return NULL;
+ }
+ dp = cp;
+pattern: ;
+ if (!gcompile(gdata, dp)) {
+ padvise("regcomp", "pattern error in %s %s", argp[-2], cp);
+ return NULL;
+ }
+ return n;
+
+ case PROTHR:
+ padvise(NULL, "internal error!");
+ return NULL;
+
+ case PRDATF:
+ if (!(datesw = nxtarg()) || *datesw == '-') {
+ padvise(NULL, "missing argument to %s",
+ argp[-2]);
+ return NULL;
+ }
+ return nexp3();
+
+ case PRAFTR:
+ case PRBEFR:
+ if (!(cp = nxtarg())) { /* allow -xyz arguments */
+ padvise(NULL, "missing argument to %s", argp[-2]);
+ return NULL;
+ }
+ 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);
+ return NULL;
+ }
+ return n;
+ }
+}
+
+
+static struct nexus *
+newnexus(enum nexus_type t)
+{
+ struct nexus *p = NULL;
+ p = mh_xcalloc(1, sizeof(struct nexus));
+ p->t = t;
+ return p;
+
+}
+
+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:
+ break;
+ }
+}
+
+static int
+pmatches(FILE *fp, int msgnum)
+{
+ struct field f = {{0}};
+ enum state s = FLD2;
+
+
+ if (!head)
+ return 1;
+
+ nexus_clear(head);
+ if (!talked++ && pdebug) {
+ nexus_debug(head, 0);
+ }
+
+ while (s == FLD2 || s == BODY2) {
+ switch (s = m_getfld2(s, &f, fp)) {
+ case LENERR2:
+ s = FLD2;
+ /* FALL */
+ case FLD2:
+ nexus_match(&f, msgnum, head);
+ break;
+ case BODY2:
+ if (!body) {
+ return head->match;
+ }
+ nexus_match(&f, msgnum, head);
+ break;
+ case IOERR2:
+ advise(NULL, "IOERR in message %d\n", msgnum);
+ return FALSE;
+ case FILEEOF2:
+ break;
+ default:
+ adios(EX_SOFTWARE, "m_getfld2", "returned unknown state %d at message %d", s, msgnum);
+ }
+ }
+ return head->match;
+}
+
+static boolean
+match_grep(struct field *f, struct grep_data *g)
+{
+ int ret;
+ char buf[BUFSIZ];
+
+ if (!g->header && *f->name) {
+ 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))) {
+ 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;
+ case REG_NOMATCH:
+ return FALSE;
+ default:
+ regerror(ret, g->preg, buf, sizeof(buf));
+ 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
+nexus_debug(struct nexus *n, size_t level)
+{
+ 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);
+ }
+}
+