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