Plan 9 version, nothing tweaked yet.
This commit is contained in:
parent
7763a61a35
commit
b8c14089d8
7 changed files with 2596 additions and 0 deletions
779
src/cmd/plumb/rules.c
Normal file
779
src/cmd/plumb/rules.c
Normal file
|
|
@ -0,0 +1,779 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <regexp.h>
|
||||
#include <thread.h>
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include "plumber.h"
|
||||
|
||||
typedef struct Input Input;
|
||||
typedef struct Var Var;
|
||||
|
||||
struct Input
|
||||
{
|
||||
char *file; /* name of file */
|
||||
Biobuf *fd; /* input buffer, if from real file */
|
||||
uchar *s; /* input string, if from /mnt/plumb/rules */
|
||||
uchar *end; /* end of input string */
|
||||
int lineno;
|
||||
Input *next; /* file to read after EOF on this one */
|
||||
};
|
||||
|
||||
struct Var
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
char *qvalue;
|
||||
};
|
||||
|
||||
static int parsing;
|
||||
static int nvars;
|
||||
static Var *vars;
|
||||
static Input *input;
|
||||
|
||||
static char ebuf[4096];
|
||||
|
||||
char *badports[] =
|
||||
{
|
||||
".",
|
||||
"..",
|
||||
"send",
|
||||
nil
|
||||
};
|
||||
|
||||
char *objects[] =
|
||||
{
|
||||
"arg",
|
||||
"attr",
|
||||
"data",
|
||||
"dst",
|
||||
"plumb",
|
||||
"src",
|
||||
"type",
|
||||
"wdir",
|
||||
nil
|
||||
};
|
||||
|
||||
char *verbs[] =
|
||||
{
|
||||
"add",
|
||||
"client",
|
||||
"delete",
|
||||
"is",
|
||||
"isdir",
|
||||
"isfile",
|
||||
"matches",
|
||||
"set",
|
||||
"start",
|
||||
"to",
|
||||
nil
|
||||
};
|
||||
|
||||
static void
|
||||
printinputstackrev(Input *in)
|
||||
{
|
||||
if(in == nil)
|
||||
return;
|
||||
printinputstackrev(in->next);
|
||||
fprint(2, "%s:%d: ", in->file, in->lineno);
|
||||
}
|
||||
|
||||
void
|
||||
printinputstack(void)
|
||||
{
|
||||
printinputstackrev(input);
|
||||
}
|
||||
|
||||
static void
|
||||
pushinput(char *name, int fd, uchar *str)
|
||||
{
|
||||
Input *in;
|
||||
int depth;
|
||||
|
||||
depth = 0;
|
||||
for(in=input; in; in=in->next)
|
||||
if(depth++ >= 10) /* prevent deep C stack in plumber and bad include structure */
|
||||
parseerror("include stack too deep; max 10");
|
||||
|
||||
in = emalloc(sizeof(Input));
|
||||
in->file = estrdup(name);
|
||||
in->next = input;
|
||||
input = in;
|
||||
if(str)
|
||||
in->s = str;
|
||||
else{
|
||||
in->fd = emalloc(sizeof(Biobuf));
|
||||
if(Binit(in->fd, fd, OREAD) < 0)
|
||||
parseerror("can't initialize Bio for rules file: %r");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
popinput(void)
|
||||
{
|
||||
Input *in;
|
||||
|
||||
in = input;
|
||||
if(in == nil)
|
||||
return 0;
|
||||
input = in->next;
|
||||
if(in->fd){
|
||||
Bterm(in->fd);
|
||||
free(in->fd);
|
||||
}
|
||||
free(in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
getc(void)
|
||||
{
|
||||
if(input == nil)
|
||||
return Beof;
|
||||
if(input->fd)
|
||||
return Bgetc(input->fd);
|
||||
if(input->s < input->end)
|
||||
return *(input->s)++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char*
|
||||
getline(void)
|
||||
{
|
||||
static int n = 0;
|
||||
static char *s, *incl;
|
||||
int c, i;
|
||||
|
||||
i = 0;
|
||||
for(;;){
|
||||
c = getc();
|
||||
if(c < 0)
|
||||
return nil;
|
||||
if(i == n){
|
||||
n += 100;
|
||||
s = erealloc(s, n);
|
||||
}
|
||||
if(c<0 || c=='\0' || c=='\n')
|
||||
break;
|
||||
s[i++] = c;
|
||||
}
|
||||
s[i] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
lookup(char *s, char *tab[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; tab[i]!=nil; i++)
|
||||
if(strcmp(s, tab[i])==0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Var*
|
||||
lookupvariable(char *s, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nvars; i++)
|
||||
if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0)
|
||||
return vars+i;
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
variable(char *s, int n)
|
||||
{
|
||||
Var *var;
|
||||
|
||||
var = lookupvariable(s, n);
|
||||
if(var)
|
||||
return var->qvalue;
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
setvariable(char *s, int n, char *val, char *qval)
|
||||
{
|
||||
Var *var;
|
||||
|
||||
var = lookupvariable(s, n);
|
||||
if(var){
|
||||
free(var->value);
|
||||
free(var->qvalue);
|
||||
}else{
|
||||
vars = erealloc(vars, (nvars+1)*sizeof(Var));
|
||||
var = vars+nvars++;
|
||||
var->name = emalloc(n+1);
|
||||
memmove(var->name, s, n);
|
||||
}
|
||||
var->value = estrdup(val);
|
||||
var->qvalue = estrdup(qval);
|
||||
}
|
||||
|
||||
static char*
|
||||
nonnil(char *s)
|
||||
{
|
||||
if(s == nil)
|
||||
return "";
|
||||
return s;
|
||||
}
|
||||
|
||||
static char*
|
||||
filename(Exec *e, char *name)
|
||||
{
|
||||
static char *buf; /* rock to hold value so we don't leak the strings */
|
||||
|
||||
free(buf);
|
||||
/* if name is defined, used it */
|
||||
if(name!=nil && name[0]!='\0'){
|
||||
buf = estrdup(name);
|
||||
return cleanname(buf);
|
||||
}
|
||||
/* if data is an absolute file name, or wdir is empty, use it */
|
||||
if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
|
||||
buf = estrdup(e->msg->data);
|
||||
return cleanname(buf);
|
||||
}
|
||||
buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
|
||||
sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
|
||||
return cleanname(buf);
|
||||
}
|
||||
|
||||
char*
|
||||
dollar(Exec *e, char *s, int *namelen)
|
||||
{
|
||||
int n;
|
||||
static char *abuf;
|
||||
char *t;
|
||||
|
||||
*namelen = 1;
|
||||
if(e!=nil && '0'<=s[0] && s[0]<='9')
|
||||
return nonnil(e->match[s[0]-'0']);
|
||||
|
||||
for(t=s; isalnum(*t); t++)
|
||||
;
|
||||
n = t-s;
|
||||
*namelen = n;
|
||||
|
||||
if(e != nil){
|
||||
if(n == 3){
|
||||
if(memcmp(s, "src", 3) == 0)
|
||||
return nonnil(e->msg->src);
|
||||
if(memcmp(s, "dst", 3) == 0)
|
||||
return nonnil(e->msg->dst);
|
||||
if(memcmp(s, "dir", 3) == 0)
|
||||
return filename(e, e->dir);
|
||||
}
|
||||
if(n == 4){
|
||||
if(memcmp(s, "attr", 4) == 0){
|
||||
free(abuf);
|
||||
abuf = plumbpackattr(e->msg->attr);
|
||||
return nonnil(abuf);
|
||||
}
|
||||
if(memcmp(s, "data", 4) == 0)
|
||||
return nonnil(e->msg->data);
|
||||
if(memcmp(s, "file", 4) == 0)
|
||||
return filename(e, e->file);
|
||||
if(memcmp(s, "type", 4) == 0)
|
||||
return nonnil(e->msg->type);
|
||||
if(memcmp(s, "wdir", 3) == 0)
|
||||
return nonnil(e->msg->wdir);
|
||||
}
|
||||
}
|
||||
|
||||
return variable(s, n);
|
||||
}
|
||||
|
||||
/* expand one blank-terminated string, processing quotes and $ signs */
|
||||
char*
|
||||
expand(Exec *e, char *s, char **ends)
|
||||
{
|
||||
char *p, *ep, *val;
|
||||
int namelen, quoting;
|
||||
|
||||
p = ebuf;
|
||||
ep = ebuf+sizeof ebuf-1;
|
||||
quoting = 0;
|
||||
while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){
|
||||
if(*s == '\''){
|
||||
s++;
|
||||
if(!quoting)
|
||||
quoting = 1;
|
||||
else if(*s == '\''){
|
||||
*p++ = '\'';
|
||||
s++;
|
||||
}else
|
||||
quoting = 0;
|
||||
continue;
|
||||
}
|
||||
if(quoting || *s!='$'){
|
||||
*p++ = *s++;
|
||||
continue;
|
||||
}
|
||||
s++;
|
||||
val = dollar(e, s, &namelen);
|
||||
if(val == nil){
|
||||
*p++ = '$';
|
||||
continue;
|
||||
}
|
||||
if(ep-p < strlen(val))
|
||||
return "string-too-long";
|
||||
strcpy(p, val);
|
||||
p += strlen(val);
|
||||
s += namelen;
|
||||
}
|
||||
if(ends)
|
||||
*ends = s;
|
||||
*p = '\0';
|
||||
return ebuf;
|
||||
}
|
||||
|
||||
void
|
||||
regerror(char *msg)
|
||||
{
|
||||
if(parsing){
|
||||
parsing = 0;
|
||||
parseerror("%s", msg);
|
||||
}
|
||||
error("%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
parserule(Rule *r)
|
||||
{
|
||||
r->qarg = estrdup(expand(nil, r->arg, nil));
|
||||
switch(r->obj){
|
||||
case OArg:
|
||||
case OAttr:
|
||||
case OData:
|
||||
case ODst:
|
||||
case OType:
|
||||
case OWdir:
|
||||
case OSrc:
|
||||
if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
if(r->verb == VMatches){
|
||||
r->regex = regcomp(r->qarg);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case OPlumb:
|
||||
if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
assignment(char *p)
|
||||
{
|
||||
char *var, *qval;
|
||||
int n;
|
||||
|
||||
if(!isalpha(p[0]))
|
||||
return 0;
|
||||
for(var=p; isalnum(*p); p++)
|
||||
;
|
||||
n = p-var;
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
if(*p++ != '=')
|
||||
return 0;
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
qval = expand(nil, p, nil);
|
||||
setvariable(var, n, p, qval);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
include(char *s)
|
||||
{
|
||||
char *t, *args[3], buf[128];
|
||||
int n, fd;
|
||||
|
||||
if(strncmp(s, "include", 7) != 0)
|
||||
return 0;
|
||||
/* either an include or an error */
|
||||
n = tokenize(s, args, nelem(args));
|
||||
if(n < 2)
|
||||
goto Err;
|
||||
if(strcmp(args[0], "include") != 0)
|
||||
goto Err;
|
||||
if(args[1][0] == '#')
|
||||
goto Err;
|
||||
if(n>2 && args[2][0] != '#')
|
||||
goto Err;
|
||||
t = args[1];
|
||||
fd = open(t, OREAD);
|
||||
if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
|
||||
snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t);
|
||||
t = buf;
|
||||
fd = open(t, OREAD);
|
||||
}
|
||||
if(fd < 0)
|
||||
parseerror("can't open %s for inclusion", t);
|
||||
pushinput(t, fd, nil);
|
||||
return 1;
|
||||
|
||||
Err:
|
||||
parseerror("malformed include statement");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Rule*
|
||||
readrule(int *eof)
|
||||
{
|
||||
Rule *rp;
|
||||
char *line, *p;
|
||||
char *word;
|
||||
|
||||
Top:
|
||||
line = getline();
|
||||
if(line == nil){
|
||||
/*
|
||||
* if input is from string, and bytes remain (input->end is within string),
|
||||
* morerules() will pop input and save remaining data. otherwise pop
|
||||
* the stack here, and if there's more input, keep reading.
|
||||
*/
|
||||
if((input!=nil && input->end==nil) && popinput())
|
||||
goto Top;
|
||||
*eof = 1;
|
||||
return nil;
|
||||
}
|
||||
input->lineno++;
|
||||
|
||||
for(p=line; *p==' ' || *p=='\t'; p++)
|
||||
;
|
||||
if(*p=='\0' || *p=='#') /* empty or comment line */
|
||||
return nil;
|
||||
|
||||
if(include(p))
|
||||
goto Top;
|
||||
|
||||
if(assignment(p))
|
||||
return nil;
|
||||
|
||||
rp = emalloc(sizeof(Rule));
|
||||
|
||||
/* object */
|
||||
for(word=p; *p!=' ' && *p!='\t'; p++)
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
*p++ = '\0';
|
||||
rp->obj = lookup(word, objects);
|
||||
if(rp->obj < 0){
|
||||
if(strcmp(word, "kind") == 0) /* backwards compatibility */
|
||||
rp->obj = OType;
|
||||
else
|
||||
parseerror("unknown object %s", word);
|
||||
}
|
||||
|
||||
/* verb */
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
for(word=p; *p!=' ' && *p!='\t'; p++)
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
*p++ = '\0';
|
||||
rp->verb = lookup(word, verbs);
|
||||
if(rp->verb < 0)
|
||||
parseerror("unknown verb %s", word);
|
||||
|
||||
/* argument */
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
rp->arg = estrdup(p);
|
||||
|
||||
parserule(rp);
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
void
|
||||
freerule(Rule *r)
|
||||
{
|
||||
free(r->arg);
|
||||
free(r->qarg);
|
||||
free(r->regex);
|
||||
}
|
||||
|
||||
void
|
||||
freerules(Rule **r)
|
||||
{
|
||||
while(*r)
|
||||
freerule(*r++);
|
||||
}
|
||||
|
||||
void
|
||||
freeruleset(Ruleset *rs)
|
||||
{
|
||||
freerules(rs->pat);
|
||||
free(rs->pat);
|
||||
freerules(rs->act);
|
||||
free(rs->act);
|
||||
free(rs->port);
|
||||
free(rs);
|
||||
}
|
||||
|
||||
Ruleset*
|
||||
readruleset(void)
|
||||
{
|
||||
Ruleset *rs;
|
||||
Rule *r;
|
||||
int eof, inrule, i, ncmd;
|
||||
|
||||
Again:
|
||||
eof = 0;
|
||||
rs = emalloc(sizeof(Ruleset));
|
||||
rs->pat = emalloc(sizeof(Rule*));
|
||||
rs->act = emalloc(sizeof(Rule*));
|
||||
inrule = 0;
|
||||
ncmd = 0;
|
||||
for(;;){
|
||||
r = readrule(&eof);
|
||||
if(eof)
|
||||
break;
|
||||
if(r==nil){
|
||||
if(inrule)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
inrule = 1;
|
||||
switch(r->obj){
|
||||
case OArg:
|
||||
case OAttr:
|
||||
case OData:
|
||||
case ODst:
|
||||
case OType:
|
||||
case OWdir:
|
||||
case OSrc:
|
||||
rs->npat++;
|
||||
rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
|
||||
rs->pat[rs->npat-1] = r;
|
||||
rs->pat[rs->npat] = nil;
|
||||
break;
|
||||
case OPlumb:
|
||||
rs->nact++;
|
||||
rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
|
||||
rs->act[rs->nact-1] = r;
|
||||
rs->act[rs->nact] = nil;
|
||||
if(r->verb == VTo){
|
||||
if(rs->npat>0 && rs->port != nil) /* npat==0 implies port declaration */
|
||||
parseerror("too many ports");
|
||||
if(lookup(r->qarg, badports) >= 0)
|
||||
parseerror("illegal port name %s", r->qarg);
|
||||
rs->port = estrdup(r->qarg);
|
||||
}else
|
||||
ncmd++; /* start or client rule */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ncmd > 1){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset has more than one client or start action");
|
||||
}
|
||||
if(rs->npat>0 && rs->nact>0)
|
||||
return rs;
|
||||
if(rs->npat==0 && rs->nact==0){
|
||||
freeruleset(rs);
|
||||
return nil;
|
||||
}
|
||||
if(rs->nact==0 || rs->port==nil){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset must have patterns and actions");
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* declare ports */
|
||||
for(i=0; i<rs->nact; i++)
|
||||
if(rs->act[i]->verb != VTo){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset must have actions");
|
||||
return nil;
|
||||
}
|
||||
for(i=0; i<rs->nact; i++)
|
||||
addport(rs->act[i]->qarg);
|
||||
freeruleset(rs);
|
||||
goto Again;
|
||||
}
|
||||
|
||||
Ruleset**
|
||||
readrules(char *name, int fd)
|
||||
{
|
||||
Ruleset *rs, **rules;
|
||||
int n;
|
||||
|
||||
parsing = 1;
|
||||
pushinput(name, fd, nil);
|
||||
rules = emalloc(sizeof(Ruleset*));
|
||||
for(n=0; (rs=readruleset())!=nil; n++){
|
||||
rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
|
||||
rules[n] = rs;
|
||||
rules[n+1] = nil;
|
||||
}
|
||||
popinput();
|
||||
parsing = 0;
|
||||
return rules;
|
||||
}
|
||||
|
||||
char*
|
||||
concat(char *s, char *t)
|
||||
{
|
||||
if(t == nil)
|
||||
return s;
|
||||
if(s == nil)
|
||||
s = estrdup(t);
|
||||
else{
|
||||
s = erealloc(s, strlen(s)+strlen(t)+1);
|
||||
strcat(s, t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printpat(Rule *r)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
|
||||
sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printvar(Var *v)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
|
||||
sprint(s, "%s=%s\n\n", v->name, v->value);
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printrule(Ruleset *r)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
for(i=0; i<r->npat; i++)
|
||||
s = concat(s, printpat(r->pat[i]));
|
||||
for(i=0; i<r->nact; i++)
|
||||
s = concat(s, printpat(r->act[i]));
|
||||
s = concat(s, "\n");
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printport(char *port)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
s = concat(s, "plumb to ");
|
||||
s = concat(s, port);
|
||||
s = concat(s, "\n");
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printrules(void)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
for(i=0; i<nvars; i++)
|
||||
s = concat(s, printvar(&vars[i]));
|
||||
for(i=0; i<nports; i++)
|
||||
s = concat(s, printport(ports[i]));
|
||||
s = concat(s, "\n");
|
||||
for(i=0; rules[i]; i++)
|
||||
s = concat(s, printrule(rules[i]));
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
stringof(char *s, int n)
|
||||
{
|
||||
char *t;
|
||||
|
||||
t = emalloc(n+1);
|
||||
memmove(t, s, n);
|
||||
return t;
|
||||
}
|
||||
|
||||
uchar*
|
||||
morerules(uchar *text, int done)
|
||||
{
|
||||
int n;
|
||||
Ruleset *rs;
|
||||
uchar *otext, *s, *endofrule;
|
||||
|
||||
pushinput("<rules input>", -1, text);
|
||||
if(done)
|
||||
input->end = text+strlen((char*)text);
|
||||
else{
|
||||
/*
|
||||
* Help user by sending any full rules to parser so any parse errors will
|
||||
* occur on write rather than close. A heuristic will do: blank line ends rule.
|
||||
*/
|
||||
endofrule = nil;
|
||||
for(s=text; *s!='\0'; s++)
|
||||
if(*s=='\n' && *++s=='\n')
|
||||
endofrule = s+1;
|
||||
if(endofrule == nil)
|
||||
return text;
|
||||
input->end = endofrule;
|
||||
}
|
||||
for(n=0; rules[n]; n++)
|
||||
;
|
||||
while((rs=readruleset()) != nil){
|
||||
rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
|
||||
rules[n++] = rs;
|
||||
rules[n] = nil;
|
||||
}
|
||||
otext =text;
|
||||
if(input == nil)
|
||||
text = (uchar*)estrdup("");
|
||||
else
|
||||
text = (uchar*)estrdup((char*)input->end);
|
||||
popinput();
|
||||
free(otext);
|
||||
return text;
|
||||
}
|
||||
|
||||
char*
|
||||
writerules(char *s, int n)
|
||||
{
|
||||
static uchar *text;
|
||||
char *tmp;
|
||||
|
||||
free(lasterror);
|
||||
lasterror = nil;
|
||||
parsing = 1;
|
||||
if(setjmp(parsejmp) == 0){
|
||||
tmp = stringof(s, n);
|
||||
text = (uchar*)concat((char*)text, tmp);
|
||||
free(tmp);
|
||||
text = morerules(text, s==nil);
|
||||
}
|
||||
if(s == nil){
|
||||
free(text);
|
||||
text = nil;
|
||||
}
|
||||
parsing = 0;
|
||||
makeports(rules);
|
||||
return lasterror;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue