add ! keyword
[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
1545e6b2 12static char sccsid[] = "@(#)eval.c 5.4 (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;
1545e6b2
MT
234 case NNOT:
235 evaltree(n->nnot.com, EV_TESTED);
236 exitstatus = !exitstatus;
237 break;
238
b3d7414a
KB
239 case NPIPE:
240 evalpipe(n);
241 break;
242 case NCMD:
243 evalcommand(n, flags, (struct backcmd *)NULL);
244 break;
245 default:
246 out1fmt("Node type = %d\n", n->type);
247 flushout(&output);
248 break;
249 }
250out:
251 if (pendingsigs)
252 dotrap();
253 if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
254 exitshell(exitstatus);
255}
256
257
258STATIC void
259evalloop(n)
260 union node *n;
261 {
262 int status;
263
264 loopnest++;
265 status = 0;
266 for (;;) {
267 evaltree(n->nbinary.ch1, EV_TESTED);
268 if (evalskip) {
269skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
270 evalskip = 0;
271 continue;
272 }
273 if (evalskip == SKIPBREAK && --skipcount <= 0)
274 evalskip = 0;
275 break;
276 }
277 if (n->type == NWHILE) {
278 if (exitstatus != 0)
279 break;
280 } else {
281 if (exitstatus == 0)
282 break;
283 }
284 evaltree(n->nbinary.ch2, 0);
285 status = exitstatus;
286 if (evalskip)
287 goto skipping;
288 }
289 loopnest--;
290 exitstatus = status;
291}
292
293
294
295STATIC void
296evalfor(n)
297 union node *n;
298 {
299 struct arglist arglist;
300 union node *argp;
301 struct strlist *sp;
302 struct stackmark smark;
303
304 setstackmark(&smark);
305 arglist.lastp = &arglist.list;
306 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
307 expandarg(argp, &arglist, 1);
308 if (evalskip)
309 goto out;
310 }
311 *arglist.lastp = NULL;
312
313 exitstatus = 0;
314 loopnest++;
315 for (sp = arglist.list ; sp ; sp = sp->next) {
316 setvar(n->nfor.var, sp->text, 0);
317 evaltree(n->nfor.body, 0);
318 if (evalskip) {
319 if (evalskip == SKIPCONT && --skipcount <= 0) {
320 evalskip = 0;
321 continue;
322 }
323 if (evalskip == SKIPBREAK && --skipcount <= 0)
324 evalskip = 0;
325 break;
326 }
327 }
328 loopnest--;
329out:
330 popstackmark(&smark);
331}
332
333
334
335STATIC void
336evalcase(n, flags)
337 union node *n;
338 {
339 union node *cp;
340 union node *patp;
341 struct arglist arglist;
342 struct stackmark smark;
343
344 setstackmark(&smark);
345 arglist.lastp = &arglist.list;
346 expandarg(n->ncase.expr, &arglist, 0);
347 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
348 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
349 if (casematch(patp, arglist.list->text)) {
350 if (evalskip == 0) {
351 evaltree(cp->nclist.body, flags);
352 }
353 goto out;
354 }
355 }
356 }
357out:
358 popstackmark(&smark);
359}
360
361
362
363/*
364 * Kick off a subshell to evaluate a tree.
365 */
366
367STATIC void
368evalsubshell(n, flags)
369 union node *n;
370 {
371 struct job *jp;
372 int backgnd = (n->type == NBACKGND);
373
374 expredir(n->nredir.redirect);
375 jp = makejob(n, 1);
376 if (forkshell(jp, n, backgnd) == 0) {
377 if (backgnd)
378 flags &=~ EV_TESTED;
379 redirect(n->nredir.redirect, 0);
380 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
381 }
382 if (! backgnd) {
383 INTOFF;
384 exitstatus = waitforjob(jp);
385 INTON;
386 }
387}
388
389
390
391/*
392 * Compute the names of the files in a redirection list.
393 */
394
395STATIC void
396expredir(n)
397 union node *n;
398 {
399 register union node *redir;
400
401 for (redir = n ; redir ; redir = redir->nfile.next) {
402 if (redir->type == NFROM
403 || redir->type == NTO
404 || redir->type == NAPPEND) {
405 struct arglist fn;
406 fn.lastp = &fn.list;
407 expandarg(redir->nfile.fname, &fn, 0);
408 redir->nfile.expfname = fn.list->text;
409 }
410 }
411}
412
413
414
415/*
416 * Evaluate a pipeline. All the processes in the pipeline are children
417 * of the process creating the pipeline. (This differs from some versions
418 * of the shell, which make the last process in a pipeline the parent
419 * of all the rest.)
420 */
421
422STATIC void
423evalpipe(n)
424 union node *n;
425 {
426 struct job *jp;
427 struct nodelist *lp;
428 int pipelen;
429 int prevfd;
430 int pip[2];
431
432 TRACE(("evalpipe(0x%x) called\n", (int)n));
433 pipelen = 0;
434 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
435 pipelen++;
436 INTOFF;
437 jp = makejob(n, pipelen);
438 prevfd = -1;
439 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
440 prehash(lp->n);
441 pip[1] = -1;
442 if (lp->next) {
443 if (pipe(pip) < 0) {
444 close(prevfd);
445 error("Pipe call failed");
446 }
447 }
448 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
449 INTON;
450 if (prevfd > 0) {
451 close(0);
452 copyfd(prevfd, 0);
453 close(prevfd);
454 }
455 if (pip[1] >= 0) {
456 close(pip[0]);
457 if (pip[1] != 1) {
458 close(1);
459 copyfd(pip[1], 1);
460 close(pip[1]);
461 }
462 }
463 evaltree(lp->n, EV_EXIT);
464 }
465 if (prevfd >= 0)
466 close(prevfd);
467 prevfd = pip[0];
468 close(pip[1]);
469 }
470 INTON;
471 if (n->npipe.backgnd == 0) {
472 INTOFF;
473 exitstatus = waitforjob(jp);
474 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
475 INTON;
476 }
477}
478
479
480
481/*
482 * Execute a command inside back quotes. If it's a builtin command, we
483 * want to save its output in a block obtained from malloc. Otherwise
484 * we fork off a subprocess and get the output of the command via a pipe.
485 * Should be called with interrupts off.
486 */
487
488void
489evalbackcmd(n, result)
490 union node *n;
491 struct backcmd *result;
492 {
493 int pip[2];
494 struct job *jp;
495 struct stackmark smark; /* unnecessary */
496
497 setstackmark(&smark);
498 result->fd = -1;
499 result->buf = NULL;
500 result->nleft = 0;
501 result->jp = NULL;
502 if (n->type == NCMD) {
503 evalcommand(n, EV_BACKCMD, result);
504 } else {
505 if (pipe(pip) < 0)
506 error("Pipe call failed");
507 jp = makejob(n, 1);
508 if (forkshell(jp, n, FORK_NOJOB) == 0) {
509 FORCEINTON;
510 close(pip[0]);
511 if (pip[1] != 1) {
512 close(1);
513 copyfd(pip[1], 1);
514 close(pip[1]);
515 }
516 evaltree(n, EV_EXIT);
517 }
518 close(pip[1]);
519 result->fd = pip[0];
520 result->jp = jp;
521 }
522 popstackmark(&smark);
523 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
524 result->fd, result->buf, result->nleft, result->jp));
525}
526
527
528
529/*
530 * Execute a simple command.
531 */
532
533STATIC void
534evalcommand(cmd, flags, backcmd)
535 union node *cmd;
536 struct backcmd *backcmd;
537 {
538 struct stackmark smark;
539 union node *argp;
540 struct arglist arglist;
541 struct arglist varlist;
542 char **argv;
543 int argc;
544 char **envp;
545 int varflag;
546 struct strlist *sp;
547 register char *p;
548 int mode;
549 int pip[2];
550 struct cmdentry cmdentry;
551 struct job *jp;
552 struct jmploc jmploc;
553 struct jmploc *volatile savehandler;
554 char *volatile savecmdname;
555 volatile struct shparam saveparam;
556 struct localvar *volatile savelocalvars;
557 volatile int e;
558 char *lastarg;
559
560 /* First expand the arguments. */
561 TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
562 setstackmark(&smark);
563 arglist.lastp = &arglist.list;
564 varlist.lastp = &varlist.list;
565 varflag = 1;
566 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
567 p = argp->narg.text;
568 if (varflag && is_name(*p)) {
569 do {
570 p++;
571 } while (is_in_name(*p));
572 if (*p == '=') {
573 expandarg(argp, &varlist, 0);
574 continue;
575 }
576 }
577 expandarg(argp, &arglist, 1);
578 varflag = 0;
579 }
580 *arglist.lastp = NULL;
581 *varlist.lastp = NULL;
582 expredir(cmd->ncmd.redirect);
583 argc = 0;
584 for (sp = arglist.list ; sp ; sp = sp->next)
585 argc++;
586 argv = stalloc(sizeof (char *) * (argc + 1));
587 for (sp = arglist.list ; sp ; sp = sp->next)
588 *argv++ = sp->text;
589 *argv = NULL;
590 lastarg = NULL;
591 if (iflag && funcnest == 0 && argc > 0)
592 lastarg = argv[-1];
593 argv -= argc;
594
595 /* Print the command if xflag is set. */
596 if (xflag) {
597 outc('+', &errout);
598 for (sp = varlist.list ; sp ; sp = sp->next) {
599 outc(' ', &errout);
600 out2str(sp->text);
601 }
602 for (sp = arglist.list ; sp ; sp = sp->next) {
603 outc(' ', &errout);
604 out2str(sp->text);
605 }
606 outc('\n', &errout);
607 flushout(&errout);
608 }
609
610 /* Now locate the command. */
611 if (argc == 0) {
612 cmdentry.cmdtype = CMDBUILTIN;
613 cmdentry.u.index = BLTINCMD;
614 } else {
615 find_command(argv[0], &cmdentry, 1);
616 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
617 exitstatus = 2;
618 flushout(&errout);
619 return;
620 }
621 /* implement the bltin builtin here */
622 if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
623 for (;;) {
624 argv++;
625 if (--argc == 0)
626 break;
627 if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
628 outfmt(&errout, "%s: not found\n", *argv);
629 exitstatus = 2;
630 flushout(&errout);
631 return;
632 }
633 if (cmdentry.u.index != BLTINCMD)
634 break;
635 }
636 }
637 }
638
639 /* Fork off a child process if necessary. */
640 if (cmd->ncmd.backgnd
641 || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
642 || (flags & EV_BACKCMD) != 0
643 && (cmdentry.cmdtype != CMDBUILTIN
644 || cmdentry.u.index == DOTCMD
645 || cmdentry.u.index == EVALCMD)) {
646 jp = makejob(cmd, 1);
647 mode = cmd->ncmd.backgnd;
648 if (flags & EV_BACKCMD) {
649 mode = FORK_NOJOB;
650 if (pipe(pip) < 0)
651 error("Pipe call failed");
652 }
653 if (forkshell(jp, cmd, mode) != 0)
654 goto parent; /* at end of routine */
655 if (flags & EV_BACKCMD) {
656 FORCEINTON;
657 close(pip[0]);
658 if (pip[1] != 1) {
659 close(1);
660 copyfd(pip[1], 1);
661 close(pip[1]);
662 }
663 }
664 flags |= EV_EXIT;
665 }
666
667 /* This is the child process if a fork occurred. */
668 /* Execute the command. */
669 if (cmdentry.cmdtype == CMDFUNCTION) {
670 trputs("Shell function: "); trargs(argv);
671 redirect(cmd->ncmd.redirect, REDIR_PUSH);
672 saveparam = shellparam;
673 shellparam.malloc = 0;
674 shellparam.nparam = argc - 1;
675 shellparam.p = argv + 1;
676 shellparam.optnext = NULL;
677 INTOFF;
678 savelocalvars = localvars;
679 localvars = NULL;
680 INTON;
681 if (setjmp(jmploc.loc)) {
682 if (exception == EXSHELLPROC)
683 freeparam((struct shparam *)&saveparam);
684 else {
685 freeparam(&shellparam);
686 shellparam = saveparam;
687 }
688 poplocalvars();
689 localvars = savelocalvars;
690 handler = savehandler;
691 longjmp(handler->loc, 1);
692 }
693 savehandler = handler;
694 handler = &jmploc;
695 for (sp = varlist.list ; sp ; sp = sp->next)
696 mklocal(sp->text);
697 funcnest++;
698 evaltree(cmdentry.u.func, 0);
699 funcnest--;
700 INTOFF;
701 poplocalvars();
702 localvars = savelocalvars;
703 freeparam(&shellparam);
704 shellparam = saveparam;
705 handler = savehandler;
706 popredir();
707 INTON;
708 if (evalskip == SKIPFUNC) {
709 evalskip = 0;
710 skipcount = 0;
711 }
712 if (flags & EV_EXIT)
713 exitshell(exitstatus);
714 } else if (cmdentry.cmdtype == CMDBUILTIN) {
715 trputs("builtin command: "); trargs(argv);
716 mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
717 if (flags == EV_BACKCMD) {
718 memout.nleft = 0;
719 memout.nextc = memout.buf;
720 memout.bufsize = 64;
721 mode |= REDIR_BACKQ;
722 }
723 redirect(cmd->ncmd.redirect, mode);
724 savecmdname = commandname;
725 cmdenviron = varlist.list;
726 e = -1;
727 if (setjmp(jmploc.loc)) {
728 e = exception;
729 exitstatus = (e == EXINT)? SIGINT+128 : 2;
730 goto cmddone;
731 }
732 savehandler = handler;
733 handler = &jmploc;
734 commandname = argv[0];
735 argptr = argv + 1;
736 optptr = NULL; /* initialize nextopt */
737 exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
738 flushall();
739cmddone:
740 out1 = &output;
741 out2 = &errout;
742 freestdout();
743 if (e != EXSHELLPROC) {
744 commandname = savecmdname;
745 if (flags & EV_EXIT) {
746 exitshell(exitstatus);
747 }
748 }
749 handler = savehandler;
750 if (e != -1) {
751 if (e != EXERROR || cmdentry.u.index == BLTINCMD
752 || cmdentry.u.index == DOTCMD
753 || cmdentry.u.index == EVALCMD
754 || cmdentry.u.index == EXECCMD)
755 exraise(e);
756 FORCEINTON;
757 }
758 if (cmdentry.u.index != EXECCMD)
759 popredir();
760 if (flags == EV_BACKCMD) {
761 backcmd->buf = memout.buf;
762 backcmd->nleft = memout.nextc - memout.buf;
763 memout.buf = NULL;
764 }
765 } else {
766 trputs("normal command: "); trargs(argv);
767 clearredir();
768 redirect(cmd->ncmd.redirect, 0);
769 if (varlist.list) {
770 p = stalloc(strlen(pathval()) + 1);
771 scopy(pathval(), p);
772 } else {
773 p = pathval();
774 }
775 for (sp = varlist.list ; sp ; sp = sp->next)
776 setvareq(sp->text, VEXPORT|VSTACK);
777 envp = environment();
778 shellexec(argv, envp, p, cmdentry.u.index);
779 /*NOTREACHED*/
780 }
781 goto out;
782
783parent: /* parent process gets here (if we forked) */
784 if (mode == 0) { /* argument to fork */
785 INTOFF;
786 exitstatus = waitforjob(jp);
787 INTON;
788 } else if (mode == 2) {
789 backcmd->fd = pip[0];
790 close(pip[1]);
791 backcmd->jp = jp;
792 }
793
794out:
795 if (lastarg)
796 setvar("_", lastarg, 0);
797 popstackmark(&smark);
798}
799
800
801
802/*
803 * Search for a command. This is called before we fork so that the
804 * location of the command will be available in the parent as well as
805 * the child. The check for "goodname" is an overly conservative
806 * check that the name will not be subject to expansion.
807 */
808
809STATIC void
810prehash(n)
811 union node *n;
812 {
813 struct cmdentry entry;
814
815 if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
816 find_command(n->ncmd.args->narg.text, &entry, 0);
817}
818
819
820
821/*
822 * Builtin commands. Builtin commands whose functions are closely
823 * tied to evaluation are implemented here.
824 */
825
826/*
827 * No command given, or a bltin command with no arguments. Set the
828 * specified variables.
829 */
830
831bltincmd(argc, argv) char **argv; {
832 listsetvar(cmdenviron);
833 return exitstatus;
834}
835
836
837/*
838 * Handle break and continue commands. Break, continue, and return are
839 * all handled by setting the evalskip flag. The evaluation routines
840 * above all check this flag, and if it is set they start skipping
841 * commands rather than executing them. The variable skipcount is
842 * the number of loops to break/continue, or the number of function
843 * levels to return. (The latter is always 1.) It should probably
844 * be an error to break out of more loops than exist, but it isn't
845 * in the standard shell so we don't make it one here.
846 */
847
848breakcmd(argc, argv) char **argv; {
849 int n;
850
851 n = 1;
852 if (argc > 1)
853 n = number(argv[1]);
854 if (n > loopnest)
855 n = loopnest;
856 if (n > 0) {
857 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
858 skipcount = n;
859 }
860 return 0;
861}
862
863
864/*
865 * The return command.
866 */
867
868returncmd(argc, argv) char **argv; {
869 int ret;
870
871 ret = exitstatus;
872 if (argc > 1)
873 ret = number(argv[1]);
874 if (funcnest) {
875 evalskip = SKIPFUNC;
876 skipcount = 1;
877 }
878 return ret;
879}
880
881
882truecmd(argc, argv) char **argv; {
883 return 0;
884}
885
886
887execcmd(argc, argv) char **argv; {
888 if (argc > 1) {
889 iflag = 0; /* exit on error */
890 setinteractive(0);
891#if JOBS
892 jflag = 0;
893 setjobctl(0);
894#endif
895 shellexec(argv + 1, environment(), pathval(), 0);
896
897 }
898 return 0;
899}