1 /* $NetBSD: eval.c,v 1.182 2021/04/04 13:24:07 kre 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. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 39 #else 40 __RCSID("$NetBSD: eval.c,v 1.182 2021/04/04 13:24:07 kre Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <stdbool.h> 45 #include <stdlib.h> 46 #include <signal.h> 47 #include <stdio.h> 48 #include <string.h> 49 #include <errno.h> 50 #include <limits.h> 51 #include <unistd.h> 52 #include <sys/fcntl.h> 53 #include <sys/stat.h> 54 #include <sys/times.h> 55 #include <sys/param.h> 56 #include <sys/types.h> 57 #include <sys/wait.h> 58 #include <sys/sysctl.h> 59 60 /* 61 * Evaluate a command. 62 */ 63 64 #include "shell.h" 65 #include "nodes.h" 66 #include "syntax.h" 67 #include "expand.h" 68 #include "parser.h" 69 #include "jobs.h" 70 #include "eval.h" 71 #include "builtins.h" 72 #include "options.h" 73 #include "exec.h" 74 #include "redir.h" 75 #include "input.h" 76 #include "output.h" 77 #include "trap.h" 78 #include "var.h" 79 #include "memalloc.h" 80 #include "error.h" 81 #include "show.h" 82 #include "mystring.h" 83 #include "main.h" 84 #ifndef SMALL 85 #include "nodenames.h" 86 #include "myhistedit.h" 87 #endif 88 89 90 STATIC struct skipsave s_k_i_p; 91 #define evalskip (s_k_i_p.state) 92 #define skipcount (s_k_i_p.count) 93 94 STATIC int loopnest; /* current loop nesting level */ 95 STATIC int funcnest; /* depth of function calls */ 96 STATIC int builtin_flags; /* evalcommand flags for builtins */ 97 /* 98 * Base function nesting level inside a dot command. Set to 0 initially 99 * and to (funcnest + 1) before every dot command to enable 100 * 1) detection of being in a file sourced by a dot command and 101 * 2) counting of function nesting in that file for the implementation 102 * of the return command. 103 * The value is reset to its previous value after the dot command. 104 */ 105 STATIC int dot_funcnest; 106 107 108 const char *commandname; 109 struct strlist *cmdenviron; 110 int exitstatus; /* exit status of last command */ 111 int back_exitstatus; /* exit status of backquoted command */ 112 113 114 STATIC void evalloop(union node *, int); 115 STATIC void evalfor(union node *, int); 116 STATIC void evalcase(union node *, int); 117 STATIC void evalsubshell(union node *, int); 118 STATIC void expredir(union node *); 119 STATIC void evalredir(union node *, int); 120 STATIC void evalpipe(union node *); 121 STATIC void evalcommand(union node *, int, struct backcmd *); 122 STATIC void prehash(union node *); 123 124 STATIC char *find_dot_file(char *); 125 126 /* 127 * Called to reset things after an exception. 128 */ 129 130 #ifdef mkinit 131 INCLUDE "eval.h" 132 133 RESET { 134 reset_eval(); 135 } 136 137 SHELLPROC { 138 exitstatus = 0; 139 } 140 #endif 141 142 void 143 reset_eval(void) 144 { 145 evalskip = SKIPNONE; 146 dot_funcnest = 0; 147 loopnest = 0; 148 funcnest = 0; 149 } 150 151 static int 152 sh_pipe(int fds[2]) 153 { 154 int nfd; 155 156 if (pipe(fds)) 157 return -1; 158 159 if (fds[0] < 3) { 160 nfd = fcntl(fds[0], F_DUPFD, 3); 161 if (nfd != -1) { 162 close(fds[0]); 163 fds[0] = nfd; 164 } 165 } 166 167 if (fds[1] < 3) { 168 nfd = fcntl(fds[1], F_DUPFD, 3); 169 if (nfd != -1) { 170 close(fds[1]); 171 fds[1] = nfd; 172 } 173 } 174 return 0; 175 } 176 177 178 /* 179 * The eval commmand. 180 */ 181 182 int 183 evalcmd(int argc, char **argv) 184 { 185 char *p; 186 char *concat; 187 char **ap; 188 189 if (argc > 1) { 190 p = argv[1]; 191 if (argc > 2) { 192 STARTSTACKSTR(concat); 193 ap = argv + 2; 194 for (;;) { 195 while (*p) 196 STPUTC(*p++, concat); 197 if ((p = *ap++) == NULL) 198 break; 199 STPUTC(' ', concat); 200 } 201 STPUTC('\0', concat); 202 p = grabstackstr(concat); 203 } 204 evalstring(p, builtin_flags & EV_TESTED); 205 } else 206 exitstatus = 0; 207 return exitstatus; 208 } 209 210 211 /* 212 * Execute a command or commands contained in a string. 213 */ 214 215 void 216 evalstring(char *s, int flag) 217 { 218 union node *n; 219 struct stackmark smark; 220 int last; 221 int any; 222 223 last = flag & EV_EXIT; 224 flag &= ~EV_EXIT; 225 226 setstackmark(&smark); 227 setinputstring(s, 1, line_number); 228 229 any = 0; /* to determine if exitstatus will have been set */ 230 while ((n = parsecmd(0)) != NEOF) { 231 XTRACE(DBG_EVAL, ("evalstring: "), showtree(n)); 232 if (n && nflag == 0) { 233 if (last && at_eof()) 234 evaltree(n, flag | EV_EXIT); 235 else 236 evaltree(n, flag); 237 any = 1; 238 if (evalskip) 239 break; 240 } 241 rststackmark(&smark); 242 } 243 popfile(); 244 popstackmark(&smark); 245 if (!any) 246 exitstatus = 0; 247 if (last) 248 exraise(EXEXIT); 249 } 250 251 252 253 /* 254 * Evaluate a parse tree. The value is left in the global variable 255 * exitstatus. 256 */ 257 258 void 259 evaltree(union node *n, int flags) 260 { 261 bool do_etest; 262 int sflags = flags & ~EV_EXIT; 263 union node *next; 264 struct stackmark smark; 265 266 do_etest = false; 267 if (n == NULL || nflag) { 268 VTRACE(DBG_EVAL, ("evaltree(%s) called\n", 269 n == NULL ? "NULL" : "-n")); 270 if (nflag == 0) 271 exitstatus = 0; 272 goto out2; 273 } 274 275 setstackmark(&smark); 276 do { 277 #ifndef SMALL 278 displayhist = 1; /* show history substitutions done with fc */ 279 #endif 280 next = NULL; 281 CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n", 282 getpid(), n, NODETYPENAME(n->type), n->type, flags)); 283 /* 284 if (n->type != NCMD && traps_invalid) 285 free_traps(); 286 */ 287 switch (n->type) { 288 case NSEMI: 289 evaltree(n->nbinary.ch1, sflags); 290 if (nflag || evalskip) 291 goto out1; 292 next = n->nbinary.ch2; 293 break; 294 case NAND: 295 evaltree(n->nbinary.ch1, EV_TESTED); 296 if (nflag || evalskip || exitstatus != 0) 297 goto out1; 298 next = n->nbinary.ch2; 299 break; 300 case NOR: 301 evaltree(n->nbinary.ch1, EV_TESTED); 302 if (nflag || evalskip || exitstatus == 0) 303 goto out1; 304 next = n->nbinary.ch2; 305 break; 306 case NREDIR: 307 if (traps_invalid) 308 free_traps(); 309 evalredir(n, flags); 310 break; 311 case NSUBSHELL: 312 evalsubshell(n, flags); 313 do_etest = !(flags & EV_TESTED); 314 break; 315 case NBACKGND: 316 if (traps_invalid) 317 free_traps(); 318 evalsubshell(n, flags); 319 break; 320 case NIF: { 321 if (traps_invalid) 322 free_traps(); 323 evaltree(n->nif.test, EV_TESTED); 324 if (nflag || evalskip) 325 goto out1; 326 if (exitstatus == 0) 327 next = n->nif.ifpart; 328 else if (n->nif.elsepart) 329 next = n->nif.elsepart; 330 else 331 exitstatus = 0; 332 break; 333 } 334 case NWHILE: 335 case NUNTIL: 336 if (traps_invalid) 337 free_traps(); 338 evalloop(n, sflags); 339 break; 340 case NFOR: 341 if (traps_invalid) 342 free_traps(); 343 evalfor(n, sflags); 344 break; 345 case NCASE: 346 if (traps_invalid) 347 free_traps(); 348 evalcase(n, sflags); 349 break; 350 case NDEFUN: 351 if (traps_invalid) 352 free_traps(); 353 CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n", 354 n->narg.text, n->narg.lineno, 355 fnline1 ? " LINENO=1" : "")); 356 defun(n->narg.text, n->narg.next, n->narg.lineno); 357 exitstatus = 0; 358 break; 359 case NNOT: 360 evaltree(n->nnot.com, EV_TESTED); 361 exitstatus = !exitstatus; 362 break; 363 case NDNOT: 364 evaltree(n->nnot.com, EV_TESTED); 365 if (exitstatus != 0) 366 exitstatus = 1; 367 break; 368 case NPIPE: 369 if (traps_invalid) 370 free_traps(); 371 evalpipe(n); 372 do_etest = !(flags & EV_TESTED); 373 break; 374 case NCMD: 375 evalcommand(n, flags, NULL); 376 do_etest = !(flags & EV_TESTED); 377 break; 378 default: 379 #ifdef NODETYPENAME 380 out1fmt("Node type = %d(%s)\n", 381 n->type, NODETYPENAME(n->type)); 382 #else 383 out1fmt("Node type = %d\n", n->type); 384 #endif 385 flushout(&output); 386 break; 387 } 388 n = next; 389 rststackmark(&smark); 390 } while(n != NULL); 391 out1: 392 popstackmark(&smark); 393 out2: 394 if (pendingsigs) 395 dotrap(); 396 if (eflag && exitstatus != 0 && do_etest) 397 exitshell(exitstatus); 398 if (flags & EV_EXIT) 399 exraise(EXEXIT); 400 } 401 402 403 STATIC void 404 evalloop(union node *n, int flags) 405 { 406 int status; 407 408 loopnest++; 409 status = 0; 410 411 CTRACE(DBG_EVAL, ("evalloop %s:", NODETYPENAME(n->type))); 412 VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1)); 413 VXTRACE(DBG_EVAL, ("evalloop do: "), showtree(n->nbinary.ch2)); 414 VTRACE(DBG_EVAL, ("evalloop done\n")); 415 CTRACE(DBG_EVAL, ("\n")); 416 417 for (;;) { 418 evaltree(n->nbinary.ch1, EV_TESTED); 419 if (nflag) 420 break; 421 if (evalskip) { 422 skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 423 evalskip = SKIPNONE; 424 continue; 425 } 426 if (evalskip == SKIPBREAK && --skipcount <= 0) 427 evalskip = SKIPNONE; 428 if (evalskip == SKIPFUNC || evalskip == SKIPFILE) 429 status = exitstatus; 430 break; 431 } 432 if (n->type == NWHILE) { 433 if (exitstatus != 0) 434 break; 435 } else { 436 if (exitstatus == 0) 437 break; 438 } 439 evaltree(n->nbinary.ch2, flags & EV_TESTED); 440 status = exitstatus; 441 if (evalskip) 442 goto skipping; 443 } 444 loopnest--; 445 exitstatus = status; 446 } 447 448 449 450 STATIC void 451 evalfor(union node *n, int flags) 452 { 453 struct arglist arglist; 454 union node *argp; 455 struct strlist *sp; 456 struct stackmark smark; 457 int status; 458 459 status = nflag ? exitstatus : 0; 460 461 setstackmark(&smark); 462 arglist.lastp = &arglist.list; 463 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 464 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 465 if (evalskip) 466 goto out; 467 } 468 *arglist.lastp = NULL; 469 470 loopnest++; 471 for (sp = arglist.list ; sp ; sp = sp->next) { 472 if (xflag) { 473 outxstr(expandstr(ps4val(), line_number)); 474 outxstr("for "); 475 outxstr(n->nfor.var); 476 outxc('='); 477 outxshstr(sp->text); 478 outxc('\n'); 479 flushout(outx); 480 } 481 482 setvar(n->nfor.var, sp->text, 0); 483 evaltree(n->nfor.body, flags & EV_TESTED); 484 status = exitstatus; 485 if (nflag) 486 break; 487 if (evalskip) { 488 if (evalskip == SKIPCONT && --skipcount <= 0) { 489 evalskip = SKIPNONE; 490 continue; 491 } 492 if (evalskip == SKIPBREAK && --skipcount <= 0) 493 evalskip = SKIPNONE; 494 break; 495 } 496 } 497 loopnest--; 498 exitstatus = status; 499 out: 500 popstackmark(&smark); 501 } 502 503 504 505 STATIC void 506 evalcase(union node *n, int flags) 507 { 508 union node *cp, *ncp; 509 union node *patp; 510 struct arglist arglist; 511 struct stackmark smark; 512 int status = 0; 513 514 setstackmark(&smark); 515 arglist.lastp = &arglist.list; 516 line_number = n->ncase.lineno; 517 expandarg(n->ncase.expr, &arglist, EXP_TILDE); 518 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { 519 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { 520 line_number = patp->narg.lineno; 521 if (casematch(patp, arglist.list->text)) { 522 while (cp != NULL && evalskip == 0 && 523 nflag == 0) { 524 if (cp->type == NCLISTCONT) 525 ncp = cp->nclist.next; 526 else 527 ncp = NULL; 528 line_number = cp->nclist.lineno; 529 evaltree(cp->nclist.body, flags); 530 status = exitstatus; 531 cp = ncp; 532 } 533 goto out; 534 } 535 } 536 } 537 out: 538 exitstatus = status; 539 popstackmark(&smark); 540 } 541 542 543 544 /* 545 * Kick off a subshell to evaluate a tree. 546 */ 547 548 STATIC void 549 evalsubshell(union node *n, int flags) 550 { 551 struct job *jp= NULL; 552 int backgnd = (n->type == NBACKGND); 553 554 expredir(n->nredir.redirect); 555 if (xflag && n->nredir.redirect) { 556 union node *rn; 557 558 outxstr(expandstr(ps4val(), line_number)); 559 outxstr("using redirections:"); 560 for (rn = n->nredir.redirect; rn; rn = rn->nfile.next) 561 (void) outredir(outx, rn, ' '); 562 outxstr(" do subshell ("/*)*/); 563 if (backgnd) 564 outxstr(/*(*/") &"); 565 outxc('\n'); 566 flushout(outx); 567 } 568 INTOFF; 569 if ((!backgnd && flags & EV_EXIT && !have_traps() && !anyjobs()) || 570 forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) { 571 if (backgnd) 572 flags &=~ EV_TESTED; 573 INTON; 574 redirect(n->nredir.redirect, REDIR_KEEP); 575 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 576 } else if (backgnd) 577 exitstatus = 0; 578 else 579 exitstatus = waitforjob(jp); 580 INTON; 581 582 if (!backgnd && xflag && n->nredir.redirect) { 583 outxstr(expandstr(ps4val(), line_number)); 584 outxstr(/*(*/") done subshell\n"); 585 flushout(outx); 586 } 587 } 588 589 590 591 /* 592 * Compute the names of the files in a redirection list. 593 */ 594 595 STATIC void 596 expredir(union node *n) 597 { 598 union node *redir; 599 600 for (redir = n ; redir ; redir = redir->nfile.next) { 601 struct arglist fn; 602 603 fn.lastp = &fn.list; 604 switch (redir->type) { 605 case NFROMTO: 606 case NFROM: 607 case NTO: 608 case NCLOBBER: 609 case NAPPEND: 610 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 611 redir->nfile.expfname = fn.list->text; 612 break; 613 case NFROMFD: 614 case NTOFD: 615 if (redir->ndup.vname) { 616 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); 617 fixredir(redir, fn.list->text, 1); 618 } 619 break; 620 } 621 } 622 } 623 624 /* 625 * Perform redirections for a compound command, and then do it (and restore) 626 */ 627 STATIC void 628 evalredir(union node *n, int flags) 629 { 630 struct jmploc jmploc; 631 struct jmploc * const savehandler = handler; 632 volatile int in_redirect = 1; 633 const char * volatile PS4 = NULL; 634 635 expredir(n->nredir.redirect); 636 637 if (xflag && n->nredir.redirect) { 638 union node *rn; 639 640 outxstr(PS4 = expandstr(ps4val(), line_number)); 641 outxstr("using redirections:"); 642 for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next) 643 (void) outredir(outx, rn, ' '); 644 outxstr(" do {\n"); /* } */ 645 flushout(outx); 646 } 647 648 if (setjmp(jmploc.loc)) { 649 int e; 650 651 handler = savehandler; 652 e = exception; 653 popredir(); 654 if (PS4 != NULL) { 655 outxstr(PS4); 656 /* { */ outxstr("} failed\n"); 657 flushout(outx); 658 } 659 if (e == EXERROR || e == EXEXEC) { 660 if (in_redirect) { 661 exitstatus = 2; 662 return; 663 } 664 } 665 longjmp(handler->loc, 1); 666 } else { 667 INTOFF; 668 handler = &jmploc; 669 redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP); 670 in_redirect = 0; 671 INTON; 672 evaltree(n->nredir.n, flags); 673 } 674 INTOFF; 675 handler = savehandler; 676 popredir(); 677 INTON; 678 679 if (PS4 != NULL) { 680 outxstr(PS4); 681 /* { */ outxstr("} done\n"); 682 flushout(outx); 683 } 684 } 685 686 687 /* 688 * Evaluate a pipeline. All the processes in the pipeline are children 689 * of the process creating the pipeline. (This differs from some versions 690 * of the shell, which make the last process in a pipeline the parent 691 * of all the rest.) 692 */ 693 694 STATIC void 695 evalpipe(union node *n) 696 { 697 struct job *jp; 698 struct nodelist *lp; 699 int pipelen; 700 int prevfd; 701 int pip[2]; 702 703 CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n)); 704 pipelen = 0; 705 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 706 pipelen++; 707 INTOFF; 708 jp = makejob(n, pipelen); 709 prevfd = -1; 710 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 711 prehash(lp->n); 712 pip[1] = -1; 713 if (lp->next) { 714 if (sh_pipe(pip) < 0) { 715 if (prevfd >= 0) 716 close(prevfd); 717 error("Pipe call failed: %s", strerror(errno)); 718 } 719 } 720 if (forkshell(jp, lp->n, 721 n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { 722 INTON; 723 if (prevfd > 0) 724 movefd(prevfd, 0); 725 if (pip[1] >= 0) { 726 close(pip[0]); 727 movefd(pip[1], 1); 728 } 729 evaltree(lp->n, EV_EXIT); 730 } 731 if (prevfd >= 0) 732 close(prevfd); 733 prevfd = pip[0]; 734 close(pip[1]); 735 } 736 if (n->npipe.backgnd == 0) { 737 exitstatus = waitforjob(jp); 738 CTRACE(DBG_EVAL, ("evalpipe: job done exit status %d\n", 739 exitstatus)); 740 } else 741 exitstatus = 0; 742 INTON; 743 } 744 745 746 747 /* 748 * Execute a command inside back quotes. If it's a builtin command, we 749 * want to save its output in a block obtained from malloc. Otherwise 750 * we fork off a subprocess and get the output of the command via a pipe. 751 * Should be called with interrupts off. 752 */ 753 754 void 755 evalbackcmd(union node *n, struct backcmd *result) 756 { 757 int pip[2]; 758 struct job *jp; 759 struct stackmark smark; /* unnecessary (because we fork) */ 760 761 result->fd = -1; 762 result->buf = NULL; 763 result->nleft = 0; 764 result->jp = NULL; 765 766 if (nflag || n == NULL) 767 goto out; 768 769 setstackmark(&smark); 770 771 #ifdef notyet 772 /* 773 * For now we disable executing builtins in the same 774 * context as the shell, because we are not keeping 775 * enough state to recover from changes that are 776 * supposed only to affect subshells. eg. echo "`cd /`" 777 */ 778 if (n->type == NCMD) { 779 exitstatus = oexitstatus; /* XXX o... no longer exists */ 780 evalcommand(n, EV_BACKCMD, result); 781 } else 782 #endif 783 { 784 INTOFF; 785 if (sh_pipe(pip) < 0) 786 error("Pipe call failed"); 787 jp = makejob(n, 1); 788 if (forkshell(jp, n, FORK_NOJOB) == 0) { 789 FORCEINTON; 790 close(pip[0]); 791 movefd(pip[1], 1); 792 evaltree(n, EV_EXIT); 793 /* NOTREACHED */ 794 } 795 close(pip[1]); 796 result->fd = pip[0]; 797 result->jp = jp; 798 INTON; 799 } 800 popstackmark(&smark); 801 out: 802 CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 803 result->fd, result->buf, result->nleft, result->jp)); 804 } 805 806 const char * 807 syspath(void) 808 { 809 static char *sys_path = NULL; 810 static int mib[] = {CTL_USER, USER_CS_PATH}; 811 static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; 812 size_t len; 813 814 if (sys_path == NULL) { 815 if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && 816 (sys_path = ckmalloc(len + 5)) != NULL && 817 sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { 818 memcpy(sys_path, "PATH=", 5); 819 } else { 820 ckfree(sys_path); 821 /* something to keep things happy */ 822 sys_path = def_path; 823 } 824 } 825 return sys_path; 826 } 827 828 static int 829 parse_command_args(int argc, char **argv, int *use_syspath) 830 { 831 int sv_argc = argc; 832 char *cp, c; 833 834 *use_syspath = 0; 835 836 for (;;) { 837 argv++; 838 if (--argc == 0) 839 break; 840 cp = *argv; 841 if (*cp++ != '-') 842 break; 843 if (*cp == '-' && cp[1] == 0) { 844 argv++; 845 argc--; 846 break; 847 } 848 while ((c = *cp++)) { 849 switch (c) { 850 case 'p': 851 *use_syspath = 1; 852 break; 853 default: 854 /* run 'typecmd' for other options */ 855 return 0; 856 } 857 } 858 } 859 return sv_argc - argc; 860 } 861 862 int vforked = 0; 863 864 /* 865 * Execute a simple command. 866 */ 867 868 STATIC void 869 evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) 870 { 871 struct stackmark smark; 872 union node *argp; 873 struct arglist arglist; 874 struct arglist varlist; 875 volatile int flags = flgs; 876 char ** volatile argv; 877 volatile int argc; 878 char **envp; 879 int varflag; 880 struct strlist *sp; 881 volatile int mode; 882 int pip[2]; 883 struct cmdentry cmdentry; 884 struct job * volatile jp; 885 struct jmploc jmploc; 886 struct jmploc *volatile savehandler = NULL; 887 const char *volatile savecmdname; 888 volatile struct shparam saveparam; 889 struct localvar *volatile savelocalvars; 890 struct parsefile *volatile savetopfile; 891 volatile int e; 892 char * volatile lastarg; 893 const char * volatile path = pathval(); 894 volatile int temp_path; 895 const int savefuncline = funclinebase; 896 const int savefuncabs = funclineabs; 897 volatile int cmd_flags = 0; 898 899 vforked = 0; 900 /* First expand the arguments. */ 901 CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags, 902 cmd->ncmd.args ? cmd->ncmd.args->narg.text : "")); 903 setstackmark(&smark); 904 back_exitstatus = 0; 905 906 line_number = cmd->ncmd.lineno; 907 908 arglist.lastp = &arglist.list; 909 varflag = 1; 910 /* Expand arguments, ignoring the initial 'name=value' ones */ 911 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 912 if (varflag && isassignment(argp->narg.text)) 913 continue; 914 varflag = 0; 915 line_number = argp->narg.lineno; 916 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 917 } 918 *arglist.lastp = NULL; 919 920 expredir(cmd->ncmd.redirect); 921 922 /* Now do the initial 'name=value' ones we skipped above */ 923 varlist.lastp = &varlist.list; 924 for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 925 line_number = argp->narg.lineno; 926 if (!isassignment(argp->narg.text)) 927 break; 928 expandarg(argp, &varlist, EXP_VARTILDE); 929 } 930 *varlist.lastp = NULL; 931 932 argc = 0; 933 for (sp = arglist.list ; sp ; sp = sp->next) 934 argc++; 935 argv = stalloc(sizeof (char *) * (argc + 1)); 936 937 for (sp = arglist.list ; sp ; sp = sp->next) { 938 VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text)); 939 *argv++ = sp->text; 940 } 941 *argv = NULL; 942 lastarg = NULL; 943 if (iflag && funcnest == 0 && argc > 0) 944 lastarg = argv[-1]; 945 argv -= argc; 946 947 /* Print the command if xflag is set. */ 948 if (xflag) { 949 char sep = 0; 950 union node *rn; 951 952 outxstr(expandstr(ps4val(), line_number)); 953 for (sp = varlist.list ; sp ; sp = sp->next) { 954 char *p; 955 956 if (sep != 0) 957 outxc(sep); 958 959 /* 960 * The "var=" part should not be quoted, regardless 961 * of the value, or it would not represent an 962 * assignment, but rather a command 963 */ 964 p = strchr(sp->text, '='); 965 if (p != NULL) { 966 *p = '\0'; /*XXX*/ 967 outxshstr(sp->text); 968 outxc('='); 969 *p++ = '='; /*XXX*/ 970 } else 971 p = sp->text; 972 outxshstr(p); 973 sep = ' '; 974 } 975 for (sp = arglist.list ; sp ; sp = sp->next) { 976 if (sep != 0) 977 outxc(sep); 978 outxshstr(sp->text); 979 sep = ' '; 980 } 981 for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next) 982 if (outredir(outx, rn, sep)) 983 sep = ' '; 984 outxc('\n'); 985 flushout(outx); 986 } 987 988 /* Now locate the command. */ 989 if (argc == 0) { 990 /* 991 * the empty command begins as a normal builtin, and 992 * remains that way while redirects are processed, then 993 * will become special before we get to doing the 994 * var assigns. 995 */ 996 cmdentry.cmdtype = CMDBUILTIN; 997 cmdentry.u.bltin = bltincmd; 998 VTRACE(DBG_CMDS, ("No command name, assume \"comamnd\"\n")); 999 } else { 1000 static const char PATH[] = "PATH="; 1001 1002 /* 1003 * Modify the command lookup path, if a PATH= assignment 1004 * is present 1005 */ 1006 for (sp = varlist.list; sp; sp = sp->next) 1007 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) 1008 path = sp->text + sizeof(PATH) - 1; 1009 1010 do { 1011 int argsused, use_syspath; 1012 1013 find_command(argv[0], &cmdentry, cmd_flags, path); 1014 VTRACE(DBG_CMDS, ("Command %s type %d\n", argv[0], 1015 cmdentry.cmdtype)); 1016 #if 0 1017 /* 1018 * This short circuits all of the processing that 1019 * should be done (including processing the 1020 * redirects), so just don't ... 1021 * 1022 * (eventually this whole #if'd block will vanish) 1023 */ 1024 if (cmdentry.cmdtype == CMDUNKNOWN) { 1025 exitstatus = 127; 1026 flushout(&errout); 1027 goto out; 1028 } 1029 #endif 1030 1031 /* implement the 'command' builtin here */ 1032 if (cmdentry.cmdtype != CMDBUILTIN || 1033 cmdentry.u.bltin != bltincmd) 1034 break; 1035 VTRACE(DBG_CMDS, ("Command \"command\"\n")); 1036 cmd_flags |= DO_NOFUNC; 1037 argsused = parse_command_args(argc, argv, &use_syspath); 1038 if (argsused == 0) { 1039 /* use 'type' builtin to display info */ 1040 VTRACE(DBG_CMDS, 1041 ("Command \"command\" -> \"type\"\n")); 1042 cmdentry.u.bltin = typecmd; 1043 break; 1044 } 1045 argc -= argsused; 1046 argv += argsused; 1047 if (use_syspath) 1048 path = syspath() + 5; 1049 } while (argc != 0); 1050 if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) 1051 /* posix mandates that 'command <splbltin>' act as if 1052 <splbltin> was a normal builtin */ 1053 cmdentry.cmdtype = CMDBUILTIN; 1054 } 1055 1056 /* 1057 * When traps are invalid, we permit the following: 1058 * trap 1059 * command trap 1060 * eval trap 1061 * command eval trap 1062 * eval command trap 1063 * without zapping the traps completely, in all other cases we do. 1064 * Function calls also do not zap the traps (but commands they execute 1065 * probably will) - this allows a function like 1066 * trapstate() { trap -p; } 1067 * called as save_traps=$(trapstate). 1068 * 1069 * The test here permits eval "anything" but when evalstring() comes 1070 * back here again, the "anything" will be validated. 1071 * This means we can actually do: 1072 * eval eval eval command eval eval command trap 1073 * as long as we end up with just "trap" 1074 * 1075 * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN 1076 * 1077 * trapcmd() takes care of doing free_traps() if it is needed there. 1078 */ 1079 if (traps_invalid && 1080 cmdentry.cmdtype != CMDFUNCTION && 1081 ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) || 1082 (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd))) 1083 free_traps(); 1084 1085 /* Fork off a child process if necessary. */ 1086 if (cmd->ncmd.backgnd 1087 || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) 1088 && (have_traps() || (flags & EV_EXIT) == 0)) 1089 #ifdef notyet /* EV_BACKCMD is never set currently */ 1090 /* this will need more work if/when it gets used */ 1091 || ((flags & EV_BACKCMD) != 0 1092 && (cmdentry.cmdtype != CMDBUILTIN 1093 && cmdentry.cmdtype != CMDSPLBLTIN) 1094 || cmdentry.u.bltin == dotcmd 1095 || cmdentry.u.bltin == evalcmd) 1096 #endif 1097 ) { 1098 INTOFF; 1099 jp = makejob(cmd, 1); 1100 mode = cmd->ncmd.backgnd; 1101 if (flags & EV_BACKCMD) { 1102 mode = FORK_NOJOB; 1103 if (sh_pipe(pip) < 0) 1104 error("Pipe call failed"); 1105 } 1106 #ifdef DO_SHAREDVFORK 1107 /* It is essential that if DO_SHAREDVFORK is defined that the 1108 * child's address space is actually shared with the parent as 1109 * we rely on this. 1110 */ 1111 if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL && 1112 (!cmd->ncmd.backgnd || cmd->ncmd.redirect == NULL)) { 1113 pid_t pid; 1114 int serrno; 1115 1116 savelocalvars = localvars; 1117 localvars = NULL; 1118 vforked = 1; 1119 VFORK_BLOCK 1120 switch (pid = vfork()) { 1121 case -1: 1122 serrno = errno; 1123 VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n", 1124 serrno)); 1125 INTON; 1126 error("Cannot vfork (%s)", strerror(serrno)); 1127 break; 1128 case 0: 1129 /* Make sure that exceptions only unwind to 1130 * after the vfork(2) 1131 */ 1132 SHELL_FORKED(); 1133 if (setjmp(jmploc.loc)) { 1134 if (exception == EXSHELLPROC) { 1135 /* 1136 * We can't progress with the 1137 * vfork, so, set vforked = 2 1138 * so the parent knows, 1139 * and _exit(); 1140 */ 1141 vforked = 2; 1142 _exit(0); 1143 } else { 1144 _exit(exception == EXEXIT ? 1145 exitstatus : exerrno); 1146 } 1147 } 1148 savehandler = handler; 1149 handler = &jmploc; 1150 listmklocal(varlist.list, 1151 VDOEXPORT | VEXPORT | VNOFUNC); 1152 forkchild(jp, cmd, mode, vforked); 1153 break; 1154 default: 1155 VFORK_UNDO(); 1156 /* restore from vfork(2) */ 1157 CTRACE(DBG_PROCS|DBG_CMDS, 1158 ("parent after vfork - vforked=%d\n", 1159 vforked)); 1160 handler = savehandler; 1161 poplocalvars(); 1162 localvars = savelocalvars; 1163 if (vforked == 2) { 1164 vforked = 0; 1165 1166 (void)waitpid(pid, NULL, 0); 1167 /* 1168 * We need to progress in a 1169 * normal fork fashion 1170 */ 1171 goto normal_fork; 1172 } 1173 /* 1174 * Here the child has left home, 1175 * getting on with its life, so 1176 * so must we... 1177 */ 1178 vforked = 0; 1179 forkparent(jp, cmd, mode, pid); 1180 goto parent; 1181 } 1182 VFORK_END 1183 } else { 1184 normal_fork: 1185 #endif 1186 if (forkshell(jp, cmd, mode) != 0) 1187 goto parent; /* at end of routine */ 1188 CTRACE(DBG_PROCS|DBG_CMDS, ("Child sets EV_EXIT\n")); 1189 flags |= EV_EXIT; 1190 FORCEINTON; 1191 #ifdef DO_SHAREDVFORK 1192 } 1193 #endif 1194 if (flags & EV_BACKCMD) { 1195 if (!vforked) { 1196 FORCEINTON; 1197 } 1198 close(pip[0]); 1199 movefd(pip[1], 1); 1200 } 1201 flags |= EV_EXIT; 1202 } 1203 1204 /* This is the child process if a fork occurred. */ 1205 /* Execute the command. */ 1206 switch (cmdentry.cmdtype) { 1207 volatile int saved; 1208 1209 case CMDFUNCTION: 1210 VXTRACE(DBG_EVAL, ("Shell function%s: ",vforked?" VF":""), 1211 trargs(argv)); 1212 redirect(cmd->ncmd.redirect, saved = 1213 !(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0); 1214 saveparam = shellparam; 1215 shellparam.malloc = 0; 1216 shellparam.reset = 1; 1217 shellparam.nparam = argc - 1; 1218 shellparam.p = argv + 1; 1219 shellparam.optnext = NULL; 1220 INTOFF; 1221 savelocalvars = localvars; 1222 localvars = NULL; 1223 reffunc(cmdentry.u.func); 1224 INTON; 1225 if (setjmp(jmploc.loc)) { 1226 if (exception == EXSHELLPROC) { 1227 freeparam((volatile struct shparam *) 1228 &saveparam); 1229 } else { 1230 freeparam(&shellparam); 1231 shellparam = saveparam; 1232 } 1233 if (saved) 1234 popredir(); 1235 unreffunc(cmdentry.u.func); 1236 poplocalvars(); 1237 localvars = savelocalvars; 1238 funclinebase = savefuncline; 1239 funclineabs = savefuncabs; 1240 handler = savehandler; 1241 longjmp(handler->loc, 1); 1242 } 1243 savehandler = handler; 1244 handler = &jmploc; 1245 if (cmdentry.u.func) { 1246 if (cmdentry.lno_frel) 1247 funclinebase = cmdentry.lineno - 1; 1248 else 1249 funclinebase = 0; 1250 funclineabs = cmdentry.lineno; 1251 1252 VTRACE(DBG_EVAL, 1253 ("function: node: %d '%s' # %d%s; funclinebase=%d\n", 1254 getfuncnode(cmdentry.u.func)->type, 1255 NODETYPENAME(getfuncnode(cmdentry.u.func)->type), 1256 cmdentry.lineno, cmdentry.lno_frel?" (=1)":"", 1257 funclinebase)); 1258 } 1259 listmklocal(varlist.list, VDOEXPORT | VEXPORT); 1260 /* stop shell blowing its stack */ 1261 if (++funcnest > 1000) 1262 error("too many nested function calls"); 1263 evaltree(getfuncnode(cmdentry.u.func), 1264 flags & (EV_TESTED|EV_EXIT)); 1265 funcnest--; 1266 INTOFF; 1267 unreffunc(cmdentry.u.func); 1268 poplocalvars(); 1269 localvars = savelocalvars; 1270 funclinebase = savefuncline; 1271 funclineabs = savefuncabs; 1272 freeparam(&shellparam); 1273 shellparam = saveparam; 1274 handler = savehandler; 1275 if (saved) 1276 popredir(); 1277 INTON; 1278 if (evalskip == SKIPFUNC) { 1279 evalskip = SKIPNONE; 1280 skipcount = 0; 1281 } 1282 if (flags & EV_EXIT) 1283 exitshell(exitstatus); 1284 break; 1285 1286 case CMDSPLBLTIN: 1287 VTRACE(DBG_EVAL, ("special ")); 1288 case CMDBUILTIN: 1289 VXTRACE(DBG_EVAL, ("builtin command [%d]%s: ", argc, 1290 vforked ? " VF" : ""), trargs(argv)); 1291 mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; 1292 if (flags == EV_BACKCMD) { 1293 memout.nleft = 0; 1294 memout.nextc = memout.buf; 1295 memout.bufsize = 64; 1296 mode |= REDIR_BACKQ; 1297 } 1298 e = -1; 1299 savecmdname = commandname; 1300 savetopfile = getcurrentfile(); 1301 savehandler = handler; 1302 temp_path = 0; 1303 if (!setjmp(jmploc.loc)) { 1304 handler = &jmploc; 1305 1306 /* 1307 * We need to ensure the command hash table isn't 1308 * corrupted by temporary PATH assignments. 1309 * However we must ensure the 'local' command works! 1310 */ 1311 if (path != pathval() && (cmdentry.u.bltin == hashcmd || 1312 cmdentry.u.bltin == typecmd)) { 1313 savelocalvars = localvars; 1314 localvars = 0; 1315 temp_path = 1; 1316 mklocal(path - 5 /* PATH= */, 0); 1317 } 1318 redirect(cmd->ncmd.redirect, mode); 1319 1320 /* 1321 * the empty command is regarded as a normal 1322 * builtin for the purposes of redirects, but 1323 * is a special builtin for var assigns. 1324 * (unless we are the "command" command.) 1325 */ 1326 if (argc == 0 && !(cmd_flags & DO_NOFUNC)) 1327 cmdentry.cmdtype = CMDSPLBLTIN; 1328 1329 /* exec is a special builtin, but needs this list... */ 1330 cmdenviron = varlist.list; 1331 /* we must check 'readonly' flag for all builtins */ 1332 listsetvar(varlist.list, 1333 cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); 1334 commandname = argv[0]; 1335 /* initialize nextopt */ 1336 argptr = argv + 1; 1337 optptr = NULL; 1338 /* and getopt */ 1339 optreset = 1; 1340 optind = 1; 1341 builtin_flags = flags; 1342 exitstatus = cmdentry.u.bltin(argc, argv); 1343 } else { 1344 e = exception; 1345 if (e == EXINT) 1346 exitstatus = SIGINT + 128; 1347 else if (e == EXEXEC) 1348 exitstatus = exerrno; 1349 else if (e != EXEXIT) 1350 exitstatus = 2; 1351 } 1352 handler = savehandler; 1353 flushall(); 1354 out1 = &output; 1355 out2 = &errout; 1356 freestdout(); 1357 if (temp_path) { 1358 poplocalvars(); 1359 localvars = savelocalvars; 1360 } 1361 cmdenviron = NULL; 1362 if (e != EXSHELLPROC) { 1363 commandname = savecmdname; 1364 if (flags & EV_EXIT) 1365 exitshell(exitstatus); 1366 } 1367 if (e != -1) { 1368 if ((e != EXERROR && e != EXEXEC) 1369 || cmdentry.cmdtype == CMDSPLBLTIN) 1370 exraise(e); 1371 popfilesupto(savetopfile); 1372 FORCEINTON; 1373 } 1374 if (cmdentry.u.bltin != execcmd) 1375 popredir(); 1376 if (flags == EV_BACKCMD) { 1377 backcmd->buf = memout.buf; 1378 backcmd->nleft = memout.nextc - memout.buf; 1379 memout.buf = NULL; 1380 } 1381 break; 1382 1383 default: 1384 VXTRACE(DBG_EVAL, ("normal command%s: ", vforked?" VF":""), 1385 trargs(argv)); 1386 redirect(cmd->ncmd.redirect, 1387 (vforked ? REDIR_VFORK : 0) | REDIR_KEEP); 1388 if (!vforked) 1389 for (sp = varlist.list ; sp ; sp = sp->next) 1390 setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); 1391 envp = environment(); 1392 shellexec(argv, envp, path, cmdentry.u.index, vforked); 1393 break; 1394 } 1395 goto out; 1396 1397 parent: /* parent process gets here (if we forked) */ 1398 1399 exitstatus = 0; /* if not altered just below */ 1400 if (mode == FORK_FG) { /* argument to fork */ 1401 exitstatus = waitforjob(jp); 1402 } else if (mode == FORK_NOJOB) { 1403 backcmd->fd = pip[0]; 1404 close(pip[1]); 1405 backcmd->jp = jp; 1406 } 1407 FORCEINTON; 1408 1409 out: 1410 if (lastarg) 1411 /* implement $_ for whatever use that really is */ 1412 (void) setvarsafe("_", lastarg, VNOERROR); 1413 popstackmark(&smark); 1414 } 1415 1416 1417 /* 1418 * Search for a command. This is called before we fork so that the 1419 * location of the command will be available in the parent as well as 1420 * the child. The check for "goodname" is an overly conservative 1421 * check that the name will not be subject to expansion. 1422 */ 1423 1424 STATIC void 1425 prehash(union node *n) 1426 { 1427 struct cmdentry entry; 1428 1429 if (n && n->type == NCMD && n->ncmd.args) 1430 if (goodname(n->ncmd.args->narg.text)) 1431 find_command(n->ncmd.args->narg.text, &entry, 0, 1432 pathval()); 1433 } 1434 1435 int 1436 in_function(void) 1437 { 1438 return funcnest; 1439 } 1440 1441 enum skipstate 1442 current_skipstate(void) 1443 { 1444 return evalskip; 1445 } 1446 1447 void 1448 save_skipstate(struct skipsave *p) 1449 { 1450 *p = s_k_i_p; 1451 } 1452 1453 void 1454 restore_skipstate(const struct skipsave *p) 1455 { 1456 s_k_i_p = *p; 1457 } 1458 1459 void 1460 stop_skipping(void) 1461 { 1462 evalskip = SKIPNONE; 1463 skipcount = 0; 1464 } 1465 1466 /* 1467 * Builtin commands. Builtin commands whose functions are closely 1468 * tied to evaluation are implemented here. 1469 */ 1470 1471 /* 1472 * No command given. 1473 */ 1474 1475 int 1476 bltincmd(int argc, char **argv) 1477 { 1478 /* 1479 * Preserve exitstatus of a previous possible redirection 1480 * as POSIX mandates 1481 */ 1482 return back_exitstatus; 1483 } 1484 1485 1486 /* 1487 * Handle break and continue commands. Break, continue, and return are 1488 * all handled by setting the evalskip flag. The evaluation routines 1489 * above all check this flag, and if it is set they start skipping 1490 * commands rather than executing them. The variable skipcount is 1491 * the number of loops to break/continue, or the number of function 1492 * levels to return. (The latter is always 1.) It should probably 1493 * be an error to break out of more loops than exist, but it isn't 1494 * in the standard shell so we don't make it one here. 1495 */ 1496 1497 int 1498 breakcmd(int argc, char **argv) 1499 { 1500 int n = argc > 1 ? number(argv[1]) : 1; 1501 1502 if (n <= 0) 1503 error("invalid count: %d", n); 1504 if (n > loopnest) 1505 n = loopnest; 1506 if (n > 0) { 1507 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 1508 skipcount = n; 1509 } 1510 return 0; 1511 } 1512 1513 int 1514 dotcmd(int argc, char **argv) 1515 { 1516 exitstatus = 0; 1517 1518 (void) nextopt(NULL); /* ignore a leading "--" */ 1519 1520 if (*argptr != NULL) { /* That's what SVR2 does */ 1521 char *fullname; 1522 /* 1523 * dot_funcnest needs to be 0 when not in a dotcmd, so it 1524 * cannot be restored with (funcnest + 1). 1525 */ 1526 int dot_funcnest_old; 1527 struct stackmark smark; 1528 1529 setstackmark(&smark); 1530 fullname = find_dot_file(*argptr); 1531 setinputfile(fullname, 1); 1532 commandname = fullname; 1533 dot_funcnest_old = dot_funcnest; 1534 dot_funcnest = funcnest + 1; 1535 cmdloop(0); 1536 dot_funcnest = dot_funcnest_old; 1537 popfile(); 1538 popstackmark(&smark); 1539 } 1540 return exitstatus; 1541 } 1542 1543 /* 1544 * allow dotfile function nesting to be manipulated 1545 * (for read_profile). This allows profile files to 1546 * be treated as if they were used as '.' commands, 1547 * (approximately) and in particular, for "return" to work. 1548 */ 1549 int 1550 set_dot_funcnest(int new) 1551 { 1552 int rv = dot_funcnest; 1553 1554 if (new >= 0) 1555 dot_funcnest = new; 1556 1557 return rv; 1558 } 1559 1560 /* 1561 * Take commands from a file. To be compatible we should do a path 1562 * search for the file, which is necessary to find sub-commands. 1563 */ 1564 1565 STATIC char * 1566 find_dot_file(char *basename) 1567 { 1568 char *fullname; 1569 const char *path = pathval(); 1570 struct stat statb; 1571 1572 /* don't try this for absolute or relative paths */ 1573 if (strchr(basename, '/')) { 1574 if (stat(basename, &statb) == 0) { 1575 if (S_ISDIR(statb.st_mode)) 1576 error("%s: is a directory", basename); 1577 if (S_ISBLK(statb.st_mode)) 1578 error("%s: is a block device", basename); 1579 return basename; 1580 } 1581 } else while ((fullname = padvance(&path, basename, 1)) != NULL) { 1582 if ((stat(fullname, &statb) == 0)) { 1583 /* weird format is to ease future code... */ 1584 if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode)) 1585 ; 1586 #if notyet 1587 else if (unreadable()) { 1588 /* 1589 * testing this via st_mode is ugly to get 1590 * correct (and would ignore ACLs). 1591 * better way is just to open the file. 1592 * But doing that here would (currently) 1593 * mean opening the file twice, which 1594 * might not be safe. So, defer this 1595 * test until code is restructures so 1596 * we can return a fd. Then we also 1597 * get to fix the mem leak just below... 1598 */ 1599 } 1600 #endif 1601 else { 1602 /* 1603 * Don't bother freeing here, since 1604 * it will be freed by the caller. 1605 * XXX no it won't - a bug for later. 1606 */ 1607 return fullname; 1608 } 1609 } 1610 stunalloc(fullname); 1611 } 1612 1613 /* not found in the PATH */ 1614 error("%s: not found", basename); 1615 /* NOTREACHED */ 1616 } 1617 1618 1619 1620 /* 1621 * The return command. 1622 * 1623 * Quoth the POSIX standard: 1624 * The return utility shall cause the shell to stop executing the current 1625 * function or dot script. If the shell is not currently executing 1626 * a function or dot script, the results are unspecified. 1627 * 1628 * As for the unspecified part, there seems to be no de-facto standard: bash 1629 * ignores the return with a warning, zsh ignores the return in interactive 1630 * mode but seems to liken it to exit in a script. (checked May 2014) 1631 * 1632 * We choose to silently ignore the return. Older versions of this shell 1633 * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This 1634 * had at least the problem of circumventing the check for stopped jobs, 1635 * which would occur for exit or ^D. 1636 */ 1637 1638 int 1639 returncmd(int argc, char **argv) 1640 { 1641 int ret = argc > 1 ? number(argv[1]) : exitstatus; 1642 1643 if ((dot_funcnest == 0 && funcnest) 1644 || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) { 1645 evalskip = SKIPFUNC; 1646 skipcount = 1; 1647 } else if (dot_funcnest > 0) { 1648 evalskip = SKIPFILE; 1649 skipcount = 1; 1650 } else { 1651 /* XXX: should a warning be issued? */ 1652 ret = 0; 1653 } 1654 1655 return ret; 1656 } 1657 1658 1659 int 1660 falsecmd(int argc, char **argv) 1661 { 1662 return 1; 1663 } 1664 1665 1666 int 1667 truecmd(int argc, char **argv) 1668 { 1669 return 0; 1670 } 1671 1672 1673 int 1674 execcmd(int argc, char **argv) 1675 { 1676 (void) nextopt(NULL); /* ignore a leading "--" */ 1677 1678 if (*argptr) { 1679 struct strlist *sp; 1680 1681 iflag = 0; /* exit on error */ 1682 mflag = 0; 1683 optschanged(); 1684 for (sp = cmdenviron; sp; sp = sp->next) 1685 setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); 1686 shellexec(argptr, environment(), pathval(), 0, 0); 1687 } 1688 return 0; 1689 } 1690 1691 static int 1692 conv_time(clock_t ticks, char *seconds, size_t l) 1693 { 1694 static clock_t tpm = 0; 1695 clock_t mins; 1696 int i; 1697 1698 if (!tpm) 1699 tpm = sysconf(_SC_CLK_TCK) * 60; 1700 1701 mins = ticks / tpm; 1702 snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); 1703 1704 if (seconds[0] == '6' && seconds[1] == '0') { 1705 /* 59.99995 got rounded up... */ 1706 mins++; 1707 strlcpy(seconds, "0.0", l); 1708 return mins; 1709 } 1710 1711 /* suppress trailing zeros */ 1712 i = strlen(seconds) - 1; 1713 for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) 1714 seconds[i] = 0; 1715 return mins; 1716 } 1717 1718 int 1719 timescmd(int argc, char **argv) 1720 { 1721 struct tms tms; 1722 int u, s, cu, cs; 1723 char us[8], ss[8], cus[8], css[8]; 1724 1725 nextopt(""); 1726 1727 times(&tms); 1728 1729 u = conv_time(tms.tms_utime, us, sizeof(us)); 1730 s = conv_time(tms.tms_stime, ss, sizeof(ss)); 1731 cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); 1732 cs = conv_time(tms.tms_cstime, css, sizeof(css)); 1733 1734 outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", 1735 u, us, s, ss, cu, cus, cs, css); 1736 1737 return 0; 1738 } 1739