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