1 |
/* $OpenBSD: exec.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */ |
2 |
|
3 |
/*- |
4 |
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, |
5 |
* 2011, 2012, 2013, 2014, 2015, 2016, 2017 |
6 |
* mirabilos <m@mirbsd.org> |
7 |
* |
8 |
* Provided that these terms and disclaimer and all copyright notices |
9 |
* are retained or reproduced in an accompanying document, permission |
10 |
* is granted to deal in this work without restriction, including un- |
11 |
* limited rights to use, publicly perform, distribute, sell, modify, |
12 |
* merge, give away, or sublicence. |
13 |
* |
14 |
* This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to |
15 |
* the utmost extent permitted by applicable law, neither express nor |
16 |
* implied; without malicious intent or gross negligence. In no event |
17 |
* may a licensor, author or contributor be held liable for indirect, |
18 |
* direct, other damage, loss, or other issues arising in any way out |
19 |
* of dealing in the work, even if advised of the possibility of such |
20 |
* damage or existence of a defect, except proven that it results out |
21 |
* of said person's immediate fault when using the work as intended. |
22 |
*/ |
23 |
|
24 |
#include "sh.h" |
25 |
|
26 |
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.201 2017/10/11 21:09:24 tg Exp $"); |
27 |
|
28 |
#ifndef MKSH_DEFAULT_EXECSHELL |
29 |
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" |
30 |
#endif |
31 |
|
32 |
static int comexec(struct op *, struct tbl * volatile, const char **, |
33 |
int volatile, volatile int *); |
34 |
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN; |
35 |
static int call_builtin(struct tbl *, const char **, const char *, bool); |
36 |
static int iosetup(struct ioword *, struct tbl *); |
37 |
static const char *do_selectargs(const char **, bool); |
38 |
static Test_op dbteste_isa(Test_env *, Test_meta); |
39 |
static const char *dbteste_getopnd(Test_env *, Test_op, bool); |
40 |
static void dbteste_error(Test_env *, int, const char *); |
41 |
/* XXX: horrible kludge to fit within the framework */ |
42 |
static void plain_fmt_entry(char *, size_t, unsigned int, const void *); |
43 |
static void select_fmt_entry(char *, size_t, unsigned int, const void *); |
44 |
|
45 |
/* |
46 |
* execute command tree |
47 |
*/ |
48 |
int |
49 |
execute(struct op * volatile t, |
50 |
/* if XEXEC don't fork */ |
51 |
volatile int flags, |
52 |
volatile int * volatile xerrok) |
53 |
{ |
54 |
int i; |
55 |
volatile int rv = 0, dummy = 0; |
56 |
int pv[2]; |
57 |
const char ** volatile ap = NULL; |
58 |
char ** volatile up; |
59 |
const char *s, *ccp; |
60 |
struct ioword **iowp; |
61 |
struct tbl *tp = NULL; |
62 |
|
63 |
if (t == NULL) |
64 |
return (0); |
65 |
|
66 |
/* Caller doesn't care if XERROK should propagate. */ |
67 |
if (xerrok == NULL) |
68 |
xerrok = &dummy; |
69 |
|
70 |
if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) |
71 |
/* run in sub-process */ |
72 |
return (exchild(t, flags & ~XTIME, xerrok, -1)); |
73 |
|
74 |
newenv(E_EXEC); |
75 |
if (trap) |
76 |
runtraps(0); |
77 |
|
78 |
/* we want to run an executable, do some variance checks */ |
79 |
if (t->type == TCOM) { |
80 |
/* |
81 |
* Clear subst_exstat before argument expansion. Used by |
82 |
* null commands (see comexec() and c_eval()) and by c_set(). |
83 |
*/ |
84 |
subst_exstat = 0; |
85 |
|
86 |
/* for $LINENO */ |
87 |
current_lineno = t->lineno; |
88 |
|
89 |
/* check if this is 'var=<<EOF' */ |
90 |
if ( |
91 |
/* we have zero arguments, i.e. no program to run */ |
92 |
t->args[0] == NULL && |
93 |
/* we have exactly one variable assignment */ |
94 |
t->vars[0] != NULL && t->vars[1] == NULL && |
95 |
/* we have exactly one I/O redirection */ |
96 |
t->ioact != NULL && t->ioact[0] != NULL && |
97 |
t->ioact[1] == NULL && |
98 |
/* of type "here document" (or "here string") */ |
99 |
(t->ioact[0]->ioflag & IOTYPE) == IOHERE && |
100 |
/* the variable assignment begins with a valid varname */ |
101 |
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && |
102 |
/* and has no right-hand side (i.e. "varname=") */ |
103 |
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) || |
104 |
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR && |
105 |
ccp[3] == '=' && ccp[4] == EOS))) { |
106 |
char *cp, *dp; |
107 |
|
108 |
if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/)) |
109 |
cp = NULL; |
110 |
dp = shf_smprintf(Tf_ss, evalstr(t->vars[0], |
111 |
DOASNTILDE | DOSCALAR), rv ? null : cp); |
112 |
typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0); |
113 |
/* free the expanded value */ |
114 |
afree(cp, APERM); |
115 |
afree(dp, ATEMP); |
116 |
goto Break; |
117 |
} |
118 |
|
119 |
/* |
120 |
* POSIX says expand command words first, then redirections, |
121 |
* and assignments last.. |
122 |
*/ |
123 |
up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); |
124 |
if (flags & XTIME) |
125 |
/* Allow option parsing (bizarre, but POSIX) */ |
126 |
timex_hook(t, &up); |
127 |
ap = (const char **)up; |
128 |
if (ap[0]) |
129 |
tp = findcom(ap[0], FC_BI|FC_FUNC); |
130 |
} |
131 |
flags &= ~XTIME; |
132 |
|
133 |
if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { |
134 |
e->savefd = alloc2(NUFILE, sizeof(short), ATEMP); |
135 |
/* initialise to not redirected */ |
136 |
memset(e->savefd, 0, NUFILE * sizeof(short)); |
137 |
} |
138 |
|
139 |
/* mark for replacement later (unless TPIPE) */ |
140 |
vp_pipest->flag |= INT_L; |
141 |
|
142 |
/* do redirection, to be restored in quitenv() */ |
143 |
if (t->ioact != NULL) |
144 |
for (iowp = t->ioact; *iowp != NULL; iowp++) { |
145 |
if (iosetup(*iowp, tp) < 0) { |
146 |
exstat = rv = 1; |
147 |
/* |
148 |
* Redirection failures for special commands |
149 |
* cause (non-interactive) shell to exit. |
150 |
*/ |
151 |
if (tp && tp->type == CSHELL && |
152 |
(tp->flag & SPEC_BI)) |
153 |
errorfz(); |
154 |
/* Deal with FERREXIT, quitenv(), etc. */ |
155 |
goto Break; |
156 |
} |
157 |
} |
158 |
|
159 |
switch (t->type) { |
160 |
case TCOM: |
161 |
rv = comexec(t, tp, (const char **)ap, flags, xerrok); |
162 |
break; |
163 |
|
164 |
case TPAREN: |
165 |
rv = execute(t->left, flags | XFORK, xerrok); |
166 |
break; |
167 |
|
168 |
case TPIPE: |
169 |
flags |= XFORK; |
170 |
flags &= ~XEXEC; |
171 |
e->savefd[0] = savefd(0); |
172 |
e->savefd[1] = savefd(1); |
173 |
while (t->type == TPIPE) { |
174 |
openpipe(pv); |
175 |
/* stdout of curr */ |
176 |
ksh_dup2(pv[1], 1, false); |
177 |
/** |
178 |
* Let exchild() close pv[0] in child |
179 |
* (if this isn't done, commands like |
180 |
* (: ; cat /etc/termcap) | sleep 1 |
181 |
* will hang forever). |
182 |
*/ |
183 |
exchild(t->left, flags | XPIPEO | XCCLOSE, |
184 |
NULL, pv[0]); |
185 |
/* stdin of next */ |
186 |
ksh_dup2(pv[0], 0, false); |
187 |
closepipe(pv); |
188 |
flags |= XPIPEI; |
189 |
t = t->right; |
190 |
} |
191 |
/* stdout of last */ |
192 |
restfd(1, e->savefd[1]); |
193 |
/* no need to re-restore this */ |
194 |
e->savefd[1] = 0; |
195 |
/* Let exchild() close 0 in parent, after fork, before wait */ |
196 |
i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0); |
197 |
if (!(flags&XBGND) && !(flags&XXCOM)) |
198 |
rv = i; |
199 |
break; |
200 |
|
201 |
case TLIST: |
202 |
while (t->type == TLIST) { |
203 |
execute(t->left, flags & XERROK, NULL); |
204 |
t = t->right; |
205 |
} |
206 |
rv = execute(t, flags & XERROK, xerrok); |
207 |
break; |
208 |
|
209 |
case TCOPROC: { |
210 |
#ifndef MKSH_NOPROSPECTOFWORK |
211 |
sigset_t omask; |
212 |
|
213 |
/* |
214 |
* Block sigchild as we are using things changed in the |
215 |
* signal handler |
216 |
*/ |
217 |
sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); |
218 |
e->type = E_ERRH; |
219 |
if ((i = kshsetjmp(e->jbuf))) { |
220 |
sigprocmask(SIG_SETMASK, &omask, NULL); |
221 |
quitenv(NULL); |
222 |
unwind(i); |
223 |
/* NOTREACHED */ |
224 |
} |
225 |
#endif |
226 |
/* Already have a (live) co-process? */ |
227 |
if (coproc.job && coproc.write >= 0) |
228 |
errorf("coprocess already exists"); |
229 |
|
230 |
/* Can we re-use the existing co-process pipe? */ |
231 |
coproc_cleanup(true); |
232 |
|
233 |
/* do this before opening pipes, in case these fail */ |
234 |
e->savefd[0] = savefd(0); |
235 |
e->savefd[1] = savefd(1); |
236 |
|
237 |
openpipe(pv); |
238 |
if (pv[0] != 0) { |
239 |
ksh_dup2(pv[0], 0, false); |
240 |
close(pv[0]); |
241 |
} |
242 |
coproc.write = pv[1]; |
243 |
coproc.job = NULL; |
244 |
|
245 |
if (coproc.readw >= 0) |
246 |
ksh_dup2(coproc.readw, 1, false); |
247 |
else { |
248 |
openpipe(pv); |
249 |
coproc.read = pv[0]; |
250 |
ksh_dup2(pv[1], 1, false); |
251 |
/* closed before first read */ |
252 |
coproc.readw = pv[1]; |
253 |
coproc.njobs = 0; |
254 |
/* create new coprocess id */ |
255 |
++coproc.id; |
256 |
} |
257 |
#ifndef MKSH_NOPROSPECTOFWORK |
258 |
sigprocmask(SIG_SETMASK, &omask, NULL); |
259 |
/* no more need for error handler */ |
260 |
e->type = E_EXEC; |
261 |
#endif |
262 |
|
263 |
/* |
264 |
* exchild() closes coproc.* in child after fork, |
265 |
* will also increment coproc.njobs when the |
266 |
* job is actually created. |
267 |
*/ |
268 |
flags &= ~XEXEC; |
269 |
exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE, |
270 |
NULL, coproc.readw); |
271 |
break; |
272 |
} |
273 |
|
274 |
case TASYNC: |
275 |
/* |
276 |
* XXX non-optimal, I think - "(foo &)", forks for (), |
277 |
* forks again for async... parent should optimise |
278 |
* this to "foo &"... |
279 |
*/ |
280 |
rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok); |
281 |
break; |
282 |
|
283 |
case TOR: |
284 |
case TAND: |
285 |
rv = execute(t->left, XERROK, NULL); |
286 |
if ((rv == 0) == (t->type == TAND)) |
287 |
rv = execute(t->right, flags & XERROK, xerrok); |
288 |
else { |
289 |
flags |= XERROK; |
290 |
if (xerrok) |
291 |
*xerrok = 1; |
292 |
} |
293 |
break; |
294 |
|
295 |
case TBANG: |
296 |
rv = !execute(t->right, XERROK, xerrok); |
297 |
flags |= XERROK; |
298 |
if (xerrok) |
299 |
*xerrok = 1; |
300 |
break; |
301 |
|
302 |
case TDBRACKET: { |
303 |
Test_env te; |
304 |
|
305 |
te.flags = TEF_DBRACKET; |
306 |
te.pos.wp = t->args; |
307 |
te.isa = dbteste_isa; |
308 |
te.getopnd = dbteste_getopnd; |
309 |
te.eval = test_eval; |
310 |
te.error = dbteste_error; |
311 |
|
312 |
rv = test_parse(&te); |
313 |
break; |
314 |
} |
315 |
|
316 |
case TFOR: |
317 |
case TSELECT: { |
318 |
volatile bool is_first = true; |
319 |
|
320 |
ap = (t->vars == NULL) ? e->loc->argv + 1 : |
321 |
(const char **)eval((const char **)t->vars, |
322 |
DOBLANK | DOGLOB | DOTILDE); |
323 |
e->type = E_LOOP; |
324 |
while ((i = kshsetjmp(e->jbuf))) { |
325 |
if ((e->flags&EF_BRKCONT_PASS) || |
326 |
(i != LBREAK && i != LCONTIN)) { |
327 |
quitenv(NULL); |
328 |
unwind(i); |
329 |
} else if (i == LBREAK) { |
330 |
rv = 0; |
331 |
goto Break; |
332 |
} |
333 |
} |
334 |
/* in case of a continue */ |
335 |
rv = 0; |
336 |
if (t->type == TFOR) { |
337 |
while (*ap != NULL) { |
338 |
setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); |
339 |
rv = execute(t->left, flags & XERROK, xerrok); |
340 |
} |
341 |
} else { |
342 |
do_TSELECT: |
343 |
if ((ccp = do_selectargs(ap, is_first))) { |
344 |
is_first = false; |
345 |
setstr(global(t->str), ccp, KSH_UNWIND_ERROR); |
346 |
execute(t->left, flags & XERROK, xerrok); |
347 |
goto do_TSELECT; |
348 |
} |
349 |
rv = 1; |
350 |
} |
351 |
break; |
352 |
} |
353 |
|
354 |
case TWHILE: |
355 |
case TUNTIL: |
356 |
e->type = E_LOOP; |
357 |
while ((i = kshsetjmp(e->jbuf))) { |
358 |
if ((e->flags&EF_BRKCONT_PASS) || |
359 |
(i != LBREAK && i != LCONTIN)) { |
360 |
quitenv(NULL); |
361 |
unwind(i); |
362 |
} else if (i == LBREAK) { |
363 |
rv = 0; |
364 |
goto Break; |
365 |
} |
366 |
} |
367 |
/* in case of a continue */ |
368 |
rv = 0; |
369 |
while ((execute(t->left, XERROK, NULL) == 0) == |
370 |
(t->type == TWHILE)) |
371 |
rv = execute(t->right, flags & XERROK, xerrok); |
372 |
break; |
373 |
|
374 |
case TIF: |
375 |
case TELIF: |
376 |
if (t->right == NULL) |
377 |
/* should be error */ |
378 |
break; |
379 |
rv = execute(execute(t->left, XERROK, NULL) == 0 ? |
380 |
t->right->left : t->right->right, flags & XERROK, xerrok); |
381 |
break; |
382 |
|
383 |
case TCASE: |
384 |
i = 0; |
385 |
ccp = evalstr(t->str, DOTILDE | DOSCALAR); |
386 |
for (t = t->left; t != NULL && t->type == TPAT; t = t->right) { |
387 |
for (ap = (const char **)t->vars; *ap; ap++) { |
388 |
if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) && |
389 |
gmatchx(ccp, s, false))) { |
390 |
record_match(ccp); |
391 |
rv = execute(t->left, flags & XERROK, |
392 |
xerrok); |
393 |
i = 0; |
394 |
switch (t->u.charflag) { |
395 |
case '&': |
396 |
i = 1; |
397 |
/* FALLTHROUGH */ |
398 |
case '|': |
399 |
goto TCASE_next; |
400 |
} |
401 |
goto TCASE_out; |
402 |
} |
403 |
} |
404 |
i = 0; |
405 |
TCASE_next: |
406 |
/* empty */; |
407 |
} |
408 |
TCASE_out: |
409 |
break; |
410 |
|
411 |
case TBRACE: |
412 |
rv = execute(t->left, flags & XERROK, xerrok); |
413 |
break; |
414 |
|
415 |
case TFUNCT: |
416 |
rv = define(t->str, t); |
417 |
break; |
418 |
|
419 |
case TTIME: |
420 |
/* |
421 |
* Clear XEXEC so nested execute() call doesn't exit |
422 |
* (allows "ls -l | time grep foo"). |
423 |
*/ |
424 |
rv = timex(t, flags & ~XEXEC, xerrok); |
425 |
break; |
426 |
|
427 |
case TEXEC: |
428 |
/* an eval'd TCOM */ |
429 |
up = makenv(); |
430 |
restoresigs(); |
431 |
cleanup_proc_env(); |
432 |
{ |
433 |
union mksh_ccphack cargs; |
434 |
|
435 |
cargs.ro = t->args; |
436 |
execve(t->str, cargs.rw, up); |
437 |
rv = errno; |
438 |
} |
439 |
if (rv == ENOEXEC) |
440 |
scriptexec(t, (const char **)up); |
441 |
else |
442 |
errorf(Tf_sD_s, t->str, cstrerror(rv)); |
443 |
} |
444 |
Break: |
445 |
exstat = rv & 0xFF; |
446 |
if (vp_pipest->flag & INT_L) { |
447 |
unset(vp_pipest, 1); |
448 |
vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY | |
449 |
ARRAY | INT_U | INT_L; |
450 |
vp_pipest->val.i = rv; |
451 |
} |
452 |
|
453 |
/* restores IO */ |
454 |
quitenv(NULL); |
455 |
if ((flags&XEXEC)) |
456 |
/* exit child */ |
457 |
unwind(LEXIT); |
458 |
if (rv != 0 && !(flags & XERROK) && |
459 |
(xerrok == NULL || !*xerrok)) { |
460 |
if (Flag(FERREXIT) & 0x80) { |
461 |
/* inside eval */ |
462 |
Flag(FERREXIT) = 0; |
463 |
} else { |
464 |
trapsig(ksh_SIGERR); |
465 |
if (Flag(FERREXIT)) |
466 |
unwind(LERROR); |
467 |
} |
468 |
} |
469 |
return (rv); |
470 |
} |
471 |
|
472 |
/* |
473 |
* execute simple command |
474 |
*/ |
475 |
|
476 |
static int |
477 |
comexec(struct op *t, struct tbl * volatile tp, const char **ap, |
478 |
volatile int flags, volatile int *xerrok) |
479 |
{ |
480 |
int i; |
481 |
volatile int rv = 0; |
482 |
const char *cp; |
483 |
const char **lastp; |
484 |
/* Must be static (XXX but why?) */ |
485 |
static struct op texec; |
486 |
int type_flags; |
487 |
bool resetspec; |
488 |
int fcflags = FC_BI|FC_FUNC|FC_PATH; |
489 |
struct block *l_expand, *l_assign; |
490 |
int optc; |
491 |
const char *exec_argv0 = NULL; |
492 |
bool exec_clrenv = false; |
493 |
|
494 |
/* snag the last argument for $_ */ |
495 |
if (Flag(FTALKING) && *(lastp = ap)) { |
496 |
/* |
497 |
* XXX not the same as AT&T ksh, which only seems to set $_ |
498 |
* after a newline (but not in functions/dot scripts, but in |
499 |
* interactive and script) - perhaps save last arg here and |
500 |
* set it in shell()?. |
501 |
*/ |
502 |
while (*++lastp) |
503 |
; |
504 |
/* setstr() can't fail here */ |
505 |
setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, |
506 |
KSH_RETURN_ERROR); |
507 |
} |
508 |
|
509 |
/** |
510 |
* Deal with the shell builtins builtin, exec and command since |
511 |
* they can be followed by other commands. This must be done before |
512 |
* we know if we should create a local block which must be done |
513 |
* before we can do a path search (in case the assignments change |
514 |
* PATH). |
515 |
* Odd cases: |
516 |
* FOO=bar exec >/dev/null FOO is kept but not exported |
517 |
* FOO=bar exec foobar FOO is exported |
518 |
* FOO=bar command exec >/dev/null FOO is neither kept nor exported |
519 |
* FOO=bar command FOO is neither kept nor exported |
520 |
* PATH=... foobar use new PATH in foobar search |
521 |
*/ |
522 |
resetspec = false; |
523 |
while (tp && tp->type == CSHELL) { |
524 |
/* undo effects of command */ |
525 |
fcflags = FC_BI|FC_FUNC|FC_PATH; |
526 |
if (tp->val.f == c_builtin) { |
527 |
if ((cp = *++ap) == NULL || |
528 |
(!strcmp(cp, "--") && (cp = *++ap) == NULL)) { |
529 |
tp = NULL; |
530 |
break; |
531 |
} |
532 |
if ((tp = findcom(cp, FC_BI)) == NULL) |
533 |
errorf(Tf_sD_sD_s, Tbuiltin, cp, Tnot_found); |
534 |
if (tp->type == CSHELL && (tp->flag & LOW_BI)) |
535 |
break; |
536 |
continue; |
537 |
} else if (tp->val.f == c_exec) { |
538 |
if (ap[1] == NULL) |
539 |
break; |
540 |
ksh_getopt_reset(&builtin_opt, GF_ERROR); |
541 |
while ((optc = ksh_getopt(ap, &builtin_opt, "a:c")) != -1) |
542 |
switch (optc) { |
543 |
case 'a': |
544 |
exec_argv0 = builtin_opt.optarg; |
545 |
break; |
546 |
case 'c': |
547 |
exec_clrenv = true; |
548 |
/* ensure we can actually do this */ |
549 |
resetspec = true; |
550 |
break; |
551 |
default: |
552 |
rv = 2; |
553 |
goto Leave; |
554 |
} |
555 |
ap += builtin_opt.optind; |
556 |
flags |= XEXEC; |
557 |
/* POSuX demands ksh88-like behaviour here */ |
558 |
if (Flag(FPOSIX)) |
559 |
fcflags = FC_PATH; |
560 |
} else if (tp->val.f == c_command) { |
561 |
bool saw_p = false; |
562 |
|
563 |
/* |
564 |
* Ugly dealing with options in two places (here |
565 |
* and in c_command(), but such is life) |
566 |
*/ |
567 |
ksh_getopt_reset(&builtin_opt, 0); |
568 |
while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') |
569 |
saw_p = true; |
570 |
if (optc != -1) |
571 |
/* command -vV or something */ |
572 |
break; |
573 |
/* don't look for functions */ |
574 |
fcflags = FC_BI|FC_PATH; |
575 |
if (saw_p) { |
576 |
if (Flag(FRESTRICTED)) { |
577 |
warningf(true, Tf_sD_s, |
578 |
"command -p", "restricted"); |
579 |
rv = 1; |
580 |
goto Leave; |
581 |
} |
582 |
fcflags |= FC_DEFPATH; |
583 |
} |
584 |
ap += builtin_opt.optind; |
585 |
/* |
586 |
* POSIX says special builtins lose their status |
587 |
* if accessed using command. |
588 |
*/ |
589 |
resetspec = true; |
590 |
if (!ap[0]) { |
591 |
/* ensure command with no args exits with 0 */ |
592 |
subst_exstat = 0; |
593 |
break; |
594 |
} |
595 |
} else if (tp->flag & LOW_BI) { |
596 |
/* if we have any flags, do not use the builtin */ |
597 |
if ((ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && |
598 |
/* argument, begins with -, is not - or -- */ |
599 |
(ap[1][1] != '-' || ap[1][2] != '\0')) || |
600 |
/* always prefer the external utility */ |
601 |
(tp->flag & LOWER_BI)) { |
602 |
struct tbl *ext_cmd; |
603 |
|
604 |
ext_cmd = findcom(tp->name, FC_PATH | FC_FUNC); |
605 |
if (ext_cmd && (ext_cmd->type != CTALIAS || |
606 |
(ext_cmd->flag & ISSET))) |
607 |
tp = ext_cmd; |
608 |
} |
609 |
break; |
610 |
} else if (tp->val.f == c_trap) { |
611 |
t->u.evalflags &= ~DOTCOMEXEC; |
612 |
break; |
613 |
} else |
614 |
break; |
615 |
tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); |
616 |
} |
617 |
if (t->u.evalflags & DOTCOMEXEC) |
618 |
flags |= XEXEC; |
619 |
l_expand = e->loc; |
620 |
if (!resetspec && (!ap[0] || (tp && (tp->flag & KEEPASN)))) |
621 |
type_flags = 0; |
622 |
else { |
623 |
/* create new variable/function block */ |
624 |
newblock(); |
625 |
/* ksh functions don't keep assignments, POSIX functions do. */ |
626 |
if (!resetspec && tp && tp->type == CFUNC && |
627 |
!(tp->flag & FKSH)) |
628 |
type_flags = EXPORT; |
629 |
else |
630 |
type_flags = LOCAL|LOCAL_COPY|EXPORT; |
631 |
} |
632 |
l_assign = e->loc; |
633 |
if (exec_clrenv) |
634 |
l_assign->flags |= BF_STOPENV; |
635 |
if (Flag(FEXPORT)) |
636 |
type_flags |= EXPORT; |
637 |
if (Flag(FXTRACE)) |
638 |
change_xtrace(2, false); |
639 |
for (i = 0; t->vars[i]; i++) { |
640 |
/* do NOT lookup in the new var/fn block just created */ |
641 |
e->loc = l_expand; |
642 |
cp = evalstr(t->vars[i], DOASNTILDE | DOSCALAR); |
643 |
e->loc = l_assign; |
644 |
if (Flag(FXTRACE)) { |
645 |
const char *ccp; |
646 |
|
647 |
ccp = skip_varname(cp, true); |
648 |
if (*ccp == '+') |
649 |
++ccp; |
650 |
if (*ccp == '=') |
651 |
++ccp; |
652 |
shf_write(cp, ccp - cp, shl_xtrace); |
653 |
print_value_quoted(shl_xtrace, ccp); |
654 |
shf_putc(' ', shl_xtrace); |
655 |
} |
656 |
/* but assign in there as usual */ |
657 |
typeset(cp, type_flags, 0, 0, 0); |
658 |
} |
659 |
|
660 |
if (Flag(FXTRACE)) { |
661 |
change_xtrace(2, false); |
662 |
if (ap[rv = 0]) { |
663 |
xtrace_ap_loop: |
664 |
print_value_quoted(shl_xtrace, ap[rv]); |
665 |
if (ap[++rv]) { |
666 |
shf_putc(' ', shl_xtrace); |
667 |
goto xtrace_ap_loop; |
668 |
} |
669 |
} |
670 |
change_xtrace(1, false); |
671 |
} |
672 |
|
673 |
if ((cp = *ap) == NULL) { |
674 |
rv = subst_exstat; |
675 |
goto Leave; |
676 |
} else if (!tp) { |
677 |
if (Flag(FRESTRICTED) && mksh_vdirsep(cp)) { |
678 |
warningf(true, Tf_sD_s, cp, "restricted"); |
679 |
rv = 1; |
680 |
goto Leave; |
681 |
} |
682 |
tp = findcom(cp, fcflags); |
683 |
} |
684 |
|
685 |
switch (tp->type) { |
686 |
|
687 |
/* shell built-in */ |
688 |
case CSHELL: |
689 |
do_call_builtin: |
690 |
rv = call_builtin(tp, (const char **)ap, null, resetspec); |
691 |
if (resetspec && tp->val.f == c_shift) { |
692 |
l_expand->argc = l_assign->argc; |
693 |
l_expand->argv = l_assign->argv; |
694 |
} |
695 |
break; |
696 |
|
697 |
/* function call */ |
698 |
case CFUNC: { |
699 |
volatile uint32_t old_inuse; |
700 |
const char * volatile old_kshname; |
701 |
volatile uint8_t old_flags[FNFLAGS]; |
702 |
|
703 |
if (!(tp->flag & ISSET)) { |
704 |
struct tbl *ftp; |
705 |
|
706 |
if (!tp->u.fpath) { |
707 |
fpath_error: |
708 |
rv = (tp->u2.errnov == ENOENT) ? 127 : 126; |
709 |
warningf(true, Tf_sD_s_sD_s, cp, |
710 |
Tcant_find, Tfile_fd, |
711 |
cstrerror(tp->u2.errnov)); |
712 |
break; |
713 |
} |
714 |
errno = 0; |
715 |
if (include(tp->u.fpath, 0, NULL, false) < 0 || |
716 |
!(ftp = findfunc(cp, hash(cp), false)) || |
717 |
!(ftp->flag & ISSET)) { |
718 |
rv = errno; |
719 |
if ((ftp = findcom(cp, FC_BI)) && |
720 |
(ftp->type == CSHELL) && |
721 |
(ftp->flag & LOW_BI)) { |
722 |
tp = ftp; |
723 |
goto do_call_builtin; |
724 |
} |
725 |
if (rv) { |
726 |
tp->u2.errnov = rv; |
727 |
cp = tp->u.fpath; |
728 |
goto fpath_error; |
729 |
} |
730 |
warningf(true, Tf_sD_s_s, cp, |
731 |
"function not defined by", tp->u.fpath); |
732 |
rv = 127; |
733 |
break; |
734 |
} |
735 |
tp = ftp; |
736 |
} |
737 |
|
738 |
/* |
739 |
* ksh functions set $0 to function name, POSIX |
740 |
* functions leave $0 unchanged. |
741 |
*/ |
742 |
old_kshname = kshname; |
743 |
if (tp->flag & FKSH) |
744 |
kshname = ap[0]; |
745 |
else |
746 |
ap[0] = kshname; |
747 |
e->loc->argv = ap; |
748 |
for (i = 0; *ap++ != NULL; i++) |
749 |
; |
750 |
e->loc->argc = i - 1; |
751 |
/* |
752 |
* ksh-style functions handle getopts sanely, |
753 |
* Bourne/POSIX functions are insane... |
754 |
*/ |
755 |
if (tp->flag & FKSH) { |
756 |
e->loc->flags |= BF_DOGETOPTS; |
757 |
e->loc->getopts_state = user_opt; |
758 |
getopts_reset(1); |
759 |
} |
760 |
|
761 |
for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) |
762 |
old_flags[type_flags] = shell_flags[type_flags]; |
763 |
change_xtrace((Flag(FXTRACEREC) ? Flag(FXTRACE) : 0) | |
764 |
((tp->flag & TRACE) ? 1 : 0), false); |
765 |
old_inuse = tp->flag & FINUSE; |
766 |
tp->flag |= FINUSE; |
767 |
|
768 |
e->type = E_FUNC; |
769 |
if (!(i = kshsetjmp(e->jbuf))) { |
770 |
execute(tp->val.t, flags & XERROK, NULL); |
771 |
i = LRETURN; |
772 |
} |
773 |
|
774 |
kshname = old_kshname; |
775 |
change_xtrace(old_flags[(int)FXTRACE], false); |
776 |
#ifndef MKSH_LEGACY_MODE |
777 |
if (tp->flag & FKSH) { |
778 |
/* Korn style functions restore Flags on return */ |
779 |
old_flags[(int)FXTRACE] = Flag(FXTRACE); |
780 |
for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) |
781 |
shell_flags[type_flags] = old_flags[type_flags]; |
782 |
} |
783 |
#endif |
784 |
tp->flag = (tp->flag & ~FINUSE) | old_inuse; |
785 |
|
786 |
/* |
787 |
* Were we deleted while executing? If so, free the |
788 |
* execution tree. |
789 |
*/ |
790 |
if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { |
791 |
if (tp->flag & ALLOC) { |
792 |
tp->flag &= ~ALLOC; |
793 |
tfree(tp->val.t, tp->areap); |
794 |
} |
795 |
tp->flag = 0; |
796 |
} |
797 |
switch (i) { |
798 |
case LRETURN: |
799 |
case LERROR: |
800 |
rv = exstat & 0xFF; |
801 |
break; |
802 |
case LINTR: |
803 |
case LEXIT: |
804 |
case LLEAVE: |
805 |
case LSHELL: |
806 |
quitenv(NULL); |
807 |
unwind(i); |
808 |
/* NOTREACHED */ |
809 |
default: |
810 |
quitenv(NULL); |
811 |
internal_errorf(Tunexpected_type, Tunwind, Tfunction, i); |
812 |
} |
813 |
break; |
814 |
} |
815 |
|
816 |
/* executable command */ |
817 |
case CEXEC: |
818 |
/* tracked alias */ |
819 |
case CTALIAS: |
820 |
if (!(tp->flag&ISSET)) { |
821 |
if (tp->u2.errnov == ENOENT) { |
822 |
rv = 127; |
823 |
warningf(true, Tf_sD_s, cp, Tnot_found); |
824 |
} else { |
825 |
rv = 126; |
826 |
warningf(true, Tf_sD_sD_s, cp, "can't execute", |
827 |
cstrerror(tp->u2.errnov)); |
828 |
} |
829 |
break; |
830 |
} |
831 |
|
832 |
/* set $_ to program's full path */ |
833 |
/* setstr() can't fail here */ |
834 |
setstr(typeset("_", LOCAL | EXPORT, 0, INTEGER, 0), |
835 |
tp->val.s, KSH_RETURN_ERROR); |
836 |
|
837 |
/* to fork, we set up a TEXEC node and call execute */ |
838 |
texec.type = TEXEC; |
839 |
/* for vistree/dumptree */ |
840 |
texec.left = t; |
841 |
texec.str = tp->val.s; |
842 |
texec.args = ap; |
843 |
|
844 |
/* in this case we do not fork, of course */ |
845 |
if (flags & XEXEC) { |
846 |
if (exec_argv0) |
847 |
texec.args[0] = exec_argv0; |
848 |
j_exit(); |
849 |
if (!(flags & XBGND) |
850 |
#ifndef MKSH_UNEMPLOYED |
851 |
|| Flag(FMONITOR) |
852 |
#endif |
853 |
) { |
854 |
setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); |
855 |
setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); |
856 |
} |
857 |
} |
858 |
|
859 |
rv = exchild(&texec, flags, xerrok, -1); |
860 |
break; |
861 |
} |
862 |
Leave: |
863 |
if (flags & XEXEC) { |
864 |
exstat = rv & 0xFF; |
865 |
unwind(LLEAVE); |
866 |
} |
867 |
return (rv); |
868 |
} |
869 |
|
870 |
static void |
871 |
scriptexec(struct op *tp, const char **ap) |
872 |
{ |
873 |
const char *sh; |
874 |
#ifndef MKSH_SMALL |
875 |
int fd; |
876 |
unsigned char buf[68]; |
877 |
#endif |
878 |
union mksh_ccphack args, cap; |
879 |
|
880 |
sh = str_val(global(TEXECSHELL)); |
881 |
if (sh && *sh) |
882 |
sh = search_path(sh, path, X_OK, NULL); |
883 |
if (!sh || !*sh) |
884 |
sh = MKSH_DEFAULT_EXECSHELL; |
885 |
|
886 |
*tp->args-- = tp->str; |
887 |
|
888 |
#ifndef MKSH_SMALL |
889 |
if ((fd = binopen2(tp->str, O_RDONLY)) >= 0) { |
890 |
unsigned char *cp; |
891 |
#ifndef MKSH_EBCDIC |
892 |
unsigned short m; |
893 |
#endif |
894 |
ssize_t n; |
895 |
|
896 |
#if defined(__OS2__) && defined(MKSH_WITH_TEXTMODE) |
897 |
setmode(fd, O_TEXT); |
898 |
#endif |
899 |
/* read first couple of octets from file */ |
900 |
n = read(fd, buf, sizeof(buf) - 1); |
901 |
close(fd); |
902 |
/* read error or short read? */ |
903 |
if (n < 5) |
904 |
goto nomagic; |
905 |
/* terminate buffer */ |
906 |
buf[n] = '\0'; |
907 |
|
908 |
/* skip UTF-8 Byte Order Mark, if present */ |
909 |
cp = buf + (n = ((buf[0] == 0xEF) && (buf[1] == 0xBB) && |
910 |
(buf[2] == 0xBF)) ? 3 : 0); |
911 |
|
912 |
/* scan for newline or NUL (end of buffer) */ |
913 |
while (!ctype(*cp, C_NL | C_NUL)) |
914 |
++cp; |
915 |
/* if the shebang line is longer than MAXINTERP, bail out */ |
916 |
if (!*cp) |
917 |
goto noshebang; |
918 |
/* replace newline by NUL */ |
919 |
*cp = '\0'; |
920 |
|
921 |
/* restore start of shebang position (buf+0 or buf+3) */ |
922 |
cp = buf + n; |
923 |
/* bail out if no shebang magic found */ |
924 |
if (cp[0] == '#' && cp[1] == '!') |
925 |
cp += 2; |
926 |
#ifdef __OS2__ |
927 |
else if (!strncmp(cp, Textproc, 7) && |
928 |
ctype(cp[7], C_BLANK)) |
929 |
cp += 8; |
930 |
#endif |
931 |
else |
932 |
goto noshebang; |
933 |
/* skip whitespace before shell name */ |
934 |
while (ctype(*cp, C_BLANK)) |
935 |
++cp; |
936 |
/* just whitespace on the line? */ |
937 |
if (*cp == '\0') |
938 |
goto noshebang; |
939 |
/* no, we actually found an interpreter name */ |
940 |
sh = (char *)cp; |
941 |
/* look for end of shell/interpreter name */ |
942 |
while (!ctype(*cp, C_BLANK | C_NUL)) |
943 |
++cp; |
944 |
/* any arguments? */ |
945 |
if (*cp) { |
946 |
*cp++ = '\0'; |
947 |
/* skip spaces before arguments */ |
948 |
while (ctype(*cp, C_BLANK)) |
949 |
++cp; |
950 |
/* pass it all in ONE argument (historic reasons) */ |
951 |
if (*cp) |
952 |
*tp->args-- = (char *)cp; |
953 |
} |
954 |
#ifdef __OS2__ |
955 |
/* |
956 |
* On OS/2, the directory structure differs from normal |
957 |
* Unix, which can make many scripts whose shebang |
958 |
* hardcodes the path to an interpreter fail (and there |
959 |
* might be no /usr/bin/env); for user convenience, if |
960 |
* the specified interpreter is not usable, do a PATH |
961 |
* search to find it. |
962 |
*/ |
963 |
if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) { |
964 |
cp = search_path(_getname(sh), path, X_OK, NULL); |
965 |
if (cp) |
966 |
sh = cp; |
967 |
} |
968 |
#endif |
969 |
goto nomagic; |
970 |
noshebang: |
971 |
#ifndef MKSH_EBCDIC |
972 |
m = buf[0] << 8 | buf[1]; |
973 |
if (m == 0x7F45 && buf[2] == 'L' && buf[3] == 'F') |
974 |
errorf("%s: not executable: %d-bit ELF file", tp->str, |
975 |
32 * buf[4]); |
976 |
if ((m == /* OMAGIC */ 0407) || |
977 |
(m == /* NMAGIC */ 0410) || |
978 |
(m == /* ZMAGIC */ 0413) || |
979 |
(m == /* QMAGIC */ 0314) || |
980 |
(m == /* ECOFF_I386 */ 0x4C01) || |
981 |
(m == /* ECOFF_M68K */ 0x0150 || m == 0x5001) || |
982 |
(m == /* ECOFF_SH */ 0x0500 || m == 0x0005) || |
983 |
(m == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) || |
984 |
(m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) || |
985 |
(m == /* ksh93 */ 0x0B13) || (m == /* LZIP */ 0x4C5A) || |
986 |
(m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' && |
987 |
buf[4] == 'Z') || (m == /* 7zip */ 0x377A) || |
988 |
(m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) |
989 |
errorf("%s: not executable: magic %04X", tp->str, m); |
990 |
#endif |
991 |
#ifdef __OS2__ |
992 |
cp = _getext(tp->str); |
993 |
if (cp && (!stricmp(cp, ".cmd") || !stricmp(cp, ".bat"))) { |
994 |
/* execute .cmd and .bat with OS2_SHELL, usually CMD.EXE */ |
995 |
sh = str_val(global("OS2_SHELL")); |
996 |
*tp->args-- = "/c"; |
997 |
/* convert slahes to backslashes */ |
998 |
for (cp = tp->str; *cp; cp++) { |
999 |
if (*cp == '/') |
1000 |
*cp = '\\'; |
1001 |
} |
1002 |
} |
1003 |
#endif |
1004 |
nomagic: |
1005 |
; |
1006 |
} |
1007 |
#endif |
1008 |
args.ro = tp->args; |
1009 |
*args.ro = sh; |
1010 |
|
1011 |
cap.ro = ap; |
1012 |
execve(args.rw[0], args.rw, cap.rw); |
1013 |
|
1014 |
/* report both the programme that was run and the bogus interpreter */ |
1015 |
errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno)); |
1016 |
} |
1017 |
|
1018 |
/* actual 'builtin' built-in utility call is handled in comexec() */ |
1019 |
int |
1020 |
c_builtin(const char **wp) |
1021 |
{ |
1022 |
return (call_builtin(get_builtin(*wp), wp, Tbuiltin, false)); |
1023 |
} |
1024 |
|
1025 |
struct tbl * |
1026 |
get_builtin(const char *s) |
1027 |
{ |
1028 |
return (s && *s ? ktsearch(&builtins, s, hash(s)) : NULL); |
1029 |
} |
1030 |
|
1031 |
/* |
1032 |
* Search function tables for a function. If create set, a table entry |
1033 |
* is created if none is found. |
1034 |
*/ |
1035 |
struct tbl * |
1036 |
findfunc(const char *name, uint32_t h, bool create) |
1037 |
{ |
1038 |
struct block *l; |
1039 |
struct tbl *tp = NULL; |
1040 |
|
1041 |
for (l = e->loc; l; l = l->next) { |
1042 |
tp = ktsearch(&l->funs, name, h); |
1043 |
if (tp) |
1044 |
break; |
1045 |
if (!l->next && create) { |
1046 |
tp = ktenter(&l->funs, name, h); |
1047 |
tp->flag = DEFINED; |
1048 |
tp->type = CFUNC; |
1049 |
tp->val.t = NULL; |
1050 |
break; |
1051 |
} |
1052 |
} |
1053 |
return (tp); |
1054 |
} |
1055 |
|
1056 |
/* |
1057 |
* define function. Returns 1 if function is being undefined (t == 0) and |
1058 |
* function did not exist, returns 0 otherwise. |
1059 |
*/ |
1060 |
int |
1061 |
define(const char *name, struct op *t) |
1062 |
{ |
1063 |
uint32_t nhash; |
1064 |
struct tbl *tp; |
1065 |
bool was_set = false; |
1066 |
|
1067 |
nhash = hash(name); |
1068 |
|
1069 |
while (/* CONSTCOND */ 1) { |
1070 |
tp = findfunc(name, nhash, true); |
1071 |
|
1072 |
if (tp->flag & ISSET) |
1073 |
was_set = true; |
1074 |
/* |
1075 |
* If this function is currently being executed, we zap |
1076 |
* this table entry so findfunc() won't see it |
1077 |
*/ |
1078 |
if (tp->flag & FINUSE) { |
1079 |
tp->name[0] = '\0'; |
1080 |
/* ensure it won't be found */ |
1081 |
tp->flag &= ~DEFINED; |
1082 |
tp->flag |= FDELETE; |
1083 |
} else |
1084 |
break; |
1085 |
} |
1086 |
|
1087 |
if (tp->flag & ALLOC) { |
1088 |
tp->flag &= ~(ISSET|ALLOC|FKSH); |
1089 |
tfree(tp->val.t, tp->areap); |
1090 |
} |
1091 |
|
1092 |
if (t == NULL) { |
1093 |
/* undefine */ |
1094 |
ktdelete(tp); |
1095 |
return (was_set ? 0 : 1); |
1096 |
} |
1097 |
|
1098 |
tp->val.t = tcopy(t->left, tp->areap); |
1099 |
tp->flag |= (ISSET|ALLOC); |
1100 |
if (t->u.ksh_func) |
1101 |
tp->flag |= FKSH; |
1102 |
|
1103 |
return (0); |
1104 |
} |
1105 |
|
1106 |
/* |
1107 |
* add builtin |
1108 |
*/ |
1109 |
const char * |
1110 |
builtin(const char *name, int (*func) (const char **)) |
1111 |
{ |
1112 |
struct tbl *tp; |
1113 |
uint32_t flag = DEFINED; |
1114 |
|
1115 |
/* see if any flags should be set for this builtin */ |
1116 |
flags_loop: |
1117 |
switch (*name) { |
1118 |
case '=': |
1119 |
/* command does variable assignment */ |
1120 |
flag |= KEEPASN; |
1121 |
break; |
1122 |
case '*': |
1123 |
/* POSIX special builtin */ |
1124 |
flag |= SPEC_BI; |
1125 |
break; |
1126 |
case '~': |
1127 |
/* external utility overrides built-in utility, always */ |
1128 |
flag |= LOWER_BI; |
1129 |
/* FALLTHROUGH */ |
1130 |
case '!': |
1131 |
/* external utility overrides built-in utility, with flags */ |
1132 |
flag |= LOW_BI; |
1133 |
break; |
1134 |
case '-': |
1135 |
/* is declaration utility if argv[1] is one (POSIX: command) */ |
1136 |
flag |= DECL_FWDR; |
1137 |
break; |
1138 |
case '^': |
1139 |
/* is declaration utility (POSIX: export, readonly) */ |
1140 |
flag |= DECL_UTIL; |
1141 |
break; |
1142 |
default: |
1143 |
goto flags_seen; |
1144 |
} |
1145 |
++name; |
1146 |
goto flags_loop; |
1147 |
flags_seen: |
1148 |
|
1149 |
/* enter into the builtins hash table */ |
1150 |
tp = ktenter(&builtins, name, hash(name)); |
1151 |
tp->flag = flag; |
1152 |
tp->type = CSHELL; |
1153 |
tp->val.f = func; |
1154 |
|
1155 |
/* return name, for direct builtin call check in main.c */ |
1156 |
return (name); |
1157 |
} |
1158 |
|
1159 |
/* |
1160 |
* find command |
1161 |
* either function, hashed command, or built-in (in that order) |
1162 |
*/ |
1163 |
struct tbl * |
1164 |
findcom(const char *name, int flags) |
1165 |
{ |
1166 |
static struct tbl temp; |
1167 |
uint32_t h = hash(name); |
1168 |
struct tbl *tp = NULL, *tbi; |
1169 |
/* insert if not found */ |
1170 |
unsigned char insert = Flag(FTRACKALL); |
1171 |
/* for function autoloading */ |
1172 |
char *fpath; |
1173 |
union mksh_cchack npath; |
1174 |
|
1175 |
if (mksh_vdirsep(name)) { |
1176 |
insert = 0; |
1177 |
/* prevent FPATH search below */ |
1178 |
flags &= ~FC_FUNC; |
1179 |
goto Search; |
1180 |
} |
1181 |
tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL; |
1182 |
/* |
1183 |
* POSIX says special builtins first, then functions, then |
1184 |
* regular builtins, then search path... |
1185 |
*/ |
1186 |
if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) |
1187 |
tp = tbi; |
1188 |
if (!tp && (flags & FC_FUNC)) { |
1189 |
tp = findfunc(name, h, false); |
1190 |
if (tp && !(tp->flag & ISSET)) { |
1191 |
if ((fpath = str_val(global(TFPATH))) == null) { |
1192 |
tp->u.fpath = NULL; |
1193 |
tp->u2.errnov = ENOENT; |
1194 |
} else |
1195 |
tp->u.fpath = search_path(name, fpath, R_OK, |
1196 |
&tp->u2.errnov); |
1197 |
} |
1198 |
} |
1199 |
if (!tp && (flags & FC_NORMBI) && tbi) |
1200 |
tp = tbi; |
1201 |
if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { |
1202 |
tp = ktsearch(&taliases, name, h); |
1203 |
if (tp && (tp->flag & ISSET) && |
1204 |
ksh_access(tp->val.s, X_OK) != 0) { |
1205 |
if (tp->flag & ALLOC) { |
1206 |
tp->flag &= ~ALLOC; |
1207 |
afree(tp->val.s, APERM); |
1208 |
} |
1209 |
tp->flag &= ~ISSET; |
1210 |
} |
1211 |
} |
1212 |
|
1213 |
Search: |
1214 |
if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) && |
1215 |
(flags & FC_PATH)) { |
1216 |
if (!tp) { |
1217 |
if (insert && !(flags & FC_DEFPATH)) { |
1218 |
tp = ktenter(&taliases, name, h); |
1219 |
tp->type = CTALIAS; |
1220 |
} else { |
1221 |
tp = &temp; |
1222 |
tp->type = CEXEC; |
1223 |
} |
1224 |
/* make ~ISSET */ |
1225 |
tp->flag = DEFINED; |
1226 |
} |
1227 |
npath.ro = search_path(name, |
1228 |
(flags & FC_DEFPATH) ? def_path : path, |
1229 |
X_OK, &tp->u2.errnov); |
1230 |
if (npath.ro) { |
1231 |
strdupx(tp->val.s, npath.ro, APERM); |
1232 |
if (npath.ro != name) |
1233 |
afree(npath.rw, ATEMP); |
1234 |
tp->flag |= ISSET|ALLOC; |
1235 |
} else if ((flags & FC_FUNC) && |
1236 |
(fpath = str_val(global(TFPATH))) != null && |
1237 |
(npath.ro = search_path(name, fpath, R_OK, |
1238 |
&tp->u2.errnov)) != NULL) { |
1239 |
/* |
1240 |
* An undocumented feature of AT&T ksh is that |
1241 |
* it searches FPATH if a command is not found, |
1242 |
* even if the command hasn't been set up as an |
1243 |
* autoloaded function (ie, no typeset -uf). |
1244 |
*/ |
1245 |
tp = &temp; |
1246 |
tp->type = CFUNC; |
1247 |
/* make ~ISSET */ |
1248 |
tp->flag = DEFINED; |
1249 |
tp->u.fpath = npath.ro; |
1250 |
} |
1251 |
} |
1252 |
return (tp); |
1253 |
} |
1254 |
|
1255 |
/* |
1256 |
* flush executable commands with relative paths |
1257 |
* (just relative or all?) |
1258 |
*/ |
1259 |
void |
1260 |
flushcom(bool all) |
1261 |
{ |
1262 |
struct tbl *tp; |
1263 |
struct tstate ts; |
1264 |
|
1265 |
for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; ) |
1266 |
if ((tp->flag&ISSET) && (all || !mksh_abspath(tp->val.s))) { |
1267 |
if (tp->flag&ALLOC) { |
1268 |
tp->flag &= ~(ALLOC|ISSET); |
1269 |
afree(tp->val.s, APERM); |
1270 |
} |
1271 |
tp->flag &= ~ISSET; |
1272 |
} |
1273 |
} |
1274 |
|
1275 |
/* check if path is something we want to find */ |
1276 |
int |
1277 |
search_access(const char *fn, int mode) |
1278 |
{ |
1279 |
struct stat sb; |
1280 |
|
1281 |
if (stat(fn, &sb) < 0) |
1282 |
/* file does not exist */ |
1283 |
return (ENOENT); |
1284 |
/* LINTED use of access */ |
1285 |
if (access(fn, mode) < 0) { |
1286 |
/* file exists, but we can't access it */ |
1287 |
int eno; |
1288 |
|
1289 |
eno = errno; |
1290 |
return (eno ? eno : EACCES); |
1291 |
} |
1292 |
#ifdef __OS2__ |
1293 |
/* treat all files as executable on OS/2 */ |
1294 |
sb.st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; |
1295 |
#endif |
1296 |
if (mode == X_OK && (!S_ISREG(sb.st_mode) || |
1297 |
!(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) |
1298 |
/* access(2) may say root can execute everything */ |
1299 |
return (S_ISDIR(sb.st_mode) ? EISDIR : EACCES); |
1300 |
return (0); |
1301 |
} |
1302 |
|
1303 |
#ifdef __OS2__ |
1304 |
/* check if path is something we want to find, adding executable extensions */ |
1305 |
#define search_access(fn, mode) access_ex((search_access), (fn), (mode)) |
1306 |
#else |
1307 |
#define search_access(fn, mode) (search_access)((fn), (mode)) |
1308 |
#endif |
1309 |
|
1310 |
/* |
1311 |
* search for command with PATH |
1312 |
*/ |
1313 |
const char * |
1314 |
search_path(const char *name, const char *lpath, |
1315 |
/* R_OK or X_OK */ |
1316 |
int mode, |
1317 |
/* set if candidate found, but not suitable */ |
1318 |
int *errnop) |
1319 |
{ |
1320 |
const char *sp, *p; |
1321 |
char *xp; |
1322 |
XString xs; |
1323 |
size_t namelen; |
1324 |
int ec = 0, ev; |
1325 |
|
1326 |
if (mksh_vdirsep(name)) { |
1327 |
if ((ec = search_access(name, mode)) == 0) { |
1328 |
search_path_ok: |
1329 |
if (errnop) |
1330 |
*errnop = 0; |
1331 |
#ifndef __OS2__ |
1332 |
return (name); |
1333 |
#else |
1334 |
return (real_exec_name(name)); |
1335 |
#endif |
1336 |
} |
1337 |
goto search_path_err; |
1338 |
} |
1339 |
|
1340 |
namelen = strlen(name) + 1; |
1341 |
Xinit(xs, xp, 128, ATEMP); |
1342 |
|
1343 |
sp = lpath; |
1344 |
while (sp != NULL) { |
1345 |
xp = Xstring(xs, xp); |
1346 |
if (!(p = cstrchr(sp, MKSH_PATHSEPC))) |
1347 |
p = strnul(sp); |
1348 |
if (p != sp) { |
1349 |
XcheckN(xs, xp, p - sp); |
1350 |
memcpy(xp, sp, p - sp); |
1351 |
xp += p - sp; |
1352 |
#ifdef __OS2__ |
1353 |
if (xp > Xstring(xs, xp) && mksh_cdirsep(xp[-1])) |
1354 |
xp--; |
1355 |
#endif |
1356 |
*xp++ = '/'; |
1357 |
} |
1358 |
sp = p; |
1359 |
XcheckN(xs, xp, namelen); |
1360 |
memcpy(xp, name, namelen); |
1361 |
if ((ev = search_access(Xstring(xs, xp), mode)) == 0) { |
1362 |
name = Xclose(xs, xp + namelen); |
1363 |
goto search_path_ok; |
1364 |
} |
1365 |
/* accumulate non-ENOENT errors only */ |
1366 |
if (ev != ENOENT && ec == 0) |
1367 |
ec = ev; |
1368 |
if (*sp++ == '\0') |
1369 |
sp = NULL; |
1370 |
} |
1371 |
Xfree(xs, xp); |
1372 |
search_path_err: |
1373 |
if (errnop) |
1374 |
*errnop = ec ? ec : ENOENT; |
1375 |
return (NULL); |
1376 |
} |
1377 |
|
1378 |
static int |
1379 |
call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec) |
1380 |
{ |
1381 |
int rv; |
1382 |
|
1383 |
if (!tp) |
1384 |
internal_errorf(Tf_sD_s, where, wp[0]); |
1385 |
builtin_argv0 = wp[0]; |
1386 |
builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI)); |
1387 |
shf_reopen(1, SHF_WR, shl_stdout); |
1388 |
shl_stdout_ok = true; |
1389 |
ksh_getopt_reset(&builtin_opt, GF_ERROR); |
1390 |
rv = (*tp->val.f)(wp); |
1391 |
shf_flush(shl_stdout); |
1392 |
shl_stdout_ok = false; |
1393 |
builtin_argv0 = NULL; |
1394 |
builtin_spec = false; |
1395 |
return (rv); |
1396 |
} |
1397 |
|
1398 |
/* |
1399 |
* set up redirection, saving old fds in e->savefd |
1400 |
*/ |
1401 |
static int |
1402 |
iosetup(struct ioword *iop, struct tbl *tp) |
1403 |
{ |
1404 |
int u = -1; |
1405 |
char *cp = iop->ioname; |
1406 |
int iotype = iop->ioflag & IOTYPE; |
1407 |
bool do_open = true, do_close = false, do_fstat = false; |
1408 |
int flags = 0; |
1409 |
struct ioword iotmp; |
1410 |
struct stat statb; |
1411 |
|
1412 |
if (iotype != IOHERE) |
1413 |
cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0)); |
1414 |
|
1415 |
/* Used for tracing and error messages to print expanded cp */ |
1416 |
iotmp = *iop; |
1417 |
iotmp.ioname = (iotype == IOHERE) ? NULL : cp; |
1418 |
iotmp.ioflag |= IONAMEXP; |
1419 |
|
1420 |
if (Flag(FXTRACE)) { |
1421 |
change_xtrace(2, false); |
1422 |
fptreef(shl_xtrace, 0, Tft_R, &iotmp); |
1423 |
change_xtrace(1, false); |
1424 |
} |
1425 |
|
1426 |
switch (iotype) { |
1427 |
case IOREAD: |
1428 |
flags = O_RDONLY; |
1429 |
break; |
1430 |
|
1431 |
case IOCAT: |
1432 |
flags = O_WRONLY | O_APPEND | O_CREAT; |
1433 |
break; |
1434 |
|
1435 |
case IOWRITE: |
1436 |
if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB)) { |
1437 |
/* >file under set -C */ |
1438 |
if (stat(cp, &statb)) { |
1439 |
/* nonexistent file */ |
1440 |
flags = O_WRONLY | O_CREAT | O_EXCL; |
1441 |
} else if (S_ISREG(statb.st_mode)) { |
1442 |
/* regular file, refuse clobbering */ |
1443 |
goto clobber_refused; |
1444 |
} else { |
1445 |
/* |
1446 |
* allow redirections to things |
1447 |
* like /dev/null without error |
1448 |
*/ |
1449 |
flags = O_WRONLY; |
1450 |
/* but check again after opening */ |
1451 |
do_fstat = true; |
1452 |
} |
1453 |
} else { |
1454 |
/* >|file or set +C */ |
1455 |
flags = O_WRONLY | O_CREAT | O_TRUNC; |
1456 |
} |
1457 |
break; |
1458 |
|
1459 |
case IORDWR: |
1460 |
flags = O_RDWR | O_CREAT; |
1461 |
break; |
1462 |
|
1463 |
case IOHERE: |
1464 |
do_open = false; |
1465 |
/* herein() returns -2 if error has been printed */ |
1466 |
u = herein(iop, NULL); |
1467 |
/* cp may have wrong name */ |
1468 |
break; |
1469 |
|
1470 |
case IODUP: { |
1471 |
const char *emsg; |
1472 |
|
1473 |
do_open = false; |
1474 |
if (ksh_isdash(cp)) { |
1475 |
/* prevent error return below */ |
1476 |
u = 1009; |
1477 |
do_close = true; |
1478 |
} else if ((u = check_fd(cp, |
1479 |
X_OK | ((iop->ioflag & IORDUP) ? R_OK : W_OK), |
1480 |
&emsg)) < 0) { |
1481 |
char *sp; |
1482 |
|
1483 |
warningf(true, Tf_sD_s, |
1484 |
(sp = snptreef(NULL, 32, Tft_R, &iotmp)), emsg); |
1485 |
afree(sp, ATEMP); |
1486 |
return (-1); |
1487 |
} |
1488 |
if (u == (int)iop->unit) |
1489 |
/* "dup from" == "dup to" */ |
1490 |
return (0); |
1491 |
break; |
1492 |
} |
1493 |
} |
1494 |
|
1495 |
if (do_open) { |
1496 |
if (Flag(FRESTRICTED) && (flags & O_CREAT)) { |
1497 |
warningf(true, Tf_sD_s, cp, "restricted"); |
1498 |
return (-1); |
1499 |
} |
1500 |
u = binopen3(cp, flags, 0666); |
1501 |
if (do_fstat && u >= 0) { |
1502 |
/* prevent race conditions */ |
1503 |
if (fstat(u, &statb) || S_ISREG(statb.st_mode)) { |
1504 |
close(u); |
1505 |
clobber_refused: |
1506 |
u = -1; |
1507 |
errno = EEXIST; |
1508 |
} |
1509 |
} |
1510 |
} |
1511 |
if (u < 0) { |
1512 |
/* herein() may already have printed message */ |
1513 |
if (u == -1) { |
1514 |
u = errno; |
1515 |
warningf(true, Tf_cant_ss_s, |
1516 |
#if 0 |
1517 |
/* can't happen */ |
1518 |
iotype == IODUP ? "dup" : |
1519 |
#endif |
1520 |
(iotype == IOREAD || iotype == IOHERE) ? |
1521 |
Topen : Tcreate, cp, cstrerror(u)); |
1522 |
} |
1523 |
return (-1); |
1524 |
} |
1525 |
/* Do not save if it has already been redirected (i.e. "cat >x >y"). */ |
1526 |
if (e->savefd[iop->unit] == 0) { |
1527 |
/* If these are the same, it means unit was previously closed */ |
1528 |
if (u == (int)iop->unit) |
1529 |
e->savefd[iop->unit] = -1; |
1530 |
else |
1531 |
/* |
1532 |
* c_exec() assumes e->savefd[fd] set for any |
1533 |
* redirections. Ask savefd() not to close iop->unit; |
1534 |
* this allows error messages to be seen if iop->unit |
1535 |
* is 2; also means we can't lose the fd (eg, both |
1536 |
* dup2 below and dup2 in restfd() failing). |
1537 |
*/ |
1538 |
e->savefd[iop->unit] = savefd(iop->unit); |
1539 |
} |
1540 |
|
1541 |
if (do_close) |
1542 |
close(iop->unit); |
1543 |
else if (u != (int)iop->unit) { |
1544 |
if (ksh_dup2(u, iop->unit, true) < 0) { |
1545 |
int eno; |
1546 |
char *sp; |
1547 |
|
1548 |
eno = errno; |
1549 |
warningf(true, Tf_s_sD_s, Tredirection_dup, |
1550 |
(sp = snptreef(NULL, 32, Tft_R, &iotmp)), |
1551 |
cstrerror(eno)); |
1552 |
afree(sp, ATEMP); |
1553 |
if (iotype != IODUP) |
1554 |
close(u); |
1555 |
return (-1); |
1556 |
} |
1557 |
if (iotype != IODUP) |
1558 |
close(u); |
1559 |
/* |
1560 |
* Touching any co-process fd in an empty exec |
1561 |
* causes the shell to close its copies |
1562 |
*/ |
1563 |
else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { |
1564 |
if (iop->ioflag & IORDUP) |
1565 |
/* possible exec <&p */ |
1566 |
coproc_read_close(u); |
1567 |
else |
1568 |
/* possible exec >&p */ |
1569 |
coproc_write_close(u); |
1570 |
} |
1571 |
} |
1572 |
if (u == 2) |
1573 |
/* Clear any write errors */ |
1574 |
shf_reopen(2, SHF_WR, shl_out); |
1575 |
return (0); |
1576 |
} |
1577 |
|
1578 |
/* |
1579 |
* Process here documents by providing the content, either as |
1580 |
* result (globally allocated) string or in a temp file; if |
1581 |
* unquoted, the string is expanded first. |
1582 |
*/ |
1583 |
static int |
1584 |
hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf) |
1585 |
{ |
1586 |
const char * volatile ccp = iop->heredoc; |
1587 |
struct source *s, *osource; |
1588 |
|
1589 |
osource = source; |
1590 |
newenv(E_ERRH); |
1591 |
if (kshsetjmp(e->jbuf)) { |
1592 |
source = osource; |
1593 |
quitenv(shf); |
1594 |
/* special to iosetup(): don't print error */ |
1595 |
return (-2); |
1596 |
} |
1597 |
if (iop->ioflag & IOHERESTR) { |
1598 |
ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC); |
1599 |
} else if (sub) { |
1600 |
/* do substitutions on the content of heredoc */ |
1601 |
s = pushs(SSTRING, ATEMP); |
1602 |
s->start = s->str = ccp; |
1603 |
source = s; |
1604 |
if (yylex(sub) != LWORD) |
1605 |
internal_errorf("herein: yylex"); |
1606 |
source = osource; |
1607 |
ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC); |
1608 |
} |
1609 |
|
1610 |
if (resbuf == NULL) |
1611 |
shf_puts(ccp, shf); |
1612 |
else |
1613 |
strdupx(*resbuf, ccp, APERM); |
1614 |
|
1615 |
quitenv(NULL); |
1616 |
return (0); |
1617 |
} |
1618 |
|
1619 |
int |
1620 |
herein(struct ioword *iop, char **resbuf) |
1621 |
{ |
1622 |
int fd = -1; |
1623 |
struct shf *shf; |
1624 |
struct temp *h; |
1625 |
int i; |
1626 |
|
1627 |
/* lexer substitution flags */ |
1628 |
i = (iop->ioflag & IOEVAL) ? (ONEWORD | HEREDOC) : 0; |
1629 |
|
1630 |
/* skip all the fd setup if we just want the value */ |
1631 |
if (resbuf != NULL) |
1632 |
return (hereinval(iop, i, resbuf, NULL)); |
1633 |
|
1634 |
/* |
1635 |
* Create temp file to hold content (done before newenv |
1636 |
* so temp doesn't get removed too soon). |
1637 |
*/ |
1638 |
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps); |
1639 |
if (!(shf = h->shf) || (fd = binopen3(h->tffn, O_RDONLY, 0)) < 0) { |
1640 |
i = errno; |
1641 |
warningf(true, Tf_temp, |
1642 |
!shf ? Tcreate : Topen, h->tffn, cstrerror(i)); |
1643 |
if (shf) |
1644 |
shf_close(shf); |
1645 |
/* special to iosetup(): don't print error */ |
1646 |
return (-2); |
1647 |
} |
1648 |
|
1649 |
if (hereinval(iop, i, NULL, shf) == -2) { |
1650 |
close(fd); |
1651 |
/* special to iosetup(): don't print error */ |
1652 |
return (-2); |
1653 |
} |
1654 |
|
1655 |
if (shf_close(shf) == -1) { |
1656 |
i = errno; |
1657 |
close(fd); |
1658 |
warningf(true, Tf_temp, |
1659 |
Twrite, h->tffn, cstrerror(i)); |
1660 |
/* special to iosetup(): don't print error */ |
1661 |
return (-2); |
1662 |
} |
1663 |
|
1664 |
return (fd); |
1665 |
} |
1666 |
|
1667 |
/* |
1668 |
* ksh special - the select command processing section |
1669 |
* print the args in column form - assuming that we can |
1670 |
*/ |
1671 |
static const char * |
1672 |
do_selectargs(const char **ap, bool print_menu) |
1673 |
{ |
1674 |
static const char *read_args[] = { |
1675 |
Tread, "-r", "REPLY", NULL |
1676 |
}; |
1677 |
char *s; |
1678 |
int i, argct; |
1679 |
|
1680 |
for (argct = 0; ap[argct]; argct++) |
1681 |
; |
1682 |
while (/* CONSTCOND */ 1) { |
1683 |
/*- |
1684 |
* Menu is printed if |
1685 |
* - this is the first time around the select loop |
1686 |
* - the user enters a blank line |
1687 |
* - the REPLY parameter is empty |
1688 |
*/ |
1689 |
if (print_menu || !*str_val(global("REPLY"))) |
1690 |
pr_menu(ap); |
1691 |
shellf(Tf_s, str_val(global("PS3"))); |
1692 |
if (call_builtin(findcom(Tread, FC_BI), read_args, Tselect, |
1693 |
false)) |
1694 |
return (NULL); |
1695 |
if (*(s = str_val(global("REPLY")))) |
1696 |
return ((getn(s, &i) && i >= 1 && i <= argct) ? |
1697 |
ap[i - 1] : null); |
1698 |
print_menu = true; |
1699 |
} |
1700 |
} |
1701 |
|
1702 |
struct select_menu_info { |
1703 |
const char * const *args; |
1704 |
int num_width; |
1705 |
}; |
1706 |
|
1707 |
/* format a single select menu item */ |
1708 |
static void |
1709 |
select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) |
1710 |
{ |
1711 |
const struct select_menu_info *smi = |
1712 |
(const struct select_menu_info *)arg; |
1713 |
|
1714 |
shf_snprintf(buf, buflen, "%*u) %s", |
1715 |
smi->num_width, i + 1, smi->args[i]); |
1716 |
} |
1717 |
|
1718 |
/* |
1719 |
* print a select style menu |
1720 |
*/ |
1721 |
void |
1722 |
pr_menu(const char * const *ap) |
1723 |
{ |
1724 |
struct select_menu_info smi; |
1725 |
const char * const *pp; |
1726 |
size_t acols = 0, aocts = 0, i; |
1727 |
unsigned int n; |
1728 |
struct columnise_opts co; |
1729 |
|
1730 |
/* |
1731 |
* width/column calculations were done once and saved, but this |
1732 |
* means select can't be used recursively so we re-calculate |
1733 |
* each time (could save in a structure that is returned, but |
1734 |
* it's probably not worth the bother) |
1735 |
*/ |
1736 |
|
1737 |
/* |
1738 |
* get dimensions of the list |
1739 |
*/ |
1740 |
for (n = 0, pp = ap; *pp; n++, pp++) { |
1741 |
i = strlen(*pp); |
1742 |
if (i > aocts) |
1743 |
aocts = i; |
1744 |
i = utf_mbswidth(*pp); |
1745 |
if (i > acols) |
1746 |
acols = i; |
1747 |
} |
1748 |
|
1749 |
/* |
1750 |
* we will print an index of the form "%d) " in front of |
1751 |
* each entry, so get the maximum width of this |
1752 |
*/ |
1753 |
for (i = n, smi.num_width = 1; i >= 10; i /= 10) |
1754 |
smi.num_width++; |
1755 |
|
1756 |
smi.args = ap; |
1757 |
co.shf = shl_out; |
1758 |
co.linesep = '\n'; |
1759 |
co.prefcol = co.do_last = true; |
1760 |
print_columns(&co, n, select_fmt_entry, (void *)&smi, |
1761 |
smi.num_width + 2 + aocts, smi.num_width + 2 + acols); |
1762 |
} |
1763 |
|
1764 |
static void |
1765 |
plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg) |
1766 |
{ |
1767 |
strlcpy(buf, ((const char * const *)arg)[i], buflen); |
1768 |
} |
1769 |
|
1770 |
void |
1771 |
pr_list(struct columnise_opts *cop, char * const *ap) |
1772 |
{ |
1773 |
size_t acols = 0, aocts = 0, i; |
1774 |
unsigned int n; |
1775 |
char * const *pp; |
1776 |
|
1777 |
for (n = 0, pp = ap; *pp; n++, pp++) { |
1778 |
i = strlen(*pp); |
1779 |
if (i > aocts) |
1780 |
aocts = i; |
1781 |
i = utf_mbswidth(*pp); |
1782 |
if (i > acols) |
1783 |
acols = i; |
1784 |
} |
1785 |
|
1786 |
print_columns(cop, n, plain_fmt_entry, (const void *)ap, |
1787 |
aocts, acols); |
1788 |
} |
1789 |
|
1790 |
/* |
1791 |
* [[ ... ]] evaluation routines |
1792 |
*/ |
1793 |
|
1794 |
/* |
1795 |
* Test if the current token is a whatever. Accepts the current token if |
1796 |
* it is. Returns 0 if it is not, non-zero if it is (in the case of |
1797 |
* TM_UNOP and TM_BINOP, the returned value is a Test_op). |
1798 |
*/ |
1799 |
static Test_op |
1800 |
dbteste_isa(Test_env *te, Test_meta meta) |
1801 |
{ |
1802 |
Test_op ret = TO_NONOP; |
1803 |
bool uqword; |
1804 |
const char *p; |
1805 |
|
1806 |
if (!*te->pos.wp) |
1807 |
return (meta == TM_END ? TO_NONNULL : TO_NONOP); |
1808 |
|
1809 |
/* unquoted word? */ |
1810 |
for (p = *te->pos.wp; *p == CHAR; p += 2) |
1811 |
; |
1812 |
uqword = *p == EOS; |
1813 |
|
1814 |
if (meta == TM_UNOP || meta == TM_BINOP) { |
1815 |
if (uqword) { |
1816 |
/* longer than the longest operator */ |
1817 |
char buf[8]; |
1818 |
char *q = buf; |
1819 |
|
1820 |
p = *te->pos.wp; |
1821 |
while (*p++ == CHAR && |
1822 |
(size_t)(q - buf) < sizeof(buf) - 1) |
1823 |
*q++ = *p++; |
1824 |
*q = '\0'; |
1825 |
ret = test_isop(meta, buf); |
1826 |
} |
1827 |
} else if (meta == TM_END) |
1828 |
ret = TO_NONOP; |
1829 |
else |
1830 |
ret = (uqword && !strcmp(*te->pos.wp, |
1831 |
dbtest_tokens[(int)meta])) ? TO_NONNULL : TO_NONOP; |
1832 |
|
1833 |
/* Accept the token? */ |
1834 |
if (ret != TO_NONOP) |
1835 |
te->pos.wp++; |
1836 |
|
1837 |
return (ret); |
1838 |
} |
1839 |
|
1840 |
static const char * |
1841 |
dbteste_getopnd(Test_env *te, Test_op op, bool do_eval) |
1842 |
{ |
1843 |
const char *s = *te->pos.wp; |
1844 |
int flags = DOTILDE | DOSCALAR; |
1845 |
|
1846 |
if (!s) |
1847 |
return (NULL); |
1848 |
|
1849 |
te->pos.wp++; |
1850 |
|
1851 |
if (!do_eval) |
1852 |
return (null); |
1853 |
|
1854 |
if (op == TO_STEQL || op == TO_STNEQ) |
1855 |
flags |= DOPAT; |
1856 |
|
1857 |
return (evalstr(s, flags)); |
1858 |
} |
1859 |
|
1860 |
static void |
1861 |
dbteste_error(Test_env *te, int offset, const char *msg) |
1862 |
{ |
1863 |
te->flags |= TEF_ERROR; |
1864 |
internal_warningf("dbteste_error: %s (offset %d)", msg, offset); |
1865 |
} |