File:  [MW Coherent from dump] / coherent / b / bin / sh_B4_420 / exec3.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:36 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * sh/exec3.c
 * Bourne shell.
 * Builtin commands and shell functions.
 */

#include "sh.h"
#include <sys/times.h>
#include <sys/const.h>		/* HZ defined here */

#define HOUR	(60L*60L*HZ)
#define MINUTE	(60L*HZ)
#define SECOND	HZ

NODE	*copy_node();
char	*cd();

extern	s_colon();
extern	s_dot();
extern	s_break();
#define s_continue	s_break
extern	s_cd();
extern	s_dirs();
extern	s_eval();
extern	s_exec();
extern	s_exit();
extern	s_export();
extern	s_login();
#define s_newgrp	s_login
extern	s_popd();
extern	s_pushd();
extern	s_read();
#define s_readonly	s_export
extern	s_set();
extern	s_shift();
extern	s_times();
extern	s_trap();
extern	s_umask();
extern	s_wait();
#define SNULL	((int (*)())0)

/* Built-in functions. */
typedef struct {
	int	i_hash;
	char	*i_name;
	int	(*i_func)();
} INLINE;

INLINE inls[] = {
	0,	":",		s_colon,
	0,	".",		s_dot,
	0,	"break",	s_break,
	0,	"continue",	s_continue,
	0,	"cd",		s_cd,
	0,	"dirs",		s_dirs,
	0,	"eval",		s_eval,
	0,	"exec",		s_exec,
	0,	"exit",		s_exit,
	0,	"export",	s_export,
	0,	"login",	s_login,
	0,	"newgrp",	s_newgrp,
	0,	"popd",		s_popd,
	0,	"pushd",	s_pushd,
	0,	"read",		s_read,
	0,	"readonly",	s_readonly,
	0,	"set",		s_set,
	0,	"shift",	s_shift,
	0,	"times",	s_times,
	0,	"trap",		s_trap,
	0,	"umask",	s_umask,
	0,	"wait",		s_wait,
	0,	NULL,		SNULL
};

inline()
{
	register int (*s_func)();
	register INLINE *ip;
	register int ahash;

	if (inls[0].i_hash==0)
		for (ip=inls; ip->i_name!=NULL; ip++)
			ip->i_hash = ihash(ip->i_name);
	if (nargv[0] == NULL)
		return 0;
	ahash = ihash(nargv[0]);
	for (ip=inls; ip->i_name!=NULL; ip++)
		if (ip->i_hash==ahash && strcmp(nargv[0], ip->i_name)==0)
			break;
	if ((s_func=ip->i_func) == SNULL)
		return 0;
	/*
	 * Process exec specially, because it has unique semantics for
	 * redirection (among other things). Redirection of builtins is done
	 * using the "undo" facility because builtins must execute in the
	 * top-level environment. The original code here permitted a small
	 * subset of builtins to be redirected, but executed them in a
	 * subshell, giving bogus semantics (eg, it permitted "export" to be
	 * redirected, but in a subshell it had no effect).
	 */
	if (s_func == s_exec)
		slret = (* s_func) ();
	else {
		REDIR_UNDO    *	undo = NULL;

		if (redirect (niovp, & undo) < 0)
			slret = 1;
		else 
			slret = (* s_func) ();

		/*
		 * NB: The rationale for the following kludge is not clear,
		 * but with the ability to properly redirect builtins and
		 * execute the in the top-level context, it no longer works.
		 * It could be added back in, but since it conflicts with
		 * POSIX.2 anyway, why bother.
		 */
#if 0
		if (s_func == s_eval)
			slret = (*s_func)();
		else {
			/* Kludge stderr output to stdout. */
			dup2(1, 2);
			close(1);
			slret = (*s_func)();
		}
#endif

		redirundo (& undo);
	}
	return 1;
}

ihash(cp)
register char *cp;
{
	register int i;
	for (i=0; *cp; i+=*cp++);
	return i;
}


/*
 * Actual builtin functions.
 */
s_colon()
{
	return 0;
}

s_dot()
{
	if (nargc==2) {
		ffind(NULL);
		if (ffind(vpath, nargv[1], 4))
			return session(SFILE, duplstr(strt, 0));
		else {
			ecantfind(nargv[1]);
			return 1;
		}
	} else if (nargc==1)
		return 0;
	syntax();
	return 1;
}

