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