Blob


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