|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.