| #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; |
| v->changefn = 0; |
| 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; |
| |
| /* needed for rcmain later */ |
| putenv("PLAN9", unsharp("#9")); |
| |
| 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()); |
| pathinit(); |
| 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(); |
| } |
| return 0; /* not reached; silence OS X Lion gcc */ |
| } |
| /* |
| * 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 |
| * 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 |
| */ |
| |
| 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(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 |
| 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); |
| 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; |
| if(v->changefn) |
| v->changefn(v); |
| 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* |
| 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 = sub->word; |
| deglob(s); |
| 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 = 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; |
| 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; |
| } |
| 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(); |
| } |
| |
| 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) |
| 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); |
| setstatus("error"); |
| 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); |
| 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(); |
| } |