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