#include <h/mh.h>
#include <h/tws.h>
-#include <h/picksbr.h>
#include <h/utils.h>
+#include <h/scansbr.h>
+#include <h/fmt_scan.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sysexits.h>
+#include <ctype.h>
+#include <regex.h>
+#include <libgen.h>
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#include <time.h>
static struct swit switches[] = {
#define ANDSW 0
#define BEFRSW 13
{ "before date", 0 },
#define DATFDSW 14
- { "datefield field", 5 },
+ { "datefield field", 5 }, /* 5 chars required to differ from -date */
#define SEQSW 15
{ "sequence name", 0 },
#define PUBLSW 16
{ "public", 0 },
#define NPUBLSW 17
- { "nopublic", 0 },
+ { "nopublic", 2 },
#define ZEROSW 18
{ "zero", 0 },
#define NZEROSW 19
- { "nozero", 0 },
+ { "nozero", 2 },
#define LISTSW 20
{ "list", 0 },
#define NLISTSW 21
- { "nolist", 0 },
-#define VERSIONSW 22
- { "version", 0 },
-#define HELPSW 23
+ { "nolist", 2 },
+#define FORMATSW 22
+ { "format format", 0 },
+#define WIDTHSW 23
+ { "width columns", 0 },
+#define THREADSW 24
+ { "thread", 0 },
+#define FILESW 25
+ { "file file", 0 },
+#define VERSIONSW 26
+ { "Version", 0 },
+#define HELPSW 27
{ "help", 0 },
{ NULL, 0 }
};
+char *version=VERSION;
+
+enum nexus_type {
+ noop_t = 0,
+ not_t,
+ and_t,
+ or_t,
+ date_t,
+ grep_t
+};
+
+struct bin_data {
+ struct nexus *left;
+ struct nexus *right;
+};
+
+struct date_data {
+ char *datef;
+ boolean after;
+ struct tws tws;
+};
+
+struct grep_data {
+ char *header;
+ char *pattern;
+ regex_t *preg;
+};
+
+struct nexus {
+ enum nexus_type t;
+ boolean match;
+ union {
+ struct bin_data b;
+ struct date_data d;
+ struct grep_data g;
+ } data;
+};
+
+static struct nexus *head;
+static boolean body = FALSE;
+
+/*
+** static prototypes
+*/
+static int pcompile(char **, char *);
+static int pmatches(FILE *, int);
+static boolean nexus_match(struct field *, int, struct nexus *);
+static void nexus_free(struct nexus **);
+static void nexus_clear(struct nexus *);
+static void nexus_debug(struct nexus *, size_t);
+static void nexus_debug_grep(struct grep_data *);
+static void print_debug_level(size_t);
+static struct nexus * createonethread(char *);
+static struct nexus * createpickthread(char *);
+static void scan_mbox(char *, char *, int);
+
+
static int listsw = -1;
-static void putzero_done(int) NORETURN;
+void putzero_done();
+
+static void printmsg(FILE *, struct msgs *, int, char *, int);
int
main(int argc, char **argv)
{
- int publicsw = -1, zerosw = 1, seqp = 0, vecp = 0;
+ int publicsw = -1, zerosw = 1, vecp = 0, width = 0;
+ unsigned int seqp = 0;
int lo, hi, msgnum;
char *maildir, *folder = NULL, buf[100];
char *cp, **argp, **arguments;
char *seqs[NUMATTRS + 1], *vec[MAXARGS];
struct msgs_array msgs = { 0, 0, NULL };
struct msgs *mp;
- register FILE *fp;
+ char *form = NULL;
+ char *fmtstr;
+ FILE *fp;
+ char *file = NULL;
- done=putzero_done;
+ if (atexit(putzero_done) != 0) {
+ adios(EX_OSERR, NULL, "atexit failed");
+ }
-#ifdef LOCALE
setlocale(LC_ALL, "");
-#endif
invo_name = mhbasename(argv[0]);
/* read user profile/context */
arguments = getarguments(invo_name, argc, argv, 1);
argp = arguments;
+ if (strcmp(invo_name, "scan")==0) {
+ form = scanformat;
+ }
+
while ((cp = *argp++)) {
if (*cp == '-') {
if (*++cp == '-') {
case AMBIGSW:
ambigsw(cp, switches);
listsw = 0; /* HACK */
- done(1);
+ exit(EX_USAGE);
case UNKWNSW:
- adios(NULL, "-%s unknown", cp);
+ adios(EX_USAGE, 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);
+ exit(argc == 2 ? EX_OK : EX_USAGE);
case VERSIONSW:
print_version(invo_name);
listsw = 0; /* HACK */
- done(1);
+ exit(argc == 2 ? EX_OK : EX_USAGE);
case CCSW:
case DATESW:
case AFTRSW:
case BEFRSW:
case SRCHSW:
+ case THREADSW:
vec[vecp++] = --cp;
pattern:
if (!(cp = *argp++)) /* allow -xyz arguments */
- adios(NULL, "missing argument to %s",
+ adios(EX_USAGE, NULL, "missing argument to %s",
argp[-2]);
vec[vecp++] = cp;
continue;
case OTHRSW:
- adios(NULL, "internal error!");
+ adios(EX_SOFTWARE, NULL, "internal error!");
case ANDSW:
case ORSW:
case SEQSW:
if (!(cp = *argp++) || *cp == '-')
- adios(NULL, "missing argument to %s",
+ adios(EX_USAGE, 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);
+ adios(EX_USAGE, NULL, "too many sequences (more than %d) specified", NUMATTRS);
if (!seq_nameok(cp))
- done(1);
+ exit(EX_USAGE);
seqs[seqp++] = cp;
continue;
case NLISTSW:
listsw = 0;
continue;
+ case FORMATSW:
+ if (!(form = *argp++) || *form == '-') {
+ adios(EX_USAGE, NULL, "missing argument to %s", argp[-2]);
+ }
+ continue;
+ case WIDTHSW:
+ if (!(cp = *argp++) || *cp == '-') {
+ adios(EX_USAGE, NULL, "missing argument to %s",
+ argp[-2]);
+ }
+ width = atoi(cp);
+ continue;
+ case FILESW:
+ if (!(cp = *argp++) || (cp[0] == '-' && cp[1])) {
+ adios(EX_USAGE, NULL, "missing argument to %s",
+ argp[-2]);
+ }
+ if (strcmp(file = cp, "-")!=0) {
+ file = mh_xstrdup(expanddir(cp));
+ }
+ continue;
}
}
if (*cp == '+' || *cp == '@') {
if (folder)
- adios(NULL, "only one folder at a time!");
+ adios(EX_USAGE, NULL, "only one folder at a time!");
else
- folder = getcpy(expandfol(cp));
+ folder = mh_xstrdup(expandfol(cp));
} else
app_msgarg(&msgs, cp);
}
vec[vecp] = NULL;
+ fmtstr = new_fs(form, "pick.default");
+
+ if (file) {
+ if (folder) {
+ adios(EX_USAGE, NULL, "\"+folder\" not allowed with -file");
+ }
+ if (msgs.size) {
+ adios(EX_USAGE, NULL, "\"msgs\" not allowed with -file");
+ }
+ if (vecp) {
+ adios(EX_USAGE, NULL, "section arguments not allowed with -file");
+ }
+
+ scan_mbox(file, fmtstr, width);
+ exit(EX_OK);
+ }
+
/*
** If we didn't specify which messages to search,
** then search the whole folder.
maildir = toabsdir(folder);
if (chdir(maildir) == NOTOK)
- adios(maildir, "unable to change directory to");
+ adios(EX_OSERR, 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);
+ adios(EX_IOERR, NULL, "unable to read folder %s", folder);
/* check for empty folder */
if (mp->nummsg == 0)
- adios(NULL, "no messages in %s", folder);
+ adios(EX_DATAERR, 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 */
+ exit(EX_USAGE);
/*
** If we aren't saving the results to a sequence,
listsw = !seqp;
if (publicsw == 1 && is_readonly(mp))
- adios(NULL, "folder %s is read-only, so -public not allowed",
+ adios(EX_NOPERM, NULL, "folder %s is read-only, so -public not allowed",
folder);
if (!pcompile(vec, NULL))
- done(1);
+ exit(EX_SOFTWARE);
lo = mp->lowsel;
hi = mp->hghsel;
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 (fp && pmatches(fp, msgnum)) {
if (msgnum < lo)
lo = msgnum;
if (msgnum > hi)
hi = msgnum;
- if (listsw)
- printf("%s\n", m_name(msgnum));
+ if (listsw) {
+ printmsg(fp, mp, msgnum, fmtstr, width);
+ }
} else {
/* if it doesn't match, then unselect it */
unset_selected(mp, msgnum);
- mp->numsel--;
}
if (fp)
fclose(fp);
}
}
+ nexus_free(&head);
+
mp->lowsel = lo;
mp->hghsel = hi;
if (mp->numsel <= 0)
- adios(NULL, "no messages match specification");
+ adios(EX_DATAERR, NULL, "no messages match specification");
seqs[seqp] = NULL;
*/
for (seqp = 0; seqs[seqp]; seqp++)
if (!seq_addsel(mp, seqs[seqp], publicsw, zerosw))
- done(1);
+ exit(EX_USAGE);
/*
** Print total matched if not printing each matched message number.
seq_save(mp); /* synchronize message sequences */
context_save(); /* save the context file */
folder_free(mp); /* free folder/message structure */
- done(0);
+ listsw = 0; /* HACK */
+ return 0;
+}
+
+static void
+scan_mbox(char *file, char *fmtstr, int width)
+{
+ FILE *in;
+ int msgnum;
+ int state;
+
+ if (strcmp(file, "-") == 0) {
+ in = stdin;
+ file = "stdin";
+ } else if (!(in = fopen(file, "r"))) {
+ adios(EX_IOERR, file, "unable to open");
+ }
+
+ for (msgnum = 1; ;msgnum++) {
+ state = scan(in, msgnum, SCN_MBOX, fmtstr, width, 0, 0);
+ if (state != SCNMSG) {
+ break;
+ }
+ }
+ fclose(in);
+}
+
+void
+putzero_done()
+{
+ if (listsw && !isatty(fileno(stdout)))
+ printf("0\n");
+}
+
+static void
+printmsg(FILE *f, struct msgs *mp, int msgnum, char *fmtstr, int width)
+{
+ int seqnum;
+ int state;
+ boolean unseen = FALSE;
+
+ fseek(f, 0L, SEEK_SET);
+
+ seqnum = seq_getnum(mp, seq_unseen);
+ unseen = in_sequence(mp, seqnum, msgnum);
+
+ switch (state = scan(f, msgnum, SCN_FOLD, fmtstr,
+ width, msgnum==mp->curmsg, unseen)) {
+ case SCNMSG:
+ case SCNERR:
+ break;
+ case SCNEOF:
+ advise(NULL, "message %d: empty", msgnum);
+ break;
+ default:
+ adios(EX_SOFTWARE, NULL, "scan() botch(%d)", state);
+ }
+}
+
+static struct swit parswit[] = {
+#define PRAND 0
+ { "and", 0 },
+#define PROR 1
+ { "or", 0 },
+#define PRNOT 2
+ { "not", 0 },
+#define PRLBR 3
+ { "lbrace", 0 },
+#define PRRBR 4
+ { "rbrace", 0 },
+#define PRCC 5
+ { "cc pattern", 0 },
+#define PRDATE 6
+ { "date pattern", 0 },
+#define PRFROM 7
+ { "from pattern", 0 },
+#define PRSRCH 8
+ { "search pattern", 0 },
+#define PRSUBJ 9
+ { "subject pattern", 0 },
+#define PRTO 10
+ { "to pattern", 0 },
+#define PROTHR 11
+ { "-othercomponent pattern", 15 },
+#define PRAFTR 12
+ { "after date", 0 },
+#define PRBEFR 13
+ { "before date", 0 },
+#define PRDATF 14
+ { "datefield field", 5 },
+#define PRTHREAD 15
+ { "thread msg", 0 },
+ { NULL, 0 }
+};
+
+/*
+** DEFINITIONS FOR NEXUS
+*/
+
+#define nxtarg() (*argp ? *argp++ : NULL)
+#define prvarg() argp--
+
+#define padvise if (!talked++) advise
+
+static int talked;
+static int pdebug = 0;
+
+static char *datesw;
+static char **argp;
+
+/*
+** prototypes for date routines
+*/
+static struct tws *tws_parse(char *, int);
+static struct tws *tws_special(char *);
+
+/*
+** static prototypes
+*/
+static int gcompile(struct grep_data *, const char *);
+static int tcompile(char *, struct tws *, int);
+
+static struct nexus *parse(void);
+static struct nexus *nexp1(void);
+static struct nexus *nexp2(void);
+static struct nexus *nexp3(void);
+static struct nexus *newnexus(enum nexus_type);
+
+static int
+pcompile(char **vec, char *date)
+{
+ char *cp;
+
+ if ((cp = getenv("MHPDEBUG")) && *cp)
+ pdebug++;
+
+ argp = vec;
+ if ((datesw = date) == NULL)
+ datesw = "date";
+ talked = 0;
+
+ if ((head = parse()) == NULL)
+ return (talked ? 0 : 1);
+
+ if (*argp) {
+ padvise(NULL, "%s unexpected", *argp);
+ return 0;
+ }
+
return 1;
}
+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);
+ }
+}
+
static void
-putzero_done(int status)
+nexus_debug_grep(struct grep_data *gd)
{
- if (listsw && status && !isatty(fileno(stdout)))
- printf("0\n");
- exit(status);
+ 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);
+}
+
+static void
+nexus_free(struct nexus **n)
+{
+ if (!(*n)) {
+ return;
+ }
+ switch((*n)->t) {
+ case and_t:
+ case or_t:
+ nexus_free(&(*n)->data.b.right);
+ /* FALL */
+ case not_t:
+ nexus_free(&(*n)->data.b.left);
+ break;
+ case grep_t:
+ mh_free0(&(*n)->data.g.header);
+ mh_free0(&(*n)->data.g.pattern);
+ regfree((*n)->data.g.preg);
+ case date_t:
+ break;
+ default:
+ advise(NULL, "Unknown nexus_type (%d) to free", (*n)->t);
+ }
+ mh_free0(n);
+}
+
+static void
+print_debug_level(size_t level)
+{
+ size_t i;
+
+ for (i = 0; i < level; i++) {
+ fputs("| ", stderr);
+ }
+}
+
+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 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 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(or_t);
+ bd = &or->data.b;
+ 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(or_t);
+ struct nexus *left = newnexus(grep_t);
+ struct nexus *right = newnexus(grep_t);
+ char buf[BUFSIZ];
+
+ ret->data.b.left = left;
+ ret->data.b.right = right;
+ left->data.g.header = mh_xstrdup("message-id");
+
+
+ snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c);
+ if(!gcompile(&left->data.g, buf)) {
+ padvise(NULL, "pattern error %s", c);
+ goto error;
+ }
+
+ right->data.g.header = mh_xstrdup("references");
+
+ snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c);
+ if(!gcompile(&right->data.g, buf)) {
+ padvise(NULL, "pattern error in %s", c);
+ goto error;
+ }
+
+ return ret;
+
+error:
+ nexus_free(&ret);
+ return NULL;
+
}