1148 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1148 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #define Extern extern
 | |
| #include <sys/types.h>
 | |
| #include <signal.h>
 | |
| #include <errno.h>
 | |
| #include <setjmp.h>
 | |
| #include <stddef.h>
 | |
| #include <time.h>
 | |
| #include <sys/times.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/wait.h>
 | |
| #undef NULL
 | |
| #include "sh.h"
 | |
| 
 | |
| /* -------- exec.c -------- */
 | |
| /* #include "sh.h" */
 | |
| 
 | |
| /*
 | |
|  * execute tree
 | |
|  */
 | |
| 
 | |
| static	char	*signame[] = {
 | |
| 	"Signal 0",
 | |
| 	"Hangup",
 | |
| 	(char *)NULL,	/* interrupt */
 | |
| 	"Quit",
 | |
| 	"Illegal instruction",
 | |
| 	"Trace/BPT trap",
 | |
| 	"Abort",
 | |
| 	"EMT trap",
 | |
| 	"Floating exception",
 | |
| 	"Killed",
 | |
| 	"Bus error",
 | |
| 	"Memory fault",
 | |
| 	"Bad system call",
 | |
| 	(char *)NULL,	/* broken pipe */
 | |
| 	"Alarm clock",
 | |
| 	"Terminated",
 | |
| };
 | |
| #define	NSIGNAL (sizeof(signame)/sizeof(signame[0]))
 | |
| 
 | |
| 
 | |
| _PROTOTYPE(static int forkexec, (struct op *t, int *pin, int *pout, int act, char **wp, int *pforked ));
 | |
| _PROTOTYPE(static int parent, (void));
 | |
| _PROTOTYPE(int iosetup, (struct ioword *iop, int pipein, int pipeout ));
 | |
| _PROTOTYPE(static void echo, (char **wp ));
 | |
| _PROTOTYPE(static struct op **find1case, (struct op *t, char *w ));
 | |
| _PROTOTYPE(static struct op *findcase, (struct op *t, char *w ));
 | |
| _PROTOTYPE(static void brkset, (struct brkcon *bc ));
 | |
| _PROTOTYPE(int dolabel, (void));
 | |
| _PROTOTYPE(int dochdir, (struct op *t ));
 | |
| _PROTOTYPE(int doshift, (struct op *t ));
 | |
| _PROTOTYPE(int dologin, (struct op *t ));
 | |
| _PROTOTYPE(int doumask, (struct op *t ));
 | |
| _PROTOTYPE(int doexec, (struct op *t ));
 | |
| _PROTOTYPE(int dodot, (struct op *t ));
 | |
| _PROTOTYPE(int dowait, (struct op *t ));
 | |
| _PROTOTYPE(int doread, (struct op *t ));
 | |
| _PROTOTYPE(int doeval, (struct op *t ));
 | |
| _PROTOTYPE(int dotrap, (struct op *t ));
 | |
| _PROTOTYPE(int getsig, (char *s ));
 | |
| _PROTOTYPE(void setsig, (int n, void (*f)()));
 | |
| _PROTOTYPE(int getn, (char *as ));
 | |
| _PROTOTYPE(int dobreak, (struct op *t ));
 | |
| _PROTOTYPE(int docontinue, (struct op *t ));
 | |
| _PROTOTYPE(static int brkcontin, (char *cp, int val ));
 | |
| _PROTOTYPE(int doexit, (struct op *t ));
 | |
| _PROTOTYPE(int doexport, (struct op *t ));
 | |
| _PROTOTYPE(int doreadonly, (struct op *t ));
 | |
| _PROTOTYPE(static void rdexp, (char **wp, void (*f)(), int key));
 | |
| _PROTOTYPE(static void badid, (char *s ));
 | |
| _PROTOTYPE(int doset, (struct op *t ));
 | |
| _PROTOTYPE(void varput, (char *s, int out ));
 | |
| _PROTOTYPE(int dotimes, (void));
 | |
| 
 | |
| int
 | |
| execute(t, pin, pout, act)
 | |
| register struct op *t;
 | |
| int *pin, *pout;
 | |
| int act;
 | |
| {
 | |
| 	register struct op *t1;
 | |
| 	int i, pv[2], rv, child, a;
 | |
| 	char *cp, **wp, **wp2;
 | |
| 	struct var *vp;
 | |
| 	struct brkcon bc;
 | |
| 
 | |
| 	if (t == NULL)
 | |
| 		return(0);
 | |
| 	rv = 0;
 | |
| 	a = areanum++;
 | |
| 	wp = (wp2 = t->words) != NULL
 | |
| 	     ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY)
 | |
| 	     : NULL;
 | |
| 
 | |
