From bf03074e346b004659196b6c17eee04dbffd3ac2 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Fri, 15 Oct 2021 16:18:02 -0700 Subject: feat(rc): working prototype of input->compile->print loop --- sys/cmd/rc/exec.c | 576 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 576 insertions(+) create mode 100644 sys/cmd/rc/exec.c (limited to 'sys/cmd/rc/exec.c') diff --git a/sys/cmd/rc/exec.c b/sys/cmd/rc/exec.c new file mode 100644 index 0000000..868b1dc --- /dev/null +++ b/sys/cmd/rc/exec.c @@ -0,0 +1,576 @@ +#include "rc.h" +#include "exec.h" + +#include + +int yyparse(void); + +struct Builtin{ + char *name; + void (*func)(void); +}; + +// ----------------------------------------------------------------------- +// globals + +static Word nullpath = { .str="", .link=nil }; + +struct Builtin builtin[]={ + {".", xdot}, + 0, +}; + +/* stub out until we need */ +void Xasync(void){} +void Xconcatenate(void){} +void Xexit(void){} +void Xfunc(void){} +void Xfor(void){} +void Xglob(void){} +void Xjump(void){} +void Xmatch(void){} +void Xpipe(void){} +void Xread(void){} +// ----------------------------------------------------------------------- +// internal + +/* words and lists */ + +static +void +pushword(char *str) +{ + if(!shell->args) + fatal("attempt to push on empty argument stack"); + + shell->args->word = makeword(str, shell->args->word); +} + +static +void +popword(void) +{ + Word *w; + if(!shell->args) + fatal("tried to pop word on empty argument stack"); + + w = shell->args->word; + if(!w) + fatal("tried to pop word but nothing there"); + + shell->args->word = w->link; + efree(w->str); + efree(w); +} + +static +Word* +copywords(Word *a, Word *tail) +{ + Word *v = nil, **end; + + for(end=&v; a; a = a->link,end=&(*end)->link) + *end = makeword(a->str, nil); + *end = tail; + + return v; +} + +static +void +freewords(Word *w) +{ + Word *n; + while(w){ + efree(w->str); + n = w->link; + efree(w); + w = n; + } +} + +static +void +freelist(Word *w) +{ + Word *n; + while(w){ + n = w->link; + efree(w->str); + efree(w); + w = n; + } + +} + +static +void +pushlist(void) +{ + List *stack = emalloc(sizeof(*stack)); + + stack->word = nil; + stack->link = shell->args; + + shell->args = stack; +} + +static +void +poplist(void) +{ + List *stack = shell->args; + if(!stack) + fatal("attempted to pop an empty argument stack"); + + freelist(stack->word); + shell->args = stack->link; + efree(stack); +} + +/* system interop */ +static +Word* +path(char *w) +{ + Word *path; + + if(strncmp(w, "/", 1)==0 + || strncmp(w, "./", 2)==0 + || strncmp(w, "../", 3)==0 + || (path = var("path")->val)==0) + path=&nullpath; + + return path; +} + +static +void +xx(void) +{ + popword(); // "exec" + if(!shell->args->word){ + Xerror("empty argument list"); + return; + } + + execute(shell->args->word, path(shell->args->word->str)); + poplist(); +} + +static +int +xforkx(void) +{ + int n, pid; + char buf[ERRMAX]; + + switch(pid=fork()){ + case -1: + return -1; + case 0: // child + clearwait(); + pushword("exec"); + xx(); + exit(1); + /*unreachable*/ + default: + addwait(pid); + return pid; + } +} + +/* redirections */ +void +pushredir(int type, int from, int to) +{ + Redir *r = emalloc(sizeof(*r)); + + r->type = type; + r->from = from; + r->to = to; + r->link = shell->redir; + + shell->redir = r; +} + +/* byte code */ +static +void +run(Code *c, int pc, Var *local) +{ + Thread *new = emalloc(sizeof(*new)); + + new->code.i = pc; + new->code.exe = copycode(c); + new->args = nil; + new->local = local; + new->cmd.path = nil; + new->cmd.io = nil; + new->flag.eof = 0; + new->line = 1; + new->link = shell; + + shell = new; +} + +// ----------------------------------------------------------------------- +// exported builtins + +// XXX: find a better place for these +Word* +makeword(char *str, Word *link) +{ + Word *w = emalloc(sizeof(*w)); + + w->str = strdup(str); + w->link = link; + + return w; +} + +void +freeword(Word *word) +{ + Word *n; + + while(word){ + efree(word->str); + n = word->link; + efree(word); + word = n; + } +} + +int +count(Word *w) +{ + int n; + for(n = 0; w; n++) + w = w->link; + return n; +} + +// ----------------------------------------------------------------------- +// builtins + +static Code dotcmd[14] = +{ + [0] = {.i = 1}, + [1] = {.f = Xmark}, + [2] = {.f = Xword}, + [3] = {.s = "0"}, + [4] = {.f = Xlocal}, + [5] = {.f = Xmark}, + [6] = {.f = Xword}, + [7] = {.s = "*"}, + [8] = {.f = Xlocal}, + [9] = {.f = Xreadcmd}, + [10] = {.f = Xunlocal}, + [11] = {.f = Xunlocal}, + [12] = {.f = Xreturn}, +}; + +void +xdot(void) +{ + Word *p; + List *argv; + char *base; + int fd, iflag = 0; + Thread *old; + char file[512]; + + old = shell; + popword(); +#if 0 + if(old->args->word && strcmp(old->args->word->str, "-i")==0){ + iflag = 1; + popword(); + } +#endif + /* get input file */ + if(!old->args->word){ + Xerror("usage: . [-i] file [arg ...]\n"); + return; + } + + base = strdup(old->args->word->str); + popword(); + for(fd=-1, p=path(base); p ; p = p->link){ + strcpy(file, p->str); + + if(file[0]) + strcat(file, "/"); + strcat(file, base); + + if((fd = open(file, 0))>=0) + break; + } + + if(fd<0){ + print(errio, "%s: ", base); + //setstatus("can't open"); + Xerror(".: can't open\n"); + return; + } + /* set up for a new command loop */ + run(dotcmd, 1, nil); + pushredir(Rclose, fd, 0); + + shell->cmd.path = base; + shell->cmd.io = openfd(fd); + + /* push $* value */ + pushlist(); + shell->args->word = old->args->word; + + /* free caller's copy of $* */ + argv = old->args; + old->args = argv->link; + efree(argv); + + /* push $0 value */ + pushlist(); + pushword(base); + //ndot++; +} + +void +xboot(int argc, char *argv[]) +{ + int i; + Code bootstrap[32]; + char num[12]; + + i = 0; + bootstrap[i++].i = 1; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xassign; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xmark; + bootstrap[i++].f = Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f = Xdollar; + bootstrap[i++].f = Xword; + bootstrap[i++].s = "/dev/stdin"; + bootstrap[i++].f = Xword; + bootstrap[i++].s="."; + bootstrap[i++].f = Xbasic; + bootstrap[i++].f = Xexit; + bootstrap[i].i = 0; + + run(bootstrap, 1, nil); + pushlist(); // prime bootstrap argv + + argv0 = strdup(argv[0]); + for(i = argc-1; i > 0; --i) + pushword(argv[i]); + + /* interpreter loop */ + for(;;){ + shell->code.i++; + (*shell->code.exe[shell->code.i-1].f)(); + } +} + +// ----------------------------------------------------------------------- +// exported interpreter bytecode + +void +Xmark(void) +{ + pushlist(); +} + +void +Xerror(char *msg) +{ + print(errio, "rc: %s", msg); + flush(errio); + while(!shell->flag.i) + Xreturn(); +} + +void +Xreturn(void) +{ + Thread *run = shell; + + printf("returning\n"); + + while(run->args) + poplist(); + freecode(run->code.exe); + + shell = run->link; + efree(run); + if(!shell) + exit(0); +} + +void +Xword(void) +{ + pushword(shell->code.exe[shell->code.i++].s); +} + +void +Xdollar(void) +{ + int n; + char *s, *t; + Word *a, *star; + + if(count(shell->args->word)!=1){ + Xerror("variable name not singleton!\n"); + return; + } + s = shell->args->word->str; + // deglob(s); + n = 0; + + for(t = s;'0'<=*t && *t<='9';t++) + n = n*10+*t-'0'; + + a = shell->args->link->word; + + if(n==0 || *t) + a = copywords(var(s)->val, a); + else{ + star = var("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) + star = star->link; + + a = makeword(star->str, a); + } + } + + poplist(); + shell->args->word = a; +} + +void +Xassign(void) +{ + Var *v; + + if(count(shell->args->word)!=1){ + Xerror("variable name not singleton!\n"); + return; + } + //deglob(runq->argv->words->word); + v = var(shell->args->word->str); + poplist(); + + //globlist(); + freewords(v->val); + v->val = shell->args->word; + v->new = 1; + if(v->update) + v->update(v); + + shell->args->word = nil; + poplist(); +} + + +void +Xreadcmd(void) +{ + Thread *root; + Word *prompt; + + flush(errio); + root = shell; + + if(yyparse()){ + exit(1); + }else{ + --root->code.i; /* re-execute Xreadcmd after codebuf runs */ + run(compiled, 1, root->local); + } + + freeparsetree(); +} + +void +Xlocal(void) +{ + if(count(shell->args->word)!=1){ + Xerror("variable name must be singleton\n"); + return; + } + //deglob(shell->args->word->str); + + shell->local = makevar(strdup(shell->args->word->str), shell->local); + shell->local->val = copywords(shell->args->link->word, nil); + shell->local->new = 1; + + poplist(); + poplist(); +} + +void +Xunlocal(void) +{ + Var *v = shell->local, *hide; + if(!v) + fatal("Xunlocal: no locals!", 0); + + printf("unlocal\n"); + + shell->local = v->link; + hide = var(v->name); + hide->new = 1; + + efree(v->name); + freewords(v->val); + efree(v); +} + +void +Xbasic(void) +{ + Var *v; + Word *arg; + int pid, status; + struct Builtin *b; + + arg = shell->args->word; + if(!arg){ + Xerror("empty argument list\n"); + return; + } + print(errio, "recieved arg: %v\n", arg); // for debugging + flush(errio); + + v = var(arg->str); + if(v->func){ + // xfunc(v); + return; + } + // see if it matches a builtin + for(b = builtin; b->name; b++){ + if(strcmp(b->name, arg->str)==0){ + b->func(); + return; + } + } + + // run the external command + if((pid = xforkx()) < 0) { + Xerror("try again"); + return; + } + + poplist(); + do{ + waitpid(pid, &status, 0); + } while(!WIFEXITED(status) && !WIFSIGNALED(status)); + + printf("done waiting\n"); +} -- cgit v1.2.1