Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <regexp.h>
5 #include <thread.h>
6 #include <ctype.h>
7 #include <plumb.h>
8 #include "plumber.h"
10 typedef struct Input Input;
11 typedef struct Var Var;
13 struct Input
14 {
15 char *file; /* name of file */
16 Biobuf *fd; /* input buffer, if from real file */
17 uchar *s; /* input string, if from /mnt/plumb/rules */
18 uchar *end; /* end of input string */
19 int lineno;
20 Input *next; /* file to read after EOF on this one */
21 };
23 struct Var
24 {
25 char *name;
26 char *value;
27 char *qvalue;
28 };
30 static int parsing;
31 static int nvars;
32 static Var *vars;
33 static Input *input;
35 static char ebuf[4096];
37 char *badports[] =
38 {
39 ".",
40 "..",
41 "send",
42 nil
43 };
45 char *objects[] =
46 {
47 "arg",
48 "attr",
49 "data",
50 "dst",
51 "plumb",
52 "src",
53 "type",
54 "wdir",
55 nil
56 };
58 char *verbs[] =
59 {
60 "add",
61 "client",
62 "delete",
63 "is",
64 "isdir",
65 "isfile",
66 "matches",
67 "set",
68 "start",
69 "to",
70 nil
71 };
73 static void
74 printinputstackrev(Input *in)
75 {
76 if(in == nil)
77 return;
78 printinputstackrev(in->next);
79 fprint(2, "%s:%d: ", in->file, in->lineno);
80 }
82 void
83 printinputstack(void)
84 {
85 printinputstackrev(input);
86 }
88 static void
89 pushinput(char *name, int fd, uchar *str)
90 {
91 Input *in;
92 int depth;
94 depth = 0;
95 for(in=input; in; in=in->next)
96 if(depth++ >= 10) /* prevent deep C stack in plumber and bad include structure */
97 parseerror("include stack too deep; max 10");
99 in = emalloc(sizeof(Input));
100 in->file = estrdup(name);
101 in->next = input;
102 input = in;
103 if(str)
104 in->s = str;
105 else{
106 in->fd = emalloc(sizeof(Biobuf));
107 if(Binit(in->fd, fd, OREAD) < 0)
108 parseerror("can't initialize Bio for rules file: %r");
113 int
114 popinput(void)
116 Input *in;
118 in = input;
119 if(in == nil)
120 return 0;
121 input = in->next;
122 if(in->fd){
123 Bterm(in->fd);
124 free(in->fd);
126 free(in);
127 return 1;
130 static int
131 getc(void)
133 if(input == nil)
134 return Beof;
135 if(input->fd)
136 return Bgetc(input->fd);
137 if(input->s < input->end)
138 return *(input->s)++;
139 return -1;
142 char*
143 getline(void)
145 static int n = 0;
146 static char *s /*, *incl*/;
147 int c, i;
149 i = 0;
150 for(;;){
151 c = getc();
152 if(c < 0)
153 return nil;
154 if(i == n){
155 n += 100;
156 s = erealloc(s, n);
158 if(c<0 || c=='\0' || c=='\n')
159 break;
160 s[i++] = c;
162 s[i] = '\0';
163 return s;
166 int
167 lookup(char *s, char *tab[])
169 int i;
171 for(i=0; tab[i]!=nil; i++)
172 if(strcmp(s, tab[i])==0)
173 return i;
174 return -1;
177 Var*
178 lookupvariable(char *s, int n)
180 int i;
182 for(i=0; i<nvars; i++)
183 if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0)
184 return vars+i;
185 return nil;
188 char*
189 variable(char *s, int n)
191 Var *var;
193 var = lookupvariable(s, n);
194 if(var)
195 return var->qvalue;
196 return nil;
199 void
200 setvariable(char *s, int n, char *val, char *qval)
202 Var *var;
204 var = lookupvariable(s, n);
205 if(var){
206 free(var->value);
207 free(var->qvalue);
208 }else{
209 vars = erealloc(vars, (nvars+1)*sizeof(Var));
210 var = vars+nvars++;
211 var->name = emalloc(n+1);
212 memmove(var->name, s, n);
214 var->value = estrdup(val);
215 var->qvalue = estrdup(qval);
218 static char*
219 scanvarname(char *s)
221 if(isalpha((uchar)*s) || *s=='_')
222 do
223 s++;
224 while(isalnum((uchar)*s) || *s=='_');
225 return s;
228 static char*
229 nonnil(char *s)
231 if(s == nil)
232 return "";
233 return s;
236 static char*
237 filename(Exec *e, char *name)
239 static char *buf; /* rock to hold value so we don't leak the strings */
241 free(buf);
242 /* if name is defined, used it */
243 if(name!=nil && name[0]!='\0'){
244 buf = estrdup(name);
245 return cleanname(buf);
247 /* if data is an absolute file name, or wdir is empty, use it */
248 if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
249 buf = estrdup(e->msg->data);
250 return cleanname(buf);
252 buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
253 sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
254 return cleanname(buf);
257 char*
258 dollar(Exec *e, char *s, int *namelen)
260 int n;
261 static char *abuf;
263 *namelen = 1;
264 if(e!=nil && '0'<=s[0] && s[0]<='9')
265 return nonnil(e->match[s[0]-'0']);
267 n = scanvarname(s)-s;
268 *namelen = n;
269 if(n == 0)
270 return nil;
272 if(e != nil){
273 if(n == 3){
274 if(memcmp(s, "src", 3) == 0)
275 return nonnil(e->msg->src);
276 if(memcmp(s, "dst", 3) == 0)
277 return nonnil(e->msg->dst);
278 if(memcmp(s, "dir", 3) == 0)
279 return filename(e, e->dir);
281 if(n == 4){
282 if(memcmp(s, "attr", 4) == 0){
283 free(abuf);
284 abuf = plumbpackattr(e->msg->attr);
285 return nonnil(abuf);
287 if(memcmp(s, "data", 4) == 0)
288 return nonnil(e->msg->data);
289 if(memcmp(s, "file", 4) == 0)
290 return filename(e, e->file);
291 if(memcmp(s, "type", 4) == 0)
292 return nonnil(e->msg->type);
293 if(memcmp(s, "wdir", 3) == 0)
294 return nonnil(e->msg->wdir);
298 return variable(s, n);
301 static void
302 ruleerror(char *msg)
304 if(parsing){
305 parsing = 0;
306 parseerror("%s", msg);
308 error("%s", msg);
311 /* expand one blank-terminated string, processing quotes and $ signs */
312 char*
313 expand(Exec *e, char *s, char **ends)
315 char *p, *ep, *val;
316 int namelen, vallen, quoting, inputleft;
318 p = ebuf;
319 ep = ebuf+sizeof ebuf-1;
320 quoting = 0;
321 for(;;){
322 inputleft = (*s!='\0' && (quoting || (*s!=' ' && *s!='\t')));
323 if(!inputleft || p==ep)
324 break;
325 if(*s == '\''){
326 s++;
327 if(!quoting)
328 quoting = 1;
329 else if(*s == '\''){
330 *p++ = '\'';
331 s++;
332 }else
333 quoting = 0;
334 continue;
336 if(quoting || *s!='$'){
337 *p++ = *s++;
338 continue;
340 s++;
341 val = dollar(e, s, &namelen);
342 if(val == nil){
343 *p++ = '$';
344 continue;
346 vallen = strlen(val);
347 if(ep-p < vallen)
348 break;
349 strcpy(p, val);
350 p += vallen;
351 s += namelen;
353 if(inputleft)
354 ruleerror("expanded string too long");
355 else if(quoting)
356 ruleerror("runaway quoted string literal");
357 if(ends)
358 *ends = s;
359 *p = '\0';
360 return ebuf;
363 void
364 regerror(char *msg)
366 ruleerror(msg);
369 void
370 parserule(Rule *r)
372 r->qarg = estrdup(expand(nil, r->arg, nil));
373 switch(r->obj){
374 case OArg:
375 case OAttr:
376 case OData:
377 case ODst:
378 case OType:
379 case OWdir:
380 case OSrc:
381 if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
382 parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
383 if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
384 parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
385 if(r->verb == VMatches){
386 r->regex = regcomp(r->qarg);
387 return;
389 break;
390 case OPlumb:
391 if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
392 parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
393 break;
397 int
398 assignment(char *p)
400 char *var, *qval;
401 int n;
403 var = p;
404 p = scanvarname(p);
405 n = p-var;
406 if(n == 0)
407 return 0;
408 while(*p==' ' || *p=='\t')
409 p++;
410 if(*p++ != '=')
411 return 0;
412 while(*p==' ' || *p=='\t')
413 p++;
414 qval = expand(nil, p, nil);
415 setvariable(var, n, p, qval);
416 return 1;
419 int
420 include(char *s)
422 char *t, *args[3], buf[128];
423 int n, fd;
425 if(strncmp(s, "include", 7) != 0)
426 return 0;
427 /* either an include or an error */
428 n = tokenize(s, args, nelem(args));
429 if(n < 2)
430 goto Err;
431 if(strcmp(args[0], "include") != 0)
432 goto Err;
433 if(args[1][0] == '#')
434 goto Err;
435 if(n>2 && args[2][0] != '#')
436 goto Err;
437 t = args[1];
438 fd = open(t, OREAD);
439 if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
440 snprint(buf, sizeof buf, "#9/plumb/%s", t);
441 t = unsharp(buf);
442 fd = open(t, OREAD);
444 if(fd < 0)
445 parseerror("can't open %s for inclusion", t);
446 pushinput(t, fd, nil);
447 return 1;
449 Err:
450 parseerror("malformed include statement");
451 return 0;
454 Rule*
455 readrule(int *eof)
457 Rule *rp;
458 char *line, *p;
459 char *word;
461 Top:
462 line = getline();
463 if(line == nil){
464 /*
465 * if input is from string, and bytes remain (input->end is within string),
466 * morerules() will pop input and save remaining data. otherwise pop
467 * the stack here, and if there's more input, keep reading.
468 */
469 if((input!=nil && input->end==nil) && popinput())
470 goto Top;
471 *eof = 1;
472 return nil;
474 input->lineno++;
476 for(p=line; *p==' ' || *p=='\t'; p++)
478 if(*p=='\0' || *p=='#') /* empty or comment line */
479 return nil;
481 if(include(p))
482 goto Top;
484 if(assignment(p))
485 return nil;
487 rp = emalloc(sizeof(Rule));
489 /* object */
490 for(word=p; *p!=' ' && *p!='\t'; p++)
491 if(*p == '\0')
492 parseerror("malformed rule");
493 *p++ = '\0';
494 rp->obj = lookup(word, objects);
495 if(rp->obj < 0){
496 if(strcmp(word, "kind") == 0) /* backwards compatibility */
497 rp->obj = OType;
498 else
499 parseerror("unknown object %s", word);
502 /* verb */
503 while(*p==' ' || *p=='\t')
504 p++;
505 for(word=p; *p!=' ' && *p!='\t'; p++)
506 if(*p == '\0')
507 parseerror("malformed rule");
508 *p++ = '\0';
509 rp->verb = lookup(word, verbs);
510 if(rp->verb < 0)
511 parseerror("unknown verb %s", word);
513 /* argument */
514 while(*p==' ' || *p=='\t')
515 p++;
516 if(*p == '\0')
517 parseerror("malformed rule");
518 rp->arg = estrdup(p);
520 parserule(rp);
522 return rp;
525 void
526 freerule(Rule *r)
528 free(r->arg);
529 free(r->qarg);
530 free(r->regex);
533 void
534 freerules(Rule **r)
536 while(*r)
537 freerule(*r++);
540 void
541 freeruleset(Ruleset *rs)
543 freerules(rs->pat);
544 free(rs->pat);
545 freerules(rs->act);
546 free(rs->act);
547 free(rs->port);
548 free(rs);
551 Ruleset*
552 readruleset(void)
554 Ruleset *rs;
555 Rule *r;
556 int eof, inrule, i, ncmd;
557 char *plan9root;
559 plan9root = get9root();
560 if(plan9root)
561 setvariable("plan9", 5, plan9root, plan9root);
563 Again:
564 eof = 0;
565 rs = emalloc(sizeof(Ruleset));
566 rs->pat = emalloc(sizeof(Rule*));
567 rs->act = emalloc(sizeof(Rule*));
568 inrule = 0;
569 ncmd = 0;
570 for(;;){
571 r = readrule(&eof);
572 if(eof)
573 break;
574 if(r==nil){
575 if(inrule)
576 break;
577 continue;
579 inrule = 1;
580 switch(r->obj){
581 case OArg:
582 case OAttr:
583 case OData:
584 case ODst:
585 case OType:
586 case OWdir:
587 case OSrc:
588 rs->npat++;
589 rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
590 rs->pat[rs->npat-1] = r;
591 rs->pat[rs->npat] = nil;
592 break;
593 case OPlumb:
594 rs->nact++;
595 rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
596 rs->act[rs->nact-1] = r;
597 rs->act[rs->nact] = nil;
598 if(r->verb == VTo){
599 if(rs->npat>0 && rs->port != nil) /* npat==0 implies port declaration */
600 parseerror("too many ports");
601 if(lookup(r->qarg, badports) >= 0)
602 parseerror("illegal port name %s", r->qarg);
603 rs->port = estrdup(r->qarg);
604 }else
605 ncmd++; /* start or client rule */
606 break;
609 if(ncmd > 1){
610 freeruleset(rs);
611 parseerror("ruleset has more than one client or start action");
613 if(rs->npat>0 && rs->nact>0)
614 return rs;
615 if(rs->npat==0 && rs->nact==0){
616 freeruleset(rs);
617 return nil;
619 if(rs->nact==0 || rs->port==nil){
620 freeruleset(rs);
621 parseerror("ruleset must have patterns and actions");
622 return nil;
625 /* declare ports */
626 for(i=0; i<rs->nact; i++)
627 if(rs->act[i]->verb != VTo){
628 freeruleset(rs);
629 parseerror("ruleset must have actions");
630 return nil;
632 for(i=0; i<rs->nact; i++)
633 addport(rs->act[i]->qarg);
634 freeruleset(rs);
635 goto Again;
638 Ruleset**
639 readrules(char *name, int fd)
641 Ruleset *rs, **rules;
642 int n;
644 parsing = 1;
645 pushinput(name, fd, nil);
646 rules = emalloc(sizeof(Ruleset*));
647 for(n=0; (rs=readruleset())!=nil; n++){
648 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
649 rules[n] = rs;
650 rules[n+1] = nil;
652 popinput();
653 parsing = 0;
654 return rules;
657 char*
658 concat(char *s, char *t)
660 if(t == nil)
661 return s;
662 if(s == nil)
663 s = estrdup(t);
664 else{
665 s = erealloc(s, strlen(s)+strlen(t)+1);
666 strcat(s, t);
668 return s;
671 char*
672 printpat(Rule *r)
674 char *s;
676 s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
677 sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
678 return s;
681 char*
682 printvar(Var *v)
684 char *s;
686 s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
687 sprint(s, "%s=%s\n\n", v->name, v->value);
688 return s;
691 char*
692 printrule(Ruleset *r)
694 int i;
695 char *s;
697 s = nil;
698 for(i=0; i<r->npat; i++)
699 s = concat(s, printpat(r->pat[i]));
700 for(i=0; i<r->nact; i++)
701 s = concat(s, printpat(r->act[i]));
702 s = concat(s, "\n");
703 return s;
706 char*
707 printport(char *port)
709 char *s;
711 s = nil;
712 s = concat(s, "plumb to ");
713 s = concat(s, port);
714 s = concat(s, "\n");
715 return s;
718 char*
719 printrules(void)
721 int i;
722 char *s;
724 s = nil;
725 for(i=0; i<nvars; i++)
726 s = concat(s, printvar(&vars[i]));
727 for(i=0; i<nports; i++)
728 s = concat(s, printport(ports[i]));
729 s = concat(s, "\n");
730 for(i=0; rules[i]; i++)
731 s = concat(s, printrule(rules[i]));
732 return s;
735 char*
736 stringof(char *s, int n)
738 char *t;
740 t = emalloc(n+1);
741 memmove(t, s, n);
742 return t;
745 uchar*
746 morerules(uchar *text, int done)
748 int n;
749 Ruleset *rs;
750 uchar *otext, *s, *endofrule;
752 pushinput("<rules input>", -1, text);
753 if(done)
754 input->end = text+strlen((char*)text);
755 else{
756 /*
757 * Help user by sending any full rules to parser so any parse errors will
758 * occur on write rather than close. A heuristic will do: blank line ends rule.
759 */
760 endofrule = nil;
761 for(s=text; *s!='\0'; s++)
762 if(*s=='\n' && *(s+1)=='\n')
763 endofrule = s+2;
764 if(endofrule == nil)
765 return text;
766 input->end = endofrule;
768 for(n=0; rules[n]; n++)
770 while((rs=readruleset()) != nil){
771 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
772 rules[n++] = rs;
773 rules[n] = nil;
775 otext =text;
776 if(input == nil)
777 text = (uchar*)estrdup("");
778 else
779 text = (uchar*)estrdup((char*)input->end);
780 popinput();
781 free(otext);
782 return text;
785 char*
786 writerules(char *s, int n)
788 static uchar *text;
789 char *tmp;
791 free(lasterror);
792 lasterror = nil;
793 parsing = 1;
794 if(setjmp(parsejmp) == 0){
795 tmp = stringof(s, n);
796 text = (uchar*)concat((char*)text, tmp);
797 free(tmp);
798 text = morerules(text, n==0);
800 if(s == nil){
801 free(text);
802 text = nil;
804 parsing = 0;
805 makeports(rules);
806 return lasterror;