+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(TYPE_OR);
+ bin = o->data;
+ 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(TYPE_AND);
+ bin = o->data;
+ 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(TYPE_NOT);
+ bin = n->data;
+ 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 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(TYPE_GREP);
+ gdata = n->data;
+ gdata->header = mh_xstrdup(dp);
+ snprintf(buffer, sizeof(buffer), "%s", cp);
+ dp = buffer;
+ goto pattern;
+
+ case PRSRCH:
+ n = newnexus(TYPE_GREP);
+ gdata = n->data;
+ 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(TYPE_DATE);
+ twsd = n->data;
+ 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;
+ struct bin_data *bin;
+
+ p = mh_xcalloc(1, sizeof(struct nexus));
+
+ 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));
+ break;
+ default:
+ adios(EX_SOFTWARE, NULL, "unknown nexus type %d", t);
+ }
+
+ return p;
+
+}
+
+static int
+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);
+ }
+
+ while (s == FLD2 || s == BODY2) {
+ switch (s = m_getfld2(s, &f, fp)) {
+ case LENERR2:
+ s = FLD2;
+ /* FALL */
+ case FLD2:
+ if (head->action(&f, msgnum, head->data)) {
+ return TRUE;
+ }
+ break;
+ case BODY2:
+ if (!body) {
+ return FALSE;
+ }
+ if (head->action(&f, msgnum, head->data)) {
+ return TRUE;
+ }
+ break;
+ case IOERR2:
+ advise(NULL, "IOERR in message %d\n", msgnum);
+ return FALSE;
+ case FILEEOF2:
+ return FALSE;
+ 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;
+}
+