- }
- 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);
- 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;
-
- /*
- * 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;
- } 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 the name of all the matches
- */
- if (listsw) {
- for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
- if (is_selected (mp, msgnum))
- printf ("%s\n", m_name (msgnum));
- } else {
- 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(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;
+}
+
+static void
+BINfree(struct nexus **n)
+{
+ struct nexus *bin = *n;
+ struct bin_data *bd = bin->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;
+
+ /* check for the write field */
+ if (g->header && *g->header && mh_strcasecmp(g->header, f->name)) {
+ return FALSE;
+ }
+
+ if (!g->header && *f->name) {
+ return FALSE;
+ }
+
+ ret = regexec(g->preg, f->value, 0, NULL, 0) == REG_NOMATCH;
+ switch (ret) {
+ case 0:
+ return TRUE;
+ case REG_NOMATCH:
+ return FALSE;
+ default:
+ buf = mh_xcalloc(BUFSIZ, sizeof(char));
+ regerror(ret, g->preg, buf, BUFSIZ*sizeof(char));
+ fprintf(stderr, "%s\n", buf);
+ return FALSE;
+ }
+