* uip/new.c: cast folder_len to int to avoid warning on
[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", (unsigned int) (*n->n_action));
542 }
543
544
545 static int
546 ORaction (params)
547 plist
548 {
549     if ((*n->n_L_child->n_action) (args (n->n_L_child)))
550         return 1;
551     return (*n->n_R_child->n_action) (args (n->n_R_child));
552 }
553
554
555 static int
556 ANDaction (params)
557 plist
558 {
559     if (!(*n->n_L_child->n_action) (args (n->n_L_child)))
560         return 0;
561     return (*n->n_R_child->n_action) (args (n->n_R_child));
562 }
563
564
565 static int
566 NOTaction (params)
567 plist
568 {
569     return (!(*n->n_L_child->n_action) (args (n->n_L_child)));
570 }
571
572
573 static int
574 gcompile (struct nexus *n, char *astr)
575 {
576     register int c;
577     int cclcnt;
578     register unsigned char *ep, *dp, *sp, *lastep = 0;
579
580     dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
581     sp = astr;
582     if (*sp == '^') {
583         n->n_circf = 1;
584         sp++;
585     }
586     else
587         n->n_circf = 0;
588     for (;;) {
589         if (ep >= dp)
590             goto cerror;
591         if ((c = *sp++) != '*')
592             lastep = ep;
593         switch (c) {
594             case '\0': 
595                 *ep++ = CEOF;
596                 return 1;
597
598             case '.': 
599                 *ep++ = CDOT;
600                 continue;
601
602             case '*': 
603                 if (lastep == 0)
604                     goto defchar;
605                 *lastep |= STAR;
606                 continue;
607
608             case '$': 
609                 if (*sp != '\0')
610                     goto defchar;
611                 *ep++ = CDOL;
612                 continue;
613
614             case '[': 
615                 *ep++ = CCL;
616                 *ep++ = 0;
617                 cclcnt = 0;
618                 if ((c = *sp++) == '^') {
619                     c = *sp++;
620                     ep[-2] = NCCL;
621                 }
622                 if (c == '-') {
623                     *ep++ = c;
624                     cclcnt++;
625                     c = *sp++;
626                 }
627                 do {
628                     if (c == '-' && *sp != '\0' && *sp != ']') {
629                         for (c = ep[-1]+1; c < *sp; c++) {
630                             *ep++ = c;
631                             cclcnt++;
632                             if (c == '\0' || ep >= dp)
633                                 goto cerror;
634                         }
635                     } else {
636                         *ep++ = c;
637                         cclcnt++;
638                         if (c == '\0' || ep >= dp)
639                             goto cerror;
640                     }
641                 } while ((c = *sp++) != ']');
642                 if (cclcnt > 255)
643                     goto cerror;
644                 lastep[1] = cclcnt;
645                 continue;
646
647             case '\\': 
648                 if ((c = *sp++) == '\0')
649                     goto cerror;
650         defchar: 
651             default: 
652                 *ep++ = CCHR;
653                 *ep++ = c;
654         }
655     }
656
657 cerror: ;
658     return 0;
659 }
660
661
662 static int
663 GREPaction (params)
664 plist
665 {
666     int c, body, lf;
667     long pos = start;
668     register char *p1, *p2, *ebp, *cbp;
669     char ibuf[BUFSIZ];
670
671     fseek (fp, start, SEEK_SET);
672     body = 0;
673     ebp = cbp = ibuf;
674     for (;;) {
675         if (body && n->n_header)
676             return 0;
677         p1 = linebuf;
678         p2 = cbp;
679         lf = 0;
680         for (;;) {
681             if (p2 >= ebp) {
682                 if (fgets (ibuf, sizeof ibuf, fp) == NULL
683                         || (stop && pos >= stop)) {
684                     if (lf)
685                         break;
686                     return 0;
687                 }
688                 pos += (long) strlen (ibuf);
689                 p2 = ibuf;
690                 ebp = ibuf + strlen (ibuf);
691             }
692             c = *p2++;
693             if (lf && c != '\n') {
694                 if (c != ' ' && c != '\t') {
695                     --p2;
696                     break;
697                 }
698                 else
699                     lf = 0;
700             }
701             if (c == '\n') {
702                 if (body)
703                     break;
704                 else {
705                     if (lf) {
706                         body++;
707                         break;
708                     }
709                     lf++;
710                     c = ' ';
711                 }
712             }
713             if (c && p1 < &linebuf[LBSIZE - 1])
714                 *p1++ = c;
715         }
716
717         *p1++ = 0;
718         cbp = p2;
719         p1 = linebuf;
720         p2 = n->n_expbuf;
721
722         if (n->n_circf) {
723             if (advance (p1, p2))
724                 return 1;
725             continue;
726         }
727
728         if (*p2 == CCHR) {
729             c = p2[1];
730             do {
731                 if (*p1 == c || cc[(unsigned char)*p1] == c)
732                     if (advance (p1, p2))
733                         return 1;
734             } while (*p1++);
735             continue;
736         }
737
738         do {
739             if (advance (p1, p2))
740                 return 1;
741         } while (*p1++);
742     }
743 }
744
745
746 static int
747 advance (char *alp, char *aep)
748 {
749     register unsigned char *lp, *ep, *curlp;
750
751     lp = (unsigned char *)alp;
752     ep = (unsigned char *)aep;
753     for (;;)
754         switch (*ep++) {
755             case CCHR: 
756                 if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
757                     continue;
758                 return 0;
759
760             case CDOT: 
761                 if (*lp++)
762                     continue;
763                 return 0;
764
765             case CDOL: 
766                 if (*lp == 0)
767                     continue;
768                 return 0;
769
770             case CEOF: 
771                 return 1;
772
773             case CCL: 
774                 if (cclass (ep, *lp++, 1)) {
775                     ep += *ep + 1;
776                     continue;
777                 }
778                 return 0;
779
780             case NCCL: 
781                 if (cclass (ep, *lp++, 0)) {
782                     ep += *ep + 1;
783                     continue;
784                 }
785                 return 0;
786
787             case CDOT | STAR: 
788                 curlp = lp;
789                 while (*lp++)
790                     continue;
791                 goto star;
792
793             case CCHR | STAR: 
794                 curlp = lp;
795                 while (*lp++ == *ep || cc[lp[-1]] == *ep)
796                     continue;
797                 ep++;
798                 goto star;
799
800             case CCL | STAR: 
801             case NCCL | STAR: 
802                 curlp = lp;
803                 while (cclass (ep, *lp++, ep[-1] == (CCL | STAR)))
804                     continue;
805                 ep += *ep + 1;
806                 goto star;
807
808         star: 
809                 do {
810                     lp--;
811                     if (advance (lp, ep))
812                         return (1);
813                 } while (lp > curlp);
814                 return 0;
815
816             default: 
817                 admonish (NULL, "advance() botch -- you lose big");
818                 return 0;
819         }
820 }
821
822
823 static int
824 cclass (unsigned char *aset, int ac, int af)
825 {
826     register unsigned int    n;
827     register unsigned char   c, *set;
828
829     set = aset;
830     if ((c = ac) == 0)
831         return (0);
832
833     n = *set++;
834     while (n--)
835         if (*set++ == c || set[-1] == cc[c])
836             return (af);
837
838     return (!af);
839 }
840
841
842 static int
843 tcompile (char *ap, struct tws *tb, int isafter)
844 {
845     register struct tws *tw;
846
847     if ((tw = tws_parse (ap, isafter)) == NULL)
848         return 0;
849
850     twscopy (tb, tw);
851     return 1;
852 }
853
854
855 static struct tws *
856 tws_parse (char *ap, int isafter)
857 {
858     char buffer[BUFSIZ];
859     register struct tws *tw, *ts;
860
861     if ((tw = tws_special (ap)) != NULL) {
862         tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
863         tw->tw_hour = isafter ? 23 : 0;
864         return tw;
865     }
866     if ((tw = dparsetime (ap)) != NULL)
867         return tw;
868
869     if ((ts = dlocaltimenow ()) == NULL)
870         return NULL;
871
872     snprintf (buffer, sizeof(buffer), "%s %s", ap, dtwszone (ts));
873     if ((tw = dparsetime (buffer)) != NULL)
874         return tw;
875
876     snprintf (buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
877             ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone (ts));
878     if ((tw = dparsetime (buffer)) != NULL)
879         return tw;
880
881     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s",
882             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
883     if ((tw = dparsetime (buffer)) != NULL)
884         return tw;
885
886     snprintf (buffer, sizeof(buffer), "%02d %s %04d %s %s",
887             ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
888             ap, dtwszone (ts));
889     if ((tw = dparsetime (buffer)) != NULL)
890         return tw;
891
892     return NULL;
893 }
894
895
896 static struct tws *
897 tws_special (char *ap)
898 {
899     int i;
900     time_t clock;
901     register struct tws *tw;
902
903     time (&clock);
904     if (!mh_strcasecmp (ap, "today"))
905         return dlocaltime (&clock);
906     if (!mh_strcasecmp (ap, "yesterday")) {
907         clock -= (long) (60 * 60 * 24);
908         return dlocaltime (&clock);
909     }
910     if (!mh_strcasecmp (ap, "tomorrow")) {
911         clock += (long) (60 * 60 * 24);
912         return dlocaltime (&clock);
913     }
914
915     for (i = 0; tw_ldotw[i]; i++)
916         if (!mh_strcasecmp (ap, tw_ldotw[i]))
917             break;
918     if (tw_ldotw[i]) {
919         if ((tw = dlocaltime (&clock)) == NULL)
920             return NULL;
921         if ((i -= tw->tw_wday) > 0)
922             i -= 7;
923     }
924     else
925         if (*ap != '-')
926             return NULL;
927         else                    /* -ddd days ago */
928             i = atoi (ap);      /* we should error check this */
929
930     clock += (long) ((60 * 60 * 24) * i);
931     return dlocaltime (&clock);
932 }
933
934
935 static int
936 TWSaction (params)
937 plist
938 {
939     int state;
940     register char *bp;
941     char buf[BUFSIZ], name[NAMESZ];
942     register struct tws *tw;
943
944     fseek (fp, start, SEEK_SET);
945     for (state = FLD, bp = NULL;;) {
946         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
947             case FLD: 
948             case FLDEOF: 
949             case FLDPLUS: 
950                 if (bp != NULL)
951                     free (bp), bp = NULL;
952                 bp = add (buf, NULL);
953                 while (state == FLDPLUS) {
954                     state = m_getfld (state, name, buf, sizeof buf, fp);
955                     bp = add (buf, bp);
956                 }
957                 if (!mh_strcasecmp (name, n->n_datef))
958                     break;
959                 if (state != FLDEOF)
960                     continue;
961
962             case BODY: 
963             case BODYEOF: 
964             case FILEEOF: 
965             case LENERR: 
966             case FMTERR: 
967                 if (state == LENERR || state == FMTERR)
968                     advise (NULL, "format error in message %d", msgnum);
969                 if (bp != NULL)
970                     free (bp);
971                 return 0;
972
973             default: 
974                 adios (NULL, "internal error -- you lose");
975         }
976         break;
977     }
978
979     if ((tw = dparsetime (bp)) == NULL)
980         advise (NULL, "unable to parse %s field in message %d, matching...",
981                 n->n_datef, msgnum), state = 1;
982     else
983         state = n->n_after ? (twsort (tw, &n->n_tws) > 0)
984             : (twsort (tw, &n->n_tws) < 0);
985
986     if (bp != NULL)
987         free (bp);
988     return state;
989 }