xref: /freebsd/contrib/tcsh/sh.exec.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
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 
39 RCSID("$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  */
69 static char *exerr;		/* Execution error message */
70 static 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 
92 static unsigned long *xhash = NULL;
93 static unsigned int hashlength = 0, uhashlength = 0;
94 static unsigned int hashwidth = 0, uhashwidth = 0;
95 static 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
133 static 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
142 static int hits, misses;
143 #endif /* VFORK */
144 
145 /* Dummy search path for just absolute search when no path */
146 static Char *justabs[] = {STRNULL, 0};
147 
148 static	void	pexerr		__P((void));
149 static	void	texec		__P((Char *, Char **));
150 static	int	hashname	__P((Char *));
151 static	int 	iscommand	__P((Char *));
152 
153 void
154 doexec(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 */
349 cont:
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 
361 static void
362 pexerr()
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  */
385 static void
386 texec(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*/
551 void
552 execash(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 
649 void
650 xechoit(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*/
662 void
663 dohash(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*/
784 void
785 dounhash(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*/
801 void
802 hashstat(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  */
826 static int
827 hashname(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 
837 static int
838 iscommand(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 	}
878 cont:
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  */
902 int
903 executable(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 
960 int
961 tellmewhat(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*/
1099 void
1100 dowhere(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 
1113 int
1114 find_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
1205 int
1206 nt_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 
1217 void
1218 nt_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);
1247 nodot:
1248     hashval = hashname(str2short(file));
1249     bis(hashval, i);
1250 }
1251 int hashval_extern(cp)
1252 	Char *cp;
1253 {
1254 	return havhash?hashname(cp):0;
1255 }
1256 int bit_extern(val,i)
1257 	int val;
1258 	int i;
1259 {
1260 	return bit(val,i);
1261 }
1262 #endif /* WINNT */
1263