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