| 	switch(t->type) {
 | |
| 	case TPAREN:
 | |
| 	case TCOM:
 | |
| 		rv = forkexec(t, pin, pout, act, wp, &child);
 | |
| 		if (child) {
 | |
| 			exstat = rv;
 | |
| 			leave();
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case TPIPE:
 | |
| 		if ((rv = openpipe(pv)) < 0)
 | |
| 			break;
 | |
| 		pv[0] = remap(pv[0]);
 | |
| 		pv[1] = remap(pv[1]);
 | |
| 		(void) execute(t->left, pin, pv, 0);
 | |
| 		rv = execute(t->right, pv, pout, 0);
 | |
| 		break;
 | |
| 
 | |
| 	case TLIST:
 | |
| 		(void) execute(t->left, pin, pout, 0);
 | |
| 		rv = execute(t->right, pin, pout, 0);
 | |
| 		break;
 | |
| 
 | |
| 	case TASYNC:
 | |
| 		i = parent();
 | |
| 		if (i != 0) {
 | |
| 			if (i != -1) {
 | |
| 				setval(lookup("!"), putn(i));
 | |
| 				if (pin != NULL)
 | |
| 					closepipe(pin);
 | |
| 				if (talking) {
 | |
| 					prs(putn(i));
 | |
| 					prs("\n");
 | |
| 				}
 | |
| 			} else
 | |
| 				rv = -1;
 | |
| 			setstatus(rv);
 | |
| 		} else {
 | |
| 			signal(SIGINT, SIG_IGN);
 | |
| 			signal(SIGQUIT, SIG_IGN);
 | |
| 			if (talking)
 | |
| 				signal(SIGTERM, SIG_DFL);
 | |
| 			talking = 0;
 | |
| 			if (pin == NULL) {
 | |
| 				close(0);
 | |
| 				open("/dev/null", 0);
 | |
| 			}
 | |
| 			exit(execute(t->left, pin, pout, FEXEC));
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case TOR:
 | |
| 	case TAND:
 | |
| 		rv = execute(t->left, pin, pout, 0);
 | |
| 		if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND))
 | |
| 			rv = execute(t1, pin, pout, 0);
 | |
| 		break;
 | |
| 
 | |
| 	case TFOR:
 | |
| 		if (wp == NULL) {
 | |
| 			wp = dolv+1;
 | |
| 			if ((i = dolc) < 0)
 | |
| 				i = 0;
 | |
| 		} else {
 | |
| 			i = -1;
 | |
| 			while (*wp++ != NULL)
 | |
| 				;			
 | |
| 		}
 | |
| 		vp = lookup(t->str);
 | |
| 		while (setjmp(bc.brkpt))
 | |
| 			if (isbreak)
 | |
| 				goto broken;
 | |
| 		brkset(&bc);
 | |
| 		for (t1 = t->left; i-- && *wp != NULL;) {
 | |
| 			setval(vp, *wp++);
 | |
| 			rv = execute(t1, pin, pout, 0);
 | |
| 		}
 | |
| 		brklist = brklist->nextlev;
 | |
| 		break;
 | |
| 
 | |
| 	case TWHILE:
 | |
| 	case TUNTIL:
 | |
| 		while (setjmp(bc.brkpt))
 | |
| 			if (isbreak)
 | |
| 				goto broken;
 | |
| 		brkset(&bc);
 | |
| 		t1 = t->left;
 | |
| 		while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
 | |
| 			rv = execute(t->right, pin, pout, 0);
 | |
| 		brklist = brklist->nextlev;
 | |
| 		break;
 | |
| 
 | |
| 	case TIF:
 | |
| 	case TELIF:
 | |
| 	 	if (t->right != NULL) {
 | |
| 		rv = !execute(t->left, pin, pout, 0) ?
 | |
| 			execute(t->right->left, pin, pout, 0):
 | |
| 			execute(t->right->right, pin, pout, 0);
 | |
| 		}
 | |
| 		break;
 | |
| 
 | |
| 	case TCASE:
 | |
| 		if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0)
 | |
| 			cp = "";
 | |
| 		if ((t1 = findcase(t->left, cp)) != NULL)
 | |
| 			rv = execute(t1, pin, pout, 0);
 | |
| 		break;
 | |
| 
 | |
| 	case TBRACE:
 | |
| /*
 | |
| 		if (iopp = t->ioact)
 | |
| 			while (*iopp)
 | |
| 				if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
 | |
| 					rv = -1;
 | |
| 					break;
 | |
| 				}
 | |
| */
 | |
| 		if (rv >= 0 && (t1 = t->left))
 | |
| 			rv = execute(t1, pin, pout, 0);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| broken:
 | |
| 	t->words = wp2;
 | |
| 	isbreak = 0;
 | |
| 	freehere(areanum);
 | |
| 	freearea(areanum);
 | |
| 	areanum = a;
 | |
| 	if (talking && intr) {
 | |
| 		closeall();
 | |
| 		fail();
 | |
| 	}
 | |
| 	if ((i = trapset) != 0) {
 | |
| 		trapset = 0;
 | |
| 		runtrap(i);
 | |
| 	}
 | |
| 	return(rv);
 | |
| }
 | |
| 
 | |
| static int
 | |
| forkexec(t, pin, pout, act, wp, pforked)
 | |
| register struct op *t;
 | |
| int *pin, *pout;
 | |
