diff options
Diffstat (limited to 'bin/csh/exec.c')
| -rw-r--r-- | bin/csh/exec.c | 736 | 
1 files changed, 736 insertions, 0 deletions
| diff --git a/bin/csh/exec.c b/bin/csh/exec.c new file mode 100644 index 000000000000..c9799cbaa34e --- /dev/null +++ b/bin/csh/exec.c @@ -0,0 +1,736 @@ +/*- + * Copyright (c) 1980, 1991, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by the University of + *	California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exec.c	8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/param.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +# include <stdarg.h> +#else +# include <varargs.h> +#endif  + +#include "csh.h" +#include "extern.h" + +/* + * System level search and execute of a command.  We look in each directory + * for the specified command name.  If the name contains a '/' then we + * execute only the full path name.  If there is no search path then we + * execute only full path names. + */ +extern char **environ; + +/* + * As we search for the command we note the first non-trivial error + * message for presentation to the user.  This allows us often + * to show that a file has the wrong mode/no access when the file + * is not in the last component of the search path, so we must + * go on after first detecting the error. + */ +static char *exerr;		/* Execution error message */ +static Char *expath;		/* Path for exerr */ + +/* + * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used + * to hash execs.  If it is allocated (havhash true), then to tell + * whether ``name'' is (possibly) present in the i'th component + * of the variable path, you look at the bit in xhash indexed by + * hash(hashname("name"), i).  This is setup automatically + * after .login is executed, and recomputed whenever ``path'' is + * changed. + * The two part hash function is designed to let texec() call the + * more expensive hashname() only once and the simple hash() several + * times (once for each path component checked). + * Byte size is assumed to be 8. + */ +#define	HSHSIZ		8192	/* 1k bytes */ +#define HSHMASK		(HSHSIZ - 1) +#define HSHMUL		243 +static char xhash[HSHSIZ / 8]; + +#define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK) +#define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */ +#define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */ +static int hits, misses; + +/* Dummy search path for just absolute search when no path */ +static Char *justabs[] = {STRNULL, 0}; + +static void	pexerr __P((void)); +static void	texec __P((Char *, Char **)); +static int	hashname __P((Char *)); +static void 	tellmewhat __P((struct wordent *)); +static int	executable __P((Char *, Char *, bool)); +static int	iscommand __P((Char *)); + + +void +/*ARGSUSED*/ +doexec(v, t) +    Char **v; +    struct command *t; +{ +    register Char *dp, **pv, **av, *sav; +    register struct varent *pathv; +    register bool slash; +    register int hashval = 0, hashval1, i; +    Char   *blk[2]; + +    /* +     * Glob the command name. We will search $path even if this does something, +     * as in sh but not in csh.  One special case: if there is no PATH, then we +     * execute only commands which start with '/'. +     */ +    blk[0] = t->t_dcom[0]; +    blk[1] = 0; +    gflag = 0, tglob(blk); +    if (gflag) { +	pv = globall(blk); +	if (pv == 0) { +	    setname(vis_str(blk[0])); +	    stderror(ERR_NAME | ERR_NOMATCH); +	} +	gargv = 0; +    } +    else +	pv = saveblk(blk); + +    trim(pv); + +    exerr = 0; +    expath = Strsave(pv[0]); +    Vexpath = expath; + +    pathv = adrof(STRpath); +    if (pathv == 0 && expath[0] != '/') { +	blkfree(pv); +	pexerr(); +    } +    slash = any(short2str(expath), '/'); + +    /* +     * Glob the argument list, if necessary. Otherwise trim off the quote bits. +     */ +    gflag = 0; +    av = &t->t_dcom[1]; +    tglob(av); +    if (gflag) { +	av = globall(av); +	if (av == 0) { +	    blkfree(pv); +	    setname(vis_str(expath)); +	    stderror(ERR_NAME | ERR_NOMATCH); +	} +	gargv = 0; +    } +    else +	av = saveblk(av); + +    blkfree(t->t_dcom); +    t->t_dcom = blkspl(pv, av); +    xfree((ptr_t) pv); +    xfree((ptr_t) av); +    av = t->t_dcom; +    trim(av); + +    if (*av == NULL || **av == '\0') +	pexerr(); + +    xechoit(av);		/* Echo command if -x */ +    /* +     * Since all internal file descriptors are set to close on exec, we don't +     * need to close them explicitly here.  Just reorient ourselves for error +     * messages. +     */ +    SHIN = 0; +    SHOUT = 1; +    SHERR = 2; +    OLDSTD = 0; +    /* +     * We must do this AFTER any possible forking (like `foo` in glob) so that +     * this shell can still do subprocesses. +     */ +    (void) sigsetmask((sigset_t) 0); +    /* +     * If no path, no words in path, or a / in the filename then restrict the +     * command search. +     */ +    if (pathv == 0 || pathv->vec[0] == 0 || slash) +	pv = justabs; +    else +	pv = pathv->vec; +    sav = Strspl(STRslash, *av);/* / command name for postpending */ +    Vsav = sav; +    if (havhash) +	hashval = hashname(*av); +    i = 0; +    hits++; +    do { +	/* +	 * Try to save time by looking at the hash table for where this command +	 * could be.  If we are doing delayed hashing, then we put the names in +	 * one at a time, as the user enters them.  This is kinda like Korn +	 * Shell's "tracked aliases". +	 */ +	if (!slash && pv[0][0] == '/' && havhash) { +	    hashval1 = hash(hashval, i); +	    if (!bit(xhash, hashval1)) +		goto cont; +	} +	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */ +	    texec(*av, av); +	else { +	    dp = Strspl(*pv, sav); +	    Vdp = dp; +	    texec(dp, av); +	    Vdp = 0; +	    xfree((ptr_t) dp); +	} +	misses++; +cont: +	pv++; +	i++; +    } while (*pv); +    hits--; +    Vsav = 0; +    xfree((ptr_t) sav); +    pexerr(); +} + +static void +pexerr() +{ +    /* Couldn't find the damn thing */ +    if (expath) { +	setname(vis_str(expath)); +	Vexpath = 0; +	xfree((ptr_t) expath); +	expath = 0; +    } +    else +	setname(""); +    if (exerr) +	stderror(ERR_NAME | ERR_STRING, exerr); +    stderror(ERR_NAME | ERR_COMMAND); +} + +/* + * Execute command f, arg list t. + * Record error message if not found. + * Also do shell scripts here. + */ +static void +texec(sf, st) +    Char   *sf; +    register Char **st; +{ +    register char **t; +    register char *f; +    register struct varent *v; +    register Char **vp; +    Char   *lastsh[2]; +    int     fd; +    unsigned char c; +    Char   *st0, **ost; + +    /* The order for the conversions is significant */ +    t = short2blk(st); +    f = short2str(sf); +    Vt = t; +    errno = 0;			/* don't use a previous error */ +    (void) execve(f, t, environ); +    Vt = 0; +    blkfree((Char **) t); +    switch (errno) { + +    case ENOEXEC: +	/* +	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute +	 * it, don't feed it to the shell if it looks like a binary! +	 */ +	if ((fd = open(f, O_RDONLY)) != -1) { +	    if (read(fd, (char *) &c, 1) == 1) { +		if (!Isprint(c) && (c != '\n' && c != '\t')) { +		    (void) close(fd); +		    /* +		     * We *know* what ENOEXEC means. +		     */ +		    stderror(ERR_ARCH, f, strerror(errno)); +		} +	    } +#ifdef _PATH_BSHELL +	    else +		c = '#'; +#endif +	    (void) close(fd); +	} +	/* +	 * If there is an alias for shell, then put the words of the alias in +	 * front of the argument list replacing the command name. Note no +	 * interpretation of the words at this point. +	 */ +	v = adrof1(STRshell, &aliases); +	if (v == 0) { +	    vp = lastsh; +	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; +	    vp[1] = NULL; +#ifdef _PATH_BSHELL +	    if (fd != -1 && c != '#') +		vp[0] = STR_BSHELL; +#endif +	} +	else +	    vp = v->vec; +	st0 = st[0]; +	st[0] = sf; +	ost = st; +	st = blkspl(vp, st);	/* Splice up the new arglst */ +	ost[0] = st0; +	sf = *st; +	/* The order for the conversions is significant */ +	t = short2blk(st); +	f = short2str(sf); +	xfree((ptr_t) st); +	Vt = t; +	(void) execve(f, t, environ); +	Vt = 0; +	blkfree((Char **) t); +	/* The sky is falling, the sky is falling! */ + +    case ENOMEM: +	stderror(ERR_SYSTEM, f, strerror(errno)); + +    case ENOENT: +	break; + +    default: +	if (exerr == 0) { +	    exerr = strerror(errno); +	    if (expath) +		xfree((ptr_t) expath); +	    expath = Strsave(sf); +	    Vexpath = expath; +	} +    } +} + +/*ARGSUSED*/ +void +execash(t, kp) +    Char  **t; +    register struct command *kp; +{ +    int     saveIN, saveOUT, saveDIAG, saveSTD; +    int     oSHIN; +    int     oSHOUT; +    int     oSHERR; +    int     oOLDSTD; +    jmp_buf osetexit; +    int	    my_reenter; +    int     odidfds; +    sig_t   osigint, osigquit, osigterm; + +    if (chkstop == 0 && setintr) +	panystop(0); +    /* +     * Hmm, we don't really want to do that now because we might +     * fail, but what is the choice +     */ +    rechist(); + +    osigint  = signal(SIGINT, parintr); +    osigquit = signal(SIGQUIT, parintr); +    osigterm = signal(SIGTERM, parterm); + +    odidfds = didfds; +    oSHIN = SHIN; +    oSHOUT = SHOUT; +    oSHERR = SHERR; +    oOLDSTD = OLDSTD; + +    saveIN = dcopy(SHIN, -1); +    saveOUT = dcopy(SHOUT, -1); +    saveDIAG = dcopy(SHERR, -1); +    saveSTD = dcopy(OLDSTD, -1); + +    lshift(kp->t_dcom, 1); + +    getexit(osetexit); + +    if ((my_reenter = setexit()) == 0) { +	SHIN = dcopy(0, -1); +	SHOUT = dcopy(1, -1); +	SHERR = dcopy(2, -1); +	didfds = 0; +	doexec(t, kp); +    } + +    (void) signal(SIGINT, osigint); +    (void) signal(SIGQUIT, osigquit); +    (void) signal(SIGTERM, osigterm); + +    doneinp = 0; +    didfds = odidfds; +    (void) close(SHIN); +    (void) close(SHOUT); +    (void) close(SHERR); +    (void) close(OLDSTD); +    SHIN = dmove(saveIN, oSHIN); +    SHOUT = dmove(saveOUT, oSHOUT); +    SHERR = dmove(saveDIAG, oSHERR); +    OLDSTD = dmove(saveSTD, oOLDSTD); + +    resexit(osetexit); +    if (my_reenter) +	stderror(ERR_SILENT); +} + +void +xechoit(t) +    Char  **t; +{ +    if (adrof(STRecho)) { +	(void) fflush(csherr); +	blkpr(csherr, t); +	(void) fputc('\n', csherr); +    } +} + +void +/*ARGSUSED*/ +dohash(v, t) +    Char **v; +    struct command *t; +{ +    DIR    *dirp; +    register struct dirent *dp; +    register int cnt; +    int     i = 0; +    struct varent *pathv = adrof(STRpath); +    Char  **pv; +    int     hashval; + +    havhash = 1; +    for (cnt = 0; cnt < sizeof xhash; cnt++) +	xhash[cnt] = 0; +    if (pathv == 0) +	return; +    for (pv = pathv->vec; *pv; pv++, i++) { +	if (pv[0][0] != '/') +	    continue; +	dirp = opendir(short2str(*pv)); +	if (dirp == NULL) +	    continue; +	while ((dp = readdir(dirp)) != NULL) { +	    if (dp->d_ino == 0) +		continue; +	    if (dp->d_name[0] == '.' && +		(dp->d_name[1] == '\0' || +		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) +		continue; +	    hashval = hash(hashname(str2short(dp->d_name)), i); +	    bis(xhash, hashval); +	    /* tw_add_comm_name (dp->d_name); */ +	} +	(void) closedir(dirp); +    } +} + +void +/*ARGSUSED*/ +dounhash(v, t) +    Char **v; +    struct command *t; +{ +    havhash = 0; +} + +void +/*ARGSUSED*/ +hashstat(v, t) +    Char **v; +    struct command *t; +{ +    if (hits + misses) +	(void) fprintf(cshout, "%d hits, %d misses, %d%%\n", +		       hits, misses, 100 * hits / (hits + misses)); +} + +/* + * Hash a command name. + */ +static int +hashname(cp) +    register Char *cp; +{ +    register long h = 0; + +    while (*cp) +	h = hash(h, *cp++); +    return ((int) h); +} + +static int +iscommand(name) +    Char   *name; +{ +    register Char **pv; +    register Char *sav; +    register struct varent *v; +    register bool slash = any(short2str(name), '/'); +    register int hashval = 0, hashval1, i; + +    v = adrof(STRpath); +    if (v == 0 || v->vec[0] == 0 || slash) +	pv = justabs; +    else +	pv = v->vec; +    sav = Strspl(STRslash, name);	/* / command name for postpending */ +    if (havhash) +	hashval = hashname(name); +    i = 0; +    do { +	if (!slash && pv[0][0] == '/' && havhash) { +	    hashval1 = hash(hashval, i); +	    if (!bit(xhash, hashval1)) +		goto cont; +	} +	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */ +	    if (executable(NULL, name, 0)) { +		xfree((ptr_t) sav); +		return i + 1; +	    } +	} +	else { +	    if (executable(*pv, sav, 0)) { +		xfree((ptr_t) sav); +		return i + 1; +	    } +	} +cont: +	pv++; +	i++; +    } while (*pv); +    xfree((ptr_t) sav); +    return 0; +} + +/* Also by: + *  Andreas Luik <luik@isaak.isa.de> + *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung + *  Azenberstr. 35 + *  D-7000 Stuttgart 1 + *  West-Germany + * is the executable() routine below and changes to iscommand(). + * Thanks again!! + */ + +/* + * executable() examines the pathname obtained by concatenating dir and name + * (dir may be NULL), and returns 1 either if it is executable by us, or + * if dir_ok is set and the pathname refers to a directory. + * This is a bit kludgy, but in the name of optimization... + */ +static int +executable(dir, name, dir_ok) +    Char   *dir, *name; +    bool    dir_ok; +{ +    struct stat stbuf; +    Char    path[MAXPATHLEN + 1], *dp, *sp; +    char   *strname; + +    if (dir && *dir) { +	for (dp = path, sp = dir; *sp; *dp++ = *sp++) +	    if (dp == &path[MAXPATHLEN + 1]) { +		*--dp = '\0'; +		break; +	    } +	for (sp = name; *sp; *dp++ = *sp++) +	    if (dp == &path[MAXPATHLEN + 1]) { +		*--dp = '\0'; +		break; +	    } +	*dp = '\0'; +	strname = short2str(path); +    } +    else +	strname = short2str(name); +    return (stat(strname, &stbuf) != -1 && +	    ((S_ISREG(stbuf.st_mode) && +    /* save time by not calling access() in the hopeless case */ +	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && +	      access(strname, X_OK) == 0) || +	     (dir_ok && S_ISDIR(stbuf.st_mode)))); +} + +/* The dowhich() is by: + *  Andreas Luik <luik@isaak.isa.de> + *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung + *  Azenberstr. 35 + *  D-7000 Stuttgart 1 + *  West-Germany + * Thanks!! + */ +/*ARGSUSED*/ +void +dowhich(v, c) +    register Char **v; +    struct command *c; +{ +    struct wordent lex[3]; +    struct varent *vp; + +    lex[0].next = &lex[1]; +    lex[1].next = &lex[2]; +    lex[2].next = &lex[0]; + +    lex[0].prev = &lex[2]; +    lex[1].prev = &lex[0]; +    lex[2].prev = &lex[1]; + +    lex[0].word = STRNULL; +    lex[2].word = STRret; + +    while (*++v) { +	if ((vp = adrof1(*v, &aliases)) != NULL) { +	    (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); +	    blkpr(cshout, vp->vec); +	    (void) fputc('\n', cshout); +	} +	else { +	    lex[1].word = *v; +	    tellmewhat(lex); +	} +    } +} + +static void +tellmewhat(lex) +    struct wordent *lex; +{ +    register int i; +    register struct biltins *bptr; +    register struct wordent *sp = lex->next; +    bool    aliased = 0; +    Char   *s0, *s1, *s2; +    Char    qc; + +    if (adrof1(sp->word, &aliases)) { +	alias(lex); +	sp = lex->next; +	aliased = 1; +    } + +    s0 = sp->word;		/* to get the memory freeing right... */ + +    /* handle quoted alias hack */ +    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) +	(sp->word)++; + +    /* do quoting, if it hasn't been done */ +    s1 = s2 = sp->word; +    while (*s2) +	switch (*s2) { +	case '\'': +	case '"': +	    qc = *s2++; +	    while (*s2 && *s2 != qc) +		*s1++ = *s2++ | QUOTE; +	    if (*s2) +		s2++; +	    break; +	case '\\': +	    if (*++s2) +		*s1++ = *s2++ | QUOTE; +	    break; +	default: +	    *s1++ = *s2++; +	} +    *s1 = '\0'; + +    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { +	if (eq(sp->word, str2short(bptr->bname))) { +	    if (aliased) +		prlex(cshout, lex); +	    (void) fprintf(cshout, "%s: shell built-in command.\n",  +			   vis_str(sp->word)); +	    sp->word = s0;	/* we save and then restore this */ +	    return; +	} +    } + +    if ((i = iscommand(strip(sp->word))) != 0) { +	register Char **pv; +	register struct varent *v; +	bool    slash = any(short2str(sp->word), '/'); + +	v = adrof(STRpath); +	if (v == 0 || v->vec[0] == 0 || slash) +	    pv = justabs; +	else +	    pv = v->vec; + +	while (--i) +	    pv++; +	if (pv[0][0] == 0 || eq(pv[0], STRdot)) { +	    sp->word = Strspl(STRdotsl, sp->word); +	    prlex(cshout, lex); +	    xfree((ptr_t) sp->word); +	    sp->word = s0;	/* we save and then restore this */ +	    return; +	} +	s1 = Strspl(*pv, STRslash); +	sp->word = Strspl(s1, sp->word); +	xfree((ptr_t) s1); +	prlex(cshout, lex); +	xfree((ptr_t) sp->word); +    } +    else { +	if (aliased) +	    prlex(cshout, lex); +	(void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word)); +    } +    sp->word = s0;		/* we save and then restore this */ +} | 
