inherit flag wasen't set to zero after it was moved out of bss. i don't know what...
[unix-history] / usr / src / bin / sh / eval.c
CommitLineData
b3d7414a
KB
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
932f34f0 12static char sccsid[] = "@(#)eval.c 5.3 (Berkeley) %G%";
b3d7414a
KB
13#endif /* not lint */
14
15/*
16 * Evaluate a command.
17 */
18
19#include "shell.h"
20#include "nodes.h"
21#include "syntax.h"
22#include "expand.h"
23#include "parser.h"
24#include "jobs.h"
25#include "eval.h"
26#include "builtins.h"
27#include "options.h"
28#include "exec.h"
29#include "redir.h"
30#include "input.h"
31#include "output.h"
32#include "trap.h"
33#include "var.h"
34#include "memalloc.h"
35#include "error.h"
36#include "mystring.h"
37#include <signal.h>
38
39
40/* flags in argument to evaltree */
41#define EV_EXIT 01 /* exit after evaluating tree */
42#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
43#define EV_BACKCMD 04 /* command executing within back quotes */
44
45
46/* reasons for skipping commands (see comment on breakcmd routine) */
47#define SKIPBREAK 1
48#define SKIPCONT 2
49#define SKIPFUNC 3
50
51MKINIT int evalskip; /* set if we are skipping commands */
52STATIC int skipcount; /* number of levels to skip */
53MKINIT int loopnest; /* current loop nesting level */
54int funcnest; /* depth of function calls */
55
56
57char *commandname;
58struct strlist *cmdenviron;
59int exitstatus; /* exit status of last command */
60
61
62#ifdef __STDC__
63STATIC void evalloop(union node *);
64STATIC void evalfor(union node *);
65STATIC void evalcase(union node *, int);
66STATIC void evalsubshell(union node *, int);
67STATIC void expredir(union node *);
68STATIC void evalpipe(union node *);
69STATIC void evalcommand(union node *, int, struct backcmd *);
70STATIC void prehash(union node *);
71#else
72STATIC void evalloop();
73STATIC void evalfor();
74STATIC void evalcase();
75STATIC void evalsubshell();
76STATIC void expredir();
77STATIC void evalpipe();
78STATIC void evalcommand();
79STATIC void prehash();
80#endif
81
82
83
84/*
85 * Called to reset things after an exception.
86 */
87
88#ifdef mkinit
89INCLUDE "eval.h"
90
91RESET {
92 evalskip = 0;
93 loopnest = 0;
94 funcnest = 0;
95}
96
97SHELLPROC {
98 exitstatus = 0;
99}
100#endif
101
102
103
104/*
ddba57cd 105 * The eval commmand.
b3d7414a
KB
106 */
107
ddba57cd
MT
108evalcmd(argc, argv)
109 char **argv;
110{
111 char *p;
112 char *concat;
113 char **ap;
114
115 if (argc > 1) {
116 p = argv[1];
117 if (argc > 2) {
118 STARTSTACKSTR(concat);
119 ap = argv + 2;
120 for (;;) {
121 while (*p)
122 STPUTC(*p++, concat);
123 if ((p = *ap++) == NULL)
124 break;
125 STPUTC(' ', concat);
126 }
127 STPUTC('\0', concat);
128 p = grabstackstr(concat);
129 }
130 evalstring(p);
131 }
132 return exitstatus;
b3d7414a 133}
b3d7414a
KB
134
135
136/*
137 * Execute a command or commands contained in a string.
138 */
139
140void
141evalstring(s)
142 char *s;
143 {
144 union node *n;
145 struct stackmark smark;
146
147 setstackmark(&smark);
148 setinputstring(s, 1);
149 while ((n = parsecmd(0)) != NEOF) {
150 evaltree(n, 0);
151 popstackmark(&smark);
152 }
153 popfile();
154 popstackmark(&smark);
155}
156
157
158
159/*
160 * Evaluate a parse tree. The value is left in the global variable
161 * exitstatus.
162 */
163
164void
165evaltree(n, flags)
166 union node *n;
167 {
168 if (n == NULL) {
169 TRACE(("evaltree(NULL) called\n"));
170 return;
171 }
172 TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
173 switch (n->type) {
174 case NSEMI:
175 evaltree(n->nbinary.ch1, 0);
176 if (evalskip)
177 goto out;
178 evaltree(n->nbinary.ch2, flags);
179 break;
180 case NAND:
181 evaltree(n->nbinary.ch1, EV_TESTED);
182 if (evalskip || exitstatus != 0)
183 goto out;
184 evaltree(n->nbinary.ch2, flags);
185 break;
186 case NOR:
187 evaltree(n->nbinary.ch1, EV_TESTED);
188 if (evalskip || exitstatus == 0)
189 goto out;
190 evaltree(n->nbinary.ch2, flags);
191 break;
192 case NREDIR:
193 expredir(n->nredir.redirect);
194 redirect(n->nredir.redirect, REDIR_PUSH);
195 evaltree(n->nredir.n, flags);
196 popredir();
197 break;
198 case NSUBSHELL:
199 evalsubshell(n, flags);
200 break;
201 case NBACKGND:
202 evalsubshell(n, flags);
203 break;
932f34f0
MT
204 case NIF: {
205 int status = 0;
206
b3d7414a
KB
207 evaltree(n->nif.test, EV_TESTED);
208 if (evalskip)
209 goto out;
210 if (exitstatus == 0) {
211 evaltree(n->nif.ifpart, flags);
932f34f0 212 status = exitstatus;
b3d7414a
KB
213 } else if (n->nif.elsepart) {
214 evaltree(n->nif.elsepart, flags);
932f34f0 215 status = exitstatus;
b3d7414a 216 }
932f34f0 217 exitstatus = status;
b3d7414a 218 break;
932f34f0 219 }
b3d7414a
KB
220 case NWHILE:
221 case NUNTIL:
222 evalloop(n);
223 break;
224 case NFOR:
225 evalfor(n);
226 break;
227 case NCASE:
228 evalcase(n, flags);
229 break;
230 case NDEFUN:
231 defun(n->narg.text, n->narg.next);
232 exitstatus = 0;
233 break;
234 case NPIPE:
235 evalpipe(n);
236 break;
237 case NCMD:
238 evalcommand(n, flags, (struct backcmd *)NULL);
239 break;
240 default:
241 out1fmt("Node type = %d\n", n->type);
242 flushout(&output);
243 break;
244 }
245out:
246 if (pendingsigs)
247 dotrap();
248 if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
249 exitshell(exitstatus);
250}
251
252
253STATIC void
254evalloop(n)
255 union node *n;
256 {
257 int status;
258
259 loopnest++;
260 status = 0;
261 for (;;) {
262 evaltree(n->nbinary.ch1, EV_TESTED);
263 if (evalskip) {
264skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
265 evalskip = 0;
266 continue;
267 }
268 if (evalskip == SKIPBREAK && --skipcount <= 0)
269 evalskip = 0;
270 break;
271 }
272 if (n->type == NWHILE) {
273 if (exitstatus != 0)
274 break;
275 } else {
276 if (exitstatus == 0)
277 break;
278 }
279 evaltree(n->nbinary.ch2, 0);
280 status = exitstatus;
281 if (evalskip)
282 goto skipping;
283 }
284 loopnest--;
285 exitstatus = status;
286}
287
288
289
290STATIC void
291evalfor(n)
292 union node *n;
293 {
294 struct arglist arglist;
295 union node *argp;
296 struct strlist *sp;
297 struct stackmark smark;
298
299 setstackmark(&smark);
300 arglist.lastp = &arglist.list;
301 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
302 expandarg(argp, &arglist, 1);
303 if (evalskip)
304 goto out;
305 }
306 *arglist.lastp = NULL;
307
308 exitstatus = 0;
309 loopnest++;
310 for (sp = arglist.list ; sp ; sp = sp->next) {
311 setvar(n->nfor.var, sp->text, 0);
312 evaltree(n->nfor.body, 0);
313 if (evalskip) {
314 if (evalskip == SKIPCONT && --skipcount <= 0) {
315 evalskip = 0;
316 continue;
317 }
318 if (evalskip == SKIPBREAK && --skipcount <= 0)
319 evalskip = 0;
320 break;
321 }
322 }
323 loopnest--;
324out:
325 popstackmark(&smark);
326}
327
328
329
330STATIC void
331evalcase(n, flags)
332 union node *n;
333 {
334 union node *cp;
335 union node *patp;
336 struct arglist arglist;
337 struct stackmark smark;
338
339 setstackmark(&smark);
340 arglist.lastp = &arglist.list;
341 expandarg(n->ncase.expr, &arglist, 0);
342 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
343 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
344 if (casematch(patp, arglist.list->text)) {
345 if (evalskip == 0) {
346 evaltree(cp->nclist.body, flags);
347 }
348 goto out;
349 }
350 }
351 }
352out:
353 popstackmark(&smark);
354}
355
356
357
358/*
359 * Kick off a subshell to evaluate a tree.
360 */
361
362STATIC void
363evalsubshell(n, flags)
364 union node *n;
365 {
366 struct job *jp;
367 int backgnd = (n->type == NBACKGND);
368
369 expredir(n->nredir.redirect);
370 jp = makejob(n, 1);
371 if (forkshell(jp, n, backgnd) == 0) {
372 if (backgnd)
373 flags &=~ EV_TESTED;
374 redirect(n->nredir.redirect, 0);
375 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
376 }
377 if (! backgnd) {
378 INTOFF;
379 exitstatus = waitforjob(jp);
380 INTON;
381 }
382}
383
384
385
386/*
387 * Compute the names of the files in a redirection list.
388 */
389
390STATIC void
391expredir(n)
392 union node *n;
393 {
394 register union node *redir;
395
396 for (redir = n ; redir ; redir = redir->nfile.next) {
397 if (redir->type == NFROM
398 || redir->type == NTO
399 || redir->type == NAPPEND) {
400 struct arglist fn;
401 fn.lastp = &fn.list;
402 expandarg(redir->nfile.fname, &fn, 0);
403 redir->nfile.expfname = fn.list->text;
404 }
405 }
406}
407
408
409
410/*
411 * Evaluate a pipeline. All the processes in the pipeline are children
412 * of the process creating the pipeline. (This differs from some versions
413 * of the shell, which make the last process in a pipeline the parent
414 * of all the rest.)
415 */
416
417STATIC void
418evalpipe(n)
419 union node *n;
420 {
421 struct job *jp;
422 struct nodelist *lp;
423 int pipelen;
424 int prevfd;
425 int pip[2];
426
427 TRACE(("evalpipe(0x%x) called\n", (int)n));
428 pipelen = 0;
429 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
430 pipelen++;
431 INTOFF;
432 jp = makejob(n, pipelen);
433 prevfd = -1;
434 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
435 prehash(lp->n);
436 pip[1] = -1;
437 if (lp->next) {
438 if (pipe(pip) < 0) {
439 close(prevfd);
440 error("Pipe call failed");
441 }
442 }
443 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
444 INTON;
445 if (prevfd > 0) {
446 close(0);
447 copyfd(prevfd, 0);
448 close(prevfd);
449 }
450 if (pip[1] >= 0) {
451 close(pip[0]);
452 if (pip[1] != 1) {
453 close(1);
454 copyfd(pip[1], 1);
455 close(pip[1]);
456 }
457 }
458 evaltree(lp->n, EV_EXIT);
459 }
460 if (prevfd >= 0)
461 close(prevfd);
462 prevfd = pip[0];
463 close(pip[1]);
464 }
465 INTON;
466 if (n->npipe.backgnd == 0) {
467 INTOFF;
468 exitstatus = waitforjob(jp);
469 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
470 INTON;
471 }
472}
473
474
475
476/*
477 * Execute a command inside back quotes. If it's a builtin command, we
478 * want to save its output in a block obtained from malloc. Otherwise
479 * we fork off a subprocess and get the output of the command via a pipe.
480 * Should be called with interrupts off.
481 */
482
483void
484evalbackcmd(n, result)
485 union node *n;
486 struct backcmd *result;
487 {
488 int pip[2];
489 struct job *jp;
490 struct stackmark smark; /* unnecessary */
491
492 setstackmark(&smark);
493 result->fd = -1;
494 result->buf = NULL;
495 result->nleft = 0;
496 result->jp = NULL;
497 if (n->type == NCMD) {
498 evalcommand(n, EV_BACKCMD, result);
499 } else {
500 if (pipe(pip) < 0)
501 error("Pipe call failed");
502 jp = makejob(n, 1);
503 if (forkshell(jp, n, FORK_NOJOB) == 0) {
504 FORCEINTON;
505 close(pip[0]);
506 if (pip[1] != 1) {
507 close(1);
508 copyfd(pip[1], 1);
509 close(pip[1]);
510 }
511 evaltree(n, EV_EXIT);
512 }
513 close(pip[1]);
514 result->fd = pip[0];
515 result->jp = jp;
516 }
517 popstackmark(&smark);
518 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
519 result->fd, result->buf, result->nleft, result->jp));
520}
521
522
523
524/*
525 * Execute a simple command.
526 */
527
528STATIC void
529evalcommand(cmd, flags, backcmd)
530 union node *cmd;
531 struct backcmd *backcmd;
532 {
533 struct stackmark smark;
534 union node *argp;
535 struct arglist arglist;
536 struct arglist varlist;
537 char **argv;
538 int argc;
539 char **envp;
540 int varflag;
541 struct strlist *sp;
542 register char *p;
543 int mode;
544 int pip[2];
545 struct cmdentry cmdentry;
546 struct job *jp;
547 struct jmploc jmploc;
548 struct jmploc *volatile savehandler;
549 char *volatile savecmdname;
550 volatile struct shparam saveparam;
551 struct localvar *volatile savelocalvars;
552 volatile int e;
553 char *lastarg;
554
555 /* First expand the arguments. */
556 TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
557 setstackmark(&smark);
558 arglist.lastp = &arglist.list;
559 varlist.lastp = &varlist.list;
560 varflag = 1;
561 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
562 p = argp->narg.text;
563 if (varflag && is_name(*p)) {
564 do {
565 p++;
566 } while (is_in_name(*p));
567 if (*p == '=') {
568 expandarg(argp, &varlist, 0);
569 continue;
570 }
571 }
572 expandarg(argp, &arglist, 1);
573 varflag = 0;
574 }
575 *arglist.lastp = NULL;
576 *varlist.lastp = NULL;
577 expredir(cmd->ncmd.redirect);
578 argc = 0;
579 for (sp = arglist.list ; sp ; sp = sp->next)
580 argc++;
581 argv = stalloc(sizeof (char *) * (argc + 1));
582 for (sp = arglist.list ; sp ; sp = sp->next)
583 *argv++ = sp->text;
584 *argv = NULL;
585 lastarg = NULL;
586 if (iflag && funcnest == 0 && argc > 0)
587 lastarg = argv[-1];
588 argv -= argc;
589
590 /* Print the command if xflag is set. */
591 if (xflag) {
592 outc('+', &errout);
593 for (sp = varlist.list ; sp ; sp = sp->next) {
594 outc(' ', &errout);
595 out2str(sp->text);
596 }
597 for (sp = arglist.list ; sp ; sp = sp->next) {
598 outc(' ', &errout);
599 out2str(sp->text);
600 }
601 outc('\n', &errout);
602 flushout(&errout);
603 }
604
605 /* Now locate the command. */
606 if (argc == 0) {
607 cmdentry.cmdtype = CMDBUILTIN;
608 cmdentry.u.index = BLTINCMD;
609 } else {
610 find_command(argv[0], &cmdentry, 1);
611 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
612 exitstatus = 2;
613 flushout(&errout);
614 return;
615 }
616 /* implement the bltin builtin here */
617 if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
618 for (;;) {
619 argv++;
620 if (--argc == 0)
621 break;
622 if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
623 outfmt(&errout, "%s: not found\n", *argv);
624 exitstatus = 2;
625 flushout(&errout);
626 return;
627 }
628 if (cmdentry.u.index != BLTINCMD)
629 break;
630 }
631 }
632 }
633
634 /* Fork off a child process if necessary. */
635 if (cmd->ncmd.backgnd
636 || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
637 || (flags & EV_BACKCMD) != 0
638 && (cmdentry.cmdtype != CMDBUILTIN
639 || cmdentry.u.index == DOTCMD
640 || cmdentry.u.index == EVALCMD)) {
641 jp = makejob(cmd, 1);
642 mode = cmd->ncmd.backgnd;
643 if (flags & EV_BACKCMD) {
644 mode = FORK_NOJOB;
645 if (pipe(pip) < 0)
646 error("Pipe call failed");
647 }
648 if (forkshell(jp, cmd, mode) != 0)
649 goto parent; /* at end of routine */
650 if (flags & EV_BACKCMD) {
651 FORCEINTON;
652 close(pip[0]);
653 if (pip[1] != 1) {
654 close(1);
655 copyfd(pip[1], 1);
656 close(pip[1]);
657 }
658 }
659 flags |= EV_EXIT;
660 }
661
662 /* This is the child process if a fork occurred. */
663 /* Execute the command. */
664 if (cmdentry.cmdtype == CMDFUNCTION) {
665 trputs("Shell function: "); trargs(argv);
666 redirect(cmd->ncmd.redirect, REDIR_PUSH);
667 saveparam = shellparam;
668 shellparam.malloc = 0;
669 shellparam.nparam = argc - 1;
670 shellparam.p = argv + 1;
671 shellparam.optnext = NULL;
672 INTOFF;
673 savelocalvars = localvars;
674 localvars = NULL;
675 INTON;
676 if (setjmp(jmploc.loc)) {
677 if (exception == EXSHELLPROC)
678 freeparam((struct shparam *)&saveparam);
679 else {
680 freeparam(&shellparam);
681 shellparam = saveparam;
682 }
683 poplocalvars();
684 localvars = savelocalvars;
685 handler = savehandler;
686 longjmp(handler->loc, 1);
687 }
688 savehandler = handler;
689 handler = &jmploc;
690 for (sp = varlist.list ; sp ; sp = sp->next)
691 mklocal(sp->text);
692 funcnest++;
693 evaltree(cmdentry.u.func, 0);
694 funcnest--;
695 INTOFF;
696 poplocalvars();
697 localvars = savelocalvars;
698 freeparam(&shellparam);
699 shellparam = saveparam;
700 handler = savehandler;
701 popredir();
702 INTON;
703 if (evalskip == SKIPFUNC) {
704 evalskip = 0;
705 skipcount = 0;
706 }
707 if (flags & EV_EXIT)
708 exitshell(exitstatus);
709 } else if (cmdentry.cmdtype == CMDBUILTIN) {
710 trputs("builtin command: "); trargs(argv);
711 mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
712 if (flags == EV_BACKCMD) {
713 memout.nleft = 0;
714 memout.nextc = memout.buf;
715 memout.bufsize = 64;
716 mode |= REDIR_BACKQ;
717 }
718 redirect(cmd->ncmd.redirect, mode);
719 savecmdname = commandname;
720 cmdenviron = varlist.list;
721 e = -1;
722 if (setjmp(jmploc.loc)) {
723 e = exception;
724 exitstatus = (e == EXINT)? SIGINT+128 : 2;
725 goto cmddone;
726 }
727 savehandler = handler;
728 handler = &jmploc;
729 commandname = argv[0];
730 argptr = argv + 1;
731 optptr = NULL; /* initialize nextopt */
732 exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
733 flushall();
734cmddone:
735 out1 = &output;
736 out2 = &errout;
737 freestdout();
738 if (e != EXSHELLPROC) {
739 commandname = savecmdname;
740 if (flags & EV_EXIT) {
741 exitshell(exitstatus);
742 }
743 }
744 handler = savehandler;
745 if (e != -1) {
746 if (e != EXERROR || cmdentry.u.index == BLTINCMD
747 || cmdentry.u.index == DOTCMD
748 || cmdentry.u.index == EVALCMD
749 || cmdentry.u.index == EXECCMD)
750 exraise(e);
751 FORCEINTON;
752 }
753 if (cmdentry.u.index != EXECCMD)
754 popredir();
755 if (flags == EV_BACKCMD) {
756 backcmd->buf = memout.buf;
757 backcmd->nleft = memout.nextc - memout.buf;
758 memout.buf = NULL;
759 }
760 } else {
761 trputs("normal command: "); trargs(argv);
762 clearredir();
763 redirect(cmd->ncmd.redirect, 0);
764 if (varlist.list) {
765 p = stalloc(strlen(pathval()) + 1);
766 scopy(pathval(), p);
767 } else {
768 p = pathval();
769 }
770 for (sp = varlist.list ; sp ; sp = sp->next)
771 setvareq(sp->text, VEXPORT|VSTACK);
772 envp = environment();
773 shellexec(argv, envp, p, cmdentry.u.index);
774 /*NOTREACHED*/
775 }
776 goto out;
777
778parent: /* parent process gets here (if we forked) */
779 if (mode == 0) { /* argument to fork */
780 INTOFF;
781 exitstatus = waitforjob(jp);
782 INTON;
783 } else if (mode == 2) {
784 backcmd->fd = pip[0];
785 close(pip[1]);
786 backcmd->jp = jp;
787 }
788
789out:
790 if (lastarg)
791 setvar("_", lastarg, 0);
792 popstackmark(&smark);
793}
794
795
796
797/*
798 * Search for a command. This is called before we fork so that the
799 * location of the command will be available in the parent as well as
800 * the child. The check for "goodname" is an overly conservative
801 * check that the name will not be subject to expansion.
802 */
803
804STATIC void
805prehash(n)
806 union node *n;
807 {
808 struct cmdentry entry;
809
810 if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
811 find_command(n->ncmd.args->narg.text, &entry, 0);
812}
813
814
815
816/*
817 * Builtin commands. Builtin commands whose functions are closely
818 * tied to evaluation are implemented here.
819 */
820
821/*
822 * No command given, or a bltin command with no arguments. Set the
823 * specified variables.
824 */
825
826bltincmd(argc, argv) char **argv; {
827 listsetvar(cmdenviron);
828 return exitstatus;
829}
830
831
832/*
833 * Handle break and continue commands. Break, continue, and return are
834 * all handled by setting the evalskip flag. The evaluation routines
835 * above all check this flag, and if it is set they start skipping
836 * commands rather than executing them. The variable skipcount is
837 * the number of loops to break/continue, or the number of function
838 * levels to return. (The latter is always 1.) It should probably
839 * be an error to break out of more loops than exist, but it isn't
840 * in the standard shell so we don't make it one here.
841 */
842
843breakcmd(argc, argv) char **argv; {
844 int n;
845
846 n = 1;
847 if (argc > 1)
848 n = number(argv[1]);
849 if (n > loopnest)
850 n = loopnest;
851 if (n > 0) {
852 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
853 skipcount = n;
854 }
855 return 0;
856}
857
858
859/*
860 * The return command.
861 */
862
863returncmd(argc, argv) char **argv; {
864 int ret;
865
866 ret = exitstatus;
867 if (argc > 1)
868 ret = number(argv[1]);
869 if (funcnest) {
870 evalskip = SKIPFUNC;
871 skipcount = 1;
872 }
873 return ret;
874}
875
876
877truecmd(argc, argv) char **argv; {
878 return 0;
879}
880
881
882execcmd(argc, argv) char **argv; {
883 if (argc > 1) {
884 iflag = 0; /* exit on error */
885 setinteractive(0);
886#if JOBS
887 jflag = 0;
888 setjobctl(0);
889#endif
890 shellexec(argv + 1, environment(), pathval(), 0);
891
892 }
893 return 0;
894}