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