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