1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/sh.exec.c,v 3.81 2016/09/12 16:33:54 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
35316958SdchaginRCSID("$tcsh: sh.exec.c,v 3.81 2016/09/12 16:33:54 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    /*
609316958Sdchagin     * Decrement the shell level, if not in a subshell
610167465Smp     */
611316958Sdchagin    if (mainpid == getpid())
612316958Sdchagin	shlvl(-1);
61369408Sache#ifdef WINNT_NATIVE
614167465Smp    __nt_really_exec=1;
61569408Sache#endif /* WINNT_NATIVE */
616167465Smp    doexec(kp, 1);
61759243Sobrien
618167465Smp    cleanup_until(&state);
61959243Sobrien}
62059243Sobrien
62159243Sobrienvoid
622167465Smpxechoit(Char **t)
62359243Sobrien{
62459243Sobrien    if (adrof(STRecho)) {
625100616Smp	int odidfds = didfds;
62659243Sobrien	flush();
62759243Sobrien	haderr = 1;
628100616Smp	didfds = 0;
62959243Sobrien	blkpr(t), xputchar('\n');
630100616Smp	flush();
631100616Smp	didfds = odidfds;
63259243Sobrien	haderr = 0;
63359243Sobrien    }
63459243Sobrien}
63559243Sobrien
63659243Sobrien/*ARGSUSED*/
63759243Sobrienvoid
638167465Smpdohash(Char **vv, struct command *c)
63959243Sobrien{
64059243Sobrien#ifdef COMMENT
64159243Sobrien    struct stat stb;
64259243Sobrien#endif
64359243Sobrien    DIR    *dirp;
644145479Smp    struct dirent *dp;
64559243Sobrien    int     i = 0;
64659243Sobrien    struct varent *v = adrof(STRpath);
64759243Sobrien    Char  **pv;
64859243Sobrien    int hashval;
64969408Sache#ifdef WINNT_NATIVE
65059243Sobrien    int is_windir; /* check if it is the windows directory */
65159243Sobrien    USE(hashval);
65269408Sache#endif /* WINNT_NATIVE */
65359243Sobrien
65459243Sobrien    USE(c);
65559243Sobrien#ifdef FASTHASH
65659243Sobrien    if (vv && vv[1]) {
65759243Sobrien        uhashlength = atoi(short2str(vv[1]));
65859243Sobrien        if (vv[2]) {
65959243Sobrien	    uhashwidth = atoi(short2str(vv[2]));
66059243Sobrien	    if ((uhashwidth != sizeof(unsigned char)) &&
66159243Sobrien	        (uhashwidth != sizeof(unsigned short)) &&
66259243Sobrien	        (uhashwidth != sizeof(unsigned long)))
66359243Sobrien	        uhashwidth = 0;
66459243Sobrien	    if (vv[3])
66559243Sobrien		hashdebug = atoi(short2str(vv[3]));
66659243Sobrien        }
66759243Sobrien    }
66859243Sobrien
66959243Sobrien    if (uhashwidth)
67059243Sobrien	hashwidth = uhashwidth;
67159243Sobrien    else {
67259243Sobrien	hashwidth = 0;
67369408Sache	if (v == NULL)
67469408Sache	    return;
675100616Smp	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
67659243Sobrien	    continue;
67759243Sobrien	if (hashwidth <= widthof(unsigned char))
67859243Sobrien	    hashwidth = sizeof(unsigned char);
67959243Sobrien	else if (hashwidth <= widthof(unsigned short))
68059243Sobrien	    hashwidth = sizeof(unsigned short);
68159243Sobrien	else if (hashwidth <= widthof(unsigned int))
68259243Sobrien	    hashwidth = sizeof(unsigned int);
68359243Sobrien	else
68459243Sobrien	    hashwidth = sizeof(unsigned long);
68559243Sobrien    }
68659243Sobrien
68759243Sobrien    if (uhashlength)
68859243Sobrien	hashlength = uhashlength;
68959243Sobrien    else
69059243Sobrien        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
691167465Smp
692167465Smp    xfree(xhash);
693167465Smp    xhash = xcalloc(hashlength * hashwidth, 1);
69459243Sobrien#endif /* FASTHASH */
69559243Sobrien
69659243Sobrien    (void) getusername(NULL);	/* flush the tilde cashe */
69759243Sobrien    tw_cmd_free();
69859243Sobrien    havhash = 1;
69959243Sobrien    if (v == NULL)
70059243Sobrien	return;
701100616Smp    for (pv = v->vec; pv && *pv; pv++, i++) {
70259243Sobrien	if (!ABSOLUTEP(pv[0]))
70359243Sobrien	    continue;
70459243Sobrien	dirp = opendir(short2str(*pv));
70559243Sobrien	if (dirp == NULL)
70659243Sobrien	    continue;
707167465Smp	cleanup_push(dirp, opendir_cleanup);
70859243Sobrien#ifdef COMMENT			/* this isn't needed.  opendir won't open
70959243Sobrien				 * non-dirs */
71059243Sobrien	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
711167465Smp	    cleanup_until(dirp);
71259243Sobrien	    continue;
71359243Sobrien	}
71459243Sobrien#endif
71569408Sache#ifdef WINNT_NATIVE
71659243Sobrien	is_windir = nt_check_if_windir(short2str(*pv));
71769408Sache#endif /* WINNT_NATIVE */
71859243Sobrien	while ((dp = readdir(dirp)) != NULL) {
71959243Sobrien	    if (dp->d_ino == 0)
72059243Sobrien		continue;
72159243Sobrien	    if (dp->d_name[0] == '.' &&
72259243Sobrien		(dp->d_name[1] == '\0' ||
72359243Sobrien		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
72459243Sobrien		continue;
72569408Sache#ifdef WINNT_NATIVE
72659243Sobrien	    nt_check_name_and_hash(is_windir, dp->d_name, i);
72769408Sache#else /* !WINNT_NATIVE*/
72869408Sache#if defined(_UWIN) || defined(__CYGWIN__)
72959243Sobrien	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
73059243Sobrien	     * the file with the .exe, .com, .bat extension
731231990Smp	     *
732231990Smp	     * Same for Cygwin, but only for .exe and .com extension.
73359243Sobrien	     */
73459243Sobrien	    {
735167465Smp		ssize_t	ext = strlen(dp->d_name) - 4;
736131962Smp		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
737231990Smp#ifndef __CYGWIN__
738131962Smp				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
739231990Smp#endif
740167465Smp				  strcasecmp(&dp->d_name[ext], ".com") == 0)) {
741167465Smp#ifdef __CYGWIN__
742167465Smp		    /* Also store the variation with extension. */
743167465Smp		    hashval = hashname(str2short(dp->d_name));
744167465Smp		    bis(hashval, i);
745131962Smp#endif /* __CYGWIN__ */
746167465Smp		    dp->d_name[ext] = '\0';
747167465Smp		}
74859243Sobrien	    }
74969408Sache#endif /* _UWIN || __CYGWIN__ */
75059243Sobrien# ifdef FASTHASH
75159243Sobrien	    hashval = hashname(str2short(dp->d_name));
75259243Sobrien	    bis(hashval, i);
75359243Sobrien	    if (hashdebug & 1)
75459243Sobrien	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
75559243Sobrien		        hashname(str2short(dp->d_name)), i, dp->d_name);
75659243Sobrien# else /* OLD HASH */
75759243Sobrien	    hashval = hash(hashname(str2short(dp->d_name)), i);
75859243Sobrien	    bis(xhash, hashval);
75959243Sobrien# endif /* FASTHASH */
76059243Sobrien	    /* tw_add_comm_name (dp->d_name); */
76169408Sache#endif /* WINNT_NATIVE */
76259243Sobrien	}
763167465Smp	cleanup_until(dirp);
76459243Sobrien    }
76559243Sobrien}
76659243Sobrien
76759243Sobrien/*ARGSUSED*/
76859243Sobrienvoid
769167465Smpdounhash(Char **v, struct command *c)
77059243Sobrien{
77159243Sobrien    USE(c);
77259243Sobrien    USE(v);
77359243Sobrien    havhash = 0;
77459243Sobrien#ifdef FASTHASH
775167465Smp    xfree(xhash);
776167465Smp    xhash = NULL;
77759243Sobrien#endif /* FASTHASH */
77859243Sobrien}
77959243Sobrien
78059243Sobrien/*ARGSUSED*/
78159243Sobrienvoid
782167465Smphashstat(Char **v, struct command *c)
78359243Sobrien{
78459243Sobrien    USE(c);
78559243Sobrien    USE(v);
78659243Sobrien#ifdef FASTHASH
78759243Sobrien   if (havhash && hashlength && hashwidth)
78859243Sobrien      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
78959243Sobrien	      hashlength, hashwidth*8);
79059243Sobrien   if (hashdebug)
79159243Sobrien      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
79259243Sobrien#endif /* FASTHASH */
79359243Sobrien#ifdef VFORK
79459243Sobrien   if (hits + misses)
79559243Sobrien      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
79659243Sobrien	      hits, misses, 100 * hits / (hits + misses));
79759243Sobrien#endif
79859243Sobrien}
79959243Sobrien
80059243Sobrien
80159243Sobrien/*
80259243Sobrien * Hash a command name.
80359243Sobrien */
80469408Sacheint
805167465Smphashname(Char *cp)
80659243Sobrien{
807145479Smp    unsigned long h;
80859243Sobrien
80959243Sobrien    for (h = 0; *cp; cp++)
81059243Sobrien	h = hash(h, *cp);
81159243Sobrien    return ((int) h);
81259243Sobrien}
81359243Sobrien
81459243Sobrienstatic int
815167465Smpiscommand(Char *name)
81659243Sobrien{
817231990Smp    Char **opv, **pv;
818145479Smp    Char *sav;
819145479Smp    struct varent *v;
820145479Smp    int slash = any(short2str(name), '/');
821231990Smp    int hashval, rehashed, i;
82259243Sobrien
82359243Sobrien    v = adrof(STRpath);
824100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
825231990Smp	opv = justabs;
82659243Sobrien    else
827231990Smp	opv = v->vec;
82859243Sobrien    sav = Strspl(STRslash, name);	/* / command name for postpending */
82959243Sobrien    hashval = havhash ? hashname(name) : 0;
830231990Smp
831231990Smp    rehashed = 0;
832231990Smpretry:
833231990Smp    pv = opv;
83459243Sobrien    i = 0;
83559243Sobrien    do {
83659243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
83759243Sobrien#ifdef FASTHASH
83859243Sobrien	    if (!bit(hashval, i))
83959243Sobrien		goto cont;
84059243Sobrien#else /* OLDHASH */
84159243Sobrien	    int hashval1 = hash(hashval, i);
84259243Sobrien	    if (!bit(xhash, hashval1))
84359243Sobrien		goto cont;
84459243Sobrien#endif /* FASTHASH */
84559243Sobrien	}
84659243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
84759243Sobrien	    if (executable(NULL, name, 0)) {
848167465Smp		xfree(sav);
84959243Sobrien		return i + 1;
85059243Sobrien	    }
85159243Sobrien	}
85259243Sobrien	else {
85359243Sobrien	    if (executable(*pv, sav, 0)) {
854167465Smp		xfree(sav);
85559243Sobrien		return i + 1;
85659243Sobrien	    }
85759243Sobrien	}
85859243Sobriencont:
85959243Sobrien	pv++;
86059243Sobrien	i++;
86159243Sobrien    } while (*pv);
862231990Smp    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
863231990Smp	dohash(NULL, NULL);
864231990Smp	rehashed = 1;
865231990Smp	goto retry;
866231990Smp    }
867167465Smp    xfree(sav);
86859243Sobrien    return 0;
86959243Sobrien}
87059243Sobrien
87159243Sobrien/* Also by:
87259243Sobrien *  Andreas Luik <luik@isaak.isa.de>
87359243Sobrien *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
87459243Sobrien *  Azenberstr. 35
87559243Sobrien *  D-7000 Stuttgart 1
87659243Sobrien *  West-Germany
87759243Sobrien * is the executable() routine below and changes to iscommand().
87859243Sobrien * Thanks again!!
87959243Sobrien */
88059243Sobrien
88169408Sache#ifndef WINNT_NATIVE
88259243Sobrien/*
88359243Sobrien * executable() examines the pathname obtained by concatenating dir and name
88459243Sobrien * (dir may be NULL), and returns 1 either if it is executable by us, or
88559243Sobrien * if dir_ok is set and the pathname refers to a directory.
88659243Sobrien * This is a bit kludgy, but in the name of optimization...
88759243Sobrien */
88859243Sobrienint
889167465Smpexecutable(const Char *dir, const Char *name, int dir_ok)
89059243Sobrien{
89159243Sobrien    struct stat stbuf;
89259243Sobrien    char   *strname;
89359243Sobrien
89459243Sobrien    if (dir && *dir) {
895167465Smp	Char *path;
896167465Smp
897167465Smp	path = Strspl(dir, name);
89859243Sobrien	strname = short2str(path);
899167465Smp	xfree(path);
90059243Sobrien    }
90159243Sobrien    else
90259243Sobrien	strname = short2str(name);
903167465Smp
90459243Sobrien    return (stat(strname, &stbuf) != -1 &&
90559243Sobrien	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
90659243Sobrien	     (S_ISREG(stbuf.st_mode) &&
90759243Sobrien    /* save time by not calling access() in the hopeless case */
90859243Sobrien	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
90959243Sobrien	      access(strname, X_OK) == 0
91059243Sobrien	)));
91159243Sobrien}
91269408Sache#endif /*!WINNT_NATIVE*/
91359243Sobrien
914167465Smpstruct tellmewhat_s0_cleanup
915167465Smp{
916167465Smp    Char **dest, *val;
917167465Smp};
918167465Smp
919167465Smpstatic void
920167465Smptellmewhat_s0_cleanup(void *xstate)
921167465Smp{
922167465Smp    struct tellmewhat_s0_cleanup *state;
923167465Smp
924167465Smp    state = xstate;
925167465Smp    *state->dest = state->val;
926167465Smp}
927167465Smp
92859243Sobrienint
929167465Smptellmewhat(struct wordent *lexp, Char **str)
93059243Sobrien{
931167465Smp    struct tellmewhat_s0_cleanup s0;
932145479Smp    int i;
933167465Smp    const struct biltins *bptr;
934145479Smp    struct wordent *sp = lexp->next;
935145479Smp    int    aliased = 0, found;
936167465Smp    Char   *s1, *s2, *cmd;
93759243Sobrien    Char    qc;
93859243Sobrien
93959243Sobrien    if (adrof1(sp->word, &aliases)) {
94059243Sobrien	alias(lexp);
94159243Sobrien	sp = lexp->next;
94259243Sobrien	aliased = 1;
94359243Sobrien    }
94459243Sobrien
945167465Smp    s0.dest = &sp->word;	/* to get the memory freeing right... */
946167465Smp    s0.val = sp->word;
947167465Smp    cleanup_push(&s0, tellmewhat_s0_cleanup);
94859243Sobrien
94959243Sobrien    /* handle quoted alias hack */
95059243Sobrien    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
95159243Sobrien	(sp->word)++;
95259243Sobrien
95359243Sobrien    /* do quoting, if it hasn't been done */
95459243Sobrien    s1 = s2 = sp->word;
95559243Sobrien    while (*s2)
95659243Sobrien	switch (*s2) {
95759243Sobrien	case '\'':
95859243Sobrien	case '"':
95959243Sobrien	    qc = *s2++;
96059243Sobrien	    while (*s2 && *s2 != qc)
96159243Sobrien		*s1++ = *s2++ | QUOTE;
96259243Sobrien	    if (*s2)
96359243Sobrien		s2++;
96459243Sobrien	    break;
96559243Sobrien	case '\\':
96659243Sobrien	    if (*++s2)
96759243Sobrien		*s1++ = *s2++ | QUOTE;
96859243Sobrien	    break;
96959243Sobrien	default:
97059243Sobrien	    *s1++ = *s2++;
97159243Sobrien	}
97259243Sobrien    *s1 = '\0';
97359243Sobrien
97459243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
97559243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
97659243Sobrien	    if (str == NULL) {
97759243Sobrien		if (aliased)
97859243Sobrien		    prlex(lexp);
97959243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
98059243Sobrien			      sp->word);
98159243Sobrien		flush();
98259243Sobrien	    }
983167465Smp	    else
984167465Smp		*str = Strsave(sp->word);
985167465Smp	    cleanup_until(&s0);
98659243Sobrien	    return TRUE;
98759243Sobrien	}
98859243Sobrien    }
98969408Sache#ifdef WINNT_NATIVE
99059243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
99159243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
99259243Sobrien	    if (str == NULL) {
99359243Sobrien		if (aliased)
99459243Sobrien		    prlex(lexp);
99559243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
99659243Sobrien			      sp->word);
99759243Sobrien		flush();
99859243Sobrien	    }
999167465Smp	    else
1000167465Smp		*str = Strsave(sp->word);
1001167465Smp	    cleanup_until(&s0);
100259243Sobrien	    return TRUE;
100359243Sobrien	}
100459243Sobrien    }
100569408Sache#endif /* WINNT_NATIVE*/
100659243Sobrien
100759243Sobrien    sp->word = cmd = globone(sp->word, G_IGNORE);
1008167465Smp    cleanup_push(cmd, xfree);
100959243Sobrien
101059243Sobrien    if ((i = iscommand(sp->word)) != 0) {
1011145479Smp	Char **pv;
1012145479Smp	struct varent *v;
1013145479Smp	int    slash = any(short2str(sp->word), '/');
101459243Sobrien
101559243Sobrien	v = adrof(STRpath);
1016100616Smp	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
101759243Sobrien	    pv = justabs;
101859243Sobrien	else
101959243Sobrien	    pv = v->vec;
102059243Sobrien
1021167465Smp	pv += i - 1;
102259243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
102359243Sobrien	    if (!slash) {
102459243Sobrien		sp->word = Strspl(STRdotsl, sp->word);
1025167465Smp		cleanup_push(sp->word, xfree);
102659243Sobrien		prlex(lexp);
1027167465Smp		cleanup_until(sp->word);
102859243Sobrien	    }
102959243Sobrien	    else
103059243Sobrien		prlex(lexp);
103159243Sobrien	}
103259243Sobrien	else {
103359243Sobrien	    s1 = Strspl(*pv, STRslash);
103459243Sobrien	    sp->word = Strspl(s1, sp->word);
1035167465Smp	    xfree(s1);
1036167465Smp	    cleanup_push(sp->word, xfree);
103759243Sobrien	    if (str == NULL)
103859243Sobrien		prlex(lexp);
103959243Sobrien	    else
1040167465Smp		*str = Strsave(sp->word);
1041167465Smp	    cleanup_until(sp->word);
104259243Sobrien	}
104359243Sobrien	found = 1;
104459243Sobrien    }
104559243Sobrien    else {
104659243Sobrien	if (str == NULL) {
104759243Sobrien	    if (aliased)
104859243Sobrien		prlex(lexp);
104959243Sobrien	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
105059243Sobrien	    flush();
105159243Sobrien	}
105259243Sobrien	else
1053167465Smp	    *str = Strsave(sp->word);
105459243Sobrien	found = 0;
105559243Sobrien    }
1056167465Smp    cleanup_until(&s0);
105759243Sobrien    return found;
105859243Sobrien}
105959243Sobrien
106059243Sobrien/*
106159243Sobrien * Builtin to look at and list all places a command may be defined:
106259243Sobrien * aliases, shell builtins, and the path.
106359243Sobrien *
106459243Sobrien * Marc Horowitz <marc@mit.edu>
106559243Sobrien * MIT Student Information Processing Board
106659243Sobrien */
106759243Sobrien
106859243Sobrien/*ARGSUSED*/
106959243Sobrienvoid
1070167465Smpdowhere(Char **v, struct command *c)
107159243Sobrien{
107259243Sobrien    int found = 1;
107359243Sobrien    USE(c);
1074316958Sdchagin
1075316958Sdchagin    if (adrof(STRautorehash))
1076316958Sdchagin	dohash(NULL, NULL);
107759243Sobrien    for (v++; *v; v++)
107859243Sobrien	found &= find_cmd(*v, 1);
107959243Sobrien    /* Make status nonzero if any command is not found. */
108059243Sobrien    if (!found)
1081167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
108259243Sobrien}
108359243Sobrien
108459243Sobrienint
1085167465Smpfind_cmd(Char *cmd, int prt)
108659243Sobrien{
108759243Sobrien    struct varent *var;
1088167465Smp    const struct biltins *bptr;
108959243Sobrien    Char **pv;
109059243Sobrien    Char *sv;
1091231990Smp    int hashval, rehashed, i, ex, rval = 0;
109259243Sobrien
109359243Sobrien    if (prt && any(short2str(cmd), '/')) {
1094195609Smp	xprintf("%s", CGETS(13, 7, "where: / in command makes no sense\n"));
109559243Sobrien	return rval;
109659243Sobrien    }
109759243Sobrien
109859243Sobrien    /* first, look for an alias */
109959243Sobrien
110059243Sobrien    if (prt && adrof1(cmd, &aliases)) {
110159243Sobrien	if ((var = adrof1(cmd, &aliases)) != NULL) {
110259243Sobrien	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1103100616Smp	    if (var->vec != NULL)
1104100616Smp		blkpr(var->vec);
110559243Sobrien	    xputchar('\n');
110659243Sobrien	    rval = 1;
110759243Sobrien	}
110859243Sobrien    }
110959243Sobrien
111059243Sobrien    /* next, look for a shell builtin */
111159243Sobrien
111259243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
111359243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
111459243Sobrien	    rval = 1;
111559243Sobrien	    if (prt)
111659243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
111759243Sobrien	    else
111859243Sobrien		return rval;
111959243Sobrien	}
112059243Sobrien    }
112169408Sache#ifdef WINNT_NATIVE
112259243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
112359243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
112459243Sobrien	    rval = 1;
112559243Sobrien	    if (prt)
112659243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
112759243Sobrien	    else
112859243Sobrien		return rval;
112959243Sobrien	}
113059243Sobrien    }
113169408Sache#endif /* WINNT_NATIVE*/
113259243Sobrien
113359243Sobrien    /* last, look through the path for the command */
113459243Sobrien
113559243Sobrien    if ((var = adrof(STRpath)) == NULL)
113659243Sobrien	return rval;
113759243Sobrien
113859243Sobrien    hashval = havhash ? hashname(cmd) : 0;
113959243Sobrien
114059243Sobrien    sv = Strspl(STRslash, cmd);
1141167465Smp    cleanup_push(sv, xfree);
114259243Sobrien
1143231990Smp    rehashed = 0;
1144231990Smpretry:
1145100616Smp    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
114659243Sobrien	if (havhash && !eq(*pv, STRdot)) {
114759243Sobrien#ifdef FASTHASH
114859243Sobrien	    if (!bit(hashval, i))
114959243Sobrien		continue;
115059243Sobrien#else /* OLDHASH */
115159243Sobrien	    int hashval1 = hash(hashval, i);
115259243Sobrien	    if (!bit(xhash, hashval1))
115359243Sobrien		continue;
115459243Sobrien#endif /* FASTHASH */
115559243Sobrien	}
115659243Sobrien	ex = executable(*pv, sv, 0);
115759243Sobrien#ifdef FASTHASH
115859243Sobrien	if (!ex && (hashdebug & 2)) {
1159195609Smp	    xprintf("%s", CGETS(13, 10, "hash miss: "));
116059243Sobrien	    ex = 1;	/* Force printing */
116159243Sobrien	}
116259243Sobrien#endif /* FASTHASH */
116359243Sobrien	if (ex) {
116459243Sobrien	    rval = 1;
116559243Sobrien	    if (prt) {
116659243Sobrien		xprintf("%S/", *pv);
116759243Sobrien		xprintf("%S\n", cmd);
116859243Sobrien	    }
116959243Sobrien	    else
117059243Sobrien		return rval;
117159243Sobrien	}
117259243Sobrien    }
1173316958Sdchagin    /*
1174316958Sdchagin     * If we are printing, we are being called from dowhere() which it
1175316958Sdchagin     * has rehashed already
1176316958Sdchagin     */
1177316958Sdchagin    if (!prt && adrof(STRautorehash) && !rehashed && havhash) {
1178231990Smp	dohash(NULL, NULL);
1179231990Smp	rehashed = 1;
1180231990Smp	goto retry;
1181231990Smp    }
1182167465Smp    cleanup_until(sv);
118359243Sobrien    return rval;
118459243Sobrien}
118569408Sache#ifdef WINNT_NATIVE
118659243Sobrienint hashval_extern(cp)
118759243Sobrien	Char *cp;
118859243Sobrien{
118959243Sobrien	return havhash?hashname(cp):0;
119059243Sobrien}
119159243Sobrienint bit_extern(val,i)
119259243Sobrien	int val;
119359243Sobrien	int i;
119459243Sobrien{
119559243Sobrien	return bit(val,i);
119659243Sobrien}
119769408Sachevoid bis_extern(val,i)
119869408Sache	int val;
119969408Sache	int i;
120069408Sache{
120169408Sache	bis(val,i);
120269408Sache}
120369408Sache#endif /* WINNT_NATIVE */
120469408Sache
1205