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