- }
- switch (smatch (cp, switches)) {
- case AMBIGSW:
- ambigsw (cp, switches);
- listsw = 0; /* HACK */
- done (1);
- case UNKWNSW:
- adios (NULL, "-%s unknown", cp);
-
- case HELPSW:
- snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
- invo_name);
- print_help (buf, switches, 1);
- listsw = 0; /* HACK */
- done (1);
- case VERSIONSW:
- print_version(invo_name);
- listsw = 0; /* HACK */
- done (1);
-
- case CCSW:
- case DATESW:
- case FROMSW:
- case SUBJSW:
- case TOSW:
- case DATFDSW:
- case AFTRSW:
- case BEFRSW:
- case SRCHSW:
- vec[vecp++] = --cp;
- pattern:
- if (!(cp = *argp++))/* allow -xyz arguments */
- adios (NULL, "missing argument to %s", argp[-2]);
- vec[vecp++] = cp;
- continue;
- case OTHRSW:
- adios (NULL, "internal error!");
-
- case ANDSW:
- case ORSW:
- case NOTSW:
- case LBRSW:
- case RBRSW:
- vec[vecp++] = --cp;
- continue;
-
- case SEQSW:
- if (!(cp = *argp++) || *cp == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
-
- /* check if too many sequences specified */
- if (seqp >= NUMATTRS)
- adios (NULL, "too many sequences (more than %d) specified", NUMATTRS);
-
- if (!seq_nameok (cp))
- done (1);
-
- seqs[seqp++] = cp;
- continue;
- case PUBLSW:
- publicsw = 1;
- continue;
- case NPUBLSW:
- publicsw = 0;
- continue;
- case ZEROSW:
- zerosw++;
- continue;
- case NZEROSW:
- zerosw = 0;
- continue;
-
- case LISTSW:
- listsw = 1;
- continue;
- case NLISTSW:
- listsw = 0;
- continue;
- }
- }
- if (*cp == '+' || *cp == '@') {
- if (folder)
- adios (NULL, "only one folder at a time!");
- else
- folder = pluspath (cp);
- } else
- app_msgarg(&msgs, cp);
- }
- vec[vecp] = NULL;
-
- if (!context_find ("path"))
- free (path ("./", TFOLDER));
-
- /*
- * If we didn't specify which messages to search,
- * then search the whole folder.
- */
- if (!msgs.size)
- app_msgarg(&msgs, "all");
-
- if (!folder)
- folder = getfolder (1);
- maildir = m_maildir (folder);
-
- if (chdir (maildir) == NOTOK)
- adios (maildir, "unable to change directory to");
-
- /* read folder and create message structure */
- if (!(mp = folder_read (folder)))
- adios (NULL, "unable to read folder %s", folder);
-
- /* check for empty folder */
- if (mp->nummsg == 0)
- adios (NULL, "no messages in %s", folder);
-
- /* parse all the message ranges/sequences and set SELECTED */
- for (msgnum = 0; msgnum < msgs.size; msgnum++)
- if (!m_convert (mp, msgs.msgs[msgnum]))
- done (1);
- seq_setprev (mp); /* set the previous-sequence */
-
- /*
- * If we aren't saving the results to a sequence,
- * we default to list the results.
- */
- if (listsw == -1)
- listsw = !seqp;
-
- if (publicsw == 1 && is_readonly(mp))
- adios (NULL, "folder %s is read-only, so -public not allowed", folder);
-
- if (!pcompile (vec, NULL))
- done (1);
-
- lo = mp->lowsel;
- hi = mp->hghsel;
-
- /* If printing message numbers to standard out, force line buffering on.
- */
- if (listsw)
- setvbuf (stdout, NULL, _IOLBF, 0);
-
- /*
- * Scan through all the SELECTED messages and check for a
- * match. If the message does not match, then unselect it.
- */
- for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
- if (is_selected (mp, msgnum)) {
- if ((fp = fopen (cp = m_name (msgnum), "r")) == NULL)
- admonish (cp, "unable to read message");
- if (fp && pmatches (fp, msgnum, 0L, 0L)) {
- if (msgnum < lo)
- lo = msgnum;
- if (msgnum > hi)
- hi = msgnum;
-
- if (listsw)
- printf ("%s\n", m_name (msgnum));
- } else {
- /* if it doesn't match, then unselect it */
- unset_selected (mp, msgnum);
- mp->numsel--;
- }
- if (fp)
- fclose (fp);
- }
- }
-
- mp->lowsel = lo;
- mp->hghsel = hi;
-
- if (mp->numsel <= 0)
- adios (NULL, "no messages match specification");
-
- seqs[seqp] = NULL;
-
- /*
- * Add the matching messages to sequences
- */
- for (seqp = 0; seqs[seqp]; seqp++)
- if (!seq_addsel (mp, seqs[seqp], publicsw, zerosw))
- done (1);
-
- /*
- * Print total matched if not printing each matched message number.
- */
- if (!listsw) {
- printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
- }
-
- context_replace (pfolder, folder); /* update current folder */
- seq_save (mp); /* synchronize message sequences */
- context_save (); /* save the context file */
- folder_free (mp); /* free folder/message structure */
- done (0);
- return 1;
+
+ 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);
+ }
+}
+
+static void
+nexus_debug_grep(struct grep_data *gd)
+{
+ char *buf, *buf2, *pbuf, *pbuf2;
+
+ pbuf = pbuf2 = mh_xstrdup(gd->pattern);
+
+ for (;*pbuf2; pbuf2++) {
+ *pbuf2 = tolower(*pbuf2);
+ }
+
+ 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);