ref: 08bcc4bcecd6514756c4bde5b5ba6942aff9a98d
dir: /sys/src/cmd/rc/exec.c/
#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->line = runq?runq->line:0; p->pc = pc; p->argv = 0; p->redir = p->startredir = runq?runq->redir:nil; p->local = local; p->cmdfile = nil; p->cmdfd = 0; p->eof = 0; p->iflag = 0; p->lineno = runq ? runq->lineno : 1; p->ret = runq; runq = p; } word* Newword(char *wd, word *next) { word *p = new(word); p->word = wd; p->next = next; p->glob = 0; return p; } word* Pushword(char *wd) { word *w; if(runq->argv==0) panic("pushword but no argv!", 0); w = Newword(wd, runq->argv->words); runq->argv->words = w; return w; } word* newword(char *wd, word *next) { return Newword(estrdup(wd), next); } word* pushword(char *wd) { return Pushword(estrdup(wd)); } 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; free(p->word); free(p); } void freelist(word *w) { word *nw; while(w){ nw = w->next; free(w->word); free(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; free(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; } void shuffleredir(void) { redir **rr, *rp; rp = runq->redir; if(rp==0) return; runq->redir = rp->next; rp->next = runq->startredir; for(rr = &runq->redir; *rr != rp->next; rr = &((*rr)->next)) ; *rr = rp; } var* newvar(char *name, var *next) { var *v = new(var); v->name = estrdup(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 */ void main(int argc, char *argv[]) { code bootstrap[17]; char num[12], *rcmain; int i; argc = getflags(argc, argv, "SsrdiIlxepvVc: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(); inttoascii(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); runq->cmdfile = strdup("rc"); runq->lexline = 0; /* prime bootstrap argv */ pushlist(); argv0 = estrdup(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(split){... 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 * Xqw(list) quote list, push result * 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 * Xglobs[string globsize] push globbing string * 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 * Xrdwr(file)[fd] open file for reading and writing * 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 * Xsrcline[line] set current line number * Xsrcfile[file] set current file name */ 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 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("*", 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 Xrdwr(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, ORDWR))<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); free(rp); } void Xreturn(void) { struct thread *p = runq; turfredir(); while(p->argv) poplist(); codefree(p->code); runq = p->ret; free(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 Xglobs(void) { word *w = pushword(runq->code[runq->pc++].s); w->glob = runq->code[runq->pc++].i; } 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; } free(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; } } free(s); if(ok) runq->pc++; else runq->pc = runq->code[runq->pc].i; poplist(); } word* conclist(word *lp, word *rp, word *tail) { word *v, *p, **end; int ln, rn; for(end = &v;;){ ln = strlen(lp->word), rn = strlen(rp->word); p = Newword(emalloc(ln+rn+1), (word *)0); memmove(p->word, lp->word, ln); memmove(p->word+ln, rp->word, rn+1); if(lp->glob || rp->glob) p->glob = Globsize(p->word); *end = p, end = &p->next; if(lp->next == 0 && rp->next == 0) break; if(lp->next) lp = lp->next; if(rp->next) rp = rp->next; } *end = tail; 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; } char* Str(word *a) { char *s = a->word; if(a->glob){ a->glob = 0; deglob(s); } return s; } void Xassign(void) { var *v; if(count(runq->argv->words)!=1){ Xerror1("variable name not singleton!"); return; } v = vlook(Str(runq->argv->words)); poplist(); freewords(v->val); v->val = globlist(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 = Str(runq->argv->words); 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 Xqw(void) { char *s; word *a; a = runq->argv->words; if(a && a->next == 0){ runq->argv->words = 0; poplist(); a->next = runq->argv->words; runq->argv->words = a; return; } s = list2str(a); poplist(); Pushword(s); } word* copynwords(word *a, word *tail, int n) { word *v, **end; v = 0; end = &v; while(n-- > 0){ *end = newword(a->word, 0); end = &(*end)->next; a = a->next; } *end = tail; return v; } word* subwords(word *val, int len, word *sub, word *a) { int n, m; char *s; if(!sub) return a; a = subwords(val, len, sub->next, a); s = Str(sub); m = 0; n = 0; while('0'<=*s && *s<='9') n = n*10+ *s++ -'0'; if(*s == '-'){ if(*++s == 0) m = len - n; else{ while('0'<=*s && *s<='9') m = m*10+ *s++ -'0'; m -= n; } } if(n<1 || n>len || m<0) return a; if(n+m>len) m = len-n; while(--n > 0) val = val->next; return copynwords(val, a, m+1); } void Xsub(void) { word *a, *v; char *s; if(count(runq->argv->next->words)!=1){ Xerror1("variable name not singleton!"); return; } s = Str(runq->argv->next->words); 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 = Str(runq->argv->words); n = 0; for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; if(n==0 || *t){ a = vlook(s)->val; inttoascii(num, count(a)); } else{ a = vlook("*")->val; inttoascii(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; } runq->local = newvar(Str(runq->argv->words), runq->local); poplist(); runq->local->val = globlist(runq->argv->words); runq->local->changed = 1; runq->argv->words = 0; 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; free(v->name); freewords(v->val); free(v); } void freewords(word *w) { word *nw; while(w){ free(w->word); nw = w->next; free(w); w = nw; } } void Xfn(void) { var *v; word *a; int end; end = runq->code[runq->pc].i; for(a = globlist(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(); } char* concstatus(char *s, char *t) { static char v[NSTATUS+1]; int n = strlen(s); strncpy(v, s, NSTATUS); if(n<NSTATUS){ v[n]='|'; strncpy(v+n+1, t, NSTATUS-n-1); } v[NSTATUS]='\0'; return v; } void Xpipewait(void) { char status[NSTATUS+1]; if(runq->pid==-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) free(p->cmdfile); closeio(p->cmdfd); Xreturn(); } 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(); } char* curfile(thread *p) { for(; p != nil; p = p->ret) if(p->cmdfile != nil) return p->cmdfile; return "unknown"; } void Xerror(char *s) { if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) pfmt(err, "rc:%d: %s: %r\n", runq->line, s); else pfmt(err, "%s:%d: %s: %r\n", curfile(runq), runq->line, s); flush(err); setstatus("error"); while(!runq->iflag) Xreturn(); } void Xerror1(char *s) { if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) pfmt(err, "rc:%d: %s\n", runq->line, s); else pfmt(err, "%s:%d: %s\n", curfile(runq), runq->line, s); flush(err); setstatus("error"); while(!runq->iflag) Xreturn(); } 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(runq->argv->words); } void Xsrcline(void) { runq->line = runq->code[runq->pc++].i; } void Xsrcfile(void) { free(runq->cmdfile); runq->cmdfile = strdup(runq->code[runq->pc++].s); }