Fixed pick(1) to properly unfold multiple-line header fields
[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                     /* Unfold by skipping the newline. */
705                     c = 0;
706                 }
707             }
708             if (c && p1 < &linebuf[LBSIZE - 1])
709                 *p1++ = c;
710         }
711
712         *p1++ = 0;
713         cbp = p2;
714         p1 = linebuf;
715         p2 = n->n_expbuf;
716
717         if (n->n_circf) {
718             if (advance (p1, p2))
719                 return 1;
720             continue;
721         }
722
723         if (*p2 == CCHR) {
724             c = p2[1];
725             do {
726                 if (*p1 == c || cc[(unsigned char)*p1] == c)
727                     if (advance (p1, p2))
728                         return 1;
729             } while (*p1++);
730             continue;
731         }
732
733         do {
734             if (advance (p1, p2))
735                 return 1;
736         } while (*p1++);
737     }
738 }
739
740
741 static int
742 advance (char *alp, char *aep)
743 {
744     register unsigned char *lp, *ep, *curlp;
745
746     lp = (unsigned char *)alp;
747     ep = (unsigned char *)aep;
748     for (;;)
749         switch (*ep++) {
750             case CCHR: 
751                 if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
752                     continue;
753                 return 0;
754
755             case CDOT: 
756                 if (*lp++)
757                     continue;
758                 return 0;
759
760             case CDOL: 
761                 if (*lp == 0)
762                     continue;
763                 return 0;
764
765             case CEOF: 
766                 return 1;
767
768             case CCL: 
769                 if (cclass (ep, *lp++, 1)) {
770                     ep += *ep + 1;
771                     continue;
772                 }
773                 return 0;
774
775             case NCCL: 
776                 if (cclass (ep, *lp++, 0)) {
777                     ep += *ep + 1;
778                     continue;
779                 }
780                 return 0;
781
782             case CDOT | STAR: 
783                 curlp = lp;
784                 while (*lp++)
785                     continue;
786                 goto star;
787
788             case CCHR | STAR: 
789                 curlp = lp;
790                 while (*lp++ == *ep || cc[lp[-1]] == *ep)
791                     continue;
792                 ep++;
793                 goto star;
794
795             case CCL | STAR: 
796             case NCCL | STAR: 
797                 curlp = lp;
798                 while (cclass (ep, *lp++, ep[-1] == (CCL | STAR)))
799                     continue;
800                 ep += *ep + 1;
801                 goto star;
802
803         star: 
804                 do {
805                     lp--;
806                     if (advance (lp, ep))
807                         return (1);
808                 } while (lp > curlp);
809                 return 0;
810
811             default: 
812                 admonish (NULL, "advance() botch -- you lose big");
813                 return 0;
814         }
815 }
816
817
818 static int
819 cclass (unsigned char *aset, int ac, int af)
820 {
821     register unsigned int    n;
822     register unsigned char   c, *set;
823
824     set = aset;
825     if ((c = ac) == 0)
826         return (0);
827
828     n = *set++;
829     while (n--)
830         if (*set++ == c || set[-1] == cc[c])
831             return (af);
832
833     return (!af);
834 }
835
836
837 static int
838 tcompile (char *ap, struct tws *tb, int isafter)
839 {
840     register struct tws *tw;
841
842     if ((tw = tws_parse (ap, isafter)) == NULL)
843         return 0;
844
845     twscopy (tb, tw);
846     return 1;
847 }
848
849
850 static struct tws *
851 tws_parse (char *ap, int isafter)
852 {
853     char buffer[BUFSIZ];
854     register struct tws *tw, *ts;
855
856     if ((tw = tws_special (ap)) != NULL) {
857         tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
858         tw->tw_hour = isafter ? 23 : 0;
859         return tw;
860     }
861     if ((tw = dparsetime (ap)) != NULL)
862         return tw;
863
864     if ((ts = dlocaltimenow ()) == NULL)
865         return NULL;
866
867     snprintf (buffer, sizeof(buffer), "%s %s", ap, dtwszone (ts));
868     if ((tw = dparsetime (buffer)) != NULL)
869         return tw;
870
871     snprintf (buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
872             ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone (ts));
873     if ((tw = dparsetime (buffer)) != NULL)
874         return tw;
875
876     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s",
877             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
878     if ((tw = dparsetime (buffer)) != NULL)
879         return tw;
880
881     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s %s",
882             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
883             ap, dtwszone (ts));
884     if ((tw = dparsetime (buffer)) != NULL)
885         return tw;
886
887     return NULL;
888 }
889
890
891 static struct tws *
892 tws_special (char *ap)
893 {
894     int i;
895     time_t clock;
896     register struct tws *tw;
897
898     time (&clock);
899     if (!mh_strcasecmp (ap, "today"))
900         return dlocaltime (&clock);
901     if (!mh_strcasecmp (ap, "yesterday")) {
902         clock -= (long) (60 * 60 * 24);
903         return dlocaltime (&clock);
904     }
905     if (!mh_strcasecmp (ap, "tomorrow")) {
906         clock += (long) (60 * 60 * 24);
907         return dlocaltime (&clock);
908     }
909
910     for (i = 0; tw_ldotw[i]; i++)
911         if (!mh_strcasecmp (ap, tw_ldotw[i]))
912             break;
913     if (tw_ldotw[i]) {
914         if ((tw = dlocaltime (&clock)) == NULL)
915             return NULL;
916         if ((i -= tw->tw_wday) > 0)
917             i -= 7;
918     }
919     else
920         if (*ap != '-')
921             return NULL;
922         else                    /* -ddd days ago */
923             i = atoi (ap);      /* we should error check this */
924
925     clock += (long) ((60 * 60 * 24) * i);
926     return dlocaltime (&clock);
927 }
928
929
930 static int
931 TWSaction (params)
932 plist
933 {
934     int state;
935     register char *bp;
936     char buf[BUFSIZ], name[NAMESZ];
937     register struct tws *tw;
938     NMH_UNUSED (stop);
939
940     fseek (fp, start, SEEK_SET);
941     for (state = FLD, bp = NULL;;) {
942         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
943             case FLD: 
944             case FLDEOF: 
945             case FLDPLUS: 
946                 if (bp != NULL)
947                     free (bp), bp = NULL;
948                 bp = add (buf, NULL);
949                 while (state == FLDPLUS) {
950                     state = m_getfld (state, name, buf, sizeof buf, fp);
951                     bp = add (buf, bp);
952                 }
953                 if (!mh_strcasecmp (name, n->n_datef))
954                     break;
955                 if (state != FLDEOF)
956                     continue;
957
958             case BODY: 
959             case BODYEOF: 
960             case FILEEOF: 
961             case LENERR: 
962             case FMTERR: 
963                 if (state == LENERR || state == FMTERR)
964                     advise (NULL, "format error in message %d", msgnum);
965                 if (bp != NULL)
966                     free (bp);
967                 return 0;
968
969             default: 
970                 adios (NULL, "internal error -- you lose");
971         }
972         break;
973     }
974
975     if ((tw = dparsetime (bp)) == NULL)
976         advise (NULL, "unable to parse %s field in message %d, matching...",
977                 n->n_datef, msgnum), state = 1;
978     else
979         state = n->n_after ? (twsort (tw, &n->n_tws) > 0)
980             : (twsort (tw, &n->n_tws) < 0);
981
982     if (bp != NULL)
983         free (bp);
984     return state;
985 }