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