s_break()
{
	register CON *cp;
	register int t;
	register int n;
	int ret;

	ret = nargv[0][0]=='b' ? 2 : 1;
	n = nargc>1 ? atoi(nargv[1]) : 1;
	for (cp = sesp->s_con; cp; cp = cp->c_next) {
		t = cp->c_node->n_type;
		if ((t==NWHILE || t==NFOR || t==NUNTIL) && --n == 0) {
			sesp->s_con = cp;
			longjmp(cp->c_envl, ret);
			break;
		}
		freebuf(cp->c_bpp);
	}
	printe("%s out of bounds", ret==1 ? "Continue" : "Break");
}

/* s_continue is overlaid with s_break */

s_cd()
{
	register char *dir;

	if ((dir = cd((nargc<2) ? vhome : nargv[1])) == NULL)
		return -1;			/* cd failed */
	if (dstack[dstkp] != NULL)
		sfree(dstack[dstkp]);
	dstack[dstkp] = duplstr(dir, 1);	/* update dir stack */
	return 0;
}

s_dirs()
{
	register int i;

	for (i = dstkp; i >= 0; i--)
		printf (stdout, "%s ", dstack[i]);
	fputc('\n', stderr);
}

s_eval()
{
	if (nargc>1)
		return session(SARGV, ++nargv);
	else
		return 0;
}

s_exec()
{
	if (redirect (niovp, NULL) < 0) {
		if (nargc>1) {
			exit(1);
			NOTREACHED;
		}
		return 1;
	}
	if (nargc==1)
		return 0;
	if (no1flag) {
		cleanup_shell_fns ();
		unlink_temp (capture_temp ());
	}
	dflttrp(ICMD);
	++nargv;
	--nargc;
	nenvp = envlvar(nenvp);
	flexec();
	exit(1);
	NOTREACHED;
}

s_exit()
{
	if (nargc > 1)
		slret = atoi(nargv[1]);
	reset(RUEXITS);
	NOTREACHED;
}

s_export()
{
	register int flag;
	register char **varv;

	flag = nargv[0][0]=='e' ? VEXP : VRDO;
	if (nargc < 2)
		tellvar(flag);
	else
		for (varv=++nargv; *varv; )
			if (namevar(*varv))
				flagvar(*varv++, flag);
			else
				eillvar(*varv++);
	return 0;
}

s_login()
{
	register char *cmd;

	cmd = nargv[0][0]=='l' ? "/bin/login" : "/bin/newgrp";
	execve(cmd, nargv, envlvar(nenvp));
	ecantfind(cmd);
	return 1;
}

/* s_newgrp is overlaid with s_login */

s_popd()
{
	register int i, j, n, ret;

	if (nargc == 1)
		return popd();
	/*
	 * Kludge to pop one or more specific dir stack elements.
	 * Do args backwards so e.g. "popd 2 3 4" works as expected.
	 * Internal indices [0, dstkp] are user indices [dstkp, 0].
	 */
	for (ret = 0, i = nargc-1; i > 0; i--) {
		if ((n = atoi(nargv[i])) == 0)
			ret |= popd();
		else if (n < 0 || n > dstkp) {
			printe("Illegal arg: %d", n);
			ret = -1;
			continue;
		} else {
			j = dstkp - n;
			if (dstack[j] != NULL)
				sfree(dstack[j]);
			for ( ; j < dstkp; j++)
				dstack[j] = dstack[j+1];
			--dstkp;
		}
	}
	return ret;
}

s_pushd()
{
	register char *dir;
	register int i, ret;

	if (nargc == 1) {
		/* Exchange top two stack elements. */
		if (dstkp == 0)
			return 1;		/* only one element on stack */
		dir = dstack[dstkp-1];
		dstack[dstkp-1] = dstack[dstkp];
		dstack[dstkp] = dir;		/* exchange top two */
		return ((cd(dir) == NULL) ? -1 : 0);	/* and cd accordingly */
	}
	/* Push one or more directories to stack. */
	for (ret = 0, i = 1; i < nargc; i++)
		ret |= pushd(nargv[i]);
	return ret;
}

/*
 * NB: For s_read (), wrap up the temporary-space global bullshit. Later we
 * can add parameters to this stuff to get the effect of string streams.
 */

#define	temp_string_begin()	((void) (strp = strt))
#define	temp_string_max()	STRSIZE
#define	temp_string_add(c) \
		(strp < strt + temp_string_max () ? (void) (* strp ++ = c) : \
		 (void) etoolong ())
#define	temp_string_end()	(strt)
#define	temp_string_end_copy()	(duplstr (temp_string_end (), 1))
#define	temp_string_temp_copy()	(duplstr (temp_string_end (), 0))
#define	temp_mark()		(0)
#define	temp_release(x)		((void) x)

