blob: 1913c6ded4dc8ef2c19043a4bb0fbcc4598e317f [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <regexp.h>
#include <libsec.h>
#include <libString.h>
#include <bio.h>
#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 'check|add' patternfile addr [addr*]\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;
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 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 */
s_append(s, "(.*\\.)?");
p = addr+strlen(addr);
dots = 0;
for(; p > at; p--){
if(*p != '.')
continue;
if(dots++ > 0){
p++;
break;
}
}
for(; *p; p++){
if(strchr(".*+?(|)\\[]^$", *p) != 0)
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 *err)
{
USED(err);
}
/*
* 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();
}