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