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