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