Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "rc.h"
#include "getflags.h"
#include "exec.h"
#include "io.h"
#include "fns.h"
/*
 * Start executing the given code at the given pc with the given redirection
 */
char *argv0="rc";

void
start(code *c, int pc, var *local)
{
        struct thread *p = new(struct thread);

        p->code = codecopy(c);
        p->pc = pc;
        p->argv = 0;
        p->redir = p->startredir = runq?runq->redir:0;
        p->local = local;
        p->cmdfile = 0;
        p->cmdfd = 0;
        p->eof = 0;
        p->iflag = 0;
        p->lineno = 1;
        p->ret = runq;
        runq = p;
}

word*
newword(char *wd, word *next)
{
        word *p = new(word);
        p->word = strdup(wd);
        p->next = next;
        return p;
}

void
pushword(char *wd)
{
        if(runq->argv==0)
                panic("pushword but no argv!", 0);
        runq->argv->words = newword(wd, runq->argv->words);
}

void
popword(void)
{
        word *p;
        if(runq->argv==0)
                panic("popword but no argv!", 0);
        p = runq->argv->words;
        if(p==0)
                panic("popword but no word!", 0);
        runq->argv->words = p->next;
        efree(p->word);
        efree((char *)p);
}

void
freelist(word *w)
{
        word *nw;
        while(w){
                nw = w->next;
                efree(w->word);
                efree((char *)w);
                w = nw;
        }
}

void
pushlist(void)
{
        list *p = new(list);
        p->next = runq->argv;
        p->words = 0;
        runq->argv = p;
}

void
poplist(void)
{
        list *p = runq->argv;
        if(p==0)
                panic("poplist but no argv", 0);
        freelist(p->words);
        runq->argv = p->next;
        efree((char *)p);
}

int
count(word *w)
{
        int n;
        for(n = 0;w;n++) w = w->next;
        return n;
}

void
pushredir(int type, int from, int to)
{
        redir * rp = new(redir);
        rp->type = type;
        rp->from = from;
        rp->to = to;
        rp->next = runq->redir;
        runq->redir = rp;
}

var*
newvar(char *name, var *next)
{
        var *v = new(var);
        v->name = name;
        v->val = 0;
        v->fn = 0;
        v->changed = 0;
        v->fnchanged = 0;
        v->next = next;
        return v;
}
/*
 * get command line flags, initialize keywords & traps.
 * get values from environment.
 * set $pid, $cflag, $*
 * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
 * start interpreting code
 */

void
main(int argc, char *argv[])
{
        code bootstrap[17];
        char num[12], *rcmain;
        int i;
        argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1);
        if(argc==-1)
                usage("[file [arg ...]]");
        if(argv[0][0]=='-')
                flag['l'] = flagset;
        if(flag['I'])
                flag['i'] = 0;
        else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
        rcmain = flag['m']?flag['m'][0]:Rcmain; 
        err = openfd(2);
        kinit();
        Trapinit();
        Vinit();
        inttoascii(num, mypid = getpid());
        setvar("pid", newword(num, (word *)0));
        setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
                                :(word *)0);
        setvar("rcname", newword(argv[0], (word *)0));
        i = 0;
        memset(bootstrap, 0, sizeof bootstrap);
        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
 * Xdelhere
 * Xdol(name)                           get variable value
 * Xdup[i j]                            dup file descriptor
 * Xeflag
 * Xerror
 * Xexit                                rc exits with status
 * Xfalse{...}                          execute {} if false
 * Xfn(name){... Xreturn}                       define function
 * Xfor(var, list){... Xreturn}         for loop
 * Xglob
 * Xif
 * Xifnot
 * 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/??
 * Xpipewait
 * Xpopm(value)                         pop value from stack
 * Xpopredir
 * Xrdcmds
 * Xrdfn
 * Xrdwr(file)[fd]                      open file for reading and writing
 * Xread(file)[fd]                      open file to read
 * Xqdol(name)                          concatenate variable components
 * Xreturn                              kill thread
 * Xsimple(args)                        run command and wait
 * Xsub
 * Xsubshell{... Xexit}                 execute {} in a subshell and wait
 * Xtrue{...}                           execute {} if true
 * Xunlocal                             delete local variable
 * Xwastrue
 * Xword[string]                        push string
 * Xwrite(file)[fd]                     open file to write
 */

