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