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