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