URI:
       trules.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       trules.c (13351B)
       ---
            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"
            9 
           10 typedef struct Input Input;
           11 typedef struct Var Var;
           12 
           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 };
           22 
           23 struct Var
           24 {
           25         char        *name;
           26         char        *value;
           27         char *qvalue;
           28 };
           29 
           30 static int                parsing;
           31 static int                nvars;
           32 static Var                *vars;
           33 static Input        *input;
           34 
           35 static char         ebuf[4096];
           36 
           37 char *badports[] =
           38 {
           39         ".",
           40         "..",
           41         "send",
           42         nil
           43 };
           44 
           45 char *objects[] =
           46 {
           47         "arg",
           48         "attr",
           49         "data",
           50         "dst",
           51         "plumb",
           52         "src",
           53         "type",
           54         "wdir",
           55         nil
           56 };
           57 
           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 };
           72 
           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 }
           81 
           82 void
           83 printinputstack(void)
           84 {
           85         printinputstackrev(input);
           86 }
           87 
           88 static void
           89 pushinput(char *name, int fd, uchar *str)
           90 {
           91         Input *in;
           92         int depth;
           93 
           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");
           98 
           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");
          109         }
          110 
          111 }
          112 
          113 int
          114 popinput(void)
          115 {
          116         Input *in;
          117 
          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);
          125         }
          126         free(in);
          127         return 1;
          128 }
          129 
          130 static int
          131 getc(void)
          132 {
          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;
          140 }
          141 
          142 char*
          143 getline(void)
          144 {
          145         static int n = 0;
          146         static char *s /*, *incl*/;
          147         int c, i;
          148 
          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);
          157                 }
          158                 if(c<0 || c=='\0' || c=='\n')
          159                         break;
          160                 s[i++] = c;
          161         }
          162         s[i] = '\0';
          163         return s;
          164 }
          165 
          166 int
          167 lookup(char *s, char *tab[])
          168 {
          169         int i;
          170 
          171         for(i=0; tab[i]!=nil; i++)
          172                 if(strcmp(s, tab[i])==0)
          173                         return i;
          174         return -1;
          175 }
          176 
          177 Var*
          178 lookupvariable(char *s, int n)
          179 {
          180         int i;
          181 
          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;
          186 }
          187 
          188 char*
          189 variable(char *s, int n)
          190 {
          191         Var *var;
          192 
          193         var = lookupvariable(s, n);
          194         if(var)
          195                 return var->qvalue;
          196         return nil;
          197 }
          198 
          199 void
          200 setvariable(char  *s, int n, char *val, char *qval)
          201 {
          202         Var *var;
          203 
          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);
          213         }
          214         var->value = estrdup(val);
          215         var->qvalue = estrdup(qval);
          216 }
          217 
          218 static char*
          219 nonnil(char *s)
          220 {
          221         if(s == nil)
          222                 return "";
          223         return s;
          224 }
          225 
          226 static char*
          227 filename(Exec *e, char *name)
          228 {
          229         static char *buf;        /* rock to hold value so we don't leak the strings */
          230 
          231         free(buf);
          232         /* if name is defined, used it */
          233         if(name!=nil && name[0]!='\0'){
          234                 buf = estrdup(name);
          235                 return cleanname(buf);
          236         }
          237         /* if data is an absolute file name, or wdir is empty, use it */
          238         if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
          239                 buf = estrdup(e->msg->data);
          240                 return cleanname(buf);
          241         }
          242         buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
          243         sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
          244         return cleanname(buf);
          245 }
          246 
          247 char*
          248 dollar(Exec *e, char *s, int *namelen)
          249 {
          250         int n;
          251         static char *abuf;
          252         char *t;
          253 
          254         *namelen = 1;
          255         if(e!=nil && '0'<=s[0] && s[0]<='9')
          256                 return nonnil(e->match[s[0]-'0']);
          257 
          258         for(t=s; isalnum((uchar)*t); t++)
          259                 ;
          260         n = t-s;
          261         *namelen = n;
          262 
          263         if(e != nil){
          264                 if(n == 3){
          265                         if(memcmp(s, "src", 3) == 0)
          266                                 return nonnil(e->msg->src);
          267                         if(memcmp(s, "dst", 3) == 0)
          268                                 return nonnil(e->msg->dst);
          269                         if(memcmp(s, "dir", 3) == 0)
          270                                 return filename(e, e->dir);
          271                 }
          272                 if(n == 4){
          273                         if(memcmp(s, "attr", 4) == 0){
          274                                 free(abuf);
          275                                 abuf = plumbpackattr(e->msg->attr);
          276                                 return nonnil(abuf);
          277                         }
          278                         if(memcmp(s, "data", 4) == 0)
          279                                 return nonnil(e->msg->data);
          280                         if(memcmp(s, "file", 4) == 0)
          281                                 return filename(e, e->file);
          282                         if(memcmp(s, "type", 4) == 0)
          283                                 return nonnil(e->msg->type);
          284                         if(memcmp(s, "wdir", 3) == 0)
          285                                 return nonnil(e->msg->wdir);
          286                 }
          287         }
          288 
          289         return variable(s, n);
          290 }
          291 
          292 /* expand one blank-terminated string, processing quotes and $ signs */
          293 char*
          294 expand(Exec *e, char *s, char **ends)
          295 {
          296         char *p, *ep, *val;
          297         int namelen, quoting;
          298 
          299         p = ebuf;
          300         ep = ebuf+sizeof ebuf-1;
          301         quoting = 0;
          302         while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){
          303                 if(*s == '\''){
          304                         s++;
          305                         if(!quoting)
          306                                 quoting = 1;
          307                         else  if(*s == '\''){
          308                                 *p++ = '\'';
          309                                 s++;
          310                         }else
          311                                 quoting = 0;
          312                         continue;
          313                 }
          314                 if(quoting || *s!='$'){
          315                         *p++ = *s++;
          316                         continue;
          317                 }
          318                 s++;
          319                 val = dollar(e, s, &namelen);
          320                 if(val == nil){
          321                         *p++ = '$';
          322                         continue;
          323                 }
          324                 if(ep-p < strlen(val))
          325                         return "string-too-long";
          326                 strcpy(p, val);
          327                 p += strlen(val);
          328                 s += namelen;
          329         }
          330         if(ends)
          331                 *ends = s;
          332         *p = '\0';
          333         return ebuf;
          334 }
          335 
          336 void
          337 regerror(char *msg)
          338 {
          339         if(parsing){
          340                 parsing = 0;
          341                 parseerror("%s", msg);
          342         }
          343         error("%s", msg);
          344 }
          345 
          346 void
          347 parserule(Rule *r)
          348 {
          349         r->qarg = estrdup(expand(nil, r->arg, nil));
          350         switch(r->obj){
          351         case OArg:
          352         case OAttr:
          353         case OData:
          354         case ODst:
          355         case OType:
          356         case OWdir:
          357         case OSrc:
          358                 if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
          359                         parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
          360                 if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
          361                         parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
          362                 if(r->verb == VMatches){
          363                         r->regex = regcomp(r->qarg);
          364                         return;
          365                 }
          366                 break;
          367         case OPlumb:
          368                 if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
          369                         parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
          370                 break;
          371         }
          372 }
          373 
          374 int
          375 assignment(char *p)
          376 {
          377         char *var, *qval;
          378         int n;
          379 
          380         if(!isalpha((uchar)p[0]))
          381                 return 0;
          382         for(var=p; isalnum((uchar)*p); p++)
          383                 ;
          384         n = p-var;
          385         while(*p==' ' || *p=='\t')
          386                         p++;
          387         if(*p++ != '=')
          388                 return 0;
          389         while(*p==' ' || *p=='\t')
          390                         p++;
          391         qval = expand(nil, p, nil);
          392         setvariable(var, n, p, qval);
          393         return 1;
          394 }
          395 
          396 int
          397 include(char *s)
          398 {
          399         char *t, *args[3], buf[128];
          400         int n, fd;
          401 
          402         if(strncmp(s, "include", 7) != 0)
          403                 return 0;
          404         /* either an include or an error */
          405         n = tokenize(s, args, nelem(args));
          406         if(n < 2)
          407                 goto Err;
          408         if(strcmp(args[0], "include") != 0)
          409                 goto Err;
          410         if(args[1][0] == '#')
          411                 goto Err;
          412         if(n>2 && args[2][0] != '#')
          413                 goto Err;
          414         t = args[1];
          415         fd = open(t, OREAD);
          416         if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
          417                 snprint(buf, sizeof buf, "#9/plumb/%s", t);
          418                 t = unsharp(buf);
          419                 fd = open(t, OREAD);
          420         }
          421         if(fd < 0)
          422                 parseerror("can't open %s for inclusion", t);
          423         pushinput(t, fd, nil);
          424         return 1;
          425 
          426     Err:
          427         parseerror("malformed include statement");
          428         return 0;
          429 }
          430 
          431 Rule*
          432 readrule(int *eof)
          433 {
          434         Rule *rp;
          435         char *line, *p;
          436         char *word;
          437 
          438 Top:
          439         line = getline();
          440         if(line == nil){
          441                 /*
          442                  * if input is from string, and bytes remain (input->end is within string),
          443                  * morerules() will pop input and save remaining data.  otherwise pop
          444                  * the stack here, and if there's more input, keep reading.
          445                  */
          446                 if((input!=nil && input->end==nil) && popinput())
          447                         goto Top;
          448                 *eof = 1;
          449                 return nil;
          450         }
          451         input->lineno++;
          452 
          453         for(p=line; *p==' ' || *p=='\t'; p++)
          454                 ;
          455         if(*p=='\0' || *p=='#')        /* empty or comment line */
          456                 return nil;
          457 
          458         if(include(p))
          459                 goto Top;
          460 
          461         if(assignment(p))
          462                 return nil;
          463 
          464         rp = emalloc(sizeof(Rule));
          465 
          466         /* object */
          467         for(word=p; *p!=' ' && *p!='\t'; p++)
          468                 if(*p == '\0')
          469                         parseerror("malformed rule");
          470         *p++ = '\0';
          471         rp->obj = lookup(word, objects);
          472         if(rp->obj < 0){
          473                 if(strcmp(word, "kind") == 0)        /* backwards compatibility */
          474                         rp->obj = OType;
          475                 else
          476                         parseerror("unknown object %s", word);
          477         }
          478 
          479         /* verb */
          480         while(*p==' ' || *p=='\t')
          481                 p++;
          482         for(word=p; *p!=' ' && *p!='\t'; p++)
          483                 if(*p == '\0')
          484                         parseerror("malformed rule");
          485         *p++ = '\0';
          486         rp->verb = lookup(word, verbs);
          487         if(rp->verb < 0)
          488                 parseerror("unknown verb %s", word);
          489 
          490         /* argument */
          491         while(*p==' ' || *p=='\t')
          492                 p++;
          493         if(*p == '\0')
          494                 parseerror("malformed rule");
          495         rp->arg = estrdup(p);
          496 
          497         parserule(rp);
          498 
          499         return rp;
          500 }
          501 
          502 void
          503 freerule(Rule *r)
          504 {
          505         free(r->arg);
          506         free(r->qarg);
          507         free(r->regex);
          508 }
          509 
          510 void
          511 freerules(Rule **r)
          512 {
          513         while(*r)
          514                 freerule(*r++);
          515 }
          516 
          517 void
          518 freeruleset(Ruleset *rs)
          519 {
          520         freerules(rs->pat);
          521         free(rs->pat);
          522         freerules(rs->act);
          523         free(rs->act);
          524         free(rs->port);
          525         free(rs);
          526 }
          527 
          528 Ruleset*
          529 readruleset(void)
          530 {
          531         Ruleset *rs;
          532         Rule *r;
          533         int eof, inrule, i, ncmd;
          534         char *plan9root;
          535 
          536         plan9root = get9root();
          537         if(plan9root)
          538                 setvariable("plan9", 5, plan9root, plan9root);
          539 
          540    Again:
          541         eof = 0;
          542         rs = emalloc(sizeof(Ruleset));
          543         rs->pat = emalloc(sizeof(Rule*));
          544         rs->act = emalloc(sizeof(Rule*));
          545         inrule = 0;
          546         ncmd = 0;
          547         for(;;){
          548                 r = readrule(&eof);
          549                 if(eof)
          550                         break;
          551                 if(r==nil){
          552                         if(inrule)
          553                                 break;
          554                         continue;
          555                 }
          556                 inrule = 1;
          557                 switch(r->obj){
          558                 case OArg:
          559                 case OAttr:
          560                 case OData:
          561                 case ODst:
          562                 case OType:
          563                 case OWdir:
          564                 case OSrc:
          565                         rs->npat++;
          566                         rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
          567                         rs->pat[rs->npat-1] = r;
          568                         rs->pat[rs->npat] = nil;
          569                         break;
          570                 case OPlumb:
          571                         rs->nact++;
          572                         rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
          573                         rs->act[rs->nact-1] = r;
          574                         rs->act[rs->nact] = nil;
          575                         if(r->verb == VTo){
          576                                 if(rs->npat>0 && rs->port != nil)        /* npat==0 implies port declaration */
          577                                         parseerror("too many ports");
          578                                 if(lookup(r->qarg, badports) >= 0)
          579                                         parseerror("illegal port name %s", r->qarg);
          580                                 rs->port = estrdup(r->qarg);
          581                         }else
          582                                 ncmd++;        /* start or client rule */
          583                         break;
          584                 }
          585         }
          586         if(ncmd > 1){
          587                 freeruleset(rs);
          588                 parseerror("ruleset has more than one client or start action");
          589         }
          590         if(rs->npat>0 && rs->nact>0)
          591                 return rs;
          592         if(rs->npat==0 && rs->nact==0){
          593                 freeruleset(rs);
          594                 return nil;
          595         }
          596         if(rs->nact==0 || rs->port==nil){
          597                 freeruleset(rs);
          598                 parseerror("ruleset must have patterns and actions");
          599                 return nil;
          600         }
          601 
          602         /* declare ports */
          603         for(i=0; i<rs->nact; i++)
          604                 if(rs->act[i]->verb != VTo){
          605                         freeruleset(rs);
          606                         parseerror("ruleset must have actions");
          607                         return nil;
          608                 }
          609         for(i=0; i<rs->nact; i++)
          610                 addport(rs->act[i]->qarg);
          611         freeruleset(rs);
          612         goto Again;
          613 }
          614 
          615 Ruleset**
          616 readrules(char *name, int fd)
          617 {
          618         Ruleset *rs, **rules;
          619         int n;
          620 
          621         parsing = 1;
          622         pushinput(name, fd, nil);
          623         rules = emalloc(sizeof(Ruleset*));
          624         for(n=0; (rs=readruleset())!=nil; n++){
          625                 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
          626                 rules[n] = rs;
          627                 rules[n+1] = nil;
          628         }
          629         popinput();
          630         parsing = 0;
          631         return rules;
          632 }
          633 
          634 char*
          635 concat(char *s, char *t)
          636 {
          637         if(t == nil)
          638                 return s;
          639         if(s == nil)
          640                 s = estrdup(t);
          641         else{
          642                 s = erealloc(s, strlen(s)+strlen(t)+1);
          643                 strcat(s, t);
          644         }
          645         return s;
          646 }
          647 
          648 char*
          649 printpat(Rule *r)
          650 {
          651         char *s;
          652 
          653         s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
          654         sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
          655         return s;
          656 }
          657 
          658 char*
          659 printvar(Var *v)
          660 {
          661         char *s;
          662 
          663         s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
          664         sprint(s, "%s=%s\n\n", v->name, v->value);
          665         return s;
          666 }
          667 
          668 char*
          669 printrule(Ruleset *r)
          670 {
          671         int i;
          672         char *s;
          673 
          674         s = nil;
          675         for(i=0; i<r->npat; i++)
          676                 s = concat(s, printpat(r->pat[i]));
          677         for(i=0; i<r->nact; i++)
          678                 s = concat(s, printpat(r->act[i]));
          679         s = concat(s, "\n");
          680         return s;
          681 }
          682 
          683 char*
          684 printport(char *port)
          685 {
          686         char *s;
          687 
          688         s = nil;
          689         s = concat(s, "plumb to ");
          690         s = concat(s, port);
          691         s = concat(s, "\n");
          692         return s;
          693 }
          694 
          695 char*
          696 printrules(void)
          697 {
          698         int i;
          699         char *s;
          700 
          701         s = nil;
          702         for(i=0; i<nvars; i++)
          703                 s = concat(s, printvar(&vars[i]));
          704         for(i=0; i<nports; i++)
          705                 s = concat(s, printport(ports[i]));
          706         s = concat(s, "\n");
          707         for(i=0; rules[i]; i++)
          708                 s = concat(s, printrule(rules[i]));
          709         return s;
          710 }
          711 
          712 char*
          713 stringof(char *s, int n)
          714 {
          715         char *t;
          716 
          717         t = emalloc(n+1);
          718         memmove(t, s, n);
          719         return t;
          720 }
          721 
          722 uchar*
          723 morerules(uchar *text, int done)
          724 {
          725         int n;
          726         Ruleset *rs;
          727         uchar *otext, *s, *endofrule;
          728 
          729         pushinput("<rules input>", -1, text);
          730         if(done)
          731                 input->end = text+strlen((char*)text);
          732         else{
          733                 /*
          734                  * Help user by sending any full rules to parser so any parse errors will
          735                  * occur on write rather than close. A heuristic will do: blank line ends rule.
          736                  */
          737                 endofrule = nil;
          738                 for(s=text; *s!='\0'; s++)
          739                         if(*s=='\n' && *(s+1)=='\n')
          740                                 endofrule = s+2;
          741                 if(endofrule == nil)
          742                         return text;
          743                 input->end = endofrule;
          744         }
          745         for(n=0; rules[n]; n++)
          746                 ;
          747         while((rs=readruleset()) != nil){
          748                 rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
          749                 rules[n++] = rs;
          750                 rules[n] = nil;
          751         }
          752         otext =text;
          753         if(input == nil)
          754                 text = (uchar*)estrdup("");
          755         else
          756                 text = (uchar*)estrdup((char*)input->end);
          757         popinput();
          758         free(otext);
          759         return text;
          760 }
          761 
          762 char*
          763 writerules(char *s, int n)
          764 {
          765         static uchar *text;
          766         char *tmp;
          767 
          768         free(lasterror);
          769         lasterror = nil;
          770         parsing = 1;
          771         if(setjmp(parsejmp) == 0){
          772                 tmp = stringof(s, n);
          773                 text = (uchar*)concat((char*)text, tmp);
          774                 free(tmp);
          775                 text = morerules(text, n==0);
          776         }
          777         if(s == nil){
          778                 free(text);
          779                 text = nil;
          780         }
          781         parsing = 0;
          782         makeports(rules);
          783         return lasterror;
          784 }