commit f08fdedcee12c06e3ce9ac9bec363915978e8289 from: rsc date: Sun Nov 23 18:04:08 2003 UTC Plan 9's rc. not a clear win over byron's, but at least it has the right syntax. commit - 5993a8f2756bc455101a8c9ce95347d5050e7883 commit + f08fdedcee12c06e3ce9ac9bec363915978e8289 blob - /dev/null blob + 748b9f8246ac5df0ebda29915ad3bfdabb00c667 (mode 644) --- /dev/null +++ src/cmd/rc/code.c @@ -0,0 +1,430 @@ +#include "rc.h" +#include "io.h" +#include "exec.h" +#include "fns.h" +#include "getflags.h" +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +int codep, ncode; +#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f=(x), codep++) +#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i=(x), codep++) +#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s=(x), codep++) +void stuffdot(int); +char *fnstr(tree*); +void outcode(tree*, int); +void codeswitch(tree*, int); +int iscase(tree*); +code *codecopy(code*); +void codefree(code*); +int morecode(void){ + ncode+=100; + codebuf=(code *)realloc((char *)codebuf, ncode*sizeof codebuf[0]); + if(codebuf==0) panic("Can't realloc %d bytes in morecode!", + ncode*sizeof codebuf[0]); + return 0; +} +void stuffdot(int a){ + if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a); + codebuf[a].i=codep; +} +int compile(tree *t) +{ + ncode=100; + codebuf=(code *)emalloc(ncode*sizeof codebuf[0]); + codep=0; + emiti(0); /* reference count */ + outcode(t, flag['e']?1:0); + if(nerror){ + efree((char *)codebuf); + return 0; + } + readhere(); + emitf(Xreturn); + emitf(0); + return 1; +} +void cleanhere(char *f) +{ + emitf(Xdelhere); + emits(strdup(f)); +} +char *fnstr(tree *t) +{ + io *f=openstr(); + char *v; + extern char nl; + char svnl=nl; + nl=';'; + pfmt(f, "%t", t); + nl=svnl; + v=f->strp; + f->strp=0; + closeio(f); + return v; +} +void outcode(tree *t, int eflag) +{ + int p, q; + tree *tt; + if(t==0) return; + if(t->type!=NOT && t->type!=';') runq->iflast=0; + switch(t->type){ + default: + pfmt(err, "bad type %d in outcode\n", t->type); + break; + case '$': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xqdol); + break; + case SUB: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmark); + outcode(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case ';': + outcode(c0, eflag); + outcode(c1, eflag); + break; + case '^': + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + p=emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + break; + case ANDAND: + outcode(c0, 0); + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case BANG: + outcode(c0, eflag); + emitf(Xbang); + break; + case PCMD: + case BRACE: + outcode(c0, eflag); + break; + case COUNT: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xcount); + break; + case FN: + emitf(Xmark); + outcode(c0, eflag); + if(c1){ + emitf(Xfn); + p=emiti(0); + emits(fnstr(c1)); + outcode(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case IF: + outcode(c0, 0); + emitf(Xif); + p=emiti(0); + outcode(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case NOT: + if(!runq->iflast) yyerror("`if not' does not follow `if(...)'"); + emitf(Xifnot); + p=emiti(0); + outcode(c0, eflag); + stuffdot(p); + break; + case OROR: + outcode(c0, 0); + emitf(Xfalse); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case PAREN: + outcode(c0, eflag); + break; + case SIMPLE: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xsimple); + if(eflag) emitf(Xeflag); + break; + case SUBSHELL: + emitf(Xsubshell); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + if(eflag) emitf(Xeflag); + break; + case SWITCH: + codeswitch(t, eflag); + break; + case TWIDDLE: + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmatch); + if(eflag) emitf(Xeflag); + break; + case WHILE: + q=codep; + outcode(c0, 0); + if(q==codep) emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case FOR: + emitf(Xmark); + if(c1){ + outcode(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + p=emitf(Xfor); + q=emiti(0); + outcode(c2, eflag); + emitf(Xjump); + emiti(p); + stuffdot(q); + emitf(Xunlocal); + break; + case WORD: + emitf(Xword); + emits(strdup(t->str)); + break; + case DUP: + if(t->rtype==DUPFD){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + outcode(c1, eflag); + emitf(Xpopredir); + break; + case PIPEFD: + emitf(Xpipefd); + emiti(t->rtype); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; + case REDIR: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case APPEND: + emitf(Xappend); + break; + case WRITE: + emitf(Xwrite); + break; + case READ: + case HERE: + emitf(Xread); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt=t; + for(;t && t->type=='=';t=c2); + if(t){ + for(t=tt;t->type=='=';t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + } + t=tt; + outcode(c2, eflag); + for(;t->type=='=';t=c2) emitf(Xunlocal); + } + else{ + for(t=tt;t;t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); + } + } + t=tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + p=emiti(0); + q=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + outcode(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=NOT && t->type!=';') + runq->iflast=t->type==IF; + else if(c0) runq->iflast=c0->type==IF; +} +/* + * switch code looks like this: + * Xmark + * (get switch value) + * Xjump 1f + * out: Xjump leave + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: + * leave: + * Xpopm + */ +void codeswitch(tree *t, int eflag) +{ + int leave; /* patch jump address to leave switch */ + int out; /* jump here to leave switch */ + int nextcase; /* patch jump address to next case */ + tree *tt; + if(c1->child[0]==nil + || c1->child[0]->type!=';' + || !iscase(c1->child[0]->child[0])){ + yyerror("case missing in switch"); + return; + } + emitf(Xmark); + outcode(c0, eflag); + emitf(Xjump); + nextcase=emiti(0); + out=emitf(Xjump); + leave=emiti(0); + stuffdot(nextcase); + t=c1->child[0]; + while(t->type==';'){ + tt=c1; + emitf(Xmark); + for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag); + emitf(Xcase); + nextcase=emiti(0); + t=tt; + for(;;){ + if(t->type==';'){ + if(iscase(c0)) break; + outcode(c0, eflag); + t=c1; + } + else{ + if(!iscase(t)) outcode(t, eflag); + break; + } + } + emitf(Xjump); + emiti(out); + stuffdot(nextcase); + } + stuffdot(leave); + emitf(Xpopm); +} +int iscase(tree *t) +{ + if(t->type!=SIMPLE) return 0; + do t=c0; while(t->type==ARGLIST); + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} +code *codecopy(code *cp) +{ + cp[0].i++; + return cp; +} +void codefree(code *cp) +{ + code *p; + if(--cp[0].i!=0) return; + for(p=cp+1;p->f;p++){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) p++; + else if(p->f==Xdup || p->f==Xpipefd) p+=2; + else if(p->f==Xpipe) p+=4; + else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); + else if(p->f==Xfn){ + efree(p[2].s); + p+=2; + } + } + efree((char *)cp); +} blob - /dev/null blob + ebed11d82e4b2728dd7e1dc26e43bcc75385b069 (mode 644) --- /dev/null +++ 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(); +} blob - /dev/null blob + fbfe2dbb137ada9c6e63ed6f94e444ba0793f303 (mode 644) --- /dev/null +++ src/cmd/rc/exec.h @@ -0,0 +1,71 @@ +/* + * Definitions used in the interpreter + */ +extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void); +extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void); +extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void); +extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void); +extern void Xrdfn(void), Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void); +extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void); +extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void); +extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void); +extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void); +extern void Xerror(char*); +extern void Xerror1(char*); +/* + * word lists are in correct order, + * i.e. word0->word1->word2->word3->0 + */ +struct word{ + char *word; + word *next; +}; +struct list{ + word *words; + list *next; +}; +word *newword(char *, word *), *copywords(word *, word *); +struct redir{ + char type; /* what to do */ + short from, to; /* what to do it to */ + struct redir *next; /* what else to do (reverse order) */ +}; +#define NSTATUS ERRMAX /* length of status (from plan 9) */ +/* + * redir types + */ +#define ROPEN 1 /* dup2(from, to); close(from); */ +#define RDUP 2 /* dup2(from, to); */ +#define RCLOSE 3 /* close(from); */ +struct thread{ + union code *code; /* code for this thread */ + int pc; /* code[pc] is the next instruction */ + struct list *argv; /* argument stack */ + struct redir *redir; /* redirection stack */ + struct redir *startredir; /* redir inheritance point */ + struct var *local; /* list of local variables */ + char *cmdfile; /* file name in Xrdcmd */ + struct io *cmdfd; /* file descriptor for Xrdcmd */ + int iflast; /* static `if not' checking */ + int eof; /* is cmdfd at eof? */ + int iflag; /* interactive? */ + int lineno; /* linenumber */ + int pid; /* process for Xpipewait to wait for */ + char status[NSTATUS]; /* status for Xpipewait */ + tree *treenodes; /* tree nodes created by this process */ + thread *ret; /* who continues when this finishes */ +}; +thread *runq; +code *codecopy(code*); +code *codebuf; /* compiler output */ +int ntrap; /* number of outstanding traps */ +int trap[NSIG]; /* number of outstanding traps per type */ +extern struct builtin{ + char *name; + void (*fnc)(void); +}Builtin[]; +int eflagok; /* kludge flag so that -e doesn't exit in startup */ +void execcd(void), execwhatis(void), execeval(void), execexec(void); +void execexit(void), execshift(void); +void execwait(void), execumask(void), execdot(void), execflag(void); +void execfunc(var*), execcmds(io *); blob - /dev/null blob + e6b91e34b35b81c4b9642dcc51c869d52918eeed (mode 644) --- /dev/null +++ src/cmd/rc/fmtquote.c @@ -0,0 +1,162 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ +#include +#include +#include "fmt.h" +#include "fmtdef.h" + +extern int (*doquote)(int); + +/* + * How many bytes of output UTF will be produced by quoting (if necessary) this string? + * How many runes? How much of the input will be consumed? + * The parameter q is filled in by _quotesetup. + * The string may be UTF or Runes (s or r). + * Return count does not include NUL. + * Terminate the scan at the first of: + * NUL in input + * count exceeded in input + * count exceeded on output + * *ninp is set to number of input bytes accepted. + * nin may be <0 initially, to avoid checking input by count. + */ +void +__quotesetup(char *s, int nin, int nout, Quoteinfo *q, int sharp) +{ + int c; + + q->quoted = 0; + q->nbytesout = 0; + q->nrunesout = 0; + q->nbytesin = 0; + q->nrunesin = 0; + if(sharp || nin==0 || *s=='\0'){ + if(nout < 2) + return; + q->quoted = 1; + q->nbytesout = 2; + q->nrunesout = 2; + } + for(; nin!=0; nin-=1){ + c = *s; + + if(c == '\0') + break; + if(q->nrunesout+1 > nout) + break; + + if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){ + if(!q->quoted){ + if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ + break; + q->nrunesout += 2; /* include quotes */ + q->nbytesout += 2; /* include quotes */ + q->quoted = 1; + } + if(c == '\'') { + q->nbytesout++; + q->nrunesout++; /* quotes reproduce as two characters */ + } + } + + /* advance input */ + s++; + q->nbytesin++; + q->nrunesin++; + + /* advance output */ + q->nbytesout++; + q->nrunesout++; + } +} + +static int +qstrfmt(char *sin, Quoteinfo *q, Fmt *f) +{ + int r; + char *t, *s, *m, *me; + ulong fl; + int nc, w; + + m = sin; + me = m + q->nbytesin; + + w = f->width; + fl = f->flags; + if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + t = f->to; + s = f->stop; + FMTCHAR(f, t, s, '\''); + for(nc = q->nrunesin; nc > 0; nc--){ + r = *(uchar*)m++; + FMTCHAR(f, t, s, r); + if(r == '\'') + FMTCHAR(f, t, s, r); + } + + FMTCHAR(f, t, s, '\''); + f->nfmt += t - (char *)f->to; + f->to = t; + if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) + return -1; + return 0; +} + +int +__quotestrfmt(int runesin, Fmt *f) +{ + int outlen; + char *s; + Quoteinfo q; + + f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */ + s = va_arg(f->args, char *); + if(!s) + return __fmtcpy(f, "", 5, 5); + + if(f->flush) + outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ + else + outlen = (char*)f->stop - (char*)f->to; + + __quotesetup(s, -1, outlen, &q, f->flags&FmtSharp); + + if(!q.quoted) + return __fmtcpy(f, s, q.nrunesin, q.nbytesin); + return qstrfmt(s, &q, f); +} + +int +quotestrfmt(Fmt *f) +{ + return __quotestrfmt(0, f); +} + +void +quotefmtinstall(void) +{ + fmtinstall('q', quotestrfmt); +} + +int +__needsquotes(char *s, int *quotelenp) +{ + Quoteinfo q; + + __quotesetup(s, -1, 0x7FFFFFFF, &q, 0); + *quotelenp = q.nbytesout; + + return q.quoted; +} blob - /dev/null blob + 4c8be104c4041c9eab5037faf01680e04a4a4454 (mode 644) --- /dev/null +++ src/cmd/rc/fns.h @@ -0,0 +1,58 @@ +void Abort(void); +void Closedir(int); +int Creat(char*); +int Dup(int, int); +int Dup1(int); +int Eintr(void); +int Executable(char*); +void Execute(word*, word*); +void Exit(char*); +int Globsize(char*); +int Isatty(int); +void Memcpy(char*, char*, long); +void Noerror(void); +int Opendir(char*); +long Read(int, char*, long); +int Readdir(int, char*); +long Seek(int, long, long); +void Trapinit(void); +void Unlink(char*); +void Updenv(void); +void Vinit(void); +int Waitfor(int, int); +long Write(int, char*, long); +int advance(void); +int back(int); +void cleanhere(char*); +void codefree(code*); +int compile(tree*); +char * list2str(word*); +int count(word*); +void deglob(char*); +void dotrap(void); +void freenodes(void); +void freewords(word*); +void globlist(void); +int idchr(int); +void itoa(char*, long); +void kinit(void); +int match(char*, char*, int); +int matchfn(char*, char*); +void panic(char*, int); +void poplist(void); +void popword(void); +void pprompt(void); +void pushlist(void); +void pushredir(int, int, int); +void pushword(char*); +void readhere(void); +void setstatus(char*); +void setvar(char*, word*); +void skipnl(void); +void start(code*, int, var*); +int truestatus(void); +void usage(char*); +int wordchr(int); +void yyerror(char*); +int yylex(void); +int yyparse(void); blob - /dev/null blob + c452067fd511aead421cdf50ea254dff05bad8ae (mode 644) --- /dev/null +++ src/cmd/rc/getflags.c @@ -0,0 +1,217 @@ +/*% cyntax -DTEST % && cc -DTEST -go # % + */ +#include "rc.h" +#include "getflags.h" +#include "fns.h" +char *flagset[]={""}; +char **flag[NFLAG]; +char cmdline[NCMDLINE+1]; +char *cmdname; +static char *flagarg=""; +static void reverse(char**, char**); +static int scanflag(int, char*); +static void errn(char*, int); +static void errs(char*); +static void errc(int); +static int reason; +#define RESET 1 +#define FEWARGS 2 +#define FLAGSYN 3 +#define BADFLAG 4 +static int badflag; +int getflags(int argc, char *argv[], char *flags, int stop) +{ + char *s, *t; + int i, j, c, count; + flagarg=flags; + if(cmdname==0) cmdname=argv[0]; + s=cmdline; + for(i=0;i!=argc;i++){ + for(t=argv[i];*t;t++) + if(s!=&cmdline[NCMDLINE]) + *s++=*t; + if(i!=argc-1 && s!=&cmdline[NCMDLINE]) + *s++=' '; + } + *s='\0'; + i=1; + while(i!=argc){ + if(argv[i][0]!='-' || argv[i][1]=='\0'){ + if(stop) return argc; + i++; + continue; + } + s=argv[i]+1; + while(*s){ + c=*s++; + count=scanflag(c, flags); + if(count==-1) return -1; + if(flag[c]){ reason=RESET; badflag=c; return -1; } + if(count==0){ + flag[c]=flagset; + if(*s=='\0'){ + for(j=i+1;j<=argc;j++) + argv[j-1]=argv[j]; + --argc; + } + } + else{ + if(*s=='\0'){ + for(j=i+1;j<=argc;j++) + argv[j-1]=argv[j]; + --argc; + s=argv[i]; + } + if(argc-inext) n++; + list=(char **)emalloc(n*sizeof(char *)); + for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word; + qsort((char *)list, n, sizeof(char *), globcmp); + for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n]; + efree((char *)list); +} +/* + * Push names prefixed by globname and suffixed by a match of p onto the astack. + * namep points to the end of the prefix in globname. + */ +void globdir(char *p, char *namep) +{ + char *t, *newp; + int f; + /* scan the pattern looking for a component with a metacharacter in it */ + if(*p=='\0'){ + globv=newword(globname, globv); + return; + } + t=namep; + newp=p; + while(*newp){ + if(*newp==GLOB) + break; + *t=*newp++; + if(*t++=='/'){ + namep=t; + p=newp; + } + } + /* If we ran out of pattern, append the name if accessible */ + if(*newp=='\0'){ + *t='\0'; + if(access(globname, 0)==0) + globv=newword(globname, globv); + return; + } + /* read the directory and recur for any entry that matches */ + *namep='\0'; + if((f=Opendir(globname[0]?globname:"."))<0) return; + while(*newp!='/' && *newp!='\0') newp++; + while(Readdir(f, namep)){ + if(matchfn(namep, p)){ + for(t=namep;*t;t++); + globdir(newp, t); + } + } + Closedir(f); +} +/* + * Push all file names matched by p on the current thread's stack. + * If there are no matches, the list consists of p. + */ +void glob(char *p) +{ + word *svglobv=globv; + int globlen=Globsize(p); + if(!globlen){ + deglob(p); + globv=newword(p, globv); + return; + } + globname=emalloc(globlen); + globname[0]='\0'; + globdir(p, globname); + efree(globname); + if(svglobv==globv){ + deglob(p); + globv=newword(p, globv); + } + else + globsort(globv, svglobv); +} +/* + * Do p and q point at equal utf codes + */ +int equtf(char *p, char *q){ + if(*p!=*q) return 0; + if(twobyte(*p)) return p[1]==q[1]; + if(threebyte(*p)){ + if(p[1]!=q[1]) return 0; + if(p[1]=='\0') return 1; /* broken code at end of string! */ + return p[2]==q[2]; + } + return 1; +} +/* + * Return a pointer to the next utf code in the string, + * not jumping past nuls in broken utf codes! + */ +char *nextutf(char *p){ + if(twobyte(*p)) return p[1]=='\0'?p+1:p+2; + if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; + return p+1; +} +/* + * Convert the utf code at *p to a unicode value + */ +int unicode(char *p){ + int u=*p&0xff; + if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f); + if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); + return u; +} +/* + * Does the string s match the pattern p + * . and .. are only matched by patterns starting with . + * * matches any sequence of characters + * ? matches any single character + * [...] matches the enclosed list of characters + */ +int matchfn(char *s, char *p) +{ + if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') + return 0; + return match(s, p, '/'); +} +int match(char *s, char *p, int stop) +{ + int compl, hit, lo, hi, t, c; + for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)){ + if(*p!=GLOB){ + if(!equtf(p, s)) return 0; + } + else switch(*++p){ + case GLOB: + if(*s!=GLOB) return 0; + break; + case '*': + for(;;){ + if(match(s, nextutf(p), stop)) return 1; + if(!*s) break; + s=nextutf(s); + } + return 0; + case '?': + if(*s=='\0') return 0; + break; + case '[': + if(*s=='\0') return 0; + c=unicode(s); + p++; + compl=*p=='~'; + if(compl) p++; + hit=0; + while(*p!=']'){ + if(*p=='\0') return 0; /* syntax error */ + lo=unicode(p); + p=nextutf(p); + if(*p!='-') hi=lo; + else{ + p++; + if(*p=='\0') return 0; /* syntax error */ + hi=unicode(p); + p=nextutf(p); + if(hinext); + glob(gl->word); + } +} +void globlist(void){ + word *a; + globv=0; + globlist1(runq->argv->words); + poplist(); + pushlist(); + if(globv){ + for(a=globv;a->next;a=a->next); + a->next=runq->argv->words; + runq->argv->words=globv; + } +} blob - /dev/null blob + e81046d619af2a1a121e2d43c44a659860b46174 (mode 644) --- /dev/null +++ src/cmd/rc/havefork.c @@ -0,0 +1,212 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 1; + +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; + inttoascii(npid, pid); + setvar("apid", newword(npid, (word *)0)); + break; + } +} + +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; + } +} + +/* + * Who should wait for the exit from the fork? + */ +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; + } +} + +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); + inttoascii(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; + } +} + +int +execforkexec(void) +{ + int pid; + int n; + char buf[ERRMAX]; + + switch(pid = fork()){ + case -1: + return -1; + case 0: + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + } + return pid; +} blob - /dev/null blob + fc8f7608ec32dd2798b99a69bc02b3cecaf626c2 (mode 644) --- /dev/null +++ src/cmd/rc/haventfork.c @@ -0,0 +1,211 @@ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +int havefork = 0; + +static char ** +rcargv(char *s) +{ + int argc; + char **argv; + word *p; + + p = vlook("*")->val; + argv = malloc((count(p)+6)*sizeof(char*)); + argc = 0; + argv[argc++] = argv0; + if(flag['e']) + argv[argc++] = "-Se"; + else + argv[argc++] = "-S"; + argv[argc++] = "-c"; + argv[argc++] = s; + for(p = vlook("*")->val; p; p = p->next) + argv[argc++] = p->word; + argv[argc] = 0; + return argv; +} + +void +Xasync(void) +{ + uint pid; + char buf[20], **argv; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid == 0) { + Xerror("proc failed"); + return; + } + + runq->pc++; + sprint(buf, "%d", pid); + setvar("apid", newword(buf, (word *)0)); +} + +void +Xbackq(void) +{ + char wd[8193], **argv; + 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; + } + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, pfd[1], 2); + free(argv); + + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + f = openfd(pfd[0]); + 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, 1); + /* 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++; +} + +void +Xpipe(void) +{ + thread *p=runq; + int pc=p->pc, pid; + int rfd=p->code[pc+1].i; + int pfd[2]; + char **argv; + + if(pipe(pfd)<0){ + Xerror1("can't get pipe"); + return; + } + + Updenv(); + + argv = rcargv(runq->code[pc+2].s); + pid = ForkExecute(argv0, argv, 0, pfd[1], 2); + free(argv); + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + start(p->code, pc+4, runq->local); + pushredir(ROPEN, pfd[0], rfd); + p->pc=p->code[pc+3].i; + p->pid=pid; +} + +void +Xpipefd(void) +{ + Abort(); +} + +void +Xsubshell(void) +{ + char **argv; + int pid; + + Updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = ForkExecute(argv0, argv, -1, 1, 2); + free(argv); + + if(pid < 0) { + Xerror("proc failed"); + return; + } + + Waitfor(pid, 1); + runq->pc++; +} + +/* + * start a process running the cmd on the stack and return its pid. + */ +int +execforkexec(void) +{ + char **argv; + char file[1024]; + int nc; + word *path; + int pid; + + if(runq->argv->words==0) + return -1; + argv = mkargv(runq->argv->words); + + for(path = searchpath(runq->argv->words->word);path;path = path->next){ + nc = strlen(path->word); + if(ncword); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])= 0){ + free(argv); + return pid; + } + } + } + } + free(argv); + return -1; +} blob - /dev/null blob + 2a0fe2f2024d130019981aeeed1a850145ad914a (mode 644) --- /dev/null +++ src/cmd/rc/here.c @@ -0,0 +1,131 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct here *here, **ehere; +int ser=0; +char tmp[]="/tmp/here0000.0000"; +char hex[]="0123456789abcdef"; +void psubst(io*, char*); +void pstrs(io*, word*); +void hexnum(char *p, int n) +{ + *p++=hex[(n>>12)&0xF]; + *p++=hex[(n>>8)&0xF]; + *p++=hex[(n>>4)&0xF]; + *p=hex[n&0xF]; +} +tree *heredoc(tree *tag) +{ + struct here *h=new(struct here); + if(tag->type!=WORD) yyerror("Bad here tag"); + h->next=0; + if(here) + *ehere=h; + else + here=h; + ehere=&h->next; + h->tag=tag; + hexnum(&tmp[9], getpid()); + hexnum(&tmp[14], ser++); + h->name=strdup(tmp); + return token(tmp, WORD); +} +/* + * bug: lines longer than NLINE get split -- this can cause spurious + * missubstitution, or a misrecognized EOF marker. + */ +#define NLINE 4096 +void readhere(void){ + struct here *h, *nexth; + io *f; + char *s, *tag; + int c, subst; + char line[NLINE+1]; + for(h=here;h;h=nexth){ + subst=!h->tag->quoted; + tag=h->tag->str; + c=Creat(h->name); + if(c<0) yyerror("can't create here document"); + f=openfd(c); + s=line; + pprompt(); + while((c=rchr(runq->cmdfd))!=EOF){ + if(c=='\n' || s==&line[NLINE]){ + *s='\0'; + if(strcmp(line, tag)==0) break; + if(subst) psubst(f, line); + else pstr(f, line); + s=line; + if(c=='\n'){ + pprompt(); + pchr(f, c); + } + else *s++=c; + } + else *s++=c; + } + flush(f); + closeio(f); + cleanhere(h->name); + nexth=h->next; + efree((char *)h); + } + here=0; + doprompt=1; +} +void psubst(io *f, char *s) +{ + char *t, *u; + int savec, n; + word *star; + while(*s){ + if(*s!='$'){ + if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){ + pchr(f, *s++); + if(*s=='\0') break; + } + else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){ + pchr(f, *s++); + if(*s=='\0') break; + pchr(f, *s++); + if(*s=='\0') break; + } + pchr(f, *s++); + } + else{ + t=++s; + if(*t=='$') pchr(f, *t++); + else{ + while(*t && idchr(*t)) t++; + savec=*t; + *t='\0'; + n=0; + for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0'; + if(n && *u=='\0'){ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + pstr(f, star->word); + } + } + else + pstrs(f, vlook(s)->val); + *t=savec; + if(savec=='^') t++; + } + s=t; + } + } +} +void pstrs(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pstr(f, a->word); + pchr(f, ' '); + a=a->next; + } + pstr(f, a->word); + } +} blob - /dev/null blob + cf143b0fab6be61c6293b6d378df56a958cf50b4 (mode 644) --- /dev/null +++ src/cmd/rc/io.c @@ -0,0 +1,179 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +int pfmtnest=0; +void pfmt(io *f, char *fmt, ...){ + va_list ap; + char err[ERRMAX]; + va_start(ap, fmt); + pfmtnest++; + for(;*fmt;fmt++) + if(*fmt!='%') pchr(f, *fmt); + else switch(*++fmt){ + case '\0': va_end(ap); return; + case 'c': pchr(f, va_arg(ap, int)); break; + case 'd': pdec(f, va_arg(ap, int)); break; + case 'o': poct(f, va_arg(ap, unsigned)); break; + case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/ + case 'Q': pquo(f, va_arg(ap, char *)); break; + case 'q': pwrd(f, va_arg(ap, char *)); break; + case 'r': errstr(err, sizeof err); pstr(f, err); break; + case 's': pstr(f, va_arg(ap, char *)); break; + case 't': pcmd(f, va_arg(ap, struct tree *)); break; + case 'v': pval(f, va_arg(ap, struct word *)); break; + default: pchr(f, *fmt); break; + } + va_end(ap); + if(--pfmtnest==0) flush(f); +} +void pchr(io *b, int c) +{ + if(b->bufp==b->ebuf) fullbuf(b, c); + else *b->bufp++=c; +} +int rchr(io *b) +{ + if(b->bufp==b->ebuf) return emptybuf(b); + return *b->bufp++ & 0xFF; +} + +void pquo(io *f, char *s) +{ + pchr(f, '\''); + for(;*s;s++) + if(*s=='\'') pfmt(f, "''"); + else pchr(f, *s); + pchr(f, '\''); +} +void pwrd(io *f, char *s) +{ + char *t; + for(t=s;*t;t++) if(!wordchr(*t)) break; + if(t==s || *t) pquo(f, s); + else pstr(f, s); +} +void phex(io *f, long p) +{ + int n; + for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} +void pstr(io *f, char *s) +{ + if(s==0) s="(null)"; + while(*s) pchr(f, *s++); +} +void pdec(io *f, long n) +{ + if(n<0){ + n=-n; + if(n>=0){ + pchr(f, '-'); + pdec(f, n); + return; + } + /* n is two's complement minimum integer */ + n=1-n; + pchr(f, '-'); + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + if(n>9) pdec(f, n/10); + pchr(f, n%10+'0'); +} +void poct(io *f, ulong n) +{ + if(n>7) poct(f, n>>3); + pchr(f, (n&7)+'0'); +} +void pval(io *f, word *a) +{ + if(a){ + while(a->next && a->next->word){ + pwrd(f, a->word); + pchr(f, ' '); + a=a->next; + } + pwrd(f, a->word); + } +} +int fullbuf(io *f, int c) +{ + flush(f); + return *f->bufp++=c; +} +void flush(io *f) +{ + int n; + char *s; + if(f->strp){ + n=f->ebuf-f->strp; + f->strp=realloc(f->strp, n+101); + if(f->strp==0) panic("Can't realloc %d bytes in flush!", n+101); + f->bufp=f->strp+n; + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) *s='\0'; + } + else{ + n=f->bufp-f->buf; + if(n && Write(f->fd, f->buf, n) < 0){ + Write(3, "Write error\n", 12); + if(ntrap) dotrap(); + } + f->bufp=f->buf; + f->ebuf=f->buf+NBUF; + } +} +io *openfd(int fd){ + io *f=new(struct io); + f->fd=fd; + f->bufp=f->ebuf=f->buf; + f->strp=0; + return f; +} +io *openstr(void){ + io *f=new(struct io); + char *s; + f->fd=-1; + f->bufp=f->strp=emalloc(101); + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) *s='\0'; + return f; +} +/* + * Open a corebuffer to read. EOF occurs after reading len + * characters from buf. + */ +io *opencore(char *s, int len) +{ + io *f=new(struct io); + char *buf=emalloc(len); + f->fd= -1 /*open("/dev/null", 0)*/; + f->bufp=f->strp=buf; + f->ebuf=buf+len; + Memcpy(buf, s, len); + return f; +} +void rewind(io *io) +{ + if(io->fd==-1) io->bufp=io->strp; + else{ + io->bufp=io->ebuf=io->buf; + Seek(io->fd, 0L, 0); + } +} +void closeio(io *io) +{ + if(io->fd>=0) close(io->fd); + if(io->strp) efree(io->strp); + efree((char *)io); +} +int emptybuf(io *f) +{ + int n; + if(f->fd==-1 || (n=Read(f->fd, f->buf, NBUF))<=0) return EOF; + f->bufp=f->buf; + f->ebuf=f->buf+n; + return *f->bufp++&0xff; +} blob - /dev/null blob + 92a439e5d921907f32cb3932b1fe897db08592c6 (mode 644) --- /dev/null +++ src/cmd/rc/io.h @@ -0,0 +1,24 @@ +#define EOF (-1) +#define NBUF 512 +struct io{ + int fd; + char *bufp, *ebuf, *strp, buf[NBUF]; +}; +io *err; +io *openfd(int), *openstr(void), *opencore(char *, int); +int emptybuf(io*); +void pchr(io*, int); +int rchr(io*); +void closeio(io*); +void flush(io*); +int fullbuf(io*, int); +void pdec(io*, long); +void poct(io*, ulong); +void phex(io*, long); +void pquo(io*, char*); +void pwrd(io*, char*); +void pstr(io*, char*); +void pcmd(io*, tree*); +void pval(io*, word*); +void pfnc(io*, thread*); +void pfmt(io*, char*, ...); blob - /dev/null blob + dd58151dbc3c0135962891755d13c85c19bf10da (mode 644) --- /dev/null +++ src/cmd/rc/lex.c @@ -0,0 +1,322 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "getflags.h" +#include "fns.h" +int getnext(void); +int wordchr(int c) +{ + return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; +} +int idchr(int c) +{ + /* + * Formerly: + * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' + * || c=='_' || c=='*'; + */ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); +} +int future=EOF; +int doprompt=1; +int inquote; +/* + * Look ahead in the input stream + */ +int nextc(void){ + if(future==EOF) future=getnext(); + return future; +} +/* + * Consume the lookahead character. + */ +int advance(void){ + int c=nextc(); + lastc=future; + future=EOF; + return c; +} +/* + * read a character from the input stream + */ +int getnext(void){ + register int c; + static int peekc=EOF; + if(peekc!=EOF){ + c=peekc; + peekc=EOF; + return c; + } + if(runq->eof) return EOF; + if(doprompt) pprompt(); + c=rchr(runq->cmdfd); + if(!inquote && c=='\\'){ + c=rchr(runq->cmdfd); + if(c=='\n'){ + doprompt=1; + c=' '; + } + else{ + peekc=c; + c='\\'; + } + } + doprompt=doprompt || c=='\n' || c==EOF; + if(c==EOF) runq->eof++; + else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c); + return c; +} +void pprompt(void){ + var *prompt; + if(runq->iflag){ + pstr(err, promptstr); + flush(err); + prompt=vlook("prompt"); + if(prompt->val && prompt->val->next) + promptstr=prompt->val->next->word; + else + promptstr="\t"; + } + runq->lineno++; + doprompt=0; +} +void skipwhite(void){ + int c; + for(;;){ + c=nextc(); + if(c=='#'){ /* Why did this used to be if(!inquote && c=='#') ?? */ + for(;;){ + c=nextc(); + if(c=='\n' || c==EOF) break; + advance(); + } + } + if(c==' ' || c=='\t') advance(); + else return; + } +} +void skipnl(void){ + register int c; + for(;;){ + skipwhite(); + c=nextc(); + if(c!='\n') return; + advance(); + } +} +int nextis(int c){ + if(nextc()==c){ + advance(); + return 1; + } + return 0; +} +char *addtok(char *p, int val){ + if(p==0) return 0; + if(p==&tok[NTOK]){ + *p=0; + yyerror("token buffer too short"); + return 0; + } + *p++=val; + return p; +} +char *addutf(char *p, int c){ + p=addtok(p, c); + if(twobyte(c)) /* 2-byte escape */ + return addtok(p, advance()); + if(threebyte(c)){ /* 3-byte escape */ + p=addtok(p, advance()); + return addtok(p, advance()); + } + return p; +} +int lastdol; /* was the last token read '$' or '$#' or '"'? */ +int lastword; /* was the last token read a word or compound word terminator? */ +int yylex(void){ + register int c, d=nextc(); + register char *w=tok; + register struct tree *t; + yylval.tree=0; + /* + * Embarassing sneakiness: if the last token read was a quoted or unquoted + * WORD then we alter the meaning of what follows. If the next character + * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, + * if the next character is the first character of a simple or compound word, + * we insert a `^' before it. + */ + if(lastword){ + lastword=0; + if(d=='('){ + advance(); + strcpy(tok, "( [SUB]"); + return SUB; + } + if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(tok, "^"); + return '^'; + } + } + inquote=0; + skipwhite(); + switch(c=advance()){ + case EOF: + lastdol=0; + strcpy(tok, "EOF"); + return EOF; + case '$': + lastdol=1; + if(nextis('#')){ + strcpy(tok, "$#"); + return COUNT; + } + if(nextis('"')){ + strcpy(tok, "$\""); + return '"'; + } + strcpy(tok, "$"); + return '$'; + case '&': + lastdol=0; + if(nextis('&')){ + skipnl(); + strcpy(tok, "&&"); + return ANDAND; + } + strcpy(tok, "&"); + return '&'; + case '|': + lastdol=0; + if(nextis(c)){ + skipnl(); + strcpy(tok, "||"); + return OROR; + } + case '<': + case '>': + lastdol=0; + /* + * funny redirection tokens: + * redir: arrow | arrow '[' fd ']' + * arrow: '<' | '<<' | '>' | '>>' | '|' + * fd: digit | digit '=' | digit '=' digit + * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' + * some possibilities are nonsensical and get a message. + */ + *w++=c; + t=newtree(); + switch(c){ + case '|': + t->type=PIPE; + t->fd0=1; + t->fd1=0; + break; + case '>': + t->type=REDIR; + if(nextis(c)){ + t->rtype=APPEND; + *w++=c; + } + else t->rtype=WRITE; + t->fd0=1; + break; + case '<': + t->type=REDIR; + if(nextis(c)){ + t->rtype=HERE; + *w++=c; + } + else t->rtype=READ; + t->fd0=0; + break; + } + if(nextis('[')){ + *w++='['; + c=advance(); + *w++=c; + if(c<'0' || '9'type==PIPE?"pipe syntax" + :"redirection syntax"); + return EOF; + } + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + if(c=='='){ + *w++='='; + if(t->type==REDIR) t->type=DUP; + c=advance(); + if('0'<=c && c<='9'){ + t->rtype=DUPFD; + t->fd1=t->fd0; + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + } + else{ + if(t->type==PIPE) goto RedirErr; + t->rtype=CLOSE; + } + } + if(c!=']' + || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) + goto RedirErr; + *w++=']'; + } + *w='\0'; + yylval.tree=t; + if(t->type==PIPE) skipnl(); + return t->type; + case '\'': + lastdol=0; + lastword=1; + inquote=1; + for(;;){ + c=advance(); + if(c==EOF) break; + if(c=='\''){ + if(nextc()!='\'') + break; + advance(); + } + w=addutf(w, c); + } + if(w!=0) *w='\0'; + t=token(tok, WORD); + t->quoted=1; + yylval.tree=t; + return t->type; + } + if(!wordchr(c)){ + lastdol=0; + tok[0]=c; + tok[1]='\0'; + return c; + } + for(;;){ + /* next line should have (char)c==GLOB, but ken's compiler is broken */ + if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) + w=addtok(w, GLOB); + w=addutf(w, c); + c=nextc(); + if(lastdol?!idchr(c):!wordchr(c)) break; + advance(); + } + + lastword=1; + lastdol=0; + if(w!=0) *w='\0'; + t=klook(tok); + if(t->type!=WORD) lastword=0; + t->quoted=0; + yylval.tree=t; + return t->type; +} blob - /dev/null blob + cc476973c176d5a27b53c7099c62c46f2404f43d (mode 644) --- /dev/null +++ src/cmd/rc/mkfile @@ -0,0 +1,39 @@ +PLAN9=../../.. +<$PLAN9/src/mkhdr +YACC=yacc -d + +TARG=rc + +OFILES=\ + code.$O\ + exec.$O\ + getflags.$O\ + glob.$O\ + here.$O\ + io.$O\ + lex.$O\ + pcmd.$O\ + pfnc.$O\ + simple.$O\ + subr.$O\ + trap.$O\ + tree.$O\ + var.$O\ + y.tab.$O\ + plan9ish.$O\ + +HFILES=\ + rc.h\ + x.tab.h\ + io.h\ + exec.h\ + fns.h\ + +YFILES=syn.y + +LDFLAGS=$LDFLAGS -l9 -lfmt -lutf + +<$PLAN9/src/mkone + +x.tab.h: y.tab.h + cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h blob - /dev/null blob + 0f84298ae3784d45484c059bd41f4244b00c881b (mode 644) --- /dev/null +++ src/cmd/rc/pcmd.c @@ -0,0 +1,108 @@ +#include "rc.h" +#include "io.h" +#include "fns.h" +char nl='\n'; /* change to semicolon for bourne-proofing */ +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +void pdeglob(io *f, char *s) +{ + while(*s){ + if(*s==GLOB) s++; + pchr(f, *s++); + } +} +void pcmd(io *f, tree *t) +{ + if(t==0) return; + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break; + case '$': pfmt(f, "$%t", c0); break; + case '"': pfmt(f, "$\"%t", c0); break; + case '&': pfmt(f, "%t&", c0); break; + case '^': pfmt(f, "%t^%t", c0, c1); break; + case '`': pfmt(f, "`%t", c0); break; + case ANDAND: pfmt(f, "%t && %t", c0, c1); break; + case BANG: pfmt(f, "! %t", c0); break; + case BRACE: pfmt(f, "{%t}", c0); break; + case COUNT: pfmt(f, "$#%t", c0); break; + case FN: pfmt(f, "fn %t %t", c0, c1); break; + case IF: pfmt(f, "if%t%t", c0, c1); break; + case NOT: pfmt(f, "if not %t", c0); break; + case OROR: pfmt(f, "%t || %t", c0, c1); break; + case PCMD: + case PAREN: pfmt(f, "(%t)", c0); break; + case SUB: pfmt(f, "$%t(%t)", c0, c1); break; + case SIMPLE: pfmt(f, "%t", c0); break; + case SUBSHELL: pfmt(f, "@ %t", c0); break; + case SWITCH: pfmt(f, "switch %t %t", c0, c1); break; + case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); break; + case WHILE: pfmt(f, "while %t%t", c0, c1); break; + case ARGLIST: + if(c0==0) + pfmt(f, "%t", c1); + else if(c1==0) + pfmt(f, "%t", c0); + else + pfmt(f, "%t %t", c0, c1); + break; + case ';': + if(c0){ + if(c1) pfmt(f, "%t%c%t", c0, nl, c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case WORDS: + if(c0) pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case FOR: + pfmt(f, "for(%t", c0); + if(c1) pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case WORD: + if(t->quoted) pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + if(t->rtype==DUPFD) + pfmt(f, ">[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, ">[%d=]", t->fd0); + pfmt(f, "%t", c1); + break; + case PIPEFD: + case REDIR: + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + pchr(f, '<'); + if(t->fd0!=0) pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) pfmt(f, "[%d]", t->fd0); + break; + } + pfmt(f, "%t", c0); + if(c1) pfmt(f, " %t", c1); + break; + case '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) pfmt(f, " %t", c2); + break; + case PIPE: + pfmt(f, "%t|", c0); + if(t->fd1==0){ + if(t->fd0!=1) pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, "%t", c1); + break; + } +} blob - /dev/null blob + a4606e7c5830f6a36dbb5a05d808c8946db83bf2 (mode 644) --- /dev/null +++ src/cmd/rc/pfnc.c @@ -0,0 +1,67 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +struct{ + void (*f)(void); + char *name; +}fname[]={ + Xappend, "Xappend", + Xasync, "Xasync", + Xbang, "Xbang", + Xclose, "Xclose", + Xdup, "Xdup", + Xeflag, "Xeflag", + Xexit, "Xexit", + Xfalse, "Xfalse", + Xifnot, "Xifnot", + Xjump, "Xjump", + Xmark, "Xmark", + Xpopm, "Xpopm", + Xread, "Xread", + Xreturn, "Xreturn", + Xtrue, "Xtrue", + Xif, "Xif", + Xwastrue, "Xwastrue", + Xword, "Xword", + Xwrite, "Xwrite", + Xmatch, "Xmatch", + Xcase, "Xcase", + Xconc, "Xconc", + Xassign, "Xassign", + Xdol, "Xdol", + Xcount, "Xcount", + Xlocal, "Xlocal", + Xunlocal, "Xunlocal", + Xfn, "Xfn", + Xdelfn, "Xdelfn", + Xpipe, "Xpipe", + Xpipewait, "Xpipewait", + Xrdcmds, "Xrdcmds", + (void (*)(void))Xerror, "Xerror", + Xbackq, "Xbackq", + Xpipefd, "Xpipefd", + Xsubshell, "Xsubshell", + Xdelhere, "Xdelhere", + Xfor, "Xfor", + Xglob, "Xglob", + Xrdfn, "Xrdfn", + Xsimple, "Xsimple", + Xrdfn, "Xrdfn", + Xqdol, "Xqdol", +0}; +void pfnc(io *fd, thread *t) +{ + int i; + void (*fn)(void)=t->code[t->pc].f; + list *a; + pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc); + for(i=0;fname[i].f;i++) if(fname[i].f==fn){ + pstr(fd, fname[i].name); + break; + } + if(!fname[i].f) pfmt(fd, "%p", fn); + for(a=t->argv;a;a=a->next) pfmt(fd, " (%v)", a->words); + pchr(fd, '\n'); + flush(fd); +} blob - /dev/null blob + 0c3076ec4fe810c9f236842fc5c7569bc47b1c14 (mode 644) --- /dev/null +++ src/cmd/rc/plan9ish.c @@ -0,0 +1,480 @@ +/* + * Plan 9 versions of system-specific functions + * By convention, exported routines herein have names beginning with an + * upper case letter. + */ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +#include "getflags.h" +char *Signame[]={ + "sigexit", "sighup", "sigint", "sigquit", + "sigalrm", "sigkill", "sigfpe", "sigterm", + 0 +}; +char *syssigname[]={ + "exit", /* can't happen */ + "hangup", + "interrupt", + "quit", /* can't happen */ + "alarm", + "kill", + "sys: fp: ", + "term", + 0 +}; +char* +Rcmain(void) +{ + static char buf[256]; + char *root; + + root = getenv("PLAN9"); + if(root == nil) + root = "/usr/local/plan9"; + snprint(buf, sizeof buf, "%s/rcmain", root); + return buf; +} + +char Fdprefix[]="/dev/fd/"; +void execfinit(void); +void execbind(void); +void execmount(void); +void execnewpgrp(void); +builtin Builtin[]={ + "cd", execcd, + "whatis", execwhatis, + "eval", execeval, + "exec", execexec, /* but with popword first */ + "exit", execexit, + "shift", execshift, + "wait", execwait, + ".", execdot, + "finit", execfinit, + "flag", execflag, + 0 +}; +#define SEP '\1' +char **environp; +struct word *enval(s) +register char *s; +{ + register char *t, c; + register struct word *v; + for(t=s;*t && *t!=SEP;t++); + c=*t; + *t='\0'; + v=newword(s, c=='\0'?(struct word *)0:enval(t+1)); + *t=c; + return v; +} +void Vinit(void){ + extern char **environ; + register char *s; + register char **env=environ; + environp=env; + for(;*env;env++){ + for(s=*env;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + pfmt(err, "environment %q?\n", *env); + break; + case '=': + *s='\0'; + setvar(*env, enval(s+1)); + *s='='; + break; + case '(': /* ignore functions for now */ + break; + } + } +} +char **envp; +void Xrdfn(void){ + char *p; + register char *s; + register int len; + for(;*envp;envp++){ + s = *envp; + if(strncmp(s, "fn#", 3) == 0){ + p = strchr(s, '='); + if(p == nil) + continue; + *p = ' '; + s[2] = ' '; + len = strlen(s); + execcmds(opencore(s, len)); + s[len] = '\0'; + return; + } +#if 0 + for(s=*envp;*s && *s!='(' && *s!='=';s++); + switch(*s){ + case '\0': + pfmt(err, "environment %q?\n", *envp); + break; + case '=': /* ignore variables */ + break; + case '(': /* Bourne again */ + s=*envp+3; + envp++; + len=strlen(s); + s[len]='\n'; + execcmds(opencore(s, len+1)); + s[len]='\0'; + return; + } +#endif + } + Xreturn(); +} +union code rdfns[4]; +void execfinit(void){ + static int first=1; + if(first){ + rdfns[0].i=1; + rdfns[1].f=Xrdfn; + rdfns[2].f=Xjump; + rdfns[3].i=1; + first=0; + } + Xpopm(); + envp=environp; + start(rdfns, 1, runq->local); +} +int Waitfor(int pid, int unused0){ + thread *p; + Waitmsg *w; + char errbuf[ERRMAX]; + + while((w = wait()) != nil){ + if(w->pid==pid){ + setstatus(w->msg); + free(w); + return 0; + } + for(p=runq->ret;p;p=p->ret) + if(p->pid==w->pid){ + p->pid=-1; + strcpy(p->status, w->msg); + } + free(w); + } + + errstr(errbuf, sizeof errbuf); + if(strcmp(errbuf, "interrupted")==0) return -1; + return 0; +} +char **mkargv(word *a) +{ + char **argv=(char **)emalloc((count(a)+2)*sizeof(char *)); + char **argp=argv+1; /* leave one at front for runcoms */ + for(;a;a=a->next) *argp++=a->word; + *argp=0; + return argv; +} +/* +void addenv(var *v) +{ + char envname[256]; + word *w; + int f; + io *fd; + if(v->changed){ + v->changed=0; + snprint(envname, sizeof envname, "/env/%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + for(w=v->val;w;w=w->next) + write(f, w->word, strlen(w->word)+1L); + close(f); + } + } + if(v->fnchanged){ + v->fnchanged=0; + snprint(envname, sizeof envname, "/env/fn#%s", v->name); + if((f=Creat(envname))<0) + pfmt(err, "rc: can't open %s: %r\n", envname); + else{ + if(v->fn){ + fd=openfd(f); + pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + closeio(fd); + } + close(f); + } + } +} +void updenvlocal(var *v) +{ + if(v){ + updenvlocal(v->next); + addenv(v); + } +} +void Updenv(void){ + var *v, **h; + for(h=gvar;h!=&gvar[NVAR];h++) + for(v=*h;v;v=v->next) + addenv(v); + if(runq) updenvlocal(runq->local); +} +*/ +int +cmpenv(a, b) +char **a, **b; +{ + return strcmp(*a, *b); +} +char **mkenv(){ + register char **env, **ep, *p, *q; + register struct var **h, *v; + register struct word *a; + register int nvar=0, nchr=0, sep; + /* + * Slightly kludgy loops look at locals then globals + */ + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + nvar++; + nchr+=strlen(v->name)+1; + for(a=v->val;a;a=a->next) + nchr+=strlen(a->word)+1; + } + if(v->fn){ + nvar++; + nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; + } + } + env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr); + ep=env; + p=(char *)&env[nvar+1]; + for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ + if((v==vlook(v->name)) && v->val){ + *ep++=p; + q=v->name; + while(*q) *p++=*q++; + sep='='; + for(a=v->val;a;a=a->next){ + *p++=sep; + sep=SEP; + q=a->word; + while(*q) *p++=*q++; + } + *p++='\0'; + } + if(v->fn){ + *ep++=p; +#if 0 + *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ + *p++='f'; *p++='n'; *p++=' '; + q=v->name; + while(*q) *p++=*q++; + *p++=' '; +#endif + *p++='f'; *p++='n'; *p++='#'; + q=v->name; + while(*q) *p++=*q++; + *p++='='; + q=v->fn[v->pc-1].s; + while(*q) *p++=*q++; + *p++='\n'; + *p++='\0'; + } + } + *ep=0; + qsort((char *)env, nvar, sizeof ep[0], cmpenv); + return env; +} +void Updenv(void){} +void Execute(word *args, word *path) +{ + char **argv=mkargv(args); + char **env=mkenv(); + char file[1024]; + int nc; + Updenv(); + for(;path;path=path->next){ + nc=strlen(path->word); + if(nc<1024){ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])<1024){ + strcat(file, argv[1]); + execve(file, argv+1, env); + } + else werrstr("command name too long"); + } + } + rerrstr(file, sizeof file); + pfmt(err, "%s: %s\n", argv[1], file); + efree((char *)argv); +} +#define NDIR 256 /* shoud be a better way */ +int Globsize(char *p) +{ + ulong isglob=0, globlen=NDIR+1; + for(;*p;p++){ + if(*p==GLOB){ + p++; + if(*p!=GLOB) isglob++; + globlen+=*p=='*'?NDIR:1; + } + else + globlen++; + } + return isglob?globlen:0; +} +#define NFD 50 +#define NDBUF 32 +struct{ + Dir *dbuf; + int i; + int n; +}dir[NFD]; +int Opendir(char *name) +{ + Dir *db; + int f; + f=open(name, 0); + if(f==-1) + return f; + db = dirfstat(f); + if(db!=nil && (db->mode&DMDIR)){ + if(f=NFD) + return 0; + if(dir[f].i==dir[f].n){ /* read */ + free(dir[f].dbuf); + dir[f].dbuf=0; + n=dirread(f, &dir[f].dbuf); + if(n>=0) + dir[f].n=n; + else + dir[f].n=0; + dir[f].i=0; + } + if(dir[f].i==dir[f].n) + return 0; + strcpy(p, dir[f].dbuf[dir[f].i].name); + dir[f].i++; + return 1; +} +void Closedir(int f){ + if(f>=0 && f=32){ /* rc is probably in a trap loop */ + pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); + abort(); + } + noted(NCONT); +} +void Trapinit(void){ + notify(notifyf); +} +void Unlink(char *name) +{ + remove(name); +} +long Write(int fd, char *buf, long cnt) +{ + return write(fd, buf, (long)cnt); +} +long Read(int fd, char *buf, long cnt) +{ + return read(fd, buf, cnt); +} +long Seek(int fd, long cnt, long whence) +{ + return seek(fd, cnt, whence); +} +int Executable(char *file) +{ + Dir *statbuf; + int ret; + + statbuf = dirstat(file); + if(statbuf == nil) return 0; + ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); + free(statbuf); + return ret; +} +int Creat(char *file) +{ + return create(file, 1, 0666L); +} +int Dup(int a, int b){ + return dup(a, b); +} +int Dup1(int unused0){ + return -1; +} +void Exit(char *stat) +{ + Updenv(); + setstatus(stat); + exits(truestatus()?"":getstatus()); +} +int Eintr(void){ + return interrupted; +} +void Noerror(void){ + interrupted=0; +} +int +Isatty(fd){ + return isatty(fd); +} +void Abort(void){ + pfmt(err, "aborting\n"); + flush(err); + Exit("aborting"); +} +void Memcpy(char *a, char *b, long n) +{ + memmove(a, b, (long)n); +} +void *Malloc(ulong n){ + return malloc(n); +} blob - /dev/null blob + c81b6280deb406f009469d8a787282dc2a578827 (mode 644) --- /dev/null +++ src/cmd/rc/rc.h @@ -0,0 +1,136 @@ +/* + * Plan9 is defined for plan 9 + * V9 is defined for 9th edition + * Sun is defined for sun-os + * Please don't litter the code with ifdefs. The three below (and one in + * getflags) should be enough. + */ +#define Plan9 +#ifdef Plan9 +#include +#include +#define NSIG 32 +#define SIGINT 2 +#define SIGQUIT 3 +#endif +#ifdef V9 +#include +#include +#endif +#ifdef Sun +#include +#endif +#define YYMAXDEPTH 500 +#ifndef PAREN +#ifndef YYMAJOR +#include "x.tab.h" +#endif +#endif +typedef struct tree tree; +typedef struct word word; +typedef struct io io; +typedef union code code; +typedef struct var var; +typedef struct list list; +typedef struct redir redir; +typedef struct thread thread; +typedef struct builtin builtin; + +struct tree{ + int type; + int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ + char *str; + int quoted; + int iskw; + tree *child[3]; + tree *next; +}; +tree *newtree(void); +tree *token(char*, int), *klook(char*), *tree1(int, tree*); +tree *tree2(int, tree*, tree*), *tree3(int, tree*, tree*, tree*); +tree *mung1(tree*, tree*), *mung2(tree*, tree*, tree*); +tree *mung3(tree*, tree*, tree*, tree*), *epimung(tree*, tree*); +tree *simplemung(tree*), *heredoc(tree*); +void freetree(tree*); +tree *cmdtree; +/* + * The first word of any code vector is a reference count. + * Always create a new reference to a code vector by calling codecopy(.). + * Always call codefree(.) when deleting a reference. + */ +union code{ + void (*f)(void); + int i; + char *s; +}; +char *promptstr; +int doprompt; +#define NTOK 8192 +char tok[NTOK]; +#define APPEND 1 +#define WRITE 2 +#define READ 3 +#define HERE 4 +#define DUPFD 5 +#define CLOSE 6 +struct var{ + char *name; /* ascii name */ + word *val; /* value */ + int changed; + code *fn; /* pointer to function's code vector */ + int fnchanged; + int pc; /* pc of start of function */ + var *next; /* next on hash or local list */ +}; +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); +#define NVAR 521 +var *gvar[NVAR]; /* hash for globals */ +#define new(type) ((type *)emalloc(sizeof(type))) +char *emalloc(long); +void *Malloc(ulong); +void efree(char*); +#define NOFILE 128 /* should come from */ +struct here{ + tree *tag; + char *name; + struct here *next; +}; +int mypid; +/* + * Glob character escape in strings: + * In a string, GLOB must be followed by *?[ or GLOB. + * GLOB* matches any string + * GLOB? matches any single character + * GLOB[...] matches anything in the brackets + * GLOBGLOB matches GLOB + */ +#define GLOB ((char)0x01) +/* + * onebyte(c), twobyte(c), threebyte(c) + * Is c the first character of a one- two- or three-byte utf sequence? + */ +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) +char **argp; +char **args; +int nerror; /* number of errors encountered during compilation */ +int doprompt; /* is it time for a prompt? */ +/* + * Which fds are the reading/writing end of a pipe? + * Unfortunately, this can vary from system to system. + * 9th edition Unix doesn't care, the following defines + * work on plan 9. + */ +#define PRD 0 +#define PWR 1 +extern char *Rcmain(), Fdprefix[]; +#define register +/* + * How many dot commands have we executed? + * Used to ensure that -v flag doesn't print rcmain. + */ +int ndot; +char *getstatus(void); +int lastc; +int lastword; blob - /dev/null blob + c85c909836a91a1ef4f810f3c73ed2cb41a98384 (mode 644) --- /dev/null +++ src/cmd/rc/simple.c @@ -0,0 +1,443 @@ +/* + * Maybe `simple' is a misnomer. + */ +#include "rc.h" +#include "getflags.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +/* + * Search through the following code to see if we're just going to exit. + */ +int +exitnext(void){ + union code *c=&runq->code[runq->pc]; + while(c->f==Xpopredir) c++; + return c->f==Xexit; +} +void Xsimple(void){ + word *a; + thread *p=runq; + var *v; + struct builtin *bp; + int pid, n; + char buf[ERRMAX]; + globlist(); + a=runq->argv->words; + if(a==0){ + Xerror1("empty argument list"); + return; + } + if(flag['x']) + pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ + v=gvlook(a->word); + if(v->fn) + execfunc(v); + else{ + if(strcmp(a->word, "builtin")==0){ + if(count(a)==1){ + pfmt(err, "builtin: empty argument list\n"); + setstatus("empty arg list"); + poplist(); + return; + } + a=a->next; + popword(); + } + for(bp=Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + (*bp->fnc)(); + return; + } + if(exitnext()){ + /* fork and wait is redundant */ + pushword("exec"); + execexec(); + Xexit(); + } + else{ + flush(err); + Updenv(); /* necessary so changes don't go out again */ + switch(pid=fork()){ + case -1: + Xerror("try again"); + return; + case 0: + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + default: + poplist(); + /* interrupts don't get us out */ + while(Waitfor(pid, 1) < 0) + ; + } + } + } +} +struct word nullpath={ "", 0}; +void doredir(redir *rp) +{ + if(rp){ + doredir(rp->next); + switch(rp->type){ + case ROPEN: + if(rp->from!=rp->to){ + Dup(rp->from, rp->to); + close(rp->from); + } + break; + case RDUP: Dup(rp->from, rp->to); break; + case RCLOSE: close(rp->from); break; + } + } +} +word *searchpath(char *w){ + word *path; + if(strncmp(w, "/", 1)==0 + || strncmp(w, "#", 1)==0 + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path=vlook("path")->val)==0) + path=&nullpath; + return path; +} +void execexec(void){ + popword(); /* "exec" */ + if(runq->argv->words==0){ + Xerror1("empty argument list"); + return; + } + doredir(runq->redir); + Execute(runq->argv->words, searchpath(runq->argv->words->word)); + poplist(); +} +void execfunc(var *func) +{ + word *starval; + popword(); + starval=runq->argv->words; + runq->argv->words=0; + poplist(); + start(func->fn, func->pc, (struct var *)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=starval; + runq->local->changed=1; +} +int dochdir(char *word){ + /* report to /dev/wdir if it exists and we're interactive */ + static int wdirfd = -2; + if(chdir(word)<0) return -1; + if(flag['i']!=0){ + if(wdirfd==-2) /* try only once */ + wdirfd = open("/dev/wdir", OWRITE|OCEXEC); + if(wdirfd>=0) + write(wdirfd, word, strlen(word)); + } + return 1; +} +void execcd(void){ + word *a=runq->argv->words; + word *cdpath; + char dir[512]; + setstatus("can't cd"); + cdpath=vlook("cdpath")->val; + switch(count(a)){ + default: + pfmt(err, "Usage: cd [directory]\n"); + break; + case 2: + if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath; + for(;cdpath;cdpath=cdpath->next){ + strcpy(dir, cdpath->word); + if(dir[0]) strcat(dir, "/"); + strcat(dir, a->next->word); + if(dochdir(dir)>=0){ + if(strlen(cdpath->word) + && strcmp(cdpath->word, ".")!=0) + pfmt(err, "%s\n", dir); + setstatus(""); + break; + } + } + if(cdpath==0) pfmt(err, "Can't cd %s: %r\n", a->next->word); + break; + case 1: + a=vlook("home")->val; + if(count(a)>=1){ + if(dochdir(a->word)>=0) + setstatus(""); + else + pfmt(err, "Can't cd %s: %r\n", a->word); + } + else + pfmt(err, "Can't cd -- $home empty\n"); + break; + } + poplist(); +} +void execexit(void){ + switch(count(runq->argv->words)){ + default: pfmt(err, "Usage: exit [status]\nExiting anyway\n"); + case 2: setstatus(runq->argv->words->next->word); + case 1: Xexit(); + } +} +void execshift(void){ + int n; + word *a; + var *star; + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: shift [n]\n"); + setstatus("shift usage"); + poplist(); + return; + case 2: n=atoi(runq->argv->words->next->word); break; + case 1: n=1; break; + } + star=vlook("*"); + for(;n && star->val;--n){ + a=star->val->next; + efree(star->val->word); + efree((char *)star->val); + star->val=a; + star->changed=1; + } + setstatus(""); + poplist(); +} +int octal(char *s) +{ + int n=0; + while(*s==' ' || *s=='\t' || *s=='\n') s++; + while('0'<=*s && *s<='7') n=n*8+*s++-'0'; + return n; +} +int mapfd(int fd) +{ + redir *rp; + for(rp=runq->redir;rp;rp=rp->next){ + switch(rp->type){ + case RCLOSE: + if(rp->from==fd) fd=-1; + break; + case RDUP: + case ROPEN: + if(rp->to==fd) fd=rp->from; + break; + } + } + return fd; +} +union code rdcmds[4]; +void execcmds(io *f) +{ + static int first=1; + if(first){ + rdcmds[0].i=1; + rdcmds[1].f=Xrdcmds; + rdcmds[2].f=Xreturn; + first=0; + } + start(rdcmds, 1, runq->local); + runq->cmdfd=f; + runq->iflast=0; +} +void execeval(void){ + char *cmdline, *s, *t; + int len=0; + word *ap; + if(count(runq->argv->words)<=1){ + Xerror1("Usage: eval cmd ..."); + return; + } + eflagok=1; + for(ap=runq->argv->words->next;ap;ap=ap->next) + len+=1+strlen(ap->word); + cmdline=emalloc(len); + s=cmdline; + for(ap=runq->argv->words->next;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=' '; + } + s[-1]='\n'; + poplist(); + execcmds(opencore(cmdline, len)); + efree(cmdline); +} +union code dotcmds[14]; +void execdot(void){ + int iflag=0; + int fd; + list *av; + thread *p=runq; + char *zero; + static int first=1; + char file[512]; + word *path; + if(first){ + dotcmds[0].i=1; + dotcmds[1].f=Xmark; + dotcmds[2].f=Xword; + dotcmds[3].s="0"; + dotcmds[4].f=Xlocal; + dotcmds[5].f=Xmark; + dotcmds[6].f=Xword; + dotcmds[7].s="*"; + dotcmds[8].f=Xlocal; + dotcmds[9].f=Xrdcmds; + dotcmds[10].f=Xunlocal; + dotcmds[11].f=Xunlocal; + dotcmds[12].f=Xreturn; + first=0; + } + else + eflagok=1; + popword(); + if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ + iflag=1; + popword(); + } + /* get input file */ + if(p->argv->words==0){ + Xerror1("Usage: . [-i] file [arg ...]"); + return; + } + zero=strdup(p->argv->words->word); + popword(); + fd=-1; + for(path=searchpath(zero);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) strcat(file, "/"); + strcat(file, zero); + if((fd=open(file, 0))>=0) break; + if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ + fd=Dup1(0); + if(fd>=0) break; + } + } + if(fd<0){ + pfmt(err, "%s: ", zero); + setstatus("can't open"); + Xerror(".: can't open"); + return; + } + /* set up for a new command loop */ + start(dotcmds, 1, (struct var *)0); + pushredir(RCLOSE, fd, 0); + runq->cmdfile=zero; + runq->cmdfd=openfd(fd); + runq->iflag=iflag; + runq->iflast=0; + /* push $* value */ + pushlist(); + runq->argv->words=p->argv->words; + /* free caller's copy of $* */ + av=p->argv; + p->argv=av->next; + efree((char *)av); + /* push $0 value */ + pushlist(); + pushword(zero); + ndot++; +} +void execflag(void){ + char *letter, *val; + switch(count(runq->argv->words)){ + case 2: + setstatus(flag[(uchar)runq->argv->words->next->word[0]]?"":"flag not set"); + break; + case 3: + letter=runq->argv->words->next->word; + val=runq->argv->words->next->next->word; + if(strlen(letter)==1){ + if(strcmp(val, "+")==0){ + flag[(uchar)letter[0]]=flagset; + break; + } + if(strcmp(val, "-")==0){ + flag[(uchar)letter[0]]=0; + break; + } + } + default: + Xerror1("Usage: flag [letter] [+-]"); + return; + } + poplist(); +} +void execwhatis(void){ /* mildly wrong -- should fork before writing */ + word *a, *b, *path; + var *v; + struct builtin *bp; + char file[512]; + struct io out[1]; + int found, sep; + a=runq->argv->words->next; + if(a==0){ + Xerror1("Usage: whatis name ..."); + return; + } + setstatus(""); + out->fd=mapfd(1); + out->bufp=out->buf; + out->ebuf=&out->buf[NBUF]; + out->strp=0; + for(;a;a=a->next){ + v=vlook(a->word); + if(v->val){ + pfmt(out, "%s=", a->word); + if(v->val->next==0) + pfmt(out, "%q\n", v->val->word); + else{ + sep='('; + for(b=v->val;b && b->word;b=b->next){ + pfmt(out, "%c%q", sep, b->word); + sep=' '; + } + pfmt(out, ")\n"); + } + found=1; + } + else + found=0; + v=gvlook(a->word); + if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + else{ + for(bp=Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + pfmt(out, "builtin %s\n", a->word); + break; + } + if(!bp->name){ + for(path=searchpath(a->word);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) strcat(file, "/"); + strcat(file, a->word); + if(Executable(file)){ + pfmt(out, "%s\n", file); + break; + } + } + if(!path && !found){ + pfmt(err, "%s: not found\n", a->word); + setstatus("not found"); + } + } + } + } + poplist(); + flush(err); +} +void execwait(void){ + switch(count(runq->argv->words)){ + default: Xerror1("Usage: wait [pid]"); return; + case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break; + case 1: Waitfor(-1, 0); break; + } + poplist(); +} blob - /dev/null blob + 2b242242ba8825baeb43825df56372ef3e4abdad (mode 644) --- /dev/null +++ src/cmd/rc/subr.c @@ -0,0 +1,59 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +char *emalloc(long n){ + char *p=(char *)Malloc(n); + if(p==0) panic("Can't malloc %d bytes", n); +/* if(err){ pfmt(err, "malloc %d->%p\n", n, p); flush(err); } *//**/ + return p; +} +void efree(char *p) +{ +/* pfmt(err, "free %p\n", p); flush(err); *//**/ + if(p) free(p); + else pfmt(err, "free 0\n"); +} +extern int lastword, lastdol; +void yyerror(char *m) +{ + pfmt(err, "rc: "); + if(runq->cmdfile && !runq->iflag) + pfmt(err, "%s:%d: ", runq->cmdfile, runq->lineno); + else if(runq->cmdfile) + pfmt(err, "%s: ", runq->cmdfile); + else if(!runq->iflag) + pfmt(err, "line %d: ", runq->lineno); + if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok); + pfmt(err, "%s\n", m); + flush(err); + lastword=0; + lastdol=0; + while(lastc!='\n' && lastc!=EOF) advance(); + nerror++; + setvar("status", newword(m, (word *)0)); +} +char *bp; +void iacvt(int n){ + if(n<0){ + *bp++='-'; + n=-n; /* doesn't work for n==-inf */ + } + if(n/10) + iacvt(n/10); + *bp++=n%10+'0'; +} +void itoa(char *s, long n) +{ + bp=s; + iacvt(n); + *bp='\0'; +} +void panic(char *s, int n) +{ + pfmt(err, "rc: "); + pfmt(err, s, n); + pchr(err, '\n'); + flush(err); + Abort(); +} blob - /dev/null blob + 96ef364a6785c4ac125aa36de4f184f72af6e366 (mode 644) --- /dev/null +++ src/cmd/rc/trap.c @@ -0,0 +1,34 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +#include "io.h" +extern char *Signame[]; +void dotrap(void){ + register int i; + register struct var *trapreq; + register struct word *starval; + starval=vlook("*")->val; + while(ntrap) for(i=0;i!=NSIG;i++) while(trap[i]){ + --trap[i]; + --ntrap; + if(getpid()!=mypid) Exit(getstatus()); + trapreq=vlook(Signame[i]); + if(trapreq->fn){ + 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; + } + else if(i==SIGINT || i==SIGQUIT){ + /* + * run the stack down until we uncover the + * command reading loop. Xreturn will exit + * if there is none (i.e. if this is not + * an interactive rc.) + */ + while(!runq->iflag) Xreturn(); + } + else Exit(getstatus()); + } +} blob - /dev/null blob + 9bf76d8efb1b7956e88b9c9d9219aaf55ebaeaf4 (mode 644) --- /dev/null +++ src/cmd/rc/tree.c @@ -0,0 +1,114 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" +tree *treenodes; +/* + * create and clear a new tree node, and add it + * to the node list. + */ +tree *newtree(void){ + tree *t=new(tree); + t->iskw=0; + t->str=0; + t->child[0]=t->child[1]=t->child[2]=0; + t->next=treenodes; + treenodes=t; + return t; +} +void freenodes(void){ + tree *t, *u; + for(t=treenodes;t;t=u){ + u=t->next; + if(t->str) efree(t->str); + efree((char *)t); + } + treenodes=0; +} +tree *tree1(int type, tree *c0) +{ + return tree3(type, c0, (tree *)0, (tree *)0); +} +tree *tree2(int type, tree *c0, tree *c1) +{ + return tree3(type, c0, c1, (tree *)0); +} +tree *tree3(int type, tree *c0, tree *c1, tree *c2) +{ + tree *t; + if(type==';'){ + if(c0==0) return c1; + if(c1==0) return c0; + } + t=newtree(); + t->type=type; + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} +tree *mung1(tree *t, tree *c0) +{ + t->child[0]=c0; + return t; +} +tree *mung2(tree *t, tree *c0, tree *c1) +{ + t->child[0]=c0; + t->child[1]=c1; + return t; +} +tree *mung3(tree *t, tree *c0, tree *c1, tree *c2) +{ + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} +tree *epimung(tree *comp, tree *epi) +{ + tree *p; + if(epi==0) return comp; + for(p=epi;p->child[1];p=p->child[1]); + p->child[1]=comp; + return epi; +} +/* + * Add a SIMPLE node at the root of t and percolate all the redirections + * up to the root. + */ +tree *simplemung(tree *t) +{ + tree *u; + struct io *s; + t=tree1(SIMPLE, t); + s=openstr(); + pfmt(s, "%t", t); + t->str=strdup(s->strp); + closeio(s); + for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){ + if(u->child[1]->type==DUP + || u->child[1]->type==REDIR){ + u->child[1]->child[1]=t; + t=u->child[1]; + u->child[1]=0; + } + } + return t; +} +tree *token(char *str, int type) +{ + tree *t=newtree(); + t->type=type; + t->str=strdup(str); + return t; +} +void freetree(tree *p) +{ + if(p==0) return; + freetree(p->child[0]); + freetree(p->child[1]); + freetree(p->child[2]); + if(p->str) efree(p->str); + efree((char *)p); +} blob - /dev/null blob + 73e4e9aec52cf7161e0412cd85c17e46165a0f39 (mode 644) --- /dev/null +++ src/cmd/rc/var.c @@ -0,0 +1,71 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +int hash(char *s, int n) +{ + register int h=0, i=1; + while(*s) h+=*s++*i++; + h%=n; + return h<0?h+n:h; +} +#define NKW 30 +struct kw{ + char *name; + int type; + struct kw *next; +}*kw[NKW]; +void kenter(int type, char *name) +{ + register int h=hash(name, NKW); + register struct kw *p=new(struct kw); + p->type=type; + p->name=name; + p->next=kw[h]; + kw[h]=p; +} +void kinit(void){ + kenter(FOR, "for"); + kenter(IN, "in"); + kenter(WHILE, "while"); + kenter(IF, "if"); + kenter(NOT, "not"); + kenter(TWIDDLE, "~"); + kenter(BANG, "!"); + kenter(SUBSHELL, "@"); + kenter(SWITCH, "switch"); + kenter(FN, "fn"); +} +tree *klook(char *name) +{ + struct kw *p; + tree *t=token(name, WORD); + for(p=kw[hash(name, NKW)];p;p=p->next) + if(strcmp(p->name, name)==0){ + t->type=p->type; + t->iskw=1; + break; + } + return t; +} +var *gvlook(char *name) +{ + int h=hash(name, NVAR); + var *v; + for(v=gvar[h];v;v=v->next) if(strcmp(v->name, name)==0) return v; + return gvar[h]=newvar(strdup(name), gvar[h]); +} +var *vlook(char *name) +{ + var *v; + if(runq) + for(v=runq->local;v;v=v->next) + if(strcmp(v->name, name)==0) return v; + return gvlook(name); +} +void setvar(char *name, word *val) +{ + register struct var *v=vlook(name); + freewords(v->val); + v->val=val; + v->changed=1; +}