xref: /plan9-contrib/sys/src/cmd/rc/exec.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include "rc.h"
2 #include "getflags.h"
3 #include "exec.h"
4 #include "io.h"
5 #include "fns.h"
6 /*
7  * Start executing the given code at the given pc with the given redirection
8  */
9 char *argv0="rc";
10 void start(code *c, int pc, var *local)
11 {
12 	struct thread *p=new(struct thread);
13 	p->code=codecopy(c);
14 	p->pc=pc;
15 	p->argv=0;
16 	p->redir=p->startredir=runq?runq->redir:0;
17 	p->local=local;
18 	p->cmdfile=0;
19 	p->cmdfd=0;
20 	p->eof=0;
21 	p->iflag=0;
22 	p->lineno=1;
23 	p->ret=runq;
24 	runq=p;
25 }
26 word *newword(char *wd, word *next)
27 {
28 	word *p=new(word);
29 	p->word=strdup(wd);
30 	p->next=next;
31 	return p;
32 }
33 void pushword(char *wd)
34 {
35 	if(runq->argv==0) panic("pushword but no argv!", 0);
36 	runq->argv->words=newword(wd, runq->argv->words);
37 }
38 void popword(void){
39 	word *p;
40 	if(runq->argv==0) panic("popword but no argv!", 0);
41 	p=runq->argv->words;
42 	if(p==0) panic("popword but no word!", 0);
43 	runq->argv->words=p->next;
44 	efree(p->word);
45 	efree((char *)p);
46 }
47 void freelist(word *w)
48 {
49 	word *nw;
50 	while(w){
51 		nw=w->next;
52 		efree(w->word);
53 		efree((char *)w);
54 		w=nw;
55 	}
56 }
57 void pushlist(void){
58 	list *p=new(list);
59 	p->next=runq->argv;
60 	p->words=0;
61 	runq->argv=p;
62 }
63 void poplist(void){
64 	list *p=runq->argv;
65 	if(p==0) panic("poplist but no argv", 0);
66 	freelist(p->words);
67 	runq->argv=p->next;
68 	efree((char *)p);
69 }
70 int count(word *w)
71 {
72 	int n;
73 	for(n=0;w;n++) w=w->next;
74 	return n;
75 }
76 void pushredir(int type, int from, int to){
77 	redir * rp=new(redir);
78 	rp->type=type;
79 	rp->from=from;
80 	rp->to=to;
81 	rp->next=runq->redir;
82 	runq->redir=rp;
83 }
84 var *newvar(char *name, var *next)
85 {
86 	var *v=new(var);
87 	v->name=name;
88 	v->val=0;
89 	v->fn=0;
90 	v->changed=0;
91 	v->fnchanged=0;
92 	v->next=next;
93 	return v;
94 }
95 /*
96  * get command line flags, initialize keywords & traps.
97  * get values from environment.
98  * set $pid, $cflag, $*
99  * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
100  * start interpreting code
101  */
102 void main(int argc, char *argv[])
103 {
104 	code bootstrap[17];
105 	char num[12], *rcmain;
106 	int i;
107 	argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1);
108 	if(argc==-1) usage("[file [arg ...]]");
109 	if(argv[0][0]=='-') flag['l']=flagset;
110 	if(flag['I']) flag['i'] = 0;
111 	else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
112 	rcmain=flag['m']?flag['m'][0]:Rcmain;
113 	err=openfd(2);
114 	kinit();
115 	Trapinit();
116 	Vinit();
117 	itoa(num, mypid=getpid());
118 	setvar("pid", newword(num, (word *)0));
119 	setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
120 				:(word *)0);
121 	setvar("rcname", newword(argv[0], (word *)0));
122 	i=0;
123 	bootstrap[i++].i=1;
124 	bootstrap[i++].f=Xmark;
125 	bootstrap[i++].f=Xword;
126 	bootstrap[i++].s="*";
127 	bootstrap[i++].f=Xassign;
128 	bootstrap[i++].f=Xmark;
129 	bootstrap[i++].f=Xmark;
130 	bootstrap[i++].f=Xword;
131 	bootstrap[i++].s="*";
132 	bootstrap[i++].f=Xdol;
133 	bootstrap[i++].f=Xword;
134 	bootstrap[i++].s=rcmain;
135 	bootstrap[i++].f=Xword;
136 	bootstrap[i++].s=".";
137 	bootstrap[i++].f=Xsimple;
138 	bootstrap[i++].f=Xexit;
139 	bootstrap[i].i=0;
140 	start(bootstrap, 1, (var *)0);
141 	/* prime bootstrap argv */
142 	pushlist();
143 	argv0 = strdup(argv[0]);
144 	for(i=argc-1;i!=0;--i) pushword(argv[i]);
145 	for(;;){
146 		if(flag['r']) pfnc(err, runq);
147 		runq->pc++;
148 		(*runq->code[runq->pc-1].f)();
149 		if(ntrap) dotrap();
150 	}
151 }
152 /*
153  * Opcode routines
154  * Arguments on stack (...)
155  * Arguments in line [...]
156  * Code in line with jump around {...}
157  *
158  * Xappend(file)[fd]			open file to append
159  * Xassign(name, val)			assign val to name
160  * Xasync{... Xexit}			make thread for {}, no wait
161  * Xbackq{... Xreturn}			make thread for {}, push stdout
162  * Xbang				complement condition
163  * Xcase(pat, value){...}		exec code on match, leave (value) on
164  * 					stack
165  * Xclose[i]				close file descriptor
166  * Xconc(left, right)			concatenate, push results
167  * Xcount(name)				push var count
168  * Xdelfn(name)				delete function definition
169  * Xdeltraps(names)			delete named traps
170  * Xdol(name)				get variable value
171  * Xqdol(name)				concatenate variable components
172  * Xdup[i j]				dup file descriptor
173  * Xexit				rc exits with status
174  * Xfalse{...}				execute {} if false
175  * Xfn(name){... Xreturn}			define function
176  * Xfor(var, list){... Xreturn}		for loop
177  * Xjump[addr]				goto
178  * Xlocal(name, val)			create local variable, assign value
179  * Xmark				mark stack
180  * Xmatch(pat, str)			match pattern, set status
181  * Xpipe[i j]{... Xreturn}{... Xreturn}	construct a pipe between 2 new threads,
182  * 					wait for both
183  * Xpipefd[type]{... Xreturn}		connect {} to pipe (input or output,
184  * 					depending on type), push /dev/fd/??
185  * Xpopm(value)				pop value from stack
186  * Xread(file)[fd]			open file to read
187  * Xsettraps(names){... Xreturn}		define trap functions
188  * Xshowtraps				print trap list
189  * Xsimple(args)			run command and wait
190  * Xreturn				kill thread
191  * Xsubshell{... Xexit}			execute {} in a subshell and wait
192  * Xtrue{...}				execute {} if true
193  * Xunlocal				delete local variable
194  * Xword[string]			push string
195  * Xwrite(file)[fd]			open file to write
196  */
197 void Xappend(void){
198 	char *file;
199 	int f;
200 	switch(count(runq->argv->words)){
201 	default: Xerror1(">> requires singleton"); return;
202 	case 0: Xerror1(">> requires file"); return;
203 	case 1: break;
204 	}
205 	file=runq->argv->words->word;
206 	if((f=open(file, 1))<0 && (f=Creat(file))<0){
207 		pfmt(err, "%s: ", file);
208 		Xerror("can't open");
209 		return;
210 	}
211 	Seek(f, 0L, 2);
212 	pushredir(ROPEN, f, runq->code[runq->pc].i);
213 	runq->pc++;
214 	poplist();
215 }
216 void Xasync(void){
217 	int null=open("/dev/null", 0);
218 	int pid;
219 	char npid[10];
220 	if(null<0){
221 		Xerror("Can't open /dev/null\n");
222 		return;
223 	}
224 	switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){
225 	case -1:
226 		close(null);
227 		Xerror("try again");
228 		break;
229 	case 0:
230 		pushredir(ROPEN, null, 0);
231 		start(runq->code, runq->pc+1, runq->local);
232 		runq->ret=0;
233 		break;
234 	default:
235 		close(null);
236 		runq->pc=runq->code[runq->pc].i;
237 		itoa(npid, pid);
238 		setvar("apid", newword(npid, (word *)0));
239 		break;
240 	}
241 }
242 void Xsettrue(void){
243 	setstatus("");
244 }
245 void Xbang(void){
246 	setstatus(truestatus()?"false":"");
247 }
248 void Xclose(void){
249 	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
250 	runq->pc++;
251 }
252 void Xdup(void){
253 	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
254 	runq->pc+=2;
255 }
256 void Xeflag(void){
257 	if(eflagok && !truestatus()) Xexit();
258 }
259 void Xexit(void){
260 	struct var *trapreq;
261 	struct word *starval;
262 	static int beenhere=0;
263 	if(getpid()==mypid && !beenhere){
264 		trapreq=vlook("sigexit");
265 		if(trapreq->fn){
266 			beenhere=1;
267 			--runq->pc;
268 			starval=vlook("*")->val;
269 			start(trapreq->fn, trapreq->pc, (struct var *)0);
270 			runq->local=newvar(strdup("*"), runq->local);
271 			runq->local->val=copywords(starval, (struct word *)0);
272 			runq->local->changed=1;
273 			runq->redir=runq->startredir=0;
274 			return;
275 		}
276 	}
277 	Exit(getstatus());
278 }
279 void Xfalse(void){
280 	if(truestatus()) runq->pc=runq->code[runq->pc].i;
281 	else runq->pc++;
282 }
283 int ifnot;		/* dynamic if not flag */
284 void Xifnot(void){
285 	if(ifnot)
286 		runq->pc++;
287 	else
288 		runq->pc=runq->code[runq->pc].i;
289 }
290 void Xjump(void){
291 	runq->pc=runq->code[runq->pc].i;
292 }
293 void Xmark(void){
294 	pushlist();
295 }
296 void Xpopm(void){
297 	poplist();
298 }
299 void Xread(void){
300 	char *file;
301 	int f;
302 	switch(count(runq->argv->words)){
303 	default: Xerror1("< requires singleton\n"); return;
304 	case 0: Xerror1("< requires file\n"); return;
305 	case 1: break;
306 	}
307 	file=runq->argv->words->word;
308 	if((f=open(file, 0))<0){
309 		pfmt(err, "%s: ", file);
310 		Xerror("can't open");
311 		return;
312 	}
313 	pushredir(ROPEN, f, runq->code[runq->pc].i);
314 	runq->pc++;
315 	poplist();
316 }
317 void turfredir(void){
318 	while(runq->redir!=runq->startredir)
319 		Xpopredir();
320 }
321 void Xpopredir(void){
322 	struct redir *rp=runq->redir;
323 	if(rp==0) panic("turfredir null!", 0);
324 	runq->redir=rp->next;
325 	if(rp->type==ROPEN) close(rp->from);
326 	efree((char *)rp);
327 }
328 void Xreturn(void){
329 	struct thread *p=runq;
330 	turfredir();
331 	while(p->argv) poplist();
332 	codefree(p->code);
333 	runq=p->ret;
334 	efree((char *)p);
335 	if(runq==0) Exit(getstatus());
336 }
337 void Xtrue(void){
338 	if(truestatus()) runq->pc++;
339 	else runq->pc=runq->code[runq->pc].i;
340 }
341 void Xif(void){
342 	ifnot=1;
343 	if(truestatus()) runq->pc++;
344 	else runq->pc=runq->code[runq->pc].i;
345 }
346 void Xwastrue(void){
347 	ifnot=0;
348 }
349 void Xword(void){
350 	pushword(runq->code[runq->pc++].s);
351 }
352 void Xwrite(void){
353 	char *file;
354 	int f;
355 	switch(count(runq->argv->words)){
356 	default: Xerror1("> requires singleton\n"); return;
357 	case 0: Xerror1("> requires file\n"); return;
358 	case 1: break;
359 	}
360 	file=runq->argv->words->word;
361 	if((f=Creat(file))<0){
362 		pfmt(err, "%s: ", file);
363 		Xerror("can't open");
364 		return;
365 	}
366 	pushredir(ROPEN, f, runq->code[runq->pc].i);
367 	runq->pc++;
368 	poplist();
369 }
370 char *list2str(word *words){
371 	char *value, *s, *t;
372 	int len=0;
373 	word *ap;
374 	for(ap=words;ap;ap=ap->next)
375 		len+=1+strlen(ap->word);
376 	value=emalloc(len+1);
377 	s=value;
378 	for(ap=words;ap;ap=ap->next){
379 		for(t=ap->word;*t;) *s++=*t++;
380 		*s++=' ';
381 	}
382 	if(s==value) *s='\0';
383 	else s[-1]='\0';
384 	return value;
385 }
386 void Xmatch(void){
387 	word *p;
388 	char *subject;
389 	subject=list2str(runq->argv->words);
390 	setstatus("no match");
391 	for(p=runq->argv->next->words;p;p=p->next)
392 		if(match(subject, p->word, '\0')){
393 			setstatus("");
394 			break;
395 		}
396 	efree(subject);
397 	poplist();
398 	poplist();
399 }
400 void Xcase(void){
401 	word *p;
402 	char *s;
403 	int ok=0;
404 	s=list2str(runq->argv->next->words);
405 	for(p=runq->argv->words;p;p=p->next){
406 		if(match(s, p->word, '\0')){
407 			ok=1;
408 			break;
409 		}
410 	}
411 	efree(s);
412 	if(ok)
413 		runq->pc++;
414 	else
415 		runq->pc=runq->code[runq->pc].i;
416 	poplist();
417 }
418 word *conclist(word *lp, word *rp, word *tail)
419 {
420 	char *buf;
421 	word *v;
422 	if(lp->next || rp->next)
423 		tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
424 			tail);
425 	buf=emalloc(strlen(lp->word)+strlen(rp->word)+1);
426 	strcpy(buf, lp->word);
427 	strcat(buf, rp->word);
428 	v=newword(buf, tail);
429 	efree(buf);
430 	return v;
431 }
432 void Xconc(void){
433 	word *lp=runq->argv->words;
434 	word *rp=runq->argv->next->words;
435 	word *vp=runq->argv->next->next->words;
436 	int lc=count(lp), rc=count(rp);
437 	if(lc!=0 || rc!=0){
438 		if(lc==0 || rc==0){
439 			Xerror1("null list in concatenation");
440 			return;
441 		}
442 		if(lc!=1 && rc!=1 && lc!=rc){
443 			Xerror1("mismatched list lengths in concatenation");
444 			return;
445 		}
446 		vp=conclist(lp, rp, vp);
447 	}
448 	poplist();
449 	poplist();
450 	runq->argv->words=vp;
451 }
452 void Xassign(void){
453 	var *v;
454 	if(count(runq->argv->words)!=1){
455 		Xerror1("variable name not singleton!");
456 		return;
457 	}
458 	deglob(runq->argv->words->word);
459 	v=vlook(runq->argv->words->word);
460 	poplist();
461 	globlist();
462 	freewords(v->val);
463 	v->val=runq->argv->words;
464 	v->changed=1;
465 	runq->argv->words=0;
466 	poplist();
467 }
468 /*
469  * copy arglist a, adding the copy to the front of tail
470  */
471 word *copywords(word *a, word *tail)
472 {
473 	word *v=0, **end;
474 	for(end=&v;a;a=a->next,end=&(*end)->next)
475 		*end=newword(a->word, 0);
476 	*end=tail;
477 	return v;
478 }
479 void Xdol(void){
480 	word *a, *star;
481 	char *s, *t;
482 	int n;
483 	if(count(runq->argv->words)!=1){
484 		Xerror1("variable name not singleton!");
485 		return;
486 	}
487 	s=runq->argv->words->word;
488 	deglob(s);
489 	n=0;
490 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
491 	a=runq->argv->next->words;
492 	if(n==0 || *t)
493 		a=copywords(vlook(s)->val, a);
494 	else{
495 		star=vlook("*")->val;
496 		if(star && 1<=n && n<=count(star)){
497 			while(--n) star=star->next;
498 			a=newword(star->word, a);
499 		}
500 	}
501 	poplist();
502 	runq->argv->words=a;
503 }
504 void Xqdol(void){
505 	word *a, *p;
506 	char *s;
507 	int n;
508 	if(count(runq->argv->words)!=1){
509 		Xerror1("variable name not singleton!");
510 		return;
511 	}
512 	s=runq->argv->words->word;
513 	deglob(s);
514 	a=vlook(s)->val;
515 	poplist();
516 	n=count(a);
517 	if(n==0){
518 		pushword("");
519 		return;
520 	}
521 	for(p=a;p;p=p->next) n+=strlen(p->word);
522 	s=emalloc(n);
523 	if(a){
524 		strcpy(s, a->word);
525 		for(p=a->next;p;p=p->next){
526 			strcat(s, " ");
527 			strcat(s, p->word);
528 		}
529 	}
530 	else
531 		s[0]='\0';
532 	pushword(s);
533 	efree(s);
534 }
535 word *subwords(word *val, int len, word *sub, word *a)
536 {
537 	int n;
538 	char *s;
539 	if(!sub) return a;
540 	a=subwords(val, len, sub->next, a);
541 	s=sub->word;
542 	deglob(s);
543 	n=0;
544 	while('0'<=*s && *s<='9') n=n*10+ *s++ -'0';
545 	if(n<1 || len<n) return a;
546 	for(;n!=1;--n) val=val->next;
547 	return newword(val->word, a);
548 }
549 void Xsub(void){
550 	word *a, *v;
551 	char *s;
552 	if(count(runq->argv->next->words)!=1){
553 		Xerror1("variable name not singleton!");
554 		return;
555 	}
556 	s=runq->argv->next->words->word;
557 	deglob(s);
558 	a=runq->argv->next->next->words;
559 	v=vlook(s)->val;
560 	a=subwords(v, count(v), runq->argv->words, a);
561 	poplist();
562 	poplist();
563 	runq->argv->words=a;
564 }
565 void Xcount(void){
566 	word *a;
567 	char *s, *t;
568 	int n;
569 	char num[12];
570 	if(count(runq->argv->words)!=1){
571 		Xerror1("variable name not singleton!");
572 		return;
573 	}
574 	s=runq->argv->words->word;
575 	deglob(s);
576 	n=0;
577 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
578 	if(n==0 || *t){
579 		a=vlook(s)->val;
580 		itoa(num, count(a));
581 	}
582 	else{
583 		a=vlook("*")->val;
584 		itoa(num, a && 1<=n && n<=count(a)?1:0);
585 	}
586 	poplist();
587 	pushword(num);
588 }
589 void Xlocal(void){
590 	if(count(runq->argv->words)!=1){
591 		Xerror1("variable name must be singleton\n");
592 		return;
593 	}
594 	deglob(runq->argv->words->word);
595 	runq->local=newvar(strdup(runq->argv->words->word), runq->local);
596 	runq->local->val=copywords(runq->argv->next->words, (word *)0);
597 	runq->local->changed=1;
598 	poplist();
599 	poplist();
600 }
601 void Xunlocal(void){
602 	var *v=runq->local, *hid;
603 	if(v==0) panic("Xunlocal: no locals!", 0);
604 	runq->local=v->next;
605 	hid=vlook(v->name);
606 	hid->changed=1;
607 	efree(v->name);
608 	freewords(v->val);
609 	efree((char *)v);
610 }
611 void freewords(word *w)
612 {
613 	word *nw;
614 	while(w){
615 		efree(w->word);
616 		nw=w->next;
617 		efree((char *)w);
618 		w=nw;
619 	}
620 }
621 void Xfn(void){
622 	var *v;
623 	word *a;
624 	int end;
625 	end=runq->code[runq->pc].i;
626 	for(a=runq->argv->words;a;a=a->next){
627 		v=gvlook(a->word);
628 		if(v->fn) codefree(v->fn);
629 		v->fn=codecopy(runq->code);
630 		v->pc=runq->pc+2;
631 		v->fnchanged=1;
632 	}
633 	runq->pc=end;
634 	poplist();
635 }
636 void Xdelfn(void){
637 	var *v;
638 	word *a;
639 	for(a=runq->argv->words;a;a=a->next){
640 		v=gvlook(a->word);
641 		if(v->fn) codefree(v->fn);
642 		v->fn=0;
643 		v->fnchanged=1;
644 	}
645 	poplist();
646 }
647 void Xpipe(void){
648 	struct thread *p=runq;
649 	int pc=p->pc, forkid;
650 	int lfd=p->code[pc++].i;
651 	int rfd=p->code[pc++].i;
652 	int pfd[2];
653 	if(pipe(pfd)<0){
654 		Xerror("can't get pipe");
655 		return;
656 	}
657 	switch(forkid=fork()){
658 	case -1:
659 		Xerror("try again");
660 		break;
661 	case 0:
662 		start(p->code, pc+2, runq->local);
663 		runq->ret=0;
664 		close(pfd[PRD]);
665 		pushredir(ROPEN, pfd[PWR], lfd);
666 		break;
667 	default:
668 		start(p->code, p->code[pc].i, runq->local);
669 		close(pfd[PWR]);
670 		pushredir(ROPEN, pfd[PRD], rfd);
671 		p->pc=p->code[pc+1].i;
672 		p->pid=forkid;
673 		break;
674 	}
675 }
676 char *concstatus(char *s, char *t)
677 {
678 	static char v[NSTATUS+1];
679 	int n=strlen(s);
680 	strncpy(v, s, NSTATUS);
681 	if(n<NSTATUS){
682 		v[n]='|';
683 		strncpy(v+n+1, t, NSTATUS-n-1);
684 	}
685 	v[NSTATUS]='\0';
686 	return v;
687 }
688 void Xpipewait(void){
689 	char status[NSTATUS+1];
690 	if(runq->pid==-1)
691 		setstatus(concstatus(runq->status, getstatus()));
692 	else{
693 		strncpy(status, getstatus(), NSTATUS);
694 		status[NSTATUS]='\0';
695 		Waitfor(runq->pid, 1);
696 		runq->pid=-1;
697 		setstatus(concstatus(getstatus(), status));
698 	}
699 }
700 void Xrdcmds(void){
701 	struct thread *p=runq;
702 	word *prompt;
703 	flush(err);
704 	nerror=0;
705 	if(flag['s'] && !truestatus())
706 		pfmt(err, "status=%v\n", vlook("status")->val);
707 	if(runq->iflag){
708 		prompt=vlook("prompt")->val;
709 		if(prompt)
710 			promptstr=prompt->word;
711 		else
712 			promptstr="% ";
713 	}
714 	Noerror();
715 	if(yyparse()){
716 		if(!p->iflag || p->eof && !Eintr()){
717 			if(p->cmdfile) efree(p->cmdfile);
718 			closeio(p->cmdfd);
719 			Xreturn();	/* should this be omitted? */
720 		}
721 		else{
722 			if(Eintr()){
723 				pchr(err, '\n');
724 				p->eof=0;
725 			}
726 			--p->pc;	/* go back for next command */
727 		}
728 	}
729 	else{
730 		ntrap = 0;	/* avoid double-interrupts during blocked writes */
731 		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
732 		start(codebuf, 1, runq->local);
733 	}
734 	freenodes();
735 }
736 void Xerror(char *s)
737 {
738 	if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
739 		pfmt(err, "rc: %s: %r\n", s);
740 	else
741 		pfmt(err, "rc (%s): %s: %r\n", argv0, s);
742 	flush(err);
743 	while(!runq->iflag) Xreturn();
744 }
745 void Xerror1(char *s)
746 {
747 	if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
748 		pfmt(err, "rc: %s\n", s);
749 	else
750 		pfmt(err, "rc (%s): %s\n", argv0, s);
751 	flush(err);
752 	while(!runq->iflag) Xreturn();
753 }
754 void Xbackq(void){
755 	char wd[8193];
756 	int c;
757 	char *s, *ewd=&wd[8192], *stop;
758 	struct io *f;
759 	var *ifs=vlook("ifs");
760 	word *v, *nextv;
761 	int pfd[2];
762 	int pid;
763 	stop=ifs->val?ifs->val->word:"";
764 	if(pipe(pfd)<0){
765 		Xerror("can't make pipe");
766 		return;
767 	}
768 	switch(pid=fork()){
769 	case -1: Xerror("try again");
770 		close(pfd[PRD]);
771 		close(pfd[PWR]);
772 		return;
773 	case 0:
774 		close(pfd[PRD]);
775 		start(runq->code, runq->pc+1, runq->local);
776 		pushredir(ROPEN, pfd[PWR], 1);
777 		return;
778 	default:
779 		close(pfd[PWR]);
780 		f=openfd(pfd[PRD]);
781 		s=wd;
782 		v=0;
783 		while((c=rchr(f))!=EOF){
784 			if(strchr(stop, c) || s==ewd){
785 				if(s!=wd){
786 					*s='\0';
787 					v=newword(wd, v);
788 					s=wd;
789 				}
790 			}
791 			else *s++=c;
792 		}
793 		if(s!=wd){
794 			*s='\0';
795 			v=newword(wd, v);
796 		}
797 		closeio(f);
798 		Waitfor(pid, 0);
799 		/* v points to reversed arglist -- reverse it onto argv */
800 		while(v){
801 			nextv=v->next;
802 			v->next=runq->argv->words;
803 			runq->argv->words=v;
804 			v=nextv;
805 		}
806 		runq->pc=runq->code[runq->pc].i;
807 		return;
808 	}
809 }
810 /*
811  * Who should wait for the exit from the fork?
812  */
813 void Xpipefd(void){
814 	struct thread *p=runq;
815 	int pc=p->pc;
816 	char name[40];
817 	int pfd[2];
818 	int sidefd, mainfd;
819 	if(pipe(pfd)<0){
820 		Xerror("can't get pipe");
821 		return;
822 	}
823 	if(p->code[pc].i==READ){
824 		sidefd=pfd[PWR];
825 		mainfd=pfd[PRD];
826 	}
827 	else{
828 		sidefd=pfd[PRD];
829 		mainfd=pfd[PWR];
830 	}
831 	switch(fork()){
832 	case -1:
833 		Xerror("try again");
834 		break;
835 	case 0:
836 		start(p->code, pc+2, runq->local);
837 		close(mainfd);
838 		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
839 		runq->ret=0;
840 		break;
841 	default:
842 		close(sidefd);
843 		pushredir(ROPEN, mainfd, mainfd);	/* isn't this a noop? */
844 		strcpy(name, Fdprefix);
845 		itoa(name+strlen(name), mainfd);
846 		pushword(name);
847 		p->pc=p->code[pc+1].i;
848 		break;
849 	}
850 }
851 void Xsubshell(void){
852 	int pid;
853 	switch(pid=fork()){
854 	case -1:
855 		Xerror("try again");
856 		break;
857 	case 0:
858 		start(runq->code, runq->pc+1, runq->local);
859 		runq->ret=0;
860 		break;
861 	default:
862 		Waitfor(pid, 1);
863 		runq->pc=runq->code[runq->pc].i;
864 		break;
865 	}
866 }
867 void setstatus(char *s)
868 {
869 	setvar("status", newword(s, (word *)0));
870 }
871 char *getstatus(void){
872 	var *status=vlook("status");
873 	return status->val?status->val->word:"";
874 }
875 int truestatus(void){
876 	char *s;
877 	for(s=getstatus();*s;s++)
878 		if(*s!='|' && *s!='0') return 0;
879 	return 1;
880 }
881 void Xdelhere(void){
882 	Unlink(runq->code[runq->pc++].s);
883 }
884 void Xfor(void){
885 	if(runq->argv->words==0){
886 		poplist();
887 		runq->pc=runq->code[runq->pc].i;
888 	}
889 	else{
890 		freelist(runq->local->val);
891 		runq->local->val=runq->argv->words;
892 		runq->local->changed=1;
893 		runq->argv->words=runq->argv->words->next;
894 		runq->local->val->next=0;
895 		runq->pc++;
896 	}
897 }
898 void Xglob(void){
899 	globlist();
900 }
901