Relayouted all switch statements: case aligns with switch.
[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",
446                                         argp[-2]);
447                         return NULL;
448                 }
449                 return nexp3();
450
451         case PRAFTR:
452         case PRBEFR:
453                 if (!(cp = nxtarg())) {  /* allow -xyz arguments */
454                         padvise(NULL, "missing argument to %s", argp[-2]);
455                         return NULL;
456                 }
457                 n = newnexus(TWSaction);
458                 n->n_datef = datesw;
459                 if (!tcompile(cp, &n->n_tws, n->n_after = i == PRAFTR)) {
460                         padvise(NULL, "unable to parse %s %s", argp[-2], cp);
461                         return NULL;
462                 }
463                 return n;
464         }
465 }
466
467
468 static struct nexus *
469 newnexus(int (*action)())
470 {
471         register struct nexus *p;
472
473         if ((p = (struct nexus *) calloc((size_t) 1, sizeof *p)) == NULL)
474                 adios(NULL, "unable to allocate component storage");
475
476         p->n_action = action;
477         return p;
478 }
479
480
481 #define args(a)  a, fp, msgnum, start, stop
482 #define params   args(n)
483 #define plist    \
484         register struct nexus  *n; \
485         register FILE *fp; \
486         int msgnum; \
487         long start, \
488         stop;
489
490 int
491 pmatches(FILE *fp, int msgnum, long start, long stop)
492 {
493         if (!head)
494                 return 1;
495
496         if (!talked++ && pdebug)
497                 PRaction(head, 0);
498
499         return (*head->n_action) (args(head));
500 }
501
502
503 static void
504 PRaction(struct nexus *n, int level)
505 {
506         register int i;
507
508         for (i = 0; i < level; i++)
509                 fprintf(stderr, "| ");
510
511         if (n->n_action == ORaction) {
512                 fprintf(stderr, "OR\n");
513                 PRaction(n->n_L_child, level + 1);
514                 PRaction(n->n_R_child, level + 1);
515                 return;
516         }
517         if (n->n_action == ANDaction) {
518                 fprintf(stderr, "AND\n");
519                 PRaction(n->n_L_child, level + 1);
520                 PRaction(n->n_R_child, level + 1);
521                 return;
522         }
523         if (n->n_action == NOTaction) {
524                 fprintf(stderr, "NOT\n");
525                 PRaction(n->n_L_child, level + 1);
526                 return;
527         }
528         if (n->n_action == GREPaction) {
529                 fprintf(stderr, "PATTERN(%s) %s\n",
530                                 n->n_header ? "header" : "body", n->n_patbuf);
531                 return;
532         }
533         if (n->n_action == TWSaction) {
534                 fprintf(stderr, "TEMPORAL(%s) %s: %s\n",
535                                 n->n_after ? "after" : "before", n->n_datef,
536                                 dasctime(&n->n_tws, TW_NULL));
537                 return;
538         }
539         fprintf(stderr, "UNKNOWN(0x%x)\n",
540                          (unsigned int)(unsigned long) (*n->n_action));
541 }
542
543
544 static int
545 ORaction(params)
546 plist
547 {
548         if ((*n->n_L_child->n_action) (args(n->n_L_child)))
549                 return 1;
550         return (*n->n_R_child->n_action) (args(n->n_R_child));
551 }
552
553
554 static int
555 ANDaction(params)
556 plist
557 {
558         if (!(*n->n_L_child->n_action) (args(n->n_L_child)))
559                 return 0;
560         return (*n->n_R_child->n_action) (args(n->n_R_child));
561 }
562
563
564 static int
565 NOTaction(params)
566 plist
567 {
568         return (!(*n->n_L_child->n_action) (args(n->n_L_child)));
569 }
570
571
572 static int
573 gcompile(struct nexus *n, char *astr)
574 {
575         register int c;
576         int cclcnt;
577         register unsigned char *ep, *dp, *sp, *lastep = 0;
578
579         dp = (ep = n->n_expbuf) + sizeof n->n_expbuf;
580         sp = astr;
581         if (*sp == '^') {
582                 n->n_circf = 1;
583                 sp++;
584         }
585         else
586                 n->n_circf = 0;
587         for (;;) {
588                 if (ep >= dp)
589                         goto cerror;
590                 if ((c = *sp++) != '*')
591                         lastep = ep;
592                 switch (c) {
593                 case '\0':
594                         *ep++ = CEOF;
595                         return 1;
596
597                 case '.':
598                         *ep++ = CDOT;
599                         continue;
600
601                 case '*':
602                         if (lastep == 0)
603                                 goto defchar;
604                         *lastep |= STAR;
605                         continue;
606
607                 case '$':
608                         if (*sp != '\0')
609                                 goto defchar;
610                         *ep++ = CDOL;
611                         continue;
612
613                 case '[':
614                         *ep++ = CCL;
615                         *ep++ = 0;
616                         cclcnt = 0;
617                         if ((c = *sp++) == '^') {
618                                 c = *sp++;
619                                 ep[-2] = NCCL;
620                         }
621                         if (c == '-') {
622                                 *ep++ = c;
623                                 cclcnt++;
624                                 c = *sp++;
625                         }
626                         do {
627                                 if (c == '-' && *sp != '\0' && *sp != ']') {
628                                         for (c = ep[-1]+1; c < *sp; c++) {
629                                                         *ep++ = c;
630                                                         cclcnt++;
631                                                 if (c == '\0' || ep >= dp)
632                                                         goto cerror;
633                                         }
634                                 } else {
635                                         *ep++ = c;
636                                         cclcnt++;
637                                         if (c == '\0' || ep >= dp)
638                                                 goto cerror;
639                                 }
640                         } while ((c = *sp++) != ']');
641                         if (cclcnt > 255)
642                                 goto cerror;
643                         lastep[1] = cclcnt;
644                         continue;
645
646                         case '\\':
647                                 if ((c = *sp++) == '\0')
648                                 goto cerror;
649 defchar:
650                 default:
651                         *ep++ = CCHR;
652                         *ep++ = c;
653                 }
654         }
655
656 cerror: ;
657         return 0;
658 }
659
660
661 static int
662 GREPaction(params)
663 plist
664 {
665         int c, body, lf;
666         long pos = start;
667         register char *p1, *p2, *ebp, *cbp;
668         char ibuf[BUFSIZ];
669
670         fseek(fp, start, SEEK_SET);
671         body = 0;
672         ebp = cbp = ibuf;
673         for (;;) {
674                 if (body && n->n_header)
675                         return 0;
676                 p1 = linebuf;
677                 p2 = cbp;
678                 lf = 0;
679                 for (;;) {
680                         if (p2 >= ebp) {
681                                 if (fgets(ibuf, sizeof ibuf, fp) == NULL
682                                                 || (stop && pos >= stop)) {
683                                         if (lf)
684                                                 break;
685                                         return 0;
686                                 }
687                                 pos += (long) strlen(ibuf);
688                                 p2 = ibuf;
689                                 ebp = ibuf + strlen(ibuf);
690                         }
691                         c = *p2++;
692                         if (lf && c != '\n') {
693                                 if (c != ' ' && c != '\t') {
694                                         --p2;
695                                         break;
696                                 }
697                                 else
698                                         lf = 0;
699                         }
700                         if (c == '\n') {
701                                 if (body)
702                                         break;
703                                 else {
704                                         if (lf) {
705                                                 body++;
706                                                 break;
707                                         }
708                                         lf++;
709                                         c = ' ';
710                                 }
711                         }
712                         if (c && p1 < &linebuf[LBSIZE - 1])
713                                 *p1++ = c;
714                 }
715
716                 *p1++ = 0;
717                 cbp = p2;
718                 p1 = linebuf;
719                 p2 = n->n_expbuf;
720
721                 if (n->n_circf) {
722                         if (advance(p1, p2))
723                                 return 1;
724                         continue;
725                 }
726
727                 if (*p2 == CCHR) {
728                         c = p2[1];
729                         do {
730                                 if (*p1 == c || cc[(unsigned char)*p1] == c)
731                                         if (advance(p1, p2))
732                                                 return 1;
733                         } while (*p1++);
734                         continue;
735                 }
736
737                 do {
738                         if (advance(p1, p2))
739                                 return 1;
740                 } while (*p1++);
741         }
742 }
743
744
745 static int
746 advance(char *alp, char *aep)
747 {
748         register unsigned char *lp, *ep, *curlp;
749
750         lp = (unsigned char *)alp;
751         ep = (unsigned char *)aep;
752         for (;;)
753                 switch (*ep++) {
754                 case CCHR:
755                         if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]])
756                                 continue;
757                         return 0;
758
759                 case CDOT:
760                         if (*lp++)
761                                 continue;
762                         return 0;
763
764                 case CDOL:
765                         if (*lp == 0)
766                                 continue;
767                         return 0;
768
769                 case CEOF:
770                         return 1;
771
772                 case CCL:
773                         if (cclass(ep, *lp++, 1)) {
774                                 ep += *ep + 1;
775                                 continue;
776                         }
777                         return 0;
778
779                 case NCCL:
780                         if (cclass(ep, *lp++, 0)) {
781                                 ep += *ep + 1;
782                                 continue;
783                         }
784                         return 0;
785
786                 case CDOT | STAR:
787                         curlp = lp;
788                         while (*lp++)
789                                 continue;
790                         goto star;
791
792                 case CCHR | STAR:
793                         curlp = lp;
794                         while (*lp++ == *ep || cc[lp[-1]] == *ep)
795                                 continue;
796                         ep++;
797                         goto star;
798
799                 case CCL | STAR:
800                 case NCCL | STAR:
801                         curlp = lp;
802                         while (cclass(ep, *lp++, ep[-1] == (CCL | STAR)))
803                                 continue;
804                         ep += *ep + 1;
805                         goto star;
806
807 star:
808                         do {
809                                 lp--;
810                                 if (advance(lp, ep))
811                                         return (1);
812                         } while (lp > curlp);
813                         return 0;
814
815                 default:
816                         admonish(NULL, "advance() botch -- you lose big");
817                         return 0;
818                 }
819 }
820
821
822 static int
823 cclass(unsigned char *aset, int ac, int af)
824 {
825         register unsigned int n;
826         register unsigned char c, *set;
827
828         set = aset;
829         if ((c = ac) == 0)
830                 return (0);
831
832         n = *set++;
833         while (n--)
834                 if (*set++ == c || set[-1] == cc[c])
835                         return (af);
836
837         return (!af);
838 }
839
840
841 static int
842 tcompile(char *ap, struct tws *tb, int isafter)
843 {
844         register struct tws *tw;
845
846         if ((tw = tws_parse(ap, isafter)) == NULL)
847                 return 0;
848
849         twscopy(tb, tw);
850         return 1;
851 }
852
853
854 static struct tws *
855 tws_parse(char *ap, int isafter)
856 {
857         char buffer[BUFSIZ];
858         register struct tws *tw, *ts;
859
860         if ((tw = tws_special(ap)) != NULL) {
861                 tw->tw_sec = tw->tw_min = isafter ? 59 : 0;
862                 tw->tw_hour = isafter ? 23 : 0;
863                 return tw;
864         }
865         if ((tw = dparsetime(ap)) != NULL)
866                 return tw;
867
868         if ((ts = dlocaltimenow()) == NULL)
869                 return NULL;
870
871         snprintf(buffer, sizeof(buffer), "%s %s", ap, dtwszone(ts));
872         if ((tw = dparsetime(buffer)) != NULL)
873                 return tw;
874
875         snprintf(buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap,
876                         ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone(ts));
877         if ((tw = dparsetime(buffer)) != NULL)
878                 return tw;
879
880         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s",
881                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap);
882         if ((tw = dparsetime(buffer)) != NULL)
883                 return tw;
884
885         snprintf(buffer, sizeof(buffer), "%02d %s %04d %s %s",
886                         ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year,
887                         ap, dtwszone(ts));
888         if ((tw = dparsetime(buffer)) != NULL)
889                 return tw;
890
891         return NULL;
892 }
893
894
895 static struct tws *
896 tws_special(char *ap)
897 {
898         int i;
899         time_t clock;
900         register struct tws *tw;
901
902         time(&clock);
903         if (!mh_strcasecmp(ap, "today"))
904                 return dlocaltime(&clock);
905         if (!mh_strcasecmp(ap, "yesterday")) {
906                 clock -= (long) (60 * 60 * 24);
907                 return dlocaltime(&clock);
908         }
909         if (!mh_strcasecmp(ap, "tomorrow")) {
910                 clock += (long) (60 * 60 * 24);
911                 return dlocaltime(&clock);
912         }
913
914         for (i = 0; tw_ldotw[i]; i++)
915                 if (!mh_strcasecmp(ap, tw_ldotw[i]))
916                         break;
917         if (tw_ldotw[i]) {
918                 if ((tw = dlocaltime(&clock)) == NULL)
919                         return NULL;
920                 if ((i -= tw->tw_wday) > 0)
921                         i -= 7;
922         }
923         else
924                 if (*ap != '-')
925                         return NULL;
926                 else  /* -ddd days ago */
927                         i = atoi(ap);  /* we should error check this */
928
929         clock += (long) ((60 * 60 * 24) * i);
930         return dlocaltime(&clock);
931 }
932
933
934 static int
935 TWSaction(params)
936 plist
937 {
938         int state;
939         register char *bp;
940         char buf[BUFSIZ], name[NAMESZ];
941         register struct tws *tw;
942
943         fseek(fp, start, SEEK_SET);
944         for (state = FLD, bp = NULL;;) {
945                 switch (state = m_getfld(state, name, buf, sizeof buf, fp)) {
946                 case FLD:
947                 case FLDEOF:
948                 case FLDPLUS:
949                         if (bp != NULL)
950                                 free(bp), bp = NULL;
951                         bp = getcpy(buf);
952                         while (state == FLDPLUS) {
953                                 state = m_getfld(state, name, buf,
954                                                 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 }