eval.c revision 218306
1178825Sdfr/*- 2178825Sdfr * Copyright (c) 1993 3178825Sdfr * The Regents of the University of California. All rights reserved. 4178825Sdfr * 5178825Sdfr * This code is derived from software contributed to Berkeley by 6178825Sdfr * Kenneth Almquist. 7178825Sdfr * 8178825Sdfr * Redistribution and use in source and binary forms, with or without 9178825Sdfr * modification, are permitted provided that the following conditions 10178825Sdfr * are met: 11233294Sstas * 1. Redistributions of source code must retain the above copyright 12178825Sdfr * notice, this list of conditions and the following disclaimer. 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 4. Neither the name of the University nor the names of its contributors 17178825Sdfr * may be used to endorse or promote products derived from this software 18178825Sdfr * without specific prior written permission. 19178825Sdfr * 20178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30178825Sdfr * SUCH DAMAGE. 31178825Sdfr */ 32178825Sdfr 33233294Sstas#ifndef lint 34178825Sdfr#if 0 35178825Sdfrstatic char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 36178825Sdfr#endif 37178825Sdfr#endif /* not lint */ 38178825Sdfr#include <sys/cdefs.h> 39178825Sdfr__FBSDID("$FreeBSD: head/bin/sh/eval.c 218306 2011-02-04 22:47:55Z jilles $"); 40178825Sdfr 41178825Sdfr#include <paths.h> 42178825Sdfr#include <signal.h> 43178825Sdfr#include <stdlib.h> 44178825Sdfr#include <unistd.h> 45178825Sdfr#include <sys/resource.h> 46178825Sdfr#include <sys/wait.h> /* For WIFSIGNALED(status) */ 47178825Sdfr#include <errno.h> 48178825Sdfr 49178825Sdfr/* 50178825Sdfr * Evaluate a command. 51178825Sdfr */ 52178825Sdfr 53178825Sdfr#include "shell.h" 54178825Sdfr#include "nodes.h" 55178825Sdfr#include "syntax.h" 56178825Sdfr#include "expand.h" 57178825Sdfr#include "parser.h" 58178825Sdfr#include "jobs.h" 59178825Sdfr#include "eval.h" 60178825Sdfr#include "builtins.h" 61178825Sdfr#include "options.h" 62178825Sdfr#include "exec.h" 63178825Sdfr#include "redir.h" 64178825Sdfr#include "input.h" 65178825Sdfr#include "output.h" 66178825Sdfr#include "trap.h" 67178825Sdfr#include "var.h" 68178825Sdfr#include "memalloc.h" 69178825Sdfr#include "error.h" 70178825Sdfr#include "show.h" 71178825Sdfr#include "mystring.h" 72178825Sdfr#ifndef NO_HISTORY 73178825Sdfr#include "myhistedit.h" 74178825Sdfr#endif 75178825Sdfr 76178825Sdfr 77178825Sdfrint evalskip; /* set if we are skipping commands */ 78178825Sdfrstatic int skipcount; /* number of levels to skip */ 79178825SdfrMKINIT int loopnest; /* current loop nesting level */ 80178825Sdfrint funcnest; /* depth of function calls */ 81178825Sdfrstatic int builtin_flags; /* evalcommand flags for builtins */ 82178825Sdfr 83178825Sdfr 84178825Sdfrchar *commandname; 85178825Sdfrstruct strlist *cmdenviron; 86178825Sdfrint exitstatus; /* exit status of last command */ 87178825Sdfrint oexitstatus; /* saved exit status */ 88178825Sdfr 89178825Sdfr 90178825Sdfrstatic void evalloop(union node *, int); 91178825Sdfrstatic void evalfor(union node *, int); 92178825Sdfrstatic void evalcase(union node *, int); 93178825Sdfrstatic void evalsubshell(union node *, int); 94178825Sdfrstatic void evalredir(union node *, int); 95178825Sdfrstatic void expredir(union node *); 96233294Sstasstatic void evalpipe(union node *); 97233294Sstasstatic int is_valid_fast_cmdsubst(union node *n); 98178825Sdfrstatic void evalcommand(union node *, int, struct backcmd *); 99178825Sdfrstatic void prehash(union node *); 100178825Sdfr 101233294Sstas 102178825Sdfr/* 103178825Sdfr * Called to reset things after an exception. 104178825Sdfr */ 105178825Sdfr 106178825Sdfr#ifdef mkinit 107178825SdfrINCLUDE "eval.h" 108178825Sdfr 109178825SdfrRESET { 110178825Sdfr evalskip = 0; 111178825Sdfr loopnest = 0; 112178825Sdfr funcnest = 0; 113178825Sdfr} 114178825Sdfr#endif 115178825Sdfr 116178825Sdfr 117178825Sdfr 118178825Sdfr/* 119178825Sdfr * The eval command. 120178825Sdfr */ 121178825Sdfr 122178825Sdfrint 123178825Sdfrevalcmd(int argc, char **argv) 124178825Sdfr{ 125178825Sdfr char *p; 126178825Sdfr char *concat; 127178825Sdfr char **ap; 128178825Sdfr 129178825Sdfr if (argc > 1) { 130178825Sdfr p = argv[1]; 131178825Sdfr if (argc > 2) { 132178825Sdfr STARTSTACKSTR(concat); 133178825Sdfr ap = argv + 2; 134178825Sdfr for (;;) { 135178825Sdfr STPUTS(p, concat); 136178825Sdfr if ((p = *ap++) == NULL) 137178825Sdfr break; 138178825Sdfr STPUTC(' ', concat); 139178825Sdfr } 140178825Sdfr STPUTC('\0', concat); 141178825Sdfr p = grabstackstr(concat); 142178825Sdfr } 143178825Sdfr evalstring(p, builtin_flags & EV_TESTED); 144178825Sdfr } else 145178825Sdfr exitstatus = 0; 146178825Sdfr return exitstatus; 147178825Sdfr} 148178825Sdfr 149178825Sdfr 150178825Sdfr/* 151178825Sdfr * Execute a command or commands contained in a string. 152178825Sdfr */ 153178825Sdfr 154178825Sdfrvoid 155233294Sstasevalstring(char *s, int flags) 156233294Sstas{ 157233294Sstas union node *n; 158233294Sstas struct stackmark smark; 159178825Sdfr int flags_exit; 160233294Sstas int any; 161233294Sstas 162178825Sdfr flags_exit = flags & EV_EXIT; 163178825Sdfr flags &= ~EV_EXIT; 164178825Sdfr any = 0; 165178825Sdfr setstackmark(&smark); 166178825Sdfr setinputstring(s, 1); 167178825Sdfr while ((n = parsecmd(0)) != NEOF) { 168178825Sdfr if (n != NULL) { 169178825Sdfr if (flags_exit && preadateof()) 170178825Sdfr evaltree(n, flags | EV_EXIT); 171178825Sdfr else 172178825Sdfr evaltree(n, flags); 173178825Sdfr any = 1; 174178825Sdfr } 175178825Sdfr popstackmark(&smark); 176178825Sdfr } 177178825Sdfr popfile(); 178178825Sdfr popstackmark(&smark); 179178825Sdfr if (!any) 180178825Sdfr exitstatus = 0; 181178825Sdfr if (flags_exit) 182178825Sdfr exitshell(exitstatus); 183178825Sdfr} 184178825Sdfr 185178825Sdfr 186178825Sdfr/* 187178825Sdfr * Evaluate a parse tree. The value is left in the global variable 188178825Sdfr * exitstatus. 189178825Sdfr */ 190178825Sdfr 191178825Sdfrvoid 192178825Sdfrevaltree(union node *n, int flags) 193178825Sdfr{ 194178825Sdfr int do_etest; 195178825Sdfr union node *next; 196178825Sdfr 197178825Sdfr do_etest = 0; 198178825Sdfr if (n == NULL) { 199178825Sdfr TRACE(("evaltree(NULL) called\n")); 200178825Sdfr exitstatus = 0; 201178825Sdfr goto out; 202178825Sdfr } 203233294Sstas do { 204178825Sdfr next = NULL; 205178825Sdfr#ifndef NO_HISTORY 206178825Sdfr displayhist = 1; /* show history substitutions done with fc */ 207178825Sdfr#endif 208178825Sdfr TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); 209178825Sdfr switch (n->type) { 210178825Sdfr case NSEMI: 211178825Sdfr evaltree(n->nbinary.ch1, flags & ~EV_EXIT); 212178825Sdfr if (evalskip) 213178825Sdfr goto out; 214178825Sdfr next = n->nbinary.ch2; 215178825Sdfr break; 216178825Sdfr case NAND: 217178825Sdfr evaltree(n->nbinary.ch1, EV_TESTED); 218178825Sdfr if (evalskip || exitstatus != 0) { 219178825Sdfr goto out; 220178825Sdfr } 221178825Sdfr next = n->nbinary.ch2; 222178825Sdfr break; 223178825Sdfr case NOR: 224178825Sdfr evaltree(n->nbinary.ch1, EV_TESTED); 225178825Sdfr if (evalskip || exitstatus == 0) 226178825Sdfr goto out; 227178825Sdfr next = n->nbinary.ch2; 228178825Sdfr break; 229178825Sdfr case NREDIR: 230178825Sdfr evalredir(n, flags); 231178825Sdfr break; 232178825Sdfr case NSUBSHELL: 233178825Sdfr evalsubshell(n, flags); 234178825Sdfr do_etest = !(flags & EV_TESTED); 235178825Sdfr break; 236178825Sdfr case NBACKGND: 237178825Sdfr evalsubshell(n, flags); 238178825Sdfr break; 239178825Sdfr case NIF: { 240178825Sdfr evaltree(n->nif.test, EV_TESTED); 241178825Sdfr if (evalskip) 242178825Sdfr goto out; 243178825Sdfr if (exitstatus == 0) 244178825Sdfr next = n->nif.ifpart; 245178825Sdfr else if (n->nif.elsepart) 246178825Sdfr next = n->nif.elsepart; 247178825Sdfr else 248178825Sdfr exitstatus = 0; 249178825Sdfr break; 250178825Sdfr } 251178825Sdfr case NWHILE: 252178825Sdfr case NUNTIL: 253178825Sdfr evalloop(n, flags & ~EV_EXIT); 254178825Sdfr break; 255178825Sdfr case NFOR: 256178825Sdfr evalfor(n, flags & ~EV_EXIT); 257178825Sdfr break; 258178825Sdfr case NCASE: 259178825Sdfr evalcase(n, flags); 260178825Sdfr break; 261178825Sdfr case NDEFUN: 262178825Sdfr defun(n->narg.text, n->narg.next); 263178825Sdfr exitstatus = 0; 264178825Sdfr break; 265178825Sdfr case NNOT: 266178825Sdfr evaltree(n->nnot.com, EV_TESTED); 267178825Sdfr exitstatus = !exitstatus; 268178825Sdfr break; 269178825Sdfr 270178825Sdfr case NPIPE: 271178825Sdfr evalpipe(n); 272178825Sdfr do_etest = !(flags & EV_TESTED); 273233294Sstas break; 274233294Sstas case NCMD: 275178825Sdfr evalcommand(n, flags, (struct backcmd *)NULL); 276178825Sdfr do_etest = !(flags & EV_TESTED); 277178825Sdfr break; 278178825Sdfr default: 279178825Sdfr out1fmt("Node type = %d\n", n->type); 280178825Sdfr flushout(&output); 281178825Sdfr break; 282178825Sdfr } 283178825Sdfr n = next; 284178825Sdfr } while (n != NULL); 285178825Sdfrout: 286178825Sdfr if (pendingsigs) 287178825Sdfr dotrap(); 288178825Sdfr if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest)) 289178825Sdfr exitshell(exitstatus); 290178825Sdfr} 291178825Sdfr 292178825Sdfr 293178825Sdfrstatic void 294178825Sdfrevalloop(union node *n, int flags) 295178825Sdfr{ 296178825Sdfr int status; 297178825Sdfr 298178825Sdfr loopnest++; 299178825Sdfr status = 0; 300178825Sdfr for (;;) { 301178825Sdfr evaltree(n->nbinary.ch1, EV_TESTED); 302233294Sstas if (evalskip) { 303178825Sdfrskipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 304178825Sdfr evalskip = 0; 305178825Sdfr continue; 306178825Sdfr } 307178825Sdfr if (evalskip == SKIPBREAK && --skipcount <= 0) 308178825Sdfr evalskip = 0; 309178825Sdfr if (evalskip == SKIPFUNC || evalskip == SKIPFILE) 310178825Sdfr status = exitstatus; 311178825Sdfr break; 312178825Sdfr } 313178825Sdfr if (n->type == NWHILE) { 314178825Sdfr if (exitstatus != 0) 315178825Sdfr break; 316178825Sdfr } else { 317178825Sdfr if (exitstatus == 0) 318178825Sdfr break; 319178825Sdfr } 320178825Sdfr evaltree(n->nbinary.ch2, flags); 321178825Sdfr status = exitstatus; 322178825Sdfr if (evalskip) 323178825Sdfr goto skipping; 324178825Sdfr } 325178825Sdfr loopnest--; 326178825Sdfr exitstatus = status; 327178825Sdfr} 328178825Sdfr 329178825Sdfr 330178825Sdfr 331178825Sdfrstatic void 332178825Sdfrevalfor(union node *n, int flags) 333178825Sdfr{ 334178825Sdfr struct arglist arglist; 335178825Sdfr union node *argp; 336178825Sdfr struct strlist *sp; 337178825Sdfr struct stackmark smark; 338178825Sdfr 339178825Sdfr setstackmark(&smark); 340178825Sdfr arglist.lastp = &arglist.list; 341178825Sdfr for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 342178825Sdfr oexitstatus = exitstatus; 343178825Sdfr expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 344178825Sdfr if (evalskip) 345178825Sdfr goto out; 346178825Sdfr } 347178825Sdfr *arglist.lastp = NULL; 348178825Sdfr 349178825Sdfr exitstatus = 0; 350178825Sdfr loopnest++; 351178825Sdfr for (sp = arglist.list ; sp ; sp = sp->next) { 352178825Sdfr setvar(n->nfor.var, sp->text, 0); 353178825Sdfr evaltree(n->nfor.body, flags); 354178825Sdfr if (evalskip) { 355178825Sdfr if (evalskip == SKIPCONT && --skipcount <= 0) { 356178825Sdfr evalskip = 0; 357178825Sdfr continue; 358178825Sdfr } 359178825Sdfr if (evalskip == SKIPBREAK && --skipcount <= 0) 360178825Sdfr evalskip = 0; 361178825Sdfr break; 362178825Sdfr } 363178825Sdfr } 364178825Sdfr loopnest--; 365178825Sdfrout: 366178825Sdfr popstackmark(&smark); 367178825Sdfr} 368178825Sdfr 369178825Sdfr 370178825Sdfr 371178825Sdfrstatic void 372178825Sdfrevalcase(union node *n, int flags) 373178825Sdfr{ 374178825Sdfr union node *cp; 375178825Sdfr union node *patp; 376178825Sdfr struct arglist arglist; 377178825Sdfr struct stackmark smark; 378178825Sdfr 379178825Sdfr setstackmark(&smark); 380178825Sdfr arglist.lastp = &arglist.list; 381178825Sdfr oexitstatus = exitstatus; 382178825Sdfr exitstatus = 0; 383178825Sdfr expandarg(n->ncase.expr, &arglist, EXP_TILDE); 384178825Sdfr for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 385178825Sdfr for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 386178825Sdfr if (casematch(patp, arglist.list->text)) { 387178825Sdfr if (evalskip == 0) { 388178825Sdfr evaltree(cp->nclist.body, flags); 389178825Sdfr } 390178825Sdfr goto out; 391178825Sdfr } 392178825Sdfr } 393178825Sdfr } 394178825Sdfrout: 395178825Sdfr popstackmark(&smark); 396178825Sdfr} 397178825Sdfr 398178825Sdfr 399178825Sdfr 400178825Sdfr/* 401178825Sdfr * Kick off a subshell to evaluate a tree. 402178825Sdfr */ 403178825Sdfr 404178825Sdfrstatic void 405178825Sdfrevalsubshell(union node *n, int flags) 406178825Sdfr{ 407178825Sdfr struct job *jp; 408178825Sdfr int backgnd = (n->type == NBACKGND); 409178825Sdfr 410178825Sdfr expredir(n->nredir.redirect); 411178825Sdfr if ((!backgnd && flags & EV_EXIT && !have_traps()) || 412178825Sdfr forkshell(jp = makejob(n, 1), n, backgnd) == 0) { 413178825Sdfr if (backgnd) 414178825Sdfr flags &=~ EV_TESTED; 415178825Sdfr redirect(n->nredir.redirect, 0); 416178825Sdfr evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 417178825Sdfr } else if (! backgnd) { 418178825Sdfr INTOFF; 419178825Sdfr exitstatus = waitforjob(jp, (int *)NULL); 420178825Sdfr INTON; 421178825Sdfr } 422178825Sdfr} 423178825Sdfr 424178825Sdfr 425178825Sdfr/* 426178825Sdfr * Evaluate a redirected compound command. 427178825Sdfr */ 428178825Sdfr 429178825Sdfrstatic void 430178825Sdfrevalredir(union node *n, int flags) 431178825Sdfr{ 432178825Sdfr struct jmploc jmploc; 433178825Sdfr struct jmploc *savehandler; 434178825Sdfr volatile int in_redirect = 1; 435178825Sdfr 436178825Sdfr expredir(n->nredir.redirect); 437178825Sdfr savehandler = handler; 438178825Sdfr if (setjmp(jmploc.loc)) { 439178825Sdfr int e; 440178825Sdfr 441178825Sdfr handler = savehandler; 442178825Sdfr e = exception; 443178825Sdfr if (e == EXERROR || e == EXEXEC) { 444178825Sdfr popredir(); 445178825Sdfr if (in_redirect) { 446178825Sdfr exitstatus = 2; 447178825Sdfr return; 448178825Sdfr } 449178825Sdfr } 450178825Sdfr longjmp(handler->loc, 1); 451178825Sdfr } else { 452178825Sdfr INTOFF; 453178825Sdfr handler = &jmploc; 454178825Sdfr redirect(n->nredir.redirect, REDIR_PUSH); 455178825Sdfr in_redirect = 0; 456178825Sdfr INTON; 457178825Sdfr evaltree(n->nredir.n, flags); 458178825Sdfr } 459178825Sdfr INTOFF; 460178825Sdfr handler = savehandler; 461178825Sdfr popredir(); 462178825Sdfr INTON; 463178825Sdfr} 464178825Sdfr 465178825Sdfr 466178825Sdfr/* 467178825Sdfr * Compute the names of the files in a redirection list. 468178825Sdfr */ 469178825Sdfr 470178825Sdfrstatic void 471178825Sdfrexpredir(union node *n) 472178825Sdfr{ 473178825Sdfr union node *redir; 474178825Sdfr 475178825Sdfr for (redir = n ; redir ; redir = redir->nfile.next) { 476178825Sdfr struct arglist fn; 477178825Sdfr fn.lastp = &fn.list; 478233294Sstas oexitstatus = exitstatus; 479233294Sstas switch (redir->type) { 480233294Sstas case NFROM: 481178825Sdfr case NTO: 482233294Sstas case NFROMTO: 483233294Sstas case NAPPEND: 484233294Sstas case NCLOBBER: 485178825Sdfr expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 486233294Sstas redir->nfile.expfname = fn.list->text; 487233294Sstas break; 488178825Sdfr case NFROMFD: 489233294Sstas case NTOFD: 490233294Sstas if (redir->ndup.vname) { 491233294Sstas expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); 492178825Sdfr fixredir(redir, fn.list->text, 1); 493233294Sstas } 494233294Sstas break; 495233294Sstas } 496178825Sdfr } 497233294Sstas} 498233294Sstas 499233294Sstas 500233294Sstas 501233294Sstas/* 502233294Sstas * Evaluate a pipeline. All the processes in the pipeline are children 503233294Sstas * of the process creating the pipeline. (This differs from some versions 504233294Sstas * of the shell, which make the last process in a pipeline the parent 505233294Sstas * of all the rest.) 506233294Sstas */ 507233294Sstas 508178825Sdfrstatic void 509178825Sdfrevalpipe(union node *n) 510178825Sdfr{ 511178825Sdfr struct job *jp; 512178825Sdfr struct nodelist *lp; 513178825Sdfr int pipelen; 514178825Sdfr int prevfd; 515178825Sdfr int pip[2]; 516178825Sdfr 517178825Sdfr TRACE(("evalpipe(%p) called\n", (void *)n)); 518178825Sdfr pipelen = 0; 519178825Sdfr for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 520178825Sdfr pipelen++; 521178825Sdfr INTOFF; 522178825Sdfr jp = makejob(n, pipelen); 523178825Sdfr prevfd = -1; 524178825Sdfr for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 525178825Sdfr prehash(lp->n); 526178825Sdfr pip[1] = -1; 527178825Sdfr if (lp->next) { 528178825Sdfr if (pipe(pip) < 0) { 529233294Sstas close(prevfd); 530178825Sdfr error("Pipe call failed: %s", strerror(errno)); 531178825Sdfr } 532178825Sdfr } 533178825Sdfr if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 534178825Sdfr INTON; 535178825Sdfr if (prevfd > 0) { 536178825Sdfr dup2(prevfd, 0); 537178825Sdfr close(prevfd); 538178825Sdfr } 539178825Sdfr if (pip[1] >= 0) { 540178825Sdfr if (!(prevfd >= 0 && pip[0] == 0)) 541178825Sdfr close(pip[0]); 542178825Sdfr if (pip[1] != 1) { 543178825Sdfr dup2(pip[1], 1); 544178825Sdfr close(pip[1]); 545178825Sdfr } 546178825Sdfr } 547233294Sstas evaltree(lp->n, EV_EXIT); 548233294Sstas } 549233294Sstas if (prevfd >= 0) 550233294Sstas close(prevfd); 551233294Sstas prevfd = pip[0]; 552233294Sstas close(pip[1]); 553233294Sstas } 554233294Sstas INTON; 555233294Sstas if (n->npipe.backgnd == 0) { 556233294Sstas INTOFF; 557233294Sstas exitstatus = waitforjob(jp, (int *)NULL); 558233294Sstas TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 559233294Sstas INTON; 560233294Sstas } 561233294Sstas} 562233294Sstas 563233294Sstas 564233294Sstas 565233294Sstasstatic int 566233294Sstasis_valid_fast_cmdsubst(union node *n) 567233294Sstas{ 568233294Sstas union node *argp; 569233294Sstas 570233294Sstas if (n->type != NCMD) 571233294Sstas return 0; 572233294Sstas for (argp = n->ncmd.args ; argp ; argp = argp->narg.next) 573233294Sstas if (expandhassideeffects(argp->narg.text)) 574233294Sstas return 0; 575233294Sstas return 1; 576178825Sdfr} 577178825Sdfr 578178825Sdfr/* 579178825Sdfr * Execute a command inside back quotes. If it's a builtin command, we 580178825Sdfr * want to save its output in a block obtained from malloc. Otherwise 581178825Sdfr * we fork off a subprocess and get the output of the command via a pipe. 582178825Sdfr * Should be called with interrupts off. 583178825Sdfr */ 584178825Sdfr 585178825Sdfrvoid 586178825Sdfrevalbackcmd(union node *n, struct backcmd *result) 587178825Sdfr{ 588178825Sdfr int pip[2]; 589178825Sdfr struct job *jp; 590178825Sdfr struct stackmark smark; /* unnecessary */ 591178825Sdfr struct jmploc jmploc; 592178825Sdfr struct jmploc *savehandler; 593178825Sdfr 594178825Sdfr setstackmark(&smark); 595178825Sdfr result->fd = -1; 596178825Sdfr result->buf = NULL; 597178825Sdfr result->nleft = 0; 598178825Sdfr result->jp = NULL; 599178825Sdfr if (n == NULL) { 600178825Sdfr exitstatus = 0; 601178825Sdfr goto out; 602178825Sdfr } 603178825Sdfr if (is_valid_fast_cmdsubst(n)) { 604178825Sdfr exitstatus = oexitstatus; 605178825Sdfr savehandler = handler; 606178825Sdfr if (setjmp(jmploc.loc)) { 607178825Sdfr if (exception == EXERROR || exception == EXEXEC) 608178825Sdfr exitstatus = 2; 609178825Sdfr else if (exception != 0) { 610178825Sdfr handler = savehandler; 611178825Sdfr longjmp(handler->loc, 1); 612178825Sdfr } 613178825Sdfr } else { 614178825Sdfr handler = &jmploc; 615178825Sdfr evalcommand(n, EV_BACKCMD, result); 616233294Sstas } 617178825Sdfr handler = savehandler; 618178825Sdfr } else { 619178825Sdfr exitstatus = 0; 620178825Sdfr if (pipe(pip) < 0) 621178825Sdfr error("Pipe call failed: %s", strerror(errno)); 622178825Sdfr jp = makejob(n, 1); 623178825Sdfr if (forkshell(jp, n, FORK_NOJOB) == 0) { 624178825Sdfr FORCEINTON; 625178825Sdfr close(pip[0]); 626178825Sdfr if (pip[1] != 1) { 627233294Sstas dup2(pip[1], 1); 628178825Sdfr close(pip[1]); 629178825Sdfr } 630178825Sdfr evaltree(n, EV_EXIT); 631178825Sdfr } 632178825Sdfr close(pip[1]); 633178825Sdfr result->fd = pip[0]; 634178825Sdfr result->jp = jp; 635178825Sdfr } 636178825Sdfrout: 637178825Sdfr popstackmark(&smark); 638178825Sdfr TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", 639178825Sdfr result->fd, result->buf, result->nleft, result->jp)); 640178825Sdfr} 641178825Sdfr 642178825Sdfr/* 643178825Sdfr * Check if a builtin can safely be executed in the same process, 644178825Sdfr * even though it should be in a subshell (command substitution). 645178825Sdfr * Note that jobid, jobs, times and trap can show information not 646178825Sdfr * available in a child process; this is deliberate. 647178825Sdfr * The arguments should already have been expanded. 648178825Sdfr */ 649178825Sdfrstatic int 650178825Sdfrsafe_builtin(int idx, int argc, char **argv) 651178825Sdfr{ 652178825Sdfr if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || 653178825Sdfr idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || 654178825Sdfr idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || 655178825Sdfr idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || 656178825Sdfr idx == TYPECMD) 657178825Sdfr return (1); 658178825Sdfr if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || 659178825Sdfr idx == UMASKCMD) 660178825Sdfr return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); 661178825Sdfr if (idx == SETCMD) 662178825Sdfr return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || 663178825Sdfr argv[1][0] == '+') && argv[1][1] == 'o' && 664178825Sdfr argv[1][2] == '\0')); 665178825Sdfr return (0); 666178825Sdfr} 667178825Sdfr 668178825Sdfr/* 669178825Sdfr * Execute a simple command. 670178825Sdfr * Note: This may or may not return if (flags & EV_EXIT). 671178825Sdfr */ 672178825Sdfr 673178825Sdfrstatic void 674178825Sdfrevalcommand(union node *cmd, int flags, struct backcmd *backcmd) 675178825Sdfr{ 676178825Sdfr struct stackmark smark; 677178825Sdfr union node *argp; 678178825Sdfr struct arglist arglist; 679178825Sdfr struct arglist varlist; 680178825Sdfr char **argv; 681178825Sdfr int argc; 682178825Sdfr char **envp; 683178825Sdfr int varflag; 684178825Sdfr struct strlist *sp; 685178825Sdfr int mode; 686178825Sdfr int pip[2]; 687178825Sdfr struct cmdentry cmdentry; 688178825Sdfr struct job *jp; 689178825Sdfr struct jmploc jmploc; 690178825Sdfr struct jmploc *savehandler; 691178825Sdfr char *savecmdname; 692178825Sdfr struct shparam saveparam; 693178825Sdfr struct localvar *savelocalvars; 694178825Sdfr struct parsefile *savetopfile; 695178825Sdfr volatile int e; 696178825Sdfr char *lastarg; 697178825Sdfr int realstatus; 698178825Sdfr int do_clearcmdentry; 699178825Sdfr const char *path = pathval(); 700178825Sdfr 701178825Sdfr /* First expand the arguments. */ 702178825Sdfr TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); 703178825Sdfr setstackmark(&smark); 704178825Sdfr arglist.lastp = &arglist.list; 705178825Sdfr varlist.lastp = &varlist.list; 706178825Sdfr varflag = 1; 707178825Sdfr jp = NULL; 708178825Sdfr do_clearcmdentry = 0; 709233294Sstas oexitstatus = exitstatus; 710178825Sdfr exitstatus = 0; 711233294Sstas for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 712178825Sdfr char *p = argp->narg.text; 713178825Sdfr if (varflag && is_name(*p)) { 714178825Sdfr do { 715178825Sdfr p++; 716178825Sdfr } while (is_in_name(*p)); 717178825Sdfr if (*p == '=') { 718178825Sdfr expandarg(argp, &varlist, EXP_VARTILDE); 719178825Sdfr continue; 720178825Sdfr } 721178825Sdfr } 722178825Sdfr expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 723178825Sdfr varflag = 0; 724178825Sdfr } 725178825Sdfr *arglist.lastp = NULL; 726178825Sdfr *varlist.lastp = NULL; 727178825Sdfr expredir(cmd->ncmd.redirect); 728178825Sdfr argc = 0; 729178825Sdfr for (sp = arglist.list ; sp ; sp = sp->next) 730178825Sdfr argc++; 731178825Sdfr /* Add one slot at the beginning for tryexec(). */ 732178825Sdfr argv = stalloc(sizeof (char *) * (argc + 2)); 733178825Sdfr argv++; 734178825Sdfr 735178825Sdfr for (sp = arglist.list ; sp ; sp = sp->next) { 736178825Sdfr TRACE(("evalcommand arg: %s\n", sp->text)); 737178825Sdfr *argv++ = sp->text; 738178825Sdfr } 739178825Sdfr *argv = NULL; 740178825Sdfr lastarg = NULL; 741178825Sdfr if (iflag && funcnest == 0 && argc > 0) 742178825Sdfr lastarg = argv[-1]; 743178825Sdfr argv -= argc; 744178825Sdfr 745178825Sdfr /* Print the command if xflag is set. */ 746178825Sdfr if (xflag) { 747178825Sdfr char sep = 0; 748178825Sdfr const char *p; 749178825Sdfr out2str(ps4val()); 750178825Sdfr for (sp = varlist.list ; sp ; sp = sp->next) { 751178825Sdfr if (sep != 0) 752178825Sdfr out2c(' '); 753178825Sdfr p = strchr(sp->text, '='); 754178825Sdfr if (p != NULL) { 755178825Sdfr p++; 756178825Sdfr outbin(sp->text, p - sp->text, out2); 757178825Sdfr out2qstr(p); 758178825Sdfr } else 759178825Sdfr out2qstr(sp->text); 760178825Sdfr sep = ' '; 761178825Sdfr } 762178825Sdfr for (sp = arglist.list ; sp ; sp = sp->next) { 763178825Sdfr if (sep != 0) 764178825Sdfr out2c(' '); 765178825Sdfr /* Disambiguate command looking like assignment. */ 766178825Sdfr if (sp == arglist.list && 767178825Sdfr strchr(sp->text, '=') != NULL && 768178825Sdfr strchr(sp->text, '\'') == NULL) { 769178825Sdfr out2c('\''); 770178825Sdfr out2str(sp->text); 771178825Sdfr out2c('\''); 772178825Sdfr } else 773178825Sdfr out2qstr(sp->text); 774178825Sdfr sep = ' '; 775178825Sdfr } 776178825Sdfr out2c('\n'); 777178825Sdfr flushout(&errout); 778178825Sdfr } 779178825Sdfr 780178825Sdfr /* Now locate the command. */ 781178825Sdfr if (argc == 0) { 782178825Sdfr /* Variable assignment(s) without command */ 783178825Sdfr cmdentry.cmdtype = CMDBUILTIN; 784178825Sdfr cmdentry.u.index = BLTINCMD; 785178825Sdfr cmdentry.special = 0; 786178825Sdfr } else { 787178825Sdfr static const char PATH[] = "PATH="; 788178825Sdfr int cmd_flags = 0, bltinonly = 0; 789178825Sdfr 790178825Sdfr /* 791178825Sdfr * Modify the command lookup path, if a PATH= assignment 792178825Sdfr * is present 793178825Sdfr */ 794178825Sdfr for (sp = varlist.list ; sp ; sp = sp->next) 795178825Sdfr if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { 796233294Sstas path = sp->text + sizeof(PATH) - 1; 797178825Sdfr /* 798178825Sdfr * On `PATH=... command`, we need to make 799178825Sdfr * sure that the command isn't using the 800178825Sdfr * non-updated hash table of the outer PATH 801233294Sstas * setting and we need to make sure that 802178825Sdfr * the hash table isn't filled with items 803178825Sdfr * from the temporary setting. 804178825Sdfr * 805178825Sdfr * It would be better to forbit using and 806233294Sstas * updating the table while this command 807178825Sdfr * runs, by the command finding mechanism 808178825Sdfr * is heavily integrated with hash handling, 809178825Sdfr * so we just delete the hash before and after 810178825Sdfr * the command runs. Partly deleting like 811233294Sstas * changepatch() does doesn't seem worth the 812178825Sdfr * bookinging effort, since most such runs add 813178825Sdfr * directories in front of the new PATH. 814178825Sdfr */ 815178825Sdfr clearcmdentry(0); 816233294Sstas do_clearcmdentry = 1; 817178825Sdfr } 818178825Sdfr 819178825Sdfr for (;;) { 820178825Sdfr if (bltinonly) { 821233294Sstas cmdentry.u.index = find_builtin(*argv, &cmdentry.special); 822178825Sdfr if (cmdentry.u.index < 0) { 823178825Sdfr cmdentry.u.index = BLTINCMD; 824178825Sdfr argv--; 825178825Sdfr argc++; 826233294Sstas break; 827178825Sdfr } 828178825Sdfr } else 829178825Sdfr find_command(argv[0], &cmdentry, cmd_flags, path); 830178825Sdfr /* implement the bltin and command builtins here */ 831233294Sstas if (cmdentry.cmdtype != CMDBUILTIN) 832178825Sdfr break; 833178825Sdfr if (cmdentry.u.index == BLTINCMD) { 834178825Sdfr if (argc == 1) 835178825Sdfr break; 836233294Sstas argv++; 837178825Sdfr argc--; 838178825Sdfr bltinonly = 1; 839178825Sdfr } else if (cmdentry.u.index == COMMANDCMD) { 840178825Sdfr if (argc == 1) 841233294Sstas break; 842178825Sdfr if (!strcmp(argv[1], "-p")) { 843178825Sdfr if (argc == 2) 844178825Sdfr break; 845178825Sdfr if (argv[2][0] == '-') { 846233294Sstas if (strcmp(argv[2], "--")) 847178825Sdfr break; 848178825Sdfr if (argc == 3) 849178825Sdfr break; 850178825Sdfr argv += 3; 851178825Sdfr argc -= 3; 852233294Sstas } else { 853178825Sdfr argv += 2; 854178825Sdfr argc -= 2; 855178825Sdfr } 856178825Sdfr path = _PATH_STDPATH; 857233294Sstas clearcmdentry(0); 858178825Sdfr do_clearcmdentry = 1; 859178825Sdfr } else if (!strcmp(argv[1], "--")) { 860178825Sdfr if (argc == 2) 861178825Sdfr break; 862233294Sstas argv += 2; 863178825Sdfr argc -= 2; 864178825Sdfr } else if (argv[1][0] == '-') 865178825Sdfr break; 866178825Sdfr else { 867233294Sstas argv++; 868178825Sdfr argc--; 869178825Sdfr } 870178825Sdfr cmd_flags |= DO_NOFUNC; 871178825Sdfr bltinonly = 0; 872233294Sstas } else 873178825Sdfr break; 874178825Sdfr } 875233294Sstas /* 876178825Sdfr * Special builtins lose their special properties when 877178825Sdfr * called via 'command'. 878178825Sdfr */ 879178825Sdfr if (cmd_flags & DO_NOFUNC) 880178825Sdfr cmdentry.special = 0; 881178825Sdfr } 882178825Sdfr 883178825Sdfr /* Fork off a child process if necessary. */ 884178825Sdfr if (cmd->ncmd.backgnd 885178825Sdfr || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) 886178825Sdfr && ((flags & EV_EXIT) == 0 || have_traps())) 887178825Sdfr || ((flags & EV_BACKCMD) != 0 888178825Sdfr && (cmdentry.cmdtype != CMDBUILTIN || 889178825Sdfr !safe_builtin(cmdentry.u.index, argc, argv)))) { 890178825Sdfr jp = makejob(cmd, 1); 891178825Sdfr mode = cmd->ncmd.backgnd; 892178825Sdfr if (flags & EV_BACKCMD) { 893178825Sdfr mode = FORK_NOJOB; 894178825Sdfr if (pipe(pip) < 0) 895178825Sdfr error("Pipe call failed: %s", strerror(errno)); 896178825Sdfr } 897178825Sdfr if (forkshell(jp, cmd, mode) != 0) 898178825Sdfr goto parent; /* at end of routine */ 899178825Sdfr if (flags & EV_BACKCMD) { 900178825Sdfr FORCEINTON; 901178825Sdfr close(pip[0]); 902178825Sdfr if (pip[1] != 1) { 903178825Sdfr dup2(pip[1], 1); 904178825Sdfr close(pip[1]); 905178825Sdfr } 906178825Sdfr } 907178825Sdfr flags |= EV_EXIT; 908178825Sdfr } 909178825Sdfr 910178825Sdfr /* This is the child process if a fork occurred. */ 911178825Sdfr /* Execute the command. */ 912178825Sdfr if (cmdentry.cmdtype == CMDFUNCTION) { 913178825Sdfr#ifdef DEBUG 914178825Sdfr trputs("Shell function: "); trargs(argv); 915178825Sdfr#endif 916178825Sdfr saveparam = shellparam; 917178825Sdfr shellparam.malloc = 0; 918178825Sdfr shellparam.reset = 1; 919178825Sdfr shellparam.nparam = argc - 1; 920178825Sdfr shellparam.p = argv + 1; 921178825Sdfr shellparam.optnext = NULL; 922178825Sdfr INTOFF; 923178825Sdfr savelocalvars = localvars; 924178825Sdfr localvars = NULL; 925178825Sdfr reffunc(cmdentry.u.func); 926178825Sdfr savehandler = handler; 927178825Sdfr if (setjmp(jmploc.loc)) { 928178825Sdfr freeparam(&shellparam); 929178825Sdfr shellparam = saveparam; 930178825Sdfr if (exception == EXERROR || exception == EXEXEC) 931178825Sdfr popredir(); 932178825Sdfr unreffunc(cmdentry.u.func); 933178825Sdfr poplocalvars(); 934178825Sdfr localvars = savelocalvars; 935178825Sdfr funcnest--; 936178825Sdfr handler = savehandler; 937178825Sdfr longjmp(handler->loc, 1); 938178825Sdfr } 939178825Sdfr handler = &jmploc; 940178825Sdfr funcnest++; 941178825Sdfr redirect(cmd->ncmd.redirect, REDIR_PUSH); 942178825Sdfr INTON; 943178825Sdfr for (sp = varlist.list ; sp ; sp = sp->next) 944178825Sdfr mklocal(sp->text); 945178825Sdfr exitstatus = oexitstatus; 946178825Sdfr if (flags & EV_TESTED) 947178825Sdfr evaltree(getfuncnode(cmdentry.u.func), EV_TESTED); 948178825Sdfr else 949178825Sdfr evaltree(getfuncnode(cmdentry.u.func), 0); 950178825Sdfr INTOFF; 951178825Sdfr unreffunc(cmdentry.u.func); 952178825Sdfr poplocalvars(); 953178825Sdfr localvars = savelocalvars; 954178825Sdfr freeparam(&shellparam); 955178825Sdfr shellparam = saveparam; 956178825Sdfr handler = savehandler; 957178825Sdfr funcnest--; 958178825Sdfr popredir(); 959178825Sdfr INTON; 960178825Sdfr if (evalskip == SKIPFUNC) { 961178825Sdfr evalskip = 0; 962178825Sdfr skipcount = 0; 963178825Sdfr } 964178825Sdfr if (jp) 965178825Sdfr exitshell(exitstatus); 966178825Sdfr } else if (cmdentry.cmdtype == CMDBUILTIN) { 967178825Sdfr#ifdef DEBUG 968178825Sdfr trputs("builtin command: "); trargs(argv); 969178825Sdfr#endif 970178825Sdfr mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 971178825Sdfr if (flags == EV_BACKCMD) { 972178825Sdfr memout.nleft = 0; 973178825Sdfr memout.nextc = memout.buf; 974178825Sdfr memout.bufsize = 64; 975178825Sdfr mode |= REDIR_BACKQ; 976178825Sdfr cmdentry.special = 0; 977178825Sdfr } 978178825Sdfr savecmdname = commandname; 979178825Sdfr savetopfile = getcurrentfile(); 980178825Sdfr cmdenviron = varlist.list; 981178825Sdfr e = -1; 982178825Sdfr savehandler = handler; 983178825Sdfr if (setjmp(jmploc.loc)) { 984178825Sdfr e = exception; 985178825Sdfr exitstatus = (e == EXINT)? SIGINT+128 : 2; 986178825Sdfr goto cmddone; 987178825Sdfr } 988178825Sdfr handler = &jmploc; 989178825Sdfr redirect(cmd->ncmd.redirect, mode); 990178825Sdfr /* 991178825Sdfr * If there is no command word, redirection errors should 992178825Sdfr * not be fatal but assignment errors should. 993178825Sdfr */ 994178825Sdfr if (argc == 0 && !(flags & EV_BACKCMD)) 995178825Sdfr cmdentry.special = 1; 996178825Sdfr listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); 997178825Sdfr if (argc > 0) 998178825Sdfr bltinsetlocale(); 999178825Sdfr commandname = argv[0]; 1000178825Sdfr argptr = argv + 1; 1001178825Sdfr nextopt_optptr = NULL; /* initialize nextopt */ 1002178825Sdfr builtin_flags = flags; 1003178825Sdfr exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 1004178825Sdfr flushall(); 1005178825Sdfrcmddone: 1006178825Sdfr if (argc > 0) 1007178825Sdfr bltinunsetlocale(); 1008178825Sdfr cmdenviron = NULL; 1009178825Sdfr out1 = &output; 1010178825Sdfr out2 = &errout; 1011178825Sdfr freestdout(); 1012178825Sdfr handler = savehandler; 1013178825Sdfr commandname = savecmdname; 1014178825Sdfr if (jp) 1015178825Sdfr exitshell(exitstatus); 1016178825Sdfr if (flags == EV_BACKCMD) { 1017178825Sdfr backcmd->buf = memout.buf; 1018178825Sdfr backcmd->nleft = memout.nextc - memout.buf; 1019178825Sdfr memout.buf = NULL; 1020178825Sdfr } 1021178825Sdfr if (cmdentry.u.index != EXECCMD && 1022178825Sdfr (e == -1 || e == EXERROR || e == EXEXEC)) 1023178825Sdfr popredir(); 1024178825Sdfr if (e != -1) { 1025178825Sdfr if ((e != EXERROR && e != EXEXEC) 1026178825Sdfr || cmdentry.special) 1027178825Sdfr exraise(e); 1028178825Sdfr popfilesupto(savetopfile); 1029178825Sdfr if (flags != EV_BACKCMD) 1030178825Sdfr FORCEINTON; 1031178825Sdfr } 1032178825Sdfr } else { 1033178825Sdfr#ifdef DEBUG 1034178825Sdfr trputs("normal command: "); trargs(argv); 1035178825Sdfr#endif 1036178825Sdfr redirect(cmd->ncmd.redirect, 0); 1037178825Sdfr for (sp = varlist.list ; sp ; sp = sp->next) 1038178825Sdfr setvareq(sp->text, VEXPORT|VSTACK); 1039178825Sdfr envp = environment(); 1040178825Sdfr shellexec(argv, envp, path, cmdentry.u.index); 1041178825Sdfr /*NOTREACHED*/ 1042178825Sdfr } 1043178825Sdfr goto out; 1044178825Sdfr 1045178825Sdfrparent: /* parent process gets here (if we forked) */ 1046178825Sdfr if (mode == FORK_FG) { /* argument to fork */ 1047178825Sdfr INTOFF; 1048178825Sdfr exitstatus = waitforjob(jp, &realstatus); 1049178825Sdfr INTON; 1050178825Sdfr if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { 1051178825Sdfr evalskip = SKIPBREAK; 1052178825Sdfr skipcount = loopnest; 1053178825Sdfr } 1054178825Sdfr } else if (mode == FORK_NOJOB) { 1055178825Sdfr backcmd->fd = pip[0]; 1056178825Sdfr close(pip[1]); 1057178825Sdfr backcmd->jp = jp; 1058178825Sdfr } 1059178825Sdfr 1060178825Sdfrout: 1061233294Sstas if (lastarg) 1062178825Sdfr setvar("_", lastarg, 0); 1063178825Sdfr if (do_clearcmdentry) 1064178825Sdfr clearcmdentry(0); 1065178825Sdfr popstackmark(&smark); 1066178825Sdfr} 1067178825Sdfr 1068178825Sdfr 1069178825Sdfr 1070178825Sdfr/* 1071178825Sdfr * Search for a command. This is called before we fork so that the 1072178825Sdfr * location of the command will be available in the parent as well as 1073178825Sdfr * the child. The check for "goodname" is an overly conservative 1074178825Sdfr * check that the name will not be subject to expansion. 1075233294Sstas */ 1076178825Sdfr 1077178825Sdfrstatic void 1078178825Sdfrprehash(union node *n) 1079178825Sdfr{ 1080178825Sdfr struct cmdentry entry; 1081178825Sdfr 1082178825Sdfr if (n && n->type == NCMD && n->ncmd.args) 1083178825Sdfr if (goodname(n->ncmd.args->narg.text)) 1084178825Sdfr find_command(n->ncmd.args->narg.text, &entry, 0, 1085178825Sdfr pathval()); 1086178825Sdfr} 1087178825Sdfr 1088178825Sdfr 1089178825Sdfr 1090178825Sdfr/* 1091178825Sdfr * Builtin commands. Builtin commands whose functions are closely 1092178825Sdfr * tied to evaluation are implemented here. 1093178825Sdfr */ 1094178825Sdfr 1095178825Sdfr/* 1096178825Sdfr * No command given, a bltin command with no arguments, or a bltin command 1097178825Sdfr * with an invalid name. 1098178825Sdfr */ 1099178825Sdfr 1100178825Sdfrint 1101178825Sdfrbltincmd(int argc, char **argv) 1102178825Sdfr{ 1103178825Sdfr if (argc > 1) { 1104178825Sdfr out2fmt_flush("%s: not found\n", argv[1]); 1105178825Sdfr return 127; 1106178825Sdfr } 1107178825Sdfr /* 1108178825Sdfr * Preserve exitstatus of a previous possible redirection 1109178825Sdfr * as POSIX mandates 1110178825Sdfr */ 1111178825Sdfr return exitstatus; 1112178825Sdfr} 1113178825Sdfr 1114178825Sdfr 1115178825Sdfr/* 1116178825Sdfr * Handle break and continue commands. Break, continue, and return are 1117178825Sdfr * all handled by setting the evalskip flag. The evaluation routines 1118178825Sdfr * above all check this flag, and if it is set they start skipping 1119178825Sdfr * commands rather than executing them. The variable skipcount is 1120178825Sdfr * the number of loops to break/continue, or the number of function 1121178825Sdfr * levels to return. (The latter is always 1.) It should probably 1122178825Sdfr * be an error to break out of more loops than exist, but it isn't 1123178825Sdfr * in the standard shell so we don't make it one here. 1124178825Sdfr */ 1125178825Sdfr 1126178825Sdfrint 1127178825Sdfrbreakcmd(int argc, char **argv) 1128178825Sdfr{ 1129178825Sdfr int n = argc > 1 ? number(argv[1]) : 1; 1130233294Sstas 1131233294Sstas if (n > loopnest) 1132233294Sstas n = loopnest; 1133233294Sstas if (n > 0) { 1134233294Sstas evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 1135233294Sstas skipcount = n; 1136233294Sstas } 1137233294Sstas return 0; 1138178825Sdfr} 1139178825Sdfr 1140178825Sdfr/* 1141178825Sdfr * The `command' command. 1142178825Sdfr */ 1143178825Sdfrint 1144178825Sdfrcommandcmd(int argc, char **argv) 1145178825Sdfr{ 1146178825Sdfr const char *path; 1147178825Sdfr int ch; 1148178825Sdfr int cmd = -1; 1149178825Sdfr 1150178825Sdfr path = bltinlookup("PATH", 1); 1151178825Sdfr 1152178825Sdfr optind = optreset = 1; 1153178825Sdfr opterr = 0; 1154178825Sdfr while ((ch = getopt(argc, argv, "pvV")) != -1) { 1155178825Sdfr switch (ch) { 1156178825Sdfr case 'p': 1157178825Sdfr path = _PATH_STDPATH; 1158178825Sdfr break; 1159178825Sdfr case 'v': 1160178825Sdfr cmd = TYPECMD_SMALLV; 1161178825Sdfr break; 1162178825Sdfr case 'V': 1163178825Sdfr cmd = TYPECMD_BIGV; 1164178825Sdfr break; 1165178825Sdfr case '?': 1166178825Sdfr default: 1167178825Sdfr error("unknown option: -%c", optopt); 1168178825Sdfr } 1169178825Sdfr } 1170178825Sdfr argc -= optind; 1171178825Sdfr argv += optind; 1172178825Sdfr 1173178825Sdfr if (cmd != -1) { 1174178825Sdfr if (argc != 1) 1175178825Sdfr error("wrong number of arguments"); 1176178825Sdfr return typecmd_impl(2, argv - 1, cmd, path); 1177178825Sdfr } 1178178825Sdfr if (argc != 0) 1179178825Sdfr error("commandcmd bad call"); 1180178825Sdfr 1181178825Sdfr /* 1182178825Sdfr * Do nothing successfully if no command was specified; 1183178825Sdfr * ksh also does this. 1184178825Sdfr */ 1185178825Sdfr return 0; 1186178825Sdfr} 1187178825Sdfr 1188178825Sdfr 1189178825Sdfr/* 1190178825Sdfr * The return command. 1191178825Sdfr */ 1192178825Sdfr 1193178825Sdfrint 1194178825Sdfrreturncmd(int argc, char **argv) 1195178825Sdfr{ 1196178825Sdfr int ret = argc > 1 ? number(argv[1]) : oexitstatus; 1197178825Sdfr 1198178825Sdfr if (funcnest) { 1199178825Sdfr evalskip = SKIPFUNC; 1200178825Sdfr skipcount = 1; 1201178825Sdfr } else { 1202178825Sdfr /* skip the rest of the file */ 1203178825Sdfr evalskip = SKIPFILE; 1204178825Sdfr skipcount = 1; 1205178825Sdfr } 1206178825Sdfr return ret; 1207178825Sdfr} 1208178825Sdfr 1209178825Sdfr 1210178825Sdfrint 1211178825Sdfrfalsecmd(int argc __unused, char **argv __unused) 1212178825Sdfr{ 1213178825Sdfr return 1; 1214178825Sdfr} 1215178825Sdfr 1216178825Sdfr 1217178825Sdfrint 1218178825Sdfrtruecmd(int argc __unused, char **argv __unused) 1219178825Sdfr{ 1220178825Sdfr return 0; 1221178825Sdfr} 1222178825Sdfr 1223178825Sdfr 1224178825Sdfrint 1225178825Sdfrexeccmd(int argc, char **argv) 1226178825Sdfr{ 1227178825Sdfr /* 1228233294Sstas * Because we have historically not supported any options, 1229178825Sdfr * only treat "--" specially. 1230178825Sdfr */ 1231178825Sdfr if (argc > 1 && strcmp(argv[1], "--") == 0) 1232178825Sdfr argc--, argv++; 1233178825Sdfr if (argc > 1) { 1234178825Sdfr struct strlist *sp; 1235178825Sdfr 1236178825Sdfr iflag = 0; /* exit on error */ 1237178825Sdfr mflag = 0; 1238178825Sdfr optschanged(); 1239178825Sdfr for (sp = cmdenviron; sp ; sp = sp->next) 1240178825Sdfr setvareq(sp->text, VEXPORT|VSTACK); 1241178825Sdfr shellexec(argv + 1, environment(), pathval(), 0); 1242178825Sdfr 1243178825Sdfr } 1244178825Sdfr return 0; 1245178825Sdfr} 1246178825Sdfr 1247178825Sdfr 1248178825Sdfrint 1249178825Sdfrtimescmd(int argc __unused, char **argv __unused) 1250178825Sdfr{ 1251178825Sdfr struct rusage ru; 1252178825Sdfr long shumins, shsmins, chumins, chsmins; 1253178825Sdfr double shusecs, shssecs, chusecs, chssecs; 1254178825Sdfr 1255178825Sdfr if (getrusage(RUSAGE_SELF, &ru) < 0) 1256178825Sdfr return 1; 1257178825Sdfr shumins = ru.ru_utime.tv_sec / 60; 1258178825Sdfr shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1259178825Sdfr shsmins = ru.ru_stime.tv_sec / 60; 1260178825Sdfr shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1261178825Sdfr if (getrusage(RUSAGE_CHILDREN, &ru) < 0) 1262178825Sdfr return 1; 1263178825Sdfr chumins = ru.ru_utime.tv_sec / 60; 1264178825Sdfr chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1265178825Sdfr chsmins = ru.ru_stime.tv_sec / 60; 1266178825Sdfr chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1267178825Sdfr out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, 1268178825Sdfr shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); 1269178825Sdfr return 0; 1270178825Sdfr} 1271178825Sdfr