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