#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}, {"fg", xfg}, {"jobs", xjob}, 0, }; // ----------------------------------------------------------------------- // internal /* words and lists */ static void pushword(char *str) { if(!proc->args) fatal("attempt to push on empty argument stack\n"); proc->args->word = makeword(str, proc->args->word); } static void popword(void) { Word *w; if(!proc->args) fatal("tried to pop word on empty argument stack\n"); w = proc->args->word; if(!w) fatal("tried to pop word but nothing there\n"); proc->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 = proc->args; proc->args = stack; } static void poplist(void) { List *stack = proc->args; if(!stack) fatal("attempted to pop an empty argument stack\n"); freelist(stack->word); proc->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(!proc->args->word){ Xerror("empty argument list"); return; } execute(proc->args->word, path(proc->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(proc); if(shell.interactive){ proc->pid = getpid(); if(proc->pgid <= 0) proc->pgid = proc->pid; setpgid(pid, proc->pgid); tcsetpgrp(0, proc->pgid); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); signal(SIGCHLD, SIG_DFL); } pushword("exec"); xx(); exit(1); // NOTE: xx does not return! /*unreachable*/ default: // parent addwait(proc, pid); /* ensure the state matches for parent and child */ proc->pid = pid; if(proc->pgid <= 0) proc->pgid = pid; setpgid(pid, proc->pgid); 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 = proc->redir; proc->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 = proc; new->pgid = new->pid = -1; proc = 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]; popword(); #if 0 if(proc->args->word && strcmp(proc->args->word->str, "-i")==0){ iflag = 1; popword(); } #endif /* get input file */ if(!proc->args->word){ Xerror("usage: . [-i] file [arg ...]\n"); return; } base = strdup(proc->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 */ old = proc; // store pointer to old code run(dotcmd, 1, nil); /* operations on new command stack */ pushredir(Rclose, fd, 0); proc->cmd.path = base; proc->cmd.io = openfd(fd); /* push $* value */ pushlist(); proc->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 xjob(void) { int i; Thread *job; for(i = 0, job = shell.jobs; job; job = job->next) report(job,i); poplist(); } void xfg(void) { int i; Thread *job, *old; popword(); // fg /* get input job id */ if(!proc->args->word){ print(errio, "usage: fg [pid|\%num]\n"); poplist(); return; } i = atoi(proc->args->word->str); popword(); // [jobid] for(job=shell.jobs; i > 0; job=job->next, --i) ; assert(!job->flag.done); addwait(job, job->pid); job->flag.stop = 0; poplist(); // this goes here? job->link = proc, proc = job; // XXX: can this leave orphans? foreground(job, 1); printf("hi\n"); } 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]); /* main interpreter loop */ for(;;){ proc->code.i++; (*proc->code.exe[proc->code.i-1].f)(); } } // ----------------------------------------------------------------------- // exported interpreter bytecode void Xmark(void) { pushlist(); } void Xerror(char *msg) { print(errio, "rc: %s", msg); flush(errio); while(!proc->flag.i) Xreturn(); } void Xreturn(void) { Thread *run = proc; /* * If our job is still running we must: * 1. move program one step back to rerun Xreturn upon recall * 2. return to our calling thread * 3. don't free! */ if(run->flag.stop){ run->code.i--; proc = run->link; return; } /* * If our job has finished: * 1. remove from our list * 2. clean up its memory! */ if(run->flag.done) deljob(run); while(run->args) poplist(); freecode(run->code.exe); efree(run->wait.pid); proc = run->link; efree(run); if(!proc) exit(0); } void Xword(void) { pushword(proc->code.exe[proc->code.i++].s); } void Xdollar(void) { int n; char *s, *t; Word *a, *star; if(count(proc->args->word)!=1){ Xerror("variable name not singleton!\n"); return; } s = proc->args->word->str; // deglob(s); n = 0; for(t = s;'0'<=*t && *t<='9';t++) n = n*10+*t-'0'; a = proc->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(); proc->args->word = a; } void Xassign(void) { Var *v; if(count(proc->args->word)!=1){ Xerror("variable name not singleton!\n"); return; } //deglob(runq->argv->words->word); v = var(proc->args->word->str); poplist(); //globlist(); freewords(v->val); v->val = proc->args->word; v->new = 1; if(v->update) v->update(v); proc->args->word = nil; poplist(); } void Xreadcmd(void) { Thread *root; Word *prompt; flush(errio); root = proc; if(yyparse()){ exit(1); }else{ --root->code.i; /* re-execute Xreadcmd after codebuf runs */ run(compiled, 1, root->local); } // killzombies(); freeparsetree(); } void Xlocal(void) { if(count(proc->args->word)!=1){ Xerror("variable name must be singleton\n"); return; } //deglob(shell->args->word->str); proc->local = makevar(strdup(proc->args->word->str), proc->local); proc->local->val = copywords(proc->args->link->word, nil); proc->local->new = 1; poplist(); poplist(); } void Xunlocal(void) { Var *v = proc->local, *hide; if(!v) fatal("Xunlocal: no locals!\n", 0); printf("unlocal\n"); proc->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 = proc->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; } } /* if we are here then it's an external command */ killzombies(); addjob(proc); // run the external command if((pid = xforkx()) < 0) { Xerror("try again"); return; } poplist(); if(!shell.interactive) waitall(proc); foreground(proc, 0); // waits for child } void Xexit(void) { exit(shell.status); }