]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/plumb/match.c
upas/fs: remove useless loop in rf822()
[plan9front.git] / sys / src / cmd / plumb / match.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <regexp.h>
5 #include <thread.h>
6 #include <plumb.h>
7 #include "plumber.h"
8
9 int
10 verbis(int obj, Plumbmsg *m, Rule *r)
11 {
12         switch(obj){
13         default:
14                 fprint(2, "unimplemented 'is' object %d\n", obj);
15                 break;
16         case OData:
17                 return strcmp(m->data, r->qarg) == 0;
18         case ODst:
19                 return strcmp(m->dst, r->qarg) == 0;
20         case OType:
21                 return strcmp(m->type, r->qarg) == 0;
22         case OWdir:
23                 return strcmp(m->wdir, r->qarg) == 0;
24         case OSrc:
25                 return strcmp(m->src, r->qarg) == 0;
26         }
27         return 0;
28 }
29
30 static void
31 setvar(Resub rs[10], char *match[10])
32 {
33         int i, n;
34
35         for(i=0; i<10; i++){
36                 free(match[i]);
37                 match[i] = nil;
38         }
39         for(i=0; i<10 && rs[i].sp!=nil; i++){
40                 n = rs[i].ep-rs[i].sp;
41                 match[i] = emalloc(n+1);
42                 memmove(match[i], rs[i].sp, n);
43                 match[i][n] = '\0';
44         }
45 }
46
47 int
48 clickmatch(Reprog *re, char *text, Resub rs[10], int click)
49 {
50         char *clickp;
51         int i, w;
52         Rune r;
53
54         /* click is in characters, not bytes */
55         for(i=0; i<click && text[i]!='\0'; i+=w)
56                 w = chartorune(&r, text+i);
57         clickp = text+i;
58         for(i=0; i<=click; i++){
59                 memset(rs, 0, 10*sizeof(Resub));
60                 if(regexec(re, text+i, rs, 10))
61                         if(rs[0].sp<=clickp && clickp<=rs[0].ep)
62                                 return 1;
63         }
64         return 0;
65 }
66
67 int
68 verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
69 {
70         Resub rs[10];
71         char *clickval, *alltext;
72         int p0, p1, ntext;
73
74         memset(rs, 0, sizeof rs);
75         ntext = -1;
76         switch(obj){
77         default:
78                 fprint(2, "unimplemented 'matches' object %d\n", obj);
79                 break;
80         case OData:
81                 clickval = plumblookup(m->attr, "click");
82                 if(clickval == nil){
83                         alltext = m->data;
84                         ntext = m->ndata;
85                         goto caseAlltext;
86                 }
87                 if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
88                         break;
89                 p0 = rs[0].sp - m->data;
90                 p1 = rs[0].ep - m->data;
91                 if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
92                         break;
93                 e->clearclick = 1;
94                 e->setdata = 1;
95                 e->p0 = p0;
96                 e->p1 = p1;
97                 setvar(rs, e->match);
98                 return 1;
99         case ODst:
100                 alltext = m->dst;
101                 goto caseAlltext;
102         case OType:
103                 alltext = m->type;
104                 goto caseAlltext;
105         case OWdir:
106                 alltext = m->wdir;
107                 goto caseAlltext;
108         case OSrc:
109                 alltext = m->src;
110                 /* fall through */
111         caseAlltext:
112                 /* must match full text */
113                 if(ntext < 0)
114                         ntext = strlen(alltext);
115                 if(!regexec(r->regex, alltext, rs, 10) || rs[0].sp!=alltext || rs[0].ep!=alltext+ntext)
116                         break;
117                 setvar(rs, e->match);
118                 return 1;
119         }
120         return 0;
121 }
122
123 int
124 isfile(char *file, ulong maskon, ulong maskoff)
125 {
126         Dir *d;
127         int mode;
128
129         d = dirstat(file);
130         if(d == nil)
131                 return 0;
132         mode = d->mode;
133         free(d);
134         if((mode & maskon) == 0)
135                 return 0;
136         if(mode & maskoff)
137                 return 0;
138         return 1;
139 }
140
141 char*
142 absolute(char *dir, char *file)
143 {
144         char *p;
145
146         if(file[0] == '/')
147                 return estrdup(file);
148         p = emalloc(strlen(dir)+1+strlen(file)+1);
149         sprint(p, "%s/%s", dir, file);
150         return cleanname(p);
151 }
152
153 int
154 verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
155 {
156         char *file;
157
158         switch(obj){
159         default:
160                 fprint(2, "unimplemented 'isfile' object %d\n", obj);
161                 break;
162         case OArg:
163                 file = absolute(m->wdir, expand(e, r->arg, nil));
164                 if(isfile(file, maskon, maskoff)){
165                         *var = file;
166                         return 1;
167                 }
168                 free(file);
169                 break;
170         case OData:
171         case OWdir:
172                 file = absolute(m->wdir, obj==OData? m->data : m->wdir);
173                 if(isfile(file, maskon, maskoff)){
174                         *var = file;
175                         return 1;
176                 }
177                 free(file);
178                 break;
179         }
180         return 0;
181 }
182
183 int
184 verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
185 {
186         char *new;
187
188         switch(obj){
189         default:
190                 fprint(2, "unimplemented 'is' object %d\n", obj);
191                 break;
192         case OData:
193                 new = estrdup(expand(e, r->arg, nil));
194                 m->ndata = strlen(new);
195                 free(m->data);
196                 m->data = new;
197                 e->p0 = -1;
198                 e->p1 = -1;
199                 e->setdata = 0;
200                 return 1;
201         case ODst:
202                 new = estrdup(expand(e, r->arg, nil));
203                 free(m->dst);
204                 m->dst = new;
205                 return 1;
206         case OType:
207                 new = estrdup(expand(e, r->arg, nil));
208                 free(m->type);
209                 m->type = new;
210                 return 1;
211         case OWdir:
212                 new = estrdup(expand(e, r->arg, nil));
213                 free(m->wdir);
214                 m->wdir = new;
215                 return 1;
216         case OSrc:
217                 new = estrdup(expand(e, r->arg, nil));
218                 free(m->src);
219                 m->src = new;
220                 return 1;
221         }
222         return 0;
223 }
224
225 int
226 verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
227 {
228         switch(obj){
229         default:
230                 fprint(2, "unimplemented 'add' object %d\n", obj);
231                 break;
232         case OAttr:
233                 m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
234                 return 1;
235         }
236         return 0;
237 }
238
239 int
240 verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
241 {
242         char *a;
243
244         switch(obj){
245         default:
246                 fprint(2, "unimplemented 'delete' object %d\n", obj);
247                 break;
248         case OAttr:
249                 a = expand(e, r->arg, nil);
250                 if(plumblookup(m->attr, a) == nil)
251                         break;
252                 m->attr = plumbdelattr(m->attr, a);
253                 return 1;
254         }
255         return 0;
256 }
257
258 int
259 matchpat(Plumbmsg *m, Exec *e, Rule *r)
260 {
261         switch(r->verb){
262         default:
263                 fprint(2, "unimplemented verb %d\n", r->verb);
264                 break;
265         case VAdd:
266                 return verbadd(r->obj, m, r, e);
267         case VDelete:
268                 return verbdelete(r->obj, m, r, e);
269         case VIs:
270                 return verbis(r->obj, m, r);
271         case VIsdir:
272                 return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
273         case VIsfile:
274                 return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
275         case VMatches:
276                 return verbmatches(r->obj, m, r, e);
277         case VSet:
278                 verbset(r->obj, m, r, e);
279                 return 1;
280         }
281         return 0;
282 }
283
284 void
285 freeexec(Exec *exec)
286 {
287         int i;
288
289         if(exec == nil)
290                 return;
291         free(exec->dir);
292         free(exec->file);
293         for(i=0; i<10; i++)
294                 free(exec->match[i]);
295         free(exec);
296 }
297
298 Exec*
299 newexec(Plumbmsg *m)
300 {
301         Exec *exec;
302         
303         exec = emalloc(sizeof(Exec));
304         exec->msg = m;
305         exec->p0 = -1;
306         exec->p1 = -1;
307         return exec;
308 }
309
310 void
311 rewrite(Plumbmsg *m, Exec *e)
312 {
313         Plumbattr *a, *prev;
314
315         if(e->clearclick){
316                 prev = nil;
317                 for(a=m->attr; a!=nil; a=a->next){
318                         if(strcmp(a->name, "click") == 0){
319                                 if(prev == nil)
320                                         m->attr = a->next;
321                                 else
322                                         prev->next = a->next;
323                                 free(a->name);
324                                 free(a->value); 
325                                 free(a);
326                                 break;
327                         }
328                         prev = a;
329                 }
330                 if(e->setdata){
331                         free(m->data);
332                         m->data = estrdup(expand(e, "$0", nil));
333                         m->ndata = strlen(m->data);
334                 }
335         }
336 }
337
338 char**
339 buildargv(char *s, Exec *e)
340 {
341         char **av;
342         int ac;
343
344         ac = 0;
345         av = nil;
346         for(;;){
347                 av = erealloc(av, (ac+1) * sizeof(char*));
348                 av[ac] = nil;
349                 while(*s==' ' || *s=='\t')
350                         s++;
351                 if(*s == '\0')
352                         break;
353                 av[ac++] = estrdup(expand(e, s, &s));
354         }
355         return av;
356 }
357
358 Exec*
359 matchruleset(Plumbmsg *m, Ruleset *rs)
360 {
361         int i;
362         Exec *exec;
363
364         if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
365                 return nil;
366         exec = newexec(m);
367         for(i=0; i<rs->npat; i++)
368                 if(!matchpat(m, exec, rs->pat[i])){
369                         freeexec(exec);
370                         return nil;
371                 }
372         if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
373                 free(m->dst);
374                 m->dst = estrdup(rs->port);
375         }
376         rewrite(m, exec);
377         return exec;
378 }
379
380 enum
381 {
382         NARGS           = 100,
383         NARGCHAR        = 8*1024,
384         EXECSTACK       = 4096+(NARGS+1)*sizeof(char*)+NARGCHAR
385 };
386
387 /* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
388 void
389 stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
390 {
391         int i, n;
392         char *s, *a;
393
394         s = args;
395         for(i=0; i<NARGS; i++){
396                 a = inargv[i];
397                 if(a == nil)
398                         break;
399                 n = strlen(a)+1;
400                 if((s-args)+n >= NARGCHAR)      /* too many characters */
401                         break;
402                 argv[i] = s;
403                 memmove(s, a, n);
404                 s += n;
405                 free(a);
406         }
407         argv[i] = nil;
408 }
409
410
411 void
412 execproc(void *v)
413 {
414         char **av;
415         char buf[1024], *args[NARGS+1], argc[NARGCHAR];
416
417         rfork(RFFDG);
418         close(0);
419         open("/dev/null", OREAD);
420         av = v;
421         stackargv(av, args, argc);
422         free(av);
423         procexec(nil, args[0], args);
424         if(args[0][0]!='/' && strncmp(args[0], "./", 2)!=0 && strncmp(args[0], "../", 3)!=0)
425                 snprint(buf, sizeof buf, "/bin/%s", args[0]);
426         procexec(nil, buf, args);
427         threadexits("can't exec");
428 }
429
430 char*
431 startup(Ruleset *rs, Exec *e)
432 {
433         char **argv;
434         int i;
435
436         if(rs != nil)
437                 for(i=0; i<rs->nact; i++){
438                         if(rs->act[i]->verb == VStart)
439                                 goto Found;
440                         if(rs->act[i]->verb == VClient){
441                                 if(e->msg->dst==nil || e->msg->dst[0]=='\0')
442                                         return "no port for \"client\" rule";
443                                 e->holdforclient = 1;
444                                 goto Found;
445                         }
446                 }
447         return "no start action for plumb message";
448
449 Found:
450         argv = buildargv(rs->act[i]->arg, e);
451         if(argv[0] == nil)
452                 return "empty argument list";
453         proccreate(execproc, argv, EXECSTACK);
454         return nil;
455 }