xref: /original-bsd/bin/csh/exec.c (revision 23a40993)
1 static	char *sccsid = "@(#)exec.c 4.7 06/11/83";
2 
3 #include "sh.h"
4 #include <dir.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 #ifdef notdef
97 	sigsys(SIGCHLD, SIG_IGN);	/* sigsys for vforks sake */
98 #endif
99 	sigsetmask(0);
100 
101 	/*
102 	 * If no path, no words in path, or a / in the filename
103 	 * then restrict the command search.
104 	 */
105 	if (v == 0 || v->vec[0] == 0 || slash)
106 		pv = justabs;
107 	else
108 		pv = v->vec;
109 	sav = strspl("/", *av);		/* / command name for postpending */
110 #ifdef VFORK
111 	Vsav = sav;
112 #endif
113 	if (havhash)
114 		hashval = xhash[hash(*av)];
115 	i = 0;
116 #ifdef VFORK
117 	hits++;
118 #endif
119 	do {
120 		if (!slash && pv[0][0] == '/' && havhash && (hashval & (1 << (i % 8))) == 0)
121 			goto cont;
122 		if (pv[0][0] == 0 || eq(pv[0], "."))	/* don't make ./xxx */
123 			texec(*av, av);
124 		else {
125 			dp = strspl(*pv, sav);
126 #ifdef VFORK
127 			Vdp = dp;
128 #endif
129 			texec(dp, av);
130 #ifdef VFORK
131 			Vdp = 0;
132 #endif
133 			xfree(dp);
134 		}
135 #ifdef VFORK
136 		misses++;
137 #endif
138 cont:
139 		pv++;
140 		i++;
141 	} while (*pv);
142 #ifdef VFORK
143 	hits--;
144 #endif
145 #ifdef VFORK
146 	Vsav = 0;
147 	Vav = 0;
148 #endif
149 	xfree(sav);
150 	xfree(av);
151 	pexerr();
152 }
153 
154 pexerr()
155 {
156 
157 	/* Couldn't find the damn thing */
158 	setname(expath);
159 	/* xfree(expath); */
160 	if (exerr)
161 		bferr(exerr);
162 	bferr("Command not found");
163 }
164 
165 /* Last resort shell */
166 char	*lastsh[] =	{ SHELLPATH, 0 };
167 
168 /*
169  * Execute command f, arg list t.
170  * Record error message if not found.
171  * Also do shell scripts here.
172  */
173 texec(f, t)
174 	char *f;
175 	register char **t;
176 {
177 	register struct varent *v;
178 	register char **vp;
179 	extern char *sys_errlist[];
180 
181 	execv(f, t);
182 	switch (errno) {
183 
184 	case ENOEXEC:
185 		/*
186 		 * If there is an alias for shell, then
187 		 * put the words of the alias in front of the
188 		 * argument list replacing the command name.
189 		 * Note no interpretation of the words at this point.
190 		 */
191 		v = adrof1("shell", &aliases);
192 		if (v == 0) {
193 #ifdef OTHERSH
194 			register int ff = open(f, 0);
195 			char ch;
196 #endif
197 
198 			vp = lastsh;
199 			vp[0] = adrof("shell") ? value("shell") : SHELLPATH;
200 #ifdef OTHERSH
201 			if (ff != -1 && read(ff, &ch, 1) == 1 && ch != '#')
202 				vp[0] = OTHERSH;
203 			close(ff);
204 #endif
205 		} else
206 			vp = v->vec;
207 		t[0] = f;
208 		t = blkspl(vp, t);		/* Splice up the new arglst */
209 		f = *t;
210 		execv(f, t);
211 		xfree((char *)t);
212 		/* The sky is falling, the sky is falling! */
213 
214 	case ENOMEM:
215 		Perror(f);
216 
217 	case ENOENT:
218 		break;
219 
220 	default:
221 		if (exerr == 0) {
222 			exerr = sys_errlist[errno];
223 			expath = savestr(f);
224 		}
225 	}
226 }
227 
228 execash(t, kp)
229 	register struct command *kp;
230 {
231 
232 	didcch++;
233 	rechist();
234 	signal(SIGINT, parintr);
235 	signal(SIGQUIT, parintr);
236 	signal(SIGTERM, parterm);		/* if doexec loses, screw */
237 	lshift(kp->t_dcom, 1);
238 	exiterr++;
239 	doexec(kp);
240 	/*NOTREACHED*/
241 }
242 
243 xechoit(t)
244 	char **t;
245 {
246 
247 	if (adrof("echo")) {
248 		flush();
249 		haderr = 1;
250 		blkpr(t), printf("\n");
251 		haderr = 0;
252 	}
253 }
254 
255 dohash()
256 {
257 	struct stat stb;
258 	DIR *dirp;
259 	register struct direct *dp;
260 	register int cnt;
261 	int i = 0;
262 	struct varent *v = adrof("path");
263 	char **pv;
264 
265 	havhash = 1;
266 	for (cnt = 0; cnt < HSHSIZ; cnt++)
267 		xhash[cnt] = 0;
268 	if (v == 0)
269 		return;
270 	for (pv = v->vec; *pv; pv++, i = (i + 1) % 8) {
271 		if (pv[0][0] != '/')
272 			continue;
273 		dirp = opendir(*pv);
274 		if (dirp == NULL)
275 			continue;
276 		if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) {
277 			closedir(dirp);
278 			continue;
279 		}
280 		while ((dp = readdir(dirp)) != NULL) {
281 			if (dp->d_ino == 0)
282 				continue;
283 			xhash[hash(dp->d_name)] |= (1 << i);
284 		}
285 		closedir(dirp);
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