From 425ef692da7e74112f88f0b368f3286dba84f846 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Thu, 18 Jun 2020 19:45:40 -0700 Subject: feat: working parser for rc shell language --- sys/cmd/rc/code.c | 356 ++++++++++++++++++++++++++++++++++++++++++ sys/cmd/rc/code.dep | 166 ++++++++++++++++++++ sys/cmd/rc/exec.c | 19 +++ sys/cmd/rc/io.c | 435 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sys/cmd/rc/lex.c | 415 +++++++++++++++++++++++++++++++++++++++++++++++++ sys/cmd/rc/main.c | 86 +++++++++++ sys/cmd/rc/parse.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++++++ sys/cmd/rc/rc.h | 254 ++++++++++++++++++++++++++++++ sys/cmd/rc/rules.mk | 21 +++ sys/cmd/rc/simple.c | 13 ++ sys/cmd/rc/tree.c | 144 +++++++++++++++++ sys/cmd/rc/util.c | 40 +++++ sys/cmd/rc/var.c | 108 +++++++++++++ sys/cmd/rc/word.c | 64 ++++++++ 14 files changed, 2551 insertions(+) create mode 100644 sys/cmd/rc/code.c create mode 100644 sys/cmd/rc/code.dep create mode 100644 sys/cmd/rc/exec.c create mode 100644 sys/cmd/rc/io.c create mode 100644 sys/cmd/rc/lex.c create mode 100644 sys/cmd/rc/main.c create mode 100644 sys/cmd/rc/parse.c create mode 100644 sys/cmd/rc/rc.h create mode 100644 sys/cmd/rc/rules.mk create mode 100644 sys/cmd/rc/simple.c create mode 100644 sys/cmd/rc/tree.c create mode 100644 sys/cmd/rc/util.c create mode 100644 sys/cmd/rc/var.c create mode 100644 sys/cmd/rc/word.c (limited to 'sys/cmd/rc') diff --git a/sys/cmd/rc/code.c b/sys/cmd/rc/code.c new file mode 100644 index 0000000..f38dd43 --- /dev/null +++ b/sys/cmd/rc/code.c @@ -0,0 +1,356 @@ +#include "rc.h" + +#define delcode 100 +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +#define emitf(x) ((code.ip!=code.end || morecode()), code.ip++->f = (x)) +#define emiti(x) ((code.ip!=code.end || morecode()), code.ip++->i = (x)) +#define emits(x) ((code.ip!=code.end || morecode()), code.ip++->s = (x)) + +static struct +{ + int cap; + Code *buf, *ip, *end; +} 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; +} + +static +void +stuffdot(Code *p) +{ + int a; + + a = p - code.buf; + if (code.ip <= p || p < code.buf) + panic("bad address %d in stuffdot", a); + codebuf[a].i = code.ip-code.buf; +} + +static +void +rcc(Tree *t, int eflag) +{ + Code *p, *q; + Tree *tt; + + if (!t) + return; + + switch(t->type) { + default: + pfmt(errio, "bad type %d in rc compiler\n", t->type); + break; + case '$': + emitf(Xmark); + rcc(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + rcc(c0, eflag); + emitf(Xqdol); + break; + case Asub: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xmark); + rcc(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case ';': + rcc(c0, eflag); + rcc(c1, eflag); + break; + case '^': + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + if(havefork){ + p = emiti(0); + rcc(c0, 0); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + break; + case Aandand: + rcc(c0, 0); + emitf(Xtrue); + p = emiti(0); + rcc(c1, eflag); + stuffdot(p); + break; + case Aargs: + rcc(c1, eflag); + rcc(c0, eflag); + break; + case Kbang: + rcc(c0, eflag); + emitf(Xbang); + break; + case Aparen: + case Abrace: + rcc(c0, eflag); + break; + case Acount: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xcount); + break; + case Kfunc: + emitf(Xmark); + rcc(c0, eflag); + if(c1){ + emitf(Xfn); + p = emiti(0); + emits(fnstr(c1)); + rcc(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case Kif: + rcc(c0, 0); + emitf(Xif); + p = emiti(0); + rcc(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case Kelse: + if(!runq->iflast) + error("`else' does not follow `if(...)'"); + emitf(Xelse); + p = emiti(0); + rcc(c0, eflag); + stuffdot(p); + break; + case Aoror: + rcc(c0, 0); + emitf(Xfalse); + p = emiti(0); + rcc(c1, eflag); + stuffdot(p); + break; + case Aparen: + rcc(c0, eflag); + break; + case Asimple: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xsimple); + if(eflag) + emitf(Xeflag); + break; + case Ksubsh: + emitf(Xsubshell); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else + emits(fnstr(c0)); + if(eflag) + emitf(Xeflag); + break; + case Kswitch: + codeswitch(t, eflag); + break; + case Ktwiddle: + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xmatch); + if(eflag) + emitf(Xeflag); + break; + case Kwhile: + q = code.ip; + rcc(c0, 0); + if(q==code.ip) + emitf(Xsettrue); /* empty condition == while(true) */ + emitf(Xtrue); + p = emiti(0); + rcc(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case Awords: + rcc(c1, eflag); + rcc(c0, eflag); + break; + case Kfor: + emitf(Xmark); + if(c1){ + rcc(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + rcc(c0, eflag); + emitf(Xlocal); + p = emitf(Xfor); + q = emiti(0); + rcc(c2, eflag); + emitf(Xjump); + emiti(p-code.buf); + stuffdot(q); + emitf(Xunlocal); + break; + case Aword: + emitf(Xword); + emits(strdup(t->str)); + break; + case Adup: + if(t->rtype==Adupfd){ + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } + else{ + emitf(Xclose); + emiti(t->fd0); + } + rcc(c1, eflag); + emitf(Xpopredir); + break; + case Apipefd: + emitf(Xpipefd); + emiti(t->rtype); + if(havefork){ + p = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + } + break; + case Aredir: + emitf(Xmark); + rcc(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case Rappend: + emitf(Xappend); + break; + case Rwrite: + emitf(Xwrite); + break; + case Rread: + case Rhere: + emitf(Xread); + break; + case Rrdwr: + emitf(Xrdwr); + break; + } + emiti(t->fd0); + rcc(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt = t; + for(;t && t->type=='=';t = c2); + if(t){ + for(t = tt;t->type=='=';t = c2){ + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xlocal); + } + rcc(t, eflag); + for(t = tt; t->type=='='; t = c2) + emitf(Xunlocal); + } + else{ + for(t = tt;t;t = c2){ + emitf(Xmark); + rcc(c1, eflag); + emitf(Xmark); + rcc(c0, eflag); + emitf(Xassign); + } + } + t = tt; /* so tests below will work */ + break; + case Apipe: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + if(havefork){ + p = emiti(0); + q = emiti(0); + rcc(c0, eflag); + emitf(Xexit); + stuffdot(p); + } else { + emits(fnstr(c0)); + q = emiti(0); + } + rcc(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=Kelse && t->type!=';') + runq->iflast = t->type==IF; + else if(c0) runq->iflast = c0->type==IF; +} + +Code* +compile(Tree *t) +{ + code.cap = delcode; + code.buf = code.ip = emalloc(code.cap*sizeof *code.buf); + code.end = code.ip + code.cap; + + emiti(0); + rcc(t, 0); + emitf(Xreturn); + emitf(nil); + + return code.buf; +} diff --git a/sys/cmd/rc/code.dep b/sys/cmd/rc/code.dep new file mode 100644 index 0000000..7fdd4bc --- /dev/null +++ b/sys/cmd/rc/code.dep @@ -0,0 +1,166 @@ +#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 new file mode 100644 index 0000000..2879678 --- /dev/null +++ b/sys/cmd/rc/exec.c @@ -0,0 +1,19 @@ +#include "rc.h" + +void +Xerror(char *s) +{ + +} + +void +Xmark(void) +{ + pushlist(); +} + +void +Xword(void) +{ + pushword(shell->ip++->s); +} diff --git a/sys/cmd/rc/io.c b/sys/cmd/rc/io.c new file mode 100644 index 0000000..8cc2f5b --- /dev/null +++ b/sys/cmd/rc/io.c @@ -0,0 +1,435 @@ +#include "rc.h" + +#undef bufsize +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +#define bufsize 512 +#define strsize 100 + +//------------------------------------------------------------------------ +// buffer operations + +/* open file */ +Io* +openfd(int fd) +{ + Io *f; + + f = emalloc(sizeof *f + bufsize); + f->fd = fd; + f->b = f->e = f->buf; + return f; +} + +/* open string */ +Io* +openstr(void) +{ + 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; +} + +/* open core (not nil terminated) */ +Io* +opencore(int len, char *s) +{ + 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; +} + +void +rewindio(Io *f) +{ + if (f->fd < 0) + f->b = f->buf; + else { + f->b = f->e = f->buf; + lseek(f->fd, 0, 0); + } +} + +void +closeio(Io *f) +{ + if (f->fd >= 0) + close(f->fd); + + efree(f); +} + +/* has the chance to realloc */ +void +flush(Io **fp) +{ + int n; + char *s; + Io *f; + + 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; + } + + *fp = f; +} + +//------------------------------------------------------------------------ +// 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) +{ + if (f->b == f->e) + flush(&f); + + return *f->b++=c; +} + +/* quote */ +void +pquo(Io *f, char *s) +{ + pchr(f, '\''); + for (; *s; s++) + if (*s == '\'') + pfmt(f, "''"); + else + pchr(f, *s); + pchr(f, '\''); +} + +/* word */ +void +pwrd(Io *f, char *s) +{ + char *t; + for (t = s; *t; t++) + if (!wordchr(*t)) + break; + if (t == s || *t) + pquo(f, s); + else + pstr(f, s); +} + +/* pointer */ +void +pptr(Io *f, void *v) +{ + int n; + uintptr p; + + p = (uintptr)v; + if ((sizeof(uintptr) == sizeof(uvlong)) && p >>32) + for (n = 60; n >= 32; n-=4) + pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); + for (n = 28; n >= 0; n-=4) + pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} + +/* string */ +void +pstr(Io *f, char *s) +{ + if (!s) + s = ""; + + while (*s) + pchr(f, *s++); +} + +/* decimal */ +void +pdec(Io *f, int n) +{ + 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'); +} + +/* octal */ +void +poct(Io *f, uint n) +{ + if (n > 7) + poct(f, n>>3); + pchr(f, (n&7)+'0'); +} + +/* value */ +void +pval(Io *f, Word *a) +{ + if (a) { + while (a->link && a->link->word) { + pwrd(f, a->word); + pchr(f, ' '); + a = a->link; + } + pwrd(f, a->word); + } +} + +/* tree */ +static +void +pdeglob(Io *f, char *s) +{ + while(*s){ + if(*s==GLOB) + s++; + pchr(f, *s++); + } +} + +void +pcmd(Io *f, Tree *t) +{ + if(!t) + return; + + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); + break; + case '$': pfmt(f, "$%t", c0); + break; + case '"': pfmt(f, "$\"%t", c0); + break; + case '&': pfmt(f, "%t&", c0); + break; + case '^': pfmt(f, "%t^%t", c0, c1); + break; + case '`': pfmt(f, "`%t", c0); + break; + case Aandand: pfmt(f, "%t && %t", c0, c1); + break; + case Kbang: pfmt(f, "! %t", c0); + break; + case Abrace: pfmt(f, "{%t}", c0); + break; + case Acount: pfmt(f, "$#%t", c0); + break; + case Kfunc: pfmt(f, "fn %t %t", c0, c1); + break; + case Kif: pfmt(f, "if%t%t", c0, c1); + break; + case Kelse: pfmt(f, "else %t", c0); + break; + case Aoror: pfmt(f, "%t || %t", c0, c1); + break; + case Aparen: pfmt(f, "(%t)", c0); + break; + case Asub: pfmt(f, "$%t(%t)", c0, c1); + break; + case Asimple: pfmt(f, "%t", c0); + break; + case Ksubsh: pfmt(f, "@ %t", c0); + break; + case Kswitch: pfmt(f, "switch %t %t", c0, c1); + break; + case Ktwiddle: pfmt(f, "~ %t %t", c0, c1); + break; + case Kwhile: pfmt(f, "while %t%t", c0, c1); + break; + case Aargs: + if(c0==0) + pfmt(f, "%t", c1); + else if(c1==0) + pfmt(f, "%t", c0); + else + pfmt(f, "%t %t", c0, c1); + break; + case ';': + if(c0){ + if(c1) + pfmt(f, "%t%c%t", c0, '\n', c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case Awords: + if(c0) + pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case Kfor: + pfmt(f, "for(%t", c0); + if(c1) + pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case Aword: + if(t->quoted) + pfmt(f, "%Q", t->str); + else + pdeglob(f, t->str); + break; + case Adup: + 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 Apipefd: + case Aredir: + 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 '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) + pfmt(f, " %t", c2); + break; + case Apipe: + 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; + } +} + +/* rc specific printf */ +static int pfmtlev; + +void +vpfmt(Io *f, char *fmt, va_list args) +{ + 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 '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); +} + +void +pfmt(Io *f, char *fmt, ...) +{ + va_list args; + char err[124]; + + va_start(args, fmt); + vpfmt(f, fmt, args); + va_end(args); +} diff --git a/sys/cmd/rc/lex.c b/sys/cmd/rc/lex.c new file mode 100644 index 0000000..1415f5c --- /dev/null +++ b/sys/cmd/rc/lex.c @@ -0,0 +1,415 @@ +#include "rc.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) + +// ----------------------------------------------------------------------- +// 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 +wordchr(char c) +{ + return !nwordc[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) +{ + return !nquotec[c] && c!=EOF; +} + +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 +varchr(char c) +{ + return !nvarc[c] && c!=EOF; +} + +static +void +prompt(void) +{ + shell->cmd.line++; + doprompt = 0; +} + +/* lookahead one byte */ +static +int +lookahead(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; +} + +/* consumes the lookahead */ +static +int +advance(void) +{ + int c = lookahead(); + lastc = nextc, nextc = EOF; + + return c; +} + +/* + * advance until we no longer hit horizontal space + * consumes all comments + */ +static +void +skipws(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; + } +} + +/* advance until we no longer hit any space */ +void +skipnl(void) +{ + int c; + for(;;) { + skipws(); + if ((c = lookahead()) != '\n') + return; + advance(); + } +} + +/* advance if next char is equal to c */ +static +int +nextis(int c) +{ + if(lookahead()==c) { + advance(); + return 1; + } + return 0; +} + +/* functions to append to our write buffer */ +static +char* +putbyte(char *s, int c) +{ + if(!s) + return s; + if(s == arrend(buf)){ + *s = 0; + rcerror("out of buffer space"); + return nil; + } + *s++ = c; + return s; +} + +static +char* +putrune(char *s, 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; +} + +// ----------------------------------------------------------------------- +// 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(); +} + +/* word is only modified in the event of a lexed word */ +int +lex(Tree **node) +{ + 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 Alparen; + } + if(quotechr(c)) + return Acarot; + } + + skipws(); + switch(c=advance()) { + case EOF: + lastdol = 0; + return EOF; + case '$': + lastdol = 1; + if(nextis('#')) + return Acount; + if (nextis('"')) + return Aquote; + return Adol; + case '&': + lastdol = 0; + if(nextis('&')) + return Aandand; + return Aand; + + case '!': + return Kbang; + case '@': + return Ksubsh; + case '~': + return Ktwiddle; + + case '|': + lastdol = 0; + if(nextis('|')){ + skipnl(); + return Aoror; + } + (*node) = newtree(); + (*node)->type = Apipe; + (*node)->redir.fd[0] = 0; + (*node)->redir.fd[1] = 1; + goto redir; + case '>': + (*node) = newtree(); + (*node)->type = Aredir; + if (nextis(c)) + (*node)->redir.type = Rappend; + else + (*node)->redir.type = Rwrite; + (*node)->redir.fd[0] = 1; + goto redir; + case '<': + (*node) = newtree(); + (*node)->type = Aredir; + 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 == Aredir) + (*node)->type = Adup; + 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 == Apipe) + goto redirerr; + (*node)->redir.type = Rclose; + } + } + if (c != ']' + ||(*node)->type==Adup && ((*node)->redir.type==Rhere || (*node)->redir.type==Rappend)) + goto redirerr; + } + if ((c = ((*node)->type)) == Apipe) + 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 Aword; + } + 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 = Aword; + (*node)->quoted = 0; + lastword = 1; + } + + lastdol = 0; + return c; +} diff --git a/sys/cmd/rc/main.c b/sys/cmd/rc/main.c new file mode 100644 index 0000000..baaf6bc --- /dev/null +++ b/sys/cmd/rc/main.c @@ -0,0 +1,86 @@ +#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); + parse(); + +#endif + exit(0); +} diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c new file mode 100644 index 0000000..d963c12 --- /dev/null +++ b/sys/cmd/rc/parse.c @@ -0,0 +1,430 @@ +#include "rc.h" + +// ----------------------------------------------------------------------- +// global data + +static int lasta, nexta=EOF; +static Tree *node; /* if token was lexed as a tree node (redirs and words), its here */ + +static uchar prectab[256] = { + [Kif] = 1, [Kfor] = 1, [Kswitch] = 1, [Kelse] = 1, + [Aandand] = 2, [Aoror] = 2, + [Kbang] = 3, [Ksubsh] = 3, + [Apipe] = 4, + [Acarot] = 5, + [Adol] = 6, [Acount] = 6, [Aquote] = 6, + [Asub] = 7, +}; + +// ----------------------------------------------------------------------- +// helpers + +static +int +lookahead(void) +{ + int tok; + + if (nexta != EOF) + return nexta; + + tok = lex(&node); + return nexta = tok; +} + +static +int +advance(void) +{ + int tok = lookahead(); + lasta = nexta, nexta = EOF; + node = nil; + + return tok; +} + +static +int +nextis(int tok) +{ + if (lookahead() == tok) { + advance(); + return 1; + } + return 0; +} + +// ----------------------------------------------------------------------- +// subparsers + +static Tree *word(void); +static Tree *comword(void); +static Tree *cmd(int prec); + +static +Tree* +body(void) +{ + int tok; + Tree *l, *r; + l = cmd(1); +loop: + switch((tok=lookahead())){ + case '&': + l = tree1('&', l); + /* fallthrough */ + case ';': case '\n': + advance(); + r = cmd(1); + l = tree2(';', l, r); + goto loop; + default: + ; + } + + return l; +} + +static +Tree* +brace(void) +{ + Tree *t; + + if (!nextis('{')) + rcerror("not a brace"); + t = tree1(Abrace, body()); + if (!nextis('}')) + rcerror("unmatched brace"); + + return t; +} + +static +Tree* +paren(void) +{ + Tree *t; + + if (!nextis('(')) + rcerror("not a paren"); + t = tree1(Aparen, body()); + 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 Adup: + t = node; + advance(); + break; + case Aredir: + advance(); + t = hang1(node, (node->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(Awords, t, tt); + + return t; +} + +static +Tree* +comword(void) +{ + int tok; + Tree *t, *tt; + + switch(tok=lookahead()){ + case Adol: + advance(); + t = word(); + if(nextis('(')) { + t = tree2(Asub, t, words()); + if (!nextis(')')) + rcerror("malformed index expression"); + } + return tree1(Adol, t); + case Acount: + advance(); + return tree1(Acount, word()); + case Atick: + advance(); + return tree1(Atick, brace()); + case Alparen: + return paren(); + case Aredir: + advance(); + t = hang1(node, brace()); + t->type = Apipefd; + return t; + case Aword: + 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('=')) + return tree3(Aeq, t, word(), cmd(prectab[Kbang])); + + /* is a 'simple' */ +simple: + switch ((tok=lookahead())) { + case Aredir: + case Adup: + t = tree2(Aargs, t, redir()); + goto simple; + default: + if ((tt = word())) { + t = tree2(Aargs, t, tt); + goto simple; + } + /* fallthrough */ + } + + return simplehang(t); +} + +static +Tree* +opand(void) +{ + int tok; + Tree *t, *tt; + + switch(tok=lookahead()) { + case Kif: + advance(); + t = paren(); + skipnl(); + tt = cmd(prectab[Kif]); + t = tree2(Kif, t, tt); + return t; + + case Kelse: + advance(); + skipnl(); + t = tree1(Kelse, cmd(prectab[Kelse])); + return t; + + case Kfor: + advance(); + if (!nextis('(')) + rcerror("malformed for statement"); + t = word(); + if (nextis(Kin)) { + advance(); + tt = words(); + t = tree3(Kin, t, tt, nil); + } else + t = tree3(Kin, t, nil, nil); + skipnl(); + tt = cmd(prectab[Kfor]); + t->child[2] = tt; + return t; + + case Kswitch: + advance(); + t = word(); + skipnl(); + tt = brace(); + t = tree2(Kswitch, t, tt); + return t; + + case Kfunc: + advance(); + t = words(); + if ((tok=lookahead()) == '{') { + tt = brace(); + t = tree2(Kfunc, t, tt); + } else + t = tree1(Kfunc, t); + return t; + + case Ksubsh: + advance(); + t = tree1(Ksubsh, cmd(prectab[Ksubsh])); + return t; + + case Kbang: + advance(); + t = tree1(Kbang, cmd(prectab[Kbang])); + return t; + + case Ktwiddle: + advance(); + tt = word(); + t = tree2(Ktwiddle, tt, words()); + return t; + + case Albrace: + t = brace(); + tt = epilog(); + return epihang(t, tt); + + case Aredir: /* fallthrough */ + case Adup: + t = redir(); + tt = cmd(prectab[Kbang]); + 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 == Apipe) + 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': case EOF: + pfmt(errio, "%t", t); + break; + default: + rcerror("unrecognized token: %d", tok); + } + return 0; +} diff --git a/sys/cmd/rc/rc.h b/sys/cmd/rc/rc.h new file mode 100644 index 0000000..ad46a77 --- /dev/null +++ b/sys/cmd/rc/rc.h @@ -0,0 +1,254 @@ +#pragma once + +#include +#include + +#include +#include + +#define alloc(ptr) ptr = emalloc(sizeof(*ptr)) + +// ----------------------------------------------------------------------- +// main enums + +#define GLOB 0x01 + +/* TODO: make sure there are no collisions */ +enum +{ + /* keywords */ + Kstart=11, Kfor, Kin, Kwhile, Kif, Kelse, + Kswitch, Kcase, Kfunc, Ktwiddle, + Kbang, Ksubsh, Kend, + + /* tokens */ + Aword='w', Aredir='r', Adup='d', Asimple='s', + Aargs='A', Awords='W', Abrace='b', Aparen='p', Asub='S', + Apcmd='c', Apipefd='-', Aandand='%', Aoror='@', Acount='#', + + Atick='`', Apipe = '|', Adol='$', Aquote='"', Aand='&', + Alparen = '(', Arparen = ')', Albrace='{', Arbrace='}', + Asemi=';', Acarot='^', Aeq='=', +}; + +enum +{ + Rappend = 1, + Rwrite = 2, + Rread = 3, + Rhere = 4, + Rdupfd = 5, + Rclose = 6, + Rrdwr = 7, +}; + +// ----------------------------------------------------------------------- +// main types + +typedef union Code Code; +typedef struct Word Word; +typedef struct List List; +typedef struct Var Var; +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 Var +{ + string name; + Word *val; + Code *func; + Var *link; +}; + +struct Tree +{ + ushort type; + uchar quoted : 1; + union { + char *str; + struct { + ushort type; + int fd[2]; + } redir; + }; + + Tree *child[3], *link; +}; + +struct Builtin +{ + char *cmd; + void (*func)(void); +}; + +struct Thread +{ + Code *code, *ip; + List *stack; + Var *local; + struct { + uchar eof : 1; + int line; + char *name; + Io *io; + } cmd; + + int pid; + Tree *nodes; + Thread *link; /* continuation */ +}; + +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 + * -> moves value + */ +extern void Xmark(void); /* Xmark: delimit stack with new list */ +extern void Xword(void); /* Xword[val] -> (val) */ +extern void Xassign(void); /* Xassign(name, val): assign name to val */ +extern void Xdol(void); /* Xdol(name): get variable value */ +extern void Xsimple(void); /* Xsimple(args): run command */ +extern void Xexit(void); /* Xexit: exit with status */ +extern void Xerror(char *s); /* Xerror: report an error */ +extern void Xparse(void); + +// ----------------------------------------------------------------------- +// 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 + */ + +Var *newvar(char *name, Var *link); +Var *gvlookup(char *name); +Var *vlookup(char *name); + +int kwlookup(char *name); +void initkw(void); + +/* + * lex.c + */ + +void skipnl(void); +int wordchr(char c); + +void rcerror(char *msg, ...); +int lex(Tree **node); + +/* + * parse.c + */ + +int parse(void); + +/* + * main.c + */ + +void dotrap(void); diff --git a/sys/cmd/rc/rules.mk b/sys/cmd/rc/rules.mk new file mode 100644 index 0000000..33d6ba8 --- /dev/null +++ b/sys/cmd/rc/rules.mk @@ -0,0 +1,21 @@ +include share/push.mk + +# Local sources +SRCS_$(d) := \ + $(d)/io.c \ + $(d)/util.c \ + $(d)/var.c \ + $(d)/word.c \ + $(d)/tree.c \ + $(d)/lex.c \ + $(d)/parse.c \ + $(d)/main.c +BINS_$(d) := $(d)/rc + +include share/paths.mk + +# Local rules +$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a + $(COMPLINK) + +include share/pop.mk diff --git a/sys/cmd/rc/simple.c b/sys/cmd/rc/simple.c new file mode 100644 index 0000000..f934aa1 --- /dev/null +++ b/sys/cmd/rc/simple.c @@ -0,0 +1,13 @@ +void +Xsimple(void) +{ + Word *a; + Var *v; + + a = shell->stack->words; + if (!a) { + Xerror("empty argument list"); + return; + } + v = vlookup(a->word); +} diff --git a/sys/cmd/rc/tree.c b/sys/cmd/rc/tree.c new file mode 100644 index 0000000..8dca67f --- /dev/null +++ b/sys/cmd/rc/tree.c @@ -0,0 +1,144 @@ +#include "rc.h" + +// ----------------------------------------------------------------------- +// globals + +static Tree *nodes; + +// ----------------------------------------------------------------------- +// exported funcs + +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; +} + +void +freetree(Tree *t) +{ + if (!t) + return; + + freetree(t->child[0]); + freetree(t->child[1]); + freetree(t->child[2]); + + if (t->str) + efree(t->str); + efree(t); +} + +void +freenodes(void) +{ + Tree *t, *u; + + for (t = nodes;t;t = u) { + u = t->link; + if (t->str) + efree(t->str); + efree(t); + } + nodes = nil; +} + +/* tree creation */ +Tree* +tree3(int type, Tree *c0, Tree *c1, Tree *c2) +{ + Tree *t; + + t = newtree(); + t->type = type; + t->child[0] = c0; + t->child[1] = c1; + t->child[2] = c2; + + return t; +} + +Tree* +tree2(int type, Tree *c0, Tree *c1) +{ + return tree3(type, c0, c1, nil); +} + +Tree* +tree1(int type, Tree *c0) +{ + return tree3(type, c0, nil, nil); +} + +/* tree hang */ +Tree* +hang1(Tree *p, Tree *c0) +{ + p->child[0] = c0; + return p; +} + +Tree* +hang2(Tree *p, Tree *c0, Tree *c1) +{ + p->child[0] = c0; + p->child[1] = c1; + return p; +} + +Tree* +hang3(Tree *p, Tree *c0, Tree *c1, Tree *c2) +{ + p->child[0] = c0; + p->child[1] = c1; + p->child[2] = c2; + return p; +} + +/* hangs the cmd underneath the epilogue */ +Tree* +epihang(Tree *c, Tree *epi) +{ + Tree *p; + if(!epi) + return c; + for(p=epi;p->child[1];p = p->child[1]) + ; + p->child[1] = c; + return epi; +} + +/* hangs tree t from a new simple node. percolates redirections to root */ +Tree* +simplehang(Tree *t) +{ + Tree *u; + t = tree1(Asimple, t); + for(u = t->child[0];u->type==Aargs;u=u->child[0]) { + if (u->child[1]->type==Adup + || u->child[1]->type==Aredir){ + u->child[1]->child[1] = t; + t = u->child[1]; + u->child[1] = nil; + } + } + return t; +} + +Tree* +wordnode(char *w) +{ + Tree *t = newtree(); + t->type = Aword; + t->str = strdup(w); + + return t; +} diff --git a/sys/cmd/rc/util.c b/sys/cmd/rc/util.c new file mode 100644 index 0000000..02b3611 --- /dev/null +++ b/sys/cmd/rc/util.c @@ -0,0 +1,40 @@ +#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 new file mode 100644 index 0000000..dbe7c14 --- /dev/null +++ b/sys/cmd/rc/var.c @@ -0,0 +1,108 @@ +#include "rc.h" + +Var *globals[1021] = { 0 }; + +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; +} + +// ----------------------------------------------------------------------- +// keywords + +static +void +putkw(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; +} + +void +initkw(void) +{ + putkw(Kfor, "for"); + putkw(Kin, "in"); + putkw(Kwhile, "while"); + putkw(Kif, "if"); + putkw(Kelse, "else"); + putkw(Kswitch, "switch"); + putkw(Kcase, "case"); + putkw(Kfunc, "func"); +} + +int +kwlookup(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; +} + +// ----------------------------------------------------------------------- +// variables + +Var * +newvar(char *name, Var *link) +{ + Var *v; + + alloc(v); + v->name = name; + v->val = 0; + v->func = nil; + v->link = link; + + return v; +} + +/* only global lookup */ + +Var * +gvlookup(char *name) +{ + Var *v; + int h = hash(name, arrlen(globals)); + for (v = globals[h]; v; v = v->link) + if (!strcmp(v->name, name)) + return v; + + return globals[h] = newvar(strdup(name), globals[h]); +} + +/* local + global lookup */ +Var * +vlookup(char *name) +{ + Var *v; + if (shell) + for (v = shell->local; v; v = v->link) + if (!strcmp(v->name, name)) + return v; + return gvlookup(name); +} diff --git a/sys/cmd/rc/word.c b/sys/cmd/rc/word.c new file mode 100644 index 0000000..84ff40c --- /dev/null +++ b/sys/cmd/rc/word.c @@ -0,0 +1,64 @@ +#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); +} -- cgit v1.2.1