X-Git-Url: http://git.marmaro.de/?p=mmh;a=blobdiff_plain;f=uip%2Fpick.c;h=f700a0c7fe25a0fe710bcc9d961fa9ab4e795b5f;hp=93a240068b4044069aee161b1ad1f608e35b1f16;hb=ec2171174c019bdc83002cc29d06357e30b4b90d;hpb=240013872c392fe644bd4f79382d9f5314b4ea60 diff --git a/uip/pick.c b/uip/pick.c index 93a2400..f700a0c 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -8,8 +8,19 @@ #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +# include +#endif +#include static struct swit switches[] = { #define ANDSW 0 @@ -41,49 +52,86 @@ static struct swit switches[] = { #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; + +struct nexus { + boolean (*action)(struct field *, int, void *); + void (*free)(struct nexus **); + void (*debug)(void *, size_t); + + void *data; +}; + +static struct nexus *head; +static boolean body = FALSE; + +/* +** static prototypes +*/ +static int pcompile(char **, char *); +static int pmatches(FILE *, int); +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 */ @@ -92,6 +140,10 @@ main(int argc, char **argv) arguments = getarguments(invo_name, argc, argv, 1); argp = arguments; + if (strcmp(invo_name, "scan")==0) { + form = scanformat; + } + while ((cp = *argp++)) { if (*cp == '-') { if (*++cp == '-') { @@ -102,19 +154,19 @@ main(int argc, char **argv) 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: @@ -125,15 +177,16 @@ main(int argc, char **argv) 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: @@ -145,15 +198,15 @@ main(int argc, char **argv) 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; @@ -176,47 +229,82 @@ main(int argc, char **argv) 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 = pluspath(cp); + folder = mh_xstrdup(expandfol(cp)); } else app_msgarg(&msgs, cp); } vec[vecp] = NULL; - if (!context_find("path")) - free(path("./", TFOLDER)); + 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. */ if (!msgs.size) - app_msgarg(&msgs, "all"); + app_msgarg(&msgs, seq_all); if (!folder) - folder = getfolder(1); - maildir = m_maildir(folder); + folder = getcurfol(); + 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); + exit(EX_USAGE); seq_setprev(mp); /* set the previous-sequence */ /* @@ -227,11 +315,11 @@ main(int argc, char **argv) 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; @@ -251,29 +339,33 @@ main(int argc, char **argv) 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); } } + if (head) { + head->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; @@ -282,7 +374,7 @@ main(int argc, char **argv) */ 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. @@ -291,19 +383,1013 @@ main(int argc, char **argv) printf("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s"); } - context_replace(pfolder, folder); /* update current folder */ + context_replace(curfolder, 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); + 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 PATTERN MATCHING */ + +/* +** We really should be using re_comp() and re_exec() here. Unfortunately, +** pick advertises that lowercase characters matches characters of both +** cases. Since re_exec() doesn't exhibit this behavior, we are stuck +** with this version. Furthermore, we need to be able to save and restore +** the state of the pattern matcher in order to do things "efficiently". +** +** The matching power of this algorithm isn't as powerful as the re_xxx() +** routines (no \(xxx\) and \n constructs). Such is life. +*/ + +#define CCHR 2 +#define CDOT 4 +#define CCL 6 +#define NCCL 8 +#define CDOL 10 +#define CEOF 11 + +#define STAR 01 + +#define LBSIZE 1024 +#define ESIZE 1024 + +/* +** DEFINITIONS FOR NEXUS +*/ + +#define nxtarg() (*argp ? *argp++ : NULL) +#define prvarg() argp-- + +#define padvise if (!talked++) advise + + +enum nexus_type { + TYPE_GREP, + TYPE_DATE, + TYPE_OR, + TYPE_AND, + TYPE_NOT +}; + +struct bin_data { + struct nexus *left; + struct nexus *right; + enum nexus_type type; + int oldmsgnum; + boolean leftmatch; + boolean rightmatch; + boolean match; +}; + +struct date_data { + char *datef; + boolean after; + struct tws tws; +}; + +struct grep_data { + char *header; + char *pattern; + regex_t *preg; +}; + +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 boolean BINaction(struct field *, int, void *); +static boolean NOTaction(struct field *, int, void *); +static boolean GREPaction(struct field *, int, void *); +static boolean DATEaction(struct field *, int, void *); + +static void BINfree(struct nexus **); +static void GREPfree(struct nexus **); +static void DATEfree(struct nexus **); + +static void BINdebug(void *, size_t); +static void GREPdebug(void *, size_t); +static void DATEdebug(void *, size_t); + +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(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 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(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; +} + +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 -putzero_done(int status) +DATEdebug(void *data, size_t level) { - if (listsw && status && !isatty(fileno(stdout))) - printf("0\n"); - exit(status); + 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; + }