typedef	int		temp_mark_type;

#define	ARGS(x)		x
void temp_string_begin ARGS (()) {
	temp_string_begin ();
}
int temp_string_max ARGS (()) {
	return temp_string_max ();
}
void temp_string_add ARGS ((c)) int c; {
	temp_string_add (c);
}
char * temp_string_end ARGS (()) {
	return temp_string_end ();
}
char * temp_string_end_copy ARGS (()) {
	return temp_string_end_copy ();
}
char * temp_string_temp_copy ARGS (()) {
	return temp_string_temp_copy ();
}
temp_mark_type temp_mark ARGS (()) {
	return temp_mark ();
}
void temp_release ARGS ((m)) temp_mark_type m; {
	temp_release (m);
}

/*
 * NB: Originally, this code used yylex () to break the input up into words.
 * This was a bad idea, because this caused the IFS variable to have no
 * useful effect. Now we just read using <stdio.h> functions, because the
 * extra machinery for session-management doesn't seem to buy anything.
 */

s_read()
{
	char	     **	argp = nargv + 1;
	char	     **	arg_end = argp + (nargc - 1);
	char	      *	delimiters;
	int		ch;

	while (argp < arg_end)
		if (! namevar (* argp ++))
			eillvar (argp - 1);

	delimiters = vifs == NULL ? " \t" : vifs;

	argp = nargv + 1;
	while (argp < arg_end)  {
		temp_mark_type	mark;
		enum	{
			EAT_WS,
			WORD
		} state = EAT_WS;

		mark = temp_mark ();
		temp_string_begin ();

		if (argp + 1 == arg_end)
			delimiters = "";

		while ((ch = getc (stdin)) != EOF && ch != '\n') {

			if (strchr (delimiters, ch) != NULL) {

				if (state == EAT_WS)
					continue;
				break;
			}

			temp_string_add (ch);
			state = WORD;
		}
		temp_string_add (0);

		assnvar (* argp ++, temp_string_temp_copy ());
		temp_release (mark);

		if (ch == EOF || ch == '\n')
			break;
	}

	while (argp < arg_end)
		assnvar (* argp ++, "");

	return ch == EOF;
}


/* s_readonly overlaid with s_export */

s_set()
{
	if (nargc < 2) {
		tellvar(0);
		return 0;
	}
	return set(nargc, nargv, 0);
}

s_shift()
{
	register int n;

	n = nargc > 1 ? atoi(nargv[1]) : 1;
	while (n > 0 && sargc > 0) {
		n -= 1;
		sargc -= 1;
		sargp += 1;
	}
	return n!=0;
}

s_times()
{
#if	_I386
#define	tb_cutime	tms_cutime
#define	tb_cstime	tms_cstime
	struct	tms	tb;
#else
	struct tbuffer tb;

#endif

	times(&tb);
	ptime(tb.tb_cutime);
	ptime(tb.tb_cstime);
	puts ("\n");
	return 0;
}

s_trap()
{
	register char **vp;
	register char *cp;
	register int err;

	err = 0;
	if (nargc==1)
		return telltrp();
	vp = ++nargv;
	cp = *vp;
	if (class(cp[0], MDIGI)
	 && (cp[1]=='\0' || (class(cp[1], MDIGI) && cp[2]=='\0')))
		cp = NULL;
	else
		++vp;
	while (*vp) {
		if (class(vp[0][0], MDIGI))
			err |= setstrp(atoi(*vp++), cp);
		else {
			printe("Bad trap: %s", *vp++);
			err |= 1;
		}
	}
	return err;
}

s_umask()
{
	if (nargc < 2)
		printf ("%03o\n", ufmask);
	else
		umask(ufmask = atoi(nargv[1]));
	return 0;
}

s_wait()
{
	register int f;

	f = (nargc > 1) ? atoi(nargv[1]) : 0;
	if (f > 0)
		f = -f;
	waitc(f);
	return slret;
}

/*
 * Change to given directory.
 * Update global variable CWD accordingly.
 * Return NULL if bad, otherwise full pathname of the directory.
 */
char *
cd(dir) register char *dir;
{
	if (chdir(dir) < 0) {
		printe("%s: bad directory", dir);
		return NULL;
	}
	if (*dir != '/') {
		/*
		 * Find an absolute pathname for the dstack and $CWD.
		 * The directory now in dstack[dstkp] is "." if _getwd() failed
		 * for any reason (e.g., the user lacks search permission
		 * down the path to "/", or "." was rm'ed by another process).
		 * Avoid _getwd() in this case, it can undo the chdir() above.
		 */
		if ((strcmp(dstack[dstkp], ".") == 0)
		 || ((dir = _getwd()) == NULL))
			return NULL;
	}
	assnvar("CWD", dir);
	return dir;
}

