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