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