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