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