⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rules.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#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 voidprintinputstackrev(Input *in){	if(in == nil)		return;	printinputstackrev(in->next);	fprint(2, "%s:%d: ", in->file, in->lineno);}voidprintinputstack(void){	printinputstackrev(input);}static voidpushinput(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");	}}intpopinput(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;}intgetc(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;}intlookup(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;}voidsetvariable(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;}voidregerror(char *msg){	if(parsing){		parsing = 0;		parseerror("%s", msg);	}	error("%s", msg);}voidparserule(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;	}}intassignment(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;}intinclude(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;}voidfreerule(Rule *r){	free(r->arg);	free(r->qarg);	free(r->regex);}voidfreerules(Rule **r){	while(*r)		freerule(*r++);}voidfreeruleset(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;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -