From f08fdedcee12c06e3ce9ac9bec363915978e8289 Mon Sep 17 00:00:00 2001 From: rsc Date: Sun, 23 Nov 2003 18:04:08 +0000 Subject: Plan 9's rc. not a clear win over byron's, but at least it has the right syntax. --- src/cmd/rc/exec.c | 902 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 902 insertions(+) create mode 100644 src/cmd/rc/exec.c (limited to 'src/cmd/rc/exec.c') diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c new file mode 100644 index 00000000..ebed11d8 --- /dev/null +++ b/src/cmd/rc/exec.c @@ -0,0 +1,902 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Start executing the given code at the given pc with the given redirection + */ +char *argv0="rc"; +void start(code *c, int pc, var *local) +{ + struct thread *p=new(struct thread); + p->code=codecopy(c); + p->pc=pc; + p->argv=0; + p->redir=p->startredir=runq?runq->redir:0; + p->local=local; + p->cmdfile=0; + p->cmdfd=0; + p->eof=0; + p->iflag=0; + p->lineno=1; + p->ret=runq; + runq=p; +} +word *newword(char *wd, word *next) +{ + word *p=new(word); + p->word=strdup(wd); + p->next=next; + return p; +} +void pushword(char *wd) +{ + if(runq->argv==0) panic("pushword but no argv!", 0); + runq->argv->words=newword(wd, runq->argv->words); +} +void popword(void){ + word *p; + if(runq->argv==0) panic("popword but no argv!", 0); + p=runq->argv->words; + if(p==0) panic("popword but no word!", 0); + runq->argv->words=p->next; + efree(p->word); + efree((char *)p); +} +void freelist(word *w) +{ + word *nw; + while(w){ + nw=w->next; + efree(w->word); + efree((char *)w); + w=nw; + } +} +void pushlist(void){ + list *p=new(list); + p->next=runq->argv; + p->words=0; + runq->argv=p; +} +void poplist(void){ + list *p=runq->argv; + if(p==0) panic("poplist but no argv", 0); + freelist(p->words); + runq->argv=p->next; + efree((char *)p); +} +int count(word *w) +{ + int n; + for(n=0;w;n++) w=w->next; + return n; +} +void pushredir(int type, int from, int to){ + redir * rp=new(redir); + rp->type=type; + rp->from=from; + rp->to=to; + rp->next=runq->redir; + runq->redir=rp; +} +var *newvar(char *name, var *next) +{ + var *v=new(var); + v->name=name; + v->val=0; + v->fn=0; + v->changed=0; + v->fnchanged=0; + v->next=next; + return v; +} +/* + * get command line flags, initialize keywords & traps. + * get values from environment. + * set $pid, $cflag, $* + * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*) + * start interpreting code + */ +int +main(int argc, char *argv[]) +{ + code bootstrap[32]; + char num[12], *rcmain; + int i; + + argc=getflags(argc, argv, "srdiIlxepvVc:1m:1[command]", 1); + if(argc==-1) usage("[file [arg ...]]"); + if(argv[0][0]=='-') flag['l']=flagset; + if(flag['I']) flag['i'] = 0; + else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; + rcmain=flag['m']?flag['m'][0]:Rcmain(); + err=openfd(2); + kinit(); + Trapinit(); + Vinit(); + itoa(num, mypid=getpid()); + setvar("pid", newword(num, (word *)0)); + setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) + :(word *)0); + setvar("rcname", newword(argv[0], (word *)0)); + i=0; + bootstrap[i++].i=1; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xassign; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xdol; + bootstrap[i++].f=Xword; + bootstrap[i++].s=rcmain; + bootstrap[i++].f=Xword; + bootstrap[i++].s="."; + bootstrap[i++].f=Xsimple; + bootstrap[i++].f=Xexit; + bootstrap[i].i=0; + start(bootstrap, 1, (var *)0); + /* prime bootstrap argv */ + pushlist(); + argv0 = strdup(argv[0]); + for(i=argc-1;i!=0;--i) pushword(argv[i]); + for(;;){ + if(flag['r']) pfnc(err, runq); + runq->pc++; + (*runq->code[runq->pc-1].f)(); + if(ntrap) dotrap(); + } +} +/* + * Opcode routines + * Arguments on stack (...) + * Arguments in line [...] + * Code in line with jump around {...} + * + * Xappend(file)[fd] open file to append + * Xassign(name, val) assign val to name + * Xasync{... Xexit} make thread for {}, no wait + * Xbackq{... Xreturn} make thread for {}, push stdout + * Xbang complement condition + * Xcase(pat, value){...} exec code on match, leave (value) on + * stack + * Xclose[i] close file descriptor + * Xconc(left, right) concatenate, push results + * Xcount(name) push var count + * Xdelfn(name) delete function definition + * Xdeltraps(names) delete named traps + * Xdol(name) get variable value + * Xqdol(name) concatenate variable components + * Xdup[i j] dup file descriptor + * Xexit rc exits with status + * Xfalse{...} execute {} if false + * Xfn(name){... Xreturn} define function + * Xfor(var, list){... Xreturn} for loop + * Xjump[addr] goto + * Xlocal(name, val) create local variable, assign value + * Xmark mark stack + * Xmatch(pat, str) match pattern, set status + * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, + * wait for both + * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, + * depending on type), push /dev/fd/?? + * Xpopm(value) pop value from stack + * Xread(file)[fd] open file to read + * Xsettraps(names){... Xreturn} define trap functions + * Xshowtraps print trap list + * Xsimple(args) run command and wait + * Xreturn kill thread + * Xsubshell{... Xexit} execute {} in a subshell and wait + * Xtrue{...} execute {} if true + * Xunlocal delete local variable + * Xword[string] push string + * Xwrite(file)[fd] open file to write + */ +void Xappend(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1(">> requires singleton"); return; + case 0: Xerror1(">> requires file"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 1))<0 && (f=Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + Seek(f, 0L, 2); + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +void Xasync(void){ + int null=open("/dev/null", 0); + int pid; + char npid[10]; + if(null<0){ + Xerror("Can't open /dev/null\n"); + return; + } + switch(pid=rfork(RFFDG|RFPROC|RFNOTEG)){ + case -1: + close(null); + Xerror("try again"); + break; + case 0: + pushredir(ROPEN, null, 0); + start(runq->code, runq->pc+1, runq->local); + runq->ret=0; + break; + default: + close(null); + runq->pc=runq->code[runq->pc].i; + itoa(npid, pid); + setvar("apid", newword(npid, (word *)0)); + break; + } +} +void Xsettrue(void){ + setstatus(""); +} +void Xbang(void){ + setstatus(truestatus()?"false":""); +} +void Xclose(void){ + pushredir(RCLOSE, runq->code[runq->pc].i, 0); + runq->pc++; +} +void Xdup(void){ + pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); + runq->pc+=2; +} +void Xeflag(void){ + if(eflagok && !truestatus()) Xexit(); +} +void Xexit(void){ + struct var *trapreq; + struct word *starval; + static int beenhere=0; + if(getpid()==mypid && !beenhere){ + trapreq=vlook("sigexit"); + if(trapreq->fn){ + beenhere=1; + --runq->pc; + starval=vlook("*")->val; + start(trapreq->fn, trapreq->pc, (struct var *)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=copywords(starval, (struct word *)0); + runq->local->changed=1; + runq->redir=runq->startredir=0; + return; + } + } + Exit(getstatus()); +} +void Xfalse(void){ + if(truestatus()) runq->pc=runq->code[runq->pc].i; + else runq->pc++; +} +int ifnot; /* dynamic if not flag */ +void Xifnot(void){ + if(ifnot) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; +} +void Xjump(void){ + runq->pc=runq->code[runq->pc].i; +} +void Xmark(void){ + pushlist(); +} +void Xpopm(void){ + poplist(); +} +void Xread(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1("< requires singleton\n"); return; + case 0: Xerror1("< requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 0))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +void turfredir(void){ + while(runq->redir!=runq->startredir) + Xpopredir(); +} +void Xpopredir(void){ + struct redir *rp=runq->redir; + if(rp==0) panic("turfredir null!", 0); + runq->redir=rp->next; + if(rp->type==ROPEN) close(rp->from); + efree((char *)rp); +} +void Xreturn(void){ + struct thread *p=runq; + turfredir(); + while(p->argv) poplist(); + codefree(p->code); + runq=p->ret; + efree((char *)p); + if(runq==0) Exit(getstatus()); +} +void Xtrue(void){ + if(truestatus()) runq->pc++; + else runq->pc=runq->code[runq->pc].i; +} +void Xif(void){ + ifnot=1; + if(truestatus()) runq->pc++; + else runq->pc=runq->code[runq->pc].i; +} +void Xwastrue(void){ + ifnot=0; +} +void Xword(void){ + pushword(runq->code[runq->pc++].s); +} +void Xwrite(void){ + char *file; + int f; + switch(count(runq->argv->words)){ + default: Xerror1("> requires singleton\n"); return; + case 0: Xerror1("> requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=Creat(file))<0){ + pfmt(err, "%s: ", file); + Xerror("can't open"); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} +char *list2str(word *words){ + char *value, *s, *t; + int len=0; + word *ap; + for(ap=words;ap;ap=ap->next) + len+=1+strlen(ap->word); + value=emalloc(len+1); + s=value; + for(ap=words;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=' '; + } + if(s==value) *s='\0'; + else s[-1]='\0'; + return value; +} +void Xmatch(void){ + word *p; + char *subject; + subject=list2str(runq->argv->words); + setstatus("no match"); + for(p=runq->argv->next->words;p;p=p->next) + if(match(subject, p->word, '\0')){ + setstatus(""); + break; + } + efree(subject); + poplist(); + poplist(); +} +void Xcase(void){ + word *p; + char *s; + int ok=0; + s=list2str(runq->argv->next->words); + for(p=runq->argv->words;p;p=p->next){ + if(match(s, p->word, '\0')){ + ok=1; + break; + } + } + efree(s); + if(ok) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; + poplist(); +} +word *conclist(word *lp, word *rp, word *tail) +{ + char *buf; + word *v; + if(lp->next || rp->next) + tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, + tail); + buf=emalloc(strlen(lp->word)+strlen(rp->word)+1); + strcpy(buf, lp->word); + strcat(buf, rp->word); + v=newword(buf, tail); + efree(buf); + return v; +} +void Xconc(void){ + word *lp=runq->argv->words; + word *rp=runq->argv->next->words; + word *vp=runq->argv->next->next->words; + int lc=count(lp), rc=count(rp); + if(lc!=0 || rc!=0){ + if(lc==0 || rc==0){ + Xerror1("null list in concatenation"); + return; + } + if(lc!=1 && rc!=1 && lc!=rc){ + Xerror1("mismatched list lengths in concatenation"); + return; + } + vp=conclist(lp, rp, vp); + } + poplist(); + poplist(); + runq->argv->words=vp; +} +void Xassign(void){ + var *v; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + deglob(runq->argv->words->word); + v=vlook(runq->argv->words->word); + poplist(); + globlist(); + freewords(v->val); + v->val=runq->argv->words; + v->changed=1; + runq->argv->words=0; + poplist(); +} +/* + * copy arglist a, adding the copy to the front of tail + */ +word *copywords(word *a, word *tail) +{ + word *v=0, **end; + for(end=&v;a;a=a->next,end=&(*end)->next) + *end=newword(a->word, 0); + *end=tail; + return v; +} +void Xdol(void){ + word *a, *star; + char *s, *t; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + a=runq->argv->next->words; + if(n==0 || *t) + a=copywords(vlook(s)->val, a); + else{ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + a=newword(star->word, a); + } + } + poplist(); + runq->argv->words=a; +} +void Xqdol(void){ + word *a, *p; + char *s; + int n; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + a=vlook(s)->val; + poplist(); + n=count(a); + if(n==0){ + pushword(""); + return; + } + for(p=a;p;p=p->next) n+=strlen(p->word); + s=emalloc(n); + if(a){ + strcpy(s, a->word); + for(p=a->next;p;p=p->next){ + strcat(s, " "); + strcat(s, p->word); + } + } + else + s[0]='\0'; + pushword(s); + efree(s); +} +word *subwords(word *val, int len, word *sub, word *a) +{ + int n; + char *s; + if(!sub) return a; + a=subwords(val, len, sub->next, a); + s=sub->word; + deglob(s); + n=0; + while('0'<=*s && *s<='9') n=n*10+ *s++ -'0'; + if(n<1 || lennext; + return newword(val->word, a); +} +void Xsub(void){ + word *a, *v; + char *s; + if(count(runq->argv->next->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->next->words->word; + deglob(s); + a=runq->argv->next->next->words; + v=vlook(s)->val; + a=subwords(v, count(v), runq->argv->words, a); + poplist(); + poplist(); + runq->argv->words=a; +} +void Xcount(void){ + word *a; + char *s, *t; + int n; + char num[12]; + if(count(runq->argv->words)!=1){ + Xerror1("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + if(n==0 || *t){ + a=vlook(s)->val; + itoa(num, count(a)); + } + else{ + a=vlook("*")->val; + itoa(num, a && 1<=n && n<=count(a)?1:0); + } + poplist(); + pushword(num); +} +void Xlocal(void){ + if(count(runq->argv->words)!=1){ + Xerror1("variable name must be singleton\n"); + return; + } + deglob(runq->argv->words->word); + runq->local=newvar(strdup(runq->argv->words->word), runq->local); + runq->local->val=copywords(runq->argv->next->words, (word *)0); + runq->local->changed=1; + poplist(); + poplist(); +} +void Xunlocal(void){ + var *v=runq->local, *hid; + if(v==0) panic("Xunlocal: no locals!", 0); + runq->local=v->next; + hid=vlook(v->name); + hid->changed=1; + efree(v->name); + freewords(v->val); + efree((char *)v); +} +void freewords(word *w) +{ + word *nw; + while(w){ + efree(w->word); + nw=w->next; + efree((char *)w); + w=nw; + } +} +void Xfn(void){ + var *v; + word *a; + int end; + end=runq->code[runq->pc].i; + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) codefree(v->fn); + v->fn=codecopy(runq->code); + v->pc=runq->pc+2; + v->fnchanged=1; + } + runq->pc=end; + poplist(); +} +void Xdelfn(void){ + var *v; + word *a; + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) codefree(v->fn); + v->fn=0; + v->fnchanged=1; + } + poplist(); +} +void Xpipe(void){ + struct thread *p=runq; + int pc=p->pc, forkid; + int lfd=p->code[pc++].i; + int rfd=p->code[pc++].i; + int pfd[2]; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + switch(forkid=fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + runq->ret=0; + close(pfd[PRD]); + pushredir(ROPEN, pfd[PWR], lfd); + break; + default: + start(p->code, p->code[pc].i, runq->local); + close(pfd[PWR]); + pushredir(ROPEN, pfd[PRD], rfd); + p->pc=p->code[pc+1].i; + p->pid=forkid; + break; + } +} +char *concstatus(char *s, char *t) +{ + static char v[NSTATUS+1]; + int n=strlen(s); + strncpy(v, s, NSTATUS); + if(npid==-1) + setstatus(concstatus(runq->status, getstatus())); + else{ + strncpy(status, getstatus(), NSTATUS); + status[NSTATUS]='\0'; + Waitfor(runq->pid, 1); + runq->pid=-1; + setstatus(concstatus(getstatus(), status)); + } +} +void Xrdcmds(void){ + struct thread *p=runq; + word *prompt; + flush(err); + nerror=0; + if(flag['s'] && !truestatus()) + pfmt(err, "status=%v\n", vlook("status")->val); + if(runq->iflag){ + prompt=vlook("prompt")->val; + if(prompt) + promptstr=prompt->word; + else + promptstr="% "; + } + Noerror(); + if(yyparse()){ + if(!p->iflag || p->eof && !Eintr()){ + if(p->cmdfile) efree(p->cmdfile); + closeio(p->cmdfd); + Xreturn(); /* should this be omitted? */ + } + else{ + if(Eintr()){ + pchr(err, '\n'); + p->eof=0; + } + --p->pc; /* go back for next command */ + } + } + else{ + ntrap = 0; /* avoid double-interrupts during blocked writes */ + --p->pc; /* re-execute Xrdcmds after codebuf runs */ + start(codebuf, 1, runq->local); + } + freenodes(); +} +void Xerror(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s: %r\n", s); + else + pfmt(err, "rc (%s): %s: %r\n", argv0, s); + flush(err); + while(!runq->iflag) Xreturn(); +} +void Xerror1(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s\n", s); + else + pfmt(err, "rc (%s): %s\n", argv0, s); + flush(err); + while(!runq->iflag) Xreturn(); +} +void Xbackq(void){ + char wd[8193]; + int c; + char *s, *ewd=&wd[8192], *stop; + struct io *f; + var *ifs=vlook("ifs"); + word *v, *nextv; + int pfd[2]; + int pid; + stop=ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + switch(pid=fork()){ + case -1: Xerror("try again"); + close(pfd[PRD]); + close(pfd[PWR]); + return; + case 0: + close(pfd[PRD]); + start(runq->code, runq->pc+1, runq->local); + pushredir(ROPEN, pfd[PWR], 1); + return; + default: + close(pfd[PWR]); + f=openfd(pfd[PRD]); + s=wd; + v=0; + while((c=rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + s=wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + } + closeio(f); + Waitfor(pid, 0); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv=v->next; + v->next=runq->argv->words; + runq->argv->words=v; + v=nextv; + } + runq->pc=runq->code[runq->pc].i; + return; + } +} +/* + * Who should wait for the exit from the fork? + */ +void Xpipefd(void){ + struct thread *p=runq; + int pc=p->pc; + char name[40]; + int pfd[2]; + int sidefd, mainfd; + if(pipe(pfd)<0){ + Xerror("can't get pipe"); + return; + } + if(p->code[pc].i==READ){ + sidefd=pfd[PWR]; + mainfd=pfd[PRD]; + } + else{ + sidefd=pfd[PRD]; + mainfd=pfd[PWR]; + } + switch(fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(p->code, pc+2, runq->local); + close(mainfd); + pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); + runq->ret=0; + break; + default: + close(sidefd); + pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ + strcpy(name, Fdprefix); + itoa(name+strlen(name), mainfd); + pushword(name); + p->pc=p->code[pc+1].i; + break; + } +} +void Xsubshell(void){ + int pid; + switch(pid=fork()){ + case -1: + Xerror("try again"); + break; + case 0: + start(runq->code, runq->pc+1, runq->local); + runq->ret=0; + break; + default: + Waitfor(pid, 1); + runq->pc=runq->code[runq->pc].i; + break; + } +} +void setstatus(char *s) +{ + setvar("status", newword(s, (word *)0)); +} +char *getstatus(void){ + var *status=vlook("status"); + return status->val?status->val->word:""; +} +int truestatus(void){ + char *s; + for(s=getstatus();*s;s++) + if(*s!='|' && *s!='0') return 0; + return 1; +} +void Xdelhere(void){ + Unlink(runq->code[runq->pc++].s); +} +void Xfor(void){ + if(runq->argv->words==0){ + poplist(); + runq->pc=runq->code[runq->pc].i; + } + else{ + freelist(runq->local->val); + runq->local->val=runq->argv->words; + runq->local->changed=1; + runq->argv->words=runq->argv->words->next; + runq->local->val->next=0; + runq->pc++; + } +} +void Xglob(void){ + globlist(); +} -- cgit v1.2.3