Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <regexp.h>
4 #include <libsec.h>
5 #include <libString.h>
6 #include <bio.h>
7 #include "dat.h"
9 int debug;
11 enum
12 {
13 Tregexp= (1<<0), /* ~ */
14 Texact= (1<<1), /* = */
15 };
17 typedef struct Pattern Pattern;
18 struct Pattern
19 {
20 Pattern *next;
21 int type;
22 char *arg;
23 int bang;
24 };
26 String *patternpath;
27 Pattern *patterns;
28 String *mbox;
30 static void
31 usage(void)
32 {
33 fprint(2, "usage: %s 'check|add' patternfile addr [addr*]\n", argv0);
34 exits("usage");
35 }
37 /*
38 * convert string to lower case
39 */
40 static void
41 mklower(char *p)
42 {
43 int c;
45 for(; *p; p++){
46 c = *p;
47 if(c <= 'Z' && c >= 'A')
48 *p = c - 'A' + 'a';
49 }
50 }
52 /*
53 * simplify an address, reduce to a domain
54 */
55 static String*
56 simplify(char *addr)
57 {
58 int dots;
59 char *p, *at;
60 String *s;
62 mklower(addr);
63 at = strchr(addr, '@');
64 if(at == nil){
65 /* local address, make it an exact match */
66 s = s_copy("=");
67 s_append(s, addr);
68 return s;
69 }
71 /* copy up to the '@' sign */
72 at++;
73 s = s_copy("~");
74 for(p = addr; p < at; p++){
75 if(strchr(".*+?(|)\\[]^$", *p))
76 s_putc(s, '\\');
77 s_putc(s, *p);
78 }
80 /* just any address matching the two most significant domain elements */
81 s_append(s, "(.*\\.)?");
82 p = addr+strlen(addr);
83 dots = 0;
84 for(; p > at; p--){
85 if(*p != '.')
86 continue;
87 if(dots++ > 0){
88 p++;
89 break;
90 }
91 }
92 for(; *p; p++){
93 if(strchr(".*+?(|)\\[]^$", *p) != 0)
94 s_putc(s, '\\');
95 s_putc(s, *p);
96 }
97 s_terminate(s);
99 return s;
102 /*
103 * link patterns in order
104 */
105 static int
106 newpattern(int type, char *arg, int bang)
108 Pattern *p;
109 static Pattern *last;
111 mklower(arg);
113 p = mallocz(sizeof *p, 1);
114 if(p == nil)
115 return -1;
116 if(type == Tregexp){
117 p->arg = malloc(strlen(arg)+3);
118 if(p->arg == nil){
119 free(p);
120 return -1;
122 p->arg[0] = 0;
123 strcat(p->arg, "^");
124 strcat(p->arg, arg);
125 strcat(p->arg, "$");
126 } else {
127 p->arg = strdup(arg);
128 if(p->arg == nil){
129 free(p);
130 return -1;
133 p->type = type;
134 p->bang = bang;
135 if(last == nil)
136 patterns = p;
137 else
138 last->next = p;
139 last = p;
141 return 0;
144 /*
145 * patterns are either
146 * ~ regular expression
147 * = exact match string
149 * all comparisons are case insensitive
150 */
151 static int
152 readpatterns(char *path)
154 Biobuf *b;
155 char *p;
156 char *token[2];
157 int n;
158 int bang;
160 b = Bopen(path, OREAD);
161 if(b == nil)
162 return -1;
163 while((p = Brdline(b, '\n')) != nil){
164 p[Blinelen(b)-1] = 0;
165 n = tokenize(p, token, 2);
166 if(n == 0)
167 continue;
169 mklower(token[0]);
170 p = token[0];
171 if(*p == '!'){
172 p++;
173 bang = 1;
174 } else
175 bang = 0;
177 if(*p == '='){
178 if(newpattern(Texact, p+1, bang) < 0)
179 return -1;
180 } else if(*p == '~'){
181 if(newpattern(Tregexp, p+1, bang) < 0)
182 return -1;
183 } else if(strcmp(token[0], "#include") == 0 && n == 2)
184 readpatterns(token[1]);
186 Bterm(b);
187 return 0;
190 /* fuck, shit, bugger, damn */
191 void regerror(char *err)
193 USED(err);
196 /*
197 * check lower case version of address agains patterns
198 */
199 static Pattern*
200 checkaddr(char *arg)
202 Pattern *p;
203 Reprog *rp;
204 String *s;
206 s = s_copy(arg);
207 mklower(s_to_c(s));
209 for(p = patterns; p != nil; p = p->next)
210 switch(p->type){
211 case Texact:
212 if(strcmp(p->arg, s_to_c(s)) == 0){
213 free(s);
214 return p;
216 break;
217 case Tregexp:
218 rp = regcomp(p->arg);
219 if(rp == nil)
220 continue;
221 if(regexec(rp, s_to_c(s), nil, 0)){
222 free(rp);
223 free(s);
224 return p;
226 free(rp);
227 break;
229 s_free(s);
230 return 0;
232 static char*
233 check(int argc, char **argv)
235 int i;
236 Addr *a;
237 Pattern *p;
238 int matchedbang;
240 matchedbang = 0;
241 for(i = 0; i < argc; i++){
242 a = readaddrs(argv[i], nil);
243 for(; a != nil; a = a->next){
244 p = checkaddr(a->val);
245 if(p == nil)
246 continue;
247 if(p->bang)
248 matchedbang = 1;
249 else
250 return nil;
253 if(matchedbang)
254 return "!match";
255 else
256 return "no match";
259 /*
260 * add anything that isn't already matched, all matches are lower case
261 */
262 static char*
263 add(char *pp, int argc, char **argv)
265 int fd, i;
266 String *s;
267 char *cp;
268 Addr *a;
270 a = nil;
271 for(i = 0; i < argc; i++)
272 a = readaddrs(argv[i], a);
274 fd = open(pp, OWRITE);
275 seek(fd, 0, 2);
276 for(; a != nil; a = a->next){
277 if(checkaddr(a->val))
278 continue;
279 s = simplify(a->val);
280 cp = s_to_c(s);
281 fprint(fd, "%q\t%q\n", cp, a->val);
282 if(*cp == '=')
283 newpattern(Texact, cp+1, 0);
284 else if(*cp == '~')
285 newpattern(Tregexp, cp+1, 0);
286 s_free(s);
288 close(fd);
289 return nil;
292 void
293 main(int argc, char **argv)
295 char *patternpath;
297 ARGBEGIN {
298 case 'd':
299 debug++;
300 break;
301 } ARGEND;
303 quotefmtinstall();
305 if(argc < 3)
306 usage();
308 patternpath = argv[1];
309 readpatterns(patternpath);
310 if(strcmp(argv[0], "add") == 0)
311 exits(add(patternpath, argc-2, argv+2));
312 else if(strcmp(argv[0], "check") == 0)
313 exits(check(argc-2, argv+2));
314 else
315 usage();