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