diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/rc |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/rc')
-rwxr-xr-x | sys/src/cmd/rc/code.c | 486 | ||||
-rwxr-xr-x | sys/src/cmd/rc/compiling.on.unix | 1866 | ||||
-rwxr-xr-x | sys/src/cmd/rc/exec.c | 996 | ||||
-rwxr-xr-x | sys/src/cmd/rc/exec.h | 76 | ||||
-rwxr-xr-x | sys/src/cmd/rc/fns.h | 67 | ||||
-rwxr-xr-x | sys/src/cmd/rc/getflags.c | 233 | ||||
-rwxr-xr-x | sys/src/cmd/rc/getflags.h | 7 | ||||
-rwxr-xr-x | sys/src/cmd/rc/glob.c | 264 | ||||
-rwxr-xr-x | sys/src/cmd/rc/havefork.c | 234 | ||||
-rwxr-xr-x | sys/src/cmd/rc/haventfork.c | 211 | ||||
-rwxr-xr-x | sys/src/cmd/rc/here.c | 152 | ||||
-rwxr-xr-x | sys/src/cmd/rc/io.c | 268 | ||||
-rwxr-xr-x | sys/src/cmd/rc/io.h | 27 | ||||
-rwxr-xr-x | sys/src/cmd/rc/lex.c | 378 | ||||
-rwxr-xr-x | sys/src/cmd/rc/mkfile | 63 | ||||
-rwxr-xr-x | sys/src/cmd/rc/pcmd.c | 147 | ||||
-rwxr-xr-x | sys/src/cmd/rc/pfnc.c | 71 | ||||
-rwxr-xr-x | sys/src/cmd/rc/plan9.c | 670 | ||||
-rwxr-xr-x | sys/src/cmd/rc/rc.h | 150 | ||||
-rwxr-xr-x | sys/src/cmd/rc/simple.c | 517 | ||||
-rwxr-xr-x | sys/src/cmd/rc/subr.c | 77 | ||||
-rwxr-xr-x | sys/src/cmd/rc/syn.y | 91 | ||||
-rwxr-xr-x | sys/src/cmd/rc/trap.c | 37 | ||||
-rwxr-xr-x | sys/src/cmd/rc/tree.c | 148 | ||||
-rwxr-xr-x | sys/src/cmd/rc/unix.c | 469 | ||||
-rwxr-xr-x | sys/src/cmd/rc/var.c | 86 | ||||
-rwxr-xr-x | sys/src/cmd/rc/win32.c | 560 |
27 files changed, 8351 insertions, 0 deletions
diff --git a/sys/src/cmd/rc/code.c b/sys/src/cmd/rc/code.c new file mode 100755 index 000000000..7aad38bc1 --- /dev/null +++ b/sys/src/cmd/rc/code.c @@ -0,0 +1,486 @@ +#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) ((codep!=ncode || morecode()), codebuf[codep].f = (x), codep++) +#define emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i = (x), codep++) +#define emits(x) ((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(); + void *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); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + 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); + if(havefork){ + p = emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + 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); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + 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); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + } + 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; + case RDWR: + emitf(Xrdwr); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt = t; + for(;t && t->type=='=';t = c2); + if(t){ /* var=value cmd */ + for(t = tt;t->type=='=';t = c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); /* push var for cmd */ + } + outcode(t, eflag); /* gen. code for cmd */ + for(t = tt; t->type == '='; t = c2) + emitf(Xunlocal); /* pop var */ + } + else{ /* var=value */ + for(t = tt;t;t = c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); /* set var permanently */ + } + } + t = tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + if(havefork){ + p = emiti(0); + q = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + q = emiti(0); + } + 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==Xrdwr + || 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); +} diff --git a/sys/src/cmd/rc/compiling.on.unix b/sys/src/cmd/rc/compiling.on.unix new file mode 100755 index 000000000..cd20c56c7 --- /dev/null +++ b/sys/src/cmd/rc/compiling.on.unix @@ -0,0 +1,1866 @@ +From geoff@collyer.net Fri Dec 19 01:21:40 EST 2003 +Received: from plan9.cs.bell-labs.com ([135.104.9.2]) by plan9; Fri Dec 19 01:21:39 EST 2003 +Received: from collyer.net ([63.192.14.226]) by plan9; Fri Dec 19 01:21:35 EST 2003 +Message-ID: <c790d8b1e06b3918ad2c7848a3ae0ec7@collyer.net> +subject: rc on unix, part 1 +From: Geoff Collyer <geoff@collyer.net> +Date: Thu, 18 Dec 2003 22:21:33 -0800 +To: presotto@plan9.bell-labs.com, rsc@plan9.bell-labs.com, geoff@collyer.net +MIME-Version: 1.0 +Content-Type: text/plain; charset="US-ASCII" +Content-Transfer-Encoding: 7bit + +I got /sys/src/cmd/rc to compile under APE (in preparation for moving it +to Unixes) with the following changed files. I cadged some include files +from rsc but had to edit lib9.h slightly. I'll send the include files +separately. I can't tell if it works yet, but it does complain about +/usr/lib/rcmain being absent when I start it. Oh, and I haven't yet +simulated the effect of the OCEXEC bit. + + +# To unbundle, run this file +echo mkfile +sed 's/^X//' >mkfile <<'!' +X</$objtype/mkfile + +TARG=rc +COMMONOFILES=\ +X code.$O\ +X exec.$O\ +X getflags.$O\ +X glob.$O\ +X here.$O\ +X io.$O\ +X lex.$O\ +X pcmd.$O\ +X pfnc.$O\ +X simple.$O\ +X subr.$O\ +X trap.$O\ +X tree.$O\ +X var.$O\ +X havefork.$O\ + +PLAN9OFILES=plan9.$O\ + +UNIXOFILES=unix.$O\ + +OFILES=$COMMONOFILES $UNIXOFILES y.tab.$O + +HFILES=rc.h\ +X x.tab.h\ +X io.h\ +X exec.h\ +X fns.h\ + +YFILES=syn.y + +BIN=/$objtype/bin + +UPDATE=\ +X mkfile\ +X $HFILES\ +X ${COMMONOFILES:%.$O=%.c}\ +X ${UNIXOFILES:%.$O=%.c}\ +X ${PLAN9OFILES:%.$O=%.c}\ +X $YFILES\ +X ${TARG:%=/386/bin/%}\ + +CC=pcc -c -B -I../include +LD=pcc + +X</sys/src/cmd/mkone + +x.tab.h: y.tab.h +X cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h + +clean:V: +X rm -f [$OS].out *.[$OS] [xy].tab.? y.debug $TARG + +regress: $O.out +X cd test +X mk + +unregress:V: +X for(test in test/*.test) rc $test >$test.out + +listing: +X pr mkfile $HFILES $FILES $FILES9 $FILESUNIX $YFILES|lp -du +! +echo simple.c +sed 's/^X//' >simple.c <<'!' +X/* +X * Maybe `simple' is a misnomer. +X */ +X#include "rc.h" +X#include "getflags.h" +X#include "exec.h" +X#include "io.h" +X#include "fns.h" +X/* +X * Search through the following code to see if we're just going to exit. +X */ +exitnext(void){ +X union code *c=&runq->code[runq->pc]; +X while(c->f==Xpopredir) c++; +X return c->f==Xexit; +X} + +void +XXsimple(void) +X{ +X word *a; +X thread *p = runq; +X var *v; +X struct builtin *bp; +X int pid; +X globlist(); +X a = runq->argv->words; +X if(a==0){ +X Xerror1("empty argument list"); +X return; +X } +X if(flag['x']) +X pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ +X v = gvlook(a->word); +X if(v->fn) +X execfunc(v); +X else{ +X if(strcmp(a->word, "builtin")==0){ +X if(count(a)==1){ +X pfmt(err, "builtin: empty argument list\n"); +X setstatus("empty arg list"); +X poplist(); +X return; +X } +X a = a->next; +X popword(); +X } +X for(bp = Builtin;bp->name;bp++) +X if(strcmp(a->word, bp->name)==0){ +X (*bp->fnc)(); +X return; +X } +X if(exitnext()){ +X /* fork and wait is redundant */ +X pushword("exec"); +X execexec(); +X Xexit(); +X } +X else{ +X flush(err); +X Updenv(); /* necessary so changes don't go out again */ +X if((pid = execforkexec()) < 0){ +X Xerror("try again"); +X return; +X } + +X /* interrupts don't get us out */ +X poplist(); +X while(Waitfor(pid, 1) < 0) +X ; +X } +X } +X} +struct word nullpath = { "", 0}; + +void +doredir(redir *rp) +X{ +X if(rp){ +X doredir(rp->next); +X switch(rp->type){ +X case ROPEN: +X if(rp->from!=rp->to){ +X Dup(rp->from, rp->to); +X close(rp->from); +X } +X break; +X case RDUP: +X Dup(rp->from, rp->to); +X break; +X case RCLOSE: +X close(rp->from); +X break; +X } +X } +X} + +word* +searchpath(char *w) +X{ +X word *path; +X if(strncmp(w, "/", 1)==0 +X || strncmp(w, "#", 1)==0 +X || strncmp(w, "./", 2)==0 +X || strncmp(w, "../", 3)==0 +X || (path = vlook("path")->val)==0) +X path=&nullpath; +X return path; +X} + +void +execexec(void) +X{ +X popword(); /* "exec" */ +X if(runq->argv->words==0){ +X Xerror1("empty argument list"); +X return; +X } +X doredir(runq->redir); +X Execute(runq->argv->words, searchpath(runq->argv->words->word)); +X poplist(); +X} + +void +execfunc(var *func) +X{ +X word *starval; +X popword(); +X starval = runq->argv->words; +X runq->argv->words = 0; +X poplist(); +X start(func->fn, func->pc, (struct var *)0); +X runq->local = newvar(strdup("*"), runq->local); +X runq->local->val = starval; +X runq->local->changed = 1; +X} + +int +dochdir(char *word) +X{ +X /* report to /dev/wdir if it exists and we're interactive */ +X static int wdirfd = -2; +X if(chdir(word)<0) return -1; +X if(flag['i']!=0){ +X if(wdirfd==-2) /* try only once */ +X /* TODO: arrange close-on-exec on Unix */ +X wdirfd = open("/dev/wdir", OWRITE|OCEXEC); +X if(wdirfd>=0) +X write(wdirfd, word, strlen(word)); +X } +X return 1; +X} + +void +execcd(void) +X{ +X word *a = runq->argv->words; +X word *cdpath; +X char dir[512]; +X setstatus("can't cd"); +X cdpath = vlook("cdpath")->val; +X switch(count(a)){ +X default: +X pfmt(err, "Usage: cd [directory]\n"); +X break; +X case 2: +X if(a->next->word[0]=='/' || cdpath==0) +X cdpath=&nullpath; +X for(;cdpath;cdpath = cdpath->next){ +X strcpy(dir, cdpath->word); +X if(dir[0]) +X strcat(dir, "/"); +X strcat(dir, a->next->word); +X if(dochdir(dir)>=0){ +X if(strlen(cdpath->word) +X && strcmp(cdpath->word, ".")!=0) +X pfmt(err, "%s\n", dir); +X setstatus(""); +X break; +X } +X } +X if(cdpath==0) +X pfmt(err, "Can't cd %s: %r\n", a->next->word); +X break; +X case 1: +X a = vlook("home")->val; +X if(count(a)>=1){ +X if(dochdir(a->word)>=0) +X setstatus(""); +X else +X pfmt(err, "Can't cd %s: %r\n", a->word); +X } +X else +X pfmt(err, "Can't cd -- $home empty\n"); +X break; +X } +X poplist(); +X} + +void +execexit(void) +X{ +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: exit [status]\nExiting anyway\n"); +X case 2: +X setstatus(runq->argv->words->next->word); +X case 1: Xexit(); +X } +X} + +void +execshift(void) +X{ +X int n; +X word *a; +X var *star; +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: shift [n]\n"); +X setstatus("shift usage"); +X poplist(); +X return; +X case 2: +X n = atoi(runq->argv->words->next->word); +X break; +X case 1: +X n = 1; +X break; +X } +X star = vlook("*"); +X for(;n && star->val;--n){ +X a = star->val->next; +X efree(star->val->word); +X efree((char *)star->val); +X star->val = a; +X star->changed = 1; +X } +X setstatus(""); +X poplist(); +X} + +int +octal(char *s) +X{ +X int n = 0; +X while(*s==' ' || *s=='\t' || *s=='\n') s++; +X while('0'<=*s && *s<='7') n = n*8+*s++-'0'; +X return n; +X} + +int +mapfd(int fd) +X{ +X redir *rp; +X for(rp = runq->redir;rp;rp = rp->next){ +X switch(rp->type){ +X case RCLOSE: +X if(rp->from==fd) +X fd=-1; +X break; +X case RDUP: +X case ROPEN: +X if(rp->to==fd) +X fd = rp->from; +X break; +X } +X } +X return fd; +X} +union code rdcmds[4]; + +void +execcmds(io *f) +X{ +X static int first = 1; +X if(first){ +X rdcmds[0].i = 1; +X rdcmds[1].f = Xrdcmds; +X rdcmds[2].f = Xreturn; +X first = 0; +X } +X start(rdcmds, 1, runq->local); +X runq->cmdfd = f; +X runq->iflast = 0; +X} + +void +execeval(void) +X{ +X char *cmdline, *s, *t; +X int len = 0; +X word *ap; +X if(count(runq->argv->words)<=1){ +X Xerror1("Usage: eval cmd ..."); +X return; +X } +X eflagok = 1; +X for(ap = runq->argv->words->next;ap;ap = ap->next) +X len+=1+strlen(ap->word); +X cmdline = emalloc(len); +X s = cmdline; +X for(ap = runq->argv->words->next;ap;ap = ap->next){ +X for(t = ap->word;*t;) *s++=*t++; +X *s++=' '; +X } +X s[-1]='\n'; +X poplist(); +X execcmds(opencore(cmdline, len)); +X efree(cmdline); +X} +union code dotcmds[14]; + +void +execdot(void) +X{ +X int iflag = 0; +X int fd; +X list *av; +X thread *p = runq; +X char *zero; +X static int first = 1; +X char file[512]; +X word *path; +X if(first){ +X dotcmds[0].i = 1; +X dotcmds[1].f = Xmark; +X dotcmds[2].f = Xword; +X dotcmds[3].s="0"; +X dotcmds[4].f = Xlocal; +X dotcmds[5].f = Xmark; +X dotcmds[6].f = Xword; +X dotcmds[7].s="*"; +X dotcmds[8].f = Xlocal; +X dotcmds[9].f = Xrdcmds; +X dotcmds[10].f = Xunlocal; +X dotcmds[11].f = Xunlocal; +X dotcmds[12].f = Xreturn; +X first = 0; +X } +X else +X eflagok = 1; +X popword(); +X if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ +X iflag = 1; +X popword(); +X } +X /* get input file */ +X if(p->argv->words==0){ +X Xerror1("Usage: . [-i] file [arg ...]"); +X return; +X } +X zero = strdup(p->argv->words->word); +X popword(); +X fd=-1; +X for(path = searchpath(zero);path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, zero); +X if((fd = open(file, 0))>=0) break; +X if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ +X fd = Dup1(0); +X if(fd>=0) +X break; +X } +X } +X if(fd<0){ +X pfmt(err, "%s: ", zero); +X setstatus("can't open"); +X Xerror(".: can't open"); +X return; +X } +X /* set up for a new command loop */ +X start(dotcmds, 1, (struct var *)0); +X pushredir(RCLOSE, fd, 0); +X runq->cmdfile = zero; +X runq->cmdfd = openfd(fd); +X runq->iflag = iflag; +X runq->iflast = 0; +X /* push $* value */ +X pushlist(); +X runq->argv->words = p->argv->words; +X /* free caller's copy of $* */ +X av = p->argv; +X p->argv = av->next; +X efree((char *)av); +X /* push $0 value */ +X pushlist(); +X pushword(zero); +X ndot++; +X} + +void +execflag(void) +X{ +X char *letter, *val; +X switch(count(runq->argv->words)){ +X case 2: +X setstatus(flag[runq->argv->words->next->word[0]]?"":"flag not set"); +X break; +X case 3: +X letter = runq->argv->words->next->word; +X val = runq->argv->words->next->next->word; +X if(strlen(letter)==1){ +X if(strcmp(val, "+")==0){ +X flag[letter[0]] = flagset; +X break; +X } +X if(strcmp(val, "-")==0){ +X flag[letter[0]] = 0; +X break; +X } +X } +X default: +X Xerror1("Usage: flag [letter] [+-]"); +X return; +X } +X poplist(); +X} + +void +execwhatis(void){ /* mildly wrong -- should fork before writing */ +X word *a, *b, *path; +X var *v; +X struct builtin *bp; +X char file[512]; +X struct io out[1]; +X int found, sep; +X a = runq->argv->words->next; +X if(a==0){ +X Xerror1("Usage: whatis name ..."); +X return; +X } +X setstatus(""); +X out->fd = mapfd(1); +X out->bufp = out->buf; +X out->ebuf = &out->buf[NBUF]; +X out->strp = 0; +X for(;a;a = a->next){ +X v = vlook(a->word); +X if(v->val){ +X pfmt(out, "%s=", a->word); +X if(v->val->next==0) +X pfmt(out, "%q\n", v->val->word); +X else{ +X sep='('; +X for(b = v->val;b && b->word;b = b->next){ +X pfmt(out, "%c%q", sep, b->word); +X sep=' '; +X } +X pfmt(out, ")\n"); +X } +X found = 1; +X } +X else +X found = 0; +X v = gvlook(a->word); +X if(v->fn) +X pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); +X else{ +X for(bp = Builtin;bp->name;bp++) +X if(strcmp(a->word, bp->name)==0){ +X pfmt(out, "builtin %s\n", a->word); +X break; +X } +X if(!bp->name){ +X for(path = searchpath(a->word);path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, a->word); +X if(Executable(file)){ +X pfmt(out, "%s\n", file); +X break; +X } +X } +X if(!path && !found){ +X pfmt(err, "%s: not found\n", a->word); +X setstatus("not found"); +X } +X } +X } +X } +X poplist(); +X flush(err); +X} + +void +execwait(void) +X{ +X switch(count(runq->argv->words)){ +X default: +X Xerror1("Usage: wait [pid]"); +X return; +X case 2: +X Waitfor(atoi(runq->argv->words->next->word), 0); +X break; +X case 1: +X Waitfor(-1, 0); +X break; +X } +X poplist(); +X} +! +echo havefork.c +sed 's/^X//' >havefork.c <<'!' +X#include "rc.h" +X#include "getflags.h" +X#include "exec.h" +X#include "io.h" +X#include "fns.h" + +int havefork = 1; + +void +XXasync(void) +X{ +X int null = open("/dev/null", 0); +X int pid; +X char npid[10]; +X if(null<0){ +X Xerror("Can't open /dev/null\n"); +X return; +X } +X#ifdef Unix +X pid = fork(); +X#else +X pid = rfork(RFFDG|RFPROC|RFNOTEG); +X#endif +X switch(pid){ +X case -1: +X close(null); +X Xerror("try again"); +X break; +X case 0: +X pushredir(ROPEN, null, 0); +X start(runq->code, runq->pc+1, runq->local); +X runq->ret = 0; +X break; +X default: +X close(null); +X runq->pc = runq->code[runq->pc].i; +X inttoascii(npid, pid); +X setvar("apid", newword(npid, (word *)0)); +X break; +X } +X} + +void +XXpipe(void) +X{ +X struct thread *p = runq; +X int pc = p->pc, forkid; +X int lfd = p->code[pc++].i; +X int rfd = p->code[pc++].i; +X int pfd[2]; +X if(pipe(pfd)<0){ +X Xerror("can't get pipe"); +X return; +X } +X switch(forkid = fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(p->code, pc+2, runq->local); +X runq->ret = 0; +X close(pfd[PRD]); +X pushredir(ROPEN, pfd[PWR], lfd); +X break; +X default: +X start(p->code, p->code[pc].i, runq->local); +X close(pfd[PWR]); +X pushredir(ROPEN, pfd[PRD], rfd); +X p->pc = p->code[pc+1].i; +X p->pid = forkid; +X break; +X } +X} + +X/* +X * Who should wait for the exit from the fork? +X */ +void +XXbackq(void) +X{ +X char wd[8193]; +X int c; +X char *s, *ewd=&wd[8192], *stop; +X struct io *f; +X var *ifs = vlook("ifs"); +X word *v, *nextv; +X int pfd[2]; +X int pid; +X stop = ifs->val?ifs->val->word:""; +X if(pipe(pfd)<0){ +X Xerror("can't make pipe"); +X return; +X } +X switch(pid = fork()){ +X case -1: +X Xerror("try again"); +X close(pfd[PRD]); +X close(pfd[PWR]); +X return; +X case 0: +X close(pfd[PRD]); +X start(runq->code, runq->pc+1, runq->local); +X pushredir(ROPEN, pfd[PWR], 1); +X return; +X default: +X close(pfd[PWR]); +X f = openfd(pfd[PRD]); +X s = wd; +X v = 0; +X while((c = rchr(f))!=EOF){ +X if(strchr(stop, c) || s==ewd){ +X if(s!=wd){ +X *s='\0'; +X v = newword(wd, v); +X s = wd; +X } +X } +X else *s++=c; +X } +X if(s!=wd){ +X *s='\0'; +X v = newword(wd, v); +X } +X closeio(f); +X Waitfor(pid, 0); +X /* v points to reversed arglist -- reverse it onto argv */ +X while(v){ +X nextv = v->next; +X v->next = runq->argv->words; +X runq->argv->words = v; +X v = nextv; +X } +X runq->pc = runq->code[runq->pc].i; +X return; +X } +X} + +void +XXpipefd(void) +X{ +X struct thread *p = runq; +X int pc = p->pc; +X char name[40]; +X int pfd[2]; +X int sidefd, mainfd; +X if(pipe(pfd)<0){ +X Xerror("can't get pipe"); +X return; +X } +X if(p->code[pc].i==READ){ +X sidefd = pfd[PWR]; +X mainfd = pfd[PRD]; +X } +X else{ +X sidefd = pfd[PRD]; +X mainfd = pfd[PWR]; +X } +X switch(fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(p->code, pc+2, runq->local); +X close(mainfd); +X pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); +X runq->ret = 0; +X break; +X default: +X close(sidefd); +X pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ +X strcpy(name, Fdprefix); +X inttoascii(name+strlen(name), mainfd); +X pushword(name); +X p->pc = p->code[pc+1].i; +X break; +X } +X} + +void +XXsubshell(void) +X{ +X int pid; +X switch(pid = fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(runq->code, runq->pc+1, runq->local); +X runq->ret = 0; +X break; +X default: +X Waitfor(pid, 1); +X runq->pc = runq->code[runq->pc].i; +X break; +X } +X} + +int +execforkexec(void) +X{ +X int pid; +X int n; +X char buf[ERRMAX]; + +X switch(pid = fork()){ +X case -1: +X return -1; +X case 0: +X pushword("exec"); +X execexec(); +X strcpy(buf, "can't exec: "); +X n = strlen(buf); +X errstr(buf+n, ERRMAX-n); +X Exit(buf); +X } +X return pid; +X} +! +echo rc.h +sed 's/^X//' >rc.h <<'!' +X/* +X * Plan9 is defined for plan 9 +X * V9 is defined for 9th edition +X * Sun is defined for sun-os +X * Please don't litter the code with ifdefs. The three below should be enough. +X */ +X#define Unix + +X#ifdef Plan9 +X#include <u.h> +X#include <libc.h> +X#define NSIG 32 +X#define SIGINT 2 +X#define SIGQUIT 3 +X#endif + +X#ifdef Unix +X#define _POSIX_SOURCE +X#define _BSD_EXTENSION + +X#include <stdlib.h> +X#include <stdarg.h> +X#include <string.h> +X#include <unistd.h> +X#include <fcntl.h> +X#include <lib9.h> +X#include <signal.h> +X#endif + +X#ifndef ERRMAX +X#define ERRMAX 128 +X#endif + +X#define YYMAXDEPTH 500 +X#ifndef PAREN +X#include "x.tab.h" +X#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{ +X int type; +X int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ +X char *str; +X int quoted; +X int iskw; +X tree *child[3]; +X tree *next; +X}; +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; +X/* +X * The first word of any code vector is a reference count. +X * Always create a new reference to a code vector by calling codecopy(.). +X * Always call codefree(.) when deleting a reference. +X */ +union code{ +X void (*f)(void); +X int i; +X char *s; +X}; +char *promptstr; +int doprompt; +X#define NTOK 8192 +char tok[NTOK]; +X#define APPEND 1 +X#define WRITE 2 +X#define READ 3 +X#define HERE 4 +X#define DUPFD 5 +X#define CLOSE 6 +struct var{ +X char *name; /* ascii name */ +X word *val; /* value */ +X int changed; +X code *fn; /* pointer to function's code vector */ +X int fnchanged; +X int pc; /* pc of start of function */ +X var *next; /* next on hash or local list */ +X}; +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); +X#define NVAR 521 +var *gvar[NVAR]; /* hash for globals */ +X#define new(type) ((type *)emalloc(sizeof(type))) +char *emalloc(long); +void *Malloc(ulong); +void efree(char*); +X#define NOFILE 128 /* should come from <param.h> */ +struct here{ +X tree *tag; +X char *name; +X struct here *next; +X}; +int mypid; +X/* +X * Glob character escape in strings: +X * In a string, GLOB must be followed by *?[ or GLOB. +X * GLOB* matches any string +X * GLOB? matches any single character +X * GLOB[...] matches anything in the brackets +X * GLOBGLOB matches GLOB +X */ +X#define GLOB ((char)0x01) +X/* +X * onebyte(c), twobyte(c), threebyte(c) +X * Is c the first character of a one- two- or three-byte utf sequence? +X */ +X#define onebyte(c) ((c&0x80)==0x00) +X#define twobyte(c) ((c&0xe0)==0xc0) +X#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? */ +X/* +X * Which fds are the reading/writing end of a pipe? +X * Unfortunately, this can vary from system to system. +X * 9th edition Unix doesn't care, the following defines +X * work on plan 9. +X */ +X#define PRD 0 +X#define PWR 1 +char Rcmain[], Fdprefix[]; +X#define register +X/* +X * How many dot commands have we executed? +X * Used to ensure that -v flag doesn't print rcmain. +X */ +int ndot; +char *getstatus(void); +int lastc; +int lastword; +! +echo unix.c +sed 's/^X//' >unix.c <<'!' +X/* +X * Unix versions of system-specific functions +X * By convention, exported routines herein have names beginning with an +X * upper case letter. +X */ +X#include "rc.h" +X#include "io.h" +X#include "exec.h" +X#include "getflags.h" +X#include <errno.h> + +char Rcmain[]="/usr/lib/rcmain"; +char Fdprefix[]="/dev/fd/"; + +void execfinit(void); + +struct builtin Builtin[] = { +X "cd", execcd, +X "whatis", execwhatis, +X "eval", execeval, +X "exec", execexec, /* but with popword first */ +X "exit", execexit, +X "shift", execshift, +X "wait", execwait, +X "umask", execumask, +X ".", execdot, +X "finit", execfinit, +X "flag", execflag, +X 0 +X}; +X#define SEP '\1' +char **environp; + +struct word* +enval(s) +register char *s; +X{ +X char *t, c; +X struct word *v; +X for(t = s;*t && *t!=SEP;t++); +X c=*t; +X *t='\0'; +X v = newword(s, c=='\0'?(struct word *)0:enval(t+1)); +X *t = c; +X return v; +X} + +void +Vinit(void) +X{ +X extern char **environ; +X char *s; +X char **env = environ; +X environp = env; +X for(;*env;env++){ +X for(s=*env;*s && *s!='(' && *s!='=';s++); +X switch(*s){ +X case '\0': +X pfmt(err, "environment %q?\n", *env); +X break; +X case '=': +X *s='\0'; +X setvar(*env, enval(s+1)); +X *s='='; +X break; +X case '(': /* ignore functions for now */ +X break; +X } +X } +X} + +char **envp; + +void +XXrdfn(void) +X{ +X char *s; +X int len; +X for(;*envp;envp++){ +X for(s=*envp;*s && *s!='(' && *s!='=';s++); +X switch(*s){ +X case '\0': +X pfmt(err, "environment %q?\n", *envp); +X break; +X case '=': /* ignore variables */ +X break; +X case '(': /* Bourne again */ +X s=*envp+3; +X envp++; +X len = strlen(s); +X s[len]='\n'; +X execcmds(opencore(s, len+1)); +X s[len]='\0'; +X return; +X } +X } +X Xreturn(); +X} + +union code rdfns[4]; + +void +execfinit(void) +X{ +X static int first = 1; +X if(first){ +X rdfns[0].i = 1; +X rdfns[1].f = Xrdfn; +X rdfns[2].f = Xjump; +X rdfns[3].i = 1; +X first = 0; +X } +X Xpopm(); +X envp = environp; +X start(rdfns, 1, runq->local); +X} + +int +cmpenv(const void *aa, const void *ab) +X{ +X char **a = aa, **b = ab; + +X return strcmp(*a, *b); +X} + +char ** +mkenv(void) +X{ +X char **env, **ep, *p, *q; +X struct var **h, *v; +X struct word *a; +X int nvar = 0, nchr = 0, sep; + +X /* +X * Slightly kludgy loops look at locals then globals. +X * locals no longer exist - geoff +X */ +X for(h = gvar-1; h != &gvar[NVAR]; h++) +X for(v = h >= gvar? *h: runq->local; v ;v = v->next){ +X if((v==vlook(v->name)) && v->val){ +X nvar++; +X nchr+=strlen(v->name)+1; +X for(a = v->val;a;a = a->next) +X nchr+=strlen(a->word)+1; +X } +X if(v->fn){ +X nvar++; +X nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; +X } +X } +X env = (char **)emalloc((nvar+1)*sizeof(char *)+nchr); +X ep = env; +X p = (char *)&env[nvar+1]; +X for(h = gvar-1; h != &gvar[NVAR]; h++) +X for(v = h >= gvar? *h: runq->local;v;v = v->next){ +X if((v==vlook(v->name)) && v->val){ +X *ep++=p; +X q = v->name; +X while(*q) *p++=*q++; +X sep='='; +X for(a = v->val;a;a = a->next){ +X *p++=sep; +X sep = SEP; +X q = a->word; +X while(*q) *p++=*q++; +X } +X *p++='\0'; +X } +X if(v->fn){ +X *ep++=p; +X *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ +X *p++='f'; *p++='n'; *p++=' '; +X q = v->name; +X while(*q) *p++=*q++; +X *p++=' '; +X q = v->fn[v->pc-1].s; +X while(*q) *p++=*q++; +X *p++='\0'; +X } +X } +X *ep = 0; +X qsort((void *)env, nvar, sizeof ep[0], cmpenv); +X return env; +X} +char *sigmsg[] = { +X/* 0 normal */ 0, +X/* 1 SIGHUP */ "Hangup", +X/* 2 SIGINT */ 0, +X/* 3 SIGQUIT */ "Quit", +X/* 4 SIGILL */ "Illegal instruction", +X/* 5 SIGTRAP */ "Trace/BPT trap", +X/* 6 SIGIOT */ "abort", +X/* 7 SIGEMT */ "EMT trap", +X/* 8 SIGFPE */ "Floating exception", +X/* 9 SIGKILL */ "Killed", +X/* 10 SIGBUS */ "Bus error", +X/* 11 SIGSEGV */ "Memory fault", +X/* 12 SIGSYS */ "Bad system call", +X/* 13 SIGPIPE */ 0, +X/* 14 SIGALRM */ "Alarm call", +X/* 15 SIGTERM */ "Terminated", +X/* 16 unused */ "signal 16", +X/* 17 SIGSTOP */ "Process stopped", +X/* 18 unused */ "signal 18", +X/* 19 SIGCONT */ "Process continued", +X/* 20 SIGCHLD */ "Child death", +X}; + +void +Waitfor(int pid, int persist) +X{ +X int wpid, sig; +X struct thread *p; +X int wstat; +X char wstatstr[12]; + +X for(;;){ +X errno = 0; +X wpid = wait(&wstat); +X if(errno==EINTR && persist) +X continue; +X if(wpid==-1) +X break; +X sig = wstat&0177; +X if(sig==0177){ +X pfmt(err, "trace: "); +X sig = (wstat>>8)&0177; +X } +X if(sig>(sizeof sigmsg/sizeof sigmsg[0]) || sigmsg[sig]){ +X if(pid!=wpid) +X pfmt(err, "%d: ", wpid); +X if(sig<=(sizeof sigmsg/sizeof sigmsg[0])) +X pfmt(err, "%s", sigmsg[sig]); +X else if(sig==0177) pfmt(err, "stopped by ptrace"); +X else pfmt(err, "signal %d", sig); +X if(wstat&0200)pfmt(err, " -- core dumped"); +X pfmt(err, "\n"); +X } +X wstat = sig?sig+1000:(wstat>>8)&0xFF; +X if(wpid==pid){ +X inttoascii(wstatstr, wstat); +X setstatus(wstatstr); +X break; +X } +X else{ +X for(p = runq->ret;p;p = p->ret) +X if(p->pid==wpid){ +X p->pid=-1; +X inttoascii(p->status, wstat); +X break; +X } +X } +X } +X} + +char ** +mkargv(a) +register struct word *a; +X{ +X char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); +X char **argp = argv+1; /* leave one at front for runcoms */ + +X for(;a;a = a->next) +X *argp++=a->word; +X *argp = 0; +X return argv; +X} + +void +Updenv(void) +X{ +X} + +void +Execute(struct word *args, struct word *path) +X{ +X char *msg="not found"; +X#ifdef ETXTBSY +X int txtbusy = 0; +X#endif +X char **env = mkenv(); +X char **argv = mkargv(args); +X char file[512]; + +X for(;path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, argv[1]); +X#ifdef ETXTBSY +ReExec: +X#endif +X execve(file, argv+1, env); +X switch(errno){ +X case ENOEXEC: +X pfmt(err, "%s: Bourne again\n", argv[1]); +X argv[0]="sh"; +X argv[1] = strdup(file); +X execve("/bin/sh", argv, env); +X goto Bad; +X#ifdef ETXTBSY +X case ETXTBSY: +X if(++txtbusy!=5){ +X sleep(txtbusy); +X goto ReExec; +X } +X msg="text busy"; goto Bad; +X#endif +X case EACCES: +X msg="no access"; +X break; +X case ENOMEM: +X msg="not enough memory"; goto Bad; +X case E2BIG: +X msg="too big"; goto Bad; +X } +X } +Bad: +X pfmt(err, "%s: %s\n", argv[1], msg); +X efree((char *)env); +X efree((char *)argv); +X} + +X#define NDIR 14 /* should get this from param.h */ + +Globsize(p) +register char *p; +X{ +X int isglob = 0, globlen = NDIR+1; +X for(;*p;p++){ +X if(*p==GLOB){ +X p++; +X if(*p!=GLOB) +X isglob++; +X globlen+=*p=='*'?NDIR:1; +X } +X else +X globlen++; +X } +X return isglob?globlen:0; +X} + +X#include <sys/types.h> +X#include <dirent.h> + +X#define NDIRLIST 50 + +DIR *dirlist[NDIRLIST]; + +Opendir(name) +char *name; +X{ +X DIR **dp; +X for(dp = dirlist;dp!=&dirlist[NDIRLIST];dp++) +X if(*dp==0){ +X *dp = opendir(name); +X return *dp?dp-dirlist:-1; +X } +X return -1; +X} + +int +Readdir(int f, char *p, int /* onlydirs */ ) +X{ +X struct dirent *dp = readdir(dirlist[f]); + +X if(dp==0) +X return 0; +X strcpy(p, dp->d_name); +X return 1; +X} + +void +Closedir(int f) +X{ +X closedir(dirlist[f]); +X dirlist[f] = 0; +X} + +char *Signame[] = { +X "sigexit", "sighup", "sigint", "sigquit", +X "sigill", "sigtrap", "sigiot", "sigemt", +X "sigfpe", "sigkill", "sigbus", "sigsegv", +X "sigsys", "sigpipe", "sigalrm", "sigterm", +X "sig16", "sigstop", "sigtstp", "sigcont", +X "sigchld", "sigttin", "sigttou", "sigtint", +X "sigxcpu", "sigxfsz", "sig26", "sig27", +X "sig28", "sig29", "sig30", "sig31", +X 0, +X}; + +void +gettrap(int sig) +X{ +X signal(sig, gettrap); +X trap[sig]++; +X ntrap++; +X if(ntrap>=NSIG){ +X pfmt(err, "rc: Too many traps (trap %d), dumping core\n", sig); +X signal(SIGABRT, (void (*)())0); +X kill(getpid(), SIGABRT); +X } +X} + +void +Trapinit(void) +X{ +X int i; +X void (*sig)(); + +X if(1 || flag['d']){ /* wrong!!! */ +X sig = signal(SIGINT, gettrap); +X if(sig==SIG_IGN) +X signal(SIGINT, SIG_IGN); +X } +X else{ +X for(i = 1;i<=NSIG;i++) if(i!=SIGCHLD){ +X sig = signal(i, gettrap); +X if(sig==SIG_IGN) +X signal(i, SIG_IGN); +X } +X } +X} + +Unlink(name) +char *name; +X{ +X return unlink(name); +X} +Write(fd, buf, cnt) +char *buf; +X{ +X return write(fd, buf, cnt); +X} +Read(fd, buf, cnt) +char *buf; +X{ +X return read(fd, buf, cnt); +X} +Seek(fd, cnt, whence) +long cnt; +X{ +X return lseek(fd, cnt, whence); +X} +Executable(file) +char *file; +X{ +X return(access(file, 01)==0); +X} +Creat(file) +char *file; +X{ +X return creat(file, 0666); +X} +Dup(a, b){ +X return dup2(a, b); +X} +Dup1(a){ +X return dup(a); +X} +X/* +X * Wrong: should go through components of a|b|c and return the maximum. +X */ +void +Exit(char *stat) +X{ +X int n = 0; + +X while(*stat){ +X if(*stat!='|'){ +X if(*stat<'0' || '9'<*stat) +X exit(1); +X else n = n*10+*stat-'0'; +X } +X stat++; +X } +X exit(n); +X} +Eintr(){ +X return errno==EINTR; +X} + +void +Noerror() +X{ +X errno = 0; +X} +Isatty(fd){ +X return isatty(fd); +X} + +void +Abort() +X{ +X abort(); +X} + +void +execumask(void) /* wrong -- should fork before writing */ +X{ +X int m; +X struct io out[1]; +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: umask [umask]\n"); +X setstatus("umask usage"); +X poplist(); +X return; +X case 2: +X umask(octal(runq->argv->words->next->word)); +X break; +X case 1: +X umask(m = umask(0)); +X out->fd = mapfd(1); +X out->bufp = out->buf; +X out->ebuf=&out->buf[NBUF]; +X out->strp = 0; +X pfmt(out, "%o\n", m); +X break; +X } +X setstatus(""); +X poplist(); +X} + +void +Memcpy(a, b, n) +char *a, *b; +X{ +X memmove(a, b, n); +X} + +void* +Malloc(unsigned long n) +X{ +X return (void *)malloc(n); +X} + +void +errstr(char *buf, int len) +X{ +X strncpy(buf, strerror(errno), len); +X} +! + +From geoff@collyer.net Fri Dec 19 01:23:26 EST 2003 +Received: from plan9.cs.bell-labs.com ([135.104.9.2]) by plan9; Fri Dec 19 01:23:25 EST 2003 +Received: from collyer.net ([63.192.14.226]) by plan9; Fri Dec 19 01:23:22 EST 2003 +Message-ID: <0b5ea130198a21a49139759d00d69939@collyer.net> +subject: rc on unix, part 2 +From: Geoff Collyer <geoff@collyer.net> +Date: Thu, 18 Dec 2003 22:23:21 -0800 +To: presotto@plan9.bell-labs.com, rsc@plan9.bell-labs.com, geoff@collyer.net +MIME-Version: 1.0 +Content-Type: text/plain; charset="US-ASCII" +Content-Transfer-Encoding: 7bit + +These are the include files I used to emulate plan 9's include +files on Unix (APE). + + +# To unbundle, run this file +mkdir include +echo include/bio.h +sed 's/^X//' >include/bio.h <<'!' +X#ifndef _BIOH_ +X#define _BIOH_ 1 + +X#include <sys/types.h> /* for off_t */ +X#include <fcntl.h> /* for O_RDONLY, O_WRONLY */ + +typedef struct Biobuf Biobuf; + +enum +X{ +X Bsize = 8*1024, +X Bungetsize = 4, /* space for ungetc */ +X Bmagic = 0x314159, +X Beof = -1, +X Bbad = -2, + +X Binactive = 0, /* states */ +X Bractive, +X Bwactive, +X Bracteof, + +X Bend +X}; + +struct Biobuf +X{ +X int icount; /* neg num of bytes at eob */ +X int ocount; /* num of bytes at bob */ +X int rdline; /* num of bytes after rdline */ +X int runesize; /* num of bytes of last getrune */ +X int state; /* r/w/inactive */ +X int fid; /* open file */ +X int flag; /* magic if malloc'ed */ +X off_t offset; /* offset of buffer in file */ +X int bsize; /* size of buffer */ +X unsigned char* bbuf; /* pointer to beginning of buffer */ +X unsigned char* ebuf; /* pointer to end of buffer */ +X unsigned char* gbuf; /* pointer to good data in buf */ +X unsigned char b[Bungetsize+Bsize]; +X}; + +X#define BGETC(bp)\ +X ((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp))) +X#define BPUTC(bp,c)\ +X ((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c))) +X#define BOFFSET(bp)\ +X (((bp)->state==Bractive)?\ +X (bp)->offset + (bp)->icount:\ +X (((bp)->state==Bwactive)?\ +X (bp)->offset + ((bp)->bsize + (bp)->ocount):\ +X -1)) +X#define BLINELEN(bp)\ +X (bp)->rdline +X#define BFILDES(bp)\ +X (bp)->fid + +int Bbuffered(Biobuf*); +int Bfildes(Biobuf*); +int Bflush(Biobuf*); +int Bgetc(Biobuf*); +int Bgetd(Biobuf*, double*); +int Binit(Biobuf*, int, int); +int Binits(Biobuf*, int, int, unsigned char*, int); +int Blinelen(Biobuf*); +off_t Boffset(Biobuf*); +Biobuf* Bopen(char*, int); +int Bprint(Biobuf*, char*, ...); +int Bputc(Biobuf*, int); +void* Brdline(Biobuf*, int); +long Bread(Biobuf*, void*, long); +off_t Bseek(Biobuf*, off_t, int); +int Bterm(Biobuf*); +int Bungetc(Biobuf*); +long Bwrite(Biobuf*, void*, long); + +long Bgetrune(Biobuf*); +int Bputrune(Biobuf*, long); +int Bungetrune(Biobuf*); + +X#endif +! +echo include/fmt.h +sed 's/^X//' >include/fmt.h <<'!' + +X/* +X * The authors of this software are Rob Pike and Ken Thompson. +X * Copyright (c) 2002 by Lucent Technologies. +X * Permission to use, copy, modify, and distribute this software for any +X * purpose without fee is hereby granted, provided that this entire notice +X * is included in all copies of any software which is or includes a copy +X * or modification of this software and in all copies of the supporting +X * documentation for such software. +X * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +X * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY +X * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +X * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +X */ + +X#ifndef _FMTH_ +X#define _FMTH_ 1 + +X#include <stdarg.h> + +X#ifndef _UTFH_ +X#include <utf.h> +X#endif + +typedef struct Fmt Fmt; +struct Fmt{ +X unsigned char runes; /* output buffer is runes or chars? */ +X void *start; /* of buffer */ +X void *to; /* current place in the buffer */ +X void *stop; /* end of the buffer; overwritten if flush fails */ +X int (*flush)(Fmt *); /* called when to == stop */ +X void *farg; /* to make flush a closure */ +X int nfmt; /* num chars formatted so far */ +X va_list args; /* args passed to dofmt */ +X int r; /* % format Rune */ +X int width; +X int prec; +X unsigned long flags; +X}; + +enum{ +X FmtWidth = 1, +X FmtLeft = FmtWidth << 1, +X FmtPrec = FmtLeft << 1, +X FmtSharp = FmtPrec << 1, +X FmtSpace = FmtSharp << 1, +X FmtSign = FmtSpace << 1, +X FmtZero = FmtSign << 1, +X FmtUnsigned = FmtZero << 1, +X FmtShort = FmtUnsigned << 1, +X FmtLong = FmtShort << 1, +X FmtVLong = FmtLong << 1, +X FmtComma = FmtVLong << 1, +X FmtByte = FmtComma << 1, +X FmtLDouble = FmtByte << 1, + +X FmtFlag = FmtLDouble << 1 +X}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); + +extern int quotestrfmt(Fmt *f); +extern void quotefmtinstall(void); +extern int (*fmtdoquote)(int); + + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); + +extern double fmtstrtod(const char *, char **); +extern double fmtcharstod(int(*)(void*), void*); + +X#endif +! +echo include/lib9.h +sed 's/^X//' >include/lib9.h <<'!' +X#include <string.h> +X#include "utf.h" + +X#define nil ((void*)0) + +X#define uchar _fmtuchar +X#define ushort _fmtushort +X#define uint _fmtuint +X#define ulong _fmtulong +X#define vlong _fmtvlong +X#define uvlong _fmtuvlong + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +X#define OREAD O_RDONLY +X#define OWRITE O_WRONLY +X#define ORDWR O_RDWR +X#define OCEXEC 0 +! +echo include/regexp9.h +sed 's/^X//' >include/regexp9.h <<'!' +X#ifndef _REGEXP9H_ + +X#define _REGEXP9H_ 1 +X#include <utf.h> + +typedef struct Resub Resub; +typedef struct Reclass Reclass; +typedef struct Reinst Reinst; +typedef struct Reprog Reprog; + +X/* +X * Sub expression matches +X */ +struct Resub{ +X union +X { +X char *sp; +X Rune *rsp; +X }s; +X union +X { +X char *ep; +X Rune *rep; +X }e; +X}; + +X/* +X * character class, each pair of rune's defines a range +X */ +struct Reclass{ +X Rune *end; +X Rune spans[64]; +X}; + +X/* +X * Machine instructions +X */ +struct Reinst{ +X int type; +X union { +X Reclass *cp; /* class pointer */ +X Rune r; /* character */ +X int subid; /* sub-expression id for RBRA and LBRA */ +X Reinst *right; /* right child of OR */ +X }u1; +X union { /* regexp relies on these two being in the same union */ +X Reinst *left; /* left child of OR */ +X Reinst *next; /* next instruction for CAT & LBRA */ +X }u2; +X}; + +X/* +X * Reprogram definition +X */ +struct Reprog{ +X Reinst *startinst; /* start pc */ +X Reclass class[16]; /* .data */ +X Reinst firstinst[5]; /* .text */ +X}; + +extern Reprog *regcomp(char*); +extern Reprog *regcomplit(char*); +extern Reprog *regcompnl(char*); +extern void regerror(char*); +extern int regexec(Reprog*, char*, Resub*, int); +extern void regsub(char*, char*, int, Resub*, int); + +extern int rregexec(Reprog*, Rune*, Resub*, int); +extern void rregsub(Rune*, Rune*, Resub*, int); + +X#endif +! +echo include/utf.h +sed 's/^X//' >include/utf.h <<'!' +X#ifndef _UTFH_ +X#define _UTFH_ 1 + +typedef unsigned short Rune; /* 16 bits */ + +enum +X{ +X UTFmax = 3, /* maximum bytes per rune */ +X Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ +X Runeself = 0x80, /* rune and UTF sequences are the same (<) */ +X Runeerror = 0x80, /* decoding error in UTF */ +X}; + +X/* +X * rune routines +X */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +X#endif +! + diff --git a/sys/src/cmd/rc/exec.c b/sys/src/cmd/rc/exec.c new file mode 100755 index 000000000..c0fb8593f --- /dev/null +++ b/sys/src/cmd/rc/exec.c @@ -0,0 +1,996 @@ +#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 + */ + +void +main(int argc, char *argv[]) +{ + code bootstrap[17]; + char num[12], *rcmain; + int i; + argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1); + if(argc==-1) + usage("[file [arg ...]]"); + if(argv[0][0]=='-') + flag['l'] = flagset; + if(flag['I']) + flag['i'] = 0; + else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset; + rcmain = flag['m']?flag['m'][0]:Rcmain; + err = openfd(2); + kinit(); + Trapinit(); + Vinit(); + inttoascii(num, mypid = getpid()); + setvar("pid", newword(num, (word *)0)); + setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0) + :(word *)0); + setvar("rcname", newword(argv[0], (word *)0)); + i = 0; + bootstrap[i++].i = 1; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xassign; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xdol; + bootstrap[i++].f = Xword; + bootstrap[i++].s = rcmain; + bootstrap[i++].f = Xword; + bootstrap[i++].s="."; + bootstrap[i++].f = Xsimple; + bootstrap[i++].f = Xexit; + bootstrap[i].i = 0; + start(bootstrap, 1, (var *)0); + /* 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 + * 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((char *)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* +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); + poplist(); + globlist(); + runq->local->val = runq->argv->words; + runq->local->changed = 1; + runq->argv->words = 0; + poplist(); +} + +void +Xunlocal(void) +{ + var *v = runq->local, *hid; + if(v==0) + panic("Xunlocal: no locals!", 0); + runq->local = v->next; + hid = vlook(v->name); + hid->changed = 1; + 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; + globlist(); + 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(); +} diff --git a/sys/src/cmd/rc/exec.h b/sys/src/cmd/rc/exec.h new file mode 100755 index 000000000..06d2991f5 --- /dev/null +++ b/sys/src/cmd/rc/exec.h @@ -0,0 +1,76 @@ +/* + * 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 Xrdwr(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 */ +struct builtin{ + char *name; + void (*fnc)(void); +}; +extern struct builtin Builtin[]; +int eflagok; /* kludge flag so that -e doesn't exit in startup */ +int havefork; + +void execcd(void), execwhatis(void), execeval(void), execexec(void); +int execforkexec(void); +void execexit(void), execshift(void); +void execwait(void), execumask(void), execdot(void), execflag(void); +void execfunc(var*), execcmds(io *); diff --git a/sys/src/cmd/rc/fns.h b/sys/src/cmd/rc/fns.h new file mode 100755 index 000000000..1559d9176 --- /dev/null +++ b/sys/src/cmd/rc/fns.h @@ -0,0 +1,67 @@ +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 ForkExecute(char*, char**, int, int, int); +int Globsize(char*); +int Isatty(int); +void Memcpy(void*, void*, long); +void Noerror(void); +int Opendir(char*); +long Read(int, void*, long); +int Readdir(int, void*, int); +long Seek(int, long, long); +void Trapinit(void); +void Unlink(char*); +void Updenv(void); +void Vinit(void); +int Waitfor(int, int); +long Write(int, void*, long); +void addwaitpid(int); +int advance(void); +int back(int); +void cleanhere(char*); +void codefree(code*); +int compile(tree*); +char * list2str(word*); +int count(word*); +void deglob(void*); +void delwaitpid(int); +void dotrap(void); +void freenodes(void); +void freewords(word*); +void globlist(void); +int havewaitpid(int); +int idchr(int); +void inttoascii(char*, long); +void kinit(void); +int mapfd(int); +int match(void*, void*, int); +int matchfn(void*, void*); +char** mkargv(word*); +void clearwaitpids(void); +void panic(char*, int); +void pathinit(void); +void poplist(void); +void popword(void); +void pprompt(void); +void pushlist(void); +void pushredir(int, int, int); +void pushword(char*); +void readhere(void); +word* searchpath(char*); +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); diff --git a/sys/src/cmd/rc/getflags.c b/sys/src/cmd/rc/getflags.c new file mode 100755 index 000000000..3c2f9adeb --- /dev/null +++ b/sys/src/cmd/rc/getflags.c @@ -0,0 +1,233 @@ +#include "rc.h" +#include "getflags.h" +#include "fns.h" +char *flagset[] = {"<flag>"}; +char **flag[NFLAG]; +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; + int i, j, c, count; + flagarg = flags; + if(cmdname==0) + cmdname = argv[0]; + + i = 1; + while(i!=argc){ + if(argv[i][0] != '-' || argv[i][1] == '\0'){ + if(stop) /* always true in rc */ + 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-i<count){ + reason = FEWARGS; + badflag = c; + return -1; + } + reverse(argv+i, argv+argc); + reverse(argv+i, argv+argc-count); + reverse(argv+argc-count+1, argv+argc); + argc-=count; + flag[c] = argv+argc+1; + flag[c][0] = s; + s=""; + } + } + } + return argc; +} + +static void +reverse(char **p, char **q) +{ + char *t; + for(;p<q;p++,--q){ t=*p; *p=*q; *q = t; } +} + +static int +scanflag(int c, char *f) +{ + int fc, count; + if(0<=c && c<NFLAG) + while(*f){ + if(*f==' '){ + f++; + continue; + } + fc=*f++; + if(*f==':'){ + f++; + if(*f<'0' || '9'<*f){ reason = FLAGSYN; return -1; } + count = 0; + while('0'<=*f && *f<='9') count = count*10+*f++-'0'; + } + else + count = 0; + if(*f=='['){ + do{ + f++; + if(*f=='\0'){ reason = FLAGSYN; return -1; } + }while(*f!=']'); + f++; + } + if(c==fc) + return count; + } + reason = BADFLAG; + badflag = c; + return -1; +} + +void +usage(char *tail) +{ + char *s, *t, c; + int count, nflag = 0; + switch(reason){ + case RESET: + errs("Flag -"); + errc(badflag); + errs(": set twice\n"); + break; + case FEWARGS: + errs("Flag -"); + errc(badflag); + errs(": too few arguments\n"); + break; + case FLAGSYN: + errs("Bad argument to getflags!\n"); + break; + case BADFLAG: + errs("Illegal flag -"); + errc(badflag); + errc('\n'); + break; + } + errs("Usage: "); + errs(cmdname); + for(s = flagarg;*s;){ + c=*s; + if(*s++==' ') + continue; + if(*s==':'){ + s++; + count = 0; + while('0'<=*s && *s<='9') count = count*10+*s++-'0'; + } + else count = 0; + if(count==0){ + if(nflag==0) + errs(" [-"); + nflag++; + errc(c); + } + if(*s=='['){ + s++; + while(*s!=']' && *s!='\0') s++; + if(*s==']') + s++; + } + } + if(nflag) + errs("]"); + for(s = flagarg;*s;){ + c=*s; + if(*s++==' ') + continue; + if(*s==':'){ + s++; + count = 0; + while('0'<=*s && *s<='9') count = count*10+*s++-'0'; + } + else count = 0; + if(count!=0){ + errs(" [-"); + errc(c); + if(*s=='['){ + s++; + t = s; + while(*s!=']' && *s!='\0') s++; + errs(" "); + errn(t, s-t); + if(*s==']') + s++; + } + else + while(count--) errs(" arg"); + errs("]"); + } + else if(*s=='['){ + s++; + while(*s!=']' && *s!='\0') s++; + if(*s==']') + s++; + } + } + if(tail){ + errs(" "); + errs(tail); + } + errs("\n"); + Exit("bad flags"); +} + +static void +errn(char *s, int count) +{ + while(count){ errc(*s++); --count; } +} + +static void +errs(char *s) +{ + while(*s) errc(*s++); +} +#define NBUF 80 +static char buf[NBUF], *bufp = buf; + +static void +errc(int c) +{ + *bufp++=c; + if(bufp==&buf[NBUF] || c=='\n'){ + Write(2, buf, bufp-buf); + bufp = buf; + } +} diff --git a/sys/src/cmd/rc/getflags.h b/sys/src/cmd/rc/getflags.h new file mode 100755 index 000000000..84c4d8ee1 --- /dev/null +++ b/sys/src/cmd/rc/getflags.h @@ -0,0 +1,7 @@ +#define NFLAG 128 + +extern char **flag[NFLAG]; +extern char *cmdname; +extern char *flagset[]; + +int getflags(int, char*[], char*, int); diff --git a/sys/src/cmd/rc/glob.c b/sys/src/cmd/rc/glob.c new file mode 100755 index 000000000..1c4983e40 --- /dev/null +++ b/sys/src/cmd/rc/glob.c @@ -0,0 +1,264 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +char *globname; +struct word *globv; +/* + * delete all the GLOB marks from s, in place + */ + +void +deglob(void *as) +{ + char *s = as; + char *t = s; + do{ + if(*t==GLOB) + t++; + *s++=*t; + }while(*t++); +} + +int +globcmp(const void *s, const void *t) +{ + return strcmp(*(char**)s, *(char**)t); +} + +void +globsort(word *left, word *right) +{ + char **list; + word *a; + int n = 0; + for(a = left;a!=right;a = a->next) n++; + list = (char **)emalloc(n*sizeof(char *)); + for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word; + qsort((void *)list, n, sizeof(void *), 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(uchar *p, uchar *namep) +{ + uchar *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, *newp=='/')){ + 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(void *ap) +{ + uchar *p = ap; + word *svglobv = globv; + int globlen = Globsize(ap); + + if(!globlen){ + deglob(p); + globv = newword((char *)p, globv); + return; + } + globname = emalloc(globlen); + globname[0]='\0'; + globdir(p, (uchar *)globname); + efree(globname); + if(svglobv==globv){ + deglob(p); + globv = newword((char *)p, globv); + } + else + globsort(globv, svglobv); +} +/* + * Do p and q point at equal utf codes + */ + +int +equtf(uchar *p, uchar *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! + */ + +uchar* +nextutf(uchar *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(uchar *p) +{ + int u = *p; + + 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(void *as, void *ap) +{ + uchar *s = as, *p = ap; + + if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') + return 0; + return match(s, p, '/'); +} + +int +match(void *as, void *ap, int stop) +{ + int compl, hit, lo, hi, t, c; + uchar *s = as, *p = ap; + + 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(hi<lo){ t = lo; lo = hi; hi = t; } + } + if(lo<=c && c<=hi) + hit = 1; + } + if(compl) + hit=!hit; + if(!hit) + return 0; + break; + } + } + return *s=='\0'; +} + +void +globlist1(word *gl) +{ + if(gl){ + globlist1(gl->next); + 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; + } +} diff --git a/sys/src/cmd/rc/havefork.c b/sys/src/cmd/rc/havefork.c new file mode 100755 index 000000000..54d8f0925 --- /dev/null +++ b/sys/src/cmd/rc/havefork.c @@ -0,0 +1,234 @@ +#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: + clearwaitpids(); + pushredir(ROPEN, null, 0); + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + addwaitpid(pid); + 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: + clearwaitpids(); + start(p->code, pc+2, runq->local); + runq->ret = 0; + close(pfd[PRD]); + pushredir(ROPEN, pfd[PWR], lfd); + break; + default: + addwaitpid(forkid); + 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; + } +} + +enum { Wordmax = 8192, }; + +/* + * Who should wait for the exit from the fork? + */ +void +Xbackq(void) +{ + int c, pid; + int pfd[2]; + char wd[Wordmax + 1]; + char *s, *ewd = &wd[Wordmax], *stop; + struct io *f; + var *ifs = vlook("ifs"); + word *v, *nextv; + + 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: + clearwaitpids(); + close(pfd[PRD]); + start(runq->code, runq->pc+1, runq->local); + pushredir(ROPEN, pfd[PWR], 1); + return; + default: + addwaitpid(pid); + close(pfd[PWR]); + f = openfd(pfd[PRD]); + s = wd; + v = 0; + /* + * this isn't quite right for utf. stop could have utf + * in it, and we're processing the input as bytes, not + * utf encodings of runes. further, if we run out of + * room in wd, we can chop in the middle of a utf encoding + * (not to mention stepping on one of the bytes). + * presotto's Strings seem like the right data structure here. + */ + 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, pid; + 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(pid = fork()){ + case -1: + Xerror("try again"); + break; + case 0: + clearwaitpids(); + start(p->code, pc+2, runq->local); + close(mainfd); + pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); + runq->ret = 0; + break; + default: + addwaitpid(pid); + 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: + clearwaitpids(); + start(runq->code, runq->pc+1, runq->local); + runq->ret = 0; + break; + default: + addwaitpid(pid); + 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: + clearwaitpids(); + pushword("exec"); + execexec(); + strcpy(buf, "can't exec: "); + n = strlen(buf); + errstr(buf+n, ERRMAX-n); + Exit(buf); + } + addwaitpid(pid); + return pid; +} diff --git a/sys/src/cmd/rc/haventfork.c b/sys/src/cmd/rc/haventfork.c new file mode 100755 index 000000000..a5464ba5b --- /dev/null +++ b/sys/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(nc < sizeof file - 1){ /* 1 for / */ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc+strlen(argv[1])<sizeof(file)){ + strcat(file, argv[1]); + pid = ForkExecute(file, argv+1, mapfd(0), mapfd(1), mapfd(2)); + if(pid >= 0){ + free(argv); + return pid; + } + } + } + } + free(argv); + return -1; +} diff --git a/sys/src/cmd/rc/here.c b/sys/src/cmd/rc/here.c new file mode 100755 index 000000000..d10ed6aa8 --- /dev/null +++ b/sys/src/cmd/rc/here.c @@ -0,0 +1,152 @@ +#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*, uchar*); +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(tag && strcmp(line, tag)==0) break; + if(subst) + psubst(f, (uchar *)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, uchar *s) +{ + int savec, n; + uchar *t, *u; + word *star; + while(*s){ + if(*s!='$'){ + if(0xa0 <= *s && *s <= 0xf5){ + pchr(f, *s++); + if(*s=='\0') + break; + } + else if(0xf6 <= *s && *s <= 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((char *)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); + } +} diff --git a/sys/src/cmd/rc/io.c b/sys/src/cmd/rc/io.c new file mode 100755 index 000000000..5480265cf --- /dev/null +++ b/sys/src/cmd/rc/io.c @@ -0,0 +1,268 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +enum { Stralloc = 100, }; + +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); + continue; + } + if(*++fmt == '\0') /* "blah%"? */ + break; + switch(*fmt){ + 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': + pptr(f, va_arg(ap, void*)); + break; + 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++; +} + +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(*t >= 0 && needsrcquote(*t)) break; + if(t==s || *t) + pquo(f, s); + else pstr(f, s); +} + +void +pptr(io *f, void *v) +{ + int n; + uintptr p; + + p = (uintptr)v; + if(sizeof(uintptr) == sizeof(uvlong) && p>>32) + for(n = 60;n>=32;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); + + 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, int 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, unsigned 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, (char *)a->word); + pchr(f, ' '); + a = a->next; + } + pwrd(f, (char *)a->word); + } +} + +int +fullbuf(io *f, int c) +{ + flush(f); + return *f->bufp++=c; +} + +void +flush(io *f) +{ + int n; + + if(f->strp){ + n = f->ebuf - f->strp; + f->strp = realloc(f->strp, n+Stralloc+1); + if(f->strp==0) + panic("Can't realloc %d bytes in flush!", n+Stralloc+1); + f->bufp = f->strp + n; + f->ebuf = f->bufp + Stralloc; + memset(f->bufp, '\0', Stralloc+1); + } + else{ + n = f->bufp-f->buf; + if(n && Write(f->fd, f->buf, n) != n){ + Write(2, "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); + + f->fd = -1; + f->bufp = f->strp = emalloc(Stralloc+1); + f->ebuf = f->bufp + Stralloc; + memset(f->bufp, '\0', Stralloc+1); + 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); + uchar *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(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++; +} diff --git a/sys/src/cmd/rc/io.h b/sys/src/cmd/rc/io.h new file mode 100755 index 000000000..7d7cd146e --- /dev/null +++ b/sys/src/cmd/rc/io.h @@ -0,0 +1,27 @@ +#define EOF (-1) +#define NBUF 512 + +struct io{ + int fd; + uchar *bufp, *ebuf, *strp; + uchar 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*, int); +void poct(io*, unsigned); +void pptr(io*, void*); +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*, ...); diff --git a/sys/src/cmd/rc/lex.c b/sys/src/cmd/rc/lex.c new file mode 100755 index 000000000..369348328 --- /dev/null +++ b/sys/src/cmd/rc/lex.c @@ -0,0 +1,378 @@ +#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; +int incomm; +/* + * 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) +{ + 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' && !incomm){ /* don't continue a comment */ + 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(); + /* Why did this used to be if(!inquote && c=='#') ?? */ + if(c=='#'){ + incomm = 1; + for(;;){ + c = nextc(); + if(c=='\n' || c==EOF) { + incomm = 0; + break; + } + advance(); + } + } + if(c==' ' || c=='\t') + advance(); + else return; + } +} + +void +skipnl(void) +{ + 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-1]){ + *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) +{ + int c, d = nextc(); + char *w = tok; + 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 if (nextis('>')){ + t->rtype = RDWR; + *w++=c; + } else t->rtype = READ; + t->fd0 = 0; + break; + } + if(nextis('[')){ + *w++='['; + c = advance(); + *w++=c; + if(c<'0' || '9'<c){ + RedirErr: + *w = 0; + yyerror(t->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; +} diff --git a/sys/src/cmd/rc/mkfile b/sys/src/cmd/rc/mkfile new file mode 100755 index 000000000..b2ea3c5eb --- /dev/null +++ b/sys/src/cmd/rc/mkfile @@ -0,0 +1,63 @@ +</$objtype/mkfile + +TARG=rc +COMMONOFILES=\ + 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\ + havefork.$O\ + +PLAN9OFILES=plan9.$O\ + +UNIXOFILES=unix.$O\ + +OFILES=$COMMONOFILES $PLAN9OFILES y.tab.$O + +HFILES=rc.h\ + x.tab.h\ + io.h\ + exec.h\ + fns.h\ + getflags.h\ + +YFILES=syn.y + +BIN=/$objtype/bin + +UPDATE=\ + mkfile\ + $HFILES\ + ${COMMONOFILES:%.$O=%.c}\ + ${UNIXOFILES:%.$O=%.c}\ + ${PLAN9OFILES:%.$O=%.c}\ + $YFILES\ + ${TARG:%=/386/bin/%}\ + +</sys/src/cmd/mkone + +x.tab.h: y.tab.h + cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h + +clean:V: + rm -f [$OS].out *.[$OS] [xy].tab.? y.debug $TARG + +regress: $O.out + cd test + mk + +unregress:V: + for(test in test/*.test) rc $test >$test.out + +listing: + pr mkfile $HFILES $FILES $FILES9 $FILESUNIX $YFILES|lp -du diff --git a/sys/src/cmd/rc/pcmd.c b/sys/src/cmd/rc/pcmd.c new file mode 100755 index 000000000..8caf60a28 --- /dev/null +++ b/sys/src/cmd/rc/pcmd.c @@ -0,0 +1,147 @@ +#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: + case RDWR: + pchr(f, '<'); + if(t->rtype==RDWR) + 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; + } +} diff --git a/sys/src/cmd/rc/pfnc.c b/sys/src/cmd/rc/pfnc.c new file mode 100755 index 000000000..3f2b4c98a --- /dev/null +++ b/sys/src/cmd/rc/pfnc.c @@ -0,0 +1,71 @@ +#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", + Xrdwr, "Xrdwr", + 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); +} diff --git a/sys/src/cmd/rc/plan9.c b/sys/src/cmd/rc/plan9.c new file mode 100755 index 000000000..d71ca675c --- /dev/null +++ b/sys/src/cmd/rc/plan9.c @@ -0,0 +1,670 @@ +/* + * 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" + +enum { + Maxenvname = 256, /* undocumented limit */ +}; + +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[]="/rc/lib/rcmain"; +char Fdprefix[]="/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, + "rfork", execnewpgrp, + 0 +}; + +void +execnewpgrp(void) +{ + int arg; + char *s; + switch(count(runq->argv->words)){ + case 1: + arg = RFENVG|RFNAMEG|RFNOTEG; + break; + case 2: + arg = 0; + for(s = runq->argv->words->next->word;*s;s++) switch(*s){ + default: + goto Usage; + case 'n': + arg|=RFNAMEG; break; + case 'N': + arg|=RFCNAMEG; + break; + case 'm': + arg|=RFNOMNT; break; + case 'e': + arg|=RFENVG; break; + case 'E': + arg|=RFCENVG; break; + case 's': + arg|=RFNOTEG; break; + case 'f': + arg|=RFFDG; break; + case 'F': + arg|=RFCFDG; break; + } + break; + default: + Usage: + pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word); + setstatus("rfork usage"); + poplist(); + return; + } + if(rfork(arg)==-1){ + pfmt(err, "rc: %s failed\n", runq->argv->words->word); + setstatus("rfork failed"); + } + else + setstatus(""); + poplist(); +} + +void +Vinit(void) +{ + int dir, f, len, i, n, nent; + char *buf, *s; + char envname[Maxenvname]; + word *val; + Dir *ent; + + dir = open("/env", OREAD); + if(dir<0){ + pfmt(err, "rc: can't open /env: %r\n"); + return; + } + ent = nil; + for(;;){ + nent = dirread(dir, &ent); + if(nent <= 0) + break; + for(i = 0; i<nent; i++){ + len = ent[i].length; + if(len && strncmp(ent[i].name, "fn#", 3)!=0){ + snprint(envname, sizeof envname, "/env/%s", ent[i].name); + if((f = open(envname, 0))>=0){ + buf = emalloc(len+1); + n = readn(f, buf, len); + if (n <= 0) + buf[0] = '\0'; + else + buf[n] = '\0'; + val = 0; + /* Charitably add a 0 at the end if need be */ + if(buf[len-1]) + buf[len++]='\0'; + s = buf+len-1; + for(;;){ + while(s!=buf && s[-1]!='\0') --s; + val = newword(s, val); + if(s==buf) + break; + --s; + } + setvar(ent[i].name, val); + vlook(ent[i].name)->changed = 0; + close(f); + efree(buf); + } + } + } + free(ent); + } + close(dir); +} +int envdir; + +void +Xrdfn(void) +{ + int f, len; + Dir *e; + char envname[Maxenvname]; + static Dir *ent, *allocent; + static int nent; + + for(;;){ + if(nent == 0){ + free(allocent); + nent = dirread(envdir, &allocent); + ent = allocent; + } + if(nent <= 0) + break; + while(nent){ + e = ent++; + nent--; + len = e->length; + if(len && strncmp(e->name, "fn#", 3)==0){ + snprint(envname, sizeof envname, "/env/%s", e->name); + if((f = open(envname, 0))>=0){ + execcmds(openfd(f)); + return; + } + } + } + } + close(envdir); + 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(); + envdir = open("/env", 0); + if(envdir<0){ + pfmt(err, "rc: can't open /env: %r\n"); + return; + } + start(rdfns, 1, runq->local); +} + +int +Waitfor(int pid, int) +{ + thread *p; + Waitmsg *w; + char errbuf[ERRMAX]; + + if(pid >= 0 && !havewaitpid(pid)) + return 0; + + while((w = wait()) != nil){ + delwaitpid(w->pid); + 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[Maxenvname]; + 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 %q %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); +} + +/* not used on plan 9 */ +int +ForkExecute(char *file, char **argv, int sin, int sout, int serr) +{ + int pid; + + if(access(file, 1) != 0) + return -1; + switch(pid = fork()){ + case -1: + return -1; + case 0: + if(sin >= 0) + dup(sin, 0); + else + close(0); + if(sout >= 0) + dup(sout, 1); + else + close(1); + if(serr >= 0) + dup(serr, 2); + else + close(2); + exec(file, argv); + exits(file); + } + return pid; +} + +void +Execute(word *args, word *path) +{ + char **argv = mkargv(args); + char file[1024], errstr[1024]; + int nc; + + Updenv(); + errstr[0] = '\0'; + for(;path;path = path->next){ + nc = strlen(path->word); + if(nc < sizeof file - 1){ /* 1 for / */ + strcpy(file, path->word); + if(file[0]){ + strcat(file, "/"); + nc++; + } + if(nc + strlen(argv[1]) < sizeof file){ + strcat(file, argv[1]); + exec(file, argv+1); + rerrstr(errstr, sizeof errstr); + /* + * if file exists and is executable, exec should + * have worked, unless it's a directory or an + * executable for another architecture. in + * particular, if it failed due to lack of + * swap/vm (e.g., arg. list too long) or other + * allocation failure, stop searching and print + * the reason for failure. + */ + if (strstr(errstr, " allocat") != nil || + strstr(errstr, " full") != nil) + break; + } + else werrstr("command name too long"); + } + } + pfmt(err, "%s: %s\n", argv[1], errstr); + efree((char *)argv); +} +#define NDIR 256 /* shoud be a better way */ + +int +Globsize(char *p) +{ + int 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 + +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){ + dir[f].i = 0; + dir[f].n = 0; + } + free(db); + return f; + } + free(db); + close(f); + return -1; +} + +static int +trimdirs(Dir *d, int nd) +{ + int r, w; + + for(r=w=0; r<nd; r++) + if(d[r].mode&DMDIR) + d[w++] = d[r]; + return w; +} + +/* + * onlydirs is advisory -- it means you only + * need to return the directories. it's okay to + * return files too (e.g., on unix where you can't + * tell during the readdir), but that just makes + * the globber work harder. + */ +int +Readdir(int f, void *p, int onlydirs) +{ + int n; + + if(f<0 || f>=NFD) + return 0; +Again: + 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){ + if(onlydirs){ + n = trimdirs(dir[f].dbuf, n); + if(n == 0) + goto Again; + } + 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<NFD){ + free(dir[f].dbuf); + dir[f].i = 0; + dir[f].n = 0; + dir[f].dbuf = 0; + } + close(f); +} +int interrupted = 0; +void +notifyf(void*, char *s) +{ + int i; + for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ + if(strncmp(s, "sys: ", 5)!=0) interrupted = 1; + goto Out; + } + pfmt(err, "rc: note: %s\n", s); + noted(NDFLT); + return; +Out: + if(strcmp(s, "interrupt")!=0 || trap[i]==0){ + trap[i]++; + ntrap++; + } + if(ntrap>=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, void *buf, long cnt) +{ + return write(fd, buf, cnt); +} + +long +Read(int fd, void *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) +{ + return -1; +} + +void +Exit(char *stat) +{ + Updenv(); + setstatus(stat); + exits(truestatus()?"":getstatus()); +} + +int +Eintr(void) +{ + return interrupted; +} + +void +Noerror(void) +{ + interrupted = 0; +} + +int +Isatty(int fd) +{ + char buf[64]; + + if(fd2path(fd, buf, sizeof buf) != 0) + return 0; + + /* might be #c/cons during boot - fixed 22 april 2005, remove this later */ + if(strcmp(buf, "#c/cons") == 0) + return 1; + + /* might be /mnt/term/dev/cons */ + return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; +} + +void +Abort(void) +{ + pfmt(err, "aborting\n"); + flush(err); + Exit("aborting"); +} + +void +Memcpy(void *a, void *b, long n) +{ + memmove(a, b, n); +} + +void* +Malloc(ulong n) +{ + return malloc(n); +} + +int *waitpids; +int nwaitpids; + +void +addwaitpid(int pid) +{ + waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); + if(waitpids == 0) + panic("Can't realloc %d waitpids", nwaitpids+1); + waitpids[nwaitpids++] = pid; +} + +void +delwaitpid(int pid) +{ + int r, w; + + for(r=w=0; r<nwaitpids; r++) + if(waitpids[r] != pid) + waitpids[w++] = waitpids[r]; + nwaitpids = w; +} + +void +clearwaitpids(void) +{ + nwaitpids = 0; +} + +int +havewaitpid(int pid) +{ + int i; + + for(i=0; i<nwaitpids; i++) + if(waitpids[i] == pid) + return 1; + return 0; +} + +/* avoid loading any floating-point library code */ +int +_efgfmt(Fmt *) +{ + return -1; +} diff --git a/sys/src/cmd/rc/rc.h b/sys/src/cmd/rc/rc.h new file mode 100755 index 000000000..8ccdc344b --- /dev/null +++ b/sys/src/cmd/rc/rc.h @@ -0,0 +1,150 @@ +/* + * 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 <u.h> +#include <libc.h> +#define NSIG 32 +#define SIGINT 2 +#define SIGQUIT 3 +#endif +#ifdef V9 +#include <signal.h> +#include <libc.h> +#endif +#ifdef Sun +#include <signal.h> +#endif +#define YYMAXDEPTH 500 +#ifndef PAREN +#include "x.tab.h" +#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; + +#pragma incomplete word +#pragma incomplete io + +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 +#define RDWR 7 + +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))) + +void *emalloc(long); +void *Malloc(ulong); +void efree(void *); + +#define NOFILE 128 /* should come from <param.h> */ + +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 +char Rcmain[], Fdprefix[]; +/* + * 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; diff --git a/sys/src/cmd/rc/simple.c b/sys/src/cmd/rc/simple.c new file mode 100755 index 000000000..d4b897cd1 --- /dev/null +++ b/sys/src/cmd/rc/simple.c @@ -0,0 +1,517 @@ +/* + * 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; + 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 */ + if((pid = execforkexec()) < 0){ + Xerror("try again"); + return; + } + + /* interrupts don't get us out */ + poplist(); + 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, runq->local); + 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; + + 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){ + if(cdpath->word[0] != '\0') + dir = smprint("%s/%s", cdpath->word, + a->next->word); + else + dir = strdup(a->next->word); + + if(dochdir(dir) >= 0){ + if(cdpath->word[0] != '\0' && + strcmp(cdpath->word, ".") != 0) + pfmt(err, "%s\n", dir); + free(dir); + setstatus(""); + break; + } + free(dir); + } + 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, *file; + word *path; + static int first = 1; + + 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){ + if(path->word[0] != '\0') + file = smprint("%s/%s", path->word, zero); + else + file = strdup(zero); + + fd = open(file, 0); + free(file); + if(fd >= 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; + 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 %q %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){ + if(path->word[0] != '\0') + file = smprint("%s/%s", + path->word, a->word); + else + file = strdup(a->word); + if(Executable(file)){ + pfmt(out, "%s\n", file); + free(file); + break; + } + free(file); + } + 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(); +} diff --git a/sys/src/cmd/rc/subr.c b/sys/src/cmd/rc/subr.c new file mode 100755 index 000000000..48e8cb091 --- /dev/null +++ b/sys/src/cmd/rc/subr.c @@ -0,0 +1,77 @@ +#include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" + +void * +emalloc(long n) +{ + void *p = 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(void *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; + +static 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 +inttoascii(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(); +} diff --git a/sys/src/cmd/rc/syn.y b/sys/src/cmd/rc/syn.y new file mode 100755 index 000000000..c7de35313 --- /dev/null +++ b/sys/src/cmd/rc/syn.y @@ -0,0 +1,91 @@ +%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN +%term WORD REDIR DUP PIPE SUB +%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ +/* operator priorities -- lowest first */ +%left IF WHILE FOR SWITCH ')' NOT +%left ANDAND OROR +%left BANG SUBSHELL +%left PIPE +%left '^' +%right '$' COUNT '"' +%left SUB +%{ +#include "rc.h" +#include "fns.h" +%} +%union{ + struct tree *tree; +}; +%type<tree> line paren brace body cmdsa cmdsan assign epilog redir +%type<tree> cmd simple first word comword keyword words +%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN +%type<tree> WORD REDIR DUP PIPE +%% +rc: { return 1;} +| line '\n' {return !compile($1);} +line: cmd +| cmdsa line {$$=tree2(';', $1, $2);} +body: cmd +| cmdsan body {$$=tree2(';', $1, $2);} +cmdsa: cmd ';' +| cmd '&' {$$=tree1('&', $1);} +cmdsan: cmdsa +| cmd '\n' +brace: '{' body '}' {$$=tree1(BRACE, $2);} +paren: '(' body ')' {$$=tree1(PCMD, $2);} +assign: first '=' word {$$=tree2('=', $1, $3);} +epilog: {$$=0;} +| redir epilog {$$=mung2($1, $1->child[0], $2);} +redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} +| DUP +cmd: {$$=0;} +| brace epilog {$$=epimung($1, $2);} +| IF paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| IF NOT {skipnl();} cmd {$$=mung1($2, $4);} +| FOR '(' word IN words ')' {skipnl();} cmd + /* + * if ``words'' is nil, we need a tree element to distinguish between + * for(i in ) and for(i), the former being a loop over the empty set + * and the latter being the implicit argument loop. so if $5 is nil + * (the empty set), we represent it as "()". don't parenthesize non-nil + * functions, to avoid growing parentheses every time we reread the + * definition. + */ + {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} +| FOR '(' word ')' {skipnl();} cmd + {$$=mung3($1, $3, (struct tree *)0, $6);} +| WHILE paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| SWITCH word {skipnl();} brace + {$$=tree2(SWITCH, $2, $4);} +| simple {$$=simplemung($1);} +| TWIDDLE word words {$$=mung2($1, $2, $3);} +| cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} +| cmd OROR cmd {$$=tree2(OROR, $1, $3);} +| cmd PIPE cmd {$$=mung2($2, $1, $3);} +| redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} +| assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} +| BANG cmd {$$=mung1($1, $2);} +| SUBSHELL cmd {$$=mung1($1, $2);} +| FN words brace {$$=tree2(FN, $2, $3);} +| FN words {$$=tree1(FN, $2);} +simple: first +| simple word {$$=tree2(ARGLIST, $1, $2);} +| simple redir {$$=tree2(ARGLIST, $1, $2);} +first: comword +| first '^' word {$$=tree2('^', $1, $3);} +word: keyword {lastword=1; $1->type=WORD;} +| comword +| word '^' word {$$=tree2('^', $1, $3);} +comword: '$' word {$$=tree1('$', $2);} +| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} +| '"' word {$$=tree1('"', $2);} +| COUNT word {$$=tree1(COUNT, $2);} +| WORD +| '`' brace {$$=tree1('`', $2);} +| '(' words ')' {$$=tree1(PAREN, $2);} +| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} +keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN +words: {$$=(struct tree*)0;} +| words word {$$=tree2(WORDS, $1, $2);} diff --git a/sys/src/cmd/rc/trap.c b/sys/src/cmd/rc/trap.c new file mode 100755 index 000000000..a572cac37 --- /dev/null +++ b/sys/src/cmd/rc/trap.c @@ -0,0 +1,37 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" +#include "io.h" +extern char *Signame[]; + +void +dotrap(void) +{ + int i; + struct var *trapreq; + 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()); + } +} diff --git a/sys/src/cmd/rc/tree.c b/sys/src/cmd/rc/tree.c new file mode 100755 index 000000000..a2cd1af69 --- /dev/null +++ b/sys/src/cmd/rc/tree.c @@ -0,0 +1,148 @@ +#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((char *)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); +} diff --git a/sys/src/cmd/rc/unix.c b/sys/src/cmd/rc/unix.c new file mode 100755 index 000000000..3be938148 --- /dev/null +++ b/sys/src/cmd/rc/unix.c @@ -0,0 +1,469 @@ +/* + * Unix 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 <errno.h> +char Rcmain[]="/usr/lib/rcmain"; +char Fdprefix[]="/dev/fd/"; +int execumask(), execfinit(); +struct builtin Builtin[] = { + "cd", execcd, + "whatis", execwhatis, + "eval", execeval, + "exec", execexec, /* but with popword first */ + "exit", execexit, + "shift", execshift, + "wait", execwait, + "umask", execumask, + ".", execdot, + "finit", execfinit, + "flag", execflag, + 0 +}; +#define SEP '\1' +char **environp; + +struct word* +enval(s) +register char *s; +{ + char *t, c; + 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; +} +Vinit(){ + extern char **environ; + char *s; + 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; +Xrdfn(){ + char *s; + int len; + for(;*envp;envp++){ + 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; + } + } + Xreturn(); +} +union code rdfns[4]; +execfinit(){ + 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); +} +cmpenv(a, b) +char **a, **b; +{ + return strcmp(*a, *b); +} + +char* +*mkenv() +{ + char **env, **ep, *p, *q; + struct var **h, *v; + struct word *a; + int nvar = 0, nchr = 0, sep; + /* + * Slightly kludgy loops look at locals then globals + */ + for(h = var-1;h!=&var[NVAR];h++) for(v = h>=var?*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 = var-1;h!=&var[NVAR];h++) for(v = h>=var?*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; + *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ + *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++='\0'; + } + } + *ep = 0; + qsort((char *)env, nvar, sizeof ep[0], cmpenv); + return env; +} +char *sigmsg[] = { +/* 0 normal */ 0, +/* 1 SIGHUP */ "Hangup", +/* 2 SIGINT */ 0, +/* 3 SIGQUIT */ "Quit", +/* 4 SIGILL */ "Illegal instruction", +/* 5 SIGTRAP */ "Trace/BPT trap", +/* 6 SIGIOT */ "abort", +/* 7 SIGEMT */ "EMT trap", +/* 8 SIGFPE */ "Floating exception", +/* 9 SIGKILL */ "Killed", +/* 10 SIGBUS */ "Bus error", +/* 11 SIGSEGV */ "Memory fault", +/* 12 SIGSYS */ "Bad system call", +/* 13 SIGPIPE */ 0, +/* 14 SIGALRM */ "Alarm call", +/* 15 SIGTERM */ "Terminated", +/* 16 unused */ "signal 16", +/* 17 SIGSTOP */ "Process stopped", +/* 18 unused */ "signal 18", +/* 19 SIGCONT */ "Process continued", +/* 20 SIGCHLD */ "Child death", +}; +Waitfor(pid, persist){ + int wpid, sig; + struct thread *p; + int wstat; + char wstatstr[12]; + for(;;){ + errno = 0; + wpid = wait(&wstat); + if(errno==EINTR && persist) + continue; + if(wpid==-1) + break; + sig = wstat&0177; + if(sig==0177){ + pfmt(err, "trace: "); + sig = (wstat>>8)&0177; + } + if(sig>(sizeof sigmsg/sizeof sigmsg[0]) || sigmsg[sig]){ + if(pid!=wpid) + pfmt(err, "%d: ", wpid); + if(sig<=(sizeof sigmsg/sizeof sigmsg[0])) + pfmt(err, "%s", sigmsg[sig]); + else if(sig==0177) pfmt(err, "stopped by ptrace"); + else pfmt(err, "signal %d", sig); + if(wstat&0200)pfmt(err, " -- core dumped"); + pfmt(err, "\n"); + } + wstat = sig?sig+1000:(wstat>>8)&0xFF; + if(wpid==pid){ + inttoascii(wstatstr, wstat); + setstatus(wstatstr); + break; + } + else{ + for(p = runq->ret;p;p = p->ret) + if(p->pid==wpid){ + p->pid=-1; + inttoascii(p->status, wstat); + break; + } + } + } +} + +char* +*mkargv(a) +register struct 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; +} +Updenv(){} +Execute(args, path) +register struct word *args, *path; +{ + char *msg="not found"; + int txtbusy = 0; + char **env = mkenv(); + char **argv = mkargv(args); + char file[512]; + for(;path;path = path->next){ + strcpy(file, path->word); + if(file[0]) + strcat(file, "/"); + strcat(file, argv[1]); + ReExec: + execve(file, argv+1, env); + switch(errno){ + case ENOEXEC: + pfmt(err, "%s: Bourne again\n", argv[1]); + argv[0]="sh"; + argv[1] = strdup(file); + execve("/bin/sh", argv, env); + goto Bad; + case ETXTBSY: + if(++txtbusy!=5){ + sleep(txtbusy); + goto ReExec; + } + msg="text busy"; goto Bad; + case EACCES: + msg="no access"; + break; + case ENOMEM: + msg="not enough memory"; goto Bad; + case E2BIG: + msg="too big"; goto Bad; + } + } +Bad: + pfmt(err, "%s: %s\n", argv[1], msg); + efree((char *)env); + efree((char *)argv); +} +#define NDIR 14 /* should get this from param.h */ +Globsize(p) +register char *p; +{ + int 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; +} +#include <sys/types.h> +#include <ndir.h> +#define NDIRLIST 50 +DIR *dirlist[NDIRLIST]; +Opendir(name) +char *name; +{ + DIR **dp; + for(dp = dirlist;dp!=&dirlist[NDIRLIST];dp++) + if(*dp==0){ + *dp = opendir(name); + return *dp?dp-dirlist:-1; + } + return -1; +} +Readdir(f, p, onlydirs) +int f; +void *p; +int onlydirs; /* ignored, just advisory */ +{ + struct direct *dp = readdir(dirlist[f]); + if(dp==0) + return 0; + strcpy(p, dp->d_name); + return 1; +} +Closedir(f){ + closedir(dirlist[f]); + dirlist[f] = 0; +} +char *Signame[] = { + "sigexit", "sighup", "sigint", "sigquit", + "sigill", "sigtrap", "sigiot", "sigemt", + "sigfpe", "sigkill", "sigbus", "sigsegv", + "sigsys", "sigpipe", "sigalrm", "sigterm", + "sig16", "sigstop", "sigtstp", "sigcont", + "sigchld", "sigttin", "sigttou", "sigtint", + "sigxcpu", "sigxfsz", "sig26", "sig27", + "sig28", "sig29", "sig30", "sig31", + 0, +}; + +int +gettrap(sig) +{ + signal(sig, gettrap); + trap[sig]++; + ntrap++; + if(ntrap>=NSIG){ + pfmt(err, "rc: Too many traps (trap %d), dumping core\n", sig); + signal(SIGIOT, (int (*)())0); + kill(getpid(), SIGIOT); + } +} +Trapinit(){ + int i; + int (*sig)(); + if(1 || flag['d']){ /* wrong!!! */ + sig = signal(SIGINT, gettrap); + if(sig==SIG_IGN) + signal(SIGINT, SIG_IGN); + } + else{ + for(i = 1;i<=NSIG;i++) if(i!=SIGCHLD){ + sig = signal(i, gettrap); + if(sig==SIG_IGN) + signal(i, SIG_IGN); + } + } +} +Unlink(name) +char *name; +{ + return unlink(name); +} +Write(fd, buf, cnt) +void *buf; +{ + return write(fd, buf, cnt); +} +Read(fd, buf, cnt) +void *buf; +{ + return read(fd, buf, cnt); +} +Seek(fd, cnt, whence) +long cnt; +{ + return lseek(fd, cnt, whence); +} +Executable(file) +char *file; +{ + return(access(file, 01)==0); +} +Creat(file) +char *file; +{ + return creat(file, 0666); +} +Dup(a, b){ + return dup2(a, b); +} +Dup1(a){ + return dup(a); +} +/* + * Wrong: should go through components of a|b|c and return the maximum. + */ +Exit(stat) +register char *stat; +{ + int n = 0; + while(*stat){ + if(*stat!='|'){ + if(*stat<'0' || '9'<*stat) + exit(1); + else n = n*10+*stat-'0'; + } + stat++; + } + exit(n); +} +Eintr(){ + return errno==EINTR; +} +Noerror(){ + errno = 0; +} +Isatty(fd){ + return isatty(fd); +} +Abort(){ + abort(); +} +execumask(){ /* wrong -- should fork before writing */ + int m; + struct io out[1]; + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: umask [umask]\n"); + setstatus("umask usage"); + poplist(); + return; + case 2: + umask(octal(runq->argv->words->next->word)); + break; + case 1: + umask(m = umask(0)); + out->fd = mapfd(1); + out->bufp = out->buf; + out->ebuf=&out->buf[NBUF]; + out->strp = 0; + pfmt(out, "%o\n", m); + break; + } + setstatus(""); + poplist(); +} +Memcpy(a, b, n) +void *a, *b; +long n; +{ + memmove(a, b, n); +} + +void* +Malloc(n) +{ + return (void *)malloc(n); +} diff --git a/sys/src/cmd/rc/var.c b/sys/src/cmd/rc/var.c new file mode 100755 index 000000000..a96af06d6 --- /dev/null +++ b/sys/src/cmd/rc/var.c @@ -0,0 +1,86 @@ +#include "rc.h" +#include "exec.h" +#include "fns.h" + +int +hash(char *s, int n) +{ + 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) +{ + int h = hash(name, NKW); + 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) +{ + struct var *v = vlook(name); + freewords(v->val); + v->val = val; + v->changed = 1; +} diff --git a/sys/src/cmd/rc/win32.c b/sys/src/cmd/rc/win32.c new file mode 100755 index 000000000..b7fcece61 --- /dev/null +++ b/sys/src/cmd/rc/win32.c @@ -0,0 +1,560 @@ +/* + * 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[]="/rc/lib/rcmain"; +char Fdprefix[]="/fd/"; +void execfinit(void); +void execbind(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 +}; + +void +Vinit(void) +{ + int dir, f, len; + word *val; + char *buf, *s; + Dir *ent; + int i, nent; + char envname[256]; + dir = open("/env", OREAD); + if(dir<0){ + pfmt(err, "rc: can't open /env: %r\n"); + return; + } + ent = nil; + for(;;){ + nent = dirread(dir, &ent); + if(nent <= 0) + break; + for(i = 0; i<nent; i++){ + len = ent[i].length; + if(len && strncmp(ent[i].name, "fn#", 3)!=0){ + snprint(envname, sizeof envname, "/env/%s", ent[i].name); + if((f = open(envname, 0))>=0){ + buf = emalloc((int)len+1); + read(f, buf, (long)len); + val = 0; + /* Charitably add a 0 at the end if need be */ + if(buf[len-1]) + buf[len++]='\0'; + s = buf+len-1; + for(;;){ + while(s!=buf && s[-1]!='\0') --s; + val = newword(s, val); + if(s==buf) + break; + --s; + } + setvar(ent[i].name, val); + vlook(ent[i].name)->changed = 0; + close(f); + efree(buf); + } + } + } + free(ent); + } + close(dir); +} +int envdir; + +void +Xrdfn(void) +{ + int f, len; + static Dir *ent, *allocent; + static int nent; + Dir *e; + char envname[256]; + + for(;;){ + if(nent == 0){ + free(allocent); + nent = dirread(envdir, &allocent); + ent = allocent; + } + if(nent <= 0) + break; + while(nent){ + e = ent++; + nent--; + len = e->length; + if(len && strncmp(e->name, "fn#", 3)==0){ + snprint(envname, sizeof envname, "/env/%s", e->name); + if((f = open(envname, 0))>=0){ + execcmds(openfd(f)); + return; + } + } + } + } + close(envdir); + 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(); + envdir = open("/env", 0); + if(envdir<0){ + pfmt(err, "rc: can't open /env: %r\n"); + return; + } + start(rdfns, 1, runq->local); +} + +int +Waitfor(int pid, int persist) +{ + 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 %q %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 +ForkExecute(char *file, char **argv, int sin, int sout, int serr) +{ + int pid; + +{int i; +fprint(2, "forkexec %s", file); +for(i = 0; argv[i]; i++)fprint(2, " %s", argv[i]); +fprint(2, " %d %d %d\n", sin, sout, serr); +} + if(access(file, 1) != 0) + return -1; +fprint(2, "forking\n"); + switch(pid = fork()){ + case -1: + return -1; + case 0: + if(sin >= 0) + dup(sin, 0); + else + close(0); + if(sout >= 0) + dup(sout, 1); + else + close(1); + if(serr >= 0) + dup(serr, 2); + else + close(2); +fprint(2, "execing\n"); + exec(file, argv); +fprint(2, "exec: %r\n"); + exits(file); + } + return pid; +} + +void +Execute(word *args, word *path) +{ + char **argv = mkargv(args); + 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]); + exec(file, argv+1); + } + 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) +{ + int 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){ + dir[f].i = 0; + dir[f].n = 0; + } + free(db); + return f; + } + free(db); + close(f); + return -1; +} + +static int +trimdirs(Dir *d, int nd) +{ + int r, w; + + for(r=w=0; r<nd; r++) + if(d[r].mode&DMDIR) + d[w++] = d[r]; + return w; +} + +/* + * onlydirs is advisory -- it means you only + * need to return the directories. it's okay to + * return files too (e.g., on unix where you can't + * tell during the readdir), but that just makes + * the globber work harder. + */ +int +Readdir(int f, void *p, int onlydirs) +{ + int n; + + if(f<0 || f>=NFD) + return 0; +Again: + 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){ + if(onlydirs){ + n = trimdirs(dir[f].dbuf, n); + if(n == 0) + goto Again; + } + 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<NFD){ + free(dir[f].dbuf); + dir[f].i = 0; + dir[f].n = 0; + dir[f].dbuf = 0; + } + close(f); +} +int interrupted = 0; +void +notifyf(void*, char *s) +{ + int i; + for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ + if(strncmp(s, "sys: ", 5)!=0) interrupted = 1; + goto Out; + } + pfmt(err, "rc: note: %s\n", s); + noted(NDFLT); + return; +Out: + if(strcmp(s, "interrupt")!=0 || trap[i]==0){ + trap[i]++; + ntrap++; + } + if(ntrap>=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, void *buf, long cnt) +{ + return write(fd, buf, (long)cnt); +} + +long +Read(int fd, void *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) +{ + return -1; +} + +void +Exit(char *stat) +{ + Updenv(); + setstatus(stat); + exits(truestatus()?"":getstatus()); +} + +int +Eintr(void) +{ + return interrupted; +} + +void +Noerror(void) +{ + interrupted = 0; +} + +int +Isatty(int fd) +{ + Dir *d1, *d2; + int ret; + + d1 = dirfstat(fd); + if(d1 == nil) + return 0; + if(strncmp(d1->name, "ptty", 4)==0){ /* fwd complaints to philw */ + free(d1); + return 1; + } + d2 = dirstat("/dev/cons"); + if(d2 == nil){ + free(d1); + return 0; + } + ret = (d1->type==d2->type&&d1->dev==d2->dev&&d1->qid.path==d2->qid.path); + free(d1); + free(d2); + return ret; +} + +void +Abort(void) +{ + pfmt(err, "aborting\n"); + flush(err); + Exit("aborting"); +} + +void +Memcpy(void *a, void *b, long n) +{ + memmove(a, b, n); +} + +void* +Malloc(ulong n) +{ + return malloc(n); +} |