xref: /plan9/sys/src/cmd/rc/exec.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
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 void 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(argv[0][0]=='-') flag['l']=flagset;
109 	if(flag['I']) flag['i'] = 0;
110 	else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
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 		pfmt(err, "%s: ", file);
205 		Xerror("can't open");
206 		return;
207 	}
208 	Seek(f, 0L, 2);
209 	pushredir(ROPEN, f, runq->code[runq->pc].i);
210 	runq->pc++;
211 	poplist();
212 }
213 void Xasync(void){
214 	int null=open("/dev/null", 0);
215 	int pid;
216 	char npid[10];
217 	if(null<0){
218 		Xerror("Can't open /dev/null\n");
219 		return;
220 	}
221 	switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){
222 	case -1:
223 		close(null);
224 		Xerror("try again");
225 		break;
226 	case 0:
227 		pushredir(ROPEN, null, 0);
228 		start(runq->code, runq->pc+1, runq->local);
229 		runq->ret=0;
230 		break;
231 	default:
232 		close(null);
233 		runq->pc=runq->code[runq->pc].i;
234 		itoa(npid, pid);
235 		setvar("apid", newword(npid, (word *)0));
236 		break;
237 	}
238 }
239 void Xsettrue(void){
240 	setstatus("");
241 }
242 void Xbang(void){
243 	setstatus(truestatus()?"false":"");
244 }
245 void Xclose(void){
246 	pushredir(RCLOSE, runq->code[runq->pc].i, 0);
247 	runq->pc++;
248 }
249 void Xdup(void){
250 	pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
251 	runq->pc+=2;
252 }
253 void Xeflag(void){
254 	if(eflagok && !truestatus()) Xexit();
255 }
256 void Xexit(void){
257 	struct var *trapreq;
258 	struct word *starval;
259 	static int beenhere=0;
260 	if(getpid()==mypid && !beenhere){
261 		trapreq=vlook("sigexit");
262 		if(trapreq->fn){
263 			beenhere=1;
264 			--runq->pc;
265 			starval=vlook("*")->val;
266 			start(trapreq->fn, trapreq->pc, (struct var *)0);
267 			runq->local=newvar(strdup("*"), runq->local);
268 			runq->local->val=copywords(starval, (struct word *)0);
269 			runq->local->changed=1;
270 			runq->redir=runq->startredir=0;
271 			return;
272 		}
273 	}
274 	Exit(getstatus());
275 }
276 void Xfalse(void){
277 	if(truestatus()) runq->pc=runq->code[runq->pc].i;
278 	else runq->pc++;
279 }
280 int ifnot;		/* dynamic if not flag */
281 void Xifnot(void){
282 	if(ifnot)
283 		runq->pc++;
284 	else
285 		runq->pc=runq->code[runq->pc].i;
286 }
287 void Xjump(void){
288 	runq->pc=runq->code[runq->pc].i;
289 }
290 void Xmark(void){
291 	pushlist();
292 }
293 void Xpopm(void){
294 	poplist();
295 }
296 void Xread(void){
297 	char *file;
298 	int f;
299 	switch(count(runq->argv->words)){
300 	default: Xerror("< requires singleton\n"); return;
301 	case 0: Xerror("< requires file\n"); return;
302 	case 1: break;
303 	}
304 	file=runq->argv->words->word;
305 	if((f=open(file, 0))<0){
306 		pfmt(err, "%s: ", file);
307 		Xerror("can't open");
308 		return;
309 	}
310 	pushredir(ROPEN, f, runq->code[runq->pc].i);
311 	runq->pc++;
312 	poplist();
313 }
314 void turfredir(void){
315 	while(runq->redir!=runq->startredir)
316 		Xpopredir();
317 }
318 void Xpopredir(void){
319 	struct redir *rp=runq->redir;
320 	if(rp==0) panic("turfredir null!", 0);
321 	runq->redir=rp->next;
322 	if(rp->type==ROPEN) close(rp->from);
323 	efree((char *)rp);
324 }
325 void Xreturn(void){
326 	struct thread *p=runq;
327 	turfredir();
328 	while(p->argv) poplist();
329 	codefree(p->code);
330 	runq=p->ret;
331 	efree((char *)p);
332 	if(runq==0) Exit(getstatus());
333 }
334 void Xtrue(void){
335 	if(truestatus()) runq->pc++;
336 	else runq->pc=runq->code[runq->pc].i;
337 }
338 void Xif(void){
339 	ifnot=1;
340 	if(truestatus()) runq->pc++;
341 	else runq->pc=runq->code[runq->pc].i;
342 }
343 void Xwastrue(void){
344 	ifnot=0;
345 }
346 void Xword(void){
347 	pushword(runq->code[runq->pc++].s);
348 }
349 void Xwrite(void){
350 	char *file;
351 	int f;
352 	switch(count(runq->argv->words)){
353 	default: Xerror("> requires singleton\n"); return;
354 	case 0: Xerror("> requires file\n"); return;
355 	case 1: break;
356 	}
357 	file=runq->argv->words->word;
358 	if((f=Creat(file))<0){
359 		pfmt(err, "%s: ", file);
360 		Xerror("can't open");
361 		return;
362 	}
363 	pushredir(ROPEN, f, runq->code[runq->pc].i);
364 	runq->pc++;
365 	poplist();
366 }
367 char *list2str(word *words){
368 	char *value, *s, *t;
369 	int len=0;
370 	word *ap;
371 	for(ap=words;ap;ap=ap->next)
372 		len+=1+strlen(ap->word);
373 	value=emalloc(len+1);
374 	s=value;
375 	for(ap=words;ap;ap=ap->next){
376 		for(t=ap->word;*t;) *s++=*t++;
377 		*s++=' ';
378 	}
379 	if(s==value) *s='\0';
380 	else s[-1]='\0';
381 	return value;
382 }
383 void Xmatch(void){
384 	word *p;
385 	char *subject;
386 	subject=list2str(runq->argv->words);
387 	setstatus("no match");
388 	for(p=runq->argv->next->words;p;p=p->next)
389 		if(match(subject, p->word, '\0')){
390 			setstatus("");
391 			break;
392 		}
393 	efree(subject);
394 	poplist();
395 	poplist();
396 }
397 void Xcase(void){
398 	word *p;
399 	char *s;
400 	int ok=0;
401 	s=list2str(runq->argv->next->words);
402 	for(p=runq->argv->words;p;p=p->next){
403 		if(match(s, p->word, '\0')){
404 			ok=1;
405 			break;
406 		}
407 	}
408 	efree(s);
409 	if(ok)
410 		runq->pc++;
411 	else
412 		runq->pc=runq->code[runq->pc].i;
413 	poplist();
414 }
415 word *conclist(word *lp, word *rp, word *tail)
416 {
417 	char *buf;
418 	word *v;
419 	if(lp->next || rp->next)
420 		tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
421 			tail);
422 	buf=emalloc(strlen(lp->word)+strlen(rp->word)+1);
423 	strcpy(buf, lp->word);
424 	strcat(buf, rp->word);
425 	v=newword(buf, tail);
426 	efree(buf);
427 	return v;
428 }
429 void Xconc(void){
430 	word *lp=runq->argv->words;
431 	word *rp=runq->argv->next->words;
432 	word *vp=runq->argv->next->next->words;
433 	int lc=count(lp), rc=count(rp);
434 	if(lc!=0 || rc!=0){
435 		if(lc==0 || rc==0){
436 			Xerror("null list in concatenation");
437 			return;
438 		}
439 		if(lc!=1 && rc!=1 && lc!=rc){
440 			Xerror("mismatched list lengths in concatenation");
441 			return;
442 		}
443 		vp=conclist(lp, rp, vp);
444 	}
445 	poplist();
446 	poplist();
447 	runq->argv->words=vp;
448 }
449 void Xassign(void){
450 	var *v;
451 	if(count(runq->argv->words)!=1){
452 		Xerror("variable name not singleton!");
453 		return;
454 	}
455 	deglob(runq->argv->words->word);
456 	v=vlook(runq->argv->words->word);
457 	poplist();
458 	globlist();
459 	freewords(v->val);
460 	v->val=runq->argv->words;
461 	v->changed=1;
462 	runq->argv->words=0;
463 	poplist();
464 }
465 /*
466  * copy arglist a, adding the copy to the front of tail
467  */
468 word *copywords(word *a, word *tail)
469 {
470 	word *v=0, **end;
471 	for(end=&v;a;a=a->next,end=&(*end)->next)
472 		*end=newword(a->word, 0);
473 	*end=tail;
474 	return v;
475 }
476 void Xdol(void){
477 	word *a, *star;
478 	char *s, *t;
479 	int n;
480 	if(count(runq->argv->words)!=1){
481 		Xerror("variable name not singleton!");
482 		return;
483 	}
484 	s=runq->argv->words->word;
485 	deglob(s);
486 	n=0;
487 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
488 	a=runq->argv->next->words;
489 	if(n==0 || *t)
490 		a=copywords(vlook(s)->val, a);
491 	else{
492 		star=vlook("*")->val;
493 		if(star && 1<=n && n<=count(star)){
494 			while(--n) star=star->next;
495 			a=newword(star->word, a);
496 		}
497 	}
498 	poplist();
499 	runq->argv->words=a;
500 }
501 void Xqdol(void){
502 	word *a, *p;
503 	char *s;
504 	int n;
505 	if(count(runq->argv->words)!=1){
506 		Xerror("variable name not singleton!");
507 		return;
508 	}
509 	s=runq->argv->words->word;
510 	deglob(s);
511 	a=vlook(s)->val;
512 	poplist();
513 	n=count(a);
514 	if(n==0){
515 		pushword("");
516 		return;
517 	}
518 	for(p=a;p;p=p->next) n+=strlen(p->word);
519 	s=emalloc(n);
520 	if(a){
521 		strcpy(s, a->word);
522 		for(p=a->next;p;p=p->next){
523 			strcat(s, " ");
524 			strcat(s, p->word);
525 		}
526 	}
527 	else
528 		s[0]='\0';
529 	pushword(s);
530 	efree(s);
531 }
532 word *subwords(word *val, int len, word *sub, word *a)
533 {
534 	int n;
535 	char *s;
536 	if(!sub) return a;
537 	a=subwords(val, len, sub->next, a);
538 	s=sub->word;
539 	deglob(s);
540 	n=0;
541 	while('0'<=*s && *s<='9') n=n*10+ *s++ -'0';
542 	if(n<1 || len<n) return a;
543 	for(;n!=1;--n) val=val->next;
544 	return newword(val->word, a);
545 }
546 void Xsub(void){
547 	word *a, *v;
548 	char *s;
549 	if(count(runq->argv->next->words)!=1){
550 		Xerror("variable name not singleton!");
551 		return;
552 	}
553 	s=runq->argv->next->words->word;
554 	deglob(s);
555 	a=runq->argv->next->next->words;
556 	v=vlook(s)->val;
557 	a=subwords(v, count(v), runq->argv->words, a);
558 	poplist();
559 	poplist();
560 	runq->argv->words=a;
561 }
562 void Xcount(void){
563 	word *a;
564 	char *s, *t;
565 	int n;
566 	char num[12];
567 	if(count(runq->argv->words)!=1){
568 		Xerror("variable name not singleton!");
569 		return;
570 	}
571 	s=runq->argv->words->word;
572 	deglob(s);
573 	n=0;
574 	for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0';
575 	if(n==0 || *t){
576 		a=vlook(s)->val;
577 		itoa(num, count(a));
578 	}
579 	else{
580 		a=vlook("*")->val;
581 		itoa(num, a && 1<=n && n<=count(a)?1:0);
582 	}
583 	poplist();
584 	pushword(num);
585 }
586 void Xlocal(void){
587 	if(count(runq->argv->words)!=1){
588 		Xerror("variable name must be singleton\n");
589 		return;
590 	}
591 	deglob(runq->argv->words->word);
592 	runq->local=newvar(strdup(runq->argv->words->word), runq->local);
593 	runq->local->val=copywords(runq->argv->next->words, (word *)0);
594 	runq->local->changed=1;
595 	poplist();
596 	poplist();
597 }
598 void Xunlocal(void){
599 	var *v=runq->local, *hid;
600 	if(v==0) panic("Xunlocal: no locals!", 0);
601 	runq->local=v->next;
602 	hid=vlook(v->name);
603 	hid->changed=1;
604 	efree(v->name);
605 	freewords(v->val);
606 	efree((char *)v);
607 }
608 void freewords(word *w)
609 {
610 	word *nw;
611 	while(w){
612 		efree(w->word);
613 		nw=w->next;
614 		efree((char *)w);
615 		w=nw;
616 	}
617 }
618 void Xfn(void){
619 	var *v;
620 	word *a;
621 	int end;
622 	end=runq->code[runq->pc].i;
623 	for(a=runq->argv->words;a;a=a->next){
624 		v=gvlook(a->word);
625 		if(v->fn) codefree(v->fn);
626 		v->fn=codecopy(runq->code);
627 		v->pc=runq->pc+2;
628 		v->fnchanged=1;
629 	}
630 	runq->pc=end;
631 	poplist();
632 }
633 void Xdelfn(void){
634 	var *v;
635 	word *a;
636 	for(a=runq->argv->words;a;a=a->next){
637 		v=gvlook(a->word);
638 		if(v->fn) codefree(v->fn);
639 		v->fn=0;
640 		v->fnchanged=1;
641 	}
642 	poplist();
643 }
644 void Xpipe(void){
645 	struct thread *p=runq;
646 	int pc=p->pc, forkid;
647 	int lfd=p->code[pc++].i;
648 	int rfd=p->code[pc++].i;
649 	int pfd[2];
650 	if(pipe(pfd)<0){
651 		Xerror("can't get pipe");
652 		return;
653 	}
654 	switch(forkid=fork()){
655 	case -1:
656 		Xerror("try again");
657 		break;
658 	case 0:
659 		start(p->code, pc+2, runq->local);
660 		runq->ret=0;
661 		close(pfd[PRD]);
662 		pushredir(ROPEN, pfd[PWR], lfd);
663 		break;
664 	default:
665 		start(p->code, p->code[pc].i, runq->local);
666 		close(pfd[PWR]);
667 		pushredir(ROPEN, pfd[PRD], rfd);
668 		p->pc=p->code[pc+1].i;
669 		p->pid=forkid;
670 		break;
671 	}
672 }
673 char *concstatus(char *s, char *t)
674 {
675 	static char v[NSTATUS+1];
676 	int n=strlen(s);
677 	strncpy(v, s, NSTATUS);
678 	if(n<NSTATUS){
679 		v[n]='|';
680 		strncpy(v+n+1, t, NSTATUS-n-1);
681 	}
682 	v[NSTATUS]='\0';
683 	return v;
684 }
685 void Xpipewait(void){
686 	char status[NSTATUS+1];
687 	if(runq->pid==-1)
688 		setstatus(concstatus(runq->status, getstatus()));
689 	else{
690 		strncpy(status, getstatus(), NSTATUS);
691 		status[NSTATUS]='\0';
692 		Waitfor(runq->pid, 1);
693 		runq->pid=-1;
694 		setstatus(concstatus(getstatus(), status));
695 	}
696 }
697 void Xrdcmds(void){
698 	struct thread *p=runq;
699 	word *prompt;
700 	flush(err);
701 	nerror=0;
702 	if(flag['s'] && !truestatus())
703 		pfmt(err, "status=%v\n", vlook("status")->val);
704 	if(runq->iflag){
705 		prompt=vlook("prompt")->val;
706 		if(prompt)
707 			promptstr=prompt->word;
708 		else
709 			promptstr="% ";
710 	}
711 	Noerror();
712 	if(yyparse()){
713 		if(!p->iflag || p->eof && !Eintr()){
714 			if(p->cmdfile) efree(p->cmdfile);
715 			closeio(p->cmdfd);
716 			Xreturn();	/* should this be omitted? */
717 		}
718 		else{
719 			if(Eintr()){
720 				pchr(err, '\n');
721 				p->eof=0;
722 			}
723 			--p->pc;	/* go back for next command */
724 		}
725 	}
726 	else{
727 		ntrap = 0;	/* avoid double-interrupts during blocked writes */
728 		--p->pc;	/* re-execute Xrdcmds after codebuf runs */
729 		start(codebuf, 1, runq->local);
730 	}
731 	freenodes();
732 }
733 void Xerror(char *s)
734 {
735 	pfmt(err, "rc: %s\n", s);
736 	flush(err);
737 	while(!runq->iflag) Xreturn();
738 }
739 void Xbackq(void){
740 	char wd[8193];
741 	int c;
742 	char *s, *ewd=&wd[8192], *stop;
743 	struct io *f;
744 	var *ifs=vlook("ifs");
745 	word *v, *nextv;
746 	int pfd[2];
747 	int pid;
748 	stop=ifs->val?ifs->val->word:"";
749 	if(pipe(pfd)<0){
750 		Xerror("can't make pipe");
751 		return;
752 	}
753 	switch(pid=fork()){
754 	case -1: Xerror("try again");
755 		close(pfd[PRD]);
756 		close(pfd[PWR]);
757 		return;
758 	case 0:
759 		close(pfd[PRD]);
760 		start(runq->code, runq->pc+1, runq->local);
761 		pushredir(ROPEN, pfd[PWR], 1);
762 		return;
763 	default:
764 		close(pfd[PWR]);
765 		f=openfd(pfd[PRD]);
766 		s=wd;
767 		v=0;
768 		while((c=rchr(f))!=EOF){
769 			if(strchr(stop, c) || s==ewd){
770 				if(s!=wd){
771 					*s='\0';
772 					v=newword(wd, v);
773 					s=wd;
774 				}
775 			}
776 			else *s++=c;
777 		}
778 		if(s!=wd){
779 			*s='\0';
780 			v=newword(wd, v);
781 		}
782 		closeio(f);
783 		Waitfor(pid, 0);
784 		/* v points to reversed arglist -- reverse it onto argv */
785 		while(v){
786 			nextv=v->next;
787 			v->next=runq->argv->words;
788 			runq->argv->words=v;
789 			v=nextv;
790 		}
791 		runq->pc=runq->code[runq->pc].i;
792 		return;
793 	}
794 }
795 /*
796  * Who should wait for the exit from the fork?
797  */
798 void Xpipefd(void){
799 	struct thread *p=runq;
800 	int pc=p->pc;
801 	char name[40];
802 	int pfd[2];
803 	int sidefd, mainfd;
804 	if(pipe(pfd)<0){
805 		Xerror("can't get pipe");
806 		return;
807 	}
808 	if(p->code[pc].i==READ){
809 		sidefd=pfd[PWR];
810 		mainfd=pfd[PRD];
811 	}
812 	else{
813 		sidefd=pfd[PRD];
814 		mainfd=pfd[PWR];
815 	}
816 	switch(fork()){
817 	case -1:
818 		Xerror("try again");
819 		break;
820 	case 0:
821 		start(p->code, pc+2, runq->local);
822 		close(mainfd);
823 		pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0);
824 		runq->ret=0;
825 		break;
826 	default:
827 		close(sidefd);
828 		pushredir(ROPEN, mainfd, mainfd);	/* isn't this a noop? */
829 		strcpy(name, Fdprefix);
830 		itoa(name+strlen(name), mainfd);
831 		pushword(name);
832 		p->pc=p->code[pc+1].i;
833 		break;
834 	}
835 }
836 void Xsubshell(void){
837 	int pid;
838 	switch(pid=fork()){
839 	case -1:
840 		Xerror("try again");
841 		break;
842 	case 0:
843 		start(runq->code, runq->pc+1, runq->local);
844 		runq->ret=0;
845 		break;
846 	default:
847 		Waitfor(pid, 1);
848 		runq->pc=runq->code[runq->pc].i;
849 		break;
850 	}
851 }
852 void setstatus(char *s)
853 {
854 	setvar("status", newword(s, (word *)0));
855 }
856 char *getstatus(void){
857 	var *status=vlook("status");
858 	return status->val?status->val->word:"";
859 }
860 int truestatus(void){
861 	char *s;
862 	for(s=getstatus();*s;s++)
863 		if(*s!='|' && *s!='0') return 0;
864 	return 1;
865 }
866 void Xdelhere(void){
867 	Unlink(runq->code[runq->pc++].s);
868 }
869 void Xfor(void){
870 	if(runq->argv->words==0){
871 		poplist();
872 		runq->pc=runq->code[runq->pc].i;
873 	}
874 	else{
875 		freelist(runq->local->val);
876 		runq->local->val=runq->argv->words;
877 		runq->local->changed=1;
878 		runq->argv->words=runq->argv->words->next;
879 		runq->local->val->next=0;
880 		runq->pc++;
881 	}
882 }
883 void Xglob(void){
884 	globlist();
885 }
886