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, "SsrdiIlxepvVc: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();
193 /*
194 * Opcode routines
195 * Arguments on stack (...)
196 * Arguments in line [...]
197 * Code in line with jump around {...}
199 * Xappend(file)[fd] open file to append
200 * Xassign(name, val) assign val to name
201 * Xasync{... Xexit} make thread for {}, no wait
202 * Xbackq{... Xreturn} make thread for {}, push stdout
203 * Xbang complement condition
204 * Xcase(pat, value){...} exec code on match, leave (value) on
205 * stack
206 * Xclose[i] close file descriptor
207 * Xconc(left, right) concatenate, push results
208 * Xcount(name) push var count
209 * Xdelfn(name) delete function definition
210 * Xdeltraps(names) delete named traps
211 * Xdol(name) get variable value
212 * Xqdol(name) concatenate variable components
213 * Xdup[i j] dup file descriptor
214 * Xexit rc exits with status
215 * Xfalse{...} execute {} if false
216 * Xfn(name){... Xreturn} define function
217 * Xfor(var, list){... Xreturn} for loop
218 * Xjump[addr] goto
219 * Xlocal(name, val) create local variable, assign value
220 * Xmark mark stack
221 * Xmatch(pat, str) match pattern, set status
222 * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads,
223 * wait for both
224 * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output,
225 * depending on type), push /dev/fd/??
226 * Xpopm(value) pop value from stack
227 * Xrdwr(file)[fd] open file for reading and writing
228 * Xread(file)[fd] open file to read
229 * Xsettraps(names){... Xreturn} define trap functions
230 * Xshowtraps print trap list
231 * Xsimple(args) run command and wait
232 * Xreturn kill thread
233 * Xsubshell{... Xexit} execute {} in a subshell and wait
234 * Xtrue{...} execute {} if true
235 * Xunlocal delete local variable
236 * Xword[string] push string
237 * Xwrite(file)[fd] open file to write
238 */
240 void
241 Xappend(void)
243 char *file;
244 int f;
245 switch(count(runq->argv->words)){
246 default:
247 Xerror1(">> requires singleton");
248 return;
249 case 0:
250 Xerror1(">> requires file");
251 return;
252 case 1:
253 break;
255 file = runq->argv->words->word;
256 if((f = open(file, 1))<0 && (f = Creat(file))<0){
257 pfmt(err, "%s: ", file);
258 Xerror("can't open");
259 return;
261 Seek(f, 0L, 2);
262 pushredir(ROPEN, f, runq->code[runq->pc].i);
263 runq->pc++;
264 poplist();
267 void
268 Xsettrue(void)
270 setstatus("");
273 void
274 Xbang(void)
276 setstatus(truestatus()?"false":"");
279 void
280 Xclose(void)
282 pushredir(RCLOSE, runq->code[runq->pc].i, 0);
283 runq->pc++;
286 void
287 Xdup(void)
289 pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
290 runq->pc+=2;
293 void
294 Xeflag(void)
296 if(eflagok && !truestatus()) Xexit();
299 void
300 Xexit(void)
302 struct var *trapreq;
303 struct word *starval;
304 static int beenhere = 0;
305 if(getpid()==mypid && !beenhere){
306 trapreq = vlook("sigexit");
307 if(trapreq->fn){
308 beenhere = 1;
309 --runq->pc;
310 starval = vlook("*")->val;
311 start(trapreq->fn, trapreq->pc, (struct var *)0);
312 runq->local = newvar(strdup("*"), runq->local);
313 runq->local->val = copywords(starval, (struct word *)0);
314 runq->local->changed = 1;
315 runq->redir = runq->startredir = 0;
316 return;
319 Exit(getstatus());
322 void
323 Xfalse(void)
325 if(truestatus()) runq->pc = runq->code[runq->pc].i;
326 else runq->pc++;
328 int ifnot; /* dynamic if not flag */
330 void
331 Xifnot(void)
333 if(ifnot)
334 runq->pc++;
335 else
336 runq->pc = runq->code[runq->pc].i;
339 void
340 Xjump(void)
342 runq->pc = runq->code[runq->pc].i;
345 void
346 Xmark(void)
348 pushlist();
351 void
352 Xpopm(void)
354 poplist();
357 void
358 Xread(void)
360 char *file;
361 int f;
362 switch(count(runq->argv->words)){
363 default:
364 Xerror1("< requires singleton\n");
365 return;
366 case 0:
367 Xerror1("< requires file\n");
368 return;
369 case 1:
370 break;
372 file = runq->argv->words->word;
373 if((f = open(file, 0))<0){
374 pfmt(err, "%s: ", file);
375 Xerror("can't open");
376 return;
378 pushredir(ROPEN, f, runq->code[runq->pc].i);
379 runq->pc++;
380 poplist();
383 void
384 Xrdwr(void)
386 char *file;
387 int f;
389 switch(count(runq->argv->words)){
390 default:
391 Xerror1("<> requires singleton\n");
392 return;
393 case 0:
394 Xerror1("<> requires file\n");
395 return;
396 case 1:
397 break;
399 file = runq->argv->words->word;
400 if((f = open(file, ORDWR))<0){
401 pfmt(err, "%s: ", file);
402 Xerror("can't open");
403 return;
405 pushredir(ROPEN, f, runq->code[runq->pc].i);
406 runq->pc++;
407 poplist();
410 void
411 turfredir(void)
413 while(runq->redir!=runq->startredir)
414 Xpopredir();
417 void
418 Xpopredir(void)
420 struct redir *rp = runq->redir;
421 if(rp==0)
422 panic("turfredir null!", 0);
423 runq->redir = rp->next;
424 if(rp->type==ROPEN)
425 close(rp->from);
426 efree((char *)rp);
429 void
430 Xreturn(void)
432 struct thread *p = runq;
433 turfredir();
434 while(p->argv) poplist();
435 codefree(p->code);
436 runq = p->ret;
437 efree((char *)p);
438 if(runq==0)
439 Exit(getstatus());
442 void
443 Xtrue(void)
445 if(truestatus()) runq->pc++;
446 else runq->pc = runq->code[runq->pc].i;
449 void
450 Xif(void)
452 ifnot = 1;
453 if(truestatus()) runq->pc++;
454 else runq->pc = runq->code[runq->pc].i;
457 void
458 Xwastrue(void)
460 ifnot = 0;
463 void
464 Xword(void)
466 pushword(runq->code[runq->pc++].s);
469 void
470 Xwrite(void)
472 char *file;
473 int f;
474 switch(count(runq->argv->words)){
475 default:
476 Xerror1("> requires singleton\n");
477 return;
478 case 0:
479 Xerror1("> requires file\n");
480 return;
481 case 1:
482 break;
484 file = runq->argv->words->word;
485 if((f = Creat(file))<0){
486 pfmt(err, "%s: ", file);
487 Xerror("can't open");
488 return;
490 pushredir(ROPEN, f, runq->code[runq->pc].i);
491 runq->pc++;
492 poplist();
495 char*
496 list2str(word *words)
498 char *value, *s, *t;
499 int len = 0;
500 word *ap;
501 for(ap = words;ap;ap = ap->next)
502 len+=1+strlen(ap->word);
503 value = emalloc(len+1);
504 s = value;
505 for(ap = words;ap;ap = ap->next){
506 for(t = ap->word;*t;) *s++=*t++;
507 *s++=' ';
509 if(s==value)
510 *s='\0';
511 else s[-1]='\0';
512 return value;
515 void
516 Xmatch(void)
518 word *p;
519 char *subject;
520 subject = list2str(runq->argv->words);
521 setstatus("no match");
522 for(p = runq->argv->next->words;p;p = p->next)
523 if(match(subject, p->word, '\0')){
524 setstatus("");
525 break;
527 efree(subject);
528 poplist();
529 poplist();
532 void
533 Xcase(void)
535 word *p;
536 char *s;
537 int ok = 0;
538 s = list2str(runq->argv->next->words);
539 for(p = runq->argv->words;p;p = p->next){
540 if(match(s, p->word, '\0')){
541 ok = 1;
542 break;
545 efree(s);
546 if(ok)
547 runq->pc++;
548 else
549 runq->pc = runq->code[runq->pc].i;
550 poplist();
553 word*
554 conclist(word *lp, word *rp, word *tail)
556 char *buf;
557 word *v;
558 if(lp->next || rp->next)
559 tail = conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next,
560 tail);
561 buf = emalloc(strlen(lp->word)+strlen(rp->word)+1);
562 strcpy(buf, lp->word);
563 strcat(buf, rp->word);
564 v = newword(buf, tail);
565 efree(buf);
566 return v;
569 void
570 Xconc(void)
572 word *lp = runq->argv->words;
573 word *rp = runq->argv->next->words;
574 word *vp = runq->argv->next->next->words;
575 int lc = count(lp), rc = count(rp);
576 if(lc!=0 || rc!=0){
577 if(lc==0 || rc==0){
578 Xerror1("null list in concatenation");
579 return;
581 if(lc!=1 && rc!=1 && lc!=rc){
582 Xerror1("mismatched list lengths in concatenation");
583 return;
585 vp = conclist(lp, rp, vp);
587 poplist();
588 poplist();
589 runq->argv->words = vp;
592 void
593 Xassign(void)
595 var *v;
596 if(count(runq->argv->words)!=1){
597 Xerror1("variable name not singleton!");
598 return;
600 deglob(runq->argv->words->word);
601 v = vlook(runq->argv->words->word);
602 poplist();
603 globlist();
604 freewords(v->val);
605 v->val = runq->argv->words;
606 v->changed = 1;
607 if(v->changefn)
608 v->changefn(v);
609 runq->argv->words = 0;
610 poplist();
612 /*
613 * copy arglist a, adding the copy to the front of tail
614 */
616 word*
617 copywords(word *a, word *tail)
619 word *v = 0, **end;
620 for(end=&v;a;a = a->next,end=&(*end)->next)
621 *end = newword(a->word, 0);
622 *end = tail;
623 return v;
626 void
627 Xdol(void)
629 word *a, *star;
630 char *s, *t;
631 int n;
632 if(count(runq->argv->words)!=1){
633 Xerror1("variable name not singleton!");
634 return;
636 s = runq->argv->words->word;
637 deglob(s);
638 n = 0;
639 for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
640 a = runq->argv->next->words;
641 if(n==0 || *t)
642 a = copywords(vlook(s)->val, a);
643 else{
644 star = vlook("*")->val;
645 if(star && 1<=n && n<=count(star)){
646 while(--n) star = star->next;
647 a = newword(star->word, a);
650 poplist();
651 runq->argv->words = a;
654 void
655 Xqdol(void)
657 word *a, *p;
658 char *s;
659 int n;
660 if(count(runq->argv->words)!=1){
661 Xerror1("variable name not singleton!");
662 return;
664 s = runq->argv->words->word;
665 deglob(s);
666 a = vlook(s)->val;
667 poplist();
668 n = count(a);
669 if(n==0){
670 pushword("");
671 return;
673 for(p = a;p;p = p->next) n+=strlen(p->word);
674 s = emalloc(n);
675 if(a){
676 strcpy(s, a->word);
677 for(p = a->next;p;p = p->next){
678 strcat(s, " ");
679 strcat(s, p->word);
682 else
683 s[0]='\0';
684 pushword(s);
685 efree(s);
688 word*
689 subwords(word *val, int len, word *sub, word *a)
691 int n;
692 char *s;
693 if(!sub)
694 return a;
695 a = subwords(val, len, sub->next, a);
696 s = sub->word;
697 deglob(s);
698 n = 0;
699 while('0'<=*s && *s<='9') n = n*10+ *s++ -'0';
700 if(n<1 || len<n)
701 return a;
702 for(;n!=1;--n) val = val->next;
703 return newword(val->word, a);
706 void
707 Xsub(void)
709 word *a, *v;
710 char *s;
711 if(count(runq->argv->next->words)!=1){
712 Xerror1("variable name not singleton!");
713 return;
715 s = runq->argv->next->words->word;
716 deglob(s);
717 a = runq->argv->next->next->words;
718 v = vlook(s)->val;
719 a = subwords(v, count(v), runq->argv->words, a);
720 poplist();
721 poplist();
722 runq->argv->words = a;
725 void
726 Xcount(void)
728 word *a;
729 char *s, *t;
730 int n;
731 char num[12];
732 if(count(runq->argv->words)!=1){
733 Xerror1("variable name not singleton!");
734 return;
736 s = runq->argv->words->word;
737 deglob(s);
738 n = 0;
739 for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
740 if(n==0 || *t){
741 a = vlook(s)->val;
742 inttoascii(num, count(a));
744 else{
745 a = vlook("*")->val;
746 inttoascii(num, a && 1<=n && n<=count(a)?1:0);
748 poplist();
749 pushword(num);
752 void
753 Xlocal(void)
755 if(count(runq->argv->words)!=1){
756 Xerror1("variable name must be singleton\n");
757 return;
759 deglob(runq->argv->words->word);
760 runq->local = newvar(strdup(runq->argv->words->word), runq->local);
761 runq->local->val = copywords(runq->argv->next->words, (word *)0);
762 runq->local->changed = 1;
763 poplist();
764 poplist();
767 void
768 Xunlocal(void)
770 var *v = runq->local, *hid;
771 if(v==0)
772 panic("Xunlocal: no locals!", 0);
773 runq->local = v->next;
774 hid = vlook(v->name);
775 hid->changed = 1;
776 efree(v->name);
777 freewords(v->val);
778 efree((char *)v);
781 void
782 freewords(word *w)
784 word *nw;
785 while(w){
786 efree(w->word);
787 nw = w->next;
788 efree((char *)w);
789 w = nw;
793 void
794 Xfn(void)
796 var *v;
797 word *a;
798 int end;
799 end = runq->code[runq->pc].i;
800 for(a = runq->argv->words;a;a = a->next){
801 v = gvlook(a->word);
802 if(v->fn)
803 codefree(v->fn);
804 v->fn = codecopy(runq->code);
805 v->pc = runq->pc+2;
806 v->fnchanged = 1;
808 runq->pc = end;
809 poplist();
812 void
813 Xdelfn(void)
815 var *v;
816 word *a;
817 for(a = runq->argv->words;a;a = a->next){
818 v = gvlook(a->word);
819 if(v->fn)
820 codefree(v->fn);
821 v->fn = 0;
822 v->fnchanged = 1;
824 poplist();
827 char*
828 concstatus(char *s, char *t)
830 static char v[NSTATUS+1];
831 int n = strlen(s);
832 strncpy(v, s, NSTATUS);
833 if(n<NSTATUS){
834 v[n]='|';
835 strncpy(v+n+1, t, NSTATUS-n-1);
837 v[NSTATUS]='\0';
838 return v;
841 void
842 Xpipewait(void)
844 char status[NSTATUS+1];
845 if(runq->pid==-1)
846 setstatus(concstatus(runq->status, getstatus()));
847 else{
848 strncpy(status, getstatus(), NSTATUS);
849 status[NSTATUS]='\0';
850 Waitfor(runq->pid, 1);
851 runq->pid=-1;
852 setstatus(concstatus(getstatus(), status));
856 void
857 Xrdcmds(void)
859 struct thread *p = runq;
860 word *prompt;
861 flush(err);
862 nerror = 0;
863 if(flag['s'] && !truestatus())
864 pfmt(err, "status=%v\n", vlook("status")->val);
865 if(runq->iflag){
866 prompt = vlook("prompt")->val;
867 if(prompt)
868 promptstr = prompt->word;
869 else
870 promptstr="% ";
872 Noerror();
873 if(yyparse()){
874 if(!p->iflag || p->eof && !Eintr()){
875 if(p->cmdfile)
876 efree(p->cmdfile);
877 closeio(p->cmdfd);
878 Xreturn(); /* should this be omitted? */
880 else{
881 if(Eintr()){
882 pchr(err, '\n');
883 p->eof = 0;
885 --p->pc; /* go back for next command */
888 else{
889 ntrap = 0; /* avoid double-interrupts during blocked writes */
890 --p->pc; /* re-execute Xrdcmds after codebuf runs */
891 start(codebuf, 1, runq->local);
893 freenodes();
896 void
897 Xerror(char *s)
899 if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
900 pfmt(err, "rc: %s: %r\n", s);
901 else
902 pfmt(err, "rc (%s): %s: %r\n", argv0, s);
903 flush(err);
904 setstatus("error");
905 while(!runq->iflag) Xreturn();
908 void
909 Xerror1(char *s)
911 if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
912 pfmt(err, "rc: %s\n", s);
913 else
914 pfmt(err, "rc (%s): %s\n", argv0, s);
915 flush(err);
916 setstatus("error");
917 while(!runq->iflag) Xreturn();
920 void
921 setstatus(char *s)
923 setvar("status", newword(s, (word *)0));
926 char*
927 getstatus(void)
929 var *status = vlook("status");
930 return status->val?status->val->word:"";
933 int
934 truestatus(void)
936 char *s;
937 for(s = getstatus();*s;s++)
938 if(*s!='|' && *s!='0')
939 return 0;
940 return 1;
943 void
944 Xdelhere(void)
946 Unlink(runq->code[runq->pc++].s);
949 void
950 Xfor(void)
952 if(runq->argv->words==0){
953 poplist();
954 runq->pc = runq->code[runq->pc].i;
956 else{
957 freelist(runq->local->val);
958 runq->local->val = runq->argv->words;
959 runq->local->changed = 1;
960 runq->argv->words = runq->argv->words->next;
961 runq->local->val->next = 0;
962 runq->pc++;
966 void
967 Xglob(void)
969 globlist();