eval.c revision 223282
1275970Scy/*- 2275970Scy * Copyright (c) 1993 3275970Scy * The Regents of the University of California. All rights reserved. 4275970Scy * 5275970Scy * This code is derived from software contributed to Berkeley by 6275970Scy * Kenneth Almquist. 7275970Scy * 8285612Sdelphij * Redistribution and use in source and binary forms, with or without 9275970Scy * modification, are permitted provided that the following conditions 10275970Scy * are met: 11275970Scy * 1. Redistributions of source code must retain the above copyright 12275970Scy * notice, this list of conditions and the following disclaimer. 13275970Scy * 2. Redistributions in binary form must reproduce the above copyright 14275970Scy * notice, this list of conditions and the following disclaimer in the 15275970Scy * documentation and/or other materials provided with the distribution. 16275970Scy * 4. Neither the name of the University nor the names of its contributors 17275970Scy * may be used to endorse or promote products derived from this software 18285612Sdelphij * without specific prior written permission. 19275970Scy * 20275970Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23275970Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30275970Scy * SUCH DAMAGE. 31275970Scy */ 32275970Scy 33275970Scy#ifndef lint 34275970Scy#if 0 35275970Scystatic char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 36275970Scy#endif 37275970Scy#endif /* not lint */ 38275970Scy#include <sys/cdefs.h> 39275970Scy__FBSDID("$FreeBSD: head/bin/sh/eval.c 223282 2011-06-18 23:58:59Z jilles $"); 40275970Scy 41275970Scy#include <paths.h> 42275970Scy#include <signal.h> 43275970Scy#include <stdlib.h> 44275970Scy#include <unistd.h> 45275970Scy#include <sys/resource.h> 46275970Scy#include <sys/wait.h> /* For WIFSIGNALED(status) */ 47275970Scy#include <errno.h> 48275970Scy 49275970Scy/* 50275970Scy * Evaluate a command. 51275970Scy */ 52275970Scy 53275970Scy#include "shell.h" 54275970Scy#include "nodes.h" 55275970Scy#include "syntax.h" 56275970Scy#include "expand.h" 57275970Scy#include "parser.h" 58275970Scy#include "jobs.h" 59275970Scy#include "eval.h" 60275970Scy#include "builtins.h" 61275970Scy#include "options.h" 62275970Scy#include "exec.h" 63275970Scy#include "redir.h" 64275970Scy#include "input.h" 65275970Scy#include "output.h" 66275970Scy#include "trap.h" 67275970Scy#include "var.h" 68275970Scy#include "memalloc.h" 69275970Scy#include "error.h" 70275970Scy#include "show.h" 71275970Scy#include "mystring.h" 72275970Scy#ifndef NO_HISTORY 73275970Scy#include "myhistedit.h" 74275970Scy#endif 75275970Scy 76275970Scy 77275970Scyint evalskip; /* set if we are skipping commands */ 78275970Scystatic int skipcount; /* number of levels to skip */ 79275970ScyMKINIT int loopnest; /* current loop nesting level */ 80275970Scyint funcnest; /* depth of function calls */ 81275970Scystatic int builtin_flags; /* evalcommand flags for builtins */ 82275970Scy 83275970Scy 84275970Scychar *commandname; 85275970Scystruct strlist *cmdenviron; 86275970Scyint exitstatus; /* exit status of last command */ 87275970Scyint oexitstatus; /* saved exit status */ 88275970Scy 89275970Scy 90275970Scystatic void evalloop(union node *, int); 91275970Scystatic void evalfor(union node *, int); 92275970Scystatic void evalcase(union node *, int); 93275970Scystatic void evalsubshell(union node *, int); 94275970Scystatic void evalredir(union node *, int); 95275970Scystatic void expredir(union node *); 96275970Scystatic void evalpipe(union node *); 97275970Scystatic int is_valid_fast_cmdsubst(union node *n); 98275970Scystatic void evalcommand(union node *, int, struct backcmd *); 99275970Scystatic void prehash(union node *); 100275970Scy 101275970Scy 102275970Scy/* 103275970Scy * Called to reset things after an exception. 104275970Scy */ 105275970Scy 106275970Scy#ifdef mkinit 107275970ScyINCLUDE "eval.h" 108275970Scy 109275970ScyRESET { 110275970Scy evalskip = 0; 111275970Scy loopnest = 0; 112275970Scy funcnest = 0; 113275970Scy} 114275970Scy#endif 115285612Sdelphij 116285612Sdelphij 117285612Sdelphij 118275970Scy/* 119275970Scy * The eval command. 120275970Scy */ 121275970Scy 122275970Scyint 123275970Scyevalcmd(int argc, char **argv) 124275970Scy{ 125275970Scy char *p; 126275970Scy char *concat; 127275970Scy char **ap; 128275970Scy 129275970Scy if (argc > 1) { 130275970Scy p = argv[1]; 131275970Scy if (argc > 2) { 132275970Scy STARTSTACKSTR(concat); 133275970Scy ap = argv + 2; 134285612Sdelphij for (;;) { 135275970Scy STPUTS(p, concat); 136275970Scy if ((p = *ap++) == NULL) 137275970Scy break; 138275970Scy STPUTC(' ', concat); 139275970Scy } 140275970Scy STPUTC('\0', concat); 141275970Scy p = grabstackstr(concat); 142275970Scy } 143275970Scy evalstring(p, builtin_flags); 144275970Scy } else 145275970Scy exitstatus = 0; 146275970Scy return exitstatus; 147275970Scy} 148275970Scy 149275970Scy 150275970Scy/* 151275970Scy * Execute a command or commands contained in a string. 152275970Scy */ 153275970Scy 154275970Scyvoid 155275970Scyevalstring(char *s, int flags) 156275970Scy{ 157275970Scy union node *n; 158275970Scy struct stackmark smark; 159275970Scy int flags_exit; 160275970Scy int any; 161275970Scy 162275970Scy flags_exit = flags & EV_EXIT; 163275970Scy flags &= ~EV_EXIT; 164275970Scy any = 0; 165275970Scy setstackmark(&smark); 166275970Scy setinputstring(s, 1); 167275970Scy while ((n = parsecmd(0)) != NEOF) { 168275970Scy if (n != NULL && !nflag) { 169275970Scy if (flags_exit && preadateof()) 170275970Scy evaltree(n, flags | EV_EXIT); 171275970Scy else 172275970Scy evaltree(n, flags); 173275970Scy any = 1; 174275970Scy } 175275970Scy popstackmark(&smark); 176275970Scy } 177275970Scy popfile(); 178275970Scy popstackmark(&smark); 179275970Scy if (!any) 180275970Scy exitstatus = 0; 181275970Scy if (flags_exit) 182275970Scy exraise(EXEXIT); 183275970Scy} 184275970Scy 185275970Scy 186275970Scy/* 187275970Scy * Evaluate a parse tree. The value is left in the global variable 188275970Scy * exitstatus. 189275970Scy */ 190275970Scy 191275970Scyvoid 192275970Scyevaltree(union node *n, int flags) 193275970Scy{ 194275970Scy int do_etest; 195275970Scy union node *next; 196275970Scy 197275970Scy do_etest = 0; 198275970Scy if (n == NULL) { 199275970Scy TRACE(("evaltree(NULL) called\n")); 200275970Scy exitstatus = 0; 201275970Scy goto out; 202275970Scy } 203275970Scy do { 204275970Scy next = NULL; 205275970Scy#ifndef NO_HISTORY 206275970Scy displayhist = 1; /* show history substitutions done with fc */ 207275970Scy#endif 208275970Scy TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); 209275970Scy switch (n->type) { 210275970Scy case NSEMI: 211275970Scy evaltree(n->nbinary.ch1, flags & ~EV_EXIT); 212275970Scy if (evalskip) 213275970Scy goto out; 214275970Scy next = n->nbinary.ch2; 215275970Scy break; 216275970Scy case NAND: 217275970Scy evaltree(n->nbinary.ch1, EV_TESTED); 218275970Scy if (evalskip || exitstatus != 0) { 219275970Scy goto out; 220275970Scy } 221275970Scy next = n->nbinary.ch2; 222275970Scy break; 223275970Scy case NOR: 224275970Scy evaltree(n->nbinary.ch1, EV_TESTED); 225275970Scy if (evalskip || exitstatus == 0) 226275970Scy goto out; 227275970Scy next = n->nbinary.ch2; 228275970Scy break; 229275970Scy case NREDIR: 230275970Scy evalredir(n, flags); 231275970Scy break; 232275970Scy case NSUBSHELL: 233275970Scy evalsubshell(n, flags); 234275970Scy do_etest = !(flags & EV_TESTED); 235275970Scy break; 236275970Scy case NBACKGND: 237275970Scy evalsubshell(n, flags); 238275970Scy break; 239275970Scy case NIF: { 240275970Scy evaltree(n->nif.test, EV_TESTED); 241275970Scy if (evalskip) 242275970Scy goto out; 243275970Scy if (exitstatus == 0) 244275970Scy next = n->nif.ifpart; 245275970Scy else if (n->nif.elsepart) 246275970Scy next = n->nif.elsepart; 247275970Scy else 248275970Scy exitstatus = 0; 249275970Scy break; 250275970Scy } 251275970Scy case NWHILE: 252275970Scy case NUNTIL: 253275970Scy evalloop(n, flags & ~EV_EXIT); 254275970Scy break; 255275970Scy case NFOR: 256275970Scy evalfor(n, flags & ~EV_EXIT); 257275970Scy break; 258275970Scy case NCASE: 259275970Scy evalcase(n, flags); 260275970Scy break; 261275970Scy case NDEFUN: 262275970Scy defun(n->narg.text, n->narg.next); 263275970Scy exitstatus = 0; 264275970Scy break; 265275970Scy case NNOT: 266275970Scy evaltree(n->nnot.com, EV_TESTED); 267275970Scy exitstatus = !exitstatus; 268275970Scy break; 269275970Scy 270275970Scy case NPIPE: 271275970Scy evalpipe(n); 272275970Scy do_etest = !(flags & EV_TESTED); 273275970Scy break; 274275970Scy case NCMD: 275275970Scy evalcommand(n, flags, (struct backcmd *)NULL); 276275970Scy do_etest = !(flags & EV_TESTED); 277275970Scy break; 278275970Scy default: 279275970Scy out1fmt("Node type = %d\n", n->type); 280275970Scy flushout(&output); 281275970Scy break; 282275970Scy } 283275970Scy n = next; 284275970Scy } while (n != NULL); 285275970Scyout: 286275970Scy if (pendingsigs) 287275970Scy dotrap(); 288275970Scy if (eflag && exitstatus != 0 && do_etest) 289275970Scy exitshell(exitstatus); 290275970Scy if (flags & EV_EXIT) 291275970Scy exraise(EXEXIT); 292275970Scy} 293275970Scy 294275970Scy 295275970Scystatic void 296275970Scyevalloop(union node *n, int flags) 297275970Scy{ 298275970Scy int status; 299275970Scy 300275970Scy loopnest++; 301275970Scy status = 0; 302275970Scy for (;;) { 303275970Scy evaltree(n->nbinary.ch1, EV_TESTED); 304275970Scy if (evalskip) { 305275970Scyskipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 306275970Scy evalskip = 0; 307275970Scy continue; 308275970Scy } 309275970Scy if (evalskip == SKIPBREAK && --skipcount <= 0) 310275970Scy evalskip = 0; 311275970Scy if (evalskip == SKIPFUNC || evalskip == SKIPFILE) 312275970Scy status = exitstatus; 313275970Scy break; 314275970Scy } 315275970Scy if (n->type == NWHILE) { 316275970Scy if (exitstatus != 0) 317275970Scy break; 318275970Scy } else { 319275970Scy if (exitstatus == 0) 320275970Scy break; 321275970Scy } 322275970Scy evaltree(n->nbinary.ch2, flags); 323275970Scy status = exitstatus; 324275970Scy if (evalskip) 325275970Scy goto skipping; 326275970Scy } 327275970Scy loopnest--; 328275970Scy exitstatus = status; 329275970Scy} 330275970Scy 331275970Scy 332275970Scy 333275970Scystatic void 334275970Scyevalfor(union node *n, int flags) 335275970Scy{ 336275970Scy struct arglist arglist; 337275970Scy union node *argp; 338275970Scy struct strlist *sp; 339275970Scy struct stackmark smark; 340275970Scy 341275970Scy setstackmark(&smark); 342275970Scy arglist.lastp = &arglist.list; 343275970Scy for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 344275970Scy oexitstatus = exitstatus; 345275970Scy expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 346275970Scy if (evalskip) 347275970Scy goto out; 348275970Scy } 349275970Scy *arglist.lastp = NULL; 350275970Scy 351275970Scy exitstatus = 0; 352275970Scy loopnest++; 353275970Scy for (sp = arglist.list ; sp ; sp = sp->next) { 354275970Scy setvar(n->nfor.var, sp->text, 0); 355275970Scy evaltree(n->nfor.body, flags); 356275970Scy if (evalskip) { 357275970Scy if (evalskip == SKIPCONT && --skipcount <= 0) { 358275970Scy evalskip = 0; 359275970Scy continue; 360275970Scy } 361275970Scy if (evalskip == SKIPBREAK && --skipcount <= 0) 362275970Scy evalskip = 0; 363275970Scy break; 364275970Scy } 365275970Scy } 366275970Scy loopnest--; 367275970Scyout: 368275970Scy popstackmark(&smark); 369275970Scy} 370275970Scy 371275970Scy 372275970Scy 373275970Scystatic void 374275970Scyevalcase(union node *n, int flags) 375275970Scy{ 376275970Scy union node *cp; 377275970Scy union node *patp; 378275970Scy struct arglist arglist; 379275970Scy struct stackmark smark; 380275970Scy 381275970Scy setstackmark(&smark); 382275970Scy arglist.lastp = &arglist.list; 383275970Scy oexitstatus = exitstatus; 384275970Scy exitstatus = 0; 385275970Scy expandarg(n->ncase.expr, &arglist, EXP_TILDE); 386275970Scy for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 387275970Scy for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 388275970Scy if (casematch(patp, arglist.list->text)) { 389275970Scy while (cp->nclist.next && 390275970Scy cp->type == NCLISTFALLTHRU) { 391275970Scy if (evalskip != 0) 392275970Scy break; 393275970Scy evaltree(cp->nclist.body, 394275970Scy flags & ~EV_EXIT); 395275970Scy cp = cp->nclist.next; 396275970Scy } 397275970Scy if (evalskip == 0) { 398275970Scy evaltree(cp->nclist.body, flags); 399275970Scy } 400275970Scy goto out; 401275970Scy } 402275970Scy } 403275970Scy } 404275970Scyout: 405275970Scy popstackmark(&smark); 406275970Scy} 407275970Scy 408275970Scy 409275970Scy 410275970Scy/* 411275970Scy * Kick off a subshell to evaluate a tree. 412275970Scy */ 413275970Scy 414275970Scystatic void 415275970Scyevalsubshell(union node *n, int flags) 416275970Scy{ 417275970Scy struct job *jp; 418275970Scy int backgnd = (n->type == NBACKGND); 419275970Scy 420275970Scy oexitstatus = exitstatus; 421275970Scy expredir(n->nredir.redirect); 422275970Scy if ((!backgnd && flags & EV_EXIT && !have_traps()) || 423275970Scy forkshell(jp = makejob(n, 1), n, backgnd) == 0) { 424275970Scy if (backgnd) 425275970Scy flags &=~ EV_TESTED; 426275970Scy redirect(n->nredir.redirect, 0); 427275970Scy evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 428275970Scy } else if (! backgnd) { 429275970Scy INTOFF; 430275970Scy exitstatus = waitforjob(jp, (int *)NULL); 431275970Scy INTON; 432275970Scy } else 433275970Scy exitstatus = 0; 434275970Scy} 435275970Scy 436275970Scy 437275970Scy/* 438275970Scy * Evaluate a redirected compound command. 439275970Scy */ 440275970Scy 441275970Scystatic void 442275970Scyevalredir(union node *n, int flags) 443275970Scy{ 444275970Scy struct jmploc jmploc; 445275970Scy struct jmploc *savehandler; 446275970Scy volatile int in_redirect = 1; 447275970Scy 448275970Scy oexitstatus = exitstatus; 449275970Scy expredir(n->nredir.redirect); 450275970Scy savehandler = handler; 451275970Scy if (setjmp(jmploc.loc)) { 452275970Scy int e; 453275970Scy 454275970Scy handler = savehandler; 455275970Scy e = exception; 456275970Scy popredir(); 457275970Scy if (e == EXERROR || e == EXEXEC) { 458275970Scy if (in_redirect) { 459275970Scy exitstatus = 2; 460275970Scy return; 461275970Scy } 462275970Scy } 463275970Scy longjmp(handler->loc, 1); 464275970Scy } else { 465275970Scy INTOFF; 466275970Scy handler = &jmploc; 467275970Scy redirect(n->nredir.redirect, REDIR_PUSH); 468275970Scy in_redirect = 0; 469275970Scy INTON; 470275970Scy evaltree(n->nredir.n, flags); 471275970Scy } 472275970Scy INTOFF; 473275970Scy handler = savehandler; 474275970Scy popredir(); 475275970Scy INTON; 476275970Scy} 477275970Scy 478275970Scy 479275970Scy/* 480275970Scy * Compute the names of the files in a redirection list. 481275970Scy */ 482275970Scy 483275970Scystatic void 484275970Scyexpredir(union node *n) 485275970Scy{ 486275970Scy union node *redir; 487275970Scy 488275970Scy for (redir = n ; redir ; redir = redir->nfile.next) { 489275970Scy struct arglist fn; 490275970Scy fn.lastp = &fn.list; 491275970Scy switch (redir->type) { 492275970Scy case NFROM: 493275970Scy case NTO: 494275970Scy case NFROMTO: 495275970Scy case NAPPEND: 496275970Scy case NCLOBBER: 497275970Scy expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 498275970Scy redir->nfile.expfname = fn.list->text; 499275970Scy break; 500275970Scy case NFROMFD: 501275970Scy case NTOFD: 502275970Scy if (redir->ndup.vname) { 503275970Scy expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); 504275970Scy fixredir(redir, fn.list->text, 1); 505275970Scy } 506275970Scy break; 507275970Scy } 508275970Scy } 509275970Scy} 510275970Scy 511275970Scy 512275970Scy 513275970Scy/* 514275970Scy * Evaluate a pipeline. All the processes in the pipeline are children 515275970Scy * of the process creating the pipeline. (This differs from some versions 516275970Scy * of the shell, which make the last process in a pipeline the parent 517275970Scy * of all the rest.) 518275970Scy */ 519275970Scy 520275970Scystatic void 521275970Scyevalpipe(union node *n) 522275970Scy{ 523275970Scy struct job *jp; 524275970Scy struct nodelist *lp; 525275970Scy int pipelen; 526275970Scy int prevfd; 527275970Scy int pip[2]; 528275970Scy 529275970Scy TRACE(("evalpipe(%p) called\n", (void *)n)); 530275970Scy pipelen = 0; 531275970Scy for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 532275970Scy pipelen++; 533275970Scy INTOFF; 534275970Scy jp = makejob(n, pipelen); 535275970Scy prevfd = -1; 536275970Scy for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 537275970Scy prehash(lp->n); 538275970Scy pip[1] = -1; 539275970Scy if (lp->next) { 540275970Scy if (pipe(pip) < 0) { 541275970Scy close(prevfd); 542275970Scy error("Pipe call failed: %s", strerror(errno)); 543275970Scy } 544275970Scy } 545275970Scy if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 546275970Scy INTON; 547275970Scy if (prevfd > 0) { 548275970Scy dup2(prevfd, 0); 549275970Scy close(prevfd); 550275970Scy } 551275970Scy if (pip[1] >= 0) { 552275970Scy if (!(prevfd >= 0 && pip[0] == 0)) 553275970Scy close(pip[0]); 554275970Scy if (pip[1] != 1) { 555275970Scy dup2(pip[1], 1); 556275970Scy close(pip[1]); 557275970Scy } 558275970Scy } 559275970Scy evaltree(lp->n, EV_EXIT); 560275970Scy } 561275970Scy if (prevfd >= 0) 562275970Scy close(prevfd); 563275970Scy prevfd = pip[0]; 564275970Scy if (pip[1] != -1) 565275970Scy close(pip[1]); 566275970Scy } 567275970Scy INTON; 568275970Scy if (n->npipe.backgnd == 0) { 569275970Scy INTOFF; 570275970Scy exitstatus = waitforjob(jp, (int *)NULL); 571275970Scy TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 572275970Scy INTON; 573275970Scy } else 574275970Scy exitstatus = 0; 575275970Scy} 576275970Scy 577275970Scy 578275970Scy 579275970Scystatic int 580275970Scyis_valid_fast_cmdsubst(union node *n) 581275970Scy{ 582275970Scy 583275970Scy return (n->type == NCMD); 584275970Scy} 585275970Scy 586275970Scy/* 587275970Scy * Execute a command inside back quotes. If it's a builtin command, we 588275970Scy * want to save its output in a block obtained from malloc. Otherwise 589275970Scy * we fork off a subprocess and get the output of the command via a pipe. 590275970Scy * Should be called with interrupts off. 591275970Scy */ 592275970Scy 593275970Scyvoid 594275970Scyevalbackcmd(union node *n, struct backcmd *result) 595275970Scy{ 596275970Scy int pip[2]; 597275970Scy struct job *jp; 598275970Scy struct stackmark smark; /* unnecessary */ 599275970Scy struct jmploc jmploc; 600275970Scy struct jmploc *savehandler; 601275970Scy struct localvar *savelocalvars; 602275970Scy 603275970Scy setstackmark(&smark); 604275970Scy result->fd = -1; 605275970Scy result->buf = NULL; 606275970Scy result->nleft = 0; 607275970Scy result->jp = NULL; 608275970Scy if (n == NULL) { 609275970Scy exitstatus = 0; 610275970Scy goto out; 611275970Scy } 612275970Scy if (is_valid_fast_cmdsubst(n)) { 613275970Scy exitstatus = oexitstatus; 614275970Scy savelocalvars = localvars; 615275970Scy localvars = NULL; 616275970Scy forcelocal++; 617275970Scy savehandler = handler; 618275970Scy if (setjmp(jmploc.loc)) { 619275970Scy if (exception == EXERROR || exception == EXEXEC) 620275970Scy exitstatus = 2; 621275970Scy else if (exception != 0) { 622275970Scy handler = savehandler; 623275970Scy forcelocal--; 624275970Scy poplocalvars(); 625275970Scy localvars = savelocalvars; 626275970Scy longjmp(handler->loc, 1); 627275970Scy } 628275970Scy } else { 629275970Scy handler = &jmploc; 630275970Scy evalcommand(n, EV_BACKCMD, result); 631275970Scy } 632275970Scy handler = savehandler; 633275970Scy forcelocal--; 634275970Scy poplocalvars(); 635275970Scy localvars = savelocalvars; 636275970Scy } else { 637275970Scy exitstatus = 0; 638275970Scy if (pipe(pip) < 0) 639275970Scy error("Pipe call failed: %s", strerror(errno)); 640275970Scy jp = makejob(n, 1); 641275970Scy if (forkshell(jp, n, FORK_NOJOB) == 0) { 642275970Scy FORCEINTON; 643275970Scy close(pip[0]); 644275970Scy if (pip[1] != 1) { 645275970Scy dup2(pip[1], 1); 646275970Scy close(pip[1]); 647275970Scy } 648275970Scy evaltree(n, EV_EXIT); 649275970Scy } 650275970Scy close(pip[1]); 651275970Scy result->fd = pip[0]; 652275970Scy result->jp = jp; 653275970Scy } 654275970Scyout: 655275970Scy popstackmark(&smark); 656275970Scy TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", 657275970Scy result->fd, result->buf, result->nleft, result->jp)); 658275970Scy} 659275970Scy 660275970Scy/* 661275970Scy * Check if a builtin can safely be executed in the same process, 662275970Scy * even though it should be in a subshell (command substitution). 663275970Scy * Note that jobid, jobs, times and trap can show information not 664275970Scy * available in a child process; this is deliberate. 665275970Scy * The arguments should already have been expanded. 666275970Scy */ 667275970Scystatic int 668275970Scysafe_builtin(int idx, int argc, char **argv) 669275970Scy{ 670275970Scy if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || 671275970Scy idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || 672275970Scy idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || 673275970Scy idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || 674275970Scy idx == TYPECMD) 675275970Scy return (1); 676275970Scy if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || 677275970Scy idx == UMASKCMD) 678275970Scy return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); 679275970Scy if (idx == SETCMD) 680275970Scy return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || 681275970Scy argv[1][0] == '+') && argv[1][1] == 'o' && 682275970Scy argv[1][2] == '\0')); 683275970Scy return (0); 684275970Scy} 685275970Scy 686275970Scy/* 687275970Scy * Execute a simple command. 688275970Scy * Note: This may or may not return if (flags & EV_EXIT). 689275970Scy */ 690275970Scy 691275970Scystatic void 692275970Scyevalcommand(union node *cmd, int flags, struct backcmd *backcmd) 693275970Scy{ 694275970Scy struct stackmark smark; 695275970Scy union node *argp; 696275970Scy struct arglist arglist; 697275970Scy struct arglist varlist; 698275970Scy char **argv; 699275970Scy int argc; 700275970Scy char **envp; 701275970Scy int varflag; 702275970Scy struct strlist *sp; 703275970Scy int mode; 704275970Scy int pip[2]; 705275970Scy struct cmdentry cmdentry; 706275970Scy struct job *jp; 707275970Scy struct jmploc jmploc; 708275970Scy struct jmploc *savehandler; 709275970Scy char *savecmdname; 710275970Scy struct shparam saveparam; 711275970Scy struct localvar *savelocalvars; 712275970Scy struct parsefile *savetopfile; 713275970Scy volatile int e; 714275970Scy char *lastarg; 715275970Scy int realstatus; 716275970Scy int do_clearcmdentry; 717275970Scy const char *path = pathval(); 718275970Scy 719275970Scy /* First expand the arguments. */ 720275970Scy TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); 721275970Scy setstackmark(&smark); 722275970Scy arglist.lastp = &arglist.list; 723275970Scy varlist.lastp = &varlist.list; 724275970Scy varflag = 1; 725275970Scy jp = NULL; 726275970Scy do_clearcmdentry = 0; 727275970Scy oexitstatus = exitstatus; 728275970Scy exitstatus = 0; 729275970Scy for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 730285612Sdelphij if (varflag && isassignment(argp->narg.text)) { 731285612Sdelphij expandarg(argp, &varlist, EXP_VARTILDE); 732285612Sdelphij continue; 733285612Sdelphij } 734285612Sdelphij expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 735285612Sdelphij varflag = 0; 736285612Sdelphij } 737285612Sdelphij *arglist.lastp = NULL; 738285612Sdelphij *varlist.lastp = NULL; 739275970Scy expredir(cmd->ncmd.redirect); 740275970Scy argc = 0; 741275970Scy for (sp = arglist.list ; sp ; sp = sp->next) 742275970Scy argc++; 743275970Scy /* Add one slot at the beginning for tryexec(). */ 744275970Scy argv = stalloc(sizeof (char *) * (argc + 2)); 745275970Scy argv++; 746275970Scy 747275970Scy for (sp = arglist.list ; sp ; sp = sp->next) { 748275970Scy TRACE(("evalcommand arg: %s\n", sp->text)); 749275970Scy *argv++ = sp->text; 750275970Scy } 751275970Scy *argv = NULL; 752275970Scy lastarg = NULL; 753275970Scy if (iflag && funcnest == 0 && argc > 0) 754275970Scy lastarg = argv[-1]; 755275970Scy argv -= argc; 756275970Scy 757275970Scy /* Print the command if xflag is set. */ 758275970Scy if (xflag) { 759275970Scy char sep = 0; 760275970Scy const char *p, *ps4; 761275970Scy ps4 = expandstr(ps4val()); 762275970Scy out2str(ps4 != NULL ? ps4 : ps4val()); 763275970Scy for (sp = varlist.list ; sp ; sp = sp->next) { 764275970Scy if (sep != 0) 765275970Scy out2c(' '); 766275970Scy p = strchr(sp->text, '='); 767275970Scy if (p != NULL) { 768275970Scy p++; 769275970Scy outbin(sp->text, p - sp->text, out2); 770275970Scy out2qstr(p); 771275970Scy } else 772275970Scy out2qstr(sp->text); 773275970Scy sep = ' '; 774275970Scy } 775275970Scy for (sp = arglist.list ; sp ; sp = sp->next) { 776275970Scy if (sep != 0) 777275970Scy out2c(' '); 778275970Scy /* Disambiguate command looking like assignment. */ 779275970Scy if (sp == arglist.list && 780275970Scy strchr(sp->text, '=') != NULL && 781275970Scy strchr(sp->text, '\'') == NULL) { 782275970Scy out2c('\''); 783275970Scy out2str(sp->text); 784275970Scy out2c('\''); 785275970Scy } else 786275970Scy out2qstr(sp->text); 787275970Scy sep = ' '; 788275970Scy } 789275970Scy out2c('\n'); 790275970Scy flushout(&errout); 791275970Scy } 792275970Scy 793275970Scy /* Now locate the command. */ 794275970Scy if (argc == 0) { 795275970Scy /* Variable assignment(s) without command */ 796275970Scy cmdentry.cmdtype = CMDBUILTIN; 797275970Scy cmdentry.u.index = BLTINCMD; 798275970Scy cmdentry.special = 0; 799275970Scy } else { 800275970Scy static const char PATH[] = "PATH="; 801275970Scy int cmd_flags = 0, bltinonly = 0; 802275970Scy 803275970Scy /* 804275970Scy * Modify the command lookup path, if a PATH= assignment 805275970Scy * is present 806275970Scy */ 807275970Scy for (sp = varlist.list ; sp ; sp = sp->next) 808275970Scy if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { 809275970Scy path = sp->text + sizeof(PATH) - 1; 810275970Scy /* 811275970Scy * On `PATH=... command`, we need to make 812275970Scy * sure that the command isn't using the 813275970Scy * non-updated hash table of the outer PATH 814275970Scy * setting and we need to make sure that 815275970Scy * the hash table isn't filled with items 816275970Scy * from the temporary setting. 817275970Scy * 818275970Scy * It would be better to forbit using and 819275970Scy * updating the table while this command 820275970Scy * runs, by the command finding mechanism 821275970Scy * is heavily integrated with hash handling, 822275970Scy * so we just delete the hash before and after 823275970Scy * the command runs. Partly deleting like 824275970Scy * changepatch() does doesn't seem worth the 825275970Scy * bookinging effort, since most such runs add 826275970Scy * directories in front of the new PATH. 827275970Scy */ 828275970Scy clearcmdentry(); 829275970Scy do_clearcmdentry = 1; 830275970Scy } 831275970Scy 832275970Scy for (;;) { 833275970Scy if (bltinonly) { 834275970Scy cmdentry.u.index = find_builtin(*argv, &cmdentry.special); 835275970Scy if (cmdentry.u.index < 0) { 836275970Scy cmdentry.u.index = BLTINCMD; 837275970Scy argv--; 838275970Scy argc++; 839275970Scy break; 840275970Scy } 841275970Scy } else 842275970Scy find_command(argv[0], &cmdentry, cmd_flags, path); 843275970Scy /* implement the bltin and command builtins here */ 844275970Scy if (cmdentry.cmdtype != CMDBUILTIN) 845275970Scy break; 846275970Scy if (cmdentry.u.index == BLTINCMD) { 847275970Scy if (argc == 1) 848275970Scy break; 849275970Scy argv++; 850275970Scy argc--; 851275970Scy bltinonly = 1; 852275970Scy } else if (cmdentry.u.index == COMMANDCMD) { 853275970Scy if (argc == 1) 854275970Scy break; 855275970Scy if (!strcmp(argv[1], "-p")) { 856275970Scy if (argc == 2) 857275970Scy break; 858275970Scy if (argv[2][0] == '-') { 859275970Scy if (strcmp(argv[2], "--")) 860275970Scy break; 861275970Scy if (argc == 3) 862275970Scy break; 863275970Scy argv += 3; 864275970Scy argc -= 3; 865275970Scy } else { 866275970Scy argv += 2; 867275970Scy argc -= 2; 868275970Scy } 869275970Scy path = _PATH_STDPATH; 870275970Scy clearcmdentry(); 871275970Scy do_clearcmdentry = 1; 872275970Scy } else if (!strcmp(argv[1], "--")) { 873275970Scy if (argc == 2) 874275970Scy break; 875275970Scy argv += 2; 876275970Scy argc -= 2; 877275970Scy } else if (argv[1][0] == '-') 878275970Scy break; 879275970Scy else { 880275970Scy argv++; 881275970Scy argc--; 882275970Scy } 883275970Scy cmd_flags |= DO_NOFUNC; 884275970Scy bltinonly = 0; 885275970Scy } else 886275970Scy break; 887275970Scy } 888275970Scy /* 889275970Scy * Special builtins lose their special properties when 890275970Scy * called via 'command'. 891275970Scy */ 892275970Scy if (cmd_flags & DO_NOFUNC) 893275970Scy cmdentry.special = 0; 894275970Scy } 895275970Scy 896275970Scy /* Fork off a child process if necessary. */ 897275970Scy if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) 898275970Scy && ((flags & EV_EXIT) == 0 || have_traps())) 899275970Scy || ((flags & EV_BACKCMD) != 0 900275970Scy && (cmdentry.cmdtype != CMDBUILTIN || 901275970Scy !safe_builtin(cmdentry.u.index, argc, argv)))) { 902275970Scy jp = makejob(cmd, 1); 903275970Scy mode = FORK_FG; 904275970Scy if (flags & EV_BACKCMD) { 905275970Scy mode = FORK_NOJOB; 906275970Scy if (pipe(pip) < 0) 907275970Scy error("Pipe call failed: %s", strerror(errno)); 908275970Scy } 909275970Scy if (forkshell(jp, cmd, mode) != 0) 910275970Scy goto parent; /* at end of routine */ 911275970Scy if (flags & EV_BACKCMD) { 912275970Scy FORCEINTON; 913275970Scy close(pip[0]); 914275970Scy if (pip[1] != 1) { 915316722Sdelphij dup2(pip[1], 1); 916316722Sdelphij close(pip[1]); 917316722Sdelphij } 918316722Sdelphij flags &= ~EV_BACKCMD; 919275970Scy } 920275970Scy flags |= EV_EXIT; 921 } 922 923 /* This is the child process if a fork occurred. */ 924 /* Execute the command. */ 925 if (cmdentry.cmdtype == CMDFUNCTION) { 926#ifdef DEBUG 927 trputs("Shell function: "); trargs(argv); 928#endif 929 saveparam = shellparam; 930 shellparam.malloc = 0; 931 shellparam.reset = 1; 932 shellparam.nparam = argc - 1; 933 shellparam.p = argv + 1; 934 shellparam.optnext = NULL; 935 INTOFF; 936 savelocalvars = localvars; 937 localvars = NULL; 938 reffunc(cmdentry.u.func); 939 savehandler = handler; 940 if (setjmp(jmploc.loc)) { 941 freeparam(&shellparam); 942 shellparam = saveparam; 943 popredir(); 944 unreffunc(cmdentry.u.func); 945 poplocalvars(); 946 localvars = savelocalvars; 947 funcnest--; 948 handler = savehandler; 949 longjmp(handler->loc, 1); 950 } 951 handler = &jmploc; 952 funcnest++; 953 redirect(cmd->ncmd.redirect, REDIR_PUSH); 954 INTON; 955 for (sp = varlist.list ; sp ; sp = sp->next) 956 mklocal(sp->text); 957 exitstatus = oexitstatus; 958 evaltree(getfuncnode(cmdentry.u.func), 959 flags & (EV_TESTED | EV_EXIT)); 960 INTOFF; 961 unreffunc(cmdentry.u.func); 962 poplocalvars(); 963 localvars = savelocalvars; 964 freeparam(&shellparam); 965 shellparam = saveparam; 966 handler = savehandler; 967 funcnest--; 968 popredir(); 969 INTON; 970 if (evalskip == SKIPFUNC) { 971 evalskip = 0; 972 skipcount = 0; 973 } 974 if (jp) 975 exitshell(exitstatus); 976 } else if (cmdentry.cmdtype == CMDBUILTIN) { 977#ifdef DEBUG 978 trputs("builtin command: "); trargs(argv); 979#endif 980 mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 981 if (flags == EV_BACKCMD) { 982 memout.nleft = 0; 983 memout.nextc = memout.buf; 984 memout.bufsize = 64; 985 mode |= REDIR_BACKQ; 986 cmdentry.special = 0; 987 } 988 savecmdname = commandname; 989 savetopfile = getcurrentfile(); 990 cmdenviron = varlist.list; 991 e = -1; 992 savehandler = handler; 993 if (setjmp(jmploc.loc)) { 994 e = exception; 995 if (e == EXINT) 996 exitstatus = SIGINT+128; 997 else if (e != EXEXIT) 998 exitstatus = 2; 999 goto cmddone; 1000 } 1001 handler = &jmploc; 1002 redirect(cmd->ncmd.redirect, mode); 1003 /* 1004 * If there is no command word, redirection errors should 1005 * not be fatal but assignment errors should. 1006 */ 1007 if (argc == 0 && !(flags & EV_BACKCMD)) 1008 cmdentry.special = 1; 1009 listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); 1010 if (argc > 0) 1011 bltinsetlocale(); 1012 commandname = argv[0]; 1013 argptr = argv + 1; 1014 nextopt_optptr = NULL; /* initialize nextopt */ 1015 builtin_flags = flags; 1016 exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 1017 flushall(); 1018cmddone: 1019 if (argc > 0) 1020 bltinunsetlocale(); 1021 cmdenviron = NULL; 1022 out1 = &output; 1023 out2 = &errout; 1024 freestdout(); 1025 handler = savehandler; 1026 commandname = savecmdname; 1027 if (jp) 1028 exitshell(exitstatus); 1029 if (flags == EV_BACKCMD) { 1030 backcmd->buf = memout.buf; 1031 backcmd->nleft = memout.nextc - memout.buf; 1032 memout.buf = NULL; 1033 } 1034 if (cmdentry.u.index != EXECCMD) 1035 popredir(); 1036 if (e != -1) { 1037 if ((e != EXERROR && e != EXEXEC) 1038 || cmdentry.special) 1039 exraise(e); 1040 popfilesupto(savetopfile); 1041 if (flags != EV_BACKCMD) 1042 FORCEINTON; 1043 } 1044 } else { 1045#ifdef DEBUG 1046 trputs("normal command: "); trargs(argv); 1047#endif 1048 redirect(cmd->ncmd.redirect, 0); 1049 for (sp = varlist.list ; sp ; sp = sp->next) 1050 setvareq(sp->text, VEXPORT|VSTACK); 1051 envp = environment(); 1052 shellexec(argv, envp, path, cmdentry.u.index); 1053 /*NOTREACHED*/ 1054 } 1055 goto out; 1056 1057parent: /* parent process gets here (if we forked) */ 1058 if (mode == FORK_FG) { /* argument to fork */ 1059 INTOFF; 1060 exitstatus = waitforjob(jp, &realstatus); 1061 INTON; 1062 if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { 1063 evalskip = SKIPBREAK; 1064 skipcount = loopnest; 1065 } 1066 } else if (mode == FORK_NOJOB) { 1067 backcmd->fd = pip[0]; 1068 close(pip[1]); 1069 backcmd->jp = jp; 1070 } 1071 1072out: 1073 if (lastarg) 1074 setvar("_", lastarg, 0); 1075 if (do_clearcmdentry) 1076 clearcmdentry(); 1077 popstackmark(&smark); 1078} 1079 1080 1081 1082/* 1083 * Search for a command. This is called before we fork so that the 1084 * location of the command will be available in the parent as well as 1085 * the child. The check for "goodname" is an overly conservative 1086 * check that the name will not be subject to expansion. 1087 */ 1088 1089static void 1090prehash(union node *n) 1091{ 1092 struct cmdentry entry; 1093 1094 if (n && n->type == NCMD && n->ncmd.args) 1095 if (goodname(n->ncmd.args->narg.text)) 1096 find_command(n->ncmd.args->narg.text, &entry, 0, 1097 pathval()); 1098} 1099 1100 1101 1102/* 1103 * Builtin commands. Builtin commands whose functions are closely 1104 * tied to evaluation are implemented here. 1105 */ 1106 1107/* 1108 * No command given, a bltin command with no arguments, or a bltin command 1109 * with an invalid name. 1110 */ 1111 1112int 1113bltincmd(int argc, char **argv) 1114{ 1115 if (argc > 1) { 1116 out2fmt_flush("%s: not found\n", argv[1]); 1117 return 127; 1118 } 1119 /* 1120 * Preserve exitstatus of a previous possible redirection 1121 * as POSIX mandates 1122 */ 1123 return exitstatus; 1124} 1125 1126 1127/* 1128 * Handle break and continue commands. Break, continue, and return are 1129 * all handled by setting the evalskip flag. The evaluation routines 1130 * above all check this flag, and if it is set they start skipping 1131 * commands rather than executing them. The variable skipcount is 1132 * the number of loops to break/continue, or the number of function 1133 * levels to return. (The latter is always 1.) It should probably 1134 * be an error to break out of more loops than exist, but it isn't 1135 * in the standard shell so we don't make it one here. 1136 */ 1137 1138int 1139breakcmd(int argc, char **argv) 1140{ 1141 int n = argc > 1 ? number(argv[1]) : 1; 1142 1143 if (n > loopnest) 1144 n = loopnest; 1145 if (n > 0) { 1146 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 1147 skipcount = n; 1148 } 1149 return 0; 1150} 1151 1152/* 1153 * The `command' command. 1154 */ 1155int 1156commandcmd(int argc, char **argv) 1157{ 1158 const char *path; 1159 int ch; 1160 int cmd = -1; 1161 1162 path = bltinlookup("PATH", 1); 1163 1164 optind = optreset = 1; 1165 opterr = 0; 1166 while ((ch = getopt(argc, argv, "pvV")) != -1) { 1167 switch (ch) { 1168 case 'p': 1169 path = _PATH_STDPATH; 1170 break; 1171 case 'v': 1172 cmd = TYPECMD_SMALLV; 1173 break; 1174 case 'V': 1175 cmd = TYPECMD_BIGV; 1176 break; 1177 case '?': 1178 default: 1179 error("unknown option: -%c", optopt); 1180 } 1181 } 1182 argc -= optind; 1183 argv += optind; 1184 1185 if (cmd != -1) { 1186 if (argc != 1) 1187 error("wrong number of arguments"); 1188 return typecmd_impl(2, argv - 1, cmd, path); 1189 } 1190 if (argc != 0) 1191 error("commandcmd bad call"); 1192 1193 /* 1194 * Do nothing successfully if no command was specified; 1195 * ksh also does this. 1196 */ 1197 return 0; 1198} 1199 1200 1201/* 1202 * The return command. 1203 */ 1204 1205int 1206returncmd(int argc, char **argv) 1207{ 1208 int ret = argc > 1 ? number(argv[1]) : oexitstatus; 1209 1210 if (funcnest) { 1211 evalskip = SKIPFUNC; 1212 skipcount = 1; 1213 } else { 1214 /* skip the rest of the file */ 1215 evalskip = SKIPFILE; 1216 skipcount = 1; 1217 } 1218 return ret; 1219} 1220 1221 1222int 1223falsecmd(int argc __unused, char **argv __unused) 1224{ 1225 return 1; 1226} 1227 1228 1229int 1230truecmd(int argc __unused, char **argv __unused) 1231{ 1232 return 0; 1233} 1234 1235 1236int 1237execcmd(int argc, char **argv) 1238{ 1239 /* 1240 * Because we have historically not supported any options, 1241 * only treat "--" specially. 1242 */ 1243 if (argc > 1 && strcmp(argv[1], "--") == 0) 1244 argc--, argv++; 1245 if (argc > 1) { 1246 struct strlist *sp; 1247 1248 iflag = 0; /* exit on error */ 1249 mflag = 0; 1250 optschanged(); 1251 for (sp = cmdenviron; sp ; sp = sp->next) 1252 setvareq(sp->text, VEXPORT|VSTACK); 1253 shellexec(argv + 1, environment(), pathval(), 0); 1254 1255 } 1256 return 0; 1257} 1258 1259 1260int 1261timescmd(int argc __unused, char **argv __unused) 1262{ 1263 struct rusage ru; 1264 long shumins, shsmins, chumins, chsmins; 1265 double shusecs, shssecs, chusecs, chssecs; 1266 1267 if (getrusage(RUSAGE_SELF, &ru) < 0) 1268 return 1; 1269 shumins = ru.ru_utime.tv_sec / 60; 1270 shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1271 shsmins = ru.ru_stime.tv_sec / 60; 1272 shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1273 if (getrusage(RUSAGE_CHILDREN, &ru) < 0) 1274 return 1; 1275 chumins = ru.ru_utime.tv_sec / 60; 1276 chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1277 chsmins = ru.ru_stime.tv_sec / 60; 1278 chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1279 out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, 1280 shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); 1281 return 0; 1282} 1283