From geoff@collyer.net Fri Dec 19 01:21:40 EST 2003Received: from plan9.cs.bell-labs.com ([135.104.9.2]) by plan9; Fri Dec 19 01:21:39 EST 2003Received: from collyer.net ([63.192.14.226]) by plan9; Fri Dec 19 01:21:35 EST 2003Message-ID: <c790d8b1e06b3918ad2c7848a3ae0ec7@collyer.net>subject: rc on unix, part 1From: Geoff Collyer <geoff@collyer.net>Date: Thu, 18 Dec 2003 22:21:33 -0800To: presotto@plan9.bell-labs.com, rsc@plan9.bell-labs.com, geoff@collyer.netMIME-Version: 1.0Content-Type: text/plain; charset="US-ASCII"Content-Transfer-Encoding: 7bit I got /sys/src/cmd/rc to compile under APE (in preparation for moving itto Unixes) with the following changed files. I cadged some include filesfrom rsc but had to edit lib9.h slightly. I'll send the include filesseparately. 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 yetsimulated the effect of the OCEXEC bit. # To unbundle, run this fileecho mkfilesed 's/^X//' >mkfile <<'!'X</$objtype/mkfile TARG=rcCOMMONOFILES=\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../includeLD=pcc X</sys/src/cmd/mkone x.tab.h: y.tab.hX 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.outX cd testX 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.csed '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} voidXXsimple(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}; voiddoredir(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)==0X || strncmp(w, "#", 1)==0X || strncmp(w, "./", 2)==0X || strncmp(w, "../", 3)==0X || (path = vlook("path")->val)==0)X path=&nullpath;X return path;X} voidexecexec(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} voidexecfunc(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} intdochdir(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} voidexeccd(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 elseX pfmt(err, "Can't cd %s: %r\n", a->word);X }X elseX pfmt(err, "Can't cd -- $home empty\n");X break;X }X poplist();X} voidexecexit(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} voidexecshift(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} intoctal(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} intmapfd(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]; voidexeccmds(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} voidexeceval(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]; voidexecdot(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 elseX 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} voidexecflag(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 }Lines 1–500 of 1866. Load more
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 }