10 typedef struct Input Input;
11 typedef struct Var Var;
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 */
20 Input *next; /* file to read after EOF on this one */
35 static char ebuf[4096];
74 printinputstackrev(Input *in)
78 printinputstackrev(in->next);
79 fprint(2, "%s:%d: ", in->file, in->lineno);
85 printinputstackrev(input);
89 pushinput(char *name, int fd, uchar *str)
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);
106 in->fd = emalloc(sizeof(Biobuf));
107 if(Binit(in->fd, fd, OREAD) < 0)
108 parseerror("can't initialize Bio for rules file: %r");
136 return Bgetc(input->fd);
137 if(input->s < input->end)
138 return *(input->s)++;
146 static char *s /*, *incl*/;
158 if(c<0 || c=='\0' || c=='\n')
167 lookup(char *s, char *tab[])
171 for(i=0; tab[i]!=nil; i++)
172 if(strcmp(s, tab[i])==0)
178 lookupvariable(char *s, int n)
182 for(i=0; i<nvars; i++)
183 if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0)
189 variable(char *s, int n)
193 var = lookupvariable(s, n);
200 setvariable(char *s, int n, char *val, char *qval)
204 var = lookupvariable(s, n);
209 vars = erealloc(vars, (nvars+1)*sizeof(Var));
211 var->name = emalloc(n+1);
212 memmove(var->name, s, n);
214 var->value = estrdup(val);
215 var->qvalue = estrdup(qval);
221 if(isalpha((uchar)*s) || *s=='_')
224 while(isalnum((uchar)*s) || *s=='_');
237 filename(Exec *e, char *name)
239 static char *buf; /* rock to hold value so we don't leak the strings */
242 /* if name is defined, used it */
243 if(name!=nil && name[0]!='\0'){
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);
258 dollar(Exec *e, char *s, int *namelen)
264 if(e!=nil && '0'<=s[0] && s[0]<='9')
265 return nonnil(e->match[s[0]-'0']);
267 n = scanvarname(s)-s;
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);
282 if(memcmp(s, "attr", 4) == 0){
284 abuf = plumbpackattr(e->msg->attr);
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);
306 parseerror("%s", msg);
311 /* expand one blank-terminated string, processing quotes and $ signs */
313 expand(Exec *e, char *s, char **ends)
316 int namelen, vallen, quoting, inputleft;
319 ep = ebuf+sizeof ebuf-1;
322 inputleft = (*s!='\0' && (quoting || (*s!=' ' && *s!='\t')));
323 if(!inputleft || p==ep)
336 if(quoting || *s!='$'){
341 val = dollar(e, s, &namelen);
346 vallen = strlen(val);
354 ruleerror("expanded string too long");
356 ruleerror("runaway quoted string literal");
372 r->qarg = estrdup(expand(nil, r->arg, nil));
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);
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]);
408 while(*p==' ' || *p=='\t')
412 while(*p==' ' || *p=='\t')
414 qval = expand(nil, p, nil);
415 setvariable(var, n, p, qval);
422 char *t, *args[3], buf[128];
425 if(strncmp(s, "include", 7) != 0)
427 /* either an include or an error */
428 n = tokenize(s, args, nelem(args));
431 if(strcmp(args[0], "include") != 0)
433 if(args[1][0] == '#')
435 if(n>2 && args[2][0] != '#')
439 if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
440 snprint(buf, sizeof buf, "#9/plumb/%s", t);
445 parseerror("can't open %s for inclusion", t);
446 pushinput(t, fd, nil);
450 parseerror("malformed include statement");
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.
469 if((input!=nil && input->end==nil) && popinput())
476 for(p=line; *p==' ' || *p=='\t'; p++)
478 if(*p=='\0' || *p=='#') /* empty or comment line */
487 rp = emalloc(sizeof(Rule));
490 for(word=p; *p!=' ' && *p!='\t'; p++)
492 parseerror("malformed rule");
494 rp->obj = lookup(word, objects);
496 if(strcmp(word, "kind") == 0) /* backwards compatibility */
499 parseerror("unknown object %s", word);
503 while(*p==' ' || *p=='\t')
505 for(word=p; *p!=' ' && *p!='\t'; p++)
507 parseerror("malformed rule");
509 rp->verb = lookup(word, verbs);
511 parseerror("unknown verb %s", word);
514 while(*p==' ' || *p=='\t')
517 parseerror("malformed rule");
518 rp->arg = estrdup(p);
541 freeruleset(Ruleset *rs)
556 int eof, inrule, i, ncmd;
559 plan9root = get9root();
561 setvariable("plan9", 5, plan9root, plan9root);
565 rs = emalloc(sizeof(Ruleset));
566 rs->pat = emalloc(sizeof(Rule*));
567 rs->act = emalloc(sizeof(Rule*));
589 rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
590 rs->pat[rs->npat-1] = r;
591 rs->pat[rs->npat] = nil;
595 rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
596 rs->act[rs->nact-1] = r;
597 rs->act[rs->nact] = nil;
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);
605 ncmd++; /* start or client rule */
611 parseerror("ruleset has more than one client or start action");
613 if(rs->npat>0 && rs->nact>0)
615 if(rs->npat==0 && rs->nact==0){
619 if(rs->nact==0 || rs->port==nil){
621 parseerror("ruleset must have patterns and actions");
626 for(i=0; i<rs->nact; i++)
627 if(rs->act[i]->verb != VTo){
629 parseerror("ruleset must have actions");
632 for(i=0; i<rs->nact; i++)
633 addport(rs->act[i]->qarg);
639 readrules(char *name, int fd)
641 Ruleset *rs, **rules;
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*));
658 concat(char *s, char *t)
665 s = erealloc(s, strlen(s)+strlen(t)+1);
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);
686 s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
687 sprint(s, "%s=%s\n\n", v->name, v->value);
692 printrule(Ruleset *r)
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]));
707 printport(char *port)
712 s = concat(s, "plumb to ");
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]));
730 for(i=0; rules[i]; i++)
731 s = concat(s, printrule(rules[i]));
736 stringof(char *s, int n)
746 morerules(uchar *text, int done)
750 uchar *otext, *s, *endofrule;
752 pushinput("<rules input>", -1, text);
754 input->end = text+strlen((char*)text);
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.
761 for(s=text; *s!='\0'; s++)
762 if(*s=='\n' && *(s+1)=='\n')
766 input->end = endofrule;
768 for(n=0; rules[n]; n++)
770 while((rs=readruleset()) != nil){
771 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
777 text = (uchar*)estrdup("");
779 text = (uchar*)estrdup((char*)input->end);
786 writerules(char *s, int n)
794 if(setjmp(parsejmp) == 0){
795 tmp = stringof(s, n);
796 text = (uchar*)concat((char*)text, tmp);
798 text = morerules(text, n==0);