]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/rc/exec.c
rc: change plan9 envname limit to 128, cleanup
[plan9front.git] / sys / src / cmd / rc / exec.c
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 copynwords(word *a, word *tail, int n)
683 {
684         word *v, **end;
685         
686         v = 0;
687         end = &v;
688         while(n-- > 0){
689                 *end = newword(a->word, 0);
690                 end = &(*end)->next;
691                 a = a->next;
692         }
693         *end = tail;
694         return v;
695 }
696
697 word*
698 subwords(word *val, int len, word *sub, word *a)
699 {
700         int n, m;
701         char *s;
702         if(!sub)
703                 return a;
704         a = subwords(val, len, sub->next, a);
705         s = sub->word;
706         deglob(s);
707         m = 0;
708         n = 0;
709         while('0'<=*s && *s<='9')
710                 n = n*10+ *s++ -'0';
711         if(*s == '-'){
712                 if(*++s == 0)
713                         m = len - n;
714                 else{
715                         while('0'<=*s && *s<='9')
716                                 m = m*10+ *s++ -'0';
717                         m -= n;
718                 }
719         }
720         if(n<1 || n>len || m<0)
721                 return a;
722         if(n+m>len)
723                 m = len-n;
724         while(--n > 0)
725                 val = val->next;
726         return copynwords(val, a, m+1);
727 }
728
729 void
730 Xsub(void)
731 {
732         word *a, *v;
733         char *s;
734         if(count(runq->argv->next->words)!=1){
735                 Xerror1("variable name not singleton!");
736                 return;
737         }
738         s = runq->argv->next->words->word;
739         deglob(s);
740         a = runq->argv->next->next->words;
741         v = vlook(s)->val;
742         a = subwords(v, count(v), runq->argv->words, a);
743         poplist();
744         poplist();
745         runq->argv->words = a;
746 }
747
748 void
749 Xcount(void)
750 {
751         word *a;
752         char *s, *t;
753         int n;
754         char num[12];
755         if(count(runq->argv->words)!=1){
756                 Xerror1("variable name not singleton!");
757                 return;
758         }
759         s = runq->argv->words->word;
760         deglob(s);
761         n = 0;
762         for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
763         if(n==0 || *t){
764                 a = vlook(s)->val;
765                 inttoascii(num, count(a));
766         }
767         else{
768                 a = vlook("*")->val;
769                 inttoascii(num, a && 1<=n && n<=count(a)?1:0);
770         }
771         poplist();
772         pushword(num);
773 }
774
775 void
776 Xlocal(void)
777 {
778         if(count(runq->argv->words)!=1){
779                 Xerror1("variable name must be singleton\n");
780                 return;
781         }
782         deglob(runq->argv->words->word);
783         runq->local = newvar(strdup(runq->argv->words->word), runq->local);
784         poplist();
785         globlist();
786         runq->local->val = runq->argv->words;
787         runq->local->changed = 1;
788         runq->argv->words = 0;
789         poplist();
790 }
791
792 void
793 Xunlocal(void)
794 {
795         var *v = runq->local, *hid;
796         if(v==0)
797                 panic("Xunlocal: no locals!", 0);
798         runq->local = v->next;
799         hid = vlook(v->name);
800         hid->changed = 1;
801         efree(v->name);
802         freewords(v->val);
803         efree((char *)v);
804 }
805
806 void
807 freewords(word *w)
808 {
809         word *nw;
810         while(w){
811                 efree(w->word);
812                 nw = w->next;
813                 efree((char *)w);
814                 w = nw;
815         }
816 }
817
818 void
819 Xfn(void)
820 {
821         var *v;
822         word *a;
823         int end;
824         end = runq->code[runq->pc].i;
825         globlist();
826         for(a = runq->argv->words;a;a = a->next){
827                 v = gvlook(a->word);
828                 if(v->fn)
829                         codefree(v->fn);
830                 v->fn = codecopy(runq->code);
831                 v->pc = runq->pc+2;
832                 v->fnchanged = 1;
833         }
834         runq->pc = end;
835         poplist();
836 }
837
838 void
839 Xdelfn(void)
840 {
841         var *v;
842         word *a;
843         for(a = runq->argv->words;a;a = a->next){
844                 v = gvlook(a->word);
845                 if(v->fn)
846                         codefree(v->fn);
847                 v->fn = 0;
848                 v->fnchanged = 1;
849         }
850         poplist();
851 }
852
853 char*
854 concstatus(char *s, char *t)
855 {
856         static char v[NSTATUS+1];
857         int n = strlen(s);
858         strncpy(v, s, NSTATUS);
859         if(n<NSTATUS){
860                 v[n]='|';
861                 strncpy(v+n+1, t, NSTATUS-n-1);
862         }
863         v[NSTATUS]='\0';
864         return v;
865 }
866
867 void
868 Xpipewait(void)
869 {
870         char status[NSTATUS+1];
871         if(runq->pid==-1)
872                 setstatus(concstatus(runq->status, getstatus()));
873         else{
874                 strncpy(status, getstatus(), NSTATUS);
875                 status[NSTATUS]='\0';
876                 Waitfor(runq->pid, 1);
877                 runq->pid=-1;
878                 setstatus(concstatus(getstatus(), status));
879         }
880 }
881
882 void
883 Xrdcmds(void)
884 {
885         struct thread *p = runq;
886         word *prompt;
887         flush(err);
888         nerror = 0;
889         if(flag['s'] && !truestatus())
890                 pfmt(err, "status=%v\n", vlook("status")->val);
891         if(runq->iflag){
892                 prompt = vlook("prompt")->val;
893                 if(prompt)
894                         promptstr = prompt->word;
895                 else
896                         promptstr="% ";
897         }
898         Noerror();
899         if(yyparse()){
900                 if(!p->iflag || p->eof && !Eintr()){
901                         if(p->cmdfile)
902                                 efree(p->cmdfile);
903                         closeio(p->cmdfd);
904                         Xreturn();      /* should this be omitted? */
905                 }
906                 else{
907                         if(Eintr()){
908                                 pchr(err, '\n');
909                                 p->eof = 0;
910                         }
911                         --p->pc;        /* go back for next command */
912                 }
913         }
914         else{
915                 ntrap = 0;      /* avoid double-interrupts during blocked writes */
916                 --p->pc;        /* re-execute Xrdcmds after codebuf runs */
917                 start(codebuf, 1, runq->local);
918         }
919         freenodes();
920 }
921
922 void
923 Xerror(char *s)
924 {
925         if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
926                 pfmt(err, "rc: %s: %r\n", s);
927         else
928                 pfmt(err, "rc (%s): %s: %r\n", argv0, s);
929         flush(err);
930         setstatus("error");
931         while(!runq->iflag) Xreturn();
932 }
933
934 void
935 Xerror1(char *s)
936 {
937         if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
938                 pfmt(err, "rc: %s\n", s);
939         else
940                 pfmt(err, "rc (%s): %s\n", argv0, s);
941         flush(err);
942         setstatus("error");
943         while(!runq->iflag) Xreturn();
944 }
945
946 void
947 setstatus(char *s)
948 {
949         setvar("status", newword(s, (word *)0));
950 }
951
952 char*
953 getstatus(void)
954 {
955         var *status = vlook("status");
956         return status->val?status->val->word:"";
957 }
958
959 int
960 truestatus(void)
961 {
962         char *s;
963         for(s = getstatus();*s;s++)
964                 if(*s!='|' && *s!='0')
965                         return 0;
966         return 1;
967 }
968
969 void
970 Xdelhere(void)
971 {
972         Unlink(runq->code[runq->pc++].s);
973 }
974
975 void
976 Xfor(void)
977 {
978         if(runq->argv->words==0){
979                 poplist();
980                 runq->pc = runq->code[runq->pc].i;
981         }
982         else{
983                 freelist(runq->local->val);
984                 runq->local->val = runq->argv->words;
985                 runq->local->changed = 1;
986                 runq->argv->words = runq->argv->words->next;
987                 runq->local->val->next = 0;
988                 runq->pc++;
989         }
990 }
991
992 void
993 Xglob(void)
994 {
995         globlist();
996 }