X-Git-Url: http://git.marmaro.de/?p=mmh;a=blobdiff_plain;f=uip%2Fpick.c;h=0b9205bbb14fbde73a7781c7c05a2d8820f319f9;hp=464a7345db4d23485fe54f0bff0ff9137e3d0eb1;hb=7af26a5bed6efce10f3e5dd8cd7520420453c4f2;hpb=3ec7019fb0a1c08f1c866f0a84fa75fe5b498596 diff --git a/uip/pick.c b/uip/pick.c index 464a734..0b9205b 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef HAVE_SYS_TIME_H # include @@ -70,24 +71,54 @@ static struct swit switches[] = { #define FORMATSW 22 { "format format", 0 }, #define WIDTHSW 23 - { "width columns", 0 }, + { "width columns", 0 }, #define THREADSW 24 { "thread", 0 }, -#define VERSIONSW 25 +#define FILESW 25 + { "file file", 0 }, +#define VERSIONSW 26 { "Version", 0 }, -#define HELPSW 26 +#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); +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; +}; - void *data; +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; @@ -98,8 +129,15 @@ static boolean body = FALSE; */ 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; @@ -122,6 +160,7 @@ main(int argc, char **argv) char *form = NULL; char *fmtstr; FILE *fp; + char *file = NULL; if (atexit(putzero_done) != 0) { adios(EX_OSERR, NULL, "atexit failed"); @@ -237,6 +276,15 @@ main(int argc, char **argv) } 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 == '@') { @@ -251,6 +299,21 @@ main(int argc, char **argv) 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. @@ -277,7 +340,6 @@ main(int argc, char **argv) for (msgnum = 0; msgnum < msgs.size; msgnum++) if (!m_convert(mp, msgs.msgs[msgnum])) exit(EX_USAGE); - seq_setprev(mp); /* set the previous-sequence */ /* ** If we aren't saving the results to a sequence, @@ -329,9 +391,7 @@ main(int argc, char **argv) } } - if (head) { - head->free(&head); - } + nexus_free(&head); mp->lowsel = lo; mp->hghsel = hi; @@ -363,6 +423,28 @@ main(int argc, char **argv) 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() @@ -396,7 +478,6 @@ printmsg(FILE *f, struct msgs *mp, int msgnum, char *fmtstr, int width) } } - static struct swit parswit[] = { #define PRAND 0 { "and", 0 }, @@ -433,31 +514,6 @@ static struct swit parswit[] = { { 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 */ @@ -467,44 +523,12 @@ static struct swit parswit[] = { #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 */ @@ -523,19 +547,6 @@ 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) { @@ -589,8 +600,8 @@ parse(void) return NULL; case PROR: - o = newnexus(TYPE_OR); - bin = o->data; + o = newnexus(or_t); + bin = &o->data.b; bin->left = n; if ((bin->right = parse())) return o; @@ -632,8 +643,8 @@ nexp1(void) return NULL; case PRAND: - o = newnexus(TYPE_AND); - bin = o->data; + o = newnexus(and_t); + bin = &o->data.b; bin->left = n; if ((bin->right = nexp1())) return o; @@ -676,8 +687,8 @@ nexp2(void) return NULL; case PRNOT: - n = newnexus(TYPE_NOT); - bin = n->data; + n = newnexus(not_t); + bin = &n->data.b; if ((bin->left = nexp3())) return n; padvise(NULL, "missing negation"); @@ -758,16 +769,16 @@ header: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(TYPE_GREP); - gdata = n->data; + 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(TYPE_GREP); - gdata = n->data; + n = newnexus(grep_t); + gdata = &n->data.g; gdata->header = NULL; body = TRUE; if (!(cp = nxtarg())) { /* allow -xyz arguments */ @@ -800,8 +811,8 @@ pattern: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(TYPE_DATE); - twsd = n->data; + 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); @@ -816,44 +827,26 @@ static struct nexus * newnexus(enum nexus_type t) { struct nexus *p = NULL; - struct bin_data *bin; - p = mh_xcalloc(1, sizeof(struct nexus)); + p->t = t; + return p; - 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)); +} + +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: - adios(EX_SOFTWARE, NULL, "unknown nexus type %d", t); + break; } - - return p; - } static int @@ -862,11 +855,13 @@ 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); + nexus_clear(head); + if (!talked++ && pdebug) { + nexus_debug(head, 0); } while (s == FLD2 || s == BODY2) { @@ -875,143 +870,212 @@ pmatches(FILE *fp, int msgnum) s = FLD2; /* FALL */ case FLD2: - if (head->action(&f, msgnum, head->data)) { - return TRUE; - } + nexus_match(&f, msgnum, head); break; case BODY2: if (!body) { - return FALSE; - } - if (head->action(&f, msgnum, head->data)) { - return TRUE; + return head->match; } + nexus_match(&f, msgnum, head); break; case IOERR2: advise(NULL, "IOERR in message %d\n", msgnum); return FALSE; case FILEEOF2: - return FALSE; + break; default: adios(EX_SOFTWARE, "m_getfld2", "returned unknown state %d at message %d", s, msgnum); } } - return FALSE; + return head->match; } -void -print_debug_level(size_t level) +static boolean +match_grep(struct field *f, struct grep_data *g) { - size_t i; + int ret; + char buf[BUFSIZ]; - for (i = 0; i < level; i++) { - fputs("| ", stderr); + if (!g->header && *f->name) { + return FALSE; } -} -void -BINdebug(void *data, size_t level) -{ - struct bin_data *bd = data; - - print_debug_level(level); + if (!g->header) { + ret = regexec(g->preg, f->value, 0, NULL, 0); + goto out; + } - 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; + /* check for the right field */ + if (!(g->header && *g->header && mh_strcasecmp(g->header, f->name)==0)) { + return FALSE; } - if (bd->left && bd->left->debug) { - bd->left->debug(bd->left->data, level+1); + if (decode_rfc2047(f->value, buf, sizeof(buf))) { + ret = regexec(g->preg, buf, 0, NULL, 0); } else { - print_debug_level(level+1); - fputs("can't debug left child\n", stderr); + ret = regexec(g->preg, f->value, 0, NULL, 0); } - - 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); +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 -NOTaction(struct field *f, int msgnum, void *data) +match_date(struct field *f, int msgnum, struct date_data *dd) { - 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; + struct tws *tw; + char *bp; + boolean ret = FALSE; - if (bin->oldmsgnum != msgnum) { - bin->oldmsgnum = msgnum; - bin->match = FALSE; - bin->leftmatch = FALSE; - bin->rightmatch = FALSE; + if (mh_strcasecmp(f->name, dd->datef)!=0) { + return FALSE; } - - if (bin->match) { - return bin->match; + 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; } - bin->leftmatch = bin->leftmatch || bin->left->action(f, msgnum, bin->left->data); - bin->rightmatch = bin->rightmatch || bin->right->action(f, msgnum, bin->right->data); + mh_free0(&bp); + return ret; +} - switch (bin->type) { - case TYPE_OR: - bin->match = bin->leftmatch || bin->rightmatch; +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 TYPE_AND: - bin->match = bin->leftmatch && bin->rightmatch; + 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, "unknown nexus type: %d\n", bin->type); + adios(EX_SOFTWARE, NULL, "nexus tree contains a unknown nexus_type (%d)", n->t); } - - return bin->match; + return n->match; } static void -BINfree(struct nexus **n) +nexus_debug(struct nexus *n, size_t level) { - struct bin_data *bd; - - if (!(*n)) { - return; + 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; - bd = (*n)->data; + pbuf = pbuf2 = mh_xstrdup(gd->pattern); - if (bd->left && bd->left->free) { - bd->left->free(&bd->left); - } else { - advise(NULL, "BUG: can't free left child"); + for (;*pbuf2; pbuf2++) { + *pbuf2 = tolower(*pbuf2); } - if (bd->right && bd->right->free) { - bd->right->free(&bd->right); + if (gd->header) { + buf = buf2 = mh_xstrdup(gd->header); + for (;*buf2; buf2++) { + *buf2 = tolower(*buf2); + } + fprintf(stderr, "PETTERN(%s) %s\n", buf, pbuf); } else { - advise(NULL, "BUG: can't free right child"); + 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) { @@ -1032,77 +1096,6 @@ gcompile(struct grep_data *g, const char *astr) } -static boolean -GREPaction(struct field *f, int msgnum, void *data) -{ - struct grep_data *g = data; - int ret; - char *buf; - - 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; - } - - 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; - } - -} - -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) { @@ -1196,53 +1189,6 @@ tws_special(char *ap) } -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(&dd->datef); - 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) { @@ -1277,6 +1223,7 @@ createpickthread(char *msgs) 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; } @@ -1288,8 +1235,8 @@ createpickthread(char *msgs) } - or = newnexus(TYPE_OR); - bd = or->data; + or = newnexus(or_t); + bd = &or->data.b; bd->right = ret; bd->left = c; ret = or; @@ -1304,28 +1251,26 @@ createpickthread(char *msgs) 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; + struct nexus *ret = newnexus(or_t); + struct nexus *left = newnexus(grep_t); + struct nexus *right = newnexus(grep_t); char buf[BUFSIZ]; - bd->left = left; - bd->right = right; - gd->header = "message-id"; + 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(gd, buf)) { + if(!gcompile(&left->data.g, buf)) { padvise(NULL, "pattern error %s", c); goto error; } - gd = right->data; - gd->header = "references"; + right->data.g.header = mh_xstrdup("references"); snprintf(buf, sizeof(buf), "^[ \t]*<%s>", c); - if(!gcompile(gd, buf)) { + if(!gcompile(&right->data.g, buf)) { padvise(NULL, "pattern error in %s", c); goto error; } @@ -1333,9 +1278,7 @@ createonethread(char *c) return ret; error: - GREPfree(&left); - GREPfree(&right); - BINfree(&ret); + nexus_free(&ret); return NULL; }