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