void
Xappend(void)
{
        char *file;
        int f;
        switch(count(runq->argv->words)){
        default:
                Xerror1(">> requires singleton");
                return;
        case 0:
                Xerror1(">> requires file");
                return;
        case 1:
                break;
        }
        file = runq->argv->words->word;
        if((f = open(file, 1))<0 && (f = Creat(file))<0){
                pfmt(err, "%s: ", file);
                Xerror("can't open");
                return;
        }
        Seek(f, 0L, 2);
        pushredir(ROPEN, f, runq->code[runq->pc].i);
        runq->pc++;
        poplist();
}

void
Xsettrue(void)
{
        setstatus("");
}

void
Xbang(void)
{
        setstatus(truestatus()?"false":"");
}

void
Xclose(void)
{
        pushredir(RCLOSE, runq->code[runq->pc].i, 0);
        runq->pc++;
}

void
Xdup(void)
{
        pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i);
        runq->pc+=2;
}

void
Xeflag(void)
{
        if(eflagok && !truestatus()) Xexit();
}

void
Xexit(void)
{
        struct var *trapreq;
        struct word *starval;
        static int beenhere = 0;
        if(getpid()==mypid && !beenhere){
                trapreq = vlook("sigexit");
                if(trapreq->fn){
                        beenhere = 1;
                        --runq->pc;
                        starval = vlook("*")->val;
                        start(trapreq->fn, trapreq->pc, (struct var *)0);
                        runq->local = newvar(strdup("*"), runq->local);
                        runq->local->val = copywords(starval, (struct word *)0);
                        runq->local->changed = 1;
                        runq->redir = runq->startredir = 0;
                        return;
                }
        }
        Exit(getstatus());
}

void
Xfalse(void)
{
        if(truestatus()) runq->pc = runq->code[runq->pc].i;
        else runq->pc++;
}
int ifnot;              /* dynamic if not flag */

void
Xifnot(void)
{
        if(ifnot)
                runq->pc++;
        else
                runq->pc = runq->code[runq->pc].i;
}

void
Xjump(void)
{
        runq->pc = runq->code[runq->pc].i;
}

void
Xmark(void)
{
        pushlist();
}

void
Xpopm(void)
{
        poplist();
}

void
Xread(void)
{
        char *file;
        int f;
        switch(count(runq->argv->words)){
        default:
                Xerror1("< requires singleton\n");
                return;
        case 0:
                Xerror1("< requires file\n");
                return;
        case 1:
                break;
        }
        file = runq->argv->words->word;
        if((f = open(file, 0))<0){
                pfmt(err, "%s: ", file);
                Xerror("can't open");
                return;
        }
        pushredir(ROPEN, f, runq->code[runq->pc].i);
        runq->pc++;
        poplist();
}

void
Xrdwr(void)
{
        char *file;
        int f;

        switch(count(runq->argv->words)){
        default:
                Xerror1("<> requires singleton\n");
                return;
        case 0:
                Xerror1("<> requires file\n");
                return;
        case 1:
                break;
        }
        file = runq->argv->words->word;
        if((f = open(file, ORDWR))<0){
                pfmt(err, "%s: ", file);
                Xerror("can't open");
                return;
        }
        pushredir(ROPEN, f, runq->code[runq->pc].i);
        runq->pc++;
        poplist();
}

void
turfredir(void)
{
        while(runq->redir!=runq->startredir)
                Xpopredir();
}

void
Xpopredir(void)
{
        struct redir *rp = runq->redir;
        if(rp==0)
                panic("turfredir null!", 0);
        runq->redir = rp->next;
        if(rp->type==ROPEN)
                close(rp->from);
        efree((char *)rp);
}

void
Xreturn(void)
{
        struct thread *p = runq;
        turfredir();
        while(p->argv) poplist();
        codefree(p->code);
        runq = p->ret;
        efree((char *)p);
        if(runq==0)
                Exit(getstatus());
}

