993527b471828578e444f703cdd7fd91419877bc
[mmh] / uip / pick.c
1 /*
2 ** pick.c -- search for messages by content
3 **
4 ** This code is Copyright (c) 2002, 2008, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <h/tws.h>
11 #include <h/utils.h>
12 #include <unistd.h>
13 #include <locale.h>
14 #include <sysexits.h>
15
16 #ifdef HAVE_SYS_TIME_H
17 # include <sys/time.h>
18 #endif
19 #include <time.h>
20
21 static struct swit switches[] = {
22 #define ANDSW  0
23         { "and", 0 },
24 #define ORSW  1
25         { "or", 0 },
26 #define NOTSW  2
27         { "not", 0 },
28 #define LBRSW  3
29         { "lbrace", 0 },
30 #define RBRSW  4
31         { "rbrace", 0 },
32 #define CCSW  5
33         { "cc  pattern", 0 },
34 #define DATESW  6
35         { "date  pattern", 0 },
36 #define FROMSW  7
37         { "from  pattern", 0 },
38 #define SRCHSW  8
39         { "search  pattern", 0 },
40 #define SUBJSW  9
41         { "subject  pattern", 0 },
42 #define TOSW  10
43         { "to  pattern", 0 },
44 #define OTHRSW  11
45         { "-othercomponent  pattern", 0 },
46 #define AFTRSW  12
47         { "after date", 0 },
48 #define BEFRSW  13
49         { "before date", 0 },
50 #define DATFDSW  14
51         { "datefield field", 5 },  /* 5 chars required to differ from -date */
52 #define SEQSW  15
53         { "sequence name", 0 },
54 #define PUBLSW  16
55         { "public", 0 },
56 #define NPUBLSW  17
57         { "nopublic", 2 },
58 #define ZEROSW  18
59         { "zero", 0 },
60 #define NZEROSW  19
61         { "nozero", 2 },
62 #define LISTSW  20
63         { "list", 0 },
64 #define NLISTSW  21
65         { "nolist", 2 },
66 #define VERSIONSW  22
67         { "Version", 0 },
68 #define HELPSW  23
69         { "help", 0 },
70         { NULL, 0 }
71 };
72
73 /*
74 ** static prototypes
75 */
76 static int pcompile(char **, char *);
77 static int pmatches(FILE *, int, long, long);
78
79
80 static int listsw = -1;
81
82 void putzero_done();
83
84 int
85 main(int argc, char **argv)
86 {
87         int publicsw = -1, zerosw = 1, vecp = 0;
88         unsigned int seqp = 0;
89         int lo, hi, msgnum;
90         char *maildir, *folder = NULL, buf[100];
91         char *cp, **argp, **arguments;
92         char *seqs[NUMATTRS + 1], *vec[MAXARGS];
93         struct msgs_array msgs = { 0, 0, NULL };
94         struct msgs *mp;
95         FILE *fp;
96
97         if (atexit(putzero_done) != 0) {
98                 adios(EX_OSERR, NULL, "atexit failed");
99         }
100
101         setlocale(LC_ALL, "");
102         invo_name = mhbasename(argv[0]);
103
104         /* read user profile/context */
105         context_read();
106
107         arguments = getarguments(invo_name, argc, argv, 1);
108         argp = arguments;
109
110         while ((cp = *argp++)) {
111                 if (*cp == '-') {
112                         if (*++cp == '-') {
113                                 vec[vecp++] = --cp;
114                                 goto pattern;
115                         }
116                         switch (smatch(cp, switches)) {
117                         case AMBIGSW:
118                                 ambigsw(cp, switches);
119                                 listsw = 0;  /* HACK */
120                                 exit(EX_USAGE);
121                         case UNKWNSW:
122                                 adios(EX_USAGE, NULL, "-%s unknown", cp);
123
124                         case HELPSW:
125                                 snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name);
126                                 print_help(buf, switches, 1);
127                                 listsw = 0;  /* HACK */
128                                 exit(argc == 2 ? EX_OK : EX_USAGE);
129                         case VERSIONSW:
130                                 print_version(invo_name);
131                                 listsw = 0;  /* HACK */
132                                 exit(argc == 2 ? EX_OK : EX_USAGE);
133
134                         case CCSW:
135                         case DATESW:
136                         case FROMSW:
137                         case SUBJSW:
138                         case TOSW:
139                         case DATFDSW:
140                         case AFTRSW:
141                         case BEFRSW:
142                         case SRCHSW:
143                                 vec[vecp++] = --cp;
144                         pattern:
145                                 if (!(cp = *argp++)) /* allow -xyz arguments */
146                                         adios(EX_USAGE, NULL, "missing argument to %s",
147                                                         argp[-2]);
148                                 vec[vecp++] = cp;
149                                 continue;
150                         case OTHRSW:
151                                 adios(EX_SOFTWARE, NULL, "internal error!");
152
153                         case ANDSW:
154                         case ORSW:
155                         case NOTSW:
156                         case LBRSW:
157                         case RBRSW:
158                                 vec[vecp++] = --cp;
159                                 continue;
160
161                         case SEQSW:
162                                 if (!(cp = *argp++) || *cp == '-')
163                                         adios(EX_USAGE, NULL, "missing argument to %s",
164                                                         argp[-2]);
165
166                                 /* check if too many sequences specified */
167                                 if (seqp >= NUMATTRS)
168                                         adios(EX_USAGE, NULL, "too many sequences (more than %d) specified", NUMATTRS);
169
170                                 if (!seq_nameok(cp))
171                                         exit(EX_USAGE);
172
173                                 seqs[seqp++] = cp;
174                                 continue;
175                         case PUBLSW:
176                                 publicsw = 1;
177                                 continue;
178                         case NPUBLSW:
179                                 publicsw = 0;
180                                 continue;
181                         case ZEROSW:
182                                 zerosw++;
183                                 continue;
184                         case NZEROSW:
185                                 zerosw = 0;
186                                 continue;
187
188                         case LISTSW:
189                                 listsw = 1;
190                                 continue;
191                         case NLISTSW:
192                                 listsw = 0;
193                                 continue;
194                         }
195                 }
196                 if (*cp == '+' || *cp == '@') {
197                         if (folder)
198                                 adios(EX_USAGE, NULL, "only one folder at a time!");
199                         else
200                                 folder = getcpy(expandfol(cp));
201                 } else
202                         app_msgarg(&msgs, cp);
203         }
204         vec[vecp] = NULL;
205
206         /*
207         ** If we didn't specify which messages to search,
208         ** then search the whole folder.
209         */
210         if (!msgs.size)
211                 app_msgarg(&msgs, seq_all);
212
213         if (!folder)
214                 folder = getcurfol();
215         maildir = toabsdir(folder);
216
217         if (chdir(maildir) == NOTOK)
218                 adios(EX_OSERR, maildir, "unable to change directory to");
219
220         /* read folder and create message structure */
221         if (!(mp = folder_read(folder)))
222                 adios(EX_IOERR, NULL, "unable to read folder %s", folder);
223
224         /* check for empty folder */
225         if (mp->nummsg == 0)
226                 adios(EX_DATAERR, NULL, "no messages in %s", folder);
227
228         /* parse all the message ranges/sequences and set SELECTED */
229         for (msgnum = 0; msgnum < msgs.size; msgnum++)
230                 if (!m_convert(mp, msgs.msgs[msgnum]))
231                         exit(EX_USAGE);
232         seq_setprev(mp);  /* set the previous-sequence */
233
234         /*
235         ** If we aren't saving the results to a sequence,
236         ** we default to list the results.
237         */
238         if (listsw == -1)
239                 listsw = !seqp;
240
241         if (publicsw == 1 && is_readonly(mp))
242                 adios(EX_NOPERM, NULL, "folder %s is read-only, so -public not allowed",
243                                 folder);
244
245         if (!pcompile(vec, NULL))
246                 exit(EX_SOFTWARE);
247
248         lo = mp->lowsel;
249         hi = mp->hghsel;
250
251         /*
252         ** If printing message numbers to standard out,
253         ** force line buffering on.
254         */
255         if (listsw)
256                 setvbuf(stdout, NULL, _IOLBF, 0);
257
258         /*
259         ** Scan through all the SELECTED messages and check for a
260         ** match.  If the message does not match, then unselect it.
261         */
262         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
263                 if (is_selected(mp, msgnum)) {
264                         if ((fp = fopen(cp = m_name(msgnum), "r")) == NULL)
265                                 admonish(cp, "unable to read message");
266                         if (fp && pmatches(fp, msgnum, 0L, 0L)) {
267                                 if (msgnum < lo)
268                                         lo = msgnum;
269                                 if (msgnum > hi)
270                                         hi = msgnum;
271
272                                 if (listsw)
273                                         printf("%s\n", m_name(msgnum));
274                         } else {
275                                 /* if it doesn't match, then unselect it */
276                                 unset_selected(mp, msgnum);
277                         }
278                         if (fp)
279                                 fclose(fp);
280                 }
281         }
282
283         mp->lowsel = lo;
284         mp->hghsel = hi;
285
286         if (mp->numsel <= 0)
287                 adios(EX_DATAERR, NULL, "no messages match specification");
288
289         seqs[seqp] = NULL;
290
291         /*
292         ** Add the matching messages to sequences
293         */
294         for (seqp = 0; seqs[seqp]; seqp++)
295                 if (!seq_addsel(mp, seqs[seqp], publicsw, zerosw))
296                         exit(EX_USAGE);
297
298         /*
299         ** Print total matched if not printing each matched message number.
300         */
301         if (!listsw) {
302                 printf("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
303         }
304
305         context_replace(curfolder, folder);  /* update current folder */
306         seq_save(mp);  /* synchronize message sequences */
307         context_save();  /* save the context file */
308         folder_free(mp);  /* free folder/message structure */
309         listsw = 0; /* HACK */
310         return 0;
311 }
312
313
314 void
315 putzero_done()
316 {
317         if (listsw && !isatty(fileno(stdout)))
318                 printf("0\n");
319 }
320
321
322 static struct swit parswit[] = {
323 #define PRAND   0
324         { "and", 0 },
325 #define PROR    1
326         { "or", 0 },
327 #define PRNOT   2
328         { "not", 0 },
329 #define PRLBR   3
330         { "lbrace", 0 },
331 #define PRRBR   4
332         { "rbrace", 0 },
333 #define PRCC    5
334         { "cc  pattern", 0 },
335 #define PRDATE  6
336         { "date  pattern", 0 },
337 #define PRFROM  7
338         { "from  pattern", 0 },
339 #define PRSRCH  8
340         { "search  pattern", 0 },
341 #define PRSUBJ  9
342         { "subject  pattern", 0 },
343 #define PRTO   10
344         { "to  pattern", 0 },
345 #define PROTHR 11
346         { "-othercomponent  pattern", 15 },
347 #define PRAFTR 12
348         { "after date", 0 },
349 #define PRBEFR 13
350         { "before date", 0 },
351 #define PRDATF 14
352         { "datefield field", 5 },
353         { NULL, 0 }
354 };
355
356 /* DEFINITIONS FOR PATTERN MATCHING */
357
358 /*
359 ** We really should be using re_comp() and re_exec() here.  Unfortunately,
360 ** pick advertises that lowercase characters matches characters of both
361 ** cases.  Since re_exec() doesn't exhibit this behavior, we are stuck
362 ** with this version.  Furthermore, we need to be able to save and restore
363 ** the state of the pattern matcher in order to do things "efficiently".
364 **
365 ** The matching power of this algorithm isn't as powerful as the re_xxx()
366 ** routines (no \(xxx\) and \n constructs).  Such is life.
367 */
368
369 #define CCHR       2
370 #define CDOT       4
371 #define CCL        6
372 #define NCCL       8
373 #define CDOL      10
374 #define CEOF      11
375
376 #define STAR      01
377
378 #define LBSIZE  1024
379 #define ESIZE   1024
380
381
382 static char linebuf[LBSIZE + 1];
383 static char decoded_linebuf[LBSIZE + 1];
384
385 /* the magic array for case-independence */
386 static char cc[] = {
387         0000,0001,0002,0003,0004,0005,0006,0007,
388         0010,0011,0012,0013,0014,0015,0016,0017,
389         0020,0021,0022,0023,0024,0025,0026,0027,
390         0030,0031,0032,0033,0034,0035,0036,0037,
391         0040,0041,0042,0043,0044,0045,0046,0047,
392         0050,0051,0052,0053,0054,0055,0056,0057,
393         0060,0061,0062,0063,0064,0065,0066,0067,
394         0070,0071,0072,0073,0074,0075,0076,0077,
395         0100,0141,0142,0143,0144,0145,0146,0147,
396         0150,0151,0152,0153,0154,0155,0156,0157,
397         0160,0161,0162,0163,0164,0165,0166,0167,
398         0170,0171,0172,0133,0134,0135,0136,0137,
399         0140,0141,0142,0143,0144,0145,0146,0147,
400         0150,0151,0152,0153,0154,0155,0156,0157,
401         0160,0161,0162,0163,0164,0165,0166,0167,
402         0170,0171,0172,0173,0174,0175,0176,0177,
403
404         0200,0201,0202,0203,0204,0205,0206,0207,
405         0210,0211,0212,0213,0214,0215,0216,0217,
406         0220,0221,0222,0223,0224,0225,0226,0227,
407         0230,0231,0232,0233,0234,0235,0236,0237,
408         0240,0241,0242,0243,0244,0245,0246,0247,
409         0250,0251,0252,0253,0254,0255,0256,0257,
410         0260,0261,0262,0263,0264,0265,0266,0267,
411         0270,0271,0272,0273,0274,0275,0276,0277,
412         0300,0301,0302,0303,0304,0305,0306,0307,
413         0310,0311,0312,0313,0314,0315,0316,0317,
414         0320,0321,0322,0323,0324,0325,0326,0327,
415         0330,0331,0332,0333,0334,0335,0336,0337,
416         0340,0341,0342,0343,0344,0345,0346,0347,
417         0350,0351,0352,0353,0354,0355,0356,0357,
418         0360,0361,0362,0363,0364,0365,0366,0367,
419         0370,0371,0372,0373,0374,0375,0376,0377,
420 };
421
422 /*
423 ** DEFINITIONS FOR NEXUS
424 */
425
426 #define nxtarg() (*argp ? *argp++ : NULL)
427 #define prvarg() argp--
428
429 #define padvise if (!talked++) advise
430
431 struct nexus {
432         int (*n_action)();
433
434         union {
435                 /* for {OR,AND,NOT}action */
436                 struct {
437                         struct nexus *un_L_child;
438                         struct nexus *un_R_child;
439                 } st1;
440
441                 /* for GREPaction */
442                 struct {
443                         int   un_header;
444                         int   un_circf;
445                         char  un_expbuf[ESIZE];
446                         char *un_patbuf;
447                 } st2;
448
449                 /* for TWSaction */
450                 struct {
451                         char *un_datef;
452                         int   un_after;
453                         struct tws un_tws;
454                 } st3;
455         } un;
456 };
457
458 #define n_L_child un.st1.un_L_child
459 #define n_R_child un.st1.un_R_child
460
461 #define n_header un.st2.un_header
462 #define n_circf  un.st2.un_circf
463 #define n_expbuf un.st2.un_expbuf
464 #define n_patbuf un.st2.un_patbuf
465
466 #define n_datef  un.st3.un_datef
467 #define n_after  un.st3.un_after
468 #define n_tws    un.st3.un_tws
469
470 static int talked;
471 static int pdebug = 0;
472
473 static char *datesw;
474 static char **argp;
475
476 static struct nexus *head;
477
478 /*
479 ** prototypes for date routines
480 */
481 static struct tws *tws_parse(char *, int);
482 static struct tws *tws_special(char *);
483
484 /*
485 ** static prototypes
486 */
487 static void PRaction(struct nexus *, int);
488 static int gcompile(struct nexus *, char *);
489 static int advance(char *, char *);
490 static int cclass(unsigned char *, int, int);
491 static int tcompile(char *, struct tws *, int);
492
493 static struct nexus *parse(void);
494 static struct nexus *nexp1(void);
495 static struct nexus *nexp2(void);
496 static struct nexus *nexp3(void);
497 static struct nexus *newnexus(int (*)());
498
499 static int ORaction();
500 static int ANDaction();
501 static int NOTaction();
502 static int GREPaction();
503 static int TWSaction();
504
505
506 static int
507 pcompile(char **vec, char *date)
508 {
509         char *cp;
510
511         if ((cp = getenv("MHPDEBUG")) && *cp)
512                 pdebug++;
513
514         argp = vec;
515         if ((datesw = date) == NULL)
516                 datesw = "date";
517         talked = 0;
518
519         if ((head = parse()) == NULL)
520                 return (talked ? 0 : 1);
521
522         if (*argp) {
523                 padvise(NULL, "%s unexpected", *argp);
524                 return 0;
525         }
526
527         return 1;
528 }
529
530
531 static struct nexus *
532 parse(void)
533 {
534         char  *cp;
535         struct nexus *n, *o;
536
537         if ((n = nexp1()) == NULL || (cp = nxtarg()) == NULL)
538                 return n;
539
540         if (*cp != '-') {
541                 padvise(NULL, "%s unexpected", cp);
542                 return NULL;
543         }
544
545         if (*++cp == '-')
546                 goto header;
547         switch (smatch(cp, parswit)) {
548         case AMBIGSW:
549                 ambigsw(cp, parswit);
550                 talked++;
551                 return NULL;
552         case UNKWNSW:
553                 fprintf(stderr, "-%s unknown\n", cp);
554                 talked++;
555                 return NULL;
556
557         case PROR:
558                 o = newnexus(ORaction);
559                 o->n_L_child = n;
560                 if ((o->n_R_child = parse()))
561                         return o;
562                 padvise(NULL, "missing disjunctive");
563                 return NULL;
564
565 header: ;
566         default:
567                 prvarg();
568                 return n;
569         }
570 }
571
572 static struct nexus *
573 nexp1(void)
574 {
575         char *cp;
576         struct nexus *n, *o;
577
578         if ((n = nexp2()) == NULL || (cp = nxtarg()) == NULL)
579                 return n;
580
581         if (*cp != '-') {
582                 padvise(NULL, "%s unexpected", cp);
583                 return NULL;
584         }
585
586         if (*++cp == '-')
587                 goto header;
588         switch (smatch(cp, parswit)) {
589         case AMBIGSW:
590                 ambigsw(cp, parswit);
591                 talked++;
592                 return NULL;
593         case UNKWNSW:
594                 fprintf(stderr, "-%s unknown\n", cp);
595                 talked++;
596                 return NULL;
597
598         case PRAND:
599                 o = newnexus(ANDaction);
600                 o->n_L_child = n;
601                 if ((o->n_R_child = nexp1()))
602                         return o;
603                 padvise(NULL, "missing conjunctive");
604                 return NULL;
605
606 header: ;
607         default:
608                 prvarg();
609                 return n;
610         }
611 }
612
613
614 static struct nexus *
615 nexp2(void)
616 {
617         char *cp;
618         struct nexus *n;
619
620         if ((cp = nxtarg()) == NULL)
621                 return NULL;
622
623         if (*cp != '-') {
624                 prvarg();
625                 return nexp3();
626         }
627
628         if (*++cp == '-')
629                 goto header;
630         switch (smatch(cp, parswit)) {
631         case AMBIGSW:
632                 ambigsw(cp, parswit);
633                 talked++;
634                 return NULL;
635         case UNKWNSW:
636                 fprintf(stderr, "-%s unknown\n", cp);
637                 talked++;
638                 return NULL;
639
640         case PRNOT:
641                 n = newnexus(NOTaction);
642                 if ((n->n_L_child = nexp3()))
643                         return n;
644                 padvise(NULL, "missing negation");
645                 return NULL;
646
647 header: ;
648         default:
649                 prvarg();
650                 return nexp3();
651         }
652 }
653
654 static struct nexus *
655 nexp3(void)
656 {
657         int i;
658         char *cp, *dp;
659         char buffer[BUFSIZ], temp[64];
660         struct nexus *n;
661
662         if ((cp = nxtarg()) == NULL)
663                 return NULL;
664
665         if (*cp != '-') {
666                 padvise(NULL, "%s unexpected", cp);
667                 return NULL;
668         }
669
670         if (*++cp == '-') {
671                 dp = ++cp;
672                 goto header;
673         }
674         switch (i = smatch(cp, parswit)) {
675         case AMBIGSW:
676                 ambigsw(cp, parswit);
677                 talked++;
678                 return NULL;
679         case UNKWNSW:
680                 fprintf(stderr, "-%s unknown\n", cp);
681                 talked++;
682                 return NULL;
683
684         case PRLBR:
685                 if ((n = parse()) == NULL) {
686                         padvise(NULL, "missing group");
687                         return NULL;
688                 }
689                 if ((cp = nxtarg()) == NULL) {
690                         padvise(NULL, "missing -rbrace");
691                         return NULL;
692                 }
693                 if (*cp++ == '-' && smatch(cp, parswit) == PRRBR)
694                         return n;
695                 padvise(NULL, "%s unexpected", --cp);
696                 return NULL;
697
698         default:
699                 prvarg();
700                 return NULL;
701
702         case PRCC:
703         case PRDATE:
704         case PRFROM:
705         case PRTO:
706         case PRSUBJ:
707                 strncpy(temp, parswit[i].sw, sizeof(temp));
708                 temp[sizeof(temp) - 1] = '\0';
709                 dp = *brkstring(temp, " ", NULL);
710 header: ;
711                 if (!(cp = nxtarg())) {  /* allow -xyz arguments */
712                         padvise(NULL, "missing argument to %s", argp[-2]);
713                         return NULL;
714                 }
715                 n = newnexus(GREPaction);
716                 n->n_header = 1;
717                 snprintf(buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp);
718                 dp = buffer;
719                 goto pattern;
720
721         case PRSRCH:
722                 n = newnexus(GREPaction);
723                 n->n_header = 0;
724                 if (!(cp = nxtarg())) {  /* allow -xyz arguments */
725                         padvise(NULL, "missing argument to %s", argp[-2]);
726                         return NULL;
727                 }
728                 dp = cp;
729 pattern: ;
730                 if (!gcompile(n, dp)) {
731                         padvise(NULL, "pattern error in %s %s", argp[-2], cp);
732                         return NULL;
733                 }
734                 n->n_patbuf = getcpy(dp);
735                 return n;
736
737         case PROTHR:
738                 padvise(NULL, "internal error!");
739                 return NULL;
740
741         case PRDATF:
742                 if (!(datesw = nxtarg()) || *datesw == '-') {
743                         padvise(NULL, "missing argument to %s",
744                                         argp[-2]);
745                         return NULL;
746                 }
747                 return nexp3();
748
749         case PRAFTR:
750         case PRBEFR:
751                 if (!(cp = nxtarg())) {  /* allow -xyz arguments */
752                         padvise(NULL, "missing argument to %s", argp[-2]);
753                         return NULL;
754                 }
755                 n = newnexus(TWSaction);
756                 n->n_datef = datesw;
757                 if (!tcompile(cp, &n->n_tws, n->n_after = i == PRAFTR)) {
758                         padvise(NULL, "unable to parse %s %s", argp[-2], cp);
759                         return NULL;
760                 }
761                 return n;
762         }
763 }
764
765
766 static struct nexus *
767 newnexus(int (*action)())
768 {
769         struct nexus *p;
770
771         p = (struct nexus *) mh_xcalloc((size_t) 1, sizeof *p);
772
773         p->n_action = action;
774         return p;
775 }
776
777
778 #define args(a)  a, fp, msgnum, start, stop
779 #define params   args(n)
780 #define plist    \
781         struct nexus  *n; \
782         FILE *fp; \
783         int msgnum; \
784         long start, \
785         stop;
786
787 static int
788 pmatches(FILE *fp, int msgnum, long start, long stop)
789 {
790         if (!head)
791                 return 1;
792
793         if (!talked++ && pdebug)
794                 PRaction(head, 0);
795
796         return (*head->n_action) (args(head));
797 }
798
799
800 static void
801 PRaction(struct nexus *n, int level)
802 {
803         int i;
804
805         for (i = 0; i < level; i++)
806                 fprintf(stderr, "| ");
807
808         if (n->n_action == ORaction) {
809                 fprintf(stderr, "OR\n");
810                 PRaction(n->n_L_child, level + 1);
811                 PRaction(n->n_R_child, level + 1);
812                 return;
813         }
814         if (n->n_action == ANDaction) {
815                 fprintf(stderr, "AND\n");
816                 PRaction(n->n_L_child, level + 1);
817                 PRaction(n->n_R_child, level + 1);
818                 return;
819         }
820         if (n->n_action == NOTaction) {
821                 fprintf(stderr, "NOT\n");
822                 PRaction(n->n_L_child, level + 1);
823                 return;
824         }
825         if (n->n_action == GREPaction) {
826                 fprintf(stderr, "PATTERN(%s) %s\n",
827                                 n->n_header ? "header" : "body", n->n_patbuf);
828                 return;
829         }
830         if (n->n_action == TWSaction) {
831                 fprintf(stderr, "TEMPORAL(%s) %s: %s\n",
832                                 n->n_after ? "after" : "before", n->n_datef,
833                                 dasctime(&n->n_tws));
834                 return;
835         }
836         fprintf(stderr, "UNKNOWN(0x%x)\n",
837                          (unsigned int)(unsigned long) (*n->n_action));
838 }
839
840
841 static int
842 ORaction(params)
843 plist
844 {
845         if ((*n->n_L_child->n_action) (args(n->n_L_child)))
846                 return 1;
847         return (*n->n_R_child->n_action) (args(n->n_R_child));
848 }
849
850
851 static int
852 ANDaction(params)
853 plist
854 {
855         if (!(*n->n_L_child->n_action) (args(n->n_L_child)))
856                 return 0;
857         return (*n->n_R_child->n_action) (args(n->n_R_child));
858 }
859
860
861 static int
862 NOTaction(params)
863 plist
864 {
865         return (!(*n->n_L_child->n_action) (args(n->n_L_child)));
866 }
867
868
869 static int
870 gcompile(struct nexus *n, char *astr)
871 {
872         int c;
873         int cclcnt;
874         unsigned char *ep, *dp, *sp, *lastep = 0;
875
876         dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
877         sp = astr;
878         if (*sp == '^') {
879                 n->n_circf = 1;
880                 sp++;
881         }
882         else
883                 n->n_circf = 0;
884         for (;;) {
885                 if (ep >= dp)
886                         goto cerror;
887                 if ((c = *sp++) != '*')
888                         lastep = ep;
889                 switch (c) {
890                 case '\0':
891                         *ep++ = CEOF;
892                         return 1;
893
894                 case '.':
895                         *ep++ = CDOT;
896                         continue;
897
898                 case '*':
899                         if (lastep == 0)
900                                 goto defchar;
901                         *lastep |= STAR;
902                         continue;
903
904                 case '$':
905                         if (*sp != '\0')
906                                 goto defchar;
907                         *ep++ = CDOL;
908                         continue;
909
910                 case '[':
911                         *ep++ = CCL;
912                         *ep++ = 0;
913                         cclcnt = 0;
914                         if ((c = *sp++) == '^') {
915                                 c = *sp++;
916                                 ep[-2] = NCCL;
917                         }
918                         if (c == '-') {
919                                 *ep++ = c;
920                                 cclcnt++;
921                                 c = *sp++;
922                         }
923                         do {
924                                 if (c == '-' && *sp != '\0' && *sp != ']') {
925                                         for (c = ep[-1]+1; c < *sp; c++) {
926                                                         *ep++ = c;
927                                                         cclcnt++;
928                                                 if (c == '\0' || ep >= dp)
929                                                         goto cerror;
930                                         }
931                                 } else {
932                                         *ep++ = c;
933                                         cclcnt++;
934                                         if (c == '\0' || ep >= dp)
935                                                 goto cerror;
936                                 }
937                         } while ((c = *sp++) != ']');
938                         if (cclcnt > 255)
939                                 goto cerror;
940                         lastep[1] = cclcnt;
941                         continue;
942
943                         case '\\':
944                                 if ((c = *sp++) == '\0')
945                                 goto cerror;
946 defchar:
947                 default:
948                         *ep++ = CCHR;
949                         *ep++ = c;
950                 }
951         }
952
953 cerror: ;
954         return 0;
955 }
956
957
958 static int
959 GREPaction(params)
960 plist
961 {
962         int c, body, lf;
963         long pos = start;
964         char *p1, *p2, *ebp, *cbp;
965         char ibuf[BUFSIZ];
966
967         fseek(fp, start, SEEK_SET);
968         body = 0;
969         ebp = cbp = ibuf;
970         for (;;) {
971                 if (body && n->n_header)
972                         return 0;
973                 p1 = linebuf;
974                 p2 = cbp;
975                 lf = 0;
976                 for (;;) {
977                         if (p2 >= ebp) {
978                                 if (fgets(ibuf, sizeof ibuf, fp) == NULL
979                                                 || (stop && pos >= stop)) {
980                                         if (lf)
981                                                 break;
982                                         return 0;
983                                 }
984                                 pos += (long) strlen(ibuf);
985                                 p2 = ibuf;
986                                 ebp = ibuf + strlen(ibuf);
987                         }
988                         c = *p2++;
989                         if (lf && c != '\n') {
990                                 if (c != ' ' && c != '\t') {
991                                         --p2;
992                                         break;
993                                 }
994                                 else
995                                         lf = 0;
996                         }
997                         if (c == '\n') {
998                                 if (body)
999                                         break;
1000                                 else {
1001                                         if (lf) {
1002                                                 body++;
1003                                                 break;
1004                                         }
1005                                         lf++;
1006                                         c = ' ';
1007                                 }
1008                         }
1009                         if (c && p1 < &linebuf[LBSIZE - 1])
1010                                 *p1++ = c;
1011                 }
1012
1013                 *p1++ = 0;
1014                 cbp = p2;
1015                 p1 = linebuf;
1016                 p2 = n->n_expbuf;
1017
1018                 /*
1019                 ** Attempt to decode as a MIME header.  If it's the
1020                 ** last header, body will be 1 and lf will be at least 1.
1021                 */
1022                 if ((body == 0 || lf > 0) && decode_rfc2047(linebuf,
1023                                 decoded_linebuf, sizeof decoded_linebuf)) {
1024                         p1 = decoded_linebuf;
1025                 }
1026
1027                 if (n->n_circf) {
1028                         if (advance(p1, p2)) 
1029                                 return 1;
1030                         continue;
1031                 }
1032
1033                 if (*p2 == CCHR) {
1034                         c = p2[1];
1035                         do {
1036                                 if (*p1 == c || cc[(unsigned char)*p1] == c)
1037                                         if (advance(p1, p2))
1038                                                 return 1;
1039                         } while (*p1++);
1040                         continue;
1041                 }
1042
1043                 do {
1044                         if (advance(p1, p2))
1045                                 return 1;
1046                 } while (*p1++);
1047         }
1048 }
1049
1050
1051 static int
1052 advance(char *alp, char *aep)
1053 {
1054         unsigned char *lp, *ep, *curlp;
1055
1056         lp = (unsigned char *)alp;
1057         ep = (unsigned char *)aep;
1058         for (;;)
1059                 switch (*ep++) {
1060                 case CCHR:
1061                         if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
1062                                 continue;
1063                         return 0;
1064
1065                 case CDOT:
1066                         if (*lp++)
1067                                 continue;
1068                         return 0;
1069
1070                 case CDOL:
1071                         if (*lp == 0)
1072                                 continue;
1073                         return 0;
1074
1075                 case CEOF:
1076                         return 1;
1077
1078                 case CCL:
1079                         if (cclass(ep, *lp++, 1)) {
1080                                 ep += *ep + 1;
1081                                 continue;
1082                         }
1083                         return 0;
1084
1085                 case NCCL:
1086                         if (cclass(ep, *lp++, 0)) {
1087                                 ep += *ep + 1;
1088                                 continue;
1089                         }
1090                         return 0;
1091
1092                 case CDOT | STAR:
1093                         curlp = lp;
1094                         while (*lp++)
1095                                 continue;
1096                         goto star;
1097
1098                 case CCHR | STAR:
1099                         curlp = lp;
1100                         while (*lp++ == *ep || cc[lp[-1]] == *ep)
1101                                 continue;
1102                         ep++;
1103                         goto star;
1104
1105                 case CCL | STAR:
1106                 case NCCL | STAR:
1107                         curlp = lp;
1108                         while (cclass(ep, *lp++, ep[-1] == (CCL | STAR)))
1109                                 continue;
1110                         ep += *ep + 1;
1111                         goto star;
1112
1113 star:
1114                         do {
1115                                 lp--;
1116                                 if (advance(lp, ep))
1117                                         return (1);
1118                         } while (lp > curlp);
1119                         return 0;
1120
1121                 default:
1122                         admonish(NULL, "advance() botch -- you lose big");
1123                         return 0;
1124                 }
1125 }
1126
1127
1128 static int
1129 cclass(unsigned char *aset, int ac, int af)
1130 {
1131         unsigned int n;
1132         unsigned char c, *set;
1133
1134         set = aset;
1135         if ((c = ac) == 0)
1136                 return (0);
1137
1138         n = *set++;
1139         while (n--)
1140                 if (*set++ == c || set[-1] == cc[c])
1141                         return (af);
1142
1143         return (!af);
1144 }
1145
1146
1147 static int
1148 tcompile(char *ap, struct tws *tb, int isafter)
1149 {
1150         struct tws *tw;
1151
1152         if ((tw = tws_parse(ap, isafter)) == NULL)
1153                 return 0;
1154
1155         twscopy(tb, tw);
1156         return 1;
1157 }
1158
1159
1160 static struct tws *
1161 tws_parse(char *ap, int isafter)
1162 {
1163         char buffer[BUFSIZ];
1164         struct tws *tw, *ts;
1165
1166         if ((tw = tws_special(ap)) != NULL) {
1167                 tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
1168                 tw->tw_hour = isafter ? 23 : 0;
1169                 return tw;
1170         }
1171         if ((tw = dparsetime(ap)) != NULL)
1172                 return tw;
1173
1174         if ((ts = dlocaltimenow()) == NULL)
1175                 return NULL;
1176
1177         snprintf(buffer, sizeof(buffer), "%s %s", ap, dtwszone(ts));
1178         if ((tw = dparsetime(buffer)) != NULL)
1179                 return tw;
1180
1181         snprintf(buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
1182                         ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone(ts));
1183         if ((tw = dparsetime(buffer)) != NULL)
1184                 return tw;
1185
1186         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s",
1187                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
1188         if ((tw = dparsetime(buffer)) != NULL)
1189                 return tw;
1190
1191         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s %s",
1192                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
1193                         ap, dtwszone(ts));
1194         if ((tw = dparsetime(buffer)) != NULL)
1195                 return tw;
1196
1197         return NULL;
1198 }
1199
1200
1201 static struct tws *
1202 tws_special(char *ap)
1203 {
1204         int i;
1205         time_t clock;
1206         struct tws *tw;
1207
1208         time(&clock);
1209         if (!mh_strcasecmp(ap, "today"))
1210                 return dlocaltime(&clock);
1211         if (!mh_strcasecmp(ap, "yesterday")) {
1212                 clock -= (long) (60 * 60 * 24);
1213                 return dlocaltime(&clock);
1214         }
1215         if (!mh_strcasecmp(ap, "tomorrow")) {
1216                 clock += (long) (60 * 60 * 24);
1217                 return dlocaltime(&clock);
1218         }
1219
1220         for (i = 0; tw_ldotw[i]; i++)
1221                 if (!mh_strcasecmp(ap, tw_ldotw[i]))
1222                         break;
1223         if (tw_ldotw[i]) {
1224                 if ((tw = dlocaltime(&clock)) == NULL)
1225                         return NULL;
1226                 if ((i -= tw->tw_wday) > 0)
1227                         i -= 7;
1228         }
1229         else
1230                 if (*ap != '-')
1231                         return NULL;
1232                 else  /* -ddd days ago */
1233                         i = atoi(ap);  /* we should error check this */
1234
1235         clock += (long) ((60 * 60 * 24) * i);
1236         return dlocaltime(&clock);
1237 }
1238
1239
1240 static int
1241 TWSaction(params)
1242 plist
1243 {
1244         enum state state;
1245         struct field f = {{0}};
1246         char *bp;
1247         struct tws *tw;
1248
1249         fseek(fp, start, SEEK_SET);
1250         for (state = FLD2, bp = NULL;;) {
1251                 switch (state = m_getfld2(state, &f, fp)) {
1252                 case FLD2:
1253                         if (bp) {
1254                                 free(bp);
1255                                 bp = NULL;
1256                         }
1257                         bp = getcpy(f.value);
1258                         if (mh_strcasecmp(f.name, n->n_datef)==0) {
1259                                 break;
1260                         }
1261                         continue;
1262
1263                 case LENERR2:
1264                 case FMTERR2:
1265                 case IOERR2:
1266                         advise(NULL, "format error in message %d", msgnum);
1267                         /* FALL */
1268
1269                 case BODY2:
1270                 case FILEEOF2:
1271                         if (bp) {
1272                                 free(bp);
1273                         }
1274                         return 0;
1275
1276                 default:
1277                         adios(EX_SOFTWARE, NULL, "internal error -- you lose");
1278                 }
1279                 break;
1280         }
1281
1282         if ((tw = dparsetime(bp)) == NULL)
1283                 advise(NULL, "unable to parse %s field in message %d, matching...",
1284                                 n->n_datef, msgnum), state = 1;
1285         else
1286                 state = n->n_after ? (twsort(tw, &n->n_tws) > 0)
1287                         : (twsort(tw, &n->n_tws) < 0);
1288
1289         if (bp != NULL)
1290                 free(bp);
1291         return state;
1292 }