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