From 09cff699a514e452eccc6420f1213967b767cb3e Mon Sep 17 00:00:00 2001 From: Kiyoshi Aman Date: Fri, 15 Mar 2019 13:08:45 -0500 Subject: rm bin/sh usr.bin/make: utilities provided by other packages --- bin/sh/eval.c | 1680 --------------------------------------------------------- 1 file changed, 1680 deletions(-) delete mode 100644 bin/sh/eval.c (limited to 'bin/sh/eval.c') diff --git a/bin/sh/eval.c b/bin/sh/eval.c deleted file mode 100644 index 2bda702..0000000 --- a/bin/sh/eval.c +++ /dev/null @@ -1,1680 +0,0 @@ -/* $NetBSD: eval.c,v 1.170 2019/01/21 14:18:59 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; -#else -__RCSID("$NetBSD: eval.c,v 1.170 2019/01/21 14:18:59 kre Exp $"); -#endif -#endif /* not lint */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Evaluate a command. - */ - -#include "shell.h" -#include "nodes.h" -#include "syntax.h" -#include "expand.h" -#include "parser.h" -#include "jobs.h" -#include "eval.h" -#include "builtins.h" -#include "options.h" -#include "exec.h" -#include "redir.h" -#include "input.h" -#include "output.h" -#include "trap.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "show.h" -#include "mystring.h" -#include "main.h" -#ifndef SMALL -#include "nodenames.h" -#include "myhistedit.h" -#endif - - -STATIC struct skipsave s_k_i_p; -#define evalskip (s_k_i_p.state) -#define skipcount (s_k_i_p.count) - -STATIC int loopnest; /* current loop nesting level */ -STATIC int funcnest; /* depth of function calls */ -STATIC int builtin_flags; /* evalcommand flags for builtins */ -/* - * Base function nesting level inside a dot command. Set to 0 initially - * and to (funcnest + 1) before every dot command to enable - * 1) detection of being in a file sourced by a dot command and - * 2) counting of function nesting in that file for the implementation - * of the return command. - * The value is reset to its previous value after the dot command. - */ -STATIC int dot_funcnest; - - -const char *commandname; -struct strlist *cmdenviron; -int exitstatus; /* exit status of last command */ -int back_exitstatus; /* exit status of backquoted command */ - - -STATIC void evalloop(union node *, int); -STATIC void evalfor(union node *, int); -STATIC void evalcase(union node *, int); -STATIC void evalsubshell(union node *, int); -STATIC void expredir(union node *); -STATIC void evalredir(union node *, int); -STATIC void evalpipe(union node *); -STATIC void evalcommand(union node *, int, struct backcmd *); -STATIC void prehash(union node *); - -STATIC char *find_dot_file(char *); - -/* - * Called to reset things after an exception. - */ - -#ifdef mkinit -INCLUDE "eval.h" - -RESET { - reset_eval(); -} - -SHELLPROC { - exitstatus = 0; -} -#endif - -void -reset_eval(void) -{ - evalskip = SKIPNONE; - dot_funcnest = 0; - loopnest = 0; - funcnest = 0; -} - -static int -sh_pipe(int fds[2]) -{ - int nfd; - - if (pipe(fds)) - return -1; - - if (fds[0] < 3) { - nfd = fcntl(fds[0], F_DUPFD, 3); - if (nfd != -1) { - close(fds[0]); - fds[0] = nfd; - } - } - - if (fds[1] < 3) { - nfd = fcntl(fds[1], F_DUPFD, 3); - if (nfd != -1) { - close(fds[1]); - fds[1] = nfd; - } - } - return 0; -} - - -/* - * The eval commmand. - */ - -int -evalcmd(int argc, char **argv) -{ - char *p; - char *concat; - char **ap; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - evalstring(p, builtin_flags & EV_TESTED); - } else - exitstatus = 0; - return exitstatus; -} - - -/* - * Execute a command or commands contained in a string. - */ - -void -evalstring(char *s, int flag) -{ - union node *n; - struct stackmark smark; - int last; - int any; - - last = flag & EV_EXIT; - flag &= ~EV_EXIT; - - setstackmark(&smark); - setinputstring(s, 1, line_number); - - any = 0; /* to determine if exitstatus will have been set */ - while ((n = parsecmd(0)) != NEOF) { - XTRACE(DBG_EVAL, ("evalstring: "), showtree(n)); - if (n && nflag == 0) { - if (last && at_eof()) - evaltree(n, flag | EV_EXIT); - else - evaltree(n, flag); - any = 1; - if (evalskip) - break; - } - rststackmark(&smark); - } - popfile(); - popstackmark(&smark); - if (!any) - exitstatus = 0; - if (last) - exraise(EXEXIT); -} - - - -/* - * Evaluate a parse tree. The value is left in the global variable - * exitstatus. - */ - -void -evaltree(union node *n, int flags) -{ - bool do_etest; - int sflags = flags & ~EV_EXIT; - union node *next; - struct stackmark smark; - - do_etest = false; - if (n == NULL || nflag) { - VTRACE(DBG_EVAL, ("evaltree(%s) called\n", - n == NULL ? "NULL" : "-n")); - if (nflag == 0) - exitstatus = 0; - goto out2; - } - - setstackmark(&smark); - do { -#ifndef SMALL - displayhist = 1; /* show history substitutions done with fc */ -#endif - next = NULL; - CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n", - getpid(), n, NODETYPENAME(n->type), n->type, flags)); - if (n->type != NCMD && traps_invalid) - free_traps(); - switch (n->type) { - case NSEMI: - evaltree(n->nbinary.ch1, sflags); - if (nflag || evalskip) - goto out1; - next = n->nbinary.ch2; - break; - case NAND: - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag || evalskip || exitstatus != 0) - goto out1; - next = n->nbinary.ch2; - break; - case NOR: - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag || evalskip || exitstatus == 0) - goto out1; - next = n->nbinary.ch2; - break; - case NREDIR: - evalredir(n, flags); - break; - case NSUBSHELL: - evalsubshell(n, flags); - do_etest = !(flags & EV_TESTED); - break; - case NBACKGND: - evalsubshell(n, flags); - break; - case NIF: { - evaltree(n->nif.test, EV_TESTED); - if (nflag || evalskip) - goto out1; - if (exitstatus == 0) - next = n->nif.ifpart; - else if (n->nif.elsepart) - next = n->nif.elsepart; - else - exitstatus = 0; - break; - } - case NWHILE: - case NUNTIL: - evalloop(n, sflags); - break; - case NFOR: - evalfor(n, sflags); - break; - case NCASE: - evalcase(n, sflags); - break; - case NDEFUN: - CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n", - n->narg.text, n->narg.lineno, - fnline1 ? " LINENO=1" : "")); - defun(n->narg.text, n->narg.next, n->narg.lineno); - exitstatus = 0; - break; - case NNOT: - evaltree(n->nnot.com, EV_TESTED); - exitstatus = !exitstatus; - break; - case NDNOT: - evaltree(n->nnot.com, EV_TESTED); - if (exitstatus != 0) - exitstatus = 1; - break; - case NPIPE: - evalpipe(n); - do_etest = !(flags & EV_TESTED); - break; - case NCMD: - evalcommand(n, flags, NULL); - do_etest = !(flags & EV_TESTED); - break; - default: -#ifdef NODETYPENAME - out1fmt("Node type = %d(%s)\n", - n->type, NODETYPENAME(n->type)); -#else - out1fmt("Node type = %d\n", n->type); -#endif - flushout(&output); - break; - } - n = next; - rststackmark(&smark); - } while(n != NULL); - out1: - popstackmark(&smark); - out2: - if (pendingsigs) - dotrap(); - if (eflag && exitstatus != 0 && do_etest) - exitshell(exitstatus); - if (flags & EV_EXIT) - exraise(EXEXIT); -} - - -STATIC void -evalloop(union node *n, int flags) -{ - int status; - - loopnest++; - status = 0; - - CTRACE(DBG_EVAL, ("evalloop %s:", NODETYPENAME(n->type))); - VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1)); - VXTRACE(DBG_EVAL, ("evalloop do: "), showtree(n->nbinary.ch2)); - VTRACE(DBG_EVAL, ("evalloop done\n")); - CTRACE(DBG_EVAL, ("\n")); - - for (;;) { - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag) - break; - if (evalskip) { - skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = SKIPNONE; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = SKIPNONE; - break; - } - if (n->type == NWHILE) { - if (exitstatus != 0) - break; - } else { - if (exitstatus == 0) - break; - } - evaltree(n->nbinary.ch2, flags & EV_TESTED); - status = exitstatus; - if (evalskip) - goto skipping; - } - loopnest--; - exitstatus = status; -} - - - -STATIC void -evalfor(union node *n, int flags) -{ - struct arglist arglist; - union node *argp; - struct strlist *sp; - struct stackmark smark; - int status; - - status = nflag ? exitstatus : 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - if (evalskip) - goto out; - } - *arglist.lastp = NULL; - - loopnest++; - for (sp = arglist.list ; sp ; sp = sp->next) { - if (xflag) { - outxstr(expandstr(ps4val(), line_number)); - outxstr("for "); - outxstr(n->nfor.var); - outxc('='); - outxshstr(sp->text); - outxc('\n'); - flushout(outx); - } - - setvar(n->nfor.var, sp->text, 0); - evaltree(n->nfor.body, flags & EV_TESTED); - status = exitstatus; - if (nflag) - break; - if (evalskip) { - if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = SKIPNONE; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = SKIPNONE; - break; - } - } - loopnest--; - exitstatus = status; - out: - popstackmark(&smark); -} - - - -STATIC void -evalcase(union node *n, int flags) -{ - union node *cp, *ncp; - union node *patp; - struct arglist arglist; - struct stackmark smark; - int status = 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - line_number = n->ncase.lineno; - expandarg(n->ncase.expr, &arglist, EXP_TILDE); - for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { - for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { - line_number = patp->narg.lineno; - if (casematch(patp, arglist.list->text)) { - while (cp != NULL && evalskip == 0 && - nflag == 0) { - if (cp->type == NCLISTCONT) - ncp = cp->nclist.next; - else - ncp = NULL; - line_number = cp->nclist.lineno; - evaltree(cp->nclist.body, flags); - status = exitstatus; - cp = ncp; - } - goto out; - } - } - } - out: - exitstatus = status; - popstackmark(&smark); -} - - - -/* - * Kick off a subshell to evaluate a tree. - */ - -STATIC void -evalsubshell(union node *n, int flags) -{ - struct job *jp= NULL; - int backgnd = (n->type == NBACKGND); - - expredir(n->nredir.redirect); - if (xflag && n->nredir.redirect) { - union node *rn; - - outxstr(expandstr(ps4val(), line_number)); - outxstr("using redirections:"); - for (rn = n->nredir.redirect; rn; rn = rn->nfile.next) - (void) outredir(outx, rn, ' '); - outxstr(" do subshell ("/*)*/); - if (backgnd) - outxstr(/*(*/") &"); - outxc('\n'); - flushout(outx); - } - if ((!backgnd && flags & EV_EXIT && !have_traps()) || - forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) { - INTON; - if (backgnd) - flags &=~ EV_TESTED; - redirect(n->nredir.redirect, REDIR_KEEP); - evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ - } else if (!backgnd) { - INTOFF; - exitstatus = waitforjob(jp); - INTON; - } else - exitstatus = 0; - - if (!backgnd && xflag && n->nredir.redirect) { - outxstr(expandstr(ps4val(), line_number)); - outxstr(/*(*/") done subshell\n"); - flushout(outx); - } -} - - - -/* - * Compute the names of the files in a redirection list. - */ - -STATIC void -expredir(union node *n) -{ - union node *redir; - - for (redir = n ; redir ; redir = redir->nfile.next) { - struct arglist fn; - - fn.lastp = &fn.list; - switch (redir->type) { - case NFROMTO: - case NFROM: - case NTO: - case NCLOBBER: - case NAPPEND: - expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); - redir->nfile.expfname = fn.list->text; - break; - case NFROMFD: - case NTOFD: - if (redir->ndup.vname) { - expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); - fixredir(redir, fn.list->text, 1); - } - break; - } - } -} - -/* - * Perform redirections for a compound command, and then do it (and restore) - */ -STATIC void -evalredir(union node *n, int flags) -{ - struct jmploc jmploc; - struct jmploc * const savehandler = handler; - volatile int in_redirect = 1; - const char * volatile PS4 = NULL; - - expredir(n->nredir.redirect); - - if (xflag && n->nredir.redirect) { - union node *rn; - - outxstr(PS4 = expandstr(ps4val(), line_number)); - outxstr("using redirections:"); - for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next) - (void) outredir(outx, rn, ' '); - outxstr(" do {\n"); /* } */ - flushout(outx); - } - - if (setjmp(jmploc.loc)) { - int e; - - handler = savehandler; - e = exception; - popredir(); - if (PS4 != NULL) { - outxstr(PS4); - /* { */ outxstr("} failed\n"); - flushout(outx); - } - if (e == EXERROR || e == EXEXEC) { - if (in_redirect) { - exitstatus = 2; - return; - } - } - longjmp(handler->loc, 1); - } else { - INTOFF; - handler = &jmploc; - redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP); - in_redirect = 0; - INTON; - evaltree(n->nredir.n, flags); - } - INTOFF; - handler = savehandler; - popredir(); - INTON; - - if (PS4 != NULL) { - outxstr(PS4); - /* { */ outxstr("} done\n"); - flushout(outx); - } -} - - -/* - * Evaluate a pipeline. All the processes in the pipeline are children - * of the process creating the pipeline. (This differs from some versions - * of the shell, which make the last process in a pipeline the parent - * of all the rest.) - */ - -STATIC void -evalpipe(union node *n) -{ - struct job *jp; - struct nodelist *lp; - int pipelen; - int prevfd; - int pip[2]; - - CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n)); - pipelen = 0; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) - pipelen++; - INTOFF; - jp = makejob(n, pipelen); - prevfd = -1; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - prehash(lp->n); - pip[1] = -1; - if (lp->next) { - if (sh_pipe(pip) < 0) { - if (prevfd >= 0) - close(prevfd); - error("Pipe call failed: %s", strerror(errno)); - } - } - if (forkshell(jp, lp->n, - n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { - INTON; - if (prevfd > 0) - movefd(prevfd, 0); - if (pip[1] >= 0) { - close(pip[0]); - movefd(pip[1], 1); - } - evaltree(lp->n, EV_EXIT); - } - if (prevfd >= 0) - close(prevfd); - prevfd = pip[0]; - close(pip[1]); - } - if (n->npipe.backgnd == 0) { - INTOFF; - exitstatus = waitforjob(jp); - CTRACE(DBG_EVAL, ("evalpipe: job done exit status %d\n", - exitstatus)); - INTON; - } else - exitstatus = 0; - INTON; -} - - - -/* - * Execute a command inside back quotes. If it's a builtin command, we - * want to save its output in a block obtained from malloc. Otherwise - * we fork off a subprocess and get the output of the command via a pipe. - * Should be called with interrupts off. - */ - -void -evalbackcmd(union node *n, struct backcmd *result) -{ - int pip[2]; - struct job *jp; - struct stackmark smark; /* unnecessary (because we fork) */ - - result->fd = -1; - result->buf = NULL; - result->nleft = 0; - result->jp = NULL; - - if (nflag || n == NULL) - goto out; - - setstackmark(&smark); - -#ifdef notyet - /* - * For now we disable executing builtins in the same - * context as the shell, because we are not keeping - * enough state to recover from changes that are - * supposed only to affect subshells. eg. echo "`cd /`" - */ - if (n->type == NCMD) { - exitstatus = oexitstatus; /* XXX o... no longer exists */ - evalcommand(n, EV_BACKCMD, result); - } else -#endif - { - INTOFF; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob(n, 1); - if (forkshell(jp, n, FORK_NOJOB) == 0) { - FORCEINTON; - close(pip[0]); - movefd(pip[1], 1); - eflag = 0; - evaltree(n, EV_EXIT); - /* NOTREACHED */ - } - close(pip[1]); - result->fd = pip[0]; - result->jp = jp; - INTON; - } - popstackmark(&smark); - out: - CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", - result->fd, result->buf, result->nleft, result->jp)); -} - -const char * -syspath(void) -{ - static char *sys_path = NULL; - static int mib[] = {CTL_USER, USER_CS_PATH}; - static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; - size_t len; - - if (sys_path == NULL) { - if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && - (sys_path = ckmalloc(len + 5)) != NULL && - sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { - memcpy(sys_path, "PATH=", 5); - } else { - ckfree(sys_path); - /* something to keep things happy */ - sys_path = def_path; - } - } - return sys_path; -} - -static int -parse_command_args(int argc, char **argv, int *use_syspath) -{ - int sv_argc = argc; - char *cp, c; - - *use_syspath = 0; - - for (;;) { - argv++; - if (--argc == 0) - break; - cp = *argv; - if (*cp++ != '-') - break; - if (*cp == '-' && cp[1] == 0) { - argv++; - argc--; - break; - } - while ((c = *cp++)) { - switch (c) { - case 'p': - *use_syspath = 1; - break; - default: - /* run 'typecmd' for other options */ - return 0; - } - } - } - return sv_argc - argc; -} - -int vforked = 0; -extern char *trap[]; - -/* - * Execute a simple command. - */ - -STATIC void -evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) -{ - struct stackmark smark; - union node *argp; - struct arglist arglist; - struct arglist varlist; - volatile int flags = flgs; - char ** volatile argv; - volatile int argc; - char **envp; - int varflag; - struct strlist *sp; - volatile int mode; - int pip[2]; - struct cmdentry cmdentry; - struct job * volatile jp; - struct jmploc jmploc; - struct jmploc *volatile savehandler = NULL; - const char *volatile savecmdname; - volatile struct shparam saveparam; - struct localvar *volatile savelocalvars; - struct parsefile *volatile savetopfile; - volatile int e; - char * volatile lastarg; - const char * volatile path = pathval(); - volatile int temp_path; - const int savefuncline = funclinebase; - const int savefuncabs = funclineabs; - volatile int cmd_flags = 0; - - vforked = 0; - /* First expand the arguments. */ - CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags, - cmd->ncmd.args ? cmd->ncmd.args->narg.text : "")); - setstackmark(&smark); - back_exitstatus = 0; - - line_number = cmd->ncmd.lineno; - - arglist.lastp = &arglist.list; - varflag = 1; - /* Expand arguments, ignoring the initial 'name=value' ones */ - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - if (varflag && isassignment(argp->narg.text)) - continue; - varflag = 0; - line_number = argp->narg.lineno; - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - } - *arglist.lastp = NULL; - - expredir(cmd->ncmd.redirect); - - /* Now do the initial 'name=value' ones we skipped above */ - varlist.lastp = &varlist.list; - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - line_number = argp->narg.lineno; - if (!isassignment(argp->narg.text)) - break; - expandarg(argp, &varlist, EXP_VARTILDE); - } - *varlist.lastp = NULL; - - argc = 0; - for (sp = arglist.list ; sp ; sp = sp->next) - argc++; - argv = stalloc(sizeof (char *) * (argc + 1)); - - for (sp = arglist.list ; sp ; sp = sp->next) { - VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text)); - *argv++ = sp->text; - } - *argv = NULL; - lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) - lastarg = argv[-1]; - argv -= argc; - - /* Print the command if xflag is set. */ - if (xflag) { - char sep = 0; - union node *rn; - - outxstr(expandstr(ps4val(), line_number)); - for (sp = varlist.list ; sp ; sp = sp->next) { - char *p; - - if (sep != 0) - outxc(sep); - - /* - * The "var=" part should not be quoted, regardless - * of the value, or it would not represent an - * assignment, but rather a command - */ - p = strchr(sp->text, '='); - if (p != NULL) { - *p = '\0'; /*XXX*/ - outxshstr(sp->text); - outxc('='); - *p++ = '='; /*XXX*/ - } else - p = sp->text; - outxshstr(p); - sep = ' '; - } - for (sp = arglist.list ; sp ; sp = sp->next) { - if (sep != 0) - outxc(sep); - outxshstr(sp->text); - sep = ' '; - } - for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next) - if (outredir(outx, rn, sep)) - sep = ' '; - outxc('\n'); - flushout(outx); - } - - /* Now locate the command. */ - if (argc == 0) { - /* - * the empty command begins as a normal builtin, and - * remains that way while redirects are processed, then - * will become special before we get to doing the - * var assigns. - */ - cmdentry.cmdtype = CMDBUILTIN; - cmdentry.u.bltin = bltincmd; - } else { - static const char PATH[] = "PATH="; - - /* - * Modify the command lookup path, if a PATH= assignment - * is present - */ - for (sp = varlist.list; sp; sp = sp->next) - if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) - path = sp->text + sizeof(PATH) - 1; - - do { - int argsused, use_syspath; - - find_command(argv[0], &cmdentry, cmd_flags, path); -#if 0 - /* - * This short circuits all of the processing that - * should be done (including processing the - * redirects), so just don't ... - * - * (eventually this whole #if'd block will vanish) - */ - if (cmdentry.cmdtype == CMDUNKNOWN) { - exitstatus = 127; - flushout(&errout); - goto out; - } -#endif - - /* implement the 'command' builtin here */ - if (cmdentry.cmdtype != CMDBUILTIN || - cmdentry.u.bltin != bltincmd) - break; - cmd_flags |= DO_NOFUNC; - argsused = parse_command_args(argc, argv, &use_syspath); - if (argsused == 0) { - /* use 'type' builtin to display info */ - cmdentry.u.bltin = typecmd; - break; - } - argc -= argsused; - argv += argsused; - if (use_syspath) - path = syspath() + 5; - } while (argc != 0); - if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) - /* posix mandates that 'command ' act as if - was a normal builtin */ - cmdentry.cmdtype = CMDBUILTIN; - } - - /* - * When traps are invalid, we permit the following: - * trap - * command trap - * eval trap - * command eval trap - * eval command trap - * without zapping the traps completely, in all other cases we do. - * - * The test here permits eval "anything" but when evalstring() comes - * back here again, the "anything" will be validated. - * This means we can actually do: - * eval eval eval command eval eval command trap - * as long as we end up with just "trap" - * - * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN - * - * trapcmd() takes care of doing free_traps() if it is needed there. - */ - if (traps_invalid && - ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) || - (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd))) - free_traps(); - - /* Fork off a child process if necessary. */ - if (cmd->ncmd.backgnd || (have_traps() && (flags & EV_EXIT) != 0) - || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) - && (flags & EV_EXIT) == 0) - || ((flags & EV_BACKCMD) != 0 && - ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) - || cmdentry.u.bltin == dotcmd - || cmdentry.u.bltin == evalcmd))) { - INTOFF; - jp = makejob(cmd, 1); - mode = cmd->ncmd.backgnd; - if (flags & EV_BACKCMD) { - mode = FORK_NOJOB; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - } -#ifdef DO_SHAREDVFORK - /* It is essential that if DO_SHAREDVFORK is defined that the - * child's address space is actually shared with the parent as - * we rely on this. - */ - if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL) { - pid_t pid; - int serrno; - - savelocalvars = localvars; - localvars = NULL; - vforked = 1; - VFORK_BLOCK - switch (pid = vfork()) { - case -1: - serrno = errno; - VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n", - serrno)); - INTON; - error("Cannot vfork (%s)", strerror(serrno)); - break; - case 0: - /* Make sure that exceptions only unwind to - * after the vfork(2) - */ - SHELL_FORKED(); - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - /* - * We can't progress with the - * vfork, so, set vforked = 2 - * so the parent knows, - * and _exit(); - */ - vforked = 2; - _exit(0); - } else { - _exit(exception == EXEXIT ? - exitstatus : exerrno); - } - } - savehandler = handler; - handler = &jmploc; - listmklocal(varlist.list, - VDOEXPORT | VEXPORT | VNOFUNC); - forkchild(jp, cmd, mode, vforked); - break; - default: - VFORK_UNDO(); - /* restore from vfork(2) */ - handler = savehandler; - poplocalvars(); - localvars = savelocalvars; - if (vforked == 2) { - vforked = 0; - - (void)waitpid(pid, NULL, 0); - /* - * We need to progress in a - * normal fork fashion - */ - goto normal_fork; - } - /* - * Here the child has left home, - * getting on with its life, so - * so must we... - */ - vforked = 0; - forkparent(jp, cmd, mode, pid); - goto parent; - } - VFORK_END - } else { - normal_fork: -#endif - if (forkshell(jp, cmd, mode) != 0) - goto parent; /* at end of routine */ - flags |= EV_EXIT; - FORCEINTON; -#ifdef DO_SHAREDVFORK - } -#endif - if (flags & EV_BACKCMD) { - if (!vforked) { - FORCEINTON; - } - close(pip[0]); - movefd(pip[1], 1); - } - flags |= EV_EXIT; - } - - /* This is the child process if a fork occurred. */ - /* Execute the command. */ - switch (cmdentry.cmdtype) { - volatile int saved; - - case CMDFUNCTION: - VXTRACE(DBG_EVAL, ("Shell function%s: ",vforked?" VF":""), - trargs(argv)); - redirect(cmd->ncmd.redirect, saved = - !(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0); - saveparam = shellparam; - shellparam.malloc = 0; - shellparam.reset = 1; - shellparam.nparam = argc - 1; - shellparam.p = argv + 1; - shellparam.optnext = NULL; - INTOFF; - savelocalvars = localvars; - localvars = NULL; - reffunc(cmdentry.u.func); - INTON; - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - freeparam((volatile struct shparam *) - &saveparam); - } else { - freeparam(&shellparam); - shellparam = saveparam; - } - if (saved) - popredir();; - unreffunc(cmdentry.u.func); - poplocalvars(); - localvars = savelocalvars; - funclinebase = savefuncline; - funclineabs = savefuncabs; - handler = savehandler; - longjmp(handler->loc, 1); - } - savehandler = handler; - handler = &jmploc; - if (cmdentry.u.func) { - if (cmdentry.lno_frel) - funclinebase = cmdentry.lineno - 1; - else - funclinebase = 0; - funclineabs = cmdentry.lineno; - - VTRACE(DBG_EVAL, - ("function: node: %d '%s' # %d%s; funclinebase=%d\n", - getfuncnode(cmdentry.u.func)->type, - NODETYPENAME(getfuncnode(cmdentry.u.func)->type), - cmdentry.lineno, cmdentry.lno_frel?" (=1)":"", - funclinebase)); - } - listmklocal(varlist.list, VDOEXPORT | VEXPORT); - /* stop shell blowing its stack */ - if (++funcnest > 1000) - error("too many nested function calls"); - evaltree(getfuncnode(cmdentry.u.func), - flags & (EV_TESTED|EV_EXIT)); - funcnest--; - INTOFF; - unreffunc(cmdentry.u.func); - poplocalvars(); - localvars = savelocalvars; - funclinebase = savefuncline; - funclineabs = savefuncabs; - freeparam(&shellparam); - shellparam = saveparam; - handler = savehandler; - if (saved) - popredir(); - INTON; - if (evalskip == SKIPFUNC) { - evalskip = SKIPNONE; - skipcount = 0; - } - if (flags & EV_EXIT) - exitshell(exitstatus); - break; - - case CMDSPLBLTIN: - VTRACE(DBG_EVAL, ("special ")); - case CMDBUILTIN: - VXTRACE(DBG_EVAL, ("builtin command [%d]%s: ", argc, - vforked ? " VF" : ""), trargs(argv)); - mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; - if (flags == EV_BACKCMD) { - memout.nleft = 0; - memout.nextc = memout.buf; - memout.bufsize = 64; - mode |= REDIR_BACKQ; - } - e = -1; - savecmdname = commandname; - savetopfile = getcurrentfile(); - savehandler = handler; - temp_path = 0; - if (!setjmp(jmploc.loc)) { - handler = &jmploc; - - /* - * We need to ensure the command hash table isn't - * corrupted by temporary PATH assignments. - * However we must ensure the 'local' command works! - */ - if (path != pathval() && (cmdentry.u.bltin == hashcmd || - cmdentry.u.bltin == typecmd)) { - savelocalvars = localvars; - localvars = 0; - temp_path = 1; - mklocal(path - 5 /* PATH= */, 0); - } - redirect(cmd->ncmd.redirect, mode); - - /* - * the empty command is regarded as a normal - * builtin for the purposes of redirects, but - * is a special builtin for var assigns. - * (unless we are the "command" command.) - */ - if (argc == 0 && !(cmd_flags & DO_NOFUNC)) - cmdentry.cmdtype = CMDSPLBLTIN; - - /* exec is a special builtin, but needs this list... */ - cmdenviron = varlist.list; - /* we must check 'readonly' flag for all builtins */ - listsetvar(varlist.list, - cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); - commandname = argv[0]; - /* initialize nextopt */ - argptr = argv + 1; - optptr = NULL; - /* and getopt */ - optreset = 1; - optind = 1; - builtin_flags = flags; - exitstatus = cmdentry.u.bltin(argc, argv); - } else { - e = exception; - if (e == EXINT) - exitstatus = SIGINT + 128; - else if (e == EXEXEC) - exitstatus = exerrno; - else if (e != EXEXIT) - exitstatus = 2; - } - handler = savehandler; - flushall(); - out1 = &output; - out2 = &errout; - freestdout(); - if (temp_path) { - poplocalvars(); - localvars = savelocalvars; - } - cmdenviron = NULL; - if (e != EXSHELLPROC) { - commandname = savecmdname; - if (flags & EV_EXIT) - exitshell(exitstatus); - } - if (e != -1) { - if ((e != EXERROR && e != EXEXEC) - || cmdentry.cmdtype == CMDSPLBLTIN) - exraise(e); - popfilesupto(savetopfile); - FORCEINTON; - } - if (cmdentry.u.bltin != execcmd) - popredir(); - if (flags == EV_BACKCMD) { - backcmd->buf = memout.buf; - backcmd->nleft = memout.nextc - memout.buf; - memout.buf = NULL; - } - break; - - default: - VXTRACE(DBG_EVAL, ("normal command%s: ", vforked?" VF":""), - trargs(argv)); - redirect(cmd->ncmd.redirect, - (vforked ? REDIR_VFORK : 0) | REDIR_KEEP); - if (!vforked) - for (sp = varlist.list ; sp ; sp = sp->next) - setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); - envp = environment(); - shellexec(argv, envp, path, cmdentry.u.index, vforked); - break; - } - goto out; - - parent: /* parent process gets here (if we forked) */ - - exitstatus = 0; /* if not altered just below */ - if (mode == FORK_FG) { /* argument to fork */ - exitstatus = waitforjob(jp); - } else if (mode == FORK_NOJOB) { - backcmd->fd = pip[0]; - close(pip[1]); - backcmd->jp = jp; - } - FORCEINTON; - - out: - if (lastarg) - /* implement $_ for whatever use that really is */ - (void) setvarsafe("_", lastarg, VNOERROR); - popstackmark(&smark); -} - - -/* - * Search for a command. This is called before we fork so that the - * location of the command will be available in the parent as well as - * the child. The check for "goodname" is an overly conservative - * check that the name will not be subject to expansion. - */ - -STATIC void -prehash(union node *n) -{ - struct cmdentry entry; - - if (n && n->type == NCMD && n->ncmd.args) - if (goodname(n->ncmd.args->narg.text)) - find_command(n->ncmd.args->narg.text, &entry, 0, - pathval()); -} - -int -in_function(void) -{ - return funcnest; -} - -enum skipstate -current_skipstate(void) -{ - return evalskip; -} - -void -save_skipstate(struct skipsave *p) -{ - *p = s_k_i_p; -} - -void -restore_skipstate(const struct skipsave *p) -{ - s_k_i_p = *p; -} - -void -stop_skipping(void) -{ - evalskip = SKIPNONE; - skipcount = 0; -} - -/* - * Builtin commands. Builtin commands whose functions are closely - * tied to evaluation are implemented here. - */ - -/* - * No command given. - */ - -int -bltincmd(int argc, char **argv) -{ - /* - * Preserve exitstatus of a previous possible redirection - * as POSIX mandates - */ - return back_exitstatus; -} - - -/* - * Handle break and continue commands. Break, continue, and return are - * all handled by setting the evalskip flag. The evaluation routines - * above all check this flag, and if it is set they start skipping - * commands rather than executing them. The variable skipcount is - * the number of loops to break/continue, or the number of function - * levels to return. (The latter is always 1.) It should probably - * be an error to break out of more loops than exist, but it isn't - * in the standard shell so we don't make it one here. - */ - -int -breakcmd(int argc, char **argv) -{ - int n = argc > 1 ? number(argv[1]) : 1; - - if (n <= 0) - error("invalid count: %d", n); - if (n > loopnest) - n = loopnest; - if (n > 0) { - evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; - skipcount = n; - } - return 0; -} - -int -dotcmd(int argc, char **argv) -{ - exitstatus = 0; - - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname; - /* - * dot_funcnest needs to be 0 when not in a dotcmd, so it - * cannot be restored with (funcnest + 1). - */ - int dot_funcnest_old; - struct stackmark smark; - - setstackmark(&smark); - fullname = find_dot_file(argv[1]); - setinputfile(fullname, 1); - commandname = fullname; - dot_funcnest_old = dot_funcnest; - dot_funcnest = funcnest + 1; - cmdloop(0); - dot_funcnest = dot_funcnest_old; - popfile(); - popstackmark(&smark); - } - return exitstatus; -} - -/* - * Take commands from a file. To be compatible we should do a path - * search for the file, which is necessary to find sub-commands. - */ - -STATIC char * -find_dot_file(char *basename) -{ - char *fullname; - const char *path = pathval(); - struct stat statb; - - /* don't try this for absolute or relative paths */ - if (strchr(basename, '/')) { - if (stat(basename, &statb) == 0) { - if (S_ISDIR(statb.st_mode)) - error("%s: is a directory", basename); - if (S_ISBLK(statb.st_mode)) - error("%s: is a block device", basename); - return basename; - } - } else while ((fullname = padvance(&path, basename, 1)) != NULL) { - if ((stat(fullname, &statb) == 0)) { - /* weird format is to ease future code... */ - if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode)) - ; -#if notyet - else if (unreadable()) { - /* - * testing this via st_mode is ugly to get - * correct (and would ignore ACLs). - * better way is just to open the file. - * But doing that here would (currently) - * mean opening the file twice, which - * might not be safe. So, defer this - * test until code is restructures so - * we can return a fd. Then we also - * get to fix the mem leak just below... - */ - } -#endif - else { - /* - * Don't bother freeing here, since - * it will be freed by the caller. - * XXX no it won't - a bug for later. - */ - return fullname; - } - } - stunalloc(fullname); - } - - /* not found in the PATH */ - error("%s: not found", basename); - /* NOTREACHED */ -} - - - -/* - * The return command. - * - * Quoth the POSIX standard: - * The return utility shall cause the shell to stop executing the current - * function or dot script. If the shell is not currently executing - * a function or dot script, the results are unspecified. - * - * As for the unspecified part, there seems to be no de-facto standard: bash - * ignores the return with a warning, zsh ignores the return in interactive - * mode but seems to liken it to exit in a script. (checked May 2014) - * - * We choose to silently ignore the return. Older versions of this shell - * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This - * had at least the problem of circumventing the check for stopped jobs, - * which would occur for exit or ^D. - */ - -int -returncmd(int argc, char **argv) -{ - int ret = argc > 1 ? number(argv[1]) : exitstatus; - - if ((dot_funcnest == 0 && funcnest) - || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) { - evalskip = SKIPFUNC; - skipcount = 1; - } else if (dot_funcnest > 0) { - evalskip = SKIPFILE; - skipcount = 1; - } else { - /* XXX: should a warning be issued? */ - ret = 0; - } - - return ret; -} - - -int -falsecmd(int argc, char **argv) -{ - return 1; -} - - -int -truecmd(int argc, char **argv) -{ - return 0; -} - - -int -execcmd(int argc, char **argv) -{ - if (argc > 1) { - struct strlist *sp; - - iflag = 0; /* exit on error */ - mflag = 0; - optschanged(); - for (sp = cmdenviron; sp; sp = sp->next) - setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); - shellexec(argv + 1, environment(), pathval(), 0, 0); - } - return 0; -} - -static int -conv_time(clock_t ticks, char *seconds, size_t l) -{ - static clock_t tpm = 0; - clock_t mins; - int i; - - if (!tpm) - tpm = sysconf(_SC_CLK_TCK) * 60; - - mins = ticks / tpm; - snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); - - if (seconds[0] == '6' && seconds[1] == '0') { - /* 59.99995 got rounded up... */ - mins++; - strlcpy(seconds, "0.0", l); - return mins; - } - - /* suppress trailing zeros */ - i = strlen(seconds) - 1; - for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) - seconds[i] = 0; - return mins; -} - -int -timescmd(int argc, char **argv) -{ - struct tms tms; - int u, s, cu, cs; - char us[8], ss[8], cus[8], css[8]; - - nextopt(""); - - times(&tms); - - u = conv_time(tms.tms_utime, us, sizeof(us)); - s = conv_time(tms.tms_stime, ss, sizeof(ss)); - cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); - cs = conv_time(tms.tms_cstime, css, sizeof(css)); - - outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", - u, us, s, ss, cu, cus, cs, css); - - return 0; -} -- cgit v1.2.3-60-g2f50