fail in pick if a massage of -thread has no thread-id
[mmh] / uip / pick.c
index 1c195be..f700a0c 100644 (file)
@@ -71,9 +71,13 @@ static struct swit switches[] = {
        { "format format", 0 },
 #define WIDTHSW  23
     { "width columns", 0 },
-#define VERSIONSW  24
+#define THREADSW  24
+       { "thread", 0 },
+#define FILESW  25
+       { "file file", 0 },
+#define VERSIONSW  26
        { "Version", 0 },
-#define HELPSW  25
+#define HELPSW  27
        { "help", 0 },
        { NULL, 0 }
 };
@@ -96,6 +100,9 @@ static boolean body = FALSE;
 */
 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;
@@ -118,6 +125,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");
@@ -169,6 +177,7 @@ main(int argc, char **argv)
                        case AFTRSW:
                        case BEFRSW:
                        case SRCHSW:
+                       case THREADSW:
                                vec[vecp++] = --cp;
                        pattern:
                                if (!(cp = *argp++)) /* allow -xyz arguments */
@@ -232,6 +241,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 == '@') {
@@ -246,6 +264,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.
@@ -324,7 +357,9 @@ main(int argc, char **argv)
                }
        }
 
-       head->free(&head);
+       if (head) {
+               head->free(&head);
+       }
 
        mp->lowsel = lo;
        mp->hghsel = hi;
@@ -356,6 +391,29 @@ 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()
@@ -421,6 +479,8 @@ static struct swit parswit[] = {
        { "before date", 0 },
 #define PRDATF 14
        { "datefield field", 5 },
+#define PRTHREAD 15
+       { "thread msg", 0 },
        { NULL, 0 }
 };
 
@@ -731,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:
@@ -975,8 +1040,13 @@ BINaction(struct field *f, int msgnum, void *data)
 static void
 BINfree(struct nexus **n)
 {
-       struct nexus *bin = *n;
-       struct bin_data *bd = bin->data;
+       struct bin_data *bd;
+
+       if (!(*n)) {
+               return;
+       }
+
+       bd = (*n)->data;
 
        if (bd->left && bd->left->free) {
                bd->left->free(&bd->left);
@@ -1018,26 +1088,29 @@ GREPaction(struct field *f, int msgnum, void *data)
 {
        struct grep_data *g = data;
        int ret;
-       char *buf;
+       char buf[BUFSIZ];
 
-       /* check for the write field */
-       if (g->header && *g->header && mh_strcasecmp(g->header, f->name)) {
+       if (!g->header && *f->name) {
                return FALSE;
        }
 
-       if (!g->header && *f->name) {
+       /* 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;
+       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:
-               buf = mh_xcalloc(BUFSIZ, sizeof(char));
-               regerror(ret, g->preg, buf, BUFSIZ*sizeof(char));
+               regerror(ret, g->preg, buf, sizeof(buf));
                fprintf(stderr, "%s\n", buf);
                return FALSE;
        }
@@ -1047,7 +1120,11 @@ GREPaction(struct field *f, int msgnum, void *data)
 static void
 GREPfree(struct nexus **n)
 {
-       struct grep_data *gd = (*n)->data;
+       struct grep_data *gd;
+       if (!(*n)) {
+               return;
+       }
+       gd = (*n)->data;
        mh_free0(&gd->header);
        regfree(gd->preg);
        mh_free0(n);
@@ -1202,9 +1279,12 @@ DATEaction(struct field *f, int msgnum, void *data)
 static void
 DATEfree(struct nexus **n)
 {
-       struct date_data *dd = (*n)->data;
+       struct date_data *dd;
+       if (!(*n)) {
+               return;
+       }
+       dd = (*n)->data;
 
-       mh_free0(&dd->datef);
        mh_free0(n);
 }
 
@@ -1215,3 +1295,101 @@ DATEdebug(void *data, size_t level)
        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;
+       
+}