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