GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: exec.c,v 1.64 2015/12/30 09:07:00 tedu Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* execute command tree |
||
5 |
*/ |
||
6 |
|||
7 |
#include <sys/stat.h> |
||
8 |
|||
9 |
#include <ctype.h> |
||
10 |
#include <errno.h> |
||
11 |
#include <fcntl.h> |
||
12 |
#include <paths.h> |
||
13 |
#include <stdio.h> |
||
14 |
#include <stdlib.h> |
||
15 |
#include <string.h> |
||
16 |
#include <unistd.h> |
||
17 |
|||
18 |
#include "sh.h" |
||
19 |
#include "c_test.h" |
||
20 |
|||
21 |
/* Does ps4 get parameter substitutions done? */ |
||
22 |
# define PS4_SUBSTITUTE(s) substitute((s), 0) |
||
23 |
|||
24 |
static int comexec(struct op *, struct tbl *volatile, char **, |
||
25 |
int volatile, volatile int *); |
||
26 |
static void scriptexec(struct op *, char **); |
||
27 |
static int call_builtin(struct tbl *, char **); |
||
28 |
static int iosetup(struct ioword *, struct tbl *); |
||
29 |
static int herein(const char *, int); |
||
30 |
static char *do_selectargs(char **, bool); |
||
31 |
static int dbteste_isa(Test_env *, Test_meta); |
||
32 |
static const char *dbteste_getopnd(Test_env *, Test_op, int); |
||
33 |
static int dbteste_eval(Test_env *, Test_op, const char *, const char *, |
||
34 |
int); |
||
35 |
static void dbteste_error(Test_env *, int, const char *); |
||
36 |
|||
37 |
|||
38 |
/* |
||
39 |
* execute command tree |
||
40 |
*/ |
||
41 |
int |
||
42 |
execute(struct op *volatile t, |
||
43 |
volatile int flags, volatile int *xerrok) /* if XEXEC don't fork */ |
||
44 |
254684 |
{ |
|
45 |
254684 |
int i, dummy = 0; |
|
46 |
254684 |
volatile int rv = 0; |
|
47 |
int pv[2]; |
||
48 |
char ** volatile ap; |
||
49 |
char *s, *cp; |
||
50 |
struct ioword **iowp; |
||
51 |
254684 |
struct tbl *tp = NULL; |
|
52 |
|||
53 |
✓✓ | 254684 |
if (t == NULL) |
54 |
5503 |
return 0; |
|
55 |
|||
56 |
/* Caller doesn't care if XERROK should propagate. */ |
||
57 |
✓✓ | 249181 |
if (xerrok == NULL) |
58 |
160520 |
xerrok = &dummy; |
|
59 |
|||
60 |
/* Is this the end of a pipeline? If so, we want to evaluate the |
||
61 |
* command arguments |
||
62 |
bool eval_done = false; |
||
63 |
if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { |
||
64 |
eval_done = true; |
||
65 |
tp = eval_execute_args(t, &ap); |
||
66 |
} |
||
67 |
*/ |
||
68 |
✓✓✓✓ ✓✓ |
249181 |
if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) |
69 |
2650 |
return exchild(t, flags & ~XTIME, xerrok, -1); /* run in sub-process */ |
|
70 |
|||
71 |
246531 |
newenv(E_EXEC); |
|
72 |
✓✓ | 246531 |
if (trap) |
73 |
7002 |
runtraps(0); |
|
74 |
|||
75 |
✓✓ | 246531 |
if (t->type == TCOM) { |
76 |
/* Clear subst_exstat before argument expansion. Used by |
||
77 |
* null commands (see comexec() and c_eval()) and by c_set(). |
||
78 |
*/ |
||
79 |
171489 |
subst_exstat = 0; |
|
80 |
|||
81 |
171489 |
current_lineno = t->lineno; /* for $LINENO */ |
|
82 |
|||
83 |
/* POSIX says expand command words first, then redirections, |
||
84 |
* and assignments last.. |
||
85 |
*/ |
||
86 |
171489 |
ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); |
|
87 |
✓✓ | 171481 |
if (flags & XTIME) |
88 |
/* Allow option parsing (bizarre, but POSIX) */ |
||
89 |
2 |
timex_hook(t, &ap); |
|
90 |
✗✓✗✗ |
171481 |
if (Flag(FXTRACE) && ap[0]) { |
91 |
shf_fprintf(shl_out, "%s", |
||
92 |
PS4_SUBSTITUTE(str_val(global("PS4")))); |
||
93 |
for (i = 0; ap[i]; i++) |
||
94 |
shf_fprintf(shl_out, "%s%s", ap[i], |
||
95 |
ap[i + 1] ? " " : "\n"); |
||
96 |
shf_flush(shl_out); |
||
97 |
} |
||
98 |
✓✓ | 171481 |
if (ap[0]) |
99 |
151770 |
tp = findcom(ap[0], FC_BI|FC_FUNC); |
|
100 |
} |
||
101 |
246523 |
flags &= ~XTIME; |
|
102 |
|||
103 |
✓✓✓✓ ✗✓ |
246523 |
if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { |
104 |
8460 |
genv->savefd = areallocarray(NULL, NUFILE, sizeof(short), ATEMP); |
|
105 |
/* initialize to not redirected */ |
||
106 |
8464 |
memset(genv->savefd, 0, NUFILE * sizeof(short)); |
|
107 |
} |
||
108 |
|||
109 |
/* do redirection, to be restored in quitenv() */ |
||
110 |
✓✓ | 246527 |
if (t->ioact != NULL) |
111 |
✓✓ | 14136 |
for (iowp = t->ioact; *iowp != NULL; iowp++) { |
112 |
✗✓ | 7859 |
if (iosetup(*iowp, tp) < 0) { |
113 |
exstat = rv = 1; |
||
114 |
/* Redirection failures for special commands |
||
115 |
* cause (non-interactive) shell to exit. |
||
116 |
*/ |
||
117 |
if (tp && tp->type == CSHELL && |
||
118 |
(tp->flag & SPEC_BI)) |
||
119 |
errorf(NULL); |
||
120 |
/* Deal with FERREXIT, quitenv(), etc. */ |
||
121 |
goto Break; |
||
122 |
} |
||
123 |
} |
||
124 |
|||
125 |
✓✓✓✓ ✗✓✓✓ ✓✓✓✓ ✓✓✓✓ ✓✗ |
246527 |
switch (t->type) { |
126 |
case TCOM: |
||
127 |
171481 |
rv = comexec(t, tp, ap, flags, xerrok); |
|
128 |
164328 |
break; |
|
129 |
|||
130 |
case TPAREN: |
||
131 |
991 |
rv = execute(t->left, flags|XFORK, xerrok); |
|
132 |
849 |
break; |
|
133 |
|||
134 |
case TPIPE: |
||
135 |
2187 |
flags |= XFORK; |
|
136 |
2187 |
flags &= ~XEXEC; |
|
137 |
2187 |
genv->savefd[0] = savefd(0); |
|
138 |
2187 |
genv->savefd[1] = savefd(1); |
|
139 |
✓✓ | 6714 |
while (t->type == TPIPE) { |
140 |
2342 |
openpipe(pv); |
|
141 |
2342 |
(void) ksh_dup2(pv[1], 1, false); /* stdout of curr */ |
|
142 |
/* Let exchild() close pv[0] in child |
||
143 |
* (if this isn't done, commands like |
||
144 |
* (: ; cat /etc/termcap) | sleep 1 |
||
145 |
* will hang forever). |
||
146 |
*/ |
||
147 |
2342 |
exchild(t->left, flags|XPIPEO|XCCLOSE, NULL, pv[0]); |
|
148 |
2340 |
(void) ksh_dup2(pv[0], 0, false); /* stdin of next */ |
|
149 |
2340 |
closepipe(pv); |
|
150 |
2340 |
flags |= XPIPEI; |
|
151 |
2340 |
t = t->right; |
|
152 |
} |
||
153 |
2185 |
restfd(1, genv->savefd[1]); /* stdout of last */ |
|
154 |
2185 |
genv->savefd[1] = 0; /* no need to re-restore this */ |
|
155 |
/* Let exchild() close 0 in parent, after fork, before wait */ |
||
156 |
2185 |
i = exchild(t, flags|XPCLOSE, xerrok, 0); |
|
157 |
✓✓✓✓ |
2188 |
if (!(flags&XBGND) && !(flags&XXCOM)) |
158 |
1450 |
rv = i; |
|
159 |
break; |
||
160 |
|||
161 |
case TLIST: |
||
162 |
✓✓ | 151047 |
while (t->type == TLIST) { |
163 |
116003 |
execute(t->left, flags & XERROK, NULL); |
|
164 |
115846 |
t = t->right; |
|
165 |
} |
||
166 |
35044 |
rv = execute(t, flags & XERROK, xerrok); |
|
167 |
34878 |
break; |
|
168 |
|||
169 |
case TCOPROC: |
||
170 |
{ |
||
171 |
sigset_t omask; |
||
172 |
|||
173 |
/* Block sigchild as we are using things changed in the |
||
174 |
* signal handler |
||
175 |
*/ |
||
176 |
sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); |
||
177 |
genv->type = E_ERRH; |
||
178 |
i = sigsetjmp(genv->jbuf, 0); |
||
179 |
if (i) { |
||
180 |
sigprocmask(SIG_SETMASK, &omask, NULL); |
||
181 |
quitenv(NULL); |
||
182 |
unwind(i); |
||
183 |
/* NOTREACHED */ |
||
184 |
} |
||
185 |
/* Already have a (live) co-process? */ |
||
186 |
if (coproc.job && coproc.write >= 0) |
||
187 |
errorf("coprocess already exists"); |
||
188 |
|||
189 |
/* Can we re-use the existing co-process pipe? */ |
||
190 |
coproc_cleanup(true); |
||
191 |
|||
192 |
/* do this before opening pipes, in case these fail */ |
||
193 |
genv->savefd[0] = savefd(0); |
||
194 |
genv->savefd[1] = savefd(1); |
||
195 |
|||
196 |
openpipe(pv); |
||
197 |
if (pv[0] != 0) { |
||
198 |
ksh_dup2(pv[0], 0, false); |
||
199 |
close(pv[0]); |
||
200 |
} |
||
201 |
coproc.write = pv[1]; |
||
202 |
coproc.job = NULL; |
||
203 |
|||
204 |
if (coproc.readw >= 0) |
||
205 |
ksh_dup2(coproc.readw, 1, false); |
||
206 |
else { |
||
207 |
openpipe(pv); |
||
208 |
coproc.read = pv[0]; |
||
209 |
ksh_dup2(pv[1], 1, false); |
||
210 |
coproc.readw = pv[1]; /* closed before first read */ |
||
211 |
coproc.njobs = 0; |
||
212 |
/* create new coprocess id */ |
||
213 |
++coproc.id; |
||
214 |
} |
||
215 |
sigprocmask(SIG_SETMASK, &omask, NULL); |
||
216 |
genv->type = E_EXEC; /* no more need for error handler */ |
||
217 |
|||
218 |
/* exchild() closes coproc.* in child after fork, |
||
219 |
* will also increment coproc.njobs when the |
||
220 |
* job is actually created. |
||
221 |
*/ |
||
222 |
flags &= ~XEXEC; |
||
223 |
exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE, |
||
224 |
NULL, coproc.readw); |
||
225 |
break; |
||
226 |
} |
||
227 |
|||
228 |
case TASYNC: |
||
229 |
/* XXX non-optimal, I think - "(foo &)", forks for (), |
||
230 |
* forks again for async... parent should optimize |
||
231 |
* this to "foo &"... |
||
232 |
*/ |
||
233 |
121 |
rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok); |
|
234 |
121 |
break; |
|
235 |
|||
236 |
case TOR: |
||
237 |
case TAND: |
||
238 |
3175 |
rv = execute(t->left, XERROK, xerrok); |
|
239 |
✓✓ | 3175 |
if ((rv == 0) == (t->type == TAND)) |
240 |
1597 |
rv = execute(t->right, flags & XERROK, xerrok); |
|
241 |
else { |
||
242 |
1578 |
flags |= XERROK; |
|
243 |
✓✗ | 1578 |
if (xerrok) |
244 |
1578 |
*xerrok = 1; |
|
245 |
} |
||
246 |
break; |
||
247 |
|||
248 |
case TBANG: |
||
249 |
5 |
rv = !execute(t->right, XERROK, xerrok); |
|
250 |
5 |
flags |= XERROK; |
|
251 |
✓✗ | 5 |
if (xerrok) |
252 |
5 |
*xerrok = 1; |
|
253 |
break; |
||
254 |
|||
255 |
case TDBRACKET: |
||
256 |
{ |
||
257 |
Test_env te; |
||
258 |
|||
259 |
2 |
te.flags = TEF_DBRACKET; |
|
260 |
2 |
te.pos.wp = t->args; |
|
261 |
2 |
te.isa = dbteste_isa; |
|
262 |
2 |
te.getopnd = dbteste_getopnd; |
|
263 |
2 |
te.eval = dbteste_eval; |
|
264 |
2 |
te.error = dbteste_error; |
|
265 |
|||
266 |
2 |
rv = test_parse(&te); |
|
267 |
2 |
break; |
|
268 |
} |
||
269 |
|||
270 |
case TFOR: |
||
271 |
case TSELECT: |
||
272 |
{ |
||
273 |
1917 |
volatile bool is_first = true; |
|
274 |
✓✓ | 1917 |
ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) : |
275 |
genv->loc->argv + 1; |
||
276 |
1917 |
genv->type = E_LOOP; |
|
277 |
while (1) { |
||
278 |
2035 |
i = sigsetjmp(genv->jbuf, 0); |
|
279 |
✓✓ | 2247 |
if (!i) |
280 |
2035 |
break; |
|
281 |
✓✓✓✓ |
212 |
if ((genv->flags&EF_BRKCONT_PASS) || |
282 |
(i != LBREAK && i != LCONTIN)) { |
||
283 |
35 |
quitenv(NULL); |
|
284 |
35 |
unwind(i); |
|
285 |
✓✓ | 177 |
} else if (i == LBREAK) { |
286 |
59 |
rv = 0; |
|
287 |
59 |
goto Break; |
|
288 |
} |
||
289 |
} |
||
290 |
2035 |
rv = 0; /* in case of a continue */ |
|
291 |
✓✗ | 2035 |
if (t->type == TFOR) { |
292 |
✓✓ | 32516 |
while (*ap != NULL) { |
293 |
30693 |
setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); |
|
294 |
30693 |
rv = execute(t->left, flags & XERROK, xerrok); |
|
295 |
} |
||
296 |
} else { /* TSELECT */ |
||
297 |
for (;;) { |
||
298 |
if (!(cp = do_selectargs(ap, is_first))) { |
||
299 |
rv = 1; |
||
300 |
break; |
||
301 |
} |
||
302 |
is_first = false; |
||
303 |
setstr(global(t->str), cp, KSH_UNWIND_ERROR); |
||
304 |
rv = execute(t->left, flags & XERROK, xerrok); |
||
305 |
} |
||
306 |
} |
||
307 |
} |
||
308 |
break; |
||
309 |
|||
310 |
case TWHILE: |
||
311 |
case TUNTIL: |
||
312 |
56 |
genv->type = E_LOOP; |
|
313 |
while (1) { |
||
314 |
58 |
i = sigsetjmp(genv->jbuf, 0); |
|
315 |
✓✓ | 70 |
if (!i) |
316 |
58 |
break; |
|
317 |
✓✗✓✓ |
12 |
if ((genv->flags&EF_BRKCONT_PASS) || |
318 |
(i != LBREAK && i != LCONTIN)) { |
||
319 |
1 |
quitenv(NULL); |
|
320 |
1 |
unwind(i); |
|
321 |
✓✓ | 11 |
} else if (i == LBREAK) { |
322 |
9 |
rv = 0; |
|
323 |
9 |
goto Break; |
|
324 |
} |
||
325 |
} |
||
326 |
58 |
rv = 0; /* in case of a continue */ |
|
327 |
✓✓ | 218 |
while ((execute(t->left, XERROK, NULL) == 0) == (t->type == TWHILE)) |
328 |
113 |
rv = execute(t->right, flags & XERROK, xerrok); |
|
329 |
break; |
||
330 |
|||
331 |
case TIF: |
||
332 |
case TELIF: |
||
333 |
✗✓ | 11752 |
if (t->right == NULL) |
334 |
break; /* should be error */ |
||
335 |
✓✓ | 11752 |
rv = execute(t->left, XERROK, NULL) == 0 ? |
336 |
execute(t->right->left, flags & XERROK, xerrok) : |
||
337 |
execute(t->right->right, flags & XERROK, xerrok); |
||
338 |
11681 |
break; |
|
339 |
|||
340 |
case TCASE: |
||
341 |
5468 |
cp = evalstr(t->str, DOTILDE); |
|
342 |
✓✓✓✗ |
11820 |
for (t = t->left; t != NULL && t->type == TPAT; t = t->right) |
343 |
✓✓ | 25602 |
for (ap = t->vars; *ap; ap++) |
344 |
✓✗✓✓ |
19250 |
if ((s = evalstr(*ap, DOTILDE|DOPAT)) && |
345 |
gmatch(cp, s, false)) |
||
346 |
2500 |
goto Found; |
|
347 |
break; |
||
348 |
2500 |
Found: |
|
349 |
2500 |
rv = execute(t->left, flags & XERROK, xerrok); |
|
350 |
2435 |
break; |
|
351 |
|||
352 |
case TBRACE: |
||
353 |
3167 |
rv = execute(t->left, flags & XERROK, xerrok); |
|
354 |
2946 |
break; |
|
355 |
|||
356 |
case TFUNCT: |
||
357 |
354 |
rv = define(t->str, t); |
|
358 |
354 |
break; |
|
359 |
|||
360 |
case TTIME: |
||
361 |
/* Clear XEXEC so nested execute() call doesn't exit |
||
362 |
* (allows "ls -l | time grep foo"). |
||
363 |
*/ |
||
364 |
2 |
rv = timex(t, flags & ~XEXEC, xerrok); |
|
365 |
2 |
break; |
|
366 |
|||
367 |
case TEXEC: /* an eval'd TCOM */ |
||
368 |
10648 |
s = t->args[0]; |
|
369 |
10648 |
ap = makenv(); |
|
370 |
10648 |
restoresigs(); |
|
371 |
10648 |
cleanup_proc_env(); |
|
372 |
10648 |
execve(t->str, t->args, ap); |
|
373 |
if (errno == ENOEXEC) |
||
374 |
scriptexec(t, ap); |
||
375 |
else |
||
376 |
errorf("%s: %s", s, strerror(errno)); |
||
377 |
} |
||
378 |
227812 |
Break: |
|
379 |
227812 |
exstat = rv; |
|
380 |
|||
381 |
227812 |
quitenv(NULL); /* restores IO */ |
|
382 |
✓✓ | 227812 |
if ((flags&XEXEC)) |
383 |
500 |
unwind(LEXIT); /* exit child */ |
|
384 |
✓✓✓✓ ✓✗✓✓ |
227312 |
if (rv != 0 && !(flags & XERROK) && |
385 |
(xerrok == NULL || !*xerrok)) { |
||
386 |
1254 |
trapsig(SIGERR_); |
|
387 |
✓✓ | 1254 |
if (Flag(FERREXIT)) |
388 |
63 |
unwind(LERROR); |
|
389 |
} |
||
390 |
227249 |
return rv; |
|
391 |
} |
||
392 |
|||
393 |
/* |
||
394 |
* execute simple command |
||
395 |
*/ |
||
396 |
|||
397 |
static int |
||
398 |
comexec(struct op *t, struct tbl *volatile tp, char **ap, volatile int flags, |
||
399 |
volatile int *xerrok) |
||
400 |
171482 |
{ |
|
401 |
int i; |
||
402 |
171482 |
volatile int rv = 0; |
|
403 |
char *cp; |
||
404 |
char **lastp; |
||
405 |
static struct op texec; /* Must be static (XXX but why?) */ |
||
406 |
int type_flags; |
||
407 |
int keepasn_ok; |
||
408 |
171482 |
int fcflags = FC_BI|FC_FUNC|FC_PATH; |
|
409 |
171482 |
int bourne_function_call = 0; |
|
410 |
|||
411 |
/* snag the last argument for $_ XXX not the same as at&t ksh, |
||
412 |
* which only seems to set $_ after a newline (but not in |
||
413 |
* functions/dot scripts, but in interactive and script) - |
||
414 |
* perhaps save last arg here and set it in shell()?. |
||
415 |
*/ |
||
416 |
✓✓✓✓ ✓✓ |
171482 |
if (!Flag(FSH) && Flag(FTALKING) && *(lastp = ap)) { |
417 |
✓✓ | 835 |
while (*++lastp) |
418 |
; |
||
419 |
/* setstr() can't fail here */ |
||
420 |
289 |
setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, |
|
421 |
KSH_RETURN_ERROR); |
||
422 |
} |
||
423 |
|||
424 |
/* Deal with the shell builtins builtin, exec and command since |
||
425 |
* they can be followed by other commands. This must be done before |
||
426 |
* we know if we should create a local block, which must be done |
||
427 |
* before we can do a path search (in case the assignments change |
||
428 |
* PATH). |
||
429 |
* Odd cases: |
||
430 |
* FOO=bar exec > /dev/null FOO is kept but not exported |
||
431 |
* FOO=bar exec foobar FOO is exported |
||
432 |
* FOO=bar command exec > /dev/null FOO is neither kept nor exported |
||
433 |
* FOO=bar command FOO is neither kept nor exported |
||
434 |
* PATH=... foobar use new PATH in foobar search |
||
435 |
*/ |
||
436 |
171482 |
keepasn_ok = 1; |
|
437 |
✓✓✓✓ |
342979 |
while (tp && tp->type == CSHELL) { |
438 |
139751 |
fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */ |
|
439 |
✗✓ | 139751 |
if (tp->val.f == c_builtin) { |
440 |
if ((cp = *++ap) == NULL) { |
||
441 |
tp = NULL; |
||
442 |
break; |
||
443 |
} |
||
444 |
tp = findcom(cp, FC_BI); |
||
445 |
if (tp == NULL) |
||
446 |
errorf("builtin: %s: not a builtin", cp); |
||
447 |
continue; |
||
448 |
✓✓ | 139751 |
} else if (tp->val.f == c_exec) { |
449 |
✓✓ | 26 |
if (ap[1] == NULL) |
450 |
15 |
break; |
|
451 |
11 |
ap++; |
|
452 |
11 |
flags |= XEXEC; |
|
453 |
✓✓ | 139725 |
} else if (tp->val.f == c_command) { |
454 |
52 |
int optc, saw_p = 0; |
|
455 |
|||
456 |
/* Ugly dealing with options in two places (here and |
||
457 |
* in c_command(), but such is life) |
||
458 |
*/ |
||
459 |
52 |
ksh_getopt_reset(&builtin_opt, 0); |
|
460 |
✓✓ | 132 |
while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') |
461 |
28 |
saw_p = 1; |
|
462 |
✓✓ | 52 |
if (optc != EOF) |
463 |
48 |
break; /* command -vV or something */ |
|
464 |
/* don't look for functions */ |
||
465 |
4 |
fcflags = FC_BI|FC_PATH; |
|
466 |
✓✗ | 4 |
if (saw_p) { |
467 |
✗✓ | 4 |
if (Flag(FRESTRICTED)) { |
468 |
warningf(true, |
||
469 |
"command -p: restricted"); |
||
470 |
rv = 1; |
||
471 |
goto Leave; |
||
472 |
} |
||
473 |
4 |
fcflags |= FC_DEFPATH; |
|
474 |
} |
||
475 |
4 |
ap += builtin_opt.optind; |
|
476 |
/* POSIX says special builtins lose their status |
||
477 |
* if accessed using command. |
||
478 |
*/ |
||
479 |
4 |
keepasn_ok = 0; |
|
480 |
✗✓ | 4 |
if (!ap[0]) { |
481 |
/* ensure command with no args exits with 0 */ |
||
482 |
subst_exstat = 0; |
||
483 |
break; |
||
484 |
} |
||
485 |
} else |
||
486 |
139673 |
break; |
|
487 |
15 |
tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); |
|
488 |
} |
||
489 |
✓✓✓✓ ✓✓✓✓ |
203152 |
if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) |
490 |
31670 |
type_flags = 0; |
|
491 |
else { |
||
492 |
/* create new variable/function block */ |
||
493 |
139812 |
newblock(); |
|
494 |
/* ksh functions don't keep assignments, POSIX functions do. */ |
||
495 |
✓✓✓✓ ✓✓✓✓ |
141154 |
if (keepasn_ok && tp && tp->type == CFUNC && |
496 |
!(tp->flag & FKSH)) { |
||
497 |
1343 |
bourne_function_call = 1; |
|
498 |
1343 |
type_flags = 0; |
|
499 |
} else |
||
500 |
138468 |
type_flags = LOCAL|LOCAL_COPY|EXPORT; |
|
501 |
} |
||
502 |
✗✓ | 171481 |
if (Flag(FEXPORT)) |
503 |
type_flags |= EXPORT; |
||
504 |
✓✓ | 191535 |
for (i = 0; t->vars[i]; i++) { |
505 |
20063 |
cp = evalstr(t->vars[i], DOASNTILDE); |
|
506 |
✗✓ | 20062 |
if (Flag(FXTRACE)) { |
507 |
if (i == 0) |
||
508 |
shf_fprintf(shl_out, "%s", |
||
509 |
PS4_SUBSTITUTE(str_val(global("PS4")))); |
||
510 |
shf_fprintf(shl_out, "%s%s", cp, |
||
511 |
t->vars[i + 1] ? " " : "\n"); |
||
512 |
if (!t->vars[i + 1]) |
||
513 |
shf_flush(shl_out); |
||
514 |
} |
||
515 |
20062 |
typeset(cp, type_flags, 0, 0, 0); |
|
516 |
✓✓✓✗ |
20054 |
if (bourne_function_call && !(type_flags & EXPORT)) |
517 |
4 |
typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0); |
|
518 |
} |
||
519 |
|||
520 |
✓✓ | 171472 |
if ((cp = *ap) == NULL) { |
521 |
19704 |
rv = subst_exstat; |
|
522 |
19704 |
goto Leave; |
|
523 |
✓✓ | 151768 |
} else if (!tp) { |
524 |
✗✓✗✗ |
10681 |
if (Flag(FRESTRICTED) && strchr(cp, '/')) { |
525 |
warningf(true, "%s: restricted", cp); |
||
526 |
rv = 1; |
||
527 |
goto Leave; |
||
528 |
} |
||
529 |
10681 |
tp = findcom(cp, fcflags); |
|
530 |
} |
||
531 |
|||
532 |
✓✓✓✗ |
151768 |
switch (tp->type) { |
533 |
case CSHELL: /* shell built-in */ |
||
534 |
139736 |
rv = call_builtin(tp, ap); |
|
535 |
139136 |
break; |
|
536 |
|||
537 |
case CFUNC: /* function call */ |
||
538 |
{ |
||
539 |
volatile int old_xflag, old_inuse; |
||
540 |
const char *volatile old_kshname; |
||
541 |
|||
542 |
✗✓ | 1351 |
if (!(tp->flag & ISSET)) { |
543 |
struct tbl *ftp; |
||
544 |
|||
545 |
if (!tp->u.fpath) { |
||
546 |
if (tp->u2.errno_) { |
||
547 |
warningf(true, |
||
548 |
"%s: can't find function " |
||
549 |
"definition file - %s", |
||
550 |
cp, strerror(tp->u2.errno_)); |
||
551 |
rv = 126; |
||
552 |
} else { |
||
553 |
warningf(true, |
||
554 |
"%s: can't find function " |
||
555 |
"definition file", cp); |
||
556 |
rv = 127; |
||
557 |
} |
||
558 |
break; |
||
559 |
} |
||
560 |
if (include(tp->u.fpath, 0, NULL, 0) < 0) { |
||
561 |
warningf(true, |
||
562 |
"%s: can't open function definition file %s - %s", |
||
563 |
cp, tp->u.fpath, strerror(errno)); |
||
564 |
rv = 127; |
||
565 |
break; |
||
566 |
} |
||
567 |
if (!(ftp = findfunc(cp, hash(cp), false)) || |
||
568 |
!(ftp->flag & ISSET)) { |
||
569 |
warningf(true, |
||
570 |
"%s: function not defined by %s", |
||
571 |
cp, tp->u.fpath); |
||
572 |
rv = 127; |
||
573 |
break; |
||
574 |
} |
||
575 |
tp = ftp; |
||
576 |
} |
||
577 |
|||
578 |
/* ksh functions set $0 to function name, POSIX functions leave |
||
579 |
* $0 unchanged. |
||
580 |
*/ |
||
581 |
1351 |
old_kshname = kshname; |
|
582 |
✓✓ | 1351 |
if (tp->flag & FKSH) |
583 |
8 |
kshname = ap[0]; |
|
584 |
else |
||
585 |
1343 |
ap[0] = (char *) kshname; |
|
586 |
1351 |
genv->loc->argv = ap; |
|
587 |
✓✓ | 1351 |
for (i = 0; *ap++ != NULL; i++) |
588 |
; |
||
589 |
1351 |
genv->loc->argc = i - 1; |
|
590 |
/* ksh-style functions handle getopts sanely, |
||
591 |
* bourne/posix functions are insane... |
||
592 |
*/ |
||
593 |
✓✓ | 1351 |
if (tp->flag & FKSH) { |
594 |
8 |
genv->loc->flags |= BF_DOGETOPTS; |
|
595 |
8 |
genv->loc->getopts_state = user_opt; |
|
596 |
8 |
getopts_reset(1); |
|
597 |
} |
||
598 |
|||
599 |
1351 |
old_xflag = Flag(FXTRACE); |
|
600 |
1351 |
Flag(FXTRACE) = tp->flag & TRACE ? true : false; |
|
601 |
|||
602 |
1351 |
old_inuse = tp->flag & FINUSE; |
|
603 |
1351 |
tp->flag |= FINUSE; |
|
604 |
|||
605 |
1351 |
genv->type = E_FUNC; |
|
606 |
1351 |
i = sigsetjmp(genv->jbuf, 0); |
|
607 |
✓✓ | 1593 |
if (i == 0) { |
608 |
/* seems odd to pass XERROK here, but at&t ksh does */ |
||
609 |
1351 |
exstat = execute(tp->val.t, flags & XERROK, xerrok); |
|
610 |
1132 |
i = LRETURN; |
|
611 |
} |
||
612 |
1374 |
kshname = old_kshname; |
|
613 |
1374 |
Flag(FXTRACE) = old_xflag; |
|
614 |
1374 |
tp->flag = (tp->flag & ~FINUSE) | old_inuse; |
|
615 |
/* Were we deleted while executing? If so, free the execution |
||
616 |
* tree. todo: Unfortunately, the table entry is never re-used |
||
617 |
* until the lookup table is expanded. |
||
618 |
*/ |
||
619 |
✗✓ | 1374 |
if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { |
620 |
if (tp->flag & ALLOC) { |
||
621 |
tp->flag &= ~ALLOC; |
||
622 |
tfree(tp->val.t, tp->areap); |
||
623 |
} |
||
624 |
tp->flag = 0; |
||
625 |
} |
||
626 |
✓✓✗ | 1374 |
switch (i) { |
627 |
case LRETURN: |
||
628 |
case LERROR: |
||
629 |
1328 |
rv = exstat; |
|
630 |
1328 |
break; |
|
631 |
case LINTR: |
||
632 |
case LEXIT: |
||
633 |
case LLEAVE: |
||
634 |
case LSHELL: |
||
635 |
46 |
quitenv(NULL); |
|
636 |
46 |
unwind(i); |
|
637 |
/* NOTREACHED */ |
||
638 |
default: |
||
639 |
quitenv(NULL); |
||
640 |
internal_errorf(1, "CFUNC %d", i); |
||
641 |
} |
||
642 |
break; |
||
643 |
} |
||
644 |
|||
645 |
case CEXEC: /* executable command */ |
||
646 |
case CTALIAS: /* tracked alias */ |
||
647 |
✓✓ | 10681 |
if (!(tp->flag&ISSET)) { |
648 |
/* errno_ will be set if the named command was found |
||
649 |
* but could not be executed (permissions, no execute |
||
650 |
* bit, directory, etc). Print out a (hopefully) |
||
651 |
* useful error message and set the exit status to 126. |
||
652 |
*/ |
||
653 |
✗✓ | 35 |
if (tp->u2.errno_) { |
654 |
warningf(true, "%s: cannot execute - %s", cp, |
||
655 |
strerror(tp->u2.errno_)); |
||
656 |
rv = 126; /* POSIX */ |
||
657 |
} else { |
||
658 |
35 |
warningf(true, "%s: not found", cp); |
|
659 |
35 |
rv = 127; |
|
660 |
} |
||
661 |
break; |
||
662 |
} |
||
663 |
|||
664 |
✓✓ | 10646 |
if (!Flag(FSH)) { |
665 |
/* set $_ to program's full path */ |
||
666 |
/* setstr() can't fail here */ |
||
667 |
233 |
setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), |
|
668 |
tp->val.s, KSH_RETURN_ERROR); |
||
669 |
} |
||
670 |
|||
671 |
✓✓ | 10646 |
if (flags&XEXEC) { |
672 |
3653 |
j_exit(); |
|
673 |
✗✓✗✗ |
3653 |
if (!(flags&XBGND) || Flag(FMONITOR)) { |
674 |
3653 |
setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); |
|
675 |
3653 |
setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); |
|
676 |
} |
||
677 |
} |
||
678 |
|||
679 |
/* to fork we set up a TEXEC node and call execute */ |
||
680 |
10646 |
texec.type = TEXEC; |
|
681 |
10646 |
texec.left = t; /* for tprint */ |
|
682 |
10646 |
texec.str = tp->val.s; |
|
683 |
10646 |
texec.args = ap; |
|
684 |
10646 |
rv = exchild(&texec, flags, xerrok, -1); |
|
685 |
break; |
||
686 |
} |
||
687 |
167194 |
Leave: |
|
688 |
✓✓ | 167194 |
if (flags & XEXEC) { |
689 |
2868 |
exstat = rv; |
|
690 |
2868 |
unwind(LLEAVE); |
|
691 |
} |
||
692 |
164326 |
return rv; |
|
693 |
} |
||
694 |
|||
695 |
static void |
||
696 |
scriptexec(struct op *tp, char **ap) |
||
697 |
{ |
||
698 |
char *shell; |
||
699 |
|||
700 |
shell = str_val(global("EXECSHELL")); |
||
701 |
if (shell && *shell) |
||
702 |
shell = search(shell, path, X_OK, NULL); |
||
703 |
if (!shell || !*shell) |
||
704 |
shell = _PATH_BSHELL; |
||
705 |
|||
706 |
*tp->args-- = tp->str; |
||
707 |
*tp->args = shell; |
||
708 |
|||
709 |
execve(tp->args[0], tp->args, ap); |
||
710 |
|||
711 |
/* report both the program that was run and the bogus shell */ |
||
712 |
errorf("%s: %s: %s", tp->str, shell, strerror(errno)); |
||
713 |
} |
||
714 |
|||
715 |
int |
||
716 |
shcomexec(char **wp) |
||
717 |
24675 |
{ |
|
718 |
struct tbl *tp; |
||
719 |
|||
720 |
24675 |
tp = ktsearch(&builtins, *wp, hash(*wp)); |
|
721 |
✗✓ | 24675 |
if (tp == NULL) |
722 |
internal_errorf(1, "shcomexec: %s", *wp); |
||
723 |
24675 |
return call_builtin(tp, wp); |
|
724 |
} |
||
725 |
|||
726 |
/* |
||
727 |
* Search function tables for a function. If create set, a table entry |
||
728 |
* is created if none is found. |
||
729 |
*/ |
||
730 |
struct tbl * |
||
731 |
findfunc(const char *name, unsigned int h, int create) |
||
732 |
152341 |
{ |
|
733 |
struct block *l; |
||
734 |
152341 |
struct tbl *tp = NULL; |
|
735 |
|||
736 |
✓✓ | 535419 |
for (l = genv->loc; l; l = l->next) { |
737 |
384797 |
tp = ktsearch(&l->funs, name, h); |
|
738 |
✓✓ | 384797 |
if (tp) |
739 |
1369 |
break; |
|
740 |
✓✓ | 383428 |
if (!l->next && create) { |
741 |
350 |
tp = ktenter(&l->funs, name, h); |
|
742 |
350 |
tp->flag = DEFINED; |
|
743 |
350 |
tp->type = CFUNC; |
|
744 |
350 |
tp->val.t = NULL; |
|
745 |
350 |
break; |
|
746 |
} |
||
747 |
} |
||
748 |
152341 |
return tp; |
|
749 |
} |
||
750 |
|||
751 |
/* |
||
752 |
* define function. Returns 1 if function is being undefined (t == 0) and |
||
753 |
* function did not exist, returns 0 otherwise. |
||
754 |
*/ |
||
755 |
int |
||
756 |
define(const char *name, struct op *t) |
||
757 |
356 |
{ |
|
758 |
struct tbl *tp; |
||
759 |
356 |
int was_set = 0; |
|
760 |
|||
761 |
while (1) { |
||
762 |
356 |
tp = findfunc(name, hash(name), true); |
|
763 |
|||
764 |
✓✓ | 356 |
if (tp->flag & ISSET) |
765 |
6 |
was_set = 1; |
|
766 |
/* If this function is currently being executed, we zap this |
||
767 |
* table entry so findfunc() won't see it |
||
768 |
*/ |
||
769 |
✗✓ | 356 |
if (tp->flag & FINUSE) { |
770 |
tp->name[0] = '\0'; |
||
771 |
tp->flag &= ~DEFINED; /* ensure it won't be found */ |
||
772 |
tp->flag |= FDELETE; |
||
773 |
} else |
||
774 |
356 |
break; |
|
775 |
} |
||
776 |
|||
777 |
✓✓ | 356 |
if (tp->flag & ALLOC) { |
778 |
6 |
tp->flag &= ~(ISSET|ALLOC); |
|
779 |
6 |
tfree(tp->val.t, tp->areap); |
|
780 |
} |
||
781 |
|||
782 |
✓✓ | 356 |
if (t == NULL) { /* undefine */ |
783 |
2 |
ktdelete(tp); |
|
784 |
2 |
return was_set ? 0 : 1; |
|
785 |
} |
||
786 |
|||
787 |
354 |
tp->val.t = tcopy(t->left, tp->areap); |
|
788 |
354 |
tp->flag |= (ISSET|ALLOC); |
|
789 |
✓✓ | 354 |
if (t->u.ksh_func) |
790 |
4 |
tp->flag |= FKSH; |
|
791 |
|||
792 |
354 |
return 0; |
|
793 |
} |
||
794 |
|||
795 |
/* |
||
796 |
* add builtin |
||
797 |
*/ |
||
798 |
void |
||
799 |
builtin(const char *name, int (*func) (char **)) |
||
800 |
148050 |
{ |
|
801 |
struct tbl *tp; |
||
802 |
int flag; |
||
803 |
|||
804 |
/* see if any flags should be set for this builtin */ |
||
805 |
317250 |
for (flag = 0; ; name++) { |
|
806 |
✓✓ | 317250 |
if (*name == '=') /* command does variable assignment */ |
807 |
63450 |
flag |= KEEPASN; |
|
808 |
✓✓ | 253800 |
else if (*name == '*') /* POSIX special builtin */ |
809 |
52875 |
flag |= SPEC_BI; |
|
810 |
✓✓ | 200925 |
else if (*name == '+') /* POSIX regular builtin */ |
811 |
52875 |
flag |= REG_BI; |
|
812 |
else |
||
813 |
148050 |
break; |
|
814 |
169200 |
} |
|
815 |
|||
816 |
148050 |
tp = ktenter(&builtins, name, hash(name)); |
|
817 |
148050 |
tp->flag = DEFINED | flag; |
|
818 |
148050 |
tp->type = CSHELL; |
|
819 |
148050 |
tp->val.f = func; |
|
820 |
148050 |
} |
|
821 |
|||
822 |
/* |
||
823 |
* find command |
||
824 |
* either function, hashed command, or built-in (in that order) |
||
825 |
*/ |
||
826 |
struct tbl * |
||
827 |
findcom(const char *name, int flags) |
||
828 |
162547 |
{ |
|
829 |
static struct tbl temp; |
||
830 |
162547 |
unsigned int h = hash(name); |
|
831 |
162547 |
struct tbl *tp = NULL, *tbi; |
|
832 |
162547 |
int insert = Flag(FTRACKALL); /* insert if not found */ |
|
833 |
char *fpath; /* for function autoloading */ |
||
834 |
char *npath; |
||
835 |
|||
836 |
✓✓ | 162547 |
if (strchr(name, '/') != NULL) { |
837 |
2196 |
insert = 0; |
|
838 |
/* prevent FPATH search below */ |
||
839 |
2196 |
flags &= ~FC_FUNC; |
|
840 |
2196 |
goto Search; |
|
841 |
} |
||
842 |
✓✓ | 160351 |
tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL; |
843 |
/* POSIX says special builtins first, then functions, then |
||
844 |
* POSIX regular builtins, then search path... |
||
845 |
*/ |
||
846 |
✓✓✓✓ |
160351 |
if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) |
847 |
8334 |
tp = tbi; |
|
848 |
✓✓✓✓ |
160351 |
if (!tp && (flags & FC_FUNC)) { |
849 |
151985 |
tp = findfunc(name, h, false); |
|
850 |
✓✓✗✓ |
151985 |
if (tp && !(tp->flag & ISSET)) { |
851 |
if ((fpath = str_val(global("FPATH"))) == null) { |
||
852 |
tp->u.fpath = NULL; |
||
853 |
tp->u2.errno_ = 0; |
||
854 |
} else |
||
855 |
tp->u.fpath = search(name, fpath, R_OK, |
||
856 |
&tp->u2.errno_); |
||
857 |
} |
||
858 |
} |
||
859 |
✓✓✓✓ ✓✓ |
160351 |
if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) |
860 |
1238 |
tp = tbi; |
|
861 |
/* todo: posix says non-special/non-regular builtins must |
||
862 |
* be triggered by some user-controllable means like a |
||
863 |
* special directory in PATH. Requires modifications to |
||
864 |
* the search() function. Tracked aliases should be |
||
865 |
* modified to allow tracking of builtin commands. |
||
866 |
* This should be under control of the FPOSIX flag. |
||
867 |
* If this is changed, also change c_whence... |
||
868 |
*/ |
||
869 |
✓✓✓✓ |
160351 |
if (!tp && (flags & FC_UNREGBI) && tbi) |
870 |
130203 |
tp = tbi; |
|
871 |
✓✓✓✓ |
160351 |
if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { |
872 |
9616 |
tp = ktsearch(&taliases, name, h); |
|
873 |
✓✓✓✓ ✗✓ |
9616 |
if (tp && (tp->flag & ISSET) && access(tp->val.s, X_OK) != 0) { |
874 |
if (tp->flag & ALLOC) { |
||
875 |
tp->flag &= ~ALLOC; |
||
876 |
afree(tp->val.s, APERM); |
||
877 |
} |
||
878 |
tp->flag &= ~ISSET; |
||
879 |
} |
||
880 |
} |
||
881 |
|||
882 |
162547 |
Search: |
|
883 |
✓✓✓✓ ✓✓ |
162547 |
if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) && |
884 |
(flags & FC_PATH)) { |
||
885 |
✓✓ | 5917 |
if (!tp) { |
886 |
✓✓✓✓ |
7825 |
if (insert && !(flags & FC_DEFPATH)) { |
887 |
3355 |
tp = ktenter(&taliases, name, h); |
|
888 |
3355 |
tp->type = CTALIAS; |
|
889 |
} else { |
||
890 |
1115 |
tp = &temp; |
|
891 |
1115 |
tp->type = CEXEC; |
|
892 |
} |
||
893 |
4470 |
tp->flag = DEFINED; /* make ~ISSET */ |
|
894 |
} |
||
895 |
✓✓ | 5917 |
npath = search(name, flags & FC_DEFPATH ? def_path : path, |
896 |
X_OK, &tp->u2.errno_); |
||
897 |
✓✓ | 5917 |
if (npath) { |
898 |
✓✓ | 5860 |
if (tp == &temp) { |
899 |
1090 |
tp->val.s = npath; |
|
900 |
} else { |
||
901 |
4770 |
tp->val.s = str_save(npath, APERM); |
|
902 |
✓✗ | 4770 |
if (npath != name) |
903 |
4770 |
afree(npath, ATEMP); |
|
904 |
} |
||
905 |
5860 |
tp->flag |= ISSET|ALLOC; |
|
906 |
✓✓✓✓ ✗✓ |
57 |
} else if ((flags & FC_FUNC) && |
907 |
(fpath = str_val(global("FPATH"))) != null && |
||
908 |
(npath = search(name, fpath, R_OK, |
||
909 |
&tp->u2.errno_)) != NULL) { |
||
910 |
/* An undocumented feature of at&t ksh is that it |
||
911 |
* searches FPATH if a command is not found, even |
||
912 |
* if the command hasn't been set up as an autoloaded |
||
913 |
* function (ie, no typeset -uf). |
||
914 |
*/ |
||
915 |
tp = &temp; |
||
916 |
tp->type = CFUNC; |
||
917 |
tp->flag = DEFINED; /* make ~ISSET */ |
||
918 |
tp->u.fpath = npath; |
||
919 |
} |
||
920 |
} |
||
921 |
162547 |
return tp; |
|
922 |
} |
||
923 |
|||
924 |
/* |
||
925 |
* flush executable commands with relative paths |
||
926 |
*/ |
||
927 |
void |
||
928 |
flushcom(int all) /* just relative or all */ |
||
929 |
7506 |
{ |
|
930 |
struct tbl *tp; |
||
931 |
struct tstate ts; |
||
932 |
|||
933 |
✓✓ | 23251 |
for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; ) |
934 |
✓✓✓✓ ✗✓ |
8239 |
if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) { |
935 |
✓✗ | 14 |
if (tp->flag&ALLOC) { |
936 |
14 |
tp->flag &= ~(ALLOC|ISSET); |
|
937 |
14 |
afree(tp->val.s, APERM); |
|
938 |
} |
||
939 |
14 |
tp->flag &= ~ISSET; |
|
940 |
} |
||
941 |
7506 |
} |
|
942 |
|||
943 |
/* Check if path is something we want to find. Returns -1 for failure. */ |
||
944 |
int |
||
945 |
search_access(const char *path, int mode, |
||
946 |
int *errnop) /* set if candidate found, but not suitable */ |
||
947 |
18608 |
{ |
|
948 |
18608 |
int ret, err = 0; |
|
949 |
struct stat statb; |
||
950 |
|||
951 |
✓✓ | 18608 |
if (stat(path, &statb) < 0) |
952 |
12734 |
return -1; |
|
953 |
5874 |
ret = access(path, mode); |
|
954 |
✗✓ | 5874 |
if (ret < 0) |
955 |
err = errno; /* File exists, but we can't access it */ |
||
956 |
✓✓✓✗ ✗✓ |
5874 |
else if (mode == X_OK && (!S_ISREG(statb.st_mode) || |
957 |
!(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { |
||
958 |
/* This 'cause access() says root can execute everything */ |
||
959 |
ret = -1; |
||
960 |
err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES; |
||
961 |
} |
||
962 |
✗✓✗✗ |
5874 |
if (err && errnop && !*errnop) |
963 |
*errnop = err; |
||
964 |
5874 |
return ret; |
|
965 |
} |
||
966 |
|||
967 |
/* |
||
968 |
* search for command with PATH |
||
969 |
*/ |
||
970 |
char * |
||
971 |
search(const char *name, const char *path, |
||
972 |
int mode, /* R_OK or X_OK */ |
||
973 |
int *errnop) /* set if candidate found, but not suitable */ |
||
974 |
5933 |
{ |
|
975 |
const char *sp, *p; |
||
976 |
char *xp; |
||
977 |
XString xs; |
||
978 |
int namelen; |
||
979 |
|||
980 |
✓✗ | 5933 |
if (errnop) |
981 |
5933 |
*errnop = 0; |
|
982 |
✓✓ | 5933 |
if (strchr(name, '/')) { |
983 |
✓✓ | 1115 |
if (search_access(name, mode, errnop) == 0) |
984 |
1086 |
return (char *) name; |
|
985 |
29 |
return NULL; |
|
986 |
} |
||
987 |
|||
988 |
4818 |
namelen = strlen(name) + 1; |
|
989 |
4818 |
Xinit(xs, xp, 128, ATEMP); |
|
990 |
|||
991 |
4818 |
sp = path; |
|
992 |
✓✓ | 22341 |
while (sp != NULL) { |
993 |
17489 |
xp = Xstring(xs, xp); |
|
994 |
✓✓ | 17489 |
if (!(p = strchr(sp, ':'))) |
995 |
37 |
p = sp + strlen(sp); |
|
996 |
✓✗ | 17489 |
if (p != sp) { |
997 |
✗✓ | 17489 |
XcheckN(xs, xp, p - sp); |
998 |
17489 |
memcpy(xp, sp, p - sp); |
|
999 |
17489 |
xp += p - sp; |
|
1000 |
17489 |
*xp++ = '/'; |
|
1001 |
} |
||
1002 |
17489 |
sp = p; |
|
1003 |
✗✓ | 17489 |
XcheckN(xs, xp, namelen); |
1004 |
17489 |
memcpy(xp, name, namelen); |
|
1005 |
✓✓ | 17489 |
if (search_access(Xstring(xs, xp), mode, errnop) == 0) |
1006 |
4784 |
return Xclose(xs, xp + namelen); |
|
1007 |
✓✓ | 12705 |
if (*sp++ == '\0') |
1008 |
34 |
sp = NULL; |
|
1009 |
} |
||
1010 |
34 |
Xfree(xs, xp); |
|
1011 |
34 |
return NULL; |
|
1012 |
} |
||
1013 |
|||
1014 |
static int |
||
1015 |
call_builtin(struct tbl *tp, char **wp) |
||
1016 |
164411 |
{ |
|
1017 |
int rv; |
||
1018 |
|||
1019 |
164411 |
builtin_argv0 = wp[0]; |
|
1020 |
164411 |
builtin_flag = tp->flag; |
|
1021 |
164411 |
shf_reopen(1, SHF_WR, shl_stdout); |
|
1022 |
164411 |
shl_stdout_ok = 1; |
|
1023 |
164411 |
ksh_getopt_reset(&builtin_opt, GF_ERROR); |
|
1024 |
164411 |
rv = (*tp->val.f)(wp); |
|
1025 |
163811 |
shf_flush(shl_stdout); |
|
1026 |
163811 |
shl_stdout_ok = 0; |
|
1027 |
163811 |
builtin_flag = 0; |
|
1028 |
163811 |
builtin_argv0 = NULL; |
|
1029 |
163811 |
return rv; |
|
1030 |
} |
||
1031 |
|||
1032 |
/* |
||
1033 |
* set up redirection, saving old fd's in e->savefd |
||
1034 |
*/ |
||
1035 |
static int |
||
1036 |
iosetup(struct ioword *iop, struct tbl *tp) |
||
1037 |
7859 |
{ |
|
1038 |
7859 |
int u = -1; |
|
1039 |
7859 |
char *cp = iop->name; |
|
1040 |
7859 |
int iotype = iop->flag & IOTYPE; |
|
1041 |
7859 |
int do_open = 1, do_close = 0, flags = 0; |
|
1042 |
struct ioword iotmp; |
||
1043 |
struct stat statb; |
||
1044 |
|||
1045 |
✓✓ | 7859 |
if (iotype != IOHERE) |
1046 |
✓✓ | 7016 |
cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0)); |
1047 |
|||
1048 |
/* Used for tracing and error messages to print expanded cp */ |
||
1049 |
7859 |
iotmp = *iop; |
|
1050 |
✓✓ | 7859 |
iotmp.name = (iotype == IOHERE) ? NULL : cp; |
1051 |
7859 |
iotmp.flag |= IONAMEXP; |
|
1052 |
|||
1053 |
✗✓ | 7859 |
if (Flag(FXTRACE)) |
1054 |
shellf("%s%s\n", |
||
1055 |
PS4_SUBSTITUTE(str_val(global("PS4"))), |
||
1056 |
snptreef(NULL, 32, "%R", &iotmp)); |
||
1057 |
|||
1058 |
✓✓✓✗ ✓✓✗ |
7859 |
switch (iotype) { |
1059 |
case IOREAD: |
||
1060 |
81 |
flags = O_RDONLY; |
|
1061 |
81 |
break; |
|
1062 |
|||
1063 |
case IOCAT: |
||
1064 |
516 |
flags = O_WRONLY | O_APPEND | O_CREAT; |
|
1065 |
516 |
break; |
|
1066 |
|||
1067 |
case IOWRITE: |
||
1068 |
2396 |
flags = O_WRONLY | O_CREAT | O_TRUNC; |
|
1069 |
/* The stat() is here to allow redirections to |
||
1070 |
* things like /dev/null without error. |
||
1071 |
*/ |
||
1072 |
✗✓✗✗ ✗✗✗✗ |
2396 |
if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) && |
1073 |
(stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) |
||
1074 |
flags |= O_EXCL; |
||
1075 |
break; |
||
1076 |
|||
1077 |
case IORDWR: |
||
1078 |
flags = O_RDWR | O_CREAT; |
||
1079 |
break; |
||
1080 |
|||
1081 |
case IOHERE: |
||
1082 |
843 |
do_open = 0; |
|
1083 |
/* herein() returns -2 if error has been printed */ |
||
1084 |
843 |
u = herein(iop->heredoc, iop->flag & IOEVAL); |
|
1085 |
/* cp may have wrong name */ |
||
1086 |
843 |
break; |
|
1087 |
|||
1088 |
case IODUP: |
||
1089 |
{ |
||
1090 |
const char *emsg; |
||
1091 |
|||
1092 |
4023 |
do_open = 0; |
|
1093 |
✗✓✗✗ |
4023 |
if (*cp == '-' && !cp[1]) { |
1094 |
u = 1009; /* prevent error return below */ |
||
1095 |
do_close = 1; |
||
1096 |
✓✓✗✓ |
4023 |
} else if ((u = check_fd(cp, |
1097 |
X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK), |
||
1098 |
&emsg)) < 0) { |
||
1099 |
warningf(true, "%s: %s", |
||
1100 |
snptreef(NULL, 32, "%R", &iotmp), emsg); |
||
1101 |
return -1; |
||
1102 |
} |
||
1103 |
✗✓ | 4023 |
if (u == iop->unit) |
1104 |
return 0; /* "dup from" == "dup to" */ |
||
1105 |
break; |
||
1106 |
} |
||
1107 |
} |
||
1108 |
|||
1109 |
✓✓ | 7859 |
if (do_open) { |
1110 |
✗✓✗✗ |
2993 |
if (Flag(FRESTRICTED) && (flags & O_CREAT)) { |
1111 |
warningf(true, "%s: restricted", cp); |
||
1112 |
return -1; |
||
1113 |
} |
||
1114 |
2993 |
u = open(cp, flags, 0666); |
|
1115 |
} |
||
1116 |
✗✓ | 7859 |
if (u < 0) { |
1117 |
/* herein() may already have printed message */ |
||
1118 |
if (u == -1) |
||
1119 |
warningf(true, "cannot %s %s: %s", |
||
1120 |
iotype == IODUP ? "dup" : |
||
1121 |
(iotype == IOREAD || iotype == IOHERE) ? |
||
1122 |
"open" : "create", cp, strerror(errno)); |
||
1123 |
return -1; |
||
1124 |
} |
||
1125 |
/* Do not save if it has already been redirected (i.e. "cat >x >y"). */ |
||
1126 |
✓✓ | 7859 |
if (genv->savefd[iop->unit] == 0) { |
1127 |
/* If these are the same, it means unit was previously closed */ |
||
1128 |
✓✓ | 7857 |
if (u == iop->unit) |
1129 |
1 |
genv->savefd[iop->unit] = -1; |
|
1130 |
else |
||
1131 |
/* c_exec() assumes e->savefd[fd] set for any |
||
1132 |
* redirections. Ask savefd() not to close iop->unit; |
||
1133 |
* this allows error messages to be seen if iop->unit |
||
1134 |
* is 2; also means we can't lose the fd (eg, both |
||
1135 |
* dup2 below and dup2 in restfd() failing). |
||
1136 |
*/ |
||
1137 |
7856 |
genv->savefd[iop->unit] = savefd(iop->unit); |
|
1138 |
} |
||
1139 |
|||
1140 |
✗✓ | 7859 |
if (do_close) |
1141 |
close(iop->unit); |
||
1142 |
✓✓ | 7859 |
else if (u != iop->unit) { |
1143 |
✗✓ | 7858 |
if (ksh_dup2(u, iop->unit, true) < 0) { |
1144 |
warningf(true, |
||
1145 |
"could not finish (dup) redirection %s: %s", |
||
1146 |
snptreef(NULL, 32, "%R", &iotmp), |
||
1147 |
strerror(errno)); |
||
1148 |
if (iotype != IODUP) |
||
1149 |
close(u); |
||
1150 |
return -1; |
||
1151 |
} |
||
1152 |
✓✓ | 7858 |
if (iotype != IODUP) |
1153 |
3835 |
close(u); |
|
1154 |
/* Touching any co-process fd in an empty exec |
||
1155 |
* causes the shell to close its copies |
||
1156 |
*/ |
||
1157 |
✓✓✓✗ ✓✓ |
4023 |
else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { |
1158 |
✓✓ | 8 |
if (iop->flag & IORDUP) /* possible exec <&p */ |
1159 |
2 |
coproc_read_close(u); |
|
1160 |
else /* possible exec >&p */ |
||
1161 |
6 |
coproc_write_close(u); |
|
1162 |
} |
||
1163 |
} |
||
1164 |
✓✓ | 7859 |
if (u == 2) /* Clear any write errors */ |
1165 |
1 |
shf_reopen(2, SHF_WR, shl_out); |
|
1166 |
7859 |
return 0; |
|
1167 |
} |
||
1168 |
|||
1169 |
/* |
||
1170 |
* open here document temp file. |
||
1171 |
* if unquoted here, expand here temp file into second temp file. |
||
1172 |
*/ |
||
1173 |
static int |
||
1174 |
herein(const char *content, int sub) |
||
1175 |
843 |
{ |
|
1176 |
843 |
volatile int fd = -1; |
|
1177 |
struct source *s, *volatile osource; |
||
1178 |
struct shf *volatile shf; |
||
1179 |
struct temp *h; |
||
1180 |
int i; |
||
1181 |
|||
1182 |
/* ksh -c 'cat << EOF' can cause this... */ |
||
1183 |
✗✓ | 843 |
if (content == NULL) { |
1184 |
warningf(true, "here document missing"); |
||
1185 |
return -2; /* special to iosetup(): don't print error */ |
||
1186 |
} |
||
1187 |
|||
1188 |
/* Create temp file to hold content (done before newenv so temp |
||
1189 |
* doesn't get removed too soon). |
||
1190 |
*/ |
||
1191 |
843 |
h = maketemp(ATEMP, TT_HEREDOC_EXP, &genv->temps); |
|
1192 |
✓✗✗✓ |
843 |
if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) { |
1193 |
warningf(true, "can't %s temporary file %s: %s", |
||
1194 |
!shf ? "create" : "open", |
||
1195 |
h->name, strerror(errno)); |
||
1196 |
if (shf) |
||
1197 |
shf_close(shf); |
||
1198 |
return -2 /* special to iosetup(): don't print error */; |
||
1199 |
} |
||
1200 |
|||
1201 |
843 |
osource = source; |
|
1202 |
843 |
newenv(E_ERRH); |
|
1203 |
843 |
i = sigsetjmp(genv->jbuf, 0); |
|
1204 |
✓✓ | 880 |
if (i) { |
1205 |
37 |
source = osource; |
|
1206 |
37 |
quitenv(shf); |
|
1207 |
close(fd); |
||
1208 |
return -2; /* special to iosetup(): don't print error */ |
||
1209 |
} |
||
1210 |
✓✓ | 843 |
if (sub) { |
1211 |
/* Do substitutions on the content of heredoc */ |
||
1212 |
765 |
s = pushs(SSTRING, ATEMP); |
|
1213 |
765 |
s->start = s->str = content; |
|
1214 |
765 |
source = s; |
|
1215 |
✗✓ | 765 |
if (yylex(ONEWORD|HEREDOC) != LWORD) |
1216 |
internal_errorf(1, "herein: yylex"); |
||
1217 |
765 |
source = osource; |
|
1218 |
765 |
shf_puts(evalstr(yylval.cp, 0), shf); |
|
1219 |
} else |
||
1220 |
78 |
shf_puts(content, shf); |
|
1221 |
|||
1222 |
843 |
quitenv(NULL); |
|
1223 |
|||
1224 |
✗✓ | 843 |
if (shf_close(shf) == EOF) { |
1225 |
close(fd); |
||
1226 |
warningf(true, "error writing %s: %s", h->name, |
||
1227 |
strerror(errno)); |
||
1228 |
return -2; /* special to iosetup(): don't print error */ |
||
1229 |
} |
||
1230 |
|||
1231 |
843 |
return fd; |
|
1232 |
} |
||
1233 |
|||
1234 |
#ifdef EDIT |
||
1235 |
/* |
||
1236 |
* ksh special - the select command processing section |
||
1237 |
* print the args in column form - assuming that we can |
||
1238 |
*/ |
||
1239 |
static char * |
||
1240 |
do_selectargs(char **ap, bool print_menu) |
||
1241 |
{ |
||
1242 |
static const char *const read_args[] = { |
||
1243 |
"read", "-r", "REPLY", NULL |
||
1244 |
}; |
||
1245 |
const char *errstr; |
||
1246 |
char *s; |
||
1247 |
int i, argct; |
||
1248 |
|||
1249 |
for (argct = 0; ap[argct]; argct++) |
||
1250 |
; |
||
1251 |
while (1) { |
||
1252 |
/* Menu is printed if |
||
1253 |
* - this is the first time around the select loop |
||
1254 |
* - the user enters a blank line |
||
1255 |
* - the REPLY parameter is empty |
||
1256 |
*/ |
||
1257 |
if (print_menu || !*str_val(global("REPLY"))) |
||
1258 |
pr_menu(ap); |
||
1259 |
shellf("%s", str_val(global("PS3"))); |
||
1260 |
if (call_builtin(findcom("read", FC_BI), (char **) read_args)) |
||
1261 |
return NULL; |
||
1262 |
s = str_val(global("REPLY")); |
||
1263 |
if (*s) { |
||
1264 |
i = strtonum(s, 1, argct, &errstr); |
||
1265 |
if (errstr) |
||
1266 |
return null; |
||
1267 |
return ap[i - 1]; |
||
1268 |
} |
||
1269 |
print_menu = 1; |
||
1270 |
} |
||
1271 |
} |
||
1272 |
|||
1273 |
struct select_menu_info { |
||
1274 |
char *const *args; |
||
1275 |
int arg_width; |
||
1276 |
int num_width; |
||
1277 |
}; |
||
1278 |
|||
1279 |
static char *select_fmt_entry(void *arg, int i, char *buf, int buflen); |
||
1280 |
|||
1281 |
/* format a single select menu item */ |
||
1282 |
static char * |
||
1283 |
select_fmt_entry(void *arg, int i, char *buf, int buflen) |
||
1284 |
{ |
||
1285 |
struct select_menu_info *smi = (struct select_menu_info *) arg; |
||
1286 |
|||
1287 |
shf_snprintf(buf, buflen, "%*d) %s", |
||
1288 |
smi->num_width, i + 1, smi->args[i]); |
||
1289 |
return buf; |
||
1290 |
} |
||
1291 |
|||
1292 |
/* |
||
1293 |
* print a select style menu |
||
1294 |
*/ |
||
1295 |
int |
||
1296 |
pr_menu(char *const *ap) |
||
1297 |
{ |
||
1298 |
struct select_menu_info smi; |
||
1299 |
char *const *pp; |
||
1300 |
int nwidth, dwidth; |
||
1301 |
int i, n; |
||
1302 |
|||
1303 |
/* Width/column calculations were done once and saved, but this |
||
1304 |
* means select can't be used recursively so we re-calculate each |
||
1305 |
* time (could save in a structure that is returned, but its probably |
||
1306 |
* not worth the bother). |
||
1307 |
*/ |
||
1308 |
|||
1309 |
/* |
||
1310 |
* get dimensions of the list |
||
1311 |
*/ |
||
1312 |
for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) { |
||
1313 |
i = strlen(*pp); |
||
1314 |
nwidth = (i > nwidth) ? i : nwidth; |
||
1315 |
} |
||
1316 |
/* |
||
1317 |
* we will print an index of the form |
||
1318 |
* %d) |
||
1319 |
* in front of each entry |
||
1320 |
* get the max width of this |
||
1321 |
*/ |
||
1322 |
for (i = n, dwidth = 1; i >= 10; i /= 10) |
||
1323 |
dwidth++; |
||
1324 |
|||
1325 |
smi.args = ap; |
||
1326 |
smi.arg_width = nwidth; |
||
1327 |
smi.num_width = dwidth; |
||
1328 |
print_columns(shl_out, n, select_fmt_entry, (void *) &smi, |
||
1329 |
dwidth + nwidth + 2, 1); |
||
1330 |
|||
1331 |
return n; |
||
1332 |
} |
||
1333 |
|||
1334 |
/* XXX: horrible kludge to fit within the framework */ |
||
1335 |
|||
1336 |
static char *plain_fmt_entry(void *arg, int i, char *buf, int buflen); |
||
1337 |
|||
1338 |
static char * |
||
1339 |
plain_fmt_entry(void *arg, int i, char *buf, int buflen) |
||
1340 |
{ |
||
1341 |
shf_snprintf(buf, buflen, "%s", ((char *const *)arg)[i]); |
||
1342 |
return buf; |
||
1343 |
} |
||
1344 |
|||
1345 |
int |
||
1346 |
pr_list(char *const *ap) |
||
1347 |
{ |
||
1348 |
char *const *pp; |
||
1349 |
int nwidth; |
||
1350 |
int i, n; |
||
1351 |
|||
1352 |
for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) { |
||
1353 |
i = strlen(*pp); |
||
1354 |
nwidth = (i > nwidth) ? i : nwidth; |
||
1355 |
} |
||
1356 |
print_columns(shl_out, n, plain_fmt_entry, (void *) ap, nwidth + 1, 0); |
||
1357 |
|||
1358 |
return n; |
||
1359 |
} |
||
1360 |
#endif /* EDIT */ |
||
1361 |
|||
1362 |
/* |
||
1363 |
* [[ ... ]] evaluation routines |
||
1364 |
*/ |
||
1365 |
|||
1366 |
extern const char *const dbtest_tokens[]; |
||
1367 |
extern const char db_close[]; |
||
1368 |
|||
1369 |
/* Test if the current token is a whatever. Accepts the current token if |
||
1370 |
* it is. Returns 0 if it is not, non-zero if it is (in the case of |
||
1371 |
* TM_UNOP and TM_BINOP, the returned value is a Test_op). |
||
1372 |
*/ |
||
1373 |
static int |
||
1374 |
dbteste_isa(Test_env *te, Test_meta meta) |
||
1375 |
12 |
{ |
|
1376 |
12 |
int ret = 0; |
|
1377 |
int uqword; |
||
1378 |
char *p; |
||
1379 |
|||
1380 |
✓✓ | 12 |
if (!*te->pos.wp) |
1381 |
6 |
return meta == TM_END; |
|
1382 |
|||
1383 |
/* unquoted word? */ |
||
1384 |
✓✓ | 6 |
for (p = *te->pos.wp; *p == CHAR; p += 2) |
1385 |
; |
||
1386 |
6 |
uqword = *p == EOS; |
|
1387 |
|||
1388 |
✓✓ | 6 |
if (meta == TM_UNOP || meta == TM_BINOP) { |
1389 |
✓✗ | 2 |
if (uqword) { |
1390 |
char buf[8]; /* longer than the longest operator */ |
||
1391 |
2 |
char *q = buf; |
|
1392 |
2 |
for (p = *te->pos.wp; |
|
1393 |
✓✓✓✗ |
6 |
*p == CHAR && q < &buf[sizeof(buf) - 1]; p += 2) |
1394 |
4 |
*q++ = p[1]; |
|
1395 |
2 |
*q = '\0'; |
|
1396 |
2 |
ret = (int) test_isop(te, meta, buf); |
|
1397 |
} |
||
1398 |
✗✓ | 4 |
} else if (meta == TM_END) |
1399 |
ret = 0; |
||
1400 |
else |
||
1401 |
✓✗✗✓ |
4 |
ret = uqword && |
1402 |
strcmp(*te->pos.wp, dbtest_tokens[(int) meta]) == 0; |
||
1403 |
|||
1404 |
/* Accept the token? */ |
||
1405 |
✓✓ | 6 |
if (ret) |
1406 |
2 |
te->pos.wp++; |
|
1407 |
|||
1408 |
6 |
return ret; |
|
1409 |
} |
||
1410 |
|||
1411 |
static const char * |
||
1412 |
dbteste_getopnd(Test_env *te, Test_op op, int do_eval) |
||
1413 |
2 |
{ |
|
1414 |
2 |
char *s = *te->pos.wp; |
|
1415 |
|||
1416 |
✗✓ | 2 |
if (!s) |
1417 |
return NULL; |
||
1418 |
|||
1419 |
2 |
te->pos.wp++; |
|
1420 |
|||
1421 |
✗✓ | 2 |
if (!do_eval) |
1422 |
return null; |
||
1423 |
|||
1424 |
✗✓ | 2 |
if (op == TO_STEQL || op == TO_STNEQ) |
1425 |
s = evalstr(s, DOTILDE | DOPAT); |
||
1426 |
else |
||
1427 |
2 |
s = evalstr(s, DOTILDE); |
|
1428 |
|||
1429 |
2 |
return s; |
|
1430 |
} |
||
1431 |
|||
1432 |
static int |
||
1433 |
dbteste_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2, |
||
1434 |
int do_eval) |
||
1435 |
2 |
{ |
|
1436 |
2 |
return test_eval(te, op, opnd1, opnd2, do_eval); |
|
1437 |
} |
||
1438 |
|||
1439 |
static void |
||
1440 |
dbteste_error(Test_env *te, int offset, const char *msg) |
||
1441 |
{ |
||
1442 |
te->flags |= TEF_ERROR; |
||
1443 |
internal_errorf(0, "dbteste_error: %s (offset %d)", msg, offset); |
||
1444 |
} |
Generated by: GCOVR (Version 3.3) |