sh.exec.c revision 167465
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.exec.c,v 3.73 2006/08/24 20:56:31 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
35167465SmpRCSID("$tcsh: sh.exec.c,v 3.73 2006/08/24 20:56:31 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
8059243Sobrien * (possibly) presend 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
144167465Smpstatic	void	pexerr		(void);
145167465Smpstatic	void	texec		(Char *, Char **);
146167465Smpint	hashname	(Char *);
147167465Smpstatic	int 	iscommand	(Char *);
14859243Sobrien
14959243Sobrienvoid
150167465Smpdoexec(struct command *t, int do_glob)
15159243Sobrien{
152100616Smp    Char *dp, **pv, **av, *sav;
153100616Smp    struct varent *v;
154167465Smp    int slash, gflag;
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)
25659243Sobrien	pv = justabs;
25759243Sobrien    else
25859243Sobrien	pv = 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
26759243Sobrien    i = 0;
26859243Sobrien#ifdef VFORK
26959243Sobrien    hits++;
27059243Sobrien#endif /* VFORK */
27159243Sobrien    do {
27259243Sobrien	/*
27359243Sobrien	 * Try to save time by looking at the hash table for where this command
27459243Sobrien	 * could be.  If we are doing delayed hashing, then we put the names in
27559243Sobrien	 * one at a time, as the user enters them.  This is kinda like Korn
27659243Sobrien	 * Shell's "tracked aliases".
27759243Sobrien	 */
27859243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
27959243Sobrien#ifdef FASTHASH
28059243Sobrien	    if (!bit(hashval, i))
28159243Sobrien		goto cont;
28259243Sobrien#else /* OLDHASH */
28359243Sobrien	    int hashval1 = hash(hashval, i);
28459243Sobrien	    if (!bit(xhash, hashval1))
28559243Sobrien		goto cont;
28659243Sobrien#endif /* FASTHASH */
28759243Sobrien	}
28859243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
28959243Sobrien	    texec(*av, av);
29059243Sobrien	else {
29159243Sobrien	    dp = Strspl(*pv, sav);
292167465Smp#ifndef VFORK
293167465Smp	    cleanup_push(dp, xfree);
294167465Smp#else /* VFORK */
29559243Sobrien	    Vdp = dp;
29659243Sobrien#endif /* VFORK */
29759243Sobrien
29859243Sobrien	    texec(dp, av);
299167465Smp#ifndef VFORK
300167465Smp	    cleanup_until(dp);
301167465Smp#else /* VFORK */
30259243Sobrien	    Vdp = 0;
303167465Smp	    xfree(dp);
30459243Sobrien#endif /* VFORK */
30559243Sobrien	}
30659243Sobrien#ifdef VFORK
30759243Sobrien	misses++;
30859243Sobrien#endif /* VFORK */
30959243Sobriencont:
31059243Sobrien	pv++;
31159243Sobrien	i++;
31259243Sobrien    } while (*pv);
31359243Sobrien#ifdef VFORK
31459243Sobrien    hits--;
315167465Smp#endif /* VFORK */
316167465Smp#ifndef VFORK
317167465Smp    cleanup_until(sav);
318167465Smp#else /* VFORK */
31959243Sobrien    Vsav = 0;
320167465Smp    xfree(sav);
32159243Sobrien#endif /* VFORK */
32259243Sobrien    pexerr();
32359243Sobrien}
32459243Sobrien
32559243Sobrienstatic void
326167465Smppexerr(void)
32759243Sobrien{
32859243Sobrien    /* Couldn't find the damn thing */
32959243Sobrien    if (expath) {
33059243Sobrien	setname(short2str(expath));
33159243Sobrien#ifdef VFORK
33259243Sobrien	Vexpath = 0;
33359243Sobrien#endif /* VFORK */
334167465Smp	xfree(expath);
33559243Sobrien	expath = 0;
33659243Sobrien    }
33759243Sobrien    else
33859243Sobrien	setname("");
33959243Sobrien    if (exerr)
34059243Sobrien	stderror(ERR_NAME | ERR_STRING, exerr);
34159243Sobrien    stderror(ERR_NAME | ERR_COMMAND);
34259243Sobrien}
34359243Sobrien
34459243Sobrien/*
34559243Sobrien * Execute command f, arg list t.
34659243Sobrien * Record error message if not found.
34759243Sobrien * Also do shell scripts here.
34859243Sobrien */
34959243Sobrienstatic void
350167465Smptexec(Char *sf, Char **st)
35159243Sobrien{
352145479Smp    char **t;
353145479Smp    char *f;
354145479Smp    struct varent *v;
35559243Sobrien    Char  **vp;
35659243Sobrien    Char   *lastsh[2];
35759243Sobrien    char    pref[2];
35859243Sobrien    int     fd;
35959243Sobrien    Char   *st0, **ost;
36059243Sobrien
36159243Sobrien    /* The order for the conversions is significant */
36259243Sobrien    t = short2blk(st);
36359243Sobrien    f = short2str(sf);
36459243Sobrien#ifdef VFORK
36559243Sobrien    Vt = t;
36659243Sobrien#endif /* VFORK */
36759243Sobrien    errno = 0;			/* don't use a previous error */
36859243Sobrien#ifdef apollo
36959243Sobrien    /*
37059243Sobrien     * If we try to execute an nfs mounted directory on the apollo, we
37159243Sobrien     * hang forever. So until apollo fixes that..
37259243Sobrien     */
37359243Sobrien    {
37459243Sobrien	struct stat stb;
37559243Sobrien	if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
37659243Sobrien	    errno = EISDIR;
37759243Sobrien    }
37859243Sobrien    if (errno == 0)
37959243Sobrien#endif /* apollo */
38059243Sobrien    {
38159243Sobrien#ifdef ISC_POSIX_EXEC_BUG
38259243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
38359243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
38459243Sobrien	(void) execv(f, t);
38559243Sobrien#ifdef ISC_POSIX_EXEC_BUG
38659243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
38759243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
38859243Sobrien    }
38959243Sobrien#ifdef VFORK
39059243Sobrien    Vt = 0;
39159243Sobrien#endif /* VFORK */
39259243Sobrien    blkfree((Char **) t);
39359243Sobrien    switch (errno) {
39459243Sobrien
39559243Sobrien    case ENOEXEC:
39669408Sache#ifdef WINNT_NATIVE
39759243Sobrien		nt_feed_to_cmd(f,t);
39869408Sache#endif /* WINNT_NATIVE */
39959243Sobrien	/*
40059243Sobrien	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
40159243Sobrien	 * it, don't feed it to the shell if it looks like a binary!
40259243Sobrien	 */
403167465Smp	if ((fd = xopen(f, O_RDONLY|O_LARGEFILE)) != -1) {
40459243Sobrien	    int nread;
405131962Smp#ifdef O_TEXT
406131962Smp	    setmode(fd, O_TEXT);
407131962Smp#endif
408167465Smp	    if ((nread = xread(fd, pref, 2)) == 2) {
409145479Smp		if (!isprint((unsigned char)pref[0]) &&
410145479Smp		    (pref[0] != '\n' && pref[0] != '\t')) {
411167465Smp		    int err;
412167465Smp
413167465Smp		    err = errno;
414167465Smp		    xclose(fd);
41559243Sobrien		    /*
41659243Sobrien		     * We *know* what ENOEXEC means.
41759243Sobrien		     */
418167465Smp		    stderror(ERR_ARCH, f, strerror(err));
41959243Sobrien		}
42059243Sobrien	    }
421167465Smp	    else if (nread < 0) {
42259243Sobrien#ifdef convex
423167465Smp		int err;
424167465Smp
425167465Smp		err = errno;
426167465Smp		xclose(fd);
42759243Sobrien		/* need to print error incase the file is migrated */
428167465Smp		stderror(ERR_SYSTEM, f, strerror(err));
42959243Sobrien#endif
43059243Sobrien	    }
43159243Sobrien#ifdef _PATH_BSHELL
43259243Sobrien	    else {
43359243Sobrien		pref[0] = '#';
43459243Sobrien		pref[1] = '\0';
43559243Sobrien	    }
43659243Sobrien#endif
43759243Sobrien	}
43859243Sobrien#ifdef HASHBANG
43959243Sobrien	if (fd == -1 ||
44059243Sobrien	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
44159243Sobrien#endif /* HASHBANG */
44259243Sobrien	/*
44359243Sobrien	 * If there is an alias for shell, then put the words of the alias in
44459243Sobrien	 * front of the argument list replacing the command name. Note no
44559243Sobrien	 * interpretation of the words at this point.
44659243Sobrien	 */
44759243Sobrien	    v = adrof1(STRshell, &aliases);
448100616Smp	    if (v == NULL || v->vec == NULL) {
44959243Sobrien		vp = lastsh;
45059243Sobrien		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
45159243Sobrien		vp[1] = NULL;
45259243Sobrien#ifdef _PATH_BSHELL
45359243Sobrien		if (fd != -1
45459243Sobrien# ifndef ISC	/* Compatible with ISC's /bin/csh */
45559243Sobrien		    && pref[0] != '#'
45659243Sobrien# endif /* ISC */
45759243Sobrien		    )
45859243Sobrien		    vp[0] = STR_BSHELL;
45959243Sobrien#endif
46059243Sobrien		vp = saveblk(vp);
46159243Sobrien	    }
46259243Sobrien	    else
46359243Sobrien		vp = saveblk(v->vec);
46459243Sobrien#ifdef HASHBANG
46559243Sobrien	}
46659243Sobrien#endif /* HASHBANG */
46759243Sobrien	if (fd != -1)
468167465Smp	    xclose(fd);
46959243Sobrien
47059243Sobrien	st0 = st[0];
47159243Sobrien	st[0] = sf;
47259243Sobrien	ost = st;
47359243Sobrien	st = blkspl(vp, st);	/* Splice up the new arglst */
47459243Sobrien	ost[0] = st0;
47559243Sobrien	sf = *st;
47659243Sobrien	/* The order for the conversions is significant */
47759243Sobrien	t = short2blk(st);
47859243Sobrien	f = short2str(sf);
479167465Smp	xfree(st);
48059243Sobrien	blkfree((Char **) vp);
48159243Sobrien#ifdef VFORK
48259243Sobrien	Vt = t;
48359243Sobrien#endif /* VFORK */
48459243Sobrien#ifdef ISC_POSIX_EXEC_BUG
48559243Sobrien	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
48659243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
48759243Sobrien	(void) execv(f, t);
48859243Sobrien#ifdef ISC_POSIX_EXEC_BUG
48959243Sobrien	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
49059243Sobrien#endif /* ISC_POSIX_EXEC_BUG */
49159243Sobrien#ifdef VFORK
49259243Sobrien	Vt = 0;
49359243Sobrien#endif /* VFORK */
49459243Sobrien	blkfree((Char **) t);
49559243Sobrien	/* The sky is falling, the sky is falling! */
49659243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
49759243Sobrien	break;
49859243Sobrien
49959243Sobrien    case ENOMEM:
50059243Sobrien	stderror(ERR_SYSTEM, f, strerror(errno));
50159243Sobrien	break;
50259243Sobrien
50359243Sobrien#ifdef _IBMR2
50459243Sobrien    case 0:			/* execv fails and returns 0! */
50559243Sobrien#endif /* _IBMR2 */
50659243Sobrien    case ENOENT:
50759243Sobrien	break;
50859243Sobrien
50959243Sobrien    default:
51059243Sobrien	if (exerr == 0) {
51159243Sobrien	    exerr = strerror(errno);
512167465Smp	    xfree(expath);
51359243Sobrien	    expath = Strsave(sf);
51459243Sobrien#ifdef VFORK
51559243Sobrien	    Vexpath = expath;
51659243Sobrien#endif /* VFORK */
51759243Sobrien	}
51859243Sobrien	break;
51959243Sobrien    }
52059243Sobrien}
52159243Sobrien
522167465Smpstruct execash_state
52359243Sobrien{
524167465Smp    int saveIN, saveOUT, saveDIAG, saveSTD;
525167465Smp    int SHIN, SHOUT, SHDIAG, OLDSTD;
526167465Smp    int didfds;
52759243Sobrien#ifndef CLOSE_ON_EXEC
528167465Smp    int didcch;
529167465Smp#endif
530167465Smp    struct sigaction sigint, sigquit, sigterm;
531167465Smp};
532167465Smp
533167465Smpstatic void
534167465Smpexecash_cleanup(void *xstate)
535167465Smp{
536167465Smp    struct execash_state *state;
537167465Smp
538167465Smp    state = xstate;
539167465Smp    sigaction(SIGINT, &state->sigint, NULL);
540167465Smp    sigaction(SIGQUIT, &state->sigquit, NULL);
541167465Smp    sigaction(SIGTERM, &state->sigterm, NULL);
542167465Smp
543167465Smp    doneinp = 0;
544167465Smp#ifndef CLOSE_ON_EXEC
545167465Smp    didcch = state->didcch;
54659243Sobrien#endif /* CLOSE_ON_EXEC */
547167465Smp    didfds = state->didfds;
548167465Smp    xclose(SHIN);
549167465Smp    xclose(SHOUT);
550167465Smp    xclose(SHDIAG);
551167465Smp    xclose(OLDSTD);
552167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
553167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
554167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
555167465Smp    close_on_exec(OLDSTD = dmove(state->saveSTD, state->OLDSTD), 1);
556167465Smp}
55759243Sobrien
558167465Smp/*ARGSUSED*/
559167465Smpvoid
560167465Smpexecash(Char **t, struct command *kp)
561167465Smp{
562167465Smp    struct execash_state state;
563167465Smp
56459243Sobrien    USE(t);
56559243Sobrien    if (chkstop == 0 && setintr)
56659243Sobrien	panystop(0);
56759243Sobrien    /*
56859243Sobrien     * Hmm, we don't really want to do that now because we might
56959243Sobrien     * fail, but what is the choice
57059243Sobrien     */
57159243Sobrien    rechist(NULL, adrof(STRsavehist) != NULL);
57259243Sobrien
57359243Sobrien
574167465Smp    sigaction(SIGINT, &parintr, &state.sigint);
575167465Smp    sigaction(SIGQUIT, &parintr, &state.sigquit);
576167465Smp    sigaction(SIGTERM, &parterm, &state.sigterm);
57759243Sobrien
578167465Smp    state.didfds = didfds;
57959243Sobrien#ifndef CLOSE_ON_EXEC
580167465Smp    state.didcch = didcch;
58159243Sobrien#endif /* CLOSE_ON_EXEC */
582167465Smp    state.SHIN = SHIN;
583167465Smp    state.SHOUT = SHOUT;
584167465Smp    state.SHDIAG = SHDIAG;
585167465Smp    state.OLDSTD = OLDSTD;
58659243Sobrien
587167465Smp    (void)close_on_exec (state.saveIN = dcopy(SHIN, -1), 1);
588167465Smp    (void)close_on_exec (state.saveOUT = dcopy(SHOUT, -1), 1);
589167465Smp    (void)close_on_exec (state.saveDIAG = dcopy(SHDIAG, -1), 1);
590167465Smp    (void)close_on_exec (state.saveSTD = dcopy(OLDSTD, -1), 1);
591167465Smp
59259243Sobrien    lshift(kp->t_dcom, 1);
59359243Sobrien
594167465Smp    (void)close_on_exec (SHIN = dcopy(0, -1), 1);
595167465Smp    (void)close_on_exec (SHOUT = dcopy(1, -1), 1);
596167465Smp    (void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
59759243Sobrien#ifndef CLOSE_ON_EXEC
598167465Smp    didcch = 0;
59959243Sobrien#endif /* CLOSE_ON_EXEC */
600167465Smp    didfds = 0;
601167465Smp    cleanup_push(&state, execash_cleanup);
602167465Smp
603167465Smp    /*
604167465Smp     * Decrement the shell level
605167465Smp     */
606167465Smp    shlvl(-1);
60769408Sache#ifdef WINNT_NATIVE
608167465Smp    __nt_really_exec=1;
60969408Sache#endif /* WINNT_NATIVE */
610167465Smp    doexec(kp, 1);
61159243Sobrien
612167465Smp    cleanup_until(&state);
61359243Sobrien}
61459243Sobrien
61559243Sobrienvoid
616167465Smpxechoit(Char **t)
61759243Sobrien{
61859243Sobrien    if (adrof(STRecho)) {
619100616Smp	int odidfds = didfds;
62059243Sobrien	flush();
62159243Sobrien	haderr = 1;
622100616Smp	didfds = 0;
62359243Sobrien	blkpr(t), xputchar('\n');
624100616Smp	flush();
625100616Smp	didfds = odidfds;
62659243Sobrien	haderr = 0;
62759243Sobrien    }
62859243Sobrien}
62959243Sobrien
63059243Sobrien/*ARGSUSED*/
63159243Sobrienvoid
632167465Smpdohash(Char **vv, struct command *c)
63359243Sobrien{
63459243Sobrien#ifdef COMMENT
63559243Sobrien    struct stat stb;
63659243Sobrien#endif
63759243Sobrien    DIR    *dirp;
638145479Smp    struct dirent *dp;
63959243Sobrien    int     i = 0;
64059243Sobrien    struct varent *v = adrof(STRpath);
64159243Sobrien    Char  **pv;
64259243Sobrien    int hashval;
64369408Sache#ifdef WINNT_NATIVE
64459243Sobrien    int is_windir; /* check if it is the windows directory */
64559243Sobrien    USE(hashval);
64669408Sache#endif /* WINNT_NATIVE */
64759243Sobrien
64859243Sobrien    USE(c);
64959243Sobrien#ifdef FASTHASH
65059243Sobrien    if (vv && vv[1]) {
65159243Sobrien        uhashlength = atoi(short2str(vv[1]));
65259243Sobrien        if (vv[2]) {
65359243Sobrien	    uhashwidth = atoi(short2str(vv[2]));
65459243Sobrien	    if ((uhashwidth != sizeof(unsigned char)) &&
65559243Sobrien	        (uhashwidth != sizeof(unsigned short)) &&
65659243Sobrien	        (uhashwidth != sizeof(unsigned long)))
65759243Sobrien	        uhashwidth = 0;
65859243Sobrien	    if (vv[3])
65959243Sobrien		hashdebug = atoi(short2str(vv[3]));
66059243Sobrien        }
66159243Sobrien    }
66259243Sobrien
66359243Sobrien    if (uhashwidth)
66459243Sobrien	hashwidth = uhashwidth;
66559243Sobrien    else {
66659243Sobrien	hashwidth = 0;
66769408Sache	if (v == NULL)
66869408Sache	    return;
669100616Smp	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
67059243Sobrien	    continue;
67159243Sobrien	if (hashwidth <= widthof(unsigned char))
67259243Sobrien	    hashwidth = sizeof(unsigned char);
67359243Sobrien	else if (hashwidth <= widthof(unsigned short))
67459243Sobrien	    hashwidth = sizeof(unsigned short);
67559243Sobrien	else if (hashwidth <= widthof(unsigned int))
67659243Sobrien	    hashwidth = sizeof(unsigned int);
67759243Sobrien	else
67859243Sobrien	    hashwidth = sizeof(unsigned long);
67959243Sobrien    }
68059243Sobrien
68159243Sobrien    if (uhashlength)
68259243Sobrien	hashlength = uhashlength;
68359243Sobrien    else
68459243Sobrien        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
685167465Smp
686167465Smp    xfree(xhash);
687167465Smp    xhash = xcalloc(hashlength * hashwidth, 1);
68859243Sobrien#endif /* FASTHASH */
68959243Sobrien
69059243Sobrien    (void) getusername(NULL);	/* flush the tilde cashe */
69159243Sobrien    tw_cmd_free();
69259243Sobrien    havhash = 1;
69359243Sobrien    if (v == NULL)
69459243Sobrien	return;
695100616Smp    for (pv = v->vec; pv && *pv; pv++, i++) {
69659243Sobrien	if (!ABSOLUTEP(pv[0]))
69759243Sobrien	    continue;
69859243Sobrien	dirp = opendir(short2str(*pv));
69959243Sobrien	if (dirp == NULL)
70059243Sobrien	    continue;
701167465Smp	cleanup_push(dirp, opendir_cleanup);
70259243Sobrien#ifdef COMMENT			/* this isn't needed.  opendir won't open
70359243Sobrien				 * non-dirs */
70459243Sobrien	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
705167465Smp	    cleanup_until(dirp);
70659243Sobrien	    continue;
70759243Sobrien	}
70859243Sobrien#endif
70969408Sache#ifdef WINNT_NATIVE
71059243Sobrien	is_windir = nt_check_if_windir(short2str(*pv));
71169408Sache#endif /* WINNT_NATIVE */
71259243Sobrien	while ((dp = readdir(dirp)) != NULL) {
71359243Sobrien	    if (dp->d_ino == 0)
71459243Sobrien		continue;
71559243Sobrien	    if (dp->d_name[0] == '.' &&
71659243Sobrien		(dp->d_name[1] == '\0' ||
71759243Sobrien		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
71859243Sobrien		continue;
71969408Sache#ifdef WINNT_NATIVE
72059243Sobrien	    nt_check_name_and_hash(is_windir, dp->d_name, i);
72169408Sache#else /* !WINNT_NATIVE*/
72269408Sache#if defined(_UWIN) || defined(__CYGWIN__)
72359243Sobrien	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
72459243Sobrien	     * the file with the .exe, .com, .bat extension
72559243Sobrien	     */
72659243Sobrien	    {
727167465Smp		ssize_t	ext = strlen(dp->d_name) - 4;
728131962Smp		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
729131962Smp				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
730167465Smp				  strcasecmp(&dp->d_name[ext], ".com") == 0)) {
731167465Smp#ifdef __CYGWIN__
732167465Smp		    /* Also store the variation with extension. */
733167465Smp		    hashval = hashname(str2short(dp->d_name));
734167465Smp		    bis(hashval, i);
735131962Smp#endif /* __CYGWIN__ */
736167465Smp		    dp->d_name[ext] = '\0';
737167465Smp		}
73859243Sobrien	    }
73969408Sache#endif /* _UWIN || __CYGWIN__ */
74059243Sobrien# ifdef FASTHASH
74159243Sobrien	    hashval = hashname(str2short(dp->d_name));
74259243Sobrien	    bis(hashval, i);
74359243Sobrien	    if (hashdebug & 1)
74459243Sobrien	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
74559243Sobrien		        hashname(str2short(dp->d_name)), i, dp->d_name);
74659243Sobrien# else /* OLD HASH */
74759243Sobrien	    hashval = hash(hashname(str2short(dp->d_name)), i);
74859243Sobrien	    bis(xhash, hashval);
74959243Sobrien# endif /* FASTHASH */
75059243Sobrien	    /* tw_add_comm_name (dp->d_name); */
75169408Sache#endif /* WINNT_NATIVE */
75259243Sobrien	}
753167465Smp	cleanup_until(dirp);
75459243Sobrien    }
75559243Sobrien}
75659243Sobrien
75759243Sobrien/*ARGSUSED*/
75859243Sobrienvoid
759167465Smpdounhash(Char **v, struct command *c)
76059243Sobrien{
76159243Sobrien    USE(c);
76259243Sobrien    USE(v);
76359243Sobrien    havhash = 0;
76459243Sobrien#ifdef FASTHASH
765167465Smp    xfree(xhash);
766167465Smp    xhash = NULL;
76759243Sobrien#endif /* FASTHASH */
76859243Sobrien}
76959243Sobrien
77059243Sobrien/*ARGSUSED*/
77159243Sobrienvoid
772167465Smphashstat(Char **v, struct command *c)
77359243Sobrien{
77459243Sobrien    USE(c);
77559243Sobrien    USE(v);
77659243Sobrien#ifdef FASTHASH
77759243Sobrien   if (havhash && hashlength && hashwidth)
77859243Sobrien      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
77959243Sobrien	      hashlength, hashwidth*8);
78059243Sobrien   if (hashdebug)
78159243Sobrien      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
78259243Sobrien#endif /* FASTHASH */
78359243Sobrien#ifdef VFORK
78459243Sobrien   if (hits + misses)
78559243Sobrien      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
78659243Sobrien	      hits, misses, 100 * hits / (hits + misses));
78759243Sobrien#endif
78859243Sobrien}
78959243Sobrien
79059243Sobrien
79159243Sobrien/*
79259243Sobrien * Hash a command name.
79359243Sobrien */
79469408Sacheint
795167465Smphashname(Char *cp)
79659243Sobrien{
797145479Smp    unsigned long h;
79859243Sobrien
79959243Sobrien    for (h = 0; *cp; cp++)
80059243Sobrien	h = hash(h, *cp);
80159243Sobrien    return ((int) h);
80259243Sobrien}
80359243Sobrien
80459243Sobrienstatic int
805167465Smpiscommand(Char *name)
80659243Sobrien{
807145479Smp    Char **pv;
808145479Smp    Char *sav;
809145479Smp    struct varent *v;
810145479Smp    int slash = any(short2str(name), '/');
811145479Smp    int hashval, i;
81259243Sobrien
81359243Sobrien    v = adrof(STRpath);
814100616Smp    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
81559243Sobrien	pv = justabs;
81659243Sobrien    else
81759243Sobrien	pv = v->vec;
81859243Sobrien    sav = Strspl(STRslash, name);	/* / command name for postpending */
81959243Sobrien    hashval = havhash ? hashname(name) : 0;
82059243Sobrien    i = 0;
82159243Sobrien    do {
82259243Sobrien	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
82359243Sobrien#ifdef FASTHASH
82459243Sobrien	    if (!bit(hashval, i))
82559243Sobrien		goto cont;
82659243Sobrien#else /* OLDHASH */
82759243Sobrien	    int hashval1 = hash(hashval, i);
82859243Sobrien	    if (!bit(xhash, hashval1))
82959243Sobrien		goto cont;
83059243Sobrien#endif /* FASTHASH */
83159243Sobrien	}
83259243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
83359243Sobrien	    if (executable(NULL, name, 0)) {
834167465Smp		xfree(sav);
83559243Sobrien		return i + 1;
83659243Sobrien	    }
83759243Sobrien	}
83859243Sobrien	else {
83959243Sobrien	    if (executable(*pv, sav, 0)) {
840167465Smp		xfree(sav);
84159243Sobrien		return i + 1;
84259243Sobrien	    }
84359243Sobrien	}
84459243Sobriencont:
84559243Sobrien	pv++;
84659243Sobrien	i++;
84759243Sobrien    } while (*pv);
848167465Smp    xfree(sav);
84959243Sobrien    return 0;
85059243Sobrien}
85159243Sobrien
85259243Sobrien/* Also by:
85359243Sobrien *  Andreas Luik <luik@isaak.isa.de>
85459243Sobrien *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
85559243Sobrien *  Azenberstr. 35
85659243Sobrien *  D-7000 Stuttgart 1
85759243Sobrien *  West-Germany
85859243Sobrien * is the executable() routine below and changes to iscommand().
85959243Sobrien * Thanks again!!
86059243Sobrien */
86159243Sobrien
86269408Sache#ifndef WINNT_NATIVE
86359243Sobrien/*
86459243Sobrien * executable() examines the pathname obtained by concatenating dir and name
86559243Sobrien * (dir may be NULL), and returns 1 either if it is executable by us, or
86659243Sobrien * if dir_ok is set and the pathname refers to a directory.
86759243Sobrien * This is a bit kludgy, but in the name of optimization...
86859243Sobrien */
86959243Sobrienint
870167465Smpexecutable(const Char *dir, const Char *name, int dir_ok)
87159243Sobrien{
87259243Sobrien    struct stat stbuf;
87359243Sobrien    char   *strname;
87459243Sobrien
87559243Sobrien    if (dir && *dir) {
876167465Smp	Char *path;
877167465Smp
878167465Smp	path = Strspl(dir, name);
87959243Sobrien	strname = short2str(path);
880167465Smp	xfree(path);
88159243Sobrien    }
88259243Sobrien    else
88359243Sobrien	strname = short2str(name);
884167465Smp
88559243Sobrien    return (stat(strname, &stbuf) != -1 &&
88659243Sobrien	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
88759243Sobrien	     (S_ISREG(stbuf.st_mode) &&
88859243Sobrien    /* save time by not calling access() in the hopeless case */
88959243Sobrien	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
89059243Sobrien	      access(strname, X_OK) == 0
89159243Sobrien	)));
89259243Sobrien}
89369408Sache#endif /*!WINNT_NATIVE*/
89459243Sobrien
895167465Smpstruct tellmewhat_s0_cleanup
896167465Smp{
897167465Smp    Char **dest, *val;
898167465Smp};
899167465Smp
900167465Smpstatic void
901167465Smptellmewhat_s0_cleanup(void *xstate)
902167465Smp{
903167465Smp    struct tellmewhat_s0_cleanup *state;
904167465Smp
905167465Smp    state = xstate;
906167465Smp    *state->dest = state->val;
907167465Smp}
908167465Smp
90959243Sobrienint
910167465Smptellmewhat(struct wordent *lexp, Char **str)
91159243Sobrien{
912167465Smp    struct tellmewhat_s0_cleanup s0;
913145479Smp    int i;
914167465Smp    const struct biltins *bptr;
915145479Smp    struct wordent *sp = lexp->next;
916145479Smp    int    aliased = 0, found;
917167465Smp    Char   *s1, *s2, *cmd;
91859243Sobrien    Char    qc;
91959243Sobrien
92059243Sobrien    if (adrof1(sp->word, &aliases)) {
92159243Sobrien	alias(lexp);
92259243Sobrien	sp = lexp->next;
92359243Sobrien	aliased = 1;
92459243Sobrien    }
92559243Sobrien
926167465Smp    s0.dest = &sp->word;	/* to get the memory freeing right... */
927167465Smp    s0.val = sp->word;
928167465Smp    cleanup_push(&s0, tellmewhat_s0_cleanup);
92959243Sobrien
93059243Sobrien    /* handle quoted alias hack */
93159243Sobrien    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
93259243Sobrien	(sp->word)++;
93359243Sobrien
93459243Sobrien    /* do quoting, if it hasn't been done */
93559243Sobrien    s1 = s2 = sp->word;
93659243Sobrien    while (*s2)
93759243Sobrien	switch (*s2) {
93859243Sobrien	case '\'':
93959243Sobrien	case '"':
94059243Sobrien	    qc = *s2++;
94159243Sobrien	    while (*s2 && *s2 != qc)
94259243Sobrien		*s1++ = *s2++ | QUOTE;
94359243Sobrien	    if (*s2)
94459243Sobrien		s2++;
94559243Sobrien	    break;
94659243Sobrien	case '\\':
94759243Sobrien	    if (*++s2)
94859243Sobrien		*s1++ = *s2++ | QUOTE;
94959243Sobrien	    break;
95059243Sobrien	default:
95159243Sobrien	    *s1++ = *s2++;
95259243Sobrien	}
95359243Sobrien    *s1 = '\0';
95459243Sobrien
95559243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
95659243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
95759243Sobrien	    if (str == NULL) {
95859243Sobrien		if (aliased)
95959243Sobrien		    prlex(lexp);
96059243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
96159243Sobrien			      sp->word);
96259243Sobrien		flush();
96359243Sobrien	    }
964167465Smp	    else
965167465Smp		*str = Strsave(sp->word);
966167465Smp	    cleanup_until(&s0);
96759243Sobrien	    return TRUE;
96859243Sobrien	}
96959243Sobrien    }
97069408Sache#ifdef WINNT_NATIVE
97159243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
97259243Sobrien	if (eq(sp->word, str2short(bptr->bname))) {
97359243Sobrien	    if (str == NULL) {
97459243Sobrien		if (aliased)
97559243Sobrien		    prlex(lexp);
97659243Sobrien		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
97759243Sobrien			      sp->word);
97859243Sobrien		flush();
97959243Sobrien	    }
980167465Smp	    else
981167465Smp		*str = Strsave(sp->word);
982167465Smp	    cleanup_until(&s0);
98359243Sobrien	    return TRUE;
98459243Sobrien	}
98559243Sobrien    }
98669408Sache#endif /* WINNT_NATIVE*/
98759243Sobrien
98859243Sobrien    sp->word = cmd = globone(sp->word, G_IGNORE);
989167465Smp    cleanup_push(cmd, xfree);
99059243Sobrien
99159243Sobrien    if ((i = iscommand(sp->word)) != 0) {
992145479Smp	Char **pv;
993145479Smp	struct varent *v;
994145479Smp	int    slash = any(short2str(sp->word), '/');
99559243Sobrien
99659243Sobrien	v = adrof(STRpath);
997100616Smp	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
99859243Sobrien	    pv = justabs;
99959243Sobrien	else
100059243Sobrien	    pv = v->vec;
100159243Sobrien
1002167465Smp	pv += i - 1;
100359243Sobrien	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
100459243Sobrien	    if (!slash) {
100559243Sobrien		sp->word = Strspl(STRdotsl, sp->word);
1006167465Smp		cleanup_push(sp->word, xfree);
100759243Sobrien		prlex(lexp);
1008167465Smp		cleanup_until(sp->word);
100959243Sobrien	    }
101059243Sobrien	    else
101159243Sobrien		prlex(lexp);
101259243Sobrien	}
101359243Sobrien	else {
101459243Sobrien	    s1 = Strspl(*pv, STRslash);
101559243Sobrien	    sp->word = Strspl(s1, sp->word);
1016167465Smp	    xfree(s1);
1017167465Smp	    cleanup_push(sp->word, xfree);
101859243Sobrien	    if (str == NULL)
101959243Sobrien		prlex(lexp);
102059243Sobrien	    else
1021167465Smp		*str = Strsave(sp->word);
1022167465Smp	    cleanup_until(sp->word);
102359243Sobrien	}
102459243Sobrien	found = 1;
102559243Sobrien    }
102659243Sobrien    else {
102759243Sobrien	if (str == NULL) {
102859243Sobrien	    if (aliased)
102959243Sobrien		prlex(lexp);
103059243Sobrien	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
103159243Sobrien	    flush();
103259243Sobrien	}
103359243Sobrien	else
1034167465Smp	    *str = Strsave(sp->word);
103559243Sobrien	found = 0;
103659243Sobrien    }
1037167465Smp    cleanup_until(&s0);
103859243Sobrien    return found;
103959243Sobrien}
104059243Sobrien
104159243Sobrien/*
104259243Sobrien * Builtin to look at and list all places a command may be defined:
104359243Sobrien * aliases, shell builtins, and the path.
104459243Sobrien *
104559243Sobrien * Marc Horowitz <marc@mit.edu>
104659243Sobrien * MIT Student Information Processing Board
104759243Sobrien */
104859243Sobrien
104959243Sobrien/*ARGSUSED*/
105059243Sobrienvoid
1051167465Smpdowhere(Char **v, struct command *c)
105259243Sobrien{
105359243Sobrien    int found = 1;
105459243Sobrien    USE(c);
105559243Sobrien    for (v++; *v; v++)
105659243Sobrien	found &= find_cmd(*v, 1);
105759243Sobrien    /* Make status nonzero if any command is not found. */
105859243Sobrien    if (!found)
1059167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
106059243Sobrien}
106159243Sobrien
106259243Sobrienint
1063167465Smpfind_cmd(Char *cmd, int prt)
106459243Sobrien{
106559243Sobrien    struct varent *var;
1066167465Smp    const struct biltins *bptr;
106759243Sobrien    Char **pv;
106859243Sobrien    Char *sv;
106959243Sobrien    int hashval, i, ex, rval = 0;
107059243Sobrien
107159243Sobrien    if (prt && any(short2str(cmd), '/')) {
107259243Sobrien	xprintf(CGETS(13, 7, "where: / in command makes no sense\n"));
107359243Sobrien	return rval;
107459243Sobrien    }
107559243Sobrien
107659243Sobrien    /* first, look for an alias */
107759243Sobrien
107859243Sobrien    if (prt && adrof1(cmd, &aliases)) {
107959243Sobrien	if ((var = adrof1(cmd, &aliases)) != NULL) {
108059243Sobrien	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1081100616Smp	    if (var->vec != NULL)
1082100616Smp		blkpr(var->vec);
108359243Sobrien	    xputchar('\n');
108459243Sobrien	    rval = 1;
108559243Sobrien	}
108659243Sobrien    }
108759243Sobrien
108859243Sobrien    /* next, look for a shell builtin */
108959243Sobrien
109059243Sobrien    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
109159243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
109259243Sobrien	    rval = 1;
109359243Sobrien	    if (prt)
109459243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
109559243Sobrien	    else
109659243Sobrien		return rval;
109759243Sobrien	}
109859243Sobrien    }
109969408Sache#ifdef WINNT_NATIVE
110059243Sobrien    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
110159243Sobrien	if (eq(cmd, str2short(bptr->bname))) {
110259243Sobrien	    rval = 1;
110359243Sobrien	    if (prt)
110459243Sobrien		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
110559243Sobrien	    else
110659243Sobrien		return rval;
110759243Sobrien	}
110859243Sobrien    }
110969408Sache#endif /* WINNT_NATIVE*/
111059243Sobrien
111159243Sobrien    /* last, look through the path for the command */
111259243Sobrien
111359243Sobrien    if ((var = adrof(STRpath)) == NULL)
111459243Sobrien	return rval;
111559243Sobrien
111659243Sobrien    hashval = havhash ? hashname(cmd) : 0;
111759243Sobrien
111859243Sobrien    sv = Strspl(STRslash, cmd);
1119167465Smp    cleanup_push(sv, xfree);
112059243Sobrien
1121100616Smp    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
112259243Sobrien	if (havhash && !eq(*pv, STRdot)) {
112359243Sobrien#ifdef FASTHASH
112459243Sobrien	    if (!bit(hashval, i))
112559243Sobrien		continue;
112659243Sobrien#else /* OLDHASH */
112759243Sobrien	    int hashval1 = hash(hashval, i);
112859243Sobrien	    if (!bit(xhash, hashval1))
112959243Sobrien		continue;
113059243Sobrien#endif /* FASTHASH */
113159243Sobrien	}
113259243Sobrien	ex = executable(*pv, sv, 0);
113359243Sobrien#ifdef FASTHASH
113459243Sobrien	if (!ex && (hashdebug & 2)) {
113559243Sobrien	    xprintf(CGETS(13, 10, "hash miss: "));
113659243Sobrien	    ex = 1;	/* Force printing */
113759243Sobrien	}
113859243Sobrien#endif /* FASTHASH */
113959243Sobrien	if (ex) {
114059243Sobrien	    rval = 1;
114159243Sobrien	    if (prt) {
114259243Sobrien		xprintf("%S/", *pv);
114359243Sobrien		xprintf("%S\n", cmd);
114459243Sobrien	    }
114559243Sobrien	    else
114659243Sobrien		return rval;
114759243Sobrien	}
114859243Sobrien    }
1149167465Smp    cleanup_until(sv);
115059243Sobrien    return rval;
115159243Sobrien}
115269408Sache#ifdef WINNT_NATIVE
115359243Sobrienint hashval_extern(cp)
115459243Sobrien	Char *cp;
115559243Sobrien{
115659243Sobrien	return havhash?hashname(cp):0;
115759243Sobrien}
115859243Sobrienint bit_extern(val,i)
115959243Sobrien	int val;
116059243Sobrien	int i;
116159243Sobrien{
116259243Sobrien	return bit(val,i);
116359243Sobrien}
116469408Sachevoid bis_extern(val,i)
116569408Sache	int val;
116669408Sache	int i;
116769408Sache{
116869408Sache	bis(val,i);
116969408Sache}
117069408Sache#endif /* WINNT_NATIVE */
117169408Sache
1172