xref: /original-bsd/bin/csh/exec.c (revision 6c8be42f)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley Software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)sh.exec.c	5.7 (Berkeley) 5/27/90";
9 #endif
10 
11 #include "sh.h"
12 #include <sys/dir.h>
13 #include <string.h>
14 #include "pathnames.h"
15 
16 /*
17  * C shell
18  */
19 
20 /*
21  * System level search and execute of a command.
22  * We look in each directory for the specified command name.
23  * If the name contains a '/' then we execute only the full path name.
24  * If there is no search path then we execute only full path names.
25  */
26 
27 /*
28  * As we search for the command we note the first non-trivial error
29  * message for presentation to the user.  This allows us often
30  * to show that a file has the wrong mode/no access when the file
31  * is not in the last component of the search path, so we must
32  * go on after first detecting the error.
33  */
34 char	*exerr;			/* Execution error message */
35 char	*expath;		/* Path for exerr */
36 
37 /*
38  * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
39  * to hash execs.  If it is allocated (havhash true), then to tell
40  * whether ``name'' is (possibly) present in the i'th component
41  * of the variable path, you look at the bit in xhash indexed by
42  * hash(hashname("name"), i).  This is setup automatically
43  * after .login is executed, and recomputed whenever ``path'' is
44  * changed.
45  * The two part hash function is designed to let texec() call the
46  * more expensive hashname() only once and the simple hash() several
47  * times (once for each path component checked).
48  * Byte size is assumed to be 8.
49  */
50 #define	HSHSIZ		8192			/* 1k bytes */
51 #define HSHMASK		(HSHSIZ - 1)
52 #define HSHMUL		243
53 char	xhash[HSHSIZ / 8];
54 #define hash(a, b)	((a) * HSHMUL + (b) & HSHMASK)
55 #define bit(h, b)	((h)[(b) >> 3] & 1 << ((b) & 7))	/* bit test */
56 #define bis(h, b)	((h)[(b) >> 3] |= 1 << ((b) & 7))	/* bit set */
57 #ifdef VFORK
58 int	hits, misses;
59 #endif
60 
61 /* Dummy search path for just absolute search when no path */
62 char	*justabs[] =	{ "", 0 };
63 
64 doexec(t)
65 	register struct command *t;
66 {
67 	char *sav;
68 	register char *dp, **pv, **av;
69 	register struct varent *v;
70 	bool slash = (bool)index(t->t_dcom[0], '/');
71 	int hashval, hashval1, i;
72 	char *blk[2];
73 
74 	/*
75 	 * Glob the command name.  If this does anything, then we
76 	 * will execute the command only relative to ".".  One special
77 	 * case: if there is no PATH, then we execute only commands
78 	 * which start with '/'.
79 	 */
80 	dp = globone(t->t_dcom[0]);
81 	sav = t->t_dcom[0];
82 	exerr = 0; expath = t->t_dcom[0] = dp;
83 	xfree(sav);
84 	v = adrof("path");
85 	if (v == 0 && expath[0] != '/')
86 		pexerr();
87 	slash |= gflag;
88 
89 	/*
90 	 * Glob the argument list, if necessary.
91 	 * Otherwise trim off the quote bits.
92 	 */
93 	gflag = 0; av = &t->t_dcom[1];
94 	tglob(av);
95 	if (gflag) {
96 		av = globall(av);
97 		if (av == 0)
98 			error("No match");
99 	}
100 	blk[0] = t->t_dcom[0];
101 	blk[1] = 0;
102 	av = blkspl(blk, av);
103 #ifdef VFORK
104 	Vav = av;
105 #endif
106 	trim(av);
107 
108 	xechoit(av);		/* Echo command if -x */
109 	/*
110 	 * Since all internal file descriptors are set to close on exec,
111 	 * we don't need to close them explicitly here.  Just reorient
112 	 * ourselves for error messages.
113 	 */
114 	SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0;
115 
116 	/*
117 	 * We must do this AFTER any possible forking (like `foo`
118 	 * in glob) so that this shell can still do subprocesses.
119 	 */
120 	(void) sigsetmask(0L);
121 
122 	/*
123 	 * If no path, no words in path, or a / in the filename
124 	 * then restrict the command search.
125 	 */
126 	if (v == 0 || v->vec[0] == 0 || slash)
127 		pv = justabs;
128 	else
129 		pv = v->vec;
130 	sav = strspl("/", *av);		/* / command name for postpending */
131 #ifdef VFORK
132 	Vsav = sav;
133 #endif
134 	if (havhash)
135 		hashval = hashname(*av);
136 	i = 0;
137 #ifdef VFORK
138 	hits++;
139 #endif
140 	do {
141 		if (!slash && pv[0][0] == '/' && havhash) {
142 			hashval1 = hash(hashval, i);
143 			if (!bit(xhash, hashval1))
144 				goto cont;
145 		}
146 		if (pv[0][0] == 0 || eq(pv[0], "."))	/* don't make ./xxx */
147 			texec(*av, av);
148 		else {
149 			dp = strspl(*pv, sav);
150 #ifdef VFORK
151 			Vdp = dp;
152 #endif
153 			texec(dp, av);
154 #ifdef VFORK
155 			Vdp = 0;
156 #endif
157 			xfree(dp);
158 		}
159 #ifdef VFORK
160 		misses++;
161 #endif
162 cont:
163 		pv++;
164 		i++;
165 	} while (*pv);
166 #ifdef VFORK
167 	hits--;
168 #endif
169 #ifdef VFORK
170 	Vsav = 0;
171 	Vav = 0;
172 #endif
173 	xfree(sav);
174 	xfree((char *)av);
175 	pexerr();
176 }
177 
178 pexerr()
179 {
180 
181 	/* Couldn't find the damn thing */
182 	setname(expath);
183 	/* xfree(expath); */
184 	if (exerr)
185 		bferr(exerr);
186 	bferr("Command not found");
187 }
188 
189 /*
190  * Execute command f, arg list t.
191  * Record error message if not found.
192  * Also do shell scripts here.
193  */
194 texec(f, t)
195 	char *f;
196 	register char **t;
197 {
198 	register struct varent *v;
199 	register char **vp;
200 	char *lastsh[2];
201 
202 	execv(f, t);
203 	switch (errno) {
204 
205 	case ENOEXEC:
206 		/*
207 		 * If there is an alias for shell, then
208 		 * put the words of the alias in front of the
209 		 * argument list replacing the command name.
210 		 * Note no interpretation of the words at this point.
211 		 */
212 		v = adrof1("shell", &aliases);
213 		if (v == 0) {
214 			register int ff = open(f, 0);
215 			char ch;
216 
217 			vp = lastsh;
218 			vp[0] = adrof("shell") ? value("shell") : _PATH_CSHELL;
219 			vp[1] = (char *) NULL;
220 			if (ff != -1 && read(ff, &ch, 1) == 1 && ch != '#')
221 				vp[0] = _PATH_BSHELL;
222 			(void) close(ff);
223 		} else
224 			vp = v->vec;
225 		t[0] = f;
226 		t = blkspl(vp, t);		/* Splice up the new arglst */
227 		f = *t;
228 		execv(f, t);
229 		xfree((char *)t);
230 		/* The sky is falling, the sky is falling! */
231 
232 	case ENOMEM:
233 		Perror(f);
234 
235 	case ENOENT:
236 		break;
237 
238 	default:
239 		if (exerr == 0) {
240 			exerr = strerror(errno);
241 			expath = savestr(f);
242 		}
243 	}
244 }
245 
246 /*ARGSUSED*/
247 execash(t, kp)
248 	char **t;
249 	register struct command *kp;
250 {
251 
252 	rechist();
253 	(void) signal(SIGINT, parintr);
254 	(void) signal(SIGQUIT, parintr);
255 	(void) signal(SIGTERM, parterm);	/* if doexec loses, screw */
256 	lshift(kp->t_dcom, 1);
257 	exiterr++;
258 	doexec(kp);
259 	/*NOTREACHED*/
260 }
261 
262 xechoit(t)
263 	char **t;
264 {
265 
266 	if (adrof("echo")) {
267 		flush();
268 		haderr = 1;
269 		blkpr(t), cshputchar('\n');
270 		haderr = 0;
271 	}
272 }
273 
274 /*VARARGS0*//*ARGSUSED*/
275 dohash()
276 {
277 	struct stat stb;
278 	DIR *dirp;
279 	register struct direct *dp;
280 	register int cnt;
281 	int i = 0;
282 	struct varent *v = adrof("path");
283 	char **pv;
284 	int hashval;
285 
286 	havhash = 1;
287 	for (cnt = 0; cnt < sizeof xhash; cnt++)
288 		xhash[cnt] = 0;
289 	if (v == 0)
290 		return;
291 	for (pv = v->vec; *pv; pv++, i++) {
292 		if (pv[0][0] != '/')
293 			continue;
294 		dirp = opendir(*pv);
295 		if (dirp == NULL)
296 			continue;
297 		if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) {
298 			closedir(dirp);
299 			continue;
300 		}
301 		while ((dp = readdir(dirp)) != NULL) {
302 			if (dp->d_ino == 0)
303 				continue;
304 			if (dp->d_name[0] == '.' &&
305 			    (dp->d_name[1] == '\0' ||
306 			     dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
307 				continue;
308 			hashval = hash(hashname(dp->d_name), i);
309 			bis(xhash, hashval);
310 		}
311 		closedir(dirp);
312 	}
313 }
314 
315 dounhash()
316 {
317 
318 	havhash = 0;
319 }
320 
321 #ifdef VFORK
322 hashstat()
323 {
324 
325 	if (hits+misses)
326 		printf("%d hits, %d misses, %d%%\n",
327 			hits, misses, 100 * hits / (hits + misses));
328 }
329 #endif
330 
331 /*
332  * Hash a command name.
333  */
334 hashname(cp)
335 	register char *cp;
336 {
337 	register long h = 0;
338 
339 	while (*cp)
340 		h = hash(h, *cp++);
341 	return ((int) h);
342 }
343