+ 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;
+
+}
+
+static boolean
+GREPaction(struct field *f, int msgnum, void *data)
+{
+ struct grep_data *g = data;
+ int ret;
+ char buf[BUFSIZ];
+
+ if (!g->header && *f->name) {
+ return FALSE;
+ }
+
+ /* 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);
+ }
+ 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 void
+GREPfree(struct nexus **n)
+{
+ struct grep_data *gd;
+ if (!(*n)) {
+ return;
+ }
+ gd = (*n)->data;
+ mh_free0(&gd->header);
+ regfree(gd->preg);
+ mh_free0(n);
+}
+
+static void
+GREPdebug(void *data, size_t level)
+{
+ struct grep_data *gd = data;
+ char *buf, *buf2, *pbuf, *pbuf2;
+
+ pbuf = pbuf2 = mh_xstrdup(gd->pattern);
+
+ for (;*pbuf2; pbuf2++) {
+ *pbuf2 = tolower(*pbuf2);
+ }
+
+ print_debug_level(level);
+
+ if (gd->header) {
+ buf = buf2 = mh_xstrdup(gd->header);
+ for (;*buf2; buf2++) {
+ *buf2 = tolower(*buf2);
+ }
+ fprintf(stderr, "PETTERN(%s) %s\n", buf, pbuf);
+ } else {
+ fprintf(stderr, "PETTERN(BODY) %s\n", pbuf);
+ }
+ mh_free0(&buf);
+ mh_free0(&pbuf);
+}
+
+static int
+tcompile(char *ap, struct tws *tb, int isafter)
+{
+ struct tws *tw;
+
+ if ((tw = tws_parse(ap, isafter)) == NULL)
+ return 0;
+
+ twscopy(tb, tw);
+ return 1;
+}
+
+
+static struct tws *
+tws_parse(char *ap, int isafter)
+{
+ char buffer[BUFSIZ];
+ struct tws *tw, *ts;
+
+ if ((tw = tws_special(ap)) != NULL) {
+ tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
+ tw->tw_hour = isafter ? 23 : 0;
+ return tw;
+ }
+ if ((tw = dparsetime(ap)) != NULL)
+ return tw;
+
+ if ((ts = dlocaltimenow()) == NULL)
+ return NULL;
+
+ snprintf(buffer, sizeof(buffer), "%s %s", ap, dtwszone(ts));
+ if ((tw = dparsetime(buffer)) != NULL)
+ return tw;
+
+ snprintf(buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
+ ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone(ts));
+ if ((tw = dparsetime(buffer)) != NULL)
+ return tw;
+
+ snprintf(buffer, sizeof(buffer), "%02d %s %04d %s",
+ ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
+ if ((tw = dparsetime(buffer)) != NULL)
+ return tw;
+
+ snprintf(buffer, sizeof(buffer), "%02d %s %04d %s %s",
+ ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
+ ap, dtwszone(ts));
+ if ((tw = dparsetime(buffer)) != NULL)
+ return tw;
+
+ return NULL;
+}
+
+
+static struct tws *
+tws_special(char *ap)
+{
+ int i;
+ time_t clock;
+ struct tws *tw;
+
+ time(&clock);
+ if (!mh_strcasecmp(ap, "today"))
+ return dlocaltime(&clock);
+ if (!mh_strcasecmp(ap, "yesterday")) {
+ clock -= (long) (60 * 60 * 24);
+ return dlocaltime(&clock);
+ }
+ if (!mh_strcasecmp(ap, "tomorrow")) {
+ clock += (long) (60 * 60 * 24);
+ return dlocaltime(&clock);
+ }
+
+ for (i = 0; tw_ldotw[i]; i++)
+ if (!mh_strcasecmp(ap, tw_ldotw[i]))
+ break;
+ if (tw_ldotw[i]) {
+ if ((tw = dlocaltime(&clock)) == NULL)
+ return NULL;
+ if ((i -= tw->tw_wday) > 0)
+ i -= 7;
+ }
+ else
+ if (*ap != '-')
+ return NULL;
+ else /* -ddd days ago */
+ i = atoi(ap); /* we should error check this */
+
+ clock += (long) ((60 * 60 * 24) * i);
+ return dlocaltime(&clock);
+}
+
+
+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)
+{
+ char *folder = NULL;
+ struct msgs_array msgarray = {0};
+ struct msgs_array files = {0};
+ struct nexus *ret = NULL;
+ struct nexus *c;
+ struct nexus *or;
+ struct bin_data *bd;
+ char *buf;
+ char **cp = brkstring(msgs, " \t", NULL);
+ int i;
+
+ for (; cp && *cp; cp++) {
+ switch (**cp) {
+ case '@':
+ case '+':
+ if (folder) {
+ advise("","");
+ break;
+ }
+ folder = mh_xstrdup(*cp);
+ break;
+ default:
+ app_msgarg(&msgarray, mh_xstrdup(*cp));
+ }
+ }
+
+ parse_msgs(&msgarray, folder, &files);
+
+ for (i = 0; i < files.size; i++) {
+ buf = getthreadid(files.msgs[i]);
+ if (!buf) {
+ adios(EX_DATAERR, NULL, "message %s is not part of a thread", basename(files.msgs[i]));
+ continue;
+ }
+
+ c = createonethread(buf);
+
+ if (!ret) {
+ ret = c;
+ continue;
+ }
+
+
+ or = newnexus(TYPE_OR);
+ bd = or->data;
+ bd->right = ret;
+ bd->left = c;
+ ret = or;
+ }
+
+ mh_free0(&(files.msgs));
+ mh_free0(&(msgarray.msgs));
+
+ return ret;
+}
+
+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;
+ char buf[BUFSIZ];
+
+ bd->left = left;
+ bd->right = right;
+ gd->header = mh_xstrdup("message-id");
+
+ snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c);
+ if(!gcompile(gd, buf)) {
+ padvise(NULL, "pattern error %s", c);
+ goto error;
+ }
+
+ gd = right->data;
+ gd->header = mh_xstrdup("references");
+
+ snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c);
+ if(!gcompile(gd, buf)) {
+ padvise(NULL, "pattern error in %s", c);
+ goto error;
+ }
+
+ return ret;
+
+error:
+ GREPfree(&left);
+ GREPfree(&right);
+ BINfree(&ret);
+ return NULL;
+