master xplshn/aruu / cmd / posix / sh / eval.c
   1/*-
   2 * SPDX-License-Identifier: BSD-3-Clause
   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 <errno.h>
  36#include <signal.h>
  37#include <stdlib.h>
  38#include <sys/resource.h>
  39#include <unistd.h>
  40
  41/*
  42 * Evaluate a command.
  43 */
  44
  45#include "../../../shared/paths.h"
  46#include "../../../shared/wexec.h"
  47#include "builtins.h"
  48#include "error.h"
  49#include "eval.h"
  50#include "exec.h"
  51#include "expand.h"
  52#include "input.h"
  53#include "jobs.h"
  54#include "memalloc.h"
  55#include "mystring.h"
  56#include "nodes.h"
  57#include "options.h"
  58#include "output.h"
  59#include "parser.h"
  60#include "redir.h"
  61#include "shell.h"
  62#include "show.h"
  63#include "syntax.h"
  64#include "trap.h"
  65#include "var.h"
  66#ifndef NO_HISTORY
  67#include "lineedit.h"
  68#endif
  69
  70int        evalskip;      /* set if we are skipping commands */
  71int        skipcount;     /* number of levels to skip */
  72static int loopnest;      /* current loop nesting level */
  73int        funcnest;      /* depth of function calls */
  74static int builtin_flags; /* evalcommand flags for builtins */
  75
  76char           *commandname;
  77struct arglist *cmdenviron;
  78int             exitstatus;  /* exit status of last command */
  79int             oexitstatus; /* saved exit status */
  80
  81static void        evalloop(union node *, int);
  82static void        evalfor(union node *, int);
  83static union node *evalcase(union node *);
  84static void        evalsubshell(union node *, int);
  85static void        evalredir(union node *, int);
  86static void        exphere(union node *, struct arglist *);
  87static void        expredir(union node *);
  88static void        evalpipe(union node *);
  89static int         is_valid_fast_cmdsubst(union node *n);
  90static void        evalcommand(union node *, int, struct backcmd *);
  91static void        prehash(union node *);
  92
  93/*
  94 * Called to reset things after an exception.
  95 */
  96
  97void
  98reseteval(void)
  99{
 100  evalskip = 0;
 101  loopnest = 0;
 102}
 103
 104/*
 105 * The eval command.
 106 */
 107
 108int
 109evalcmd(int argc, char **argv)
 110{
 111  char  *p;
 112  char  *concat;
 113  char **ap;
 114
 115  if (argc > 1) {
 116    p = argv[1];
 117    if (argc > 2) {
 118      STARTSTACKSTR(concat);
 119      ap = argv + 2;
 120      for (;;) {
 121        STPUTS(p, concat);
 122        if ((p = *ap++) == NULL)
 123          break;
 124        STPUTC(' ', concat);
 125      }
 126      STPUTC('\0', concat);
 127      p = grabstackstr(concat);
 128    }
 129    evalstring(p, builtin_flags);
 130  } else
 131    exitstatus = 0;
 132  return exitstatus;
 133}
 134
 135/*
 136 * Execute a command or commands contained in a string.
 137 */
 138
 139void
 140evalstring(const char *s, int flags)
 141{
 142  union node      *n;
 143  struct stackmark smark;
 144  int              flags_exit;
 145  int              any;
 146
 147  flags_exit = flags & EV_EXIT;
 148  flags &= ~EV_EXIT;
 149  any = 0;
 150  setstackmark(&smark);
 151  setinputstring(s);
 152  while ((n = parsecmd(0)) != NEOF) {
 153    if (n != NULL && !nflag) {
 154      if (flags_exit && preadateof())
 155        evaltree(n, flags | EV_EXIT);
 156      else
 157        evaltree(n, flags);
 158      any = 1;
 159      if (evalskip)
 160        break;
 161    }
 162    popstackmark(&smark);
 163    setstackmark(&smark);
 164  }
 165  popfile();
 166  popstackmark(&smark);
 167  if (!any)
 168    exitstatus = 0;
 169  if (flags_exit)
 170    exraise(EXEXIT);
 171}
 172
 173/*
 174 * Evaluate a parse tree.  The value is left in the global variable
 175 * exitstatus.
 176 */
 177
 178void
 179evaltree(union node *n, int flags)
 180{
 181  int              do_etest;
 182  union node      *next;
 183  struct stackmark smark;
 184
 185  setstackmark(&smark);
 186  do_etest = 0;
 187  if (n == NULL) {
 188    TRACE(("evaltree(NULL) called\n"));
 189    exitstatus = 0;
 190    goto out;
 191  }
 192  do {
 193    next = NULL;
 194#ifndef NO_HISTORY
 195    displayhist = 1; /* show history substitutions done with fc */
 196#endif
 197    TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type));
 198    switch (n->type) {
 199      case NSEMI:
 200        evaltree(n->nbinary.ch1, flags & ~EV_EXIT);
 201        if (evalskip)
 202          goto out;
 203        next = n->nbinary.ch2;
 204        break;
 205      case NAND:
 206        evaltree(n->nbinary.ch1, EV_TESTED);
 207        if (evalskip || exitstatus != 0) {
 208          goto out;
 209        }
 210        next = n->nbinary.ch2;
 211        break;
 212      case NOR:
 213        evaltree(n->nbinary.ch1, EV_TESTED);
 214        if (evalskip || exitstatus == 0)
 215          goto out;
 216        next = n->nbinary.ch2;
 217        break;
 218      case NREDIR:
 219        evalredir(n, flags);
 220        break;
 221      case NSUBSHELL:
 222        evalsubshell(n, flags);
 223        do_etest = !(flags & EV_TESTED);
 224        break;
 225      case NBACKGND:
 226        evalsubshell(n, flags);
 227        break;
 228      case NIF: {
 229        evaltree(n->nif.test, EV_TESTED);
 230        if (evalskip)
 231          goto out;
 232        if (exitstatus == 0)
 233          next = n->nif.ifpart;
 234        else if (n->nif.elsepart)
 235          next = n->nif.elsepart;
 236        else
 237          exitstatus = 0;
 238        break;
 239      }
 240      case NWHILE:
 241      case NUNTIL:
 242        evalloop(n, flags & ~EV_EXIT);
 243        break;
 244      case NFOR:
 245        evalfor(n, flags & ~EV_EXIT);
 246        break;
 247      case NCASE:
 248        next = evalcase(n);
 249        break;
 250      case NCLIST:
 251        next = n->nclist.body;
 252        break;
 253      case NCLISTFALLTHRU:
 254        if (n->nclist.body) {
 255          evaltree(n->nclist.body, flags & ~EV_EXIT);
 256          if (evalskip)
 257            goto out;
 258        }
 259        next = n->nclist.next;
 260        break;
 261      case NDEFUN:
 262        defun(n->narg.text, n->narg.next);
 263        exitstatus = 0;
 264        break;
 265      case NNOT:
 266        evaltree(n->nnot.com, EV_TESTED);
 267        if (evalskip)
 268          goto out;
 269        exitstatus = !exitstatus;
 270        break;
 271
 272      case NPIPE:
 273        evalpipe(n);
 274        do_etest = !(flags & EV_TESTED);
 275        break;
 276      case NCMD:
 277        evalcommand(n, flags, (struct backcmd *)NULL);
 278        do_etest = !(flags & EV_TESTED);
 279        break;
 280      default:
 281        out1fmt("Node type = %d\n", n->type);
 282        flushout(&output);
 283        break;
 284    }
 285    n = next;
 286    popstackmark(&smark);
 287    setstackmark(&smark);
 288  } while (n != NULL);
 289out:
 290  popstackmark(&smark);
 291  if (pendingsig)
 292    dotrap();
 293  if (eflag && exitstatus != 0 && do_etest)
 294    exitshell(exitstatus);
 295  if (flags & EV_EXIT)
 296    exraise(EXEXIT);
 297}
 298
 299static void
 300evalloop(union node *n, int flags)
 301{
 302  int status;
 303
 304  loopnest++;
 305  status = 0;
 306  for (;;) {
 307    if (!evalskip)
 308      evaltree(n->nbinary.ch1, EV_TESTED);
 309    if (evalskip) {
 310      if (evalskip == SKIPCONT && --skipcount <= 0) {
 311        evalskip = 0;
 312        continue;
 313      }
 314      if (evalskip == SKIPBREAK && --skipcount <= 0)
 315        evalskip = 0;
 316      if (evalskip == SKIPRETURN)
 317        status = exitstatus;
 318      break;
 319    }
 320    if (n->type == NWHILE) {
 321      if (exitstatus != 0)
 322        break;
 323    } else {
 324      if (exitstatus == 0)
 325        break;
 326    }
 327    evaltree(n->nbinary.ch2, flags);
 328    status = exitstatus;
 329  }
 330  loopnest--;
 331  exitstatus = status;
 332}
 333
 334static void
 335evalfor(union node *n, int flags)
 336{
 337  struct arglist arglist;
 338  union node    *argp;
 339  int            i;
 340  int            status;
 341
 342  emptyarglist(&arglist);
 343  for (argp = n->nfor.args; argp; argp = argp->narg.next) {
 344    oexitstatus = exitstatus;
 345    expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
 346  }
 347
 348  loopnest++;
 349  status = 0;
 350  for (i = 0; i < arglist.count; i++) {
 351    setvar(n->nfor.var, arglist.args[i], 0);
 352    evaltree(n->nfor.body, flags);
 353    status = exitstatus;
 354    if (evalskip) {
 355      if (evalskip == SKIPCONT && --skipcount <= 0) {
 356        evalskip = 0;
 357        continue;
 358      }
 359      if (evalskip == SKIPBREAK && --skipcount <= 0)
 360        evalskip = 0;
 361      break;
 362    }
 363  }
 364  loopnest--;
 365  exitstatus = status;
 366}
 367
 368/*
 369 * Evaluate a case statement, returning the selected tree.
 370 *
 371 * The exit status needs care to get right.
 372 */
 373
 374static union node *
 375evalcase(union node *n)
 376{
 377  union node    *cp;
 378  union node    *patp;
 379  struct arglist arglist;
 380
 381  emptyarglist(&arglist);
 382  oexitstatus = exitstatus;
 383  expandarg(n->ncase.expr, &arglist, EXP_TILDE);
 384  for (cp = n->ncase.cases; cp; cp = cp->nclist.next) {
 385    for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
 386      if (casematch(patp, arglist.args[0])) {
 387        while (cp->nclist.next && cp->type == NCLISTFALLTHRU && cp->nclist.body == NULL)
 388          cp = cp->nclist.next;
 389        if (cp->nclist.next && cp->type == NCLISTFALLTHRU)
 390          return (cp);
 391        if (cp->nclist.body == NULL)
 392          exitstatus = 0;
 393        return (cp->nclist.body);
 394      }
 395    }
 396  }
 397  exitstatus = 0;
 398  return (NULL);
 399}
 400
 401/*
 402 * Kick off a subshell to evaluate a tree.
 403 */
 404
 405static void
 406evalsubshell(union node *n, int flags)
 407{
 408  struct job *jp;
 409  int         backgnd = (n->type == NBACKGND);
 410
 411  oexitstatus = exitstatus;
 412  expredir(n->nredir.redirect);
 413  if ((!backgnd && flags & EV_EXIT && !have_traps())
 414      || forkshell(jp = makejob(n, 1), n, backgnd) == 0) {
 415    if (backgnd)
 416      flags &= ~EV_TESTED;
 417    redirect(n->nredir.redirect, 0);
 418    evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
 419  } else if (!backgnd) {
 420    INTOFF;
 421    exitstatus = waitforjob(jp, (int *)NULL);
 422    INTON;
 423  } else
 424    exitstatus = 0;
 425}
 426
 427/*
 428 * Evaluate a redirected compound command.
 429 */
 430
 431static void
 432evalredir(union node *n, int flags)
 433{
 434  struct jmploc  jmploc;
 435  struct jmploc *savehandler;
 436  volatile int   in_redirect = 1;
 437
 438  oexitstatus = exitstatus;
 439  expredir(n->nredir.redirect);
 440  savehandler = handler;
 441  if (setjmp(jmploc.loc)) {
 442    int e;
 443
 444    handler = savehandler;
 445    e       = exception;
 446    popredir();
 447    if (e == EXERROR && in_redirect) {
 448      FORCEINTON;
 449      return;
 450    }
 451    longjmp(handler->loc, 1);
 452  } else {
 453    INTOFF;
 454    handler = &jmploc;
 455    redirect(n->nredir.redirect, REDIR_PUSH);
 456    in_redirect = 0;
 457    INTON;
 458    evaltree(n->nredir.n, flags);
 459  }
 460  INTOFF;
 461  handler = savehandler;
 462  popredir();
 463  INTON;
 464}
 465
 466static void
 467exphere(union node *redir, struct arglist *fn)
 468{
 469  struct jmploc    jmploc;
 470  struct jmploc   *savehandler;
 471  struct localvar *savelocalvars;
 472  int              need_longjmp = 0;
 473  unsigned char    saveoptreset;
 474
 475  redir->nhere.expdoc = "";
 476  savelocalvars       = localvars;
 477  localvars           = NULL;
 478  saveoptreset        = shellparam.reset;
 479  forcelocal++;
 480  savehandler = handler;
 481  if (setjmp(jmploc.loc))
 482    need_longjmp = exception != EXERROR;
 483  else {
 484    handler = &jmploc;
 485    expandarg(redir->nhere.doc, fn, 0);
 486    redir->nhere.expdoc = fn->args[0];
 487    INTOFF;
 488  }
 489  handler = savehandler;
 490  forcelocal--;
 491  poplocalvars();
 492  localvars        = savelocalvars;
 493  shellparam.reset = saveoptreset;
 494  if (need_longjmp)
 495    longjmp(handler->loc, 1);
 496  INTON;
 497}
 498
 499/*
 500 * Compute the names of the files in a redirection list.
 501 */
 502
 503static void
 504expredir(union node *n)
 505{
 506  union node *redir;
 507
 508  for (redir = n; redir; redir = redir->nfile.next) {
 509    struct arglist fn;
 510    emptyarglist(&fn);
 511    switch (redir->type) {
 512      case NFROM:
 513      case NTO:
 514      case NFROMTO:
 515      case NAPPEND:
 516      case NCLOBBER:
 517        expandarg(redir->nfile.fname, &fn, EXP_TILDE);
 518        redir->nfile.expfname = fn.args[0];
 519        break;
 520      case NFROMFD:
 521      case NTOFD:
 522        if (redir->ndup.vname) {
 523          expandarg(redir->ndup.vname, &fn, EXP_TILDE);
 524          fixredir(redir, fn.args[0], 1);
 525        }
 526        break;
 527      case NXHERE:
 528        exphere(redir, &fn);
 529        break;
 530    }
 531  }
 532}
 533
 534/*
 535 * Evaluate a pipeline.  All the processes in the pipeline are children
 536 * of the process creating the pipeline.  (This differs from some versions
 537 * of the shell, which make the last process in a pipeline the parent
 538 * of all the rest.)
 539 */
 540
 541static void
 542evalpipe(union node *n)
 543{
 544  struct job      *jp;
 545  struct nodelist *lp;
 546  int              pipelen;
 547  int              prevfd;
 548  int              pip[2];
 549
 550  TRACE(("evalpipe(%p) called\n", (void *)n));
 551  pipelen = 0;
 552  for (lp = n->npipe.cmdlist; lp; lp = lp->next)
 553    pipelen++;
 554  INTOFF;
 555  jp     = makejob(n, pipelen);
 556  prevfd = -1;
 557  for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
 558    prehash(lp->n);
 559    pip[1] = -1;
 560    if (lp->next) {
 561      if (pipe(pip) < 0) {
 562        if (prevfd >= 0)
 563          close(prevfd);
 564        error("Pipe call failed: %s", strerror(errno));
 565      }
 566    }
 567    if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
 568      INTON;
 569      if (prevfd > 0) {
 570        dup2(prevfd, 0);
 571        close(prevfd);
 572      }
 573      if (pip[1] >= 0) {
 574        if (!(prevfd >= 0 && pip[0] == 0))
 575          close(pip[0]);
 576        if (pip[1] != 1) {
 577          dup2(pip[1], 1);
 578          close(pip[1]);
 579        }
 580      }
 581      evaltree(lp->n, EV_EXIT);
 582    }
 583    if (prevfd >= 0)
 584      close(prevfd);
 585    prevfd = pip[0];
 586    if (pip[1] != -1)
 587      close(pip[1]);
 588  }
 589  INTON;
 590  if (n->npipe.backgnd == 0) {
 591    INTOFF;
 592    exitstatus = waitforjob(jp, (int *)NULL);
 593    TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
 594    INTON;
 595  } else
 596    exitstatus = 0;
 597}
 598
 599static int
 600is_valid_fast_cmdsubst(union node *n)
 601{
 602  return (n->type == NCMD);
 603}
 604
 605/*
 606 * Execute a command inside back quotes.  If it's a builtin command, we
 607 * want to save its output in a block obtained from malloc.  Otherwise
 608 * we fork off a subprocess and get the output of the command via a pipe.
 609 * Should be called with interrupts off.
 610 */
 611
 612void
 613evalbackcmd(union node *n, struct backcmd *result)
 614{
 615  int              pip[2];
 616  struct job      *jp;
 617  struct stackmark smark;
 618  struct jmploc    jmploc;
 619  struct jmploc   *savehandler;
 620  struct localvar *savelocalvars;
 621  unsigned char    saveoptreset;
 622
 623  result->fd    = -1;
 624  result->buf   = NULL;
 625  result->nleft = 0;
 626  result->jp    = NULL;
 627  if (n == NULL) {
 628    exitstatus = 0;
 629    return;
 630  }
 631  setstackmark(&smark);
 632  exitstatus = oexitstatus;
 633  if (is_valid_fast_cmdsubst(n)) {
 634    savelocalvars = localvars;
 635    localvars     = NULL;
 636    saveoptreset  = shellparam.reset;
 637    forcelocal++;
 638    savehandler = handler;
 639    if (setjmp(jmploc.loc)) {
 640      if (exception == EXERROR)
 641        /* nothing */;
 642      else if (exception != 0) {
 643        handler = savehandler;
 644        forcelocal--;
 645        poplocalvars();
 646        localvars        = savelocalvars;
 647        shellparam.reset = saveoptreset;
 648        longjmp(handler->loc, 1);
 649      }
 650    } else {
 651      handler = &jmploc;
 652      evalcommand(n, EV_BACKCMD, result);
 653    }
 654    handler = savehandler;
 655    forcelocal--;
 656    poplocalvars();
 657    localvars        = savelocalvars;
 658    shellparam.reset = saveoptreset;
 659  } else {
 660    if (pipe(pip) < 0)
 661      error("Pipe call failed: %s", strerror(errno));
 662    jp = makejob(n, 1);
 663    if (forkshell(jp, n, FORK_NOJOB) == 0) {
 664      FORCEINTON;
 665      close(pip[0]);
 666      if (pip[1] != 1) {
 667        dup2(pip[1], 1);
 668        close(pip[1]);
 669      }
 670      evaltree(n, EV_EXIT);
 671    }
 672    close(pip[1]);
 673    result->fd = pip[0];
 674    result->jp = jp;
 675  }
 676  popstackmark(&smark);
 677  TRACE(
 678      ("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
 679       result->fd,
 680       result->buf,
 681       result->nleft,
 682       result->jp)
 683  );
 684}
 685
 686static int
 687mustexpandto(const char *argtext, const char *mask)
 688{
 689  for (;;) {
 690    if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
 691      argtext++;
 692      continue;
 693    }
 694    if (*argtext == CTLESC)
 695      argtext++;
 696    else if (BASESYNTAX[(int)*argtext] == CCTL)
 697      return (0);
 698    if (*argtext != *mask)
 699      return (0);
 700    if (*argtext == '\0')
 701      return (1);
 702    argtext++;
 703    mask++;
 704  }
 705}
 706
 707static int
 708isdeclarationcmd(struct narg *arg)
 709{
 710  int have_command = 0;
 711
 712  if (arg == NULL)
 713    return (0);
 714  while (mustexpandto(arg->text, "command")) {
 715    have_command = 1;
 716    arg          = &arg->next->narg;
 717    if (arg == NULL)
 718      return (0);
 719    /*
 720     * To also allow "command -p" and "command --" as part of
 721     * a declaration command, add code here.
 722     * We do not do this, as ksh does not do it either and it
 723     * is not required by POSIX.
 724     */
 725  }
 726  return (
 727      mustexpandto(arg->text, "export") || mustexpandto(arg->text, "readonly")
 728      || (mustexpandto(arg->text, "local") && (have_command || !isfunc("local")))
 729  );
 730}
 731
 732static void
 733xtracecommand(struct arglist *varlist, int argc, char **argv)
 734{
 735  char        sep = 0;
 736  const char *text, *p, *ps4;
 737  int         i;
 738
 739  ps4 = expandstr(ps4val());
 740  out2str(ps4 != NULL ? ps4 : ps4val());
 741  for (i = 0; i < varlist->count; i++) {
 742    text = varlist->args[i];
 743    if (sep != 0)
 744      out2c(' ');
 745    p = strchr(text, '=');
 746    if (p != NULL) {
 747      p++;
 748      outbin(text, p - text, out2);
 749      out2qstr(p);
 750    } else
 751      out2qstr(text);
 752    sep = ' ';
 753  }
 754  for (i = 0; i < argc; i++) {
 755    text = argv[i];
 756    if (sep != 0)
 757      out2c(' ');
 758    out2qstr(text);
 759    sep = ' ';
 760  }
 761  out2c('\n');
 762  flushout(&errout);
 763}
 764
 765/*
 766 * Check if a builtin can safely be executed in the same process,
 767 * even though it should be in a subshell (command substitution).
 768 * Note that jobid, jobs, times and trap can show information not
 769 * available in a child process; this is deliberate.
 770 * The arguments should already have been expanded.
 771 */
 772static int
 773safe_builtin(int idx, int argc, char **argv)
 774{
 775  /* Generated from builtins.def. */
 776  if (safe_builtin_always(idx))
 777    return (1);
 778  if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || idx == UMASKCMD)
 779    return (argc <= 1 || (argc == 2 && argv[1][0] == '-'));
 780  if (idx == SETCMD)
 781    return (
 782        argc <= 1
 783        || (argc == 2 && (argv[1][0] == '-' || argv[1][0] == '+') && argv[1][1] == 'o'
 784            && argv[1][2] == '\0')
 785    );
 786  return (0);
 787}
 788
 789/*
 790 * Perform redirections, then execute a simple command with vfork.
 791 * This cannot be used for command substitutions for two reasons:
 792 * - Redirections might cause the error message for later redirections or for
 793 *   an unknown command to be sent to the pipe (to be substituted), and this
 794 *   might cause a deadlock if the message is too long.
 795 * - The assignment of the pipe needs to come before instead of after the
 796 *   redirections.
 797 */
 798static int
 799redirected_vforkexecshell(
 800    struct job *jp, union node *redir, char **argv, char **envp, const char *path, int idx
 801)
 802{
 803  struct jmploc  jmploc;
 804  struct jmploc *savehandler;
 805  volatile int   in_redirect = 1;
 806
 807  savehandler = handler;
 808  if (setjmp(jmploc.loc)) {
 809    int e;
 810
 811    handler = savehandler;
 812    e       = exception;
 813    popredir();
 814    if (e == EXERROR && in_redirect) {
 815      FORCEINTON;
 816      return 0;
 817    }
 818    longjmp(handler->loc, 1);
 819  } else {
 820    INTOFF;
 821    handler = &jmploc;
 822    redirect(redir, REDIR_PUSH);
 823    in_redirect = 0;
 824    INTON;
 825    vforkexecshell(jp, argv, envp, path, idx, NULL);
 826  }
 827  INTOFF;
 828  handler = savehandler;
 829  popredir();
 830  INTON;
 831  return 1;
 832}
 833
 834/*
 835 * Execute a simple command.
 836 * Note: This may or may not return if (flags & EV_EXIT).
 837 */
 838
 839static void
 840evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
 841{
 842  union node       *argp;
 843  struct arglist    arglist;
 844  struct arglist    varlist;
 845  char            **argv;
 846  int               argc;
 847  char            **envp;
 848  int               varflag;
 849  int               mode;
 850  int               pip[2];
 851  struct cmdentry   cmdentry;
 852  struct job       *jp;
 853  struct jmploc     jmploc;
 854  struct jmploc    *savehandler;
 855  char             *savecmdname;
 856  struct shparam    saveparam;
 857  struct localvar  *savelocalvars;
 858  struct parsefile *savetopfile;
 859  volatile int      e;
 860  char             *lastarg;
 861  int               signaled;
 862  int               do_clearcmdentry;
 863  const char       *path = pathval();
 864  int               i;
 865
 866  /* First expand the arguments. */
 867  TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
 868  emptyarglist(&arglist);
 869  emptyarglist(&varlist);
 870  varflag          = 1;
 871  jp               = NULL;
 872  do_clearcmdentry = 0;
 873  oexitstatus      = exitstatus;
 874  exitstatus       = 0;
 875  /* Add one slot at the beginning for tryexec(). */
 876  appendarglist(&arglist, nullstr);
 877  for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
 878    if (varflag && isassignment(argp->narg.text)) {
 879      expandarg(argp, varflag == 1 ? &varlist : &arglist, EXP_VARTILDE);
 880      continue;
 881    } else if (varflag == 1)
 882      varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
 883    expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
 884  }
 885  appendarglist(&arglist, nullstr);
 886  expredir(cmd->ncmd.redirect);
 887  argc = arglist.count - 2;
 888  argv = &arglist.args[1];
 889
 890  argv[argc] = NULL;
 891  lastarg    = NULL;
 892  if (iflag && funcnest == 0 && argc > 0)
 893    lastarg = argv[argc - 1];
 894
 895  /* Print the command if xflag is set. */
 896  if (xflag)
 897    xtracecommand(&varlist, argc, argv);
 898
 899  /* Now locate the command. */
 900  if (argc == 0) {
 901    /* Variable assignment(s) without command */
 902    cmdentry.cmdtype = CMDBUILTIN;
 903    cmdentry.u.index = BLTINCMD;
 904    cmdentry.special = 0;
 905  } else {
 906    static const char PATH[]    = "PATH=";
 907    int               cmd_flags = 0, bltinonly = 0;
 908
 909    /*
 910     * Modify the command lookup path, if a PATH= assignment
 911     * is present
 912     */
 913    for (i = 0; i < varlist.count; i++)
 914      if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) {
 915        path = varlist.args[i] + sizeof(PATH) - 1;
 916        /*
 917         * On `PATH=... command`, we need to make
 918         * sure that the command isn't using the
 919         * non-updated hash table of the outer PATH
 920         * setting and we need to make sure that
 921         * the hash table isn't filled with items
 922         * from the temporary setting.
 923         *
 924         * It would be better to forbid using and
 925         * updating the table while this command
 926         * runs, by the command finding mechanism
 927         * is heavily integrated with hash handling,
 928         * so we just delete the hash before and after
 929         * the command runs. Partly deleting like
 930         * changepatch() does doesn't seem worth the
 931         * booking effort, since most such runs add
 932         * directories in front of the new PATH.
 933         */
 934        clearcmdentry();
 935        do_clearcmdentry = 1;
 936      }
 937
 938    for (;;) {
 939      if (bltinonly) {
 940        cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
 941        if (cmdentry.u.index < 0) {
 942          cmdentry.u.index = BLTINCMD;
 943          argv--;
 944          argc++;
 945          break;
 946        }
 947      } else
 948        find_command(argv[0], &cmdentry, cmd_flags, path);
 949      /* implement the bltin and command builtins here */
 950      if (cmdentry.cmdtype != CMDBUILTIN)
 951        break;
 952      if (cmdentry.u.index == BLTINCMD) {
 953        if (argc == 1)
 954          break;
 955        argv++;
 956        argc--;
 957        bltinonly = 1;
 958      } else if (cmdentry.u.index == COMMANDCMD) {
 959        if (argc == 1)
 960          break;
 961        if (!strcmp(argv[1], "-p")) {
 962          if (argc == 2)
 963            break;
 964          if (argv[2][0] == '-') {
 965            if (strcmp(argv[2], "--"))
 966              break;
 967            if (argc == 3)
 968              break;
 969            argv += 3;
 970            argc -= 3;
 971          } else {
 972            argv += 2;
 973            argc -= 2;
 974          }
 975          path = ARUU_PATH_STDPATH;
 976          clearcmdentry();
 977          do_clearcmdentry = 1;
 978        } else if (!strcmp(argv[1], "--")) {
 979          if (argc == 2)
 980            break;
 981          argv += 2;
 982          argc -= 2;
 983        } else if (argv[1][0] == '-')
 984          break;
 985        else {
 986          argv++;
 987          argc--;
 988        }
 989        cmd_flags |= DO_NOFUNC;
 990        bltinonly = 0;
 991      } else
 992        break;
 993    }
 994    /*
 995     * Special builtins lose their special properties when
 996     * called via 'command'.
 997     */
 998    if (cmd_flags & DO_NOFUNC)
 999      cmdentry.special = 0;