/*
 * Pop the directory stack and change to the previous stacked directory.
 */
popd()
{
	if (dstkp == 0) {
		printe("Directory stack underflow");
		return -1;
	}
	if (dstack[dstkp] != NULL)
		sfree(dstack[dstkp]);
	return (cd(dstack[--dstkp]) == NULL ? -1 : 0);
}

/*
 * Change to given directory and add it to the directory stack.
 */
pushd(dir) register char *dir;
{
	if ((dir = cd(dir)) == NULL)
		return -1;			/* cd failed */
	if (++dstkp >= DSTACKN) {
		--dstkp;
		printe("Directory stack overflow");
		return -1;
	}
	dstack[dstkp] = duplstr(dir, 1);
	return 0;
}

/*
 * The set command.  This is also called from `main' to set
 * options from the command line.  In this case `flag' is
 * set.
 */
set(argc, argv, flag)
register char *argv[];
{
	int n;
	register char *cp;

	if (flag) {
		cflag = 0;
		iflag = 0;
		sflag = 0;
	}
	n = 0;
	if (argc > 0)
		n = 1;
	if (argc>1 && argv[1][0]=='-') {
		register char *fp;

		n++;
		for (cp = &argv[1][1]; *cp; cp++) {
			if ((fp=index(shfnams, *cp)) == NULL
			 || (fp+=shflags-shfnams) == NULL
			 || (fp != &lgnflag && fp > &xflag && flag == 0))
				printe("-%c: Bad option", *cp);
			else if (fp != &lgnflag)
				*fp = *cp;
		}
		if (cp == &argv[1][1]) {
			vflag = 0;
			xflag = 0;
		}
		if (flag == 0 && argc == 2)
			return errflag;
	}
	if (errflag)
		return 1;
	if (sargv != NULL)
		vfree(sargv);
	sargv = vdupl(argv);
	sargc = argc - n;
	sargp = sargv + n;
	return 0;
}

/*
 * print the time as XmX.Xs
 * as fixed by Randall.
 */
ptime(t)
long t;
{
	register int ticks, tenths, seconds;

	printf ("%ldm", t/MINUTE);
	ticks = t%MINUTE;
	seconds = ticks/SECOND;
	tenths = (ticks%SECOND + SECOND/20)/(SECOND/10);
	if (tenths == 10) {
		tenths = 0;
		seconds++;
	}
	printf ("%d.%ds ", seconds, tenths);
}

/* User-defined shell functions. */

/*
 * Lookup a shell function name.
 * The hashing is probably irrelevant.
 */
SHFUNC *
lookup_sh_fn(name) char *name;
{
	register SHFUNC *fnp;
	register int ahash;

	ahash = ihash(name);
	for (fnp=sh_fnp; fnp != NULL; fnp = fnp->fn_link)
		if (fnp->fn_hash==ahash && strcmp(name, fnp->fn_name)==0)
			return fnp;
	return NULL;
}


/*
 * When a subshell is created, the functions are available in the subshell
 * but we don't want to lose the here-documents.
 */

void subshell_shell_fns ()
{
	SHFUNC	      *	scan;

	scan = sh_fnp;

	while (scan != NULL) {
		forget_temp (scan->fn_temp);
		scan->fn_temp = NULL;
		scan = scan->fn_link;
	}
}


/*
 * Unset a shell function, freeing the function body and detaching any
 * temporary files.
 */

void cleanup_shell_fn (fnp)
SHFUNC	      *	fnp;
{
	free_node (fnp->fn_body);
	unlink_temp (fnp->fn_temp);
}


/*
 * Before final exit, clean up all shell functions. Freeing the memory of
 * the function bodies is a waste of time here, so we leave that alone.
 */

void cleanup_shell_fns ()
{
	SHFUNC	      *	scan;
	SHFUNC	      *	next;

	for (scan = sh_fnp ; scan != NULL ; scan = next) {
		next = scan->fn_link;
		unlink_temp (scan->fn_temp);
#if 0
		cleanup_shell_fn (scan);
		sfree (scan->fn_name);
		sfree (scan);
#endif
	}

	sh_fnp = NULL;
}


/*
 * Define a shell function.
 */
