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