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