def_shell_fn(np) register NODE *np;
{
	register char *name;
	register SHFUNC *fnp;

	name = np->n_strp;
	if ((fnp = lookup_sh_fn(name)) != NULL)
		cleanup_shell_fn (fnp);	/* redeclared, free old body */
	else {
		fnp = salloc(sizeof *fnp);	/* allocate new function */
		fnp->fn_link = sh_fnp;		/* add it to list */
		sh_fnp = fnp;
		fnp->fn_hash = ihash(name);	/* and set member info */ 
		fnp->fn_name = duplstr(name, 1);
	}
	fnp->fn_temp = capture_temp ();
	fnp->fn_body = copy_node(np->n_next);	/* and copy function body */
}

/*
 * Look for a shell function, execute it if found.
 */
int
sh_fn()
{
	register SHFUNC *fnp;
	CON *ocon;
	int oargc;
	char *oarg0;
	char **oargv, **oargp;

	if (nargv[0] == NULL)
		return 0;
	if ((fnp = lookup_sh_fn(nargv[0])) == NULL)
		return 0;

	/* Set up sargc, sarg0, sargv, sargp here for $1 etc. to work. */
	oarg0 = sarg0;
	oargc = sargc;
	oargv = sargv;
	oargp = sargp;
	sarg0 = nargv[0];
	sargc = nargc - 1;
	sargp = sargv = vdupl(nargv+1);
	ocon = sesp->s_con;
	sesp->s_con = NULL;

	++in_sh_fn;
	slret = command(fnp->fn_body);		/* execute it */
	--in_sh_fn;
	ret_done = 0;
	vfree(sargv);

	sesp->s_con = ocon;
	sarg0 = oarg0;
	sargc = oargc;
	sargv = oargv;
	sargp = oargp;
	return 1;
}

/*
 * Recursively allocate a fresh copy of a NODE.
 * Examines type to decide if node uses strp or auxp.
 * Watch out for nodes which contain loops: NFOR2, NWHILE, NUNTIL.
 */
NODE *
copy_node(np) NODE *np;
{
	register NODE *newnp;
	int flag;

#if	0
	printf("copy_node(%x)", np);
	if (np != NULL)
		printf(" type=%d", np->n_type);
	printf("\n");
#endif
	if (np == NULL)
		return NULL;
	flag = 0;
	newnp = salloc(sizeof *np);			/* allocate new NODE */
	newnp->n_type = np->n_type;
	switch(np->n_type) {

	/* The following cases use the strp member. */
	case NRET:
	case NFOR:
	case NARGS:
	case NASSG:
	case NCASE:
	case NCASE3:
	case NIORS:
	case NFUNC:
		newnp->n_strp = duplstr(np->n_strp, 1);
		break;

	/* The following cases use the auxp member. */
	case NWHILE:
	case NUNTIL:
	case NFOR2:
		flag++;
		np->n_next->n_next = NULL;	/* zap the loop for recursion */
		/* fall through... */
	case NNULL:
	case NCOMS:
	case NCTRL:
	case NBRAC:
	case NPARN:
	case NIF:
	case NELSE:
	case NCASE2:
	case NLIST:
	case NANDF:
	case NORF:
	case NBACK:
	case NPIPE:
		newnp->n_auxp = copy_node(np->n_auxp);
		break;

	case NRPIPE:
	case NWPIPE:
	default:
		/* ??? */
		printf("type=%d\n", np->n_type);
		panic(9);
	}
	newnp->n_next = copy_node(np->n_next);
	if (flag)
		newnp->n_next->n_next = newnp;		/* restore loop */
	return newnp;
}

/*
 * Undo the above.
 */
free_node(np) register NODE *np;
{
	if (np == NULL)
		return;
	switch(np->n_type) {

	/* strp */
	case NRET:
	case NFOR:
	case NARGS:
	case NASSG:
	case NCASE:
	case NCASE3:
	case NIORS:
	case NFUNC:
		sfree(np->n_strp);
		break;

	/* auxp */
	case NWHILE:
	case NUNTIL:
	case NFOR2:
		np->n_next->n_next = NULL;	/* zap the loop for recursion */
		/* fall through... */
	case NNULL:
	case NCOMS:
	case NCTRL:
	case NBRAC:
	case NPARN:
	case NIF:
	case NELSE:
	case NCASE2:
	case NLIST:
	case NANDF:
	case NORF:
	case NBACK:
	case NPIPE:
		free_node(np->n_auxp);
		break;

	case NRPIPE:
	case NWPIPE:
	default:
		/* ??? */
		printf("type=%d\n", np->n_type);
		panic(10);
	}
	free_node(np->n_next);
	sfree(np);
}

/* end of sh/exec3.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.