| int act;
 | |
| char **wp;
 | |
| int *pforked;
 | |
| {
 | |
| 	int i, rv, (*shcom)();
 | |
| 	register int f;
 | |
| 	char *cp;
 | |
| 	struct ioword **iopp;
 | |
| 	int resetsig;
 | |
| 	char **owp;
 | |
| 
 | |
| 	owp = wp;
 | |
| 	resetsig = 0;
 | |
| 	*pforked = 0;
 | |
| 	shcom = NULL;
 | |
| 	rv = -1;	/* system-detected error */
 | |
| 	if (t->type == TCOM) {
 | |
| 		while ((cp = *wp++) != NULL)
 | |
| 			;
 | |
| 		cp = *wp;
 | |
| 
 | |
| 		/* strip all initial assignments */
 | |
| 		/* not correct wrt PATH=yyy command  etc */
 | |
| 		if (flag['x'])
 | |
| 			echo (cp ? wp: owp);
 | |
| 		if (cp == NULL && t->ioact == NULL) {
 | |
| 			while ((cp = *owp++) != NULL && assign(cp, COPYV))
 | |
| 				;
 | |
| 			return(setstatus(0));
 | |
| 		}
 | |
| 		else if (cp != NULL)
 | |
| 			shcom = inbuilt(cp);
 | |
| 	}
 | |
| 	t->words = wp;
 | |
| 	f = act;
 | |
| 	if (shcom == NULL && (f & FEXEC) == 0) {
 | |
| 		i = parent();
 | |
| 		if (i != 0) {
 | |
| 			if (i == -1)
 | |
| 				return(rv);
 | |
| 			if (pin != NULL)
 | |
| 				closepipe(pin);
 | |
| 			return(pout==NULL? setstatus(waitfor(i,0)): 0);
 | |
| 		}
 | |
| 		if (talking) {
 | |
| 			signal(SIGINT, SIG_IGN);
 | |
| 			signal(SIGQUIT, SIG_IGN);
 | |
| 			resetsig = 1;
 | |
| 		}
 | |
| 		talking = 0;
 | |
| 		intr = 0;
 | |
| 		(*pforked)++;
 | |
| 		brklist = 0;
 | |
| 		execflg = 0;
 | |
| 	}
 | |
| 	if (owp != NULL)
 | |
| 		while ((cp = *owp++) != NULL && assign(cp, COPYV))
 | |
| 			if (shcom == NULL)
 | |
| 				export(lookup(cp));
 | |
| #ifdef COMPIPE
 | |
| 	if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
 | |
| 		err("piping to/from shell builtins not yet done");
 | |
| 		return(-1);
 | |
| 	}
 | |
| #endif
 | |
| 	if (pin != NULL) {
 | |
| 		dup2(pin[0], 0);
 | |
| 		closepipe(pin);
 | |
| 	}
 | |
| 	if (pout != NULL) {
 | |
| 		dup2(pout[1], 1);
 | |
| 		closepipe(pout);
 | |
| 	}
 | |
| 	if ((iopp = t->ioact) != NULL) {
 | |
| 		if (shcom != NULL && shcom != doexec) {
 | |
| 			prs(cp);
 | |
| 			err(": cannot redirect shell command");
 | |
| 			return(-1);
 | |
| 		}
 | |
| 		while (*iopp)
 | |
| 			if (iosetup(*iopp++, pin!=NULL, pout!=NULL))
 | |
| 				return(rv);
 | |
| 	}
 | |
| 	if (shcom)
 | |
| 		return(setstatus((*shcom)(t)));
 | |
| 	/* should use FIOCEXCL */
 | |
| 	for (i=FDBASE; i<NOFILE; i++)
 | |
| 		close(i);
 | |
| 	if (resetsig) {
 | |
| 		signal(SIGINT, SIG_DFL);
 | |
| 		signal(SIGQUIT, SIG_DFL);
 | |
| 	}
 | |
| 	if (t->type == TPAREN)
 | |
| 		exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
 | |
| 	if (wp[0] == NULL)
 | |
| 		exit(0);
 | |
| 	cp = rexecve(wp[0], wp, makenv());
 | |
| 	prs(wp[0]); prs(": "); warn(cp);
 | |
| 	if (!execflg)
 | |
| 		trap[0] = NULL;
 | |
| 	leave();
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * common actions when creating a new child
 | |
|  */
 | |
| static int
 | |
| parent()
 | |
