| /* | 
 |  * 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, n; | 
 | 	char buf[ERRMAX]; | 
 | 	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 */ | 
 | 			switch(pid=fork()){ | 
 | 			case -1: | 
 | 				Xerror("try again"); | 
 | 				return; | 
 | 			case 0: | 
 | 				pushword("exec"); | 
 | 				execexec(); | 
 | 				strcpy(buf, "can't exec: "); | 
 | 				n = strlen(buf); | 
 | 				errstr(buf+n, ERRMAX-n); | 
 | 				Exit(buf); | 
 | 			default: | 
 | 				kidpid = pid; | 
 | 				poplist(); | 
 | 				/* interrupts don't get us out */ | 
 | 				while(Waitfor(pid, 1) < 0) | 
 | 					; | 
 | 				kidpid = 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, (struct var *)0); | 
 | 	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(strcmp(file, "/dev/stdin")==0){	/* for sun & ucb */ | 
 | 			fd=Dup1(0); | 
 | 			if(fd>=0) break; | 
 | 		} | 
 | 		if((fd=open(file, 0))>=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(); | 
 | } |