1231990Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.exec.c,v 3.79 2011/02/25 23:58:34 christos Exp $ */
259243Sobrien/*
359243Sobrien * sh.exec.c: Search, find, and execute a command!
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35231990SmpRCSID("$tcsh: sh.exec.c,v 3.79 2011/02/25 23:58:34 christos Exp $")
3659243Sobrien
3759243Sobrien#include "tc.h"
3859243Sobrien#include "tw.h"
3969408Sache#ifdef WINNT_NATIVE
4059243Sobrien#include <nt.const.h>
4169408Sache#endif /*WINNT_NATIVE*/
4259243Sobrien
4359243Sobrien/*
4459243Sobrien * C shell
4559243Sobrien */
4659243Sobrien
4759243Sobrien#ifndef OLDHASH
4859243Sobrien# define FASTHASH	/* Fast hashing is the default */
4959243Sobrien#endif /* OLDHASH */
5059243Sobrien
5159243Sobrien/*
5259243Sobrien * System level search and execute of a command.
5359243Sobrien * We look in each directory for the specified command name.
5459243Sobrien * If the name contains a '/' then we execute only the full path name.
5559243Sobrien * If there is no search path then we execute only full path names.
5659243Sobrien */
5759243Sobrien
5859243Sobrien/*
5959243Sobrien * As we search for the command we note the first non-trivial error
6059243Sobrien * message for presentation to the user.  This allows us often
6159243Sobrien * to show that a file has the wrong mode/no access when the file
6259243Sobrien * is not in the last component of the search path, so we must
6359243Sobrien * go on after first detecting the error.
6459243Sobrien */
6559243Sobrienstatic char *exerr;		/* Execution error message */
6659243Sobrienstatic Char *expath;		/* Path for exerr */
6759243Sobrien
6859243Sobrien/*
6959243Sobrien * The two part hash function is designed to let texec() call the
7059243Sobrien * more expensive hashname() only once and the simple hash() several
7159243Sobrien * times (once for each path component checked).
7259243Sobrien * Byte size is assumed to be 8.
7359243Sobrien */
7459243Sobrien#define BITS_PER_BYTE	8
7559243Sobrien
7659243Sobrien#ifdef FASTHASH
7759243Sobrien/*
7859243Sobrien * xhash is an array of hash buckets which are used to hash execs.  If
7959243Sobrien * it is allocated (havhash true), then to tell if ``name'' is
80231990Smp * (possibly) present in the i'th component of the variable path, look
8159243Sobrien * at the [hashname(name)] bucket of size [hashwidth] bytes, in the [i
8259243Sobrien * mod size*8]'th bit.  The cache size is defaults to a length of 1024
8359243Sobrien * buckets, each 1 byte wide.  This implementation guarantees that
8459243Sobrien * objects n bytes wide will be aligned on n byte boundaries.
8559243Sobrien */
8659243Sobrien# define HSHMUL		241
8759243Sobrien
8859243Sobrienstatic unsigned long *xhash = NULL;
8959243Sobrienstatic unsigned int hashlength = 0, uhashlength = 0;
9059243Sobrienstatic unsigned int hashwidth = 0, uhashwidth = 0;
9159243Sobrienstatic int hashdebug = 0;
9259243Sobrien
9359243Sobrien# define hash(a, b)	(((a) * HSHMUL + (b)) % (hashlength))
9459243Sobrien# define widthof(t)	(sizeof(t) * BITS_PER_BYTE)
9559243Sobrien# define tbit(f, i, t)	(((t *) xhash)[(f)] &  \
96145479Smp			 (1UL << (i & (widthof(t) - 1))))
9759243Sobrien# define tbis(f, i, t)	(((t *) xhash)[(f)] |= \
98145479Smp			 (1UL << (i & (widthof(t) - 1))))
9959243Sobrien# define cbit(f, i)	tbit(f, i, unsigned char)
10059243Sobrien# define cbis(f, i)	tbis(f, i, unsigned char)
10159243Sobrien# define sbit(f, i)	tbit(f, i, unsigned short)
10259243Sobrien# define sbis(f, i)	tbis(f, i, unsigned short)
10359243Sobrien# define ibit(f, i)	tbit(f, i, unsigned int)
10459243Sobrien# define ibis(f, i)	tbis(f, i, unsigned int)
10559243Sobrien# define lbit(f, i)	tbit(f, i, unsigned long)
10659243Sobrien# define lbis(f, i)	tbis(f, i, unsigned long)
10759243Sobrien
10859243Sobrien# define bit(f, i) (hashwidth==sizeof(unsigned char)  ? cbit(f,i) : \
10959243Sobrien 		    ((hashwidth==sizeof(unsigned short) ? sbit(f,i) : \
11059243Sobrien		     ((hashwidth==sizeof(unsigned int)   ? ibit(f,i) : \
11159243Sobrien		     lbit(f,i))))))
11259243Sobrien# define bis(f, i) (hashwidth==sizeof(unsigned char)  ? cbis(f,i) : \
11359243Sobrien 		    ((hashwidth==sizeof(unsigned short) ? sbis(f,i) : \
11459243Sobrien		     ((hashwidth==sizeof(unsigned int)   ? ibis(f,i) : \
11559243Sobrien		     lbis(f,i))))))
11659243Sobrien#else /* OLDHASH */
11759243Sobrien/*
11859243Sobrien * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
11959243Sobrien * to hash execs.  If it is allocated (havhash true), then to tell
12059243Sobrien * whether ``name'' is (possibly) present in the i'th component
12159243Sobrien * of the variable path, you look at the bit in xhash indexed by
12259243Sobrien * hash(hashname("name"), i).  This is setup automatically
12359243Sobrien * after .login is executed, and recomputed whenever ``path'' is
12459243Sobrien * changed.
12559243Sobrien */
12659243Sobrien# define HSHSIZ		8192	/* 1k bytes */
12759243Sobrien# define HSHMASK		(HSHSIZ - 1)
12859243Sobrien# define HSHMUL		243
12959243Sobrienstatic char xhash[HSHSIZ / BITS_PER_BYTE];
13059243Sobrien
13159243Sobrien# define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
13259243Sobrien# define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
13359243Sobrien# define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
13459243Sobrien
13559243Sobrien#endif /* FASTHASH */
13659243Sobrien
13759243Sobrien#ifdef VFORK
13859243Sobrienstatic int hits, misses;
13959243Sobrien#endif /* VFORK */
14059243Sobrien
14159243Sobrien/* Dummy search path for just absolute search when no path */
14259243Sobrienstatic Char *justabs[] = {STRNULL, 0};
14359243Sobrien
144231990Smpstatic	void	pexerr		(void) __attribute__((__noreturn__));
145167465Smpstatic	void	texec		(Char *, Char **);
146167465Smpint	hashname	(Char *);
147167465Smpstatic	int 	iscommand	(Char *);
14859243Sobrien
14959243Sobrienvoid
150167465Smpdoexec(struct command *t, int do_glob)
15159243Sobrien{
152231990Smp    Char *dp, **pv, **opv, **av, *sav;
153100616Smp    struct varent *v;
154231990Smp    int slash, gflag, rehashed;
155100616Smp    int hashval, i;
15659243Sobrien    Char   *blk[2];
15759243Sobrien
15859243Sobrien    /*
15959243Sobrien     * Glob the command name. We will search $path even if this does something,
16059243Sobrien     * as in sh but not in csh.  One special case: if there is no PATH, then we
16159243Sobrien     * execute only commands which start with '/'.
16259243Sobrien     */
16359243Sobrien    blk[0] = t->t_dcom[0];
16459243Sobrien    blk[1] = 0;
165100616Smp    gflag = 0;
166100616Smp    if (do_glob)
167167465Smp	gflag = tglob(blk);
16859243Sobrien    if (gflag) {
169167465Smp	pv = globall(blk, gflag);
17059243Sobrien	if (pv == 0) {
17159243Sobrien	    setname(short2str(blk[0]));
17259243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
17359243Sobrien	}
17459243Sobrien    }
17559243Sobrien    else
17659243Sobrien	pv = saveblk(blk);
177167465Smp    cleanup_push(pv, blk_cleanup);
17859243Sobrien
17959243Sobrien    trim(pv);
18059243Sobrien
18159243Sobrien    exerr = 0;
18259243Sobrien    expath = Strsave(pv[0]);
18359243Sobrien#ifdef VFORK
18459243Sobrien    Vexpath = expath;
18559243Sobrien#endif /* VFORK */
18659243Sobrien
18759243Sobrien    v = adrof(STRpath);
188167465Smp    if (v == 0 && expath[0] != '/' && expath[0] != '.')
18959243Sobrien	pexerr();
19059243Sobrien    slash = any(short2str(expath), '/');
19159243Sobrien
19259243Sobrien    /*
19359243Sobrien     * Glob the argument list, if necessary. Otherwise trim off the quote bits.
19459243Sobrien     */
19559243Sobrien    gflag = 0;
19659243Sobrien    av = &t->t_dcom[1];
197100616Smp    if (do_glob)
198167465Smp	gflag = tglob(av);
19959243Sobrien    if (gflag) {
200167465Smp	av = globall(av, gflag);
20159243Sobrien	if (av == 0) {
20259243Sobrien	    setname(short2str(expath));
20359243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
20459243Sobrien	}
20559243Sobrien    }
20659243Sobrien    else
20759243Sobrien	av = saveblk(av);
20859243Sobrien
20959243Sobrien    blkfree(t->t_dcom);
210167465Smp    cleanup_ignore(pv);
211167465Smp    cleanup_until(pv);
21259243Sobrien    t->t_dcom = blkspl(pv, av);
213167465Smp    xfree(pv);
214167465Smp    xfree(av);
21559243Sobrien    av = t->t_dcom;
21659243Sobrien    trim(av);
21759243Sobrien
21859243Sobrien    if (*av == NULL || **av == '\0')
21959243Sobrien	pexerr();
22059243Sobrien
22159243Sobrien    xechoit(av);		/* Echo command if -x */
22259243Sobrien#ifdef CLOSE_ON_EXEC
22359243Sobrien    /*
22459243Sobrien     * Since all internal file descriptors are set to close on exec, we don't
22559243Sobrien     * need to close them explicitly here.  Just reorient ourselves for error
22659243Sobrien     * messages.
22759243Sobrien     */
22859243Sobrien    SHIN = 0;
22959243Sobrien    SHOUT = 1;
23059243Sobrien    SHDIAG = 2;
23159243Sobrien    OLDSTD = 0;
23259243Sobrien    isoutatty = isatty(SHOUT);
23359243Sobrien    isdiagatty = isatty(SHDIAG);
23459243Sobrien#else
23559243Sobrien    closech();			/* Close random fd's */
23659243Sobrien#endif
23759243Sobrien    /*
23859243Sobrien     * We must do this AFTER any possible forking (like `foo` in glob) so that
23959243Sobrien     * this shell can still do subprocesses.
24059243Sobrien     */
241167465Smp    {
242167465Smp	sigset_t set;
243167465Smp	sigemptyset(&set);
244167465Smp	sigaddset(&set, SIGINT);
245167465Smp	sigaddset(&set, SIGCHLD);
246167465Smp	sigprocmask(SIG_UNBLOCK, &set, NULL);
247167465Smp    }
248167465Smp    pintr_disabled = 0;
249167465Smp    pchild_disabled = 0;
25059243Sobrien
25159243Sobrien    /*
25259243Sobrien     * If no path, no words in path, or a / in the filename then restrict the
25359243Sobrien     * command search.
25459243Sobrien     */
255100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
256231990Smp	opv = justabs;
25759243Sobrien    else
258231990Smp	opv = v->vec;
25959243Sobrien    sav = Strspl(STRslash, *av);/* / command name for postpending */
260167465Smp#ifndef VFORK
261167465Smp    cleanup_push(sav, xfree);
262167465Smp#else /* VFORK */
26359243Sobrien    Vsav = sav;
26459243Sobrien#endif /* VFORK */
26559243Sobrien    hashval = havhash ? hashname(*av) : 0;
26659243Sobrien
267231990Smp    rehashed = 0;
268231990Smpretry:
269231990Smp    pv = opv;
27059243Sobrien    i = 0;
27159243Sobrien#ifdef VFORK
27259243Sobrien    hits++;
27359243Sobrien#endif /* VFORK */
27459243Sobrien    do {
27559243Sobrien	/*
27659243Sobrien	 * Try to save time by looking at the hash table for where this command
27759243Sobrien	 * could be.  If we are doing delayed hashing, then we put the names in
27859243Sobrien	 * one at a time, as the user enters them.  This is kinda like Korn
27959243Sobrien	 * Shell's "tracked aliases".
28059243Sobrien	 */
28159243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
28259243Sobrien#ifdef FASTHASH
28359243Sobrien	    if (!bit(hashval, i))
28459243Sobrien		goto cont;
28559243Sobrien#else /* OLDHASH */
28659243Sobrien	    int hashval1 = hash(hashval, i);
28759243Sobrien	    if (!bit(xhash, hashval1))
28859243Sobrien		goto cont;
28959243Sobrien#endif /* FASTHASH */
29059243Sobrien	}
29159243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
29259243Sobrien	    texec(*av, av);
29359243Sobrien	else {
29459243Sobrien	    dp = Strspl(*pv, sav);
295167465Smp#ifndef VFORK
296167465Smp	    cleanup_push(dp, xfree);
297167465Smp#else /* VFORK */
29859243Sobrien	    Vdp = dp;
29959243Sobrien#endif /* VFORK */
30059243Sobrien
30159243Sobrien	    texec(dp, av);
302167465Smp#ifndef VFORK
303167465Smp	    cleanup_until(dp);
304167465Smp#else /* VFORK */
30559243Sobrien	    Vdp = 0;
306167465Smp	    xfree(dp);
30759243Sobrien#endif /* VFORK */
30859243Sobrien	}
30959243Sobrien#ifdef VFORK
31059243Sobrien	misses++;
31159243Sobrien#endif /* VFORK */
31259243Sobriencont:
31359243Sobrien	pv++;
31459243Sobrien	i++;
31559243Sobrien    } while (*pv);
31659243Sobrien#ifdef VFORK
31759243Sobrien    hits--;
318167465Smp#endif /* VFORK */
319231990Smp    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
320231990Smp	dohash(NULL, NULL);
321231990Smp	rehashed = 1;
322231990Smp	goto retry;
323231990Smp    }
324167465Smp#ifndef VFORK
325167465Smp    cleanup_until(sav);
326167465Smp#else /* VFORK */
32759243Sobrien    Vsav = 0;
328167465Smp    xfree(sav);
32959243Sobrien#endif /* VFORK */
33059243Sobrien    pexerr();
33159243Sobrien}
33259243Sobrien
33359243Sobrienstatic void
334167465Smppexerr(void)
33559243Sobrien{
33659243Sobrien    /* Couldn't find the damn thing */
33759243Sobrien    if (expath) {
33859243Sobrien	setname(short2str(expath));
33959243Sobrien#ifdef VFORK
34059243Sobrien	Vexpath = 0;
34159243Sobrien#endif /* VFORK */
342167465Smp	xfree(expath);
34359243Sobrien	expath = 0;
34459243Sobrien    }
34559243Sobrien    else
34659243Sobrien	setname("");
34759243Sobrien    if (exerr)
34859243Sobrien	stderror(ERR_NAME | ERR_STRING, exerr);
34959243Sobrien    stderror(ERR_NAME | ERR_COMMAND);
35059243Sobrien}
35159243Sobrien
35259243Sobrien/*
35359243Sobrien * Execute command f, arg list t.
35459243Sobrien * Record error message if not found.
35559243Sobrien * Also do shell scripts here.
35659243Sobrien */
35759243Sobrienstatic void
358167465Smptexec(Char *sf, Char **st)
35959243Sobrien{
360145479Smp    char **t;
361145479Smp    char *f;
362145479Smp    struct varent *v;
36359243Sobrien    Char  **vp;
36459243Sobrien    Char   *lastsh[2];
36559243Sobrien    char    pref[2];
36659243Sobrien    int     fd;
36759243Sobrien    Char   *st0, **ost;
36859243Sobrien
36959243Sobrien    /* The order for the conversions is significant */
37059243Sobrien    t = short2blk(st);
37159243Sobrien    f = short2str(sf);
37259243Sobrien#ifdef VFORK
37359243Sobrien    Vt = t;
37459243Sobrien#endif /* VFORK */
37559243Sobrien    errno = 0;			/* don't use a previous error */
37659243Sobrien#ifdef apollo
37759243Sobrien    /*
37859243Sobrien     * If we try to execute an nfs mounted directory on the apollo, we
37959243Sobrien     * hang forever. So until apollo fixes that..
38059243Sobrien     */
38159243Sobrien    {
38259243Sobrien	struct stat stb;
38359243Sobrien	if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
38459243Sobrien	    errno = EISDIR;
38559243Sobrien    }
38659243Sobrien    if (errno == 0)
38759243Sobrien#endif /* apollo */
38859243Sobrien    {
38959243Sobrien#ifdef ISC_POSIX_EXEC_BUG
39059243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
39159243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
39259243Sobrien	(void) execv(f, t);
39359243Sobrien#ifdef ISC_POSIX_EXEC_BUG
39459243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
39559243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
39659243Sobrien    }
39759243Sobrien#ifdef VFORK
39859243Sobrien    Vt = 0;
39959243Sobrien#endif /* VFORK */
40059243Sobrien    blkfree((Char **) t);
40159243Sobrien    switch (errno) {
40259243Sobrien
40359243Sobrien    case ENOEXEC:
40469408Sache#ifdef WINNT_NATIVE
40559243Sobrien		nt_feed_to_cmd(f,t);
40669408Sache#endif /* WINNT_NATIVE */
40759243Sobrien	/*
40859243Sobrien	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
40959243Sobrien	 * it, don't feed it to the shell if it looks like a binary!
41059243Sobrien	 */
411167465Smp	if ((fd = xopen(f, O_RDONLY|O_LARGEFILE)) != -1) {
41259243Sobrien	    int nread;
413167465Smp	    if ((nread = xread(fd, pref, 2)) == 2) {
414145479Smp		if (!isprint((unsigned char)pref[0]) &&
415145479Smp		    (pref[0] != '\n' && pref[0] != '\t')) {
416167465Smp		    int err;
417167465Smp
418167465Smp		    err = errno;
419167465Smp		    xclose(fd);
42059243Sobrien		    /*
42159243Sobrien		     * We *know* what ENOEXEC means.
42259243Sobrien		     */
423167465Smp		    stderror(ERR_ARCH, f, strerror(err));
42459243Sobrien		}
42559243Sobrien	    }
426167465Smp	    else if (nread < 0) {
42759243Sobrien#ifdef convex
428167465Smp		int err;
429167465Smp
430167465Smp		err = errno;
431167465Smp		xclose(fd);
43259243Sobrien		/* need to print error incase the file is migrated */
433167465Smp		stderror(ERR_SYSTEM, f, strerror(err));
43459243Sobrien#endif
43559243Sobrien	    }
43659243Sobrien#ifdef _PATH_BSHELL
43759243Sobrien	    else {
43859243Sobrien		pref[0] = '#';
43959243Sobrien		pref[1] = '\0';
44059243Sobrien	    }
44159243Sobrien#endif
44259243Sobrien	}
44359243Sobrien#ifdef HASHBANG
44459243Sobrien	if (fd == -1 ||
44559243Sobrien	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
44659243Sobrien#endif /* HASHBANG */
44759243Sobrien	/*
44859243Sobrien	 * If there is an alias for shell, then put the words of the alias in
44959243Sobrien	 * front of the argument list replacing the command name. Note no
45059243Sobrien	 * interpretation of the words at this point.
45159243Sobrien	 */
45259243Sobrien	    v = adrof1(STRshell, &aliases);
453100616Smp	    if (v == NULL || v->vec == NULL) {
45459243Sobrien		vp = lastsh;
45559243Sobrien		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
45659243Sobrien		vp[1] = NULL;
45759243Sobrien#ifdef _PATH_BSHELL
45859243Sobrien		if (fd != -1
45959243Sobrien# ifndef ISC	/* Compatible with ISC's /bin/csh */
46059243Sobrien		    && pref[0] != '#'
46159243Sobrien# endif /* ISC */
46259243Sobrien		    )
46359243Sobrien		    vp[0] = STR_BSHELL;
46459243Sobrien#endif
46559243Sobrien		vp = saveblk(vp);
46659243Sobrien	    }
46759243Sobrien	    else
46859243Sobrien		vp = saveblk(v->vec);
46959243Sobrien#ifdef HASHBANG
47059243Sobrien	}
47159243Sobrien#endif /* HASHBANG */
47259243Sobrien	if (fd != -1)
473167465Smp	    xclose(fd);
47459243Sobrien
47559243Sobrien	st0 = st[0];
47659243Sobrien	st[0] = sf;
47759243Sobrien	ost = st;
47859243Sobrien	st = blkspl(vp, st);	/* Splice up the new arglst */
47959243Sobrien	ost[0] = st0;
48059243Sobrien	sf = *st;
48159243Sobrien	/* The order for the conversions is significant */
48259243Sobrien	t = short2blk(st);
48359243Sobrien	f = short2str(sf);
484167465Smp	xfree(st);
48559243Sobrien	blkfree((Char **) vp);
48659243Sobrien#ifdef VFORK
48759243Sobrien	Vt = t;
48859243Sobrien#endif /* VFORK */
48959243Sobrien#ifdef ISC_POSIX_EXEC_BUG
49059243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
49159243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
49259243Sobrien	(void) execv(f, t);
49359243Sobrien#ifdef ISC_POSIX_EXEC_BUG
49459243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
49559243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
49659243Sobrien#ifdef VFORK
49759243Sobrien	Vt = 0;
49859243Sobrien#endif /* VFORK */
49959243Sobrien	blkfree((Char **) t);
50059243Sobrien	/* The sky is falling, the sky is falling! */
50159243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
50259243Sobrien	break;
50359243Sobrien
50459243Sobrien    case ENOMEM:
50559243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
50659243Sobrien	break;
50759243Sobrien
50859243Sobrien#ifdef _IBMR2
50959243Sobrien    case 0:			/* execv fails and returns 0! */
51059243Sobrien#endif /* _IBMR2 */
51159243Sobrien    case ENOENT:
51259243Sobrien	break;
51359243Sobrien
51459243Sobrien    default:
51559243Sobrien	if (exerr == 0) {
51659243Sobrien	    exerr = strerror(errno);
517167465Smp	    xfree(expath);
51859243Sobrien	    expath = Strsave(sf);
51959243Sobrien#ifdef VFORK
52059243Sobrien	    Vexpath = expath;
52159243Sobrien#endif /* VFORK */
52259243Sobrien	}
52359243Sobrien	break;
52459243Sobrien    }
52559243Sobrien}
52659243Sobrien
527167465Smpstruct execash_state
52859243Sobrien{
529167465Smp    int saveIN, saveOUT, saveDIAG, saveSTD;
530167465Smp    int SHIN, SHOUT, SHDIAG, OLDSTD;
531167465Smp    int didfds;
53259243Sobrien#ifndef CLOSE_ON_EXEC
533167465Smp    int didcch;
534167465Smp#endif
535167465Smp    struct sigaction sigint, sigquit, sigterm;
536167465Smp};
537167465Smp
538167465Smpstatic void
539167465Smpexecash_cleanup(void *xstate)
540167465Smp{
541167465Smp    struct execash_state *state;
542167465Smp
543167465Smp    state = xstate;
544167465Smp    sigaction(SIGINT, &state->sigint, NULL);
545167465Smp    sigaction(SIGQUIT, &state->sigquit, NULL);
546167465Smp    sigaction(SIGTERM, &state->sigterm, NULL);
547167465Smp
548167465Smp    doneinp = 0;
549167465Smp#ifndef CLOSE_ON_EXEC
550167465Smp    didcch = state->didcch;
55159243Sobrien#endif /* CLOSE_ON_EXEC */
552167465Smp    didfds = state->didfds;
553167465Smp    xclose(SHIN);
554167465Smp    xclose(SHOUT);
555167465Smp    xclose(SHDIAG);
556167465Smp    xclose(OLDSTD);
557167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
558167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
559167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
560167465Smp    close_on_exec(OLDSTD = dmove(state->saveSTD, state->OLDSTD), 1);
561167465Smp}
56259243Sobrien
563167465Smp/*ARGSUSED*/
564167465Smpvoid
565167465Smpexecash(Char **t, struct command *kp)
566167465Smp{
567167465Smp    struct execash_state state;
568167465Smp
56959243Sobrien    USE(t);
57059243Sobrien    if (chkstop == 0 && setintr)
57159243Sobrien	panystop(0);
57259243Sobrien    /*
57359243Sobrien     * Hmm, we don't really want to do that now because we might
57459243Sobrien     * fail, but what is the choice
57559243Sobrien     */
57659243Sobrien    rechist(NULL, adrof(STRsavehist) != NULL);
57759243Sobrien
57859243Sobrien
579167465Smp    sigaction(SIGINT, &parintr, &state.sigint);
580167465Smp    sigaction(SIGQUIT, &parintr, &state.sigquit);
581167465Smp    sigaction(SIGTERM, &parterm, &state.sigterm);
58259243Sobrien
583167465Smp    state.didfds = didfds;
58459243Sobrien#ifndef CLOSE_ON_EXEC
585167465Smp    state.didcch = didcch;
58659243Sobrien#endif /* CLOSE_ON_EXEC */
587167465Smp    state.SHIN = SHIN;
588167465Smp    state.SHOUT = SHOUT;
589167465Smp    state.SHDIAG = SHDIAG;
590167465Smp    state.OLDSTD = OLDSTD;
59159243Sobrien
592167465Smp    (void)close_on_exec (state.saveIN = dcopy(SHIN, -1), 1);
593167465Smp    (void)close_on_exec (state.saveOUT = dcopy(SHOUT, -1), 1);
594167465Smp    (void)close_on_exec (state.saveDIAG = dcopy(SHDIAG, -1), 1);
595167465Smp    (void)close_on_exec (state.saveSTD = dcopy(OLDSTD, -1), 1);
596167465Smp
59759243Sobrien    lshift(kp->t_dcom, 1);
59859243Sobrien
599167465Smp    (void)close_on_exec (SHIN = dcopy(0, -1), 1);
600167465Smp    (void)close_on_exec (SHOUT = dcopy(1, -1), 1);
601167465Smp    (void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
60259243Sobrien#ifndef CLOSE_ON_EXEC
603167465Smp    didcch = 0;
60459243Sobrien#endif /* CLOSE_ON_EXEC */
605167465Smp    didfds = 0;
606167465Smp    cleanup_push(&state, execash_cleanup);
607167465Smp
608167465Smp    /*
609167465Smp     * Decrement the shell level
610167465Smp     */
611167465Smp    shlvl(-1);
61269408Sache#ifdef WINNT_NATIVE
613167465Smp    __nt_really_exec=1;
61469408Sache#endif /* WINNT_NATIVE */
615167465Smp    doexec(kp, 1);
61659243Sobrien
617167465Smp    cleanup_until(&state);
61859243Sobrien}
61959243Sobrien
62059243Sobrienvoid
621167465Smpxechoit(Char **t)
62259243Sobrien{
62359243Sobrien    if (adrof(STRecho)) {
624100616Smp	int odidfds = didfds;
62559243Sobrien	flush();
62659243Sobrien	haderr = 1;
627100616Smp	didfds = 0;
62859243Sobrien	blkpr(t), xputchar('\n');
629100616Smp	flush();
630100616Smp	didfds = odidfds;
63159243Sobrien	haderr = 0;
63259243Sobrien    }
63359243Sobrien}
63459243Sobrien
63559243Sobrien/*ARGSUSED*/
63659243Sobrienvoid
637167465Smpdohash(Char **vv, struct command *c)
63859243Sobrien{
63959243Sobrien#ifdef COMMENT
64059243Sobrien    struct stat stb;
64159243Sobrien#endif
64259243Sobrien    DIR    *dirp;
643145479Smp    struct dirent *dp;
64459243Sobrien    int     i = 0;
64559243Sobrien    struct varent *v = adrof(STRpath);
64659243Sobrien    Char  **pv;
64759243Sobrien    int hashval;
64869408Sache#ifdef WINNT_NATIVE
64959243Sobrien    int is_windir; /* check if it is the windows directory */
65059243Sobrien    USE(hashval);
65169408Sache#endif /* WINNT_NATIVE */
65259243Sobrien
65359243Sobrien    USE(c);
65459243Sobrien#ifdef FASTHASH
65559243Sobrien    if (vv && vv[1]) {
65659243Sobrien        uhashlength = atoi(short2str(vv[1]));
65759243Sobrien        if (vv[2]) {
65859243Sobrien	    uhashwidth = atoi(short2str(vv[2]));
65959243Sobrien	    if ((uhashwidth != sizeof(unsigned char)) &&
66059243Sobrien	        (uhashwidth != sizeof(unsigned short)) &&
66159243Sobrien	        (uhashwidth != sizeof(unsigned long)))
66259243Sobrien	        uhashwidth = 0;
66359243Sobrien	    if (vv[3])
66459243Sobrien		hashdebug = atoi(short2str(vv[3]));
66559243Sobrien        }
66659243Sobrien    }
66759243Sobrien
66859243Sobrien    if (uhashwidth)
66959243Sobrien	hashwidth = uhashwidth;
67059243Sobrien    else {
67159243Sobrien	hashwidth = 0;
67269408Sache	if (v == NULL)
67369408Sache	    return;
674100616Smp	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
67559243Sobrien	    continue;
67659243Sobrien	if (hashwidth <= widthof(unsigned char))
67759243Sobrien	    hashwidth = sizeof(unsigned char);
67859243Sobrien	else if (hashwidth <= widthof(unsigned short))
67959243Sobrien	    hashwidth = sizeof(unsigned short);
68059243Sobrien	else if (hashwidth <= widthof(unsigned int))
68159243Sobrien	    hashwidth = sizeof(unsigned int);
68259243Sobrien	else
68359243Sobrien	    hashwidth = sizeof(unsigned long);
68459243Sobrien    }
68559243Sobrien
68659243Sobrien    if (uhashlength)
68759243Sobrien	hashlength = uhashlength;
68859243Sobrien    else
68959243Sobrien        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
690167465Smp
691167465Smp    xfree(xhash);
692167465Smp    xhash = xcalloc(hashlength * hashwidth, 1);
69359243Sobrien#endif /* FASTHASH */
69459243Sobrien
69559243Sobrien    (void) getusername(NULL);	/* flush the tilde cashe */
69659243Sobrien    tw_cmd_free();
69759243Sobrien    havhash = 1;
69859243Sobrien    if (v == NULL)
69959243Sobrien	return;
700100616Smp    for (pv = v->vec; pv && *pv; pv++, i++) {
70159243Sobrien	if (!ABSOLUTEP(pv[0]))
70259243Sobrien	    continue;
70359243Sobrien	dirp = opendir(short2str(*pv));
70459243Sobrien	if (dirp == NULL)
70559243Sobrien	    continue;
706167465Smp	cleanup_push(dirp, opendir_cleanup);
70759243Sobrien#ifdef COMMENT			/* this isn't needed.  opendir won't open
70859243Sobrien				 * non-dirs */
70959243Sobrien	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
710167465Smp	    cleanup_until(dirp);
71159243Sobrien	    continue;
71259243Sobrien	}
71359243Sobrien#endif
71469408Sache#ifdef WINNT_NATIVE
71559243Sobrien	is_windir = nt_check_if_windir(short2str(*pv));
71669408Sache#endif /* WINNT_NATIVE */
71759243Sobrien	while ((dp = readdir(dirp)) != NULL) {
71859243Sobrien	    if (dp->d_ino == 0)
71959243Sobrien		continue;
72059243Sobrien	    if (dp->d_name[0] == '.' &&
72159243Sobrien		(dp->d_name[1] == '\0' ||
72259243Sobrien		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
72359243Sobrien		continue;
72469408Sache#ifdef WINNT_NATIVE
72559243Sobrien	    nt_check_name_and_hash(is_windir, dp->d_name, i);
72669408Sache#else /* !WINNT_NATIVE*/
72769408Sache#if defined(_UWIN) || defined(__CYGWIN__)
72859243Sobrien	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
72959243Sobrien	     * the file with the .exe, .com, .bat extension
730231990Smp	     *
731231990Smp	     * Same for Cygwin, but only for .exe and .com extension.
73259243Sobrien	     */
73359243Sobrien	    {
734167465Smp		ssize_t	ext = strlen(dp->d_name) - 4;
735131962Smp		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
736231990Smp#ifndef __CYGWIN__
737131962Smp				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
738231990Smp#endif
739167465Smp				  strcasecmp(&dp->d_name[ext], ".com") == 0)) {
740167465Smp#ifdef __CYGWIN__
741167465Smp		    /* Also store the variation with extension. */
742167465Smp		    hashval = hashname(str2short(dp->d_name));
743167465Smp		    bis(hashval, i);
744131962Smp#endif /* __CYGWIN__ */
745167465Smp		    dp->d_name[ext] = '\0';
746167465Smp		}
74759243Sobrien	    }
74869408Sache#endif /* _UWIN || __CYGWIN__ */
74959243Sobrien# ifdef FASTHASH
75059243Sobrien	    hashval = hashname(str2short(dp->d_name));
75159243Sobrien	    bis(hashval, i);
75259243Sobrien	    if (hashdebug & 1)
75359243Sobrien	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
75459243Sobrien		        hashname(str2short(dp->d_name)), i, dp->d_name);
75559243Sobrien# else /* OLD HASH */
75659243Sobrien	    hashval = hash(hashname(str2short(dp->d_name)), i);
75759243Sobrien	    bis(xhash, hashval);
75859243Sobrien# endif /* FASTHASH */
75959243Sobrien	    /* tw_add_comm_name (dp->d_name); */
76069408Sache#endif /* WINNT_NATIVE */
76159243Sobrien	}
762167465Smp	cleanup_until(dirp);
76359243Sobrien    }
76459243Sobrien}
76559243Sobrien
76659243Sobrien/*ARGSUSED*/
76759243Sobrienvoid
768167465Smpdounhash(Char **v, struct command *c)
76959243Sobrien{
77059243Sobrien    USE(c);
77159243Sobrien    USE(v);
77259243Sobrien    havhash = 0;
77359243Sobrien#ifdef FASTHASH
774167465Smp    xfree(xhash);
775167465Smp    xhash = NULL;
77659243Sobrien#endif /* FASTHASH */
77759243Sobrien}
77859243Sobrien
77959243Sobrien/*ARGSUSED*/
78059243Sobrienvoid
781167465Smphashstat(Char **v, struct command *c)
78259243Sobrien{
78359243Sobrien    USE(c);
78459243Sobrien    USE(v);
78559243Sobrien#ifdef FASTHASH
78659243Sobrien   if (havhash && hashlength && hashwidth)
78759243Sobrien      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
78859243Sobrien	      hashlength, hashwidth*8);
78959243Sobrien   if (hashdebug)
79059243Sobrien      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
79159243Sobrien#endif /* FASTHASH */
79259243Sobrien#ifdef VFORK
79359243Sobrien   if (hits + misses)
79459243Sobrien      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
79559243Sobrien	      hits, misses, 100 * hits / (hits + misses));
79659243Sobrien#endif
79759243Sobrien}
79859243Sobrien
79959243Sobrien
80059243Sobrien/*
80159243Sobrien * Hash a command name.
80259243Sobrien */
80369408Sacheint
804167465Smphashname(Char *cp)
80559243Sobrien{
806145479Smp    unsigned long h;
80759243Sobrien
80859243Sobrien    for (h = 0; *cp; cp++)
80959243Sobrien	h = hash(h, *cp);
81059243Sobrien    return ((int) h);
81159243Sobrien}
81259243Sobrien
81359243Sobrienstatic int
814167465Smpiscommand(Char *name)
81559243Sobrien{
816231990Smp    Char **opv, **pv;
817145479Smp    Char *sav;
818145479Smp    struct varent *v;
819145479Smp    int slash = any(short2str(name), '/');
820231990Smp    int hashval, rehashed, i;
82159243Sobrien
82259243Sobrien    v = adrof(STRpath);
823100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
824231990Smp	opv = justabs;
82559243Sobrien    else
826231990Smp	opv = v->vec;
82759243Sobrien    sav = Strspl(STRslash, name);	/* / command name for postpending */
82859243Sobrien    hashval = havhash ? hashname(name) : 0;
829231990Smp
830231990Smp    rehashed = 0;
831231990Smpretry:
832231990Smp    pv = opv;
83359243Sobrien    i = 0;
83459243Sobrien    do {
83559243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
83659243Sobrien#ifdef FASTHASH
83759243Sobrien	    if (!bit(hashval, i))
83859243Sobrien		goto cont;
83959243Sobrien#else /* OLDHASH */
84059243Sobrien	    int hashval1 = hash(hashval, i);
84159243Sobrien	    if (!bit(xhash, hashval1))
84259243Sobrien		goto cont;
84359243Sobrien#endif /* FASTHASH */
84459243Sobrien	}
84559243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
84659243Sobrien	    if (executable(NULL, name, 0)) {
847167465Smp		xfree(sav);
84859243Sobrien		return i + 1;
84959243Sobrien	    }
85059243Sobrien	}
85159243Sobrien	else {
85259243Sobrien	    if (executable(*pv, sav, 0)) {
853167465Smp		xfree(sav);
85459243Sobrien		return i + 1;
85559243Sobrien	    }
85659243Sobrien	}
85759243Sobriencont:
85859243Sobrien	pv++;
85959243Sobrien	i++;
86059243Sobrien    } while (*pv);
861231990Smp    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
862231990Smp	dohash(NULL, NULL);
863231990Smp	rehashed = 1;
864231990Smp	goto retry;
865231990Smp    }
866167465Smp    xfree(sav);
86759243Sobrien    return 0;
86859243Sobrien}
86959243Sobrien
87059243Sobrien/* Also by:
87159243Sobrien *  Andreas Luik <luik@isaak.isa.de>
87259243Sobrien *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
87359243Sobrien *  Azenberstr. 35
87459243Sobrien *  D-7000 Stuttgart 1
87559243Sobrien *  West-Germany
87659243Sobrien * is the executable() routine below and changes to iscommand().
87759243Sobrien * Thanks again!!
87859243Sobrien */
87959243Sobrien
88069408Sache#ifndef WINNT_NATIVE
88159243Sobrien/*
88259243Sobrien * executable() examines the pathname obtained by concatenating dir and name
88359243Sobrien * (dir may be NULL), and returns 1 either if it is executable by us, or
88459243Sobrien * if dir_ok is set and the pathname refers to a directory.
88559243Sobrien * This is a bit kludgy, but in the name of optimization...
88659243Sobrien */
88759243Sobrienint
888167465Smpexecutable(const Char *dir, const Char *name, int dir_ok)
88959243Sobrien{
89059243Sobrien    struct stat stbuf;
89159243Sobrien    char   *strname;
89259243Sobrien
89359243Sobrien    if (dir && *dir) {
894167465Smp	Char *path;
895167465Smp
896167465Smp	path = Strspl(dir, name);
89759243Sobrien	strname = short2str(path);
898167465Smp	xfree(path);
89959243Sobrien    }
90059243Sobrien    else
90159243Sobrien	strname = short2str(name);
902167465Smp
90359243Sobrien    return (stat(strname, &stbuf) != -1 &&
90459243Sobrien	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
90559243Sobrien	     (S_ISREG(stbuf.st_mode) &&
90659243Sobrien    /* save time by not calling access() in the hopeless case */
90759243Sobrien	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
90859243Sobrien	      access(strname, X_OK) == 0
90959243Sobrien	)));
91059243Sobrien}
91169408Sache#endif /*!WINNT_NATIVE*/
91259243Sobrien
913167465Smpstruct tellmewhat_s0_cleanup
914167465Smp{
915167465Smp    Char **dest, *val;
916167465Smp};
917167465Smp
918167465Smpstatic void
919167465Smptellmewhat_s0_cleanup(void *xstate)
920167465Smp{
921167465Smp    struct tellmewhat_s0_cleanup *state;
922167465Smp
923167465Smp    state = xstate;
924167465Smp    *state->dest = state->val;
925167465Smp}
926167465Smp
92759243Sobrienint
928167465Smptellmewhat(struct wordent *lexp, Char **str)
92959243Sobrien{
930167465Smp    struct tellmewhat_s0_cleanup s0;
931145479Smp    int i;
932167465Smp    const struct biltins *bptr;
933145479Smp    struct wordent *sp = lexp->next;
934145479Smp    int    aliased = 0, found;
935167465Smp    Char   *s1, *s2, *cmd;
93659243Sobrien    Char    qc;
93759243Sobrien
93859243Sobrien    if (adrof1(sp->word, &aliases)) {
93959243Sobrien	alias(lexp);
94059243Sobrien	sp = lexp->next;
94159243Sobrien	aliased = 1;
94259243Sobrien    }
94359243Sobrien
944167465Smp    s0.dest = &sp->word;	/* to get the memory freeing right... */
945167465Smp    s0.val = sp->word;
946167465Smp    cleanup_push(&s0, tellmewhat_s0_cleanup);
94759243Sobrien
94859243Sobrien    /* handle quoted alias hack */
94959243Sobrien    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
95059243Sobrien	(sp->word)++;
95159243Sobrien
95259243Sobrien    /* do quoting, if it hasn't been done */
95359243Sobrien    s1 = s2 = sp->word;
95459243Sobrien    while (*s2)
95559243Sobrien	switch (*s2) {
95659243Sobrien	case '\'':
95759243Sobrien	case '"':
95859243Sobrien	    qc = *s2++;
95959243Sobrien	    while (*s2 && *s2 != qc)
96059243Sobrien		*s1++ = *s2++ | QUOTE;
96159243Sobrien	    if (*s2)
96259243Sobrien		s2++;
96359243Sobrien	    break;
96459243Sobrien	case '\\':
96559243Sobrien	    if (*++s2)
96659243Sobrien		*s1++ = *s2++ | QUOTE;
96759243Sobrien	    break;
96859243Sobrien	default:
96959243Sobrien	    *s1++ = *s2++;
97059243Sobrien	}
97159243Sobrien    *s1 = '\0';
97259243Sobrien
97359243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
97459243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
97559243Sobrien	    if (str == NULL) {
97659243Sobrien		if (aliased)
97759243Sobrien		    prlex(lexp);
97859243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
97959243Sobrien			      sp->word);
98059243Sobrien		flush();
98159243Sobrien	    }
982167465Smp	    else
983167465Smp		*str = Strsave(sp->word);
984167465Smp	    cleanup_until(&s0);
98559243Sobrien	    return TRUE;
98659243Sobrien	}
98759243Sobrien    }
98869408Sache#ifdef WINNT_NATIVE
98959243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
99059243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
99159243Sobrien	    if (str == NULL) {
99259243Sobrien		if (aliased)
99359243Sobrien		    prlex(lexp);
99459243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
99559243Sobrien			      sp->word);
99659243Sobrien		flush();
99759243Sobrien	    }
998167465Smp	    else
999167465Smp		*str = Strsave(sp->word);
1000167465Smp	    cleanup_until(&s0);
100159243Sobrien	    return TRUE;
100259243Sobrien	}
100359243Sobrien    }
100469408Sache#endif /* WINNT_NATIVE*/
100559243Sobrien
100659243Sobrien    sp->word = cmd = globone(sp->word, G_IGNORE);
1007167465Smp    cleanup_push(cmd, xfree);
100859243Sobrien
100959243Sobrien    if ((i = iscommand(sp->word)) != 0) {
1010145479Smp	Char **pv;
1011145479Smp	struct varent *v;
1012145479Smp	int    slash = any(short2str(sp->word), '/');
101359243Sobrien
101459243Sobrien	v = adrof(STRpath);
1015100616Smp	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
101659243Sobrien	    pv = justabs;
101759243Sobrien	else
101859243Sobrien	    pv = v->vec;
101959243Sobrien
1020167465Smp	pv += i - 1;
102159243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
102259243Sobrien	    if (!slash) {
102359243Sobrien		sp->word = Strspl(STRdotsl, sp->word);
1024167465Smp		cleanup_push(sp->word, xfree);
102559243Sobrien		prlex(lexp);
1026167465Smp		cleanup_until(sp->word);
102759243Sobrien	    }
102859243Sobrien	    else
102959243Sobrien		prlex(lexp);
103059243Sobrien	}
103159243Sobrien	else {
103259243Sobrien	    s1 = Strspl(*pv, STRslash);
103359243Sobrien	    sp->word = Strspl(s1, sp->word);
1034167465Smp	    xfree(s1);
1035167465Smp	    cleanup_push(sp->word, xfree);
103659243Sobrien	    if (str == NULL)
103759243Sobrien		prlex(lexp);
103859243Sobrien	    else
1039167465Smp		*str = Strsave(sp->word);
1040167465Smp	    cleanup_until(sp->word);
104159243Sobrien	}
104259243Sobrien	found = 1;
104359243Sobrien    }
104459243Sobrien    else {
104559243Sobrien	if (str == NULL) {
104659243Sobrien	    if (aliased)
104759243Sobrien		prlex(lexp);
104859243Sobrien	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
104959243Sobrien	    flush();
105059243Sobrien	}
105159243Sobrien	else
1052167465Smp	    *str = Strsave(sp->word);
105359243Sobrien	found = 0;
105459243Sobrien    }
1055167465Smp    cleanup_until(&s0);
105659243Sobrien    return found;
105759243Sobrien}
105859243Sobrien
105959243Sobrien/*
106059243Sobrien * Builtin to look at and list all places a command may be defined:
106159243Sobrien * aliases, shell builtins, and the path.
106259243Sobrien *
106359243Sobrien * Marc Horowitz <marc@mit.edu>
106459243Sobrien * MIT Student Information Processing Board
106559243Sobrien */
106659243Sobrien
106759243Sobrien/*ARGSUSED*/
106859243Sobrienvoid
1069167465Smpdowhere(Char **v, struct command *c)
107059243Sobrien{
107159243Sobrien    int found = 1;
107259243Sobrien    USE(c);
107359243Sobrien    for (v++; *v; v++)
107459243Sobrien	found &= find_cmd(*v, 1);
107559243Sobrien    /* Make status nonzero if any command is not found. */
107659243Sobrien    if (!found)
1077167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
107859243Sobrien}
107959243Sobrien
108059243Sobrienint
1081167465Smpfind_cmd(Char *cmd, int prt)
108259243Sobrien{
108359243Sobrien    struct varent *var;
1084167465Smp    const struct biltins *bptr;
108559243Sobrien    Char **pv;
108659243Sobrien    Char *sv;
1087231990Smp    int hashval, rehashed, i, ex, rval = 0;
108859243Sobrien
108959243Sobrien    if (prt && any(short2str(cmd), '/')) {
1090195609Smp	xprintf("%s", CGETS(13, 7, "where: / in command makes no sense\n"));
109159243Sobrien	return rval;
109259243Sobrien    }
109359243Sobrien
109459243Sobrien    /* first, look for an alias */
109559243Sobrien
109659243Sobrien    if (prt && adrof1(cmd, &aliases)) {
109759243Sobrien	if ((var = adrof1(cmd, &aliases)) != NULL) {
109859243Sobrien	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1099100616Smp	    if (var->vec != NULL)
1100100616Smp		blkpr(var->vec);
110159243Sobrien	    xputchar('\n');
110259243Sobrien	    rval = 1;
110359243Sobrien	}
110459243Sobrien    }
110559243Sobrien
110659243Sobrien    /* next, look for a shell builtin */
110759243Sobrien
110859243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
110959243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
111059243Sobrien	    rval = 1;
111159243Sobrien	    if (prt)
111259243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
111359243Sobrien	    else
111459243Sobrien		return rval;
111559243Sobrien	}
111659243Sobrien    }
111769408Sache#ifdef WINNT_NATIVE
111859243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
111959243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
112059243Sobrien	    rval = 1;
112159243Sobrien	    if (prt)
112259243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
112359243Sobrien	    else
112459243Sobrien		return rval;
112559243Sobrien	}
112659243Sobrien    }
112769408Sache#endif /* WINNT_NATIVE*/
112859243Sobrien
112959243Sobrien    /* last, look through the path for the command */
113059243Sobrien
113159243Sobrien    if ((var = adrof(STRpath)) == NULL)
113259243Sobrien	return rval;
113359243Sobrien
113459243Sobrien    hashval = havhash ? hashname(cmd) : 0;
113559243Sobrien
113659243Sobrien    sv = Strspl(STRslash, cmd);
1137167465Smp    cleanup_push(sv, xfree);
113859243Sobrien
1139231990Smp    rehashed = 0;
1140231990Smpretry:
1141100616Smp    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
114259243Sobrien	if (havhash && !eq(*pv, STRdot)) {
114359243Sobrien#ifdef FASTHASH
114459243Sobrien	    if (!bit(hashval, i))
114559243Sobrien		continue;
114659243Sobrien#else /* OLDHASH */
114759243Sobrien	    int hashval1 = hash(hashval, i);
114859243Sobrien	    if (!bit(xhash, hashval1))
114959243Sobrien		continue;
115059243Sobrien#endif /* FASTHASH */
115159243Sobrien	}
115259243Sobrien	ex = executable(*pv, sv, 0);
115359243Sobrien#ifdef FASTHASH
115459243Sobrien	if (!ex && (hashdebug & 2)) {
1155195609Smp	    xprintf("%s", CGETS(13, 10, "hash miss: "));
115659243Sobrien	    ex = 1;	/* Force printing */
115759243Sobrien	}
115859243Sobrien#endif /* FASTHASH */
115959243Sobrien	if (ex) {
116059243Sobrien	    rval = 1;
116159243Sobrien	    if (prt) {
116259243Sobrien		xprintf("%S/", *pv);
116359243Sobrien		xprintf("%S\n", cmd);
116459243Sobrien	    }
116559243Sobrien	    else
116659243Sobrien		return rval;
116759243Sobrien	}
116859243Sobrien    }
1169231990Smp    if (adrof(STRautorehash) && !rehashed && havhash) {
1170231990Smp	dohash(NULL, NULL);
1171231990Smp	rehashed = 1;
1172231990Smp	goto retry;
1173231990Smp    }
1174167465Smp    cleanup_until(sv);
117559243Sobrien    return rval;
117659243Sobrien}
117769408Sache#ifdef WINNT_NATIVE
117859243Sobrienint hashval_extern(cp)
117959243Sobrien	Char *cp;
118059243Sobrien{
118159243Sobrien	return havhash?hashname(cp):0;
118259243Sobrien}
118359243Sobrienint bit_extern(val,i)
118459243Sobrien	int val;
118559243Sobrien	int i;
118659243Sobrien{
118759243Sobrien	return bit(val,i);
118859243Sobrien}
118969408Sachevoid bis_extern(val,i)
119069408Sache	int val;
119169408Sache	int i;
119269408Sache{
119369408Sache	bis(val,i);
119469408Sache}
119569408Sache#endif /* WINNT_NATIVE */
119669408Sache
1197