xref: /plan9/sys/src/cmd/plumb/rules.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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