| {
 | |
| 	register int i;
 | |
| 
 | |
| 	i = fork();
 | |
| 	if (i != 0) {
 | |
| 		if (i == -1)
 | |
| 			warn("try again");
 | |
| 	}
 | |
| 	return(i);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 0< 1> are ignored as required
 | |
|  * within pipelines.
 | |
|  */
 | |
| int
 | |
| iosetup(iop, pipein, pipeout)
 | |
| register struct ioword *iop;
 | |
| int pipein, pipeout;
 | |
| {
 | |
| 	register u;
 | |
| 	char *cp, *msg;
 | |
| 
 | |
| 	if (iop->io_unit == IODEFAULT)	/* take default */
 | |
| 		iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1;
 | |
| 	if (pipein && iop->io_unit == 0)
 | |
| 		return(0);
 | |
| 	if (pipeout && iop->io_unit == 1)
 | |
| 		return(0);
 | |
| 	msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create";
 | |
| 	if ((iop->io_flag & IOHERE) == 0) {
 | |
| 		cp = iop->io_name;
 | |
| 		if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL)
 | |
| 			return(1);
 | |
| 	}
 | |
| 	if (iop->io_flag & IODUP) {
 | |
| 		if (cp[1] || (!digit(*cp) && *cp != '-')) {
 | |
| 			prs(cp);
 | |
| 			err(": illegal >& argument");
 | |
| 			return(1);
 | |
| 		}
 | |
| 		if (*cp == '-')
 | |
| 			iop->io_flag = IOCLOSE;
 | |
| 		iop->io_flag &= ~(IOREAD|IOWRITE);
 | |
| 	}
 | |
| 	switch (iop->io_flag) {
 | |
| 	case IOREAD:
 | |
| 		u = open(cp, 0);
 | |
| 		break;
 | |
| 
 | |
| 	case IOHERE:
 | |
| 	case IOHERE|IOXHERE:
 | |
| 		u = herein(iop->io_name, iop->io_flag&IOXHERE);
 | |
| 		cp = "here file ";
 | |
| 		break;
 | |
| 
 | |
| 	case IOWRITE|IOCAT:
 | |
| 		if ((u = open(cp, 1)) >= 0) {
 | |
| 			lseek(u, (long)0, 2);
 | |
| 			break;
 | |
| 		}
 | |
| 	case IOWRITE:
 | |
| 		u = creat(cp, 0666);
 | |
| 		break;
 | |
| 
 | |
| 	case IODUP:
 | |
| 		u = dup2(*cp-'0', iop->io_unit);
 | |
| 		break;
 | |
| 
 | |
| 	case IOCLOSE:
 | |
| 		close(iop->io_unit);
 | |
| 		return(0);
 | |
| 	}
 | |
| 	if (u < 0) {
 | |
| 		int e=errno;
 | |
| 		prs(cp);
 | |
|                 if (iop->io_flag&IOHERE) prs(iop->io_name);
 | |
| 		prs(": cannot ");
 | |
| 	        prs(msg);
 | |
| 	        prs(" (");
 | |
| 	        prs(strerror(e));
 | |
| 		warn(")");
 | |
| 		return(1);
 | |
| 	} else {
 | |
| 		if (u != iop->io_unit) {
 | |
| 			dup2(u, iop->io_unit);
 | |
| 			close(u);
 | |
| 		}
 | |
| 	}
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| echo(wp)
 | |
| register char **wp;
 | |
| {
 | |
| 	register i;
 | |
| 
 | |
| 	prs("+");
 | |
| 	for (i=0; wp[i]; i++) {
 | |
| 		if (i)
 | |
| 			prs(" ");
 | |
| 		prs(wp[i]);
 | |
| 	}
 | |
| 	prs("\n");
 | |
| }
 | |
| 
 | |
| static struct op **
 | |
| find1case(t, w)
 | |
| struct op *t;
 | |
| char *w;
 | |
| {
 | |
| 	register struct op *t1;
 | |
| 	struct op **tp;
 | |
| 	register char **wp, *cp;
 | |
| 
 | |
| 	if (t == NULL)
 | |
| 		return((struct op **)NULL);
 | |
| 	if (t->type == TLIST) {
 | |
| 		if ((tp = find1case(t->left, w)) != NULL)
 | |
| 			return(tp);
 | |
| 		t1 = t->right;	/* TPAT */
 | |
| 	} else
 | |
| 		t1 = t;
 | |
| 	for (wp = t1->words; *wp;)
 | |
| 		if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp))
 | |
| 			return(&t1->left);
 | |
| 	return((struct op **)NULL);
 | |
| }
 | |
| 
 | |
| static struct op *
 | |
| findcase(t, w)
 | |
| struct op *t;
 | |
| char *w;
 | |
