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