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