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