819d81a247399dacaea06a9f771cde48fc278cf2
[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                                 mp->numsel--;
278                         }
279                         if (fp)
280                                 fclose(fp);
281                 }
282         }
283
284         mp->lowsel = lo;
285         mp->hghsel = hi;
286
287         if (mp->numsel <= 0)
288                 adios(EX_DATAERR, NULL, "no messages match specification");
289
290         seqs[seqp] = NULL;
291
292         /*
293         ** Add the matching messages to sequences
294         */
295         for (seqp = 0; seqs[seqp]; seqp++)
296                 if (!seq_addsel(mp, seqs[seqp], publicsw, zerosw))
297                         exit(EX_USAGE);
298
299         /*
300         ** Print total matched if not printing each matched message number.
301         */
302         if (!listsw) {
303                 printf("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
304         }
305
306         context_replace(curfolder, folder);  /* update current folder */
307         seq_save(mp);  /* synchronize message sequences */
308         context_save();  /* save the context file */
309         folder_free(mp);  /* free folder/message structure */
310         listsw = 0; /* HACK */
311         return 0;
312 }
313
314
315 void
316 putzero_done()
317 {
318         if (listsw && !isatty(fileno(stdout)))
319                 printf("0\n");
320 }
321
322
323 static struct swit parswit[] = {
324 #define PRAND   0
325         { "and", 0 },
326 #define PROR    1
327         { "or", 0 },
328 #define PRNOT   2
329         { "not", 0 },
330 #define PRLBR   3
331         { "lbrace", 0 },
332 #define PRRBR   4
333         { "rbrace", 0 },
334 #define PRCC    5
335         { "cc  pattern", 0 },
336 #define PRDATE  6
337         { "date  pattern", 0 },
338 #define PRFROM  7
339         { "from  pattern", 0 },
340 #define PRSRCH  8
341         { "search  pattern", 0 },
342 #define PRSUBJ  9
343         { "subject  pattern", 0 },
344 #define PRTO   10
345         { "to  pattern", 0 },
346 #define PROTHR 11
347         { "-othercomponent  pattern", 15 },
348 #define PRAFTR 12
349         { "after date", 0 },
350 #define PRBEFR 13
351         { "before date", 0 },
352 #define PRDATF 14
353         { "datefield field", 5 },
354         { NULL, 0 }
355 };
356
357 /* DEFINITIONS FOR PATTERN MATCHING */
358
359 /*
360 ** We really should be using re_comp() and re_exec() here.  Unfortunately,
361 ** pick advertises that lowercase characters matches characters of both
362 ** cases.  Since re_exec() doesn't exhibit this behavior, we are stuck
363 ** with this version.  Furthermore, we need to be able to save and restore
364 ** the state of the pattern matcher in order to do things "efficiently".
365 **
366 ** The matching power of this algorithm isn't as powerful as the re_xxx()
367 ** routines (no \(xxx\) and \n constructs).  Such is life.
368 */
369
370 #define CCHR       2
371 #define CDOT       4
372 #define CCL        6
373 #define NCCL       8
374 #define CDOL      10
375 #define CEOF      11
376
377 #define STAR      01
378
379 #define LBSIZE  1024
380 #define ESIZE   1024
381
382
383 static char 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         if ((p = (struct nexus *) mh_xcalloc((size_t) 1, sizeof *p)) == NULL)
772                 adios(EX_OSERR, NULL, "unable to allocate component storage");
773
774         p->n_action = action;
775         return p;
776 }
777
778
779 #define args(a)  a, fp, msgnum, start, stop
780 #define params   args(n)
781 #define plist    \
782         struct nexus  *n; \
783         FILE *fp; \
784         int msgnum; \
785         long start, \
786         stop;
787
788 static int
789 pmatches(FILE *fp, int msgnum, long start, long stop)
790 {
791         if (!head)
792                 return 1;
793
794         if (!talked++ && pdebug)
795                 PRaction(head, 0);
796
797         return (*head->n_action) (args(head));
798 }
799
800
801 static void
802 PRaction(struct nexus *n, int level)
803 {
804         int i;
805
806         for (i = 0; i < level; i++)
807                 fprintf(stderr, "| ");
808
809         if (n->n_action == ORaction) {
810                 fprintf(stderr, "OR\n");
811                 PRaction(n->n_L_child, level + 1);
812                 PRaction(n->n_R_child, level + 1);
813                 return;
814         }
815         if (n->n_action == ANDaction) {
816                 fprintf(stderr, "AND\n");
817                 PRaction(n->n_L_child, level + 1);
818                 PRaction(n->n_R_child, level + 1);
819                 return;
820         }
821         if (n->n_action == NOTaction) {
822                 fprintf(stderr, "NOT\n");
823                 PRaction(n->n_L_child, level + 1);
824                 return;
825         }
826         if (n->n_action == GREPaction) {
827                 fprintf(stderr, "PATTERN(%s) %s\n",
828                                 n->n_header ? "header" : "body", n->n_patbuf);
829                 return;
830         }
831         if (n->n_action == TWSaction) {
832                 fprintf(stderr, "TEMPORAL(%s) %s: %s\n",
833                                 n->n_after ? "after" : "before", n->n_datef,
834                                 dasctime(&n->n_tws));
835                 return;
836         }
837         fprintf(stderr, "UNKNOWN(0x%x)\n",
838                          (unsigned int)(unsigned long) (*n->n_action));
839 }
840
841
842 static int
843 ORaction(params)
844 plist
845 {
846         if ((*n->n_L_child->n_action) (args(n->n_L_child)))
847                 return 1;
848         return (*n->n_R_child->n_action) (args(n->n_R_child));
849 }
850
851
852 static int
853 ANDaction(params)
854 plist
855 {
856         if (!(*n->n_L_child->n_action) (args(n->n_L_child)))
857                 return 0;
858         return (*n->n_R_child->n_action) (args(n->n_R_child));
859 }
860
861
862 static int
863 NOTaction(params)
864 plist
865 {
866         return (!(*n->n_L_child->n_action) (args(n->n_L_child)));
867 }
868
869
870 static int
871 gcompile(struct nexus *n, char *astr)
872 {
873         int c;
874         int cclcnt;
875         unsigned char *ep, *dp, *sp, *lastep = 0;
876
877         dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
878         sp = astr;
879         if (*sp == '^') {
880                 n->n_circf = 1;
881                 sp++;
882         }
883         else
884                 n->n_circf = 0;
885         for (;;) {
886                 if (ep >= dp)
887                         goto cerror;
888                 if ((c = *sp++) != '*')
889                         lastep = ep;
890                 switch (c) {
891                 case '\0':
892                         *ep++ = CEOF;
893                         return 1;
894
895                 case '.':
896                         *ep++ = CDOT;
897                         continue;
898
899                 case '*':
900                         if (lastep == 0)
901                                 goto defchar;
902                         *lastep |= STAR;
903                         continue;
904
905                 case '$':
906                         if (*sp != '\0')
907                                 goto defchar;
908                         *ep++ = CDOL;
909                         continue;
910
911                 case '[':
912                         *ep++ = CCL;
913                         *ep++ = 0;
914                         cclcnt = 0;
915                         if ((c = *sp++) == '^') {
916                                 c = *sp++;
917                                 ep[-2] = NCCL;
918                         }
919                         if (c == '-') {
920                                 *ep++ = c;
921                                 cclcnt++;
922                                 c = *sp++;
923                         }
924                         do {
925                                 if (c == '-' && *sp != '\0' && *sp != ']') {
926                                         for (c = ep[-1]+1; c < *sp; c++) {
927                                                         *ep++ = c;
928                                                         cclcnt++;
929                                                 if (c == '\0' || ep >= dp)
930                                                         goto cerror;
931                                         }
932                                 } else {
933                                         *ep++ = c;
934                                         cclcnt++;
935                                         if (c == '\0' || ep >= dp)
936                                                 goto cerror;
937                                 }
938                         } while ((c = *sp++) != ']');
939                         if (cclcnt > 255)
940                                 goto cerror;
941                         lastep[1] = cclcnt;
942                         continue;
943
944                         case '\\':
945                                 if ((c = *sp++) == '\0')
946                                 goto cerror;
947 defchar:
948                 default:
949                         *ep++ = CCHR;
950                         *ep++ = c;
951                 }
952         }
953
954 cerror: ;
955         return 0;
956 }
957
958
959 static int
960 GREPaction(params)
961 plist
962 {
963         int c, body, lf;
964         long pos = start;
965         char *p1, *p2, *ebp, *cbp;
966         char ibuf[BUFSIZ];
967
968         fseek(fp, start, SEEK_SET);
969         body = 0;
970         ebp = cbp = ibuf;
971         for (;;) {
972                 if (body && n->n_header)
973                         return 0;
974                 p1 = linebuf;
975                 p2 = cbp;
976                 lf = 0;
977                 for (;;) {
978                         if (p2 >= ebp) {
979                                 if (fgets(ibuf, sizeof ibuf, fp) == NULL
980                                                 || (stop && pos >= stop)) {
981                                         if (lf)
982                                                 break;
983                                         return 0;
984                                 }
985                                 pos += (long) strlen(ibuf);
986                                 p2 = ibuf;
987                                 ebp = ibuf + strlen(ibuf);
988                         }
989                         c = *p2++;
990                         if (lf && c != '\n') {
991                                 if (c != ' ' && c != '\t') {
992                                         --p2;
993                                         break;
994                                 }
995                                 else
996                                         lf = 0;
997                         }
998                         if (c == '\n') {
999                                 if (body)
1000                                         break;
1001                                 else {
1002                                         if (lf) {
1003                                                 body++;
1004                                                 break;
1005                                         }
1006                                         lf++;
1007                                         c = ' ';
1008                                 }
1009                         }
1010                         if (c && p1 < &linebuf[LBSIZE - 1])
1011                                 *p1++ = c;
1012                 }
1013
1014                 *p1++ = 0;
1015                 cbp = p2;
1016                 p1 = linebuf;
1017                 p2 = n->n_expbuf;
1018
1019                 if (n->n_circf) {
1020                         if (advance(p1, p2))
1021                                 return 1;
1022                         continue;
1023                 }
1024
1025                 if (*p2 == CCHR) {
1026                         c = p2[1];
1027                         do {
1028                                 if (*p1 == c || cc[(unsigned char)*p1] == c)
1029                                         if (advance(p1, p2))
1030                                                 return 1;
1031                         } while (*p1++);
1032                         continue;
1033                 }
1034
1035                 do {
1036                         if (advance(p1, p2))
1037                                 return 1;
1038                 } while (*p1++);
1039         }
1040 }
1041
1042
1043 static int
1044 advance(char *alp, char *aep)
1045 {
1046         unsigned char *lp, *ep, *curlp;
1047
1048         lp = (unsigned char *)alp;
1049         ep = (unsigned char *)aep;
1050         for (;;)
1051                 switch (*ep++) {
1052                 case CCHR:
1053                         if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
1054                                 continue;
1055                         return 0;
1056
1057                 case CDOT:
1058                         if (*lp++)
1059                                 continue;
1060                         return 0;
1061
1062                 case CDOL:
1063                         if (*lp == 0)
1064                                 continue;
1065                         return 0;
1066
1067                 case CEOF:
1068                         return 1;
1069
1070                 case CCL:
1071                         if (cclass(ep, *lp++, 1)) {
1072                                 ep += *ep + 1;
1073                                 continue;
1074                         }
1075                         return 0;
1076
1077                 case NCCL:
1078                         if (cclass(ep, *lp++, 0)) {
1079                                 ep += *ep + 1;
1080                                 continue;
1081                         }
1082                         return 0;
1083
1084                 case CDOT | STAR:
1085                         curlp = lp;
1086                         while (*lp++)
1087                                 continue;
1088                         goto star;
1089
1090                 case CCHR | STAR:
1091                         curlp = lp;
1092                         while (*lp++ == *ep || cc[lp[-1]] == *ep)
1093                                 continue;
1094                         ep++;
1095                         goto star;
1096
1097                 case CCL | STAR:
1098                 case NCCL | STAR:
1099                         curlp = lp;
1100                         while (cclass(ep, *lp++, ep[-1] == (CCL | STAR)))
1101                                 continue;
1102                         ep += *ep + 1;
1103                         goto star;
1104
1105 star:
1106                         do {
1107                                 lp--;
1108                                 if (advance(lp, ep))
1109                                         return (1);
1110                         } while (lp > curlp);
1111                         return 0;
1112
1113                 default:
1114                         admonish(NULL, "advance() botch -- you lose big");
1115                         return 0;
1116                 }
1117 }
1118
1119
1120 static int
1121 cclass(unsigned char *aset, int ac, int af)
1122 {
1123         unsigned int n;
1124         unsigned char c, *set;
1125
1126         set = aset;
1127         if ((c = ac) == 0)
1128                 return (0);
1129
1130         n = *set++;
1131         while (n--)
1132                 if (*set++ == c || set[-1] == cc[c])
1133                         return (af);
1134
1135         return (!af);
1136 }
1137
1138
1139 static int
1140 tcompile(char *ap, struct tws *tb, int isafter)
1141 {
1142         struct tws *tw;
1143
1144         if ((tw = tws_parse(ap, isafter)) == NULL)
1145                 return 0;
1146
1147         twscopy(tb, tw);
1148         return 1;
1149 }
1150
1151
1152 static struct tws *
1153 tws_parse(char *ap, int isafter)
1154 {
1155         char buffer[BUFSIZ];
1156         struct tws *tw, *ts;
1157
1158         if ((tw = tws_special(ap)) != NULL) {
1159                 tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
1160                 tw->tw_hour = isafter ? 23 : 0;
1161                 return tw;
1162         }
1163         if ((tw = dparsetime(ap)) != NULL)
1164                 return tw;
1165
1166         if ((ts = dlocaltimenow()) == NULL)
1167                 return NULL;
1168
1169         snprintf(buffer, sizeof(buffer), "%s %s", ap, dtwszone(ts));
1170         if ((tw = dparsetime(buffer)) != NULL)
1171                 return tw;
1172
1173         snprintf(buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
1174                         ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone(ts));
1175         if ((tw = dparsetime(buffer)) != NULL)
1176                 return tw;
1177
1178         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s",
1179                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
1180         if ((tw = dparsetime(buffer)) != NULL)
1181                 return tw;
1182
1183         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s %s",
1184                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
1185                         ap, dtwszone(ts));
1186         if ((tw = dparsetime(buffer)) != NULL)
1187                 return tw;
1188
1189         return NULL;
1190 }
1191
1192
1193 static struct tws *
1194 tws_special(char *ap)
1195 {
1196         int i;
1197         time_t clock;
1198         struct tws *tw;
1199
1200         time(&clock);
1201         if (!mh_strcasecmp(ap, "today"))
1202                 return dlocaltime(&clock);
1203         if (!mh_strcasecmp(ap, "yesterday")) {
1204                 clock -= (long) (60 * 60 * 24);
1205                 return dlocaltime(&clock);
1206         }
1207         if (!mh_strcasecmp(ap, "tomorrow")) {
1208                 clock += (long) (60 * 60 * 24);
1209                 return dlocaltime(&clock);
1210         }
1211
1212         for (i = 0; tw_ldotw[i]; i++)
1213                 if (!mh_strcasecmp(ap, tw_ldotw[i]))
1214                         break;
1215         if (tw_ldotw[i]) {
1216                 if ((tw = dlocaltime(&clock)) == NULL)
1217                         return NULL;
1218                 if ((i -= tw->tw_wday) > 0)
1219                         i -= 7;
1220         }
1221         else
1222                 if (*ap != '-')
1223                         return NULL;
1224                 else  /* -ddd days ago */
1225                         i = atoi(ap);  /* we should error check this */
1226
1227         clock += (long) ((60 * 60 * 24) * i);
1228         return dlocaltime(&clock);
1229 }
1230
1231
1232 static int
1233 TWSaction(params)
1234 plist
1235 {
1236         int state;
1237         char *bp;
1238         char buf[BUFSIZ], name[NAMESZ];
1239         struct tws *tw;
1240
1241         fseek(fp, start, SEEK_SET);
1242         for (state = FLD, bp = NULL;;) {
1243                 switch (state = m_getfld(state, name, buf, sizeof buf, fp)) {
1244                 case FLD:
1245                 case FLDEOF:
1246                 case FLDPLUS:
1247                         if (bp != NULL) {
1248                                 free(bp);
1249                                 bp = NULL;
1250                         }
1251                         bp = getcpy(buf);
1252                         while (state == FLDPLUS) {
1253                                 state = m_getfld(state, name, buf,
1254                                                 sizeof buf, fp);
1255                                 bp = add(buf, bp);
1256                         }
1257                         if (!mh_strcasecmp(name, n->n_datef))
1258                                 break;
1259                         if (state != FLDEOF)
1260                                 continue;
1261
1262                 case BODY:
1263                 case BODYEOF:
1264                 case FILEEOF:
1265                 case LENERR:
1266                 case FMTERR:
1267                         if (state == LENERR || state == FMTERR)
1268                                 advise(NULL, "format error in message %d", msgnum);
1269                         if (bp != NULL)
1270                                 free(bp);
1271                         return 0;
1272
1273                 default:
1274                         adios(EX_SOFTWARE, NULL, "internal error -- you lose");
1275                 }
1276                 break;
1277         }
1278
1279         if ((tw = dparsetime(bp)) == NULL)
1280                 advise(NULL, "unable to parse %s field in message %d, matching...",
1281                                 n->n_datef, msgnum), state = 1;
1282         else
1283                 state = n->n_after ? (twsort(tw, &n->n_tws) > 0)
1284                         : (twsort(tw, &n->n_tws) < 0);
1285
1286         if (bp != NULL)
1287                 free(bp);
1288         return state;
1289 }