159243Sobrien/*
259243Sobrien * sh.exec.c: Search, find, and execute a command!
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "tc.h"
3459243Sobrien#include "tw.h"
3569408Sache#ifdef WINNT_NATIVE
3659243Sobrien#include <nt.const.h>
3769408Sache#endif /*WINNT_NATIVE*/
3859243Sobrien
3959243Sobrien/*
4059243Sobrien * C shell
4159243Sobrien */
4259243Sobrien
4359243Sobrien#ifndef OLDHASH
4459243Sobrien# define FASTHASH	/* Fast hashing is the default */
4559243Sobrien#endif /* OLDHASH */
4659243Sobrien
4759243Sobrien/*
4859243Sobrien * System level search and execute of a command.
4959243Sobrien * We look in each directory for the specified command name.
5059243Sobrien * If the name contains a '/' then we execute only the full path name.
5159243Sobrien * If there is no search path then we execute only full path names.
5259243Sobrien */
5359243Sobrien
5459243Sobrien/*
5559243Sobrien * As we search for the command we note the first non-trivial error
5659243Sobrien * message for presentation to the user.  This allows us often
5759243Sobrien * to show that a file has the wrong mode/no access when the file
5859243Sobrien * is not in the last component of the search path, so we must
5959243Sobrien * go on after first detecting the error.
6059243Sobrien */
6159243Sobrienstatic char *exerr;		/* Execution error message */
6259243Sobrienstatic Char *expath;		/* Path for exerr */
6359243Sobrien
6459243Sobrien/*
6559243Sobrien * The two part hash function is designed to let texec() call the
6659243Sobrien * more expensive hashname() only once and the simple hash() several
6759243Sobrien * times (once for each path component checked).
6859243Sobrien * Byte size is assumed to be 8.
6959243Sobrien */
7059243Sobrien#define BITS_PER_BYTE	8
7159243Sobrien
7259243Sobrien#ifdef FASTHASH
7359243Sobrien/*
7459243Sobrien * xhash is an array of hash buckets which are used to hash execs.  If
7559243Sobrien * it is allocated (havhash true), then to tell if ``name'' is
76231990Smp * (possibly) present in the i'th component of the variable path, look
7759243Sobrien * at the [hashname(name)] bucket of size [hashwidth] bytes, in the [i
7859243Sobrien * mod size*8]'th bit.  The cache size is defaults to a length of 1024
7959243Sobrien * buckets, each 1 byte wide.  This implementation guarantees that
8059243Sobrien * objects n bytes wide will be aligned on n byte boundaries.
8159243Sobrien */
8259243Sobrien# define HSHMUL		241
8359243Sobrien
8459243Sobrienstatic unsigned long *xhash = NULL;
8559243Sobrienstatic unsigned int hashlength = 0, uhashlength = 0;
8659243Sobrienstatic unsigned int hashwidth = 0, uhashwidth = 0;
8759243Sobrienstatic int hashdebug = 0;
8859243Sobrien
8959243Sobrien# define hash(a, b)	(((a) * HSHMUL + (b)) % (hashlength))
9059243Sobrien# define widthof(t)	(sizeof(t) * BITS_PER_BYTE)
9159243Sobrien# define tbit(f, i, t)	(((t *) xhash)[(f)] &  \
92145479Smp			 (1UL << (i & (widthof(t) - 1))))
9359243Sobrien# define tbis(f, i, t)	(((t *) xhash)[(f)] |= \
94145479Smp			 (1UL << (i & (widthof(t) - 1))))
9559243Sobrien# define cbit(f, i)	tbit(f, i, unsigned char)
9659243Sobrien# define cbis(f, i)	tbis(f, i, unsigned char)
9759243Sobrien# define sbit(f, i)	tbit(f, i, unsigned short)
9859243Sobrien# define sbis(f, i)	tbis(f, i, unsigned short)
9959243Sobrien# define ibit(f, i)	tbit(f, i, unsigned int)
10059243Sobrien# define ibis(f, i)	tbis(f, i, unsigned int)
10159243Sobrien# define lbit(f, i)	tbit(f, i, unsigned long)
10259243Sobrien# define lbis(f, i)	tbis(f, i, unsigned long)
10359243Sobrien
10459243Sobrien# define bit(f, i) (hashwidth==sizeof(unsigned char)  ? cbit(f,i) : \
10559243Sobrien 		    ((hashwidth==sizeof(unsigned short) ? sbit(f,i) : \
10659243Sobrien		     ((hashwidth==sizeof(unsigned int)   ? ibit(f,i) : \
10759243Sobrien		     lbit(f,i))))))
10859243Sobrien# define bis(f, i) (hashwidth==sizeof(unsigned char)  ? cbis(f,i) : \
10959243Sobrien 		    ((hashwidth==sizeof(unsigned short) ? sbis(f,i) : \
11059243Sobrien		     ((hashwidth==sizeof(unsigned int)   ? ibis(f,i) : \
11159243Sobrien		     lbis(f,i))))))
11259243Sobrien#else /* OLDHASH */
11359243Sobrien/*
11459243Sobrien * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
11559243Sobrien * to hash execs.  If it is allocated (havhash true), then to tell
11659243Sobrien * whether ``name'' is (possibly) present in the i'th component
11759243Sobrien * of the variable path, you look at the bit in xhash indexed by
11859243Sobrien * hash(hashname("name"), i).  This is setup automatically
11959243Sobrien * after .login is executed, and recomputed whenever ``path'' is
12059243Sobrien * changed.
12159243Sobrien */
12259243Sobrien# define HSHSIZ		8192	/* 1k bytes */
12359243Sobrien# define HSHMASK		(HSHSIZ - 1)
12459243Sobrien# define HSHMUL		243
12559243Sobrienstatic char xhash[HSHSIZ / BITS_PER_BYTE];
12659243Sobrien
12759243Sobrien# define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
12859243Sobrien# define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
12959243Sobrien# define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
13059243Sobrien
13159243Sobrien#endif /* FASTHASH */
13259243Sobrien
13359243Sobrien#ifdef VFORK
13459243Sobrienstatic int hits, misses;
13559243Sobrien#endif /* VFORK */
13659243Sobrien
13759243Sobrien/* Dummy search path for just absolute search when no path */
13859243Sobrienstatic Char *justabs[] = {STRNULL, 0};
13959243Sobrien
140231990Smpstatic	void	pexerr		(void) __attribute__((__noreturn__));
141167465Smpstatic	void	texec		(Char *, Char **);
142167465Smpint	hashname	(Char *);
143167465Smpstatic	int 	iscommand	(Char *);
14459243Sobrien
14559243Sobrienvoid
146167465Smpdoexec(struct command *t, int do_glob)
14759243Sobrien{
148231990Smp    Char *dp, **pv, **opv, **av, *sav;
149100616Smp    struct varent *v;
150231990Smp    int slash, gflag, rehashed;
151100616Smp    int hashval, i;
15259243Sobrien    Char   *blk[2];
15359243Sobrien
15459243Sobrien    /*
15559243Sobrien     * Glob the command name. We will search $path even if this does something,
15659243Sobrien     * as in sh but not in csh.  One special case: if there is no PATH, then we
15759243Sobrien     * execute only commands which start with '/'.
15859243Sobrien     */
15959243Sobrien    blk[0] = t->t_dcom[0];
16059243Sobrien    blk[1] = 0;
161100616Smp    gflag = 0;
162100616Smp    if (do_glob)
163167465Smp	gflag = tglob(blk);
16459243Sobrien    if (gflag) {
165167465Smp	pv = globall(blk, gflag);
16659243Sobrien	if (pv == 0) {
16759243Sobrien	    setname(short2str(blk[0]));
16859243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
16959243Sobrien	}
17059243Sobrien    }
17159243Sobrien    else
17259243Sobrien	pv = saveblk(blk);
173167465Smp    cleanup_push(pv, blk_cleanup);
17459243Sobrien
17559243Sobrien    trim(pv);
17659243Sobrien
17759243Sobrien    exerr = 0;
17859243Sobrien    expath = Strsave(pv[0]);
17959243Sobrien#ifdef VFORK
18059243Sobrien    Vexpath = expath;
18159243Sobrien#endif /* VFORK */
18259243Sobrien
18359243Sobrien    v = adrof(STRpath);
184167465Smp    if (v == 0 && expath[0] != '/' && expath[0] != '.')
18559243Sobrien	pexerr();
18659243Sobrien    slash = any(short2str(expath), '/');
18759243Sobrien
18859243Sobrien    /*
18959243Sobrien     * Glob the argument list, if necessary. Otherwise trim off the quote bits.
19059243Sobrien     */
19159243Sobrien    gflag = 0;
19259243Sobrien    av = &t->t_dcom[1];
193100616Smp    if (do_glob)
194167465Smp	gflag = tglob(av);
19559243Sobrien    if (gflag) {
196167465Smp	av = globall(av, gflag);
19759243Sobrien	if (av == 0) {
19859243Sobrien	    setname(short2str(expath));
19959243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
20059243Sobrien	}
20159243Sobrien    }
20259243Sobrien    else
20359243Sobrien	av = saveblk(av);
20459243Sobrien
20559243Sobrien    blkfree(t->t_dcom);
206167465Smp    cleanup_ignore(pv);
207167465Smp    cleanup_until(pv);
20859243Sobrien    t->t_dcom = blkspl(pv, av);
209167465Smp    xfree(pv);
210167465Smp    xfree(av);
21159243Sobrien    av = t->t_dcom;
21259243Sobrien    trim(av);
21359243Sobrien
21459243Sobrien    if (*av == NULL || **av == '\0')
21559243Sobrien	pexerr();
21659243Sobrien
21759243Sobrien    xechoit(av);		/* Echo command if -x */
21859243Sobrien#ifdef CLOSE_ON_EXEC
21959243Sobrien    /*
22059243Sobrien     * Since all internal file descriptors are set to close on exec, we don't
22159243Sobrien     * need to close them explicitly here.  Just reorient ourselves for error
22259243Sobrien     * messages.
22359243Sobrien     */
22459243Sobrien    SHIN = 0;
22559243Sobrien    SHOUT = 1;
22659243Sobrien    SHDIAG = 2;
22759243Sobrien    OLDSTD = 0;
22859243Sobrien    isoutatty = isatty(SHOUT);
22959243Sobrien    isdiagatty = isatty(SHDIAG);
23059243Sobrien#else
23159243Sobrien    closech();			/* Close random fd's */
23259243Sobrien#endif
23359243Sobrien    /*
23459243Sobrien     * We must do this AFTER any possible forking (like `foo` in glob) so that
23559243Sobrien     * this shell can still do subprocesses.
23659243Sobrien     */
237167465Smp    {
238167465Smp	sigset_t set;
239167465Smp	sigemptyset(&set);
240167465Smp	sigaddset(&set, SIGINT);
241167465Smp	sigaddset(&set, SIGCHLD);
242167465Smp	sigprocmask(SIG_UNBLOCK, &set, NULL);
243167465Smp    }
244167465Smp    pintr_disabled = 0;
245167465Smp    pchild_disabled = 0;
24659243Sobrien
24759243Sobrien    /*
24859243Sobrien     * If no path, no words in path, or a / in the filename then restrict the
24959243Sobrien     * command search.
25059243Sobrien     */
251100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
252231990Smp	opv = justabs;
25359243Sobrien    else
254231990Smp	opv = v->vec;
25559243Sobrien    sav = Strspl(STRslash, *av);/* / command name for postpending */
256167465Smp#ifndef VFORK
257167465Smp    cleanup_push(sav, xfree);
258167465Smp#else /* VFORK */
25959243Sobrien    Vsav = sav;
26059243Sobrien#endif /* VFORK */
26159243Sobrien    hashval = havhash ? hashname(*av) : 0;
26259243Sobrien
263231990Smp    rehashed = 0;
264231990Smpretry:
265231990Smp    pv = opv;
26659243Sobrien    i = 0;
26759243Sobrien#ifdef VFORK
26859243Sobrien    hits++;
26959243Sobrien#endif /* VFORK */
27059243Sobrien    do {
27159243Sobrien	/*
27259243Sobrien	 * Try to save time by looking at the hash table for where this command
27359243Sobrien	 * could be.  If we are doing delayed hashing, then we put the names in
27459243Sobrien	 * one at a time, as the user enters them.  This is kinda like Korn
27559243Sobrien	 * Shell's "tracked aliases".
27659243Sobrien	 */
27759243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
27859243Sobrien#ifdef FASTHASH
27959243Sobrien	    if (!bit(hashval, i))
28059243Sobrien		goto cont;
28159243Sobrien#else /* OLDHASH */
28259243Sobrien	    int hashval1 = hash(hashval, i);
28359243Sobrien	    if (!bit(xhash, hashval1))
28459243Sobrien		goto cont;
28559243Sobrien#endif /* FASTHASH */
28659243Sobrien	}
28759243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
28859243Sobrien	    texec(*av, av);
28959243Sobrien	else {
29059243Sobrien	    dp = Strspl(*pv, sav);
291167465Smp#ifndef VFORK
292167465Smp	    cleanup_push(dp, xfree);
293167465Smp#else /* VFORK */
29459243Sobrien	    Vdp = dp;
29559243Sobrien#endif /* VFORK */
29659243Sobrien
29759243Sobrien	    texec(dp, av);
298167465Smp#ifndef VFORK
299167465Smp	    cleanup_until(dp);
300167465Smp#else /* VFORK */
30159243Sobrien	    Vdp = 0;
302167465Smp	    xfree(dp);
30359243Sobrien#endif /* VFORK */
30459243Sobrien	}
30559243Sobrien#ifdef VFORK
30659243Sobrien	misses++;
30759243Sobrien#endif /* VFORK */
30859243Sobriencont:
30959243Sobrien	pv++;
31059243Sobrien	i++;
31159243Sobrien    } while (*pv);
31259243Sobrien#ifdef VFORK
31359243Sobrien    hits--;
314167465Smp#endif /* VFORK */
315231990Smp    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
316231990Smp	dohash(NULL, NULL);
317231990Smp	rehashed = 1;
318231990Smp	goto retry;
319231990Smp    }
320167465Smp#ifndef VFORK
321167465Smp    cleanup_until(sav);
322167465Smp#else /* VFORK */
32359243Sobrien    Vsav = 0;
324167465Smp    xfree(sav);
32559243Sobrien#endif /* VFORK */
32659243Sobrien    pexerr();
32759243Sobrien}
32859243Sobrien
32959243Sobrienstatic void
330167465Smppexerr(void)
33159243Sobrien{
33259243Sobrien    /* Couldn't find the damn thing */
33359243Sobrien    if (expath) {
33459243Sobrien	setname(short2str(expath));
33559243Sobrien#ifdef VFORK
33659243Sobrien	Vexpath = 0;
33759243Sobrien#endif /* VFORK */
338167465Smp	xfree(expath);
33959243Sobrien	expath = 0;
34059243Sobrien    }
34159243Sobrien    else
34259243Sobrien	setname("");
34359243Sobrien    if (exerr)
34459243Sobrien	stderror(ERR_NAME | ERR_STRING, exerr);
34559243Sobrien    stderror(ERR_NAME | ERR_COMMAND);
34659243Sobrien}
34759243Sobrien
34859243Sobrien/*
34959243Sobrien * Execute command f, arg list t.
35059243Sobrien * Record error message if not found.
35159243Sobrien * Also do shell scripts here.
35259243Sobrien */
35359243Sobrienstatic void
354167465Smptexec(Char *sf, Char **st)
35559243Sobrien{
356145479Smp    char **t;
357145479Smp    char *f;
358145479Smp    struct varent *v;
35959243Sobrien    Char  **vp;
36059243Sobrien    Char   *lastsh[2];
36159243Sobrien    char    pref[2];
36259243Sobrien    int     fd;
36359243Sobrien    Char   *st0, **ost;
36459243Sobrien
36559243Sobrien    /* The order for the conversions is significant */
36659243Sobrien    t = short2blk(st);
36759243Sobrien    f = short2str(sf);
36859243Sobrien#ifdef VFORK
36959243Sobrien    Vt = t;
37059243Sobrien#endif /* VFORK */
37159243Sobrien    errno = 0;			/* don't use a previous error */
37259243Sobrien#ifdef apollo
37359243Sobrien    /*
37459243Sobrien     * If we try to execute an nfs mounted directory on the apollo, we
37559243Sobrien     * hang forever. So until apollo fixes that..
37659243Sobrien     */
37759243Sobrien    {
37859243Sobrien	struct stat stb;
37959243Sobrien	if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
38059243Sobrien	    errno = EISDIR;
38159243Sobrien    }
38259243Sobrien    if (errno == 0)
38359243Sobrien#endif /* apollo */
38459243Sobrien    {
38559243Sobrien#ifdef ISC_POSIX_EXEC_BUG
38659243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
38759243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
38859243Sobrien	(void) execv(f, t);
38959243Sobrien#ifdef ISC_POSIX_EXEC_BUG
39059243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
39159243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
39259243Sobrien    }
39359243Sobrien#ifdef VFORK
39459243Sobrien    Vt = 0;
39559243Sobrien#endif /* VFORK */
39659243Sobrien    blkfree((Char **) t);
39759243Sobrien    switch (errno) {
39859243Sobrien
39959243Sobrien    case ENOEXEC:
40069408Sache#ifdef WINNT_NATIVE
40159243Sobrien		nt_feed_to_cmd(f,t);
40269408Sache#endif /* WINNT_NATIVE */
40359243Sobrien	/*
40459243Sobrien	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
40559243Sobrien	 * it, don't feed it to the shell if it looks like a binary!
40659243Sobrien	 */
407167465Smp	if ((fd = xopen(f, O_RDONLY|O_LARGEFILE)) != -1) {
40859243Sobrien	    int nread;
409167465Smp	    if ((nread = xread(fd, pref, 2)) == 2) {
410145479Smp		if (!isprint((unsigned char)pref[0]) &&
411145479Smp		    (pref[0] != '\n' && pref[0] != '\t')) {
412167465Smp		    int err;
413167465Smp
414167465Smp		    err = errno;
415167465Smp		    xclose(fd);
41659243Sobrien		    /*
41759243Sobrien		     * We *know* what ENOEXEC means.
41859243Sobrien		     */
419167465Smp		    stderror(ERR_ARCH, f, strerror(err));
42059243Sobrien		}
42159243Sobrien	    }
422167465Smp	    else if (nread < 0) {
42359243Sobrien#ifdef convex
424167465Smp		int err;
425167465Smp
426167465Smp		err = errno;
427167465Smp		xclose(fd);
42859243Sobrien		/* need to print error incase the file is migrated */
429167465Smp		stderror(ERR_SYSTEM, f, strerror(err));
43059243Sobrien#endif
43159243Sobrien	    }
43259243Sobrien#ifdef _PATH_BSHELL
43359243Sobrien	    else {
43459243Sobrien		pref[0] = '#';
43559243Sobrien		pref[1] = '\0';
43659243Sobrien	    }
43759243Sobrien#endif
43859243Sobrien	}
43959243Sobrien#ifdef HASHBANG
44059243Sobrien	if (fd == -1 ||
44159243Sobrien	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
44259243Sobrien#endif /* HASHBANG */
44359243Sobrien	/*
44459243Sobrien	 * If there is an alias for shell, then put the words of the alias in
44559243Sobrien	 * front of the argument list replacing the command name. Note no
44659243Sobrien	 * interpretation of the words at this point.
44759243Sobrien	 */
44859243Sobrien	    v = adrof1(STRshell, &aliases);
449100616Smp	    if (v == NULL || v->vec == NULL) {
45059243Sobrien		vp = lastsh;
45159243Sobrien		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
45259243Sobrien		vp[1] = NULL;
45359243Sobrien#ifdef _PATH_BSHELL
45459243Sobrien		if (fd != -1
45559243Sobrien# ifndef ISC	/* Compatible with ISC's /bin/csh */
45659243Sobrien		    && pref[0] != '#'
45759243Sobrien# endif /* ISC */
45859243Sobrien		    )
45959243Sobrien		    vp[0] = STR_BSHELL;
46059243Sobrien#endif
46159243Sobrien		vp = saveblk(vp);
46259243Sobrien	    }
46359243Sobrien	    else
46459243Sobrien		vp = saveblk(v->vec);
46559243Sobrien#ifdef HASHBANG
46659243Sobrien	}
46759243Sobrien#endif /* HASHBANG */
46859243Sobrien	if (fd != -1)
469167465Smp	    xclose(fd);
47059243Sobrien
47159243Sobrien	st0 = st[0];
47259243Sobrien	st[0] = sf;
47359243Sobrien	ost = st;
47459243Sobrien	st = blkspl(vp, st);	/* Splice up the new arglst */
47559243Sobrien	ost[0] = st0;
47659243Sobrien	sf = *st;
47759243Sobrien	/* The order for the conversions is significant */
47859243Sobrien	t = short2blk(st);
47959243Sobrien	f = short2str(sf);
480167465Smp	xfree(st);
48159243Sobrien	blkfree((Char **) vp);
48259243Sobrien#ifdef VFORK
48359243Sobrien	Vt = t;
48459243Sobrien#endif /* VFORK */
48559243Sobrien#ifdef ISC_POSIX_EXEC_BUG
48659243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
48759243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
48859243Sobrien	(void) execv(f, t);
48959243Sobrien#ifdef ISC_POSIX_EXEC_BUG
49059243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
49159243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
49259243Sobrien#ifdef VFORK
49359243Sobrien	Vt = 0;
49459243Sobrien#endif /* VFORK */
49559243Sobrien	blkfree((Char **) t);
49659243Sobrien	/* The sky is falling, the sky is falling! */
49759243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
49859243Sobrien	break;
49959243Sobrien
50059243Sobrien    case ENOMEM:
50159243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
50259243Sobrien	break;
50359243Sobrien
50459243Sobrien#ifdef _IBMR2
50559243Sobrien    case 0:			/* execv fails and returns 0! */
50659243Sobrien#endif /* _IBMR2 */
50759243Sobrien    case ENOENT:
50859243Sobrien	break;
50959243Sobrien
51059243Sobrien    default:
51159243Sobrien	if (exerr == 0) {
51259243Sobrien	    exerr = strerror(errno);
513167465Smp	    xfree(expath);
51459243Sobrien	    expath = Strsave(sf);
51559243Sobrien#ifdef VFORK
51659243Sobrien	    Vexpath = expath;
51759243Sobrien#endif /* VFORK */
51859243Sobrien	}
51959243Sobrien	break;
52059243Sobrien    }
52159243Sobrien}
52259243Sobrien
523167465Smpstruct execash_state
52459243Sobrien{
525167465Smp    int saveIN, saveOUT, saveDIAG, saveSTD;
526167465Smp    int SHIN, SHOUT, SHDIAG, OLDSTD;
527167465Smp    int didfds;
52859243Sobrien#ifndef CLOSE_ON_EXEC
529167465Smp    int didcch;
530167465Smp#endif
531167465Smp    struct sigaction sigint, sigquit, sigterm;
532167465Smp};
533167465Smp
534167465Smpstatic void
535167465Smpexecash_cleanup(void *xstate)
536167465Smp{
537167465Smp    struct execash_state *state;
538167465Smp
539167465Smp    state = xstate;
540167465Smp    sigaction(SIGINT, &state->sigint, NULL);
541167465Smp    sigaction(SIGQUIT, &state->sigquit, NULL);
542167465Smp    sigaction(SIGTERM, &state->sigterm, NULL);
543167465Smp
544167465Smp    doneinp = 0;
545167465Smp#ifndef CLOSE_ON_EXEC
546167465Smp    didcch = state->didcch;
54759243Sobrien#endif /* CLOSE_ON_EXEC */
548167465Smp    didfds = state->didfds;
549167465Smp    xclose(SHIN);
550167465Smp    xclose(SHOUT);
551167465Smp    xclose(SHDIAG);
552167465Smp    xclose(OLDSTD);
553167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
554167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
555167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
556167465Smp    close_on_exec(OLDSTD = dmove(state->saveSTD, state->OLDSTD), 1);
557167465Smp}
55859243Sobrien
559167465Smp/*ARGSUSED*/
560167465Smpvoid
561167465Smpexecash(Char **t, struct command *kp)
562167465Smp{
563167465Smp    struct execash_state state;
564167465Smp
56559243Sobrien    USE(t);
56659243Sobrien    if (chkstop == 0 && setintr)
56759243Sobrien	panystop(0);
56859243Sobrien    /*
56959243Sobrien     * Hmm, we don't really want to do that now because we might
57059243Sobrien     * fail, but what is the choice
57159243Sobrien     */
57259243Sobrien    rechist(NULL, adrof(STRsavehist) != NULL);
57359243Sobrien
57459243Sobrien
575167465Smp    sigaction(SIGINT, &parintr, &state.sigint);
576167465Smp    sigaction(SIGQUIT, &parintr, &state.sigquit);
577167465Smp    sigaction(SIGTERM, &parterm, &state.sigterm);
57859243Sobrien
579167465Smp    state.didfds = didfds;
58059243Sobrien#ifndef CLOSE_ON_EXEC
581167465Smp    state.didcch = didcch;
58259243Sobrien#endif /* CLOSE_ON_EXEC */
583167465Smp    state.SHIN = SHIN;
584167465Smp    state.SHOUT = SHOUT;
585167465Smp    state.SHDIAG = SHDIAG;
586167465Smp    state.OLDSTD = OLDSTD;
58759243Sobrien
588167465Smp    (void)close_on_exec (state.saveIN = dcopy(SHIN, -1), 1);
589167465Smp    (void)close_on_exec (state.saveOUT = dcopy(SHOUT, -1), 1);
590167465Smp    (void)close_on_exec (state.saveDIAG = dcopy(SHDIAG, -1), 1);
591167465Smp    (void)close_on_exec (state.saveSTD = dcopy(OLDSTD, -1), 1);
592167465Smp
59359243Sobrien    lshift(kp->t_dcom, 1);
59459243Sobrien
595167465Smp    (void)close_on_exec (SHIN = dcopy(0, -1), 1);
596167465Smp    (void)close_on_exec (SHOUT = dcopy(1, -1), 1);
597167465Smp    (void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
59859243Sobrien#ifndef CLOSE_ON_EXEC
599167465Smp    didcch = 0;
60059243Sobrien#endif /* CLOSE_ON_EXEC */
601167465Smp    didfds = 0;
602167465Smp    cleanup_push(&state, execash_cleanup);
603167465Smp
604167465Smp    /*
605316957Sdchagin     * Decrement the shell level, if not in a subshell
606167465Smp     */
607316957Sdchagin    if (mainpid == getpid())
608316957Sdchagin	shlvl(-1);
60969408Sache#ifdef WINNT_NATIVE
610167465Smp    __nt_really_exec=1;
61169408Sache#endif /* WINNT_NATIVE */
612167465Smp    doexec(kp, 1);
61359243Sobrien
614167465Smp    cleanup_until(&state);
61559243Sobrien}
61659243Sobrien
61759243Sobrienvoid
618167465Smpxechoit(Char **t)
61959243Sobrien{
62059243Sobrien    if (adrof(STRecho)) {
621100616Smp	int odidfds = didfds;
62259243Sobrien	flush();
62359243Sobrien	haderr = 1;
624100616Smp	didfds = 0;
62559243Sobrien	blkpr(t), xputchar('\n');
626100616Smp	flush();
627100616Smp	didfds = odidfds;
62859243Sobrien	haderr = 0;
62959243Sobrien    }
63059243Sobrien}
63159243Sobrien
63259243Sobrien/*ARGSUSED*/
63359243Sobrienvoid
634167465Smpdohash(Char **vv, struct command *c)
63559243Sobrien{
63659243Sobrien#ifdef COMMENT
63759243Sobrien    struct stat stb;
63859243Sobrien#endif
63959243Sobrien    DIR    *dirp;
640145479Smp    struct dirent *dp;
64159243Sobrien    int     i = 0;
64259243Sobrien    struct varent *v = adrof(STRpath);
64359243Sobrien    Char  **pv;
64459243Sobrien    int hashval;
64569408Sache#ifdef WINNT_NATIVE
64659243Sobrien    int is_windir; /* check if it is the windows directory */
64759243Sobrien    USE(hashval);
64869408Sache#endif /* WINNT_NATIVE */
64959243Sobrien
65059243Sobrien    USE(c);
65159243Sobrien#ifdef FASTHASH
65259243Sobrien    if (vv && vv[1]) {
65359243Sobrien        uhashlength = atoi(short2str(vv[1]));
65459243Sobrien        if (vv[2]) {
65559243Sobrien	    uhashwidth = atoi(short2str(vv[2]));
65659243Sobrien	    if ((uhashwidth != sizeof(unsigned char)) &&
65759243Sobrien	        (uhashwidth != sizeof(unsigned short)) &&
65859243Sobrien	        (uhashwidth != sizeof(unsigned long)))
65959243Sobrien	        uhashwidth = 0;
66059243Sobrien	    if (vv[3])
66159243Sobrien		hashdebug = atoi(short2str(vv[3]));
66259243Sobrien        }
66359243Sobrien    }
66459243Sobrien
66559243Sobrien    if (uhashwidth)
66659243Sobrien	hashwidth = uhashwidth;
66759243Sobrien    else {
66859243Sobrien	hashwidth = 0;
66969408Sache	if (v == NULL)
67069408Sache	    return;
671100616Smp	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
67259243Sobrien	    continue;
67359243Sobrien	if (hashwidth <= widthof(unsigned char))
67459243Sobrien	    hashwidth = sizeof(unsigned char);
67559243Sobrien	else if (hashwidth <= widthof(unsigned short))
67659243Sobrien	    hashwidth = sizeof(unsigned short);
67759243Sobrien	else if (hashwidth <= widthof(unsigned int))
67859243Sobrien	    hashwidth = sizeof(unsigned int);
67959243Sobrien	else
68059243Sobrien	    hashwidth = sizeof(unsigned long);
68159243Sobrien    }
68259243Sobrien
68359243Sobrien    if (uhashlength)
68459243Sobrien	hashlength = uhashlength;
68559243Sobrien    else
68659243Sobrien        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
687167465Smp
688167465Smp    xfree(xhash);
689167465Smp    xhash = xcalloc(hashlength * hashwidth, 1);
69059243Sobrien#endif /* FASTHASH */
69159243Sobrien
69259243Sobrien    (void) getusername(NULL);	/* flush the tilde cashe */
69359243Sobrien    tw_cmd_free();
69459243Sobrien    havhash = 1;
69559243Sobrien    if (v == NULL)
69659243Sobrien	return;
697100616Smp    for (pv = v->vec; pv && *pv; pv++, i++) {
69859243Sobrien	if (!ABSOLUTEP(pv[0]))
69959243Sobrien	    continue;
70059243Sobrien	dirp = opendir(short2str(*pv));
70159243Sobrien	if (dirp == NULL)
70259243Sobrien	    continue;
703167465Smp	cleanup_push(dirp, opendir_cleanup);
70459243Sobrien#ifdef COMMENT			/* this isn't needed.  opendir won't open
70559243Sobrien				 * non-dirs */
70659243Sobrien	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
707167465Smp	    cleanup_until(dirp);
70859243Sobrien	    continue;
70959243Sobrien	}
71059243Sobrien#endif
71169408Sache#ifdef WINNT_NATIVE
71259243Sobrien	is_windir = nt_check_if_windir(short2str(*pv));
71369408Sache#endif /* WINNT_NATIVE */
71459243Sobrien	while ((dp = readdir(dirp)) != NULL) {
71559243Sobrien	    if (dp->d_ino == 0)
71659243Sobrien		continue;
71759243Sobrien	    if (dp->d_name[0] == '.' &&
71859243Sobrien		(dp->d_name[1] == '\0' ||
71959243Sobrien		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
72059243Sobrien		continue;
72169408Sache#ifdef WINNT_NATIVE
72259243Sobrien	    nt_check_name_and_hash(is_windir, dp->d_name, i);
72369408Sache#else /* !WINNT_NATIVE*/
72469408Sache#if defined(_UWIN) || defined(__CYGWIN__)
72559243Sobrien	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
72659243Sobrien	     * the file with the .exe, .com, .bat extension
727231990Smp	     *
728231990Smp	     * Same for Cygwin, but only for .exe and .com extension.
72959243Sobrien	     */
73059243Sobrien	    {
731167465Smp		ssize_t	ext = strlen(dp->d_name) - 4;
732131962Smp		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
733231990Smp#ifndef __CYGWIN__
734131962Smp				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
735231990Smp#endif
736167465Smp				  strcasecmp(&dp->d_name[ext], ".com") == 0)) {
737167465Smp#ifdef __CYGWIN__
738167465Smp		    /* Also store the variation with extension. */
739167465Smp		    hashval = hashname(str2short(dp->d_name));
740167465Smp		    bis(hashval, i);
741131962Smp#endif /* __CYGWIN__ */
742167465Smp		    dp->d_name[ext] = '\0';
743167465Smp		}
74459243Sobrien	    }
74569408Sache#endif /* _UWIN || __CYGWIN__ */
74659243Sobrien# ifdef FASTHASH
74759243Sobrien	    hashval = hashname(str2short(dp->d_name));
74859243Sobrien	    bis(hashval, i);
74959243Sobrien	    if (hashdebug & 1)
75059243Sobrien	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
75159243Sobrien		        hashname(str2short(dp->d_name)), i, dp->d_name);
75259243Sobrien# else /* OLD HASH */
75359243Sobrien	    hashval = hash(hashname(str2short(dp->d_name)), i);
75459243Sobrien	    bis(xhash, hashval);
75559243Sobrien# endif /* FASTHASH */
75659243Sobrien	    /* tw_add_comm_name (dp->d_name); */
75769408Sache#endif /* WINNT_NATIVE */
75859243Sobrien	}
759167465Smp	cleanup_until(dirp);
76059243Sobrien    }
76159243Sobrien}
76259243Sobrien
76359243Sobrien/*ARGSUSED*/
76459243Sobrienvoid
765167465Smpdounhash(Char **v, struct command *c)
76659243Sobrien{
76759243Sobrien    USE(c);
76859243Sobrien    USE(v);
76959243Sobrien    havhash = 0;
77059243Sobrien#ifdef FASTHASH
771167465Smp    xfree(xhash);
772167465Smp    xhash = NULL;
77359243Sobrien#endif /* FASTHASH */
77459243Sobrien}
77559243Sobrien
77659243Sobrien/*ARGSUSED*/
77759243Sobrienvoid
778167465Smphashstat(Char **v, struct command *c)
77959243Sobrien{
78059243Sobrien    USE(c);
78159243Sobrien    USE(v);
78259243Sobrien#ifdef FASTHASH
78359243Sobrien   if (havhash && hashlength && hashwidth)
78459243Sobrien      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
78559243Sobrien	      hashlength, hashwidth*8);
78659243Sobrien   if (hashdebug)
78759243Sobrien      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
78859243Sobrien#endif /* FASTHASH */
78959243Sobrien#ifdef VFORK
79059243Sobrien   if (hits + misses)
79159243Sobrien      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
79259243Sobrien	      hits, misses, 100 * hits / (hits + misses));
79359243Sobrien#endif
79459243Sobrien}
79559243Sobrien
79659243Sobrien
79759243Sobrien/*
79859243Sobrien * Hash a command name.
79959243Sobrien */
80069408Sacheint
801167465Smphashname(Char *cp)
80259243Sobrien{
803145479Smp    unsigned long h;
80459243Sobrien
80559243Sobrien    for (h = 0; *cp; cp++)
80659243Sobrien	h = hash(h, *cp);
80759243Sobrien    return ((int) h);
80859243Sobrien}
80959243Sobrien
81059243Sobrienstatic int
811167465Smpiscommand(Char *name)
81259243Sobrien{
813231990Smp    Char **opv, **pv;
814145479Smp    Char *sav;
815145479Smp    struct varent *v;
816145479Smp    int slash = any(short2str(name), '/');
817231990Smp    int hashval, rehashed, i;
81859243Sobrien
81959243Sobrien    v = adrof(STRpath);
820100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
821231990Smp	opv = justabs;
82259243Sobrien    else
823231990Smp	opv = v->vec;
82459243Sobrien    sav = Strspl(STRslash, name);	/* / command name for postpending */
82559243Sobrien    hashval = havhash ? hashname(name) : 0;
826231990Smp
827231990Smp    rehashed = 0;
828231990Smpretry:
829231990Smp    pv = opv;
83059243Sobrien    i = 0;
83159243Sobrien    do {
83259243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
83359243Sobrien#ifdef FASTHASH
83459243Sobrien	    if (!bit(hashval, i))
83559243Sobrien		goto cont;
83659243Sobrien#else /* OLDHASH */
83759243Sobrien	    int hashval1 = hash(hashval, i);
83859243Sobrien	    if (!bit(xhash, hashval1))
83959243Sobrien		goto cont;
84059243Sobrien#endif /* FASTHASH */
84159243Sobrien	}
84259243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
84359243Sobrien	    if (executable(NULL, name, 0)) {
844167465Smp		xfree(sav);
84559243Sobrien		return i + 1;
84659243Sobrien	    }
84759243Sobrien	}
84859243Sobrien	else {
84959243Sobrien	    if (executable(*pv, sav, 0)) {
850167465Smp		xfree(sav);
85159243Sobrien		return i + 1;
85259243Sobrien	    }
85359243Sobrien	}
85459243Sobriencont:
85559243Sobrien	pv++;
85659243Sobrien	i++;
85759243Sobrien    } while (*pv);
858231990Smp    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
859231990Smp	dohash(NULL, NULL);
860231990Smp	rehashed = 1;
861231990Smp	goto retry;
862231990Smp    }
863167465Smp    xfree(sav);
86459243Sobrien    return 0;
86559243Sobrien}
86659243Sobrien
86759243Sobrien/* Also by:
86859243Sobrien *  Andreas Luik <luik@isaak.isa.de>
86959243Sobrien *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
87059243Sobrien *  Azenberstr. 35
87159243Sobrien *  D-7000 Stuttgart 1
87259243Sobrien *  West-Germany
87359243Sobrien * is the executable() routine below and changes to iscommand().
87459243Sobrien * Thanks again!!
87559243Sobrien */
87659243Sobrien
87769408Sache#ifndef WINNT_NATIVE
87859243Sobrien/*
87959243Sobrien * executable() examines the pathname obtained by concatenating dir and name
88059243Sobrien * (dir may be NULL), and returns 1 either if it is executable by us, or
88159243Sobrien * if dir_ok is set and the pathname refers to a directory.
88259243Sobrien * This is a bit kludgy, but in the name of optimization...
88359243Sobrien */
88459243Sobrienint
885167465Smpexecutable(const Char *dir, const Char *name, int dir_ok)
88659243Sobrien{
88759243Sobrien    struct stat stbuf;
88859243Sobrien    char   *strname;
88959243Sobrien
89059243Sobrien    if (dir && *dir) {
891167465Smp	Char *path;
892167465Smp
893167465Smp	path = Strspl(dir, name);
89459243Sobrien	strname = short2str(path);
895167465Smp	xfree(path);
89659243Sobrien    }
89759243Sobrien    else
89859243Sobrien	strname = short2str(name);
899167465Smp
90059243Sobrien    return (stat(strname, &stbuf) != -1 &&
90159243Sobrien	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
90259243Sobrien	     (S_ISREG(stbuf.st_mode) &&
90359243Sobrien    /* save time by not calling access() in the hopeless case */
90459243Sobrien	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
90559243Sobrien	      access(strname, X_OK) == 0
90659243Sobrien	)));
90759243Sobrien}
90869408Sache#endif /*!WINNT_NATIVE*/
90959243Sobrien
910167465Smpstruct tellmewhat_s0_cleanup
911167465Smp{
912167465Smp    Char **dest, *val;
913167465Smp};
914167465Smp
915167465Smpstatic void
916167465Smptellmewhat_s0_cleanup(void *xstate)
917167465Smp{
918167465Smp    struct tellmewhat_s0_cleanup *state;
919167465Smp
920167465Smp    state = xstate;
921167465Smp    *state->dest = state->val;
922167465Smp}
923167465Smp
92459243Sobrienint
925167465Smptellmewhat(struct wordent *lexp, Char **str)
92659243Sobrien{
927167465Smp    struct tellmewhat_s0_cleanup s0;
928145479Smp    int i;
929167465Smp    const struct biltins *bptr;
930145479Smp    struct wordent *sp = lexp->next;
931145479Smp    int    aliased = 0, found;
932167465Smp    Char   *s1, *s2, *cmd;
93359243Sobrien    Char    qc;
93459243Sobrien
93559243Sobrien    if (adrof1(sp->word, &aliases)) {
93659243Sobrien	alias(lexp);
93759243Sobrien	sp = lexp->next;
93859243Sobrien	aliased = 1;
93959243Sobrien    }
94059243Sobrien
941167465Smp    s0.dest = &sp->word;	/* to get the memory freeing right... */
942167465Smp    s0.val = sp->word;
943167465Smp    cleanup_push(&s0, tellmewhat_s0_cleanup);
94459243Sobrien
94559243Sobrien    /* handle quoted alias hack */
94659243Sobrien    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
94759243Sobrien	(sp->word)++;
94859243Sobrien
94959243Sobrien    /* do quoting, if it hasn't been done */
95059243Sobrien    s1 = s2 = sp->word;
95159243Sobrien    while (*s2)
95259243Sobrien	switch (*s2) {
95359243Sobrien	case '\'':
95459243Sobrien	case '"':
95559243Sobrien	    qc = *s2++;
95659243Sobrien	    while (*s2 && *s2 != qc)
95759243Sobrien		*s1++ = *s2++ | QUOTE;
95859243Sobrien	    if (*s2)
95959243Sobrien		s2++;
96059243Sobrien	    break;
96159243Sobrien	case '\\':
96259243Sobrien	    if (*++s2)
96359243Sobrien		*s1++ = *s2++ | QUOTE;
96459243Sobrien	    break;
96559243Sobrien	default:
96659243Sobrien	    *s1++ = *s2++;
96759243Sobrien	}
96859243Sobrien    *s1 = '\0';
96959243Sobrien
97059243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
97159243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
97259243Sobrien	    if (str == NULL) {
97359243Sobrien		if (aliased)
97459243Sobrien		    prlex(lexp);
97559243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
97659243Sobrien			      sp->word);
97759243Sobrien		flush();
97859243Sobrien	    }
979167465Smp	    else
980167465Smp		*str = Strsave(sp->word);
981167465Smp	    cleanup_until(&s0);
98259243Sobrien	    return TRUE;
98359243Sobrien	}
98459243Sobrien    }
98569408Sache#ifdef WINNT_NATIVE
98659243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
98759243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
98859243Sobrien	    if (str == NULL) {
98959243Sobrien		if (aliased)
99059243Sobrien		    prlex(lexp);
99159243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
99259243Sobrien			      sp->word);
99359243Sobrien		flush();
99459243Sobrien	    }
995167465Smp	    else
996167465Smp		*str = Strsave(sp->word);
997167465Smp	    cleanup_until(&s0);
99859243Sobrien	    return TRUE;
99959243Sobrien	}
100059243Sobrien    }
100169408Sache#endif /* WINNT_NATIVE*/
100259243Sobrien
100359243Sobrien    sp->word = cmd = globone(sp->word, G_IGNORE);
1004167465Smp    cleanup_push(cmd, xfree);
100559243Sobrien
100659243Sobrien    if ((i = iscommand(sp->word)) != 0) {
1007145479Smp	Char **pv;
1008145479Smp	struct varent *v;
1009145479Smp	int    slash = any(short2str(sp->word), '/');
101059243Sobrien
101159243Sobrien	v = adrof(STRpath);
1012100616Smp	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
101359243Sobrien	    pv = justabs;
101459243Sobrien	else
101559243Sobrien	    pv = v->vec;
101659243Sobrien
1017167465Smp	pv += i - 1;
101859243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
101959243Sobrien	    if (!slash) {
102059243Sobrien		sp->word = Strspl(STRdotsl, sp->word);
1021167465Smp		cleanup_push(sp->word, xfree);
102259243Sobrien		prlex(lexp);
1023167465Smp		cleanup_until(sp->word);
102459243Sobrien	    }
102559243Sobrien	    else
102659243Sobrien		prlex(lexp);
102759243Sobrien	}
102859243Sobrien	else {
102959243Sobrien	    s1 = Strspl(*pv, STRslash);
103059243Sobrien	    sp->word = Strspl(s1, sp->word);
1031167465Smp	    xfree(s1);
1032167465Smp	    cleanup_push(sp->word, xfree);
103359243Sobrien	    if (str == NULL)
103459243Sobrien		prlex(lexp);
103559243Sobrien	    else
1036167465Smp		*str = Strsave(sp->word);
1037167465Smp	    cleanup_until(sp->word);
103859243Sobrien	}
103959243Sobrien	found = 1;
104059243Sobrien    }
104159243Sobrien    else {
104259243Sobrien	if (str == NULL) {
104359243Sobrien	    if (aliased)
104459243Sobrien		prlex(lexp);
104559243Sobrien	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
104659243Sobrien	    flush();
104759243Sobrien	}
104859243Sobrien	else
1049167465Smp	    *str = Strsave(sp->word);
105059243Sobrien	found = 0;
105159243Sobrien    }
1052167465Smp    cleanup_until(&s0);
105359243Sobrien    return found;
105459243Sobrien}
105559243Sobrien
105659243Sobrien/*
105759243Sobrien * Builtin to look at and list all places a command may be defined:
105859243Sobrien * aliases, shell builtins, and the path.
105959243Sobrien *
106059243Sobrien * Marc Horowitz <marc@mit.edu>
106159243Sobrien * MIT Student Information Processing Board
106259243Sobrien */
106359243Sobrien
106459243Sobrien/*ARGSUSED*/
106559243Sobrienvoid
1066167465Smpdowhere(Char **v, struct command *c)
106759243Sobrien{
106859243Sobrien    int found = 1;
106959243Sobrien    USE(c);
1070316957Sdchagin
1071316957Sdchagin    if (adrof(STRautorehash))
1072316957Sdchagin	dohash(NULL, NULL);
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    }
1169316957Sdchagin    /*
1170316957Sdchagin     * If we are printing, we are being called from dowhere() which it
1171316957Sdchagin     * has rehashed already
1172316957Sdchagin     */
1173316957Sdchagin    if (!prt && adrof(STRautorehash) && !rehashed && havhash) {
1174231990Smp	dohash(NULL, NULL);
1175231990Smp	rehashed = 1;
1176231990Smp	goto retry;
1177231990Smp    }
1178167465Smp    cleanup_until(sv);
117959243Sobrien    return rval;
118059243Sobrien}
118169408Sache#ifdef WINNT_NATIVE
118259243Sobrienint hashval_extern(cp)
118359243Sobrien	Char *cp;
118459243Sobrien{
118559243Sobrien	return havhash?hashname(cp):0;
118659243Sobrien}
118759243Sobrienint bit_extern(val,i)
118859243Sobrien	int val;
118959243Sobrien	int i;
119059243Sobrien{
119159243Sobrien	return bit(val,i);
119259243Sobrien}
119369408Sachevoid bis_extern(val,i)
119469408Sache	int val;
119569408Sache	int i;
119669408Sache{
119769408Sache	bis(val,i);
119869408Sache}
119969408Sache#endif /* WINNT_NATIVE */
120069408Sache
1201