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