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