xref: /plan9-contrib/sys/src/cmd/rc/code.c (revision dbee78772299b06484db59f95f481ad96b19f995)
1 #include "rc.h"
2 #include "io.h"
3 #include "exec.h"
4 #include "fns.h"
5 
6 #define	c0	t->child[0]
7 #define	c1	t->child[1]
8 #define	c2	t->child[2]
9 int codep, ncode;
10 #define	emitf(x) ((codep!=ncode || morecode()), codebuf[codep].f = (x), codep++)
11 #define	emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i = (x), codep++)
12 #define	emits(x) ((codep!=ncode || morecode()), codebuf[codep].s = (x), codep++)
13 void stuffdot(int);
14 char *fnstr(tree*);
15 void outcode(tree*, int);
16 void codeswitch(tree*, int);
17 int iscase(tree*);
18 code *codecopy(code*);
19 void codefree(code*);
20 
21 int
morecode(void)22 morecode(void)
23 {
24 	ncode+=100;
25 	codebuf = (code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]);
26 	if(codebuf==0)
27 		panic("Can't realloc %d bytes in morecode!",
28 				ncode*sizeof codebuf[0]);
29 	return 0;
30 }
31 
32 void
stuffdot(int a)33 stuffdot(int a)
34 {
35 	if(a<0 || codep<=a)
36 		panic("Bad address %d in stuffdot", a);
37 	codebuf[a].i = codep;
38 }
39 
40 int
compile(tree * t)41 compile(tree *t)
42 {
43 	ncode = 100;
44 	codebuf = (code *)emalloc(ncode*sizeof codebuf[0]);
45 	codep = 0;
46 	emiti(0);			/* reference count */
47 	outcode(t, flag['e']?1:0);
48 	if(nerror){
49 		efree((char *)codebuf);
50 		return 0;
51 	}
52 	readhere();
53 	emitf(Xreturn);
54 	emitf(0);
55 	return 1;
56 }
57 
58 void
cleanhere(char * f)59 cleanhere(char *f)
60 {
61 	emitf(Xdelhere);
62 	emits(strdup(f));
63 }
64 
65 char*
fnstr(tree * t)66 fnstr(tree *t)
67 {
68 	io *f = openstr();
69 	void *v;
70 	extern char nl;
71 	char svnl = nl;
72 
73 	nl = ';';
74 	pfmt(f, "%t", t);
75 	nl = svnl;
76 	v = f->strp;
77 	f->strp = 0;
78 	closeio(f);
79 	return v;
80 }
81 
82 void
outcode(tree * t,int eflag)83 outcode(tree *t, int eflag)
84 {
85 	int p, q;
86 	tree *tt;
87 	char *ifs;
88 
89 	if(t==0)
90 		return;
91 	if(t->type!=NOT && t->type!=';')
92 		runq->iflast = 0;
93 	switch(t->type){
94 	default:
95 		pfmt(err, "bad type %d in outcode\n", t->type);
96 		break;
97 	case '$':
98 		emitf(Xmark);
99 		outcode(c0, eflag);
100 		emitf(Xdol);
101 		break;
102 	case '"':
103 		emitf(Xmark);
104 		outcode(c0, eflag);
105 		emitf(Xqdol);
106 		break;
107 	case SUB:
108 		emitf(Xmark);
109 		outcode(c0, eflag);
110 		emitf(Xmark);
111 		outcode(c1, eflag);
112 		emitf(Xsub);
113 		break;
114 	case '&':
115 		emitf(Xasync);
116 		if(havefork){
117 			p = emiti(0);
118 			outcode(c0, eflag);
119 			emitf(Xexit);
120 			stuffdot(p);
121 		} else
122 			emits(fnstr(c0));
123 		break;
124 	case ';':
125 		outcode(c0, eflag);
126 		outcode(c1, eflag);
127 		break;
128 	case '^':
129 		emitf(Xmark);
130 		outcode(c1, eflag);
131 		emitf(Xmark);
132 		outcode(c0, eflag);
133 		emitf(Xconc);
134 		break;
135 	case '`':
136  		emitf(Xmark);
137  		if(c0){
138  			outcode(c0, 0);
139  			emitf(Xglob);
140  		} else {
141 			if((ifs = strdup("ifs")) == nil)
142 				sysfatal("strdup: %r");
143  			emitf(Xmark);
144  			emitf(Xword);
145  			emits(ifs);
146  			emitf(Xdol);
147  		}
148 		emitf(Xbackq);
149 		if(havefork){
150 			p = emiti(0);
151 			outcode(c1, 0);
152 			emitf(Xexit);
153 			stuffdot(p);
154 		} else
155 			emits(fnstr(c0));
156 		break;
157 	case ANDAND:
158 		outcode(c0, 0);
159 		emitf(Xtrue);
160 		p = emiti(0);
161 		outcode(c1, eflag);
162 		stuffdot(p);
163 		break;
164 	case ARGLIST:
165 		outcode(c1, eflag);
166 		outcode(c0, eflag);
167 		break;
168 	case BANG:
169 		outcode(c0, eflag);
170 		emitf(Xbang);
171 		break;
172 	case PCMD:
173 	case BRACE:
174 		outcode(c0, eflag);
175 		break;
176 	case COUNT:
177 		emitf(Xmark);
178 		outcode(c0, eflag);
179 		emitf(Xcount);
180 		break;
181 	case FN:
182 		emitf(Xmark);
183 		outcode(c0, eflag);
184 		if(c1){
185 			emitf(Xfn);
186 			p = emiti(0);
187 			emits(fnstr(c1));
188 			outcode(c1, eflag);
189 			emitf(Xunlocal);	/* get rid of $* */
190 			emitf(Xreturn);
191 			stuffdot(p);
192 		}
193 		else
194 			emitf(Xdelfn);
195 		break;
196 	case IF:
197 		outcode(c0, 0);
198 		emitf(Xif);
199 		p = emiti(0);
200 		outcode(c1, eflag);
201 		emitf(Xwastrue);
202 		stuffdot(p);
203 		break;
204 	case NOT:
205 		if(!runq->iflast)
206 			yyerror("`if not' does not follow `if(...)'");
207 		emitf(Xifnot);
208 		p = emiti(0);
209 		outcode(c0, eflag);
210 		stuffdot(p);
211 		break;
212 	case OROR:
213 		outcode(c0, 0);
214 		emitf(Xfalse);
215 		p = emiti(0);
216 		outcode(c1, eflag);
217 		stuffdot(p);
218 		break;
219 	case PAREN:
220 		outcode(c0, eflag);
221 		break;
222 	case SIMPLE:
223 		emitf(Xmark);
224 		outcode(c0, eflag);
225 		emitf(Xsimple);
226 		if(eflag)
227 			emitf(Xeflag);
228 		break;
229 	case SUBSHELL:
230 		emitf(Xsubshell);
231 		if(havefork){
232 			p = emiti(0);
233 			outcode(c0, eflag);
234 			emitf(Xexit);
235 			stuffdot(p);
236 		} else
237 			emits(fnstr(c0));
238 		if(eflag)
239 			emitf(Xeflag);
240 		break;
241 	case SWITCH:
242 		codeswitch(t, eflag);
243 		break;
244 	case TWIDDLE:
245 		emitf(Xmark);
246 		outcode(c1, eflag);
247 		emitf(Xmark);
248 		outcode(c0, eflag);
249 		emitf(Xmatch);
250 		if(eflag)
251 			emitf(Xeflag);
252 		break;
253 	case WHILE:
254 		q = codep;
255 		outcode(c0, 0);
256 		if(q==codep)
257 			emitf(Xsettrue);	/* empty condition == while(true) */
258 		emitf(Xtrue);
259 		p = emiti(0);
260 		outcode(c1, eflag);
261 		emitf(Xjump);
262 		emiti(q);
263 		stuffdot(p);
264 		break;
265 	case WORDS:
266 		outcode(c1, eflag);
267 		outcode(c0, eflag);
268 		break;
269 	case FOR:
270 		emitf(Xmark);
271 		if(c1){
272 			outcode(c1, eflag);
273 			emitf(Xglob);
274 		}
275 		else{
276 			emitf(Xmark);
277 			emitf(Xword);
278 			emits(strdup("*"));
279 			emitf(Xdol);
280 		}
281 		emitf(Xmark);		/* dummy value for Xlocal */
282 		emitf(Xmark);
283 		outcode(c0, eflag);
284 		emitf(Xlocal);
285 		p = emitf(Xfor);
286 		q = emiti(0);
287 		outcode(c2, eflag);
288 		emitf(Xjump);
289 		emiti(p);
290 		stuffdot(q);
291 		emitf(Xunlocal);
292 		break;
293 	case WORD:
294 		emitf(Xword);
295 		emits(strdup(t->str));
296 		break;
297 	case DUP:
298 		if(t->rtype==DUPFD){
299 			emitf(Xdup);
300 			emiti(t->fd0);
301 			emiti(t->fd1);
302 		}
303 		else{
304 			emitf(Xclose);
305 			emiti(t->fd0);
306 		}
307 		outcode(c1, eflag);
308 		emitf(Xpopredir);
309 		break;
310 	case PIPEFD:
311 		emitf(Xpipefd);
312 		emiti(t->rtype);
313 		if(havefork){
314 			p = emiti(0);
315 			outcode(c0, eflag);
316 			emitf(Xexit);
317 			stuffdot(p);
318 		} else {
319 			emits(fnstr(c0));
320 		}
321 		break;
322 	case REDIR:
323 		emitf(Xmark);
324 		outcode(c0, eflag);
325 		emitf(Xglob);
326 		switch(t->rtype){
327 		case APPEND:
328 			emitf(Xappend);
329 			break;
330 		case WRITE:
331 			emitf(Xwrite);
332 			break;
333 		case READ:
334 		case HERE:
335 			emitf(Xread);
336 			break;
337 		case RDWR:
338 			emitf(Xrdwr);
339 			break;
340 		}
341 		emiti(t->fd0);
342 		outcode(c1, eflag);
343 		emitf(Xpopredir);
344 		break;
345 	case '=':
346 		tt = t;
347 		for(;t && t->type=='=';t = c2);
348 		if(t){					/* var=value cmd */
349 			for(t = tt;t->type=='=';t = c2){
350 				emitf(Xmark);
351 				outcode(c1, eflag);
352 				emitf(Xmark);
353 				outcode(c0, eflag);
354 				emitf(Xlocal);		/* push var for cmd */
355 			}
356 			outcode(t, eflag);		/* gen. code for cmd */
357 			for(t = tt; t->type == '='; t = c2)
358 				emitf(Xunlocal);	/* pop var */
359 		}
360 		else{					/* var=value */
361 			for(t = tt;t;t = c2){
362 				emitf(Xmark);
363 				outcode(c1, eflag);
364 				emitf(Xmark);
365 				outcode(c0, eflag);
366 				emitf(Xassign);	/* set var permanently */
367 			}
368 		}
369 		t = tt;	/* so tests below will work */
370 		break;
371 	case PIPE:
372 		emitf(Xpipe);
373 		emiti(t->fd0);
374 		emiti(t->fd1);
375 		if(havefork){
376 			p = emiti(0);
377 			q = emiti(0);
378 			outcode(c0, eflag);
379 			emitf(Xexit);
380 			stuffdot(p);
381 		} else {
382 			emits(fnstr(c0));
383 			q = emiti(0);
384 		}
385 		outcode(c1, eflag);
386 		emitf(Xreturn);
387 		stuffdot(q);
388 		emitf(Xpipewait);
389 		break;
390 	}
391 	if(t->type!=NOT && t->type!=';')
392 		runq->iflast = t->type==IF;
393 	else if(c0) runq->iflast = c0->type==IF;
394 }
395 /*
396  * switch code looks like this:
397  *	Xmark
398  *	(get switch value)
399  *	Xjump	1f
400  * out:	Xjump	leave
401  * 1:	Xmark
402  *	(get case values)
403  *	Xcase	1f
404  *	(commands)
405  *	Xjump	out
406  * 1:	Xmark
407  *	(get case values)
408  *	Xcase	1f
409  *	(commands)
410  *	Xjump	out
411  * 1:
412  * leave:
413  *	Xpopm
414  */
415 
416 void
codeswitch(tree * t,int eflag)417 codeswitch(tree *t, int eflag)
418 {
419 	int leave;		/* patch jump address to leave switch */
420 	int out;		/* jump here to leave switch */
421 	int nextcase;	/* patch jump address to next case */
422 	tree *tt;
423 	if(c1->child[0]==nil
424 	|| c1->child[0]->type!=';'
425 	|| !iscase(c1->child[0]->child[0])){
426 		yyerror("case missing in switch");
427 		return;
428 	}
429 	emitf(Xmark);
430 	outcode(c0, eflag);
431 	emitf(Xjump);
432 	nextcase = emiti(0);
433 	out = emitf(Xjump);
434 	leave = emiti(0);
435 	stuffdot(nextcase);
436 	t = c1->child[0];
437 	while(t->type==';'){
438 		tt = c1;
439 		emitf(Xmark);
440 		for(t = c0->child[0];t->type==ARGLIST;t = c0) outcode(c1, eflag);
441 		emitf(Xcase);
442 		nextcase = emiti(0);
443 		t = tt;
444 		for(;;){
445 			if(t->type==';'){
446 				if(iscase(c0)) break;
447 				outcode(c0, eflag);
448 				t = c1;
449 			}
450 			else{
451 				if(!iscase(t)) outcode(t, eflag);
452 				break;
453 			}
454 		}
455 		emitf(Xjump);
456 		emiti(out);
457 		stuffdot(nextcase);
458 	}
459 	stuffdot(leave);
460 	emitf(Xpopm);
461 }
462 
463 int
iscase(tree * t)464 iscase(tree *t)
465 {
466 	if(t->type!=SIMPLE)
467 		return 0;
468 	do t = c0; while(t->type==ARGLIST);
469 	return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0;
470 }
471 
472 code*
codecopy(code * cp)473 codecopy(code *cp)
474 {
475 	cp[0].i++;
476 	return cp;
477 }
478 
479 void
codefree(code * cp)480 codefree(code *cp)
481 {
482 	code *p;
483 	if(--cp[0].i!=0)
484 		return;
485 	for(p = cp+1;p->f;p++){
486 		if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
487 		|| p->f==Xrdwr
488 		|| p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
489 		|| p->f==Xfor || p->f==Xjump
490 		|| p->f==Xsubshell || p->f==Xtrue) p++;
491 		else if(p->f==Xdup || p->f==Xpipefd) p+=2;
492 		else if(p->f==Xpipe) p+=4;
493 		else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s);
494 		else if(p->f==Xfn){
495 			efree(p[2].s);
496 			p+=2;
497 		}
498 	}
499 	efree((char *)cp);
500 }
501