sh.exec.c revision 145479
1168404Spjd/* $Header: /src/pub/tcsh/sh.exec.c,v 3.63 2004/11/23 02:10:48 christos Exp $ */
2168404Spjd/*
3168404Spjd * sh.exec.c: Search, find, and execute a command!
4168404Spjd */
5168404Spjd/*-
6168404Spjd * Copyright (c) 1980, 1991 The Regents of the University of California.
7168404Spjd * All rights reserved.
8168404Spjd *
9168404Spjd * Redistribution and use in source and binary forms, with or without
10168404Spjd * modification, are permitted provided that the following conditions
11168404Spjd * are met:
12168404Spjd * 1. Redistributions of source code must retain the above copyright
13168404Spjd *    notice, this list of conditions and the following disclaimer.
14168404Spjd * 2. Redistributions in binary form must reproduce the above copyright
15168404Spjd *    notice, this list of conditions and the following disclaimer in the
16168404Spjd *    documentation and/or other materials provided with the distribution.
17168404Spjd * 3. Neither the name of the University nor the names of its contributors
18168404Spjd *    may be used to endorse or promote products derived from this software
19168404Spjd *    without specific prior written permission.
20168404Spjd *
21168404Spjd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23219089Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24249195Smm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25249188Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26247265Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28168404Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29168404Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30251629Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31251629Sdelphij * SUCH DAMAGE.
32168404Spjd */
33168404Spjd#include "sh.h"
34168404Spjd
35168404SpjdRCSID("$Id: sh.exec.c,v 3.63 2004/11/23 02:10:48 christos Exp $")
36168404Spjd
37168404Spjd#include "tc.h"
38168404Spjd#include "tw.h"
39168404Spjd#ifdef WINNT_NATIVE
40168404Spjd#include <nt.const.h>
41168404Spjd#endif /*WINNT_NATIVE*/
42168404Spjd
43168404Spjd/*
44168404Spjd * C shell
45168404Spjd */
46219089Spjd
47168404Spjd#ifndef OLDHASH
48168404Spjd# define FASTHASH	/* Fast hashing is the default */
49219089Spjd#endif /* OLDHASH */
50168404Spjd
51168404Spjd/*
52168404Spjd * System level search and execute of a command.
53168404Spjd * We look in each directory for the specified command name.
54168404Spjd * If the name contains a '/' then we execute only the full path name.
55168404Spjd * If there is no search path then we execute only full path names.
56168404Spjd */
57168404Spjd
58168404Spjd/*
59168404Spjd * As we search for the command we note the first non-trivial error
60168404Spjd * message for presentation to the user.  This allows us often
61168404Spjd * to show that a file has the wrong mode/no access when the file
62185029Spjd * is not in the last component of the search path, so we must
63168404Spjd * go on after first detecting the error.
64185029Spjd */
65219089Spjdstatic char *exerr;		/* Execution error message */
66219089Spjdstatic Char *expath;		/* Path for exerr */
67248571Smm
68248571Smm/*
69248571Smm * The two part hash function is designed to let texec() call the
70236884Smm * more expensive hashname() only once and the simple hash() several
71219089Spjd * times (once for each path component checked).
72240868Spjd * Byte size is assumed to be 8.
73168404Spjd */
74219089Spjd#define BITS_PER_BYTE	8
75219089Spjd
76219089Spjd#ifdef FASTHASH
77219089Spjd/*
78219089Spjd * xhash is an array of hash buckets which are used to hash execs.  If
79219089Spjd * it is allocated (havhash true), then to tell if ``name'' is
80185029Spjd * (possibly) presend in the i'th component of the variable path, look
81185029Spjd * at the [hashname(name)] bucket of size [hashwidth] bytes, in the [i
82168404Spjd * mod size*8]'th bit.  The cache size is defaults to a length of 1024
83204073Spjd * buckets, each 1 byte wide.  This implementation guarantees that
84204073Spjd * objects n bytes wide will be aligned on n byte boundaries.
85204073Spjd */
86204073Spjd# define HSHMUL		241
87204073Spjd
88204073Spjdstatic unsigned long *xhash = NULL;
89204073Spjdstatic unsigned int hashlength = 0, uhashlength = 0;
90204073Spjdstatic unsigned int hashwidth = 0, uhashwidth = 0;
91251636Sdelphijstatic int hashdebug = 0;
92251636Sdelphij
93251636Sdelphij# define hash(a, b)	(((a) * HSHMUL + (b)) % (hashlength))
94251636Sdelphij# define widthof(t)	(sizeof(t) * BITS_PER_BYTE)
95251636Sdelphij# define tbit(f, i, t)	(((t *) xhash)[(f)] &  \
96251636Sdelphij			 (1UL << (i & (widthof(t) - 1))))
97219089Spjd# define tbis(f, i, t)	(((t *) xhash)[(f)] |= \
98258631Savg			 (1UL << (i & (widthof(t) - 1))))
99258631Savg# define cbit(f, i)	tbit(f, i, unsigned char)
100258631Savg# define cbis(f, i)	tbis(f, i, unsigned char)
101258631Savg# define sbit(f, i)	tbit(f, i, unsigned short)
102258631Savg# define sbis(f, i)	tbis(f, i, unsigned short)
103219089Spjd# define ibit(f, i)	tbit(f, i, unsigned int)
104168712Spjd# define ibis(f, i)	tbis(f, i, unsigned int)
105258631Savg# define lbit(f, i)	tbit(f, i, unsigned long)
106258631Savg# define lbis(f, i)	tbis(f, i, unsigned long)
107258631Savg
108258631Savg# define bit(f, i) (hashwidth==sizeof(unsigned char)  ? cbit(f,i) : \
109209962Smm 		    ((hashwidth==sizeof(unsigned short) ? sbit(f,i) : \
110258631Savg		     ((hashwidth==sizeof(unsigned int)   ? ibit(f,i) : \
111258631Savg		     lbit(f,i))))))
112209962Smm# define bis(f, i) (hashwidth==sizeof(unsigned char)  ? cbis(f,i) : \
113209962Smm 		    ((hashwidth==sizeof(unsigned short) ? sbis(f,i) : \
114258631Savg		     ((hashwidth==sizeof(unsigned int)   ? ibis(f,i) : \
115211931Smm		     lbis(f,i))))))
116258631Savg#else /* OLDHASH */
117209962Smm/*
118209962Smm * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
119209962Smm * to hash execs.  If it is allocated (havhash true), then to tell
120219089Spjd * whether ``name'' is (possibly) present in the i'th component
121209962Smm * of the variable path, you look at the bit in xhash indexed by
122209962Smm * hash(hashname("name"), i).  This is setup automatically
123211931Smm * after .login is executed, and recomputed whenever ``path'' is
124258631Savg * changed.
125258631Savg */
126258631Savg# define HSHSIZ		8192	/* 1k bytes */
127258631Savg# define HSHMASK		(HSHSIZ - 1)
128258631Savg# define HSHMUL		243
129258631Savgstatic char xhash[HSHSIZ / BITS_PER_BYTE];
130258631Savg
131258631Savg# define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
132258631Savg# define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
133258631Savg# define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
134258631Savg
135258631Savg#endif /* FASTHASH */
136258631Savg
137258631Savg#ifdef VFORK
138258631Savgstatic int hits, misses;
139211931Smm#endif /* VFORK */
140211931Smm
141211931Smm/* Dummy search path for just absolute search when no path */
142258631Savgstatic Char *justabs[] = {STRNULL, 0};
143258631Savg
144258631Savgstatic	void	pexerr		__P((void));
145258631Savgstatic	void	texec		__P((Char *, Char **));
146258631Savgint	hashname	__P((Char *));
147258631Savgstatic	int 	iscommand	__P((Char *));
148209962Smm
149209962Smmvoid
150248571Smmdoexec(t, do_glob)
151248571Smm    struct command *t;
152185029Spjd    int do_glob;
153219089Spjd{
154219089Spjd    Char *dp, **pv, **av, *sav;
155219089Spjd    struct varent *v;
156219089Spjd    int slash;
157185029Spjd    int hashval, i;
158219089Spjd    Char   *blk[2];
159219089Spjd
160219089Spjd    /*
161219089Spjd     * Glob the command name. We will search $path even if this does something,
162219089Spjd     * as in sh but not in csh.  One special case: if there is no PATH, then we
163219089Spjd     * execute only commands which start with '/'.
164219089Spjd     */
165219089Spjd    blk[0] = t->t_dcom[0];
166219089Spjd    blk[1] = 0;
167219089Spjd    gflag = 0;
168243503Smm    if (do_glob)
169219089Spjd	tglob(blk);
170247265Smm    if (gflag) {
171247265Smm	pv = globall(blk);
172247265Smm	if (pv == 0) {
173247265Smm	    setname(short2str(blk[0]));
174168404Spjd	    stderror(ERR_NAME | ERR_NOMATCH);
175219089Spjd	}
176219089Spjd	gargv = 0;
177219089Spjd    }
178219089Spjd    else
179219089Spjd	pv = saveblk(blk);
180219089Spjd
181168404Spjd    trim(pv);
182185029Spjd
183185029Spjd    exerr = 0;
184185029Spjd    expath = Strsave(pv[0]);
185185029Spjd#ifdef VFORK
186185029Spjd    Vexpath = expath;
187185029Spjd#endif /* VFORK */
188185029Spjd
189185029Spjd    v = adrof(STRpath);
190185029Spjd    if (v == 0 && expath[0] != '/' && expath[0] != '.') {
191185029Spjd	blkfree(pv);
192185029Spjd	pexerr();
193185029Spjd    }
194185029Spjd    slash = any(short2str(expath), '/');
195185029Spjd
196185029Spjd    /*
197185029Spjd     * Glob the argument list, if necessary. Otherwise trim off the quote bits.
198185029Spjd     */
199185029Spjd    gflag = 0;
200185029Spjd    av = &t->t_dcom[1];
201185029Spjd    if (do_glob)
202185029Spjd	tglob(av);
203185029Spjd    if (gflag) {
204185029Spjd	av = globall(av);
205185029Spjd	if (av == 0) {
206185029Spjd	    blkfree(pv);
207185029Spjd	    setname(short2str(expath));
208185029Spjd	    stderror(ERR_NAME | ERR_NOMATCH);
209185029Spjd	}
210185029Spjd	gargv = 0;
211185029Spjd    }
212185029Spjd    else
213185029Spjd	av = saveblk(av);
214236155Smm
215236884Smm    blkfree(t->t_dcom);
216209962Smm    t->t_dcom = blkspl(pv, av);
217219089Spjd    xfree((ptr_t) pv);
218236155Smm    xfree((ptr_t) av);
219185029Spjd    av = t->t_dcom;
220185029Spjd    trim(av);
221185029Spjd
222185029Spjd    if (*av == NULL || **av == '\0')
223185029Spjd	pexerr();
224185029Spjd
225236155Smm    xechoit(av);		/* Echo command if -x */
226219089Spjd#ifdef CLOSE_ON_EXEC
227219089Spjd    /*
228209962Smm     * Since all internal file descriptors are set to close on exec, we don't
229209962Smm     * need to close them explicitly here.  Just reorient ourselves for error
230219089Spjd     * messages.
231219089Spjd     */
232219089Spjd    SHIN = 0;
233236155Smm    SHOUT = 1;
234236155Smm    SHDIAG = 2;
235236155Smm    OLDSTD = 0;
236236155Smm    isoutatty = isatty(SHOUT);
237236155Smm    isdiagatty = isatty(SHDIAG);
238236155Smm#else
239236155Smm    closech();			/* Close random fd's */
240236155Smm#endif
241236155Smm    /*
242219089Spjd     * We must do this AFTER any possible forking (like `foo` in glob) so that
243219089Spjd     * this shell can still do subprocesses.
244185029Spjd     */
245219089Spjd#ifdef BSDSIGS
246209962Smm    (void) sigsetmask((sigmask_t) 0);
247185029Spjd#else /* BSDSIGS */
248219089Spjd    (void) sigrelse(SIGINT);
249219089Spjd    (void) sigrelse(SIGCHLD);
250219089Spjd#endif /* BSDSIGS */
251209962Smm
252236155Smm    /*
253209962Smm     * If no path, no words in path, or a / in the filename then restrict the
254209962Smm     * command search.
255209962Smm     */
256209962Smm    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
257209962Smm	pv = justabs;
258209962Smm    else
259209962Smm	pv = v->vec;
260209962Smm    sav = Strspl(STRslash, *av);/* / command name for postpending */
261209962Smm#ifdef VFORK
262236884Smm    Vsav = sav;
263236884Smm#endif /* VFORK */
264236884Smm    hashval = havhash ? hashname(*av) : 0;
265236884Smm
266236884Smm    i = 0;
267236884Smm#ifdef VFORK
268236884Smm    hits++;
269236884Smm#endif /* VFORK */
270236884Smm    do {
271236884Smm	/*
272236884Smm	 * Try to save time by looking at the hash table for where this command
273236884Smm	 * could be.  If we are doing delayed hashing, then we put the names in
274236884Smm	 * one at a time, as the user enters them.  This is kinda like Korn
275236884Smm	 * Shell's "tracked aliases".
276236884Smm	 */
277236884Smm	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
278185029Spjd#ifdef FASTHASH
279185029Spjd	    if (!bit(hashval, i))
280228103Smm		goto cont;
281228103Smm#else /* OLDHASH */
282228103Smm	    int hashval1 = hash(hashval, i);
283228103Smm	    if (!bit(xhash, hashval1))
284228103Smm		goto cont;
285185029Spjd#endif /* FASTHASH */
286185029Spjd	}
287185029Spjd	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
288185029Spjd	{
289185029Spjd
290185029Spjd#ifdef COHERENT
291185029Spjd	    if (t->t_dflg & F_AMPERSAND) {
292185029Spjd# ifdef JOBDEBUG
293185029Spjd    	        xprintf("set SIGINT to SIG_IGN\n");
294185029Spjd    	        xprintf("set SIGQUIT to SIG_DFL\n");
295185029Spjd# endif /* JOBDEBUG */
296185029Spjd    	        (void) signal(SIGINT,SIG_IGN); /* may not be necessary */
297185029Spjd	        (void) signal(SIGQUIT,SIG_DFL);
298185029Spjd	    }
299185029Spjd
300185029Spjd	    if (gointr && eq(gointr, STRminus)) {
301185029Spjd# ifdef JOBDEBUG
302185029Spjd    	        xprintf("set SIGINT to SIG_IGN\n");
303185029Spjd    	        xprintf("set SIGQUIT to SIG_IGN\n");
304185029Spjd# endif /* JOBDEBUG */
305185029Spjd    	        (void) signal(SIGINT,SIG_IGN); /* may not be necessary */
306219089Spjd	        (void) signal(SIGQUIT,SIG_IGN);
307185029Spjd	    }
308185029Spjd#endif /* COHERENT */
309185029Spjd
310185029Spjd	    texec(*av, av);
311185029Spjd}
312185029Spjd	else {
313185029Spjd	    dp = Strspl(*pv, sav);
314185029Spjd#ifdef VFORK
315185029Spjd	    Vdp = dp;
316185029Spjd#endif /* VFORK */
317185029Spjd
318185029Spjd#ifdef COHERENT
319185029Spjd	    if ((t->t_dflg & F_AMPERSAND)) {
320185029Spjd# ifdef JOBDEBUG
321219089Spjd    	        xprintf("set SIGINT to SIG_IGN\n");
322185029Spjd# endif /* JOBDEBUG */
323185029Spjd		/*
324185029Spjd		 * this is necessary on Coherent or all background
325185029Spjd		 * jobs are killed by CTRL-C
326185029Spjd		 * (there must be a better fix for this)
327185029Spjd		 */
328185029Spjd    	        (void) signal(SIGINT,SIG_IGN);
329185029Spjd	    }
330185029Spjd	    if (gointr && eq(gointr,STRminus)) {
331185029Spjd# ifdef JOBDEBUG
332185029Spjd    	        xprintf("set SIGINT to SIG_IGN\n");
333185029Spjd    	        xprintf("set SIGQUIT to SIG_IGN\n");
334185029Spjd# endif /* JOBDEBUG */
335185029Spjd    	        (void) signal(SIGINT,SIG_IGN); /* may not be necessary */
336185029Spjd	        (void) signal(SIGQUIT,SIG_IGN);
337185029Spjd	    }
338185029Spjd#endif /* COHERENT */
339185029Spjd
340185029Spjd	    texec(dp, av);
341185029Spjd#ifdef VFORK
342185029Spjd	    Vdp = 0;
343185029Spjd#endif /* VFORK */
344185029Spjd	    xfree((ptr_t) dp);
345185029Spjd	}
346185029Spjd#ifdef VFORK
347185029Spjd	misses++;
348185029Spjd#endif /* VFORK */
349185029Spjdcont:
350185029Spjd	pv++;
351185029Spjd	i++;
352248571Smm    } while (*pv);
353185029Spjd#ifdef VFORK
354185029Spjd    hits--;
355248571Smm    Vsav = 0;
356185029Spjd#endif /* VFORK */
357185029Spjd    xfree((ptr_t) sav);
358185029Spjd    pexerr();
359185029Spjd}
360185029Spjd
361185029Spjdstatic void
362185029Spjdpexerr()
363185029Spjd{
364248571Smm    /* Couldn't find the damn thing */
365185029Spjd    if (expath) {
366185029Spjd	setname(short2str(expath));
367185029Spjd#ifdef VFORK
368185029Spjd	Vexpath = 0;
369185029Spjd#endif /* VFORK */
370185029Spjd	xfree((ptr_t) expath);
371185029Spjd	expath = 0;
372185029Spjd    }
373185029Spjd    else
374185029Spjd	setname("");
375185029Spjd    if (exerr)
376185029Spjd	stderror(ERR_NAME | ERR_STRING, exerr);
377185029Spjd    stderror(ERR_NAME | ERR_COMMAND);
378185029Spjd}
379185029Spjd
380185029Spjd/*
381185029Spjd * Execute command f, arg list t.
382185029Spjd * Record error message if not found.
383185029Spjd * Also do shell scripts here.
384185029Spjd */
385185029Spjdstatic void
386185029Spjdtexec(sf, st)
387185029Spjd    Char   *sf;
388185029Spjd    Char **st;
389185029Spjd{
390185029Spjd    char **t;
391185029Spjd    char *f;
392185029Spjd    struct varent *v;
393185029Spjd    Char  **vp;
394185029Spjd    Char   *lastsh[2];
395185029Spjd    char    pref[2];
396185029Spjd    int     fd;
397185029Spjd    Char   *st0, **ost;
398185029Spjd
399185029Spjd    /* The order for the conversions is significant */
400185029Spjd    t = short2blk(st);
401185029Spjd    f = short2str(sf);
402185029Spjd#ifdef VFORK
403185029Spjd    Vt = t;
404185029Spjd#endif /* VFORK */
405185029Spjd    errno = 0;			/* don't use a previous error */
406185029Spjd#ifdef apollo
407185029Spjd    /*
408185029Spjd     * If we try to execute an nfs mounted directory on the apollo, we
409185029Spjd     * hang forever. So until apollo fixes that..
410185029Spjd     */
411185029Spjd    {
412185029Spjd	struct stat stb;
413185029Spjd	if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
414185029Spjd	    errno = EISDIR;
415185029Spjd    }
416247187Smm    if (errno == 0)
417236884Smm#endif /* apollo */
418185029Spjd    {
419185029Spjd#ifdef ISC_POSIX_EXEC_BUG
420185029Spjd	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
421185029Spjd#endif /* ISC_POSIX_EXEC_BUG */
422236884Smm	(void) execv(f, t);
423236884Smm#ifdef ISC_POSIX_EXEC_BUG
424236884Smm	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
425185029Spjd#endif /* ISC_POSIX_EXEC_BUG */
426236884Smm    }
427236884Smm#ifdef VFORK
428236884Smm    Vt = 0;
429249195Smm#endif /* VFORK */
430236884Smm    blkfree((Char **) t);
431236884Smm    switch (errno) {
432185029Spjd
433236884Smm    case ENOEXEC:
434236884Smm#ifdef WINNT_NATIVE
435236884Smm		nt_feed_to_cmd(f,t);
436236884Smm#endif /* WINNT_NATIVE */
437249195Smm	/*
438236884Smm	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
439236884Smm	 * it, don't feed it to the shell if it looks like a binary!
440185029Spjd	 */
441236884Smm	if ((fd = open(f, O_RDONLY|O_LARGEFILE)) != -1) {
442249195Smm	    int nread;
443236884Smm#ifdef O_TEXT
444236884Smm	    setmode(fd, O_TEXT);
445236884Smm#endif
446236884Smm	    if ((nread = read(fd, (char *) pref, 2)) == 2) {
447249195Smm		if (!isprint((unsigned char)pref[0]) &&
448236884Smm		    (pref[0] != '\n' && pref[0] != '\t')) {
449236884Smm		    (void) close(fd);
450236884Smm		    /*
451236884Smm		     * We *know* what ENOEXEC means.
452236884Smm		     */
453249195Smm		    stderror(ERR_ARCH, f, strerror(errno));
454236884Smm		}
455236884Smm	    }
456236884Smm	    else if (nread < 0 && errno != EINTR) {
457236884Smm#ifdef convex
458236884Smm		/* need to print error incase the file is migrated */
459236884Smm		stderror(ERR_SYSTEM, f, strerror(errno));
460185029Spjd#endif
461185029Spjd	    }
462185029Spjd#ifdef _PATH_BSHELL
463236884Smm	    else {
464236884Smm		pref[0] = '#';
465236884Smm		pref[1] = '\0';
466249195Smm	    }
467185029Spjd#endif
468185029Spjd	}
469185029Spjd#ifdef HASHBANG
470185029Spjd	if (fd == -1 ||
471185029Spjd	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
472219089Spjd#endif /* HASHBANG */
473185029Spjd	/*
474185029Spjd	 * If there is an alias for shell, then put the words of the alias in
475249195Smm	 * front of the argument list replacing the command name. Note no
476185029Spjd	 * interpretation of the words at this point.
477185029Spjd	 */
478185029Spjd	    v = adrof1(STRshell, &aliases);
479209962Smm	    if (v == NULL || v->vec == NULL) {
480209962Smm		vp = lastsh;
481209962Smm		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
482209962Smm		vp[1] = NULL;
483209962Smm#ifdef _PATH_BSHELL
484185029Spjd		if (fd != -1
485249195Smm# ifndef ISC	/* Compatible with ISC's /bin/csh */
486185029Spjd		    && pref[0] != '#'
487185029Spjd# endif /* ISC */
488185029Spjd		    )
489185029Spjd		    vp[0] = STR_BSHELL;
490185029Spjd#endif
491185029Spjd		vp = saveblk(vp);
492185029Spjd	    }
493249195Smm	    else
494185029Spjd		vp = saveblk(v->vec);
495185029Spjd#ifdef HASHBANG
496185029Spjd	}
497185029Spjd#endif /* HASHBANG */
498185029Spjd	if (fd != -1)
499185029Spjd	    (void) close(fd);
500185029Spjd
501185029Spjd	st0 = st[0];
502236884Smm	st[0] = sf;
503185029Spjd	ost = st;
504185029Spjd	st = blkspl(vp, st);	/* Splice up the new arglst */
505185029Spjd	ost[0] = st0;
506185029Spjd	sf = *st;
507185029Spjd	/* The order for the conversions is significant */
508185029Spjd	t = short2blk(st);
509185029Spjd	f = short2str(sf);
510185029Spjd	xfree((ptr_t) st);
511219089Spjd	blkfree((Char **) vp);
512185029Spjd#ifdef VFORK
513185029Spjd	Vt = t;
514219089Spjd#endif /* VFORK */
515219089Spjd#ifdef ISC_POSIX_EXEC_BUG
516219089Spjd	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
517249195Smm#endif /* ISC_POSIX_EXEC_BUG */
518248571Smm	(void) execv(f, t);
519248571Smm#ifdef ISC_POSIX_EXEC_BUG
520185029Spjd	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
521248571Smm#endif /* ISC_POSIX_EXEC_BUG */
522185029Spjd#ifdef VFORK
523249195Smm	Vt = 0;
524185029Spjd#endif /* VFORK */
525185029Spjd	blkfree((Char **) t);
526185029Spjd	/* The sky is falling, the sky is falling! */
527219089Spjd	stderror(ERR_SYSTEM, f, strerror(errno));
528185029Spjd	break;
529185029Spjd
530185029Spjd    case ENOMEM:
531185029Spjd	stderror(ERR_SYSTEM, f, strerror(errno));
532185029Spjd	break;
533185029Spjd
534185029Spjd#ifdef _IBMR2
535249195Smm    case 0:			/* execv fails and returns 0! */
536185029Spjd#endif /* _IBMR2 */
537185029Spjd    case ENOENT:
538185029Spjd	break;
539185029Spjd
540185029Spjd    default:
541185029Spjd	if (exerr == 0) {
542185029Spjd	    exerr = strerror(errno);
543185029Spjd	    if (expath)
544185029Spjd		xfree((ptr_t) expath);
545185029Spjd	    expath = Strsave(sf);
546185029Spjd#ifdef VFORK
547185029Spjd	    Vexpath = expath;
548185029Spjd#endif /* VFORK */
549249195Smm	}
550185029Spjd	break;
551185029Spjd    }
552185029Spjd}
553185029Spjd
554185029Spjd/*ARGSUSED*/
555185029Spjdvoid
556185029Spjdexecash(t, kp)
557185029Spjd    Char  **t;
558185029Spjd    struct command *kp;
559185029Spjd{
560185029Spjd    int     saveIN, saveOUT, saveDIAG, saveSTD;
561185029Spjd    int     oSHIN;
562185029Spjd    int     oSHOUT;
563185029Spjd    int     oSHDIAG;
564249195Smm    int     oOLDSTD;
565185029Spjd    jmp_buf_t osetexit;
566185029Spjd    int	    my_reenter;
567185029Spjd    int     odidfds;
568185029Spjd#ifndef CLOSE_ON_EXEC
569185029Spjd    int	    odidcch;
570185029Spjd#endif /* CLOSE_ON_EXEC */
571185029Spjd    signalfun_t osigint, osigquit, osigterm;
572185029Spjd
573249195Smm    USE(t);
574185029Spjd    if (chkstop == 0 && setintr)
575219089Spjd	panystop(0);
576228103Smm    /*
577228103Smm     * Hmm, we don't really want to do that now because we might
578228103Smm     * fail, but what is the choice
579228103Smm     */
580228103Smm    rechist(NULL, adrof(STRsavehist) != NULL);
581228103Smm
582228103Smm
583228103Smm    osigint  = signal(SIGINT, parintr);
584228103Smm    osigquit = signal(SIGQUIT, parintr);
585228103Smm    osigterm = signal(SIGTERM, parterm);
586228103Smm
587249195Smm    odidfds = didfds;
588228103Smm#ifndef CLOSE_ON_EXEC
589228103Smm    odidcch = didcch;
590228103Smm#endif /* CLOSE_ON_EXEC */
591228103Smm    oSHIN = SHIN;
592228103Smm    oSHOUT = SHOUT;
593228103Smm    oSHDIAG = SHDIAG;
594228103Smm    oOLDSTD = OLDSTD;
595228103Smm
596219089Spjd    (void)close_on_exec (saveIN = dcopy(SHIN, -1), 1);
597219089Spjd    (void)close_on_exec (saveOUT = dcopy(SHOUT, -1), 1);
598249195Smm    (void)close_on_exec (saveDIAG = dcopy(SHDIAG, -1), 1);
599219089Spjd    (void)close_on_exec (saveSTD = dcopy(OLDSTD, -1), 1);
600219089Spjd
601219089Spjd    lshift(kp->t_dcom, 1);
602219089Spjd
603249195Smm    getexit(osetexit);
604219089Spjd
605185029Spjd    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
606185029Spjd#ifdef cray
607185029Spjd    my_reenter = 1;             /* assume non-zero return val */
608185029Spjd    if (setexit() == 0) {
609185029Spjd        my_reenter = 0;         /* Oh well, we were wrong */
610185029Spjd#else /* !cray */
611185029Spjd    if ((my_reenter = setexit()) == 0) {
612185029Spjd#endif /* cray */
613185029Spjd	(void)close_on_exec (SHIN = dcopy(0, -1), 1);
614185029Spjd	(void)close_on_exec (SHOUT = dcopy(1, -1), 1);
615185029Spjd	(void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
616185029Spjd#ifndef CLOSE_ON_EXEC
617185029Spjd	didcch = 0;
618185029Spjd#endif /* CLOSE_ON_EXEC */
619185029Spjd	didfds = 0;
620185029Spjd	/*
621185029Spjd	 * Decrement the shell level
622185029Spjd	 */
623185029Spjd	shlvl(-1);
624209962Smm#ifdef WINNT_NATIVE
625209962Smm	__nt_really_exec=1;
626209962Smm#endif /* WINNT_NATIVE */
627209962Smm	doexec(kp, 1);
628209962Smm    }
629209962Smm
630209962Smm    (void) sigset(SIGINT, osigint);
631209962Smm    (void) sigset(SIGQUIT, osigquit);
632209962Smm    (void) sigset(SIGTERM, osigterm);
633209962Smm
634209962Smm    doneinp = 0;
635209962Smm#ifndef CLOSE_ON_EXEC
636209962Smm    didcch = odidcch;
637209962Smm#endif /* CLOSE_ON_EXEC */
638209962Smm    didfds = odidfds;
639209962Smm    (void) close(SHIN);
640209962Smm    (void) close(SHOUT);
641209962Smm    (void) close(SHDIAG);
642209962Smm    (void) close(OLDSTD);
643209962Smm    (void)close_on_exec(SHIN = dmove(saveIN, oSHIN), 1);
644209962Smm    (void)close_on_exec(SHOUT = dmove(saveOUT, oSHOUT), 1);
645209962Smm    (void)close_on_exec(SHDIAG = dmove(saveDIAG, oSHDIAG), 1);
646209962Smm    (void)close_on_exec(OLDSTD = dmove(saveSTD, oOLDSTD), 1);
647209962Smm
648209962Smm    resexit(osetexit);
649185029Spjd    if (my_reenter)
650185029Spjd	stderror(ERR_SILENT);
651185029Spjd}
652185029Spjd
653236884Smmvoid
654209962Smmxechoit(t)
655185029Spjd    Char  **t;
656185029Spjd{
657185029Spjd    if (adrof(STRecho)) {
658185029Spjd	int odidfds = didfds;
659209962Smm	flush();
660236884Smm	haderr = 1;
661209962Smm	didfds = 0;
662219089Spjd	blkpr(t), xputchar('\n');
663219089Spjd	flush();
664219089Spjd	didfds = odidfds;
665209962Smm	haderr = 0;
666209962Smm    }
667236884Smm}
668236884Smm
669236884Smm/*ARGSUSED*/
670236884Smmvoid
671236884Smmdohash(vv, c)
672236884Smm    Char **vv;
673236884Smm    struct command *c;
674236884Smm{
675236884Smm#ifdef COMMENT
676236884Smm    struct stat stb;
677236884Smm#endif
678236884Smm    DIR    *dirp;
679236884Smm    struct dirent *dp;
680236884Smm    int     i = 0;
681236884Smm    struct varent *v = adrof(STRpath);
682236884Smm    Char  **pv;
683236884Smm    int hashval;
684236884Smm#ifdef WINNT_NATIVE
685236884Smm    int is_windir; /* check if it is the windows directory */
686236884Smm    USE(hashval);
687236884Smm#endif /* WINNT_NATIVE */
688248571Smm
689248571Smm    USE(c);
690236884Smm#ifdef FASTHASH
691236884Smm    if (vv && vv[1]) {
692236884Smm        uhashlength = atoi(short2str(vv[1]));
693236884Smm        if (vv[2]) {
694236884Smm	    uhashwidth = atoi(short2str(vv[2]));
695209962Smm	    if ((uhashwidth != sizeof(unsigned char)) &&
696209962Smm	        (uhashwidth != sizeof(unsigned short)) &&
697209962Smm	        (uhashwidth != sizeof(unsigned long)))
698209962Smm	        uhashwidth = 0;
699236884Smm	    if (vv[3])
700248571Smm		hashdebug = atoi(short2str(vv[3]));
701248571Smm        }
702236884Smm    }
703236884Smm
704236884Smm    if (uhashwidth)
705185029Spjd	hashwidth = uhashwidth;
706185029Spjd    else {
707185029Spjd	hashwidth = 0;
708185029Spjd	if (v == NULL)
709185029Spjd	    return;
710185029Spjd	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
711185029Spjd	    continue;
712185029Spjd	if (hashwidth <= widthof(unsigned char))
713185029Spjd	    hashwidth = sizeof(unsigned char);
714185029Spjd	else if (hashwidth <= widthof(unsigned short))
715185029Spjd	    hashwidth = sizeof(unsigned short);
716185029Spjd	else if (hashwidth <= widthof(unsigned int))
717185029Spjd	    hashwidth = sizeof(unsigned int);
718185029Spjd	else
719185029Spjd	    hashwidth = sizeof(unsigned long);
720185029Spjd    }
721239620Smm
722239620Smm    if (uhashlength)
723248571Smm	hashlength = uhashlength;
724239620Smm    else
725248571Smm        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
726248571Smm
727239620Smm    if (xhash)
728239620Smm        xfree((ptr_t) xhash);
729239620Smm    xhash = (unsigned long *) xcalloc((size_t) (hashlength * hashwidth),
730239620Smm				      (size_t) 1);
731239620Smm#endif /* FASTHASH */
732239620Smm
733239620Smm    (void) getusername(NULL);	/* flush the tilde cashe */
734239620Smm    tw_cmd_free();
735249195Smm    havhash = 1;
736239620Smm    if (v == NULL)
737239620Smm	return;
738239620Smm    for (pv = v->vec; pv && *pv; pv++, i++) {
739239620Smm	if (!ABSOLUTEP(pv[0]))
740239620Smm	    continue;
741239620Smm	dirp = opendir(short2str(*pv));
742239620Smm	if (dirp == NULL)
743248571Smm	    continue;
744239620Smm#ifdef COMMENT			/* this isn't needed.  opendir won't open
745248571Smm				 * non-dirs */
746248571Smm	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
747239620Smm	    (void) closedir(dirp);
748239620Smm	    continue;
749239620Smm	}
750239620Smm#endif
751239620Smm#ifdef WINNT_NATIVE
752239620Smm	is_windir = nt_check_if_windir(short2str(*pv));
753239620Smm#endif /* WINNT_NATIVE */
754239620Smm	while ((dp = readdir(dirp)) != NULL) {
755239620Smm	    if (dp->d_ino == 0)
756239620Smm		continue;
757239620Smm	    if (dp->d_name[0] == '.' &&
758248571Smm		(dp->d_name[1] == '\0' ||
759239620Smm		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
760239620Smm		continue;
761239620Smm#ifdef WINNT_NATIVE
762185029Spjd	    nt_check_name_and_hash(is_windir, dp->d_name, i);
763228103Smm#else /* !WINNT_NATIVE*/
764228103Smm#if defined(_UWIN) || defined(__CYGWIN__)
765228103Smm	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
766228103Smm	     * the file with the .exe, .com, .bat extension
767228103Smm	     */
768228103Smm	    {
769228103Smm		size_t	ext = strlen(dp->d_name) - 4;
770228103Smm		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
771228103Smm				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
772228103Smm				  strcasecmp(&dp->d_name[ext], ".com") == 0))
773228103Smm		    {
774239620Smm			dp->d_name[ext] = '\0';
775239620Smm#if defined(__CYGWIN__)
776228103Smm			strlwr(dp->d_name);
777254074Sdelphij#endif /* __CYGWIN__ */
778239620Smm		    }
779239620Smm	    }
780228103Smm#endif /* _UWIN || __CYGWIN__ */
781248571Smm# ifdef FASTHASH
782248571Smm	    hashval = hashname(str2short(dp->d_name));
783228103Smm	    bis(hashval, i);
784239620Smm	    if (hashdebug & 1)
785239620Smm	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
786239620Smm		        hashname(str2short(dp->d_name)), i, dp->d_name);
787239620Smm# else /* OLD HASH */
788228103Smm	    hashval = hash(hashname(str2short(dp->d_name)), i);
789239620Smm	    bis(xhash, hashval);
790254074Sdelphij# endif /* FASTHASH */
791228103Smm	    /* tw_add_comm_name (dp->d_name); */
792239620Smm#endif /* WINNT_NATIVE */
793228103Smm	}
794228103Smm	(void) closedir(dirp);
795228103Smm    }
796185029Spjd}
797168404Spjd
798168404Spjd/*ARGSUSED*/
799168404Spjdvoid
800168404Spjddounhash(v, c)
801168404Spjd    Char **v;
802168404Spjd    struct command *c;
803168404Spjd{
804168404Spjd    USE(c);
805168404Spjd    USE(v);
806168404Spjd    havhash = 0;
807168404Spjd#ifdef FASTHASH
808168404Spjd    if (xhash) {
809168404Spjd       xfree((ptr_t) xhash);
810168404Spjd       xhash = NULL;
811168404Spjd    }
812168404Spjd#endif /* FASTHASH */
813168404Spjd}
814168404Spjd
815168404Spjd/*ARGSUSED*/
816168404Spjdvoid
817168404Spjdhashstat(v, c)
818168404Spjd    Char **v;
819168404Spjd    struct command *c;
820168404Spjd{
821168404Spjd    USE(c);
822168404Spjd    USE(v);
823168404Spjd#ifdef FASTHASH
824168404Spjd   if (havhash && hashlength && hashwidth)
825168404Spjd      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
826168404Spjd	      hashlength, hashwidth*8);
827168404Spjd   if (hashdebug)
828168404Spjd      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
829168404Spjd#endif /* FASTHASH */
830168404Spjd#ifdef VFORK
831168404Spjd   if (hits + misses)
832168404Spjd      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
833168404Spjd	      hits, misses, 100 * hits / (hits + misses));
834168404Spjd#endif
835168404Spjd}
836168404Spjd
837168404Spjd
838168404Spjd/*
839258631Savg * Hash a command name.
840258631Savg */
841168404Spjdint
842258631Savghashname(cp)
843258631Savg    Char *cp;
844258631Savg{
845258631Savg    unsigned long h;
846258631Savg
847258631Savg    for (h = 0; *cp; cp++)
848258630Savg	h = hash(h, *cp);
849219089Spjd    return ((int) h);
850168404Spjd}
851258631Savg
852258631Savgstatic int
853258631Savgiscommand(name)
854258631Savg    Char   *name;
855258631Savg{
856168404Spjd    Char **pv;
857258631Savg    Char *sav;
858168404Spjd    struct varent *v;
859258631Savg    int slash = any(short2str(name), '/');
860258631Savg    int hashval, i;
861219089Spjd
862258631Savg    v = adrof(STRpath);
863258631Savg    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
864219089Spjd	pv = justabs;
865258631Savg    else
866258631Savg	pv = v->vec;
867258631Savg    sav = Strspl(STRslash, name);	/* / command name for postpending */
868258631Savg    hashval = havhash ? hashname(name) : 0;
869258631Savg    i = 0;
870219089Spjd    do {
871258631Savg	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
872258631Savg#ifdef FASTHASH
873258631Savg	    if (!bit(hashval, i))
874258631Savg		goto cont;
875258631Savg#else /* OLDHASH */
876258631Savg	    int hashval1 = hash(hashval, i);
877258631Savg	    if (!bit(xhash, hashval1))
878258631Savg		goto cont;
879258631Savg#endif /* FASTHASH */
880258631Savg	}
881258631Savg	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
882258631Savg	    if (executable(NULL, name, 0)) {
883258631Savg		xfree((ptr_t) sav);
884258631Savg		return i + 1;
885258631Savg	    }
886258631Savg	}
887258631Savg	else {
888258631Savg	    if (executable(*pv, sav, 0)) {
889258631Savg		xfree((ptr_t) sav);
890258631Savg		return i + 1;
891258631Savg	    }
892258631Savg	}
893258631Savgcont:
894258631Savg	pv++;
895258631Savg	i++;
896219089Spjd    } while (*pv);
897258631Savg    xfree((ptr_t) sav);
898258631Savg    return 0;
899258631Savg}
900219089Spjd
901258631Savg/* Also by:
902258631Savg *  Andreas Luik <luik@isaak.isa.de>
903258631Savg *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
904258631Savg *  Azenberstr. 35
905258631Savg *  D-7000 Stuttgart 1
906258631Savg *  West-Germany
907258631Savg * is the executable() routine below and changes to iscommand().
908258631Savg * Thanks again!!
909258631Savg */
910258631Savg
911258631Savg#ifndef WINNT_NATIVE
912219089Spjd/*
913219089Spjd * executable() examines the pathname obtained by concatenating dir and name
914219089Spjd * (dir may be NULL), and returns 1 either if it is executable by us, or
915219089Spjd * if dir_ok is set and the pathname refers to a directory.
916258631Savg * This is a bit kludgy, but in the name of optimization...
917258631Savg */
918258631Savgint
919258631Savgexecutable(dir, name, dir_ok)
920258631Savg    Char   *dir, *name;
921258631Savg    int    dir_ok;
922258631Savg{
923258631Savg    struct stat stbuf;
924258631Savg    Char    path[MAXPATHLEN + 1];
925258631Savg    char   *strname;
926258631Savg    (void) memset(path, 0, sizeof(path));
927258631Savg
928258631Savg    if (dir && *dir) {
929258631Savg	copyn(path, dir, MAXPATHLEN);
930258631Savg	catn(path, name, MAXPATHLEN);
931258631Savg	strname = short2str(path);
932258631Savg    }
933258631Savg    else
934258631Savg	strname = short2str(name);
935258631Savg
936258631Savg    return (stat(strname, &stbuf) != -1 &&
937258631Savg	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
938258631Savg	     (S_ISREG(stbuf.st_mode) &&
939258631Savg    /* save time by not calling access() in the hopeless case */
940258631Savg	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
941258631Savg	      access(strname, X_OK) == 0
942258631Savg	)));
943258631Savg}
944258631Savg#endif /*!WINNT_NATIVE*/
945258631Savg
946258631Savgint
947258631Savgtellmewhat(lexp, str)
948258631Savg    struct wordent *lexp;
949258631Savg    Char *str;
950258631Savg{
951258631Savg    int i;
952258631Savg    struct biltins *bptr;
953258631Savg    struct wordent *sp = lexp->next;
954258631Savg    int    aliased = 0, found;
955258631Savg    Char   *s0, *s1, *s2, *cmd;
956258631Savg    Char    qc;
957258631Savg
958258631Savg    if (adrof1(sp->word, &aliases)) {
959258631Savg	alias(lexp);
960219089Spjd	sp = lexp->next;
961219089Spjd	aliased = 1;
962185029Spjd    }
963185029Spjd
964258631Savg    s0 = sp->word;		/* to get the memory freeing right... */
965219089Spjd
966219089Spjd    /* handle quoted alias hack */
967219089Spjd    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
968209962Smm	(sp->word)++;
969219089Spjd
970219089Spjd    /* do quoting, if it hasn't been done */
971219089Spjd    s1 = s2 = sp->word;
972219089Spjd    while (*s2)
973219089Spjd	switch (*s2) {
974219089Spjd	case '\'':
975209962Smm	case '"':
976219089Spjd	    qc = *s2++;
977219089Spjd	    while (*s2 && *s2 != qc)
978209962Smm		*s1++ = *s2++ | QUOTE;
979219089Spjd	    if (*s2)
980219089Spjd		s2++;
981209962Smm	    break;
982219089Spjd	case '\\':
983219089Spjd	    if (*++s2)
984219089Spjd		*s1++ = *s2++ | QUOTE;
985219089Spjd	    break;
986211931Smm	default:
987219089Spjd	    *s1++ = *s2++;
988219089Spjd	}
989219089Spjd    *s1 = '\0';
990219089Spjd
991219089Spjd    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
992219089Spjd	if (eq(sp->word, str2short(bptr->bname))) {
993219089Spjd	    if (str == NULL) {
994219089Spjd		if (aliased)
995219089Spjd		    prlex(lexp);
996219089Spjd		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
997219089Spjd			      sp->word);
998219089Spjd		flush();
999219089Spjd	    }
1000219089Spjd	    else
1001219089Spjd		(void) Strcpy(str, sp->word);
1002219089Spjd	    sp->word = s0;	/* we save and then restore this */
1003219089Spjd	    return TRUE;
1004219089Spjd	}
1005219089Spjd    }
1006219089Spjd#ifdef WINNT_NATIVE
1007219089Spjd    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1008219089Spjd	if (eq(sp->word, str2short(bptr->bname))) {
1009219089Spjd	    if (str == NULL) {
1010219089Spjd		if (aliased)
1011219089Spjd		    prlex(lexp);
1012219089Spjd		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
1013219089Spjd			      sp->word);
1014219089Spjd		flush();
1015219089Spjd	    }
1016219089Spjd	    else
1017219089Spjd		(void) Strcpy(str, sp->word);
1018219089Spjd	    sp->word = s0;	/* we save and then restore this */
1019219089Spjd	    return TRUE;
1020219089Spjd	}
1021219089Spjd    }
1022219089Spjd#endif /* WINNT_NATIVE*/
1023219089Spjd
1024219089Spjd    sp->word = cmd = globone(sp->word, G_IGNORE);
1025219089Spjd
1026219089Spjd    if ((i = iscommand(sp->word)) != 0) {
1027219089Spjd	Char **pv;
1028219089Spjd	struct varent *v;
1029219089Spjd	int    slash = any(short2str(sp->word), '/');
1030219089Spjd
1031219089Spjd	v = adrof(STRpath);
1032219089Spjd	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
1033219089Spjd	    pv = justabs;
1034219089Spjd	else
1035219089Spjd	    pv = v->vec;
1036219089Spjd
1037219089Spjd	while (--i)
1038219089Spjd	    pv++;
1039219089Spjd	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
1040219089Spjd	    if (!slash) {
1041219089Spjd		sp->word = Strspl(STRdotsl, sp->word);
1042219089Spjd		prlex(lexp);
1043219089Spjd		xfree((ptr_t) sp->word);
1044219089Spjd	    }
1045219089Spjd	    else
1046219089Spjd		prlex(lexp);
1047219089Spjd	}
1048219089Spjd	else {
1049219089Spjd	    s1 = Strspl(*pv, STRslash);
1050219089Spjd	    sp->word = Strspl(s1, sp->word);
1051219089Spjd	    xfree((ptr_t) s1);
1052219089Spjd	    if (str == NULL)
1053219089Spjd		prlex(lexp);
1054219089Spjd	    else
1055219089Spjd		(void) Strcpy(str, sp->word);
1056219089Spjd	    xfree((ptr_t) sp->word);
1057219089Spjd	}
1058219089Spjd	found = 1;
1059219089Spjd    }
1060219089Spjd    else {
1061219089Spjd	if (str == NULL) {
1062219089Spjd	    if (aliased)
1063219089Spjd		prlex(lexp);
1064219089Spjd	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
1065219089Spjd	    flush();
1066219089Spjd	}
1067219089Spjd	else
1068219089Spjd	    (void) Strcpy(str, sp->word);
1069219089Spjd	found = 0;
1070219089Spjd    }
1071219089Spjd    sp->word = s0;		/* we save and then restore this */
1072219089Spjd    xfree((ptr_t) cmd);
1073219089Spjd    return found;
1074209962Smm}
1075219089Spjd
1076219089Spjd/*
1077219089Spjd * Builtin to look at and list all places a command may be defined:
1078219089Spjd * aliases, shell builtins, and the path.
1079219089Spjd *
1080219089Spjd * Marc Horowitz <marc@mit.edu>
1081219089Spjd * MIT Student Information Processing Board
1082219089Spjd */
1083219089Spjd
1084185029Spjd/*ARGSUSED*/
1085168404Spjdvoid
1086219089Spjddowhere(v, c)
1087219089Spjd    Char **v;
1088168404Spjd    struct command *c;
1089219089Spjd{
1090219089Spjd    int found = 1;
1091219089Spjd    USE(c);
1092219089Spjd    for (v++; *v; v++)
1093219089Spjd	found &= find_cmd(*v, 1);
1094219089Spjd    /* Make status nonzero if any command is not found. */
1095240868Spjd    if (!found)
1096240868Spjd      set(STRstatus, Strsave(STR1), VAR_READWRITE);
1097240868Spjd}
1098240868Spjd
1099240868Spjdint
1100185029Spjdfind_cmd(cmd, prt)
1101185029Spjd    Char *cmd;
1102185029Spjd    int prt;
1103185029Spjd{
1104168404Spjd    struct varent *var;
1105168404Spjd    struct biltins *bptr;
1106168404Spjd    Char **pv;
1107168404Spjd    Char *sv;
1108168404Spjd    int hashval, i, ex, rval = 0;
1109168404Spjd
1110168404Spjd    if (prt && any(short2str(cmd), '/')) {
1111168404Spjd	xprintf(CGETS(13, 7, "where: / in command makes no sense\n"));
1112168404Spjd	return rval;
1113168404Spjd    }
1114168404Spjd
1115168404Spjd    /* first, look for an alias */
1116168404Spjd
1117168404Spjd    if (prt && adrof1(cmd, &aliases)) {
1118168404Spjd	if ((var = adrof1(cmd, &aliases)) != NULL) {
1119168404Spjd	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1120168404Spjd	    if (var->vec != NULL)
1121168404Spjd		blkpr(var->vec);
1122168404Spjd	    xputchar('\n');
1123168404Spjd	    rval = 1;
1124168404Spjd	}
1125209962Smm    }
1126168404Spjd
1127168404Spjd    /* next, look for a shell builtin */
1128240868Spjd
1129240868Spjd    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
1130240868Spjd	if (eq(cmd, str2short(bptr->bname))) {
1131240868Spjd	    rval = 1;
1132240868Spjd	    if (prt)
1133240868Spjd		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1134168404Spjd	    else
1135168404Spjd		return rval;
1136185029Spjd	}
1137185029Spjd    }
1138168404Spjd#ifdef WINNT_NATIVE
1139185029Spjd    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1140185029Spjd	if (eq(cmd, str2short(bptr->bname))) {
1141258631Savg	    rval = 1;
1142185029Spjd	    if (prt)
1143168404Spjd		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1144168404Spjd	    else
1145168404Spjd		return rval;
1146168404Spjd	}
1147168404Spjd    }
1148185029Spjd#endif /* WINNT_NATIVE*/
1149185029Spjd
1150185029Spjd    /* last, look through the path for the command */
1151168404Spjd
1152168404Spjd    if ((var = adrof(STRpath)) == NULL)
1153168404Spjd	return rval;
1154168404Spjd
1155168404Spjd    hashval = havhash ? hashname(cmd) : 0;
1156168404Spjd
1157168404Spjd    sv = Strspl(STRslash, cmd);
1158168404Spjd
1159168404Spjd    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
1160168404Spjd	if (havhash && !eq(*pv, STRdot)) {
1161219089Spjd#ifdef FASTHASH
1162219089Spjd	    if (!bit(hashval, i))
1163219089Spjd		continue;
1164219089Spjd#else /* OLDHASH */
1165219089Spjd	    int hashval1 = hash(hashval, i);
1166219089Spjd	    if (!bit(xhash, hashval1))
1167219089Spjd		continue;
1168219089Spjd#endif /* FASTHASH */
1169219089Spjd	}
1170219089Spjd	ex = executable(*pv, sv, 0);
1171219089Spjd#ifdef FASTHASH
1172219089Spjd	if (!ex && (hashdebug & 2)) {
1173219089Spjd	    xprintf(CGETS(13, 10, "hash miss: "));
1174219089Spjd	    ex = 1;	/* Force printing */
1175219089Spjd	}
1176219089Spjd#endif /* FASTHASH */
1177219089Spjd	if (ex) {
1178219089Spjd	    rval = 1;
1179219089Spjd	    if (prt) {
1180219089Spjd		xprintf("%S/", *pv);
1181219089Spjd		xprintf("%S\n", cmd);
1182219089Spjd	    }
1183219089Spjd	    else
1184219089Spjd		return rval;
1185219089Spjd	}
1186219089Spjd    }
1187219089Spjd    xfree((ptr_t) sv);
1188168404Spjd    return rval;
1189168404Spjd}
1190168404Spjd#ifdef WINNT_NATIVE
1191168404Spjdint hashval_extern(cp)
1192168404Spjd	Char *cp;
1193168404Spjd{
1194168404Spjd	return havhash?hashname(cp):0;
1195168404Spjd}
1196168404Spjdint bit_extern(val,i)
1197168404Spjd	int val;
1198168404Spjd	int i;
1199168404Spjd{
1200168404Spjd	return bit(val,i);
1201219089Spjd}
1202168404Spjdvoid bis_extern(val,i)
1203168404Spjd	int val;
1204168404Spjd	int i;
1205168404Spjd{
1206168404Spjd	bis(val,i);
1207168404Spjd}
1208168404Spjd#endif /* WINNT_NATIVE */
1209168404Spjd
1210185029Spjd