| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * Copyright (c) 1993 | |
| 3 | * The Regents of the University of California. All rights reserved. | |
| 4 | * | |
| 5 | * This code is derived from software contributed to Berkeley by | |
| 6 | * Kenneth Almquist. | |
| 7 | * | |
| 8 | * Redistribution and use in source and binary forms, with or without | |
| 9 | * modification, are permitted provided that the following conditions | |
| 10 | * are met: | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in the | |
| 15 | * documentation and/or other materials provided with the distribution. | |
| 16 | * 3. All advertising materials mentioning features or use of this software | |
| 17 | * must display the following acknowledgement: | |
| 18 | * This product includes software developed by the University of | |
| 19 | * California, Berkeley and its contributors. | |
| 20 | * 4. Neither the name of the University nor the names of its contributors | |
| 21 | * may be used to endorse or promote products derived from this software | |
| 22 | * without specific prior written permission. | |
| 23 | * | |
| 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 34 | * SUCH DAMAGE. | |
| 1de703da MD |
35 | * |
| 36 | * @(#)eval.c 8.9 (Berkeley) 6/8/95 | |
| f15f3668 | 37 | * $FreeBSD: src/bin/sh/eval.c,v 1.110 2011/06/16 21:50:28 jilles Exp $ |
| 984263bc MD |
38 | */ |
| 39 | ||
| bec497dd | 40 | #include <sys/time.h> |
| 0d5aaed6 | 41 | #include <sys/resource.h> |
| f75716ef JS |
42 | #include <sys/wait.h> /* For WIFSIGNALED(status) */ |
| 43 | ||
| 44 | #include <errno.h> | |
| 984263bc MD |
45 | #include <paths.h> |
| 46 | #include <signal.h> | |
| f75716ef | 47 | #include <stdlib.h> |
| 984263bc | 48 | #include <unistd.h> |
| 984263bc MD |
49 | |
| 50 | /* | |
| 51 | * Evaluate a command. | |
| 52 | */ | |
| 53 | ||
| 54 | #include "shell.h" | |
| 55 | #include "nodes.h" | |
| 56 | #include "syntax.h" | |
| 57 | #include "expand.h" | |
| 58 | #include "parser.h" | |
| 59 | #include "jobs.h" | |
| 60 | #include "eval.h" | |
| 61 | #include "builtins.h" | |
| 62 | #include "options.h" | |
| 63 | #include "exec.h" | |
| 64 | #include "redir.h" | |
| 65 | #include "input.h" | |
| 66 | #include "output.h" | |
| 67 | #include "trap.h" | |
| 68 | #include "var.h" | |
| 69 | #include "memalloc.h" | |
| 70 | #include "error.h" | |
| 71 | #include "show.h" | |
| 72 | #include "mystring.h" | |
| 73 | #ifndef NO_HISTORY | |
| 74 | #include "myhistedit.h" | |
| 75 | #endif | |
| 76 | ||
| 77 | ||
| 934863d7 | 78 | int evalskip; /* set if we are skipping commands */ |
| 99512ac4 | 79 | static int skipcount; /* number of levels to skip */ |
| 984263bc MD |
80 | MKINIT int loopnest; /* current loop nesting level */ |
| 81 | int funcnest; /* depth of function calls */ | |
| 99512ac4 | 82 | static int builtin_flags; /* evalcommand flags for builtins */ |
| 984263bc MD |
83 | |
| 84 | ||
| 492efe05 | 85 | const char *commandname; |
| 984263bc MD |
86 | struct strlist *cmdenviron; |
| 87 | int exitstatus; /* exit status of last command */ | |
| 88 | int oexitstatus; /* saved exit status */ | |
| 89 | ||
| 90 | ||
| 99512ac4 PA |
91 | static void evalloop(union node *, int); |
| 92 | static void evalfor(union node *, int); | |
| 93 | static void evalcase(union node *, int); | |
| 94 | static void evalsubshell(union node *, int); | |
| 95 | static void evalredir(union node *, int); | |
| 96 | static void expredir(union node *); | |
| 97 | static void evalpipe(union node *); | |
| 98 | static int is_valid_fast_cmdsubst(union node *n); | |
| 99 | static void evalcommand(union node *, int, struct backcmd *); | |
| 100 | static void prehash(union node *); | |
| 984263bc MD |
101 | |
| 102 | ||
| 103 | /* | |
| 104 | * Called to reset things after an exception. | |
| 105 | */ | |
| 106 | ||
| 107 | #ifdef mkinit | |
| 108 | INCLUDE "eval.h" | |
| 109 | ||
| 110 | RESET { | |
| 111 | evalskip = 0; | |
| 112 | loopnest = 0; | |
| 113 | funcnest = 0; | |
| 114 | } | |
| 984263bc MD |
115 | #endif |
| 116 | ||
| 117 | ||
| 118 | ||
| 119 | /* | |
| 120 | * The eval command. | |
| 121 | */ | |
| 122 | ||
| 123 | int | |
| 124 | evalcmd(int argc, char **argv) | |
| 125 | { | |
| 126 | char *p; | |
| 127 | char *concat; | |
| 128 | char **ap; | |
| 129 | ||
| 130 | if (argc > 1) { | |
| 131 | p = argv[1]; | |
| 132 | if (argc > 2) { | |
| 133 | STARTSTACKSTR(concat); | |
| 134 | ap = argv + 2; | |
| 135 | for (;;) { | |
| 99512ac4 | 136 | STPUTS(p, concat); |
| 984263bc MD |
137 | if ((p = *ap++) == NULL) |
| 138 | break; | |
| 139 | STPUTC(' ', concat); | |
| 140 | } | |
| 141 | STPUTC('\0', concat); | |
| 142 | p = grabstackstr(concat); | |
| 143 | } | |
| f15f3668 | 144 | evalstring(p, builtin_flags); |
| 99512ac4 PA |
145 | } else |
| 146 | exitstatus = 0; | |
| 984263bc MD |
147 | return exitstatus; |
| 148 | } | |
| 149 | ||
| 150 | ||
| 151 | /* | |
| 152 | * Execute a command or commands contained in a string. | |
| 153 | */ | |
| 154 | ||
| 155 | void | |
| 99512ac4 | 156 | evalstring(char *s, int flags) |
| 984263bc MD |
157 | { |
| 158 | union node *n; | |
| 159 | struct stackmark smark; | |
| 99512ac4 PA |
160 | int flags_exit; |
| 161 | int any; | |
| 984263bc | 162 | |
| 99512ac4 PA |
163 | flags_exit = flags & EV_EXIT; |
| 164 | flags &= ~EV_EXIT; | |
| 165 | any = 0; | |
| 984263bc MD |
166 | setstackmark(&smark); |
| 167 | setinputstring(s, 1); | |
| 168 | while ((n = parsecmd(0)) != NEOF) { | |
| 276d61f5 | 169 | if (n != NULL && !nflag) { |
| 99512ac4 PA |
170 | if (flags_exit && preadateof()) |
| 171 | evaltree(n, flags | EV_EXIT); | |
| 172 | else | |
| 173 | evaltree(n, flags); | |
| 174 | any = 1; | |
| 175 | } | |
| 984263bc MD |
176 | popstackmark(&smark); |
| 177 | } | |
| 178 | popfile(); | |
| 179 | popstackmark(&smark); | |
| 99512ac4 PA |
180 | if (!any) |
| 181 | exitstatus = 0; | |
| 182 | if (flags_exit) | |
| 2b23d27f | 183 | exraise(EXEXIT); |
| 984263bc MD |
184 | } |
| 185 | ||
| 186 | ||
| 984263bc MD |
187 | /* |
| 188 | * Evaluate a parse tree. The value is left in the global variable | |
| 189 | * exitstatus. | |
| 190 | */ | |
| 191 | ||
| 192 | void | |
| 193 | evaltree(union node *n, int flags) | |
| 194 | { | |
| 6a3f0d3d | 195 | int do_etest; |
| 99512ac4 | 196 | union node *next; |
| 6a3f0d3d SS |
197 | |
| 198 | do_etest = 0; | |
| 984263bc MD |
199 | if (n == NULL) { |
| 200 | TRACE(("evaltree(NULL) called\n")); | |
| 201 | exitstatus = 0; | |
| 202 | goto out; | |
| 203 | } | |
| 99512ac4 PA |
204 | do { |
| 205 | next = NULL; | |
| 984263bc | 206 | #ifndef NO_HISTORY |
| 99512ac4 | 207 | displayhist = 1; /* show history substitutions done with fc */ |
| 984263bc | 208 | #endif |
| 99512ac4 PA |
209 | TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); |
| 210 | switch (n->type) { | |
| 211 | case NSEMI: | |
| 212 | evaltree(n->nbinary.ch1, flags & ~EV_EXIT); | |
| 213 | if (evalskip) | |
| 214 | goto out; | |
| 215 | next = n->nbinary.ch2; | |
| 216 | break; | |
| 217 | case NAND: | |
| 218 | evaltree(n->nbinary.ch1, EV_TESTED); | |
| 219 | if (evalskip || exitstatus != 0) { | |
| 220 | goto out; | |
| 221 | } | |
| 222 | next = n->nbinary.ch2; | |
| 223 | break; | |
| 224 | case NOR: | |
| 225 | evaltree(n->nbinary.ch1, EV_TESTED); | |
| 226 | if (evalskip || exitstatus == 0) | |
| 227 | goto out; | |
| 228 | next = n->nbinary.ch2; | |
| 229 | break; | |
| 230 | case NREDIR: | |
| 231 | evalredir(n, flags); | |
| 232 | break; | |
| 233 | case NSUBSHELL: | |
| 234 | evalsubshell(n, flags); | |
| 235 | do_etest = !(flags & EV_TESTED); | |
| 236 | break; | |
| 237 | case NBACKGND: | |
| 238 | evalsubshell(n, flags); | |
| 239 | break; | |
| 240 | case NIF: { | |
| 241 | evaltree(n->nif.test, EV_TESTED); | |
| 242 | if (evalskip) | |
| 243 | goto out; | |
| 244 | if (exitstatus == 0) | |
| 245 | next = n->nif.ifpart; | |
| 246 | else if (n->nif.elsepart) | |
| 247 | next = n->nif.elsepart; | |
| 248 | else | |
| 249 | exitstatus = 0; | |
| 250 | break; | |
| 984263bc | 251 | } |
| 99512ac4 PA |
252 | case NWHILE: |
| 253 | case NUNTIL: | |
| 254 | evalloop(n, flags & ~EV_EXIT); | |
| 255 | break; | |
| 256 | case NFOR: | |
| 257 | evalfor(n, flags & ~EV_EXIT); | |
| 258 | break; | |
| 259 | case NCASE: | |
| 260 | evalcase(n, flags); | |
| 261 | break; | |
| 262 | case NDEFUN: | |
| 263 | defun(n->narg.text, n->narg.next); | |
| 984263bc | 264 | exitstatus = 0; |
| 99512ac4 PA |
265 | break; |
| 266 | case NNOT: | |
| 267 | evaltree(n->nnot.com, EV_TESTED); | |
| 268 | exitstatus = !exitstatus; | |
| 269 | break; | |
| 270 | ||
| 271 | case NPIPE: | |
| 272 | evalpipe(n); | |
| 273 | do_etest = !(flags & EV_TESTED); | |
| 274 | break; | |
| 275 | case NCMD: | |
| 276 | evalcommand(n, flags, NULL); | |
| 277 | do_etest = !(flags & EV_TESTED); | |
| 278 | break; | |
| 279 | default: | |
| 280 | out1fmt("Node type = %d\n", n->type); | |
| 281 | flushout(&output); | |
| 282 | break; | |
| 283 | } | |
| 284 | n = next; | |
| 285 | } while (n != NULL); | |
| 984263bc MD |
286 | out: |
| 287 | if (pendingsigs) | |
| 288 | dotrap(); | |
| 2b23d27f | 289 | if (eflag && exitstatus != 0 && do_etest) |
| 984263bc | 290 | exitshell(exitstatus); |
| 2b23d27f PA |
291 | if (flags & EV_EXIT) |
| 292 | exraise(EXEXIT); | |
| 984263bc MD |
293 | } |
| 294 | ||
| 295 | ||
| 99512ac4 | 296 | static void |
| 6a3f0d3d | 297 | evalloop(union node *n, int flags) |
| 984263bc MD |
298 | { |
| 299 | int status; | |
| 300 | ||
| 301 | loopnest++; | |
| 302 | status = 0; | |
| 303 | for (;;) { | |
| 304 | evaltree(n->nbinary.ch1, EV_TESTED); | |
| 305 | if (evalskip) { | |
| 306 | skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { | |
| 307 | evalskip = 0; | |
| 308 | continue; | |
| 309 | } | |
| 310 | if (evalskip == SKIPBREAK && --skipcount <= 0) | |
| 311 | evalskip = 0; | |
| 99512ac4 PA |
312 | if (evalskip == SKIPFUNC || evalskip == SKIPFILE) |
| 313 | status = exitstatus; | |
| 984263bc MD |
314 | break; |
| 315 | } | |
| 316 | if (n->type == NWHILE) { | |
| 317 | if (exitstatus != 0) | |
| 318 | break; | |
| 319 | } else { | |
| 320 | if (exitstatus == 0) | |
| 321 | break; | |
| 322 | } | |
| 6a3f0d3d | 323 | evaltree(n->nbinary.ch2, flags); |
| 984263bc MD |
324 | status = exitstatus; |
| 325 | if (evalskip) | |
| 326 | goto skipping; | |
| 327 | } | |
| 328 | loopnest--; | |
| 329 | exitstatus = status; | |
| 330 | } | |
| 331 | ||
| 332 | ||
| 333 | ||
| 99512ac4 | 334 | static void |
| 6a3f0d3d | 335 | evalfor(union node *n, int flags) |
| 984263bc MD |
336 | { |
| 337 | struct arglist arglist; | |
| 338 | union node *argp; | |
| 339 | struct strlist *sp; | |
| 340 | struct stackmark smark; | |
| 341 | ||
| 342 | setstackmark(&smark); | |
| 343 | arglist.lastp = &arglist.list; | |
| 344 | for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { | |
| 345 | oexitstatus = exitstatus; | |
| 346 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | |
| 347 | if (evalskip) | |
| 348 | goto out; | |
| 349 | } | |
| 350 | *arglist.lastp = NULL; | |
| 351 | ||
| 352 | exitstatus = 0; | |
| 353 | loopnest++; | |
| 354 | for (sp = arglist.list ; sp ; sp = sp->next) { | |
| 355 | setvar(n->nfor.var, sp->text, 0); | |
| 6a3f0d3d | 356 | evaltree(n->nfor.body, flags); |
| 984263bc MD |
357 | if (evalskip) { |
| 358 | if (evalskip == SKIPCONT && --skipcount <= 0) { | |
| 359 | evalskip = 0; | |
| 360 | continue; | |
| 361 | } | |
| 362 | if (evalskip == SKIPBREAK && --skipcount <= 0) | |
| 363 | evalskip = 0; | |
| 364 | break; | |
| 365 | } | |
| 366 | } | |
| 367 | loopnest--; | |
| 368 | out: | |
| 369 | popstackmark(&smark); | |
| 370 | } | |
| 371 | ||
| 372 | ||
| 373 | ||
| 99512ac4 | 374 | static void |
| 984263bc MD |
375 | evalcase(union node *n, int flags) |
| 376 | { | |
| 377 | union node *cp; | |
| 378 | union node *patp; | |
| 379 | struct arglist arglist; | |
| 380 | struct stackmark smark; | |
| 381 | ||
| 382 | setstackmark(&smark); | |
| 383 | arglist.lastp = &arglist.list; | |
| 384 | oexitstatus = exitstatus; | |
| 99512ac4 | 385 | exitstatus = 0; |
| 984263bc MD |
386 | expandarg(n->ncase.expr, &arglist, EXP_TILDE); |
| 387 | for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { | |
| 388 | for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { | |
| 389 | if (casematch(patp, arglist.list->text)) { | |
| 390 | if (evalskip == 0) { | |
| 391 | evaltree(cp->nclist.body, flags); | |
| 392 | } | |
| 393 | goto out; | |
| 394 | } | |
| 395 | } | |
| 396 | } | |
| 397 | out: | |
| 398 | popstackmark(&smark); | |
| 399 | } | |
| 400 | ||
| 401 | ||
| 402 | ||
| 403 | /* | |
| 404 | * Kick off a subshell to evaluate a tree. | |
| 405 | */ | |
| 406 | ||
| 99512ac4 | 407 | static void |
| 984263bc MD |
408 | evalsubshell(union node *n, int flags) |
| 409 | { | |
| 410 | struct job *jp; | |
| 411 | int backgnd = (n->type == NBACKGND); | |
| 412 | ||
| 87838cc9 | 413 | oexitstatus = exitstatus; |
| 984263bc | 414 | expredir(n->nredir.redirect); |
| 99512ac4 PA |
415 | if ((!backgnd && flags & EV_EXIT && !have_traps()) || |
| 416 | forkshell(jp = makejob(n, 1), n, backgnd) == 0) { | |
| 984263bc MD |
417 | if (backgnd) |
| 418 | flags &=~ EV_TESTED; | |
| 419 | redirect(n->nredir.redirect, 0); | |
| 420 | evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ | |
| 99512ac4 | 421 | } else if (!backgnd) { |
| 984263bc | 422 | INTOFF; |
| 2038fb68 | 423 | exitstatus = waitforjob(jp, NULL); |
| 984263bc | 424 | INTON; |
| 5d4731c8 PA |
425 | } else |
| 426 | exitstatus = 0; | |
| 984263bc MD |
427 | } |
| 428 | ||
| 429 | ||
| 99512ac4 PA |
430 | /* |
| 431 | * Evaluate a redirected compound command. | |
| 432 | */ | |
| 433 | ||
| 434 | static void | |
| 435 | evalredir(union node *n, int flags) | |
| 436 | { | |
| 437 | struct jmploc jmploc; | |
| 438 | struct jmploc *savehandler; | |
| 439 | volatile int in_redirect = 1; | |
| 440 | ||
| 87838cc9 | 441 | oexitstatus = exitstatus; |
| 99512ac4 PA |
442 | expredir(n->nredir.redirect); |
| 443 | savehandler = handler; | |
| 444 | if (setjmp(jmploc.loc)) { | |
| 445 | int e; | |
| 446 | ||
| 447 | handler = savehandler; | |
| 448 | e = exception; | |
| 2b23d27f | 449 | popredir(); |
| 99512ac4 | 450 | if (e == EXERROR || e == EXEXEC) { |
| 99512ac4 PA |
451 | if (in_redirect) { |
| 452 | exitstatus = 2; | |
| 453 | return; | |
| 454 | } | |
| 455 | } | |
| 456 | longjmp(handler->loc, 1); | |
| 457 | } else { | |
| 458 | INTOFF; | |
| 459 | handler = &jmploc; | |
| 460 | redirect(n->nredir.redirect, REDIR_PUSH); | |
| 461 | in_redirect = 0; | |
| 462 | INTON; | |
| 463 | evaltree(n->nredir.n, flags); | |
| 464 | } | |
| 465 | INTOFF; | |
| 466 | handler = savehandler; | |
| 467 | popredir(); | |
| 468 | INTON; | |
| 469 | } | |
| 470 | ||
| 984263bc MD |
471 | |
| 472 | /* | |
| 473 | * Compute the names of the files in a redirection list. | |
| 474 | */ | |
| 475 | ||
| 99512ac4 | 476 | static void |
| 984263bc MD |
477 | expredir(union node *n) |
| 478 | { | |
| 479 | union node *redir; | |
| 480 | ||
| 481 | for (redir = n ; redir ; redir = redir->nfile.next) { | |
| 482 | struct arglist fn; | |
| 483 | fn.lastp = &fn.list; | |
| 984263bc MD |
484 | switch (redir->type) { |
| 485 | case NFROM: | |
| 486 | case NTO: | |
| 487 | case NFROMTO: | |
| 488 | case NAPPEND: | |
| 489 | case NCLOBBER: | |
| 490 | expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); | |
| 491 | redir->nfile.expfname = fn.list->text; | |
| 492 | break; | |
| 493 | case NFROMFD: | |
| 494 | case NTOFD: | |
| 495 | if (redir->ndup.vname) { | |
| 99512ac4 | 496 | expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); |
| 984263bc MD |
497 | fixredir(redir, fn.list->text, 1); |
| 498 | } | |
| 499 | break; | |
| 500 | } | |
| 501 | } | |
| 502 | } | |
| 503 | ||
| 504 | ||
| 505 | ||
| 506 | /* | |
| 507 | * Evaluate a pipeline. All the processes in the pipeline are children | |
| 508 | * of the process creating the pipeline. (This differs from some versions | |
| 509 | * of the shell, which make the last process in a pipeline the parent | |
| 510 | * of all the rest.) | |
| 511 | */ | |
| 512 | ||
| 99512ac4 | 513 | static void |
| 984263bc MD |
514 | evalpipe(union node *n) |
| 515 | { | |
| 516 | struct job *jp; | |
| 517 | struct nodelist *lp; | |
| 518 | int pipelen; | |
| 519 | int prevfd; | |
| 520 | int pip[2]; | |
| 521 | ||
| 0d5aaed6 | 522 | TRACE(("evalpipe(%p) called\n", (void *)n)); |
| 984263bc MD |
523 | pipelen = 0; |
| 524 | for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) | |
| 525 | pipelen++; | |
| 526 | INTOFF; | |
| 527 | jp = makejob(n, pipelen); | |
| 528 | prevfd = -1; | |
| 529 | for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { | |
| 530 | prehash(lp->n); | |
| 531 | pip[1] = -1; | |
| 532 | if (lp->next) { | |
| 533 | if (pipe(pip) < 0) { | |
| 42ccfa71 SS |
534 | if (prevfd >= 0) |
| 535 | close(prevfd); | |
| 984263bc MD |
536 | error("Pipe call failed: %s", strerror(errno)); |
| 537 | } | |
| 538 | } | |
| 539 | if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { | |
| 540 | INTON; | |
| 541 | if (prevfd > 0) { | |
| 0d5aaed6 | 542 | dup2(prevfd, 0); |
| 984263bc MD |
543 | close(prevfd); |
| 544 | } | |
| 545 | if (pip[1] >= 0) { | |
| 546 | if (!(prevfd >= 0 && pip[0] == 0)) | |
| 547 | close(pip[0]); | |
| 548 | if (pip[1] != 1) { | |
| 0d5aaed6 | 549 | dup2(pip[1], 1); |
| 984263bc MD |
550 | close(pip[1]); |
| 551 | } | |
| 552 | } | |
| 553 | evaltree(lp->n, EV_EXIT); | |
| 554 | } | |
| 555 | if (prevfd >= 0) | |
| 556 | close(prevfd); | |
| 557 | prevfd = pip[0]; | |
| d23796a8 PA |
558 | if (pip[1] != -1) |
| 559 | close(pip[1]); | |
| 984263bc MD |
560 | } |
| 561 | INTON; | |
| 562 | if (n->npipe.backgnd == 0) { | |
| 563 | INTOFF; | |
| 2038fb68 | 564 | exitstatus = waitforjob(jp, NULL); |
| 984263bc MD |
565 | TRACE(("evalpipe: job done exit status %d\n", exitstatus)); |
| 566 | INTON; | |
| 5d4731c8 PA |
567 | } else |
| 568 | exitstatus = 0; | |
| 984263bc MD |
569 | } |
| 570 | ||
| 571 | ||
| 572 | ||
| 99512ac4 PA |
573 | static int |
| 574 | is_valid_fast_cmdsubst(union node *n) | |
| 575 | { | |
| 99512ac4 | 576 | |
| 32931063 | 577 | return (n->type == NCMD); |
| 99512ac4 PA |
578 | } |
| 579 | ||
| 984263bc MD |
580 | /* |
| 581 | * Execute a command inside back quotes. If it's a builtin command, we | |
| 582 | * want to save its output in a block obtained from malloc. Otherwise | |
| 583 | * we fork off a subprocess and get the output of the command via a pipe. | |
| 584 | * Should be called with interrupts off. | |
| 585 | */ | |
| 586 | ||
| 587 | void | |
| 588 | evalbackcmd(union node *n, struct backcmd *result) | |
| 589 | { | |
| 590 | int pip[2]; | |
| 591 | struct job *jp; | |
| 592 | struct stackmark smark; /* unnecessary */ | |
| 99512ac4 PA |
593 | struct jmploc jmploc; |
| 594 | struct jmploc *savehandler; | |
| 32931063 | 595 | struct localvar *savelocalvars; |
| 984263bc MD |
596 | |
| 597 | setstackmark(&smark); | |
| 598 | result->fd = -1; | |
| 599 | result->buf = NULL; | |
| 600 | result->nleft = 0; | |
| 601 | result->jp = NULL; | |
| 602 | if (n == NULL) { | |
| 603 | exitstatus = 0; | |
| 604 | goto out; | |
| 605 | } | |
| 99512ac4 | 606 | if (is_valid_fast_cmdsubst(n)) { |
| 984263bc | 607 | exitstatus = oexitstatus; |
| 32931063 PA |
608 | savelocalvars = localvars; |
| 609 | localvars = NULL; | |
| 610 | forcelocal++; | |
| 99512ac4 PA |
611 | savehandler = handler; |
| 612 | if (setjmp(jmploc.loc)) { | |
| 613 | if (exception == EXERROR || exception == EXEXEC) | |
| 614 | exitstatus = 2; | |
| 615 | else if (exception != 0) { | |
| 616 | handler = savehandler; | |
| 32931063 PA |
617 | forcelocal--; |
| 618 | poplocalvars(); | |
| 619 | localvars = savelocalvars; | |
| 99512ac4 PA |
620 | longjmp(handler->loc, 1); |
| 621 | } | |
| 622 | } else { | |
| 623 | handler = &jmploc; | |
| 624 | evalcommand(n, EV_BACKCMD, result); | |
| 625 | } | |
| 626 | handler = savehandler; | |
| 32931063 PA |
627 | forcelocal--; |
| 628 | poplocalvars(); | |
| 629 | localvars = savelocalvars; | |
| 984263bc MD |
630 | } else { |
| 631 | exitstatus = 0; | |
| 632 | if (pipe(pip) < 0) | |
| 633 | error("Pipe call failed: %s", strerror(errno)); | |
| 634 | jp = makejob(n, 1); | |
| 635 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | |
| 636 | FORCEINTON; | |
| 637 | close(pip[0]); | |
| 638 | if (pip[1] != 1) { | |
| 0d5aaed6 | 639 | dup2(pip[1], 1); |
| 984263bc MD |
640 | close(pip[1]); |
| 641 | } | |
| 642 | evaltree(n, EV_EXIT); | |
| 643 | } | |
| 644 | close(pip[1]); | |
| 645 | result->fd = pip[0]; | |
| 646 | result->jp = jp; | |
| 647 | } | |
| 648 | out: | |
| 649 | popstackmark(&smark); | |
| 0d5aaed6 | 650 | TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", |
| 984263bc MD |
651 | result->fd, result->buf, result->nleft, result->jp)); |
| 652 | } | |
| 653 | ||
| 99512ac4 PA |
654 | /* |
| 655 | * Check if a builtin can safely be executed in the same process, | |
| 656 | * even though it should be in a subshell (command substitution). | |
| 657 | * Note that jobid, jobs, times and trap can show information not | |
| 658 | * available in a child process; this is deliberate. | |
| 659 | * The arguments should already have been expanded. | |
| 660 | */ | |
| 661 | static int | |
| 662 | safe_builtin(int idx, int argc, char **argv) | |
| 663 | { | |
| 664 | if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || | |
| 665 | idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || | |
| 666 | idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || | |
| 667 | idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || | |
| 668 | idx == TYPECMD) | |
| 669 | return (1); | |
| 670 | if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || | |
| 671 | idx == UMASKCMD) | |
| 672 | return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); | |
| 673 | if (idx == SETCMD) | |
| 674 | return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || | |
| 675 | argv[1][0] == '+') && argv[1][1] == 'o' && | |
| 676 | argv[1][2] == '\0')); | |
| 677 | return (0); | |
| 678 | } | |
| 984263bc MD |
679 | |
| 680 | /* | |
| 681 | * Execute a simple command. | |
| 99512ac4 | 682 | * Note: This may or may not return if (flags & EV_EXIT). |
| 984263bc MD |
683 | */ |
| 684 | ||
| 99512ac4 | 685 | static void |
| f2aa3fa5 | 686 | evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) |
| 984263bc MD |
687 | { |
| 688 | struct stackmark smark; | |
| 689 | union node *argp; | |
| 690 | struct arglist arglist; | |
| 691 | struct arglist varlist; | |
| f2aa3fa5 PA |
692 | volatile int flags = flgs; |
| 693 | char **volatile argv; | |
| 694 | volatile int argc; | |
| 984263bc MD |
695 | char **envp; |
| 696 | int varflag; | |
| 697 | struct strlist *sp; | |
| 698 | int mode; | |
| 699 | int pip[2]; | |
| 700 | struct cmdentry cmdentry; | |
| 99512ac4 | 701 | struct job *volatile jp; |
| 984263bc | 702 | struct jmploc jmploc; |
| 99512ac4 PA |
703 | struct jmploc *savehandler; |
| 704 | const char *savecmdname; | |
| 705 | struct shparam saveparam; | |
| 706 | struct localvar *savelocalvars; | |
| 707 | struct parsefile *savetopfile; | |
| 984263bc | 708 | volatile int e; |
| f2aa3fa5 | 709 | char *volatile lastarg; |
| 984263bc | 710 | int realstatus; |
| f2aa3fa5 | 711 | volatile int do_clearcmdentry; |
| 99512ac4 | 712 | const char *path = pathval(); |
| 984263bc MD |
713 | |
| 714 | /* First expand the arguments. */ | |
| 0d5aaed6 | 715 | TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); |
| 984263bc MD |
716 | setstackmark(&smark); |
| 717 | arglist.lastp = &arglist.list; | |
| 718 | varlist.lastp = &varlist.list; | |
| 719 | varflag = 1; | |
| 99512ac4 | 720 | jp = NULL; |
| 984263bc MD |
721 | do_clearcmdentry = 0; |
| 722 | oexitstatus = exitstatus; | |
| 723 | exitstatus = 0; | |
| 724 | for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { | |
| 088f2cdc PA |
725 | if (varflag && isassignment(argp->narg.text)) { |
| 726 | expandarg(argp, &varlist, EXP_VARTILDE); | |
| 727 | continue; | |
| 984263bc MD |
728 | } |
| 729 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | |
| 730 | varflag = 0; | |
| 731 | } | |
| 732 | *arglist.lastp = NULL; | |
| 733 | *varlist.lastp = NULL; | |
| 734 | expredir(cmd->ncmd.redirect); | |
| 735 | argc = 0; | |
| 736 | for (sp = arglist.list ; sp ; sp = sp->next) | |
| 737 | argc++; | |
| 99512ac4 PA |
738 | /* Add one slot at the beginning for tryexec(). */ |
| 739 | argv = stalloc(sizeof (char *) * (argc + 2)); | |
| 740 | argv++; | |
| 984263bc MD |
741 | |
| 742 | for (sp = arglist.list ; sp ; sp = sp->next) { | |
| 743 | TRACE(("evalcommand arg: %s\n", sp->text)); | |
| 744 | *argv++ = sp->text; | |
| 745 | } | |
| 746 | *argv = NULL; | |
| 747 | lastarg = NULL; | |
| 748 | if (iflag && funcnest == 0 && argc > 0) | |
| 749 | lastarg = argv[-1]; | |
| 750 | argv -= argc; | |
| 751 | ||
| 752 | /* Print the command if xflag is set. */ | |
| 753 | if (xflag) { | |
| 0d5aaed6 | 754 | char sep = 0; |
| 97fe2313 PA |
755 | const char *p, *ps4; |
| 756 | ps4 = expandstr(ps4val()); | |
| 757 | out2str(ps4 != NULL ? ps4 : ps4val()); | |
| 984263bc | 758 | for (sp = varlist.list ; sp ; sp = sp->next) { |
| 0d5aaed6 | 759 | if (sep != 0) |
| 99512ac4 PA |
760 | out2c(' '); |
| 761 | p = strchr(sp->text, '='); | |
| 762 | if (p != NULL) { | |
| 763 | p++; | |
| 764 | outbin(sp->text, p - sp->text, out2); | |
| 765 | out2qstr(p); | |
| 766 | } else | |
| 767 | out2qstr(sp->text); | |
| 0d5aaed6 | 768 | sep = ' '; |
| 984263bc MD |
769 | } |
| 770 | for (sp = arglist.list ; sp ; sp = sp->next) { | |
| 0d5aaed6 | 771 | if (sep != 0) |
| 99512ac4 PA |
772 | out2c(' '); |
| 773 | /* Disambiguate command looking like assignment. */ | |
| 774 | if (sp == arglist.list && | |
| 775 | strchr(sp->text, '=') != NULL && | |
| 776 | strchr(sp->text, '\'') == NULL) { | |
| 777 | out2c('\''); | |
| 778 | out2str(sp->text); | |
| 779 | out2c('\''); | |
| 780 | } else | |
| 781 | out2qstr(sp->text); | |
| 0d5aaed6 | 782 | sep = ' '; |
| 984263bc | 783 | } |
| 99512ac4 | 784 | out2c('\n'); |
| 984263bc MD |
785 | flushout(&errout); |
| 786 | } | |
| 787 | ||
| 788 | /* Now locate the command. */ | |
| 789 | if (argc == 0) { | |
| 0d5aaed6 | 790 | /* Variable assignment(s) without command */ |
| 984263bc MD |
791 | cmdentry.cmdtype = CMDBUILTIN; |
| 792 | cmdentry.u.index = BLTINCMD; | |
| 99512ac4 | 793 | cmdentry.special = 0; |
| 984263bc MD |
794 | } else { |
| 795 | static const char PATH[] = "PATH="; | |
| 99512ac4 | 796 | int cmd_flags = 0, bltinonly = 0; |
| 984263bc MD |
797 | |
| 798 | /* | |
| 799 | * Modify the command lookup path, if a PATH= assignment | |
| 800 | * is present | |
| 801 | */ | |
| 802 | for (sp = varlist.list ; sp ; sp = sp->next) | |
| 803 | if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { | |
| 804 | path = sp->text + sizeof(PATH) - 1; | |
| 0d5aaed6 | 805 | /* |
| 984263bc MD |
806 | * On `PATH=... command`, we need to make |
| 807 | * sure that the command isn't using the | |
| 808 | * non-updated hash table of the outer PATH | |
| 0d5aaed6 | 809 | * setting and we need to make sure that |
| 984263bc MD |
810 | * the hash table isn't filled with items |
| 811 | * from the temporary setting. | |
| 812 | * | |
| 0d5aaed6 | 813 | * It would be better to forbit using and |
| 984263bc MD |
814 | * updating the table while this command |
| 815 | * runs, by the command finding mechanism | |
| 816 | * is heavily integrated with hash handling, | |
| 817 | * so we just delete the hash before and after | |
| 818 | * the command runs. Partly deleting like | |
| 819 | * changepatch() does doesn't seem worth the | |
| 820 | * bookinging effort, since most such runs add | |
| 0d5aaed6 | 821 | * directories in front of the new PATH. |
| 984263bc | 822 | */ |
| 99512ac4 | 823 | clearcmdentry(); |
| 984263bc MD |
824 | do_clearcmdentry = 1; |
| 825 | } | |
| 826 | ||
| 99512ac4 PA |
827 | for (;;) { |
| 828 | if (bltinonly) { | |
| 829 | cmdentry.u.index = find_builtin(*argv, &cmdentry.special); | |
| 830 | if (cmdentry.u.index < 0) { | |
| 831 | cmdentry.u.index = BLTINCMD; | |
| 832 | argv--; | |
| 833 | argc++; | |
| 984263bc | 834 | break; |
| 984263bc | 835 | } |
| 99512ac4 PA |
836 | } else |
| 837 | find_command(argv[0], &cmdentry, cmd_flags, path); | |
| 838 | /* implement the bltin and command builtins here */ | |
| 839 | if (cmdentry.cmdtype != CMDBUILTIN) | |
| 840 | break; | |
| 841 | if (cmdentry.u.index == BLTINCMD) { | |
| 842 | if (argc == 1) | |
| 984263bc | 843 | break; |
| 99512ac4 PA |
844 | argv++; |
| 845 | argc--; | |
| 846 | bltinonly = 1; | |
| 847 | } else if (cmdentry.u.index == COMMANDCMD) { | |
| 848 | if (argc == 1) | |
| 849 | break; | |
| 850 | if (!strcmp(argv[1], "-p")) { | |
| 851 | if (argc == 2) | |
| 852 | break; | |
| 853 | if (argv[2][0] == '-') { | |
| 854 | if (strcmp(argv[2], "--")) | |
| 855 | break; | |
| 856 | if (argc == 3) | |
| 857 | break; | |
| 858 | argv += 3; | |
| 859 | argc -= 3; | |
| 860 | } else { | |
| 861 | argv += 2; | |
| 862 | argc -= 2; | |
| 863 | } | |
| 864 | path = _PATH_STDPATH; | |
| 865 | clearcmdentry(); | |
| 866 | do_clearcmdentry = 1; | |
| 867 | } else if (!strcmp(argv[1], "--")) { | |
| 868 | if (argc == 2) | |
| 869 | break; | |
| 870 | argv += 2; | |
| 871 | argc -= 2; | |
| 872 | } else if (argv[1][0] == '-') | |
| 873 | break; | |
| 874 | else { | |
| 875 | argv++; | |
| 876 | argc--; | |
| 877 | } | |
| 878 | cmd_flags |= DO_NOFUNC; | |
| 879 | bltinonly = 0; | |
| 880 | } else | |
| 881 | break; | |
| 984263bc | 882 | } |
| 99512ac4 PA |
883 | /* |
| 884 | * Special builtins lose their special properties when | |
| 885 | * called via 'command'. | |
| 886 | */ | |
| 887 | if (cmd_flags & DO_NOFUNC) | |
| 888 | cmdentry.special = 0; | |
| 984263bc MD |
889 | } |
| 890 | ||
| 891 | /* Fork off a child process if necessary. */ | |
| 892 | if (cmd->ncmd.backgnd | |
| 99512ac4 PA |
893 | || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) |
| 894 | && ((flags & EV_EXIT) == 0 || have_traps())) | |
| 984263bc | 895 | || ((flags & EV_BACKCMD) != 0 |
| 99512ac4 PA |
896 | && (cmdentry.cmdtype != CMDBUILTIN || |
| 897 | !safe_builtin(cmdentry.u.index, argc, argv)))) { | |
| 984263bc MD |
898 | jp = makejob(cmd, 1); |
| 899 | mode = cmd->ncmd.backgnd; | |
| 900 | if (flags & EV_BACKCMD) { | |
| 901 | mode = FORK_NOJOB; | |
| 902 | if (pipe(pip) < 0) | |
| 903 | error("Pipe call failed: %s", strerror(errno)); | |
| 904 | } | |
| 905 | if (forkshell(jp, cmd, mode) != 0) | |
| 906 | goto parent; /* at end of routine */ | |
| 907 | if (flags & EV_BACKCMD) { | |
| 908 | FORCEINTON; | |
| 909 | close(pip[0]); | |
| 910 | if (pip[1] != 1) { | |
| 0d5aaed6 | 911 | dup2(pip[1], 1); |
| 984263bc MD |
912 | close(pip[1]); |
| 913 | } | |
| f15f3668 | 914 | flags &= ~EV_BACKCMD; |
| 984263bc MD |
915 | } |
| 916 | flags |= EV_EXIT; | |
| 917 | } | |
| 918 | ||
| 919 | /* This is the child process if a fork occurred. */ | |
| 920 | /* Execute the command. */ | |
| 921 | if (cmdentry.cmdtype == CMDFUNCTION) { | |
| 922 | #ifdef DEBUG | |
| 923 | trputs("Shell function: "); trargs(argv); | |
| 924 | #endif | |
| 984263bc MD |
925 | saveparam = shellparam; |
| 926 | shellparam.malloc = 0; | |
| 927 | shellparam.reset = 1; | |
| 928 | shellparam.nparam = argc - 1; | |
| 929 | shellparam.p = argv + 1; | |
| 930 | shellparam.optnext = NULL; | |
| 931 | INTOFF; | |
| 932 | savelocalvars = localvars; | |
| 933 | localvars = NULL; | |
| 99512ac4 PA |
934 | reffunc(cmdentry.u.func); |
| 935 | savehandler = handler; | |
| 984263bc | 936 | if (setjmp(jmploc.loc)) { |
| 99512ac4 PA |
937 | freeparam(&shellparam); |
| 938 | shellparam = saveparam; | |
| 2b23d27f | 939 | popredir(); |
| 99512ac4 | 940 | unreffunc(cmdentry.u.func); |
| 984263bc MD |
941 | poplocalvars(); |
| 942 | localvars = savelocalvars; | |
| 99512ac4 | 943 | funcnest--; |
| 984263bc MD |
944 | handler = savehandler; |
| 945 | longjmp(handler->loc, 1); | |
| 946 | } | |
| 984263bc | 947 | handler = &jmploc; |
| 99512ac4 PA |
948 | funcnest++; |
| 949 | redirect(cmd->ncmd.redirect, REDIR_PUSH); | |
| 950 | INTON; | |
| 984263bc MD |
951 | for (sp = varlist.list ; sp ; sp = sp->next) |
| 952 | mklocal(sp->text); | |
| 99512ac4 | 953 | exitstatus = oexitstatus; |
| 2b23d27f PA |
954 | evaltree(getfuncnode(cmdentry.u.func), |
| 955 | flags & (EV_TESTED | EV_EXIT)); | |
| 984263bc | 956 | INTOFF; |
| 99512ac4 | 957 | unreffunc(cmdentry.u.func); |
| 984263bc MD |
958 | poplocalvars(); |
| 959 | localvars = savelocalvars; | |
| 960 | freeparam(&shellparam); | |
| 961 | shellparam = saveparam; | |
| 962 | handler = savehandler; | |
| 99512ac4 | 963 | funcnest--; |
| 984263bc MD |
964 | popredir(); |
| 965 | INTON; | |
| 966 | if (evalskip == SKIPFUNC) { | |
| 967 | evalskip = 0; | |
| 968 | skipcount = 0; | |
| 969 | } | |
| 99512ac4 | 970 | if (jp) |
| 984263bc MD |
971 | exitshell(exitstatus); |
| 972 | } else if (cmdentry.cmdtype == CMDBUILTIN) { | |
| 973 | #ifdef DEBUG | |
| 974 | trputs("builtin command: "); trargs(argv); | |
| 975 | #endif | |
| 976 | mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; | |
| 977 | if (flags == EV_BACKCMD) { | |
| 978 | memout.nleft = 0; | |
| 979 | memout.nextc = memout.buf; | |
| 980 | memout.bufsize = 64; | |
| 981 | mode |= REDIR_BACKQ; | |
| 99512ac4 | 982 | cmdentry.special = 0; |
| 984263bc | 983 | } |
| 984263bc | 984 | savecmdname = commandname; |
| 99512ac4 | 985 | savetopfile = getcurrentfile(); |
| 984263bc MD |
986 | cmdenviron = varlist.list; |
| 987 | e = -1; | |
| 99512ac4 | 988 | savehandler = handler; |
| 984263bc MD |
989 | if (setjmp(jmploc.loc)) { |
| 990 | e = exception; | |
| 2b23d27f PA |
991 | if (e == EXINT) |
| 992 | exitstatus = SIGINT+128; | |
| 993 | else if (e != EXEXIT) | |
| 994 | exitstatus = 2; | |
| 984263bc MD |
995 | goto cmddone; |
| 996 | } | |
| 984263bc | 997 | handler = &jmploc; |
| 0d5aaed6 | 998 | redirect(cmd->ncmd.redirect, mode); |
| 99512ac4 PA |
999 | /* |
| 1000 | * If there is no command word, redirection errors should | |
| 1001 | * not be fatal but assignment errors should. | |
| 1002 | */ | |
| 1003 | if (argc == 0 && !(flags & EV_BACKCMD)) | |
| 1004 | cmdentry.special = 1; | |
| 1005 | listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); | |
| 1006 | if (argc > 0) | |
| 1007 | bltinsetlocale(); | |
| 984263bc MD |
1008 | commandname = argv[0]; |
| 1009 | argptr = argv + 1; | |
| 99512ac4 PA |
1010 | nextopt_optptr = NULL; /* initialize nextopt */ |
| 1011 | builtin_flags = flags; | |
| 984263bc MD |
1012 | exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); |
| 1013 | flushall(); | |
| 1014 | cmddone: | |
| 99512ac4 PA |
1015 | if (argc > 0) |
| 1016 | bltinunsetlocale(); | |
| 984263bc MD |
1017 | cmdenviron = NULL; |
| 1018 | out1 = &output; | |
| 1019 | out2 = &errout; | |
| 1020 | freestdout(); | |
| 984263bc | 1021 | handler = savehandler; |
| 99512ac4 PA |
1022 | commandname = savecmdname; |
| 1023 | if (jp) | |
| 1024 | exitshell(exitstatus); | |
| 984263bc MD |
1025 | if (flags == EV_BACKCMD) { |
| 1026 | backcmd->buf = memout.buf; | |
| 1027 | backcmd->nleft = memout.nextc - memout.buf; | |
| 1028 | memout.buf = NULL; | |
| 1029 | } | |
| 2b23d27f | 1030 | if (cmdentry.u.index != EXECCMD) |
| 99512ac4 PA |
1031 | popredir(); |
| 1032 | if (e != -1) { | |
| 1033 | if ((e != EXERROR && e != EXEXEC) | |
| 1034 | || cmdentry.special) | |
| 1035 | exraise(e); | |
| 1036 | popfilesupto(savetopfile); | |
| 1037 | if (flags != EV_BACKCMD) | |
| 1038 | FORCEINTON; | |
| 1039 | } | |
| 984263bc MD |
1040 | } else { |
| 1041 | #ifdef DEBUG | |
| 1042 | trputs("normal command: "); trargs(argv); | |
| 1043 | #endif | |
| 984263bc MD |
1044 | redirect(cmd->ncmd.redirect, 0); |
| 1045 | for (sp = varlist.list ; sp ; sp = sp->next) | |
| 1046 | setvareq(sp->text, VEXPORT|VSTACK); | |
| 1047 | envp = environment(); | |
| 99512ac4 | 1048 | shellexec(argv, envp, path, cmdentry.u.index); |
| 984263bc MD |
1049 | /*NOTREACHED*/ |
| 1050 | } | |
| 1051 | goto out; | |
| 1052 | ||
| 1053 | parent: /* parent process gets here (if we forked) */ | |
| 99512ac4 | 1054 | if (mode == FORK_FG) { /* argument to fork */ |
| 984263bc MD |
1055 | INTOFF; |
| 1056 | exitstatus = waitforjob(jp, &realstatus); | |
| 1057 | INTON; | |
| 1058 | if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { | |
| 1059 | evalskip = SKIPBREAK; | |
| 1060 | skipcount = loopnest; | |
| 1061 | } | |
| 99512ac4 | 1062 | } else if (mode == FORK_NOJOB) { |
| 984263bc MD |
1063 | backcmd->fd = pip[0]; |
| 1064 | close(pip[1]); | |
| 1065 | backcmd->jp = jp; | |
| 5d4731c8 PA |
1066 | } else |
| 1067 | exitstatus = 0; | |
| 984263bc MD |
1068 | |
| 1069 | out: | |
| 1070 | if (lastarg) | |
| 1071 | setvar("_", lastarg, 0); | |
| 1072 | if (do_clearcmdentry) | |
| 99512ac4 | 1073 | clearcmdentry(); |
| 984263bc MD |
1074 | popstackmark(&smark); |
| 1075 | } | |
| 1076 | ||
| 1077 | ||
| 1078 | ||
| 1079 | /* | |
| 1080 | * Search for a command. This is called before we fork so that the | |
| 1081 | * location of the command will be available in the parent as well as | |
| 1082 | * the child. The check for "goodname" is an overly conservative | |
| 1083 | * check that the name will not be subject to expansion. | |
| 1084 | */ | |
| 1085 | ||
| 99512ac4 | 1086 | static void |
| 984263bc MD |
1087 | prehash(union node *n) |
| 1088 | { | |
| 1089 | struct cmdentry entry; | |
| 1090 | ||
| 0d5aaed6 | 1091 | if (n && n->type == NCMD && n->ncmd.args) |
| 984263bc MD |
1092 | if (goodname(n->ncmd.args->narg.text)) |
| 1093 | find_command(n->ncmd.args->narg.text, &entry, 0, | |
| 1094 | pathval()); | |
| 1095 | } | |
| 1096 | ||
| 1097 | ||
| 1098 | ||
| 1099 | /* | |
| 1100 | * Builtin commands. Builtin commands whose functions are closely | |
| 1101 | * tied to evaluation are implemented here. | |
| 1102 | */ | |
| 1103 | ||
| 1104 | /* | |
| 99512ac4 PA |
1105 | * No command given, a bltin command with no arguments, or a bltin command |
| 1106 | * with an invalid name. | |
| 984263bc MD |
1107 | */ |
| 1108 | ||
| 1109 | int | |
| 99512ac4 | 1110 | bltincmd(int argc, char **argv) |
| 984263bc | 1111 | { |
| 99512ac4 PA |
1112 | if (argc > 1) { |
| 1113 | out2fmt_flush("%s: not found\n", argv[1]); | |
| 1114 | return 127; | |
| 1115 | } | |
| 984263bc MD |
1116 | /* |
| 1117 | * Preserve exitstatus of a previous possible redirection | |
| 1118 | * as POSIX mandates | |
| 1119 | */ | |
| 1120 | return exitstatus; | |
| 1121 | } | |
| 1122 | ||
| 1123 | ||
| 1124 | /* | |
| 1125 | * Handle break and continue commands. Break, continue, and return are | |
| 1126 | * all handled by setting the evalskip flag. The evaluation routines | |
| 1127 | * above all check this flag, and if it is set they start skipping | |
| 1128 | * commands rather than executing them. The variable skipcount is | |
| 1129 | * the number of loops to break/continue, or the number of function | |
| 1130 | * levels to return. (The latter is always 1.) It should probably | |
| 1131 | * be an error to break out of more loops than exist, but it isn't | |
| 1132 | * in the standard shell so we don't make it one here. | |
| 1133 | */ | |
| 1134 | ||
| 1135 | int | |
| 1136 | breakcmd(int argc, char **argv) | |
| 1137 | { | |
| 1138 | int n = argc > 1 ? number(argv[1]) : 1; | |
| 1139 | ||
| 1140 | if (n > loopnest) | |
| 1141 | n = loopnest; | |
| 1142 | if (n > 0) { | |
| 1143 | evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; | |
| 1144 | skipcount = n; | |
| 1145 | } | |
| 1146 | return 0; | |
| 1147 | } | |
| 1148 | ||
| 1149 | /* | |
| 1150 | * The `command' command. | |
| 1151 | */ | |
| 1152 | int | |
| 1153 | commandcmd(int argc, char **argv) | |
| 1154 | { | |
| 99512ac4 | 1155 | const char *path; |
| 984263bc | 1156 | int ch; |
| 0d5aaed6 | 1157 | int cmd = -1; |
| 984263bc | 1158 | |
| 99512ac4 | 1159 | path = bltinlookup("PATH", 1); |
| 984263bc MD |
1160 | |
| 1161 | optind = optreset = 1; | |
| 1162 | opterr = 0; | |
| 0d5aaed6 | 1163 | while ((ch = getopt(argc, argv, "pvV")) != -1) { |
| 984263bc MD |
1164 | switch (ch) { |
| 1165 | case 'p': | |
| 99512ac4 | 1166 | path = _PATH_STDPATH; |
| 984263bc | 1167 | break; |
| 0d5aaed6 PA |
1168 | case 'v': |
| 1169 | cmd = TYPECMD_SMALLV; | |
| 1170 | break; | |
| 1171 | case 'V': | |
| 1172 | cmd = TYPECMD_BIGV; | |
| 1173 | break; | |
| 984263bc MD |
1174 | case '?': |
| 1175 | default: | |
| 1176 | error("unknown option: -%c", optopt); | |
| 1177 | } | |
| 1178 | } | |
| 1179 | argc -= optind; | |
| 1180 | argv += optind; | |
| 1181 | ||
| 0d5aaed6 PA |
1182 | if (cmd != -1) { |
| 1183 | if (argc != 1) | |
| 1184 | error("wrong number of arguments"); | |
| 99512ac4 | 1185 | return typecmd_impl(2, argv - 1, cmd, path); |
| 984263bc | 1186 | } |
| 99512ac4 PA |
1187 | if (argc != 0) |
| 1188 | error("commandcmd bad call"); | |
| 984263bc MD |
1189 | |
| 1190 | /* | |
| 1191 | * Do nothing successfully if no command was specified; | |
| 1192 | * ksh also does this. | |
| 1193 | */ | |
| 99512ac4 | 1194 | return(0); |
| 984263bc MD |
1195 | } |
| 1196 | ||
| 1197 | ||
| 1198 | /* | |
| 1199 | * The return command. | |
| 1200 | */ | |
| 1201 | ||
| 1202 | int | |
| 1203 | returncmd(int argc, char **argv) | |
| 1204 | { | |
| 1205 | int ret = argc > 1 ? number(argv[1]) : oexitstatus; | |
| 1206 | ||
| 1207 | if (funcnest) { | |
| 1208 | evalskip = SKIPFUNC; | |
| 1209 | skipcount = 1; | |
| 1210 | } else { | |
| 1211 | /* skip the rest of the file */ | |
| 1212 | evalskip = SKIPFILE; | |
| 1213 | skipcount = 1; | |
| 1214 | } | |
| 1215 | return ret; | |
| 1216 | } | |
| 1217 | ||
| 1218 | ||
| 1219 | int | |
| 1220 | falsecmd(int argc __unused, char **argv __unused) | |
| 1221 | { | |
| 1222 | return 1; | |
| 1223 | } | |
| 1224 | ||
| 1225 | ||
| 1226 | int | |
| 1227 | truecmd(int argc __unused, char **argv __unused) | |
| 1228 | { | |
| 1229 | return 0; | |
| 1230 | } | |
| 1231 | ||
| 1232 | ||
| 1233 | int | |
| 1234 | execcmd(int argc, char **argv) | |
| 1235 | { | |
| 99512ac4 PA |
1236 | /* |
| 1237 | * Because we have historically not supported any options, | |
| 1238 | * only treat "--" specially. | |
| 1239 | */ | |
| 1240 | if (argc > 1 && strcmp(argv[1], "--") == 0) | |
| 1241 | argc--, argv++; | |
| 984263bc MD |
1242 | if (argc > 1) { |
| 1243 | struct strlist *sp; | |
| 1244 | ||
| 1245 | iflag = 0; /* exit on error */ | |
| 1246 | mflag = 0; | |
| 1247 | optschanged(); | |
| 1248 | for (sp = cmdenviron; sp ; sp = sp->next) | |
| 1249 | setvareq(sp->text, VEXPORT|VSTACK); | |
| 1250 | shellexec(argv + 1, environment(), pathval(), 0); | |
| 1251 | ||
| 1252 | } | |
| 1253 | return 0; | |
| 1254 | } | |
| 0d5aaed6 PA |
1255 | |
| 1256 | ||
| 1257 | int | |
| 1258 | timescmd(int argc __unused, char **argv __unused) | |
| 1259 | { | |
| 1260 | struct rusage ru; | |
| 1261 | long shumins, shsmins, chumins, chsmins; | |
| 1262 | double shusecs, shssecs, chusecs, chssecs; | |
| 1263 | ||
| 1264 | if (getrusage(RUSAGE_SELF, &ru) < 0) | |
| 1265 | return 1; | |
| 1266 | shumins = ru.ru_utime.tv_sec / 60; | |
| 1267 | shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; | |
| 1268 | shsmins = ru.ru_stime.tv_sec / 60; | |
| 1269 | shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; | |
| 1270 | if (getrusage(RUSAGE_CHILDREN, &ru) < 0) | |
| 1271 | return 1; | |
| 1272 | chumins = ru.ru_utime.tv_sec / 60; | |
| 1273 | chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; | |
| 1274 | chsmins = ru.ru_stime.tv_sec / 60; | |
| 1275 | chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; | |
| 1276 | out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, | |
| 1277 | shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); | |
| 1278 | return 0; | |
| 1279 | } |