1 /* $NetBSD: eval.c,v 1.72 2003/01/23 14:58:07 agc Exp $ */ 2 3 /*- 4 * Copyright (c) 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 43 #else 44 __RCSID("$NetBSD: eval.c,v 1.72 2003/01/23 14:58:07 agc Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 #include <stdlib.h> 49 #include <signal.h> 50 #include <stdio.h> 51 #include <unistd.h> 52 #include <sys/times.h> 53 #include <sys/param.h> 54 #include <sys/types.h> 55 #include <sys/wait.h> 56 #include <sys/sysctl.h> 57 58 /* 59 * Evaluate a command. 60 */ 61 62 #include "shell.h" 63 #include "nodes.h" 64 #include "syntax.h" 65 #include "expand.h" 66 #include "parser.h" 67 #include "jobs.h" 68 #include "eval.h" 69 #include "builtins.h" 70 #include "options.h" 71 #include "exec.h" 72 #include "redir.h" 73 #include "input.h" 74 #include "output.h" 75 #include "trap.h" 76 #include "var.h" 77 #include "memalloc.h" 78 #include "error.h" 79 #include "show.h" 80 #include "mystring.h" 81 #include "main.h" 82 #ifndef SMALL 83 #include "myhistedit.h" 84 #endif 85 86 87 /* flags in argument to evaltree */ 88 #define EV_EXIT 01 /* exit after evaluating tree */ 89 #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ 90 #define EV_BACKCMD 04 /* command executing within back quotes */ 91 92 int evalskip; /* set if we are skipping commands */ 93 STATIC int skipcount; /* number of levels to skip */ 94 MKINIT int loopnest; /* current loop nesting level */ 95 int funcnest; /* depth of function calls */ 96 97 98 char *commandname; 99 struct strlist *cmdenviron; 100 int exitstatus; /* exit status of last command */ 101 int back_exitstatus; /* exit status of backquoted command */ 102 103 104 STATIC void evalloop(union node *, int); 105 STATIC void evalfor(union node *, int); 106 STATIC void evalcase(union node *, int); 107 STATIC void evalsubshell(union node *, int); 108 STATIC void expredir(union node *); 109 STATIC void evalpipe(union node *); 110 STATIC void evalcommand(union node *, int, struct backcmd *); 111 STATIC void prehash(union node *); 112 113 114 /* 115 * Called to reset things after an exception. 116 */ 117 118 #ifdef mkinit 119 INCLUDE "eval.h" 120 121 RESET { 122 evalskip = 0; 123 loopnest = 0; 124 funcnest = 0; 125 } 126 127 SHELLPROC { 128 exitstatus = 0; 129 } 130 #endif 131 132 133 134 /* 135 * The eval commmand. 136 */ 137 138 int 139 evalcmd(int argc, char **argv) 140 { 141 char *p; 142 char *concat; 143 char **ap; 144 145 if (argc > 1) { 146 p = argv[1]; 147 if (argc > 2) { 148 STARTSTACKSTR(concat); 149 ap = argv + 2; 150 for (;;) { 151 while (*p) 152 STPUTC(*p++, concat); 153 if ((p = *ap++) == NULL) 154 break; 155 STPUTC(' ', concat); 156 } 157 STPUTC('\0', concat); 158 p = grabstackstr(concat); 159 } 160 evalstring(p, EV_TESTED); 161 } 162 return exitstatus; 163 } 164 165 166 /* 167 * Execute a command or commands contained in a string. 168 */ 169 170 void 171 evalstring(char *s, int flag) 172 { 173 union node *n; 174 struct stackmark smark; 175 176 setstackmark(&smark); 177 setinputstring(s, 1); 178 179 while ((n = parsecmd(0)) != NEOF) { 180 evaltree(n, flag); 181 popstackmark(&smark); 182 } 183 popfile(); 184 popstackmark(&smark); 185 } 186 187 188 189 /* 190 * Evaluate a parse tree. The value is left in the global variable 191 * exitstatus. 192 */ 193 194 void 195 evaltree(union node *n, int flags) 196 { 197 if (n == NULL) { 198 TRACE(("evaltree(NULL) called\n")); 199 exitstatus = 0; 200 goto out; 201 } 202 #ifndef SMALL 203 displayhist = 1; /* show history substitutions done with fc */ 204 #endif 205 TRACE(("pid %d, evaltree(%p: %d, %d) called\n", 206 getpid(), n, n->type, flags)); 207 switch (n->type) { 208 case NSEMI: 209 evaltree(n->nbinary.ch1, flags & EV_TESTED); 210 if (evalskip) 211 goto out; 212 evaltree(n->nbinary.ch2, flags); 213 break; 214 case NAND: 215 evaltree(n->nbinary.ch1, EV_TESTED); 216 if (evalskip || exitstatus != 0) 217 goto out; 218 evaltree(n->nbinary.ch2, flags | EV_TESTED); 219 break; 220 case NOR: 221 evaltree(n->nbinary.ch1, EV_TESTED); 222 if (evalskip || exitstatus == 0) 223 goto out; 224 evaltree(n->nbinary.ch2, flags | EV_TESTED); 225 break; 226 case NREDIR: 227 expredir(n->nredir.redirect); 228 redirect(n->nredir.redirect, REDIR_PUSH); 229 evaltree(n->nredir.n, flags); 230 popredir(); 231 break; 232 case NSUBSHELL: 233 evalsubshell(n, flags); 234 break; 235 case NBACKGND: 236 evalsubshell(n, flags); 237 break; 238 case NIF: { 239 evaltree(n->nif.test, EV_TESTED); 240 if (evalskip) 241 goto out; 242 if (exitstatus == 0) 243 evaltree(n->nif.ifpart, flags); 244 else if (n->nif.elsepart) 245 evaltree(n->nif.elsepart, flags); 246 else 247 exitstatus = 0; 248 break; 249 } 250 case NWHILE: 251 case NUNTIL: 252 evalloop(n, flags); 253 break; 254 case NFOR: 255 evalfor(n, flags); 256 break; 257 case NCASE: 258 evalcase(n, flags); 259 break; 260 case NDEFUN: 261 defun(n->narg.text, n->narg.next); 262 exitstatus = 0; 263 break; 264 case NNOT: 265 evaltree(n->nnot.com, EV_TESTED); 266 exitstatus = !exitstatus; 267 break; 268 case NPIPE: 269 evalpipe(n); 270 break; 271 case NCMD: 272 evalcommand(n, flags, (struct backcmd *)NULL); 273 break; 274 default: 275 out1fmt("Node type = %d\n", n->type); 276 flushout(&output); 277 break; 278 } 279 out: 280 if (pendingsigs) 281 dotrap(); 282 if ((flags & EV_EXIT) != 0) 283 exitshell(exitstatus); 284 } 285 286 287 STATIC void 288 evalloop(union node *n, int flags) 289 { 290 int status; 291 292 loopnest++; 293 status = 0; 294 for (;;) { 295 evaltree(n->nbinary.ch1, EV_TESTED); 296 if (evalskip) { 297 skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 298 evalskip = 0; 299 continue; 300 } 301 if (evalskip == SKIPBREAK && --skipcount <= 0) 302 evalskip = 0; 303 break; 304 } 305 if (n->type == NWHILE) { 306 if (exitstatus != 0) 307 break; 308 } else { 309 if (exitstatus == 0) 310 break; 311 } 312 evaltree(n->nbinary.ch2, flags & EV_TESTED); 313 status = exitstatus; 314 if (evalskip) 315 goto skipping; 316 } 317 loopnest--; 318 exitstatus = status; 319 } 320 321 322 323 STATIC void 324 evalfor(union node *n, int flags) 325 { 326 struct arglist arglist; 327 union node *argp; 328 struct strlist *sp; 329 struct stackmark smark; 330 int status = 0; 331 332 setstackmark(&smark); 333 arglist.lastp = &arglist.list; 334 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 335 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); 336 if (evalskip) 337 goto out; 338 } 339 *arglist.lastp = NULL; 340 341 loopnest++; 342 for (sp = arglist.list ; sp ; sp = sp->next) { 343 setvar(n->nfor.var, sp->text, 0); 344 evaltree(n->nfor.body, flags & EV_TESTED); 345 status = exitstatus; 346 if (evalskip) { 347 if (evalskip == SKIPCONT && --skipcount <= 0) { 348 evalskip = 0; 349 continue; 350 } 351 if (evalskip == SKIPBREAK && --skipcount <= 0) 352 evalskip = 0; 353 break; 354 } 355 } 356 loopnest--; 357 exitstatus = status; 358 out: 359 popstackmark(&smark); 360 } 361 362 363 364 STATIC void 365 evalcase(union node *n, int flags) 366 { 367 union node *cp; 368 union node *patp; 369 struct arglist arglist; 370 struct stackmark smark; 371 int status = 0; 372 373 setstackmark(&smark); 374 arglist.lastp = &arglist.list; 375 expandarg(n->ncase.expr, &arglist, EXP_TILDE); 376 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 377 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 378 if (casematch(patp, arglist.list->text)) { 379 if (evalskip == 0) { 380 evaltree(cp->nclist.body, flags); 381 status = exitstatus; 382 } 383 goto out; 384 } 385 } 386 } 387 out: 388 exitstatus = status; 389 popstackmark(&smark); 390 } 391 392 393 394 /* 395 * Kick off a subshell to evaluate a tree. 396 */ 397 398 STATIC void 399 evalsubshell(union node *n, int flags) 400 { 401 struct job *jp; 402 int backgnd = (n->type == NBACKGND); 403 404 expredir(n->nredir.redirect); 405 INTOFF; 406 jp = makejob(n, 1); 407 if (forkshell(jp, n, backgnd) == 0) { 408 INTON; 409 if (backgnd) 410 flags &=~ EV_TESTED; 411 redirect(n->nredir.redirect, 0); 412 /* never returns */ 413 evaltree(n->nredir.n, flags | EV_EXIT); 414 } 415 if (! backgnd) 416 exitstatus = waitforjob(jp); 417 INTON; 418 } 419 420 421 422 /* 423 * Compute the names of the files in a redirection list. 424 */ 425 426 STATIC void 427 expredir(union node *n) 428 { 429 union node *redir; 430 431 for (redir = n ; redir ; redir = redir->nfile.next) { 432 struct arglist fn; 433 fn.lastp = &fn.list; 434 switch (redir->type) { 435 case NFROMTO: 436 case NFROM: 437 case NTO: 438 case NCLOBBER: 439 case NAPPEND: 440 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 441 redir->nfile.expfname = fn.list->text; 442 break; 443 case NFROMFD: 444 case NTOFD: 445 if (redir->ndup.vname) { 446 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); 447 fixredir(redir, fn.list->text, 1); 448 } 449 break; 450 } 451 } 452 } 453 454 455 456 /* 457 * Evaluate a pipeline. All the processes in the pipeline are children 458 * of the process creating the pipeline. (This differs from some versions 459 * of the shell, which make the last process in a pipeline the parent 460 * of all the rest.) 461 */ 462 463 STATIC void 464 evalpipe(union node *n) 465 { 466 struct job *jp; 467 struct nodelist *lp; 468 int pipelen; 469 int prevfd; 470 int pip[2]; 471 472 TRACE(("evalpipe(0x%lx) called\n", (long)n)); 473 pipelen = 0; 474 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 475 pipelen++; 476 INTOFF; 477 jp = makejob(n, pipelen); 478 prevfd = -1; 479 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 480 prehash(lp->n); 481 pip[1] = -1; 482 if (lp->next) { 483 if (pipe(pip) < 0) { 484 close(prevfd); 485 error("Pipe call failed"); 486 } 487 } 488 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 489 INTON; 490 if (prevfd > 0) { 491 close(0); 492 copyfd(prevfd, 0); 493 close(prevfd); 494 } 495 if (pip[1] >= 0) { 496 close(pip[0]); 497 if (pip[1] != 1) { 498 close(1); 499 copyfd(pip[1], 1); 500 close(pip[1]); 501 } 502 } 503 evaltree(lp->n, EV_EXIT); 504 } 505 if (prevfd >= 0) 506 close(prevfd); 507 prevfd = pip[0]; 508 close(pip[1]); 509 } 510 if (n->npipe.backgnd == 0) { 511 exitstatus = waitforjob(jp); 512 TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 513 } 514 INTON; 515 } 516 517 518 519 /* 520 * Execute a command inside back quotes. If it's a builtin command, we 521 * want to save its output in a block obtained from malloc. Otherwise 522 * we fork off a subprocess and get the output of the command via a pipe. 523 * Should be called with interrupts off. 524 */ 525 526 void 527 evalbackcmd(union node *n, struct backcmd *result) 528 { 529 int pip[2]; 530 struct job *jp; 531 struct stackmark smark; /* unnecessary */ 532 533 setstackmark(&smark); 534 result->fd = -1; 535 result->buf = NULL; 536 result->nleft = 0; 537 result->jp = NULL; 538 if (n == NULL) { 539 goto out; 540 } 541 #ifdef notyet 542 /* 543 * For now we disable executing builtins in the same 544 * context as the shell, because we are not keeping 545 * enough state to recover from changes that are 546 * supposed only to affect subshells. eg. echo "`cd /`" 547 */ 548 if (n->type == NCMD) { 549 exitstatus = oexitstatus; 550 evalcommand(n, EV_BACKCMD, result); 551 } else 552 #endif 553 { 554 INTOFF; 555 if (pipe(pip) < 0) 556 error("Pipe call failed"); 557 jp = makejob(n, 1); 558 if (forkshell(jp, n, FORK_NOJOB) == 0) { 559 FORCEINTON; 560 close(pip[0]); 561 if (pip[1] != 1) { 562 close(1); 563 copyfd(pip[1], 1); 564 close(pip[1]); 565 } 566 eflag = 0; 567 evaltree(n, EV_EXIT); 568 /* NOTREACHED */ 569 } 570 close(pip[1]); 571 result->fd = pip[0]; 572 result->jp = jp; 573 INTON; 574 } 575 out: 576 popstackmark(&smark); 577 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 578 result->fd, result->buf, result->nleft, result->jp)); 579 } 580 581 static const char * 582 syspath(void) 583 { 584 static char *sys_path = NULL; 585 static int mib[] = {CTL_USER, USER_CS_PATH}; 586 size_t len; 587 588 if (sys_path == NULL) { 589 if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && 590 (sys_path = ckmalloc(len + 5)) != NULL && 591 sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { 592 memcpy(sys_path, "PATH=", 5); 593 } else { 594 ckfree(sys_path); 595 /* something to keep things happy */ 596 sys_path = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; 597 } 598 } 599 return sys_path; 600 } 601 602 static int 603 parse_command_args(int argc, char **argv, int *use_syspath) 604 { 605 int sv_argc = argc; 606 char *cp, c; 607 608 *use_syspath = 0; 609 610 for (;;) { 611 argv++; 612 if (--argc == 0) 613 break; 614 cp = *argv; 615 if (*cp++ != '-') 616 break; 617 if (*cp == '-' && cp[1] == 0) { 618 argv++; 619 argc--; 620 break; 621 } 622 while ((c = *cp++)) { 623 switch (c) { 624 case 'p': 625 *use_syspath = 1; 626 break; 627 default: 628 /* run 'typecmd' for other options */ 629 return 0; 630 } 631 } 632 } 633 return sv_argc - argc; 634 } 635 636 int vforked = 0; 637 638 /* 639 * Execute a simple command. 640 */ 641 642 STATIC void 643 evalcommand(union node *cmd, int flags, struct backcmd *backcmd) 644 { 645 struct stackmark smark; 646 union node *argp; 647 struct arglist arglist; 648 struct arglist varlist; 649 char **argv; 650 int argc; 651 char **envp; 652 int varflag; 653 struct strlist *sp; 654 int mode; 655 int pip[2]; 656 struct cmdentry cmdentry; 657 struct job *jp; 658 struct jmploc jmploc; 659 struct jmploc *volatile savehandler; 660 char *volatile savecmdname; 661 volatile struct shparam saveparam; 662 struct localvar *volatile savelocalvars; 663 volatile int e; 664 char *lastarg; 665 const char *path = pathval(); 666 int temp_path; 667 #if __GNUC__ 668 /* Avoid longjmp clobbering */ 669 (void) &argv; 670 (void) &argc; 671 (void) &lastarg; 672 (void) &flags; 673 #endif 674 675 vforked = 0; 676 /* First expand the arguments. */ 677 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); 678 setstackmark(&smark); 679 back_exitstatus = 0; 680 681 arglist.lastp = &arglist.list; 682 varflag = 1; 683 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 684 char *p = argp->narg.text; 685 if (varflag && is_name(*p)) { 686 do { 687 p++; 688 } while (is_in_name(*p)); 689 if (*p == '=') 690 continue; 691 } 692 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 693 varflag = 0; 694 } 695 *arglist.lastp = NULL; 696 697 expredir(cmd->ncmd.redirect); 698 699 varlist.lastp = &varlist.list; 700 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 701 char *p = argp->narg.text; 702 if (!is_name(*p)) 703 break; 704 do 705 p++; 706 while (is_in_name(*p)); 707 if (*p != '=') 708 break; 709 expandarg(argp, &varlist, EXP_VARTILDE); 710 } 711 *varlist.lastp = NULL; 712 713 argc = 0; 714 for (sp = arglist.list ; sp ; sp = sp->next) 715 argc++; 716 argv = stalloc(sizeof (char *) * (argc + 1)); 717 718 for (sp = arglist.list ; sp ; sp = sp->next) { 719 TRACE(("evalcommand arg: %s\n", sp->text)); 720 *argv++ = sp->text; 721 } 722 *argv = NULL; 723 lastarg = NULL; 724 if (iflag && funcnest == 0 && argc > 0) 725 lastarg = argv[-1]; 726 argv -= argc; 727 728 /* Print the command if xflag is set. */ 729 if (xflag) { 730 char sep = 0; 731 out2str(ps4val()); 732 for (sp = varlist.list ; sp ; sp = sp->next) { 733 if (sep != 0) 734 outc(sep, &errout); 735 out2str(sp->text); 736 sep = ' '; 737 } 738 for (sp = arglist.list ; sp ; sp = sp->next) { 739 if (sep != 0) 740 outc(sep, &errout); 741 out2str(sp->text); 742 sep = ' '; 743 } 744 outc('\n', &errout); 745 flushout(&errout); 746 } 747 748 /* Now locate the command. */ 749 if (argc == 0) { 750 cmdentry.cmdtype = CMDSPLBLTIN; 751 cmdentry.u.bltin = bltincmd; 752 } else { 753 static const char PATH[] = "PATH="; 754 int cmd_flags = DO_ERR; 755 756 /* 757 * Modify the command lookup path, if a PATH= assignment 758 * is present 759 */ 760 for (sp = varlist.list; sp; sp = sp->next) 761 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) 762 path = sp->text + sizeof(PATH) - 1; 763 764 do { 765 int argsused, use_syspath; 766 find_command(argv[0], &cmdentry, cmd_flags, path); 767 if (cmdentry.cmdtype == CMDUNKNOWN) { 768 exitstatus = 127; 769 flushout(&errout); 770 goto out; 771 } 772 773 /* implement the 'command' builtin here */ 774 if (cmdentry.cmdtype != CMDBUILTIN || 775 cmdentry.u.bltin != bltincmd) 776 break; 777 cmd_flags |= DO_NOFUNC; 778 argsused = parse_command_args(argc, argv, &use_syspath); 779 if (argsused == 0) { 780 /* use 'type' builting to display info */ 781 cmdentry.u.bltin = typecmd; 782 break; 783 } 784 argc -= argsused; 785 argv += argsused; 786 if (use_syspath) 787 path = syspath() + 5; 788 } while (argc != 0); 789 if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) 790 /* posix mandates that 'command <splbltin>' act as if 791 <splbltin> was a normal builtin */ 792 cmdentry.cmdtype = CMDBUILTIN; 793 } 794 795 /* Fork off a child process if necessary. */ 796 if (cmd->ncmd.backgnd 797 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) 798 || ((flags & EV_BACKCMD) != 0 799 && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) 800 || cmdentry.u.bltin == dotcmd 801 || cmdentry.u.bltin == evalcmd))) { 802 INTOFF; 803 jp = makejob(cmd, 1); 804 mode = cmd->ncmd.backgnd; 805 if (flags & EV_BACKCMD) { 806 mode = FORK_NOJOB; 807 if (pipe(pip) < 0) 808 error("Pipe call failed"); 809 } 810 #ifdef DO_SHAREDVFORK 811 /* It is essential that if DO_SHAREDVFORK is defined that the 812 * child's address space is actually shared with the parent as 813 * we rely on this. 814 */ 815 if (cmdentry.cmdtype == CMDNORMAL) { 816 pid_t pid; 817 818 savelocalvars = localvars; 819 localvars = NULL; 820 vforked = 1; 821 switch (pid = vfork()) { 822 case -1: 823 TRACE(("Vfork failed, errno=%d", errno)); 824 INTON; 825 error("Cannot vfork"); 826 break; 827 case 0: 828 /* Make sure that exceptions only unwind to 829 * after the vfork(2) 830 */ 831 if (setjmp(jmploc.loc)) { 832 if (exception == EXSHELLPROC) { 833 /* We can't progress with the vfork, 834 * so, set vforked = 2 so the parent 835 * knows, and _exit(); 836 */ 837 vforked = 2; 838 _exit(0); 839 } else { 840 _exit(exerrno); 841 } 842 } 843 savehandler = handler; 844 handler = &jmploc; 845 listmklocal(varlist.list, VEXPORT | VNOFUNC); 846 forkchild(jp, cmd, mode, vforked); 847 break; 848 default: 849 handler = savehandler; /* restore from vfork(2) */ 850 poplocalvars(); 851 localvars = savelocalvars; 852 if (vforked == 2) { 853 vforked = 0; 854 855 (void)waitpid(pid, NULL, 0); 856 /* We need to progress in a normal fork fashion */ 857 goto normal_fork; 858 } 859 vforked = 0; 860 forkparent(jp, cmd, mode, pid); 861 goto parent; 862 } 863 } else { 864 normal_fork: 865 #endif 866 if (forkshell(jp, cmd, mode) != 0) 867 goto parent; /* at end of routine */ 868 FORCEINTON; 869 #ifdef DO_SHAREDVFORK 870 } 871 #endif 872 if (flags & EV_BACKCMD) { 873 if (!vforked) { 874 FORCEINTON; 875 } 876 close(pip[0]); 877 if (pip[1] != 1) { 878 close(1); 879 copyfd(pip[1], 1); 880 close(pip[1]); 881 } 882 } 883 flags |= EV_EXIT; 884 } 885 886 /* This is the child process if a fork occurred. */ 887 /* Execute the command. */ 888 switch (cmdentry.cmdtype) { 889 case CMDFUNCTION: 890 #ifdef DEBUG 891 trputs("Shell function: "); trargs(argv); 892 #endif 893 redirect(cmd->ncmd.redirect, REDIR_PUSH); 894 saveparam = shellparam; 895 shellparam.malloc = 0; 896 shellparam.reset = 1; 897 shellparam.nparam = argc - 1; 898 shellparam.p = argv + 1; 899 shellparam.optnext = NULL; 900 INTOFF; 901 savelocalvars = localvars; 902 localvars = NULL; 903 INTON; 904 if (setjmp(jmploc.loc)) { 905 if (exception == EXSHELLPROC) { 906 freeparam((volatile struct shparam *) 907 &saveparam); 908 } else { 909 freeparam(&shellparam); 910 shellparam = saveparam; 911 } 912 poplocalvars(); 913 localvars = savelocalvars; 914 handler = savehandler; 915 longjmp(handler->loc, 1); 916 } 917 savehandler = handler; 918 handler = &jmploc; 919 listmklocal(varlist.list, 0); 920 /* stop shell blowing its stack */ 921 if (++funcnest > 1000) 922 error("too many nested function calls"); 923 evaltree(cmdentry.u.func, flags & EV_TESTED); 924 funcnest--; 925 INTOFF; 926 poplocalvars(); 927 localvars = savelocalvars; 928 freeparam(&shellparam); 929 shellparam = saveparam; 930 handler = savehandler; 931 popredir(); 932 INTON; 933 if (evalskip == SKIPFUNC) { 934 evalskip = 0; 935 skipcount = 0; 936 } 937 if (flags & EV_EXIT) 938 exitshell(exitstatus); 939 break; 940 941 case CMDBUILTIN: 942 case CMDSPLBLTIN: 943 #ifdef DEBUG 944 trputs("builtin command: "); trargs(argv); 945 #endif 946 mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; 947 if (flags == EV_BACKCMD) { 948 memout.nleft = 0; 949 memout.nextc = memout.buf; 950 memout.bufsize = 64; 951 mode |= REDIR_BACKQ; 952 } 953 e = -1; 954 savehandler = handler; 955 handler = &jmploc; 956 if (!setjmp(jmploc.loc)) { 957 /* We need to ensure the command hash table isn't 958 * corruped by temporary PATH assignments. 959 * However we must ensure the 'local' command works! 960 */ 961 if (path != pathval() && (cmdentry.u.bltin == hashcmd || 962 cmdentry.u.bltin == typecmd)) { 963 savelocalvars = localvars; 964 localvars = 0; 965 mklocal(path - 5 /* PATH= */, 0); 966 temp_path = 1; 967 } else 968 temp_path = 0; 969 redirect(cmd->ncmd.redirect, mode); 970 971 savecmdname = commandname; 972 /* exec is a special builtin, but needs this list... */ 973 cmdenviron = varlist.list; 974 /* we must check 'readonly' flag for all builtins */ 975 listsetvar(varlist.list, 976 cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); 977 commandname = argv[0]; 978 /* initialize nextopt */ 979 argptr = argv + 1; 980 optptr = NULL; 981 /* and getopt */ 982 optreset = 1; 983 optind = 1; 984 exitstatus = cmdentry.u.bltin(argc, argv); 985 } else { 986 e = exception; 987 exitstatus = e == EXINT ? SIGINT + 128 : 988 e == EXEXEC ? exerrno : 2; 989 } 990 handler = savehandler; 991 flushall(); 992 out1 = &output; 993 out2 = &errout; 994 freestdout(); 995 if (temp_path) { 996 poplocalvars(); 997 localvars = savelocalvars; 998 } 999 cmdenviron = NULL; 1000 if (e != EXSHELLPROC) { 1001 commandname = savecmdname; 1002 if (flags & EV_EXIT) 1003 exitshell(exitstatus); 1004 } 1005 if (e != -1) { 1006 if ((e != EXERROR && e != EXEXEC) 1007 || cmdentry.cmdtype == CMDSPLBLTIN) 1008 exraise(e); 1009 FORCEINTON; 1010 } 1011 if (cmdentry.u.bltin != execcmd) 1012 popredir(); 1013 if (flags == EV_BACKCMD) { 1014 backcmd->buf = memout.buf; 1015 backcmd->nleft = memout.nextc - memout.buf; 1016 memout.buf = NULL; 1017 } 1018 break; 1019 1020 default: 1021 #ifdef DEBUG 1022 trputs("normal command: "); trargs(argv); 1023 #endif 1024 clearredir(vforked); 1025 redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0); 1026 if (!vforked) 1027 for (sp = varlist.list ; sp ; sp = sp->next) 1028 setvareq(sp->text, VEXPORT|VSTACK); 1029 envp = environment(); 1030 shellexec(argv, envp, path, cmdentry.u.index, vforked); 1031 break; 1032 } 1033 goto out; 1034 1035 parent: /* parent process gets here (if we forked) */ 1036 if (mode == FORK_FG) { /* argument to fork */ 1037 exitstatus = waitforjob(jp); 1038 } else if (mode == FORK_NOJOB) { 1039 backcmd->fd = pip[0]; 1040 close(pip[1]); 1041 backcmd->jp = jp; 1042 } 1043 FORCEINTON; 1044 1045 out: 1046 if (lastarg) 1047 /* dsl: I think this is intended to be used to support 1048 * '_' in 'vi' command mode during line editing... 1049 * However I implemented that within libedit itself. 1050 */ 1051 setvar("_", lastarg, 0); 1052 popstackmark(&smark); 1053 1054 if (eflag && exitstatus && !(flags & EV_TESTED)) 1055 exitshell(exitstatus); 1056 } 1057 1058 1059 /* 1060 * Search for a command. This is called before we fork so that the 1061 * location of the command will be available in the parent as well as 1062 * the child. The check for "goodname" is an overly conservative 1063 * check that the name will not be subject to expansion. 1064 */ 1065 1066 STATIC void 1067 prehash(union node *n) 1068 { 1069 struct cmdentry entry; 1070 1071 if (n->type == NCMD && n->ncmd.args) 1072 if (goodname(n->ncmd.args->narg.text)) 1073 find_command(n->ncmd.args->narg.text, &entry, 0, 1074 pathval()); 1075 } 1076 1077 1078 1079 /* 1080 * Builtin commands. Builtin commands whose functions are closely 1081 * tied to evaluation are implemented here. 1082 */ 1083 1084 /* 1085 * No command given. 1086 */ 1087 1088 int 1089 bltincmd(int argc, char **argv) 1090 { 1091 /* 1092 * Preserve exitstatus of a previous possible redirection 1093 * as POSIX mandates 1094 */ 1095 return back_exitstatus; 1096 } 1097 1098 1099 /* 1100 * Handle break and continue commands. Break, continue, and return are 1101 * all handled by setting the evalskip flag. The evaluation routines 1102 * above all check this flag, and if it is set they start skipping 1103 * commands rather than executing them. The variable skipcount is 1104 * the number of loops to break/continue, or the number of function 1105 * levels to return. (The latter is always 1.) It should probably 1106 * be an error to break out of more loops than exist, but it isn't 1107 * in the standard shell so we don't make it one here. 1108 */ 1109 1110 int 1111 breakcmd(int argc, char **argv) 1112 { 1113 int n = argc > 1 ? number(argv[1]) : 1; 1114 1115 if (n > loopnest) 1116 n = loopnest; 1117 if (n > 0) { 1118 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 1119 skipcount = n; 1120 } 1121 return 0; 1122 } 1123 1124 1125 /* 1126 * The return command. 1127 */ 1128 1129 int 1130 returncmd(int argc, char **argv) 1131 { 1132 int ret = argc > 1 ? number(argv[1]) : exitstatus; 1133 1134 if (funcnest) { 1135 evalskip = SKIPFUNC; 1136 skipcount = 1; 1137 return ret; 1138 } 1139 else { 1140 /* Do what ksh does; skip the rest of the file */ 1141 evalskip = SKIPFILE; 1142 skipcount = 1; 1143 return ret; 1144 } 1145 } 1146 1147 1148 int 1149 falsecmd(int argc, char **argv) 1150 { 1151 return 1; 1152 } 1153 1154 1155 int 1156 truecmd(int argc, char **argv) 1157 { 1158 return 0; 1159 } 1160 1161 1162 int 1163 execcmd(int argc, char **argv) 1164 { 1165 if (argc > 1) { 1166 struct strlist *sp; 1167 1168 iflag = 0; /* exit on error */ 1169 mflag = 0; 1170 optschanged(); 1171 for (sp = cmdenviron; sp; sp = sp->next) 1172 setvareq(sp->text, VEXPORT|VSTACK); 1173 shellexec(argv + 1, environment(), pathval(), 0, 0); 1174 } 1175 return 0; 1176 } 1177 1178 static int 1179 conv_time(clock_t ticks, char *seconds) 1180 { 1181 static clock_t tpm = 0; 1182 clock_t mins; 1183 int i; 1184 1185 if (!tpm) 1186 tpm = sysconf(_SC_CLK_TCK) * 60; 1187 1188 mins = ticks / tpm; 1189 sprintf(seconds, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); 1190 1191 if (seconds[0] == '6' && seconds[1] == '0') { 1192 /* 59.99995 got rounded up... */ 1193 mins++; 1194 strcpy(seconds, "0.0"); 1195 return mins; 1196 } 1197 1198 /* suppress trailing zeros */ 1199 i = strlen(seconds) - 1; 1200 for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) 1201 seconds[i] = 0; 1202 return mins; 1203 } 1204 1205 int 1206 timescmd(int argc, char **argv) 1207 { 1208 struct tms tms; 1209 int u, s, cu, cs; 1210 char us[8], ss[8], cus[8], css[8]; 1211 1212 nextopt(""); 1213 1214 times(&tms); 1215 1216 u = conv_time( tms.tms_utime, us ); 1217 s = conv_time( tms.tms_stime, ss ); 1218 cu = conv_time( tms.tms_cutime, cus ); 1219 cs = conv_time( tms.tms_cstime, css ); 1220 1221 outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", 1222 u, us, s, ss, cu, cus, cs, css); 1223 1224 return 0; 1225 } 1226