/* * picksbr.c -- routines to help pick along... * * $Id: picksbr.c,v 1.1.1.1 1999-02-07 18:14:15 danw Exp $ */ #include #include #include static struct swit parswit[] = { #define PRAND 0 { "and", 0 }, #define PROR 1 { "or", 0 }, #define PRNOT 2 { "not", 0 }, #define PRLBR 3 { "lbrace", 0 }, #define PRRBR 4 { "rbrace", 0 }, #define PRCC 5 { "cc pattern", 0 }, #define PRDATE 6 { "date pattern", 0 }, #define PRFROM 7 { "from pattern", 0 }, #define PRSRCH 8 { "search pattern", 0 }, #define PRSUBJ 9 { "subject pattern", 0 }, #define PRTO 10 { "to pattern", 0 }, #define PROTHR 11 { "-othercomponent pattern", 15 }, #define PRAFTR 12 { "after date", 0 }, #define PRBEFR 13 { "before date", 0 }, #define PRDATF 14 { "datefield field", 5 }, { NULL, 0 } }; /* DEFINITIONS FOR PATTERN MATCHING */ /* * We really should be using re_comp() and re_exec() here. Unfortunately, * pick advertises that lowercase characters matches characters of both * cases. Since re_exec() doesn't exhibit this behavior, we are stuck * with this version. Furthermore, we need to be able to save and restore * the state of the pattern matcher in order to do things "efficiently". * * The matching power of this algorithm isn't as powerful as the re_xxx() * routines (no \(xxx\) and \n constructs). Such is life. */ #define CCHR 2 #define CDOT 4 #define CCL 6 #define NCCL 8 #define CDOL 10 #define CEOF 11 #define STAR 01 #define LBSIZE 1024 #define ESIZE 256 static char linebuf[LBSIZE + 1]; /* the magic array for case-independence */ static char cc[] = { 0000,0001,0002,0003,0004,0005,0006,0007, 0010,0011,0012,0013,0014,0015,0016,0017, 0020,0021,0022,0023,0024,0025,0026,0027, 0030,0031,0032,0033,0034,0035,0036,0037, 0040,0041,0042,0043,0044,0045,0046,0047, 0050,0051,0052,0053,0054,0055,0056,0057, 0060,0061,0062,0063,0064,0065,0066,0067, 0070,0071,0072,0073,0074,0075,0076,0077, 0100,0141,0142,0143,0144,0145,0146,0147, 0150,0151,0152,0153,0154,0155,0156,0157, 0160,0161,0162,0163,0164,0165,0166,0167, 0170,0171,0172,0133,0134,0135,0136,0137, 0140,0141,0142,0143,0144,0145,0146,0147, 0150,0151,0152,0153,0154,0155,0156,0157, 0160,0161,0162,0163,0164,0165,0166,0167, 0170,0171,0172,0173,0174,0175,0176,0177, }; /* * DEFINITIONS FOR NEXUS */ #define nxtarg() (*argp ? *argp++ : NULL) #define prvarg() argp-- #define padvise if (!talked++) advise struct nexus { int (*n_action)(); union { /* for {OR,AND,NOT}action */ struct { struct nexus *un_L_child; struct nexus *un_R_child; } st1; /* for GREPaction */ struct { int un_header; int un_circf; char un_expbuf[ESIZE]; char *un_patbuf; } st2; /* for TWSaction */ struct { char *un_datef; int un_after; struct tws un_tws; } st3; } un; }; #define n_L_child un.st1.un_L_child #define n_R_child un.st1.un_R_child #define n_header un.st2.un_header #define n_circf un.st2.un_circf #define n_expbuf un.st2.un_expbuf #define n_patbuf un.st2.un_patbuf #define n_datef un.st3.un_datef #define n_after un.st3.un_after #define n_tws un.st3.un_tws static int talked; static int pdebug = 0; static char *datesw; static char **argp; static struct nexus *head; /* * prototypes for date routines */ static struct tws *tws_parse(); static struct tws *tws_special(); /* * static prototypes */ static void PRaction(); static int gcompile(); static int advance(); static int cclass(); static int tcompile(); static struct nexus *parse(); static struct nexus *exp1(); static struct nexus *exp2(); static struct nexus *exp3(); static struct nexus *newnexus(); static int ORaction(); static int ANDaction(); static int NOTaction(); static int GREPaction(); static int TWSaction(); int pcompile (char **vec, char *date) { register char *cp; if ((cp = getenv ("MHPDEBUG")) && *cp) pdebug++; argp = vec; if ((datesw = date) == NULL) datesw = "date"; talked = 0; if ((head = parse ()) == NULL) return (talked ? 0 : 1); if (*argp) { padvise (NULL, "%s unexpected", *argp); return 0; } return 1; } static struct nexus * parse (void) { register char *cp; register struct nexus *n, *o; if ((n = exp1 ()) == NULL || (cp = nxtarg ()) == NULL) return n; if (*cp != '-') { padvise (NULL, "%s unexpected", cp); return NULL; } if (*++cp == '-') goto header; switch (smatch (cp, parswit)) { case AMBIGSW: ambigsw (cp, parswit); talked++; return NULL; case UNKWNSW: fprintf (stderr, "-%s unknown\n", cp); talked++; return NULL; case PROR: o = newnexus (ORaction); o->n_L_child = n; if ((o->n_R_child = parse ())) return o; padvise (NULL, "missing disjunctive"); return NULL; header: ; default: prvarg (); return n; } } static struct nexus * exp1 (void) { register char *cp; register struct nexus *n, *o; if ((n = exp2 ()) == NULL || (cp = nxtarg ()) == NULL) return n; if (*cp != '-') { padvise (NULL, "%s unexpected", cp); return NULL; } if (*++cp == '-') goto header; switch (smatch (cp, parswit)) { case AMBIGSW: ambigsw (cp, parswit); talked++; return NULL; case UNKWNSW: fprintf (stderr, "-%s unknown\n", cp); talked++; return NULL; case PRAND: o = newnexus (ANDaction); o->n_L_child = n; if ((o->n_R_child = exp1 ())) return o; padvise (NULL, "missing conjunctive"); return NULL; header: ; default: prvarg (); return n; } } static struct nexus * exp2 (void) { register char *cp; register struct nexus *n; if ((cp = nxtarg ()) == NULL) return NULL; if (*cp != '-') { prvarg (); return exp3 (); } if (*++cp == '-') goto header; switch (smatch (cp, parswit)) { case AMBIGSW: ambigsw (cp, parswit); talked++; return NULL; case UNKWNSW: fprintf (stderr, "-%s unknown\n", cp); talked++; return NULL; case PRNOT: n = newnexus (NOTaction); if ((n->n_L_child = exp3 ())) return n; padvise (NULL, "missing negation"); return NULL; header: ; default: prvarg (); return exp3 (); } } static struct nexus * exp3 (void) { int i; register char *cp, *dp; char buffer[BUFSIZ], temp[64]; register struct nexus *n; if ((cp = nxtarg ()) == NULL) return NULL; if (*cp != '-') { padvise (NULL, "%s unexpected", cp); return NULL; } if (*++cp == '-') { dp = ++cp; goto header; } switch (i = smatch (cp, parswit)) { case AMBIGSW: ambigsw (cp, parswit); talked++; return NULL; case UNKWNSW: fprintf (stderr, "-%s unknown\n", cp); talked++; return NULL; case PRLBR: if ((n = parse ()) == NULL) { padvise (NULL, "missing group"); return NULL; } if ((cp = nxtarg ()) == NULL) { padvise (NULL, "missing -rbrace"); return NULL; } if (*cp++ == '-' && smatch (cp, parswit) == PRRBR) return n; padvise (NULL, "%s unexpected", --cp); return NULL; default: prvarg (); return NULL; case PRCC: case PRDATE: case PRFROM: case PRTO: case PRSUBJ: strncpy(temp, parswit[i].sw, sizeof(temp)); temp[sizeof(temp) - 1] = '\0'; dp = *brkstring (temp, " ", NULL); header: ; if (!(cp = nxtarg ())) {/* allow -xyz arguments */ padvise (NULL, "missing argument to %s", argp[-2]); return NULL; } n = newnexus (GREPaction); n->n_header = 1; snprintf (buffer, sizeof(buffer), "^%s[ \t]*:.*%s", dp, cp); dp = buffer; goto pattern; case PRSRCH: n = newnexus (GREPaction); n->n_header = 0; if (!(cp = nxtarg ())) {/* allow -xyz arguments */ padvise (NULL, "missing argument to %s", argp[-2]); return NULL; } dp = cp; pattern: ; if (!gcompile (n, dp)) { padvise (NULL, "pattern error in %s %s", argp[-2], cp); return NULL; } n->n_patbuf = getcpy (dp); return n; case PROTHR: padvise (NULL, "internal error!"); return NULL; case PRDATF: if (!(datesw = nxtarg ()) || *datesw == '-') { padvise (NULL, "missing argument to %s", argp[-2]); return NULL; } return exp3 (); case PRAFTR: case PRBEFR: if (!(cp = nxtarg ())) {/* allow -xyz arguments */ padvise (NULL, "missing argument to %s", argp[-2]); return NULL; } n = newnexus (TWSaction); n->n_datef = datesw; if (!tcompile (cp, &n->n_tws, n->n_after = i == PRAFTR)) { padvise (NULL, "unable to parse %s %s", argp[-2], cp); return NULL; } return n; } } static struct nexus * newnexus (int (*action)()) { register struct nexus *p; if ((p = (struct nexus *) calloc ((size_t) 1, sizeof *p)) == NULL) adios (NULL, "unable to allocate component storage"); p->n_action = action; return p; } #define args(a) a, fp, msgnum, start, stop #define params args (n) #define plist \ register struct nexus *n; \ register FILE *fp; \ int msgnum; \ long start, \ stop; int pmatches (FILE *fp, int msgnum, long start, long stop) { if (!head) return 1; if (!talked++ && pdebug) PRaction (head, 0); return (*head->n_action) (args (head)); } static void PRaction (struct nexus *n, int level) { register int i; for (i = 0; i < level; i++) fprintf (stderr, "| "); if (n->n_action == ORaction) { fprintf (stderr, "OR\n"); PRaction (n->n_L_child, level + 1); PRaction (n->n_R_child, level + 1); return; } if (n->n_action == ANDaction) { fprintf (stderr, "AND\n"); PRaction (n->n_L_child, level + 1); PRaction (n->n_R_child, level + 1); return; } if (n->n_action == NOTaction) { fprintf (stderr, "NOT\n"); PRaction (n->n_L_child, level + 1); return; } if (n->n_action == GREPaction) { fprintf (stderr, "PATTERN(%s) %s\n", n->n_header ? "header" : "body", n->n_patbuf); return; } if (n->n_action == TWSaction) { fprintf (stderr, "TEMPORAL(%s) %s: %s\n", n->n_after ? "after" : "before", n->n_datef, dasctime (&n->n_tws, TW_NULL)); return; } fprintf (stderr, "UNKNOWN(0x%x)\n", (unsigned int) (*n->n_action)); } static int ORaction (params) plist { if ((*n->n_L_child->n_action) (args (n->n_L_child))) return 1; return (*n->n_R_child->n_action) (args (n->n_R_child)); } static int ANDaction (params) plist { if (!(*n->n_L_child->n_action) (args (n->n_L_child))) return 0; return (*n->n_R_child->n_action) (args (n->n_R_child)); } static int NOTaction (params) plist { return (!(*n->n_L_child->n_action) (args (n->n_L_child))); } static int gcompile (struct nexus *n, char *astr) { register int c; int cclcnt; register char *ep, *dp, *sp, *lastep; dp = (ep = n->n_expbuf) + sizeof n->n_expbuf; sp = astr; if (*sp == '^') { n->n_circf = 1; sp++; } else n->n_circf = 0; for (;;) { if (ep >= dp) goto cerror; if ((c = *sp++) != '*') lastep = ep; switch (c) { case '\0': *ep++ = CEOF; return 1; case '.': *ep++ = CDOT; continue; case '*': if (lastep == 0) goto defchar; *lastep |= STAR; continue; case '$': if (*sp != '\0') goto defchar; *ep++ = CDOL; continue; case '[': *ep++ = CCL; *ep++ = 0; cclcnt = 1; if ((c = *sp++) == '^') { c = *sp++; ep[-2] = NCCL; } do { *ep++ = c; cclcnt++; if (c == '\0' || ep >= dp) goto cerror; } while ((c = *sp++) != ']'); lastep[1] = cclcnt; continue; case '\\': if ((c = *sp++) == '\0') goto cerror; defchar: default: *ep++ = CCHR; *ep++ = c; } } cerror: ; return 0; } static int GREPaction (params) plist { int c, body, lf; long pos = start; register char *p1, *p2, *ebp, *cbp; char ibuf[BUFSIZ]; fseek (fp, start, SEEK_SET); body = 0; ebp = cbp = ibuf; for (;;) { if (body && n->n_header) return 0; p1 = linebuf; p2 = cbp; lf = 0; for (;;) { if (p2 >= ebp) { if (fgets (ibuf, sizeof ibuf, fp) == NULL || (stop && pos >= stop)) { if (lf) break; return 0; } pos += (long) strlen (ibuf); p2 = ibuf; ebp = ibuf + strlen (ibuf); } c = *p2++; if (lf && c != '\n') if (c != ' ' && c != '\t') { --p2; break; } else lf = 0; if (c == '\n') if (body) break; else { if (lf) { body++; break; } lf++; c = ' '; } if (c && p1 < &linebuf[LBSIZE - 1]) *p1++ = c; } *p1++ = 0; cbp = p2; p1 = linebuf; p2 = n->n_expbuf; if (n->n_circf) { if (advance (p1, p2)) return 1; continue; } if (*p2 == CCHR) { c = p2[1]; do { if (*p1 == c || cc[*p1] == c) if (advance (p1, p2)) return 1; } while (*p1++); continue; } do { if (advance (p1, p2)) return 1; } while (*p1++); } } static int advance (char *alp, char *aep) { register char *lp, *ep, *curlp; lp = alp; ep = aep; for (;;) switch (*ep++) { case CCHR: if (*ep++ == *lp++ || ep[-1] == cc[lp[-1]]) continue; return 0; case CDOT: if (*lp++) continue; return 0; case CDOL: if (*lp == 0) continue; return 0; case CEOF: return 1; case CCL: if (cclass (ep, *lp++, 1)) { ep += *ep; continue; } return 0; case NCCL: if (cclass (ep, *lp++, 0)) { ep += *ep; continue; } return 0; case CDOT | STAR: curlp = lp; while (*lp++) continue; goto star; case CCHR | STAR: curlp = lp; while (*lp++ == *ep || cc[lp[-1]] == *ep) continue; ep++; goto star; case CCL | STAR: case NCCL | STAR: curlp = lp; while (cclass (ep, *lp++, ep[-1] == (CCL | STAR))) continue; ep += *ep; goto star; star: do { lp--; if (advance (lp, ep)) return (1); } while (lp > curlp); return 0; default: admonish (NULL, "advance() botch -- you lose big"); return 0; } } static int cclass (char *aset, int ac, int af) { register int n; register char c, *set; set = aset; if ((c = ac) == 0) return (0); n = *set++; while (--n) if (*set++ == c) return (af); return (!af); } static int tcompile (char *ap, struct tws *tb, int isafter) { register struct tws *tw; if ((tw = tws_parse (ap, isafter)) == NULL) return 0; twscopy (tb, tw); return 1; } static struct tws * tws_parse (char *ap, int isafter) { char buffer[BUFSIZ]; register struct tws *tw, *ts; if ((tw = tws_special (ap)) != NULL) { tw->tw_sec = tw->tw_min = isafter ? 59 : 0; tw->tw_hour = isafter ? 23 : 0; return tw; } if ((tw = dparsetime (ap)) != NULL) return tw; if ((ts = dlocaltimenow ()) == NULL) return NULL; snprintf (buffer, sizeof(buffer), "%s %s", ap, dtwszone (ts)); if ((tw = dparsetime (buffer)) != NULL) return tw; snprintf (buffer, sizeof(buffer), "%s %02d:%02d:%02d %s", ap, ts->tw_hour, ts->tw_min, ts->tw_sec, dtwszone (ts)); if ((tw = dparsetime (buffer)) != NULL) return tw; snprintf (buffer, sizeof(buffer), "%02d %s %04d %s", ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap); if ((tw = dparsetime (buffer)) != NULL) return tw; snprintf (buffer, sizeof(buffer), "%02d %s %04d %s %s", ts->tw_mday, tw_moty[ts->tw_mon], ts->tw_year, ap, dtwszone (ts)); if ((tw = dparsetime (buffer)) != NULL) return tw; return NULL; } static struct tws * tws_special (char *ap) { int i; time_t clock; register struct tws *tw; time (&clock); if (!strcasecmp (ap, "today")) return dlocaltime (&clock); if (!strcasecmp (ap, "yesterday")) { clock -= (long) (60 * 60 * 24); return dlocaltime (&clock); } if (!strcasecmp (ap, "tomorrow")) { clock += (long) (60 * 60 * 24); return dlocaltime (&clock); } for (i = 0; tw_ldotw[i]; i++) if (!strcasecmp (ap, tw_ldotw[i])) break; if (tw_ldotw[i]) { if ((tw = dlocaltime (&clock)) == NULL) return NULL; if ((i -= tw->tw_wday) > 0) i -= 7; } else if (*ap != '-') return NULL; else /* -ddd days ago */ i = atoi (ap); /* we should error check this */ clock += (long) ((60 * 60 * 24) * i); return dlocaltime (&clock); } static int TWSaction (params) plist { int state; register char *bp; char buf[BUFSIZ], name[NAMESZ]; register struct tws *tw; fseek (fp, start, SEEK_SET); for (state = FLD, bp = NULL;;) { switch (state = m_getfld (state, name, buf, sizeof buf, fp)) { case FLD: case FLDEOF: case FLDPLUS: if (bp != NULL) free (bp), bp = NULL; bp = add (buf, NULL); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, fp); bp = add (buf, bp); } if (!strcasecmp (name, n->n_datef)) break; if (state != FLDEOF) continue; case BODY: case BODYEOF: case FILEEOF: case LENERR: case FMTERR: if (state == LENERR || state == FMTERR) advise (NULL, "format error in message %d", msgnum); if (bp != NULL) free (bp); return 0; default: adios (NULL, "internal error -- you lose"); } break; } if ((tw = dparsetime (bp)) == NULL) advise (NULL, "unable to parse %s field in message %d, matching...", n->n_datef, msgnum), state = 1; else state = n->n_after ? (twsort (tw, &n->n_tws) > 0) : (twsort (tw, &n->n_tws) < 0); if (bp != NULL) free (bp); return state; }