1000  }
1001
1002  /* Fork off a child process if necessary. */
1003  if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN
1004        || (cmdentry.cmdtype == CMDWEXEC && !(wexec_get_nofork() && wexec_is_nofork(argv[0]))))
1005       && ((flags & EV_EXIT) == 0 || have_traps()))
1006      || ((flags & EV_BACKCMD) != 0
1007          && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) {
1008    jp   = makejob(cmd, 1);
1009    mode = FORK_FG;
1010    if (flags & EV_BACKCMD) {
1011      mode = FORK_NOJOB;
1012      if (pipe(pip) < 0)
1013        error("Pipe call failed: %s", strerror(errno));
1014    }
1015    if (cmdentry.cmdtype == CMDNORMAL && (cmd->ncmd.redirect == NULL || (flags & EV_BACKCMD) == 0)
1016        && varlist.count == 0 && (mode == FORK_FG || mode == FORK_NOJOB) && !disvforkset() && !iflag
1017        && !mflag) {
1018      if (cmd->ncmd.redirect != NULL) {
1019        if (redirected_vforkexecshell(
1020                jp, cmd->ncmd.redirect, argv, environment(), path, cmdentry.u.index
1021            ))
1022          goto parent;
1023        else
1024          goto out;
1025      } else
1026        vforkexecshell(
1027            jp, argv, environment(), path, cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL
1028        );
1029      goto parent;
1030    }
1031    if (forkshell(jp, cmd, mode) != 0)
1032      goto parent; /* at end of routine */
1033    if (flags & EV_BACKCMD) {
1034      FORCEINTON;
1035      close(pip[0]);
1036      if (pip[1] != 1) {
1037        dup2(pip[1], 1);
1038        close(pip[1]);
1039      }
1040      flags &= ~EV_BACKCMD;
1041    }
1042    flags |= EV_EXIT;
1043  }
1044
1045  /* This is the child process if a fork occurred. */
1046  /* Execute the command. */
1047  if (cmdentry.cmdtype == CMDFUNCTION) {
1048#ifdef DEBUG
1049    trputs("Shell function:  ");
1050    trargs(argv);
1051#endif
1052    saveparam          = shellparam;
1053    shellparam.malloc  = 0;
1054    shellparam.reset   = 1;
1055    shellparam.nparam  = argc - 1;
1056    shellparam.p       = argv + 1;
1057    shellparam.optp    = NULL;
1058    shellparam.optnext = NULL;
1059    INTOFF;
1060    savelocalvars = localvars;
1061    localvars     = NULL;
1062    reffunc(cmdentry.u.func);
1063    savehandler = handler;
1064    if (setjmp(jmploc.loc)) {
1065      popredir();
1066      unreffunc(cmdentry.u.func);
1067      poplocalvars();
1068      localvars = savelocalvars;
1069      freeparam(&shellparam);
1070      shellparam = saveparam;
1071      funcnest--;
1072      handler = savehandler;
1073      longjmp(handler->loc, 1);
1074    }
1075    handler = &jmploc;
1076    funcnest++;
1077    redirect(cmd->ncmd.redirect, REDIR_PUSH);
1078    INTON;
1079    for (i = 0; i < varlist.count; i++)
1080      mklocal(varlist.args[i]);
1081    exitstatus = oexitstatus;
1082    evaltree(getfuncnode(cmdentry.u.func), flags & (EV_TESTED | EV_EXIT));
1083    INTOFF;
1084    unreffunc(cmdentry.u.func);
1085    poplocalvars();
1086    localvars = savelocalvars;
1087    freeparam(&shellparam);
1088    shellparam = saveparam;
1089    handler    = savehandler;
1090    funcnest--;
1091    popredir();
1092    INTON;
1093    if (evalskip == SKIPRETURN) {
1094      evalskip  = 0;
1095      skipcount = 0;
1096    }
1097    if (jp)
1098      exitshell(exitstatus);
1099  } else if (cmdentry.cmdtype == CMDWEXEC) {
1100    savecmdname = commandname;
1101    savetopfile = getcurrentfile();
1102    cmdenviron  = &varlist;
1103    e           = -1;
1104    savehandler = handler;
1105    if (setjmp(jmploc.loc)) {
1106      e = exception;
1107      if (e == EXINT)
1108        exitstatus = SIGINT + 128;
1109      goto cmddone_wexec;
1110    }
1111    handler = &jmploc;
1112    mode    = REDIR_PUSH;
1113    if (flags == EV_BACKCMD) {
1114      memout.nextc = memout.buf;
1115      mode |= REDIR_BACKQ;
1116    }
1117    redirect(cmd->ncmd.redirect, mode);
1118    outclearerror(out1);
1119    listsetvar(cmdenviron, VNOSET);
1120    commandname = argv[0];
1121    exitstatus  = wexecvp(argv[0], argv);
1122    flushall();
1123    if (outiserror(out1)) {
1124      warning("write error on stdout");
1125      if (exitstatus == 0 || exitstatus == 1)
1126        exitstatus = 2;
1127    }
1128  cmddone_wexec:
1129    cmdenviron = NULL;
1130    out1       = &output;
1131    out2       = &errout;
1132    freestdout();
1133    handler     = savehandler;
1134    commandname = savecmdname;
1135    if (jp)
1136      exitshell(exitstatus);
1137    if (flags == EV_BACKCMD) {
1138      backcmd->buf   = memout.buf;
1139      backcmd->nleft = memout.buf != NULL ? memout.nextc - memout.buf : 0;
1140      memout.buf     = NULL;
1141      memout.nextc   = NULL;
1142      memout.bufend  = NULL;
1143      memout.bufsize = 64;
1144    }
1145  } else if (cmdentry.cmdtype == CMDBUILTIN) {
1146#ifdef DEBUG
1147    trputs("builtin command:  ");
1148    trargs(argv);
1149#endif
1150    mode = (cmdentry.u.index == EXECCMD) ? 0 : REDIR_PUSH;
1151    if (flags == EV_BACKCMD) {
1152      memout.nextc = memout.buf;
1153      mode |= REDIR_BACKQ;
1154    }
1155    savecmdname = commandname;
1156    savetopfile = getcurrentfile();
1157    cmdenviron  = &varlist;
1158    e           = -1;
1159    savehandler = handler;
1160    if (setjmp(jmploc.loc)) {
1161      e = exception;
1162      if (e == EXINT)
1163        exitstatus = SIGINT + 128;
1164      goto cmddone;
1165    }
1166    handler = &jmploc;
1167    redirect(cmd->ncmd.redirect, mode);
1168    outclearerror(out1);
1169    /*
1170     * If there is no command word, redirection errors should
1171     * not be fatal but assignment errors should.
1172     */
1173    if (argc == 0)
1174      cmdentry.special = 1;
1175    listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET);
1176    if (argc > 0)
1177      bltinsetlocale();
1178    commandname    = argv[0];
1179    argptr         = argv + 1;
1180    nextopt_optptr = NULL; /* initialize nextopt */
1181    builtin_flags  = flags;
1182    exitstatus     = (*builtinfunc[cmdentry.u.index])(argc, argv);
1183    flushall();
1184    if (outiserror(out1)) {
1185      warning("write error on stdout");
1186      if (exitstatus == 0 || exitstatus == 1)
1187        exitstatus = 2;
1188    }
1189  cmddone:
1190    if (argc > 0)
1191      bltinunsetlocale();
1192    cmdenviron = NULL;
1193    out1       = &output;
1194    out2       = &errout;
1195    freestdout();
1196    handler     = savehandler;
1197    commandname = savecmdname;
1198    if (jp)
1199      exitshell(exitstatus);
1200    if (flags == EV_BACKCMD) {
1201      backcmd->buf   = memout.buf;
1202      backcmd->nleft = memout.buf != NULL ? memout.nextc - memout.buf : 0;
1203      memout.buf     = NULL;
1204      memout.nextc   = NULL;
1205      memout.bufend  = NULL;
1206      memout.bufsize = 64;
1207    }
1208    if (cmdentry.u.index != EXECCMD)
1209      popredir();
1210    if (e != -1) {
1211      if (e != EXERROR || cmdentry.special)
1212        exraise(e);
1213      popfilesupto(savetopfile);
1214      if (flags != EV_BACKCMD)
1215        FORCEINTON;
1216    }
1217  } else {
1218#ifdef DEBUG
1219    trputs("normal command:  ");
1220    trargs(argv);
1221#endif
1222    redirect(cmd->ncmd.redirect, 0);
1223    for (i = 0; i < varlist.count; i++)
1224      setvareq(varlist.args[i], VEXPORT | VSTACK);
1225    envp = environment();
1226    shellexec(argv, envp, path, cmdentry.u.index);
1227    /*NOTREACHED*/
1228  }
1229  goto out;
1230
1231parent:                  /* parent process gets here (if we forked) */
1232  if (mode == FORK_FG) { /* argument to fork */
1233    INTOFF;
1234    exitstatus = waitforjob(jp, &signaled);
1235    INTON;
1236    if (iflag && loopnest > 0 && signaled) {
1237      evalskip  = SKIPBREAK;
1238      skipcount = loopnest;
1239    }
1240  } else if (mode == FORK_NOJOB) {
1241    backcmd->fd = pip[0];
1242    close(pip[1]);
1243    backcmd->jp = jp;
1244  }
1245
1246out:
1247  if (lastarg)
1248    setvar("_", lastarg, 0);
1249  if (do_clearcmdentry)
1250    clearcmdentry();
1251}
1252
1253/*
1254 * Search for a command.  This is called before we fork so that the
1255 * location of the command will be available in the parent as well as
1256 * the child.  The check for "goodname" is an overly conservative
1257 * check that the name will not be subject to expansion.
1258 */
1259
1260static void
1261prehash(union node *n)
1262{
1263  struct cmdentry entry;
1264
1265  if (n && n->type == NCMD && n->ncmd.args)
1266    if (goodname(n->ncmd.args->narg.text))
1267      find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
1268}
1269
1270/*
1271 * Builtin commands.  Builtin commands whose functions are closely
1272 * tied to evaluation are implemented here.
1273 */
1274
1275/*
1276 * No command given, a bltin command with no arguments, or a bltin command
1277 * with an invalid name.
1278 */
1279
1280int
1281bltincmd(int argc, char **argv)
1282{
1283  if (argc > 1) {
1284    out2fmt_flush("%s: not found\n", argv[1]);
1285    return 127;
1286  }
1287  /*
1288   * Preserve exitstatus of a previous possible command substitution
1289   * as POSIX mandates
1290   */
1291  return exitstatus;
1292}
1293
1294/*
1295 * Handle break and continue commands.  Break, continue, and return are
1296 * all handled by setting the evalskip flag.  The evaluation routines
1297 * above all check this flag, and if it is set they start skipping
1298 * commands rather than executing them.  The variable skipcount is
1299 * the number of loops to break/continue, or the number of function
1300 * levels to return.  (The latter is always 1.)  It should probably
1301 * be an error to break out of more loops than exist, but it isn't
1302 * in the standard shell so we don't make it one here.
1303 */
1304
1305int
1306breakcmd(int argc, char **argv)
1307{
1308  long  n;
1309  char *end;
1310
1311  if (argc > 1) {
1312    /* Allow arbitrarily large numbers. */
1313    n = strtol(argv[1], &end, 10);
1314    if (!is_digit(argv[1][0]) || *end != '\0')
1315      error("Illegal number: %s", argv[1]);
1316  } else
1317    n = 1;
1318  if (n > loopnest)
1319    n = loopnest;
1320  if (n > 0) {
1321    evalskip  = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
1322    skipcount = n;
1323  }
1324  return 0;
1325}
1326
1327/*
1328 * The `command' command.
1329 */
1330int
1331commandcmd(int argc __unused, char **argv __unused)
1332{
1333  const char *path;
1334  int         ch;
1335  int         cmd = -1;
1336
1337  path = bltinlookup("PATH", 1);
1338
1339  while ((ch = nextopt("pvV")) != '\0') {
1340    switch (ch) {
1341      case 'p':
1342        path = ARUU_PATH_STDPATH;
1343        break;
1344      case 'v':
1345        cmd = TYPECMD_SMALLV;
1346        break;
1347      case 'V':
1348        cmd = TYPECMD_BIGV;
1349        break;
1350    }
1351  }
1352
1353  if (cmd != -1) {
1354    if (*argptr == NULL || argptr[1] != NULL)
1355      error("wrong number of arguments");
1356    return typecmd_impl(2, argptr - 1, cmd, path);
1357  }
1358  if (*argptr != NULL)
1359    error("commandcmd bad call");
1360
1361  /*
1362   * Do nothing successfully if no command was specified;
1363   * ksh also does this.
1364   */
1365  return 0;
1366}
1367
1368/*
1369 * The return command.
1370 */
1371
1372int
1373returncmd(int argc, char **argv)
1374{
1375  int ret = argc > 1 ? number(argv[1]) : oexitstatus;
1376
1377  evalskip  = SKIPRETURN;
1378  skipcount = 1;
1379  return ret;
1380}
1381
1382int
1383falsecmd(int argc __unused, char **argv __unused)
1384{
1385  return 1;
1386}
1387
1388int
1389truecmd(int argc __unused, char **argv __unused)
1390{
1391  return 0;
1392}
1393
1394int
1395execcmd(int argc, char **argv)
1396{
1397  int i;
1398
1399  /*
1400   * Because we have historically not supported any options,
1401   * only treat "--" specially.
1402   */
1403  if (argc > 1 && strcmp(argv[1], "--") == 0)
1404    argc--, argv++;
1405  if (argc > 1) {
1406    iflag = 0; /* exit on error */
1407    mflag = 0;
1408    optschanged();
1409    for (i = 0; i < cmdenviron->count; i++)
1410      setvareq(cmdenviron->args[i], VEXPORT | VSTACK);
1411    shellexec(argv + 1, environment(), pathval(), 0);
1412  }
1413  return 0;
1414}
1415
1416int
1417timescmd(int argc __unused, char **argv __unused)
1418{
1419  struct rusage ru;
1420  long          shumins, shsmins, chumins, chsmins;
1421  double        shusecs, shssecs, chusecs, chssecs;
1422
1423  if (getrusage(RUSAGE_SELF, &ru) < 0)
1424    return 1;
1425  shumins = ru.ru_utime.tv_sec / 60;
1426  shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1427  shsmins = ru.ru_stime.tv_sec / 60;
1428  shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1429  if (getrusage(RUSAGE_CHILDREN, &ru) < 0)
1430    return 1;
1431  chumins = ru.ru_utime.tv_sec / 60;
1432  chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.;
1433  chsmins = ru.ru_stime.tv_sec / 60;
1434  chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.;
1435  out1fmt(
1436      "%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n",
1437      shumins,
1438      shusecs,
1439      shsmins,
1440      shssecs,
1441      chumins,
1442      chusecs,
1443      chsmins,
1444      chssecs
1445  );
1446  return 0;
1447}