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