void
Xtrue(void)
{
        if(truestatus()) runq->pc++;
        else runq->pc = runq->code[runq->pc].i;
}

void
Xif(void)
{
        ifnot = 1;
        if(truestatus()) runq->pc++;
        else runq->pc = runq->code[runq->pc].i;
}

void
Xwastrue(void)
{
        ifnot = 0;
}

void
Xword(void)
{
        pushword(runq->code[runq->pc++].s);
}

void
Xwrite(void)
{
        char *file;
        int f;
        switch(count(runq->argv->words)){
        default:
                Xerror1("> requires singleton\n");
                return;
        case 0:
                Xerror1("> requires file\n");
                return;
        case 1:
                break;
        }
        file = runq->argv->words->word;
        if((f = Creat(file))<0){
                pfmt(err, "%s: ", file);
                Xerror("can't open");
                return;
        }
        pushredir(ROPEN, f, runq->code[runq->pc].i);
        runq->pc++;
        poplist();
}

char*
list2str(word *words)
{
        char *value, *s, *t;
        int len = 0;
        word *ap;
        for(ap = words;ap;ap = ap->next)
                len+=1+strlen(ap->word);
        value = emalloc(len+1);
        s = value;
        for(ap = words;ap;ap = ap->next){
                for(t = ap->word;*t;) *s++=*t++;
                *s++=' ';
        }
        if(s==value)
                *s='\0';
        else s[-1]='\0';
        return value;
}

void
Xmatch(void)
{
        word *p;
        char *subject;
        subject = list2str(runq->argv->words);
        setstatus("no match");
        for(p = runq->argv->next->words;p;p = p->next)
                if(match(subject, p->word, '\0')){
                        setstatus("");
                        break;
                }
        efree(subject);
        poplist();
        poplist();
}

void
Xcase(void)
{
        word *p;
        char *s;
        int ok = 0;
        s = list2str(runq->argv->next->words);
        for(p = runq->argv->words;p;p = p->next){
                if(match(s, p->word, '\0')){
                        ok = 1;
                        break;
                }
        }
        efree(s);
        if(ok)
                runq->pc++;
        else
                runq->pc = runq->code[runq->pc].i;
        poplist();
}

word*
conclist(word *lp, word *rp, word *tail)
{
        char *buf;
        word *v;
        if(lp->next || rp->next)
                tail = conclist(lp->next==0? lp: lp->next,
                        rp->next==0? rp: rp->next, tail);
        buf = emalloc(strlen(lp->word)+strlen((char *)rp->word)+1);
        strcpy(buf, lp->word);
        strcat(buf, rp->word);
        v = newword(buf, tail);
        efree(buf);
        return v;
}

void
Xconc(void)
{
        word *lp = runq->argv->words;
        word *rp = runq->argv->next->words;
        word *vp = runq->argv->next->next->words;
        int lc = count(lp), rc = count(rp);
        if(lc!=0 || rc!=0){
                if(lc==0 || rc==0){
                        Xerror1("null list in concatenation");
                        return;
                }
                if(lc!=1 && rc!=1 && lc!=rc){
                        Xerror1("mismatched list lengths in concatenation");
                        return;
                }
                vp = conclist(lp, rp, vp);
        }
        poplist();
        poplist();
        runq->argv->words = vp;
}

void
Xassign(void)
{
        var *v;
        if(count(runq->argv->words)!=1){
                Xerror1("variable name not singleton!");
                return;
        }
        deglob(runq->argv->words->word);
        v = vlook(runq->argv->words->word);
        poplist();
        globlist();
        freewords(v->val);
        v->val = runq->argv->words;
        v->changed = 1;
        runq->argv->words = 0;
        poplist();
}
/*
 * copy arglist a, adding the copy to the front of tail
 */

word*
copywords(word *a, word *tail)
{
        word *v = 0, **end;
        for(end=&v;a;a = a->next,end=&(*end)->next)
                *end = newword(a->word, 0);
        *end = tail;
        return v;
}

