sh.exec.c revision 145479
1/* $Header: /src/pub/tcsh/sh.exec.c,v 3.63 2004/11/23 02:10:48 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("$Id: sh.exec.c,v 3.63 2004/11/23 02:10:48 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		__P((void));
145static	void	texec		__P((Char *, Char **));
146int	hashname	__P((Char *));
147static	int 	iscommand	__P((Char *));
148
149void
150doexec(t, do_glob)
151    struct command *t;
152    int do_glob;
153{
154    Char *dp, **pv, **av, *sav;
155    struct varent *v;
156    int slash;
157    int hashval, i;
158    Char   *blk[2];
159
160    /*
161     * Glob the command name. We will search $path even if this does something,
162     * as in sh but not in csh.  One special case: if there is no PATH, then we
163     * execute only commands which start with '/'.
164     */
165    blk[0] = t->t_dcom[0];
166    blk[1] = 0;
167    gflag = 0;
168    if (do_glob)
169	tglob(blk);
170    if (gflag) {
171	pv = globall(blk);
172	if (pv == 0) {
173	    setname(short2str(blk[0]));
174	    stderror(ERR_NAME | ERR_NOMATCH);
175	}
176	gargv = 0;
177    }
178    else
179	pv = saveblk(blk);
180
181    trim(pv);
182
183    exerr = 0;
184    expath = Strsave(pv[0]);
185#ifdef VFORK
186    Vexpath = expath;
187#endif /* VFORK */
188
189    v = adrof(STRpath);
190    if (v == 0 && expath[0] != '/' && expath[0] != '.') {
191	blkfree(pv);
192	pexerr();
193    }
194    slash = any(short2str(expath), '/');
195
196    /*
197     * Glob the argument list, if necessary. Otherwise trim off the quote bits.
198     */
199    gflag = 0;
200    av = &t->t_dcom[1];
201    if (do_glob)
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 == NULL || v->vec == NULL || v->vec[0] == NULL || 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    Char **st;
389{
390    char **t;
391    char *f;
392    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_NATIVE
435		nt_feed_to_cmd(f,t);
436#endif /* WINNT_NATIVE */
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|O_LARGEFILE)) != -1) {
442	    int nread;
443#ifdef O_TEXT
444	    setmode(fd, O_TEXT);
445#endif
446	    if ((nread = read(fd, (char *) pref, 2)) == 2) {
447		if (!isprint((unsigned char)pref[0]) &&
448		    (pref[0] != '\n' && pref[0] != '\t')) {
449		    (void) close(fd);
450		    /*
451		     * We *know* what ENOEXEC means.
452		     */
453		    stderror(ERR_ARCH, f, strerror(errno));
454		}
455	    }
456	    else if (nread < 0 && errno != EINTR) {
457#ifdef convex
458		/* need to print error incase the file is migrated */
459		stderror(ERR_SYSTEM, f, strerror(errno));
460#endif
461	    }
462#ifdef _PATH_BSHELL
463	    else {
464		pref[0] = '#';
465		pref[1] = '\0';
466	    }
467#endif
468	}
469#ifdef HASHBANG
470	if (fd == -1 ||
471	    pref[0] != '#' || pref[1] != '!' || hashbang(fd, &vp) == -1) {
472#endif /* HASHBANG */
473	/*
474	 * If there is an alias for shell, then put the words of the alias in
475	 * front of the argument list replacing the command name. Note no
476	 * interpretation of the words at this point.
477	 */
478	    v = adrof1(STRshell, &aliases);
479	    if (v == NULL || v->vec == NULL) {
480		vp = lastsh;
481		vp[0] = adrof(STRshell) ? varval(STRshell) : STR_SHELLPATH;
482		vp[1] = NULL;
483#ifdef _PATH_BSHELL
484		if (fd != -1
485# ifndef ISC	/* Compatible with ISC's /bin/csh */
486		    && pref[0] != '#'
487# endif /* ISC */
488		    )
489		    vp[0] = STR_BSHELL;
490#endif
491		vp = saveblk(vp);
492	    }
493	    else
494		vp = saveblk(v->vec);
495#ifdef HASHBANG
496	}
497#endif /* HASHBANG */
498	if (fd != -1)
499	    (void) close(fd);
500
501	st0 = st[0];
502	st[0] = sf;
503	ost = st;
504	st = blkspl(vp, st);	/* Splice up the new arglst */
505	ost[0] = st0;
506	sf = *st;
507	/* The order for the conversions is significant */
508	t = short2blk(st);
509	f = short2str(sf);
510	xfree((ptr_t) st);
511	blkfree((Char **) vp);
512#ifdef VFORK
513	Vt = t;
514#endif /* VFORK */
515#ifdef ISC_POSIX_EXEC_BUG
516	__setostype(0);		/* "0" is "__OS_SYSV" in <sys/user.h> */
517#endif /* ISC_POSIX_EXEC_BUG */
518	(void) execv(f, t);
519#ifdef ISC_POSIX_EXEC_BUG
520	__setostype(1);		/* "1" is "__OS_POSIX" in <sys/user.h> */
521#endif /* ISC_POSIX_EXEC_BUG */
522#ifdef VFORK
523	Vt = 0;
524#endif /* VFORK */
525	blkfree((Char **) t);
526	/* The sky is falling, the sky is falling! */
527	stderror(ERR_SYSTEM, f, strerror(errno));
528	break;
529
530    case ENOMEM:
531	stderror(ERR_SYSTEM, f, strerror(errno));
532	break;
533
534#ifdef _IBMR2
535    case 0:			/* execv fails and returns 0! */
536#endif /* _IBMR2 */
537    case ENOENT:
538	break;
539
540    default:
541	if (exerr == 0) {
542	    exerr = strerror(errno);
543	    if (expath)
544		xfree((ptr_t) expath);
545	    expath = Strsave(sf);
546#ifdef VFORK
547	    Vexpath = expath;
548#endif /* VFORK */
549	}
550	break;
551    }
552}
553
554/*ARGSUSED*/
555void
556execash(t, kp)
557    Char  **t;
558    struct command *kp;
559{
560    int     saveIN, saveOUT, saveDIAG, saveSTD;
561    int     oSHIN;
562    int     oSHOUT;
563    int     oSHDIAG;
564    int     oOLDSTD;
565    jmp_buf_t osetexit;
566    int	    my_reenter;
567    int     odidfds;
568#ifndef CLOSE_ON_EXEC
569    int	    odidcch;
570#endif /* CLOSE_ON_EXEC */
571    signalfun_t osigint, osigquit, osigterm;
572
573    USE(t);
574    if (chkstop == 0 && setintr)
575	panystop(0);
576    /*
577     * Hmm, we don't really want to do that now because we might
578     * fail, but what is the choice
579     */
580    rechist(NULL, adrof(STRsavehist) != NULL);
581
582
583    osigint  = signal(SIGINT, parintr);
584    osigquit = signal(SIGQUIT, parintr);
585    osigterm = signal(SIGTERM, parterm);
586
587    odidfds = didfds;
588#ifndef CLOSE_ON_EXEC
589    odidcch = didcch;
590#endif /* CLOSE_ON_EXEC */
591    oSHIN = SHIN;
592    oSHOUT = SHOUT;
593    oSHDIAG = SHDIAG;
594    oOLDSTD = OLDSTD;
595
596    (void)close_on_exec (saveIN = dcopy(SHIN, -1), 1);
597    (void)close_on_exec (saveOUT = dcopy(SHOUT, -1), 1);
598    (void)close_on_exec (saveDIAG = dcopy(SHDIAG, -1), 1);
599    (void)close_on_exec (saveSTD = dcopy(OLDSTD, -1), 1);
600
601    lshift(kp->t_dcom, 1);
602
603    getexit(osetexit);
604
605    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
606#ifdef cray
607    my_reenter = 1;             /* assume non-zero return val */
608    if (setexit() == 0) {
609        my_reenter = 0;         /* Oh well, we were wrong */
610#else /* !cray */
611    if ((my_reenter = setexit()) == 0) {
612#endif /* cray */
613	(void)close_on_exec (SHIN = dcopy(0, -1), 1);
614	(void)close_on_exec (SHOUT = dcopy(1, -1), 1);
615	(void)close_on_exec (SHDIAG = dcopy(2, -1), 1);
616#ifndef CLOSE_ON_EXEC
617	didcch = 0;
618#endif /* CLOSE_ON_EXEC */
619	didfds = 0;
620	/*
621	 * Decrement the shell level
622	 */
623	shlvl(-1);
624#ifdef WINNT_NATIVE
625	__nt_really_exec=1;
626#endif /* WINNT_NATIVE */
627	doexec(kp, 1);
628    }
629
630    (void) sigset(SIGINT, osigint);
631    (void) sigset(SIGQUIT, osigquit);
632    (void) sigset(SIGTERM, osigterm);
633
634    doneinp = 0;
635#ifndef CLOSE_ON_EXEC
636    didcch = odidcch;
637#endif /* CLOSE_ON_EXEC */
638    didfds = odidfds;
639    (void) close(SHIN);
640    (void) close(SHOUT);
641    (void) close(SHDIAG);
642    (void) close(OLDSTD);
643    (void)close_on_exec(SHIN = dmove(saveIN, oSHIN), 1);
644    (void)close_on_exec(SHOUT = dmove(saveOUT, oSHOUT), 1);
645    (void)close_on_exec(SHDIAG = dmove(saveDIAG, oSHDIAG), 1);
646    (void)close_on_exec(OLDSTD = dmove(saveSTD, oOLDSTD), 1);
647
648    resexit(osetexit);
649    if (my_reenter)
650	stderror(ERR_SILENT);
651}
652
653void
654xechoit(t)
655    Char  **t;
656{
657    if (adrof(STRecho)) {
658	int odidfds = didfds;
659	flush();
660	haderr = 1;
661	didfds = 0;
662	blkpr(t), xputchar('\n');
663	flush();
664	didfds = odidfds;
665	haderr = 0;
666    }
667}
668
669/*ARGSUSED*/
670void
671dohash(vv, c)
672    Char **vv;
673    struct command *c;
674{
675#ifdef COMMENT
676    struct stat stb;
677#endif
678    DIR    *dirp;
679    struct dirent *dp;
680    int     i = 0;
681    struct varent *v = adrof(STRpath);
682    Char  **pv;
683    int hashval;
684#ifdef WINNT_NATIVE
685    int is_windir; /* check if it is the windows directory */
686    USE(hashval);
687#endif /* WINNT_NATIVE */
688
689    USE(c);
690#ifdef FASTHASH
691    if (vv && vv[1]) {
692        uhashlength = atoi(short2str(vv[1]));
693        if (vv[2]) {
694	    uhashwidth = atoi(short2str(vv[2]));
695	    if ((uhashwidth != sizeof(unsigned char)) &&
696	        (uhashwidth != sizeof(unsigned short)) &&
697	        (uhashwidth != sizeof(unsigned long)))
698	        uhashwidth = 0;
699	    if (vv[3])
700		hashdebug = atoi(short2str(vv[3]));
701        }
702    }
703
704    if (uhashwidth)
705	hashwidth = uhashwidth;
706    else {
707	hashwidth = 0;
708	if (v == NULL)
709	    return;
710	for (pv = v->vec; pv && *pv; pv++, hashwidth++)
711	    continue;
712	if (hashwidth <= widthof(unsigned char))
713	    hashwidth = sizeof(unsigned char);
714	else if (hashwidth <= widthof(unsigned short))
715	    hashwidth = sizeof(unsigned short);
716	else if (hashwidth <= widthof(unsigned int))
717	    hashwidth = sizeof(unsigned int);
718	else
719	    hashwidth = sizeof(unsigned long);
720    }
721
722    if (uhashlength)
723	hashlength = uhashlength;
724    else
725        hashlength = hashwidth * (8*64);/* "average" files per dir in path */
726
727    if (xhash)
728        xfree((ptr_t) xhash);
729    xhash = (unsigned long *) xcalloc((size_t) (hashlength * hashwidth),
730				      (size_t) 1);
731#endif /* FASTHASH */
732
733    (void) getusername(NULL);	/* flush the tilde cashe */
734    tw_cmd_free();
735    havhash = 1;
736    if (v == NULL)
737	return;
738    for (pv = v->vec; pv && *pv; pv++, i++) {
739	if (!ABSOLUTEP(pv[0]))
740	    continue;
741	dirp = opendir(short2str(*pv));
742	if (dirp == NULL)
743	    continue;
744#ifdef COMMENT			/* this isn't needed.  opendir won't open
745				 * non-dirs */
746	if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
747	    (void) closedir(dirp);
748	    continue;
749	}
750#endif
751#ifdef WINNT_NATIVE
752	is_windir = nt_check_if_windir(short2str(*pv));
753#endif /* WINNT_NATIVE */
754	while ((dp = readdir(dirp)) != NULL) {
755	    if (dp->d_ino == 0)
756		continue;
757	    if (dp->d_name[0] == '.' &&
758		(dp->d_name[1] == '\0' ||
759		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
760		continue;
761#ifdef WINNT_NATIVE
762	    nt_check_name_and_hash(is_windir, dp->d_name, i);
763#else /* !WINNT_NATIVE*/
764#if defined(_UWIN) || defined(__CYGWIN__)
765	    /* Turn foo.{exe,com,bat} into foo since UWIN's readdir returns
766	     * the file with the .exe, .com, .bat extension
767	     */
768	    {
769		size_t	ext = strlen(dp->d_name) - 4;
770		if ((ext > 0) && (strcasecmp(&dp->d_name[ext], ".exe") == 0 ||
771				  strcasecmp(&dp->d_name[ext], ".bat") == 0 ||
772				  strcasecmp(&dp->d_name[ext], ".com") == 0))
773		    {
774			dp->d_name[ext] = '\0';
775#if defined(__CYGWIN__)
776			strlwr(dp->d_name);
777#endif /* __CYGWIN__ */
778		    }
779	    }
780#endif /* _UWIN || __CYGWIN__ */
781# ifdef FASTHASH
782	    hashval = hashname(str2short(dp->d_name));
783	    bis(hashval, i);
784	    if (hashdebug & 1)
785	        xprintf(CGETS(13, 1, "hash=%-4d dir=%-2d prog=%s\n"),
786		        hashname(str2short(dp->d_name)), i, dp->d_name);
787# else /* OLD HASH */
788	    hashval = hash(hashname(str2short(dp->d_name)), i);
789	    bis(xhash, hashval);
790# endif /* FASTHASH */
791	    /* tw_add_comm_name (dp->d_name); */
792#endif /* WINNT_NATIVE */
793	}
794	(void) closedir(dirp);
795    }
796}
797
798/*ARGSUSED*/
799void
800dounhash(v, c)
801    Char **v;
802    struct command *c;
803{
804    USE(c);
805    USE(v);
806    havhash = 0;
807#ifdef FASTHASH
808    if (xhash) {
809       xfree((ptr_t) xhash);
810       xhash = NULL;
811    }
812#endif /* FASTHASH */
813}
814
815/*ARGSUSED*/
816void
817hashstat(v, c)
818    Char **v;
819    struct command *c;
820{
821    USE(c);
822    USE(v);
823#ifdef FASTHASH
824   if (havhash && hashlength && hashwidth)
825      xprintf(CGETS(13, 2, "%d hash buckets of %d bits each\n"),
826	      hashlength, hashwidth*8);
827   if (hashdebug)
828      xprintf(CGETS(13, 3, "debug mask = 0x%08x\n"), hashdebug);
829#endif /* FASTHASH */
830#ifdef VFORK
831   if (hits + misses)
832      xprintf(CGETS(13, 4, "%d hits, %d misses, %d%%\n"),
833	      hits, misses, 100 * hits / (hits + misses));
834#endif
835}
836
837
838/*
839 * Hash a command name.
840 */
841int
842hashname(cp)
843    Char *cp;
844{
845    unsigned long h;
846
847    for (h = 0; *cp; cp++)
848	h = hash(h, *cp);
849    return ((int) h);
850}
851
852static int
853iscommand(name)
854    Char   *name;
855{
856    Char **pv;
857    Char *sav;
858    struct varent *v;
859    int slash = any(short2str(name), '/');
860    int hashval, i;
861
862    v = adrof(STRpath);
863    if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
864	pv = justabs;
865    else
866	pv = v->vec;
867    sav = Strspl(STRslash, name);	/* / command name for postpending */
868    hashval = havhash ? hashname(name) : 0;
869    i = 0;
870    do {
871	if (!slash && ABSOLUTEP(pv[0]) && havhash) {
872#ifdef FASTHASH
873	    if (!bit(hashval, i))
874		goto cont;
875#else /* OLDHASH */
876	    int hashval1 = hash(hashval, i);
877	    if (!bit(xhash, hashval1))
878		goto cont;
879#endif /* FASTHASH */
880	}
881	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
882	    if (executable(NULL, name, 0)) {
883		xfree((ptr_t) sav);
884		return i + 1;
885	    }
886	}
887	else {
888	    if (executable(*pv, sav, 0)) {
889		xfree((ptr_t) sav);
890		return i + 1;
891	    }
892	}
893cont:
894	pv++;
895	i++;
896    } while (*pv);
897    xfree((ptr_t) sav);
898    return 0;
899}
900
901/* Also by:
902 *  Andreas Luik <luik@isaak.isa.de>
903 *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
904 *  Azenberstr. 35
905 *  D-7000 Stuttgart 1
906 *  West-Germany
907 * is the executable() routine below and changes to iscommand().
908 * Thanks again!!
909 */
910
911#ifndef WINNT_NATIVE
912/*
913 * executable() examines the pathname obtained by concatenating dir and name
914 * (dir may be NULL), and returns 1 either if it is executable by us, or
915 * if dir_ok is set and the pathname refers to a directory.
916 * This is a bit kludgy, but in the name of optimization...
917 */
918int
919executable(dir, name, dir_ok)
920    Char   *dir, *name;
921    int    dir_ok;
922{
923    struct stat stbuf;
924    Char    path[MAXPATHLEN + 1];
925    char   *strname;
926    (void) memset(path, 0, sizeof(path));
927
928    if (dir && *dir) {
929	copyn(path, dir, MAXPATHLEN);
930	catn(path, name, MAXPATHLEN);
931	strname = short2str(path);
932    }
933    else
934	strname = short2str(name);
935
936    return (stat(strname, &stbuf) != -1 &&
937	    ((dir_ok && S_ISDIR(stbuf.st_mode)) ||
938	     (S_ISREG(stbuf.st_mode) &&
939    /* save time by not calling access() in the hopeless case */
940	      (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
941	      access(strname, X_OK) == 0
942	)));
943}
944#endif /*!WINNT_NATIVE*/
945
946int
947tellmewhat(lexp, str)
948    struct wordent *lexp;
949    Char *str;
950{
951    int i;
952    struct biltins *bptr;
953    struct wordent *sp = lexp->next;
954    int    aliased = 0, found;
955    Char   *s0, *s1, *s2, *cmd;
956    Char    qc;
957
958    if (adrof1(sp->word, &aliases)) {
959	alias(lexp);
960	sp = lexp->next;
961	aliased = 1;
962    }
963
964    s0 = sp->word;		/* to get the memory freeing right... */
965
966    /* handle quoted alias hack */
967    if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
968	(sp->word)++;
969
970    /* do quoting, if it hasn't been done */
971    s1 = s2 = sp->word;
972    while (*s2)
973	switch (*s2) {
974	case '\'':
975	case '"':
976	    qc = *s2++;
977	    while (*s2 && *s2 != qc)
978		*s1++ = *s2++ | QUOTE;
979	    if (*s2)
980		s2++;
981	    break;
982	case '\\':
983	    if (*++s2)
984		*s1++ = *s2++ | QUOTE;
985	    break;
986	default:
987	    *s1++ = *s2++;
988	}
989    *s1 = '\0';
990
991    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
992	if (eq(sp->word, str2short(bptr->bname))) {
993	    if (str == NULL) {
994		if (aliased)
995		    prlex(lexp);
996		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
997			      sp->word);
998		flush();
999	    }
1000	    else
1001		(void) Strcpy(str, sp->word);
1002	    sp->word = s0;	/* we save and then restore this */
1003	    return TRUE;
1004	}
1005    }
1006#ifdef WINNT_NATIVE
1007    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1008	if (eq(sp->word, str2short(bptr->bname))) {
1009	    if (str == NULL) {
1010		if (aliased)
1011		    prlex(lexp);
1012		xprintf(CGETS(13, 5, "%S: shell built-in command.\n"),
1013			      sp->word);
1014		flush();
1015	    }
1016	    else
1017		(void) Strcpy(str, sp->word);
1018	    sp->word = s0;	/* we save and then restore this */
1019	    return TRUE;
1020	}
1021    }
1022#endif /* WINNT_NATIVE*/
1023
1024    sp->word = cmd = globone(sp->word, G_IGNORE);
1025
1026    if ((i = iscommand(sp->word)) != 0) {
1027	Char **pv;
1028	struct varent *v;
1029	int    slash = any(short2str(sp->word), '/');
1030
1031	v = adrof(STRpath);
1032	if (v == NULL || v->vec == NULL || v->vec[0] == NULL || slash)
1033	    pv = justabs;
1034	else
1035	    pv = v->vec;
1036
1037	while (--i)
1038	    pv++;
1039	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
1040	    if (!slash) {
1041		sp->word = Strspl(STRdotsl, sp->word);
1042		prlex(lexp);
1043		xfree((ptr_t) sp->word);
1044	    }
1045	    else
1046		prlex(lexp);
1047	}
1048	else {
1049	    s1 = Strspl(*pv, STRslash);
1050	    sp->word = Strspl(s1, sp->word);
1051	    xfree((ptr_t) s1);
1052	    if (str == NULL)
1053		prlex(lexp);
1054	    else
1055		(void) Strcpy(str, sp->word);
1056	    xfree((ptr_t) sp->word);
1057	}
1058	found = 1;
1059    }
1060    else {
1061	if (str == NULL) {
1062	    if (aliased)
1063		prlex(lexp);
1064	    xprintf(CGETS(13, 6, "%S: Command not found.\n"), sp->word);
1065	    flush();
1066	}
1067	else
1068	    (void) Strcpy(str, sp->word);
1069	found = 0;
1070    }
1071    sp->word = s0;		/* we save and then restore this */
1072    xfree((ptr_t) cmd);
1073    return found;
1074}
1075
1076/*
1077 * Builtin to look at and list all places a command may be defined:
1078 * aliases, shell builtins, and the path.
1079 *
1080 * Marc Horowitz <marc@mit.edu>
1081 * MIT Student Information Processing Board
1082 */
1083
1084/*ARGSUSED*/
1085void
1086dowhere(v, c)
1087    Char **v;
1088    struct command *c;
1089{
1090    int found = 1;
1091    USE(c);
1092    for (v++; *v; v++)
1093	found &= find_cmd(*v, 1);
1094    /* Make status nonzero if any command is not found. */
1095    if (!found)
1096      set(STRstatus, Strsave(STR1), VAR_READWRITE);
1097}
1098
1099int
1100find_cmd(cmd, prt)
1101    Char *cmd;
1102    int prt;
1103{
1104    struct varent *var;
1105    struct biltins *bptr;
1106    Char **pv;
1107    Char *sv;
1108    int hashval, i, ex, rval = 0;
1109
1110    if (prt && any(short2str(cmd), '/')) {
1111	xprintf(CGETS(13, 7, "where: / in command makes no sense\n"));
1112	return rval;
1113    }
1114
1115    /* first, look for an alias */
1116
1117    if (prt && adrof1(cmd, &aliases)) {
1118	if ((var = adrof1(cmd, &aliases)) != NULL) {
1119	    xprintf(CGETS(13, 8, "%S is aliased to "), cmd);
1120	    if (var->vec != NULL)
1121		blkpr(var->vec);
1122	    xputchar('\n');
1123	    rval = 1;
1124	}
1125    }
1126
1127    /* next, look for a shell builtin */
1128
1129    for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
1130	if (eq(cmd, str2short(bptr->bname))) {
1131	    rval = 1;
1132	    if (prt)
1133		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1134	    else
1135		return rval;
1136	}
1137    }
1138#ifdef WINNT_NATIVE
1139    for (bptr = nt_bfunc; bptr < &nt_bfunc[nt_nbfunc]; bptr++) {
1140	if (eq(cmd, str2short(bptr->bname))) {
1141	    rval = 1;
1142	    if (prt)
1143		xprintf(CGETS(13, 9, "%S is a shell built-in\n"), cmd);
1144	    else
1145		return rval;
1146	}
1147    }
1148#endif /* WINNT_NATIVE*/
1149
1150    /* last, look through the path for the command */
1151
1152    if ((var = adrof(STRpath)) == NULL)
1153	return rval;
1154
1155    hashval = havhash ? hashname(cmd) : 0;
1156
1157    sv = Strspl(STRslash, cmd);
1158
1159    for (pv = var->vec, i = 0; pv && *pv; pv++, i++) {
1160	if (havhash && !eq(*pv, STRdot)) {
1161#ifdef FASTHASH
1162	    if (!bit(hashval, i))
1163		continue;
1164#else /* OLDHASH */
1165	    int hashval1 = hash(hashval, i);
1166	    if (!bit(xhash, hashval1))
1167		continue;
1168#endif /* FASTHASH */
1169	}
1170	ex = executable(*pv, sv, 0);
1171#ifdef FASTHASH
1172	if (!ex && (hashdebug & 2)) {
1173	    xprintf(CGETS(13, 10, "hash miss: "));
1174	    ex = 1;	/* Force printing */
1175	}
1176#endif /* FASTHASH */
1177	if (ex) {
1178	    rval = 1;
1179	    if (prt) {
1180		xprintf("%S/", *pv);
1181		xprintf("%S\n", cmd);
1182	    }
1183	    else
1184		return rval;
1185	}
1186    }
1187    xfree((ptr_t) sv);
1188    return rval;
1189}
1190#ifdef WINNT_NATIVE
1191int hashval_extern(cp)
1192	Char *cp;
1193{
1194	return havhash?hashname(cp):0;
1195}
1196int bit_extern(val,i)
1197	int val;
1198	int i;
1199{
1200	return bit(val,i);
1201}
1202void bis_extern(val,i)
1203	int val;
1204	int i;
1205{
1206	bis(val,i);
1207}
1208#endif /* WINNT_NATIVE */
1209
1210