| {
 | |
| 	register struct op **tp;
 | |
| 
 | |
| 	return((tp = find1case(t, w)) != NULL? *tp: (struct op *)NULL);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Enter a new loop level (marked for break/continue).
 | |
|  */
 | |
| static void
 | |
| brkset(bc)
 | |
| struct brkcon *bc;
 | |
| {
 | |
| 	bc->nextlev = brklist;
 | |
| 	brklist = bc;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Wait for the last process created.
 | |
|  * Print a message for each process found
 | |
|  * that was killed by a signal.
 | |
|  * Ignore interrupt signals while waiting
 | |
|  * unless `canintr' is true.
 | |
|  */
 | |
| int
 | |
| waitfor(lastpid, canintr)
 | |
| register int lastpid;
 | |
| int canintr;
 | |
| {
 | |
| 	register int pid, rv;
 | |
| 	int s;
 | |
| 	int oheedint = heedint;
 | |
| 
 | |
| 	heedint = 0;
 | |
| 	rv = 0;
 | |
| 	do {
 | |
| 		pid = wait(&s);
 | |
| 		if (pid == -1) {
 | |
| 			if (errno != EINTR || canintr)
 | |
| 				break;
 | |
| 		} else {
 | |
| 			if ((rv = WAITSIG(s)) != 0) {
 | |
| 				if (rv < NSIGNAL) {
 | |
| 					if (signame[rv] != NULL) {
 | |
| 						if (pid != lastpid) {
 | |
| 							prn(pid);
 | |
| 							prs(": ");
 | |
| 						}
 | |
| 						prs(signame[rv]);
 | |
| 					}
 | |
| 				} else {
 | |
| 					if (pid != lastpid) {
 | |
| 						prn(pid);
 | |
| 						prs(": ");
 | |
| 					}
 | |
| 					prs("Signal "); prn(rv); prs(" ");
 | |
| 				}
 | |
| 				if (WAITCORE(s))
 | |
| 					prs(" - core dumped");
 | |
| 				if (rv >= NSIGNAL || signame[rv])
 | |
| 					prs("\n");
 | |
| 				rv = -1;
 | |
| 			} else
 | |
| 				rv = WAITVAL(s);
 | |
| 		}
 | |
| 	} while (pid != lastpid);
 | |
| 	heedint = oheedint;
 | |
| 	if (intr)
 | |
| 		if (talking) {
 | |
| 			if (canintr)
 | |
| 				intr = 0;
 | |
| 		} else {
 | |
| 			if (exstat == 0) exstat = rv;
 | |
| 			onintr(0);
 | |
| 		}
 | |
| 	return(rv);
 | |
| }
 | |
| 
 | |
| int
 | |
| setstatus(s)
 | |
| register int s;
 | |
| {
 | |
| 	exstat = s;
 | |
| 	setval(lookup("?"), putn(s));
 | |
| 	return(s);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * PATH-searching interface to execve.
 | |
|  * If getenv("PATH") were kept up-to-date,
 | |
|  * execvp might be used.
 | |
|  */
 | |
| char *
 | |
| rexecve(c, v, envp)
 | |
| char *c, **v, **envp;
 | |
| {
 | |
| 	register int i;
 | |
| 	register char *sp, *tp;
 | |
| 	int eacces = 0, asis = 0;
 | |
| 
 | |
| 	sp = any('/', c)? "": path->value;
 | |
| 	asis = *sp == '\0';
 | |
| 	while (asis || *sp != '\0') {
 | |
| 		asis = 0;
 | |
| 		tp = e.linep;
 | |
| 		for (; *sp != '\0'; tp++)
 | |
| 			if ((*tp = *sp++) == ':') {
 | |
| 				asis = *sp == '\0';
 | |
| 				break;
 | |
| 			}
 | |
| 		if (tp != e.linep)
 | |
| 			*tp++ = '/';
 | |
| 		for (i = 0; (*tp++ = c[i++]) != '\0';)
 | |
| 			;
 | |
| 		execve(e.linep, v, envp);
 | |
| 		switch (errno) {
 | |
| 		case ENOEXEC:
 | |
| 			*v = e.linep;
 | |
| 			tp = *--v;
 | |
| 			*v = e.linep;
 | |
| 			execve("/bin/sh", v, envp);
 | |
| 			*v = tp;
 | |
| 			return("no Shell");
 | |
| 
 | |
| 		case ENOMEM:
 | |
| 			return("program too big");
 | |
| 
 | |
| 		case E2BIG:
 | |
| 			return("argument list too long");
 | |
| 
 | |
| 		case EACCES:
 | |
| 			eacces++;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	return(errno==ENOENT ? "not found" : "cannot execute");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Run the command produced by generator `f'
 | |
|  * applied to stream `arg'.
 | |
|  */
 | |
| int
 | |
| run(argp, f)
 | |
| struct ioarg *argp;
 | |
| int (*f)();
 | |
| {
 | |
| 	struct op *otree;
 | |
| 	struct wdblock *swdlist;
 | |
| 	struct wdblock *siolist;
 | |
| 	jmp_buf ev, rt;
 | |
| 	xint *ofail;
 | |
| 	int rv;
 | |
| 
 | |
| 	areanum++;
 | |
| 	swdlist = wdlist;
 | |
| 	siolist = iolist;
 | |
| 	otree = outtree;
 | |
| 	ofail = failpt;
 | |
| 	rv = -1;
 | |
| 	if (newenv(setjmp(errpt = ev)) == 0) {
 | |
| 		wdlist = 0;
 | |
| 		iolist = 0;
 | |
| 		pushio(argp, f);
 | |
| 		e.iobase = e.iop;
 | |
| 		yynerrs = 0;
 | |
| 		if (setjmp(failpt = rt) == 0 && yyparse() == 0)
 | |
| 			rv = execute(outtree, NOPIPE, NOPIPE, 0);
 | |
| 		quitenv();
 | |
| 	}
 | |
| 	wdlist = swdlist;
 | |
| 	iolist = siolist;
 | |
| 	failpt = ofail;
 | |
| 	outtree = otree;
 | |
| 	freearea(areanum--);
 | |
| 	return(rv);
 | |
| }
 | |
| 
 | |
| /* -------- do.c -------- */
 | |
| /* #include "sh.h" */
 | |
| 
 | |
| /*
 | |
|  * built-in commands: doX
 | |
|  */
 | |
| 
 | |
| int
 | |
| dolabel()
 | |
| {
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| int
 | |
| dochdir(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register char *cp, *er;
 | |
| 
 | |
| 	if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL)
 | |
| 		er = ": no home directory";
 | |
| 	else if(chdir(cp) < 0)
 | |
| 		er = ": bad directory";
 | |
| 	else
 | |
| 		return(0);
 | |
| 	prs(cp != NULL? cp: "cd");
 | |
| 	err(er);
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| doshift(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register n;
 | |
| 
 | |
| 	n = t->words[1]? getn(t->words[1]): 1;
 | |
| 	if(dolc < n) {
 | |
| 		err("nothing to shift");
 | |
| 		return(1);
 | |
| 	}
 | |
| 	dolv[n] = dolv[0];
 | |
| 	dolv += n;
 | |
| 	dolc -= n;
 | |
| 	setval(lookup("#"), putn(dolc));
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * execute login and newgrp directly
 | |
|  */
 | |
| int
 | |
| dologin(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	register char *cp;
 | |
| 
 | |
| 	if (talking) {
 | |
| 		signal(SIGINT, SIG_DFL);
 | |
| 		signal(SIGQUIT, SIG_DFL);
 | |
| 	}
 | |
| 	cp = rexecve(t->words[0], t->words, makenv());
 | |
| 	prs(t->words[0]); prs(": "); err(cp);
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| doumask(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register int i, n;
 | |
| 	register char *cp;
 | |
| 
 | |
| 	if ((cp = t->words[1]) == NULL) {
 | |
| 		i = umask(0);
 | |
| 		umask(i);
 | |
| 		for (n=3*4; (n-=3) >= 0;)
 | |
| 			putc('0'+((i>>n)&07));
 | |
| 		putc('\n');
 | |
| 	} else {
 | |
| 		for (n=0; *cp>='0' && *cp<='9'; cp++)
 | |
| 			n = n*8 + (*cp-'0');
 | |
| 		umask(n);
 | |
| 	}
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| int
 | |
| doexec(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register i;
 | |
| 	jmp_buf ex;
 | |
| 	xint *ofail;
 | |
| 
 | |
| 	t->ioact = NULL;
 | |
| 	for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++)
 | |
| 		;
 | |
| 	if (i == 0)
 | |
| 		return(1);
 | |
| 	execflg = 1;
 | |
| 	ofail = failpt;
 | |
| 	if (setjmp(failpt = ex) == 0)
 | |
| 		execute(t, NOPIPE, NOPIPE, FEXEC);
 | |
| 	failpt = ofail;
 | |
| 	execflg = 0;
 | |
| 	return(1);
 | |
| }
 | |
| 
 | |
| int
 | |
| dodot(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	register i;
 | |
| 	register char *sp, *tp;
 | |
| 	char *cp;
 | |
| 
 | |
| 	if ((cp = t->words[1]) == NULL)
 | |
| 		return(0);
 | |
| 	sp = any('/', cp)? ":": path->value;
 | |
| 	while (*sp) {
 | |
| 		tp = e.linep;
 | |
| 		while (*sp && (*tp = *sp++) != ':')
 | |
| 			tp++;
 | |
| 		if (tp != e.linep)
 | |
| 			*tp++ = '/';
 | |
| 		for (i = 0; (*tp++ = cp[i++]) != '\0';)
 | |
| 			;
 | |
| 		if ((i = open(e.linep, 0)) >= 0) {
 | |
| 			exstat = 0;
 | |
| 			next(remap(i));
 | |
| 			return(exstat);
 | |
| 		}
 | |
| 	}
 | |
| 	prs(cp);
 | |
| 	err(": not found");
 | |
| 	return(-1);
 | |
| }
 | |
| 
 | |
| int
 | |
| dowait(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	register i;
 | |
| 	register char *cp;
 | |
| 
 | |
| 	if ((cp = t->words[1]) != NULL) {
 | |
| 		i = getn(cp);
 | |
| 		if (i == 0)
 | |
| 			return(0);
 | |
| 	} else
 | |
| 		i = -1;
 | |
| 	setstatus(waitfor(i, 1));
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| int
 | |
| doread(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	register char *cp, **wp;
 | |
| 	register nb;
 | |
| 	register int  nl = 0;
 | |
| 
 | |
| 	if (t->words[1] == NULL) {
 | |
| 		err("Usage: read name ...");
 | |
| 		return(1);
 | |
| 	}
 | |
| 	for (wp = t->words+1; *wp; wp++) {
 | |
| 		for (cp = e.linep; !nl && cp < elinep-1; cp++)
 | |
| 			if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
 | |
| 			    (nl = (*cp == '\n')) ||
 | |
| 			    (wp[1] && any(*cp, ifs->value)))
 | |
| 				break;
 | |
| 		*cp = 0;
 | |
| 		if (nb <= 0)
 | |
| 			break;
 | |
| 		setval(lookup(*wp), e.linep);
 | |
| 	}
 | |
| 	return(nb <= 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| doeval(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	return(RUN(awordlist, t->words+1, wdchar));
 | |
| }
 | |
| 
 | |
| int
 | |
| dotrap(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register int  n, i;
 | |
| 	register int  resetsig;
 | |
| 
 | |
| 	if (t->words[1] == NULL) {
 | |
| 		for (i=0; i<_NSIG; i++)
 | |
| 			if (trap[i]) {
 | |
| 				prn(i);
 | |
| 				prs(": ");
 | |
| 				prs(trap[i]);
 | |
| 				prs("\n");
 | |
| 			}
 | |
| 		return(0);
 | |
| 	}
 | |
| 	resetsig = digit(*t->words[1]);
 | |
| 	for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) {
 | |
| 		n = getsig(t->words[i]);
 | |
| 		xfree(trap[n]);
 | |
| 		trap[n] = 0;
 | |
| 		if (!resetsig) {
 | |
| 			if (*t->words[1] != '\0') {
 | |
| 				trap[n] = strsave(t->words[1], 0);
 | |
| 				setsig(n, sig);
 | |
| 			} else
 | |
| 				setsig(n, SIG_IGN);
 | |
| 		} else {
 | |
| 			if (talking)
 | |
| 				if (n == SIGINT)
 | |
| 					setsig(n, onintr);
 | |
| 				else
 | |
| 					setsig(n, n == SIGQUIT ? SIG_IGN 
 | |
| 							       : SIG_DFL);
 | |
| 			else
 | |
| 				setsig(n, SIG_DFL);
 | |
| 		}
 | |
| 	}
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| int
 | |
| getsig(s)
 | |
| char *s;
 | |
| {
 | |
| 	register int n;
 | |
| 
 | |
| 	if ((n = getn(s)) < 0 || n >= _NSIG) {
 | |
| 		err("trap: bad signal number");
 | |
| 		n = 0;
 | |
| 	}
 | |
| 	return(n);
 | |
| }
 | |
| 
 | |
| void
 | |
| setsig(n, f)
 | |
| register n;
 | |
| _PROTOTYPE(void (*f), (int));
 | |
| {
 | |
| 	if (n == 0)
 | |
| 		return;
 | |
| 	if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
 | |
| 		ourtrap[n] = 1;
 | |
| 		signal(n, f);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int
 | |
| getn(as)
 | |
| char *as;
 | |
| {
 | |
| 	register char *s;
 | |
| 	register n, m;
 | |
| 
 | |
| 	s = as;
 | |
| 	m = 1;
 | |
| 	if (*s == '-') {
 | |
| 		m = -1;
 | |
| 		s++;
 | |
| 	}
 | |
| 	for (n = 0; digit(*s); s++)
 | |
| 		n = (n*10) + (*s-'0');
 | |
| 	if (*s) {
 | |
| 		prs(as);
 | |
| 		err(": bad number");
 | |
| 	}
 | |
| 	return(n*m);
 | |
| }
 | |
| 
 | |
| int
 | |
| dobreak(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	return(brkcontin(t->words[1], 1));
 | |
| }
 | |
| 
 | |
| int
 | |
| docontinue(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	return(brkcontin(t->words[1], 0));
 | |
| }
 | |
| 
 | |
| static int
 | |
| brkcontin(cp, val)
 | |
| register char *cp;
 | |
| int val;
 | |
| {
 | |
| 	register struct brkcon *bc;
 | |
| 	register nl;
 | |
| 
 | |
| 	nl = cp == NULL? 1: getn(cp);
 | |
| 	if (nl <= 0)
 | |
| 		nl = 999;
 | |
| 	do {
 | |
| 		if ((bc = brklist) == NULL)
 | |
| 			break;
 | |
| 		brklist = bc->nextlev;
 | |
| 	} while (--nl);
 | |
| 	if (nl) {
 | |
| 		err("bad break/continue level");
 | |
| 		return(1);
 | |
| 	}
 | |
| 	isbreak = val;
 | |
| 	longjmp(bc->brkpt, 1);
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| int
 | |
| doexit(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	register char *cp;
 | |
| 
 | |
| 	execflg = 0;
 | |
| 	if ((cp = t->words[1]) != NULL)
 | |
| 		setstatus(getn(cp));
 | |
| 	leave();
 | |
| 	/* NOTREACHED */
 | |
| }
 | |
| 
 | |
| int
 | |
| doexport(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	rdexp(t->words+1, export, EXPORT);
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| int
 | |
| doreadonly(t)
 | |
| struct op *t;
 | |
| {
 | |
| 	rdexp(t->words+1, ronly, RONLY);
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| static void
 | |
| rdexp(wp, f, key)
 | |
| register char **wp;
 | |
| void (*f)();
 | |
| int key;
 | |
| {
 | |
| 	if (*wp != NULL) {
 | |
| 		for (; *wp != NULL; wp++)
 | |
| 			if (checkname(*wp))
 | |
| 				(*f)(lookup(*wp));
 | |
| 			else
 | |
| 				badid(*wp);
 | |
| 	} else
 | |
| 		putvlist(key, 1);
 | |
| }
 | |
| 
 | |
| static void
 | |
| badid(s)
 | |
| register char *s;
 | |
| {
 | |
| 	prs(s);
 | |
| 	err(": bad identifier");
 | |
| }
 | |
| 
 | |
| int
 | |
| doset(t)
 | |
| register struct op *t;
 | |
| {
 | |
| 	register struct var *vp;
 | |
| 	register char *cp;
 | |
| 	register n;
 | |
| 
 | |
| 	if ((cp = t->words[1]) == NULL) {
 | |
| 		for (vp = vlist; vp; vp = vp->next)
 | |
| 			varput(vp->name, 1);
 | |
| 		return(0);
 | |
| 	}
 | |
| 	if (*cp == '-') {
 | |
| 		/* bad: t->words++; */
 | |
| 		for(n = 0; (t->words[n]=t->words[n+1]) != NULL; n++)
 | |
| 			;
 | |
| 		if (*++cp == 0)
 | |
| 			flag['x'] = flag['v'] = 0;
 | |
| 		else
 | |
| 			for (; *cp; cp++)
 | |
| 				switch (*cp) {
 | |
| 				case 'e':
 | |
| 					if (!talking)
 | |
| 						flag['e']++;
 | |
| 					break;
 | |
| 
 | |
| 				default:
 | |
| 					if (*cp>='a' && *cp<='z')
 | |
| 						flag[*cp]++;
 | |
| 					break;
 | |
| 				}
 | |
| 		setdash();
 | |
| 	}
 | |
| 	if (t->words[1]) {
 | |
| 		t->words[0] = dolv[0];
 | |
| 		for (n=1; t->words[n]; n++)
 | |
| 			setarea((char *)t->words[n], 0);
 | |
| 		dolc = n-1;
 | |
| 		dolv = t->words;
 | |
| 		setval(lookup("#"), putn(dolc));
 | |
| 		setarea((char *)(dolv-1), 0);
 | |
| 	}
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| void
 | |
| varput(s, out)
 | |
| register char *s;
 | |
| int out;
 | |
| {
 | |
| 	if (letnum(*s)) {
 | |
| 		write(out, s, strlen(s));
 | |
| 		write(out, "\n", 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| #define	SECS	60L
 | |
| #define	MINS	3600L
 | |
| 
 | |
| int
 | |
| dotimes()
 | |
| {
 | |
| 	struct tms tbuf;
 | |
| 
 | |
| 	times(&tbuf);
 | |
| 
 | |
| 	prn((int)(tbuf.tms_cutime / MINS));
 | |
| 	prs("m");
 | |
| 	prn((int)((tbuf.tms_cutime % MINS) / SECS));
 | |
| 	prs("s ");
 | |
| 	prn((int)(tbuf.tms_cstime / MINS));
 | |
| 	prs("m");
 | |
| 	prn((int)((tbuf.tms_cstime % MINS) / SECS));
 | |
| 	prs("s\n");
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| struct	builtin {
 | |
| 	char	*command;
 | |
| 	int	(*fn)();
 | |
| };
 | |
| static struct	builtin	builtin[] = {
 | |
| 	":",		dolabel,
 | |
| 	"cd",		dochdir,
 | |
| 	"shift",	doshift,
 | |
| 	"exec",		doexec,
 | |
| 	"wait",		dowait,
 | |
| 	"read",		doread,
 | |
| 	"eval",		doeval,
 | |
| 	"trap",		dotrap,
 | |
| 	"break",	dobreak,
 | |
| 	"continue",	docontinue,
 | |
| 	"exit",		doexit,
 | |
| 	"export",	doexport,
 | |
| 	"readonly",	doreadonly,
 | |
| 	"set",		doset,
 | |
| 	".",		dodot,
 | |
| 	"umask",	doumask,
 | |
| 	"login",	dologin,
 | |
| 	"newgrp",	dologin,
 | |
| 	"times",	dotimes,
 | |
| 	0,
 | |
| };
 | |
| 
 | |
| int (*inbuilt(s))()
 | |
| register char *s;
 | |
| {
 | |
| 	register struct builtin *bp;
 | |
| 
 | |
| 	for (bp = builtin; bp->command != NULL; bp++)
 | |
| 		if (strcmp(bp->command, s) == 0)
 | |
| 			return(bp->fn);
 | |
| 	return((int(*)())NULL);
 | |
| }
 | |
| 
 | 
