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