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