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