|  | /* | 
|  | * 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; | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | } |