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