summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/exec.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/exec.c6250
1 files changed, 0 insertions, 6250 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/exec.c b/dotfiles/system/.zsh/modules/Src/exec.c
deleted file mode 100644
index 615a508..0000000
--- a/dotfiles/system/.zsh/modules/Src/exec.c
+++ /dev/null
@@ -1,6250 +0,0 @@
-/*
- * exec.c - command execution
- *
- * This file is part of zsh, the Z shell.
- *
- * Copyright (c) 1992-1997 Paul Falstad
- * All rights reserved.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and to distribute modified versions of this software for any
- * purpose, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * In no event shall Paul Falstad or the Zsh Development Group be liable
- * to any party for direct, indirect, special, incidental, or consequential
- * damages arising out of the use of this software and its documentation,
- * even if Paul Falstad and the Zsh Development Group have been advised of
- * the possibility of such damage.
- *
- * Paul Falstad and the Zsh Development Group specifically disclaim any
- * warranties, including, but not limited to, the implied warranties of
- * merchantability and fitness for a particular purpose. The software
- * provided hereunder is on an "as is" basis, and Paul Falstad and the
- * Zsh Development Group have no obligation to provide maintenance,
- * support, updates, enhancements, or modifications.
- *
- */
-
-#include "zsh.mdh"
-#include "exec.pro"
-
-/* Flags for last argument of addvars */
-
-enum {
- /* Export the variable for "VAR=val cmd ..." */
- ADDVAR_EXPORT = 1 << 0,
- /* Apply restrictions for variable */
- ADDVAR_RESTRICT = 1 << 1,
- /* Variable list is being restored later */
- ADDVAR_RESTORE = 1 << 2
-};
-
-/* Structure in which to save values around shell function call */
-
-struct funcsave {
- char opts[OPT_SIZE];
- char *argv0;
- int zoptind, lastval, optcind, numpipestats;
- int *pipestats;
- char *scriptname;
- int breaks, contflag, loops, emulation, noerrexit, oflags, restore_sticky;
- Emulation_options sticky;
- struct funcstack fstack;
-};
-typedef struct funcsave *Funcsave;
-
-/*
- * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT.
- * Bits from noerrexit_bits.
- */
-
-/**/
-int noerrexit;
-
-/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */
-
-/**/
-int this_noerrexit;
-
-/*
- * noerrs = 1: suppress error messages
- * noerrs = 2: don't set errflag on parse error, either
- */
-
-/**/
-mod_export int noerrs;
-
-/* do not save history on exec and exit */
-
-/**/
-int nohistsave;
-
-/* error flag: bits from enum errflag_bits */
-
-/**/
-mod_export int errflag;
-
-/*
- * State of trap return value. Value is from enum trap_state.
- */
-
-/**/
-int trap_state;
-
-/*
- * Value associated with return from a trap.
- * This is only active if we are inside a trap, else its value
- * is irrelevant. It is initialised to -1 for a function trap and
- * -2 for a non-function trap and if negative is decremented as
- * we go deeper into functions and incremented as we come back up.
- * The value is used to decide if an explicit "return" should cause
- * a return from the caller of the trap; it does this by setting
- * trap_return to a status (i.e. a non-negative value).
- *
- * In summary, trap_return is
- * - zero unless we are in a trap
- * - negative in a trap unless it has triggered. Code uses this
- * to detect an active trap.
- * - non-negative in a trap once it was triggered. It should remain
- * non-negative until restored after execution of the trap.
- */
-
-/**/
-int trap_return;
-
-/* != 0 if this is a subshell */
-
-/**/
-int subsh;
-
-/* != 0 if we have a return pending */
-
-/**/
-mod_export int retflag;
-
-/**/
-long lastval2;
-
-/* The table of file descriptors. A table element is zero if the *
- * corresponding fd is not used by the shell. It is greater than *
- * 1 if the fd is used by a <(...) or >(...) substitution and 1 if *
- * it is an internal file descriptor which must be closed before *
- * executing an external command. The first ten elements of the *
- * table is not used. A table element is set by movefd and cleard *
- * by zclose. */
-
-/**/
-mod_export unsigned char *fdtable;
-
-/* The allocated size of fdtable */
-
-/**/
-int fdtable_size;
-
-/* The highest fd that marked with nonzero in fdtable */
-
-/**/
-mod_export int max_zsh_fd;
-
-/* input fd from the coprocess */
-
-/**/
-mod_export int coprocin;
-
-/* output fd from the coprocess */
-
-/**/
-mod_export int coprocout;
-
-/* count of file locks recorded in fdtable */
-
-/**/
-int fdtable_flocks;
-
-
-/* != 0 if the line editor is active */
-
-/**/
-mod_export int zleactive;
-
-/* pid of process undergoing 'process substitution' */
-
-/**/
-pid_t cmdoutpid;
-
-/* pid of last process started by <(...), >(...) */
-
-/**/
-mod_export pid_t procsubstpid;
-
-/* exit status of process undergoing 'process substitution' */
-
-/**/
-int cmdoutval;
-
-/*
- * This is set by an exiting $(...) substitution to indicate we need
- * to retain the status. We initialize it to zero if we think we need
- * to reset the status for a command.
- */
-
-/**/
-int use_cmdoutval;
-
-/* The context in which a shell function is called, see SFC_* in zsh.h. */
-
-/**/
-mod_export int sfcontext;
-
-/* Stack to save some variables before executing a signal handler function */
-
-/**/
-struct execstack *exstack;
-
-/* Stack with names of function calls, 'source' calls, and 'eval' calls
- * currently active. */
-
-/**/
-mod_export Funcstack funcstack;
-
-#define execerr() \
- do { \
- if (!forked) { \
- redir_err = lastval = 1; \
- goto done; \
- } else { \
- _exit(1); \
- } \
- } while (0)
-
-static int doneps4;
-static char *STTYval;
-static char *blank_env[] = { NULL };
-
-/* Execution functions. */
-
-static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
- execcursh, exectime, NULL /* execfuncdef handled specially */,
- execfor, execselect,
- execwhile, execrepeat, execcase, execif, execcond,
- execarith, execautofn, exectry
-};
-
-/* structure for command builtin for when it is used with -v or -V */
-static struct builtin commandbn =
- BUILTIN("command", 0, bin_whence, 0, -1, BIN_COMMAND, "pvV", NULL);
-
-/* parse string into a list */
-
-/**/
-mod_export Eprog
-parse_string(char *s, int reset_lineno)
-{
- Eprog p;
- zlong oldlineno;
-
- zcontext_save();
- inpush(s, INP_LINENO, NULL);
- strinbeg(0);
- oldlineno = lineno;
- if (reset_lineno)
- lineno = 1;
- p = parse_list();
- lineno = oldlineno;
- if (tok == LEXERR && !lastval)
- lastval = 1;
- strinend();
- inpop();
- zcontext_restore();
- return p;
-}
-
-/**/
-#ifdef HAVE_GETRLIMIT
-
-/* the resource limits for the shell and its children */
-
-/**/
-mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS];
-
-/**/
-mod_export int
-zsetlimit(int limnum, char *nam)
-{
- if (limits[limnum].rlim_max != current_limits[limnum].rlim_max ||
- limits[limnum].rlim_cur != current_limits[limnum].rlim_cur) {
- if (setrlimit(limnum, limits + limnum)) {
- if (nam)
- zwarnnam(nam, "setrlimit failed: %e", errno);
- limits[limnum] = current_limits[limnum];
- return -1;
- }
- current_limits[limnum] = limits[limnum];
- }
- return 0;
-}
-
-/**/
-mod_export int
-setlimits(char *nam)
-{
- int limnum;
- int ret = 0;
-
- for (limnum = 0; limnum < RLIM_NLIMITS; limnum++)
- if (zsetlimit(limnum, nam))
- ret++;
- return ret;
-}
-
-/**/
-#endif /* HAVE_GETRLIMIT */
-
-/* fork and set limits */
-
-/**/
-static pid_t
-zfork(struct timeval *tv)
-{
- pid_t pid;
- struct timezone dummy_tz;
-
- /*
- * Is anybody willing to explain this test?
- */
- if (thisjob != -1 && thisjob >= jobtabsize - 1 && !expandjobtab()) {
- zerr("job table full");
- return -1;
- }
- if (tv)
- gettimeofday(tv, &dummy_tz);
- /*
- * Queueing signals is necessary on Linux because fork()
- * manipulates mutexes, leading to deadlock in memory
- * allocation. We don't expect fork() to be particularly
- * zippy anyway.
- */
- queue_signals();
- pid = fork();
- unqueue_signals();
- if (pid == -1) {
- zerr("fork failed: %e", errno);
- return -1;
- }
-#ifdef HAVE_GETRLIMIT
- if (!pid)
- /* set resource limits for the child process */
- setlimits(NULL);
-#endif
- return pid;
-}
-
-/*
- * Allen Edeln gebiet ich Andacht,
- * Hohen und Niedern von Heimdalls Geschlecht;
- * Ich will list_pipe's Wirken kuenden
- * Die aeltesten Sagen, der ich mich entsinne...
- *
- * In most shells, if you do something like:
- *
- * cat foo | while read a; do grep $a bar; done
- *
- * the shell forks and executes the loop in the sub-shell thus created.
- * In zsh this traditionally executes the loop in the current shell, which
- * is nice to have if the loop does something to change the shell, like
- * setting parameters or calling builtins.
- * Putting the loop in a sub-shell makes life easy, because the shell only
- * has to put it into the job-structure and then treats it as a normal
- * process. Suspending and interrupting is no problem then.
- * Some years ago, zsh either couldn't suspend such things at all, or
- * it got really messed up when users tried to do it. As a solution, we
- * implemented the list_pipe-stuff, which has since then become a reason
- * for many nightmares.
- * Pipelines like the one above are executed by the functions in this file
- * which call each other (and sometimes recursively). The one above, for
- * example would lead to a function call stack roughly like:
- *
- * execlist->execpline->execcmd->execwhile->execlist->execpline
- *
- * (when waiting for the grep, ignoring execpline2 for now). At this time,
- * zsh has built two job-table entries for it: one for the cat and one for
- * the grep. If the user hits ^Z at this point (and jobbing is used), the
- * shell is notified that the grep was suspended. The list_pipe flag is
- * used to tell the execpline where it was waiting that it was in a pipeline
- * with a shell construct at the end (which may also be a shell function or
- * several other things). When zsh sees the suspended grep, it forks to let
- * the sub-shell execute the rest of the while loop. The parent shell walks
- * up in the function call stack to the first execpline. There it has to find
- * out that it has just forked and then has to add information about the sub-
- * shell (its pid and the text for it) in the job entry of the cat. The pid
- * is passed down in the list_pipe_pid variable.
- * But there is a problem: the suspended grep is a child of the parent shell
- * and can't be adopted by the sub-shell. So the parent shell also has to
- * keep the information about this process (more precisely: this pipeline)
- * by keeping the job table entry it created for it. The fact that there
- * are two jobs which have to be treated together is remembered by setting
- * the STAT_SUPERJOB flag in the entry for the cat-job (which now also
- * contains a process-entry for the whole loop -- the sub-shell) and by
- * setting STAT_SUBJOB in the job of the grep-job. With that we can keep
- * sub-jobs from being displayed and we can handle an fg/bg on the super-
- * job correctly. When the super-job is continued, the shell also wakes up
- * the sub-job. But then, the grep will exit sometime. Now the parent shell
- * has to remember not to try to wake it up again (in case of another ^Z).
- * It also has to wake up the sub-shell (which suspended itself immediately
- * after creation), so that the rest of the loop is executed by it.
- * But there is more: when the sub-shell is created, the cat may already
- * have exited, so we can't put the sub-shell in the process group of it.
- * In this case, we put the sub-shell in the process group of the parent
- * shell and in any case, the sub-shell has to put all commands executed
- * by it into its own process group, because only this way the parent
- * shell can control them since it only knows the process group of the sub-
- * shell. Of course, this information is also important when putting a job
- * in the foreground, where we have to attach its process group to the
- * controlling tty.
- * All this is made more difficult because we have to handle return values
- * correctly. If the grep is signaled, its exit status has to be propagated
- * back to the parent shell which needs it to set the exit status of the
- * super-job. And of course, when the grep is signaled (including ^C), the
- * loop has to be stopped, etc.
- * The code for all this is distributed over three files (exec.c, jobs.c,
- * and signals.c) and none of them is a simple one. So, all in all, there
- * may still be bugs, but considering the complexity (with race conditions,
- * signal handling, and all that), this should probably be expected.
- */
-
-/**/
-int list_pipe = 0, simple_pline = 0;
-
-static pid_t list_pipe_pid;
-static struct timeval list_pipe_start;
-static int nowait, pline_level = 0;
-static int list_pipe_child = 0, list_pipe_job;
-static char list_pipe_text[JOBTEXTSIZE];
-
-/* execute a current shell command */
-
-/**/
-static int
-execcursh(Estate state, int do_exec)
-{
- Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]);
-
- /* Skip word only used for try/always */
- state->pc++;
-
- /*
- * The test thisjob != -1 was added because sometimes thisjob
- * can be invalid at this point. The case in question was
- * in a precmd function after operations involving background
- * jobs.
- *
- * This is because sometimes we bypass job control to execute
- * very simple functions via execssimple().
- */
- if (!list_pipe && thisjob != -1 && thisjob != list_pipe_job &&
- !hasprocs(thisjob))
- deletejob(jobtab + thisjob, 0);
- cmdpush(CS_CURSH);
- execlist(state, 1, do_exec);
- cmdpop();
-
- state->pc = end;
- this_noerrexit = 1;
-
- return lastval;
-}
-
-/* execve after handling $_ and #! */
-
-#define POUNDBANGLIMIT 64
-
-/**/
-static int
-zexecve(char *pth, char **argv, char **newenvp)
-{
- int eno;
- static char buf[PATH_MAX * 2+1];
- char **eep;
-
- unmetafy(pth, NULL);
- for (eep = argv; *eep; eep++)
- if (*eep != pth)
- unmetafy(*eep, NULL);
- buf[0] = '_';
- buf[1] = '=';
- if (*pth == '/')
- strcpy(buf + 2, pth);
- else
- sprintf(buf + 2, "%s/%s", pwd, pth);
- zputenv(buf);
-#ifndef FD_CLOEXEC
- closedumps();
-#endif
-
- if (newenvp == NULL)
- newenvp = environ;
- winch_unblock();
- execve(pth, argv, newenvp);
-
- /* If the execve returns (which in general shouldn't happen), *
- * then check for an errno equal to ENOEXEC. This errno is set *
- * if the process file has the appropriate access permission, *
- * but has an invalid magic number in its header. */
- if ((eno = errno) == ENOEXEC || eno == ENOENT) {
- char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0;
- int fd, ct, t0;
-
- if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) {
- argv0 = *argv;
- *argv = pth;
- execvebuf[0] = '\0';
- ct = read(fd, execvebuf, POUNDBANGLIMIT);
- close(fd);
- if (ct >= 0) {
- if (execvebuf[0] == '#') {
- if (execvebuf[1] == '!') {
- for (t0 = 0; t0 != ct; t0++)
- if (execvebuf[t0] == '\n')
- break;
- while (inblank(execvebuf[t0]))
- execvebuf[t0--] = '\0';
- execvebuf[POUNDBANGLIMIT] = '\0';
- for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
- for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
- if (eno == ENOENT) {
- char *pprog;
- if (*ptr)
- *ptr = '\0';
- if (*ptr2 != '/' &&
- (pprog = pathprog(ptr2, NULL))) {
- argv[-2] = ptr2;
- argv[-1] = ptr + 1;
- winch_unblock();
- execve(pprog, argv - 2, newenvp);
- }
- zerr("%s: bad interpreter: %s: %e", pth, ptr2,
- eno);
- } else if (*ptr) {
- *ptr = '\0';
- argv[-2] = ptr2;
- argv[-1] = ptr + 1;
- winch_unblock();
- execve(ptr2, argv - 2, newenvp);
- } else {
- argv[-1] = ptr2;
- winch_unblock();
- execve(ptr2, argv - 1, newenvp);
- }
- } else if (eno == ENOEXEC) {
- argv[-1] = "sh";
- winch_unblock();
- execve("/bin/sh", argv - 1, newenvp);
- }
- } else if (eno == ENOEXEC) {
- for (t0 = 0; t0 != ct; t0++)
- if (!execvebuf[t0])
- break;
- if (t0 == ct) {
- argv[-1] = "sh";
- winch_unblock();
- execve("/bin/sh", argv - 1, newenvp);
- }
- }
- } else
- eno = errno;
- *argv = argv0;
- } else
- eno = errno;
- }
- /* restore the original arguments and path but do not bother with *
- * null characters as these cannot be passed to external commands *
- * anyway. So the result is truncated at the first null char. */
- pth = metafy(pth, -1, META_NOALLOC);
- for (eep = argv; *eep; eep++)
- if (*eep != pth)
- (void) metafy(*eep, -1, META_NOALLOC);
- return eno;
-}
-
-#define MAXCMDLEN (PATH_MAX*4)
-
-/* test whether we really want to believe the error number */
-
-/**/
-static int
-isgooderr(int e, char *dir)
-{
- /*
- * Maybe the directory was unreadable, or maybe it wasn't
- * even a directory.
- */
- return ((e != EACCES || !access(dir, X_OK)) &&
- e != ENOENT && e != ENOTDIR);
-}
-
-/*
- * Attempt to handle command not found.
- * Return 0 if the condition was handled, non-zero otherwise.
- */
-
-/**/
-static int
-commandnotfound(char *arg0, LinkList args)
-{
- Shfunc shf = (Shfunc)
- shfunctab->getnode(shfunctab, "command_not_found_handler");
-
- if (!shf) {
- lastval = 127;
- return 1;
- }
-
- pushnode(args, arg0);
- lastval = doshfunc(shf, args, 1);
- return 0;
-}
-
-/*
- * Search the default path for cmd.
- * pbuf of length plen is the buffer to use.
- * Return NULL if not found.
- */
-
-static char *
-search_defpath(char *cmd, char *pbuf, int plen)
-{
- char *ps = DEFAULT_PATH, *pe = NULL, *s;
-
- for (ps = DEFAULT_PATH; ps; ps = pe ? pe+1 : NULL) {
- pe = strchr(ps, ':');
- if (*ps == '/') {
- s = pbuf;
- if (pe) {
- if (pe - ps >= plen)
- continue;
- struncpy(&s, ps, pe-ps);
- } else {
- if (strlen(ps) >= plen)
- continue;
- strucpy(&s, ps);
- }
- *s++ = '/';
- if ((s - pbuf) + strlen(cmd) >= plen)
- continue;
- strucpy(&s, cmd);
- if (iscom(pbuf))
- return pbuf;
- }
- }
- return NULL;
-}
-
-/* execute an external command */
-
-/**/
-static void
-execute(LinkList args, int flags, int defpath)
-{
- Cmdnam cn;
- char buf[MAXCMDLEN+1], buf2[MAXCMDLEN+1];
- char *s, *z, *arg0;
- char **argv, **pp, **newenvp = NULL;
- int eno = 0, ee;
-
- arg0 = (char *) peekfirst(args);
- if (isset(RESTRICTED) && (strchr(arg0, '/') || defpath)) {
- zerr("%s: restricted", arg0);
- _exit(1);
- }
-
- /* If the parameter STTY is set in the command's environment, *
- * we first run the stty command with the value of this *
- * parameter as it arguments. */
- if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) {
- char *t = tricat("stty", " ", s);
-
- STTYval = 0; /* this prevents infinite recursion */
- zsfree(s);
- execstring(t, 1, 0, "stty");
- zsfree(t);
- } else if (s) {
- STTYval = 0;
- zsfree(s);
- }
-
- /* If ARGV0 is in the commands environment, we use *
- * that as argv[0] for this external command */
- if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) {
- setdata(firstnode(args), (void *) ztrdup(z));
- /*
- * Note we don't do anything with the parameter structure
- * for ARGV0: that's OK since we're about to exec or exit
- * on failure.
- */
-#ifdef USE_SET_UNSET_ENV
- unsetenv("ARGV0");
-#else
- delenvvalue(z - 6);
-#endif
- } else if (flags & BINF_DASH) {
- /* Else if the pre-command `-' was given, we add `-' *
- * to the front of argv[0] for this command. */
- sprintf(buf2, "-%s", arg0);
- setdata(firstnode(args), (void *) ztrdup(buf2));
- }
-
- argv = makecline(args);
- if (flags & BINF_CLEARENV)
- newenvp = blank_env;
-
- /*
- * Note that we don't close fd's attached to process substitution
- * here, which should be visible to external processes.
- */
- closem(FDT_XTRACE, 0);
-#ifndef FD_CLOEXEC
- if (SHTTY != -1) {
- close(SHTTY);
- SHTTY = -1;
- }
-#endif
- child_unblock();
- if ((int) strlen(arg0) >= PATH_MAX) {
- zerr("command too long: %s", arg0);
- _exit(1);
- }
- for (s = arg0; *s; s++)
- if (*s == '/') {
- int lerrno = zexecve(arg0, argv, newenvp);
- if (arg0 == s || unset(PATHDIRS) ||
- (arg0[0] == '.' && (arg0 + 1 == s ||
- (arg0[1] == '.' && arg0 + 2 == s)))) {
- zerr("%e: %s", lerrno, arg0);
- _exit((lerrno == EACCES || lerrno == ENOEXEC) ? 126 : 127);
- }
- break;
- }
-
- /* for command -p, search the default path */
- if (defpath) {
- char pbuf[PATH_MAX+1];
- char *dptr;
-
- if (!search_defpath(arg0, pbuf, PATH_MAX)) {
- if (commandnotfound(arg0, args) == 0)
- _exit(lastval);
- zerr("command not found: %s", arg0);
- _exit(127);
- }
-
- ee = zexecve(pbuf, argv, newenvp);
-
- if ((dptr = strrchr(pbuf, '/')))
- *dptr = '\0';
- if (isgooderr(ee, *pbuf ? pbuf : "/"))
- eno = ee;
-
- } else {
-
- if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) {
- char nn[PATH_MAX+1], *dptr;
-
- if (cn->node.flags & HASHED)
- strcpy(nn, cn->u.cmd);
- else {
- for (pp = path; pp < cn->u.name; pp++)
- if (!**pp || (**pp == '.' && (*pp)[1] == '\0')) {
- ee = zexecve(arg0, argv, newenvp);
- if (isgooderr(ee, *pp))
- eno = ee;
- } else if (**pp != '/') {
- z = buf;
- strucpy(&z, *pp);
- *z++ = '/';
- strcpy(z, arg0);
- ee = zexecve(buf, argv, newenvp);
- if (isgooderr(ee, *pp))
- eno = ee;
- }
- strcpy(nn, cn->u.name ? *(cn->u.name) : "");
- strcat(nn, "/");
- strcat(nn, cn->node.nam);
- }
- ee = zexecve(nn, argv, newenvp);
-
- if ((dptr = strrchr(nn, '/')))
- *dptr = '\0';
- if (isgooderr(ee, *nn ? nn : "/"))
- eno = ee;
- }
- for (pp = path; *pp; pp++)
- if (!(*pp)[0] || ((*pp)[0] == '.' && !(*pp)[1])) {
- ee = zexecve(arg0, argv, newenvp);
- if (isgooderr(ee, *pp))
- eno = ee;
- } else {
- z = buf;
- strucpy(&z, *pp);
- *z++ = '/';
- strcpy(z, arg0);
- ee = zexecve(buf, argv, newenvp);
- if (isgooderr(ee, *pp))
- eno = ee;
- }
- }
-
- if (eno)
- zerr("%e: %s", eno, arg0);
- else if (commandnotfound(arg0, args) == 0)
- _exit(lastval);
- else
- zerr("command not found: %s", arg0);
- _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
-}
-
-#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; }
-
-/*
- * Get the full pathname of an external command.
- * If the second argument is zero, return the first argument if found;
- * if non-zero, return the path using heap memory. (RET_IF_COM(X),
- * above).
- * If the third argument is non-zero, use the system default path
- * instead of the current path.
- */
-
-/**/
-mod_export char *
-findcmd(char *arg0, int docopy, int default_path)
-{
- char **pp;
- char *z, *s, buf[MAXCMDLEN];
- Cmdnam cn;
-
- if (default_path)
- {
- if (search_defpath(arg0, buf, MAXCMDLEN))
- return docopy ? dupstring(buf) : arg0;
- return NULL;
- }
- cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
- if (!cn && isset(HASHCMDS) && !isrelative(arg0))
- cn = hashcmd(arg0, path);
- if ((int) strlen(arg0) > PATH_MAX)
- return NULL;
- if ((s = strchr(arg0, '/'))) {
- RET_IF_COM(arg0);
- if (arg0 == s || unset(PATHDIRS) || !strncmp(arg0, "./", 2) ||
- !strncmp(arg0, "../", 3)) {
- return NULL;
- }
- }
- if (cn) {
- char nn[PATH_MAX+1];
-
- if (cn->node.flags & HASHED)
- strcpy(nn, cn->u.cmd);
- else {
- for (pp = path; pp < cn->u.name; pp++)
- if (**pp != '/') {
- z = buf;
- if (**pp) {
- strucpy(&z, *pp);
- *z++ = '/';
- }
- strcpy(z, arg0);
- RET_IF_COM(buf);
- }
- strcpy(nn, cn->u.name ? *(cn->u.name) : "");
- strcat(nn, "/");
- strcat(nn, cn->node.nam);
- }
- RET_IF_COM(nn);
- }
- for (pp = path; *pp; pp++) {
- z = buf;
- if (**pp) {
- strucpy(&z, *pp);
- *z++ = '/';
- }
- strcpy(z, arg0);
- RET_IF_COM(buf);
- }
- return NULL;
-}
-
-/*
- * Return TRUE if the given path denotes an executable regular file, or a
- * symlink to one.
- */
-
-/**/
-int
-iscom(char *s)
-{
- struct stat statbuf;
- char *us = unmeta(s);
-
- return (access(us, X_OK) == 0 && stat(us, &statbuf) >= 0 &&
- S_ISREG(statbuf.st_mode));
-}
-
-/**/
-int
-isreallycom(Cmdnam cn)
-{
- char fullnam[MAXCMDLEN];
-
- if (cn->node.flags & HASHED)
- strcpy(fullnam, cn->u.cmd);
- else if (!cn->u.name)
- return 0;
- else {
- strcpy(fullnam, *(cn->u.name));
- strcat(fullnam, "/");
- strcat(fullnam, cn->node.nam);
- }
- return iscom(fullnam);
-}
-
-/*
- * Return TRUE if the given path contains a dot or dot-dot component
- * and does not start with a slash.
- */
-
-/**/
-int
-isrelative(char *s)
-{
- if (*s != '/')
- return 1;
- for (; *s; s++)
- if (*s == '.' && s[-1] == '/' &&
- (s[1] == '/' || s[1] == '\0' ||
- (s[1] == '.' && (s[2] == '/' || s[2] == '\0'))))
- return 1;
- return 0;
-}
-
-/**/
-mod_export Cmdnam
-hashcmd(char *arg0, char **pp)
-{
- Cmdnam cn;
- char *s, buf[PATH_MAX+1];
- char **pq;
-
- for (; *pp; pp++)
- if (**pp == '/') {
- s = buf;
- struncpy(&s, *pp, PATH_MAX);
- *s++ = '/';
- if ((s - buf) + strlen(arg0) >= PATH_MAX)
- continue;
- strcpy(s, arg0);
- if (iscom(buf))
- break;
- }
-
- if (!*pp)
- return NULL;
-
- cn = (Cmdnam) zshcalloc(sizeof *cn);
- cn->node.flags = 0;
- cn->u.name = pp;
- cmdnamtab->addnode(cmdnamtab, ztrdup(arg0), cn);
-
- if (isset(HASHDIRS)) {
- for (pq = pathchecked; pq <= pp; pq++)
- hashdir(pq);
- pathchecked = pp + 1;
- }
-
- return cn;
-}
-
-/**/
-int
-forklevel;
-
-/* Arguments to entersubsh() */
-enum {
- /* Subshell is to be run asynchronously (else synchronously) */
- ESUB_ASYNC = 0x01,
- /*
- * Perform process group and tty handling and clear the
- * (real) job table, since it won't be any longer valid
- */
- ESUB_PGRP = 0x02,
- /* Don't unset traps */
- ESUB_KEEPTRAP = 0x04,
- /* This is only a fake entry to a subshell */
- ESUB_FAKE = 0x08,
- /* Release the process group if pid is the shell's process group */
- ESUB_REVERTPGRP = 0x10,
- /* Don't handle the MONITOR option even if previously set */
- ESUB_NOMONITOR = 0x20,
- /* This is a subshell where job control is allowed */
- ESUB_JOB_CONTROL = 0x40
-};
-
-/**/
-static void
-entersubsh(int flags)
-{
- int i, sig, monitor, job_control_ok;
-
- if (!(flags & ESUB_KEEPTRAP))
- for (sig = 0; sig < SIGCOUNT; sig++)
- if (!(sigtrapped[sig] & ZSIG_FUNC))
- unsettrap(sig);
- monitor = isset(MONITOR);
- job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS);
- if (flags & ESUB_NOMONITOR)
- opts[MONITOR] = 0;
- if (!isset(MONITOR)) {
- if (flags & ESUB_ASYNC) {
- settrap(SIGINT, NULL, 0);
- settrap(SIGQUIT, NULL, 0);
- if (isatty(0)) {
- close(0);
- if (open("/dev/null", O_RDWR | O_NOCTTY)) {
- zerr("can't open /dev/null: %e", errno);
- _exit(1);
- }
- }
- }
- } else if (thisjob != -1 && (flags & ESUB_PGRP)) {
- if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) {
- if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 ||
- killpg(jobtab[list_pipe_job].gleader, 0) == -1) {
- jobtab[list_pipe_job].gleader =
- jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid());
- setpgrp(0L, jobtab[list_pipe_job].gleader);
- if (!(flags & ESUB_ASYNC))
- attachtty(jobtab[thisjob].gleader);
- }
- }
- else if (!jobtab[thisjob].gleader ||
- setpgrp(0L, jobtab[thisjob].gleader) == -1) {
- /*
- * This is the standard point at which a newly started
- * process gets put into the foreground by taking over
- * the terminal. Note that in normal circumstances we do
- * this only from the process itself. This only works if
- * we are still ignoring SIGTTOU at this point; in this
- * case ignoring the signal has the special effect that
- * the operation is allowed to work (in addition to not
- * causing the shell to be suspended).
- */
- jobtab[thisjob].gleader = getpid();
- if (list_pipe_job != thisjob &&
- !jobtab[list_pipe_job].gleader)
- jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader;
- setpgrp(0L, jobtab[thisjob].gleader);
- if (!(flags & ESUB_ASYNC))
- attachtty(jobtab[thisjob].gleader);
- }
- }
- if (!(flags & ESUB_FAKE))
- subsh = 1;
- /*
- * Increment the visible parameter ZSH_SUBSHELL even if this
- * is a fake subshell because we are exec'ing at the end.
- * Logically this should be equivalent to a real subshell so
- * we don't hang out the dirty washing.
- */
- zsh_subshell++;
- if ((flags & ESUB_REVERTPGRP) && getpid() == mypgrp)
- release_pgrp();
- shout = NULL;
- if (flags & ESUB_NOMONITOR) {
- /*
- * Allowing any form of interactive signalling here is
- * actively harmful as we are in a context where there is no
- * control over the process.
- */
- signal_ignore(SIGTTOU);
- signal_ignore(SIGTTIN);
- signal_ignore(SIGTSTP);
- } else if (!job_control_ok) {
- /*
- * If this process is not going to be doing job control,
- * we don't want to do special things with the corresponding
- * signals. If it is, we need to keep the special behaviour:
- * see note about attachtty() above.
- */
- signal_default(SIGTTOU);
- signal_default(SIGTTIN);
- signal_default(SIGTSTP);
- }
- if (interact) {
- signal_default(SIGTERM);
- if (!(sigtrapped[SIGINT] & ZSIG_IGNORED))
- signal_default(SIGINT);
- if (!(sigtrapped[SIGPIPE]))
- signal_default(SIGPIPE);
- }
- if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED))
- signal_default(SIGQUIT);
- /*
- * sigtrapped[sig] == ZSIG_IGNORED for signals that remain ignored,
- * but other trapped signals are temporarily blocked when intrap,
- * and must be unblocked before continuing into the subshell. This
- * is orthogonal to what the default handler for the signal may be.
- *
- * Start loop at 1 because 0 is SIGEXIT
- */
- if (intrap)
- for (sig = 1; sig < SIGCOUNT; sig++)
- if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED)
- signal_unblock(signal_mask(sig));
- if (!job_control_ok)
- opts[MONITOR] = 0;
- opts[USEZLE] = 0;
- zleactive = 0;
- /*
- * If we've saved fd's for later restoring, we're never going
- * to restore them now, so just close them.
- */
- for (i = 10; i <= max_zsh_fd; i++) {
- if (fdtable[i] & FDT_SAVED_MASK)
- zclose(i);
- }
- if (flags & ESUB_PGRP)
- clearjobtab(monitor);
- get_usage();
- forklevel = locallevel;
-}
-
-/* execute a string */
-
-/**/
-mod_export void
-execstring(char *s, int dont_change_job, int exiting, char *context)
-{
- Eprog prog;
-
- pushheap();
- if (isset(VERBOSE)) {
- zputs(s, stderr);
- fputc('\n', stderr);
- fflush(stderr);
- }
- if ((prog = parse_string(s, 0)))
- execode(prog, dont_change_job, exiting, context);
- popheap();
-}
-
-/**/
-mod_export void
-execode(Eprog p, int dont_change_job, int exiting, char *context)
-{
- struct estate s;
- static int zsh_eval_context_len;
- int alen;
-
- if (!zsh_eval_context_len) {
- zsh_eval_context_len = 16;
- alen = 0;
- zsh_eval_context = (char **)zalloc(zsh_eval_context_len *
- sizeof(*zsh_eval_context));
- } else {
- alen = arrlen(zsh_eval_context);
- if (zsh_eval_context_len == alen + 1) {
- zsh_eval_context_len *= 2;
- zsh_eval_context = zrealloc(zsh_eval_context,
- zsh_eval_context_len *
- sizeof(*zsh_eval_context));
- }
- }
- zsh_eval_context[alen] = context;
- zsh_eval_context[alen+1] = NULL;
-
- s.prog = p;
- s.pc = p->prog;
- s.strs = p->strs;
- useeprog(p); /* Mark as in use */
-
- execlist(&s, dont_change_job, exiting);
-
- freeeprog(p); /* Free if now unused */
-
- /*
- * zsh_eval_context may have been altered by a recursive
- * call, but that's OK since we're using the global value.
- */
- zsh_eval_context[alen] = NULL;
-}
-
-/* Execute a simplified command. This is used to execute things that
- * will run completely in the shell, so that we can by-pass all that
- * nasty job-handling and redirection stuff in execpline and execcmd. */
-
-/**/
-static int
-execsimple(Estate state)
-{
- wordcode code = *state->pc++;
- int lv, otj;
-
- if (errflag)
- return (lastval = 1);
-
- if (!isset(EXECOPT))
- return lastval = 0;
-
- /* In evaluated traps, don't modify the line number. */
- if (!IN_EVAL_TRAP() && !ineval && code)
- lineno = code - 1;
-
- code = wc_code(*state->pc++);
-
- /*
- * Because we're bypassing job control, ensure the called
- * code doesn't see the current job.
- */
- otj = thisjob;
- thisjob = -1;
-
- if (code == WC_ASSIGN) {
- cmdoutval = 0;
- addvars(state, state->pc - 1, 0);
- setunderscore("");
- if (isset(XTRACE)) {
- fputc('\n', xtrerr);
- fflush(xtrerr);
- }
- lv = (errflag ? errflag : cmdoutval);
- } else {
- int q = queue_signal_level();
- dont_queue_signals();
- if (code == WC_FUNCDEF)
- lv = execfuncdef(state, NULL);
- else
- lv = (execfuncs[code - WC_CURSH])(state, 0);
- restore_queue_signals(q);
- }
-
- thisjob = otj;
-
- return lastval = lv;
-}
-
-/* Main routine for executing a list. *
- * exiting means that the (sub)shell we are in is a definite goner *
- * after the current list is finished, so we may be able to exec the *
- * last command directly instead of forking. If dont_change_job is *
- * nonzero, then restore the current job number after executing the *
- * list. */
-
-/**/
-void
-execlist(Estate state, int dont_change_job, int exiting)
-{
- static int donetrap;
- Wordcode next;
- wordcode code;
- int ret, cj, csp, ltype;
- int old_pline_level, old_list_pipe, old_list_pipe_job;
- char *old_list_pipe_text;
- zlong oldlineno;
- /*
- * ERREXIT only forces the shell to exit if the last command in a &&
- * or || fails. This is the case even if an earlier command is a
- * shell function or other current shell structure, so we have to set
- * noerrexit here if the sublist is not of type END.
- */
- int oldnoerrexit = noerrexit;
-
- queue_signals();
-
- cj = thisjob;
- old_pline_level = pline_level;
- old_list_pipe = list_pipe;
- old_list_pipe_job = list_pipe_job;
- if (*list_pipe_text)
- old_list_pipe_text = ztrdup(list_pipe_text);
- else
- old_list_pipe_text = NULL;
- oldlineno = lineno;
-
- if (sourcelevel && unset(SHINSTDIN)) {
- pline_level = list_pipe = list_pipe_job = 0;
- *list_pipe_text = '\0';
- }
-
- /* Loop over all sets of comands separated by newline, *
- * semi-colon or ampersand (`sublists'). */
- code = *state->pc++;
- if (wc_code(code) != WC_LIST) {
- /* Empty list; this returns status zero. */
- lastval = 0;
- }
- while (wc_code(code) == WC_LIST && !breaks && !retflag && !errflag) {
- int donedebug;
- int this_donetrap = 0;
- this_noerrexit = 0;
-
- ltype = WC_LIST_TYPE(code);
- csp = cmdsp;
-
- if (!IN_EVAL_TRAP() && !ineval) {
- /*
- * Ensure we have a valid line number for debugging,
- * unless we are in an evaluated trap in which case
- * we retain the line number from the context.
- * This was added for DEBUGBEFORECMD but I've made
- * it unconditional to keep dependencies to a minimum.
- *
- * The line number is updated for individual pipelines.
- * This isn't necessary for debug traps since they only
- * run once per sublist.
- */
- wordcode code2 = *state->pc, lnp1 = 0;
- if (ltype & Z_SIMPLE) {
- lnp1 = code2;
- } else if (wc_code(code2) == WC_SUBLIST) {
- if (WC_SUBLIST_FLAGS(code2) == WC_SUBLIST_SIMPLE)
- lnp1 = state->pc[1];
- else
- lnp1 = WC_PIPE_LINENO(state->pc[1]);
- }
- if (lnp1)
- lineno = lnp1 - 1;
- }
-
- if (sigtrapped[SIGDEBUG] && isset(DEBUGBEFORECMD) && !intrap) {
- Wordcode pc2 = state->pc;
- int oerrexit_opt = opts[ERREXIT];
- Param pm;
- opts[ERREXIT] = 0;
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
- if (ltype & Z_SIMPLE) /* skip the line number */
- pc2++;
- pm = assignsparam("ZSH_DEBUG_CMD",
- getpermtext(state->prog, pc2, 0),
- 0);
-
- exiting = donetrap;
- ret = lastval;
- dotrap(SIGDEBUG);
- if (!retflag)
- lastval = ret;
- donetrap = exiting;
- noerrexit = oldnoerrexit;
- /*
- * Only execute the trap once per sublist, even
- * if the DEBUGBEFORECMD option changes.
- */
- donedebug = isset(ERREXIT) ? 2 : 1;
- opts[ERREXIT] = oerrexit_opt;
- if (pm)
- unsetparam_pm(pm, 0, 1);
- } else
- donedebug = intrap ? 1 : 0;
-
- /* Reset donetrap: this ensures that a trap is only *
- * called once for each sublist that fails. */
- donetrap = 0;
- if (ltype & Z_SIMPLE) {
- next = state->pc + WC_LIST_SKIP(code);
- if (donedebug != 2)
- execsimple(state);
- state->pc = next;
- goto sublist_done;
- }
-
- /* Loop through code followed by &&, ||, or end of sublist. */
- code = *state->pc++;
- if (donedebug == 2) {
- /* Skip sublist. */
- while (wc_code(code) == WC_SUBLIST) {
- state->pc = state->pc + WC_SUBLIST_SKIP(code);
- if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END)
- break;
- code = *state->pc++;
- }
- donetrap = 1;
- /* yucky but consistent... */
- goto sublist_done;
- }
- while (wc_code(code) == WC_SUBLIST) {
- int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END);
- next = state->pc + WC_SUBLIST_SKIP(code);
- if (!oldnoerrexit)
- noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN;
- if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) {
- /* suppress errexit for "! this_command" */
- if (isend)
- this_noerrexit = 1;
- /* suppress errexit for ! <list-of-shell-commands> */
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
- }
- switch (WC_SUBLIST_TYPE(code)) {
- case WC_SUBLIST_END:
- /* End of sublist; just execute, ignoring status. */
- if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE)
- execsimple(state);
- else
- execpline(state, code, ltype, (ltype & Z_END) && exiting);
- state->pc = next;
- goto sublist_done;
- break;
- case WC_SUBLIST_AND:
- /* If the return code is non-zero, we skip pipelines until *
- * we find a sublist followed by ORNEXT. */
- if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
- execsimple(state) :
- execpline(state, code, Z_SYNC, 0)))) {
- state->pc = next;
- code = *state->pc++;
- next = state->pc + WC_SUBLIST_SKIP(code);
- while (wc_code(code) == WC_SUBLIST &&
- WC_SUBLIST_TYPE(code) == WC_SUBLIST_AND) {
- state->pc = next;
- code = *state->pc++;
- next = state->pc + WC_SUBLIST_SKIP(code);
- }
- if (wc_code(code) != WC_SUBLIST) {
- /* We've skipped to the end of the list, not executing *
- * the final pipeline, so don't perform error handling *
- * for this sublist. */
- this_donetrap = 1;
- goto sublist_done;
- } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
- this_donetrap = 1;
- /*
- * Treat this in the same way as if we reached
- * the end of the sublist normally.
- */
- state->pc = next;
- goto sublist_done;
- }
- }
- cmdpush(CS_CMDAND);
- break;
- case WC_SUBLIST_OR:
- /* If the return code is zero, we skip pipelines until *
- * we find a sublist followed by ANDNEXT. */
- if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
- execsimple(state) :
- execpline(state, code, Z_SYNC, 0)))) {
- state->pc = next;
- code = *state->pc++;
- next = state->pc + WC_SUBLIST_SKIP(code);
- while (wc_code(code) == WC_SUBLIST &&
- WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) {
- state->pc = next;
- code = *state->pc++;
- next = state->pc + WC_SUBLIST_SKIP(code);
- }
- if (wc_code(code) != WC_SUBLIST) {
- /* We've skipped to the end of the list, not executing *
- * the final pipeline, so don't perform error handling *
- * for this sublist. */
- this_donetrap = 1;
- goto sublist_done;
- } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
- this_donetrap = 1;
- /*
- * Treat this in the same way as if we reached
- * the end of the sublist normally.
- */
- state->pc = next;
- goto sublist_done;
- }
- }
- cmdpush(CS_CMDOR);
- break;
- }
- state->pc = next;
- code = *state->pc++;
- }
- state->pc--;
-sublist_done:
-
- /*
- * See hairy code near the end of execif() for the
- * following. "noerrexit " only applies until
- * we hit execcmd on the way down. We're now
- * on the way back up, so don't restore it.
- */
- if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC))
- noerrexit = oldnoerrexit;
-
- if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) {
- /*
- * Save and restore ERREXIT for consistency with
- * DEBUGBEFORECMD, even though it's not used.
- */
- int oerrexit_opt = opts[ERREXIT];
- opts[ERREXIT] = 0;
- noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN;
- exiting = donetrap;
- ret = lastval;
- dotrap(SIGDEBUG);
- if (!retflag)
- lastval = ret;
- donetrap = exiting;
- noerrexit = oldnoerrexit;
- opts[ERREXIT] = oerrexit_opt;
- }
-
- cmdsp = csp;
-
- /* Check whether we are suppressing traps/errexit *
- * (typically in init scripts) and if we haven't *
- * already performed them for this sublist. */
- if (!this_noerrexit && !donetrap && !this_donetrap) {
- if (sigtrapped[SIGZERR] && lastval &&
- !(noerrexit & NOERREXIT_EXIT)) {
- dotrap(SIGZERR);
- donetrap = 1;
- }
- if (lastval) {
- int errreturn = isset(ERRRETURN) &&
- (isset(INTERACTIVE) || locallevel || sourcelevel) &&
- !(noerrexit & NOERREXIT_RETURN);
- int errexit = (isset(ERREXIT) ||
- (isset(ERRRETURN) && !errreturn)) &&
- !(noerrexit & NOERREXIT_EXIT);
- if (errexit) {
- if (sigtrapped[SIGEXIT])
- dotrap(SIGEXIT);
- if (mypid != getpid())
- _exit(lastval);
- else
- exit(lastval);
- }
- if (errreturn) {
- retflag = 1;
- breaks = loops;
- }
- }
- }
- if (ltype & Z_END)
- break;
- code = *state->pc++;
- }
- pline_level = old_pline_level;
- list_pipe = old_list_pipe;
- list_pipe_job = old_list_pipe_job;
- if (old_list_pipe_text) {
- strcpy(list_pipe_text, old_list_pipe_text);
- zsfree(old_list_pipe_text);
- } else {
- *list_pipe_text = '\0';
- }
- lineno = oldlineno;
- if (dont_change_job)
- thisjob = cj;
-
- if (exiting && sigtrapped[SIGEXIT]) {
- dotrap(SIGEXIT);
- /* Make sure this doesn't get executed again. */
- sigtrapped[SIGEXIT] = 0;
- }
-
- unqueue_signals();
-}
-
-/* Execute a pipeline. *
- * last1 is a flag that this command is the last command in a shell *
- * that is about to exit, so we can exec instead of forking. It gets *
- * passed all the way down to execcmd() which actually makes the *
- * decision. A 0 is always passed if the command is not the last in *
- * the pipeline. This function assumes that the sublist is not NULL. *
- * If last1 is zero but the command is at the end of a pipeline, we *
- * pass 2 down to execcmd(). *
- */
-
-/**/
-static int
-execpline(Estate state, wordcode slcode, int how, int last1)
-{
- int ipipe[2], opipe[2];
- int pj, newjob;
- int old_simple_pline = simple_pline;
- int slflags = WC_SUBLIST_FLAGS(slcode);
- wordcode code = *state->pc++;
- static int lastwj, lpforked;
-
- if (wc_code(code) != WC_PIPE)
- return lastval = (slflags & WC_SUBLIST_NOT) != 0;
- else if (slflags & WC_SUBLIST_NOT)
- last1 = 0;
-
- /* If trap handlers are allowed to run here, they may start another
- * external job in the middle of us starting this one, which can
- * result in jobs being reaped before their job table entries have
- * been initialized, which in turn leads to waiting forever for
- * jobs that no longer exist. So don't do that.
- */
- queue_signals();
-
- pj = thisjob;
- ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
- child_block();
-
- /*
- * Get free entry in job table and initialize it. This is currently
- * the only call to initjob() (apart from a minor exception in
- * clearjobtab()), so this is also the only place where we can
- * expand the job table under us.
- */
- if ((thisjob = newjob = initjob()) == -1) {
- child_unblock();
- unqueue_signals();
- return 1;
- }
- if (how & Z_TIMED)
- jobtab[thisjob].stat |= STAT_TIMED;
-
- if (slflags & WC_SUBLIST_COPROC) {
- how = Z_ASYNC;
- if (coprocin >= 0) {
- zclose(coprocin);
- zclose(coprocout);
- }
- if (mpipe(ipipe) < 0) {
- coprocin = coprocout = -1;
- slflags &= ~WC_SUBLIST_COPROC;
- } else if (mpipe(opipe) < 0) {
- close(ipipe[0]);
- close(ipipe[1]);
- coprocin = coprocout = -1;
- slflags &= ~WC_SUBLIST_COPROC;
- } else {
- coprocin = ipipe[0];
- coprocout = opipe[1];
- fdtable[coprocin] = fdtable[coprocout] = FDT_UNUSED;
- }
- }
- /* This used to set list_pipe_pid=0 unconditionally, but in things
- * like `ls|if true; then sleep 20; cat; fi' where the sleep was
- * stopped, the top-level execpline() didn't get the pid for the
- * sub-shell because it was overwritten. */
- if (!pline_level++) {
- list_pipe_pid = 0;
- nowait = 0;
- simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END);
- list_pipe_job = newjob;
- }
- lastwj = lpforked = 0;
- execpline2(state, code, how, opipe[0], ipipe[1], last1);
- pline_level--;
- if (how & Z_ASYNC) {
- lastwj = newjob;
-
- if (thisjob == list_pipe_job)
- list_pipe_job = 0;
- jobtab[thisjob].stat |= STAT_NOSTTY;
- if (slflags & WC_SUBLIST_COPROC) {
- zclose(ipipe[1]);
- zclose(opipe[0]);
- }
- if (how & Z_DISOWN) {
- pipecleanfilelist(jobtab[thisjob].filelist, 0);
- deletejob(jobtab + thisjob, 1);
- thisjob = -1;
- }
- else
- spawnjob();
- child_unblock();
- unqueue_signals();
- /* Executing background code resets shell status */
- return lastval = 0;
- } else {
- if (newjob != lastwj) {
- Job jn = jobtab + newjob;
- int updated;
-
- if (newjob == list_pipe_job && list_pipe_child)
- _exit(0);
-
- lastwj = thisjob = newjob;
-
- if (list_pipe || (pline_level && !(how & Z_TIMED)))
- jn->stat |= STAT_NOPRINT;
-
- if (nowait) {
- if(!pline_level) {
- int jobsub;
- struct process *pn, *qn;
-
- curjob = newjob;
- DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
- addproc(list_pipe_pid, list_pipe_text, 0,
- &list_pipe_start);
-
- /* If the super-job contains only the sub-shell, the
- sub-shell is the group leader. */
- if (!jn->procs->next || lpforked == 2) {
- jn->gleader = list_pipe_pid;
- jn->stat |= STAT_SUBLEADER;
- /*
- * Pick up any subjob that's still lying around
- * as it's now our responsibility.
- * If we find it we're a SUPERJOB.
- */
- for (jobsub = 1; jobsub <= maxjob; jobsub++) {
- Job jnsub = jobtab + jobsub;
- if (jnsub->stat & STAT_SUBJOB_ORPHANED) {
- jn->other = jobsub;
- jn->stat |= STAT_SUPERJOB;
- jnsub->stat &= ~STAT_SUBJOB_ORPHANED;
- jnsub->other = list_pipe_pid;
- }
- }
- }
- for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
- if (WIFSTOPPED(pn->status))
- break;
-
- if (pn) {
- for (qn = jn->procs; qn->next; qn = qn->next);
- qn->status = pn->status;
- }
-
- jn->stat &= ~(STAT_DONE | STAT_NOPRINT);
- jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED |
- STAT_INUSE;
- printjob(jn, !!isset(LONGLISTJOBS), 1);
- }
- else if (newjob != list_pipe_job)
- deletejob(jn, 0);
- else
- lastwj = -1;
- }
-
- errbrk_saved = 0;
- for (; !nowait;) {
- if (list_pipe_child) {
- jn->stat |= STAT_NOPRINT;
- makerunning(jn);
- }
- if (!(jn->stat & STAT_LOCKED)) {
- updated = hasprocs(thisjob);
- waitjobs(); /* deals with signal queue */
- child_block();
- } else
- updated = 0;
- if (!updated &&
- list_pipe_job && hasprocs(list_pipe_job) &&
- !(jobtab[list_pipe_job].stat & STAT_STOPPED)) {
- int q = queue_signal_level();
- child_unblock();
- child_block();
- dont_queue_signals();
- restore_queue_signals(q);
- }
- if (list_pipe_child &&
- jn->stat & STAT_DONE &&
- lastval2 & 0200)
- killpg(mypgrp, lastval2 & ~0200);
- if (!list_pipe_child && !lpforked && !subsh && jobbing &&
- (list_pipe || last1 || pline_level) &&
- ((jn->stat & STAT_STOPPED) ||
- (list_pipe_job && pline_level &&
- (jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
- pid_t pid = 0;
- int synch[2];
- struct timeval bgtime;
-
- /*
- * A pipeline with the shell handling the right
- * hand side was stopped. We'll fork to allow
- * it to continue.
- */
- if (pipe(synch) < 0 || (pid = zfork(&bgtime)) == -1) {
- /* Failure */
- if (pid < 0) {
- close(synch[0]);
- close(synch[1]);
- } else
- zerr("pipe failed: %e", errno);
- zleentry(ZLE_CMD_TRASH);
- fprintf(stderr, "zsh: job can't be suspended\n");
- fflush(stderr);
- makerunning(jn);
- killjb(jn, SIGCONT);
- thisjob = newjob;
- }
- else if (pid) {
- /*
- * Parent: job control is here. If the job
- * started for the RHS of the pipeline is still
- * around, then its a SUBJOB and the job for
- * earlier parts of the pipeeline is its SUPERJOB.
- * The newly forked shell isn't recorded as a
- * separate job here, just as list_pipe_pid.
- * If the superjob exits (it may already have
- * done so, see child branch below), we'll use
- * list_pipe_pid to form the basis of a
- * replacement job --- see SUBLEADER code above.
- */
- char dummy;
-
- lpforked =
- (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
- list_pipe_pid = pid;
- list_pipe_start = bgtime;
- nowait = 1;
- errflag |= ERRFLAG_ERROR;
- breaks = loops;
- close(synch[1]);
- read_loop(synch[0], &dummy, 1);
- close(synch[0]);
- /* If this job has finished, we leave it as a
- * normal (non-super-) job. */
- if (!(jn->stat & STAT_DONE)) {
- jobtab[list_pipe_job].other = newjob;
- jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
- jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
- jn->other = list_pipe_pid; /* see zsh.h */
- if (hasprocs(list_pipe_job))
- jn->gleader = jobtab[list_pipe_job].gleader;
- }
- if ((list_pipe || last1) && hasprocs(list_pipe_job))
- killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
- break;
- }
- else {
- close(synch[0]);
- entersubsh(ESUB_ASYNC);
- /*
- * At this point, we used to attach this process
- * to the process group of list_pipe_job (the
- * new superjob) any time that was still available.
- * That caused problems in at least two
- * cases because this forked shell was then
- * suspended with the right hand side of the
- * pipeline, and the SIGSTOP below suspended
- * it a second time when it was continued.
- *
- * It's therefore not clear entirely why you'd ever
- * do anything other than the following, but no
- * doubt we'll find out...
- */
- setpgrp(0L, mypgrp = getpid());
- close(synch[1]);
- kill(getpid(), SIGSTOP);
- list_pipe = 0;
- list_pipe_child = 1;
- opts[INTERACTIVE] = 0;
- if (errbrk_saved) {
- /*
- * Keep any user interrupt bit in errflag.
- */
- errflag = prev_errflag | (errflag & ERRFLAG_INT);
- breaks = prev_breaks;
- }
- break;
- }
- }
- else if (subsh && jn->stat & STAT_STOPPED)
- thisjob = newjob;
- else
- break;
- }
- child_unblock();
- unqueue_signals();
-
- if (list_pipe && (lastval & 0200) && pj >= 0 &&
- (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
- deletejob(jn, 0);
- jn = jobtab + pj;
- if (jn->gleader)
- killjb(jn, lastval & ~0200);
- }
- if (list_pipe_child ||
- ((jn->stat & STAT_DONE) &&
- (list_pipe || (pline_level && !(jn->stat & STAT_SUBJOB)))))
- deletejob(jn, 0);
- thisjob = pj;
- }
- else
- unqueue_signals();
- if ((slflags & WC_SUBLIST_NOT) && !errflag)
- lastval = !lastval;
- }
- if (!pline_level)
- simple_pline = old_simple_pline;
- return lastval;
-}
-
-/* execute pipeline. This function assumes the `pline' is not NULL. */
-
-/**/
-static void
-execpline2(Estate state, wordcode pcode,
- int how, int input, int output, int last1)
-{
- struct execcmd_params eparams;
-
- if (breaks || retflag)
- return;
-
- /* In evaluated traps, don't modify the line number. */
- if (!IN_EVAL_TRAP() && !ineval && WC_PIPE_LINENO(pcode))
- lineno = WC_PIPE_LINENO(pcode) - 1;
-
- if (pline_level == 1) {
- if ((how & Z_ASYNC) || !sfcontext)
- strcpy(list_pipe_text,
- getjobtext(state->prog,
- state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ?
- 0 : 1)));
- else
- list_pipe_text[0] = '\0';
- }
- if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) {
- execcmd_analyse(state, &eparams);
- execcmd_exec(state, &eparams, input, output, how, last1 ? 1 : 2, -1);
- } else {
- int pipes[2];
- int old_list_pipe = list_pipe;
- Wordcode next = state->pc + (*state->pc);
-
- ++state->pc;
- execcmd_analyse(state, &eparams);
-
- if (mpipe(pipes) < 0) {
- /* FIXME */
- }
-
- addfilelist(NULL, pipes[0]);
- execcmd_exec(state, &eparams, input, pipes[1], how, 0, pipes[0]);
- zclose(pipes[1]);
- state->pc = next;
-
- /* if another execpline() is invoked because the command is *
- * a list it must know that we're already in a pipeline */
- cmdpush(CS_PIPE);
- list_pipe = 1;
- execpline2(state, *state->pc++, how, pipes[0], output, last1);
- list_pipe = old_list_pipe;
- cmdpop();
- }
-}
-
-/* make the argv array */
-
-/**/
-static char **
-makecline(LinkList list)
-{
- LinkNode node;
- char **argv, **ptr;
-
- /* A bigger argv is necessary for executing scripts */
- ptr = argv = 2 + (char **) hcalloc((countlinknodes(list) + 4) *
- sizeof(char *));
-
- if (isset(XTRACE)) {
- if (!doneps4)
- printprompt4();
-
- for (node = firstnode(list); node; incnode(node)) {
- *ptr++ = (char *)getdata(node);
- quotedzputs(getdata(node), xtrerr);
- if (nextnode(node))
- fputc(' ', xtrerr);
- }
- fputc('\n', xtrerr);
- fflush(xtrerr);
- } else {
- for (node = firstnode(list); node; incnode(node))
- *ptr++ = (char *)getdata(node);
- }
- *ptr = NULL;
- return (argv);
-}
-
-/**/
-mod_export void
-untokenize(char *s)
-{
- if (*s) {
- int c;
-
- while ((c = *s++))
- if (itok(c)) {
- char *p = s - 1;
-
- if (c != Nularg)
- *p++ = ztokens[c - Pound];
-
- while ((c = *s++)) {
- if (itok(c)) {
- if (c != Nularg)
- *p++ = ztokens[c - Pound];
- } else
- *p++ = c;
- }
- *p = '\0';
- break;
- }
- }
-}
-
-
-/*
- * Given a tokenized string, output it to standard output in
- * such a way that it's clear which tokens are active.
- * Hence Star becomes an unquoted "*", while a "*" becomes "\*".
- *
- * The code here is a kind of amalgamation of the tests in
- * zshtokenize() and untokenize() with some outputting.
- */
-
-/**/
-void
-quote_tokenized_output(char *str, FILE *file)
-{
- char *s = str;
-
- for (; *s; s++) {
- switch (*s) {
- case Meta:
- putc(*++s ^ 32, file);
- continue;
-
- case Nularg:
- /* Do nothing. I think. */
- continue;
-
- case '\\':
- case '<':
- case '>':
- case '(':
- case '|':
- case ')':
- case '^':
- case '#':
- case '~':
- case '[':
- case ']':
- case '*':
- case '?':
- case '$':
- case ' ':
- putc('\\', file);
- break;
-
- case '\t':
- fputs("$'\\t'", file);
- continue;
-
- case '\n':
- fputs("$'\\n'", file);
- continue;
-
- case '\r':
- fputs("$'\\r'", file);
- continue;
-
- case '=':
- if (s == str)
- putc('\\', file);
- break;
-
- default:
- if (itok(*s)) {
- putc(ztokens[*s - Pound], file);
- continue;
- }
- break;
- }
-
- putc(*s, file);
- }
-}
-
-/* Check that we can use a parameter for allocating a file descriptor. */
-
-static int
-checkclobberparam(struct redir *f)
-{
- struct value vbuf;
- Value v;
- char *s = f->varid;
- int fd;
-
- if (!s)
- return 1;
-
- if (!(v = getvalue(&vbuf, &s, 0)))
- return 1;
-
- if (v->pm->node.flags & PM_READONLY) {
- zwarn("can't allocate file descriptor to readonly parameter %s",
- f->varid);
- /* don't flag a system error for this */
- errno = 0;
- return 0;
- }
-
- /*
- * We can't clobber the value in the parameter if it's
- * already an opened file descriptor --- that means it's a decimal
- * integer corresponding to an opened file descriptor,
- * not merely an expression that evaluates to a file descriptor.
- */
- if (!isset(CLOBBER) && (s = getstrvalue(v)) &&
- (fd = (int)zstrtol(s, &s, 10)) >= 0 && !*s &&
- fd <= max_zsh_fd && fdtable[fd] == FDT_EXTERNAL) {
- zwarn("can't clobber parameter %s containing file descriptor %d",
- f->varid, fd);
- /* don't flag a system error for this */
- errno = 0;
- return 0;
- }
- return 1;
-}
-
-/* Open a file for writing redirection */
-
-/**/
-static int
-clobber_open(struct redir *f)
-{
- struct stat buf;
- int fd, oerrno;
-
- /* If clobbering, just open. */
- if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type))
- return open(unmeta(f->name),
- O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666);
-
- /* If not clobbering, attempt to create file exclusively. */
- if ((fd = open(unmeta(f->name),
- O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0)
- return fd;
-
- /* If that fails, we are still allowed to open non-regular files. *
- * Try opening, and if it's a regular file then close it again *
- * because we weren't supposed to open it. */
- oerrno = errno;
- if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) {
- if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode))
- return fd;
- close(fd);
- }
- errno = oerrno;
- return -1;
-}
-
-/* size of buffer for tee and cat processes */
-#define TCBUFSIZE 4092
-
-/* close an multio (success) */
-
-/**/
-static void
-closemn(struct multio **mfds, int fd, int type)
-{
- if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) {
- struct multio *mn = mfds[fd];
- char buf[TCBUFSIZE];
- int len, i;
- pid_t pid;
- struct timeval bgtime;
-
- /*
- * We need to block SIGCHLD in case the process
- * we are spawning terminates before the job table
- * is set up to handle it.
- */
- child_block();
- if ((pid = zfork(&bgtime))) {
- for (i = 0; i < mn->ct; i++)
- zclose(mn->fds[i]);
- zclose(mn->pipe);
- if (pid == -1) {
- mfds[fd] = NULL;
- child_unblock();
- return;
- }
- mn->ct = 1;
- mn->fds[0] = fd;
- addproc(pid, NULL, 1, &bgtime);
- child_unblock();
- return;
- }
- /* pid == 0 */
- child_unblock();
- closeallelse(mn);
- if (mn->rflag) {
- /* tee process */
- while ((len = read(mn->pipe, buf, TCBUFSIZE)) != 0) {
- if (len < 0) {
- if (errno == EINTR)
- continue;
- else
- break;
- }
- for (i = 0; i < mn->ct; i++)
- write_loop(mn->fds[i], buf, len);
- }
- } else {
- /* cat process */
- for (i = 0; i < mn->ct; i++)
- while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) {
- if (len < 0) {
- if (errno == EINTR)
- continue;
- else
- break;
- }
- write_loop(mn->pipe, buf, len);
- }
- }
- _exit(0);
- } else if (fd >= 0 && type == REDIR_CLOSE)
- mfds[fd] = NULL;
-}
-
-/* close all the mnodes (failure) */
-
-/**/
-static void
-closemnodes(struct multio **mfds)
-{
- int i, j;
-
- for (i = 0; i < 10; i++)
- if (mfds[i]) {
- for (j = 0; j < mfds[i]->ct; j++)
- zclose(mfds[i]->fds[j]);
- mfds[i] = NULL;
- }
-}
-
-/**/
-static void
-closeallelse(struct multio *mn)
-{
- int i, j;
- long openmax;
-
- openmax = fdtable_size;
-
- for (i = 0; i < openmax; i++)
- if (mn->pipe != i) {
- for (j = 0; j < mn->ct; j++)
- if (mn->fds[j] == i)
- break;
- if (j == mn->ct)
- zclose(i);
- }
-}
-
-/*
- * A multio is a list of fds associated with a certain fd.
- * Thus if you do "foo >bar >ble", the multio for fd 1 will have
- * two fds, the result of open("bar",...), and the result of
- * open("ble",....).
- */
-
-/*
- * Add a fd to an multio. fd1 must be < 10, and may be in any state.
- * fd2 must be open, and is `consumed' by this function. Note that
- * fd1 == fd2 is possible, and indicates that fd1 was really closed.
- * We effectively do `fd2 = movefd(fd2)' at the beginning of this
- * function, but in most cases we can avoid an extra dup by delaying
- * the movefd: we only >need< to move it if we're actually doing a
- * multiple redirection.
- *
- * If varid is not NULL, we open an fd above 10 and set the parameter
- * named varid to that value. fd1 is not used.
- */
-
-/**/
-static void
-addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag,
- char *varid)
-{
- int pipes[2];
-
- if (varid) {
- /* fd will be over 10, don't touch mfds */
- fd1 = movefd(fd2);
- if (fd1 == -1) {
- zerr("cannot moved fd %d: %e", fd2, errno);
- return;
- } else {
- fdtable[fd1] = FDT_EXTERNAL;
- setiparam(varid, (zlong)fd1);
- /*
- * If setting the parameter failed, close the fd else
- * it will leak.
- */
- if (errflag)
- zclose(fd1);
- }
- } else if (!mfds[fd1] || unset(MULTIOS)) {
- if(!mfds[fd1]) { /* starting a new multio */
- mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio));
- if (!forked && save[fd1] == -2) {
- if (fd1 == fd2)
- save[fd1] = -1;
- else {
- int fdN = movefd(fd1);
- /*
- * fd1 may already be closed here, so
- * ignore bad file descriptor error
- */
- if (fdN < 0) {
- if (errno != EBADF) {
- zerr("cannot duplicate fd %d: %e", fd1, errno);
- mfds[fd1] = NULL;
- closemnodes(mfds);
- return;
- }
- } else {
- DPUTS(fdtable[fdN] != FDT_INTERNAL,
- "Saved file descriptor not marked as internal");
- fdtable[fdN] |= FDT_SAVED_MASK;
- }
- save[fd1] = fdN;
- }
- }
- }
- if (!varid)
- redup(fd2, fd1);
- mfds[fd1]->ct = 1;
- mfds[fd1]->fds[0] = fd1;
- mfds[fd1]->rflag = rflag;
- } else {
- if (mfds[fd1]->rflag != rflag) {
- zerr("file mode mismatch on fd %d", fd1);
- closemnodes(mfds);
- return;
- }
- if (mfds[fd1]->ct == 1) { /* split the stream */
- int fdN = movefd(fd1);
- if (fdN < 0) {
- zerr("multio failed for fd %d: %e", fd1, errno);
- closemnodes(mfds);
- return;
- }
- mfds[fd1]->fds[0] = fdN;
- fdN = movefd(fd2);
- if (fdN < 0) {
- zerr("multio failed for fd %d: %e", fd2, errno);
- closemnodes(mfds);
- return;
- }
- mfds[fd1]->fds[1] = fdN;
- if (mpipe(pipes) < 0) {
- zerr("multio failed for fd %d: %e", fd2, errno);
- closemnodes(mfds);
- return;
- }
- mfds[fd1]->pipe = pipes[1 - rflag];
- redup(pipes[rflag], fd1);
- mfds[fd1]->ct = 2;
- } else { /* add another fd to an already split stream */
- int fdN;
- if(!(mfds[fd1]->ct % MULTIOUNIT)) {
- int new = sizeof(struct multio) + sizeof(int) * mfds[fd1]->ct;
- int old = new - sizeof(int) * MULTIOUNIT;
- mfds[fd1] = hrealloc((char *)mfds[fd1], old, new);
- }
- if ((fdN = movefd(fd2)) < 0) {
- zerr("multio failed for fd %d: %e", fd2, errno);
- closemnodes(mfds);
- return;
- }
- mfds[fd1]->fds[mfds[fd1]->ct++] = fdN;
- }
- }
-}
-
-/**/
-static void
-addvars(Estate state, Wordcode pc, int addflags)
-{
- LinkList vl;
- int xtr, isstr, htok = 0;
- char **arr, **ptr, *name;
- int flags;
-
- Wordcode opc = state->pc;
- wordcode ac;
- local_list1(svl);
-
- /*
- * Warn when creating a global without using typeset -g in a
- * function. Don't do this if there is a list of variables marked
- * to be restored after the command, since then the assignment
- * is implicitly scoped.
- */
- flags = !(addflags & ADDVAR_RESTORE) ? ASSPM_WARN : 0;
- xtr = isset(XTRACE);
- if (xtr) {
- printprompt4();
- doneps4 = 1;
- }
- state->pc = pc;
- while (wc_code(ac = *state->pc++) == WC_ASSIGN) {
- int myflags = flags;
- name = ecgetstr(state, EC_DUPTOK, &htok);
- if (htok)
- untokenize(name);
- if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC)
- myflags |= ASSPM_AUGMENT;
- if (xtr)
- fprintf(xtrerr,
- WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC ? "%s+=" : "%s=", name);
- if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) {
- init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok));
- vl = &svl;
- } else {
- vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok);
- if (errflag) {
- state->pc = opc;
- return;
- }
- }
-
- if (vl && htok) {
- int prefork_ret = 0;
- prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) :
- PREFORK_ASSIGN), &prefork_ret);
- if (errflag) {
- state->pc = opc;
- return;
- }
- if (prefork_ret & PREFORK_KEY_VALUE)
- myflags |= ASSPM_KEY_VALUE;
- if (!isstr || (isset(GLOBASSIGN) && isstr &&
- haswilds((char *)getdata(firstnode(vl))))) {
- globlist(vl, prefork_ret);
- /* Unset the parameter to force it to be recreated
- * as either scalar or array depending on how many
- * matches were found for the glob.
- */
- if (isset(GLOBASSIGN) && isstr)
- unsetparam(name);
- if (errflag) {
- state->pc = opc;
- return;
- }
- }
- }
- if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) {
- Param pm;
- char *val;
- int allexp;
-
- if (empty(vl))
- val = ztrdup("");
- else {
- untokenize(peekfirst(vl));
- val = ztrdup(ugetnode(vl));
- }
- if (xtr) {
- quotedzputs(val, xtrerr);
- fputc(' ', xtrerr);
- }
- if ((addflags & ADDVAR_EXPORT) && !strchr(name, '[')) {
- if ((addflags & ADDVAR_RESTRICT) && isset(RESTRICTED) &&
- (pm = (Param) paramtab->removenode(paramtab, name)) &&
- (pm->node.flags & PM_RESTRICTED)) {
- zerr("%s: restricted", pm->node.nam);
- zsfree(val);
- state->pc = opc;
- return;
- }
- if (strcmp(name, "STTY") == 0) {
- zsfree(STTYval);
- STTYval = ztrdup(val);
- }
- allexp = opts[ALLEXPORT];
- opts[ALLEXPORT] = 1;
- if (isset(KSHARRAYS))
- unsetparam(name);
- pm = assignsparam(name, val, myflags);
- opts[ALLEXPORT] = allexp;
- } else
- pm = assignsparam(name, val, myflags);
- if (errflag) {
- state->pc = opc;
- return;
- }
- continue;
- }
- if (vl) {
- ptr = arr = (char **) zalloc(sizeof(char *) *
- (countlinknodes(vl) + 1));
-
- while (nonempty(vl))
- *ptr++ = ztrdup((char *) ugetnode(vl));
- } else
- ptr = arr = (char **) zalloc(sizeof(char *));
-
- *ptr = NULL;
- if (xtr) {
- fprintf(xtrerr, "( ");
- for (ptr = arr; *ptr; ptr++) {
- quotedzputs(*ptr, xtrerr);
- fputc(' ', xtrerr);
- }
- fprintf(xtrerr, ") ");
- }
- assignaparam(name, arr, myflags);
- if (errflag) {
- state->pc = opc;
- return;
- }
- }
- state->pc = opc;
-}
-
-/**/
-void
-setunderscore(char *str)
-{
- queue_signals();
- if (str && *str) {
- int l = strlen(str) + 1, nl = (l + 31) & ~31;
-
- if (nl > underscorelen || (underscorelen - nl) > 64) {
- zfree(zunderscore, underscorelen);
- zunderscore = (char *) zalloc(underscorelen = nl);
- }
- strcpy(zunderscore, str);
- underscoreused = l;
- } else {
- if (underscorelen > 128) {
- zfree(zunderscore, underscorelen);
- zunderscore = (char *) zalloc(underscorelen = 32);
- }
- *zunderscore = '\0';
- underscoreused = 1;
- }
- unqueue_signals();
-}
-
-/* These describe the type of expansions that need to be done on the words
- * used in the thing we are about to execute. They are set in execcmd() and
- * used in execsubst() which might be called from one of the functions
- * called from execcmd() (like execfor() and so on). */
-
-static int esprefork, esglob = 1;
-
-/**/
-void
-execsubst(LinkList strs)
-{
- if (strs) {
- prefork(strs, esprefork, NULL);
- if (esglob && !errflag) {
- LinkList ostrs = strs;
- globlist(strs, 0);
- strs = ostrs;
- }
- }
-}
-
-/*
- * Check if a builtin requires an autoload and if so
- * deal with it. This may return NULL.
- */
-
-/**/
-static HashNode
-resolvebuiltin(const char *cmdarg, HashNode hn)
-{
- if (!((Builtin) hn)->handlerfunc) {
- char *modname = dupstring(((Builtin) hn)->optstr);
- /*
- * Ensure the module is loaded and the
- * feature corresponding to the builtin
- * is enabled.
- */
- (void)ensurefeature(modname, "b:",
- (hn->flags & BINF_AUTOALL) ? NULL :
- hn->nam);
- hn = builtintab->getnode(builtintab, cmdarg);
- if (!hn) {
- lastval = 1;
- zerr("autoloading module %s failed to define builtin: %s",
- modname, cmdarg);
- return NULL;
- }
- }
- return hn;
-}
-
-/*
- * We are about to execute a command at the lowest level of the
- * hierarchy. Analyse the parameters from the wordcode.
- */
-
-/**/
-static void
-execcmd_analyse(Estate state, Execcmd_params eparams)
-{
- wordcode code;
- int i;
-
- eparams->beg = state->pc;
- eparams->redir =
- (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
- if (wc_code(*state->pc) == WC_ASSIGN) {
- cmdoutval = 0;
- eparams->varspc = state->pc;
- while (wc_code((code = *state->pc)) == WC_ASSIGN)
- state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
- 3 : WC_ASSIGN_NUM(code) + 2);
- } else
- eparams->varspc = NULL;
-
- code = *state->pc++;
-
- eparams->type = wc_code(code);
- eparams->postassigns = 0;
-
- /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
- * But for that we would need to check/change all builtins so that
- * they don't modify their argument strings. */
- switch (eparams->type) {
- case WC_SIMPLE:
- eparams->args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP,
- &eparams->htok);
- eparams->assignspc = NULL;
- break;
-
- case WC_TYPESET:
- eparams->args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP,
- &eparams->htok);
- eparams->postassigns = *state->pc++;
- eparams->assignspc = state->pc;
- for (i = 0; i < eparams->postassigns; i++) {
- code = *state->pc;
- DPUTS(wc_code(code) != WC_ASSIGN,
- "BUG: miscounted typeset assignments");
- state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
- 3 : WC_ASSIGN_NUM(code) + 2);
- }
- break;
-
- default:
- eparams->args = NULL;
- eparams->assignspc = NULL;
- eparams->htok = 0;
- break;
- }
-}
-
-/*
- * Transfer the first node of args to preargs, performing
- * prefork expansion on the way if necessary.
- */
-static void execcmd_getargs(LinkList preargs, LinkList args, int expand)
-{
- if (!firstnode(args)) {
- return;
- } else if (expand) {
- local_list0(svl);
- init_list0(svl);
- /* not init_list1, as we need real nodes */
- addlinknode(&svl, uremnode(args, firstnode(args)));
- /* Analysing commands, so vanilla options to prefork */
- prefork(&svl, 0, NULL);
- joinlists(preargs, &svl);
- } else {
- addlinknode(preargs, uremnode(args, firstnode(args)));
- }
-}
-
-/**/
-static int
-execcmd_fork(Estate state, int how, int type, Wordcode varspc,
- LinkList *filelistp, char *text, int oautocont,
- int close_if_forked)
-{
- pid_t pid;
- int synch[2], flags;
- char dummy;
- struct timeval bgtime;
-
- child_block();
-
- if (pipe(synch) < 0) {
- zerr("pipe failed: %e", errno);
- return -1;
- } else if ((pid = zfork(&bgtime)) == -1) {
- close(synch[0]);
- close(synch[1]);
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- return -1;
- }
- if (pid) {
- close(synch[1]);
- read_loop(synch[0], &dummy, 1);
- close(synch[0]);
- if (how & Z_ASYNC) {
- lastpid = (zlong) pid;
- } else if (!jobtab[thisjob].stty_in_env && varspc) {
- /* search for STTY=... */
- Wordcode p = varspc;
- wordcode ac;
-
- while (wc_code(ac = *p) == WC_ASSIGN) {
- if (!strcmp(ecrawstr(state->prog, p + 1, NULL), "STTY")) {
- jobtab[thisjob].stty_in_env = 1;
- break;
- }
- p += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
- 3 : WC_ASSIGN_NUM(ac) + 2);
- }
- }
- addproc(pid, text, 0, &bgtime);
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
- pipecleanfilelist(jobtab[thisjob].filelist, 1);
- return pid;
- }
-
- /* pid == 0 */
- close(synch[0]);
- flags = ((how & Z_ASYNC) ? ESUB_ASYNC : 0) | ESUB_PGRP;
- if ((type != WC_SUBSH) && !(how & Z_ASYNC))
- flags |= ESUB_KEEPTRAP;
- if (type == WC_SUBSH && !(how & Z_ASYNC))
- flags |= ESUB_JOB_CONTROL;
- *filelistp = jobtab[thisjob].filelist;
- entersubsh(flags);
- close(synch[1]);
- zclose(close_if_forked);
-
- if (sigtrapped[SIGINT] & ZSIG_IGNORED)
- holdintr();
- /*
- * EXIT traps shouldn't be called even if we forked to run
- * shell code as this isn't the main shell.
- */
- sigtrapped[SIGEXIT] = 0;
-#ifdef HAVE_NICE
- /* Check if we should run background jobs at a lower priority. */
- if ((how & Z_ASYNC) && isset(BGNICE))
- if (nice(5) < 0)
- zwarn("nice(5) failed: %e", errno);
-#endif /* HAVE_NICE */
-
- return 0;
-}
-
-/*
- * Execute a command at the lowest level of the hierarchy.
- */
-
-/**/
-static void
-execcmd_exec(Estate state, Execcmd_params eparams,
- int input, int output, int how, int last1, int close_if_forked)
-{
- HashNode hn = NULL;
- LinkList filelist = NULL;
- LinkNode node;
- Redir fn;
- struct multio *mfds[10];
- char *text;
- int save[10];
- int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i;
- int nullexec = 0, magic_assign = 0, forked = 0, old_lastval;
- int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
- /* Various flags to the command. */
- int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
- FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
- /*
- * Retrieve parameters for quick reference (they are unique
- * to us so we can modify the structure if we want).
- */
- LinkList args = eparams->args;
- LinkList redir = eparams->redir;
- Wordcode varspc = eparams->varspc;
- int type = eparams->type;
- /*
- * preargs comes from expanding the head of the args list
- * in order to check for prefix commands.
- */
- LinkList preargs;
-
- doneps4 = 0;
-
- /*
- * If assignment but no command get the status from variable
- * assignment.
- */
- old_lastval = lastval;
- if (!args && varspc)
- lastval = errflag ? errflag : cmdoutval;
- /*
- * If there are arguments, we should reset the status for the
- * command before execution---unless we are using the result of a
- * command substitution, which will be indicated by setting
- * use_cmdoutval to 1. We haven't kicked those off yet, so
- * there's no race.
- */
- use_cmdoutval = !args;
-
- for (i = 0; i < 10; i++) {
- save[i] = -2;
- mfds[i] = NULL;
- }
-
- /* If the command begins with `%', then assume it is a *
- * reference to a job in the job table. */
- if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) &&
- *(char *)peekfirst(args) == '%') {
- if (how & Z_DISOWN) {
- oautocont = opts[AUTOCONTINUE];
- opts[AUTOCONTINUE] = 1;
- }
- pushnode(args, dupstring((how & Z_DISOWN)
- ? "disown" : (how & Z_ASYNC) ? "bg" : "fg"));
- how = Z_SYNC;
- }
-
- /* If AUTORESUME is set, the command is SIMPLE, and doesn't have *
- * any redirections, then check if it matches as a prefix of a *
- * job currently in the job table. If it does, then we treat it *
- * as a command to resume this job. */
- if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) &&
- args && nonempty(args) && (!redir || empty(redir)) && !input &&
- !nextnode(firstnode(args))) {
- if (unset(NOTIFY))
- scanjobs();
- if (findjobnam(peekfirst(args)) != -1)
- pushnode(args, dupstring("fg"));
- }
-
- if ((how & Z_ASYNC) || output) {
- /*
- * If running in the background, or not the last command in a
- * pipeline, we don't need any of the rest of this function to
- * affect the state in the main shell, so fork immediately.
- *
- * In other cases we may need to process the command line
- * a bit further before we make the decision.
- */
- text = getjobtext(state->prog, eparams->beg);
- switch (execcmd_fork(state, how, type, varspc, &filelist,
- text, oautocont, close_if_forked)) {
- case -1:
- goto fatal;
- case 0:
- break;
- default:
- return;
- }
- last1 = forked = 1;
- } else
- text = NULL;
-
- /* Check if it's a builtin needing automatic MAGIC_EQUALS_SUBST *
- * handling. Things like typeset need this. We can't detect the *
- * command if it contains some tokens (e.g. x=ex; ${x}port), so this *
- * only works in simple cases. has_token() is called to make sure *
- * this really is a simple case. */
- if ((type == WC_SIMPLE || type == WC_TYPESET) && args) {
- /*
- * preargs contains args that have been expanded by prefork.
- * Running execcmd_getargs() causes any argument available
- * in args to be exanded where necessary and transferred to
- * preargs. We call execcmd_getargs() every time we need to
- * analyse an argument not available in preargs, though there is
- * no guarantee a further argument will be available.
- */
- preargs = newlinklist();
- execcmd_getargs(preargs, args, eparams->htok);
- while (nonempty(preargs)) {
- char *cmdarg = (char *) peekfirst(preargs);
- checked = !has_token(cmdarg);
- if (!checked)
- break;
- if (type == WC_TYPESET &&
- (hn = builtintab->getnode2(builtintab, cmdarg))) {
- /*
- * If reserved word for typeset command found (and so
- * enabled), use regardless of whether builtin is
- * enabled as we share the implementation.
- *
- * Reserved words take precedence over shell functions.
- */
- checked = 1;
- } else if (isset(POSIXBUILTINS) && (cflags & BINF_EXEC)) {
- /*
- * POSIX doesn't allow "exec" to operate on builtins
- * or shell functions.
- */
- break;
- } else {
- if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
- (hn = shfunctab->getnode(shfunctab, cmdarg))) {
- is_shfunc = 1;
- break;
- }
- if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
- checked = !(cflags & BINF_BUILTIN);
- break;
- }
- }
- orig_cflags |= cflags;
- cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
- cflags |= hn->flags;
- if (!(hn->flags & BINF_PREFIX)) {
- is_builtin = 1;
-
- /* autoload the builtin if necessary */
- if (!(hn = resolvebuiltin(cmdarg, hn))) {
- if (forked)
- _exit(lastval);
- return;
- }
- if (type != WC_TYPESET)
- magic_assign = (hn->flags & BINF_MAGICEQUALS);
- break;
- }
- checked = 0;
- /*
- * We usually don't need the argument containing the
- * precommand modifier itself. Exception: when "command"
- * will implemented by a call to "whence", in which case
- * we'll simply re-insert the argument.
- */
- uremnode(preargs, firstnode(preargs));
- if (!firstnode(preargs)) {
- execcmd_getargs(preargs, args, eparams->htok);
- if (!firstnode(preargs))
- break;
- }
- if ((cflags & BINF_COMMAND)) {
- /*
- * Check for options to "command".
- * If just -p, this is handled here: use the default
- * path to execute.
- * If -v or -V, possibly with -p, dispatch to bin_whence
- * but with flag to indicate special handling of -p.
- * Otherwise, just leave marked as BINF_COMMAND
- * modifier with no additional action.
- */
- LinkNode argnode, oldnode, pnode = NULL;
- char *argdata, *cmdopt;
- int has_p = 0, has_vV = 0, has_other = 0;
- argnode = firstnode(preargs);
- argdata = (char *) getdata(argnode);
- while (IS_DASH(*argdata)) {
- /* Just to be definite, stop on single "-", too, */
- if (!argdata[1] ||
- (IS_DASH(argdata[1]) && !argdata[2]))
- break;
- for (cmdopt = argdata+1; *cmdopt; cmdopt++) {
- switch (*cmdopt) {
- case 'p':
- /*
- * If we've got this multiple times (command
- * -p -p) we'll treat the second -p as a
- * command because we only remove one below.
- * Don't think that's a big issue, and it's
- * also traditional behaviour.
- */
- has_p = 1;
- pnode = argnode;
- break;
- case 'v':
- case 'V':
- has_vV = 1;
- break;
- default:
- has_other = 1;
- break;
- }
- }
- if (has_other) {
- /* Don't know how to handle this, so don't */
- has_p = has_vV = 0;
- break;
- }
-
- oldnode = argnode;
- argnode = nextnode(argnode);
- if (!argnode) {
- execcmd_getargs(preargs, args, eparams->htok);
- if (!(argnode = nextnode(oldnode)))
- break;
- }
- argdata = (char *) getdata(argnode);
- }
- if (has_vV) {
- /*
- * Leave everything alone, dispatch to whence.
- * We need to put the name back in the list.
- */
- pushnode(preargs, "command");
- hn = &commandbn.node;
- is_builtin = 1;
- break;
- } else if (has_p) {
- /* Use default path */
- use_defpath = 1;
- /*
- * We don't need this node as we're not treating
- * "command" as a builtin this time.
- */
- if (pnode)
- uremnode(preargs, pnode);
- }
- /*
- * Else just any trailing
- * end-of-options marker. This can only occur
- * if we just had -p or something including more
- * than just -p, -v and -V, in which case we behave
- * as if this is command [non-option-stuff]. This
- * isn't a good place for standard option handling.
- */
- if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
- uremnode(preargs, argnode);
- } else if (cflags & BINF_EXEC) {
- /*
- * Check for compatibility options to exec builtin.
- * It would be nice to do these more generically,
- * but currently we don't have a mechanism for
- * precommand modifiers.
- */
- LinkNode argnode = firstnode(preargs), oldnode;
- char *argdata = (char *) getdata(argnode);
- char *cmdopt, *exec_argv0 = NULL;
- /*
- * Careful here: we want to make sure a final dash
- * is passed through in order that it still behaves
- * as a precommand modifier (zsh equivalent of -l).
- * It has to be last, but I think that's OK since
- * people aren't likely to mix the option style
- * with the zsh style.
- */
- while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) {
- oldnode = argnode;
- argnode = nextnode(oldnode);
- if (!argnode) {
- execcmd_getargs(preargs, args, eparams->htok);
- argnode = nextnode(oldnode);
- }
- if (!argnode) {
- zerr("exec requires a command to execute");
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- goto done;
- }
- uremnode(preargs, oldnode);
- if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
- break;
- for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) {
- switch (*cmdopt) {
- case 'a':
- /* argument is ARGV0 string */
- if (cmdopt[1]) {
- exec_argv0 = cmdopt+1;
- /* position on last non-NULL character */
- cmdopt += strlen(cmdopt+1);
- } else {
- if (!argnode) {
- zerr("exec requires a command to execute");
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- goto done;
- }
- if (!nextnode(argnode))
- execcmd_getargs(preargs, args,
- eparams->htok);
- if (!nextnode(argnode)) {
- zerr("exec flag -a requires a parameter");
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- goto done;
- }
- exec_argv0 = (char *) getdata(argnode);
- oldnode = argnode;
- argnode = nextnode(argnode);
- uremnode(args, oldnode);
- }
- break;
- case 'c':
- cflags |= BINF_CLEARENV;
- break;
- case 'l':
- cflags |= BINF_DASH;
- break;
- default:
- zerr("unknown exec flag -%c", *cmdopt);
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- if (forked)
- _exit(lastval);
- return;
- }
- }
- if (!argnode)
- break;
- argdata = (char *) getdata(argnode);
- }
- if (exec_argv0) {
- char *str, *s;
- exec_argv0 = dupstring(exec_argv0);
- remnulargs(exec_argv0);
- untokenize(exec_argv0);
- size_t sz = strlen(exec_argv0);
- str = s = zalloc(5 + 1 + sz + 1);
- strcpy(s, "ARGV0=");
- s+=6;
- strcpy(s, exec_argv0);
- zputenv(str);
- }
- }
- hn = NULL;
- if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
- break;
- if (!nonempty(preargs))
- execcmd_getargs(preargs, args, eparams->htok);
- }
- } else
- preargs = NULL;
-
- /* if we get this far, it is OK to pay attention to lastval again */
- if (noerrexit & NOERREXIT_UNTIL_EXEC)
- noerrexit = 0;
-
- /* Do prefork substitutions.
- *
- * Decide if we need "magic" handling of ~'s etc. in
- * assignment-like arguments.
- * - If magic_assign is set, we are using a builtin of the
- * tyepset family, but did not recognise this as a keyword,
- * so need guess-o-matic behaviour.
- * - Otherwise, if we did recognise the keyword, we never need
- * guess-o-matic behaviour as the argument was properly parsed
- * as such.
- * - Otherwise, use the behaviour specified by the MAGIC_EQUAL_SUBST
- * option.
- */
- esprefork = (magic_assign ||
- (isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
- PREFORK_TYPESET : 0;
-
- if (args) {
- if (eparams->htok)
- prefork(args, esprefork, NULL);
- if (preargs)
- args = joinlists(preargs, args);
- }
-
- if (type == WC_SIMPLE || type == WC_TYPESET) {
- int unglobbed = 0;
-
- for (;;) {
- char *cmdarg;
-
- if (!(cflags & BINF_NOGLOB))
- while (!checked && !errflag && args && nonempty(args) &&
- has_token((char *) peekfirst(args)))
- zglob(args, firstnode(args), 0);
- else if (!unglobbed) {
- for (node = firstnode(args); node; incnode(node))
- untokenize((char *) getdata(node));
- unglobbed = 1;
- }
-
- /* Current shell should not fork unless the *
- * exec occurs at the end of a pipeline. */
- if ((cflags & BINF_EXEC) && last1)
- do_exec = 1;
-
- /* Empty command */
- if (!args || empty(args)) {
- if (redir && nonempty(redir)) {
- if (do_exec) {
- /* Was this "exec < foobar"? */
- nullexec = 1;
- break;
- } else if (varspc) {
- nullexec = 2;
- break;
- } else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] ||
- (cflags & BINF_PREFIX)) {
- zerr("redirection with no command");
- lastval = 1;
- errflag |= ERRFLAG_ERROR;
- if (forked)
- _exit(lastval);
- return;
- } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
- if (!args)
- args = newlinklist();
- addlinknode(args, dupstring(":"));
- } else if (readnullcmd && *readnullcmd &&
- ((Redir) peekfirst(redir))->type == REDIR_READ &&
- !nextnode(firstnode(redir))) {
- if (!args)
- args = newlinklist();
- addlinknode(args, dupstring(readnullcmd));
- } else {
- if (!args)
- args = newlinklist();
- addlinknode(args, dupstring(nullcmd));
- }
- } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) {
- lastval = 0;
- if (forked)
- _exit(lastval);
- return;
- } else {
- /*
- * No arguments. Reset the status if there were
- * arguments before and no command substitution
- * has provided a status.
- */
- if (badcshglob == 1) {
- zerr("no match");
- lastval = 1;
- if (forked)
- _exit(lastval);
- return;
- }
- cmdoutval = use_cmdoutval ? lastval : 0;
- if (varspc) {
- /* Make sure $? is still correct for assignment */
- lastval = old_lastval;
- addvars(state, varspc, 0);
- }
- if (errflag)
- lastval = 1;
- else
- lastval = cmdoutval;
- if (isset(XTRACE)) {
- fputc('\n', xtrerr);
- fflush(xtrerr);
- }
- if (forked)
- _exit(lastval);
- return;
- }
- } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
- zerrnam("exec", "%s: restricted",
- (char *) getdata(firstnode(args)));
- lastval = 1;
- if (forked)
- _exit(lastval);
- return;
- }
-
- /*
- * Quit looking for a command if:
- * - there was an error; or
- * - we checked the simple cases needing MAGIC_EQUAL_SUBST; or
- * - we know we already found a builtin (because either:
- * - we loaded a builtin from a module, or
- * - we have determined there are options which would
- * require us to use the "command" builtin); or
- * - we aren't using POSIX and so BINF_COMMAND indicates a zsh
- * precommand modifier is being used in place of the
- * builtin
- * - we are using POSIX and this is an EXEC, so we can't
- * execute a builtin or function.
- */
- if (errflag || checked || is_builtin ||
- (isset(POSIXBUILTINS) ?
- (cflags & BINF_EXEC) : (cflags & BINF_COMMAND)))
- break;
-
- cmdarg = (char *) peekfirst(args);
- if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
- (hn = shfunctab->getnode(shfunctab, cmdarg))) {
- is_shfunc = 1;
- break;
- }
- if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
- if (cflags & BINF_BUILTIN) {
- zwarn("no such builtin: %s", cmdarg);
- lastval = 1;
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
- if (forked)
- _exit(lastval);
- return;
- }
- break;
- }
- if (!(hn->flags & BINF_PREFIX)) {
- is_builtin = 1;
-
- /* autoload the builtin if necessary */
- if (!(hn = resolvebuiltin(cmdarg, hn))) {
- if (forked)
- _exit(lastval);
- return;
- }
- break;
- }
- cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
- cflags |= hn->flags;
- uremnode(args, firstnode(args));
- hn = NULL;
- }
- }
-
- if (errflag) {
- if (!lastval)
- lastval = 1;
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
- if (forked)
- _exit(lastval);
- return;
- }
-
- /* Get the text associated with this command. */
- if (!text &&
- (!sfcontext && (jobbing || (how & Z_TIMED))))
- text = getjobtext(state->prog, eparams->beg);
-
- /*
- * Set up special parameter $_
- * For execfuncdef we may need to take account of an
- * anonymous function with arguments.
- */
- if (type != WC_FUNCDEF)
- setunderscore((args && nonempty(args)) ?
- ((char *) getdata(lastnode(args))) : "");
-
- /* Warn about "rm *" */
- if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) &&
- isset(SHINSTDIN) && args && nonempty(args) &&
- nextnode(firstnode(args)) && !strcmp(peekfirst(args), "rm")) {
- LinkNode node, next;
-
- for (node = nextnode(firstnode(args)); node && !errflag; node = next) {
- char *s = (char *) getdata(node);
- int l = strlen(s);
-
- next = nextnode(node);
- if (s[0] == Star && !s[1]) {
- if (!checkrmall(pwd)) {
- errflag |= ERRFLAG_ERROR;
- break;
- }
- } else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) {
- char t = s[l - 2];
- int rmall;
-
- s[l - 2] = 0;
- rmall = checkrmall(s);
- s[l - 2] = t;
-
- if (!rmall) {
- errflag |= ERRFLAG_ERROR;
- break;
- }
- }
- }
- }
-
- if (type == WC_FUNCDEF) {
- /*
- * The first word of a function definition is a list of
- * names. If this is empty, we're doing an anonymous function:
- * in that case redirections are handled normally.
- * If not, it's a function definition: then we don't do
- * redirections here but pass in the list of redirections to
- * be stored for recall with the function.
- */
- if (*state->pc != 0) {
- /* Nonymous, don't do redirections here */
- redir = NULL;
- }
- } else if (is_shfunc || type == WC_AUTOFN) {
- Shfunc shf;
- if (is_shfunc)
- shf = (Shfunc)hn;
- else {
- shf = loadautofn(state->prog->shf, 1, 0, 0);
- if (shf)
- state->prog->shf = shf;
- else {
- /*
- * This doesn't set errflag, so just return now.
- */
- lastval = 1;
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
- if (forked)
- _exit(lastval);
- return;
- }
- }
- /*
- * A function definition may have a list of additional
- * redirections to apply, so retrieve it.
- */
- if (shf->redir) {
- struct estate s;
- LinkList redir2;
-
- s.prog = shf->redir;
- s.pc = shf->redir->prog;
- s.strs = shf->redir->strs;
- redir2 = ecgetredirs(&s);
- if (!redir)
- redir = redir2;
- else {
- while (nonempty(redir2))
- addlinknode(redir, ugetnode(redir2));
- }
- }
- }
-
- if (errflag) {
- lastval = 1;
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
- if (forked)
- _exit(lastval);
- return;
- }
-
- if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) {
- char *s;
- char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
- (!redir || empty(redir)) && args && !empty(args) &&
- !nextnode(firstnode(args)) && *(char *)peekfirst(args));
-
- DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c");
- if (!hn) {
- /* Resolve external commands */
- char *cmdarg = (char *) peekfirst(args);
- char **checkpath = pathchecked;
- int dohashcmd = isset(HASHCMDS);
-
- hn = cmdnamtab->getnode(cmdnamtab, cmdarg);
- if (hn && trycd && !isreallycom((Cmdnam)hn)) {
- if (!(((Cmdnam)hn)->node.flags & HASHED)) {
- checkpath = path;
- dohashcmd = 1;
- }
- cmdnamtab->removenode(cmdnamtab, cmdarg);
- cmdnamtab->freenode(hn);
- hn = NULL;
- }
- if (!hn && dohashcmd && strcmp(cmdarg, "..")) {
- for (s = cmdarg; *s && *s != '/'; s++);
- if (!*s)
- hn = (HashNode) hashcmd(cmdarg, checkpath);
- }
- }
-
- /* If no command found yet, see if it *
- * is a directory we should AUTOCD to. */
- if (!hn && trycd && (s = cancd(peekfirst(args)))) {
- peekfirst(args) = (void *) s;
- pushnode(args, dupstring("--"));
- pushnode(args, dupstring("cd"));
- if ((hn = builtintab->getnode(builtintab, "cd")))
- is_builtin = 1;
- }
- }
-
- /* This is nonzero if the command is a current shell procedure? */
- is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH);
-
- /**************************************************************************
- * Do we need to fork? We need to fork if: *
- * 1) The command is supposed to run in the background. This *
- * case is now handled above (forked = 1 here). (or) *
- * 2) There is no `exec' flag, and either: *
- * a) This is a builtin or shell function with output piped somewhere. *
- * b) This is an external command and we can't do a `fake exec'. *
- * *
- * A `fake exec' is possible if we have all the following conditions: *
- * 1) last1 flag is 1. This indicates that the current shell will not *
- * be needed after the current command. This is typically the case *
- * when the command is the last stage in a subshell, or is the *
- * last command after the option `-c'. *
- * 2) We don't have any traps set. *
- * 3) We don't have any files to delete. *
- * *
- * The condition above for a `fake exec' will also work for a current *
- * shell command such as a builtin, but doesn't really buy us anything *
- * (doesn't save us a process), since it is already running in the *
- * current shell. *
- **************************************************************************/
-
- if (!forked) {
- if (!do_exec &&
- (((is_builtin || is_shfunc) && output) ||
- (!is_cursh && (last1 != 1 || nsigtrapped || havefiles() ||
- fdtable_flocks)))) {
- switch (execcmd_fork(state, how, type, varspc, &filelist,
- text, oautocont, close_if_forked)) {
- case -1:
- goto fatal;
- case 0:
- break;
- default:
- return;
- }
- forked = 1;
- } else if (is_cursh) {
- /* This is a current shell procedure that didn't need to fork. *
- * This includes current shell procedures that are being exec'ed, *
- * as well as null execs. */
- jobtab[thisjob].stat |= STAT_CURSH;
- if (!jobtab[thisjob].procs)
- jobtab[thisjob].stat |= STAT_NOPRINT;
- if (is_builtin)
- jobtab[thisjob].stat |= STAT_BUILTIN;
- } else {
- /* This is an exec (real or fake) for an external command. *
- * Note that any form of exec means that the subshell is fake *
- * (but we may be in a subshell already). */
- is_exec = 1;
- /*
- * If we are in a subshell environment anyway, say we're forked,
- * even if we're actually not forked because we know the
- * subshell is exiting. This ensures SHLVL reflects the current
- * shell, and also optimises out any save/restore we'd need to
- * do if we were returning to the main shell.
- */
- if (type == WC_SUBSH)
- forked = 1;
- }
- }
-
- if ((esglob = !(cflags & BINF_NOGLOB)) && args && eparams->htok) {
- LinkList oargs = args;
- globlist(args, 0);
- args = oargs;
- }
- if (errflag) {
- lastval = 1;
- goto err;
- }
-
- /* Make a copy of stderr for xtrace output before redirecting */
- fflush(xtrerr);
- if (isset(XTRACE) && xtrerr == stderr &&
- (type < WC_SUBSH || type == WC_TIMED)) {
- if ((newxtrerr = fdopen(movefd(dup(fileno(stderr))), "w"))) {
- xtrerr = newxtrerr;
- fdtable[fileno(xtrerr)] = FDT_XTRACE;
- }
- }
-
- /* Add pipeline input/output to mnodes */
- if (input)
- addfd(forked, save, mfds, 0, input, 0, NULL);
- if (output)
- addfd(forked, save, mfds, 1, output, 1, NULL);
-
- /* Do process substitutions */
- if (redir)
- spawnpipes(redir, nullexec);
-
- /* Do io redirections */
- while (redir && nonempty(redir)) {
- fn = (Redir) ugetnode(redir);
-
- DPUTS(fn->type == REDIR_HEREDOC || fn->type == REDIR_HEREDOCDASH,
- "BUG: unexpanded here document");
- if (fn->type == REDIR_INPIPE) {
- if (!checkclobberparam(fn) || fn->fd2 == -1) {
- if (fn->fd2 != -1)
- zclose(fn->fd2);
- closemnodes(mfds);
- fixfds(save);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fn->fd2, 0, fn->varid);
- } else if (fn->type == REDIR_OUTPIPE) {
- if (!checkclobberparam(fn) || fn->fd2 == -1) {
- if (fn->fd2 != -1)
- zclose(fn->fd2);
- closemnodes(mfds);
- fixfds(save);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fn->fd2, 1, fn->varid);
- } else {
- int closed;
- if (fn->type != REDIR_HERESTR && xpandredir(fn, redir))
- continue;
- if (errflag) {
- closemnodes(mfds);
- fixfds(save);
- execerr();
- }
- if (isset(RESTRICTED) && IS_WRITE_FILE(fn->type)) {
- zwarn("writing redirection not allowed in restricted mode");
- execerr();
- }
- if (unset(EXECOPT))
- continue;
- switch(fn->type) {
- case REDIR_HERESTR:
- if (!checkclobberparam(fn))
- fil = -1;
- else
- fil = getherestr(fn);
- if (fil == -1) {
- if (errno && errno != EINTR)
- zwarn("can't create temp file for here document: %e",
- errno);
- closemnodes(mfds);
- fixfds(save);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid);
- break;
- case REDIR_READ:
- case REDIR_READWRITE:
- if (!checkclobberparam(fn))
- fil = -1;
- else if (fn->type == REDIR_READ)
- fil = open(unmeta(fn->name), O_RDONLY | O_NOCTTY);
- else
- fil = open(unmeta(fn->name),
- O_RDWR | O_CREAT | O_NOCTTY, 0666);
- if (fil == -1) {
- closemnodes(mfds);
- fixfds(save);
- if (errno != EINTR)
- zwarn("%e: %s", errno, fn->name);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid);
- /* If this is 'exec < file', read from stdin, *
- * not terminal, unless `file' is a terminal. */
- if (nullexec == 1 && fn->fd1 == 0 &&
- isset(SHINSTDIN) && interact && !zleactive)
- init_io(NULL);
- break;
- case REDIR_CLOSE:
- if (fn->varid) {
- char *s = fn->varid, *t;
- struct value vbuf;
- Value v;
- int bad = 0;
-
- if (!(v = getvalue(&vbuf, &s, 0))) {
- bad = 1;
- } else if (v->pm->node.flags & PM_READONLY) {
- bad = 2;
- } else {
- s = getstrvalue(v);
- if (errflag)
- bad = 1;
- else {
- fn->fd1 = zstrtol(s, &t, 0);
- if (s == t)
- bad = 1;
- else if (*t) {
- /* Check for base#number format */
- if (*t == '#' && *s != '0')
- fn->fd1 = zstrtol(s = t+1, &t, fn->fd1);
- if (s == t || *t)
- bad = 1;
- }
- if (!bad && fn->fd1 <= max_zsh_fd) {
- if (fn->fd1 >= 10 &&
- (fdtable[fn->fd1] & FDT_TYPE_MASK) ==
- FDT_INTERNAL)
- bad = 3;
- }
- }
- }
- if (bad) {
- const char *bad_msg[] = {
- "parameter %s does not contain a file descriptor",
- "can't close file descriptor from readonly parameter %s",
- "file descriptor %d used by shell, not closed"
- };
- if (bad > 2)
- zwarn(bad_msg[bad-1], fn->fd1);
- else
- zwarn(bad_msg[bad-1], fn->varid);
- execerr();
- }
- }
- /*
- * Note we may attempt to close an fd beyond max_zsh_fd:
- * OK as long as we never look in fdtable for it.
- */
- closed = 0;
- if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2) {
- save[fn->fd1] = movefd(fn->fd1);
- if (save[fn->fd1] >= 0) {
- /*
- * The original fd is now closed, we don't need
- * to do it below.
- */
- closed = 1;
- }
- }
- if (fn->fd1 < 10)
- closemn(mfds, fn->fd1, REDIR_CLOSE);
- /*
- * Only report failures to close file descriptors
- * if they're under user control as we don't know
- * what the previous status of others was.
- */
- if (!closed && zclose(fn->fd1) < 0 && fn->varid) {
- zwarn("failed to close file descriptor %d: %e",
- fn->fd1, errno);
- }
- break;
- case REDIR_MERGEIN:
- case REDIR_MERGEOUT:
- if (fn->fd2 < 10)
- closemn(mfds, fn->fd2, fn->type);
- if (!checkclobberparam(fn))
- fil = -1;
- else if (fn->fd2 > 9 &&
- /*
- * If the requested fd is > max_zsh_fd,
- * the shell doesn't know about it.
- * Just assume the user knows what they're
- * doing.
- */
- (fn->fd2 <= max_zsh_fd &&
- ((fdtable[fn->fd2] != FDT_UNUSED &&
- fdtable[fn->fd2] != FDT_EXTERNAL) ||
- fn->fd2 == coprocin ||
- fn->fd2 == coprocout))) {
- fil = -1;
- errno = EBADF;
- } else {
- int fd = fn->fd2;
- if(fd == -2)
- fd = (fn->type == REDIR_MERGEOUT) ? coprocout : coprocin;
- fil = movefd(dup(fd));
- }
- if (fil == -1) {
- char fdstr[DIGBUFSIZE];
-
- closemnodes(mfds);
- fixfds(save);
- if (fn->fd2 != -2)
- sprintf(fdstr, "%d", fn->fd2);
- if (errno)
- zwarn("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr,
- errno);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fil,
- fn->type == REDIR_MERGEOUT, fn->varid);
- break;
- default:
- if (!checkclobberparam(fn))
- fil = -1;
- else if (IS_APPEND_REDIR(fn->type))
- fil = open(unmeta(fn->name),
- ((unset(CLOBBER) && unset(APPENDCREATE)) &&
- !IS_CLOBBER_REDIR(fn->type)) ?
- O_WRONLY | O_APPEND | O_NOCTTY :
- O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666);
- else
- fil = clobber_open(fn);
- if(fil != -1 && IS_ERROR_REDIR(fn->type))
- dfil = movefd(dup(fil));
- else
- dfil = 0;
- if (fil == -1 || dfil == -1) {
- if(fil != -1)
- close(fil);
- closemnodes(mfds);
- fixfds(save);
- if (errno && errno != EINTR)
- zwarn("%e: %s", errno, fn->name);
- execerr();
- }
- addfd(forked, save, mfds, fn->fd1, fil, 1, fn->varid);
- if(IS_ERROR_REDIR(fn->type))
- addfd(forked, save, mfds, 2, dfil, 1, NULL);
- break;
- }
- /* May be error in addfd due to setting parameter. */
- if (errflag) {
- closemnodes(mfds);
- fixfds(save);
- execerr();
- }
- }
- }
-
- /* We are done with redirection. close the mnodes, *
- * spawning tee/cat processes as necessary. */
- for (i = 0; i < 10; i++)
- if (mfds[i] && mfds[i]->ct >= 2)
- closemn(mfds, i, REDIR_CLOSE);
-
- if (nullexec) {
- /*
- * If nullexec is 2, we have variables to add with the redirections
- * in place. If nullexec is 1, we may have variables but they
- * need the standard restore logic.
- */
- if (varspc) {
- LinkList restorelist = 0, removelist = 0;
- if (!isset(POSIXBUILTINS) && nullexec != 2)
- save_params(state, varspc, &restorelist, &removelist);
- addvars(state, varspc, 0);
- if (restorelist)
- restore_params(restorelist, removelist);
- }
- lastval = errflag ? errflag : cmdoutval;
- if (nullexec == 1) {
- /*
- * If nullexec is 1 we specifically *don't* restore the original
- * fd's before returning.
- */
- for (i = 0; i < 10; i++)
- if (save[i] != -2)
- zclose(save[i]);
- goto done;
- }
- if (isset(XTRACE)) {
- fputc('\n', xtrerr);
- fflush(xtrerr);
- }
- } else if (isset(EXECOPT) && !errflag) {
- int q = queue_signal_level();
- /*
- * We delay the entersubsh() to here when we are exec'ing
- * the current shell (including a fake exec to run a builtin then
- * exit) in case there is an error return.
- */
- if (is_exec) {
- int flags = ((how & Z_ASYNC) ? ESUB_ASYNC : 0) |
- ESUB_PGRP | ESUB_FAKE;
- if (type != WC_SUBSH)
- flags |= ESUB_KEEPTRAP;
- if ((do_exec || (type >= WC_CURSH && last1 == 1))
- && !forked)
- flags |= ESUB_REVERTPGRP;
- entersubsh(flags);
- }
- if (type == WC_FUNCDEF) {
- Eprog redir_prog;
- if (!redir && wc_code(*eparams->beg) == WC_REDIR) {
- /*
- * We're not using a redirection from the currently
- * parsed environment, which is what we'd do for an
- * anonymous function, but there are redirections we
- * should store with the new function.
- */
- struct estate s;
-
- s.prog = state->prog;
- s.pc = eparams->beg;
- s.strs = state->prog->strs;
-
- /*
- * The copy uses the wordcode parsing area, so save and
- * restore state.
- */
- zcontext_save();
- redir_prog = eccopyredirs(&s);
- zcontext_restore();
- } else
- redir_prog = NULL;
-
- dont_queue_signals();
- lastval = execfuncdef(state, redir_prog);
- restore_queue_signals(q);
- }
- else if (type >= WC_CURSH) {
- if (last1 == 1)
- do_exec = 1;
- dont_queue_signals();
- if (type == WC_AUTOFN) {
- /*
- * We pre-loaded this to get any redirs.
- * So we execuate a simplified function here.
- */
- lastval = execautofn_basic(state, do_exec);
- } else
- lastval = (execfuncs[type - WC_CURSH])(state, do_exec);
- restore_queue_signals(q);
- } else if (is_builtin || is_shfunc) {
- LinkList restorelist = 0, removelist = 0;
- int do_save = 0;
- /* builtin or shell function */
-
- if (!forked) {
- if (isset(POSIXBUILTINS)) {
- /*
- * If it's a function or special builtin --- save
- * if it's got "command" in front.
- * If it's a normal command --- save.
- */
- if (is_shfunc || (hn->flags & (BINF_PSPECIAL|BINF_ASSIGN)))
- do_save = (orig_cflags & BINF_COMMAND);
- else
- do_save = 1;
- } else {
- /*
- * Save if it's got "command" in front or it's
- * not a magic-equals assignment.
- */
- if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !magic_assign)
- do_save = 1;
- }
- if (do_save && varspc)
- save_params(state, varspc, &restorelist, &removelist);
- }
- if (varspc) {
- /* Export this if the command is a shell function,
- * but not if it's a builtin.
- */
- int flags = 0;
- if (is_shfunc)
- flags |= ADDVAR_EXPORT;
- if (restorelist)
- flags |= ADDVAR_RESTORE;
-
- addvars(state, varspc, flags);
- if (errflag) {
- if (restorelist)
- restore_params(restorelist, removelist);
- lastval = 1;
- fixfds(save);
- goto done;
- }
- }
-
- if (is_shfunc) {
- /* It's a shell function */
- pipecleanfilelist(filelist, 0);
- execshfunc((Shfunc) hn, args);
- } else {
- /* It's a builtin */
- LinkList assigns = (LinkList)0;
- int postassigns = eparams->postassigns;
- if (forked)
- closem(FDT_INTERNAL, 0);
- if (postassigns) {
- Wordcode opc = state->pc;
- state->pc = eparams->assignspc;
- assigns = newlinklist();
- while (postassigns--) {
- int htok;
- wordcode ac = *state->pc++;
- char *name = ecgetstr(state, EC_DUPTOK, &htok);
- Asgment asg;
- local_list1(svl);
-
- DPUTS(wc_code(ac) != WC_ASSIGN,
- "BUG: bad assignment list for typeset");
- if (htok) {
- init_list1(svl, name);
- if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR &&
- WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
- char *data;
- /*
- * Special case: this is a name only, so
- * it's not required to be a single
- * expansion. Furthermore, for
- * consistency with the builtin
- * interface, it may expand into
- * scalar assignments:
- * ass=(one=two three=four)
- * typeset a=b $ass
- */
- /* Unused dummy value for name */
- (void)ecgetstr(state, EC_DUPTOK, &htok);
- prefork(&svl, PREFORK_TYPESET, NULL);
- if (errflag) {
- state->pc = opc;
- break;
- }
- globlist(&svl, 0);
- if (errflag) {
- state->pc = opc;
- break;
- }
- while ((data = ugetnode(&svl))) {
- char *ptr;
- asg = (Asgment)zhalloc(sizeof(struct asgment));
- asg->flags = 0;
- if ((ptr = strchr(data, '='))) {
- *ptr++ = '\0';
- asg->name = data;
- asg->value.scalar = ptr;
- } else {
- asg->name = data;
- asg->value.scalar = NULL;
- }
- uaddlinknode(assigns, &asg->node);
- }
- continue;
- }
- prefork(&svl, PREFORK_SINGLE, NULL);
- name = empty(&svl) ? "" :
- (char *)getdata(firstnode(&svl));
- }
- untokenize(name);
- asg = (Asgment)zhalloc(sizeof(struct asgment));
- asg->name = name;
- if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) {
- char *val = ecgetstr(state, EC_DUPTOK, &htok);
- asg->flags = 0;
- if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
- /* Fake assignment, no value */
- asg->value.scalar = NULL;
- } else {
- if (htok) {
- init_list1(svl, val);
- prefork(&svl,
- PREFORK_SINGLE|PREFORK_ASSIGN,
- NULL);
- if (errflag) {
- state->pc = opc;
- break;
- }
- /*
- * No globassign for typeset
- * arguments, thank you
- */
- val = empty(&svl) ? "" :
- (char *)getdata(firstnode(&svl));
- }
- untokenize(val);
- asg->value.scalar = val;
- }
- } else {
- asg->flags = ASG_ARRAY;
- asg->value.array =
- ecgetlist(state, WC_ASSIGN_NUM(ac),
- EC_DUPTOK, &htok);
- if (asg->value.array)
- {
- if (!errflag) {
- int prefork_ret = 0;
- prefork(asg->value.array, PREFORK_ASSIGN,
- &prefork_ret);
- if (errflag) {
- state->pc = opc;
- break;
- }
- if (prefork_ret & PREFORK_KEY_VALUE)
- asg->flags |= ASG_KEY_VALUE;
- globlist(asg->value.array, prefork_ret);
- }
- if (errflag) {
- state->pc = opc;
- break;
- }
- }
- }
-
- uaddlinknode(assigns, &asg->node);
- }
- state->pc = opc;
- }
- dont_queue_signals();
- if (!errflag) {
- int ret = execbuiltin(args, assigns, (Builtin) hn);
- /*
- * In case of interruption assume builtin status
- * is less useful than what interrupt set.
- */
- if (!(errflag & ERRFLAG_INT))
- lastval = ret;
- }
- if (do_save & BINF_COMMAND)
- errflag &= ~ERRFLAG_ERROR;
- restore_queue_signals(q);
- fflush(stdout);
- if (save[1] == -2) {
- if (ferror(stdout)) {
- zwarn("write error: %e", errno);
- clearerr(stdout);
- }
- } else
- clearerr(stdout);
- }
- if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
- lastval && !subsh) {
-#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- fprintf(stderr, "zsh: exit %lld\n", lastval);
-#else
- fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
-#endif
- fflush(stderr);
- }
-
- if (do_exec) {
- if (subsh)
- _exit(lastval);
-
- /* If we are exec'ing a command, and we are not in a subshell, *
- * then check if we should save the history file. */
- if (isset(RCS) && interact && !nohistsave)
- savehistfile(NULL, 1, HFILE_USE_OPTIONS);
- exit(lastval);
- }
- if (restorelist)
- restore_params(restorelist, removelist);
-
- } else {
- if (!subsh) {
- /* for either implicit or explicit "exec", decrease $SHLVL
- * as we're now done as a shell */
- if (!forked)
- setiparam("SHLVL", --shlvl);
-
- /* If we are exec'ing a command, and we are not *
- * in a subshell, then save the history file. */
- if (do_exec && isset(RCS) && interact && !nohistsave)
- savehistfile(NULL, 1, HFILE_USE_OPTIONS);
- }
- if (type == WC_SIMPLE || type == WC_TYPESET) {
- if (varspc) {
- int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT;
- if (forked)
- addflags |= ADDVAR_RESTORE;
- addvars(state, varspc, addflags);
- if (errflag)
- _exit(1);
- }
- closem(FDT_INTERNAL, 0);
- if (coprocin != -1) {
- zclose(coprocin);
- coprocin = -1;
- }
- if (coprocout != -1) {
- zclose(coprocout);
- coprocout = -1;
- }
-#ifdef HAVE_GETRLIMIT
- if (!forked)
- setlimits(NULL);
-#endif
- if (how & Z_ASYNC) {
- zsfree(STTYval);
- STTYval = 0;
- }
- execute(args, cflags, use_defpath);
- } else { /* ( ... ) */
- DPUTS(varspc,
- "BUG: assignment before complex command");
- list_pipe = 0;
- pipecleanfilelist(filelist, 0);
- /* If we're forked (and we should be), no need to return */
- DPUTS(last1 != 1 && !forked, "BUG: not exiting?");
- DPUTS(type != WC_SUBSH, "Not sure what we're doing.");
- /* Skip word only used for try/always blocks */
- state->pc++;
- execlist(state, 0, 1);
- }
- }
- }
-
- err:
- if (forked) {
- /*
- * So what's going on here then? Well, I'm glad you asked.
- *
- * If we create multios for use in a subshell we do
- * this after forking, in this function above. That
- * means that the current (sub)process is responsible
- * for clearing them up. However, the processes won't
- * go away until we have closed the fd's talking to them.
- * Since we're about to exit the shell there's nothing
- * to stop us closing all fd's (including the ones 0 to 9
- * that we usually leave alone).
- *
- * Then we wait for any processes. When we forked,
- * we cleared the jobtable and started a new job just for
- * any oddments like this, so if there aren't any we won't
- * need to wait. The result of not waiting is that
- * the multios haven't flushed the fd's properly, leading
- * to obscure missing data.
- *
- * It would probably be cleaner to ensure that the
- * parent shell handled multios, but that requires
- * some architectural changes which are likely to be
- * hairy.
- */
- for (i = 0; i < 10; i++)
- if (fdtable[i] != FDT_UNUSED)
- close(i);
- closem(FDT_UNUSED, 1);
- if (thisjob != -1)
- waitjobs();
- _exit(lastval);
- }
- fixfds(save);
-
- done:
- if (isset(POSIXBUILTINS) &&
- (cflags & (BINF_PSPECIAL|BINF_EXEC)) &&
- !(orig_cflags & BINF_COMMAND)) {
- /*
- * For POSIX-compatible behaviour with special
- * builtins (including exec which we don't usually
- * classify as a builtin) we treat all errors as fatal.
- * The "command" builtin is not special so resets this behaviour.
- */
- forked |= zsh_subshell;
- fatal:
- if (redir_err || errflag) {
- if (!isset(INTERACTIVE)) {
- if (forked)
- _exit(1);
- else
- exit(1);
- }
- errflag |= ERRFLAG_ERROR;
- }
- }
- if (newxtrerr) {
- fil = fileno(newxtrerr);
- fclose(newxtrerr);
- xtrerr = oxtrerr;
- zclose(fil);
- }
-
- zsfree(STTYval);
- STTYval = 0;
- if (oautocont >= 0)
- opts[AUTOCONTINUE] = oautocont;
-}
-
-/* Arrange to have variables restored. */
-
-/**/
-static void
-save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
-{
- Param pm;
- char *s;
- wordcode ac;
-
- *restore_p = newlinklist();
- *remove_p = newlinklist();
-
- while (wc_code(ac = *pc) == WC_ASSIGN) {
- s = ecrawstr(state->prog, pc + 1, NULL);
- if ((pm = (Param) paramtab->getnode(paramtab, s))) {
- Param tpm;
- if (pm->env)
- delenv(pm);
- if (!(pm->node.flags & PM_SPECIAL)) {
- /*
- * We used to remove ordinary parameters from the
- * table, but that meant "HELLO=$HELLO shellfunc"
- * failed because the expansion of $HELLO hasn't
- * been done at this point. Instead, copy the
- * parameter: in this case, we'll insert the
- * copied parameter straight back into the parameter
- * table so we want to be sure everything is
- * properly set up and in permanent memory.
- */
- tpm = (Param) zshcalloc(sizeof *tpm);
- tpm->node.nam = ztrdup(pm->node.nam);
- copyparam(tpm, pm, 0);
- pm = tpm;
- } else if (!(pm->node.flags & PM_READONLY) &&
- (unset(RESTRICTED) || !(pm->node.flags & PM_RESTRICTED))) {
- /*
- * In this case we're just saving parts of
- * the parameter in a tempory, so use heap allocation
- * and don't bother copying every detail.
- */
- tpm = (Param) hcalloc(sizeof *tpm);
- tpm->node.nam = pm->node.nam;
- copyparam(tpm, pm, 1);
- pm = tpm;
- }
- addlinknode(*remove_p, dupstring(s));
- addlinknode(*restore_p, pm);
- } else
- addlinknode(*remove_p, dupstring(s));
-
- pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
- 3 : WC_ASSIGN_NUM(ac) + 2);
- }
-}
-
-/* Restore saved parameters after executing a shfunc or builtin */
-
-/**/
-static void
-restore_params(LinkList restorelist, LinkList removelist)
-{
- Param pm;
- char *s;
-
- /* remove temporary parameters */
- while ((s = (char *) ugetnode(removelist))) {
- if ((pm = (Param) paramtab->getnode(paramtab, s)) &&
- !(pm->node.flags & PM_SPECIAL)) {
- pm->node.flags &= ~PM_READONLY;
- unsetparam_pm(pm, 0, 0);
- }
- }
-
- if (restorelist) {
- /* restore saved parameters */
- while ((pm = (Param) ugetnode(restorelist))) {
- if (pm->node.flags & PM_SPECIAL) {
- Param tpm = (Param) paramtab->getnode(paramtab, pm->node.nam);
-
- DPUTS(!tpm || PM_TYPE(pm->node.flags) != PM_TYPE(tpm->node.flags) ||
- !(pm->node.flags & PM_SPECIAL),
- "BUG: in restoring special parameters");
- if (!pm->env && tpm->env)
- delenv(tpm);
- tpm->node.flags = pm->node.flags;
- switch (PM_TYPE(pm->node.flags)) {
- case PM_SCALAR:
- tpm->gsu.s->setfn(tpm, pm->u.str);
- break;
- case PM_INTEGER:
- tpm->gsu.i->setfn(tpm, pm->u.val);
- break;
- case PM_EFLOAT:
- case PM_FFLOAT:
- tpm->gsu.f->setfn(tpm, pm->u.dval);
- break;
- case PM_ARRAY:
- tpm->gsu.a->setfn(tpm, pm->u.arr);
- break;
- case PM_HASHED:
- tpm->gsu.h->setfn(tpm, pm->u.hash);
- break;
- }
- pm = tpm;
- } else {
- paramtab->addnode(paramtab, pm->node.nam, pm);
- }
- if ((pm->node.flags & PM_EXPORTED) && ((s = getsparam(pm->node.nam))))
- addenv(pm, s);
- }
- }
-}
-
-/* restore fds after redirecting a builtin */
-
-/**/
-static void
-fixfds(int *save)
-{
- int old_errno = errno;
- int i;
-
- for (i = 0; i != 10; i++)
- if (save[i] != -2)
- redup(save[i], i);
- errno = old_errno;
-}
-
-/*
- * Close internal shell fds.
- *
- * Close any that are marked as used if "how" is FDT_UNUSED, else
- * close any with the value "how".
- *
- * If "all" is zero, we'll skip cases where we need the file
- * descriptor to be visible externally.
- */
-
-/**/
-mod_export void
-closem(int how, int all)
-{
- int i;
-
- for (i = 10; i <= max_zsh_fd; i++)
- if (fdtable[i] != FDT_UNUSED &&
- /*
- * Process substitution needs to be visible to user;
- * fd's are explicitly cleaned up by filelist handling.
- */
- (all || fdtable[i] != FDT_PROC_SUBST) &&
- (how == FDT_UNUSED || (fdtable[i] & FDT_TYPE_MASK) == how)) {
- if (i == SHTTY)
- SHTTY = -1;
- zclose(i);
- }
-}
-
-/* convert here document into a here string */
-
-/**/
-char *
-gethere(char **strp, int typ)
-{
- char *buf;
- int bsiz, qt = 0, strip = 0;
- char *s, *t, *bptr, c;
- char *str = *strp;
-
- for (s = str; *s; s++)
- if (inull(*s)) {
- qt = 1;
- break;
- }
- str = quotesubst(str);
- untokenize(str);
- if (typ == REDIR_HEREDOCDASH) {
- strip = 1;
- while (*str == '\t')
- str++;
- }
- *strp = str;
- bptr = buf = zalloc(bsiz = 256);
- for (;;) {
- t = bptr;
-
- while ((c = hgetc()) == '\t' && strip)
- ;
- for (;;) {
- if (bptr >= buf + bsiz - 2) {
- ptrdiff_t toff = t - buf;
- ptrdiff_t bptroff = bptr - buf;
- char *newbuf = realloc(buf, 2 * bsiz);
- if (!newbuf) {
- /* out of memory */
- zfree(buf, bsiz);
- return NULL;
- }
- buf = newbuf;
- t = buf + toff;
- bptr = buf + bptroff;
- bsiz *= 2;
- }
- if (lexstop || c == '\n')
- break;
- if (!qt && c == '\\') {
- *bptr++ = c;
- c = hgetc();
- if (c == '\n') {
- bptr--;
- c = hgetc();
- continue;
- }
- }
- *bptr++ = c;
- c = hgetc();
- }
- *bptr = '\0';
- if (!strcmp(t, str))
- break;
- if (lexstop) {
- t = bptr;
- break;
- }
- *bptr++ = '\n';
- }
- *t = '\0';
- s = buf;
- buf = dupstring(buf);
- zfree(s, bsiz);
- if (!qt) {
- int ef = errflag;
-
- parsestr(&buf);
-
- if (!(errflag & ERRFLAG_ERROR)) {
- /* Retain any user interrupt error */
- errflag = ef | (errflag & ERRFLAG_INT);
- }
- }
- return buf;
-}
-
-/* open here string fd */
-
-/**/
-static int
-getherestr(struct redir *fn)
-{
- char *s, *t;
- int fd, len;
-
- t = fn->name;
- singsub(&t);
- untokenize(t);
- unmetafy(t, &len);
- /*
- * For real here-strings we append a newline, as if the
- * string given was a complete command line.
- *
- * For here-strings from here documents, we use the original
- * text exactly.
- */
- if (!(fn->flags & REDIRF_FROM_HEREDOC))
- t[len++] = '\n';
- if ((fd = gettempfile(NULL, 1, &s)) < 0)
- return -1;
- write_loop(fd, t, len);
- close(fd);
- fd = open(s, O_RDONLY | O_NOCTTY);
- unlink(s);
- return fd;
-}
-
-/*
- * Test if some wordcode starts with a simple redirection of type
- * redir_type. If it does, return the name of the file, copied onto
- * the heap. If it doesn't, return NULL.
- */
-
-static char *
-simple_redir_name(Eprog prog, int redir_type)
-{
- Wordcode pc;
-
- pc = prog->prog;
- if (prog != &dummy_eprog &&
- wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) &&
- wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) &&
- WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END &&
- wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END &&
- wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == redir_type &&
- !WC_REDIR_VARID(pc[3]) &&
- !pc[4] &&
- wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) {
- return dupstring(ecrawstr(prog, pc + 5, NULL));
- }
-
- return NULL;
-}
-
-/* $(...) */
-
-/**/
-LinkList
-getoutput(char *cmd, int qt)
-{
- Eprog prog;
- int pipes[2];
- pid_t pid;
- char *s;
-
- int onc = nocomments;
- nocomments = (interact && unset(INTERACTIVECOMMENTS));
- prog = parse_string(cmd, 0);
- nocomments = onc;
-
- if (!prog)
- return NULL;
-
- if ((s = simple_redir_name(prog, REDIR_READ))) {
- /* $(< word) */
- int stream;
- LinkList retval;
- int readerror;
-
- singsub(&s);
- if (errflag)
- return NULL;
- untokenize(s);
- if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
- zwarn("%e: %s", errno, s);
- lastval = cmdoutval = 1;
- return newlinklist();
- }
- retval = readoutput(stream, qt, &readerror);
- if (readerror) {
- zwarn("error when reading %s: %e", s, readerror);
- lastval = cmdoutval = 1;
- }
- return retval;
- }
- if (mpipe(pipes) < 0) {
- errflag |= ERRFLAG_ERROR;
- cmdoutpid = 0;
- return NULL;
- }
- child_block();
- cmdoutval = 0;
- if ((cmdoutpid = pid = zfork(NULL)) == -1) {
- /* fork error */
- zclose(pipes[0]);
- zclose(pipes[1]);
- errflag |= ERRFLAG_ERROR;
- cmdoutpid = 0;
- child_unblock();
- return NULL;
- } else if (pid) {
- LinkList retval;
-
- zclose(pipes[1]);
- retval = readoutput(pipes[0], qt, NULL);
- fdtable[pipes[0]] = FDT_UNUSED;
- waitforpid(pid, 0); /* unblocks */
- lastval = cmdoutval;
- return retval;
- }
- /* pid == 0 */
- child_unblock();
- zclose(pipes[0]);
- redup(pipes[1], 1);
- entersubsh(ESUB_PGRP|ESUB_NOMONITOR);
- cmdpush(CS_CMDSUBST);
- execode(prog, 0, 1, "cmdsubst");
- cmdpop();
- close(1);
- _exit(lastval);
- zerr("exit returned in child!!");
- kill(getpid(), SIGKILL);
- return NULL;
-}
-
-/* read output of command substitution */
-
-/**/
-mod_export LinkList
-readoutput(int in, int qt, int *readerror)
-{
- LinkList ret;
- char *buf, *ptr;
- int bsiz, c, cnt = 0;
- FILE *fin;
- int q = queue_signal_level();
-
- fin = fdopen(in, "r");
- ret = newlinklist();
- ptr = buf = (char *) hcalloc(bsiz = 64);
- /*
- * We need to be sensitive to SIGCHLD else we can be
- * stuck forever with important processes unreaped.
- * The case that triggered this was where the exiting
- * process is group leader of the foreground process and we need
- * to reclaim the terminal else ^C doesn't work.
- */
- dont_queue_signals();
- child_unblock();
- while ((c = fgetc(fin)) != EOF || errno == EINTR) {
- if (c == EOF) {
- errno = 0;
- clearerr(fin);
- continue;
- }
- if (imeta(c)) {
- *ptr++ = Meta;
- c ^= 32;
- cnt++;
- }
- if (++cnt >= bsiz) {
- char *pp;
- queue_signals();
- pp = (char *) hcalloc(bsiz *= 2);
- dont_queue_signals();
-
- memcpy(pp, buf, cnt - 1);
- ptr = (buf = pp) + cnt - 1;
- }
- *ptr++ = c;
- }
- child_block();
- restore_queue_signals(q);
- if (readerror)
- *readerror = ferror(fin) ? errno : 0;
- fclose(fin);
- while (cnt && ptr[-1] == '\n')
- ptr--, cnt--;
- *ptr = '\0';
- if (qt) {
- if (!cnt) {
- *ptr++ = Nularg;
- *ptr = '\0';
- }
- addlinknode(ret, buf);
- } else {
- char **words = spacesplit(buf, 0, 1, 0);
-
- while (*words) {
- if (isset(GLOBSUBST))
- shtokenize(*words);
- addlinknode(ret, *words++);
- }
- }
- return ret;
-}
-
-/**/
-static Eprog
-parsecmd(char *cmd, char **eptr)
-{
- char *str;
- Eprog prog;
-
- for (str = cmd + 2; *str && *str != Outpar; str++);
- if (!*str || cmd[1] != Inpar) {
- /*
- * This can happen if the expression is being parsed
- * inside another construct, e.g. as a value within ${..:..} etc.
- * So print a proper error message instead of the not very
- * useful but traditional "oops".
- */
- char *errstr = dupstrpfx(cmd, 2);
- untokenize(errstr);
- zerr("unterminated `%s...)'", errstr);
- return NULL;
- }
- *str = '\0';
- if (eptr)
- *eptr = str+1;
- if (!(prog = parse_string(cmd + 2, 0))) {
- zerr("parse error in process substitution");
- return NULL;
- }
- return prog;
-}
-
-/* =(...) */
-
-/**/
-char *
-getoutputfile(char *cmd, char **eptr)
-{
- pid_t pid;
- char *nam;
- Eprog prog;
- int fd;
- char *s;
-
- if (thisjob == -1){
- zerr("process substitution %s cannot be used here", cmd);
- return NULL;
- }
- if (!(prog = parsecmd(cmd, eptr)))
- return NULL;
- if (!(nam = gettempname(NULL, 1)))
- return NULL;
-
- if ((s = simple_redir_name(prog, REDIR_HERESTR))) {
- /*
- * =(<<<stuff). Optimise a la $(<file). It's
- * effectively the reverse, converting a string into a file name
- * rather than vice versa.
- */
- singsub(&s);
- if (errflag)
- s = NULL;
- else
- untokenize(s);
- }
-
- if (!s) /* Unclear why we need to do this before open() */
- child_block(); /* but it has been so for a long time: leave it */
-
- if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) {
- zerr("process substitution failed: %e", errno);
- free(nam);
- if (!s)
- child_unblock();
- return NULL;
- } else {
- char *suffix = getsparam("TMPSUFFIX");
- if (suffix && *suffix && !strstr(suffix, "/")) {
- suffix = dyncat(nam, unmeta(suffix));
- if (link(nam, suffix) == 0) {
- addfilelist(nam, 0);
- nam = suffix;
- }
- }
- }
- addfilelist(nam, 0);
-
- if (s) {
- /* optimised here-string */
- int len;
- unmetafy(s, &len);
- write_loop(fd, s, len);
- close(fd);
- return nam;
- }
-
- if ((cmdoutpid = pid = zfork(NULL)) == -1) {
- /* fork or open error */
- child_unblock();
- return nam;
- } else if (pid) {
- int os;
-
- close(fd);
- os = jobtab[thisjob].stat;
- waitforpid(pid, 0);
- cmdoutval = 0;
- jobtab[thisjob].stat = os;
- return nam;
- }
-
- /* pid == 0 */
- redup(fd, 1);
- entersubsh(ESUB_PGRP|ESUB_NOMONITOR);
- cmdpush(CS_CMDSUBST);
- execode(prog, 0, 1, "equalsubst");
- cmdpop();
- close(1);
- _exit(lastval);
- zerr("exit returned in child!!");
- kill(getpid(), SIGKILL);
- return NULL;
-}
-
-#if !defined(PATH_DEV_FD) && defined(HAVE_FIFOS)
-/* get a temporary named pipe */
-
-static char *
-namedpipe(void)
-{
- char *tnam = gettempname(NULL, 1);
-
- if (!tnam) {
- zerr("failed to create named pipe: %e", errno);
- return NULL;
- }
-# ifdef HAVE_MKFIFO
- if (mkfifo(tnam, 0600) < 0){
-# else
- if (mknod(tnam, 0010600, 0) < 0){
-# endif
- zerr("failed to create named pipe: %s, %e", tnam, errno);
- return NULL;
- }
- return tnam;
-}
-#endif /* ! PATH_DEV_FD && HAVE_FIFOS */
-
-/* <(...) or >(...) */
-
-/**/
-char *
-getproc(char *cmd, char **eptr)
-{
-#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD)
- zerr("doesn't look like your system supports FIFOs.");
- return NULL;
-#else
- Eprog prog;
- int out = *cmd == Inang;
- char *pnam;
- pid_t pid;
- struct timeval bgtime;
-
-#ifndef PATH_DEV_FD
- int fd;
- if (thisjob == -1) {
- zerr("process substitution %s cannot be used here", cmd);
- return NULL;
- }
- if (!(pnam = namedpipe()))
- return NULL;
- if (!(prog = parsecmd(cmd, eptr)))
- return NULL;
- addfilelist(pnam, 0);
-
- if ((pid = zfork(&bgtime))) {
- if (pid == -1)
- return NULL;
- if (!out)
- addproc(pid, NULL, 1, &bgtime);
- procsubstpid = pid;
- return pnam;
- }
- closem(FDT_UNUSED, 0);
- fd = open(pnam, out ? O_WRONLY | O_NOCTTY : O_RDONLY | O_NOCTTY);
- if (fd == -1) {
- zerr("can't open %s: %e", pnam, errno);
- _exit(1);
- }
- entersubsh(ESUB_ASYNC|ESUB_PGRP);
- redup(fd, out);
-#else /* PATH_DEV_FD */
- int pipes[2], fd;
-
- if (thisjob == -1) {
- zerr("process substitution %s cannot be used here", cmd);
- return NULL;
- }
- pnam = zhalloc(strlen(PATH_DEV_FD) + 1 + DIGBUFSIZE);
- if (!(prog = parsecmd(cmd, eptr)))
- return NULL;
- if (mpipe(pipes) < 0)
- return NULL;
- if ((pid = zfork(&bgtime))) {
- sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]);
- zclose(pipes[out]);
- if (pid == -1)
- {
- zclose(pipes[!out]);
- return NULL;
- }
- fd = pipes[!out];
- fdtable[fd] = FDT_PROC_SUBST;
- addfilelist(NULL, fd);
- if (!out)
- {
- addproc(pid, NULL, 1, &bgtime);
- }
- procsubstpid = pid;
- return pnam;
- }
- entersubsh(ESUB_ASYNC|ESUB_PGRP);
- redup(pipes[out], out);
- closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */
-#endif /* PATH_DEV_FD */
-
- cmdpush(CS_CMDSUBST);
- execode(prog, 0, 1, out ? "outsubst" : "insubst");
- cmdpop();
- zclose(out);
- _exit(lastval);
- return NULL;
-#endif /* HAVE_FIFOS and PATH_DEV_FD not defined */
-}
-
-/*
- * > >(...) or < <(...) (does not use named pipes)
- *
- * If the second argument is 1, this is part of
- * an "exec < <(...)" or "exec > >(...)" and we shouldn't
- * wait for the job to finish before continuing.
- */
-
-/**/
-static int
-getpipe(char *cmd, int nullexec)
-{
- Eprog prog;
- int pipes[2], out = *cmd == Inang;
- pid_t pid;
- struct timeval bgtime;
- char *ends;
-
- if (!(prog = parsecmd(cmd, &ends)))
- return -1;
- if (*ends) {
- zerr("invalid syntax for process substitution in redirection");
- return -1;
- }
- if (mpipe(pipes) < 0)
- return -1;
- if ((pid = zfork(&bgtime))) {
- zclose(pipes[out]);
- if (pid == -1) {
- zclose(pipes[!out]);
- return -1;
- }
- if (!nullexec)
- addproc(pid, NULL, 1, &bgtime);
- procsubstpid = pid;
- return pipes[!out];
- }
- entersubsh(ESUB_PGRP);
- redup(pipes[out], out);
- closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */
- cmdpush(CS_CMDSUBST);
- execode(prog, 0, 1, out ? "outsubst" : "insubst");
- cmdpop();
- _exit(lastval);
- return 0;
-}
-
-/* open pipes with fds >= 10 */
-
-/**/
-static int
-mpipe(int *pp)
-{
- if (pipe(pp) < 0) {
- zerr("pipe failed: %e", errno);
- return -1;
- }
- pp[0] = movefd(pp[0]);
- pp[1] = movefd(pp[1]);
- return 0;
-}
-
-/*
- * Do process substitution with redirection
- *
- * If the second argument is 1, this is part of
- * an "exec < <(...)" or "exec > >(...)" and we shouldn't
- * wait for the job to finish before continuing.
- * Likewise, we shouldn't wait if we are opening the file
- * descriptor using the {fd}>>(...) notation since it stays
- * valid for subsequent commands.
- */
-
-/**/
-static void
-spawnpipes(LinkList l, int nullexec)
-{
- LinkNode n;
- Redir f;
- char *str;
-
- n = firstnode(l);
- for (; n; incnode(n)) {
- f = (Redir) getdata(n);
- if (f->type == REDIR_OUTPIPE || f->type == REDIR_INPIPE) {
- str = f->name;
- f->fd2 = getpipe(str, nullexec || f->varid);
- }
- }
-}
-
-/* evaluate a [[ ... ]] */
-
-/**/
-static int
-execcond(Estate state, UNUSED(int do_exec))
-{
- int stat;
-
- state->pc--;
- if (isset(XTRACE)) {
- printprompt4();
- fprintf(xtrerr, "[[");
- tracingcond++;
- }
- cmdpush(CS_COND);
- stat = evalcond(state, NULL);
- /*
- * 2 indicates a syntax error. For compatibility, turn this
- * into a shell error.
- */
- if (stat == 2)
- errflag |= ERRFLAG_ERROR;
- cmdpop();
- if (isset(XTRACE)) {
- fprintf(xtrerr, " ]]\n");
- fflush(xtrerr);
- tracingcond--;
- }
- return stat;
-}
-
-/* evaluate a ((...)) arithmetic command */
-
-/**/
-static int
-execarith(Estate state, UNUSED(int do_exec))
-{
- char *e;
- mnumber val = zero_mnumber;
- int htok = 0;
-
- if (isset(XTRACE)) {
- printprompt4();
- fprintf(xtrerr, "((");
- }
- cmdpush(CS_MATH);
- e = ecgetstr(state, EC_DUPTOK, &htok);
- if (htok)
- singsub(&e);
- if (isset(XTRACE))
- fprintf(xtrerr, " %s", e);
-
- val = matheval(e);
-
- cmdpop();
-
- if (isset(XTRACE)) {
- fprintf(xtrerr, " ))\n");
- fflush(xtrerr);
- }
- if (errflag) {
- errflag &= ~ERRFLAG_ERROR;
- return 2;
- }
- /* should test for fabs(val.u.d) < epsilon? */
- return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
-}
-
-/* perform time ... command */
-
-/**/
-static int
-exectime(Estate state, UNUSED(int do_exec))
-{
- int jb;
-
- jb = thisjob;
- if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
- shelltime();
- return 0;
- }
- execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
- thisjob = jb;
- return lastval;
-}
-
-/* Define a shell function */
-
-static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)";
-
-/**/
-static int
-execfuncdef(Estate state, Eprog redir_prog)
-{
- Shfunc shf;
- char *s = NULL;
- int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0;
- int anon_func = 0;
- Wordcode beg = state->pc, end;
- Eprog prog;
- Patprog *pp;
- LinkList names;
-
- end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
- names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
- nprg = end - beg;
- sbeg = *state->pc++;
- nstrs = *state->pc++;
- npats = *state->pc++;
-
- nprg = (end - state->pc);
- plen = nprg * sizeof(wordcode);
- len = plen + (npats * sizeof(Patprog)) + nstrs;
-
- if (htok && names) {
- execsubst(names);
- if (errflag) {
- state->pc = end;
- return 1;
- }
- }
-
- DPUTS(!names && redir_prog,
- "Passing redirection to anon function definition.");
- while (!names || (s = (char *) ugetnode(names))) {
- if (!names) {
- prog = (Eprog) zhalloc(sizeof(*prog));
- prog->nref = -1; /* on the heap */
- } else {
- prog = (Eprog) zalloc(sizeof(*prog));
- prog->nref = 1; /* allocated from permanent storage */
- }
- prog->npats = npats;
- prog->len = len;
- if (state->prog->dump || !names) {
- if (!names) {
- prog->flags = EF_HEAP;
- prog->dump = NULL;
- prog->pats = pp = (Patprog *) zhalloc(npats * sizeof(Patprog));
- } else {
- prog->flags = EF_MAP;
- incrdumpcount(state->prog->dump);
- prog->dump = state->prog->dump;
- prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
- }
- prog->prog = state->pc;
- prog->strs = state->strs + sbeg;
- } else {
- prog->flags = EF_REAL;
- prog->pats = pp = (Patprog *) zalloc(len);
- prog->prog = (Wordcode) (prog->pats + npats);
- prog->strs = (char *) (prog->prog + nprg);
- prog->dump = NULL;
- memcpy(prog->prog, state->pc, plen);
- memcpy(prog->strs, state->strs + sbeg, nstrs);
- }
- for (i = npats; i--; pp++)
- *pp = dummy_patprog1;
- prog->shf = NULL;
-
- shf = (Shfunc) zalloc(sizeof(*shf));
- shf->funcdef = prog;
- shf->node.flags = 0;
- /* No dircache here, not a directory */
- shf->filename = ztrdup(scriptfilename);
- shf->lineno =
- (funcstack && (funcstack->tp == FS_FUNC ||
- funcstack->tp == FS_EVAL)) ?
- funcstack->flineno + lineno :
- lineno;
- /*
- * redir_prog is permanently allocated --- but if
- * this function has multiple names we need an additional
- * one. Original redir_prog used with the last name
- * because earlier functions are freed in case of duplicate
- * names.
- */
- if (names && nonempty(names) && redir_prog)
- shf->redir = dupeprog(redir_prog, 0);
- else {
- shf->redir = redir_prog;
- redir_prog = 0;
- }
- shfunc_set_sticky(shf);
-
- if (!names) {
- /*
- * Anonymous function, execute immediately.
- * Function name is "(anon)".
- */
- LinkList args;
-
- anon_func = 1;
- shf->node.flags |= PM_ANONYMOUS;
-
- state->pc = end;
- end += *state->pc++;
- args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
-
- if (htok && args) {
- execsubst(args);
- if (errflag) {
- freeeprog(shf->funcdef);
- if (shf->redir) /* shouldn't be */
- freeeprog(shf->redir);
- dircache_set(&shf->filename, NULL);
- zfree(shf, sizeof(*shf));
- state->pc = end;
- return 1;
- }
- }
-
- setunderscore((args && nonempty(args)) ?
- ((char *) getdata(lastnode(args))) : "");
-
- if (!args)
- args = newlinklist();
- shf->node.nam = (char *) ANONYMOUS_FUNCTION_NAME;
- pushnode(args, shf->node.nam);
-
- execshfunc(shf, args);
- ret = lastval;
-
- if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
- lastval) {
-#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
- fprintf(stderr, "zsh: exit %lld\n", lastval);
-#else
- fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
-#endif
- fflush(stderr);
- }
-
- freeeprog(shf->funcdef);
- if (shf->redir) /* shouldn't be */
- freeeprog(shf->redir);
- dircache_set(&shf->filename, NULL);
- zfree(shf, sizeof(*shf));
- break;
- } else {
- /* is this shell function a signal trap? */
- if (!strncmp(s, "TRAP", 4) &&
- (signum = getsignum(s + 4)) != -1) {
- if (settrap(signum, NULL, ZSIG_FUNC)) {
- freeeprog(shf->funcdef);
- dircache_set(&shf->filename, NULL);
- zfree(shf, sizeof(*shf));
- state->pc = end;
- return 1;
- }
-
- /*
- * Remove the old node explicitly in case it has
- * an alternative name
- */
- removetrapnode(signum);
- }
- shfunctab->addnode(shfunctab, ztrdup(s), shf);
- }
- }
- if (!anon_func)
- setunderscore("");
- if (redir_prog) {
- /* For completeness, shouldn't happen */
- freeeprog(redir_prog);
- }
- state->pc = end;
- return ret;
-}
-
-/* Duplicate a sticky emulation */
-
-/**/
-
-mod_export Emulation_options
-sticky_emulation_dup(Emulation_options src, int useheap)
-{
- Emulation_options newsticky = useheap ?
- hcalloc(sizeof(*src)) : zshcalloc(sizeof(*src));
- newsticky->emulation = src->emulation;
- if (src->n_on_opts) {
- size_t sz = src->n_on_opts * sizeof(*src->on_opts);
- newsticky->n_on_opts = src->n_on_opts;
- newsticky->on_opts = useheap ? zhalloc(sz) : zalloc(sz);
- memcpy(newsticky->on_opts, src->on_opts, sz);
- }
- if (src->n_off_opts) {
- size_t sz = src->n_off_opts * sizeof(*src->off_opts);
- newsticky->n_off_opts = src->n_off_opts;
- newsticky->off_opts = useheap ? zhalloc(sz) : zalloc(sz);
- memcpy(newsticky->off_opts, src->off_opts, sz);
- }
-
- return newsticky;
-}
-
-/* Set the sticky emulation attributes for a shell function */
-
-/**/
-
-mod_export void
-shfunc_set_sticky(Shfunc shf)
-{
- if (sticky)
- shf->sticky = sticky_emulation_dup(sticky, 0);
- else
- shf->sticky = NULL;
-}
-
-
-/* Main entry point to execute a shell function. */
-
-/**/
-static void
-execshfunc(Shfunc shf, LinkList args)
-{
- LinkList last_file_list = NULL;
- unsigned char *ocs;
- int ocsp, osfc;
-
- if (errflag)
- return;
-
- /* thisjob may be invalid if we're called via execsimple: see execcursh */
- if (!list_pipe && thisjob != -1 && thisjob != list_pipe_job &&
- !hasprocs(thisjob)) {
- /* Without this deletejob the process table *
- * would be filled by a recursive function. */
- last_file_list = jobtab[thisjob].filelist;
- jobtab[thisjob].filelist = NULL;
- deletejob(jobtab + thisjob, 0);
- }
-
- if (isset(XTRACE)) {
- LinkNode lptr;
- printprompt4();
- if (args)
- for (lptr = firstnode(args); lptr; incnode(lptr)) {
- if (lptr != firstnode(args))
- fputc(' ', xtrerr);
- quotedzputs((char *)getdata(lptr), xtrerr);
- }
- fputc('\n', xtrerr);
- fflush(xtrerr);
- }
- queue_signals();
- ocs = cmdstack;
- ocsp = cmdsp;
- cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
- cmdsp = 0;
- if ((osfc = sfcontext) == SFC_NONE)
- sfcontext = SFC_DIRECT;
- xtrerr = stderr;
-
- doshfunc(shf, args, 0);
-
- sfcontext = osfc;
- free(cmdstack);
- cmdstack = ocs;
- cmdsp = ocsp;
-
- if (!list_pipe)
- deletefilelist(last_file_list, 0);
- unqueue_signals();
-}
-
-/*
- * Function to execute the special type of command that represents an
- * autoloaded shell function. The command structure tells us which
- * function it is. This function is actually called as part of the
- * execution of the autoloaded function itself, so when the function
- * has been autoloaded, its list is just run with no frills.
- *
- * There are two cases because if we are doing all-singing, all-dancing
- * non-simple code we load the shell function early in execcmd() (the
- * action also present in the non-basic version) to check if
- * there are redirections that need to be handled at that point.
- * Then we call execautofn_basic() to do the rest.
- */
-
-/**/
-static int
-execautofn_basic(Estate state, UNUSED(int do_exec))
-{
- Shfunc shf;
- char *oldscriptname, *oldscriptfilename;
-
- shf = state->prog->shf;
-
- /*
- * Probably we didn't know the filename where this function was
- * defined yet.
- */
- if (funcstack && !funcstack->filename)
- funcstack->filename = getshfuncfile(shf);
-
- oldscriptname = scriptname;
- oldscriptfilename = scriptfilename;
- scriptname = dupstring(shf->node.nam);
- scriptfilename = getshfuncfile(shf);
- execode(shf->funcdef, 1, 0, "loadautofunc");
- scriptname = oldscriptname;
- scriptfilename = oldscriptfilename;
-
- return lastval;
-}
-
-/**/
-static int
-execautofn(Estate state, UNUSED(int do_exec))
-{
- Shfunc shf;
-
- if (!(shf = loadautofn(state->prog->shf, 1, 0, 0)))
- return 1;
-
- state->prog->shf = shf;
- return execautofn_basic(state, 0);
-}
-
-/*
- * Helper function to install the source file name of a shell function
- * just autoloaded.
- *
- * We attempt to do this efficiently as the typical case is the
- * directory part is a well-known directory, which is cached, and
- * the non-directory part is the same as the node name.
- */
-
-/**/
-static void
-loadautofnsetfile(Shfunc shf, char *fdir)
-{
- /*
- * If shf->filename is already the load directory ---
- * keep it as we can still use it to get the load file.
- * This makes autoload with an absolute path particularly efficient.
- */
- if (!(shf->node.flags & PM_LOADDIR) ||
- strcmp(shf->filename, fdir) != 0) {
- /* Old directory name not useful... */
- dircache_set(&shf->filename, NULL);
- if (fdir) {
- /* ...can still cache directory */
- shf->node.flags |= PM_LOADDIR;
- dircache_set(&shf->filename, fdir);
- } else {
- /* ...no separate directory part to cache, for some reason. */
- shf->node.flags &= ~PM_LOADDIR;
- shf->filename = ztrdup(shf->node.nam);
- }
- }
-}
-
-/**/
-Shfunc
-loadautofn(Shfunc shf, int fksh, int autol, int current_fpath)
-{
- int noalias = noaliases, ksh = 1;
- Eprog prog;
- char *fdir; /* Directory path where func found */
-
- pushheap();
-
- noaliases = (shf->node.flags & PM_UNALIASED);
- if (shf->filename && shf->filename[0] == '/' &&
- (shf->node.flags & PM_LOADDIR))
- {
- char *spec_path[2];
- spec_path[0] = dupstring(shf->filename);
- spec_path[1] = NULL;
- prog = getfpfunc(shf->node.nam, &ksh, &fdir, spec_path, 0);
- if (prog == &dummy_eprog &&
- (current_fpath || (shf->node.flags & PM_CUR_FPATH)))
- prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
- }
- else
- prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
- noaliases = noalias;
-
- if (ksh == 1) {
- ksh = fksh;
- if (ksh == 1)
- ksh = (shf->node.flags & PM_KSHSTORED) ? 2 :
- (shf->node.flags & PM_ZSHSTORED) ? 0 : 1;
- }
-
- if (prog == &dummy_eprog) {
- /* We're not actually in the function; decrement locallevel */
- locallevel--;
- zwarn("%s: function definition file not found", shf->node.nam);
- locallevel++;
- popheap();
- return NULL;
- }
- if (!prog) {
- popheap();
- return NULL;
- }
- if (ksh == 2 || (ksh == 1 && isset(KSHAUTOLOAD))) {
- if (autol) {
- prog->flags |= EF_RUN;
-
- freeeprog(shf->funcdef);
- if (prog->flags & EF_MAP)
- shf->funcdef = prog;
- else
- shf->funcdef = dupeprog(prog, 0);
- shf->node.flags &= ~PM_UNDEFINED;
- loadautofnsetfile(shf, fdir);
- } else {
- VARARR(char, n, strlen(shf->node.nam) + 1);
- strcpy(n, shf->node.nam);
- execode(prog, 1, 0, "evalautofunc");
- shf = (Shfunc) shfunctab->getnode(shfunctab, n);
- if (!shf || (shf->node.flags & PM_UNDEFINED)) {
- /* We're not actually in the function; decrement locallevel */
- locallevel--;
- zwarn("%s: function not defined by file", n);
- locallevel++;
- popheap();
- return NULL;
- }
- }
- } else {
- freeeprog(shf->funcdef);
- if (prog->flags & EF_MAP)
- shf->funcdef = stripkshdef(prog, shf->node.nam);
- else
- shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0);
- shf->node.flags &= ~PM_UNDEFINED;
- loadautofnsetfile(shf, fdir);
- }
- popheap();
-
- return shf;
-}
-
-/*
- * Check if a sticky emulation differs from the current one.
- */
-
-/**/
-
-int sticky_emulation_differs(Emulation_options sticky2)
-{
- /* If no new sticky emulation, not a different emulation */
- if (!sticky2)
- return 0;
- /* If no current sticky emulation, different */
- if (!sticky)
- return 1;
- /* If basic emulation different, different */
- if (sticky->emulation != sticky2->emulation)
- return 1;
- /* If differing numbers of options, different */
- if (sticky->n_on_opts != sticky2->n_on_opts ||
- sticky->n_off_opts != sticky2->n_off_opts)
- return 1;
- /*
- * We need to compare option arrays, if non-null.
- * We made parseopts() create the list of options in option
- * order to make this easy.
- */
- /* If different options turned on, different */
- if (sticky->n_on_opts &&
- memcmp(sticky->on_opts, sticky2->on_opts,
- sticky->n_on_opts * sizeof(*sticky->on_opts)) != 0)
- return 1;
- /* If different options turned on, different */
- if (sticky->n_off_opts &&
- memcmp(sticky->off_opts, sticky2->off_opts,
- sticky->n_off_opts * sizeof(*sticky->off_opts)) != 0)
- return 1;
- return 0;
-}
-
-/*
- * execute a shell function
- *
- * name is the name of the function
- *
- * prog is the code to execute
- *
- * doshargs, if set, are parameters to pass to the function,
- * in which the first element is the function name (even if
- * FUNCTIONARGZERO is set as this is handled inside this function).
- *
- * If noreturnval is nonzero, then reset the current return
- * value (lastval) to its value before the shell function
- * was executed. However, in any case return the status value
- * from the function (i.e. if noreturnval is not set, this
- * will be the same as lastval).
- */
-
-/**/
-mod_export int
-doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
-{
- char **pptab, **x;
- int ret;
- char *name = shfunc->node.nam;
- int flags = shfunc->node.flags;
- char *fname = dupstring(name);
- Eprog prog;
- static int oflags;
- static int funcdepth;
- Heap funcheap;
-
- queue_signals(); /* Lots of memory and global state changes coming */
-
- NEWHEAPS(funcheap) {
- /*
- * Save data in heap rather than on stack to keep recursive
- * function cost down --- use of heap memory should be efficient
- * at this point. Saving is not actually massive.
- */
- Funcsave funcsave = zhalloc(sizeof(struct funcsave));
- funcsave->scriptname = scriptname;
- funcsave->argv0 = NULL;
- funcsave->breaks = breaks;
- funcsave->contflag = contflag;
- funcsave->loops = loops;
- funcsave->lastval = lastval;
- funcsave->pipestats = NULL;
- funcsave->numpipestats = numpipestats;
- funcsave->noerrexit = noerrexit;
- if (trap_state == TRAP_STATE_PRIMED)
- trap_return--;
- /*
- * Suppression of ERR_RETURN is turned off in function scope.
- */
- noerrexit &= ~NOERREXIT_RETURN;
- if (noreturnval) {
- /*
- * Easiest to use the heap here since we're bracketed
- * immediately by a pushheap/popheap pair.
- */
- size_t bytes = sizeof(int)*numpipestats;
- funcsave->pipestats = (int *)zhalloc(bytes);
- memcpy(funcsave->pipestats, pipestats, bytes);
- }
-
- starttrapscope();
- startpatternscope();
-
- pptab = pparams;
- if (!(flags & PM_UNDEFINED))
- scriptname = dupstring(name);
- funcsave->zoptind = zoptind;
- funcsave->optcind = optcind;
- if (!isset(POSIXBUILTINS)) {
- zoptind = 1;
- optcind = 0;
- }
-
- /* We need to save the current options even if LOCALOPTIONS is *
- * not currently set. That's because if it gets set in the *
- * function we need to restore the original options on exit. */
- memcpy(funcsave->opts, opts, sizeof(opts));
- funcsave->emulation = emulation;
- funcsave->sticky = sticky;
-
- if (sticky_emulation_differs(shfunc->sticky)) {
- /*
- * Function is marked for sticky emulation.
- * Enable it now.
- *
- * We deliberately do not do this if the sticky emulation
- * in effect is the same as that requested. This enables
- * option setting naturally within emulation environments.
- * Note that a difference in EMULATE_FULLY (emulate with
- * or without -R) counts as a different environment.
- *
- * This propagates the sticky emulation to subfunctions.
- */
- sticky = sticky_emulation_dup(shfunc->sticky, 1);
- emulation = sticky->emulation;
- funcsave->restore_sticky = 1;
- installemulation(emulation, opts);
- if (sticky->n_on_opts) {
- OptIndex *onptr;
- for (onptr = sticky->on_opts;
- onptr < sticky->on_opts + sticky->n_on_opts;
- onptr++)
- opts[*onptr] = 1;
- }
- if (sticky->n_off_opts) {
- OptIndex *offptr;
- for (offptr = sticky->off_opts;
- offptr < sticky->off_opts + sticky->n_off_opts;
- offptr++)
- opts[*offptr] = 0;
- }
- /* All emulations start with pattern disables clear */
- clearpatterndisables();
- } else
- funcsave->restore_sticky = 0;
-
- if (flags & (PM_TAGGED|PM_TAGGED_LOCAL))
- opts[XTRACE] = 1;
- else if (oflags & PM_TAGGED_LOCAL) {
- if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME /* pointer comparison */)
- flags |= PM_TAGGED_LOCAL;
- else
- opts[XTRACE] = 0;
- }
- if (flags & PM_WARNNESTED)
- opts[WARNNESTEDVAR] = 1;
- else if (oflags & PM_WARNNESTED) {
- if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME)
- flags |= PM_WARNNESTED;
- else
- opts[WARNNESTEDVAR] = 0;
- }
- funcsave->oflags = oflags;
- /*
- * oflags is static, because we compare it on the next recursive
- * call. Hence also we maintain a saved version for restoring
- * the previous value of oflags after the call.
- */
- oflags = flags;
- opts[PRINTEXITVALUE] = 0;
- if (doshargs) {
- LinkNode node;
-
- node = firstnode(doshargs);
- pparams = x = (char **) zshcalloc(((sizeof *x) *
- (1 + countlinknodes(doshargs))));
- if (isset(FUNCTIONARGZERO)) {
- funcsave->argv0 = argzero;
- argzero = ztrdup(getdata(node));
- }
- /* first node contains name regardless of option */
- node = node->next;
- for (; node; node = node->next, x++)
- *x = ztrdup(getdata(node));
- } else {
- pparams = (char **) zshcalloc(sizeof *pparams);
- if (isset(FUNCTIONARGZERO)) {
- funcsave->argv0 = argzero;
- argzero = ztrdup(argzero);
- }
- }
- ++funcdepth;
- if (zsh_funcnest >= 0 && funcdepth > zsh_funcnest) {
- zerr("maximum nested function level reached; increase FUNCNEST?");
- lastval = 1;
- goto undoshfunc;
- }
- funcsave->fstack.name = dupstring(name);
- /*
- * The caller is whatever is immediately before on the stack,
- * unless we're at the top, in which case it's the script
- * or interactive shell name.
- */
- funcsave->fstack.caller = funcstack ? funcstack->name :
- dupstring(funcsave->argv0 ? funcsave->argv0 : argzero);
- funcsave->fstack.lineno = lineno;
- funcsave->fstack.prev = funcstack;
- funcsave->fstack.tp = FS_FUNC;
- funcstack = &funcsave->fstack;
-
- funcsave->fstack.flineno = shfunc->lineno;
- funcsave->fstack.filename = getshfuncfile(shfunc);
-
- prog = shfunc->funcdef;
- if (prog->flags & EF_RUN) {
- Shfunc shf;
-
- prog->flags &= ~EF_RUN;
-
- runshfunc(prog, NULL, funcsave->fstack.name);
-
- if (!(shf = (Shfunc) shfunctab->getnode(shfunctab,
- (name = fname)))) {
- zwarn("%s: function not defined by file", name);
- if (noreturnval)
- errflag |= ERRFLAG_ERROR;
- else
- lastval = 1;
- goto doneshfunc;
- }
- prog = shf->funcdef;
- }
- runshfunc(prog, wrappers, funcsave->fstack.name);
- doneshfunc:
- funcstack = funcsave->fstack.prev;
- undoshfunc:
- --funcdepth;
- if (retflag) {
- /*
- * This function is forced to return.
- */
- retflag = 0;
- /*
- * The calling function isn't necessarily forced to return,
- * but it should be made sensitive to ERR_EXIT and
- * ERR_RETURN as the assumptions we made at the end of
- * constructs within this function no longer apply. If
- * there are cases where this is not true, they need adding
- * to C03traps.ztst.
- */
- this_noerrexit = 0;
- breaks = funcsave->breaks;
- }
- freearray(pparams);
- if (funcsave->argv0) {
- zsfree(argzero);
- argzero = funcsave->argv0;
- }
- pparams = pptab;
- if (!isset(POSIXBUILTINS)) {
- zoptind = funcsave->zoptind;
- optcind = funcsave->optcind;
- }
- scriptname = funcsave->scriptname;
- oflags = funcsave->oflags;
-
- endpatternscope(); /* before restoring old LOCALPATTERNS */
-
- if (funcsave->restore_sticky) {
- /*
- * If we switched to an emulation environment just for
- * this function, we interpret the option and emulation
- * switch as being a firewall between environments.
- */
- memcpy(opts, funcsave->opts, sizeof(opts));
- emulation = funcsave->emulation;
- sticky = funcsave->sticky;
- } else if (isset(LOCALOPTIONS)) {
- /* restore all shell options except PRIVILEGED and RESTRICTED */
- funcsave->opts[PRIVILEGED] = opts[PRIVILEGED];
- funcsave->opts[RESTRICTED] = opts[RESTRICTED];
- memcpy(opts, funcsave->opts, sizeof(opts));
- emulation = funcsave->emulation;
- } else {
- /* just restore a couple. */
- opts[XTRACE] = funcsave->opts[XTRACE];
- opts[PRINTEXITVALUE] = funcsave->opts[PRINTEXITVALUE];
- opts[LOCALOPTIONS] = funcsave->opts[LOCALOPTIONS];
- opts[LOCALLOOPS] = funcsave->opts[LOCALLOOPS];
- opts[WARNNESTEDVAR] = funcsave->opts[WARNNESTEDVAR];
- }
-
- if (opts[LOCALLOOPS]) {
- if (contflag)
- zwarn("`continue' active at end of function scope");
- if (breaks)
- zwarn("`break' active at end of function scope");
- breaks = funcsave->breaks;
- contflag = funcsave->contflag;
- loops = funcsave->loops;
- }
-
- endtrapscope();
-
- if (trap_state == TRAP_STATE_PRIMED)
- trap_return++;
- ret = lastval;
- noerrexit = funcsave->noerrexit;
- if (noreturnval) {
- lastval = funcsave->lastval;
- numpipestats = funcsave->numpipestats;
- memcpy(pipestats, funcsave->pipestats, sizeof(int)*numpipestats);
- }
- } OLDHEAPS;
-
- unqueue_signals();
-
- /*
- * Exit with a tidy up.
- * Only leave if we're at the end of the appropriate function ---
- * not a nested function. As we usually skip the function body,
- * the only likely case where we need that second test is
- * when we have an "always" block. The endparamscope() has
- * already happened, hence the "+1" here.
- *
- * If we are in an exit trap, finish it first... we wouldn't set
- * exit_pending if we were already in one.
- */
- if (exit_pending && exit_level >= locallevel+1 && !in_exit_trap) {
- if (locallevel > forklevel) {
- /* Still functions to return: force them to do so. */
- retflag = 1;
- breaks = loops;
- } else {
- /*
- * All functions finished: time to exit the shell.
- * We already did the `stopmsg' test when the
- * exit command was handled.
- */
- stopmsg = 1;
- zexit(exit_pending >> 1, 0);
- }
- }
-
- return ret;
-}
-
-/* This finally executes a shell function and any function wrappers *
- * defined by modules. This works by calling the wrapper function which *
- * in turn has to call back this function with the arguments it gets. */
-
-/**/
-mod_export void
-runshfunc(Eprog prog, FuncWrap wrap, char *name)
-{
- int cont, ouu;
- char *ou;
-
- queue_signals();
-
- ou = zalloc(ouu = underscoreused);
- if (ou)
- memcpy(ou, zunderscore, underscoreused);
-
- while (wrap) {
- wrap->module->wrapper++;
- cont = wrap->handler(prog, wrap->next, name);
- wrap->module->wrapper--;
-
- if (!wrap->module->wrapper &&
- (wrap->module->node.flags & MOD_UNLOAD))
- unload_module(wrap->module);
-
- if (!cont) {
- if (ou)
- zfree(ou, ouu);
- unqueue_signals();
- return;
- }
- wrap = wrap->next;
- }
- startparamscope();
- execode(prog, 1, 0, "shfunc"); /* handles signal unqueueing */
- if (ou) {
- setunderscore(ou);
- zfree(ou, ouu);
- }
- endparamscope();
-
- unqueue_signals();
-}
-
-/*
- * Search fpath for an undefined function. Finds the file, and returns the
- * list of its contents.
- *
- * If test is 0, load the function.
- *
- * If test_only is 1, don't load function, just test for it:
- * Non-null return means function was found
- *
- * *fdir points to path at which found (as passed in, not duplicated)
- */
-
-/**/
-Eprog
-getfpfunc(char *s, int *ksh, char **fdir, char **alt_path, int test_only)
-{
- char **pp, buf[PATH_MAX+1];
- off_t len;
- off_t rlen;
- char *d;
- Eprog r;
- int fd;
-
- pp = alt_path ? alt_path : fpath;
- for (; *pp; pp++) {
- if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX)
- continue;
- if (**pp)
- sprintf(buf, "%s/%s", *pp, s);
- else
- strcpy(buf, s);
- if ((r = try_dump_file(*pp, s, buf, ksh, test_only))) {
- if (fdir)
- *fdir = *pp;
- return r;
- }
- unmetafy(buf, NULL);
- if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) {
- struct stat st;
- if (!fstat(fd, &st) && S_ISREG(st.st_mode) &&
- (len = lseek(fd, 0, 2)) != -1) {
- if (test_only) {
- close(fd);
- if (fdir)
- *fdir = *pp;
- return &dummy_eprog;
- }
- d = (char *) zalloc(len + 1);
- lseek(fd, 0, 0);
- if ((rlen = read(fd, d, len)) >= 0) {
- char *oldscriptname = scriptname;
-
- close(fd);
- d[rlen] = '\0';
- d = metafy(d, rlen, META_REALLOC);
-
- scriptname = dupstring(s);
- r = parse_string(d, 1);
- scriptname = oldscriptname;
-
- if (fdir)
- *fdir = *pp;
-
- zfree(d, len + 1);
-
- return r;
- } else
- close(fd);
-
- zfree(d, len + 1);
- } else
- close(fd);
- }
- }
- return test_only ? NULL : &dummy_eprog;
-}
-
-/* Handle the most common type of ksh-style autoloading, when doing a *
- * zsh-style autoload. Given the list read from an autoload file, and the *
- * name of the function being defined, check to see if the file consists *
- * entirely of a single definition for that function. If so, use the *
- * contents of that definition. Otherwise, use the entire file. */
-
-/**/
-Eprog
-stripkshdef(Eprog prog, char *name)
-{
- Wordcode pc;
- wordcode code;
- char *ptr1, *ptr2;
-
- if (!prog)
- return NULL;
- pc = prog->prog;
- code = *pc++;
- if (wc_code(code) != WC_LIST ||
- (WC_LIST_TYPE(code) & (Z_SYNC|Z_END|Z_SIMPLE)) != (Z_SYNC|Z_END|Z_SIMPLE))
- return prog;
- pc++;
- code = *pc++;
- if (wc_code(code) != WC_FUNCDEF || *pc != 1)
- return prog;
-
- /*
- * See if name of function requested (name) is same as
- * name of function in word code. name may still have "-"
- * tokenised. The word code shouldn't, as function names should be
- * untokenised, but reports say it sometimes does.
- */
- ptr1 = name;
- ptr2 = ecrawstr(prog, pc + 1, NULL);
- while (*ptr1 && *ptr2) {
- if (*ptr1 != *ptr2 && *ptr1 != Dash && *ptr1 != '-' &&
- *ptr2 != Dash && *ptr2 != '-')
- break;
- ptr1++;
- ptr2++;
- }
- if (*ptr1 || *ptr2)
- return prog;
-
- {
- Eprog ret;
- Wordcode end = pc + WC_FUNCDEF_SKIP(code);
- int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i;
- Patprog *pp;
-
- pc += 5;
-
- nprg = end - pc;
- plen = nprg * sizeof(wordcode);
- len = plen + (npats * sizeof(Patprog)) + nstrs;
-
- if (prog->flags & EF_MAP) {
- ret = prog;
- free(prog->pats);
- ret->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
- ret->prog = pc;
- ret->strs = prog->strs + sbeg;
- } else {
- ret = (Eprog) zhalloc(sizeof(*ret));
- ret->flags = EF_HEAP;
- ret->pats = pp = (Patprog *) zhalloc(len);
- ret->prog = (Wordcode) (ret->pats + npats);
- ret->strs = (char *) (ret->prog + nprg);
- memcpy(ret->prog, pc, plen);
- memcpy(ret->strs, prog->strs + sbeg, nstrs);
- ret->dump = NULL;
- }
- ret->len = len;
- ret->npats = npats;
- for (i = npats; i--; pp++)
- *pp = dummy_patprog1;
- ret->shf = NULL;
-
- return ret;
- }
-}
-
-/* check to see if AUTOCD applies here */
-
-/**/
-static char *
-cancd(char *s)
-{
- int nocdpath = s[0] == '.' &&
- (s[1] == '/' || !s[1] || (s[1] == '.' && (s[2] == '/' || !s[1])));
- char *t;
-
- if (*s != '/') {
- char sbuf[PATH_MAX+1], **cp;
-
- if (cancd2(s))
- return s;
- if (access(unmeta(s), X_OK) == 0)
- return NULL;
- if (!nocdpath)
- for (cp = cdpath; *cp; cp++) {
- if (strlen(*cp) + strlen(s) + 1 >= PATH_MAX)
- continue;
- if (**cp)
- sprintf(sbuf, "%s/%s", *cp, s);
- else
- strcpy(sbuf, s);
- if (cancd2(sbuf)) {
- doprintdir = -1;
- return dupstring(sbuf);
- }
- }
- if ((t = cd_able_vars(s))) {
- if (cancd2(t)) {
- doprintdir = -1;
- return t;
- }
- }
- return NULL;
- }
- return cancd2(s) ? s : NULL;
-}
-
-/**/
-static int
-cancd2(char *s)
-{
- struct stat buf;
- char *us, *us2 = NULL;
- int ret;
-
- /*
- * If CHASEDOTS and CHASELINKS are not set, we want to rationalize the
- * path by removing foo/.. combinations in the logical rather than
- * the physical path. If either is set, we test the physical path.
- */
- if (!isset(CHASEDOTS) && !isset(CHASELINKS)) {
- if (*s != '/')
- us = tricat(pwd[1] ? pwd : "", "/", s);
- else
- us = ztrdup(s);
- fixdir(us2 = us);
- } else
- us = unmeta(s);
- ret = !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode));
- if (us2)
- free(us2);
- return ret;
-}
-
-/**/
-void
-execsave(void)
-{
- struct execstack *es;
-
- es = (struct execstack *) zalloc(sizeof(struct execstack));
- es->list_pipe_pid = list_pipe_pid;
- es->nowait = nowait;
- es->pline_level = pline_level;
- es->list_pipe_child = list_pipe_child;
- es->list_pipe_job = list_pipe_job;
- strcpy(es->list_pipe_text, list_pipe_text);
- es->lastval = lastval;
- es->noeval = noeval;
- es->badcshglob = badcshglob;
- es->cmdoutpid = cmdoutpid;
- es->cmdoutval = cmdoutval;
- es->use_cmdoutval = use_cmdoutval;
- es->procsubstpid = procsubstpid;
- es->trap_return = trap_return;
- es->trap_state = trap_state;
- es->trapisfunc = trapisfunc;
- es->traplocallevel = traplocallevel;
- es->noerrs = noerrs;
- es->this_noerrexit = this_noerrexit;
- es->underscore = ztrdup(zunderscore);
- es->next = exstack;
- exstack = es;
- noerrs = cmdoutpid = 0;
-}
-
-/**/
-void
-execrestore(void)
-{
- struct execstack *en = exstack;
-
- DPUTS(!exstack, "BUG: execrestore() without execsave()");
-
- queue_signals();
- exstack = exstack->next;
-
- list_pipe_pid = en->list_pipe_pid;
- nowait = en->nowait;
- pline_level = en->pline_level;
- list_pipe_child = en->list_pipe_child;
- list_pipe_job = en->list_pipe_job;
- strcpy(list_pipe_text, en->list_pipe_text);
- lastval = en->lastval;
- noeval = en->noeval;
- badcshglob = en->badcshglob;
- cmdoutpid = en->cmdoutpid;
- cmdoutval = en->cmdoutval;
- use_cmdoutval = en->use_cmdoutval;
- procsubstpid = en->procsubstpid;
- trap_return = en->trap_return;
- trap_state = en->trap_state;
- trapisfunc = en->trapisfunc;
- traplocallevel = en->traplocallevel;
- noerrs = en->noerrs;
- this_noerrexit = en->this_noerrexit;
- setunderscore(en->underscore);
- zsfree(en->underscore);
- free(en);
-
- unqueue_signals();
-}