* 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
* 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
static char sccsid
[] = "@(#)exec.c 8.3 (Berkeley) 5/23/95";
* 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};
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
*));
register Char
*dp
, **pv
, **av
, *sav
;
register struct varent
*pathv
;
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(vis_str(blk
[0]));
stderror(ERR_NAME
| ERR_NOMATCH
);
if (pathv
== 0 && expath
[0] != '/') {
slash
= any(short2str(expath
), '/');
* Glob the argument list, if necessary. Otherwise trim off the quote bits.
setname(vis_str(expath
));
stderror(ERR_NAME
| ERR_NOMATCH
);
t
->t_dcom
= blkspl(pv
, av
);
if (*av
== NULL
|| **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.
sigprocmask(SIG_SETMASK
, &sigset
, NULL
);
* If no path, no words in path, or a / in the filename then restrict the
if (pathv
== 0 || pathv
->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(vis_str(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 */
(void) execve(f
, t
, environ
);
* 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 */
(void) execve(f
, t
, environ
);
/* The sky is falling, the sky is falling! */
stderror(ERR_SYSTEM
, f
, strerror(errno
));
register struct command
*kp
;
int saveIN
, saveOUT
, saveDIAG
, saveSTD
;
sig_t osigint
, osigquit
, osigterm
;
if (chkstop
== 0 && setintr
)
* Hmm, we don't really want to do that now because we might
* fail, but what is the choice
osigint
= signal(SIGINT
, parintr
);
osigquit
= signal(SIGQUIT
, parintr
);
osigterm
= signal(SIGTERM
, parterm
);
saveIN
= dcopy(SHIN
, -1);
saveOUT
= dcopy(SHOUT
, -1);
saveDIAG
= dcopy(SHERR
, -1);
saveSTD
= dcopy(OLDSTD
, -1);
if ((my_reenter
= setexit()) == 0) {
(void) signal(SIGINT
, osigint
);
(void) signal(SIGQUIT
, osigquit
);
(void) signal(SIGTERM
, osigterm
);
SHIN
= dmove(saveIN
, oSHIN
);
SHOUT
= dmove(saveOUT
, oSHOUT
);
SHERR
= dmove(saveDIAG
, oSHERR
);
OLDSTD
= dmove(saveSTD
, oOLDSTD
);
(void) fputc('\n', csherr
);
register struct dirent
*dp
;
struct varent
*pathv
= adrof(STRpath
);
for (cnt
= 0; cnt
< sizeof xhash
; cnt
++)
for (pv
= pathv
->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); */
(void) fprintf(cshout
, "%d hits, %d misses, %d%%\n",
hits
, misses
, 100 * hits
/ (hits
+ misses
));
register struct varent
*v
;
register bool slash
= any(short2str(name
), '/');
register int hashval
= 0, hashval1
, i
;
if (v
== 0 || v
->vec
[0] == 0 || slash
)
sav
= Strspl(STRslash
, name
); /* / command name for postpending */
hashval
= hashname(name
);
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 */
if (executable(NULL
, name
, 0)) {
if (executable(*pv
, sav
, 0)) {
* Andreas Luik <luik@isaak.isa.de>
* I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
* is the executable() routine below and changes to iscommand().
* 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...
executable(dir
, name
, dir_ok
)
Char path
[MAXPATHLEN
+ 1], *dp
, *sp
;
for (dp
= path
, sp
= dir
; *sp
; *dp
++ = *sp
++)
if (dp
== &path
[MAXPATHLEN
+ 1]) {
for (sp
= name
; *sp
; *dp
++ = *sp
++)
if (dp
== &path
[MAXPATHLEN
+ 1]) {
strname
= short2str(path
);
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
))));
* Andreas Luik <luik@isaak.isa.de>
* I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
if ((vp
= adrof1(*v
, &aliases
)) != NULL
) {
(void) fprintf(cshout
, "%s: \t aliased to ", vis_str(*v
));
(void) fputc('\n', cshout
);
register struct biltins
*bptr
;
register struct wordent
*sp
= lex
->next
;
Char
*s0
, *s1
, *s2
, *cmd
;
if (adrof1(sp
->word
, &aliases
)) {
s0
= sp
->word
; /* to get the memory freeing right... */
/* handle quoted alias hack */
if ((*(sp
->word
) & (QUOTE
| TRIM
)) == QUOTE
)
/* do quoting, if it hasn't been done */
for (bptr
= bfunc
; bptr
< &bfunc
[nbfunc
]; bptr
++) {
if (eq(sp
->word
, str2short(bptr
->bname
))) {
(void) fprintf(cshout
, "%s: shell built-in command.\n",
sp
->word
= s0
; /* we save and then restore this */
sp
->word
= cmd
= globone(sp
->word
, G_IGNORE
);
if ((i
= iscommand(strip(sp
->word
))) != 0) {
register struct varent
*v
;
bool slash
= any(short2str(sp
->word
), '/');
if (v
== 0 || v
->vec
[0] == 0 || slash
)
if (pv
[0][0] == 0 || eq(pv
[0], STRdot
)) {
sp
->word
= Strspl(STRdotsl
, sp
->word
);
sp
->word
= s0
; /* we save and then restore this */
s1
= Strspl(*pv
, STRslash
);
sp
->word
= Strspl(s1
, sp
->word
);
(void) fprintf(csherr
, "%s: Command not found.\n", vis_str(sp
->word
));
sp
->word
= s0
; /* we save and then restore this */