4073919d61835e16f6b5313a15ca8073757f0759
[mmh] / uip / picksbr.c
1
2 /*
3  * picksbr.c -- routines to help pick along...
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #include <h/mh.h>
11 #include <h/tws.h>
12 #include <h/picksbr.h>
13 #include <h/utils.h>
14
15 #ifdef HAVE_SYS_TIME_H
16 # include <sys/time.h>
17 #endif
18 #include <time.h>
19
20 static struct swit parswit[] = {
21 #define PRAND                   0
22     { "and", 0 },
23 #define PROR                    1
24     { "or", 0 },
25 #define PRNOT                   2
26     { "not", 0 },
27 #define PRLBR                   3
28     { "lbrace", 0 },
29 #define PRRBR                   4
30     { "rbrace", 0 },
31 #define PRCC                    5
32     { "cc  pattern", 0 },
33 #define PRDATE                  6
34     { "date  pattern", 0 },
35 #define PRFROM                  7
36     { "from  pattern", 0 },
37 #define PRSRCH                  8
38     { "search  pattern", 0 },
39 #define PRSUBJ                  9
40     { "subject  pattern", 0 },
41 #define PRTO                   10
42     { "to  pattern", 0 },
43 #define PROTHR                 11
44     { "-othercomponent  pattern", 15 },
45 #define PRAFTR                 12
46     { "after date", 0 },
47 #define PRBEFR                 13
48     { "before date", 0 },
49 #define PRDATF                 14
50     { "datefield field", 5 },
51     { NULL, 0 }
52 };
53
54 /* DEFINITIONS FOR PATTERN MATCHING */
55
56 /*
57  * We really should be using re_comp() and re_exec() here.  Unfortunately,
58  * pick advertises that lowercase characters matches characters of both
59  * cases.  Since re_exec() doesn't exhibit this behavior, we are stuck
60  * with this version.  Furthermore, we need to be able to save and restore
61  * the state of the pattern matcher in order to do things "efficiently".
62  *
63  * The matching power of this algorithm isn't as powerful as the re_xxx()
64  * routines (no \(xxx\) and \n constructs).  Such is life.
65  */
66
67 #define CCHR    2
68 #define CDOT    4
69 #define CCL     6
70 #define NCCL    8
71 #define CDOL    10
72 #define CEOF    11
73
74 #define STAR    01
75
76 #define LBSIZE  1024
77 #define ESIZE   1024
78
79
80 static char linebuf[LBSIZE + 1];
81
82 /* the magic array for case-independence */
83 static unsigned char cc[] = {
84         0000,0001,0002,0003,0004,0005,0006,0007,
85         0010,0011,0012,0013,0014,0015,0016,0017,
86         0020,0021,0022,0023,0024,0025,0026,0027,
87         0030,0031,0032,0033,0034,0035,0036,0037,
88         0040,0041,0042,0043,0044,0045,0046,0047,
89         0050,0051,0052,0053,0054,0055,0056,0057,
90         0060,0061,0062,0063,0064,0065,0066,0067,
91         0070,0071,0072,0073,0074,0075,0076,0077,
92         0100,0141,0142,0143,0144,0145,0146,0147,
93         0150,0151,0152,0153,0154,0155,0156,0157,
94         0160,0161,0162,0163,0164,0165,0166,0167,
95         0170,0171,0172,0133,0134,0135,0136,0137,
96         0140,0141,0142,0143,0144,0145,0146,0147,
97         0150,0151,0152,0153,0154,0155,0156,0157,
98         0160,0161,0162,0163,0164,0165,0166,0167,
99         0170,0171,0172,0173,0174,0175,0176,0177,
100
101         0200,0201,0202,0203,0204,0205,0206,0207,
102         0210,0211,0212,0213,0214,0215,0216,0217,
103         0220,0221,0222,0223,0224,0225,0226,0227,
104         0230,0231,0232,0233,0234,0235,0236,0237,
105         0240,0241,0242,0243,0244,0245,0246,0247,
106         0250,0251,0252,0253,0254,0255,0256,0257,
107         0260,0261,0262,0263,0264,0265,0266,0267,
108         0270,0271,0272,0273,0274,0275,0276,0277,
109         0300,0301,0302,0303,0304,0305,0306,0307,
110         0310,0311,0312,0313,0314,0315,0316,0317,
111         0320,0321,0322,0323,0324,0325,0326,0327,
112         0330,0331,0332,0333,0334,0335,0336,0337,
113         0340,0341,0342,0343,0344,0345,0346,0347,
114         0350,0351,0352,0353,0354,0355,0356,0357,
115         0360,0361,0362,0363,0364,0365,0366,0367,
116         0370,0371,0372,0373,0374,0375,0376,0377,
117 };
118
119 /*
120  * DEFINITIONS FOR NEXUS
121  */
122
123 #define nxtarg()        (*argp ? *argp++ : NULL)
124 #define prvarg()        argp--
125
126 #define padvise         if (!talked++) advise
127
128 struct nexus {
129     int (*n_action)();
130
131     union {
132         /* for {OR,AND,NOT}action */
133         struct {
134             struct nexus *un_L_child;
135             struct nexus *un_R_child;
136         } st1;
137
138         /* for GREPaction */
139         struct {
140             int   un_header;
141             int   un_circf;
142             char  un_expbuf[ESIZE];
143             char *un_patbuf;
144         } st2;
145
146         /* for TWSaction */
147         struct {
148             char *un_datef;
149             int   un_after;
150             struct tws un_tws;
151         } st3;
152     } un;
153 };
154
155 #define n_L_child un.st1.un_L_child
156 #define n_R_child un.st1.un_R_child
157
158 #define n_header un.st2.un_header
159 #define n_circf  un.st2.un_circf
160 #define n_expbuf un.st2.un_expbuf
161 #define n_patbuf un.st2.un_patbuf
162
163 #define n_datef  un.st3.un_datef
164 #define n_after  un.st3.un_after
165 #define n_tws    un.st3.un_tws
166
167 static int talked;
168 static int pdebug = 0;
169
170 static char *datesw;
171 static char **argp;
172
173 static struct nexus *head;
174
175 /*
176  * prototypes for date routines
177  */
178 static struct tws *tws_parse(char *, int);
179 static struct tws *tws_special(char *);
180
181 /*
182  * static prototypes
183  */
184 static void PRaction(struct nexus *, int);
185 static int gcompile(struct nexus *, char *);
186 static int advance(char *, char *);
187 static int cclass(unsigned char *, int, int);
188 static int tcompile(char *, struct tws *, int);
189
190 static struct nexus *parse(void);
191 static struct nexus *nexp1(void);
192 static struct nexus *nexp2(void);
193 static struct nexus *nexp3(void);
194 static struct nexus *newnexus(int (*)());
195
196 static int ORaction();
197 static int ANDaction();
198 static int NOTaction();
199 static int GREPaction();
200 static int TWSaction();
201
202
203 int
204 pcompile (char **vec, char *date)
205 {
206     register char *cp;
207
208     if ((cp = getenv ("MHPDEBUG")) && *cp)
209         pdebug++;
210
211     argp = vec;
212     if ((datesw = date) == NULL)
213         datesw = "date";
214     talked = 0;
215
216     if ((head = parse ()) == NULL)
217         return (talked ? 0 : 1);
218
219     if (*argp) {
220         padvise (NULL, "%s unexpected", *argp);
221         return 0;
222     }
223
224     return 1;
225 }
226
227
228 static struct nexus *
229 parse (void)
230 {
231     register char  *cp;
232     register struct nexus *n, *o;
233
234     if ((n = nexp1 ()) == NULL || (cp = nxtarg ()) == NULL)
235         return n;
236
237     if (*cp != '-') {
238         padvise (NULL, "%s unexpected", cp);
239         return NULL;
240     }
241
242     if (*++cp == '-')
243         goto header;
244     switch (smatch (cp, parswit)) {
245         case AMBIGSW: 
246             ambigsw (cp, parswit);
247             talked++;
248             return NULL;
249         case UNKWNSW: 
250             fprintf (stderr, "-%s unknown\n", cp);
251             talked++;
252             return NULL;
253
254         case PROR: 
255             o = newnexus (ORaction);
256             o->n_L_child = n;
257             if ((o->n_R_child = parse ()))
258                 return o;
259             padvise (NULL, "missing disjunctive");
260             return NULL;
261
262 header: ;
263         default: 
264             prvarg ();
265             return n;
266     }
267 }
268
269 static struct nexus *
270 nexp1 (void)
271 {
272     register char *cp;
273     register struct nexus *n, *o;
274
275     if ((n = nexp2 ()) == NULL || (cp = nxtarg ()) == NULL)
276         return n;
277
278     if (*cp != '-') {
279         padvise (NULL, "%s unexpected", cp);
280         return NULL;
281     }
282
283     if (*++cp == '-')
284         goto header;
285     switch (smatch (cp, parswit)) {
286         case AMBIGSW: 
287             ambigsw (cp, parswit);
288             talked++;
289             return NULL;
290         case UNKWNSW: 
291             fprintf (stderr, "-%s unknown\n", cp);
292             talked++;
293             return NULL;
294
295         case PRAND: 
296             o = newnexus (ANDaction);
297             o->n_L_child = n;
298             if ((o->n_R_child = nexp1 ()))
299                 return o;
300             padvise (NULL, "missing conjunctive");
301             return NULL;
302
303 header: ;
304         default: 
305             prvarg ();
306             return n;
307     }
308 }
309
310
311 static struct nexus *
312 nexp2 (void)
313 {
314     register char *cp;
315     register struct nexus *n;
316
317     if ((cp = nxtarg ()) == NULL)
318         return NULL;
319
320     if (*cp != '-') {
321         prvarg ();
322         return nexp3 ();
323     }
324
325     if (*++cp == '-')
326         goto header;
327     switch (smatch (cp, parswit)) {
328         case AMBIGSW: 
329             ambigsw (cp, parswit);
330             talked++;
331             return NULL;
332         case UNKWNSW: 
333             fprintf (stderr, "-%s unknown\n", cp);
334             talked++;
335             return NULL;
336
337         case PRNOT: 
338             n = newnexus (NOTaction);
339             if ((n->n_L_child = nexp3 ()))
340                 return n;
341             padvise (NULL, "missing negation");
342             return NULL;
343
344 header: ;
345         default: 
346             prvarg ();
347             return nexp3 ();
348     }
349 }
350
351 static struct nexus *
352 nexp3 (void)
353 {
354     int i;
355     register char *cp, *dp;
356     char buffer[BUFSIZ], temp[64];
357     register struct nexus *n;
358
359     if ((cp = nxtarg ()) == NULL)
360         return NULL;
361
362     if (*cp != '-') {
363         padvise (NULL, "%s unexpected", cp);
364         return NULL;
365     }
366
367     if (*++cp == '-') {
368         dp = ++cp;
369         goto header;
370     }
371     switch (i = smatch (cp, parswit)) {
372         case AMBIGSW: 
373             ambigsw (cp, parswit);
374             talked++;
375             return NULL;
376         case UNKWNSW: 
377             fprintf (stderr, "-%s unknown\n", cp);
378             talked++;
379             return NULL;
380
381         case PRLBR: 
382             if ((n = parse ()) == NULL) {
383                 padvise (NULL, "missing group");
384                 return NULL;
385             }
386             if ((cp = nxtarg ()) == NULL) {
387                 padvise (NULL, "missing -rbrace");
388                 return NULL;
389             }
390             if (*cp++ == '-' && smatch (cp, parswit) == PRRBR)
391                 return n;
392             padvise (NULL, "%s unexpected", --cp);
393             return NULL;
394
395         default: 
396             prvarg ();
397             return NULL;
398
399         case PRCC: 
400         case PRDATE: 
401         case PRFROM: 
402         case PRTO: 
403         case PRSUBJ: 
404             strncpy(temp, parswit[i].sw, sizeof(temp));
405             temp[sizeof(temp) - 1] = '\0';
406             dp = *brkstring (temp, " ", NULL);
407     header: ;
408             if (!(cp = nxtarg ())) {/* allow -xyz arguments */
409                 padvise (NULL, "missing argument to %s", argp[-2]);
410                 return NULL;
411             }
412             n = newnexus (GREPaction);
413             n->n_header = 1;
414             snprintf (buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp);
415             dp = buffer;
416             goto pattern;
417
418         case PRSRCH: 
419             n = newnexus (GREPaction);
420             n->n_header = 0;
421             if (!(cp = nxtarg ())) {/* allow -xyz arguments */
422                 padvise (NULL, "missing argument to %s", argp[-2]);
423                 return NULL;
424             }
425             dp = cp;
426     pattern: ;
427             if (!gcompile (n, dp)) {
428                 padvise (NULL, "pattern error in %s %s", argp[-2], cp);
429                 return NULL;
430             }
431             n->n_patbuf = getcpy (dp);
432             return n;
433
434         case PROTHR: 
435             padvise (NULL, "internal error!");
436             return NULL;
437
438         case PRDATF: 
439             if (!(datesw = nxtarg ()) || *datesw == '-') {
440                 padvise (NULL, "missing argument to %s", argp[-2]);
441                 return NULL;
442             }
443             return nexp3 ();
444
445         case PRAFTR: 
446         case PRBEFR: 
447             if (!(cp = nxtarg ())) {/* allow -xyz arguments */
448                 padvise (NULL, "missing argument to %s", argp[-2]);
449                 return NULL;
450             }
451             n = newnexus (TWSaction);
452             n->n_datef = datesw;
453             if (!tcompile (cp, &n->n_tws, n->n_after = i == PRAFTR)) {
454                 padvise (NULL, "unable to parse %s %s", argp[-2], cp);
455                 return NULL;
456             }
457             return n;
458     }
459 }
460
461
462 static struct nexus *
463 newnexus (int (*action)())
464 {
465     register struct nexus *p;
466
467     if ((p = (struct nexus *) calloc ((size_t) 1, sizeof *p)) == NULL)
468         adios (NULL, "unable to allocate component storage");
469
470     p->n_action = action;
471     return p;
472 }
473
474
475 #define args(a) a, fp, msgnum, start, stop
476 #define params  args (n)
477 #define plist   \
478             register struct nexus  *n; \
479             register FILE *fp; \
480             int msgnum; \
481             long    start, \
482                     stop;
483
484 int
485 pmatches (FILE *fp, int msgnum, long start, long stop)
486 {
487     if (!head)
488         return 1;
489
490     if (!talked++ && pdebug)
491         PRaction (head, 0);
492
493     return (*head->n_action) (args (head));
494 }
495
496
497 static void
498 PRaction (struct nexus *n, int level)
499 {
500     register int i;
501
502     for (i = 0; i < level; i++)
503         fprintf (stderr, "| ");
504
505     if (n->n_action == ORaction) {
506         fprintf (stderr, "OR\n");
507         PRaction (n->n_L_child, level + 1);
508         PRaction (n->n_R_child, level + 1);
509         return;
510     }
511     if (n->n_action == ANDaction) {
512         fprintf (stderr, "AND\n");
513         PRaction (n->n_L_child, level + 1);
514         PRaction (n->n_R_child, level + 1);
515         return;
516     }
517     if (n->n_action == NOTaction) {
518         fprintf (stderr, "NOT\n");
519         PRaction (n->n_L_child, level + 1);
520         return;
521     }
522     if (n->n_action == GREPaction) {
523         fprintf (stderr, "PATTERN(%s) %s\n",
524                 n->n_header ? "header" : "body", n->n_patbuf);
525         return;
526     }
527     if (n->n_action == TWSaction) {
528         fprintf (stderr, "TEMPORAL(%s) %s: %s\n",
529                 n->n_after ? "after" : "before", n->n_datef,
530                 dasctime (&n->n_tws, TW_NULL));
531         return;
532     }
533     fprintf (stderr, "UNKNOWN(0x%x)\n",
534              (unsigned int)(unsigned long) (*n->n_action));
535 }
536
537
538 static int
539 ORaction (params)
540 plist
541 {
542     if ((*n->n_L_child->n_action) (args (n->n_L_child)))
543         return 1;
544     return (*n->n_R_child->n_action) (args (n->n_R_child));
545 }
546
547
548 static int
549 ANDaction (params)
550 plist
551 {
552     if (!(*n->n_L_child->n_action) (args (n->n_L_child)))
553         return 0;
554     return (*n->n_R_child->n_action) (args (n->n_R_child));
555 }
556
557
558 static int
559 NOTaction (params)
560 plist
561 {
562     return (!(*n->n_L_child->n_action) (args (n->n_L_child)));
563 }
564
565
566 static int
567 gcompile (struct nexus *n, char *astr)
568 {
569     register int c;
570     int cclcnt;
571     register unsigned char *ep, *dp, *sp, *lastep = 0;
572
573     dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
574     sp = astr;
575     if (*sp == '^') {
576         n->n_circf = 1;
577         sp++;
578     }
579     else
580         n->n_circf = 0;
581     for (;;) {
582         if (ep >= dp)
583             goto cerror;
584         if ((c = *sp++) != '*')
585             lastep = ep;
586         switch (c) {
587             case '\0': 
588                 *ep++ = CEOF;
589                 return 1;
590
591             case '.': 
592                 *ep++ = CDOT;
593                 continue;
594
595             case '*': 
596                 if (lastep == 0)
597                     goto defchar;
598                 *lastep |= STAR;
599                 continue;
600
601             case '$': 
602                 if (*sp != '\0')
603                     goto defchar;
604                 *ep++ = CDOL;
605                 continue;
606
607             case '[': 
608                 *ep++ = CCL;
609                 *ep++ = 0;
610                 cclcnt = 0;
611                 if ((c = *sp++) == '^') {
612                     c = *sp++;
613                     ep[-2] = NCCL;
614                 }
615                 if (c == '-') {
616                     *ep++ = c;
617                     cclcnt++;
618                     c = *sp++;
619                 }
620                 do {
621                     if (c == '-' && *sp != '\0' && *sp != ']') {
622                         for (c = ep[-1]+1; c < *sp; c++) {
623                             *ep++ = c;
624                             cclcnt++;
625                             if (c == '\0' || ep >= dp)
626                                 goto cerror;
627                         }
628                     } else {
629                         *ep++ = c;
630                         cclcnt++;
631                         if (c == '\0' || ep >= dp)
632                             goto cerror;
633                     }
634                 } while ((c = *sp++) != ']');
635                 if (cclcnt > 255)
636                     goto cerror;
637                 lastep[1] = cclcnt;
638                 continue;
639
640             case '\\': 
641                 if ((c = *sp++) == '\0')
642                     goto cerror;
643         defchar: 
644             default: 
645                 *ep++ = CCHR;
646                 *ep++ = c;
647         }
648     }
649
650 cerror: ;
651     return 0;
652 }
653
654
655 static int
656 GREPaction (params)
657 plist
658 {
659     int c, body, lf;
660     long pos = start;
661     register char *p1, *p2, *ebp, *cbp;
662     char ibuf[BUFSIZ];
663     NMH_UNUSED (msgnum);
664
665     fseek (fp, start, SEEK_SET);
666     body = 0;
667     ebp = cbp = ibuf;
668     for (;;) {
669         if (body && n->n_header)
670             return 0;
671         p1 = linebuf;
672         p2 = cbp;
673         lf = 0;
674         for (;;) {
675             if (p2 >= ebp) {
676                 if (fgets (ibuf, sizeof ibuf, fp) == NULL
677                         || (stop && pos >= stop)) {
678                     if (lf)
679                         break;
680                     return 0;
681                 }
682                 pos += (long) strlen (ibuf);
683                 p2 = ibuf;
684                 ebp = ibuf + strlen (ibuf);
685             }
686             c = *p2++;
687             if (lf && c != '\n') {
688                 if (c != ' ' && c != '\t') {
689                     --p2;
690                     break;
691                 }
692                 else
693                     lf = 0;
694             }
695             if (c == '\n') {
696                 if (body)
697                     break;
698                 else {
699                     if (lf) {
700                         body++;
701                         break;
702                     }
703                     lf++;
704                     c = ' ';
705                 }
706             }
707             if (c && p1 < &linebuf[LBSIZE - 1])
708                 *p1++ = c;
709         }
710
711         *p1++ = 0;
712         cbp = p2;
713         p1 = linebuf;
714         p2 = n->n_expbuf;
715
716         if (n->n_circf) {
717             if (advance (p1, p2))
718                 return 1;
719             continue;
720         }
721
722         if (*p2 == CCHR) {
723             c = p2[1];
724             do {
725                 if (*p1 == c || cc[(unsigned char)*p1] == c)
726                     if (advance (p1, p2))
727                         return 1;
728             } while (*p1++);
729             continue;
730         }
731
732         do {
733             if (advance (p1, p2))
734                 return 1;
735         } while (*p1++);
736     }
737 }
738
739
740 static int
741 advance (char *alp, char *aep)
742 {
743     register unsigned char *lp, *ep, *curlp;
744
745     lp = (unsigned char *)alp;
746     ep = (unsigned char *)aep;
747     for (;;)
748         switch (*ep++) {
749             case CCHR: 
750                 if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
751                     continue;
752                 return 0;
753
754             case CDOT: 
755                 if (*lp++)
756                     continue;
757                 return 0;
758
759             case CDOL: 
760                 if (*lp == 0)
761                     continue;
762                 return 0;
763
764             case CEOF: 
765                 return 1;
766
767             case CCL: 
768                 if (cclass (ep, *lp++, 1)) {
769                     ep += *ep + 1;
770                     continue;
771                 }
772                 return 0;
773
774             case NCCL: 
775                 if (cclass (ep, *lp++, 0)) {
776                     ep += *ep + 1;
777                     continue;
778                 }
779                 return 0;
780
781             case CDOT | STAR: 
782                 curlp = lp;
783                 while (*lp++)
784                     continue;
785                 goto star;
786
787             case CCHR | STAR: 
788                 curlp = lp;
789                 while (*lp++ == *ep || cc[lp[-1]] == *ep)
790                     continue;
791                 ep++;
792                 goto star;
793
794             case CCL | STAR: 
795             case NCCL | STAR: 
796                 curlp = lp;
797                 while (cclass (ep, *lp++, ep[-1] == (CCL | STAR)))
798                     continue;
799                 ep += *ep + 1;
800                 goto star;
801
802         star: 
803                 do {
804                     lp--;
805                     if (advance (lp, ep))
806                         return (1);
807                 } while (lp > curlp);
808                 return 0;
809
810             default: 
811                 admonish (NULL, "advance() botch -- you lose big");
812                 return 0;
813         }
814 }
815
816
817 static int
818 cclass (unsigned char *aset, int ac, int af)
819 {
820     register unsigned int    n;
821     register unsigned char   c, *set;
822
823     set = aset;
824     if ((c = ac) == 0)
825         return (0);
826
827     n = *set++;
828     while (n--)
829         if (*set++ == c || set[-1] == cc[c])
830             return (af);
831
832     return (!af);
833 }
834
835
836 static int
837 tcompile (char *ap, struct tws *tb, int isafter)
838 {
839     register struct tws *tw;
840
841     if ((tw = tws_parse (ap, isafter)) == NULL)
842         return 0;
843
844     twscopy (tb, tw);
845     return 1;
846 }
847
848
849 static struct tws *
850 tws_parse (char *ap, int isafter)
851 {
852     char buffer[BUFSIZ];
853     register struct tws *tw, *ts;
854
855     if ((tw = tws_special (ap)) != NULL) {
856         tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
857         tw->tw_hour = isafter ? 23 : 0;
858         return tw;
859     }
860     if ((tw = dparsetime (ap)) != NULL)
861         return tw;
862
863     if ((ts = dlocaltimenow ()) == NULL)
864         return NULL;
865
866     snprintf (buffer, sizeof(buffer), "%s %s", ap, dtwszone (ts));
867     if ((tw = dparsetime (buffer)) != NULL)
868         return tw;
869
870     snprintf (buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
871             ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone (ts));
872     if ((tw = dparsetime (buffer)) != NULL)
873         return tw;
874
875     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s",
876             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
877     if ((tw = dparsetime (buffer)) != NULL)
878         return tw;
879
880     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s %s",
881             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
882             ap, dtwszone (ts));
883     if ((tw = dparsetime (buffer)) != NULL)
884         return tw;
885
886     return NULL;
887 }
888
889
890 static struct tws *
891 tws_special (char *ap)
892 {
893     int i;
894     time_t clock;
895     register struct tws *tw;
896
897     time (&clock);
898     if (!mh_strcasecmp (ap, "today"))
899         return dlocaltime (&clock);
900     if (!mh_strcasecmp (ap, "yesterday")) {
901         clock -= (long) (60 * 60 * 24);
902         return dlocaltime (&clock);
903     }
904     if (!mh_strcasecmp (ap, "tomorrow")) {
905         clock += (long) (60 * 60 * 24);
906         return dlocaltime (&clock);
907     }
908
909     for (i = 0; tw_ldotw[i]; i++)
910         if (!mh_strcasecmp (ap, tw_ldotw[i]))
911             break;
912     if (tw_ldotw[i]) {
913         if ((tw = dlocaltime (&clock)) == NULL)
914             return NULL;
915         if ((i -= tw->tw_wday) > 0)
916             i -= 7;
917     }
918     else
919         if (*ap != '-')
920             return NULL;
921         else                    /* -ddd days ago */
922             i = atoi (ap);      /* we should error check this */
923
924     clock += (long) ((60 * 60 * 24) * i);
925     return dlocaltime (&clock);
926 }
927
928
929 static int
930 TWSaction (params)
931 plist
932 {
933     int state;
934     register char *bp;
935     char buf[BUFSIZ], name[NAMESZ];
936     register struct tws *tw;
937     NMH_UNUSED (stop);
938
939     fseek (fp, start, SEEK_SET);
940     for (state = FLD, bp = NULL;;) {
941         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
942             case FLD: 
943             case FLDEOF: 
944             case FLDPLUS: 
945                 if (bp != NULL)
946                     free (bp), bp = NULL;
947                 bp = add (buf, NULL);
948                 while (state == FLDPLUS) {
949                     state = m_getfld (state, name, buf, sizeof buf, fp);
950                     bp = add (buf, bp);
951                 }
952                 if (!mh_strcasecmp (name, n->n_datef))
953                     break;
954                 if (state != FLDEOF)
955                     continue;
956
957             case BODY: 
958             case BODYEOF: 
959             case FILEEOF: 
960             case LENERR: 
961             case FMTERR: 
962                 if (state == LENERR || state == FMTERR)
963                     advise (NULL, "format error in message %d", msgnum);
964                 if (bp != NULL)
965                     free (bp);
966                 return 0;
967
968             default: 
969                 adios (NULL, "internal error -- you lose");
970         }
971         break;
972     }
973
974     if ((tw = dparsetime (bp)) == NULL)
975         advise (NULL, "unable to parse %s field in message %d, matching...",
976                 n->n_datef, msgnum), state = 1;
977     else
978         state = n->n_after ? (twsort (tw, &n->n_tws) > 0)
979             : (twsort (tw, &n->n_tws) < 0);
980
981     if (bp != NULL)
982         free (bp);
983     return state;
984 }