From d1a19f0d477a6249d8af9322317b8434b86260ea Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Wed, 13 Oct 2021 08:27:37 -0700 Subject: fix(email): bytes error. updated vendoring --- sys/cmd/rc/code.c | 520 ++++++++++++++++--------- sys/cmd/rc/code.dep | 166 -------- sys/cmd/rc/exec.c | 1077 ++++++++++++++++++++++++++++++++++++++++++++++----- sys/cmd/rc/glob.c | 412 ++++++++++++-------- sys/cmd/rc/io.c | 573 ++++++++++----------------- sys/cmd/rc/lex.c | 688 +++++++++++++++----------------- sys/cmd/rc/main.c | 86 ---- sys/cmd/rc/parse.c | 496 ------------------------ sys/cmd/rc/rc.h | 413 ++++++-------------- sys/cmd/rc/rules.mk | 37 +- sys/cmd/rc/simple.c | 507 +++++++++++++++++++++++- sys/cmd/rc/tree.c | 214 +++++----- sys/cmd/rc/util.c | 40 -- sys/cmd/rc/var.c | 230 ++++++----- sys/cmd/rc/word.c | 64 --- sys/cmd/rules.mk | 3 + 16 files changed, 2961 insertions(+), 2565 deletions(-) delete mode 100644 sys/cmd/rc/code.dep delete mode 100644 sys/cmd/rc/main.c delete mode 100644 sys/cmd/rc/parse.c delete mode 100644 sys/cmd/rc/util.c delete mode 100644 sys/cmd/rc/word.c (limited to 'sys') diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c index edf47cf..fbb5435 100644 --- a/sys/cmd/rc/code.c +++ b/sys/cmd/rc/code.c @@ -1,214 +1,267 @@ #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] -#define delcode 100 -#define c0 t->child[0] -#define c1 t->child[1] -#define c2 t->child[2] +int codep, ncode; -#define emitf(x) ((code.ip!=code.end || morecode()), code.ip++->f = (x), code.ip) -#define emiti(x) ((code.ip!=code.end || morecode()), code.ip++->i = (x), code.ip) -#define emits(x) ((code.ip!=code.end || morecode()), code.ip++->s = (x), code.ip) +#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++) -static struct -{ - int cap; - Code *buf, *ip, *end; -} code; +void stuffdot(int); +char *fnstr(tree*); +void outcode(tree*, int); +void codeswitch(tree*, int); +int iscase(tree*); +code *codecopy(code*); +void codefree(code*); -static int morecode(void) { - code.cap += delcode; - code.buf = erealloc(code.buf, code.cap*sizeof(*code.buf)); - code.end = code.ip + delcode; - memset(code.ip, 0, delcode*sizeof(*code.buf)); - - return 0; + 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]); + memset(codebuf+ncode-100, 0, 100*sizeof codebuf[0]); + return 0; } -static void -stuffdot(Code *p) +stuffdot(int a) { - int a; + if(a<0 || codep<=a) + panic("Bad address %d in stuffdot", a); + codebuf[a].i = codep; +} - a = p - code.buf; - if (code.ip <= p || p < code.buf) - panic("bad address %d in stuffdot", a); - code.buf[a].i = code.ip-code.buf; +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; } -static void -rcc(Tree *t, int eflag) +cleanhere(char *f) { - Code *p, *q; - Tree *tt; + emitf(Xdelhere); + emits(strdup(f)); +} - if (!t) - return; +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; +} - switch(t->type) { - default: - pfmt(errio, "bad type %d in rc compiler\n", t->type); +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 Tdol: + case '$': emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xdol); break; - case Tquote: + case '"': emitf(Xmark); - rcc(c0, eflag); - emitf(Xflatten); + outcode(c0, eflag); + emitf(Xqdol); break; - case Tsub: + case SUB: emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xmark); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xsub); break; - case Tand: + case '&': emitf(Xasync); - p = emiti(0); - rcc(c0, eflag); - emitf(Xexit); - stuffdot(p); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); break; - case Tsemi: - rcc(c0, eflag); - rcc(c1, eflag); + case ';': + outcode(c0, eflag); + outcode(c1, eflag); break; - case Tcarot: + case '^': emitf(Xmark); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xmark); - rcc(c0, eflag); - emitf(Xcat); + outcode(c0, eflag); + emitf(Xconc); break; - case Ttick: - emitf(Xcmdsub); - p = emiti(0); - rcc(c0, 0); - emitf(Xexit); - stuffdot(p); + case '`': + emitf(Xbackq); + if(havefork){ + p = emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); break; - case Tandand: - rcc(c0, 0); + case ANDAND: + outcode(c0, 0); emitf(Xtrue); p = emiti(0); - rcc(c1, eflag); + outcode(c1, eflag); stuffdot(p); break; - case Targs: - rcc(c1, eflag); - rcc(c0, eflag); + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); break; - case Tbang: - rcc(c0, eflag); - emitf(Xnegate); + case BANG: + outcode(c0, eflag); + emitf(Xbang); break; - case Tparen: - case Tbrace: - rcc(c0, eflag); + case PCMD: + case BRACE: + outcode(c0, eflag); break; - case Tcount: + case COUNT: emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xcount); break; - case Tfunc: + case FN: emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); if(c1){ - emitf(Xfunc); + emitf(Xfn); p = emiti(0); emits(fnstr(c1)); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xunlocal); /* get rid of $* */ - emitf(Xkill); + emitf(Xreturn); stuffdot(p); - } else - emitf(Xunfunc); + } + else + emitf(Xdelfn); break; - case Tif: - rcc(c0, 0); + case IF: + outcode(c0, 0); emitf(Xif); p = emiti(0); - rcc(c1, eflag); - // emitf(Xwastrue); + outcode(c1, eflag); + emitf(Xwastrue); stuffdot(p); break; - // case Telse: - // if(!runq->iflast) - // rcerror("`else' does not follow `if(...)'"); - // emitf(Xelse); - // p = emiti(0); - // rcc(c0, eflag); - // stuffdot(p); - // break; - case Toror: - rcc(c0, 0); + 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); - rcc(c1, eflag); + outcode(c1, eflag); stuffdot(p); break; - case Tpcmd: - rcc(c0, eflag); + case PAREN: + outcode(c0, eflag); break; - case Tsimple: + case SIMPLE: emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xsimple); if(eflag) emitf(Xeflag); break; - case Tsubshell: + case SUBSHELL: emitf(Xsubshell); - p = emiti(0); - rcc(c0, eflag); - emitf(Xexit); - stuffdot(p); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); if(eflag) emitf(Xeflag); break; - case Tswitch: + case SWITCH: codeswitch(t, eflag); break; - case Ttwiddle: + case TWIDDLE: emitf(Xmark); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xmatch); if(eflag) emitf(Xeflag); break; - case Twhile: - q = code.ip; - rcc(c0, 0); - if(q==code.ip) + case WHILE: + q = codep; + outcode(c0, 0); + if(q==codep) emitf(Xsettrue); /* empty condition == while(true) */ emitf(Xtrue); p = emiti(0); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xjump); - emiti(q-code.buf); + emiti(q); stuffdot(p); break; - case Twords: - rcc(c1, eflag); - rcc(c0, eflag); + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); break; - case Tfor: + case FOR: emitf(Xmark); if(c1){ - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xglob); - } else{ + } + else{ emitf(Xmark); emitf(Xword); emits(strdup("*")); @@ -216,120 +269,221 @@ rcc(Tree *t, int eflag) } emitf(Xmark); /* dummy value for Xlocal */ emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xlocal); p = emitf(Xfor); q = emiti(0); - rcc(c2, eflag); + outcode(c2, eflag); emitf(Xjump); - emiti(p-code.buf); + emiti(p); stuffdot(q); emitf(Xunlocal); break; - case Tword: + case WORD: emitf(Xword); emits(strdup(t->str)); break; - case Tdup: - if(t->redir.type == Rdupfd){ + case DUP: + if(t->rtype==DUPFD){ emitf(Xdup); - emiti(t->redir.fd[0]); - emiti(t->redir.fd[1]); - } else{ + emiti(t->fd0); + emiti(t->fd1); + } + else{ emitf(Xclose); - emiti(t->redir.fd[0]); + emiti(t->fd0); } - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xpopredir); break; - case Tpipefd: + case PIPEFD: emitf(Xpipefd); - emiti(t->redir.type); - p = emiti(0); - rcc(c0, eflag); - emitf(Xexit); - stuffdot(p); + emiti(t->rtype); + if(havefork){ + p = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + } break; - case Tredir: + case REDIR: emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xglob); - switch(t->redir.type){ - case Rappend: + switch(t->rtype){ + case APPEND: emitf(Xappend); break; - case Rwrite: + case WRITE: emitf(Xwrite); break; - case Rread: - case Rhere: + case READ: + case HERE: emitf(Xread); break; - case Rrdwr: + case RDWR: emitf(Xrdwr); break; } - emiti(t->redir.fd[0]); - rcc(c1, eflag); + emiti(t->fd0); + outcode(c1, eflag); emitf(Xpopredir); break; - case Teq: + case '=': tt = t; - for(;t && t->type==Teq;t = c2); + for(;t && t->type=='=';t = c2); if(t){ - for(t = tt;t->type==Teq;t = c2){ + for(t = tt;t->type=='=';t = c2){ emitf(Xmark); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xlocal); } - rcc(t, eflag); - for(t = tt; t->type==Teq; t = c2) + outcode(t, eflag); + for(t = tt; t->type=='='; t = c2) emitf(Xunlocal); - } else{ + } + else{ for(t = tt;t;t = c2){ emitf(Xmark); - rcc(c1, eflag); + outcode(c1, eflag); emitf(Xmark); - rcc(c0, eflag); + outcode(c0, eflag); emitf(Xassign); } } t = tt; /* so tests below will work */ break; - case Tpipe: + case PIPE: emitf(Xpipe); - emiti(t->redir.fd[0]); - emiti(t->redir.fd[1]); - p = emiti(0); - q = emiti(0); - rcc(c0, eflag); - emitf(Xexit); - stuffdot(p); - rcc(c1, eflag); - emitf(Xkill); + emiti(t->fd0); + emiti(t->fd1); + if(havefork){ + p = emiti(0); + q = emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + q = emiti(0); + } + outcode(c1, eflag); + emitf(Xreturn); stuffdot(q); emitf(Xpipewait); break; } - if(t->type!=Telse && t->type!=Tsemi) - shell->iflast = t->type==Tif; - else if (c0) - shell->iflast = c0->type==Tif; + 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); } -Code* -compile(Tree *t) +int +iscase(tree *t) { - code.cap = delcode; - code.buf = code.ip = emalloc(code.cap*sizeof *code.buf); - code.end = code.ip + code.cap; + if(t->type!=SIMPLE) + return 0; + do t = c0; while(t->type==ARGLIST); + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} - emiti(0); - rcc(t, 0); - emitf(Xkill); - emitf(nil); +code* +codecopy(code *cp) +{ + cp[0].i++; + return cp; +} - return code.buf; +void +codefree(code *cp) +{ + code *p; + if(--cp[0].i!=0) + return; + for(p = cp+1;p->f;p++){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xrdwr + || p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) p++; + else if(p->f==Xdup || p->f==Xpipefd) p+=2; + else if(p->f==Xpipe) p+=4; + else if(p->f==Xword || p->f==Xdelhere) efree((++p)->s); + else if(p->f==Xfn){ + efree(p[2].s); + p+=2; + } + } + efree((char *)cp); } diff --git a/sys/cmd/rc/code.dep b/sys/cmd/rc/code.dep deleted file mode 100644 index 7fdd4bc..0000000 --- a/sys/cmd/rc/code.dep +++ /dev/null @@ -1,166 +0,0 @@ -#if 0 -// simple example code -error -cd(Args args) -{ - switch (args.len) { - case 0: - errorf("reached cd with no arguments!"); - return 1; - case 1: - one: - errorf("sh: expected argument to command 'cd'"); - return 1; - case 2: - if (args.a[1] == nil) - goto one; - break; - default: - errorf("sh: too many arguments to command 'cd'"); - return 1; - } - if (chdir(args.a[1])) - errorf("cd fail: %s", strerror(errno)); - - return 0; -} - -error -quit(Args args) -{ - exit(0); -} - -Builtin builtins[] = { - { "cd", cd }, - { "exit", quit }, -}; - -void -clear(Header *arr) -{ - arr->len = 0; -} - -int -readline(Code *code) -{ - int n, b; - - n = code->len; -getchar: - if (code->len >= code->cap) { - code->cap += 100; - code->s = realloc(code->s, code->cap); - } - /* TODO: unicode? */ - switch ((b = getchar())) { - case EOF: - n = -1; - goto null; - case '\n': - n = code->len - n; - null: - code->s[code->len] = '\0'; - break; - default: - code->s[code->len++] = b; - goto getchar; - } - - return n; -} - -/* TODO: unicode */ -int -readargs(Code code, Args *args) -{ - if (args->a) - clear(&args->hdr); - else { - args->cap += 20; - args->a = realloc(args->a, args->cap); - } - - args->a[args->len++] = code.s; - while (*code.s) { - if (!isspace(*code.s++)) - continue; - - code.s[-1] = '\0'; - /* consume all remaining space */ - while (isspace(*code.s)) - code.s++; - - if (args->len >= args->cap-1) { - args->cap += 20; - args->a = realloc(args->a, args->cap); - } - args->a[args->len++] = code.s; - } - /* nil acts as a sentinel value */ - args->a[args->len] = nil; - - return args->len; -} - -error -execute(Args args) -{ - int i, status; - pid_t cid, wid; - - for (i = 0; i < arrlen(builtins); i++) { - if (strcmp(args.a[0], builtins[i].cmd) == 0) - return builtins[i].func(args); - } - - if ((cid = fork()) == 0) { - if (execvp(args.a[0], args.a) == -1) - errorf("exec failed: %s", strerror(errno)); - exit(1); - } else if (cid > 0) - do - wid = waitpid(cid, &status, WUNTRACED); - while (!WIFEXITED(status) && !WIFSIGNALED(status)); - else - errorf("fork failed: %s", strerror(errno)); - - return status; -} - -static -void -flush(void) -{ - io·flush(stdout); -} - -static -void -prompt(void) -{ - printf(";"); - flush(); -} - -int -main(int argc, char *argv[]) -{ - int i, err, n; - Code code = {0}; - Args args = {0}; - - ARGBEGIN { - } ARGEND; - - do { - clear(&code.hdr); - prompt(); - - n = readline(&code); - readargs(code, &args); - err = execute(args); - } while (!err && n > 0); -} -#endif diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c index 0155b22..15d1cdf 100644 --- a/sys/cmd/rc/exec.c +++ b/sys/cmd/rc/exec.c @@ -1,139 +1,1014 @@ #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"; +int ndot; +int lastc; +int kidpid; +int mypid; -#define W0 shell->stack->words -// ----------------------------------------------------------------------- -// helper functions +thread *runq; +code *codebuf; /* compiler output */ +int ntrap; /* number of outstanding traps */ +int trap[NSIG]; /* number of outstanding traps per type */ + +int eflagok; /* kludge flag so that -e doesn't exit in startup */ + +char **argp; +char **args; +int nerror; /* number of errors encountered during compilation */ -static void -setstatus(char *s) +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) { - setvar("status", newword(s, nil)); + word *p = new(word); + p->word = strdup(wd); + p->next = next; + return p; } -static void -pushredir(int type, int from, int to) +pushword(char *wd) { - Redir *r; + if(runq->argv==0) + panic("pushword but no argv!", 0); + runq->argv->words = newword(wd, runq->argv->words); +} - alloc(r); - r->type = type; - r->from = from; - r->to = to; - r->link = shell->redir, shell->redir = r; +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); } -// ----------------------------------------------------------------------- -// interpreter functions +void +freelist(word *w) +{ + word *nw; + while(w){ + nw = w->next; + efree(w->word); + efree((char *)w); + w = nw; + } +} void -Xerror(char *s) +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) { - if(!strcmp(argv0, "rc")||!strcmp(argv0, "/bin/rc")) - pfmt(errio, "rc: %s: %r\n", s); - else - pfmt(errio, "rc (%s): %s: %r\n", argv0, s); - flush(&errio); + redir * rp = new(redir); + rp->type = type; + rp->from = from; + rp->to = to; + rp->next = runq->redir; + runq->redir = rp; +} - setstatus("error"); - while(!shell->interactive) - Xkill(); +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; + v->changefn = 0; + 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, "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()); + pathinit(); + 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(); + } + return 0; /* not reached; silence OS X Lion gcc */ +} +/* + * Opcode routines + * Arguments on stack (...) + * Arguments in line [...] + * Code in line with jump around {...} + * + * Xappend(file)[fd] open file to append + * Xassign(name, val) assign val to name + * Xasync{... Xexit} make thread for {}, no wait + * Xbackq{... Xreturn} make thread for {}, push stdout + * Xbang complement condition + * Xcase(pat, value){...} exec code on match, leave (value) on + * stack + * Xclose[i] close file descriptor + * Xconc(left, right) concatenate, push results + * Xcount(name) push var count + * Xdelfn(name) delete function definition + * Xdeltraps(names) delete named traps + * Xdol(name) get variable value + * Xqdol(name) concatenate variable components + * Xdup[i j] dup file descriptor + * Xexit rc exits with status + * Xfalse{...} execute {} if false + * Xfn(name){... Xreturn} define function + * Xfor(var, list){... Xreturn} for loop + * Xjump[addr] goto + * Xlocal(name, val) create local variable, assign value + * Xmark mark stack + * Xmatch(pat, str) match pattern, set status + * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, + * wait for both + * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, + * depending on type), push /dev/fd/?? + * Xpopm(value) pop value from stack + * Xrdwr(file)[fd] open file for reading and writing + * Xread(file)[fd] open file to read + * Xsettraps(names){... Xreturn} define trap functions + * Xshowtraps print trap list + * Xsimple(args) run command and wait + * Xreturn kill thread + * Xsubshell{... Xexit} execute {} in a subshell and wait + * Xtrue{...} execute {} if true + * Xunlocal delete local variable + * Xword[string] push string + * Xwrite(file)[fd] open file to write + */ void Xappend(void) { - int fd; - char *path; + 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(); +} - switch(count(W0)) { - default: - Xerror(">> requires a singleton list"); - return; - case 0: - Xerror(">> requires one file"); - return; - case 1: - ; - } +void +Xsettrue(void) +{ + setstatus(""); +} - path = shell->stack->words->word; - if ((fd=open(path, 1))< 0 && (fd=creat(path, 0666L))<0) { - pfmt(errio, "%s: ", path); - Xerror("can't open"); - return; - } - lseek(fd, 0L, 2); - pushredir(Fopen, fd, shell->ip++->i); - poplist(); +void +Xbang(void) +{ + setstatus(truestatus()?"false":""); } void -Xassign(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) { - Var *v; - if(count(W0)!=1) { - Xerror("variable name not singleton"); - return; - } - unglob(W0->word); - v = vlookup(W0->word); - poplist(); - globlist(); - freelist(v->val); + if(truestatus()) runq->pc = runq->code[runq->pc].i; + else runq->pc++; +} +int ifnot; /* dynamic if not flag */ - v->val = W0; - if(v->update) - v->update(v); - W0 = nil; - poplist(); +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(); + 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(shell->ip++->s); -} - -void Xasync(void); -void Xcat(void); -void Xclose(void); -void Xcmdsub(void); -void Xcount(void); -void Xdol(void); -void Xdup(void); -void Xexit(void); -void Xfalse(void); -void Xflatten(void); -void Xfor(void); -void Xfunc(void); -void Xglob(void); -void Xif(void); -void Xjump(void); -void Xkill(void); -void Xlocal(void); -void Xmark(void); -void Xmatch(void); -void Xnegate(void); -void Xpipe(void); -void Xpipefd(void); -void Xpipewait(void); -void Xpop(void); -void Xpopredir(void); -void Xrdwr(void); -void Xread(void); -void Xsub(void); -void Xsimple(void); -void Xsubshell(void); -void Xtrue(void); -void Xunfunc(void); -void Xunlocal(void); -void Xword(void); -void Xwrite(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; + if(v->changefn) + v->changefn(v); + 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); + 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(); +} + +char* +concstatus(char *s, char *t) +{ + static char v[NSTATUS+1]; + int n = strlen(s); + strncpy(v, s, NSTATUS); + if(npid==-1) + setstatus(concstatus(runq->status, getstatus())); + else{ + strncpy(status, getstatus(), NSTATUS); + status[NSTATUS]='\0'; + Waitfor(runq->pid, 1); + runq->pid=-1; + setstatus(concstatus(getstatus(), status)); + } +} + +void +Xrdcmds(void) +{ + struct thread *p = runq; + word *prompt; + flush(err); + nerror = 0; + if(flag['s'] && !truestatus()) + pfmt(err, "status=%v\n", vlook("status")->val); + if(runq->iflag){ + prompt = vlook("prompt")->val; + if(prompt) + promptstr = prompt->word; + else + promptstr="% "; + } + Noerror(); + if(yyparse()){ + if(!p->iflag || p->eof && !Eintr()){ + if(p->cmdfile) + efree(p->cmdfile); + closeio(p->cmdfd); + Xreturn(); /* should this be omitted? */ + } + else{ + if(Eintr()){ + pchr(err, '\n'); + p->eof = 0; + } + --p->pc; /* go back for next command */ + } + } + else{ + ntrap = 0; /* avoid double-interrupts during blocked writes */ + --p->pc; /* re-execute Xrdcmds after codebuf runs */ + start(codebuf, 1, runq->local); + } + freenodes(); +} + +void +Xerror(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s: %r\n", s); + else + pfmt(err, "rc (%s): %s: %r\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} + +void +Xerror1(char *s) +{ + if(strcmp(argv0, "rc")==0 || strcmp(argv0, "/bin/rc")==0) + pfmt(err, "rc: %s\n", s); + else + pfmt(err, "rc (%s): %s\n", argv0, s); + flush(err); + setstatus("error"); + while(!runq->iflag) Xreturn(); +} + +void +setstatus(char *s) +{ + setvar("status", newword(s, (word *)0)); +} + +char* +getstatus(void) +{ + var *status = vlook("status"); + return status->val?status->val->word:""; +} + +int +truestatus(void) +{ + char *s; + for(s = getstatus();*s;s++) + if(*s!='|' && *s!='0') + return 0; + return 1; +} + +void +Xdelhere(void) +{ + Unlink(runq->code[runq->pc++].s); +} + +void +Xfor(void) +{ + if(runq->argv->words==0){ + poplist(); + runq->pc = runq->code[runq->pc].i; + } + else{ + freelist(runq->local->val); + runq->local->val = runq->argv->words; + runq->local->changed = 1; + runq->argv->words = runq->argv->words->next; + runq->local->val->next = 0; + runq->pc++; + } +} + +void +Xglob(void) +{ + globlist(); +} diff --git a/sys/cmd/rc/glob.c b/sys/cmd/rc/glob.c index 95b2ef3..baea44d 100644 --- a/sys/cmd/rc/glob.c +++ b/sys/cmd/rc/glob.c @@ -1,199 +1,267 @@ #include "rc.h" -#include - -static Word *matches; -static char buffer[6*1024]; - -// ----------------------------------------------------------------------- -// main exports +#include "exec.h" +#include "fns.h" +char *globname; +struct word *globv; +/* + * delete all the GLOB marks from s, in place + */ void -unglob(char *s) +deglob(char *s) { - char *t = s; - do { - if(*t==GLOB) - t++; - *s++ = *t; - } while(*t++); + char *t = s; + do{ + if(*t==GLOB) + t++; + *s++=*t; + }while(*t++); } -/* - * inspiration from rsc's blog post - * modified for utf8 sequences and character classes - * returns 1 if string matches pattern is found, 0 otherwise - */ -static int -match(char *s, char *p) +globcmp(const void *s, const void *t) { - int c, ns, np; - rune sr, pr, lo, tr, hi; - char *sb = s, *ss = s, *pp = p; - while(*s || *p){ - if(*p){ - ns = utf8·bytetorune(&sr, s); - np = utf8·bytetorune(&pr, p); - - if(pr==GLOB){ - np = utf8·bytetorune(&pr, ++p); - switch(pr){ - case '?': /* single match */ - if(*s){ - p+=np, s+=ns; - continue; - } - case '[': /* class match */ - np = utf8·bytetorune(&pr, ++p); - if((c = (pr == '~'))) - np = utf8·bytetorune(&pr, ++p); - - lo = pr; - while(lo != ']' && *p){ - utf8·bytetorune(&tr, p+np); /* peek ahead */ - if(tr != '-') - hi = lo; - else { - p += np + 1, np = utf8·bytetorune(&hi, p); - if(!hi) /* we hit a syntax error */ - return 0; - if(hi < lo) - tr = hi, hi = lo, lo = tr; - } - if(c ^ (lo<=sr && sr<= hi)) - goto match; - p += np, np = utf8·bytetorune(&lo, p); - } - return 0; - match: - while (*p++ != ']' && *p); /* just iterate byte-wise */ - s += ns; - continue; - case '*': /* zero-or-more match */ - pp = p-1, ss = s+ns; - p++; - continue; - case GLOB: - if (sr != GLOB) - return 0; - s++, p++; - continue; - default: - panic("unrecognized glob operation", pr); - } - } - - if (sr==pr){ - s+=ns, p+=np; - continue; - } - } - /* hit end of pattern with no match, restart at last star */ - if (ss > sb) { - if (!*ss) /* hit end of string while matching a star */ - return 1; - - s = ss, p = pp; - continue; - } - /* mismatch */ - return 0; - } - return 1; + return strcmp(*(char**)s, *(char**)t); } -static void -globdir(char *p, char *path, int fd) +globsort(word *left, word *right) { - DIR *d = nil; - char *g; /* pattern offset (base of new GLOB) */ - char *b; /* pointer into path */ - int i, j; - struct dirent *e; - - if(!*p) { - printf("making path %s\n", path); - matches = newword(buffer, matches); - return; - } - - if((fd = openat(fd, path[0]?path:".", O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) - return; - d = fdopendir(fd); - - for(g = p, b = path; *g; b++) { - if(*g==GLOB) - break; - *b=*g++; - if(*b == '/') { - *b = 0; - /* open new directory (close if we have opened another already */ - if ((fd = openat(fd, path, O_RDONLY|O_CLOEXEC|O_DIRECTORY)) < 0) - goto cleanup; - closedir(d); - d = fdopendir(fd); - *b = '/'; - path = b, p = g; - } - } + char **list; + word *a; + int n = 0; + for(a = left;a!=right;a = a->next) n++; + list = (char **)emalloc(n*sizeof(char *)); + for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word; + qsort((void *)list, n, sizeof(void *), globcmp); + for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n]; + efree((char *)list); +} +/* + * Push names prefixed by globname and suffixed by a match of p onto the astack. + * namep points to the end of the prefix in globname. + */ - /* if we are at the end of the pattern, check if name exists */ - if(!*g) { - *b = 0; - if(faccessat(fd, path, F_OK, AT_SYMLINK_NOFOLLOW) == 0) - matches = newword(buffer, matches); - goto cleanup; - } +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, *newp=='/')){ + if(matchfn(namep, p)){ + for(t = namep;*t;t++); + globdir(newp, t); + } + } + Closedir(f); +} +/* + * Push all file names matched by p on the current thread's stack. + * If there are no matches, the list consists of p. + */ - /* we have a non-trivial pattern to match */ - /* partition on the next directory */ - while(*g && *g!='/') - g++; +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 + */ - if(*g){ - j = 1; - *g = 0; - } else - j = 0; +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]; + } + if(fourbyte(*p)){ + if(p[1]!=q[1]) + return 0; + if(p[1]=='\0') + return 1; + if(p[2]!=q[2]) + return 0; + if(p[2]=='\0') + return 1; + return p[3]==q[3]; + } + return 1; +} +/* + * Return a pointer to the next utf code in the string, + * not jumping past nuls in broken utf codes! + */ - while((e = readdir(d))) { - if (e->d_name[0] == '.') - if (e->d_name[1] == 0 || /* . */ - (e->d_name[1] == '.' && e->d_name[2] == 0)) /* .. */ - continue; +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; + if(fourbyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p[3]=='\0'?p+3:p+4; + return p+1; +} +/* + * Convert the utf code at *p to a unicode value + */ - for(i=0;e->d_name[i];i++) - b[i]=e->d_name[i]; - b[i]=0; +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); + if(fourbyte(u)) return (u<<18)|((p[1]&0x3f)<<12)|((p[2]&0x3f)<<6)|(p[3]&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 + */ - if(match(path, p)) - globdir(g+j, b, fd); - } +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, '/'); +} - printf("successful\n"); -cleanup: - printf("cleaning up\n"); - /* NOTE: a successful closedir also closes the file descriptor */ - closedir(d); - return; +int +match(char *s, char *p, int stop) +{ + int compl, hit, lo, hi, t, c; + for(;*p!=stop && *p!='\0';s = nextutf(s),p = nextutf(p)){ + if(*p!=GLOB){ + if(!equtf(p, s)) return 0; + } + else switch(*++p){ + case GLOB: + if(*s!=GLOB) + return 0; + break; + case '*': + for(;;){ + if(match(s, nextutf(p), stop)) return 1; + if(!*s) + break; + s = nextutf(s); + } + return 0; + case '?': + if(*s=='\0') + return 0; + break; + case '[': + if(*s=='\0') + return 0; + c = unicode(s); + p++; + compl=*p=='~'; + if(compl) + p++; + hit = 0; + while(*p!=']'){ + if(*p=='\0') + return 0; /* syntax error */ + lo = unicode(p); + p = nextutf(p); + if(*p!='-') + hi = lo; + else{ + p++; + if(*p=='\0') + return 0; /* syntax error */ + hi = unicode(p); + p = nextutf(p); + if(hinext); + glob(gl->word); + } } -#if 0 -int -main() +void +globlist(void) { - errio = openfd(2); - glob("\x01*"); - pval(errio, matches); - flush(&errio); + 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; + } } -#endif diff --git a/sys/cmd/rc/io.c b/sys/cmd/rc/io.c index e06bfcc..3e17369 100644 --- a/sys/cmd/rc/io.c +++ b/sys/cmd/rc/io.c @@ -1,446 +1,263 @@ +#include #include "rc.h" +#include "exec.h" +#include "io.h" +#include "fns.h" -#define c0 t->child[0] -#define c1 t->child[1] -#define c2 t->child[2] +io *err; +int pfmtnest = 0; -#undef bufsize -#define bufsize 512 -#define strsize 100 - -//------------------------------------------------------------------------ -// buffer operations - -/* open file */ -Io* -openfd(int fd) +void +pfmt(io *f, char *fmt, ...) { - Io *f; - - f = emalloc(sizeof *f + bufsize); - f->fd = fd; - f->b = f->e = f->buf; - return f; + 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': + pptr(f, va_arg(ap, void*)); + break; + case 'Q': + pquo(f, va_arg(ap, char *)); + break; + case 'q': + pwrd(f, va_arg(ap, char *)); + break; + case 'r': + rerrstr(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); } -/* open string */ -Io* -openstr(void) +void +pchr(io *b, int c) { - Io *f; - - f = emalloc(sizeof *f + strsize + 1); - f->fd = -1; - f->b = f->buf; - f->e = f->buf+strsize; - memset(f->b, 0, strsize+1); - - return f; + if(b->bufp==b->ebuf) + fullbuf(b, c); + else *b->bufp++=c; } -/* open core (not nil terminated) */ -Io* -opencore(int len, char *s) +int +rchr(io *b) { - Io *f; - - f = emalloc(sizeof *f + len); - f->fd = -1; - f->b = f->buf; - f->e = f->buf+len; - memcpy(f->b, s, len); - - return f; + if(b->bufp==b->ebuf) + return emptybuf(b); + return *b->bufp++ & 0xFF; } void -rewindio(Io *f) +pquo(io *f, char *s) { - if (f->fd < 0) - f->b = f->buf; - else { - f->b = f->e = f->buf; - lseek(f->fd, 0, 0); - } + pchr(f, '\''); + for(;*s;s++) + if(*s=='\'') + pfmt(f, "''"); + else pchr(f, *s); + pchr(f, '\''); } void -closeio(Io *f) +pwrd(io *f, char *s) { - if (f->fd >= 0) - close(f->fd); - - efree(f); + char *t; + for(t = s;*t;t++) if(!wordchr(*t)) break; + if(t==s || *t) + pquo(f, s); + else pstr(f, s); } -/* has the chance to realloc */ void -flush(Io **fp) +pptr(io *f, void *v) { - int n; - char *s; - Io *f; + int n; + uintptr p; - f = *fp; - if (f->fd < 0) { - n = f->e - f->b; - f = erealloc(f, sizeof *f + n + strsize + 1); - if (!f) - panic("can't realloc %d bytes in flush", n+strsize+1); - f->b = f->buf+n; - f->e = f->buf+n+strsize; - memset(f->b, 0, strsize+1); - } else { - n = f->b - f->buf; - if (n && write(f->fd, f->buf, n) < 0) { - write(3, "write error\n", 12); - // if (ntrap) - // dotrap(); - } - f->b = f->buf; - f->e = f->buf + bufsize; - } + p = (uintptr)v; + if(sizeof(uintptr) == sizeof(uvlong) && p>>32) + for(n = 60;n>=32;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); - *fp = f; + for(n = 28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); } -//------------------------------------------------------------------------ -// read from io - -int -rchr(Io *f) -{ - int n; - if (f->b == f->e) { - if (f->fd < 0 || (n = read(f->fd, f->buf, bufsize)) <= 0) - return EOF; - - f->b = f->buf; - f->e = f->b + n; - } - - return *f->b++&0xFF; -} - -//------------------------------------------------------------------------ -// printf functionality - -/* character literal */ -int -pchr(Io *f, int c) +void +pstr(io *f, char *s) { - if (f->b == f->e) - flush(&f); - - return *f->b++=c; + if(s==0) + s="(null)"; + while(*s) pchr(f, *s++); } -/* quote */ void -pquo(Io *f, char *s) +pdec(io *f, int n) { - pchr(f, '\''); - for (; *s; s++) - if (*s == '\'') - pfmt(f, "''"); - else - pchr(f, *s); - pchr(f, '\''); + if(n<0){ + if(n!=INT_MIN){ + pchr(f, '-'); + pdec(f, -n); + return; + } + /* n is two's complement minimum integer */ + n = -(INT_MIN+1); + pchr(f, '-'); + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + if(n>9) + pdec(f, n/10); + pchr(f, n%10+'0'); } -/* word */ void -pwrd(Io *f, char *s) +poct(io *f, unsigned n) { - char *t; - for(t = s; *t; t++) - if(!wordchr(*t)) - break; - if(t == s || *t) - pquo(f, s); - else - pstr(f, s); + if(n>7) + poct(f, n>>3); + pchr(f, (n&7)+'0'); } -/* pointer */ void -pptr(Io *f, void *v) +pval(io *f, word *a) { - int n; - uintptr p; - - if (!v) { - pstr(f, ""); - return; - } - - p = (uintptr)v; - if ((sizeof(uintptr) == sizeof(uvlong)) && p >>32) - for (n = 60; n >= 32; n-=4) - pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); - for (n = 28; n >= 0; n-=4) - pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); + if(a){ + while(a->next && a->next->word){ + pwrd(f, a->word); + pchr(f, ' '); + a = a->next; + } + pwrd(f, a->word); + } } -/* string */ -void -pstr(Io *f, char *s) +int +fullbuf(io *f, int c) { - if(!s || !s[0]) - s = ""; - - while(*s) - pchr(f, *s++); + flush(f); + return *f->bufp++=c; } -/* decimal */ void -pdec(Io *f, int n) +flush(io *f) { - if (n < 0) { - n = -n; - pchr(f, '-'); - if (n >= 0) { - pdec(f, n); - return; - } - n = 1 - n; - pdec(f, n/10); - pchr(f, n%10+'1'); - return; - } - - if (n > 9) - pdec(f, n/10); - pchr(f, n%10+'0'); + 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; + } } -/* octal */ -void -poct(Io *f, uint n) +io* +openfd(int fd) { - if (n > 7) - poct(f, n>>3); - pchr(f, (n&7)+'0'); + io *f = new(struct io); + f->fd = fd; + f->bufp = f->ebuf = f->buf; + f->strp = 0; + return f; } -/* value */ -void -pval(Io *f, Word *a) +io* +openstr(void) { - if(a) { - while(a->link && a->link->word) { - pwrd(f, a->word); - pchr(f, ' '); - a = a->link; - } - pwrd(f, a->word); - } + 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. + */ -/* tree */ -static -void -pdeglob(Io *f, char *s) +io* +opencore(char *s, int len) { - while(*s){ - if(*s==GLOB) - s++; - pchr(f, *s++); - } + 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 -pcmd(Io *f, Tree *t) +iorewind(io *io) { - if(!t) - return; - - switch(t->type){ - default: pfmt(f, "bad<%d> %p %p %p", t->type, c0, c1, c2); - break; - case Tdol: pfmt(f, "$%t", c0); - break; - case Tquote: pfmt(f, "$\"%t", c0); - break; - case Tand: pfmt(f, "%t&", c0); - break; - case Tcarot: pfmt(f, "%t^%t", c0, c1); - break; - case Ttick: pfmt(f, "`%t", c0); - break; - case Tandand: pfmt(f, "%t && %t", c0, c1); - break; - case Tbang: pfmt(f, "! %t", c0); - break; - case Tbrace: pfmt(f, "{%t}", c0); - break; - case Tcount: pfmt(f, "$#%t", c0); - break; - case Tfunc: pfmt(f, "func %t %t", c0, c1); - break; - case Tif: (c2) ? pfmt(f, "if%t%t else %t", c0, c1, c2): pfmt(f, "if%t%t", c0, c1); - break; - case Toror: pfmt(f, "%t || %t", c0, c1); - break; - case Tpcmd: /* fallthrough */ - case Tparen: pfmt(f, "(%t)", c0); - break; - case Tsub: pfmt(f, "$%t(%t)", c0, c1); - break; - case Tsimple: pfmt(f, "%t", c0); - break; - case Tsubshell: pfmt(f, "@ %t", c0); - break; - case Tswitch: pfmt(f, "switch %t %t", c0, c1); - break; - case Tcase: pfmt(f, "case %t:\n%t", c0, c1); - break; - case Ttwiddle: pfmt(f, "~ %t %t", c0, c1); - break; - case Twhile: pfmt(f, "while %t%t", c0, c1); - break; - case Targs: - if(c0==0) - pfmt(f, "%t", c1); - else if(c1==0) - pfmt(f, "%t", c0); - else - pfmt(f, "%t %t", c0, c1); - break; - case Tsemi: - if(c0) { - if(c1) - pfmt(f, "%t%c%t", c0, '\n', c1); - else - pfmt(f, "%t", c0); - } else - pfmt(f, "%t", c1); - break; - case Twords: - if(c0) - pfmt(f, "%t ", c0); - pfmt(f, "%t", c1); - break; - case Tfor: - pfmt(f, "for(%t", c0); - if(c1) - pfmt(f, " in %t", c1); - pfmt(f, ")%t", c2); - break; - case Tword: - if(t->quoted) - pfmt(f, "%Q", t->str); - else - pdeglob(f, t->str); - break; - case Tdup: - if(t->redir.type==Rdupfd) - pfmt(f, ">[%d=%d]", t->redir.fd[1], t->redir.fd[0]); /* yes, fd1, then fd0; read lex.c */ - else - pfmt(f, ">[%d=]", t->redir.fd[0]); - pfmt(f, "%t", c1); - break; - case Tpipefd: - case Tredir: - switch(t->redir.type){ - case Rhere: - pchr(f, '<'); - case Rread: - case Rrdwr: - pchr(f, '<'); - if(t->redir.type==Rrdwr) - pchr(f, '>'); - if(t->redir.fd[0]!=0) - pfmt(f, "[%d]", t->redir.fd[0]); - break; - case Rappend: - pchr(f, '>'); - case Rwrite: - pchr(f, '>'); - if(t->redir.fd[0]!=1) - pfmt(f, "[%d]", t->redir.fd[0]); - break; - } - pfmt(f, "%t", c0); - if(c1) - pfmt(f, " %t", c1); - break; - case Teq: - pfmt(f, "%t=%t", c0, c1); - if(c2) - pfmt(f, " %t", c2); - break; - case Tpipe: - pfmt(f, "%t|", c0); - if(t->redir.fd[1]==0){ - if(t->redir.fd[0]!=1) - pfmt(f, "[%d]", t->redir.fd[0]); - } - else pfmt(f, "[%d=%d]", t->redir.fd[0], t->redir.fd[1]); - pfmt(f, "%t", c1); - break; + if(io->fd==-1) + io->bufp = io->strp; + else{ + io->bufp = io->ebuf = io->buf; + Seek(io->fd, 0L, 0); } } -/* rc specific printf */ -static int pfmtlev; - void -vpfmt(Io *f, char *fmt, va_list args) +closeio(io *io) { - char err[124]; - - pfmtlev++; - for (; *fmt; fmt++) - if (*fmt!='%') - pchr(f, *fmt); - else switch(*++fmt) { - case '\0': - break; - case 'c': - pchr(f, va_arg(args, int)); - break; - case 'd': - pdec(f, va_arg(args, int)); - break; - case 'o': - poct(f, va_arg(args, uint)); - break; - case 'p': - pptr(f, va_arg(args, void*)); - break; - case 'Q': - pquo(f, va_arg(args, char*)); - break; - case 'q': - pwrd(f, va_arg(args, char*)); - break; - case 'r': - pstr(f, strerror(errno)); - break; - case 's': - pstr(f, va_arg(args, char*)); - break; - case 't': - pcmd(f, va_arg(args, Tree*)); - break; - case 'v': - pval(f, va_arg(args, Word*)); - break; - } - - if (--pfmtlev==0) - flush(&f); + if(io->fd>=0) + close(io->fd); + if(io->strp) + efree(io->strp); + efree((char *)io); } -void -pfmt(Io *f, char *fmt, ...) +int +emptybuf(io *f) { - va_list args; - char err[124]; - - va_start(args, fmt); - vpfmt(f, fmt, args); - va_end(args); + 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; } diff --git a/sys/cmd/rc/lex.c b/sys/cmd/rc/lex.c index f6e2b4e..253d05a 100644 --- a/sys/cmd/rc/lex.c +++ b/sys/cmd/rc/lex.c @@ -1,417 +1,371 @@ #include "rc.h" +#include "exec.h" +#include "io.h" +#include "getflags.h" +#include "fns.h" -#define onebyte(c) ((c&0x80)==0x00) -#define twobyte(c) ((c&0xe0)==0xc0) -#define threebyte(c) ((c&0xf0)==0xe0) -#define fourbyte(c) ((c&0xf8)==0xf0) +char *promptstr; +int doprompt; +char tok[NTOK]; -// ----------------------------------------------------------------------- -// globals - -static int lastc, nextc=EOF, lastdol, lastword, doprompt = 1; -static char buf[8*1024]; - -// ----------------------------------------------------------------------- -// utilities - -static uchar nwordc[256] = -{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +int getnext(void); int wordchr(int c) { - return !nwordc[c] && c!=EOF; + return !strchr("\n \t#;&|^$=`'{}()<>", c) && c!=EOF; } - -static uchar nquotec[256] = -{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - int -quotechr(char c) +idchr(int c) { - return !nquotec[c] && c!=EOF; + /* + * Formerly: + * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' + * || c=='_' || c=='*'; + */ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); } - -static uchar nvarc[256] = -{ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - +int future = EOF; +int doprompt = 1; +int inquote; +int incomm; +/* + * Look ahead in the input stream + */ int -varchr(char c) +nextc(void) { - return !nvarc[c] && c!=EOF; + if(future==EOF) + future = getnext(); + return future; } +/* + * Consume the lookahead character. + */ -static -void -prompt(void) -{ - shell->cmd.line++; - doprompt = 0; -} - -/* lookahead one byte */ -static int -lookahead(void) +advance(void) { - int c; - - if(nextc != EOF) - return nextc; - if(shell->cmd.eof) - return EOF; - - if(doprompt) - prompt(); - - c = rchr(shell->cmd.io); - doprompt = c == '\n' || c == EOF; - - if(c == EOF) - shell->cmd.eof++; - - return nextc = c; + int c = nextc(); + lastc = future; + future = EOF; + return c; } +/* + * read a character from the input stream + */ -/* consumes the lookahead */ -static int -advance(void) +getnext(void) { - int c = lookahead(); - lastc = nextc, nextc = EOF; - - return c; + int c; + static int peekc = EOF; + if(peekc!=EOF){ + c = peekc; + peekc = EOF; + return c; + } + if(runq->eof) + return EOF; + if(doprompt) + pprompt(); + c = rchr(runq->cmdfd); + if(!inquote && c=='\\'){ + c = rchr(runq->cmdfd); + if(c=='\n' && !incomm){ /* don't continue a comment */ + doprompt = 1; + c=' '; + } + else{ + peekc = c; + c='\\'; + } + } + doprompt = doprompt || c=='\n' || c==EOF; + if(c==EOF) + runq->eof++; + else if(flag['V'] || ndot>=2 && flag['v']) pchr(err, c); + return c; } -/* - * advance until we no longer hit horizontal space - * consumes all comments - */ -static void -skipws(void) +skipwhite(void) { - int c; - for(;;) { - c = lookahead(); - if(c=='#'){ - for(;;){ - c = lookahead(); - if(c=='\n' || c==EOF) - break; - advance(); - } - } - if(c==' ' || c=='\t') - advance(); - else - return; - } + int c; + for(;;){ + c = nextc(); + /* Why did this used to be if(!inquote && c=='#') ?? */ + if(c=='#'){ + incomm = 1; + for(;;){ + c = nextc(); + if(c=='\n' || c==EOF) { + incomm = 0; + break; + } + advance(); + } + } + if(c==' ' || c=='\t') + advance(); + else return; + } } -/* advance until we no longer hit any space */ void skipnl(void) { - int c; - for(;;) { - skipws(); - if ((c = lookahead()) != '\n') - return; - advance(); - } + int c; + for(;;){ + skipwhite(); + c = nextc(); + if(c!='\n') + return; + advance(); + } } -/* advance if next char is equal to c */ -static int nextis(int c) { - if(lookahead()==c) { - advance(); - return 1; - } - return 0; + if(nextc()==c){ + advance(); + return 1; + } + return 0; } -/* functions to append to our write buffer */ -static char* -putbyte(char *s, int c) +addtok(char *p, int val) { - if(!s) - return s; - if(s == arrend(buf)){ - *s = 0; - rcerror("out of buffer space"); - return nil; - } - *s++ = c; - return s; + if(p==0) + return 0; + if(p==&tok[NTOK-1]){ + *p = 0; + yyerror("token buffer too short"); + return 0; + } + *p++=val; + return p; } -static char* -putrune(char *s, int c) +addutf(char *p, int c) { - s = putbyte(s, c); - if (onebyte(c)) - return s; - if (twobyte(c)) - return putbyte(s, advance()); - if (threebyte(c)) { - putbyte(s, advance()); - return putbyte(s, advance()); - } - if (fourbyte(c)) { - putbyte(s, advance()); - putbyte(s, advance()); - return putbyte(s, advance()); - } - rcerror("malformed utf8 stream"); - return nil; + 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()); + } + if(fourbyte(c)){ /* 4-byte escape */ + p = addtok(p, advance()); + 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? */ -// ----------------------------------------------------------------------- -// main exports - -void -rcerror(char *fmt, ...) -{ - va_list args; - - pfmt(errio, "rc:"); - if (shell->cmd.io) - pfmt(errio, "%s:%d ", shell->cmd.name, shell->cmd.line); - - va_start(args, fmt); - vpfmt(errio, fmt, args); - va_end(args); - - pfmt(errio, "\n"); - - flush(&errio); - lastword = lastdol = 0; - while (lastc != '\n' && lastc != EOF) - advance(); - /* for debugging only */ - abort(); -} - -/* word is only modified in the event of a lexed word */ int -lex(Tree **node) +yylex(void) { - int c; - char *w = buf; - /* - * NOTE: - * we inject tokens into the lexer based on context if last token = word: - * if we see a (, then we interpret that as a subscript - * otherwise, if the next character is the first char of a word, we return a ^ operator. - */ - if(lastword){ - lastword=0; - c = lookahead(); - if(c=='('){ - advance(); - return Tlparen; - } - if(quotechr(c)) - return Tcarot; - } - - skipws(); - switch(c=advance()) { - case EOF: - lastdol = 0; - return EOF; - case '$': - lastdol = 1; - if(nextis('#')) - return Tcount; - if (nextis('"')) - return Tquote; - return Tdol; - case '&': - lastdol = 0; - if(nextis('&')) - return Tandand; - return Tand; - - case '!': - return Tbang; - case '@': - return Tsubshell; - case '~': - return Ttwiddle; - - case '|': - lastdol = 0; - if(nextis('|')){ - skipnl(); - return Toror; - } - (*node) = newtree(); - (*node)->type = Tpipe; - (*node)->redir.fd[0] = 0; - (*node)->redir.fd[1] = 1; - goto redir; - case '>': - (*node) = newtree(); - (*node)->type = Tredir; - if (nextis(c)) - (*node)->redir.type = Rappend; - else - (*node)->redir.type = Rwrite; - (*node)->redir.fd[0] = 1; - goto redir; - case '<': - (*node) = newtree(); - (*node)->type = Tredir; - if(nextis(c)) - (*node)->redir.type = Rhere; - else if(nextis('>')) - (*node)->redir.type = Rrdwr; - else - (*node)->redir.type = Rread; - (*node)->redir.fd[0] = 0; - /* fallthrough */ - redir: - if(nextis('[')) { - c = advance(); - if(c < '0' || '9' < c) { - redirerr: - rcerror("incorrect redirection syntax"); - return EOF; - } - (*node)->redir.fd[0] = 0; - do { - (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0'); - c = advance(); - } while('0'<=c && c<='9'); - - if(c == '=') { - if((*node)->type == Tredir) - (*node)->type = Tdup; - c = advance(); - if('0'<=c && c<='9') { - (*node)->redir.type = Rdupfd; - (*node)->redir.fd[1] = (*node)->redir.fd[0]; - (*node)->redir.fd[0] = 0; - do { - (*node)->redir.fd[0] = 10*(*node)->redir.fd[0]+(c-'0'); - c = advance(); - } while('0'<=c && c<='9'); - } else { - if((*node)->type == Tpipe) - goto redirerr; - (*node)->redir.type = Rclose; - } - } - if (c != ']' - ||(*node)->type==Tdup && ((*node)->redir.type==Rhere || (*node)->redir.type==Rappend)) - goto redirerr; - } - if ((c = ((*node)->type)) == Tpipe) - skipnl(); - return c; - - case '\'': - lastdol = 0; - lastword = 1; - for(;;){ - c = advance(); - if(c==EOF) - break; - if(c=='\''){ - if(lookahead()!='\'') - break; - advance(); - } - w = putrune(w, c); - } - *w = 0; - *node = wordnode(buf); - (*node)->quoted = 1; - return Tword; - } - if (!wordchr(c)) { - lastdol = 0; - return c; - } - for(;;){ - if(c=='*'||c=='['||c=='?'||c==GLOB) - w = putbyte(w, GLOB); - w = putrune(w, c); - c = lookahead(); - if(lastdol?!varchr(c):!wordchr(c)) - break; - advance(); - } - *w = 0; - - if ((c = kwlookup(buf)) == -1) { - (*node) = wordnode(buf); - (*node)->type = c = Tword; - (*node)->quoted = 0; - lastword = 1; - } - - lastdol = 0; - return c; + int c, d = nextc(); + char *w = tok; + struct tree *t; + yylval.tree = 0; + /* + * Embarassing sneakiness: if the last token read was a quoted or unquoted + * WORD then we alter the meaning of what follows. If the next character + * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, + * if the next character is the first character of a simple or compound word, + * we insert a `^' before it. + */ + if(lastword){ + lastword = 0; + if(d=='('){ + advance(); + strcpy(tok, "( [SUB]"); + return SUB; + } + if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(tok, "^"); + return '^'; + } + } + inquote = 0; + skipwhite(); + switch(c = advance()){ + case EOF: + lastdol = 0; + strcpy(tok, "EOF"); + return EOF; + case '$': + lastdol = 1; + if(nextis('#')){ + strcpy(tok, "$#"); + return COUNT; + } + if(nextis('"')){ + strcpy(tok, "$\""); + return '"'; + } + strcpy(tok, "$"); + return '$'; + case '&': + lastdol = 0; + if(nextis('&')){ + skipnl(); + strcpy(tok, "&&"); + return ANDAND; + } + strcpy(tok, "&"); + return '&'; + case '|': + lastdol = 0; + if(nextis(c)){ + skipnl(); + strcpy(tok, "||"); + return OROR; + } + case '<': + case '>': + lastdol = 0; + /* + * funny redirection tokens: + * redir: arrow | arrow '[' fd ']' + * arrow: '<' | '<<' | '>' | '>>' | '|' + * fd: digit | digit '=' | digit '=' digit + * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' + * some possibilities are nonsensical and get a message. + */ + *w++=c; + t = newtree(); + switch(c){ + case '|': + t->type = PIPE; + t->fd0 = 1; + t->fd1 = 0; + break; + case '>': + t->type = REDIR; + if(nextis(c)){ + t->rtype = APPEND; + *w++=c; + } + else t->rtype = WRITE; + t->fd0 = 1; + break; + case '<': + t->type = REDIR; + if(nextis(c)){ + t->rtype = HERE; + *w++=c; + } else if (nextis('>')){ + t->rtype = RDWR; + *w++=c; + } else t->rtype = READ; + t->fd0 = 0; + break; + } + if(nextis('[')){ + *w++='['; + c = advance(); + *w++=c; + if(c<'0' || '9'type==PIPE?"pipe syntax" + :"redirection syntax"); + return EOF; + } + t->fd0 = 0; + do{ + t->fd0 = t->fd0*10+c-'0'; + *w++=c; + c = advance(); + }while('0'<=c && c<='9'); + if(c=='='){ + *w++='='; + if(t->type==REDIR) + t->type = DUP; + c = advance(); + if('0'<=c && c<='9'){ + t->rtype = DUPFD; + t->fd1 = t->fd0; + t->fd0 = 0; + do{ + t->fd0 = t->fd0*10+c-'0'; + *w++=c; + c = advance(); + }while('0'<=c && c<='9'); + } + else{ + if(t->type==PIPE) + goto RedirErr; + t->rtype = CLOSE; + } + } + if(c!=']' + || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) + goto RedirErr; + *w++=']'; + } + *w='\0'; + yylval.tree = t; + if(t->type==PIPE) + skipnl(); + return t->type; + case '\'': + lastdol = 0; + lastword = 1; + inquote = 1; + for(;;){ + c = advance(); + if(c==EOF) + break; + if(c=='\''){ + if(nextc()!='\'') + break; + advance(); + } + w = addutf(w, c); + } + if(w!=0) + *w='\0'; + t = token(tok, WORD); + t->quoted = 1; + yylval.tree = t; + return t->type; + } + if(!wordchr(c)){ + lastdol = 0; + tok[0] = c; + tok[1]='\0'; + return c; + } + for(;;){ + /* next line should have (char)c==GLOB, but ken's compiler is broken */ + if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) + w = addtok(w, GLOB); + w = addutf(w, c); + c = nextc(); + if(lastdol?!idchr(c):!wordchr(c)) break; + advance(); + } + + lastword = 1; + lastdol = 0; + if(w!=0) + *w='\0'; + t = klook(tok); + if(t->type!=WORD) + lastword = 0; + t->quoted = 0; + yylval.tree = t; + return t->type; } diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c deleted file mode 100644 index b4a355e..0000000 --- a/sys/cmd/rc/main.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "rc.h" - -/* globals */ -Thread *shell = nil; -int ntrap = 0; -Io *errio; - -/* main execution */ - -void -dotrap(void) -{ - exit(1); -} - -void -bootup(Code *c, int off, Var *vars) -{ - Thread *sh; - - alloc(sh); - sh->code = c, c->i++; - sh->ip = sh->code + off; - sh->local = vars; - sh->stack = nil; - - sh->link = shell, shell = sh; -} - -int -main(int argc, char *argv[]) -{ - int i; - Code *ip, sh[32]; - - ARGBEGIN { - } ARGEND; - - errio = openfd(2); - - initkw(); - - ip = sh; - memset(sh, 0, sizeof(sh)); - /* - * NOTE: first element of code is a reference count - * bootup runs: - * 1. *=argv[1:] - * 2. . rcmain $* - */ -#if 0 - ip++->i = 1; - ip++->f = Xmark; - ip++->f = Xword; - ip++->s = "*"; - ip++->f = Xassign; - ip++->f = Xmark; - ip++->f = Xmark; - ip++->s = "*"; - ip++->f = Xdol; - ip++->s = "rcmain"; - ip++->f = Xword; - ip++->s = "."; - ip++->f = Xsimple; - ip++->f = Xexit; - ip++->i = 0; - - bootup(sh, 1, nil); - pushlist(); - for (i = argc-1; i != 0; i--) - pushword(argv[i]); - - for (;;) { - shell->ip++->f(); - if (ntrap) - dotrap(); - } -#else - bootup(sh, 1, nil); - shell->cmd.io = openfd(0); - while (parse()) - ; - -#endif - exit(0); -} diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c deleted file mode 100644 index b61ac3c..0000000 --- a/sys/cmd/rc/parse.c +++ /dev/null @@ -1,496 +0,0 @@ -#include "rc.h" - -/* TODO: better error messages */ - -// ----------------------------------------------------------------------- -// global data - -static int lastt, nextt=EOF; -static Tree *node; /* if token was lexed as a tree node (redirs and words), its here */ - -/* anything that is not listed will automatically terminate parsing the given command */ -static uchar prectab[256] = { - [Tif] = 1, [Tfor] = 1, [Tswitch] = 1, /* NOTE: we give else lower precedence than if [Telse] = 1, */ - [Tandand] = 2, [Toror] = 2, - [Tbang] = 3, [Tsubshell] = 3, - [Tpipe] = 4, - [Tcarot] = 5, - [Tdol] = 6, [Tcount] = 6, [Tquote] = 6, - [Tsub] = 7, -}; - -// ----------------------------------------------------------------------- -// helpers - -static -int -lookahead(void) -{ - int tok; - - if (nextt != EOF) - return nextt; - - tok = lex(&node); - return nextt = tok; -} - -static -int -advance(void) -{ - int tok = lookahead(); - lastt = nextt, nextt = EOF; - node = nil; - - return tok; -} - -static -int -nextis(int tok) -{ - if (lookahead() == tok) { - advance(); - return 1; - } - return 0; -} - -// ----------------------------------------------------------------------- -// subparsers - -/* forward declarations */ -static Tree *word(void); -static Tree *words(void); -static Tree* body(int c); -static Tree *comword(void); -static Tree *cmd(int prec); - -/* implementations */ - -/* - * TODO: - * i don't like all this branching. - * think of a better way - */ - -static -Tree* -case_or_cmd(int c) -{ - Tree *t; - if (!c || !nextis(Tcase)) - return cmd(1); - - t = words(); - if (!nextis(';') && !nextis('\n')) - rcerror("case: missing terminator: recieved %d", nextt); - - t = tree2(Tcase, t, body(0)); - pfmt(errio, "%t\n", t); - - return t; -} - -static -Tree* -body(int c) -{ - int tok; - Tree *l, *r; - - skipnl(); - l = case_or_cmd(c); -loop: - switch((tok=lookahead())){ - case '&': - l = tree1('&', l); - /* fallthrough */ - case ';': case '\n': - advance(); - /* fallthrough */ - case Tcase: - if ((r = case_or_cmd(c))) { - l = tree2(';', l, r); - goto loop; - } - /* fallthrough */ - default: - ; - } - - return l; -} - -static -Tree* -brace(int c) -{ - Tree *t; - - if (!nextis('{')) - rcerror("brace: expected { found: %c", nextt); - t = tree1(Tbrace, body(c)); - if (!nextis('}')) - rcerror("brace: expected } found: %c", nextt); - - return t; -} - -static -Tree* -paren(void) -{ - Tree *t; - - if (!nextis('(')) - rcerror("not a paren"); - t = tree1(Tparen, body(0)); - if (!nextis(')')) - rcerror("unmatched paren"); - - return t; -} - -/* TODO: fill in */ -static -Tree* -heredoc(Tree* t) -{ - return t; -} - -static -Tree* -redir(void) -{ - int tok; - Tree *t; - - switch (tok = lookahead()) { - case Tdup: - t = node; - advance(); - break; - case Tredir: - t = node; - advance(); - t = hang1(t, (t->redir.type == Rhere) ? heredoc(word()) : word()); - break; - default: - t = nil; - } - - return t; -} - -static -Tree* -epilog(void) -{ - Tree *t, *tt; - - t = redir(); - while((tt = redir())) - t = hang2(t, t->child[0], tt); - - return t; -} - -static -Tree* -sword(void) -{ - int tok; - if (Kstart < (tok=lookahead()) && tok < Kend) - return node; - - return comword(); -} - -static -Tree* -word(void) -{ - int tok; - Tree *t; - - t = sword(); - while(nextis('^')) - t = tree2('^', t, sword()); - - return t; -} - - -static -Tree* -words(void) -{ - Tree *t, *tt; - t = word(); - while((tt=word())) - t = tree2(Twords, t, tt); - - return t; -} - -/* - * NOTE: we divergence from Duff's yacc grammar here. - * he has [dol|count|"]->word, we have [dol|count]->sword - * calling sword ensures we don't cat strings - * this was done in Tom's version by setting precedence - */ -static -Tree* -comword(void) -{ - int tok; - Tree *t, *tt; - - switch(tok=lookahead()){ - case Tdol: - advance(); - t = sword(); - if(nextis('(')) { - t = tree2(Tsub, t, words()); - if (!nextis(')')) - rcerror("malformed index expression"); - } - return tree1(Tdol, t); - case Tquote: - return tree1(Tquote, sword()); - case Tcount: - advance(); - return tree1(Tcount, sword()); - case Ttick: - advance(); - return tree1(Ttick, brace(0)); - case Tlparen: - return paren(); - case Tredir: - advance(); - t = hang1(node, brace(0)); - t->type = Tpipefd; - return t; - case Tword: - t = node; - advance(); - return t; - } - return nil; -} - -static -Tree* -first(void) -{ - int tok; - Tree *t; - - t = comword(); - while(nextis('^')) { - t = tree2('^', t, word()); - } - - return t; -} - -/* simple _or_ assignment */ -static -Tree* -simple_or_assign(void) -{ - int tok; - Tree *t, *tt; - - /* can't continue */ - if (!(t = first())) - return nil; - - /* is an assignment */ -assign: - if(nextis('=')) { - tt = word(); - return tree3(Teq, t, tt, cmd(prectab[Tbang])); - } - - /* is a 'simple' */ -simple: - switch ((tok=lookahead())) { - case Tredir: - case Tdup: - t = tree2(Targs, t, redir()); - goto simple; - default: - if ((tt = word())) { - t = tree2(Targs, t, tt); - goto simple; - } - /* fallthrough */ - } - - return simplehang(t); -} - -static -Tree* -opand(void) -{ - int tok; - Tree *t, *tt; - - switch(tok=lookahead()) { - case Tif: - advance(); - t = paren(); - skipnl(); - tt = cmd(prectab[Tif]); - if (nextis(Telse)) { - skipnl(); - t = tree3(Tif, t, tt, cmd(prectab[Tif])); - } else - t = tree3(Tif, t, tt, nil); - return t; - case Telse: - rcerror("invalid hanging else"); - break; - - case Tswitch: - advance(); - t = word(); - skipnl(); - tt = brace(1); - t = tree2(Tswitch, t, tt); - return t; - - case Tfor: - advance(); - - if (!nextis('(')) - rcerror("for: missing opening paren"); - t = word(); - if (nextis(Tin)) { - advance(); - tt = words(); - t = tree3(Tfor, t, tt, nil); - } else - t = tree3(Tfor, t, nil, nil); - if (!nextis(')')) - rcerror("for: missing closing paren"); - - skipnl(); - tt = cmd(prectab[Tfor]); - t->child[2] = tt; - return t; - - case Twhile: - advance(); - t = paren(); - skipnl(); - tt = cmd(1); - return tree2(Twhile, t, tt); - - case Tfunc: - advance(); - t = words(); - if ((tok=lookahead()) == '{') { - tt = brace(0); - t = tree2(Tfunc, t, tt); - } else - t = tree1(Tfunc, t); - return t; - - case Tsubshell: - advance(); - t = tree1(Tsubshell, cmd(prectab[Tsubshell])); - return t; - - case Tbang: - advance(); - t = tree1(Tbang, cmd(prectab[Tbang])); - return t; - - case Ttwiddle: - advance(); - tt = word(); - t = tree2(Ttwiddle, tt, words()); - return t; - - case Tlbrace: - t = brace(0); - tt = epilog(); - return epihang(t, tt); - - case Tredir: /* fallthrough */ - case Tdup: - t = redir(); - tt = cmd(prectab[Tbang]); - t = hang2(t, t->child[0], tt); - return t; - } - - return simple_or_assign(); -} - -static -Tree * -cmd(int prec) -{ - int np, tok; - Tree *l, *r, *p; - - if (!(l = opand())) - return nil; - - for(;;) { - tok = lookahead(); - np = prectab[tok]; - if (np < prec) - break; - p = node; - advance(); - r = cmd(np+1); - if (tok == Tpipe) - l = hang2(p, l, r); - else - l = tree2(tok, l, r); - } - - return l; -} - -// ----------------------------------------------------------------------- -// main function - -int -parse(void) -{ - int tok; - Tree *t, *tt; - - t = cmd(1); -loop: - switch(tok=lookahead()) { - case '&': - t = tree1('&', t); - /* fallthrough */ - case ';': - advance(); - tt = cmd(1); - t = tree2(';', t, tt); - goto loop; - case '\n': - advance(); - case EOF: - pfmt(errio, "%t\n", t); - break; - default: - if (tok > 0x20) - rcerror("unrecognized token: %c[%d]", tok, tok); - else - rcerror("unrecognized token: %d", tok, tok); - } - return tok != EOF; -} diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h index f32a737..1f73f11 100644 --- a/sys/cmd/rc/rc.h +++ b/sys/cmd/rc/rc.h @@ -1,312 +1,133 @@ -#pragma once - -#include -#include - -#include -#include - -#define alloc(ptr) ptr = emalloc(sizeof(*ptr)) - -// ----------------------------------------------------------------------- -// main enums - -#define GLOB 0x01 - -enum -{ - /* keywords */ - Kstart = 11, - Tfor, Tin, Twhile, Tif, Telse, - Tswitch, Tcase, Tfunc, Ttwiddle, - Tbang, Tsubshell, - Kend, - - /* tokens */ - Tword='w', Tredir='r', Tdup='d', Tsimple='s', - Targs='A', Twords='W', Tbrace='b', Tparen='p', Tsub='S', - Tpcmd='c', Tpipefd='-', Tandand='%', Toror='@', Tcount='#', - - Ttick='`', Tpipe = '|', Tdol='$', Tquote='"', Tand='&', - Tlparen = '(', Trparen = ')', Tlbrace='{', Trbrace='}', - Tsemi=';', Tcarot='^', Teq='=', +#include "unix.h" + +#define YYMAXDEPTH 500 +#ifndef PAREN +#ifndef YYMAJOR +#include "x.tab.h" +#endif +#endif + +#undef pipe /* so that /dev/fd works */ +#define searchpath rcsearchpath /* avoid new libc function */ + +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*); -enum -{ - Rappend = 1, - Rwrite = 2, - Rread = 3, - Rhere = 4, - Rdupfd = 5, - Rclose = 6, - Rrdwr = 7, -}; - -enum -{ - Fopen = 1, - Fdup = 2, - Fclose = 3, -}; - -// ----------------------------------------------------------------------- -// main types +extern tree *cmdtree; -typedef union Code Code; -typedef struct Word Word; -typedef struct List List; -typedef struct Var Var; -typedef struct Redir Redir; -typedef struct Tree Tree; -typedef struct Builtin Builtin; -typedef struct Thread Thread; -typedef struct Io Io; - -union Code -{ - int i; - char *s; - void (*f)(void); -}; - -struct Word -{ - char *word; - Word *link; -}; - -struct List -{ - Word *words; - List *link; -}; - -struct Redir -{ - uchar type; - short from, to; - Redir *link; -}; - -struct Var -{ - string name; - Word *val; - struct { - Code *func, *ip; - uint funcnew : 1; - }; - struct { - uint new : 1; - void (*update)(Var*); - }; - Var *link; -}; - -struct Tree -{ - ushort type; - uchar quoted : 1; - union { - char *str; - struct { - ushort type; - int fd[2]; - } redir; - }; - - Tree *child[3], *link; +/* + * 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; }; -struct Builtin -{ - char *cmd; - void (*func)(void); +extern char *promptstr; +extern int doprompt; + +#define NTOK 8192 +extern char tok[NTOK]; + +#define APPEND 1 +#define WRITE 2 +#define READ 3 +#define HERE 4 +#define DUPFD 5 +#define CLOSE 6 +#define RDWR 7 +struct var{ + char *name; /* ascii name */ + word *val; /* value */ + int changed; + code *fn; /* pointer to function's code vector */ + int fnchanged; + int pc; /* pc of start of function */ + var *next; /* next on hash or local list */ + void (*changefn)(var*); }; - -struct Thread -{ - Code *code, *ip; - List *stack; - Redir *redir, *root; - Var *local; - uchar interactive : 1; - struct { - uchar eof : 1; - int line; - char *name; - Io *io; - } cmd; - - int pid; - Tree *nodes; - Thread *link; /* continuation */ +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); + +#define NVAR 521 +extern var *gvar[NVAR]; /* hash for globals */ + +#define new(type) ((type *)emalloc(sizeof(type))) +void *emalloc(long); +void *Malloc(ulong); +void efree(void*); +#define NOFILE 128 /* should come from */ +struct here{ + tree *tag; + char *name; + struct here *next; }; - -struct Io -{ - int fd; - uchar *b, *e, buf[]; -}; - -// ----------------------------------------------------------------------- -// global interpreter variables - -extern Thread *shell; -extern int ntrap; -extern int status; - -extern Io *errio; - -extern Builtin builtins[]; -extern Var *globals[1021]; /* for now must be prime */ - -// ----------------------------------------------------------------------- -// interpreter functions (defined in exec.c) - -/* - * notation: - * (var1, var2, ...) : items from stack - * [var1, var2, ...] : items from code stream - * {var1, var2, ...} : jump block from code stream - * -> moves value (stack) [code stream] - */ -extern void Xappend(void); /* Xappend(file)[fd]: open file to append */ -extern void Xassign(void); /* Xassign(name, val): assign name to val */ -extern void Xasync(void); /* Xasync(cmd): run command asynchronously */ -extern void Xcat(void); /* Xcat(list1, list2): concatenate strings */ -extern void Xclose(void); /* Xclose[fd]: close file descriptor */ -extern void Xcmdsub(void); /* Xcmdsub(cmd): use output of command as input to other */ -extern void Xcount(void); /* Xcount(name) -> (number): count items in list*/ -extern void Xdol(void); /* Xdol(name): get variable value */ -extern void Xdup(void); /* Xdup[i, j]: duplicate file descriptor */ -extern void Xexit(void); /* Xexit: exit with status */ -extern void Xfalse(void); /* Xfalse{...}: run only if $status=1 */ -extern void Xflatten(void); /* Xflatten(list) -> (string): flatten list */ -extern void Xfor(void); /* Xfor(list): flatten list */ -extern void Xfunc(void); /* Xfunc(name){... Xreturn}: define function */ -extern void Xglob(void); /* Xglob(list): globs value */ -extern void Xif(void); /* Xif: execute if $status */ -extern void Xjump(void); /* Xjump[addr]: jump to address */ -extern void Xkill(void); /* Xkill kill thread */ -extern void Xlocal(void); /* Xlocal(name, val): define local variable */ -extern void Xmark(void); /* Xmark: delimit stack with new list */ -extern void Xmatch(void); /* Xmatch(pat, str): sets status with result */ -extern void Xnegate(void); /* Xnegate: negate condition */ -extern void Xpipe(void); /* Xpipe[i j]{... Xkill}{... Xkill}: construct a pipe between 2 threads*/ -extern void Xpipefd(void); /* Xpipe[type]{... Xkill}: connect {} to a pipe */ -extern void Xpipewait(void); /* Xpipewait: wait on a pipe */ -extern void Xpop(void); /* Xpop(value): pops value from stack */ -extern void Xpopredir(void); /* Xpopredir(value): pops redir from redir stack */ -extern void Xrdwr(void); /* Xrdwr(file)[fd]: open file for reads/writes */ -extern void Xread(void); /* Xread(file)[fd]: open file for reads */ -extern void Xsub(void); /* Xsub(list, index): subscript list */ -extern void Xsimple(void); /* Xsimple(args): run command */ -extern void Xsubshell(void); /* Xsubshell(args): run command in a subshell */ -extern void Xtrue(void); /* Xtrue{...}: run only if $status=0 */ -extern void Xunfunc(void); /* Xunfunc(name) undefine function */ -extern void Xunlocal(void); /* Xunlocal(name) undefine local */ -extern void Xword(void); /* Xword[val] -> (val) */ -extern void Xwrite(void); /* Xwrite(file)[fd]: open file to write */ - -extern void Xerror(char *s); /* Xerror report an error */ - -// ----------------------------------------------------------------------- -// shell functions - -/* - * util.c - */ -void *emalloc(uintptr size); -void *erealloc(void *ptr, uintptr size); -void efree(void *); -void panic(char *msg, int n); - -/* - * io.c - */ -Io *openfd(int fd); -Io *openstr(void); -Io *opencore(int len, char *s); -void rewindio(Io *f); -void closeio(Io *f); -void flush(Io **fp); - -/* reads */ -int rchr(Io *f); - -/* writes */ -int pchr(Io *f, int c); -void pquo(Io *f, char *s); -void pwrd(Io *f, char *s); -void pptr(Io *f, void *v); -void pstr(Io *f, char *s); -void pdec(Io *f, int n); -void poct(Io *f, uint n); -void pval(Io *f, Word *a); -void pcmd(Io *f, Tree *t); -void pfmt(Io *f, char *fmt, ...); -void vpfmt(Io *f, char *fmt, va_list args); - -/* - * word.c - */ -void pushlist(void); -void freelist(Word *w); -void poplist(void); - -int count(Word *w); -Word *newword(char *w, Word *link); -void pushword(char *w); - -/* - * tree.c - */ - -Tree *newtree(void); -void freetree(Tree *t); -Tree *tree3(int type, Tree *c0, Tree *c1, Tree *c2); -Tree *tree2(int type, Tree *c0, Tree *c1); -Tree *tree1(int type, Tree *c0); - -Tree *hang1(Tree *p, Tree *c0); -Tree *hang2(Tree *p, Tree *c0, Tree *c1); -Tree *hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2); -Tree *epihang(Tree *c, Tree *epi); -Tree *simplehang(Tree *t); -Tree *wordnode(char *w); - /* - * var.c + * 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 */ - -Var *newvar(char *name, Var *link); -Var *gvlookup(char *name); -Var *vlookup(char *name); -void setvar(char *name, Word *val); - -int kwlookup(char *name); -void initkw(void); - -/* - * lex.c +#define GLOB ((char)0x01) +/* + * onebyte(c), twobyte(c), threebyte(c) + * Is c the first character of a one- two- or three-byte utf sequence? */ - -void skipnl(void); -int wordchr(int c); - -void rcerror(char *msg, ...); -int lex(Tree **node); - +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) +#define fourbyte(c) ((c&0xf8)==0xf0) + +extern char **argp; +extern char **args; +extern int nerror; /* number of errors encountered during compilation */ /* - * parse.c + * 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 -int parse(void); +char *getstatus(void); /* - * main.c + * How many dot commands have we executed? + * Used to ensure that -v flag doesn't print rcmain. */ - -void dotrap(void); +extern int ndot; +extern int lastc; +extern int lastword; +extern int kidpid; +extern int mypid; diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk index 654a44e..0a5af08 100644 --- a/sys/cmd/rc/rules.mk +++ b/sys/cmd/rc/rules.mk @@ -1,22 +1,39 @@ include share/push.mk +# Iterate through subdirectory tree # Local sources SRCS_$(d) := \ - $(d)/glob.c \ - $(d)/word.c \ - $(d)/util.c \ - $(d)/io.c \ - $(d)/var.c \ - $(d)/tree.c \ - $(d)/lex.c \ - $(d)/parse.c \ - $(d)/main.c + $(d)/code.c\ + $(d)/exec.c\ + $(d)/getflags.c\ + $(d)/glob.c\ + $(d)/here.c\ + $(d)/io.c\ + $(d)/lex.c\ + $(d)/pcmd.c\ + $(d)/pfnc.c\ + $(d)/simple.c\ + $(d)/subr.c\ + $(d)/trap.c\ + $(d)/tree.c\ + $(d)/var.c\ + $(d)/y.tab.c\ + $(d)/unix.c\ + $(d)/havefork.c\ + $(d)/prompt.c + BINS_$(d) := $(d)/rc include share/paths.mk # Local rules -$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a +$(d)/y.tab.h $(d)/y.tab.c: $(d)/syn.y + yacc -d $^ + +$(BINS_$(d)): TCFLAGS = \ + -D_XOPEN_SOURCE=500 + +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/sys/libn/libn.a /home/nolln/root/lib/libreadline.a /home/nolln/root/lib/libhistory.a /home/nolln/root/lib/libncursesw.a $(COMPLINK) include share/pop.mk diff --git a/sys/cmd/rc/simple.c b/sys/cmd/rc/simple.c index f934aa1..d587227 100644 --- a/sys/cmd/rc/simple.c +++ b/sys/cmd/rc/simple.c @@ -1,13 +1,504 @@ +/* + * 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; - Var *v; + word *a; + thread *p = runq; + var *v; + struct builtin *bp; + int pid; + globlist(); + a = runq->argv->words; + if(a==0){ + Xerror1("empty argument list"); + return; + } + if(flag['x']) + pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ + v = gvlook(a->word); + if(v->fn) + execfunc(v); + else{ + if(strcmp(a->word, "builtin")==0){ + if(count(a)==1){ + pfmt(err, "builtin: empty argument list\n"); + setstatus("empty arg list"); + poplist(); + return; + } + a = a->next; + popword(); + } + for(bp = Builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + (*bp->fnc)(); + return; + } + if(exitnext()){ + /* fork and wait is redundant */ + pushword("exec"); + execexec(); + Xexit(); + } + else{ + flush(err); + Updenv(); /* necessary so changes don't go out again */ + if((pid = execforkexec()) < 0){ + Xerror("try again"); + return; + } + + /* interrupts don't get us out */ + poplist(); + while(Waitfor(pid, 1) < 0) + ; + } + } +} +struct word nullpath = { "", 0}; + +void +doredir(redir *rp) +{ + if(rp){ + doredir(rp->next); + switch(rp->type){ + case ROPEN: + if(rp->from!=rp->to){ + Dup(rp->from, rp->to); + close(rp->from); + } + break; + case RDUP: + Dup(rp->from, rp->to); + break; + case RCLOSE: + close(rp->from); + break; + } + } +} + +word* +searchpath(char *w) +{ + word *path; + if(strncmp(w, "/", 1)==0 +/* || strncmp(w, "#", 1)==0 */ + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path = vlook("path")->val)==0) + path=&nullpath; + return path; +} + +void +execexec(void) +{ + popword(); /* "exec" */ + if(runq->argv->words==0){ + Xerror1("empty argument list"); + return; + } + doredir(runq->redir); + Execute(runq->argv->words, searchpath(runq->argv->words->word)); + poplist(); +} - a = shell->stack->words; - if (!a) { - Xerror("empty argument list"); - return; - } - v = vlookup(a->word); +void +execfunc(var *func) +{ + word *starval; + popword(); + starval = runq->argv->words; + runq->argv->words = 0; + poplist(); + start(func->fn, func->pc, runq->local); + runq->local = newvar(strdup("*"), runq->local); + runq->local->val = starval; + runq->local->changed = 1; +} + +int +dochdir(char *word) +{ + /* report to /dev/wdir if it exists and we're interactive */ + static int wdirfd = -2; + if(chdir(word)<0) return -1; + if(flag['i']!=0){ + if(wdirfd==-2) /* try only once */ + wdirfd = open("/dev/wdir", OWRITE|OCEXEC); + if(wdirfd>=0) + write(wdirfd, word, strlen(word)); + } + return 1; +} + +void +execcd(void) +{ + word *a = runq->argv->words; + word *cdpath; + char dir[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(); } diff --git a/sys/cmd/rc/tree.c b/sys/cmd/rc/tree.c index 14049e5..897597e 100644 --- a/sys/cmd/rc/tree.c +++ b/sys/cmd/rc/tree.c @@ -1,144 +1,146 @@ #include "rc.h" - -// ----------------------------------------------------------------------- -// globals - -static Tree *nodes; - -// ----------------------------------------------------------------------- -// exported funcs - -Tree* +#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; - - alloc(t); - t->str = nil; - t->child[0] = t->child[1] = t->child[2] = nil; - t->redir.fd[0] = t->redir.fd[1] = t->redir.type = 0; - - t->link = nodes, nodes = t; - return t; + 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 -freetree(Tree *t) +freenodes(void) { - if (!t) - return; - - freetree(t->child[0]); - freetree(t->child[1]); - freetree(t->child[2]); - - if (t->str) - efree(t->str); - efree(t); + tree *t, *u; + for(t = treenodes;t;t = u){ + u = t->next; + if(t->str) + efree(t->str); + efree((char *)t); + } + treenodes = 0; } -void -freenodes(void) +tree* +tree1(int type, tree *c0) { - Tree *t, *u; - - for (t = nodes;t;t = u) { - u = t->link; - if (t->str) - efree(t->str); - efree(t); - } - nodes = nil; + return tree3(type, c0, (tree *)0, (tree *)0); } -/* tree creation */ -Tree* -tree3(int type, Tree *c0, Tree *c1, Tree *c2) +tree* +tree2(int type, tree *c0, tree *c1) { - Tree *t; - - t = newtree(); - t->type = type; - t->child[0] = c0; - t->child[1] = c1; - t->child[2] = c2; - - return t; + return tree3(type, c0, c1, (tree *)0); } -Tree* -tree2(int type, Tree *c0, Tree *c1) +tree* +tree3(int type, tree *c0, tree *c1, tree *c2) { - return tree3(type, c0, c1, nil); + 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* -tree1(int type, Tree *c0) +tree* +mung1(tree *t, tree *c0) { - return tree3(type, c0, nil, nil); + t->child[0] = c0; + return t; } -/* tree hang */ -Tree* -hang1(Tree *p, Tree *c0) +tree* +mung2(tree *t, tree *c0, tree *c1) { - p->child[0] = c0; - return p; + t->child[0] = c0; + t->child[1] = c1; + return t; } -Tree* -hang2(Tree *p, Tree *c0, Tree *c1) +tree* +mung3(tree *t, tree *c0, tree *c1, tree *c2) { - p->child[0] = c0; - p->child[1] = c1; - return p; + t->child[0] = c0; + t->child[1] = c1; + t->child[2] = c2; + return t; } -Tree* -hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2) +tree* +epimung(tree *comp, tree *epi) { - p->child[0] = c0; - p->child[1] = c1; - p->child[2] = c2; - return p; + 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. + */ -/* hangs the cmd underneath the epilogue */ -Tree* -epihang(Tree *c, Tree *epi) +tree* +simplemung(tree *t) { - Tree *p; - if(!epi) - return c; - for(p=epi;p->child[1];p = p->child[1]) - ; - p->child[1] = c; - return epi; + 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; } -/* hangs tree t from a new simple node. percolates redirections to root */ -Tree* -simplehang(Tree *t) +tree* +token(char *str, int type) { - Tree *u; - t = tree1(Tsimple, t); - for(u = t->child[0];u->type==Targs;u=u->child[0]) { - if (u->child[1]->type==Tdup - || u->child[1]->type==Tredir){ - u->child[1]->child[1] = t; - t = u->child[1]; - u->child[1] = nil; - } - } - return t; + tree *t = newtree(); + t->type = type; + t->str = strdup(str); + return t; } -Tree* -wordnode(char *w) +void +freetree(tree *p) { - Tree *t = newtree(); - t->type = Tword; - t->str = strdup(w); - - return t; + if(p==0) + return; + freetree(p->child[0]); + freetree(p->child[1]); + freetree(p->child[2]); + if(p->str) + efree(p->str); + efree((char *)p); } diff --git a/sys/cmd/rc/util.c b/sys/cmd/rc/util.c deleted file mode 100644 index 02b3611..0000000 --- a/sys/cmd/rc/util.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "rc.h" - -void * -emalloc(uintptr n) -{ - void *p = malloc(n); - if (!p) - panic("can't malloc %d bytes", n); - - return p; -} - -void * -erealloc(void *p, uintptr n) -{ - void *new = realloc(p, n); - if (!new) - panic("can't realloc %d bytes", n); - - return new; -} - -void -efree(void *p) -{ - if (p) - free(p); - else - pfmt(errio, "free \n"); -} - -void -panic(char *s, int n) -{ - pfmt(errio, "rc: "); - pfmt(errio, s, n); - pchr(errio, '\n'); - flush(&errio); - abort(); -} diff --git a/sys/cmd/rc/var.c b/sys/cmd/rc/var.c index d442369..d48dc66 100644 --- a/sys/cmd/rc/var.c +++ b/sys/cmd/rc/var.c @@ -1,129 +1,175 @@ #include "rc.h" +#include "exec.h" +#include "fns.h" -Var *globals[1021] = { 0 }; +var *gvar[NVAR] = { 0 }; /* hash for globals */ -struct Keyword { - ushort type; - char *name; - struct Keyword *link; -} *keywords[41]; - -// ----------------------------------------------------------------------- -// utility - -static int hash(char *s, int n) { - int i = 1, h = 0; - while (*s) - h += *s++*i++; - h %= n; - return (h<0)?h+n:h; + 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]; -// ----------------------------------------------------------------------- -// keywords - -static void -putkw(int type, char *name) +kenter(int type, char *name) { - struct Keyword *kw; - int h = hash(name, arrlen(keywords)); - - alloc(kw); - kw->type = type; - kw->name = name; - kw->link = keywords[h]; - - keywords[h] = kw; + int h = hash(name, NKW); + struct kw *p = new(struct kw); + p->type = type; + p->name = name; + p->next = kw[h]; + kw[h] = p; } void -initkw(void) +kinit(void) { - putkw(Tfor, "for"); - putkw(Tin, "in"); - putkw(Twhile, "while"); - putkw(Tif, "if"); - putkw(Telse, "else"); - putkw(Tswitch, "switch"); - putkw(Tcase, "case"); - putkw(Tfunc, "func"); + 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"); } -int -kwlookup(char *name) +tree* +klook(char *name) { - int t; - struct Keyword *it; - for(t=-1,it = keywords[hash(name, arrlen(keywords))];it;it = it->link) - if(!strcmp(it->name, name)) - t = it->type; - return t; + 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; } -// ----------------------------------------------------------------------- -// variables - -Var * -newvar(char *name, Var *link) +var* +gvlook(char *name) { - Var *v; - - alloc(v); - v->name = name; - v->val = 0; - v->func = nil; - v->funcnew = 0; - v->new = 0; - v->update = nil; - v->link = link; - - return v; + 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]); } -/* only global lookup */ +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); +} -Var * -gvlookup(char *name) +void +_setvar(char *name, word *val, int callfn) { - Var *v; - int h = hash(name, arrlen(globals)); - for (v = globals[h]; v; v = v->link) - if (!strcmp(v->name, name)) - return v; + struct var *v = vlook(name); + freewords(v->val); + v->val=val; + v->changed=1; + if(callfn && v->changefn) + v->changefn(v); +} - return globals[h] = newvar(strdup(name), globals[h]); +void +setvar(char *name, word *val) +{ + _setvar(name, val, 1); } -/* local + global lookup */ -Var * -vlookup(char *name) +void +bigpath(var *v) { - Var *v; - if (shell) - for (v = shell->local; v; v = v->link) - if (!strcmp(v->name, name)) - return v; - return gvlookup(name); + /* convert $PATH to $path */ + char *p, *q; + word **l, *w; + + if(v->val == nil){ + _setvar("path", nil, 0); + return; + } + p = v->val->word; + w = nil; + l = &w; + /* + * Doesn't handle escaped colon nonsense. + */ + if(p[0] == 0) + p = nil; + while(p){ + q = strchr(p, ':'); + if(q) + *q = 0; + *l = newword(p[0] ? p : ".", nil); + l = &(*l)->next; + if(q){ + *q = ':'; + p = q+1; + }else + p = nil; + } + _setvar("path", w, 0); } -static +char* +list2strcolon(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 -set(char *name, Word *val, int call) +littlepath(var *v) { - Var *v = vlookup(name); - freelist(v->val); - v->val = val; - v->new = 1; - if (call && v->update) - v->update(v); + /* convert $path to $PATH */ + char *p; + word *w; + + p = list2strcolon(v->val); + w = new(word); + w->word = p; + w->next = nil; + _setvar("PATH", w, 1); /* 1: recompute $path to expose colon problems */ } void -setvar(char *name, Word *val) +pathinit(void) { - set(name, val, 1); + var *v; + + v = gvlook("path"); + v->changefn = littlepath; + v = gvlook("PATH"); + v->changefn = bigpath; + bigpath(v); } diff --git a/sys/cmd/rc/word.c b/sys/cmd/rc/word.c deleted file mode 100644 index 84ff40c..0000000 --- a/sys/cmd/rc/word.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "rc.h" - -void -pushlist(void) -{ - List *ls; - - alloc(ls); - ls->words = nil; - ls->link = shell->stack, shell->stack = ls; -} - -void -freelist(Word *w) -{ - Word *it; - while (w) { - it = w->link; - efree(w->word); - efree(w); - w = it; - } -} - -void -poplist(void) -{ - List *ls = shell->stack; - if (!ls) - panicf("shell stack underflow"); - - freelist(ls->words); - shell->stack = ls->link; - efree(ls); -} - -int -count(Word *w) -{ - int n; - for (n=0; w; n++) - w = w->link; - return n; -} - -Word* -newword(char *w, Word *link) -{ - Word *wd; - - alloc(wd); - wd->word = strdup(w); - wd->link = link; - - return wd; -} - -void -pushword(char *w) -{ - if (shell->stack == nil) - panicf("no active stack"); - shell->stack->words = newword(w, shell->stack->words); -} diff --git a/sys/cmd/rules.mk b/sys/cmd/rules.mk index 250b1ac..2302c49 100644 --- a/sys/cmd/rules.mk +++ b/sys/cmd/rules.mk @@ -2,6 +2,9 @@ include share/push.mk # Iterate through subdirectory tree +DIR := $(d)/rc +include $(DIR)/rules.mk + # DIR := $(d)/cc # include $(DIR)/rules.mk -- cgit v1.2.1