X-Git-Url: http://git.marmaro.de/?p=mmh;a=blobdiff_plain;f=uip%2Fpick.c;h=f700a0c7fe25a0fe710bcc9d961fa9ab4e795b5f;hp=3fb853cdc40e512dfaf1fbfcc38f525953f2efc8;hb=ec2171174c019bdc83002cc29d06357e30b4b90d;hpb=d5b5e6e4813b7fd77dc1664df4304537f3002cf3 diff --git a/uip/pick.c b/uip/pick.c index 3fb853c..f700a0c 100644 --- a/uip/pick.c +++ b/uip/pick.c @@ -9,6 +9,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #ifdef HAVE_SYS_TIME_H # include @@ -45,43 +52,69 @@ 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, long, long); +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, 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]; @@ -89,13 +122,16 @@ main(int argc, char **argv) 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 */ @@ -104,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 == '-') { @@ -114,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: @@ -137,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: @@ -157,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; @@ -188,18 +229,56 @@ 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 = 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. @@ -212,20 +291,20 @@ main(int argc, char **argv) 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 */ /* @@ -236,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; @@ -260,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; @@ -291,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. @@ -304,17 +387,64 @@ main(int argc, char **argv) seq_save(mp); /* synchronize message sequences */ context_save(); /* save the context file */ folder_free(mp); /* free folder/message structure */ - done(0); - return 1; + listsw = 0; /* HACK */ + return 0; } - static void -putzero_done(int status) +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 && status && !isatty(fileno(stdout))) + if (listsw && !isatty(fileno(stdout))) printf("0\n"); - exit(status); +} + +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); + } } @@ -349,6 +479,8 @@ static struct swit parswit[] = { { "before date", 0 }, #define PRDATF 14 { "datefield field", 5 }, +#define PRTHREAD 15 + { "thread msg", 0 }, { NULL, 0 } }; @@ -377,46 +509,6 @@ static struct swit parswit[] = { #define LBSIZE 1024 #define ESIZE 1024 - -static char linebuf[LBSIZE + 1]; - -/* the magic array for case-independence */ -static char cc[] = { - 0000,0001,0002,0003,0004,0005,0006,0007, - 0010,0011,0012,0013,0014,0015,0016,0017, - 0020,0021,0022,0023,0024,0025,0026,0027, - 0030,0031,0032,0033,0034,0035,0036,0037, - 0040,0041,0042,0043,0044,0045,0046,0047, - 0050,0051,0052,0053,0054,0055,0056,0057, - 0060,0061,0062,0063,0064,0065,0066,0067, - 0070,0071,0072,0073,0074,0075,0076,0077, - 0100,0141,0142,0143,0144,0145,0146,0147, - 0150,0151,0152,0153,0154,0155,0156,0157, - 0160,0161,0162,0163,0164,0165,0166,0167, - 0170,0171,0172,0133,0134,0135,0136,0137, - 0140,0141,0142,0143,0144,0145,0146,0147, - 0150,0151,0152,0153,0154,0155,0156,0157, - 0160,0161,0162,0163,0164,0165,0166,0167, - 0170,0171,0172,0173,0174,0175,0176,0177, - - 0200,0201,0202,0203,0204,0205,0206,0207, - 0210,0211,0212,0213,0214,0215,0216,0217, - 0220,0221,0222,0223,0224,0225,0226,0227, - 0230,0231,0232,0233,0234,0235,0236,0237, - 0240,0241,0242,0243,0244,0245,0246,0247, - 0250,0251,0252,0253,0254,0255,0256,0257, - 0260,0261,0262,0263,0264,0265,0266,0267, - 0270,0271,0272,0273,0274,0275,0276,0277, - 0300,0301,0302,0303,0304,0305,0306,0307, - 0310,0311,0312,0313,0314,0315,0316,0317, - 0320,0321,0322,0323,0324,0325,0326,0327, - 0330,0331,0332,0333,0334,0335,0336,0337, - 0340,0341,0342,0343,0344,0345,0346,0347, - 0350,0351,0352,0353,0354,0355,0356,0357, - 0360,0361,0362,0363,0364,0365,0366,0367, - 0370,0371,0372,0373,0374,0375,0376,0377, -}; - /* ** DEFINITIONS FOR NEXUS */ @@ -426,44 +518,36 @@ static char cc[] = { #define padvise if (!talked++) advise -struct nexus { - int (*n_action)(); - - union { - /* for {OR,AND,NOT}action */ - struct { - struct nexus *un_L_child; - struct nexus *un_R_child; - } st1; - - /* for GREPaction */ - struct { - int un_header; - int un_circf; - char un_expbuf[ESIZE]; - char *un_patbuf; - } st2; - - /* for TWSaction */ - struct { - char *un_datef; - int un_after; - struct tws un_tws; - } st3; - } un; + +enum nexus_type { + TYPE_GREP, + TYPE_DATE, + TYPE_OR, + TYPE_AND, + TYPE_NOT }; -#define n_L_child un.st1.un_L_child -#define n_R_child un.st1.un_R_child +struct bin_data { + struct nexus *left; + struct nexus *right; + enum nexus_type type; + int oldmsgnum; + boolean leftmatch; + boolean rightmatch; + boolean match; +}; -#define n_header un.st2.un_header -#define n_circf un.st2.un_circf -#define n_expbuf un.st2.un_expbuf -#define n_patbuf un.st2.un_patbuf +struct date_data { + char *datef; + boolean after; + struct tws tws; +}; -#define n_datef un.st3.un_datef -#define n_after un.st3.un_after -#define n_tws un.st3.un_tws +struct grep_data { + char *header; + char *pattern; + regex_t *preg; +}; static int talked; static int pdebug = 0; @@ -471,40 +555,42 @@ static int pdebug = 0; static char *datesw; static char **argp; -static struct nexus *head; /* ** prototypes for date routines */ -static struct tws *tws_parse(); -static struct tws *tws_special(); +static struct tws *tws_parse(char *, int); +static struct tws *tws_special(char *); /* ** static prototypes */ -static void PRaction(); -static int gcompile(); -static int advance(); -static int cclass(); -static int tcompile(); +static int gcompile(struct grep_data *, const char *); +static int tcompile(char *, struct tws *, int); -static struct nexus *parse(); -static struct nexus *nexp1(); -static struct nexus *nexp2(); -static struct nexus *nexp3(); -static struct nexus *newnexus(); +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 ORaction(); -static int ANDaction(); -static int NOTaction(); -static int GREPaction(); -static int TWSaction(); +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) { - register char *cp; + char *cp; if ((cp = getenv("MHPDEBUG")) && *cp) pdebug++; @@ -529,8 +615,9 @@ pcompile(char **vec, char *date) static struct nexus * parse(void) { - register char *cp; - register struct nexus *n, *o; + char *cp; + struct nexus *n, *o; + struct bin_data *bin; if ((n = nexp1()) == NULL || (cp = nxtarg()) == NULL) return n; @@ -553,9 +640,10 @@ parse(void) return NULL; case PROR: - o = newnexus(ORaction); - o->n_L_child = n; - if ((o->n_R_child = parse())) + o = newnexus(TYPE_OR); + bin = o->data; + bin->left = n; + if ((bin->right = parse())) return o; padvise(NULL, "missing disjunctive"); return NULL; @@ -570,8 +658,9 @@ header: ; static struct nexus * nexp1(void) { - register char *cp; - register struct nexus *n, *o; + char *cp; + struct nexus *n, *o; + struct bin_data *bin; if ((n = nexp2()) == NULL || (cp = nxtarg()) == NULL) return n; @@ -594,9 +683,10 @@ nexp1(void) return NULL; case PRAND: - o = newnexus(ANDaction); - o->n_L_child = n; - if ((o->n_R_child = nexp1())) + o = newnexus(TYPE_AND); + bin = o->data; + bin->left = n; + if ((bin->right = nexp1())) return o; padvise(NULL, "missing conjunctive"); return NULL; @@ -612,8 +702,9 @@ header: ; static struct nexus * nexp2(void) { - register char *cp; - register struct nexus *n; + char *cp; + struct nexus *n; + struct bin_data *bin; if ((cp = nxtarg()) == NULL) return NULL; @@ -636,8 +727,9 @@ nexp2(void) return NULL; case PRNOT: - n = newnexus(NOTaction); - if ((n->n_L_child = nexp3())) + n = newnexus(TYPE_NOT); + bin = n->data; + if ((bin->left = nexp3())) return n; padvise(NULL, "missing negation"); return NULL; @@ -653,9 +745,11 @@ static struct nexus * nexp3(void) { int i; - register char *cp, *dp; + char *cp, *dp; char buffer[BUFSIZ], temp[64]; - register struct nexus *n; + struct nexus *n; + struct grep_data *gdata; + struct date_data *twsd; if ((cp = nxtarg()) == NULL) return NULL; @@ -697,6 +791,11 @@ nexp3(void) 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: @@ -710,26 +809,28 @@ header: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(GREPaction); - n->n_header = 1; - snprintf(buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp); + 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(GREPaction); - n->n_header = 0; + 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(n, dp)) { - padvise(NULL, "pattern error in %s %s", argp[-2], cp); + if (!gcompile(gdata, dp)) { + padvise("regcomp", "pattern error in %s %s", argp[-2], cp); return NULL; } - n->n_patbuf = getcpy(dp); return n; case PROTHR: @@ -750,9 +851,10 @@ pattern: ; padvise(NULL, "missing argument to %s", argp[-2]); return NULL; } - n = newnexus(TWSaction); - n->n_datef = datesw; - if (!tcompile(cp, &n->n_tws, n->n_after = i == PRAFTR)) { + 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; } @@ -762,382 +864,303 @@ pattern: ; static struct nexus * -newnexus(int (*action)()) +newnexus(enum nexus_type t) { - register struct nexus *p; - - if ((p = (struct nexus *) calloc((size_t) 1, sizeof *p)) == NULL) - adios(NULL, "unable to allocate component storage"); + 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); + } - p->n_action = action; return p; -} - -#define args(a) a, fp, msgnum, start, stop -#define params args(n) -#define plist \ - register struct nexus *n; \ - register FILE *fp; \ - int msgnum; \ - long start, \ - stop; +} static int -pmatches(FILE *fp, int msgnum, long start, long stop) +pmatches(FILE *fp, int msgnum) { + struct field f = {{0}}; + enum state s = FLD2; + if (!head) return 1; - if (!talked++ && pdebug) - PRaction(head, 0); + if (!talked++ && pdebug && head->debug) { + head->debug(head->data, 0); + } - return (*head->n_action) (args(head)); + 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; -static void -PRaction(struct nexus *n, int level) + for (i = 0; i < level; i++) { + fputs("| ", stderr); + } +} + +void +BINdebug(void *data, size_t level) { - register int i; + struct bin_data *bd = data; - for (i = 0; i < level; i++) - fprintf(stderr, "| "); + print_debug_level(level); - if (n->n_action == ORaction) { - fprintf(stderr, "OR\n"); - PRaction(n->n_L_child, level + 1); - PRaction(n->n_R_child, level + 1); - return; - } - if (n->n_action == ANDaction) { - fprintf(stderr, "AND\n"); - PRaction(n->n_L_child, level + 1); - PRaction(n->n_R_child, level + 1); - return; - } - if (n->n_action == NOTaction) { - fprintf(stderr, "NOT\n"); - PRaction(n->n_L_child, level + 1); - return; - } - if (n->n_action == GREPaction) { - fprintf(stderr, "PATTERN(%s) %s\n", - n->n_header ? "header" : "body", n->n_patbuf); - return; - } - if (n->n_action == TWSaction) { - fprintf(stderr, "TEMPORAL(%s) %s: %s\n", - n->n_after ? "after" : "before", n->n_datef, - dasctime(&n->n_tws, TW_NULL)); + 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; } - fprintf(stderr, "UNKNOWN(0x%x)\n", - (unsigned int)(unsigned long) (*n->n_action)); -} + 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); + } -static int -ORaction(params) -plist -{ - if ((*n->n_L_child->n_action) (args(n->n_L_child))) - return 1; - return (*n->n_R_child->n_action) (args(n->n_R_child)); + 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 int -ANDaction(params) -plist +static boolean +NOTaction(struct field *f, int msgnum, void *data) { - if (!(*n->n_L_child->n_action) (args(n->n_L_child))) - return 0; - return (*n->n_R_child->n_action) (args(n->n_R_child)); + struct bin_data *bin = data; + return !bin->left->action(f, msgnum, bin->left->data); } - -static int -NOTaction(params) -plist +static boolean +BINaction(struct field *f, int msgnum, void *data) { - return (!(*n->n_L_child->n_action) (args(n->n_L_child))); -} + struct bin_data *bin = data; - -static int -gcompile(struct nexus *n, char *astr) -{ - register int c; - int cclcnt; - register unsigned char *ep, *dp, *sp, *lastep = 0; - - dp = (ep = n->n_expbuf) + sizeof n->n_expbuf; - sp = astr; - if (*sp == '^') { - n->n_circf = 1; - sp++; + if (bin->oldmsgnum != msgnum) { + bin->oldmsgnum = msgnum; + bin->match = FALSE; + bin->leftmatch = FALSE; + bin->rightmatch = FALSE; } - else - n->n_circf = 0; - for (;;) { - if (ep >= dp) - goto cerror; - if ((c = *sp++) != '*') - lastep = ep; - switch (c) { - case '\0': - *ep++ = CEOF; - return 1; - - case '.': - *ep++ = CDOT; - continue; - case '*': - if (lastep == 0) - goto defchar; - *lastep |= STAR; - continue; - - case '$': - if (*sp != '\0') - goto defchar; - *ep++ = CDOL; - continue; + if (bin->match) { + return bin->match; + } - case '[': - *ep++ = CCL; - *ep++ = 0; - cclcnt = 0; - if ((c = *sp++) == '^') { - c = *sp++; - ep[-2] = NCCL; - } - if (c == '-') { - *ep++ = c; - cclcnt++; - c = *sp++; - } - do { - if (c == '-' && *sp != '\0' && *sp != ']') { - for (c = ep[-1]+1; c < *sp; c++) { - *ep++ = c; - cclcnt++; - if (c == '\0' || ep >= dp) - goto cerror; - } - } else { - *ep++ = c; - cclcnt++; - if (c == '\0' || ep >= dp) - goto cerror; - } - } while ((c = *sp++) != ']'); - if (cclcnt > 255) - goto cerror; - lastep[1] = cclcnt; - continue; + bin->leftmatch = bin->leftmatch || bin->left->action(f, msgnum, bin->left->data); + bin->rightmatch = bin->rightmatch || bin->right->action(f, msgnum, bin->right->data); - case '\\': - if ((c = *sp++) == '\0') - goto cerror; -defchar: - default: - *ep++ = CCHR; - *ep++ = c; - } + 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); } -cerror: ; - return 0; + return bin->match; } - -static int -GREPaction(params) -plist +static void +BINfree(struct nexus **n) { - int c, body, lf; - long pos = start; - register char *p1, *p2, *ebp, *cbp; - char ibuf[BUFSIZ]; - - fseek(fp, start, SEEK_SET); - body = 0; - ebp = cbp = ibuf; - for (;;) { - if (body && n->n_header) - return 0; - p1 = linebuf; - p2 = cbp; - lf = 0; - for (;;) { - if (p2 >= ebp) { - if (fgets(ibuf, sizeof ibuf, fp) == NULL - || (stop && pos >= stop)) { - if (lf) - break; - return 0; - } - pos += (long) strlen(ibuf); - p2 = ibuf; - ebp = ibuf + strlen(ibuf); - } - c = *p2++; - if (lf && c != '\n') { - if (c != ' ' && c != '\t') { - --p2; - break; - } - else - lf = 0; - } - if (c == '\n') { - if (body) - break; - else { - if (lf) { - body++; - break; - } - lf++; - c = ' '; - } - } - if (c && p1 < &linebuf[LBSIZE - 1]) - *p1++ = c; - } + struct bin_data *bd; - *p1++ = 0; - cbp = p2; - p1 = linebuf; - p2 = n->n_expbuf; + if (!(*n)) { + return; + } - if (n->n_circf) { - if (advance(p1, p2)) - return 1; - continue; - } + bd = (*n)->data; - if (*p2 == CCHR) { - c = p2[1]; - do { - if (*p1 == c || cc[(unsigned char)*p1] == c) - if (advance(p1, p2)) - return 1; - } while (*p1++); - continue; - } + if (bd->left && bd->left->free) { + bd->left->free(&bd->left); + } else { + advise(NULL, "BUG: can't free left child"); + } - do { - if (advance(p1, p2)) - return 1; - } while (*p1++); + 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 -advance(char *alp, char *aep) +gcompile(struct grep_data *g, const char *astr) { - register unsigned char *lp, *ep, *curlp; - - lp = (unsigned char *)alp; - ep = (unsigned char *)aep; - for (;;) - switch (*ep++) { - case CCHR: - if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]]) - continue; - return 0; - - case CDOT: - if (*lp++) - continue; - return 0; - - case CDOL: - if (*lp == 0) - continue; - return 0; - - case CEOF: - return 1; - - case CCL: - if (cclass(ep, *lp++, 1)) { - ep += *ep + 1; - continue; - } - return 0; + 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; - case NCCL: - if (cclass(ep, *lp++, 0)) { - ep += *ep + 1; - continue; - } - return 0; +} - case CDOT | STAR: - curlp = lp; - while (*lp++) - continue; - goto star; +static boolean +GREPaction(struct field *f, int msgnum, void *data) +{ + struct grep_data *g = data; + int ret; + char buf[BUFSIZ]; - case CCHR | STAR: - curlp = lp; - while (*lp++ == *ep || cc[lp[-1]] == *ep) - continue; - ep++; - goto star; + if (!g->header && *f->name) { + return FALSE; + } - case CCL | STAR: - case NCCL | STAR: - curlp = lp; - while (cclass(ep, *lp++, ep[-1] == (CCL | STAR))) - continue; - ep += *ep + 1; - goto star; + /* check for the right field */ + if (!(g->header && *g->header && mh_strcasecmp(g->header, f->name)==0)) { + return FALSE; + } -star: - do { - lp--; - if (advance(lp, ep)) - return (1); - } while (lp > curlp); - return 0; + 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; + } - default: - admonish(NULL, "advance() botch -- you lose big"); - return 0; - } } +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 int -cclass(unsigned char *aset, int ac, int af) +static void +GREPdebug(void *data, size_t level) { - register unsigned int n; - register unsigned char c, *set; + struct grep_data *gd = data; + char *buf, *buf2, *pbuf, *pbuf2; - set = aset; - if ((c = ac) == 0) - return (0); + pbuf = pbuf2 = mh_xstrdup(gd->pattern); - n = *set++; - while (n--) - if (*set++ == c || set[-1] == cc[c]) - return (af); + for (;*pbuf2; pbuf2++) { + *pbuf2 = tolower(*pbuf2); + } - return (!af); -} + 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) { - register struct tws *tw; + struct tws *tw; if ((tw = tws_parse(ap, isafter)) == NULL) return 0; @@ -1151,7 +1174,7 @@ static struct tws * tws_parse(char *ap, int isafter) { char buffer[BUFSIZ]; - register struct tws *tw, *ts; + struct tws *tw, *ts; if ((tw = tws_special(ap)) != NULL) { tw->tw_sec = tw->tw_min = isafter ? 59 : 0; @@ -1193,7 +1216,7 @@ tws_special(char *ap) { int i; time_t clock; - register struct tws *tw; + struct tws *tw; time(&clock); if (!mh_strcasecmp(ap, "today")) @@ -1227,61 +1250,146 @@ tws_special(char *ap) } -static int -TWSaction(params) -plist +static boolean +DATEaction(struct field *f, int msgnum, void *data) { - int state; - register char *bp; - char buf[BUFSIZ], name[NAMESZ]; - register struct tws *tw; - - fseek(fp, start, SEEK_SET); - for (state = FLD, bp = NULL;;) { - switch (state = m_getfld(state, name, buf, sizeof buf, fp)) { - case FLD: - case FLDEOF: - case FLDPLUS: - if (bp != NULL) { - free(bp); - bp = NULL; - } - bp = getcpy(buf); - while (state == FLDPLUS) { - state = m_getfld(state, name, buf, - sizeof buf, fp); - bp = add(buf, bp); - } - if (!mh_strcasecmp(name, n->n_datef)) - break; - if (state != FLDEOF) - continue; + 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 +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)); +} - case BODY: - case BODYEOF: - case FILEEOF: - case LENERR: - case FMTERR: - if (state == LENERR || state == FMTERR) - advise(NULL, "format error in message %d", msgnum); - if (bp != NULL) - free(bp); - return 0; +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: - adios(NULL, "internal error -- you lose"); + app_msgarg(&msgarray, mh_xstrdup(*cp)); } - break; } - if ((tw = dparsetime(bp)) == NULL) - advise(NULL, "unable to parse %s field in message %d, matching...", - n->n_datef, msgnum), state = 1; - else - state = n->n_after ? (twsort(tw, &n->n_tws) > 0) - : (twsort(tw, &n->n_tws) < 0); + parse_msgs(&msgarray, folder, &files); - if (bp != NULL) - free(bp); - return state; + 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; + }