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