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 12 static char sccsid[] = "@(#)eval.c 5.3 (Berkeley) 04/12/91"; 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 51 MKINIT int evalskip; /* set if we are skipping commands */ 52 STATIC int skipcount; /* number of levels to skip */ 53 MKINIT int loopnest; /* current loop nesting level */ 54 int funcnest; /* depth of function calls */ 55 56 57 char *commandname; 58 struct strlist *cmdenviron; 59 int exitstatus; /* exit status of last command */ 60 61 62 #ifdef __STDC__ 63 STATIC void evalloop(union node *); 64 STATIC void evalfor(union node *); 65 STATIC void evalcase(union node *, int); 66 STATIC void evalsubshell(union node *, int); 67 STATIC void expredir(union node *); 68 STATIC void evalpipe(union node *); 69 STATIC void evalcommand(union node *, int, struct backcmd *); 70 STATIC void prehash(union node *); 71 #else 72 STATIC void evalloop(); 73 STATIC void evalfor(); 74 STATIC void evalcase(); 75 STATIC void evalsubshell(); 76 STATIC void expredir(); 77 STATIC void evalpipe(); 78 STATIC void evalcommand(); 79 STATIC void prehash(); 80 #endif 81 82 83 84 /* 85 * Called to reset things after an exception. 86 */ 87 88 #ifdef mkinit 89 INCLUDE "eval.h" 90 91 RESET { 92 evalskip = 0; 93 loopnest = 0; 94 funcnest = 0; 95 } 96 97 SHELLPROC { 98 exitstatus = 0; 99 } 100 #endif 101 102 103 104 /* 105 * The eval commmand. 106 */ 107 108 evalcmd(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; 133 } 134 135 136 /* 137 * Execute a command or commands contained in a string. 138 */ 139 140 void 141 evalstring(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 164 void 165 evaltree(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; 204 case NIF: { 205 int status = 0; 206 207 evaltree(n->nif.test, EV_TESTED); 208 if (evalskip) 209 goto out; 210 if (exitstatus == 0) { 211 evaltree(n->nif.ifpart, flags); 212 status = exitstatus; 213 } else if (n->nif.elsepart) { 214 evaltree(n->nif.elsepart, flags); 215 status = exitstatus; 216 } 217 exitstatus = status; 218 break; 219 } 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 } 245 out: 246 if (pendingsigs) 247 dotrap(); 248 if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED))) 249 exitshell(exitstatus); 250 } 251 252 253 STATIC void 254 evalloop(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) { 264 skipping: 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 290 STATIC void 291 evalfor(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--; 324 out: 325 popstackmark(&smark); 326 } 327 328 329 330 STATIC void 331 evalcase(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 } 352 out: 353 popstackmark(&smark); 354 } 355 356 357 358 /* 359 * Kick off a subshell to evaluate a tree. 360 */ 361 362 STATIC void 363 evalsubshell(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 390 STATIC void 391 expredir(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 417 STATIC void 418 evalpipe(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 483 void 484 evalbackcmd(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 528 STATIC void 529 evalcommand(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(); 734 cmddone: 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 778 parent: /* 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 789 out: 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 804 STATIC void 805 prehash(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 826 bltincmd(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 843 breakcmd(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 863 returncmd(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 877 truecmd(argc, argv) char **argv; { 878 return 0; 879 } 880 881 882 execcmd(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 } 895