File:  [MW Coherent from dump] / coherent / b / bin / sh.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 <time.h>

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

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_ulimit();
extern	s_umask();
extern	s_unset();
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,	"ulimit",	s_ulimit,
	0,	"umask",	s_umask,
	0,	"unset",	s_unset,
	0,	"wait",		s_wait,
	0,	NULL,		SNULL
};

int shell_builtin ()
{
	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_exec ();
	else {
		REDIR_UNDO    *	undo = NULL;

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

		redirundo (& undo);
	}
	return 1;
}

ihash(cp)
register char *cp;
{
	register int i;
	for (i = 0 ; *cp ; i += * cp ++)
		/* DO NOTHING */ ;
	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;
		}
		DESTROY_CON (cp);
	}
	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 ("%s ", dstack[i]);
	fputc('\n', stdout);
}

s_eval()
{
	return nargc > 1 ? session (SARGV, ++ nargv) : 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 {
		varv = ++ nargv;
		while (* 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;
}

#include <ulimit.h>

s_ulimit ()
{
	long		newlimit;

	if (nargc < 2)
		printf ("%ld\n", ulimit (UL_GETFSIZE));
	else if ((newlimit = atol (nargv [1])) > 0)
		ulimit (UL_SETFSIZE, newlimit);
	return 0;
}

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);
}


/*
 * Delete a shell function. Returns -1 on failure.
 */

int unset_shell_fn (name) char * name; {
	register SHFUNC * fnp, * prev;

	prev = NULL;
	for (fnp = sh_fnp ; fnp != NULL; fnp = (prev = fnp)->fn_link)
		if (strcmp (name, fnp->fn_name) == 0) {
			if (prev == NULL)
				sh_fnp = fnp->fn_link;
			else
				prev->fn_link = fnp->fn_link;
			cleanup_shell_fn (fnp);
			sfree (fnp);
			return 0;
		}
	return -1;
}


/*
 * 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, temps)
NODE	      *	np;
TEMP_FILE     *	temps;
{
	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 = (SHFUNC *) 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 = temps;
	fnp->fn_body = copy_node(np->n_next);	/* and copy function body */
}

/*
 * Look for a shell function, execute it if found. Let's not forget that
 * shell functions have to be redirected in a similar manner to builtins.
 */

int shell_function()
{
	register SHFUNC *fnp;
	CON *ocon;
	int oargc;
	char *oarg0;
	char **oargv, **oargp;
	REDIR_UNDO    *	undo = NULL;

	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;

	if (redirect (niovp, & undo) < 0)
		slret = 1;
	else  {
		++in_sh_fn;
		slret = command(fnp->fn_body);		/* execute it */
		--in_sh_fn;

		redirundo (& undo);
	}

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

extern int unsetvar ();

int s_unset ()
{
	int		i;

	if (nargc > 1 && strcmp (nargv [1], "-f") == 0) {
		/*
		 * Unsetting functions.
		 */

		for (i = 2 ; i < nargc ; i ++)
			(void) unset_shell_fn (nargv [i]);
	} else
		for (i = 1 ; i < nargc ; i ++)
			(void) unsetvar (nargv [i]);

	return 0;
}

/* 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.