* Copyright (c) 1980, 1991 The Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)exec.c 5.13 (Berkeley) %G%";
* 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.
* 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
* 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)
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 */
/* Dummy search path for just absolute search when no path */
static Char
*justabs
[] = {STRNULL
, 0};
register struct command
*t
;
register Char
*dp
, **pv
, **av
, *sav
;
register struct varent
*v
;
register int hashval
= 0, hashval1
, i
;
* 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 '/'.
setname(short2str(blk
[0]));
stderror(ERR_NAME
| ERR_NOMATCH
);
if (v
== 0 && expath
[0] != '/') {
slash
= any(short2str(expath
), '/');
* Glob the argument list, if necessary. Otherwise trim off the quote bits.
setname(short2str(expath
));
stderror(ERR_NAME
| ERR_NOMATCH
);
t
->t_dcom
= blkspl(pv
, av
);
if (*av
== (Char
*) 0 || **av
== '\0')
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
* We must do this AFTER any possible forking (like `foo` in glob) so that
* this shell can still do subprocesses.
(void) sigsetmask((sigmask_t
) 0);
* If no path, no words in path, or a / in the filename then restrict the
if (v
== 0 || v
->vec
[0] == 0 || slash
)
sav
= Strspl(STRslash
, *av
);/* / command name for postpending */
* 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
))
if (pv
[0][0] == 0 || eq(pv
[0], STRdot
)) /* don't make ./xxx */
/* Couldn't find the damn thing */
setname(short2str(expath
));
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.
register struct varent
*v
;
/* The order for the conversions is significant */
errno
= 0; /* don't use a previous error */
* 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')) {
* We *know* what ENOEXEC means.
stderror(ERR_ARCH
, f
, strerror(errno
));
* 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
);
vp
[0] = adrof(STRshell
) ? value(STRshell
) : STR_SHELLPATH
;
if (fd
!= -1 && c
!= '#')
st
= blkspl(vp
, st
); /* Splice up the new arglst */
/* The order for the conversions is significant */
/* The sky is falling, the sky is falling! */
stderror(ERR_SYSTEM
, f
, strerror(errno
));
register struct command
*kp
;
if (chkstop
== 0 && setintr
)
(void) signal(SIGINT
, parintr
);
(void) signal(SIGQUIT
, parintr
);
(void) signal(SIGTERM
, parterm
); /* if doexec loses, screw */
blkpr(t
), xputchar('\n');
register struct dirent
*dp
;
struct varent
*v
= adrof(STRpath
);
for (cnt
= 0; cnt
< sizeof xhash
; cnt
++)
for (pv
= v
->vec
; *pv
; pv
++, i
++) {
dirp
= opendir(short2str(*pv
));
while ((dp
= readdir(dirp
)) != NULL
) {
if (dp
->d_name
[0] == '.' &&
(dp
->d_name
[1] == '\0' ||
dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0'))
hashval
= hash(hashname(str2short(dp
->d_name
)), i
);
/* tw_add_comm_name (dp->d_name); */
xprintf("%d hits, %d misses, %d%%\n",
hits
, misses
, 100 * hits
/ (hits
+ misses
));