void
Xdol(void)
{
        word *a, *star;
        char *s, *t;
        int n;
        if(count(runq->argv->words)!=1){
                Xerror1("variable name not singleton!");
                return;
        }
        s = runq->argv->words->word;
        deglob(s);
        n = 0;
        for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
        a = runq->argv->next->words;
        if(n==0 || *t)
                a = copywords(vlook(s)->val, a);
        else{
                star = vlook("*")->val;
                if(star && 1<=n && n<=count(star)){
                        while(--n) star = star->next;
                        a = newword(star->word, a);
                }
        }
        poplist();
        runq->argv->words = a;
}

void
Xqdol(void)
{
        word *a, *p;
        char *s;
        int n;
        if(count(runq->argv->words)!=1){
                Xerror1("variable name not singleton!");
                return;
        }
        s = runq->argv->words->word;
        deglob(s);
        a = vlook(s)->val;
        poplist();
        n = count(a);
        if(n==0){
                pushword("");
                return;
        }
        for(p = a;p;p = p->next) n+=strlen(p->word);
        s = emalloc(n);
        if(a){
                strcpy(s, a->word);
                for(p = a->next;p;p = p->next){
                        strcat(s, " ");
                        strcat(s, p->word);
                }
        }
        else
                s[0]='\0';
        pushword(s);
        efree(s);
}

word*
copynwords(word *a, word *tail, int n)
{
        word *v, **end;
        
        v = 0;
        end = &v;
        while(n-- > 0){
                *end = newword(a->word, 0);
                end = &(*end)->next;
                a = a->next;
        }
        *end = tail;
        return v;
}

word*
subwords(word *val, int len, word *sub, word *a)
{
        int n, m;
        char *s;
        if(!sub)
                return a;
        a = subwords(val, len, sub->next, a);
        s = sub->word;
        deglob(s);
        m = 0;
        n = 0;
        while('0'<=*s && *s<='9')
                n = n*10+ *s++ -'0';
        if(*s == '-'){
                if(*++s == 0)
                        m = len - n;
                else{
                        while('0'<=*s && *s<='9')
                                m = m*10+ *s++ -'0';
                        m -= n;
                }
        }
        if(n<1 || n>len || m<0)
                return a;
        if(n+m>len)
                m = len-n;
        while(--n > 0)
                val = val->next;
        return copynwords(val, a, m+1);
}

void
Xsub(void)
{
        word *a, *v;
        char *s;
        if(count(runq->argv->next->words)!=1){
                Xerror1("variable name not singleton!");
                return;
        }
        s = runq->argv->next->words->word;
        deglob(s);
        a = runq->argv->next->next->words;
        v = vlook(s)->val;
        a = subwords(v, count(v), runq->argv->words, a);
        poplist();
        poplist();
        runq->argv->words = a;
}

void
Xcount(void)
{
        word *a;
        char *s, *t;
        int n;
        char num[12];
        if(count(runq->argv->words)!=1){
                Xerror1("variable name not singleton!");
                return;
        }
        s = runq->argv->words->word;
        deglob(s);
        n = 0;
        for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0';
        if(n==0 || *t){
                a = vlook(s)->val;
                inttoascii(num, count(a));
        }
        else{
                a = vlook("*")->val;
                inttoascii(num, a && 1<=n && n<=count(a)?1:0);
        }
        poplist();
        pushword(num);
}

void
Xlocal(void)
{
        if(count(runq->argv->words)!=1){
                Xerror1("variable name must be singleton\n");
                return;
        }
        deglob(runq->argv->words->word);
        runq->local = newvar(strdup(runq->argv->words->word), runq->local);
        poplist();
        globlist();
        runq->local->val = runq->argv->words;
        runq->local->changed = 1;
        runq->argv->words = 0;
        poplist();
}

void
Xunlocal(void)
{
        var *v = runq->local, *hid;
        if(v==0)
                panic("Xunlocal: no locals!", 0);
        runq->local = v->next;
        hid = vlook(v->name);
        hid->changed = 1;
        efree(v->name);
        freewords(v->val);
        efree((char *)v);
}

