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