1*1c3b0983Skre /* $NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Kenneth Almquist. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35cd799663Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 3749f0ad86Scgd #if 0 38a45947b2Schristos static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 3949f0ad86Scgd #else 40*1c3b0983Skre __RCSID("$NetBSD: eval.c,v 1.197 2024/11/11 22:57:42 kre Exp $"); 41b5f33fc1Scgd #endif 4261f28255Scgd #endif /* not lint */ 4361f28255Scgd 4435fbf8ddStron #include <stdbool.h> 45e314f958Sdsl #include <stdlib.h> 4607bae7edSchristos #include <signal.h> 471aafd1a4Sagc #include <stdio.h> 48f87bc150Schristos #include <string.h> 49eb956cbdSchristos #include <errno.h> 500e82f4ebSdsl #include <limits.h> 5107bae7edSchristos #include <unistd.h> 5200a6315eSdsl #include <sys/fcntl.h> 537792ef1aSchristos #include <sys/stat.h> 54c02b3bbdSchristos #include <sys/times.h> 550923f4beSrafal #include <sys/param.h> 56edcb4544Schristos #include <sys/types.h> 57edcb4544Schristos #include <sys/wait.h> 58e314f958Sdsl #include <sys/sysctl.h> 59edcb4544Schristos 6061f28255Scgd /* 6161f28255Scgd * Evaluate a command. 6261f28255Scgd */ 6361f28255Scgd 6461f28255Scgd #include "shell.h" 6561f28255Scgd #include "nodes.h" 6661f28255Scgd #include "syntax.h" 6761f28255Scgd #include "expand.h" 6861f28255Scgd #include "parser.h" 6961f28255Scgd #include "jobs.h" 7061f28255Scgd #include "eval.h" 7161f28255Scgd #include "builtins.h" 7261f28255Scgd #include "options.h" 7361f28255Scgd #include "exec.h" 7461f28255Scgd #include "redir.h" 7561f28255Scgd #include "input.h" 7661f28255Scgd #include "output.h" 7761f28255Scgd #include "trap.h" 7861f28255Scgd #include "var.h" 7961f28255Scgd #include "memalloc.h" 8061f28255Scgd #include "error.h" 8107bae7edSchristos #include "show.h" 8261f28255Scgd #include "mystring.h" 836f482334Schristos #include "main.h" 847accaec4Schristos #ifndef SMALL 85bc8cfb7dSchristos #include "nodenames.h" 8637ed7877Sjtc #include "myhistedit.h" 87242337bcScgd #endif 8861f28255Scgd 8961f28255Scgd 9013fc5c1bSkre STATIC struct skipsave s_k_i_p; 9113fc5c1bSkre #define evalskip (s_k_i_p.state) 9213fc5c1bSkre #define skipcount (s_k_i_p.count) 9313fc5c1bSkre 9401f35fccSchristos STATIC int loopnest; /* current loop nesting level */ 9501f35fccSchristos STATIC int funcnest; /* depth of function calls */ 961847bab5Schristos STATIC int builtin_flags; /* evalcommand flags for builtins */ 9701f35fccSchristos /* 9801f35fccSchristos * Base function nesting level inside a dot command. Set to 0 initially 9901f35fccSchristos * and to (funcnest + 1) before every dot command to enable 10001f35fccSchristos * 1) detection of being in a file sourced by a dot command and 10101f35fccSchristos * 2) counting of function nesting in that file for the implementation 10201f35fccSchristos * of the return command. 10301f35fccSchristos * The value is reset to its previous value after the dot command. 10401f35fccSchristos */ 10501f35fccSchristos STATIC int dot_funcnest; 10661f28255Scgd 10761f28255Scgd 1084498b1feSmatt const char *commandname; 10961f28255Scgd struct strlist *cmdenviron; 11061f28255Scgd int exitstatus; /* exit status of last command */ 111c02b3bbdSchristos int back_exitstatus; /* exit status of backquoted command */ 11261f28255Scgd 11361f28255Scgd 114c02b3bbdSchristos STATIC void evalloop(union node *, int); 115c02b3bbdSchristos STATIC void evalfor(union node *, int); 116c02b3bbdSchristos STATIC void evalcase(union node *, int); 117c02b3bbdSchristos STATIC void evalsubshell(union node *, int); 118c02b3bbdSchristos STATIC void expredir(union node *); 1198a9a9619Skre STATIC void evalredir(union node *, int); 120c02b3bbdSchristos STATIC void evalpipe(union node *); 121c02b3bbdSchristos STATIC void evalcommand(union node *, int, struct backcmd *); 122c02b3bbdSchristos STATIC void prehash(union node *); 12361f28255Scgd 12401f35fccSchristos STATIC char *find_dot_file(char *); 12561f28255Scgd 12661f28255Scgd /* 12761f28255Scgd * Called to reset things after an exception. 12861f28255Scgd */ 12961f28255Scgd 13061f28255Scgd #ifdef mkinit 13161f28255Scgd INCLUDE "eval.h" 13261f28255Scgd 13361f28255Scgd RESET { 13401f35fccSchristos reset_eval(); 13561f28255Scgd } 13661f28255Scgd 13761f28255Scgd SHELLPROC { 13861f28255Scgd exitstatus = 0; 13961f28255Scgd } 14061f28255Scgd #endif 14161f28255Scgd 14201f35fccSchristos void 14301f35fccSchristos reset_eval(void) 14401f35fccSchristos { 14501f35fccSchristos evalskip = SKIPNONE; 14601f35fccSchristos dot_funcnest = 0; 14701f35fccSchristos loopnest = 0; 14801f35fccSchristos funcnest = 0; 14901f35fccSchristos } 15001f35fccSchristos 15100a6315eSdsl static int 15200a6315eSdsl sh_pipe(int fds[2]) 15300a6315eSdsl { 15400a6315eSdsl int nfd; 15500a6315eSdsl 15600a6315eSdsl if (pipe(fds)) 15700a6315eSdsl return -1; 15800a6315eSdsl 15900a6315eSdsl if (fds[0] < 3) { 16000a6315eSdsl nfd = fcntl(fds[0], F_DUPFD, 3); 16100a6315eSdsl if (nfd != -1) { 16200a6315eSdsl close(fds[0]); 16300a6315eSdsl fds[0] = nfd; 16400a6315eSdsl } 16500a6315eSdsl } 16600a6315eSdsl 16700a6315eSdsl if (fds[1] < 3) { 16800a6315eSdsl nfd = fcntl(fds[1], F_DUPFD, 3); 16900a6315eSdsl if (nfd != -1) { 17000a6315eSdsl close(fds[1]); 17100a6315eSdsl fds[1] = nfd; 17200a6315eSdsl } 17300a6315eSdsl } 17400a6315eSdsl return 0; 17500a6315eSdsl } 17661f28255Scgd 17761f28255Scgd 17861f28255Scgd /* 17922b6bc1fSmsaitoh * The eval command. 18061f28255Scgd */ 18161f28255Scgd 1825dad1439Scgd int 183c02b3bbdSchristos evalcmd(int argc, char **argv) 18461f28255Scgd { 18561f28255Scgd char *p; 18661f28255Scgd char *concat; 18761f28255Scgd char **ap; 18861f28255Scgd 18961f28255Scgd if (argc > 1) { 19061f28255Scgd p = argv[1]; 19161f28255Scgd if (argc > 2) { 19261f28255Scgd STARTSTACKSTR(concat); 19361f28255Scgd ap = argv + 2; 19461f28255Scgd for (;;) { 19561f28255Scgd while (*p) 19661f28255Scgd STPUTC(*p++, concat); 19761f28255Scgd if ((p = *ap++) == NULL) 19861f28255Scgd break; 19961f28255Scgd STPUTC(' ', concat); 20061f28255Scgd } 20161f28255Scgd STPUTC('\0', concat); 20261f28255Scgd p = grabstackstr(concat); 20361f28255Scgd } 2041847bab5Schristos evalstring(p, builtin_flags & EV_TESTED); 205b8d196a5Skre } else 206b8d196a5Skre exitstatus = 0; 20761f28255Scgd return exitstatus; 20861f28255Scgd } 20961f28255Scgd 21061f28255Scgd 21161f28255Scgd /* 21261f28255Scgd * Execute a command or commands contained in a string. 21361f28255Scgd */ 21461f28255Scgd 21561f28255Scgd void 21650a5715bSkre evalstring(const char *s, int flag) 21761f28255Scgd { 21861f28255Scgd union node *n; 21961f28255Scgd struct stackmark smark; 2208a9a9619Skre int last; 2218a9a9619Skre int any; 2228a9a9619Skre 2238a9a9619Skre last = flag & EV_EXIT; 2248a9a9619Skre flag &= ~EV_EXIT; 22561f28255Scgd 22661f28255Scgd setstackmark(&smark); 227727a69dcSkre setinputstring(s, 1, line_number); 2286f482334Schristos 2298a9a9619Skre any = 0; /* to determine if exitstatus will have been set */ 23061f28255Scgd while ((n = parsecmd(0)) != NEOF) { 23120013177Skre XTRACE(DBG_EVAL, ("evalstring: "), showtree(n)); 2328a9a9619Skre if (n && nflag == 0) { 2338a9a9619Skre if (last && at_eof()) 2348a9a9619Skre evaltree(n, flag | EV_EXIT); 2358a9a9619Skre else 236120267c3Skre evaltree(n, flag); 2378a9a9619Skre any = 1; 238c43d5e9fSkre if (evalskip) 239c43d5e9fSkre break; 2408a9a9619Skre } 24170696c01Skre rststackmark(&smark); 24261f28255Scgd } 24361f28255Scgd popfile(); 24461f28255Scgd popstackmark(&smark); 2458a9a9619Skre if (!any) 2468a9a9619Skre exitstatus = 0; 2478a9a9619Skre if (last) 2488a9a9619Skre exraise(EXEXIT); 24961f28255Scgd } 25061f28255Scgd 25161f28255Scgd 25261f28255Scgd 25361f28255Scgd /* 25461f28255Scgd * Evaluate a parse tree. The value is left in the global variable 25561f28255Scgd * exitstatus. 25661f28255Scgd */ 25761f28255Scgd 25861f28255Scgd void 259c02b3bbdSchristos evaltree(union node *n, int flags) 26061f28255Scgd { 26135fbf8ddStron bool do_etest; 2625e63d1ceSkre int sflags = flags & ~EV_EXIT; 2638a9a9619Skre union node *next; 2648a9a9619Skre struct stackmark smark; 26535fbf8ddStron 26635fbf8ddStron do_etest = false; 267a584b40fSchristos if (n == NULL || nflag) { 26820013177Skre VTRACE(DBG_EVAL, ("evaltree(%s) called\n", 26920013177Skre n == NULL ? "NULL" : "-n")); 270a584b40fSchristos if (nflag == 0) 27137ed7877Sjtc exitstatus = 0; 2728a9a9619Skre goto out2; 27361f28255Scgd } 2748a9a9619Skre 2758a9a9619Skre setstackmark(&smark); 2768a9a9619Skre do { 2777accaec4Schristos #ifndef SMALL 27837ed7877Sjtc displayhist = 1; /* show history substitutions done with fc */ 279e3c63ad9Scgd #endif 2808a9a9619Skre next = NULL; 28120013177Skre CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n", 282bc8cfb7dSchristos getpid(), n, NODETYPENAME(n->type), n->type, flags)); 2834a370dceSkre /* 28413fc5c1bSkre if (n->type != NCMD && traps_invalid) 28513fc5c1bSkre free_traps(); 2864a370dceSkre */ 28761f28255Scgd switch (n->type) { 28861f28255Scgd case NSEMI: 2898a9a9619Skre evaltree(n->nbinary.ch1, sflags); 290a584b40fSchristos if (nflag || evalskip) 2918a9a9619Skre goto out1; 2928a9a9619Skre next = n->nbinary.ch2; 29361f28255Scgd break; 29461f28255Scgd case NAND: 295120267c3Skre evaltree(n->nbinary.ch1, EV_TESTED); 296a584b40fSchristos if (nflag || evalskip || exitstatus != 0) 2978a9a9619Skre goto out1; 2988a9a9619Skre next = n->nbinary.ch2; 29961f28255Scgd break; 30061f28255Scgd case NOR: 301120267c3Skre evaltree(n->nbinary.ch1, EV_TESTED); 302a584b40fSchristos if (nflag || evalskip || exitstatus == 0) 3038a9a9619Skre goto out1; 3048a9a9619Skre next = n->nbinary.ch2; 30561f28255Scgd break; 30661f28255Scgd case NREDIR: 3074a370dceSkre if (traps_invalid) 3084a370dceSkre free_traps(); 3098a9a9619Skre evalredir(n, flags); 31061f28255Scgd break; 31161f28255Scgd case NSUBSHELL: 312120267c3Skre evalsubshell(n, flags); 3139cae530bStron do_etest = !(flags & EV_TESTED); 31461f28255Scgd break; 31561f28255Scgd case NBACKGND: 3164a370dceSkre if (traps_invalid) 3174a370dceSkre free_traps(); 318120267c3Skre evalsubshell(n, flags); 31961f28255Scgd break; 32061f28255Scgd case NIF: { 3214a370dceSkre if (traps_invalid) 3224a370dceSkre free_traps(); 323120267c3Skre evaltree(n->nif.test, EV_TESTED); 324a584b40fSchristos if (nflag || evalskip) 3258a9a9619Skre goto out1; 326b09ffc42Schristos if (exitstatus == 0) 3278a9a9619Skre next = n->nif.ifpart; 3282e197048Schristos else if (n->nif.elsepart) 3298a9a9619Skre next = n->nif.elsepart; 3300b9acf16Spk else 3310b9acf16Spk exitstatus = 0; 33261f28255Scgd break; 33361f28255Scgd } 33461f28255Scgd case NWHILE: 33561f28255Scgd case NUNTIL: 3364a370dceSkre if (traps_invalid) 3374a370dceSkre free_traps(); 3385e63d1ceSkre evalloop(n, sflags); 33961f28255Scgd break; 34061f28255Scgd case NFOR: 3414a370dceSkre if (traps_invalid) 3424a370dceSkre free_traps(); 3435e63d1ceSkre evalfor(n, sflags); 34461f28255Scgd break; 34561f28255Scgd case NCASE: 3464a370dceSkre if (traps_invalid) 3474a370dceSkre free_traps(); 3485e63d1ceSkre evalcase(n, sflags); 34961f28255Scgd break; 35061f28255Scgd case NDEFUN: 3514a370dceSkre if (traps_invalid) 3524a370dceSkre free_traps(); 3538a9a9619Skre CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n", 3548a9a9619Skre n->narg.text, n->narg.lineno, 3558a9a9619Skre fnline1 ? " LINENO=1" : "")); 356727a69dcSkre defun(n->narg.text, n->narg.next, n->narg.lineno); 35761f28255Scgd exitstatus = 0; 35861f28255Scgd break; 35937ed7877Sjtc case NNOT: 360120267c3Skre evaltree(n->nnot.com, EV_TESTED); 36137ed7877Sjtc exitstatus = !exitstatus; 36237ed7877Sjtc break; 363aa563ca4Skre case NDNOT: 364120267c3Skre evaltree(n->nnot.com, EV_TESTED); 365aa563ca4Skre if (exitstatus != 0) 366aa563ca4Skre exitstatus = 1; 367aa563ca4Skre break; 36861f28255Scgd case NPIPE: 3694a370dceSkre if (traps_invalid) 3704a370dceSkre free_traps(); 3714783843fSchristos evalpipe(n); 3729cae530bStron do_etest = !(flags & EV_TESTED); 37361f28255Scgd break; 37461f28255Scgd case NCMD: 3759f61b804Splunky evalcommand(n, flags, NULL); 3769cae530bStron do_etest = !(flags & EV_TESTED); 37761f28255Scgd break; 37861f28255Scgd default: 379a584b40fSchristos #ifdef NODETYPENAME 3808a9a9619Skre out1fmt("Node type = %d(%s)\n", 3818a9a9619Skre n->type, NODETYPENAME(n->type)); 382a584b40fSchristos #else 38361f28255Scgd out1fmt("Node type = %d\n", n->type); 384a584b40fSchristos #endif 38561f28255Scgd flushout(&output); 38661f28255Scgd break; 38761f28255Scgd } 3888a9a9619Skre n = next; 38970696c01Skre rststackmark(&smark); 3908a9a9619Skre } while(n != NULL); 3918a9a9619Skre out1: 3928a9a9619Skre popstackmark(&smark); 3938a9a9619Skre out2: 39461f28255Scgd if (pendingsigs) 39561f28255Scgd dotrap(); 3968a9a9619Skre if (eflag && exitstatus != 0 && do_etest) 39761f28255Scgd exitshell(exitstatus); 3988a9a9619Skre if (flags & EV_EXIT) 3998a9a9619Skre exraise(EXEXIT); 40061f28255Scgd } 40161f28255Scgd 40261f28255Scgd 40361f28255Scgd STATIC void 404c02b3bbdSchristos evalloop(union node *n, int flags) 40561f28255Scgd { 40661f28255Scgd int status; 40761f28255Scgd 40861f28255Scgd loopnest++; 40961f28255Scgd status = 0; 410bc8cfb7dSchristos 41120013177Skre CTRACE(DBG_EVAL, ("evalloop %s:", NODETYPENAME(n->type))); 41220013177Skre VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1)); 41320013177Skre VXTRACE(DBG_EVAL, ("evalloop do: "), showtree(n->nbinary.ch2)); 41420013177Skre VTRACE(DBG_EVAL, ("evalloop done\n")); 41520013177Skre CTRACE(DBG_EVAL, ("\n")); 416bc8cfb7dSchristos 41761f28255Scgd for (;;) { 418120267c3Skre evaltree(n->nbinary.ch1, EV_TESTED); 419a584b40fSchristos if (nflag) 420a584b40fSchristos break; 42161f28255Scgd if (evalskip) { 42261f28255Scgd skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 42301f35fccSchristos evalskip = SKIPNONE; 42461f28255Scgd continue; 42561f28255Scgd } 42661f28255Scgd if (evalskip == SKIPBREAK && --skipcount <= 0) 42701f35fccSchristos evalskip = SKIPNONE; 428c531b568Skre if (evalskip == SKIPFUNC || evalskip == SKIPFILE) 429c531b568Skre status = exitstatus; 43061f28255Scgd break; 43161f28255Scgd } 43261f28255Scgd if (n->type == NWHILE) { 43361f28255Scgd if (exitstatus != 0) 43461f28255Scgd break; 43561f28255Scgd } else { 43661f28255Scgd if (exitstatus == 0) 43761f28255Scgd break; 43861f28255Scgd } 439120267c3Skre evaltree(n->nbinary.ch2, flags & EV_TESTED); 44061f28255Scgd status = exitstatus; 44161f28255Scgd if (evalskip) 44261f28255Scgd goto skipping; 44361f28255Scgd } 44461f28255Scgd loopnest--; 44561f28255Scgd exitstatus = status; 44661f28255Scgd } 44761f28255Scgd 44861f28255Scgd 44961f28255Scgd 45061f28255Scgd STATIC void 451c02b3bbdSchristos evalfor(union node *n, int flags) 45261f28255Scgd { 45361f28255Scgd struct arglist arglist; 45461f28255Scgd union node *argp; 45561f28255Scgd struct strlist *sp; 45661f28255Scgd struct stackmark smark; 457a584b40fSchristos int status; 458a584b40fSchristos 459a584b40fSchristos status = nflag ? exitstatus : 0; 46061f28255Scgd 46161f28255Scgd setstackmark(&smark); 46261f28255Scgd arglist.lastp = &arglist.list; 46361f28255Scgd for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 464c6cbc16dSdsl expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 46561f28255Scgd if (evalskip) 46661f28255Scgd goto out; 46761f28255Scgd } 46861f28255Scgd *arglist.lastp = NULL; 46961f28255Scgd 47061f28255Scgd loopnest++; 47161f28255Scgd for (sp = arglist.list ; sp ; sp = sp->next) { 472a41dbcb0Skre line_number = n->nfor.lineno; 473a3881786Skre if (xflag) { 474ffc64c63Skre outxstr(expandstr(ps4val(), line_number)); 475ffc64c63Skre outxstr("for "); 476ffc64c63Skre outxstr(n->nfor.var); 477ffc64c63Skre outxc('='); 478ffc64c63Skre outxshstr(sp->text); 479ffc64c63Skre outxc('\n'); 480ffc64c63Skre flushout(outx); 481a3881786Skre } 482a3881786Skre 48361f28255Scgd setvar(n->nfor.var, sp->text, 0); 484120267c3Skre evaltree(n->nfor.body, flags & EV_TESTED); 485c02b3bbdSchristos status = exitstatus; 486a584b40fSchristos if (nflag) 487a584b40fSchristos break; 48861f28255Scgd if (evalskip) { 48961f28255Scgd if (evalskip == SKIPCONT && --skipcount <= 0) { 49001f35fccSchristos evalskip = SKIPNONE; 49161f28255Scgd continue; 49261f28255Scgd } 49361f28255Scgd if (evalskip == SKIPBREAK && --skipcount <= 0) 49401f35fccSchristos evalskip = SKIPNONE; 49561f28255Scgd break; 49661f28255Scgd } 49761f28255Scgd } 49861f28255Scgd loopnest--; 499c02b3bbdSchristos exitstatus = status; 50061f28255Scgd out: 50161f28255Scgd popstackmark(&smark); 50261f28255Scgd } 50361f28255Scgd 50461f28255Scgd 50561f28255Scgd 50661f28255Scgd STATIC void 507c02b3bbdSchristos evalcase(union node *n, int flags) 50861f28255Scgd { 5097d41ae4eSkre union node *cp, *ncp; 51061f28255Scgd union node *patp; 51161f28255Scgd struct arglist arglist; 51261f28255Scgd struct stackmark smark; 513c02b3bbdSchristos int status = 0; 51461f28255Scgd 51561f28255Scgd setstackmark(&smark); 51661f28255Scgd arglist.lastp = &arglist.list; 517727a69dcSkre line_number = n->ncase.lineno; 51837ed7877Sjtc expandarg(n->ncase.expr, &arglist, EXP_TILDE); 51961f28255Scgd for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { 52061f28255Scgd for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { 521727a69dcSkre line_number = patp->narg.lineno; 52261f28255Scgd if (casematch(patp, arglist.list->text)) { 5237d41ae4eSkre while (cp != NULL && evalskip == 0 && 5247d41ae4eSkre nflag == 0) { 5257d41ae4eSkre if (cp->type == NCLISTCONT) 5267d41ae4eSkre ncp = cp->nclist.next; 5277d41ae4eSkre else 5287d41ae4eSkre ncp = NULL; 529727a69dcSkre line_number = cp->nclist.lineno; 530120267c3Skre evaltree(cp->nclist.body, flags); 531c02b3bbdSchristos status = exitstatus; 5327d41ae4eSkre cp = ncp; 53361f28255Scgd } 53461f28255Scgd goto out; 53561f28255Scgd } 53661f28255Scgd } 53761f28255Scgd } 53861f28255Scgd out: 539c02b3bbdSchristos exitstatus = status; 54061f28255Scgd popstackmark(&smark); 54161f28255Scgd } 54261f28255Scgd 54361f28255Scgd 54461f28255Scgd 54561f28255Scgd /* 54661f28255Scgd * Kick off a subshell to evaluate a tree. 54761f28255Scgd */ 54861f28255Scgd 54961f28255Scgd STATIC void 550c02b3bbdSchristos evalsubshell(union node *n, int flags) 55161f28255Scgd { 5528a9a9619Skre struct job *jp= NULL; 55361f28255Scgd int backgnd = (n->type == NBACKGND); 55461f28255Scgd 55561f28255Scgd expredir(n->nredir.redirect); 5563b297678Skre if (xflag && n->nredir.redirect) { 5573b297678Skre union node *rn; 5583b297678Skre 559ffc64c63Skre outxstr(expandstr(ps4val(), line_number)); 560ffc64c63Skre outxstr("using redirections:"); 5613b297678Skre for (rn = n->nredir.redirect; rn; rn = rn->nfile.next) 562ffc64c63Skre (void) outredir(outx, rn, ' '); 5638a9a9619Skre outxstr(" do subshell ("/*)*/); 5648a9a9619Skre if (backgnd) 5658a9a9619Skre outxstr(/*(*/") &"); 5668a9a9619Skre outxc('\n'); 567ffc64c63Skre flushout(outx); 5683b297678Skre } 569e8999de4Skre INTOFF; 5708821fc2cSkre if ((!backgnd && flags & EV_EXIT && !have_traps() && !anyjobs()) || 5718a9a9619Skre forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) { 57261f28255Scgd if (backgnd) 57361f28255Scgd flags &=~ EV_TESTED; 574e8999de4Skre INTON; 575a6dacc22Skre redirect(n->nredir.redirect, REDIR_KEEP); 5768a9a9619Skre evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 577aec4b6e2Skre } else if (backgnd) { 578aec4b6e2Skre jobstarted(jp); 579e8999de4Skre exitstatus = 0; 580aec4b6e2Skre } else 5818a9a9619Skre exitstatus = waitforjob(jp); 58261f28255Scgd INTON; 5838a9a9619Skre 5843b297678Skre if (!backgnd && xflag && n->nredir.redirect) { 585ffc64c63Skre outxstr(expandstr(ps4val(), line_number)); 5868a9a9619Skre outxstr(/*(*/") done subshell\n"); 587ffc64c63Skre flushout(outx); 5883b297678Skre } 58961f28255Scgd } 59061f28255Scgd 59161f28255Scgd 59261f28255Scgd 59361f28255Scgd /* 59461f28255Scgd * Compute the names of the files in a redirection list. 59561f28255Scgd */ 59661f28255Scgd 59761f28255Scgd STATIC void 598c02b3bbdSchristos expredir(union node *n) 59961f28255Scgd { 60048250187Stls union node *redir; 60161f28255Scgd 60261f28255Scgd for (redir = n ; redir ; redir = redir->nfile.next) { 60361f28255Scgd struct arglist fn; 6041fad4bb6Schristos 60561f28255Scgd fn.lastp = &fn.list; 606918ce04fSjtc switch (redir->type) { 6076e50d7a8Schristos case NFROMTO: 608918ce04fSjtc case NFROM: 609918ce04fSjtc case NTO: 610f629aa28Schristos case NCLOBBER: 611918ce04fSjtc case NAPPEND: 61237ed7877Sjtc expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 61361f28255Scgd redir->nfile.expfname = fn.list->text; 614918ce04fSjtc break; 615918ce04fSjtc case NFROMFD: 616918ce04fSjtc case NTOFD: 617918ce04fSjtc if (redir->ndup.vname) { 6183892a5b0Skre expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); 619918ce04fSjtc fixredir(redir, fn.list->text, 1); 620918ce04fSjtc } 621918ce04fSjtc break; 62216d85571Skre case NHERE: 62316d85571Skre redir->nhere.text = redir->nhere.doc->narg.text; 62416d85571Skre break; 62516d85571Skre case NXHERE: 62616d85571Skre redir->nhere.text = expandhere(redir->nhere.doc); 62716d85571Skre break; 62861f28255Scgd } 62961f28255Scgd } 63061f28255Scgd } 63161f28255Scgd 6328a9a9619Skre /* 6338a9a9619Skre * Perform redirections for a compound command, and then do it (and restore) 6348a9a9619Skre */ 6358a9a9619Skre STATIC void 6368a9a9619Skre evalredir(union node *n, int flags) 6378a9a9619Skre { 6388a9a9619Skre struct jmploc jmploc; 6398a9a9619Skre struct jmploc * const savehandler = handler; 6408a9a9619Skre volatile int in_redirect = 1; 6418a9a9619Skre const char * volatile PS4 = NULL; 6428a9a9619Skre 6438a9a9619Skre expredir(n->nredir.redirect); 6448a9a9619Skre 6458a9a9619Skre if (xflag && n->nredir.redirect) { 6468a9a9619Skre union node *rn; 6478a9a9619Skre 6488a9a9619Skre outxstr(PS4 = expandstr(ps4val(), line_number)); 6498a9a9619Skre outxstr("using redirections:"); 6508a9a9619Skre for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next) 6518a9a9619Skre (void) outredir(outx, rn, ' '); 6528a9a9619Skre outxstr(" do {\n"); /* } */ 6538a9a9619Skre flushout(outx); 6548a9a9619Skre } 6558a9a9619Skre 6568a9a9619Skre if (setjmp(jmploc.loc)) { 6578a9a9619Skre int e; 6588a9a9619Skre 6598a9a9619Skre handler = savehandler; 6608a9a9619Skre e = exception; 661*1c3b0983Skre popredir(POPREDIR_UNDO); 6620db5b60cSkre if (PS4 != NULL) { 6638a9a9619Skre outxstr(PS4); 6648a9a9619Skre /* { */ outxstr("} failed\n"); 6658a9a9619Skre flushout(outx); 6668a9a9619Skre } 6678a9a9619Skre if (e == EXERROR || e == EXEXEC) { 6688a9a9619Skre if (in_redirect) { 6698a9a9619Skre exitstatus = 2; 6708a9a9619Skre return; 6718a9a9619Skre } 6728a9a9619Skre } 6738a9a9619Skre longjmp(handler->loc, 1); 6748a9a9619Skre } else { 6758a9a9619Skre INTOFF; 6768a9a9619Skre handler = &jmploc; 6778a9a9619Skre redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP); 6788a9a9619Skre in_redirect = 0; 6798a9a9619Skre INTON; 6808a9a9619Skre evaltree(n->nredir.n, flags); 6818a9a9619Skre } 6828a9a9619Skre INTOFF; 6838a9a9619Skre handler = savehandler; 684*1c3b0983Skre popredir(POPREDIR_UNDO); 6858a9a9619Skre INTON; 6868a9a9619Skre 6870db5b60cSkre if (PS4 != NULL) { 6888a9a9619Skre outxstr(PS4); 6898a9a9619Skre /* { */ outxstr("} done\n"); 6908a9a9619Skre flushout(outx); 6918a9a9619Skre } 6928a9a9619Skre } 69361f28255Scgd 69461f28255Scgd 69561f28255Scgd /* 69661f28255Scgd * Evaluate a pipeline. All the processes in the pipeline are children 69761f28255Scgd * of the process creating the pipeline. (This differs from some versions 69861f28255Scgd * of the shell, which make the last process in a pipeline the parent 69961f28255Scgd * of all the rest.) 70061f28255Scgd */ 70161f28255Scgd 70261f28255Scgd STATIC void 703c02b3bbdSchristos evalpipe(union node *n) 70461f28255Scgd { 70561f28255Scgd struct job *jp; 70661f28255Scgd struct nodelist *lp; 70761f28255Scgd int pipelen; 70861f28255Scgd int prevfd; 70961f28255Scgd int pip[2]; 71061f28255Scgd 71120013177Skre CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n)); 71261f28255Scgd pipelen = 0; 71361f28255Scgd for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 71461f28255Scgd pipelen++; 71561f28255Scgd INTOFF; 71661f28255Scgd jp = makejob(n, pipelen); 71761f28255Scgd prevfd = -1; 71861f28255Scgd for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 71961f28255Scgd prehash(lp->n); 72061f28255Scgd pip[1] = -1; 72161f28255Scgd if (lp->next) { 72200a6315eSdsl if (sh_pipe(pip) < 0) { 723e80f354dSchristos if (prevfd >= 0) 72461f28255Scgd close(prevfd); 7258a9a9619Skre error("Pipe call failed: %s", strerror(errno)); 72661f28255Scgd } 72761f28255Scgd } 7288a9a9619Skre if (forkshell(jp, lp->n, 7298a9a9619Skre n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { 73061f28255Scgd INTON; 7311fad4bb6Schristos if (prevfd > 0) 7321fad4bb6Schristos movefd(prevfd, 0); 73361f28255Scgd if (pip[1] >= 0) { 73461f28255Scgd close(pip[0]); 7351fad4bb6Schristos movefd(pip[1], 1); 73661f28255Scgd } 7374783843fSchristos evaltree(lp->n, EV_EXIT); 73861f28255Scgd } 73961f28255Scgd if (prevfd >= 0) 74061f28255Scgd close(prevfd); 74161f28255Scgd prevfd = pip[0]; 74261f28255Scgd close(pip[1]); 74361f28255Scgd } 74461f28255Scgd if (n->npipe.backgnd == 0) { 74561f28255Scgd exitstatus = waitforjob(jp); 74620013177Skre CTRACE(DBG_EVAL, ("evalpipe: job done exit status %d\n", 74720013177Skre exitstatus)); 748aec4b6e2Skre } else { 749aec4b6e2Skre jobstarted(jp); 750a584b40fSchristos exitstatus = 0; 751aec4b6e2Skre } 7522aa6ebd4Smycroft INTON; 75361f28255Scgd } 75461f28255Scgd 75561f28255Scgd 75661f28255Scgd 75761f28255Scgd /* 75861f28255Scgd * Execute a command inside back quotes. If it's a builtin command, we 75961f28255Scgd * want to save its output in a block obtained from malloc. Otherwise 76061f28255Scgd * we fork off a subprocess and get the output of the command via a pipe. 76161f28255Scgd * Should be called with interrupts off. 76261f28255Scgd */ 76361f28255Scgd 76461f28255Scgd void 765c02b3bbdSchristos evalbackcmd(union node *n, struct backcmd *result) 76661f28255Scgd { 76761f28255Scgd int pip[2]; 76861f28255Scgd struct job *jp; 7698a9a9619Skre struct stackmark smark; /* unnecessary (because we fork) */ 77061f28255Scgd 77161f28255Scgd result->fd = -1; 77261f28255Scgd result->buf = NULL; 77361f28255Scgd result->nleft = 0; 77461f28255Scgd result->jp = NULL; 7758a9a9619Skre 7768a9a9619Skre if (nflag || n == NULL) 77737ed7877Sjtc goto out; 7788a9a9619Skre 7798a9a9619Skre setstackmark(&smark); 7808a9a9619Skre 78102048e84Schristos #ifdef notyet 78202048e84Schristos /* 78302048e84Schristos * For now we disable executing builtins in the same 78402048e84Schristos * context as the shell, because we are not keeping 78502048e84Schristos * enough state to recover from changes that are 78602048e84Schristos * supposed only to affect subshells. eg. echo "`cd /`" 78702048e84Schristos */ 78837ed7877Sjtc if (n->type == NCMD) { 7898a9a9619Skre exitstatus = oexitstatus; /* XXX o... no longer exists */ 7904783843fSchristos evalcommand(n, EV_BACKCMD, result); 79102048e84Schristos } else 79202048e84Schristos #endif 79302048e84Schristos { 794d84d3616Smycroft INTOFF; 79500a6315eSdsl if (sh_pipe(pip) < 0) 79661f28255Scgd error("Pipe call failed"); 79761f28255Scgd jp = makejob(n, 1); 7984783843fSchristos if (forkshell(jp, n, FORK_NOJOB) == 0) { 79961f28255Scgd FORCEINTON; 80061f28255Scgd close(pip[0]); 8011fad4bb6Schristos movefd(pip[1], 1); 8024783843fSchristos evaltree(n, EV_EXIT); 803c02b3bbdSchristos /* NOTREACHED */ 80461f28255Scgd } 80561f28255Scgd close(pip[1]); 80661f28255Scgd result->fd = pip[0]; 80761f28255Scgd result->jp = jp; 808d84d3616Smycroft INTON; 80961f28255Scgd } 81061f28255Scgd popstackmark(&smark); 8118a9a9619Skre out: 81220013177Skre CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", 81361f28255Scgd result->fd, result->buf, result->nleft, result->jp)); 81461f28255Scgd } 81561f28255Scgd 816e2f17f9aSkre const char * 817e314f958Sdsl syspath(void) 818e314f958Sdsl { 819e314f958Sdsl static char *sys_path = NULL; 820e314f958Sdsl static int mib[] = {CTL_USER, USER_CS_PATH}; 821027df12dSchristos static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; 82253ed48f3Sagc size_t len; 823e314f958Sdsl 824e314f958Sdsl if (sys_path == NULL) { 825e314f958Sdsl if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && 826e314f958Sdsl (sys_path = ckmalloc(len + 5)) != NULL && 827e314f958Sdsl sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { 828e314f958Sdsl memcpy(sys_path, "PATH=", 5); 829e314f958Sdsl } else { 830e314f958Sdsl ckfree(sys_path); 831e314f958Sdsl /* something to keep things happy */ 832027df12dSchristos sys_path = def_path; 833e314f958Sdsl } 834e314f958Sdsl } 835e314f958Sdsl return sys_path; 836e314f958Sdsl } 837e314f958Sdsl 838e314f958Sdsl static int 839e314f958Sdsl parse_command_args(int argc, char **argv, int *use_syspath) 840e314f958Sdsl { 841e314f958Sdsl int sv_argc = argc; 842e314f958Sdsl char *cp, c; 843e314f958Sdsl 844e314f958Sdsl *use_syspath = 0; 845e314f958Sdsl 846e314f958Sdsl for (;;) { 847e314f958Sdsl argv++; 848e314f958Sdsl if (--argc == 0) 849e314f958Sdsl break; 850e314f958Sdsl cp = *argv; 851e314f958Sdsl if (*cp++ != '-') 852e314f958Sdsl break; 853e314f958Sdsl if (*cp == '-' && cp[1] == 0) { 854e314f958Sdsl argv++; 855e314f958Sdsl argc--; 856e314f958Sdsl break; 857e314f958Sdsl } 858e314f958Sdsl while ((c = *cp++)) { 859e314f958Sdsl switch (c) { 860e314f958Sdsl case 'p': 861e314f958Sdsl *use_syspath = 1; 862e314f958Sdsl break; 863e314f958Sdsl default: 864e314f958Sdsl /* run 'typecmd' for other options */ 865e314f958Sdsl return 0; 866e314f958Sdsl } 867e314f958Sdsl } 868e314f958Sdsl } 869e314f958Sdsl return sv_argc - argc; 870e314f958Sdsl } 87161f28255Scgd 872edcb4544Schristos int vforked = 0; 87361f28255Scgd 87461f28255Scgd /* 87561f28255Scgd * Execute a simple command. 87661f28255Scgd */ 87761f28255Scgd 87861f28255Scgd STATIC void 87927293760Schristos evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) 88061f28255Scgd { 88161f28255Scgd struct stackmark smark; 88261f28255Scgd union node *argp; 88361f28255Scgd struct arglist arglist; 88461f28255Scgd struct arglist varlist; 88527293760Schristos volatile int flags = flgs; 88627293760Schristos char ** volatile argv; 88727293760Schristos volatile int argc; 88861f28255Scgd char **envp; 88961f28255Scgd int varflag; 89061f28255Scgd struct strlist *sp; 89127293760Schristos volatile int mode; 89261f28255Scgd int pip[2]; 89361f28255Scgd struct cmdentry cmdentry; 89427293760Schristos struct job * volatile jp; 89561f28255Scgd struct jmploc jmploc; 896df8cbb18Schristos struct jmploc *volatile savehandler = NULL; 8974498b1feSmatt const char *volatile savecmdname; 89861f28255Scgd volatile struct shparam saveparam; 89961f28255Scgd struct localvar *volatile savelocalvars; 900a582e232Skre struct parsefile *volatile savetopfile; 90161f28255Scgd volatile int e; 90227293760Schristos char * volatile lastarg; 90327293760Schristos const char * volatile path = pathval(); 90450cde64bSlukem volatile int temp_path; 905727a69dcSkre const int savefuncline = funclinebase; 906727a69dcSkre const int savefuncabs = funclineabs; 907d7fa5d2fSkre volatile int cmd_flags = 0; 90861f28255Scgd 909edcb4544Schristos vforked = 0; 91061f28255Scgd /* First expand the arguments. */ 911ffc64c63Skre CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags, 912ffc64c63Skre cmd->ncmd.args ? cmd->ncmd.args->narg.text : "")); 91361f28255Scgd setstackmark(&smark); 914c02b3bbdSchristos back_exitstatus = 0; 915c02b3bbdSchristos 916727a69dcSkre line_number = cmd->ncmd.lineno; 917fd38bbe2Skre 91861f28255Scgd arglist.lastp = &arglist.list; 91961f28255Scgd varflag = 1; 920c6cbc16dSdsl /* Expand arguments, ignoring the initial 'name=value' ones */ 92161f28255Scgd for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 922021ba509Skre if (varflag && isassignment(argp->narg.text)) 92361f28255Scgd continue; 92461f28255Scgd varflag = 0; 925021ba509Skre line_number = argp->narg.lineno; 926021ba509Skre expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 92761f28255Scgd } 92861f28255Scgd *arglist.lastp = NULL; 929c02b3bbdSchristos 93061f28255Scgd expredir(cmd->ncmd.redirect); 931c02b3bbdSchristos 932c6cbc16dSdsl /* Now do the initial 'name=value' ones we skipped above */ 933c02b3bbdSchristos varlist.lastp = &varlist.list; 934c02b3bbdSchristos for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 935727a69dcSkre line_number = argp->narg.lineno; 936021ba509Skre if (!isassignment(argp->narg.text)) 937c02b3bbdSchristos break; 938c6d0f408Skre /* EXP_CASE handles CTL* chars in expansions properly */ 939c6d0f408Skre expandarg(argp, &varlist, EXP_VARTILDE | EXP_CASE); 940c02b3bbdSchristos } 941c02b3bbdSchristos *varlist.lastp = NULL; 942c02b3bbdSchristos 94361f28255Scgd argc = 0; 94461f28255Scgd for (sp = arglist.list ; sp ; sp = sp->next) 94561f28255Scgd argc++; 94661f28255Scgd argv = stalloc(sizeof (char *) * (argc + 1)); 94737ed7877Sjtc 94837ed7877Sjtc for (sp = arglist.list ; sp ; sp = sp->next) { 94920013177Skre VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text)); 95061f28255Scgd *argv++ = sp->text; 95137ed7877Sjtc } 95261f28255Scgd *argv = NULL; 95361f28255Scgd lastarg = NULL; 95461f28255Scgd if (iflag && funcnest == 0 && argc > 0) 95561f28255Scgd lastarg = argv[-1]; 95661f28255Scgd argv -= argc; 95761f28255Scgd 95861f28255Scgd /* Print the command if xflag is set. */ 95961f28255Scgd if (xflag) { 960e314f958Sdsl char sep = 0; 9613b297678Skre union node *rn; 9623b297678Skre 963ffc64c63Skre outxstr(expandstr(ps4val(), line_number)); 96461f28255Scgd for (sp = varlist.list ; sp ; sp = sp->next) { 965f87bc150Schristos char *p; 966f87bc150Schristos 967e314f958Sdsl if (sep != 0) 968ffc64c63Skre outxc(sep); 969f87bc150Schristos 970f87bc150Schristos /* 971f87bc150Schristos * The "var=" part should not be quoted, regardless 972f87bc150Schristos * of the value, or it would not represent an 973f87bc150Schristos * assignment, but rather a command 974f87bc150Schristos */ 975f87bc150Schristos p = strchr(sp->text, '='); 976f87bc150Schristos if (p != NULL) { 977f87bc150Schristos *p = '\0'; /*XXX*/ 978ffc64c63Skre outxshstr(sp->text); 979ffc64c63Skre outxc('='); 980f87bc150Schristos *p++ = '='; /*XXX*/ 981f87bc150Schristos } else 982f87bc150Schristos p = sp->text; 983ffc64c63Skre outxshstr(p); 984e314f958Sdsl sep = ' '; 98561f28255Scgd } 98661f28255Scgd for (sp = arglist.list ; sp ; sp = sp->next) { 987e314f958Sdsl if (sep != 0) 988ffc64c63Skre outxc(sep); 989ffc64c63Skre outxshstr(sp->text); 990e314f958Sdsl sep = ' '; 99161f28255Scgd } 9923b297678Skre for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next) 993ffc64c63Skre if (outredir(outx, rn, sep)) 9943b297678Skre sep = ' '; 995ffc64c63Skre outxc('\n'); 996ffc64c63Skre flushout(outx); 99761f28255Scgd } 99861f28255Scgd 99961f28255Scgd /* Now locate the command. */ 100061f28255Scgd if (argc == 0) { 1001d7fa5d2fSkre /* 1002d7fa5d2fSkre * the empty command begins as a normal builtin, and 1003d7fa5d2fSkre * remains that way while redirects are processed, then 1004d7fa5d2fSkre * will become special before we get to doing the 1005d7fa5d2fSkre * var assigns. 1006d7fa5d2fSkre */ 1007d7fa5d2fSkre cmdentry.cmdtype = CMDBUILTIN; 1008c02b3bbdSchristos cmdentry.u.bltin = bltincmd; 1009418b4863Smsaitoh VTRACE(DBG_CMDS, ("No command name, assume \"command\"\n")); 101061f28255Scgd } else { 1011a45947b2Schristos static const char PATH[] = "PATH="; 1012a45947b2Schristos 1013a45947b2Schristos /* 1014a45947b2Schristos * Modify the command lookup path, if a PATH= assignment 1015a45947b2Schristos * is present 1016a45947b2Schristos */ 1017a45947b2Schristos for (sp = varlist.list; sp; sp = sp->next) 1018a45947b2Schristos if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) 1019a45947b2Schristos path = sp->text + sizeof(PATH) - 1; 1020a45947b2Schristos 1021e314f958Sdsl do { 1022e314f958Sdsl int argsused, use_syspath; 1023727a69dcSkre 1024e314f958Sdsl find_command(argv[0], &cmdentry, cmd_flags, path); 102539879a1cSkre VTRACE(DBG_CMDS, ("Command %s type %d\n", argv[0], 102639879a1cSkre cmdentry.cmdtype)); 102716ccf0eeSkre #if 0 102816ccf0eeSkre /* 102916ccf0eeSkre * This short circuits all of the processing that 103016ccf0eeSkre * should be done (including processing the 103116ccf0eeSkre * redirects), so just don't ... 103216ccf0eeSkre * 103316ccf0eeSkre * (eventually this whole #if'd block will vanish) 103416ccf0eeSkre */ 1035e314f958Sdsl if (cmdentry.cmdtype == CMDUNKNOWN) { 1036886c875aSchristos exitstatus = 127; 103761f28255Scgd flushout(&errout); 1038e314f958Sdsl goto out; 103961f28255Scgd } 104016ccf0eeSkre #endif 1041e314f958Sdsl 1042e314f958Sdsl /* implement the 'command' builtin here */ 1043e314f958Sdsl if (cmdentry.cmdtype != CMDBUILTIN || 1044e314f958Sdsl cmdentry.u.bltin != bltincmd) 104561f28255Scgd break; 104639879a1cSkre VTRACE(DBG_CMDS, ("Command \"command\"\n")); 1047e314f958Sdsl cmd_flags |= DO_NOFUNC; 1048e314f958Sdsl argsused = parse_command_args(argc, argv, &use_syspath); 1049e314f958Sdsl if (argsused == 0) { 1050765ad10cSkre /* use 'type' builtin to display info */ 105139879a1cSkre VTRACE(DBG_CMDS, 105239879a1cSkre ("Command \"command\" -> \"type\"\n")); 1053e314f958Sdsl cmdentry.u.bltin = typecmd; 105461f28255Scgd break; 105561f28255Scgd } 1056e314f958Sdsl argc -= argsused; 1057e314f958Sdsl argv += argsused; 1058e314f958Sdsl if (use_syspath) 1059e314f958Sdsl path = syspath() + 5; 1060e314f958Sdsl } while (argc != 0); 1061e314f958Sdsl if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) 1062e314f958Sdsl /* posix mandates that 'command <splbltin>' act as if 1063e314f958Sdsl <splbltin> was a normal builtin */ 1064e314f958Sdsl cmdentry.cmdtype = CMDBUILTIN; 106561f28255Scgd } 106661f28255Scgd 10676b3f9583Skre /* 10686b3f9583Skre * When traps are invalid, we permit the following: 10696b3f9583Skre * trap 10706b3f9583Skre * command trap 10716b3f9583Skre * eval trap 10726b3f9583Skre * command eval trap 10736b3f9583Skre * eval command trap 10746b3f9583Skre * without zapping the traps completely, in all other cases we do. 10754a370dceSkre * Function calls also do not zap the traps (but commands they execute 10764a370dceSkre * probably will) - this allows a function like 10774a370dceSkre * trapstate() { trap -p; } 10784a370dceSkre * called as save_traps=$(trapstate). 10796b3f9583Skre * 10806b3f9583Skre * The test here permits eval "anything" but when evalstring() comes 10816b3f9583Skre * back here again, the "anything" will be validated. 10826b3f9583Skre * This means we can actually do: 10836b3f9583Skre * eval eval eval command eval eval command trap 10846b3f9583Skre * as long as we end up with just "trap" 10856b3f9583Skre * 10866b3f9583Skre * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN 10876b3f9583Skre * 10886b3f9583Skre * trapcmd() takes care of doing free_traps() if it is needed there. 10896b3f9583Skre */ 10906b3f9583Skre if (traps_invalid && 10914a370dceSkre cmdentry.cmdtype != CMDFUNCTION && 10926b3f9583Skre ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) || 10936b3f9583Skre (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd))) 109413fc5c1bSkre free_traps(); 109513fc5c1bSkre 109661f28255Scgd /* Fork off a child process if necessary. */ 10976fc2ffa0Skre if (cmd->ncmd.backgnd 109816ccf0eeSkre || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) 10996fc2ffa0Skre && (have_traps() || (flags & EV_EXIT) == 0)) 11006fc2ffa0Skre #ifdef notyet /* EV_BACKCMD is never set currently */ 11016fc2ffa0Skre /* this will need more work if/when it gets used */ 11026fc2ffa0Skre || ((flags & EV_BACKCMD) != 0 11036fc2ffa0Skre && (cmdentry.cmdtype != CMDBUILTIN 11046fc2ffa0Skre && cmdentry.cmdtype != CMDSPLBLTIN) 1105c02b3bbdSchristos || cmdentry.u.bltin == dotcmd 11066fc2ffa0Skre || cmdentry.u.bltin == evalcmd) 11076fc2ffa0Skre #endif 11086fc2ffa0Skre ) { 110935975338Schristos INTOFF; 111061f28255Scgd jp = makejob(cmd, 1); 111161f28255Scgd mode = cmd->ncmd.backgnd; 111261f28255Scgd if (flags & EV_BACKCMD) { 111361f28255Scgd mode = FORK_NOJOB; 111400a6315eSdsl if (sh_pipe(pip) < 0) 111561f28255Scgd error("Pipe call failed"); 111661f28255Scgd } 1117edcb4544Schristos #ifdef DO_SHAREDVFORK 1118edcb4544Schristos /* It is essential that if DO_SHAREDVFORK is defined that the 1119edcb4544Schristos * child's address space is actually shared with the parent as 1120edcb4544Schristos * we rely on this. 1121edcb4544Schristos */ 1122a6dacc22Skre if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL && 1123a6dacc22Skre (!cmd->ncmd.backgnd || cmd->ncmd.redirect == NULL)) { 1124edcb4544Schristos pid_t pid; 11251468e9a3Schristos int serrno; 1126edcb4544Schristos 1127edcb4544Schristos savelocalvars = localvars; 1128edcb4544Schristos localvars = NULL; 1129edcb4544Schristos vforked = 1; 11305dd8362aSkre VFORK_BLOCK 1131edcb4544Schristos switch (pid = vfork()) { 1132edcb4544Schristos case -1: 11331468e9a3Schristos serrno = errno; 113420013177Skre VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n", 113520013177Skre serrno)); 1136edcb4544Schristos INTON; 11371468e9a3Schristos error("Cannot vfork (%s)", strerror(serrno)); 1138edcb4544Schristos break; 1139edcb4544Schristos case 0: 1140edcb4544Schristos /* Make sure that exceptions only unwind to 1141edcb4544Schristos * after the vfork(2) 1142edcb4544Schristos */ 11435dd8362aSkre SHELL_FORKED(); 1144edcb4544Schristos if (setjmp(jmploc.loc)) { 1145b8bee70dSkre VTRACE(DBG_EVAL|DBG_ERRS| 1146b8bee70dSkre DBG_PROCS|DBG_CMDS|DBG_TRAP, 1147b8bee70dSkre ("vfork child exit exception:%d " 1148b8bee70dSkre "exitstatus:%d exerrno:%d\n", 1149b8bee70dSkre exception, exitstatus, exerrno)); 1150b8bee70dSkre 1151edcb4544Schristos if (exception == EXSHELLPROC) { 115220013177Skre /* 115320013177Skre * We can't progress with the 115420013177Skre * vfork, so, set vforked = 2 115520013177Skre * so the parent knows, 115620013177Skre * and _exit(); 1157edcb4544Schristos */ 1158edcb4544Schristos vforked = 2; 1159edcb4544Schristos _exit(0); 1160edcb4544Schristos } else { 1161ce53a30dSkre _exit(exception == EXEXIT ? 1162ce53a30dSkre exitstatus : exerrno); 1163edcb4544Schristos } 1164edcb4544Schristos } 1165edcb4544Schristos savehandler = handler; 1166edcb4544Schristos handler = &jmploc; 1167522d1631Skre listmklocal(varlist.list, 1168522d1631Skre VDOEXPORT | VEXPORT | VNOFUNC); 11694783843fSchristos forkchild(jp, cmd, mode, vforked); 1170edcb4544Schristos break; 1171edcb4544Schristos default: 11725dd8362aSkre VFORK_UNDO(); 117320013177Skre /* restore from vfork(2) */ 117439879a1cSkre CTRACE(DBG_PROCS|DBG_CMDS, 117539879a1cSkre ("parent after vfork - vforked=%d\n", 117639879a1cSkre vforked)); 117720013177Skre handler = savehandler; 1178edcb4544Schristos poplocalvars(); 1179edcb4544Schristos localvars = savelocalvars; 1180edcb4544Schristos if (vforked == 2) { 1181edcb4544Schristos vforked = 0; 1182edcb4544Schristos 1183edcb4544Schristos (void)waitpid(pid, NULL, 0); 118420013177Skre /* 118520013177Skre * We need to progress in a 118620013177Skre * normal fork fashion 118720013177Skre */ 1188edcb4544Schristos goto normal_fork; 1189edcb4544Schristos } 119020013177Skre /* 119120013177Skre * Here the child has left home, 119220013177Skre * getting on with its life, so 119320013177Skre * so must we... 119420013177Skre */ 1195edcb4544Schristos vforked = 0; 11964783843fSchristos forkparent(jp, cmd, mode, pid); 1197edcb4544Schristos goto parent; 1198edcb4544Schristos } 11995dd8362aSkre VFORK_END 1200edcb4544Schristos } else { 1201edcb4544Schristos normal_fork: 1202edcb4544Schristos #endif 12034783843fSchristos if (forkshell(jp, cmd, mode) != 0) 120461f28255Scgd goto parent; /* at end of routine */ 120539879a1cSkre CTRACE(DBG_PROCS|DBG_CMDS, ("Child sets EV_EXIT\n")); 12068a9a9619Skre flags |= EV_EXIT; 12076166cc6eSchristos FORCEINTON; 1208edcb4544Schristos #ifdef DO_SHAREDVFORK 1209edcb4544Schristos } 1210edcb4544Schristos #endif 121161f28255Scgd if (flags & EV_BACKCMD) { 1212edcb4544Schristos if (!vforked) { 121361f28255Scgd FORCEINTON; 1214edcb4544Schristos } 121561f28255Scgd close(pip[0]); 12161fad4bb6Schristos movefd(pip[1], 1); 121761f28255Scgd } 121861f28255Scgd flags |= EV_EXIT; 121961f28255Scgd } 122061f28255Scgd 122161f28255Scgd /* This is the child process if a fork occurred. */ 122261f28255Scgd /* Execute the command. */ 1223e314f958Sdsl switch (cmdentry.cmdtype) { 12248a9a9619Skre volatile int saved; 122529a26373Skre struct funcdef * volatile savefunc; 12265a21c2d7Skre const char * volatile savectx; 12278a9a9619Skre 1228e314f958Sdsl case CMDFUNCTION: 1229f07d3f9bSkre VXTRACE(DBG_EVAL, ("Shell function%s: ",vforked?" VF":""), 1230f07d3f9bSkre trargs(argv)); 1231*1c3b0983Skre redirect(cmd->ncmd.redirect, REDIR_KEEP | (saved = 1232*1c3b0983Skre !(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0)); 123361f28255Scgd saveparam = shellparam; 123461f28255Scgd shellparam.malloc = 0; 1235ccce082dSchristos shellparam.reset = 1; 123661f28255Scgd shellparam.nparam = argc - 1; 123761f28255Scgd shellparam.p = argv + 1; 123861f28255Scgd shellparam.optnext = NULL; 123961f28255Scgd INTOFF; 124061f28255Scgd savelocalvars = localvars; 12415a21c2d7Skre savectx = currentcontext; 124261f28255Scgd localvars = NULL; 124329a26373Skre reffunc(savefunc = cmdentry.u.func); 124461f28255Scgd INTON; 124561f28255Scgd if (setjmp(jmploc.loc)) { 12463d424690Schristos if (exception == EXSHELLPROC) { 12473d424690Schristos freeparam((volatile struct shparam *) 12483d424690Schristos &saveparam); 12493d424690Schristos } else { 125061f28255Scgd freeparam(&shellparam); 125161f28255Scgd shellparam = saveparam; 125261f28255Scgd } 12538a9a9619Skre if (saved) 1254*1c3b0983Skre popredir(POPREDIR_UNDO); 125529a26373Skre unreffunc(savefunc); 125661f28255Scgd poplocalvars(); 125761f28255Scgd localvars = savelocalvars; 1258727a69dcSkre funclinebase = savefuncline; 1259727a69dcSkre funclineabs = savefuncabs; 12605a21c2d7Skre currentcontext = savectx; 126161f28255Scgd handler = savehandler; 126261f28255Scgd longjmp(handler->loc, 1); 126361f28255Scgd } 126461f28255Scgd savehandler = handler; 126561f28255Scgd handler = &jmploc; 1266727a69dcSkre if (cmdentry.u.func) { 1267727a69dcSkre if (cmdentry.lno_frel) 1268727a69dcSkre funclinebase = cmdentry.lineno - 1; 1269727a69dcSkre else 1270727a69dcSkre funclinebase = 0; 1271727a69dcSkre funclineabs = cmdentry.lineno; 12725a21c2d7Skre currentcontext = argv[0]; 1273727a69dcSkre 1274727a69dcSkre VTRACE(DBG_EVAL, 1275727a69dcSkre ("function: node: %d '%s' # %d%s; funclinebase=%d\n", 1276c7c0722aSkre getfuncnode(cmdentry.u.func)->type, 1277c7c0722aSkre NODETYPENAME(getfuncnode(cmdentry.u.func)->type), 1278727a69dcSkre cmdentry.lineno, cmdentry.lno_frel?" (=1)":"", 1279727a69dcSkre funclinebase)); 1280727a69dcSkre } 1281522d1631Skre listmklocal(varlist.list, VDOEXPORT | VEXPORT); 1282e314f958Sdsl /* stop shell blowing its stack */ 1283e314f958Sdsl if (++funcnest > 1000) 1284e314f958Sdsl error("too many nested function calls"); 12858a9a9619Skre evaltree(getfuncnode(cmdentry.u.func), 12868a9a9619Skre flags & (EV_TESTED|EV_EXIT)); 128761f28255Scgd funcnest--; 128861f28255Scgd INTOFF; 1289c7c0722aSkre unreffunc(cmdentry.u.func); 129061f28255Scgd poplocalvars(); 129161f28255Scgd localvars = savelocalvars; 1292727a69dcSkre funclinebase = savefuncline; 1293727a69dcSkre funclineabs = savefuncabs; 12945a21c2d7Skre currentcontext = savectx; 129561f28255Scgd freeparam(&shellparam); 129661f28255Scgd shellparam = saveparam; 129761f28255Scgd handler = savehandler; 12988a9a9619Skre if (saved) 1299*1c3b0983Skre popredir(POPREDIR_UNDO); 130061f28255Scgd INTON; 130161f28255Scgd if (evalskip == SKIPFUNC) { 130201f35fccSchristos evalskip = SKIPNONE; 130361f28255Scgd skipcount = 0; 130461f28255Scgd } 130561f28255Scgd if (flags & EV_EXIT) 130661f28255Scgd exitshell(exitstatus); 1307e314f958Sdsl break; 1308e314f958Sdsl 1309e314f958Sdsl case CMDSPLBLTIN: 1310d7fa5d2fSkre VTRACE(DBG_EVAL, ("special ")); 1311d7fa5d2fSkre case CMDBUILTIN: 1312d7fa5d2fSkre VXTRACE(DBG_EVAL, ("builtin command [%d]%s: ", argc, 1313d7fa5d2fSkre vforked ? " VF" : ""), trargs(argv)); 1314*1c3b0983Skre 1315*1c3b0983Skre if (cmdentry.u.bltin == execcmd) { 1316*1c3b0983Skre char **ap; 1317*1c3b0983Skre 1318*1c3b0983Skre /* 1319*1c3b0983Skre * Work out how we should process redirections 1320*1c3b0983Skre * on the "exec" command. We need REDIR_KEEP 1321*1c3b0983Skre * if we must not set close-on-exec, and REDIR_PUSH 1322*1c3b0983Skre * if we need to be able to undo them (in the 1323*1c3b0983Skre * exec command, only on some kind of error). 1324*1c3b0983Skre * 1325*1c3b0983Skre * Skip "exec" (argv[0]) then examine args. 1326*1c3b0983Skre * 1327*1c3b0983Skre * This must be done manually, as nextopt() 1328*1c3b0983Skre * hasn't been init'd for this command yet. 1329*1c3b0983Skre * And it won't be until after redirections are done. 1330*1c3b0983Skre * 1331*1c3b0983Skre * "exec" currently takes no options (except "--"), 1332*1c3b0983Skre * but might one day, and this needs to keep working, 1333*1c3b0983Skre * so do it, kind of, properly. 1334*1c3b0983Skre * 1335*1c3b0983Skre * Note in the common cases argv[1] will be NULL 1336*1c3b0983Skre * (for exec just setting up redirectons) or will 1337*1c3b0983Skre * not start with a '-' ("exec cmd") so normally 1338*1c3b0983Skre * this loop will either never start or will break 1339*1c3b0983Skre * at the first test of the first iteration. 1340*1c3b0983Skre */ 1341*1c3b0983Skre for (ap = argv + 1; *ap != NULL; ap++) { 1342*1c3b0983Skre 1343*1c3b0983Skre if (ap[0][0] != '-') 1344*1c3b0983Skre break; 1345*1c3b0983Skre 1346*1c3b0983Skre if (ap[0][1] == '\0') /* "exec -" */ 1347*1c3b0983Skre break; /* or continue ?? */ 1348*1c3b0983Skre 1349*1c3b0983Skre if (ap[0][1] == '-' && ap[0][2] == '\0') { 1350*1c3b0983Skre ap++; 1351*1c3b0983Skre break; 1352*1c3b0983Skre } 1353*1c3b0983Skre 1354*1c3b0983Skre #if defined(DUMMY_EXAMPLE_CODE) && 0 1355*1c3b0983Skre /* 1356*1c3b0983Skre * if options are added to "exec" then 1357*1c3b0983Skre * any which take an arg (like the common 1358*1c3b0983Skre * in other shells "-a cmdname") need to 1359*1c3b0983Skre * be recognised here, lest "cmdname" be 1360*1c3b0983Skre * thought to be the cmd to exec 1361*1c3b0983Skre */ 1362*1c3b0983Skre 1363*1c3b0983Skre for (char *op = ap[0] + 1; *op; op++) { 1364*1c3b0983Skre switch (*op) { 1365*1c3b0983Skre case 'a': 1366*1c3b0983Skre case any others similar: 1367*1c3b0983Skre /* options needing an optarg */ 1368*1c3b0983Skre if (op[1] == '\0' && ap[1]) 1369*1c3b0983Skre ap++; 1370*1c3b0983Skre break; 1371*1c3b0983Skre 1372*1c3b0983Skre default: 1373*1c3b0983Skre /* options with no optarg */ 1374*1c3b0983Skre continue; 1375*1c3b0983Skre } 1376*1c3b0983Skre break; 1377*1c3b0983Skre } 1378*1c3b0983Skre #endif /* DUMMY EXAMPLE CODE */ 1379*1c3b0983Skre } 1380*1c3b0983Skre 1381*1c3b0983Skre if (*ap != NULL) 1382*1c3b0983Skre mode = REDIR_KEEP; /* exec cmd <... */ 1383*1c3b0983Skre else 1384*1c3b0983Skre mode = 0; /* exec < .... */ 1385*1c3b0983Skre 1386*1c3b0983Skre /* 1387*1c3b0983Skre * always save old fd setup in case of error() 1388*1c3b0983Skre * execcmd() will undo this if no error occurs 1389*1c3b0983Skre * (that is, in the case the shell has not vanished) 1390*1c3b0983Skre */ 1391*1c3b0983Skre mode |= REDIR_PUSH; 1392*1c3b0983Skre } else /* any builtin execpt "exec" */ 1393*1c3b0983Skre mode = REDIR_PUSH | REDIR_KEEP; 1394*1c3b0983Skre 139561f28255Scgd if (flags == EV_BACKCMD) { 139661f28255Scgd memout.nleft = 0; 139761f28255Scgd memout.nextc = memout.buf; 139861f28255Scgd memout.bufsize = 64; 139961f28255Scgd mode |= REDIR_BACKQ; 140061f28255Scgd } 1401*1c3b0983Skre 1402e314f958Sdsl e = -1; 14038693718bSdsl savecmdname = commandname; 1404a582e232Skre savetopfile = getcurrentfile(); 1405a582e232Skre savehandler = handler; 14060adfd5e0Sjoerg temp_path = 0; 1407*1c3b0983Skre 1408c02b3bbdSchristos if (!setjmp(jmploc.loc)) { 1409a582e232Skre handler = &jmploc; 1410a582e232Skre 141120013177Skre /* 141220013177Skre * We need to ensure the command hash table isn't 1413afa63a66Schristos * corrupted by temporary PATH assignments. 1414e314f958Sdsl * However we must ensure the 'local' command works! 1415e314f958Sdsl */ 1416e314f958Sdsl if (path != pathval() && (cmdentry.u.bltin == hashcmd || 1417e314f958Sdsl cmdentry.u.bltin == typecmd)) { 1418e314f958Sdsl savelocalvars = localvars; 1419e314f958Sdsl localvars = 0; 1420e314f958Sdsl temp_path = 1; 14210adfd5e0Sjoerg mklocal(path - 5 /* PATH= */, 0); 14220adfd5e0Sjoerg } 1423c02b3bbdSchristos redirect(cmd->ncmd.redirect, mode); 1424c02b3bbdSchristos 1425d7fa5d2fSkre /* 1426d7fa5d2fSkre * the empty command is regarded as a normal 1427d7fa5d2fSkre * builtin for the purposes of redirects, but 1428d7fa5d2fSkre * is a special builtin for var assigns. 1429d7fa5d2fSkre * (unless we are the "command" command.) 1430d7fa5d2fSkre */ 1431d7fa5d2fSkre if (argc == 0 && !(cmd_flags & DO_NOFUNC)) 1432d7fa5d2fSkre cmdentry.cmdtype = CMDSPLBLTIN; 1433d7fa5d2fSkre 1434c02b3bbdSchristos /* exec is a special builtin, but needs this list... */ 1435c02b3bbdSchristos cmdenviron = varlist.list; 1436c02b3bbdSchristos /* we must check 'readonly' flag for all builtins */ 1437c02b3bbdSchristos listsetvar(varlist.list, 1438c02b3bbdSchristos cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); 143961f28255Scgd commandname = argv[0]; 1440c02b3bbdSchristos /* initialize nextopt */ 144161f28255Scgd argptr = argv + 1; 1442c02b3bbdSchristos optptr = NULL; 1443c02b3bbdSchristos /* and getopt */ 1444c02b3bbdSchristos optreset = 1; 1445c02b3bbdSchristos optind = 1; 14461847bab5Schristos builtin_flags = flags; 1447b65a5b90Skre clr_err(out1); /* discard previous I/O errors */ 1448c02b3bbdSchristos exitstatus = cmdentry.u.bltin(argc, argv); 1449c02b3bbdSchristos } else { 1450c02b3bbdSchristos e = exception; 14518a9a9619Skre if (e == EXINT) 14528a9a9619Skre exitstatus = SIGINT + 128; 14538a9a9619Skre else if (e == EXEXEC) 14548a9a9619Skre exitstatus = exerrno; 14558a9a9619Skre else if (e != EXEXIT) 14568a9a9619Skre exitstatus = 2; 1457c02b3bbdSchristos } 1458e314f958Sdsl handler = savehandler; 145961f28255Scgd flushall(); 146061f28255Scgd out1 = &output; 146161f28255Scgd out2 = &errout; 146261f28255Scgd freestdout(); 1463e314f958Sdsl if (temp_path) { 1464e314f958Sdsl poplocalvars(); 1465e314f958Sdsl localvars = savelocalvars; 1466e314f958Sdsl } 146723eb2db1Sthorpej cmdenviron = NULL; 146861f28255Scgd if (e != EXSHELLPROC) { 146961f28255Scgd commandname = savecmdname; 1470ee9e50eaSmycroft if (flags & EV_EXIT) 147161f28255Scgd exitshell(exitstatus); 147261f28255Scgd } 147361f28255Scgd if (e != -1) { 1474886c875aSchristos if ((e != EXERROR && e != EXEXEC) 1475e314f958Sdsl || cmdentry.cmdtype == CMDSPLBLTIN) 147661f28255Scgd exraise(e); 1477a582e232Skre popfilesupto(savetopfile); 147861f28255Scgd FORCEINTON; 147961f28255Scgd } 1480*1c3b0983Skre 1481c02b3bbdSchristos if (cmdentry.u.bltin != execcmd) 1482*1c3b0983Skre popredir(POPREDIR_UNDO); 1483*1c3b0983Skre 148461f28255Scgd if (flags == EV_BACKCMD) { 148561f28255Scgd backcmd->buf = memout.buf; 148661f28255Scgd backcmd->nleft = memout.nextc - memout.buf; 148761f28255Scgd memout.buf = NULL; 148861f28255Scgd } 1489e314f958Sdsl break; 1490e314f958Sdsl 1491e314f958Sdsl default: 1492f07d3f9bSkre VXTRACE(DBG_EVAL, ("normal command%s: ", vforked?" VF":""), 1493f07d3f9bSkre trargs(argv)); 14945047abd1Schristos redirect(cmd->ncmd.redirect, 14955047abd1Schristos (vforked ? REDIR_VFORK : 0) | REDIR_KEEP); 1496edcb4544Schristos if (!vforked) 149761f28255Scgd for (sp = varlist.list ; sp ; sp = sp->next) 1498522d1631Skre setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); 149961f28255Scgd envp = environment(); 1500e314f958Sdsl shellexec(argv, envp, path, cmdentry.u.index, vforked); 1501e314f958Sdsl break; 150261f28255Scgd } 150361f28255Scgd goto out; 150461f28255Scgd 150561f28255Scgd parent: /* parent process gets here (if we forked) */ 150620013177Skre 15078b2383fcSchristos exitstatus = 0; /* if not altered just below */ 1508c02b3bbdSchristos if (mode == FORK_FG) { /* argument to fork */ 150961f28255Scgd exitstatus = waitforjob(jp); 1510c02b3bbdSchristos } else if (mode == FORK_NOJOB) { 151161f28255Scgd backcmd->fd = pip[0]; 151261f28255Scgd close(pip[1]); 151361f28255Scgd backcmd->jp = jp; 1514aec4b6e2Skre } else 1515aec4b6e2Skre jobstarted(jp); 1516aec4b6e2Skre 15176166cc6eSchristos FORCEINTON; 151861f28255Scgd 151961f28255Scgd out: 152061f28255Scgd if (lastarg) 15214d69dafdSkre /* implement $_ for whatever use that really is */ 1522ad0b72d9Skre (void) setvarsafe("_", lastarg, VNOERROR); 152361f28255Scgd popstackmark(&smark); 152461f28255Scgd } 152561f28255Scgd 152661f28255Scgd 152761f28255Scgd /* 152861f28255Scgd * Search for a command. This is called before we fork so that the 152961f28255Scgd * location of the command will be available in the parent as well as 153061f28255Scgd * the child. The check for "goodname" is an overly conservative 153161f28255Scgd * check that the name will not be subject to expansion. 153261f28255Scgd */ 153361f28255Scgd 153461f28255Scgd STATIC void 1535c02b3bbdSchristos prehash(union node *n) 153661f28255Scgd { 153761f28255Scgd struct cmdentry entry; 153861f28255Scgd 1539da09d1a8Schristos if (n && n->type == NCMD && n->ncmd.args) 154008a67300Smycroft if (goodname(n->ncmd.args->narg.text)) 1541a45947b2Schristos find_command(n->ncmd.args->narg.text, &entry, 0, 1542a45947b2Schristos pathval()); 154361f28255Scgd } 154461f28255Scgd 1545404b1d02Skre int 154601f35fccSchristos in_function(void) 154701f35fccSchristos { 154801f35fccSchristos return funcnest; 154901f35fccSchristos } 155061f28255Scgd 1551404b1d02Skre enum skipstate 155201f35fccSchristos current_skipstate(void) 155301f35fccSchristos { 155401f35fccSchristos return evalskip; 155501f35fccSchristos } 155601f35fccSchristos 1557404b1d02Skre void 155813fc5c1bSkre save_skipstate(struct skipsave *p) 155913fc5c1bSkre { 156013fc5c1bSkre *p = s_k_i_p; 156113fc5c1bSkre } 156213fc5c1bSkre 156313fc5c1bSkre void 156413fc5c1bSkre restore_skipstate(const struct skipsave *p) 156513fc5c1bSkre { 156613fc5c1bSkre s_k_i_p = *p; 156713fc5c1bSkre } 156813fc5c1bSkre 156913fc5c1bSkre void 157001f35fccSchristos stop_skipping(void) 157101f35fccSchristos { 157201f35fccSchristos evalskip = SKIPNONE; 157301f35fccSchristos skipcount = 0; 157401f35fccSchristos } 157561f28255Scgd 157661f28255Scgd /* 157761f28255Scgd * Builtin commands. Builtin commands whose functions are closely 157861f28255Scgd * tied to evaluation are implemented here. 157961f28255Scgd */ 158061f28255Scgd 158161f28255Scgd /* 1582e314f958Sdsl * No command given. 158361f28255Scgd */ 158461f28255Scgd 15855dad1439Scgd int 1586c02b3bbdSchristos bltincmd(int argc, char **argv) 15875dad1439Scgd { 15882e197048Schristos /* 15892e197048Schristos * Preserve exitstatus of a previous possible redirection 15902e197048Schristos * as POSIX mandates 15912e197048Schristos */ 1592c02b3bbdSchristos return back_exitstatus; 159361f28255Scgd } 159461f28255Scgd 159561f28255Scgd 159661f28255Scgd /* 159761f28255Scgd * Handle break and continue commands. Break, continue, and return are 159861f28255Scgd * all handled by setting the evalskip flag. The evaluation routines 159961f28255Scgd * above all check this flag, and if it is set they start skipping 160061f28255Scgd * commands rather than executing them. The variable skipcount is 160161f28255Scgd * the number of loops to break/continue, or the number of function 160261f28255Scgd * levels to return. (The latter is always 1.) It should probably 160361f28255Scgd * be an error to break out of more loops than exist, but it isn't 160461f28255Scgd * in the standard shell so we don't make it one here. 160561f28255Scgd */ 160661f28255Scgd 16075dad1439Scgd int 1608c02b3bbdSchristos breakcmd(int argc, char **argv) 16095dad1439Scgd { 161066824391Schristos int n = argc > 1 ? number(argv[1]) : 1; 161161f28255Scgd 1612e0f1e164Skre if (n <= 0) 1613e0f1e164Skre error("invalid count: %d", n); 161461f28255Scgd if (n > loopnest) 161561f28255Scgd n = loopnest; 161661f28255Scgd if (n > 0) { 161761f28255Scgd evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 161861f28255Scgd skipcount = n; 161961f28255Scgd } 162061f28255Scgd return 0; 162161f28255Scgd } 162261f28255Scgd 162301f35fccSchristos int 162401f35fccSchristos dotcmd(int argc, char **argv) 162501f35fccSchristos { 162601f35fccSchristos exitstatus = 0; 162701f35fccSchristos 1628ebc4cf1cSkre (void) nextopt(NULL); /* ignore a leading "--" */ 1629ebc4cf1cSkre 1630ebc4cf1cSkre if (*argptr != NULL) { /* That's what SVR2 does */ 163101f35fccSchristos char *fullname; 163201f35fccSchristos /* 163301f35fccSchristos * dot_funcnest needs to be 0 when not in a dotcmd, so it 163401f35fccSchristos * cannot be restored with (funcnest + 1). 163501f35fccSchristos */ 163601f35fccSchristos int dot_funcnest_old; 163701f35fccSchristos struct stackmark smark; 163801f35fccSchristos 163901f35fccSchristos setstackmark(&smark); 1640ebc4cf1cSkre fullname = find_dot_file(*argptr); 164101f35fccSchristos setinputfile(fullname, 1); 164201f35fccSchristos commandname = fullname; 164301f35fccSchristos dot_funcnest_old = dot_funcnest; 164401f35fccSchristos dot_funcnest = funcnest + 1; 164501f35fccSchristos cmdloop(0); 164601f35fccSchristos dot_funcnest = dot_funcnest_old; 164701f35fccSchristos popfile(); 164801f35fccSchristos popstackmark(&smark); 164901f35fccSchristos } 165001f35fccSchristos return exitstatus; 165101f35fccSchristos } 165201f35fccSchristos 165301f35fccSchristos /* 16544084f829Skre * allow dotfile function nesting to be manipulated 16554084f829Skre * (for read_profile). This allows profile files to 16564084f829Skre * be treated as if they were used as '.' commands, 16574084f829Skre * (approximately) and in particular, for "return" to work. 16584084f829Skre */ 16594084f829Skre int 16604084f829Skre set_dot_funcnest(int new) 16614084f829Skre { 16624084f829Skre int rv = dot_funcnest; 16634084f829Skre 16644084f829Skre if (new >= 0) 16654084f829Skre dot_funcnest = new; 16664084f829Skre 16674084f829Skre return rv; 16684084f829Skre } 16694084f829Skre 16704084f829Skre /* 167101f35fccSchristos * Take commands from a file. To be compatible we should do a path 167201f35fccSchristos * search for the file, which is necessary to find sub-commands. 167301f35fccSchristos */ 167401f35fccSchristos 167501f35fccSchristos STATIC char * 167601f35fccSchristos find_dot_file(char *basename) 167701f35fccSchristos { 167801f35fccSchristos char *fullname; 167901f35fccSchristos const char *path = pathval(); 168001f35fccSchristos struct stat statb; 168101f35fccSchristos 168201f35fccSchristos /* don't try this for absolute or relative paths */ 16831d680406Skre if (strchr(basename, '/')) { 16841d680406Skre if (stat(basename, &statb) == 0) { 1685cdb38c6bSkre if (S_ISDIR(statb.st_mode)) 1686cdb38c6bSkre error("%s: is a directory", basename); 1687cdb38c6bSkre if (S_ISBLK(statb.st_mode)) 1688cdb38c6bSkre error("%s: is a block device", basename); 168901f35fccSchristos return basename; 16901d680406Skre } 16911676135eSkre } else while ((fullname = padvance(&path, basename, 1)) != NULL) { 1692cdb38c6bSkre if ((stat(fullname, &statb) == 0)) { 1693cdb38c6bSkre /* weird format is to ease future code... */ 1694cdb38c6bSkre if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode)) 1695cdb38c6bSkre ; 1696cdb38c6bSkre #if notyet 1697cdb38c6bSkre else if (unreadable()) { 169801f35fccSchristos /* 1699cdb38c6bSkre * testing this via st_mode is ugly to get 1700cdb38c6bSkre * correct (and would ignore ACLs). 1701cdb38c6bSkre * better way is just to open the file. 1702cdb38c6bSkre * But doing that here would (currently) 1703cdb38c6bSkre * mean opening the file twice, which 1704cdb38c6bSkre * might not be safe. So, defer this 1705cdb38c6bSkre * test until code is restructures so 1706cdb38c6bSkre * we can return a fd. Then we also 1707cdb38c6bSkre * get to fix the mem leak just below... 1708cdb38c6bSkre */ 1709cdb38c6bSkre } 1710cdb38c6bSkre #endif 1711cdb38c6bSkre else { 1712cdb38c6bSkre /* 1713cdb38c6bSkre * Don't bother freeing here, since 1714cdb38c6bSkre * it will be freed by the caller. 1715cdb38c6bSkre * XXX no it won't - a bug for later. 171601f35fccSchristos */ 171701f35fccSchristos return fullname; 171801f35fccSchristos } 1719cdb38c6bSkre } 172001f35fccSchristos stunalloc(fullname); 172101f35fccSchristos } 172201f35fccSchristos 172301f35fccSchristos /* not found in the PATH */ 172401f35fccSchristos error("%s: not found", basename); 172501f35fccSchristos /* NOTREACHED */ 172601f35fccSchristos } 172701f35fccSchristos 172801f35fccSchristos 172961f28255Scgd 173061f28255Scgd /* 173161f28255Scgd * The return command. 173201f35fccSchristos * 173301f35fccSchristos * Quoth the POSIX standard: 173401f35fccSchristos * The return utility shall cause the shell to stop executing the current 173501f35fccSchristos * function or dot script. If the shell is not currently executing 173601f35fccSchristos * a function or dot script, the results are unspecified. 173701f35fccSchristos * 173801f35fccSchristos * As for the unspecified part, there seems to be no de-facto standard: bash 173901f35fccSchristos * ignores the return with a warning, zsh ignores the return in interactive 174001f35fccSchristos * mode but seems to liken it to exit in a script. (checked May 2014) 174101f35fccSchristos * 174201f35fccSchristos * We choose to silently ignore the return. Older versions of this shell 174301f35fccSchristos * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This 174401f35fccSchristos * had at least the problem of circumventing the check for stopped jobs, 174501f35fccSchristos * which would occur for exit or ^D. 174661f28255Scgd */ 174761f28255Scgd 17485dad1439Scgd int 1749c02b3bbdSchristos returncmd(int argc, char **argv) 17505dad1439Scgd { 1751c02b3bbdSchristos int ret = argc > 1 ? number(argv[1]) : exitstatus; 175261f28255Scgd 175301f35fccSchristos if ((dot_funcnest == 0 && funcnest) 175401f35fccSchristos || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) { 175561f28255Scgd evalskip = SKIPFUNC; 175661f28255Scgd skipcount = 1; 175701f35fccSchristos } else if (dot_funcnest > 0) { 1758f9382bcaSchristos evalskip = SKIPFILE; 1759f9382bcaSchristos skipcount = 1; 176001f35fccSchristos } else { 176101f35fccSchristos /* XXX: should a warning be issued? */ 176201f35fccSchristos ret = 0; 1763f9382bcaSchristos } 176401f35fccSchristos 176501f35fccSchristos return ret; 1766f9382bcaSchristos } 176761f28255Scgd 176861f28255Scgd 17695dad1439Scgd int 1770c02b3bbdSchristos falsecmd(int argc, char **argv) 17715dad1439Scgd { 1772542ace3bSjtc return 1; 1773542ace3bSjtc } 1774542ace3bSjtc 1775542ace3bSjtc 17765dad1439Scgd int 1777c02b3bbdSchristos truecmd(int argc, char **argv) 17785dad1439Scgd { 177961f28255Scgd return 0; 178061f28255Scgd } 178161f28255Scgd 178261f28255Scgd 17835dad1439Scgd int 1784c02b3bbdSchristos execcmd(int argc, char **argv) 17855dad1439Scgd { 1786*1c3b0983Skre /* 1787*1c3b0983Skre * BEWARE: if any options are added here, they must 1788*1c3b0983Skre * also be added in evalcommand(), look for "DUMMY_EXAMPLE_CODE" 1789*1c3b0983Skre * for example code for there. Here the options would be 1790*1c3b0983Skre * processed completely normally. 1791*1c3b0983Skre */ 1792*1c3b0983Skre (void) nextopt(""); /* ignore a leading "--" */ 1793ebc4cf1cSkre 1794ebc4cf1cSkre if (*argptr) { 1795df504509Schristos struct strlist *sp; 1796df504509Schristos 179761f28255Scgd iflag = 0; /* exit on error */ 179837ed7877Sjtc mflag = 0; 179937ed7877Sjtc optschanged(); 1800df504509Schristos for (sp = cmdenviron; sp; sp = sp->next) 1801522d1631Skre setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); 1802ebc4cf1cSkre shellexec(argptr, environment(), pathval(), 0, 0); 1803*1c3b0983Skre /* NOTREACHED */ 180461f28255Scgd } 1805*1c3b0983Skre popredir(POPREDIR_PERMANENT); /* make any redirections permanent */ 180661f28255Scgd return 0; 180761f28255Scgd } 1808c02b3bbdSchristos 1809c02b3bbdSchristos static int 1810a69cd45bSitojun conv_time(clock_t ticks, char *seconds, size_t l) 1811c02b3bbdSchristos { 1812c02b3bbdSchristos static clock_t tpm = 0; 1813c02b3bbdSchristos clock_t mins; 1814c02b3bbdSchristos int i; 1815c02b3bbdSchristos 1816c02b3bbdSchristos if (!tpm) 1817c02b3bbdSchristos tpm = sysconf(_SC_CLK_TCK) * 60; 1818c02b3bbdSchristos 1819c02b3bbdSchristos mins = ticks / tpm; 1820a69cd45bSitojun snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); 1821c02b3bbdSchristos 1822c02b3bbdSchristos if (seconds[0] == '6' && seconds[1] == '0') { 1823c02b3bbdSchristos /* 59.99995 got rounded up... */ 1824c02b3bbdSchristos mins++; 1825a69cd45bSitojun strlcpy(seconds, "0.0", l); 1826c02b3bbdSchristos return mins; 1827c02b3bbdSchristos } 1828c02b3bbdSchristos 1829c02b3bbdSchristos /* suppress trailing zeros */ 1830c02b3bbdSchristos i = strlen(seconds) - 1; 1831c02b3bbdSchristos for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) 1832c02b3bbdSchristos seconds[i] = 0; 1833c02b3bbdSchristos return mins; 1834c02b3bbdSchristos } 1835c02b3bbdSchristos 1836c02b3bbdSchristos int 1837c02b3bbdSchristos timescmd(int argc, char **argv) 1838c02b3bbdSchristos { 1839c02b3bbdSchristos struct tms tms; 1840c02b3bbdSchristos int u, s, cu, cs; 1841c02b3bbdSchristos char us[8], ss[8], cus[8], css[8]; 1842c02b3bbdSchristos 1843c02b3bbdSchristos nextopt(""); 1844c02b3bbdSchristos 1845c02b3bbdSchristos times(&tms); 1846c02b3bbdSchristos 1847a69cd45bSitojun u = conv_time(tms.tms_utime, us, sizeof(us)); 1848a69cd45bSitojun s = conv_time(tms.tms_stime, ss, sizeof(ss)); 1849a69cd45bSitojun cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); 1850a69cd45bSitojun cs = conv_time(tms.tms_cstime, css, sizeof(css)); 1851c02b3bbdSchristos 1852c02b3bbdSchristos outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", 1853c02b3bbdSchristos u, us, s, ss, cu, cus, cs, css); 1854c02b3bbdSchristos 1855c02b3bbdSchristos return 0; 1856c02b3bbdSchristos } 1857