void
freewords(word *w)
{
        word *nw;
        while(w){
                efree(w->word);
                nw = w->next;
                efree((char *)w);
                w = nw;
        }
}

void
Xfn(void)
{
        var *v;
        word *a;
        int end;
        end = runq->code[runq->pc].i;
        globlist();
        for(a = runq->argv->words;a;a = a->next){
                v = gvlook(a->word);
                if(v->fn)
                        codefree(v->fn);
                v->fn = codecopy(runq->code);
                v->pc = runq->pc+2;
                v->fnchanged = 1;
        }
        runq->pc = end;
        poplist();
}

void
Xdelfn(void)
{
        var *v;
        word *a;
        for(a = runq->argv->words;a;a = a->next){
                v = gvlook(a->word);
                if(v->fn)
                        codefree(v->fn);
                v->fn = 0;
                v->fnchanged = 1;
        }
        poplist();
}

char*
concstatus(char *s, char *t)
{
        static char v[NSTATUS+1];
        int n = strlen(s);
        strncpy(v, s, NSTATUS);
        if(n<NSTATUS){
                v[n]='|';
                strncpy(v+n+1, t, NSTATUS-n-1);
        }
        v[NSTATUS]='\0';
        return v;
}

void
Xpipewait(void)
{
        char status[NSTATUS+1];
        if(runq->pid==-1)
                setstatus(concstatus(runq->status, getstatus()));
        else{
                strncpy(status, getstatus(), NSTATUS);
                status[NSTATUS]='\0';
                Waitfor(runq->pid, 1);
                runq->pid=-1;
                setstatus(concstatus(getstatus(), status));
        }
}

void
Xrdcmds(void)
{
        struct thread *p = runq;
        word *prompt;
        flush(err);
        nerror = 0;
        if(flag['s'] && !truestatus())
                pfmt(err, "status=%v\n", vlook("status")->val);
        if(runq->iflag){
                prompt = vlook("prompt")->val;
                if(prompt)
                        promptstr = prompt->word;
                else
                        promptstr="% ";
        }
        Noerror();
        if(yyparse()){
                if(!p->iflag || p->eof && !Eintr()){
                        if(p->cmdfile)
                                efree(p->cmdfile);
                        closeio(p->cmdfd);
                        Xreturn();      /* should this be omitted? */
                }
                else{
                        if(Eintr()){
                                pchr(err, '\n');
                                p->eof = 0;
                        }
                        --p->pc;        /* go back for next command */
                }
        }
        else{
                ntrap = 0;      /* avoid double-interrupts during blocked writes */
                --p->pc;        /* re-execute Xrdcmds after codebuf runs */
                start(codebuf, 1, runq->local);
        }
        freenodes();
}

void
Xerror(char *s)
{
        if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
                pfmt(err, "rc: %s: %r\n", s);
        else
                pfmt(err, "rc (%s): %s: %r\n", argv0, s);
        flush(err);
        setstatus("error");
        while(!runq->iflag) Xreturn();
}

void
Xerror1(char *s)
{
        if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0)
                pfmt(err, "rc: %s\n", s);
        else
                pfmt(err, "rc (%s): %s\n", argv0, s);
        flush(err);
        setstatus("error");
        while(!runq->iflag) Xreturn();
}

void
setstatus(char *s)
{
        setvar("status", newword(s, (word *)0));
}

char*
getstatus(void)
{
        var *status = vlook("status");
        return status->val?status->val->word:"";
}

int
truestatus(void)
{
        char *s;
        for(s = getstatus();*s;s++)
                if(*s!='|' && *s!='0')
                        return 0;
        return 1;
}

void
Xdelhere(void)
{
        Unlink(runq->code[runq->pc++].s);
}

void
Xfor(void)
{
        if(runq->argv->words==0){
                poplist();
                runq->pc = runq->code[runq->pc].i;
        }
        else{
                freelist(runq->local->val);
                runq->local->val = runq->argv->words;
                runq->local->changed = 1;
                runq->argv->words = runq->argv->words->next;
                runq->local->val->next = 0;
                runq->pc++;
        }
}

void
Xglob(void)
{
        globlist();
}