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