xref: /csrg-svn/bin/csh/exec.c (revision 47738)
1*47738Sbostic /*-
2*47738Sbostic  * Copyright (c) 1980 The Regents of the University of California.
3*47738Sbostic  * All rights reserved.
4*47738Sbostic  *
5*47738Sbostic  * %sccs.include.redist.c%
621930Sdist  */
721930Sdist 
817508Sedward #ifndef lint
9*47738Sbostic static char sccsid[] = "@(#)exec.c	5.10 (Berkeley) 04/02/91";
10*47738Sbostic #endif /* not lint */
111292Sbill 
121292Sbill #include "sh.h"
1313560Ssam #include <sys/dir.h>
1442406Sbostic #include <string.h>
1537251Sbostic #include "pathnames.h"
161292Sbill 
171292Sbill /*
181292Sbill  * C shell
191292Sbill  */
201292Sbill 
211292Sbill /*
221292Sbill  * System level search and execute of a command.
231292Sbill  * We look in each directory for the specified command name.
241292Sbill  * If the name contains a '/' then we execute only the full path name.
251292Sbill  * If there is no search path then we execute only full path names.
261292Sbill  */
271292Sbill 
281292Sbill /*
291292Sbill  * As we search for the command we note the first non-trivial error
301292Sbill  * message for presentation to the user.  This allows us often
311292Sbill  * to show that a file has the wrong mode/no access when the file
321292Sbill  * is not in the last component of the search path, so we must
331292Sbill  * go on after first detecting the error.
341292Sbill  */
351292Sbill char	*exerr;			/* Execution error message */
361292Sbill char	*expath;		/* Path for exerr */
371292Sbill 
381292Sbill /*
3917508Sedward  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
4017508Sedward  * to hash execs.  If it is allocated (havhash true), then to tell
4117508Sedward  * whether ``name'' is (possibly) present in the i'th component
4217508Sedward  * of the variable path, you look at the bit in xhash indexed by
4317508Sedward  * hash(hashname("name"), i).  This is setup automatically
441292Sbill  * after .login is executed, and recomputed whenever ``path'' is
451292Sbill  * changed.
4617508Sedward  * The two part hash function is designed to let texec() call the
4717508Sedward  * more expensive hashname() only once and the simple hash() several
4817508Sedward  * times (once for each path component checked).
4917508Sedward  * Byte size is assumed to be 8.
501292Sbill  */
5117508Sedward #define	HSHSIZ		8192			/* 1k bytes */
5217508Sedward #define HSHMASK		(HSHSIZ - 1)
5317508Sedward #define HSHMUL		243
5417508Sedward char	xhash[HSHSIZ / 8];
5517508Sedward #define hash(a, b)	((a) * HSHMUL + (b) & HSHMASK)
5617508Sedward #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
5717508Sedward #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
581292Sbill #ifdef VFORK
591292Sbill int	hits, misses;
601292Sbill #endif
611292Sbill 
621292Sbill /* Dummy search path for just absolute search when no path */
631292Sbill char	*justabs[] =	{ "", 0 };
641292Sbill 
651292Sbill doexec(t)
661292Sbill 	register struct command *t;
671292Sbill {
681292Sbill 	char *sav;
691292Sbill 	register char *dp, **pv, **av;
701292Sbill 	register struct varent *v;
7147408Sbostic 	bool slash = (bool)index(t->t_dcom[0], '/');
7217508Sedward 	int hashval, hashval1, i;
731292Sbill 	char *blk[2];
741292Sbill 
751292Sbill 	/*
761292Sbill 	 * Glob the command name.  If this does anything, then we
771292Sbill 	 * will execute the command only relative to ".".  One special
781292Sbill 	 * case: if there is no PATH, then we execute only commands
791292Sbill 	 * which start with '/'.
801292Sbill 	 */
811292Sbill 	dp = globone(t->t_dcom[0]);
821292Sbill 	sav = t->t_dcom[0];
831292Sbill 	exerr = 0; expath = t->t_dcom[0] = dp;
841292Sbill 	xfree(sav);
851292Sbill 	v = adrof("path");
861292Sbill 	if (v == 0 && expath[0] != '/')
871292Sbill 		pexerr();
881292Sbill 	slash |= gflag;
891292Sbill 
901292Sbill 	/*
911292Sbill 	 * Glob the argument list, if necessary.
921292Sbill 	 * Otherwise trim off the quote bits.
931292Sbill 	 */
941292Sbill 	gflag = 0; av = &t->t_dcom[1];
9517508Sedward 	tglob(av);
961292Sbill 	if (gflag) {
9747408Sbostic 		av = globall(av);
981292Sbill 		if (av == 0)
991292Sbill 			error("No match");
1001292Sbill 	}
1011292Sbill 	blk[0] = t->t_dcom[0];
1021292Sbill 	blk[1] = 0;
1031292Sbill 	av = blkspl(blk, av);
1041292Sbill #ifdef VFORK
1051292Sbill 	Vav = av;
1061292Sbill #endif
10717508Sedward 	trim(av);
1081292Sbill 
1091292Sbill 	xechoit(av);		/* Echo command if -x */
11017508Sedward 	/*
11117508Sedward 	 * Since all internal file descriptors are set to close on exec,
11217508Sedward 	 * we don't need to close them explicitly here.  Just reorient
11317508Sedward 	 * ourselves for error messages.
11417508Sedward 	 */
11517508Sedward 	SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0;
1161292Sbill 
1171292Sbill 	/*
11812996Ssam 	 * We must do this AFTER any possible forking (like `foo`
1191292Sbill 	 * in glob) so that this shell can still do subprocesses.
1201292Sbill 	 */
12131685Sbostic 	(void) sigsetmask(0L);
1221292Sbill 
1231292Sbill 	/*
1241292Sbill 	 * If no path, no words in path, or a / in the filename
1251292Sbill 	 * then restrict the command search.
1261292Sbill 	 */
1271292Sbill 	if (v == 0 || v->vec[0] == 0 || slash)
1281292Sbill 		pv = justabs;
1291292Sbill 	else
1301292Sbill 		pv = v->vec;
1311292Sbill 	sav = strspl("/", *av);		/* / command name for postpending */
1321292Sbill #ifdef VFORK
1331292Sbill 	Vsav = sav;
1341292Sbill #endif
1351292Sbill 	if (havhash)
13617508Sedward 		hashval = hashname(*av);
1371292Sbill 	i = 0;
1381292Sbill #ifdef VFORK
1391292Sbill 	hits++;
1401292Sbill #endif
1411292Sbill 	do {
14217508Sedward 		if (!slash && pv[0][0] == '/' && havhash) {
14317508Sedward 			hashval1 = hash(hashval, i);
14417508Sedward 			if (!bit(xhash, hashval1))
14517508Sedward 				goto cont;
14617508Sedward 		}
1471292Sbill 		if (pv[0][0] == 0 || eq(pv[0], "."))	/* don't make ./xxx */
1481292Sbill 			texec(*av, av);
1491292Sbill 		else {
1501292Sbill 			dp = strspl(*pv, sav);
1511292Sbill #ifdef VFORK
1521292Sbill 			Vdp = dp;
1531292Sbill #endif
1541292Sbill 			texec(dp, av);
1551292Sbill #ifdef VFORK
1561292Sbill 			Vdp = 0;
1571292Sbill #endif
1581292Sbill 			xfree(dp);
1591292Sbill 		}
1601292Sbill #ifdef VFORK
1611292Sbill 		misses++;
1621292Sbill #endif
1631292Sbill cont:
1641292Sbill 		pv++;
1651292Sbill 		i++;
1661292Sbill 	} while (*pv);
1671292Sbill #ifdef VFORK
1681292Sbill 	hits--;
1691292Sbill #endif
1701292Sbill #ifdef VFORK
1711292Sbill 	Vsav = 0;
1721292Sbill 	Vav = 0;
1731292Sbill #endif
1741292Sbill 	xfree(sav);
17517508Sedward 	xfree((char *)av);
1761292Sbill 	pexerr();
1771292Sbill }
1781292Sbill 
1791292Sbill pexerr()
1801292Sbill {
1811292Sbill 
1821292Sbill 	/* Couldn't find the damn thing */
1831292Sbill 	setname(expath);
1841292Sbill 	/* xfree(expath); */
1851292Sbill 	if (exerr)
1861292Sbill 		bferr(exerr);
1871292Sbill 	bferr("Command not found");
1881292Sbill }
1891292Sbill 
1901292Sbill /*
1911292Sbill  * Execute command f, arg list t.
1921292Sbill  * Record error message if not found.
1931292Sbill  * Also do shell scripts here.
1941292Sbill  */
1951292Sbill texec(f, t)
1961292Sbill 	char *f;
1971292Sbill 	register char **t;
1981292Sbill {
1991292Sbill 	register struct varent *v;
2001292Sbill 	register char **vp;
20117508Sedward 	char *lastsh[2];
2021292Sbill 
2031292Sbill 	execv(f, t);
2041292Sbill 	switch (errno) {
2051292Sbill 
2061292Sbill 	case ENOEXEC:
2071292Sbill 		/*
2081292Sbill 		 * If there is an alias for shell, then
2091292Sbill 		 * put the words of the alias in front of the
2101292Sbill 		 * argument list replacing the command name.
2111292Sbill 		 * Note no interpretation of the words at this point.
2121292Sbill 		 */
2131292Sbill 		v = adrof1("shell", &aliases);
2141292Sbill 		if (v == 0) {
2151292Sbill 			register int ff = open(f, 0);
2161292Sbill 			char ch;
2171292Sbill 
2181292Sbill 			vp = lastsh;
21936786Sbostic 			vp[0] = adrof("shell") ? value("shell") : _PATH_CSHELL;
22018131Skarels 			vp[1] = (char *) NULL;
2211292Sbill 			if (ff != -1 && read(ff, &ch, 1) == 1 && ch != '#')
22236786Sbostic 				vp[0] = _PATH_BSHELL;
22317508Sedward 			(void) close(ff);
2241292Sbill 		} else
2251292Sbill 			vp = v->vec;
2261292Sbill 		t[0] = f;
2271292Sbill 		t = blkspl(vp, t);		/* Splice up the new arglst */
2281292Sbill 		f = *t;
2291292Sbill 		execv(f, t);
2301292Sbill 		xfree((char *)t);
2311292Sbill 		/* The sky is falling, the sky is falling! */
2321292Sbill 
2331292Sbill 	case ENOMEM:
2341292Sbill 		Perror(f);
2351292Sbill 
2361292Sbill 	case ENOENT:
2371292Sbill 		break;
2381292Sbill 
2391292Sbill 	default:
2401292Sbill 		if (exerr == 0) {
24142406Sbostic 			exerr = strerror(errno);
2421292Sbill 			expath = savestr(f);
2431292Sbill 		}
2441292Sbill 	}
2451292Sbill }
2461292Sbill 
24717508Sedward /*ARGSUSED*/
2481292Sbill execash(t, kp)
24917508Sedward 	char **t;
2501292Sbill 	register struct command *kp;
2511292Sbill {
2521292Sbill 
2534188Smckusic 	rechist();
25417508Sedward 	(void) signal(SIGINT, parintr);
25517508Sedward 	(void) signal(SIGQUIT, parintr);
25617508Sedward 	(void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
2571292Sbill 	lshift(kp->t_dcom, 1);
2581292Sbill 	exiterr++;
2591292Sbill 	doexec(kp);
2601292Sbill 	/*NOTREACHED*/
2611292Sbill }
2621292Sbill 
2631292Sbill xechoit(t)
2641292Sbill 	char **t;
2651292Sbill {
2661292Sbill 
2671292Sbill 	if (adrof("echo")) {
2681292Sbill 		flush();
2691292Sbill 		haderr = 1;
27034340Sbostic 		blkpr(t), cshputchar('\n');
2711292Sbill 		haderr = 0;
2721292Sbill 	}
2731292Sbill }
2741292Sbill 
27517508Sedward /*VARARGS0*//*ARGSUSED*/
27631685Sbostic dohash()
2771292Sbill {
2781292Sbill 	struct stat stb;
2795774Smckusic 	DIR *dirp;
2805774Smckusic 	register struct direct *dp;
2815774Smckusic 	register int cnt;
2821292Sbill 	int i = 0;
2831292Sbill 	struct varent *v = adrof("path");
2841292Sbill 	char **pv;
28517508Sedward 	int hashval;
2861292Sbill 
2871292Sbill 	havhash = 1;
28817508Sedward 	for (cnt = 0; cnt < sizeof xhash; cnt++)
2891292Sbill 		xhash[cnt] = 0;
2901292Sbill 	if (v == 0)
2911292Sbill 		return;
29217508Sedward 	for (pv = v->vec; *pv; pv++, i++) {
2931292Sbill 		if (pv[0][0] != '/')
2941292Sbill 			continue;
2955774Smckusic 		dirp = opendir(*pv);
2965774Smckusic 		if (dirp == NULL)
2971292Sbill 			continue;
29847724Sbostic 		if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) {
2995774Smckusic 			closedir(dirp);
3001292Sbill 			continue;
3011292Sbill 		}
3025774Smckusic 		while ((dp = readdir(dirp)) != NULL) {
3035774Smckusic 			if (dp->d_ino == 0)
3045774Smckusic 				continue;
30517508Sedward 			if (dp->d_name[0] == '.' &&
30617508Sedward 			    (dp->d_name[1] == '\0' ||
30717508Sedward 			     dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
30817508Sedward 				continue;
30917508Sedward 			hashval = hash(hashname(dp->d_name), i);
31017508Sedward 			bis(xhash, hashval);
3111292Sbill 		}
3125774Smckusic 		closedir(dirp);
3131292Sbill 	}
3141292Sbill }
3151292Sbill 
3161292Sbill dounhash()
3171292Sbill {
3181292Sbill 
3191292Sbill 	havhash = 0;
3201292Sbill }
3211292Sbill 
3221292Sbill #ifdef VFORK
3231292Sbill hashstat()
3241292Sbill {
3251292Sbill 
3261292Sbill 	if (hits+misses)
32717508Sedward 		printf("%d hits, %d misses, %d%%\n",
32817508Sedward 			hits, misses, 100 * hits / (hits + misses));
3291292Sbill }
3301292Sbill #endif
3311292Sbill 
33217508Sedward /*
33317508Sedward  * Hash a command name.
33417508Sedward  */
33517508Sedward hashname(cp)
3361292Sbill 	register char *cp;
3371292Sbill {
33817508Sedward 	register long h = 0;
3391292Sbill 
3401292Sbill 	while (*cp)
34117508Sedward 		h = hash(h, *cp++);
34217508Sedward 	return ((int) h);
3431292Sbill }
344