]> diplodocus.org Git - nmh/blob - uip/picksbr.c
4732785cae3b5b81ba5c80afe3019f9542e4cd05
[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 "sbr/ambigsw.h"
10 #include "sbr/error.h"
11 #include "h/tws.h"
12 #include "h/picksbr.h"
13 #include "h/utils.h"
14
15 #ifdef HAVE_SYS_TIME_H
16 # include <sys/time.h>
17 #endif
18 #include <time.h>
19
20 #define PARSE_SWITCHES \
21 X("and", 0, PRAND) \
22 X("or", 0, PROR) \
23 X("not", 0, PRNOT) \
24 X("lbrace", 0, PRLBR) \
25 X("rbrace", 0, PRRBR) \
26 X("cc pattern", 0, PRCC) \
27 X("date pattern", 0, PRDATE) \
28 X("from pattern", 0, PRFROM) \
29 X("search pattern", 0, PRSRCH) \
30 X("subject pattern", 0, PRSUBJ) \
31 X("to pattern", 0, PRTO) \
32 X("-othercomponent pattern", 15, PROTHR) \
33 X("after date", 0, PRAFTR) \
34 X("before date", 0, PRBEFR) \
35 X("datefield field", 5, PRDATF) \
36
37 #define X(sw, minchars, id) id,
38 DEFINE_SWITCH_ENUM(PARSE);
39 #undef X
40
41 #define X(sw, minchars, id) { sw, minchars, id },
42 DEFINE_SWITCH_ARRAY(PARSE, parswit);
43 #undef X
44
45 /* DEFINITIONS FOR PATTERN MATCHING */
46
47 /*
48 * We really should be using re_comp() and re_exec() here. Unfortunately,
49 * pick advertises that lowercase characters matches characters of both
50 * cases. Since re_exec() doesn't exhibit this behavior, we are stuck
51 * with this version. Furthermore, we need to be able to save and restore
52 * the state of the pattern matcher in order to do things "efficiently".
53 *
54 * The matching power of this algorithm isn't as powerful as the re_xxx()
55 * routines (no \(xxx\) and \n constructs). Such is life.
56 */
57
58 #define CCHR 2
59 #define CDOT 4
60 #define CCL 6
61 #define NCCL 8
62 #define CDOL 10
63 #define CEOF 11
64
65 #define STAR 01
66
67 #define LBSIZE NMH_BUFSIZ
68 #define ESIZE 1024
69
70
71 static char linebuf[LBSIZE + 1];
72 static char decoded_linebuf[LBSIZE + 1];
73
74 /* the magic array for case-independence */
75 static unsigned char cc[] = {
76 0000,0001,0002,0003,0004,0005,0006,0007,
77 0010,0011,0012,0013,0014,0015,0016,0017,
78 0020,0021,0022,0023,0024,0025,0026,0027,
79 0030,0031,0032,0033,0034,0035,0036,0037,
80 0040,0041,0042,0043,0044,0045,0046,0047,
81 0050,0051,0052,0053,0054,0055,0056,0057,
82 0060,0061,0062,0063,0064,0065,0066,0067,
83 0070,0071,0072,0073,0074,0075,0076,0077,
84 0100,0141,0142,0143,0144,0145,0146,0147,
85 0150,0151,0152,0153,0154,0155,0156,0157,
86 0160,0161,0162,0163,0164,0165,0166,0167,
87 0170,0171,0172,0133,0134,0135,0136,0137,
88 0140,0141,0142,0143,0144,0145,0146,0147,
89 0150,0151,0152,0153,0154,0155,0156,0157,
90 0160,0161,0162,0163,0164,0165,0166,0167,
91 0170,0171,0172,0173,0174,0175,0176,0177,
92
93 0200,0201,0202,0203,0204,0205,0206,0207,
94 0210,0211,0212,0213,0214,0215,0216,0217,
95 0220,0221,0222,0223,0224,0225,0226,0227,
96 0230,0231,0232,0233,0234,0235,0236,0237,
97 0240,0241,0242,0243,0244,0245,0246,0247,
98 0250,0251,0252,0253,0254,0255,0256,0257,
99 0260,0261,0262,0263,0264,0265,0266,0267,
100 0270,0271,0272,0273,0274,0275,0276,0277,
101 0300,0301,0302,0303,0304,0305,0306,0307,
102 0310,0311,0312,0313,0314,0315,0316,0317,
103 0320,0321,0322,0323,0324,0325,0326,0327,
104 0330,0331,0332,0333,0334,0335,0336,0337,
105 0340,0341,0342,0343,0344,0345,0346,0347,
106 0350,0351,0352,0353,0354,0355,0356,0357,
107 0360,0361,0362,0363,0364,0365,0366,0367,
108 0370,0371,0372,0373,0374,0375,0376,0377,
109 };
110
111 /*
112 * DEFINITIONS FOR NEXUS
113 */
114
115 #define nxtarg() (*argp ? *argp++ : NULL)
116 #define prvarg() argp--
117
118 #define pinform if (!talked++) inform
119
120 struct nexus {
121 int (*n_action)(struct nexus *n, FILE *fp, int msgnum, long start, long stop);
122
123 union {
124 /* for {OR,AND,NOT}action */
125 struct {
126 struct nexus *un_L_child;
127 struct nexus *un_R_child;
128 } st1;
129
130 /* for GREPaction */
131 struct {
132 int un_header;
133 int un_circf;
134 char un_expbuf[ESIZE];
135 char *un_patbuf;
136 } st2;
137
138 /* for TWSaction */
139 struct {
140 char *un_datef;
141 int un_after;
142 struct tws un_tws;
143 } st3;
144 } un;
145 };
146
147 #define n_L_child un.st1.un_L_child
148 #define n_R_child un.st1.un_R_child
149
150 #define n_header un.st2.un_header
151 #define n_circf un.st2.un_circf
152 #define n_expbuf un.st2.un_expbuf
153 #define n_patbuf un.st2.un_patbuf
154
155 #define n_datef un.st3.un_datef
156 #define n_after un.st3.un_after
157 #define n_tws un.st3.un_tws
158
159 static int talked;
160
161 static char *datesw;
162 static char **argp;
163
164 static struct nexus *head;
165
166 /*
167 * prototypes for date routines
168 */
169 static struct tws *tws_parse(char *, int);
170 static struct tws *tws_special(char *);
171
172 /*
173 * static prototypes
174 */
175 static void PRaction(struct nexus *, int);
176 static int gcompile(struct nexus *, char *);
177 static int advance(char *, char *);
178 static int cclass(unsigned char *, int, int);
179 static int tcompile(char *, struct tws *, int);
180
181 static struct nexus *parse(void);
182 static struct nexus *nexp1(void);
183 static struct nexus *nexp2(void);
184 static struct nexus *nexp3(void);
185 static struct nexus *newnexus(int (*action)(struct nexus *n,
186 FILE *fp, int msgnum, long start, long stop));
187
188 static int ORaction(struct nexus *n, FILE *fp, int msgnum,
189 long start, long stop);
190 static int ANDaction(struct nexus *n, FILE *fp, int msgnum,
191 long start, long stop);
192 static int NOTaction(struct nexus *n, FILE *fp, int msgnum,
193 long start, long stop);
194 static int GREPaction(struct nexus *n, FILE *fp, int msgnum,
195 long start, long stop);
196 static int TWSaction(struct nexus *n, FILE *fp, int msgnum,
197 long start, long stop);
198
199
200 int
201 pcompile (char **vec, char *date)
202 {
203 argp = vec;
204 if ((datesw = date) == NULL)
205 datesw = "date";
206 talked = 0;
207
208 if ((head = parse ()) == NULL)
209 return !talked;
210
211 if (*argp) {
212 inform("%s unexpected", *argp);
213 return 0;
214 }
215
216 return 1;
217 }
218
219
220 static struct nexus *
221 parse (void)
222 {
223 char *cp;
224 struct nexus *n, *o;
225
226 if ((n = nexp1 ()) == NULL || (cp = nxtarg ()) == NULL)
227 return n;
228
229 if (*cp != '-') {
230 pinform("%s unexpected", cp);
231 return NULL;
232 }
233
234 if (*++cp == '-')
235 goto header;
236 switch (smatch (cp, parswit)) {
237 case AMBIGSW:
238 ambigsw (cp, parswit);
239 talked++;
240 return NULL;
241 case UNKWNSW:
242 fprintf (stderr, "-%s unknown\n", cp);
243 talked++;
244 return NULL;
245
246 case PROR:
247 o = newnexus (ORaction);
248 o->n_L_child = n;
249 if ((o->n_R_child = parse ()))
250 return o;
251 pinform("missing disjunctive");
252 free (o);
253 return NULL;
254
255 header: ;
256 default:
257 prvarg ();
258 return n;
259 }
260 }
261
262 static struct nexus *
263 nexp1 (void)
264 {
265 char *cp;
266 struct nexus *n, *o;
267
268 if ((n = nexp2 ()) == NULL || (cp = nxtarg ()) == NULL)
269 return n;
270
271 if (*cp != '-') {
272 pinform("%s unexpected", cp);
273 free (n);
274 return NULL;
275 }
276
277 if (*++cp == '-')
278 goto header;
279 switch (smatch (cp, parswit)) {
280 case AMBIGSW:
281 ambigsw (cp, parswit);
282 talked++;
283 free (n);
284 return NULL;
285 case UNKWNSW:
286 fprintf (stderr, "-%s unknown\n", cp);
287 talked++;
288 free (n);
289 return NULL;
290
291 case PRAND:
292 o = newnexus (ANDaction);
293 o->n_L_child = n;
294 if ((o->n_R_child = nexp1 ()))
295 return o;
296 pinform("missing conjunctive");
297 free (o);
298 return NULL;
299
300 header: ;
301 default:
302 prvarg ();
303 return n;
304 }
305 }
306
307
308 static struct nexus *
309 nexp2 (void)
310 {
311 char *cp;
312 struct nexus *n;
313
314 if ((cp = nxtarg ()) == NULL)
315 return NULL;
316
317 if (*cp != '-') {
318 prvarg ();
319 return nexp3 ();
320 }
321
322 if (*++cp == '-')
323 goto header;
324 switch (smatch (cp, parswit)) {
325 case AMBIGSW:
326 ambigsw (cp, parswit);
327 talked++;
328 return NULL;
329 case UNKWNSW:
330 fprintf (stderr, "-%s unknown\n", cp);
331 talked++;
332 return NULL;
333
334 case PRNOT:
335 n = newnexus (NOTaction);
336 if ((n->n_L_child = nexp3 ()))
337 return n;
338 pinform("missing negation");
339 free (n);
340 return NULL;
341
342 header: ;
343 default:
344 prvarg ();
345 return nexp3 ();
346 }
347 }
348
349 static struct nexus *
350 nexp3 (void)
351 {
352 int i;
353 char *cp, *dp;
354 char buffer[BUFSIZ], temp[64];
355 struct nexus *n;
356
357 if ((cp = nxtarg ()) == NULL)
358 return NULL;
359
360 if (*cp != '-') {
361 pinform("%s unexpected", cp);
362 return NULL;
363 }
364
365 if (*++cp == '-') {
366 dp = ++cp;
367 goto header;
368 }
369 switch (i = smatch (cp, parswit)) {
370 case AMBIGSW:
371 ambigsw (cp, parswit);
372 talked++;
373 return NULL;
374 case UNKWNSW:
375 fprintf (stderr, "-%s unknown\n", cp);
376 talked++;
377 return NULL;
378
379 case PRLBR:
380 if ((n = parse ()) == NULL) {
381 pinform("missing group");
382 return NULL;
383 }
384 if ((cp = nxtarg ()) == NULL) {
385 pinform("missing -rbrace");
386 return NULL;
387 }
388 if (*cp++ == '-' && smatch (cp, parswit) == PRRBR)
389 return n;
390 pinform("%s unexpected", --cp);
391 return NULL;
392
393 default:
394 prvarg ();
395 return NULL;
396
397 case PRCC:
398 case PRDATE:
399 case PRFROM:
400 case PRTO:
401 case PRSUBJ:
402 strncpy(temp, parswit[i].sw, sizeof(temp));
403 temp[sizeof(temp) - 1] = '\0';
404 dp = *brkstring (temp, " ", NULL);
405 header: ;
406 if (!(cp = nxtarg ())) {/* allow -xyz arguments */
407 pinform("missing argument to %s", argp[-2]);
408 return NULL;
409 }
410 n = newnexus (GREPaction);
411 n->n_header = 1;
412 snprintf (buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp);
413 dp = buffer;
414 goto pattern;
415
416 case PRSRCH:
417 n = newnexus (GREPaction);
418 n->n_header = 0;
419 if (!(cp = nxtarg ())) {/* allow -xyz arguments */
420 pinform("missing argument to %s", argp[-2]);
421 free (n);
422 return NULL;
423 }
424 dp = cp;
425 pattern: ;
426 if (!gcompile (n, dp)) {
427 pinform("pattern error in %s %s", argp[-2], cp);
428 free (n);
429 return NULL;
430 }
431 n->n_patbuf = mh_xstrdup(dp);
432 return n;
433
434 case PROTHR:
435 pinform("internal error!");
436 return NULL;
437
438 case PRDATF:
439 if (!(datesw = nxtarg ()) || *datesw == '-') {
440 pinform("missing argument to %s", argp[-2]);
441 return NULL;
442 }
443 return nexp3 ();
444
445 case PRAFTR:
446 case PRBEFR:
447 if (!(cp = nxtarg ())) {/* allow -xyz arguments */
448 pinform("missing argument to %s", argp[-2]);
449 return NULL;
450 }
451 n = newnexus (TWSaction);
452 n->n_datef = datesw;
453 if (!tcompile (cp, &n->n_tws, n->n_after = i == PRAFTR)) {
454 pinform("unable to parse %s %s", argp[-2], cp);
455 free (n);
456 return NULL;
457 }
458 return n;
459 }
460 }
461
462
463 static struct nexus *
464 newnexus(int (*action)(struct nexus *n, FILE *fp, int msgnum,
465 long start, long stop))
466 {
467 struct nexus *p;
468
469 NEW0(p);
470 p->n_action = action;
471 return p;
472 }
473
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)(head, fp, msgnum, start, stop);
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
525 generic_pointer gp;
526 gp.f = (void (*)(void))n->n_action;
527 fprintf(stderr, "UNKNOWN(%p)\n", gp.v);
528 }
529
530
531 static int
532 ORaction(struct nexus *n, FILE *fp, int msgnum, long start, long stop)
533 {
534 if ((*n->n_L_child->n_action)(n->n_L_child, fp, msgnum, start, stop))
535 return 1;
536 return (*n->n_R_child->n_action)(n->n_R_child, fp, msgnum, start, stop);
537 }
538
539
540 static int
541 ANDaction(struct nexus *n, FILE *fp, int msgnum, long start, long stop)
542 {
543 if (!(*n->n_L_child->n_action)(n->n_L_child, fp, msgnum, start, stop))
544 return 0;
545 return (*n->n_R_child->n_action)(n->n_R_child, fp, msgnum, start, stop);
546 }
547
548
549 static int
550 NOTaction(struct nexus *n, FILE *fp, int msgnum, long start, long stop)
551 {
552 return (!(*n->n_L_child->n_action)(n->n_L_child, fp, msgnum, start, stop));
553 }
554
555
556 static int
557 gcompile (struct nexus *n, char *astr)
558 {
559 int c;
560 int cclcnt;
561 unsigned char *ep, *dp, *sp, *lastep = 0;
562
563 dp = (ep = (unsigned char *) n->n_expbuf) + sizeof n->n_expbuf;
564 sp = (unsigned char *) astr;
565 if (*sp == '^') {
566 n->n_circf = 1;
567 sp++;
568 }
569 else
570 n->n_circf = 0;
571 for (;;) {
572 if (ep >= dp)
573 goto cerror;
574 if ((c = *sp++) != '*')
575 lastep = ep;
576 switch (c) {
577 case '\0':
578 *ep++ = CEOF;
579 return 1;
580
581 case '.':
582 *ep++ = CDOT;
583 continue;
584
585 case '*':
586 if (lastep == 0)
587 goto defchar;
588 *lastep |= STAR;
589 continue;
590
591 case '$':
592 if (*sp != '\0')
593 goto defchar;
594 *ep++ = CDOL;
595 continue;
596
597 case '[':
598 *ep++ = CCL;
599 *ep++ = 0;
600 cclcnt = 0;
601 if ((c = *sp++) == '^') {
602 c = *sp++;
603 ep[-2] = NCCL;
604 }
605 if (c == '-') {
606 *ep++ = c;
607 cclcnt++;
608 c = *sp++;
609 }
610 do {
611 if (c == '-' && *sp != '\0' && *sp != ']') {
612 for (c = ep[-1]+1; c < *sp; c++) {
613 *ep++ = c;
614 cclcnt++;
615 if (c == '\0' || ep >= dp)
616 goto cerror;
617 }
618 } else {
619 *ep++ = c;
620 cclcnt++;
621 if (c == '\0' || ep >= dp)
622 goto cerror;
623 }
624 } while ((c = *sp++) != ']');
625 if (cclcnt > 255)
626 goto cerror;
627 lastep[1] = cclcnt;
628 continue;
629
630 case '\\':
631 if ((c = *sp++) == '\0')
632 goto cerror;
633 /* FALLTHRU */
634 defchar:
635 default:
636 *ep++ = CCHR;
637 *ep++ = c;
638 }
639 }
640
641 cerror: ;
642 return 0;
643 }
644
645
646 static int
647 GREPaction(struct nexus *n, FILE *fp, int msgnum, long start, long stop)
648 {
649 int c;
650 bool body;
651 bool 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 = false;
659 ebp = cbp = ibuf;
660 for (;;) {
661 if (body && n->n_header)
662 return 0;
663 p1 = linebuf;
664 p2 = cbp;
665 lf = false;
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 = false;
685 }
686 if (c == '\n') {
687 if (body)
688 break;
689 if (lf) {
690 body = true;
691 break;
692 }
693 lf = true;
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 || lf) &&
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(struct nexus *n, FILE *fp, int msgnum, long start, long stop)
929 {
930 int state;
931 char *bp;
932 char buf[NMH_BUFSIZ], name[NAMESZ];
933 struct tws *tw;
934 m_getfld_state_t gstate;
935 NMH_UNUSED (stop);
936
937 fseek (fp, start, SEEK_SET);
938 gstate = m_getfld_state_init(fp);
939 for (bp = NULL;;) {
940 int bufsz = sizeof buf;
941 switch (state = m_getfld2(&gstate, name, buf, &bufsz)) {
942 case FLD:
943 case FLDPLUS:
944 free(bp);
945 bp = mh_xstrdup(buf);
946 while (state == FLDPLUS) {
947 bufsz = sizeof buf;
948 state = m_getfld2(&gstate, name, buf, &bufsz);
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 free(bp);
962 return 0;
963
964 default:
965 die("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 free(bp);
979 return state;
980 }