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";
11 void
12 start(code *c, int pc, var *local)
13 {
14 struct thread *p = new(struct thread);
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 }
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 }
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 }
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 }
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 }
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 }
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 }
93 int
94 count(word *w)
95 {
96 int n;
97 for(n = 0;w;n++) w = w->next;
98 return n;
99 }
101 void
102 pushredir(int type, int from, int to)
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;
112 var*
113 newvar(char *name, var *next)
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 v->changefn = 0;
123 return v;
125 /*
126 * get command line flags, initialize keywords & traps.
127 * get values from environment.
128 * set $pid, $cflag, $*
129 * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
130 * start interpreting code
131 */
132 int
133 main(int argc, char *argv[])
135 code bootstrap[32];
136 char num[12], *rcmain;
137 int i;
139 /* needed for rcmain later */
140 putenv("PLAN9", unsharp("#9"));
142 argc = getflags(argc, argv, "DSYsrdiIlxepvVc:1m:1[command]", 1);
143 if(argc==-1)
144 usage("[file [arg ...]]");
145 if(argv[0][0]=='-')
146 flag['l'] = flagset;
147 if(flag['I'])
148 flag['i'] = 0;
149 else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
150 rcmain = flag['m'] ? flag['m'][0] : Rcmain();
151 err = openfd(2);
152 kinit();
153 Trapinit();
154 Vinit();
155 inttoascii(num, mypid = getpid());
156 pathinit();
157 setvar("pid", newword(num, (word *)0));
158 setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
159 :(word *)0);
160 setvar("rcname", newword(argv[0], (word *)0));
161 i = 0;
162 bootstrap[i++].i = 1;
163 bootstrap[i++].f = Xmark;
164 bootstrap[i++].f = Xword;
165 bootstrap[i++].s="*";
166 bootstrap[i++].f = Xassign;
167 bootstrap[i++].f = Xmark;
168 bootstrap[i++].f = Xmark;
169 bootstrap[i++].f = Xword;
170 bootstrap[i++].s="*";
171 bootstrap[i++].f = Xdol;
172 bootstrap[i++].f = Xword;
173 bootstrap[i++].s = rcmain;
174 bootstrap[i++].f = Xword;
175 bootstrap[i++].s=".";
176 bootstrap[i++].f = Xsimple;
177 bootstrap[i++].f = Xexit;
178 bootstrap[i].i = 0;
179 start(bootstrap, 1, (var *)0);
180 /* prime bootstrap argv */
181 pushlist();
182 argv0 = strdup(argv[0]);
183 for(i = argc-1;i!=0;--i) pushword(argv[i]);
184 for(;;){
185 if(flag['r'])
186 pfnc(err, runq);
187 runq->pc++;
188 (*runq->code[runq->pc-1].f)();
189 if(ntrap)
190 dotrap();
192 return 0; /* not reached; silence OS X Lion gcc */
194 /*
195 * Opcode routines
196 * Arguments on stack (...)
197 * Arguments in line [...]
198 * Code in line with jump around {...}
200 * Xappend(file)[fd] open file to append
201 * Xassign(name, val) assign val to name
202 * Xasync{... Xexit} make thread for {}, no wait
203 * Xbackq{... Xreturn} make thread for {}, push stdout
204 * Xbang complement condition
205 * Xcase(pat, value){...} exec code on match, leave (value) on
206 * stack
207 * Xclose[i] close file descriptor
208 * Xconc(left, right) concatenate, push results
209 * Xcount(name) push var count
210 * Xdelfn(name) delete function definition
211 * Xdeltraps(names) delete named traps
212 * Xdol(name) get variable value
213 * Xqdol(name) concatenate variable components
214 * Xdup[i j] dup file descriptor
215 * Xexit rc exits with status
216 * Xfalse{...} execute {} if false
217 * Xfn(name){... Xreturn} define function
218 * Xfor(var, list){... Xreturn} for loop
219 * Xjump[addr] goto
220 * Xlocal(name, val) create local variable, assign value
221 * Xmark mark stack
222 * Xmatch(pat, str) match pattern, set status
223 * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads,
224 * wait for both
225 * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output,
226 * depending on type), push /dev/fd/??
227 * Xpopm(value) pop value from stack
228 * Xrdwr(file)[fd] open file for reading and writing
229 * Xread(file)[fd] open file to read
230 * Xsettraps(names){... Xreturn} define trap functions
231 * Xshowtraps print trap list
232 * Xsimple(args) run command and wait
233 * Xreturn kill thread
234 * Xsubshell{... Xexit} execute {} in a subshell and wait
235 * Xtrue{...} execute {} if true
236 * Xunlocal delete local variable
237 * Xword[string] push string
238 * Xwrite(file)[fd] open file to write
239 */
241 void
242 Xappend(void)
244 char *file;
245 int f;
246 switch(count(runq->argv->words)){
247 default:
248 Xerror1(">> requires singleton");
249 return;
250 case 0:
251 Xerror1(">> requires file");
252 return;
253 case 1:
254 break;
256 file = runq->argv->words->word;
257 if((f = open(file, 1))<0 && (f = Creat(file))<0){
258 pfmt(err, "%s: ", file);
259 Xerror("can't open");
260 return;
262 Seek(f, 0L, 2);
263 pushredir(ROPEN, f, runq->code[runq->pc].i);
264 runq->pc++;
265 poplist();
268 void
269 Xsettrue(void)
271 setstatus("");
274 void
275 Xbang(void)
277 setstatus(truestatus()?"false":"");
280 void
281 Xclose(void)
283 pushredir(RCLOSE, runq->code[runq->pc].i, 0);
284 runq->pc++;
287 void
288 Xdup(void)
290 pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
291 runq->pc+=2;
294 void
295 Xeflag(void)
297 if(eflagok && !truestatus()) Xexit();
300 void
301 Xexit(void)
303 struct var *trapreq;
304 struct word *starval;
305 static int beenhere = 0;
306 if(getpid()==mypid && !beenhere){
307 trapreq = vlook("sigexit");
308 if(trapreq->fn){
309 beenhere = 1;
310 --runq->pc;
311 starval = vlook("*")->val;
312 start(trapreq->fn, trapreq->pc, (struct var *)0);
313 runq->local = newvar(strdup("*"), runq->local);
314 runq->local->val = copywords(starval, (struct word *)0);
315 runq->local->changed = 1;
316 runq->redir = runq->startredir = 0;
317 return;
320 Exit(getstatus());
323 void
324 Xfalse(void)
326 if(truestatus()) runq->pc = runq->code[runq->pc].i;
327 else runq->pc++;
329 int ifnot; /* dynamic if not flag */
331 void
332 Xifnot(void)
334 if(ifnot)
335 runq->pc++;
336 else
337 runq->pc = runq->code[runq->pc].i;
340 void
341 Xjump(void)
343 runq->pc = runq->code[runq->pc].i;
346 void
347 Xmark(void)
349 pushlist();
352 void
353 Xpopm(void)
355 poplist();
358 void
359 Xread(void)
361 char *file;
362 int f;
363 switch(count(runq->argv->words)){
364 default:
365 Xerror1("< requires singleton\n");
366 return;
367 case 0:
368 Xerror1("< requires file\n");
369 return;
370 case 1:
371 break;
373 file = runq->argv->words->word;
374 if((f = open(file, 0))<0){
375 pfmt(err, "%s: ", file);
376 Xerror("can't open");
377 return;
379 pushredir(ROPEN, f, runq->code[runq->pc].i);
380 runq->pc++;
381 poplist();
384 void
385 Xrdwr(void)
387 char *file;
388 int f;
390 switch(count(runq->argv->words)){
391 default:
392 Xerror1("<> requires singleton\n");
393 return;
394 case 0:
395 Xerror1("<> requires file\n");
396 return;
397 case 1:
398 break;
400 file = runq->argv->words->word;
401 if((f = open(file, ORDWR))<0){
402 pfmt(err, "%s: ", file);
403 Xerror("can't open");
404 return;
406 pushredir(ROPEN, f, runq->code[runq->pc].i);
407 runq->pc++;
408 poplist();
411 void
412 turfredir(void)
414 while(runq->redir!=runq->startredir)
415 Xpopredir();
418 void
419 Xpopredir(void)
421 struct redir *rp = runq->redir;
422 if(rp==0)
423 panic("turfredir null!", 0);
424 runq->redir = rp->next;
425 if(rp->type==ROPEN)
426 close(rp->from);
427 efree((char *)rp);
430 void
431 Xreturn(void)
433 struct thread *p = runq;
434 turfredir();
435 while(p->argv) poplist();
436 codefree(p->code);
437 runq = p->ret;
438 efree((char *)p);
439 if(runq==0)
440 Exit(getstatus());
443 void
444 Xtrue(void)
446 if(truestatus()) runq->pc++;
447 else runq->pc = runq->code[runq->pc].i;
450 void
451 Xif(void)
453 ifnot = 1;
454 if(truestatus()) runq->pc++;
455 else runq->pc = runq->code[runq->pc].i;
458 void
459 Xwastrue(void)
461 ifnot = 0;
464 void
465 Xword(void)
467 pushword(runq->code[runq->pc++].s);
470 void
471 Xwrite(void)
473 char *file;
474 int f;
475 switch(count(runq->argv->words)){
476 default:
477 Xerror1("> requires singleton\n");
478 return;
479 case 0:
480 Xerror1("> requires file\n");
481 return;
482 case 1:
483 break;
485 file = runq->argv->words->word;
486 if((f = Creat(file))<0){
487 pfmt(err, "%s: ", file);
488 Xerror("can't open");
489 return;
491 pushredir(ROPEN, f, runq->code[runq->pc].i);
492 runq->pc++;
493 poplist();
496 char*
497 list2str(word *words)
499 char *value, *s, *t;
500 int len = 0;
501 word *ap;
502 for(ap = words;ap;ap = ap->next)
503 len+=1+strlen(ap->word);
504 value = emalloc(len+1);
505 s = value;
506 for(ap = words;ap;ap = ap->next){
507 for(t = ap->word;*t;) *s++=*t++;
508 *s++=' ';
510 if(s==value)
511 *s='\0';
512 else s[-1]='\0';
513 return value;
516 void
517 Xmatch(void)
519 word *p;
520 char *subject;
521 subject = list2str(runq->argv->words);
522 setstatus("no match");
523 for(p = runq->argv->next->words;p;p = p->next)
524 if(match(subject, p->word, '\0')){
525 setstatus("");
526 break;
528 efree(subject);
529 poplist();
530 poplist();
533 void
534 Xcase(void)
536 word *p;
537 char *s;
538 int ok = 0;
539 s = list2str(runq->argv->next->words);
540 for(p = runq->argv->words;p;p = p->next){
541 if(match(s, p->word, '\0')){
542 ok = 1;
543 break;
546 efree(s);
547 if(ok)
548 runq->pc++;
549 else
550 runq->pc = runq->code[runq->pc].i;
551 poplist();
554 word*
555 conclist(word *lp, word *rp, word *tail)
557 char *buf;
558 word *v;
559 if(lp->next || rp->next)
560 tail = conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
561 tail);
562 buf = emalloc(strlen(lp->word)+strlen(rp->word)+1);
563 strcpy(buf, lp->word);
564 strcat(buf, rp->word);
565 v = newword(buf, tail);
566 efree(buf);
567 return v;
570 void
571 Xconc(void)
573 word *lp = runq->argv->words;
574 word *rp = runq->argv->next->words;
575 word *vp = runq->argv->next->next->words;
576 int lc = count(lp), rc = count(rp);
577 if(lc!=0 || rc!=0){
578 if(lc==0 || rc==0){
579 Xerror1("null list in concatenation");
580 return;
582 if(lc!=1 && rc!=1 && lc!=rc){
583 Xerror1("mismatched list lengths in concatenation");
584 return;
586 vp = conclist(lp, rp, vp);
588 poplist();
589 poplist();
590 runq->argv->words = vp;
593 void
594 Xassign(void)
596 var *v;
597 if(count(runq->argv->words)!=1){
598 Xerror1("variable name not singleton!");
599 return;
601 deglob(runq->argv->words->word);
602 v = vlook(runq->argv->words->word);
603 poplist();
604 globlist();
605 freewords(v->val);
606 v->val = runq->argv->words;
607 v->changed = 1;
608 if(v->changefn)
609 v->changefn(v);
610 runq->argv->words = 0;
611 poplist();
613 /*
614 * copy arglist a, adding the copy to the front of tail
615 */
617 word*
618 copywords(word *a, word *tail)
620 word *v = 0, **end;
621 for(end=&v;a;a = a->next,end=&(*end)->next)
622 *end = newword(a->word, 0);
623 *end = tail;
624 return v;
627 void
628 Xdol(void)
630 word *a, *star;
631 char *s, *t;
632 int n;
633 if(count(runq->argv->words)!=1){
634 Xerror1("variable name not singleton!");
635 return;
637 s = runq->argv->words->word;
638 deglob(s);
639 n = 0;
640 for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
641 a = runq->argv->next->words;
642 if(n==0 || *t)
643 a = copywords(vlook(s)->val, a);
644 else{
645 star = vlook("*")->val;
646 if(star && 1<=n && n<=count(star)){
647 while(--n) star = star->next;
648 a = newword(star->word, a);
651 poplist();
652 runq->argv->words = a;
655 void
656 Xqdol(void)
658 word *a, *p;
659 char *s;
660 int n;
661 if(count(runq->argv->words)!=1){
662 Xerror1("variable name not singleton!");
663 return;
665 s = runq->argv->words->word;
666 deglob(s);
667 a = vlook(s)->val;
668 poplist();
669 n = count(a);
670 if(n==0){
671 pushword("");
672 return;
674 for(p = a;p;p = p->next) n+=strlen(p->word);
675 s = emalloc(n);
676 if(a){
677 strcpy(s, a->word);
678 for(p = a->next;p;p = p->next){
679 strcat(s, " ");
680 strcat(s, p->word);
683 else
684 s[0]='\0';
685 pushword(s);
686 efree(s);
689 word*
690 copynwords(word *a, word *tail, int n)
692 word *v, **end;
694 v = 0;
695 end = &v;
696 while(n-- > 0){
697 *end = newword(a->word, 0);
698 end = &(*end)->next;
699 a = a->next;
701 *end = tail;
702 return v;
705 word*
706 subwords(word *val, int len, word *sub, word *a)
708 int n, m;
709 char *s;
710 if(!sub)
711 return a;
712 a = subwords(val, len, sub->next, a);
713 s = sub->word;
714 deglob(s);
715 m = 0;
716 n = 0;
717 while('0'<=*s && *s<='9')
718 n = n*10+ *s++ -'0';
719 if(*s == '-'){
720 if(*++s == 0)
721 m = len - n;
722 else{
723 while('0'<=*s && *s<='9')
724 m = m*10+ *s++ -'0';
725 m -= n;
728 if(n<1 || n>len || m<0)
729 return a;
730 if(n+m>len)
731 m = len-n;
732 while(--n > 0)
733 val = val->next;
734 return copynwords(val, a, m+1);
737 void
738 Xsub(void)
740 word *a, *v;
741 char *s;
742 if(count(runq->argv->next->words)!=1){
743 Xerror1("variable name not singleton!");
744 return;
746 s = runq->argv->next->words->word;
747 deglob(s);
748 a = runq->argv->next->next->words;
749 v = vlook(s)->val;
750 a = subwords(v, count(v), runq->argv->words, a);
751 poplist();
752 poplist();
753 runq->argv->words = a;
756 void
757 Xcount(void)
759 word *a;
760 char *s, *t;
761 int n;
762 char num[12];
763 if(count(runq->argv->words)!=1){
764 Xerror1("variable name not singleton!");
765 return;
767 s = runq->argv->words->word;
768 deglob(s);
769 n = 0;
770 for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
771 if(n==0 || *t){
772 a = vlook(s)->val;
773 inttoascii(num, count(a));
775 else{
776 a = vlook("*")->val;
777 inttoascii(num, a && 1<=n && n<=count(a)?1:0);
779 poplist();
780 pushword(num);
783 void
784 Xlocal(void)
786 if(count(runq->argv->words)!=1){
787 Xerror1("variable name must be singleton\n");
788 return;
790 deglob(runq->argv->words->word);
791 runq->local = newvar(strdup(runq->argv->words->word), runq->local);
792 runq->local->val = copywords(runq->argv->next->words, (word *)0);
793 runq->local->changed = 1;
794 poplist();
795 poplist();
798 void
799 Xunlocal(void)
801 var *v = runq->local, *hid;
802 if(v==0)
803 panic("Xunlocal: no locals!", 0);
804 runq->local = v->next;
805 hid = vlook(v->name);
806 hid->changed = 1;
807 efree(v->name);
808 freewords(v->val);
809 efree((char *)v);
812 void
813 freewords(word *w)
815 word *nw;
816 while(w){
817 efree(w->word);
818 nw = w->next;
819 efree((char *)w);
820 w = nw;
824 void
825 Xfn(void)
827 var *v;
828 word *a;
829 int end;
830 end = runq->code[runq->pc].i;
831 for(a = runq->argv->words;a;a = a->next){
832 v = gvlook(a->word);
833 if(v->fn)
834 codefree(v->fn);
835 v->fn = codecopy(runq->code);
836 v->pc = runq->pc+2;
837 v->fnchanged = 1;
839 runq->pc = end;
840 poplist();
843 void
844 Xdelfn(void)
846 var *v;
847 word *a;
848 for(a = runq->argv->words;a;a = a->next){
849 v = gvlook(a->word);
850 if(v->fn)
851 codefree(v->fn);
852 v->fn = 0;
853 v->fnchanged = 1;
855 poplist();
858 char*
859 concstatus(char *s, char *t)
861 static char v[NSTATUS+1];
862 int n = strlen(s);
863 strncpy(v, s, NSTATUS);
864 if(n<NSTATUS){
865 v[n]='|';
866 strncpy(v+n+1, t, NSTATUS-n-1);
868 v[NSTATUS]='\0';
869 return v;
872 void
873 Xpipewait(void)
875 char status[NSTATUS+1];
876 if(runq->pid==-1)
877 setstatus(concstatus(runq->status, getstatus()));
878 else{
879 strncpy(status, getstatus(), NSTATUS);
880 status[NSTATUS]='\0';
881 Waitfor(runq->pid, 1);
882 runq->pid=-1;
883 setstatus(concstatus(getstatus(), status));
887 void
888 Xrdcmds(void)
890 struct thread *p = runq;
891 word *prompt;
892 flush(err);
893 nerror = 0;
894 if(flag['s'] && !truestatus())
895 pfmt(err, "status=%v\n", vlook("status")->val);
896 if(runq->iflag){
897 prompt = vlook("prompt")->val;
898 if(prompt)
899 promptstr = prompt->word;
900 else
901 promptstr="% ";
903 Noerror();
904 if((flag['Y'] ? yyparse : parse)()){
905 if(!p->iflag || p->eof && !Eintr()){
906 if(p->cmdfile)
907 efree(p->cmdfile);
908 closeio(p->cmdfd);
909 Xreturn(); /* should this be omitted? */
911 else{
912 if(Eintr()){
913 pchr(err, '\n');
914 p->eof = 0;
916 --p->pc; /* go back for next command */
919 else{
920 ntrap = 0; /* avoid double-interrupts during blocked writes */
921 --p->pc; /* re-execute Xrdcmds after codebuf runs */
922 start(codebuf, 1, runq->local);
924 freenodes();
927 void
928 Xerror(char *s)
930 if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
931 pfmt(err, "rc: %s: %r\n", s);
932 else
933 pfmt(err, "rc (%s): %s: %r\n", argv0, s);
934 flush(err);
935 setstatus("error");
936 while(!runq->iflag) Xreturn();
939 void
940 Xerror1(char *s)
942 if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
943 pfmt(err, "rc: %s\n", s);
944 else
945 pfmt(err, "rc (%s): %s\n", argv0, s);
946 flush(err);
947 setstatus("error");
948 while(!runq->iflag) Xreturn();
951 void
952 setstatus(char *s)
954 setvar("status", newword(s, (word *)0));
957 char*
958 getstatus(void)
960 var *status = vlook("status");
961 return status->val?status->val->word:"";
964 int
965 truestatus(void)
967 char *s;
968 for(s = getstatus();*s;s++)
969 if(*s!='|' && *s!='0')
970 return 0;
971 return 1;
974 void
975 Xdelhere(void)
977 Unlink(runq->code[runq->pc++].s);
980 void
981 Xfor(void)
983 if(runq->argv->words==0){
984 poplist();
985 runq->pc = runq->code[runq->pc].i;
987 else{
988 freelist(runq->local->val);
989 runq->local->val = runq->argv->words;
990 runq->local->changed = 1;
991 runq->argv->words = runq->argv->words->next;
992 runq->local->val->next = 0;
993 runq->pc++;
997 void
998 Xglob(void)
1000 globlist();