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