xref: /netbsd-src/bin/csh/exec.c (revision 1767ce60a2fbaa51b3860ae327733a38db5c12f1)
1*1767ce60Schristos /* $NetBSD: exec.c,v 1.33 2019/01/05 16:54:00 christos Exp $ */
249f0ad86Scgd 
361f28255Scgd /*-
4cee2bad8Smycroft  * Copyright (c) 1980, 1991, 1993
5cee2bad8Smycroft  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
15b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
328ea378c6Schristos #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3449f0ad86Scgd #if 0
35ec16f0d3Schristos static char sccsid[] = "@(#)exec.c	8.3 (Berkeley) 5/23/95";
3649f0ad86Scgd #else
37*1767ce60Schristos __RCSID("$NetBSD: exec.c,v 1.33 2019/01/05 16:54:00 christos Exp $");
3849f0ad86Scgd #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
41cee2bad8Smycroft #include <sys/param.h>
42cee2bad8Smycroft #include <sys/stat.h>
43b771e65bSwiz #include <sys/types.h>
44b771e65bSwiz 
45b771e65bSwiz #include <dirent.h>
4661f28255Scgd #include <errno.h>
47b771e65bSwiz #include <fcntl.h>
4818158540Swiz #include <stdarg.h>
4961f28255Scgd #include <stdlib.h>
5061f28255Scgd #include <string.h>
5161f28255Scgd #include <unistd.h>
52b771e65bSwiz 
5361f28255Scgd #include "csh.h"
5461f28255Scgd #include "extern.h"
5561f28255Scgd 
5661f28255Scgd /*
5761f28255Scgd  * System level search and execute of a command.  We look in each directory
5861f28255Scgd  * for the specified command name.  If the name contains a '/' then we
5961f28255Scgd  * execute only the full path name.  If there is no search path then we
6061f28255Scgd  * execute only full path names.
6161f28255Scgd  */
6261f28255Scgd extern char **environ;
6361f28255Scgd 
6461f28255Scgd /*
6561f28255Scgd  * As we search for the command we note the first non-trivial error
6661f28255Scgd  * message for presentation to the user.  This allows us often
6761f28255Scgd  * to show that a file has the wrong mode/no access when the file
6861f28255Scgd  * is not in the last component of the search path, so we must
6961f28255Scgd  * go on after first detecting the error.
7061f28255Scgd  */
71be432ed4Smycroft static const char *exerr;	/* Execution error message */
7261f28255Scgd static Char *expath;		/* Path for exerr */
7361f28255Scgd 
7461f28255Scgd /*
7561f28255Scgd  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
7661f28255Scgd  * to hash execs.  If it is allocated (havhash true), then to tell
7761f28255Scgd  * whether ``name'' is (possibly) present in the i'th component
7861f28255Scgd  * of the variable path, you look at the bit in xhash indexed by
7961f28255Scgd  * hash(hashname("name"), i).  This is setup automatically
8061f28255Scgd  * after .login is executed, and recomputed whenever ``path'' is
8161f28255Scgd  * changed.
8261f28255Scgd  * The two part hash function is designed to let texec() call the
8361f28255Scgd  * more expensive hashname() only once and the simple hash() several
8461f28255Scgd  * times (once for each path component checked).
8561f28255Scgd  * Byte size is assumed to be 8.
8661f28255Scgd  */
8761f28255Scgd #define	HSHSIZ 8192	/* 1k bytes */
8861f28255Scgd #define HSHMASK	(HSHSIZ - 1)
8961f28255Scgd #define HSHMUL 243
9037e39248Schristos static unsigned char xhash[HSHSIZ / 8];
9161f28255Scgd 
92cee2bad8Smycroft #define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK)
93c1d7b11eSchristos /* these macros eval their arguments multiple times, so be careful */
9461f28255Scgd #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
95b4f0f90aSchristos #define bis(h, b) ((h)[(b) >> 3] = \
96b4f0f90aSchristos     (unsigned char)((1 << ((b) & 7)) | (h)[(b) >> 3]))/* bit set */
9761f28255Scgd static int hits, misses;
9861f28255Scgd 
9961f28255Scgd /* Dummy search path for just absolute search when no path */
10061f28255Scgd static Char *justabs[] = {STRNULL, 0};
10161f28255Scgd 
1028b0f9554Sperry static void pexerr(void) __dead;
103b771e65bSwiz static void texec(Char *, Char **);
104b771e65bSwiz static int hashname(Char *);
105b771e65bSwiz static int tellmewhat(struct wordent *, Char *);
106b79c2ef2Schristos static int executable(Char *, Char *, int);
107b771e65bSwiz static int iscommand(Char *);
10861f28255Scgd 
10961f28255Scgd void
110cee2bad8Smycroft /*ARGSUSED*/
doexec(Char ** v,struct command * t)111b771e65bSwiz doexec(Char **v, struct command *t)
11261f28255Scgd {
11376adbe2bStls     struct varent *pathv;
114b771e65bSwiz     Char *blk[2], **av, *dp, **pv, *sav;
115b771e65bSwiz     int i, hashval, hashval1;
116b3df6303Skleink     sigset_t nsigset;
117b79c2ef2Schristos     int slash;
11861f28255Scgd 
119b771e65bSwiz     hashval = 0;
12061f28255Scgd     /*
12161f28255Scgd      * Glob the command name. We will search $path even if this does something,
12261f28255Scgd      * as in sh but not in csh.  One special case: if there is no PATH, then we
12361f28255Scgd      * execute only commands which start with '/'.
12461f28255Scgd      */
12561f28255Scgd     blk[0] = t->t_dcom[0];
12661f28255Scgd     blk[1] = 0;
12761f28255Scgd     gflag = 0, tglob(blk);
12861f28255Scgd     if (gflag) {
12961f28255Scgd 	pv = globall(blk);
13061f28255Scgd 	if (pv == 0) {
131cee2bad8Smycroft 	    setname(vis_str(blk[0]));
13261f28255Scgd 	    stderror(ERR_NAME | ERR_NOMATCH);
13361f28255Scgd 	}
13461f28255Scgd 	gargv = 0;
13561f28255Scgd     }
13661f28255Scgd     else
13761f28255Scgd 	pv = saveblk(blk);
13861f28255Scgd 
13961f28255Scgd     trim(pv);
14061f28255Scgd 
14161f28255Scgd     exerr = 0;
14261f28255Scgd     expath = Strsave(pv[0]);
14361f28255Scgd     Vexpath = expath;
14461f28255Scgd 
145cee2bad8Smycroft     pathv = adrof(STRpath);
146cee2bad8Smycroft     if (pathv == 0 && expath[0] != '/') {
14761f28255Scgd 	blkfree(pv);
14861f28255Scgd 	pexerr();
14961f28255Scgd     }
15061f28255Scgd     slash = any(short2str(expath), '/');
15161f28255Scgd 
15261f28255Scgd     /*
15361f28255Scgd      * Glob the argument list, if necessary. Otherwise trim off the quote bits.
15461f28255Scgd      */
15561f28255Scgd     gflag = 0;
15661f28255Scgd     av = &t->t_dcom[1];
15761f28255Scgd     tglob(av);
15861f28255Scgd     if (gflag) {
15961f28255Scgd 	av = globall(av);
16061f28255Scgd 	if (av == 0) {
16161f28255Scgd 	    blkfree(pv);
162cee2bad8Smycroft 	    setname(vis_str(expath));
16361f28255Scgd 	    stderror(ERR_NAME | ERR_NOMATCH);
16461f28255Scgd 	}
16561f28255Scgd 	gargv = 0;
16661f28255Scgd     }
16761f28255Scgd     else
16861f28255Scgd 	av = saveblk(av);
16961f28255Scgd 
17061f28255Scgd     blkfree(t->t_dcom);
17161f28255Scgd     t->t_dcom = blkspl(pv, av);
172*1767ce60Schristos     free(pv);
173*1767ce60Schristos     free(av);
17461f28255Scgd     av = t->t_dcom;
17561f28255Scgd     trim(av);
17661f28255Scgd 
17761f28255Scgd     if (*av == NULL || **av == '\0')
17861f28255Scgd 	pexerr();
17961f28255Scgd 
18061f28255Scgd     xechoit(av);		/* Echo command if -x */
18161f28255Scgd     /*
18261f28255Scgd      * Since all internal file descriptors are set to close on exec, we don't
18361f28255Scgd      * need to close them explicitly here.  Just reorient ourselves for error
18461f28255Scgd      * messages.
18561f28255Scgd      */
18661f28255Scgd     SHIN = 0;
18761f28255Scgd     SHOUT = 1;
188cee2bad8Smycroft     SHERR = 2;
18961f28255Scgd     OLDSTD = 0;
19061f28255Scgd     /*
19161f28255Scgd      * We must do this AFTER any possible forking (like `foo` in glob) so that
19261f28255Scgd      * this shell can still do subprocesses.
19361f28255Scgd      */
194b3df6303Skleink     sigemptyset(&nsigset);
195b3df6303Skleink     (void)sigprocmask(SIG_SETMASK, &nsigset, NULL);
19661f28255Scgd     /*
19761f28255Scgd      * If no path, no words in path, or a / in the filename then restrict the
19861f28255Scgd      * command search.
19961f28255Scgd      */
200cee2bad8Smycroft     if (pathv == 0 || pathv->vec[0] == 0 || slash)
20161f28255Scgd 	pv = justabs;
20261f28255Scgd     else
203cee2bad8Smycroft 	pv = pathv->vec;
20461f28255Scgd     sav = Strspl(STRslash, *av); 	/* / command name for postpending */
20561f28255Scgd     Vsav = sav;
20661f28255Scgd     if (havhash)
20761f28255Scgd 	hashval = hashname(*av);
20861f28255Scgd     i = 0;
20961f28255Scgd     hits++;
21061f28255Scgd     do {
21161f28255Scgd 	/*
21261f28255Scgd 	 * Try to save time by looking at the hash table for where this command
21361f28255Scgd 	 * could be.  If we are doing delayed hashing, then we put the names in
21461f28255Scgd 	 * one at a time, as the user enters them.  This is kinda like Korn
21561f28255Scgd 	 * Shell's "tracked aliases".
21661f28255Scgd 	 */
21761f28255Scgd 	if (!slash && pv[0][0] == '/' && havhash) {
21861f28255Scgd 	    hashval1 = hash(hashval, i);
21961f28255Scgd 	    if (!bit(xhash, hashval1))
22061f28255Scgd 		goto cont;
22161f28255Scgd 	}
22261f28255Scgd 	if (pv[0][0] == 0 || eq(pv[0], STRdot))	/* don't make ./xxx */
22361f28255Scgd 	    texec(*av, av);
22461f28255Scgd 	else {
22561f28255Scgd 	    dp = Strspl(*pv, sav);
22661f28255Scgd 	    Vdp = dp;
22761f28255Scgd 	    texec(dp, av);
22861f28255Scgd 	    Vdp = 0;
229*1767ce60Schristos 	    free(dp);
23061f28255Scgd 	}
23161f28255Scgd 	misses++;
23261f28255Scgd cont:
23361f28255Scgd 	pv++;
23461f28255Scgd 	i++;
23561f28255Scgd     } while (*pv);
23661f28255Scgd     hits--;
23761f28255Scgd     Vsav = 0;
238*1767ce60Schristos     free(sav);
23961f28255Scgd     pexerr();
240ee9e50eaSmycroft     /* NOTREACHED */
24161f28255Scgd }
24261f28255Scgd 
24361f28255Scgd static void
pexerr(void)244b771e65bSwiz pexerr(void)
24561f28255Scgd {
24661f28255Scgd     /* Couldn't find the damn thing */
24761f28255Scgd     if (expath) {
248cee2bad8Smycroft 	setname(vis_str(expath));
24961f28255Scgd 	Vexpath = 0;
250*1767ce60Schristos 	free(expath);
25161f28255Scgd 	expath = 0;
25261f28255Scgd     }
25361f28255Scgd     else
25461f28255Scgd 	setname("");
25561f28255Scgd     if (exerr)
25661f28255Scgd 	stderror(ERR_NAME | ERR_STRING, exerr);
257cdbd74daSmycroft     else
25861f28255Scgd 	stderror(ERR_NAME | ERR_COMMAND);
259cdbd74daSmycroft     /* NOTREACHED */
26061f28255Scgd }
26161f28255Scgd 
26261f28255Scgd /*
26361f28255Scgd  * Execute command f, arg list t.
26461f28255Scgd  * Record error message if not found.
26561f28255Scgd  * Also do shell scripts here.
26661f28255Scgd  */
26761f28255Scgd static void
texec(Char * sf,Char ** st)268b771e65bSwiz texec(Char *sf, Char **st)
26961f28255Scgd {
27076adbe2bStls     struct varent *v;
271b771e65bSwiz     Char *lastsh[2], **vp, *st0, **ost;
272b771e65bSwiz     char *f, **t;
27361f28255Scgd     int fd;
2742701e5b9Schristos     unsigned char c = '\0';
27561f28255Scgd 
27661f28255Scgd     /* The order for the conversions is significant */
27761f28255Scgd     t = short2blk(st);
27861f28255Scgd     f = short2str(sf);
27961f28255Scgd     Vt = t;
28061f28255Scgd     errno = 0;			/* don't use a previous error */
28161f28255Scgd     (void)execve(f, t, environ);
28261f28255Scgd     Vt = 0;
28361f28255Scgd     blkfree((Char **)t);
28461f28255Scgd     switch (errno) {
28561f28255Scgd 
28661f28255Scgd     case ENOEXEC:
28761f28255Scgd 	/*
28861f28255Scgd 	 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
28961f28255Scgd 	 * it, don't feed it to the shell if it looks like a binary!
29061f28255Scgd 	 */
29161f28255Scgd 	if ((fd = open(f, O_RDONLY)) != -1) {
29261f28255Scgd 	    if (read(fd, (char *)&c, 1) == 1) {
29361f28255Scgd 		if (!Isprint(c) && (c != '\n' && c != '\t')) {
29461f28255Scgd 		    (void)close(fd);
29561f28255Scgd 		    /*
29661f28255Scgd 		     * We *know* what ENOEXEC means.
29761f28255Scgd 		     */
29861f28255Scgd 		    stderror(ERR_ARCH, f, strerror(errno));
29961f28255Scgd 		}
30061f28255Scgd 	    }
30161f28255Scgd #ifdef _PATH_BSHELL
30261f28255Scgd 	    else
30361f28255Scgd 		c = '#';
30461f28255Scgd #endif
30561f28255Scgd 	    (void)close(fd);
30661f28255Scgd 	}
30761f28255Scgd 	/*
30861f28255Scgd 	 * If there is an alias for shell, then put the words of the alias in
30961f28255Scgd 	 * front of the argument list replacing the command name. Note no
31061f28255Scgd 	 * interpretation of the words at this point.
31161f28255Scgd 	 */
31261f28255Scgd 	v = adrof1(STRshell, &aliases);
31361f28255Scgd 	if (v == 0) {
31461f28255Scgd 	    vp = lastsh;
31561f28255Scgd 	    vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH;
31661f28255Scgd 	    vp[1] = NULL;
31761f28255Scgd #ifdef _PATH_BSHELL
31861f28255Scgd 	    if (fd != -1 && c != '#')
31961f28255Scgd 		vp[0] = STR_BSHELL;
32061f28255Scgd #endif
32161f28255Scgd 	}
32261f28255Scgd 	else
32361f28255Scgd 	    vp = v->vec;
32461f28255Scgd 	st0 = st[0];
32561f28255Scgd 	st[0] = sf;
32661f28255Scgd 	ost = st;
32761f28255Scgd 	st = blkspl(vp, st);	/* Splice up the new arglst */
32861f28255Scgd 	ost[0] = st0;
32961f28255Scgd 	sf = *st;
33061f28255Scgd 	/* The order for the conversions is significant */
33161f28255Scgd 	t = short2blk(st);
33261f28255Scgd 	f = short2str(sf);
333*1767ce60Schristos 	free(st);
33461f28255Scgd 	Vt = t;
33561f28255Scgd 	(void)execve(f, t, environ);
33661f28255Scgd 	Vt = 0;
33761f28255Scgd 	blkfree((Char **)t);
338cdbd74daSmycroft 	/* FALLTHROUGH */
33961f28255Scgd 
34061f28255Scgd     case ENOMEM:
34161f28255Scgd 	stderror(ERR_SYSTEM, f, strerror(errno));
342cdbd74daSmycroft 	/* NOTREACHED */
34361f28255Scgd 
34461f28255Scgd     case ENOENT:
34561f28255Scgd 	break;
34661f28255Scgd 
34761f28255Scgd     default:
34861f28255Scgd 	if (exerr == 0) {
34961f28255Scgd 	    exerr = strerror(errno);
35061f28255Scgd 	    if (expath)
351*1767ce60Schristos 		free(expath);
35261f28255Scgd 	    expath = Strsave(sf);
35361f28255Scgd 	    Vexpath = expath;
35461f28255Scgd 	}
35561f28255Scgd     }
35661f28255Scgd }
35761f28255Scgd 
35861f28255Scgd /*ARGSUSED*/
35961f28255Scgd void
execash(Char ** t,struct command * kp)360b771e65bSwiz execash(Char **t, struct command *kp)
36161f28255Scgd {
362cee2bad8Smycroft     jmp_buf osetexit;
363cee2bad8Smycroft     sig_t osigint, osigquit, osigterm;
364b771e65bSwiz     int my_reenter, odidfds, oOLDSTD, oSHERR, oSHIN, oSHOUT;
365b771e65bSwiz     int saveDIAG, saveIN, saveOUT, saveSTD;
366cee2bad8Smycroft 
36761f28255Scgd     if (chkstop == 0 && setintr)
36861f28255Scgd 	panystop(0);
369cee2bad8Smycroft     /*
370cee2bad8Smycroft      * Hmm, we don't really want to do that now because we might
371cee2bad8Smycroft      * fail, but what is the choice
372cee2bad8Smycroft      */
37361f28255Scgd     rechist();
374cee2bad8Smycroft 
375cee2bad8Smycroft     osigint  = signal(SIGINT, parintr);
376cee2bad8Smycroft     osigquit = signal(SIGQUIT, parintr);
377cee2bad8Smycroft     osigterm = signal(SIGTERM, parterm);
378cee2bad8Smycroft 
379cee2bad8Smycroft     odidfds = didfds;
380cee2bad8Smycroft     oSHIN = SHIN;
381cee2bad8Smycroft     oSHOUT = SHOUT;
382cee2bad8Smycroft     oSHERR = SHERR;
383cee2bad8Smycroft     oOLDSTD = OLDSTD;
384cee2bad8Smycroft 
385cee2bad8Smycroft     saveIN = dcopy(SHIN, -1);
386cee2bad8Smycroft     saveOUT = dcopy(SHOUT, -1);
387cee2bad8Smycroft     saveDIAG = dcopy(SHERR, -1);
388cee2bad8Smycroft     saveSTD = dcopy(OLDSTD, -1);
389cee2bad8Smycroft 
39061f28255Scgd     lshift(kp->t_dcom, 1);
391cee2bad8Smycroft 
392cee2bad8Smycroft     getexit(osetexit);
393cee2bad8Smycroft 
394cee2bad8Smycroft     if ((my_reenter = setexit()) == 0) {
395cee2bad8Smycroft 	SHIN = dcopy(0, -1);
396cee2bad8Smycroft 	SHOUT = dcopy(1, -1);
397cee2bad8Smycroft 	SHERR = dcopy(2, -1);
398cee2bad8Smycroft 	didfds = 0;
399cee2bad8Smycroft 	doexec(t, kp);
400cee2bad8Smycroft     }
401cee2bad8Smycroft 
402cee2bad8Smycroft     (void)signal(SIGINT, osigint);
403cee2bad8Smycroft     (void)signal(SIGQUIT, osigquit);
404cee2bad8Smycroft     (void)signal(SIGTERM, osigterm);
405cee2bad8Smycroft 
406cee2bad8Smycroft     doneinp = 0;
407cee2bad8Smycroft     didfds = odidfds;
408cee2bad8Smycroft     (void)close(SHIN);
409cee2bad8Smycroft     (void)close(SHOUT);
410cee2bad8Smycroft     (void)close(SHERR);
411cee2bad8Smycroft     (void)close(OLDSTD);
412cee2bad8Smycroft     SHIN = dmove(saveIN, oSHIN);
413cee2bad8Smycroft     SHOUT = dmove(saveOUT, oSHOUT);
414cee2bad8Smycroft     SHERR = dmove(saveDIAG, oSHERR);
415cee2bad8Smycroft     OLDSTD = dmove(saveSTD, oOLDSTD);
416cee2bad8Smycroft 
417cee2bad8Smycroft     resexit(osetexit);
418ee9e50eaSmycroft     if (my_reenter)
419cee2bad8Smycroft 	stderror(ERR_SILENT);
42061f28255Scgd }
42161f28255Scgd 
42261f28255Scgd void
xechoit(Char ** t)423b771e65bSwiz xechoit(Char **t)
42461f28255Scgd {
42561f28255Scgd     if (adrof(STRecho)) {
426971f3382Schristos 	int odidfds = didfds;
427cee2bad8Smycroft 	(void)fflush(csherr);
428971f3382Schristos 	odidfds = didfds;
429971f3382Schristos 	didfds = 0;
430cee2bad8Smycroft 	blkpr(csherr, t);
431cee2bad8Smycroft 	(void)fputc('\n', csherr);
432971f3382Schristos 	(void)fflush(csherr);
433971f3382Schristos 	didfds = odidfds;
43461f28255Scgd     }
43561f28255Scgd }
43661f28255Scgd 
43761f28255Scgd void
438cee2bad8Smycroft /*ARGSUSED*/
dohash(Char ** v,struct command * t)439b771e65bSwiz dohash(Char **v, struct command *t)
44061f28255Scgd {
44176adbe2bStls     struct dirent *dp;
442b771e65bSwiz     struct varent *pathv;
443b771e65bSwiz     DIR *dirp;
44461f28255Scgd     Char **pv;
4459050ab5cSlukem     size_t cnt;
4469050ab5cSlukem     int hashval, i;
44761f28255Scgd 
448b771e65bSwiz     i = 0;
44961f28255Scgd     havhash = 1;
450b771e65bSwiz     pathv = adrof(STRpath);
451b771e65bSwiz 
45261f28255Scgd     for (cnt = 0; cnt < sizeof xhash; cnt++)
45361f28255Scgd 	xhash[cnt] = 0;
454cee2bad8Smycroft     if (pathv == 0)
45561f28255Scgd 	return;
456cee2bad8Smycroft     for (pv = pathv->vec; *pv; pv++, i++) {
45761f28255Scgd 	if (pv[0][0] != '/')
45861f28255Scgd 	    continue;
45961f28255Scgd 	dirp = opendir(short2str(*pv));
46061f28255Scgd 	if (dirp == NULL)
46161f28255Scgd 	    continue;
46261f28255Scgd 	while ((dp = readdir(dirp)) != NULL) {
46361f28255Scgd 	    if (dp->d_ino == 0)
46461f28255Scgd 		continue;
46561f28255Scgd 	    if (dp->d_name[0] == '.' &&
46661f28255Scgd 		(dp->d_name[1] == '\0' ||
467cee2bad8Smycroft 		 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
46861f28255Scgd 		continue;
46961f28255Scgd 	    hashval = hash(hashname(str2short(dp->d_name)), i);
47061f28255Scgd 	    bis(xhash, hashval);
47161f28255Scgd 	    /* tw_add_comm_name (dp->d_name); */
47261f28255Scgd 	}
47361f28255Scgd 	(void) closedir(dirp);
47461f28255Scgd     }
47561f28255Scgd }
47661f28255Scgd 
47761f28255Scgd void
478cee2bad8Smycroft /*ARGSUSED*/
dounhash(Char ** v,struct command * t)479b771e65bSwiz dounhash(Char **v, struct command *t)
48061f28255Scgd {
48161f28255Scgd     havhash = 0;
48261f28255Scgd }
48361f28255Scgd 
48461f28255Scgd void
485cee2bad8Smycroft /*ARGSUSED*/
hashstat(Char ** v,struct command * t)486b771e65bSwiz hashstat(Char **v, struct command *t)
48761f28255Scgd {
48861f28255Scgd     if (hits + misses)
489cee2bad8Smycroft 	(void)fprintf(cshout, "%d hits, %d misses, %d%%\n",
49061f28255Scgd 	    hits, misses, 100 * hits / (hits + misses));
49161f28255Scgd }
49261f28255Scgd 
49361f28255Scgd /*
49461f28255Scgd  * Hash a command name.
49561f28255Scgd  */
49661f28255Scgd static int
hashname(Char * cp)497b771e65bSwiz hashname(Char *cp)
49861f28255Scgd {
49976adbe2bStls     long h = 0;
50061f28255Scgd 
50161f28255Scgd     while (*cp)
50261f28255Scgd 	h = hash(h, *cp++);
50361f28255Scgd     return ((int) h);
50461f28255Scgd }
505cee2bad8Smycroft 
506cee2bad8Smycroft static int
iscommand(Char * name)507b771e65bSwiz iscommand(Char *name)
508cee2bad8Smycroft {
50976adbe2bStls     struct varent *v;
510b771e65bSwiz     Char **pv, *sav;
511b771e65bSwiz     int hashval, hashval1, i;
512b79c2ef2Schristos     int slash;
513cee2bad8Smycroft 
514b771e65bSwiz     hashval = 0;
515b771e65bSwiz     slash = any(short2str(name), '/');
516cee2bad8Smycroft     v = adrof(STRpath);
517b771e65bSwiz 
518cee2bad8Smycroft     if (v == 0 || v->vec[0] == 0 || slash)
519cee2bad8Smycroft 	pv = justabs;
520cee2bad8Smycroft     else
521cee2bad8Smycroft 	pv = v->vec;
522cee2bad8Smycroft     sav = Strspl(STRslash, name);	/* / command name for postpending */
523cee2bad8Smycroft     if (havhash)
524cee2bad8Smycroft 	hashval = hashname(name);
525cee2bad8Smycroft     i = 0;
526cee2bad8Smycroft     do {
527cee2bad8Smycroft 	if (!slash && pv[0][0] == '/' && havhash) {
528cee2bad8Smycroft 	    hashval1 = hash(hashval, i);
529cee2bad8Smycroft 	    if (!bit(xhash, hashval1))
530cee2bad8Smycroft 		goto cont;
531cee2bad8Smycroft 	}
532cee2bad8Smycroft 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {	/* don't make ./xxx */
533cee2bad8Smycroft 	    if (executable(NULL, name, 0)) {
534*1767ce60Schristos 		free(sav);
535cee2bad8Smycroft 		return i + 1;
536cee2bad8Smycroft 	    }
537cee2bad8Smycroft 	}
538cee2bad8Smycroft 	else {
539cee2bad8Smycroft 	    if (executable(*pv, sav, 0)) {
540*1767ce60Schristos 		free(sav);
541cee2bad8Smycroft 		return i + 1;
542cee2bad8Smycroft 	    }
543cee2bad8Smycroft 	}
544cee2bad8Smycroft cont:
545cee2bad8Smycroft 	pv++;
546cee2bad8Smycroft 	i++;
547cee2bad8Smycroft     } while (*pv);
548*1767ce60Schristos     free(sav);
549cee2bad8Smycroft     return 0;
550cee2bad8Smycroft }
551cee2bad8Smycroft 
552cee2bad8Smycroft /* Also by:
553cee2bad8Smycroft  *  Andreas Luik <luik@isaak.isa.de>
554cee2bad8Smycroft  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
555cee2bad8Smycroft  *  Azenberstr. 35
556cee2bad8Smycroft  *  D-7000 Stuttgart 1
557cee2bad8Smycroft  *  West-Germany
558cee2bad8Smycroft  * is the executable() routine below and changes to iscommand().
559cee2bad8Smycroft  * Thanks again!!
560cee2bad8Smycroft  */
561cee2bad8Smycroft 
562cee2bad8Smycroft /*
563cee2bad8Smycroft  * executable() examines the pathname obtained by concatenating dir and name
564cee2bad8Smycroft  * (dir may be NULL), and returns 1 either if it is executable by us, or
565cee2bad8Smycroft  * if dir_ok is set and the pathname refers to a directory.
566cee2bad8Smycroft  * This is a bit kludgy, but in the name of optimization...
567cee2bad8Smycroft  */
568cee2bad8Smycroft static int
executable(Char * dir,Char * name,int dir_ok)569b79c2ef2Schristos executable(Char *dir, Char *name, int dir_ok)
570cee2bad8Smycroft {
571cee2bad8Smycroft     struct stat stbuf;
572cee2bad8Smycroft     Char path[MAXPATHLEN + 1], *dp, *sp;
573cee2bad8Smycroft     char *strname;
574cee2bad8Smycroft 
575cee2bad8Smycroft     if (dir && *dir) {
576cee2bad8Smycroft 	for (dp = path, sp = dir; *sp; *dp++ = *sp++)
577cee2bad8Smycroft 	    if (dp == &path[MAXPATHLEN + 1]) {
578cee2bad8Smycroft 		*--dp = '\0';
579cee2bad8Smycroft 		break;
580cee2bad8Smycroft 	    }
581cee2bad8Smycroft 	for (sp = name; *sp; *dp++ = *sp++)
582cee2bad8Smycroft 	    if (dp == &path[MAXPATHLEN + 1]) {
583cee2bad8Smycroft 		*--dp = '\0';
584cee2bad8Smycroft 		break;
585cee2bad8Smycroft 	    }
586cee2bad8Smycroft 	*dp = '\0';
587cee2bad8Smycroft 	strname = short2str(path);
588cee2bad8Smycroft     }
589cee2bad8Smycroft     else
590cee2bad8Smycroft 	strname = short2str(name);
591b771e65bSwiz     return (stat(strname, &stbuf) != -1 && ((S_ISREG(stbuf.st_mode) &&
592cee2bad8Smycroft         /* save time by not calling access() in the hopeless case */
593cee2bad8Smycroft 	(stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
594b771e65bSwiz 	access(strname, X_OK) == 0) || (dir_ok && S_ISDIR(stbuf.st_mode))));
595cee2bad8Smycroft }
596cee2bad8Smycroft 
597cee2bad8Smycroft /* The dowhich() is by:
598cee2bad8Smycroft  *  Andreas Luik <luik@isaak.isa.de>
599cee2bad8Smycroft  *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
600cee2bad8Smycroft  *  Azenberstr. 35
601cee2bad8Smycroft  *  D-7000 Stuttgart 1
602cee2bad8Smycroft  *  West-Germany
603cee2bad8Smycroft  * Thanks!!
604cee2bad8Smycroft  */
605cee2bad8Smycroft /*ARGSUSED*/
606cee2bad8Smycroft void
dowhich(Char ** v,struct command * c)607b771e65bSwiz dowhich(Char **v, struct command *c)
608cee2bad8Smycroft {
609537f55c6Slukem     struct wordent lexw[3];
610cee2bad8Smycroft     struct varent *vp;
611cee2bad8Smycroft 
612537f55c6Slukem     lexw[0].next = &lexw[1];
613537f55c6Slukem     lexw[1].next = &lexw[2];
614537f55c6Slukem     lexw[2].next = &lexw[0];
615cee2bad8Smycroft 
616537f55c6Slukem     lexw[0].prev = &lexw[2];
617537f55c6Slukem     lexw[1].prev = &lexw[0];
618537f55c6Slukem     lexw[2].prev = &lexw[1];
619cee2bad8Smycroft 
620537f55c6Slukem     lexw[0].word = STRNULL;
621537f55c6Slukem     lexw[2].word = STRret;
622cee2bad8Smycroft 
623cee2bad8Smycroft     while (*++v) {
624cee2bad8Smycroft 	if ((vp = adrof1(*v, &aliases)) != NULL) {
625cee2bad8Smycroft 	    (void)fprintf(cshout, "%s: \t aliased to ", vis_str(*v));
626cee2bad8Smycroft 	    blkpr(cshout, vp->vec);
627cee2bad8Smycroft 	    (void)fputc('\n', cshout);
6280c750830Schristos 	    set(STRstatus, Strsave(STR0));
629cee2bad8Smycroft 	}
630cee2bad8Smycroft 	else {
631537f55c6Slukem 	    lexw[1].word = *v;
632537f55c6Slukem 	    set(STRstatus, Strsave(tellmewhat(lexw, NULL) ? STR0 : STR1));
633cee2bad8Smycroft 	}
634cee2bad8Smycroft     }
635cee2bad8Smycroft }
636cee2bad8Smycroft 
6370c750830Schristos static int
tellmewhat(struct wordent * lexp,Char * str)638b771e65bSwiz tellmewhat(struct wordent *lexp, Char *str)
639cee2bad8Smycroft {
64076adbe2bStls     struct biltins *bptr;
641b771e65bSwiz     struct wordent *sp;
642b771e65bSwiz     Char *cmd, *s0, *s1, *s2;
643b771e65bSwiz     int i;
644b79c2ef2Schristos     int aliased, found;
645cee2bad8Smycroft     Char qc;
646cee2bad8Smycroft 
647b771e65bSwiz     aliased = 0;
648b771e65bSwiz     sp = lexp->next;
649b771e65bSwiz 
650cee2bad8Smycroft     if (adrof1(sp->word, &aliases)) {
6510c750830Schristos 	alias(lexp);
6520c750830Schristos 	sp = lexp->next;
653cee2bad8Smycroft 	aliased = 1;
654cee2bad8Smycroft     }
655cee2bad8Smycroft 
656cee2bad8Smycroft     s0 = sp->word;		/* to get the memory freeing right... */
657cee2bad8Smycroft 
658cee2bad8Smycroft     /* handle quoted alias hack */
659cee2bad8Smycroft     if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE)
660cee2bad8Smycroft 	(sp->word)++;
661cee2bad8Smycroft 
662cee2bad8Smycroft     /* do quoting, if it hasn't been done */
663cee2bad8Smycroft     s1 = s2 = sp->word;
664cee2bad8Smycroft     while (*s2)
665cee2bad8Smycroft 	switch (*s2) {
666cee2bad8Smycroft 	case '\'':
667cee2bad8Smycroft 	case '"':
668cee2bad8Smycroft 	    qc = *s2++;
669cee2bad8Smycroft 	    while (*s2 && *s2 != qc)
67037e39248Schristos 		*s1++ = (Char)(*s2++ | QUOTE);
671cee2bad8Smycroft 	    if (*s2)
672cee2bad8Smycroft 		s2++;
673cee2bad8Smycroft 	    break;
674cee2bad8Smycroft 	case '\\':
675cee2bad8Smycroft 	    if (*++s2)
67637e39248Schristos 		*s1++ = (Char)(*s2++ | QUOTE);
677cee2bad8Smycroft 	    break;
678cee2bad8Smycroft 	default:
679cee2bad8Smycroft 	    *s1++ = *s2++;
680cee2bad8Smycroft 	}
681cee2bad8Smycroft     *s1 = '\0';
682cee2bad8Smycroft 
683cee2bad8Smycroft     for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) {
684cee2bad8Smycroft 	if (eq(sp->word, str2short(bptr->bname))) {
6850c750830Schristos 	    if (str == NULL) {
686cee2bad8Smycroft 		if (aliased)
6870c750830Schristos 		    prlex(cshout, lexp);
688cee2bad8Smycroft 		(void)fprintf(cshout, "%s: shell built-in command.\n",
689cee2bad8Smycroft 			       vis_str(sp->word));
6900c750830Schristos 	    }
6910c750830Schristos 	    else
6920c750830Schristos 		(void)Strcpy(str, sp->word);
693cee2bad8Smycroft 	    sp->word = s0;	/* we save and then restore this */
6940c750830Schristos 	    return 1;
695cee2bad8Smycroft 	}
696cee2bad8Smycroft     }
697cee2bad8Smycroft 
698ec16f0d3Schristos     sp->word = cmd = globone(sp->word, G_IGNORE);
699ec16f0d3Schristos 
7000c750830Schristos     if ((i = iscommand(sp->word)) != 0) {
70176adbe2bStls 	Char **pv;
70276adbe2bStls 	struct varent *v;
703b79c2ef2Schristos 	int    slash = any(short2str(sp->word), '/');
704cee2bad8Smycroft 
705cee2bad8Smycroft 	v = adrof(STRpath);
706cee2bad8Smycroft 	if (v == 0 || v->vec[0] == 0 || slash)
707cee2bad8Smycroft 	    pv = justabs;
708cee2bad8Smycroft 	else
709cee2bad8Smycroft 	    pv = v->vec;
710cee2bad8Smycroft 
711cee2bad8Smycroft 	while (--i)
712cee2bad8Smycroft 	    pv++;
713cee2bad8Smycroft 	if (pv[0][0] == 0 || eq(pv[0], STRdot)) {
714ec16f0d3Schristos 	    if (!slash) {
715cee2bad8Smycroft 		sp->word = Strspl(STRdotsl, sp->word);
7160c750830Schristos 		prlex(cshout, lexp);
717*1767ce60Schristos 		free(sp->word);
718ec16f0d3Schristos 	    }
719ec16f0d3Schristos 	    else
7200c750830Schristos 		prlex(cshout, lexp);
721cee2bad8Smycroft 	}
7220c750830Schristos 	else {
723cee2bad8Smycroft 	    s1 = Strspl(*pv, STRslash);
724cee2bad8Smycroft 	    sp->word = Strspl(s1, sp->word);
725*1767ce60Schristos 	    free(s1);
7260c750830Schristos 	    if (str == NULL)
7270c750830Schristos 		prlex(cshout, lexp);
7280c750830Schristos 	    else
7290c750830Schristos 		(void)Strcpy(str, sp->word);
730*1767ce60Schristos 	    free(sp->word);
731cee2bad8Smycroft 	}
7320c750830Schristos 	found = 1;
7330c750830Schristos     }
734cee2bad8Smycroft     else {
7350c750830Schristos  	if (str == NULL) {
736cee2bad8Smycroft 	    if (aliased)
7370c750830Schristos 		prlex(cshout, lexp);
7380c750830Schristos 	    (void)fprintf(csherr,
7390c750830Schristos 			   "%s: Command not found.\n", vis_str(sp->word));
7400c750830Schristos 	}
7410c750830Schristos 	else
7420c750830Schristos 	    (void)Strcpy(str, sp->word);
7430c750830Schristos 	found = 0;
744cee2bad8Smycroft     }
745cee2bad8Smycroft     sp->word = s0;		/* we save and then restore this */
746*1767ce60Schristos     free(cmd);
7470c750830Schristos     return found;
748cee2bad8Smycroft }
749