#include #include #include #include #include #include #include "dat.h" int debug; enum { Tregexp= (1<<0), /* ~ */ Texact= (1<<1), /* = */ }; typedef struct Pattern Pattern; struct Pattern { Pattern *next; int type; char *arg; int bang; }; String *patternpath; Pattern *patterns; String *mbox; static void usage(void) { fprint(2, "usage: %s add|check patternfile [addressfile ...]\n", argv0); exits("usage"); } /* * convert string to lower case */ static void mklower(char *p) { int c; for(; *p; p++){ c = *p; if(c <= 'Z' && c >= 'A') *p = c - 'A' + 'a'; } } /* * simplify an address, reduce to a domain */ static String* simplify(char *addr) { int dots, dotlim; char *p, *at; String *s; mklower(addr); at = strchr(addr, '@'); if(at == nil){ /* local address, make it an exact match */ s = s_copy("="); s_append(s, addr); return s; } /* copy up to, and including, the '@' sign */ at++; s = s_copy("~"); for(p = addr; p < at; p++){ if(strchr(".*+?(|)\\[]^$", *p)) s_putc(s, '\\'); s_putc(s, *p); } /* * just any address matching the two most significant domain elements, * except for .uk, which needs three. */ s_append(s, "(.*\\.)?"); p = addr+strlen(addr); /* point at NUL */ if (p[-1] == '.') *--p = '\0'; if (p - addr > 3 && strcmp(".uk", p - 3) == 0) dotlim = 3; else dotlim = 2; dots = 0; while(--p > at) if(*p == '.' && ++dots >= dotlim){ p++; break; } for(; *p; p++){ if(strchr(".*+?(|)\\[]^$", *p) != nil) s_putc(s, '\\'); s_putc(s, *p); } s_terminate(s); return s; } /* * link patterns in order */ static int newpattern(int type, char *arg, int bang) { Pattern *p; static Pattern *last; mklower(arg); p = mallocz(sizeof *p, 1); if(p == nil) return -1; if(type == Tregexp){ p->arg = malloc(strlen(arg)+3); if(p->arg == nil){ free(p); return -1; } p->arg[0] = 0; strcat(p->arg, "^"); strcat(p->arg, arg); strcat(p->arg, "$"); } else { p->arg = strdup(arg); if(p->arg == nil){ free(p); return -1; } } p->type = type; p->bang = bang; if(last == nil) patterns = p; else last->next = p; last = p; return 0; } /* * patterns are either * ~ regular expression * = exact match string * * all comparisons are case insensitive */ static int readpatterns(char *path) { Biobuf *b; char *p; char *token[2]; int n; int bang; b = Bopen(path, OREAD); if(b == nil) return -1; while((p = Brdline(b, '\n')) != nil){ p[Blinelen(b)-1] = 0; n = tokenize(p, token, 2); if(n == 0) continue; mklower(token[0]); p = token[0]; if(*p == '!'){ p++; bang = 1; } else bang = 0; if(*p == '='){ if(newpattern(Texact, p+1, bang) < 0) return -1; } else if(*p == '~'){ if(newpattern(Tregexp, p+1, bang) < 0) return -1; } else if(strcmp(token[0], "#include") == 0 && n == 2) readpatterns(token[1]); } Bterm(b); return 0; } /* fuck, shit, bugger, damn */ void regerror(char*) { } /* * check lower case version of address agains patterns */ static Pattern* checkaddr(char *arg) { Pattern *p; Reprog *rp; String *s; s = s_copy(arg); mklower(s_to_c(s)); for(p = patterns; p != nil; p = p->next) switch(p->type){ case Texact: if(strcmp(p->arg, s_to_c(s)) == 0){ free(s); return p; } break; case Tregexp: rp = regcomp(p->arg); if(rp == nil) continue; if(regexec(rp, s_to_c(s), nil, 0)){ free(rp); free(s); return p; } free(rp); break; } s_free(s); return 0; } static char* check(int argc, char **argv) { int i; Addr *a; Pattern *p; int matchedbang; matchedbang = 0; for(i = 0; i < argc; i++){ a = readaddrs(argv[i], nil); for(; a != nil; a = a->next){ p = checkaddr(a->val); if(p == nil) continue; if(p->bang) matchedbang = 1; else return nil; } } if(matchedbang) return "!match"; else return "no match"; } /* * add anything that isn't already matched, all matches are lower case */ static char* add(char *pp, int argc, char **argv) { int fd, i; String *s; char *cp; Addr *a; a = nil; for(i = 0; i < argc; i++) a = readaddrs(argv[i], a); fd = open(pp, OWRITE); seek(fd, 0, 2); for(; a != nil; a = a->next){ if(checkaddr(a->val)) continue; s = simplify(a->val); cp = s_to_c(s); fprint(fd, "%q\t%q\n", cp, a->val); if(*cp == '=') newpattern(Texact, cp+1, 0); else if(*cp == '~') newpattern(Tregexp, cp+1, 0); s_free(s); } close(fd); return nil; } void main(int argc, char **argv) { char *patternpath; ARGBEGIN { case 'd': debug++; break; } ARGEND; quotefmtinstall(); if(argc < 3) usage(); patternpath = argv[1]; readpatterns(patternpath); if(strcmp(argv[0], "add") == 0) exits(add(patternpath, argc-2, argv+2)); else if(strcmp(argv[0], "check") == 0) exits(check(argc-2, argv+2)); else usage(); }