Commit | Line | Data |
---|---|---|
90cd41ad | 1 | /*- |
ed72f0a0 KB |
2 | * Copyright (c) 1980, 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
90cd41ad | 4 | * |
ad787160 C |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
b79f4fa9 DF |
32 | */ |
33 | ||
35371dec | 34 | #ifndef lint |
fd88f5c5 | 35 | static char sccsid[] = "@(#)exec.c 8.3 (Berkeley) 5/23/95"; |
90cd41ad | 36 | #endif /* not lint */ |
c5fd306c | 37 | |
b9c4f741 | 38 | #include <sys/types.h> |
17b1f379 | 39 | #include <sys/param.h> |
b9c4f741 KB |
40 | #include <dirent.h> |
41 | #include <fcntl.h> | |
17b1f379 | 42 | #include <sys/stat.h> |
b9c4f741 KB |
43 | #include <errno.h> |
44 | #include <stdlib.h> | |
45 | #include <string.h> | |
46 | #include <unistd.h> | |
4df6491c CZ |
47 | #if __STDC__ |
48 | # include <stdarg.h> | |
49 | #else | |
50 | # include <varargs.h> | |
51 | #endif | |
52 | ||
4d7b2685 KB |
53 | #include "csh.h" |
54 | #include "extern.h" | |
c5fd306c BJ |
55 | |
56 | /* | |
b9c4f741 KB |
57 | * System level search and execute of a command. We look in each directory |
58 | * for the specified command name. If the name contains a '/' then we | |
59 | * execute only the full path name. If there is no search path then we | |
60 | * execute only full path names. | |
c5fd306c | 61 | */ |
4a21d672 | 62 | extern char **environ; |
c5fd306c | 63 | |
6e37afca | 64 | /* |
c5fd306c BJ |
65 | * As we search for the command we note the first non-trivial error |
66 | * message for presentation to the user. This allows us often | |
67 | * to show that a file has the wrong mode/no access when the file | |
68 | * is not in the last component of the search path, so we must | |
69 | * go on after first detecting the error. | |
70 | */ | |
6e37afca KB |
71 | static char *exerr; /* Execution error message */ |
72 | static Char *expath; /* Path for exerr */ | |
c5fd306c BJ |
73 | |
74 | /* | |
35371dec EW |
75 | * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used |
76 | * to hash execs. If it is allocated (havhash true), then to tell | |
77 | * whether ``name'' is (possibly) present in the i'th component | |
78 | * of the variable path, you look at the bit in xhash indexed by | |
79 | * hash(hashname("name"), i). This is setup automatically | |
c5fd306c BJ |
80 | * after .login is executed, and recomputed whenever ``path'' is |
81 | * changed. | |
35371dec EW |
82 | * The two part hash function is designed to let texec() call the |
83 | * more expensive hashname() only once and the simple hash() several | |
84 | * times (once for each path component checked). | |
85 | * Byte size is assumed to be 8. | |
c5fd306c | 86 | */ |
6e37afca | 87 | #define HSHSIZ 8192 /* 1k bytes */ |
35371dec EW |
88 | #define HSHMASK (HSHSIZ - 1) |
89 | #define HSHMUL 243 | |
6e37afca KB |
90 | static char xhash[HSHSIZ / 8]; |
91 | ||
37999c01 | 92 | #define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK) |
35371dec EW |
93 | #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ |
94 | #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ | |
6e37afca KB |
95 | static int hits, misses; |
96 | ||
c5fd306c | 97 | /* Dummy search path for just absolute search when no path */ |
6e37afca KB |
98 | static Char *justabs[] = {STRNULL, 0}; |
99 | ||
0aec749d CZ |
100 | static void pexerr __P((void)); |
101 | static void texec __P((Char *, Char **)); | |
102 | static int hashname __P((Char *)); | |
17b1f379 CZ |
103 | static void tellmewhat __P((struct wordent *)); |
104 | static int executable __P((Char *, Char *, bool)); | |
105 | static int iscommand __P((Char *)); | |
106 | ||
c5fd306c | 107 | |
6e37afca | 108 | void |
454c2aa3 CZ |
109 | /*ARGSUSED*/ |
110 | doexec(v, t) | |
111 | Char **v; | |
112 | struct command *t; | |
c5fd306c | 113 | { |
6e37afca | 114 | register Char *dp, **pv, **av, *sav; |
454c2aa3 | 115 | register struct varent *pathv; |
6e37afca KB |
116 | register bool slash; |
117 | register int hashval = 0, hashval1, i; | |
118 | Char *blk[2]; | |
fca4b346 | 119 | sigset_t sigset; |
6e37afca KB |
120 | |
121 | /* | |
122 | * Glob the command name. We will search $path even if this does something, | |
123 | * as in sh but not in csh. One special case: if there is no PATH, then we | |
124 | * execute only commands which start with '/'. | |
125 | */ | |
126 | blk[0] = t->t_dcom[0]; | |
127 | blk[1] = 0; | |
128 | gflag = 0, tglob(blk); | |
129 | if (gflag) { | |
130 | pv = globall(blk); | |
131 | if (pv == 0) { | |
abf583a4 | 132 | setname(vis_str(blk[0])); |
6e37afca KB |
133 | stderror(ERR_NAME | ERR_NOMATCH); |
134 | } | |
135 | gargv = 0; | |
136 | } | |
137 | else | |
138 | pv = saveblk(blk); | |
c5fd306c | 139 | |
6e37afca | 140 | trim(pv); |
c5fd306c | 141 | |
6e37afca KB |
142 | exerr = 0; |
143 | expath = Strsave(pv[0]); | |
6e37afca | 144 | Vexpath = expath; |
c5fd306c | 145 | |
454c2aa3 CZ |
146 | pathv = adrof(STRpath); |
147 | if (pathv == 0 && expath[0] != '/') { | |
6e37afca KB |
148 | blkfree(pv); |
149 | pexerr(); | |
150 | } | |
151 | slash = any(short2str(expath), '/'); | |
152 | ||
153 | /* | |
154 | * Glob the argument list, if necessary. Otherwise trim off the quote bits. | |
155 | */ | |
156 | gflag = 0; | |
157 | av = &t->t_dcom[1]; | |
158 | tglob(av); | |
159 | if (gflag) { | |
160 | av = globall(av); | |
161 | if (av == 0) { | |
162 | blkfree(pv); | |
abf583a4 | 163 | setname(vis_str(expath)); |
6e37afca KB |
164 | stderror(ERR_NAME | ERR_NOMATCH); |
165 | } | |
166 | gargv = 0; | |
167 | } | |
168 | else | |
169 | av = saveblk(av); | |
170 | ||
171 | blkfree(t->t_dcom); | |
172 | t->t_dcom = blkspl(pv, av); | |
173 | xfree((ptr_t) pv); | |
174 | xfree((ptr_t) av); | |
175 | av = t->t_dcom; | |
176 | trim(av); | |
177 | ||
0aec749d | 178 | if (*av == NULL || **av == '\0') |
6e37afca | 179 | pexerr(); |
c5fd306c | 180 | |
6e37afca KB |
181 | xechoit(av); /* Echo command if -x */ |
182 | /* | |
183 | * Since all internal file descriptors are set to close on exec, we don't | |
184 | * need to close them explicitly here. Just reorient ourselves for error | |
185 | * messages. | |
186 | */ | |
187 | SHIN = 0; | |
188 | SHOUT = 1; | |
454c2aa3 | 189 | SHERR = 2; |
6e37afca KB |
190 | OLDSTD = 0; |
191 | /* | |
192 | * We must do this AFTER any possible forking (like `foo` in glob) so that | |
193 | * this shell can still do subprocesses. | |
194 | */ | |
fca4b346 CZ |
195 | sigemptyset(&sigset); |
196 | sigprocmask(SIG_SETMASK, &sigset, NULL); | |
6e37afca KB |
197 | /* |
198 | * If no path, no words in path, or a / in the filename then restrict the | |
199 | * command search. | |
200 | */ | |
454c2aa3 | 201 | if (pathv == 0 || pathv->vec[0] == 0 || slash) |
6e37afca KB |
202 | pv = justabs; |
203 | else | |
454c2aa3 | 204 | pv = pathv->vec; |
6e37afca | 205 | sav = Strspl(STRslash, *av);/* / command name for postpending */ |
6e37afca | 206 | Vsav = sav; |
6e37afca KB |
207 | if (havhash) |
208 | hashval = hashname(*av); | |
209 | i = 0; | |
6e37afca | 210 | hits++; |
6e37afca KB |
211 | do { |
212 | /* | |
213 | * Try to save time by looking at the hash table for where this command | |
214 | * could be. If we are doing delayed hashing, then we put the names in | |
215 | * one at a time, as the user enters them. This is kinda like Korn | |
216 | * Shell's "tracked aliases". | |
217 | */ | |
218 | if (!slash && pv[0][0] == '/' && havhash) { | |
219 | hashval1 = hash(hashval, i); | |
220 | if (!bit(xhash, hashval1)) | |
221 | goto cont; | |
222 | } | |
223 | if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ | |
224 | texec(*av, av); | |
225 | else { | |
226 | dp = Strspl(*pv, sav); | |
6e37afca | 227 | Vdp = dp; |
6e37afca | 228 | texec(dp, av); |
6e37afca | 229 | Vdp = 0; |
6e37afca KB |
230 | xfree((ptr_t) dp); |
231 | } | |
6e37afca | 232 | misses++; |
c5fd306c | 233 | cont: |
6e37afca KB |
234 | pv++; |
235 | i++; | |
236 | } while (*pv); | |
6e37afca | 237 | hits--; |
6e37afca | 238 | Vsav = 0; |
6e37afca KB |
239 | xfree((ptr_t) sav); |
240 | pexerr(); | |
c5fd306c BJ |
241 | } |
242 | ||
6e37afca | 243 | static void |
c5fd306c BJ |
244 | pexerr() |
245 | { | |
6e37afca KB |
246 | /* Couldn't find the damn thing */ |
247 | if (expath) { | |
abf583a4 | 248 | setname(vis_str(expath)); |
6e37afca | 249 | Vexpath = 0; |
6e37afca KB |
250 | xfree((ptr_t) expath); |
251 | expath = 0; | |
252 | } | |
253 | else | |
254 | setname(""); | |
255 | if (exerr) | |
256 | stderror(ERR_NAME | ERR_STRING, exerr); | |
257 | stderror(ERR_NAME | ERR_COMMAND); | |
c5fd306c BJ |
258 | } |
259 | ||
c5fd306c BJ |
260 | /* |
261 | * Execute command f, arg list t. | |
262 | * Record error message if not found. | |
263 | * Also do shell scripts here. | |
264 | */ | |
6e37afca KB |
265 | static void |
266 | texec(sf, st) | |
267 | Char *sf; | |
268 | register Char **st; | |
c5fd306c | 269 | { |
6e37afca KB |
270 | register char **t; |
271 | register char *f; | |
272 | register struct varent *v; | |
273 | register Char **vp; | |
274 | Char *lastsh[2]; | |
275 | int fd; | |
276 | unsigned char c; | |
277 | Char *st0, **ost; | |
278 | ||
279 | /* The order for the conversions is significant */ | |
280 | t = short2blk(st); | |
281 | f = short2str(sf); | |
6e37afca | 282 | Vt = t; |
6e37afca | 283 | errno = 0; /* don't use a previous error */ |
4a21d672 | 284 | (void) execve(f, t, environ); |
6e37afca | 285 | Vt = 0; |
6e37afca KB |
286 | blkfree((Char **) t); |
287 | switch (errno) { | |
288 | ||
289 | case ENOEXEC: | |
290 | /* | |
291 | * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute | |
292 | * it, don't feed it to the shell if it looks like a binary! | |
293 | */ | |
294 | if ((fd = open(f, O_RDONLY)) != -1) { | |
295 | if (read(fd, (char *) &c, 1) == 1) { | |
296 | if (!Isprint(c) && (c != '\n' && c != '\t')) { | |
297 | (void) close(fd); | |
298 | /* | |
299 | * We *know* what ENOEXEC means. | |
300 | */ | |
301 | stderror(ERR_ARCH, f, strerror(errno)); | |
c5fd306c | 302 | } |
6e37afca KB |
303 | } |
304 | #ifdef _PATH_BSHELL | |
305 | else | |
306 | c = '#'; | |
307 | #endif | |
308 | (void) close(fd); | |
309 | } | |
310 | /* | |
311 | * If there is an alias for shell, then put the words of the alias in | |
312 | * front of the argument list replacing the command name. Note no | |
313 | * interpretation of the words at this point. | |
314 | */ | |
315 | v = adrof1(STRshell, &aliases); | |
316 | if (v == 0) { | |
317 | vp = lastsh; | |
318 | vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; | |
319 | vp[1] = NULL; | |
320 | #ifdef _PATH_BSHELL | |
321 | if (fd != -1 && c != '#') | |
322 | vp[0] = STR_BSHELL; | |
323 | #endif | |
324 | } | |
325 | else | |
326 | vp = v->vec; | |
327 | st0 = st[0]; | |
328 | st[0] = sf; | |
329 | ost = st; | |
330 | st = blkspl(vp, st); /* Splice up the new arglst */ | |
331 | ost[0] = st0; | |
332 | sf = *st; | |
333 | /* The order for the conversions is significant */ | |
334 | t = short2blk(st); | |
335 | f = short2str(sf); | |
336 | xfree((ptr_t) st); | |
6e37afca | 337 | Vt = t; |
4a21d672 | 338 | (void) execve(f, t, environ); |
6e37afca | 339 | Vt = 0; |
6e37afca KB |
340 | blkfree((Char **) t); |
341 | /* The sky is falling, the sky is falling! */ | |
342 | ||
343 | case ENOMEM: | |
344 | stderror(ERR_SYSTEM, f, strerror(errno)); | |
345 | ||
346 | case ENOENT: | |
347 | break; | |
348 | ||
349 | default: | |
350 | if (exerr == 0) { | |
351 | exerr = strerror(errno); | |
352 | if (expath) | |
353 | xfree((ptr_t) expath); | |
354 | expath = Strsave(sf); | |
6e37afca | 355 | Vexpath = expath; |
c5fd306c | 356 | } |
6e37afca | 357 | } |
c5fd306c BJ |
358 | } |
359 | ||
35371dec | 360 | /*ARGSUSED*/ |
6e37afca | 361 | void |
a657dadc CZ |
362 | execash(t, kp) |
363 | Char **t; | |
364 | register struct command *kp; | |
c5fd306c | 365 | { |
a657dadc CZ |
366 | int saveIN, saveOUT, saveDIAG, saveSTD; |
367 | int oSHIN; | |
368 | int oSHOUT; | |
369 | int oSHERR; | |
370 | int oOLDSTD; | |
371 | jmp_buf osetexit; | |
372 | int my_reenter; | |
373 | int odidfds; | |
374 | sig_t osigint, osigquit, osigterm; | |
375 | ||
6e37afca KB |
376 | if (chkstop == 0 && setintr) |
377 | panystop(0); | |
a657dadc CZ |
378 | /* |
379 | * Hmm, we don't really want to do that now because we might | |
380 | * fail, but what is the choice | |
381 | */ | |
6e37afca | 382 | rechist(); |
a657dadc CZ |
383 | |
384 | osigint = signal(SIGINT, parintr); | |
385 | osigquit = signal(SIGQUIT, parintr); | |
386 | osigterm = signal(SIGTERM, parterm); | |
387 | ||
388 | odidfds = didfds; | |
389 | oSHIN = SHIN; | |
390 | oSHOUT = SHOUT; | |
391 | oSHERR = SHERR; | |
392 | oOLDSTD = OLDSTD; | |
393 | ||
394 | saveIN = dcopy(SHIN, -1); | |
395 | saveOUT = dcopy(SHOUT, -1); | |
396 | saveDIAG = dcopy(SHERR, -1); | |
397 | saveSTD = dcopy(OLDSTD, -1); | |
398 | ||
399 | lshift(kp->t_dcom, 1); | |
400 | ||
401 | getexit(osetexit); | |
402 | ||
403 | if ((my_reenter = setexit()) == 0) { | |
404 | SHIN = dcopy(0, -1); | |
405 | SHOUT = dcopy(1, -1); | |
406 | SHERR = dcopy(2, -1); | |
407 | didfds = 0; | |
408 | doexec(t, kp); | |
409 | } | |
410 | ||
411 | (void) signal(SIGINT, osigint); | |
412 | (void) signal(SIGQUIT, osigquit); | |
413 | (void) signal(SIGTERM, osigterm); | |
414 | ||
415 | doneinp = 0; | |
416 | didfds = odidfds; | |
417 | (void) close(SHIN); | |
418 | (void) close(SHOUT); | |
419 | (void) close(SHERR); | |
420 | (void) close(OLDSTD); | |
421 | SHIN = dmove(saveIN, oSHIN); | |
422 | SHOUT = dmove(saveOUT, oSHOUT); | |
423 | SHERR = dmove(saveDIAG, oSHERR); | |
424 | OLDSTD = dmove(saveSTD, oOLDSTD); | |
425 | ||
426 | resexit(osetexit); | |
427 | if (my_reenter) | |
428 | stderror(ERR_SILENT); | |
c5fd306c BJ |
429 | } |
430 | ||
6e37afca | 431 | void |
c5fd306c | 432 | xechoit(t) |
6e37afca | 433 | Char **t; |
c5fd306c | 434 | { |
6e37afca | 435 | if (adrof(STRecho)) { |
454c2aa3 CZ |
436 | (void) fflush(csherr); |
437 | blkpr(csherr, t); | |
438 | (void) fputc('\n', csherr); | |
6e37afca | 439 | } |
c5fd306c BJ |
440 | } |
441 | ||
6e37afca | 442 | void |
454c2aa3 CZ |
443 | /*ARGSUSED*/ |
444 | dohash(v, t) | |
445 | Char **v; | |
446 | struct command *t; | |
c5fd306c | 447 | { |
6e37afca KB |
448 | DIR *dirp; |
449 | register struct dirent *dp; | |
450 | register int cnt; | |
451 | int i = 0; | |
454c2aa3 | 452 | struct varent *pathv = adrof(STRpath); |
6e37afca KB |
453 | Char **pv; |
454 | int hashval; | |
455 | ||
456 | havhash = 1; | |
457 | for (cnt = 0; cnt < sizeof xhash; cnt++) | |
458 | xhash[cnt] = 0; | |
454c2aa3 | 459 | if (pathv == 0) |
6e37afca | 460 | return; |
454c2aa3 | 461 | for (pv = pathv->vec; *pv; pv++, i++) { |
6e37afca KB |
462 | if (pv[0][0] != '/') |
463 | continue; | |
464 | dirp = opendir(short2str(*pv)); | |
465 | if (dirp == NULL) | |
466 | continue; | |
467 | while ((dp = readdir(dirp)) != NULL) { | |
468 | if (dp->d_ino == 0) | |
469 | continue; | |
470 | if (dp->d_name[0] == '.' && | |
471 | (dp->d_name[1] == '\0' || | |
37999c01 | 472 | (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
6e37afca KB |
473 | continue; |
474 | hashval = hash(hashname(str2short(dp->d_name)), i); | |
475 | bis(xhash, hashval); | |
476 | /* tw_add_comm_name (dp->d_name); */ | |
c5fd306c | 477 | } |
6e37afca KB |
478 | (void) closedir(dirp); |
479 | } | |
c5fd306c BJ |
480 | } |
481 | ||
6e37afca | 482 | void |
454c2aa3 CZ |
483 | /*ARGSUSED*/ |
484 | dounhash(v, t) | |
485 | Char **v; | |
486 | struct command *t; | |
c5fd306c | 487 | { |
6e37afca | 488 | havhash = 0; |
c5fd306c BJ |
489 | } |
490 | ||
6e37afca | 491 | void |
454c2aa3 CZ |
492 | /*ARGSUSED*/ |
493 | hashstat(v, t) | |
494 | Char **v; | |
495 | struct command *t; | |
c5fd306c | 496 | { |
6e37afca | 497 | if (hits + misses) |
454c2aa3 CZ |
498 | (void) fprintf(cshout, "%d hits, %d misses, %d%%\n", |
499 | hits, misses, 100 * hits / (hits + misses)); | |
c5fd306c | 500 | } |
6e37afca | 501 | |
35371dec EW |
502 | /* |
503 | * Hash a command name. | |
504 | */ | |
6e37afca | 505 | static int |
35371dec | 506 | hashname(cp) |
6e37afca | 507 | register Char *cp; |
c5fd306c | 508 | { |
6e37afca | 509 | register long h = 0; |
c5fd306c | 510 | |
6e37afca KB |
511 | while (*cp) |
512 | h = hash(h, *cp++); | |
513 | return ((int) h); | |
c5fd306c | 514 | } |
17b1f379 CZ |
515 | |
516 | static int | |
517 | iscommand(name) | |
518 | Char *name; | |
519 | { | |
520 | register Char **pv; | |
521 | register Char *sav; | |
522 | register struct varent *v; | |
523 | register bool slash = any(short2str(name), '/'); | |
524 | register int hashval = 0, hashval1, i; | |
525 | ||
526 | v = adrof(STRpath); | |
527 | if (v == 0 || v->vec[0] == 0 || slash) | |
528 | pv = justabs; | |
529 | else | |
530 | pv = v->vec; | |
531 | sav = Strspl(STRslash, name); /* / command name for postpending */ | |
532 | if (havhash) | |
533 | hashval = hashname(name); | |
534 | i = 0; | |
535 | do { | |
536 | if (!slash && pv[0][0] == '/' && havhash) { | |
537 | hashval1 = hash(hashval, i); | |
538 | if (!bit(xhash, hashval1)) | |
539 | goto cont; | |
540 | } | |
541 | if (pv[0][0] == 0 || eq(pv[0], STRdot)) { /* don't make ./xxx */ | |
542 | if (executable(NULL, name, 0)) { | |
543 | xfree((ptr_t) sav); | |
544 | return i + 1; | |
545 | } | |
546 | } | |
547 | else { | |
548 | if (executable(*pv, sav, 0)) { | |
549 | xfree((ptr_t) sav); | |
550 | return i + 1; | |
551 | } | |
552 | } | |
553 | cont: | |
554 | pv++; | |
555 | i++; | |
556 | } while (*pv); | |
557 | xfree((ptr_t) sav); | |
558 | return 0; | |
559 | } | |
560 | ||
561 | /* Also by: | |
562 | * Andreas Luik <luik@isaak.isa.de> | |
563 | * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung | |
564 | * Azenberstr. 35 | |
565 | * D-7000 Stuttgart 1 | |
566 | * West-Germany | |
567 | * is the executable() routine below and changes to iscommand(). | |
568 | * Thanks again!! | |
569 | */ | |
570 | ||
571 | /* | |
572 | * executable() examines the pathname obtained by concatenating dir and name | |
573 | * (dir may be NULL), and returns 1 either if it is executable by us, or | |
574 | * if dir_ok is set and the pathname refers to a directory. | |
575 | * This is a bit kludgy, but in the name of optimization... | |
576 | */ | |
577 | static int | |
578 | executable(dir, name, dir_ok) | |
579 | Char *dir, *name; | |
580 | bool dir_ok; | |
581 | { | |
582 | struct stat stbuf; | |
583 | Char path[MAXPATHLEN + 1], *dp, *sp; | |
584 | char *strname; | |
585 | ||
586 | if (dir && *dir) { | |
587 | for (dp = path, sp = dir; *sp; *dp++ = *sp++) | |
588 | if (dp == &path[MAXPATHLEN + 1]) { | |
589 | *--dp = '\0'; | |
590 | break; | |
591 | } | |
592 | for (sp = name; *sp; *dp++ = *sp++) | |
593 | if (dp == &path[MAXPATHLEN + 1]) { | |
594 | *--dp = '\0'; | |
595 | break; | |
596 | } | |
597 | *dp = '\0'; | |
598 | strname = short2str(path); | |
599 | } | |
600 | else | |
601 | strname = short2str(name); | |
602 | return (stat(strname, &stbuf) != -1 && | |
603 | ((S_ISREG(stbuf.st_mode) && | |
604 | /* save time by not calling access() in the hopeless case */ | |
605 | (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && | |
606 | access(strname, X_OK) == 0) || | |
607 | (dir_ok && S_ISDIR(stbuf.st_mode)))); | |
608 | } | |
609 | ||
610 | /* The dowhich() is by: | |
611 | * Andreas Luik <luik@isaak.isa.de> | |
612 | * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung | |
613 | * Azenberstr. 35 | |
614 | * D-7000 Stuttgart 1 | |
615 | * West-Germany | |
616 | * Thanks!! | |
617 | */ | |
618 | /*ARGSUSED*/ | |
619 | void | |
620 | dowhich(v, c) | |
621 | register Char **v; | |
622 | struct command *c; | |
623 | { | |
624 | struct wordent lex[3]; | |
625 | struct varent *vp; | |
626 | ||
627 | lex[0].next = &lex[1]; | |
628 | lex[1].next = &lex[2]; | |
629 | lex[2].next = &lex[0]; | |
630 | ||
631 | lex[0].prev = &lex[2]; | |
632 | lex[1].prev = &lex[0]; | |
633 | lex[2].prev = &lex[1]; | |
634 | ||
635 | lex[0].word = STRNULL; | |
636 | lex[2].word = STRret; | |
637 | ||
638 | while (*++v) { | |
37999c01 | 639 | if ((vp = adrof1(*v, &aliases)) != NULL) { |
abf583a4 | 640 | (void) fprintf(cshout, "%s: \t aliased to ", vis_str(*v)); |
17b1f379 CZ |
641 | blkpr(cshout, vp->vec); |
642 | (void) fputc('\n', cshout); | |
643 | } | |
644 | else { | |
645 | lex[1].word = *v; | |
646 | tellmewhat(lex); | |
647 | } | |
648 | } | |
649 | } | |
650 | ||
651 | static void | |
652 | tellmewhat(lex) | |
653 | struct wordent *lex; | |
654 | { | |
655 | register int i; | |
656 | register struct biltins *bptr; | |
657 | register struct wordent *sp = lex->next; | |
658 | bool aliased = 0; | |
6f94c2f8 | 659 | Char *s0, *s1, *s2, *cmd; |
17b1f379 CZ |
660 | Char qc; |
661 | ||
662 | if (adrof1(sp->word, &aliases)) { | |
663 | alias(lex); | |
664 | sp = lex->next; | |
665 | aliased = 1; | |
666 | } | |
667 | ||
668 | s0 = sp->word; /* to get the memory freeing right... */ | |
669 | ||
670 | /* handle quoted alias hack */ | |
671 | if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) | |
672 | (sp->word)++; | |
673 | ||
674 | /* do quoting, if it hasn't been done */ | |
675 | s1 = s2 = sp->word; | |
676 | while (*s2) | |
677 | switch (*s2) { | |
678 | case '\'': | |
679 | case '"': | |
680 | qc = *s2++; | |
681 | while (*s2 && *s2 != qc) | |
682 | *s1++ = *s2++ | QUOTE; | |
683 | if (*s2) | |
684 | s2++; | |
685 | break; | |
686 | case '\\': | |
687 | if (*++s2) | |
688 | *s1++ = *s2++ | QUOTE; | |
689 | break; | |
690 | default: | |
691 | *s1++ = *s2++; | |
692 | } | |
693 | *s1 = '\0'; | |
694 | ||
695 | for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { | |
696 | if (eq(sp->word, str2short(bptr->bname))) { | |
697 | if (aliased) | |
698 | prlex(cshout, lex); | |
699 | (void) fprintf(cshout, "%s: shell built-in command.\n", | |
abf583a4 | 700 | vis_str(sp->word)); |
17b1f379 CZ |
701 | sp->word = s0; /* we save and then restore this */ |
702 | return; | |
703 | } | |
704 | } | |
705 | ||
6f94c2f8 CZ |
706 | sp->word = cmd = globone(sp->word, G_IGNORE); |
707 | ||
37999c01 | 708 | if ((i = iscommand(strip(sp->word))) != 0) { |
17b1f379 CZ |
709 | register Char **pv; |
710 | register struct varent *v; | |
711 | bool slash = any(short2str(sp->word), '/'); | |
712 | ||
713 | v = adrof(STRpath); | |
714 | if (v == 0 || v->vec[0] == 0 || slash) | |
715 | pv = justabs; | |
716 | else | |
717 | pv = v->vec; | |
718 | ||
719 | while (--i) | |
720 | pv++; | |
721 | if (pv[0][0] == 0 || eq(pv[0], STRdot)) { | |
6f94c2f8 CZ |
722 | if (!slash) { |
723 | sp->word = Strspl(STRdotsl, sp->word); | |
724 | prlex(cshout, lex); | |
725 | xfree((ptr_t) sp->word); | |
726 | } | |
727 | else | |
728 | prlex(cshout, lex); | |
17b1f379 | 729 | sp->word = s0; /* we save and then restore this */ |
6f94c2f8 | 730 | xfree((ptr_t) cmd); |
17b1f379 CZ |
731 | return; |
732 | } | |
733 | s1 = Strspl(*pv, STRslash); | |
734 | sp->word = Strspl(s1, sp->word); | |
735 | xfree((ptr_t) s1); | |
736 | prlex(cshout, lex); | |
737 | xfree((ptr_t) sp->word); | |
738 | } | |
739 | else { | |
740 | if (aliased) | |
741 | prlex(cshout, lex); | |
abf583a4 | 742 | (void) fprintf(csherr, "%s: Command not found.\n", vis_str(sp->word)); |
17b1f379 CZ |
743 | } |
744 | sp->word = s0; /* we save and then restore this */ | |
6f94c2f8 | 745 | xfree((ptr_t) cmd); |
17b1f379 | 746 | } |