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