1/*
2 * sh.exec.c: Search, find, and execute a command!
3 */
4/*-
5 * Copyright (c) 1980, 1991 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32#include "sh.h"
33#include "tc.h"
34#include "tw.h"
35#ifdef WINNT_NATIVE
36#include <nt.const.h>
37#endif /*WINNT_NATIVE*/
38
39/*
40 * C shell
41 */
42
43#ifndef OLDHASH
44# define FASTHASH	/* Fast hashing is the default */
45#endif /* OLDHASH */
46
47/*
48 * System level search and execute of a command.
49 * We look in each directory for the specified command name.
50 * If the name contains a '/' then we execute only the full path name.
51 * If there is no search path then we execute only full path names.
52 */
53
54/*
55 * As we search for the command we note the first non-trivial error
56 * message for presentation to the user.  This allows us often
57 * to show that a file has the wrong mode/no access when the file
58 * is not in the last component of the search path, so we must
59 * go on after first detecting the error.
60 */
61static char *exerr;		/* Execution error message */
62static Char *expath;		/* Path for exerr */
63
64/*
65 * The two part hash function is designed to let texec() call the
66 * more expensive hashname() only once and the simple hash() several
67 * times (once for each path component checked).
68 * Byte size is assumed to be 8.
69 */
70#define BITS_PER_BYTE	8
71
72#ifdef FASTHASH
73/*
74 * xhash is an array of hash buckets which are used to hash execs.  If
75 * it is allocated (havhash true), then to tell if ``name'' is
76 * (possibly) present in the i'th component of the variable path, look
77 * at the [hashname(name)] bucket of size [hashwidth] bytes, in the [i
78 * mod size*8]'th bit.  The cache size is defaults to a length of 1024
79 * buckets, each 1 byte wide.  This implementation guarantees that
80 * objects n bytes wide will be aligned on n byte boundaries.
81 */
82# define HSHMUL		241
83
84static unsigned long *xhash = NULL;
85static unsigned int hashlength = 0, uhashlength = 0;
86static unsigned int hashwidth = 0, uhashwidth = 0;
87static int hashdebug = 0;
88
89# define hash(a, b)	(((a) * HSHMUL + (b)) % (hashlength))
90# define widthof(t)	(sizeof(t) * BITS_PER_BYTE)
91# define tbit(f, i, t)	(((t *) xhash)[(f)] &  \
92			 (1UL << (i & (widthof(t) - 1))))
93# define tbis(f, i, t)	(((t *) xhash)[(f)] |= \
94			 (1UL << (i & (widthof(t) - 1))))
95# define cbit(f, i)	tbit(f, i, unsigned char)
96# define cbis(f, i)	tbis(f, i, unsigned char)
97# define sbit(f, i)	tbit(f, i, unsigned short)
98# define sbis(f, i)	tbis(f, i, unsigned short)
99# define ibit(f, i)	tbit(f, i, unsigned int)
100# define ibis(f, i)	tbis(f, i, unsigned int)
101# define lbit(f, i)	tbit(f, i, unsigned long)
102# define lbis(f, i)	tbis(f, i, unsigned long)
103
104# define bit(f, i) (hashwidth==sizeof(unsigned char)  ? cbit(f,i) : \
105 		    ((hashwidth==sizeof(unsigned short) ? sbit(f,i) : \
106		     ((hashwidth==sizeof(unsigned int)   ? ibit(f,i) : \
107		     lbit(f,i))))))
108# define bis(f, i) (hashwidth==sizeof(unsigned char)  ? cbis(f,i) : \
109 		    ((hashwidth==sizeof(unsigned short) ? sbis(f,i) : \
110		     ((hashwidth==sizeof(unsigned int)   ? ibis(f,i) : \
111		     lbis(f,i))))))
112#else /* OLDHASH */
113/*
114 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
115 * to hash execs.  If it is allocated (havhash true), then to tell
116 * whether ``name'' is (possibly) present in the i'th component
117 * of the variable path, you look at the bit in xhash indexed by
118 * hash(hashname("name"), i).  This is setup automatically
119 * after .login is executed, and recomputed whenever ``path'' is
120 * changed.
121 */
122# define HSHSIZ		8192	/* 1k bytes */
123# define HSHMASK		(HSHSIZ - 1)
124# define HSHMUL		243
125static char xhash[HSHSIZ / BITS_PER_BYTE];
126
127# define hash(a, b)	(((a) * HSHMUL + (b)) & HSHMASK)
128# define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
129# define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
130
131#endif /* FASTHASH */
132
133#ifdef VFORK
134static int hits, misses;
135#endif /* VFORK */
136
137/* Dummy search path for just absolute search when no path */
138static Char *justabs[] = {STRNULL, 0};
139
140static	void	pexerr		(void) __attribute__((__noreturn__));
141static	void	texec		(Char *, Char **);
142int	hashname	(Char *);
143static	int 	iscommand	(Char *);
144
145void
146doexec(struct command *t, int do_glob)
147{
148    Char *dp, **pv, **opv, **av, *sav;
149    struct varent *v;
150    int slash, gflag, rehashed;
151    int hashval, i;
152    Char   *blk[2];
153
154    /*
155     * Glob the command name. We will search $path even if this does something,
156     * as in sh but not in csh.  One special case: if there is no PATH, then we
157     * execute only commands which start with '/'.
158     */
159    blk[0] = t->t_dcom[0];
160    blk[1] = 0;
161    gflag = 0;
162    if (do_glob)
163	gflag = tglob(blk);
164    if (gflag) {
165	pv = globall(blk, gflag);
166	if (pv == 0) {
167	    setname(short2str(blk[0]));
168	    stderror(ERR_NAME | ERR_NOMATCH);
169	}
170    }
171    else
172	pv = saveblk(blk);
173    cleanup_push(pv, blk_cleanup);
174
175    trim(pv);
176
177    exerr = 0;
178    expath = Strsave(pv[0]);
179#ifdef VFORK
180    Vexpath = expath;
181#endif /* VFORK */
182
183    v = adrof(STRpath);
184    if (v == 0 && expath[0] != '/' && expath[0] != '.')
185	pexerr();
186    slash = any(short2str(expath), '/');
187
188    /*
189     * Glob the argument list, if necessary. Otherwise trim off the quote bits.
190     */
191    gflag = 0;
192    av = &t->t_dcom[1];
193    if (do_glob)
194	gflag = tglob(av);
195    if (gflag) {
196	av = globall(av, gflag);
197	if (av == 0) {
198	    setname(short2str(expath));
199	    stderror(ERR_NAME | ERR_NOMATCH);
200	}
201    }
202    else
203	av = saveblk(av);
204
205    blkfree(t->t_dcom);
206    cleanup_ignore(pv);
207    cleanup_until(pv);
208    t->t_dcom = blkspl(pv, av);
209    xfree(pv);
210    xfree(av);
211    av = t->t_dcom;
212    trim(av);
213
214    if (*av == NULL || **av == '\0')
215	pexerr();
216
217    xechoit(av);		/* Echo command if -x */
218#ifdef CLOSE_ON_EXEC
219    /*
220     * Since all internal file descriptors are set to close on exec, we don't
221     * need to close them explicitly here.  Just reorient ourselves for error
222     * messages.
223     */
224    SHIN = 0;
225    SHOUT = 1;
226    SHDIAG = 2;
227    OLDSTD = 0;
228    isoutatty = isatty(SHOUT);
229    isdiagatty = isatty(SHDIAG);
230#else
231    closech();			/* Close random fd's */
232#endif
233    /*
234     * We must do this AFTER any possible forking (like `foo` in glob) so that
235     * this shell can still do subprocesses.
236     */
237    {
238	sigset_t set;
239	sigemptyset(&set);
240	sigaddset(&set, SIGINT);
241	sigaddset(&set, SIGCHLD);
242	sigprocmask(SIG_UNBLOCK, &set, NULL);
243    }
244    pintr_disabled = 0;
245    pchild_disabled = 0;
246
247    /*
248     * If no path, no words in path, or a / in the filename then restrict the
249     * command search.
250     */
251    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
252	opv = justabs;
253    else
254	opv = v->vec;
255    sav = Strspl(STRslash, *av);/* / command name for postpending */
256#ifndef VFORK
257    cleanup_push(sav, xfree);
258#else /* VFORK */
259    Vsav = sav;
260#endif /* VFORK */
261    hashval = havhash ? hashname(*av) : 0;
262
263    rehashed = 0;
264retry:
265    pv = opv;
266    i = 0;
267#ifdef VFORK
268    hits++;
269#endif /* VFORK */
270    do {
271	/*
272	 * Try to save time by looking at the hash table for where this command
273	 * could be.  If we are doing delayed hashing, then we put the names in
274	 * one at a time, as the user enters them.  This is kinda like Korn
275	 * Shell's "tracked aliases".
276	 */
277	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
278#ifdef FASTHASH
279	    if (!bit(hashval, i))
280		goto cont;
281#else /* OLDHASH */
282	    int hashval1 = hash(hashval, i);
283	    if (!bit(xhash, hashval1))
284		goto cont;
285#endif /* FASTHASH */
286	}
287	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
288	    texec(*av, av);
289	else {
290	    dp = Strspl(*pv, sav);
291#ifndef VFORK
292	    cleanup_push(dp, xfree);
293#else /* VFORK */
294	    Vdp = dp;
295#endif /* VFORK */
296
297	    texec(dp, av);
298#ifndef VFORK
299	    cleanup_until(dp);
300#else /* VFORK */
301	    Vdp = 0;
302	    xfree(dp);
303#endif /* VFORK */
304	}
305#ifdef VFORK
306	misses++;
307#endif /* VFORK */
308cont:
309	pv++;
310	i++;
311    } while (*pv);
312#ifdef VFORK
313    hits--;
314#endif /* VFORK */
315    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
316	dohash(NULL, NULL);
317	rehashed = 1;
318	goto retry;
319    }
320#ifndef VFORK
321    cleanup_until(sav);
322#else /* VFORK */
323    Vsav = 0;
324    xfree(sav);
325#endif /* VFORK */
326    pexerr();
327}
328
329static void
330pexerr(void)
331{
332    /* Couldn't find the damn thing */
333    if (expath) {
334	setname(short2str(expath));
335#ifdef VFORK
336	Vexpath = 0;
337#endif /* VFORK */
338	xfree(expath);
339	expath = 0;
340    }
341    else
342	setname("");
343    if (exerr)
344	stderror(ERR_NAME | ERR_STRING, exerr);
345    stderror(ERR_NAME | ERR_COMMAND);
346}
347
348/*
349 * Execute command f, arg list t.
350 * Record error message if not found.
351 * Also do shell scripts here.
352 */
353static void
354texec(Char *sf, Char **st)
355{
356    char **t;
357    char *f;
358    struct varent *v;
359    Char  **vp;
360    Char   *lastsh[2];
361    char    pref[2];
362    int     fd;
363    Char   *st0, **ost;
364
365    /* The order for the conversions is significant */
366    t = short2blk(st);
367    f = short2str(sf);
368#ifdef VFORK
369    Vt = t;
370#endif /* VFORK */
371    errno = 0;			/* don't use a previous error */
372#ifdef apollo
373    /*
374     * If we try to execute an nfs mounted directory on the apollo, we
375     * hang forever. So until apollo fixes that..
376     */
377    {
378	struct stat stb;
379	if (stat(f, &stb) == 0 && S_ISDIR(stb.st_mode))
380	    errno = EISDIR;
381    }
382    if (errno == 0)
383#endif /* apollo */
384    {
385#ifdef ISC_POSIX_EXEC_BUG
386	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
387#endif /* ISC_POSIX_EXEC_BUG */
388	(void) execv(f, t);
389#ifdef ISC_POSIX_EXEC_BUG
390	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
391#endif /* ISC_POSIX_EXEC_BUG */
392    }
393#ifdef VFORK
394    Vt = 0;
395#endif /* VFORK */
396    blkfree((Char **) t);
397    switch (errno) {
398
399    case ENOEXEC:
400#ifdef WINNT_NATIVE
401		nt_feed_to_cmd(f,t);
402#endif /* WINNT_NATIVE */
403	/*
404	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
405	 * it, don't feed it to the shell if it looks like a binary!
406	 */
407	if ((fd = xopen(f, O_RDONLY|O_LARGEFILE)) != -1) {
408	    int nread;
409	    if ((nread = xread(fd, pref, 2)) == 2) {
410		if (!isprint((unsigned char)pref[0]) &&
411		    (pref[0] != '\n' && pref[0] != '\t')) {
412		    int err;
413
414		    err = errno;
415		    xclose(fd);
416		    /*
417		     * We *know* what ENOEXEC means.
418		     */
419		    stderror(ERR_ARCH, f, strerror(err));
420		}
421	    }
422	    else if (nread < 0) {
423#ifdef convex
424		int err;
425
426		err = errno;
427		xclose(fd);
428		/* need to print error incase the file is migrated */
429		stderror(ERR_SYSTEM, f, strerror(err));
430#endif
431	    }
432#ifdef _PATH_BSHELL
433	    else {
434		pref[0] = '#';
435		pref[1] = '\0';
436	    }
437#endif
438	}
439#ifdef HASHBANG
440	if (fd == -1 ||
441	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
442#endif /* HASHBANG */
443	/*
444	 * If there is an alias for shell, then put the words of the alias in
445	 * front of the argument list replacing the command name. Note no
446	 * interpretation of the words at this point.
447	 */
448	    v = adrof1(STRshell, &aliases);
449	    if (v == NULL || v->vec == NULL) {
450		vp = lastsh;
451		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
452		vp[1] = NULL;
453#ifdef _PATH_BSHELL
454		if (fd != -1
455# ifndef ISC	/* Compatible with ISC's /bin/csh */
456		    && pref[0] != '#'
457# endif /* ISC */
458		    )
459		    vp[0] = STR_BSHELL;
460#endif
461		vp = saveblk(vp);
462	    }
463	    else
464		vp = saveblk(v->vec);
465#ifdef HASHBANG
466	}
467#endif /* HASHBANG */
468	if (fd != -1)
469	    xclose(fd);
470
471	st0 = st[0];
472	st[0] = sf;
473	ost = st;
474	st = blkspl(vp, st);	/* Splice up the new arglst */
475	ost[0] = st0;
476	sf = *st;
477	/* The order for the conversions is significant */
478	t = short2blk(st);
479	f = short2str(sf);
480	xfree(st);
481	blkfree((Char **) vp);
482#ifdef VFORK
483	Vt = t;
484#endif /* VFORK */
485#ifdef ISC_POSIX_EXEC_BUG
486	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
487#endif /* ISC_POSIX_EXEC_BUG */
488	(void) execv(f, t);
489#ifdef ISC_POSIX_EXEC_BUG
490	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
491#endif /* ISC_POSIX_EXEC_BUG */
492#ifdef VFORK
493	Vt = 0;
494#endif /* VFORK */
495	blkfree((Char **) t);
496	/* The sky is falling, the sky is falling! */
497	stderror(ERR_SYSTEM, f, strerror(errno));
498	break;
499
500    case ENOMEM:
501	stderror(ERR_SYSTEM, f, strerror(errno));
502	break;
503
504#ifdef _IBMR2
505    case 0:			/* execv fails and returns 0! */
506#endif /* _IBMR2 */
507    case ENOENT:
508	break;
509
510    default:
511	if (exerr == 0) {
512	    exerr = strerror(errno);
513	    xfree(expath);
514	    expath = Strsave(sf);
515#ifdef VFORK
516	    Vexpath = expath;
517#endif /* VFORK */
518	}
519	break;
520    }
521}
522
523struct execash_state
524{
525    int saveIN, saveOUT, saveDIAG, saveSTD;
526    int SHIN, SHOUT, SHDIAG, OLDSTD;
527    int didfds;
528#ifndef CLOSE_ON_EXEC
529    int didcch;
530#endif
531    struct sigaction sigint, sigquit, sigterm;
532};
533
534static void
535execash_cleanup(void *xstate)
536{
537    struct execash_state *state;
538
539    state = xstate;
540    sigaction(SIGINT, &state->sigint, NULL);
541    sigaction(SIGQUIT, &state->sigquit, NULL);
542    sigaction(SIGTERM, &state->sigterm, NULL);
543
544    doneinp = 0;
545#ifndef CLOSE_ON_EXEC
546    didcch = state->didcch;
547#endif /* CLOSE_ON_EXEC */
548    didfds = state->didfds;
549    xclose(SHIN);
550    xclose(SHOUT);
551    xclose(SHDIAG);
552    xclose(OLDSTD);
553    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
554    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
555    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
556    close_on_exec(OLDSTD = dmove(state->saveSTD, state->OLDSTD), 1);
557}
558
559/*ARGSUSED*/
560void
561execash(Char **t, struct command *kp)
562{
563    struct execash_state state;
564
565    USE(t);
566    if (chkstop == 0 && setintr)
567	panystop(0);
568    /*
569     * Hmm, we don't really want to do that now because we might
570     * fail, but what is the choice
571     */
572    rechist(NULL, adrof(STRsavehist) != NULL);
573
574
575    sigaction(SIGINT, &parintr, &state.sigint);
576    sigaction(SIGQUIT, &parintr, &state.sigquit);
577    sigaction(SIGTERM, &parterm, &state.sigterm);
578
579    state.didfds = didfds;
580#ifndef CLOSE_ON_EXEC
581    state.didcch = didcch;
582#endif /* CLOSE_ON_EXEC */
583    state.SHIN = SHIN;
584    state.SHOUT = SHOUT;
585    state.SHDIAG = SHDIAG;
586    state.OLDSTD = OLDSTD;
587
588    (void)close_on_exec (state.saveIN = dcopy(SHIN, -1), 1);
589    (void)close_on_exec (state.saveOUT = dcopy(SHOUT, -1), 1);
590    (void)close_on_exec (state.saveDIAG = dcopy(SHDIAG, -1), 1);
591    (void)close_on_exec (state.saveSTD = dcopy(OLDSTD, -1), 1);
592
593    lshift(kp->t_dcom, 1);
594
595    (void)close_on_exec (SHIN = dcopy(0, -1), 1);
596    (void)close_on_exec (SHOUT = dcopy(1, -1), 1);
597    (void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
598#ifndef CLOSE_ON_EXEC
599    didcch = 0;
600#endif /* CLOSE_ON_EXEC */
601    didfds = 0;
602    cleanup_push(&state, execash_cleanup);
603
604    /*
605     * Decrement the shell level, if not in a subshell
606     */
607    if (mainpid == getpid())
608	shlvl(-1);
609#ifdef WINNT_NATIVE
610    __nt_really_exec=1;
611#endif /* WINNT_NATIVE */
612    doexec(kp, 1);
613
614    cleanup_until(&state);
615}
616
617void
618xechoit(Char **t)
619{
620    if (adrof(STRecho)) {
621	int odidfds = didfds;
622	flush();
623	haderr = 1;
624	didfds = 0;
625	blkpr(t), xputchar('\n');
626	flush();
627	didfds = odidfds;
628	haderr = 0;
629    }
630}
631
632/*ARGSUSED*/
633void
634dohash(Char **vv, struct command *c)
635{
636#ifdef COMMENT
637    struct stat stb;
638#endif
639    DIR    *dirp;
640    struct dirent *dp;
641    int     i = 0;
642    struct varent *v = adrof(STRpath);
643    Char  **pv;
644    int hashval;
645#ifdef WINNT_NATIVE
646    int is_windir; /* check if it is the windows directory */
647    USE(hashval);
648#endif /* WINNT_NATIVE */
649
650    USE(c);
651#ifdef FASTHASH
652    if (vv && vv[1]) {
653        uhashlength = atoi(short2str(vv[1]));
654        if (vv[2]) {
655	    uhashwidth = atoi(short2str(vv[2]));
656	    if ((uhashwidth != sizeof(unsigned char)) &&
657	        (uhashwidth != sizeof(unsigned short)) &&
658	        (uhashwidth != sizeof(unsigned long)))
659	        uhashwidth = 0;
660	    if (vv[3])
661		hashdebug = atoi(short2str(vv[3]));
662        }
663    }
664
665    if (uhashwidth)
666	hashwidth = uhashwidth;
667    else {
668	hashwidth = 0;
669	if (v == NULL)
670	    return;
671	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
672	    continue;
673	if (hashwidth <= widthof(unsigned char))
674	    hashwidth = sizeof(unsigned char);
675	else if (hashwidth <= widthof(unsigned short))
676	    hashwidth = sizeof(unsigned short);
677	else if (hashwidth <= widthof(unsigned int))
678	    hashwidth = sizeof(unsigned int);
679	else
680	    hashwidth = sizeof(unsigned long);
681    }
682
683    if (uhashlength)
684	hashlength = uhashlength;
685    else
686        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
687
688    xfree(xhash);
689    xhash = xcalloc(hashlength * hashwidth, 1);
690#endif /* FASTHASH */
691
692    (void) getusername(NULL);	/* flush the tilde cashe */
693    tw_cmd_free();
694    havhash = 1;
695    if (v == NULL)
696	return;
697    for (pv = v->vec; pv && *pv; pv++, i++) {
698	if (!ABSOLUTEP(pv[0]))
699	    continue;
700	dirp = opendir(short2str(*pv));
701	if (dirp == NULL)
702	    continue;
703	cleanup_push(dirp, opendir_cleanup);
704#ifdef COMMENT			/* this isn't needed.  opendir won't open
705				 * non-dirs */
706	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
707	    cleanup_until(dirp);
708	    continue;
709	}
710#endif
711#ifdef WINNT_NATIVE
712	is_windir = nt_check_if_windir(short2str(*pv));
713#endif /* WINNT_NATIVE */
714	while ((dp = readdir(dirp)) != NULL) {
715	    if (dp->d_ino == 0)
716		continue;
717	    if (dp->d_name[0] == '.' &&
718		(dp->d_name[1] == '\0' ||
719		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
720		continue;
721#ifdef WINNT_NATIVE
722	    nt_check_name_and_hash(is_windir, dp->d_name, i);
723#else /* !WINNT_NATIVE*/
724#if defined(_UWIN) || defined(__CYGWIN__)
725	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
726	     * the file with the .exe, .com, .bat extension
727	     *
728	     * Same for Cygwin, but only for .exe and .com extension.
729	     */
730	    {
731		ssize_t	ext = strlen(dp->d_name) - 4;
732		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
733#ifndef __CYGWIN__
734				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
735#endif
736				  strcasecmp(&dp->d_name[ext], ".com") == 0)) {
737#ifdef __CYGWIN__
738		    /* Also store the variation with extension. */
739		    hashval = hashname(str2short(dp->d_name));
740		    bis(hashval, i);
741#endif /* __CYGWIN__ */
742		    dp->d_name[ext] = '\0';
743		}
744	    }
745#endif /* _UWIN || __CYGWIN__ */
746# ifdef FASTHASH
747	    hashval = hashname(str2short(dp->d_name));
748	    bis(hashval, i);
749	    if (hashdebug & 1)
750	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
751		        hashname(str2short(dp->d_name)), i, dp->d_name);
752# else /* OLD HASH */
753	    hashval = hash(hashname(str2short(dp->d_name)), i);
754	    bis(xhash, hashval);
755# endif /* FASTHASH */
756	    /* tw_add_comm_name (dp->d_name); */
757#endif /* WINNT_NATIVE */
758	}
759	cleanup_until(dirp);
760    }
761}
762
763/*ARGSUSED*/
764void
765dounhash(Char **v, struct command *c)
766{
767    USE(c);
768    USE(v);
769    havhash = 0;
770#ifdef FASTHASH
771    xfree(xhash);
772    xhash = NULL;
773#endif /* FASTHASH */
774}
775
776/*ARGSUSED*/
777void
778hashstat(Char **v, struct command *c)
779{
780    USE(c);
781    USE(v);
782#ifdef FASTHASH
783   if (havhash && hashlength && hashwidth)
784      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
785	      hashlength, hashwidth*8);
786   if (hashdebug)
787      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
788#endif /* FASTHASH */
789#ifdef VFORK
790   if (hits + misses)
791      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
792	      hits, misses, 100 * hits / (hits + misses));
793#endif
794}
795
796
797/*
798 * Hash a command name.
799 */
800int
801hashname(Char *cp)
802{
803    unsigned long h;
804
805    for (h = 0; *cp; cp++)
806	h = hash(h, *cp);
807    return ((int) h);
808}
809
810static int
811iscommand(Char *name)
812{
813    Char **opv, **pv;
814    Char *sav;
815    struct varent *v;
816    int slash = any(short2str(name), '/');
817    int hashval, rehashed, i;
818
819    v = adrof(STRpath);
820    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
821	opv = justabs;
822    else
823	opv = v->vec;
824    sav = Strspl(STRslash, name);	/* / command name for postpending */
825    hashval = havhash ? hashname(name) : 0;
826
827    rehashed = 0;
828retry:
829    pv = opv;
830    i = 0;
831    do {
832	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
833#ifdef FASTHASH
834	    if (!bit(hashval, i))
835		goto cont;
836#else /* OLDHASH */
837	    int hashval1 = hash(hashval, i);
838	    if (!bit(xhash, hashval1))
839		goto cont;
840#endif /* FASTHASH */
841	}
842	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
843	    if (executable(NULL, name, 0)) {
844		xfree(sav);
845		return i + 1;
846	    }
847	}
848	else {
849	    if (executable(*pv, sav, 0)) {
850		xfree(sav);
851		return i + 1;
852	    }
853	}
854cont:
855	pv++;
856	i++;
857    } while (*pv);
858    if (adrof(STRautorehash) && !rehashed && havhash && opv != justabs) {
859	dohash(NULL, NULL);
860	rehashed = 1;
861	goto retry;
862    }
863    xfree(sav);
864    return 0;
865}
866
867/* Also by:
868 *  Andreas Luik <luik@isaak.isa.de>
869 *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
870 *  Azenberstr. 35
871 *  D-7000 Stuttgart 1
872 *  West-Germany
873 * is the executable() routine below and changes to iscommand().
874 * Thanks again!!
875 */
876
877#ifndef WINNT_NATIVE
878/*
879 * executable() examines the pathname obtained by concatenating dir and name
880 * (dir may be NULL), and returns 1 either if it is executable by us, or
881 * if dir_ok is set and the pathname refers to a directory.
882 * This is a bit kludgy, but in the name of optimization...
883 */
884int
885executable(const Char *dir, const Char *name, int dir_ok)
886{
887    struct stat stbuf;
888    char   *strname;
889
890    if (dir && *dir) {
891	Char *path;
892
893	path = Strspl(dir, name);
894	strname = short2str(path);
895	xfree(path);
896    }
897    else
898	strname = short2str(name);
899
900    return (stat(strname, &stbuf) != -1 &&
901	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
902	     (S_ISREG(stbuf.st_mode) &&
903    /* save time by not calling access() in the hopeless case */
904	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
905	      access(strname, X_OK) == 0
906	)));
907}
908#endif /*!WINNT_NATIVE*/
909
910struct tellmewhat_s0_cleanup
911{
912    Char **dest, *val;
913};
914
915static void
916tellmewhat_s0_cleanup(void *xstate)
917{
918    struct tellmewhat_s0_cleanup *state;
919
920    state = xstate;
921    *state->dest = state->val;
922}
923
924int
925tellmewhat(struct wordent *lexp, Char **str)
926{
927    struct tellmewhat_s0_cleanup s0;
928    int i;
929    const struct biltins *bptr;
930    struct wordent *sp = lexp->next;
931    int    aliased = 0, found;
932    Char   *s1, *s2, *cmd;
933    Char    qc;
934
935    if (adrof1(sp->word, &aliases)) {
936	alias(lexp);
937	sp = lexp->next;
938	aliased = 1;
939    }
940
941    s0.dest = &sp->word;	/* to get the memory freeing right... */
942    s0.val = sp->word;
943    cleanup_push(&s0, tellmewhat_s0_cleanup);
944
945    /* handle quoted alias hack */
946    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
947	(sp->word)++;
948
949    /* do quoting, if it hasn't been done */
950    s1 = s2 = sp->word;
951    while (*s2)
952	switch (*s2) {
953	case '\'':
954	case '"':
955	    qc = *s2++;
956	    while (*s2 && *s2 != qc)
957		*s1++ = *s2++ | QUOTE;
958	    if (*s2)
959		s2++;
960	    break;
961	case '\\':
962	    if (*++s2)
963		*s1++ = *s2++ | QUOTE;
964	    break;
965	default:
966	    *s1++ = *s2++;
967	}
968    *s1 = '\0';
969
970    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
971	if (eq(sp->word, str2short(bptr->bname))) {
972	    if (str == NULL) {
973		if (aliased)
974		    prlex(lexp);
975		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
976			      sp->word);
977		flush();
978	    }
979	    else
980		*str = Strsave(sp->word);
981	    cleanup_until(&s0);
982	    return TRUE;
983	}
984    }
985#ifdef WINNT_NATIVE
986    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
987	if (eq(sp->word, str2short(bptr->bname))) {
988	    if (str == NULL) {
989		if (aliased)
990		    prlex(lexp);
991		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
992			      sp->word);
993		flush();
994	    }
995	    else
996		*str = Strsave(sp->word);
997	    cleanup_until(&s0);
998	    return TRUE;
999	}
1000    }
1001#endif /* WINNT_NATIVE*/
1002
1003    sp->word = cmd = globone(sp->word, G_IGNORE);
1004    cleanup_push(cmd, xfree);
1005
1006    if ((i = iscommand(sp->word)) != 0) {
1007	Char **pv;
1008	struct varent *v;
1009	int    slash = any(short2str(sp->word), '/');
1010
1011	v = adrof(STRpath);
1012	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
1013	    pv = justabs;
1014	else
1015	    pv = v->vec;
1016
1017	pv += i - 1;
1018	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
1019	    if (!slash) {
1020		sp->word = Strspl(STRdotsl, sp->word);
1021		cleanup_push(sp->word, xfree);
1022		prlex(lexp);
1023		cleanup_until(sp->word);
1024	    }
1025	    else
1026		prlex(lexp);
1027	}
1028	else {
1029	    s1 = Strspl(*pv, STRslash);
1030	    sp->word = Strspl(s1, sp->word);
1031	    xfree(s1);
1032	    cleanup_push(sp->word, xfree);
1033	    if (str == NULL)
1034		prlex(lexp);
1035	    else
1036		*str = Strsave(sp->word);
1037	    cleanup_until(sp->word);
1038	}
1039	found = 1;
1040    }
1041    else {
1042	if (str == NULL) {
1043	    if (aliased)
1044		prlex(lexp);
1045	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
1046	    flush();
1047	}
1048	else
1049	    *str = Strsave(sp->word);
1050	found = 0;
1051    }
1052    cleanup_until(&s0);
1053    return found;
1054}
1055
1056/*
1057 * Builtin to look at and list all places a command may be defined:
1058 * aliases, shell builtins, and the path.
1059 *
1060 * Marc Horowitz <marc@mit.edu>
1061 * MIT Student Information Processing Board
1062 */
1063
1064/*ARGSUSED*/
1065void
1066dowhere(Char **v, struct command *c)
1067{
1068    int found = 1;
1069    USE(c);
1070
1071    if (adrof(STRautorehash))
1072	dohash(NULL, NULL);
1073    for (v++; *v; v++)
1074	found &= find_cmd(*v, 1);
1075    /* Make status nonzero if any command is not found. */
1076    if (!found)
1077	setcopy(STRstatus, STR1, VAR_READWRITE);
1078}
1079
1080int
1081find_cmd(Char *cmd, int prt)
1082{
1083    struct varent *var;
1084    const struct biltins *bptr;
1085    Char **pv;
1086    Char *sv;
1087    int hashval, rehashed, i, ex, rval = 0;
1088
1089    if (prt && any(short2str(cmd), '/')) {
1090	xprintf("%s", CGETS(13, 7, "where: / in command makes no sense\n"));
1091	return rval;
1092    }
1093
1094    /* first, look for an alias */
1095
1096    if (prt && adrof1(cmd, &aliases)) {
1097	if ((var = adrof1(cmd, &aliases)) != NULL) {
1098	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1099	    if (var->vec != NULL)
1100		blkpr(var->vec);
1101	    xputchar('\n');
1102	    rval = 1;
1103	}
1104    }
1105
1106    /* next, look for a shell builtin */
1107
1108    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
1109	if (eq(cmd, str2short(bptr->bname))) {
1110	    rval = 1;
1111	    if (prt)
1112		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1113	    else
1114		return rval;
1115	}
1116    }
1117#ifdef WINNT_NATIVE
1118    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1119	if (eq(cmd, str2short(bptr->bname))) {
1120	    rval = 1;
1121	    if (prt)
1122		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1123	    else
1124		return rval;
1125	}
1126    }
1127#endif /* WINNT_NATIVE*/
1128
1129    /* last, look through the path for the command */
1130
1131    if ((var = adrof(STRpath)) == NULL)
1132	return rval;
1133
1134    hashval = havhash ? hashname(cmd) : 0;
1135
1136    sv = Strspl(STRslash, cmd);
1137    cleanup_push(sv, xfree);
1138
1139    rehashed = 0;
1140retry:
1141    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
1142	if (havhash && !eq(*pv, STRdot)) {
1143#ifdef FASTHASH
1144	    if (!bit(hashval, i))
1145		continue;
1146#else /* OLDHASH */
1147	    int hashval1 = hash(hashval, i);
1148	    if (!bit(xhash, hashval1))
1149		continue;
1150#endif /* FASTHASH */
1151	}
1152	ex = executable(*pv, sv, 0);
1153#ifdef FASTHASH
1154	if (!ex && (hashdebug & 2)) {
1155	    xprintf("%s", CGETS(13, 10, "hash miss: "));
1156	    ex = 1;	/* Force printing */
1157	}
1158#endif /* FASTHASH */
1159	if (ex) {
1160	    rval = 1;
1161	    if (prt) {
1162		xprintf("%S/", *pv);
1163		xprintf("%S\n", cmd);
1164	    }
1165	    else
1166		return rval;
1167	}
1168    }
1169    /*
1170     * If we are printing, we are being called from dowhere() which it
1171     * has rehashed already
1172     */
1173    if (!prt && adrof(STRautorehash) && !rehashed && havhash) {
1174	dohash(NULL, NULL);
1175	rehashed = 1;
1176	goto retry;
1177    }
1178    cleanup_until(sv);
1179    return rval;
1180}
1181#ifdef WINNT_NATIVE
1182int hashval_extern(cp)
1183	Char *cp;
1184{
1185	return havhash?hashname(cp):0;
1186}
1187int bit_extern(val,i)
1188	int val;
1189	int i;
1190{
1191	return bit(val,i);
1192}
1193void bis_extern(val,i)
1194	int val;
1195	int i;
1196{
1197	bis(val,i);
1198}
1199#endif /* WINNT_NATIVE */
1200
1201