summaryrefslogtreecommitdiff
path: root/Src/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c2965
1 files changed, 2965 insertions, 0 deletions
diff --git a/Src/exec.c b/Src/exec.c
new file mode 100644
index 000000000..1b355d028
--- /dev/null
+++ b/Src/exec.c
@@ -0,0 +1,2965 @@
+/*
+ * 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"
+
+/* used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. */
+
+/**/
+int noerrexit;
+
+/* suppress error messages */
+
+/**/
+int noerrs;
+
+/* do not save history on exec and exit */
+
+/**/
+int nohistsave;
+
+/* error/break flag */
+
+/**/
+int errflag;
+
+/* Status of return from a trap */
+
+/**/
+int trapreturn;
+
+/* != 0 if this is a subshell */
+
+/**/
+int subsh;
+
+/* != 0 if we have a return pending */
+
+/**/
+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. */
+
+/**/
+char *fdtable;
+
+/* The allocated size of fdtable */
+
+/**/
+int fdtable_size;
+
+/* The highest fd that marked with nonzero in fdtable */
+
+/**/
+int max_zsh_fd;
+
+/* input fd from the coprocess */
+
+/**/
+int coprocin;
+
+/* output fd from the coprocess */
+
+/**/
+int coprocout;
+
+/* != 0 if the line editor is active */
+
+/**/
+int zleactive;
+
+/* pid of process undergoing 'process substitution' */
+
+/**/
+pid_t cmdoutpid;
+
+/* exit status of process undergoing 'process substitution' */
+
+/**/
+int cmdoutval;
+
+/* Stack to save some variables before executing a signal handler function */
+
+/**/
+struct execstack *exstack;
+
+#define execerr() if (!forked) { lastval = 1; return; } else _exit(1)
+
+static LinkList args;
+static int doneps4;
+
+/* parse string into a list */
+
+/**/
+List
+parse_string(char *s)
+{
+ List l;
+
+ lexsave();
+ inpush(s, 0, NULL);
+ strinbeg();
+ stophist = 2;
+ l = parse_list();
+ strinend();
+ inpop();
+ lexrestore();
+ return l;
+}
+
+#ifdef HAVE_GETRLIMIT
+
+/* the resource limits for the shell and its children */
+
+/**/
+struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS];
+
+/**/
+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", NULL, errno);
+ return -1;
+ }
+ current_limits[limnum] = limits[limnum];
+ }
+ return 0;
+}
+
+/**/
+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(void)
+{
+ pid_t pid;
+
+ if (thisjob >= MAXJOB - 1) {
+ zerr("job table full", NULL, 0);
+ return -1;
+ }
+ pid = fork();
+ if (pid == -1) {
+ zerr("fork failed: %e", NULL, errno);
+ return -1;
+ }
+#ifdef HAVE_GETRLIMIT
+ if (!pid)
+ /* set resource limits for the child process */
+ setlimits(NULL);
+#endif
+ return pid;
+}
+
+
+/**/
+int list_pipe = 0, simple_pline = 0;
+
+static pid_t list_pipe_pid;
+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(Cmd cmd)
+{
+ if (!list_pipe)
+ deletejob(jobtab + thisjob);
+ execlist(cmd->u.list, 1, cmd->flags & CFLAG_EXEC);
+ cmd->u.list = NULL;
+ return lastval;
+}
+
+/* execve after handling $_ and #! */
+
+#define POUNDBANGLIMIT 64
+
+/**/
+static int
+zexecve(char *pth, char **argv)
+{
+ int eno;
+ static char buf[PATH_MAX * 2];
+ char **eep;
+
+ unmetafy(pth, NULL);
+ for (eep = argv; *eep; eep++)
+ if (*eep != pth)
+ unmetafy(*eep, NULL);
+ for (eep = environ; *eep; eep++)
+ if (**eep == '_' && (*eep)[1] == '=')
+ break;
+ buf[0] = '_';
+ buf[1] = '=';
+ if (*pth == '/')
+ strcpy(buf + 2, pth);
+ else
+ sprintf(buf + 2, "%s/%s", pwd, pth);
+ if (!*eep)
+ eep[1] = NULL;
+ *eep = buf;
+ execve(pth, argv, environ);
+
+ /* 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) {
+ char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0;
+ int fd, ct, t0;
+
+ if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) {
+ argv0 = *argv;
+ *argv = pth;
+ 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')
+ execvebuf[t0] = '\0';
+ execvebuf[POUNDBANGLIMIT] = '\0';
+ for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
+ for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
+ if (*ptr) {
+ *ptr = '\0';
+ argv[-2] = ptr2;
+ argv[-1] = ptr + 1;
+ execve(ptr2, argv - 2, environ);
+ } else {
+ argv[-1] = ptr2;
+ execve(ptr2, argv - 1, environ);
+ }
+ } else {
+ argv[-1] = "sh";
+ execve("/bin/sh", argv - 1, environ);
+ }
+ } else {
+ for (t0 = 0; t0 != ct; t0++)
+ if (!execvebuf[t0])
+ break;
+ if (t0 == ct) {
+ argv[-1] = "sh";
+ execve("/bin/sh", argv - 1, environ);
+ }
+ }
+ } 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);
+}
+
+/* execute an external command */
+
+/**/
+void
+execute(Cmdnam not_used_yet, int dash)
+{
+ Cmdnam cn;
+ static LinkList exargs;
+ char buf[MAXCMDLEN], buf2[MAXCMDLEN];
+ char *s, *z, *arg0;
+ char **argv, **pp;
+ int eno = 0, ee;
+
+ arg0 = (char *) peekfirst(args);
+ if (isset(RESTRICTED) && strchr(arg0, '/')) {
+ zerr("%s: restricted", arg0, 0);
+ _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 (!exargs && (s = zgetenv("STTY")) && isatty(0)) {
+ char *t;
+
+ exargs = args; /* this prevents infinite recursion */
+ args = NULL;
+ t = tricat("stty", " ", s);
+ execstring(t, 1, 0);
+ zsfree(t);
+ args = exargs;
+ exargs = NULL;
+ }
+
+ cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
+
+ /* 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));
+ delenv(z - 6);
+ } else if (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);
+ child_unblock();
+ if ((int) strlen(arg0) >= PATH_MAX) {
+ zerr("command too long: %s", arg0, 0);
+ _exit(1);
+ }
+ for (s = arg0; *s; s++)
+ if (*s == '/') {
+ errno = zexecve(arg0, argv);
+ if (arg0 == s || unset(PATHDIRS) ||
+ (arg0[0] == '.' && (arg0 + 1 == s ||
+ (arg0[1] == '.' && arg0 + 2 == s)))) {
+ zerr("%e: %s", arg0, errno);
+ _exit(1);
+ }
+ break;
+ }
+
+ if (cn) {
+ char nn[PATH_MAX], *dptr;
+
+ if (cn->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);
+ if (isgooderr(ee, *pp))
+ eno = ee;
+ } else if (**pp != '/') {
+ z = buf;
+ strucpy(&z, *pp);
+ *z++ = '/';
+ strcpy(z, arg0);
+ ee = zexecve(buf, argv);
+ if (isgooderr(ee, *pp))
+ eno = ee;
+ }
+ strcpy(nn, cn->u.name ? *(cn->u.name) : "");
+ strcat(nn, "/");
+ strcat(nn, cn->nam);
+ }
+ ee = zexecve(nn, argv);
+
+ 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);
+ if (isgooderr(ee, *pp))
+ eno = ee;
+ } else {
+ z = buf;
+ strucpy(&z, *pp);
+ *z++ = '/';
+ strcpy(z, arg0);
+ ee = zexecve(buf, argv);
+ if (isgooderr(ee, *pp))
+ eno = ee;
+ }
+ if (eno)
+ zerr("%e: %s", arg0, eno);
+ else
+ zerr("command not found: %s", arg0, 0);
+ _exit(1);
+}
+
+#define try(X) { if (iscom(X)) return ztrdup(X); }
+
+/* get the full pathname of an external command */
+
+/**/
+char *
+findcmd(char *arg0)
+{
+ char **pp;
+ char *z, *s, buf[MAXCMDLEN];
+ Cmdnam cn;
+
+ cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
+ if (!cn && isset(HASHCMDS))
+ cn = hashcmd(arg0, path);
+ if ((int) strlen(arg0) > PATH_MAX)
+ return NULL;
+ for (s = arg0; *s; s++)
+ if (*s == '/') {
+ try(arg0);
+ if (arg0 == s || unset(PATHDIRS)) {
+ return NULL;
+ }
+ break;
+ }
+ if (cn) {
+ char nn[PATH_MAX];
+
+ if (cn->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);
+ try(buf);
+ }
+ strcpy(nn, cn->u.name ? *(cn->u.name) : "");
+ strcat(nn, "/");
+ strcat(nn, cn->nam);
+ }
+ try(nn);
+ }
+ for (pp = path; *pp; pp++) {
+ z = buf;
+ if (**pp) {
+ strucpy(&z, *pp);
+ *z++ = '/';
+ }
+ strcpy(z, arg0);
+ try(buf);
+ }
+ return NULL;
+}
+
+/**/
+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];
+
+ strcpy(fullnam, cn->u.name ? *(cn->u.name) : "");
+ strcat(fullnam, "/");
+ strcat(fullnam, cn->nam);
+ return iscom(fullnam);
+}
+
+/**/
+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;
+}
+
+/**/
+Cmdnam
+hashcmd(char *arg0, char **pp)
+{
+ Cmdnam cn;
+ char *s, buf[PATH_MAX];
+ char **pq;
+
+ for (; *pp; pp++)
+ if (**pp == '/') {
+ s = buf;
+ strucpy(&s, *pp);
+ *s++ = '/';
+ if ((s - buf) + strlen(arg0) >= PATH_MAX)
+ continue;
+ strcpy(s, arg0);
+ if (iscom(buf))
+ break;
+ }
+
+ if (!*pp)
+ return NULL;
+
+ cn = (Cmdnam) zcalloc(sizeof *cn);
+ cn->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;
+}
+
+/* execute a string */
+
+/**/
+void
+execstring(char *s, int dont_change_job, int exiting)
+{
+ List list;
+
+ pushheap();
+ if ((list = parse_string(s)))
+ execlist(list, dont_change_job, exiting);
+ popheap();
+}
+
+/* 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(List list, int dont_change_job, int exiting)
+{
+ Sublist slist;
+ static int donetrap;
+ int ret, cj;
+ int old_pline_level, old_list_pipe;
+
+ cj = thisjob;
+ old_pline_level = pline_level;
+ old_list_pipe = list_pipe;
+
+ if (sourcelevel && unset(SHINSTDIN))
+ pline_level = list_pipe = 0;
+
+ /* Loop over all sets of comands separated by newline, *
+ * semi-colon or ampersand (`sublists'). */
+ while (list && list != &dummy_list && !breaks && !retflag) {
+ /* Reset donetrap: this ensures that a trap is only *
+ * called once for each sublist that fails. */
+ donetrap = 0;
+ simplifyright(list);
+ slist = list->left;
+
+ /* Loop through code followed by &&, ||, or end of sublist. */
+ while (slist) {
+ switch (slist->type) {
+ case END:
+ /* End of sublist; just execute, ignoring status. */
+ execpline(slist, list->type, !list->right && exiting);
+ goto sublist_done;
+ break;
+ case ANDNEXT:
+ /* If the return code is non-zero, we skip pipelines until *
+ * we find a sublist followed by ORNEXT. */
+ if ((ret = execpline(slist, Z_SYNC, 0))) {
+ while ((slist = slist->right))
+ if (slist->type == ORNEXT)
+ break;
+ if (!slist) {
+ /* We've skipped to the end of the list, not executing *
+ * the final pipeline, so don't perform error handling *
+ * for this sublist. */
+ donetrap = 1;
+ goto sublist_done;
+ }
+ }
+ break;
+ case ORNEXT:
+ /* If the return code is zero, we skip pipelines until *
+ * we find a sublist followed by ANDNEXT. */
+ if (!(ret = execpline(slist, Z_SYNC, 0))) {
+ while ((slist = slist->right))
+ if (slist->type == ANDNEXT)
+ break;
+ if (!slist) {
+ /* We've skipped to the end of the list, not executing *
+ * the final pipeline, so don't perform error handling *
+ * for this sublist. */
+ donetrap = 1;
+ goto sublist_done;
+ }
+ }
+ break;
+ }
+ slist = slist->right;
+ }
+sublist_done:
+
+ if (sigtrapped[SIGDEBUG])
+ dotrap(SIGDEBUG);
+
+ /* Check whether we are suppressing traps/errexit *
+ * (typically in init scripts) and if we haven't *
+ * already performed them for this sublist. */
+ if (!noerrexit && !donetrap) {
+ if (sigtrapped[SIGZERR] && lastval) {
+ dotrap(SIGZERR);
+ donetrap = 1;
+ }
+ if (lastval && isset(ERREXIT)) {
+ if (sigtrapped[SIGEXIT])
+ dotrap(SIGEXIT);
+ if (mypid != getpid())
+ _exit(lastval);
+ else
+ exit(lastval);
+ }
+ }
+
+ list = list->right;
+ }
+
+ pline_level = old_pline_level;
+ list_pipe = old_list_pipe;
+ if (dont_change_job)
+ thisjob = cj;
+}
+
+/* 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(Sublist l, int how, int last1)
+{
+ int ipipe[2], opipe[2];
+ int pj, newjob;
+ int old_simple_pline = simple_pline;
+ static int lastwj;
+
+ if (!l->left)
+ return lastval = (l->flags & PFLAG_NOT) != 0;
+
+ pj = thisjob;
+ ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
+ child_block();
+
+ /* get free entry in job table and initialize it */
+ if ((thisjob = newjob = initjob()) == -1)
+ return 1;
+ if (how & Z_TIMED)
+ jobtab[thisjob].stat |= STAT_TIMED;
+
+ if (l->flags & PFLAG_COPROC) {
+ how = Z_ASYNC;
+ if (coprocin >= 0) {
+ zclose(coprocin);
+ zclose(coprocout);
+ }
+ mpipe(ipipe);
+ mpipe(opipe);
+ coprocin = ipipe[0];
+ coprocout = opipe[1];
+ fdtable[coprocin] = fdtable[coprocout] = 0;
+ }
+ if (!pline_level++) {
+ list_pipe_job = newjob;
+ nowait = 0;
+ }
+ list_pipe_pid = lastwj = 0;
+ if (pline_level == 1)
+ simple_pline = (l->left->type == END);
+ execpline2(l->left, how, opipe[0], ipipe[1], last1);
+ pline_level--;
+ if (how & Z_ASYNC) {
+ lastwj = newjob;
+ jobtab[thisjob].stat |= STAT_NOSTTY;
+ if (l->flags & PFLAG_COPROC)
+ zclose(ipipe[1]);
+ if (how & Z_DISOWN) {
+ deletejob(jobtab + thisjob);
+ thisjob = -1;
+ }
+ else
+ spawnjob();
+ child_unblock();
+ return 0;
+ } else {
+ if (newjob != lastwj) {
+ Job jn = jobtab + newjob;
+
+ if (newjob == list_pipe_job && list_pipe_child)
+ _exit(0);
+
+ lastwj = thisjob = newjob;
+
+ if (list_pipe)
+ jn->stat |= STAT_NOPRINT;
+
+ if (nowait) {
+ if(!pline_level) {
+ struct process *pn, *qn;
+
+ curjob = newjob;
+ addproc(list_pipe_pid, list_pipe_text);
+
+ 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;
+ printjob(jn, !!isset(LONGLISTJOBS), 1);
+ }
+ else
+ deletejob(jn);
+ }
+
+ for (; !nowait;) {
+ if (list_pipe_child) {
+ jn->stat |= STAT_NOPRINT;
+ makerunning(jn);
+ }
+ if (!(jn->stat & STAT_LOCKED))
+ waitjobs();
+
+ if (list_pipe_child &&
+ jn->stat & STAT_DONE &&
+ lastval2 & 0200)
+ killpg(mypgrp, lastval2 & ~0200);
+ if ((list_pipe || last1) && !list_pipe_child &&
+ jn->stat & STAT_STOPPED) {
+ pid_t pid;
+ int synch[2];
+
+ pipe(synch);
+
+ if ((pid = fork()) == -1) {
+ trashzle();
+ close(synch[0]);
+ close(synch[1]);
+ putc('\n', stderr);
+ fprintf(stderr, "zsh: job can't be suspended\n");
+ fflush(stderr);
+ makerunning(jn);
+ killjb(jn, SIGCONT);
+ thisjob = newjob;
+ }
+ else if (pid) {
+ char dummy;
+
+ list_pipe_pid = pid;
+ nowait = errflag = 1;
+ breaks = loops;
+ close(synch[1]);
+ read(synch[0], &dummy, 1);
+ close(synch[0]);
+ jobtab[list_pipe_job].other = newjob;
+ jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
+ jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
+ jn->other = pid;
+ killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
+ break;
+ }
+ else {
+ close(synch[0]);
+ entersubsh(Z_ASYNC, 0, 0);
+ setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader);
+ close(synch[1]);
+ kill(getpid(), SIGSTOP);
+ list_pipe = 0;
+ list_pipe_child = 1;
+ break;
+ }
+ }
+ else if (subsh && jn->stat & STAT_STOPPED)
+ thisjob = newjob;
+ else
+ break;
+ }
+ child_unblock();
+
+ if (list_pipe && (lastval & 0200) && pj >= 0 &&
+ (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
+ jn = jobtab + pj;
+ jn->stat |= STAT_NOPRINT;
+ killjb(jobtab + pj, lastval & ~0200);
+ }
+ if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE)))
+ deletejob(jn);
+ thisjob = pj;
+
+ }
+ if (l->flags & PFLAG_NOT)
+ lastval = !lastval;
+ }
+ if (!pline_level)
+ simple_pline = old_simple_pline;
+ return lastval;
+}
+
+static int subsh_close = -1;
+
+/* execute pipeline. This function assumes the `pline' is not NULL. */
+
+/**/
+static void
+execpline2(Pline pline, int how, int input, int output, int last1)
+{
+ pid_t pid;
+ int pipes[2];
+ int oldlineno;
+
+ if (breaks || retflag)
+ return;
+
+ oldlineno = lineno;
+ lineno = pline->left->lineno;
+
+ if (pline_level == 1)
+ strcpy(list_pipe_text, getjobtext((void *) pline->left));
+ if (pline->type == END) {
+ execcmd(pline->left, input, output, how, last1 ? 1 : 2);
+ pline->left = NULL;
+ } else {
+ int old_list_pipe = list_pipe;
+
+ mpipe(pipes);
+
+ /* if we are doing "foo | bar" where foo is a current *
+ * shell command, do foo in a subshell and do the *
+ * rest of the pipeline in the current shell. */
+ if (pline->left->type >= CURSH && (how & Z_SYNC)) {
+ int synch[2];
+
+ pipe(synch);
+ if ((pid = fork()) == -1) {
+ close(synch[0]);
+ close(synch[1]);
+ zerr("fork failed: %e", NULL, errno);
+ } else if (pid) {
+ char dummy, *text;
+
+ text = getjobtext((void *) pline->left);
+ addproc(pid, text);
+ close(synch[1]);
+ read(synch[0], &dummy, 1);
+ close(synch[0]);
+ } else {
+ zclose(pipes[0]);
+ close(synch[0]);
+ entersubsh(how, 2, 0);
+ close(synch[1]);
+ execcmd(pline->left, input, pipes[1], how, 0);
+ _exit(lastval);
+ }
+ } else {
+ /* otherwise just do the pipeline normally. */
+ subsh_close = pipes[0];
+ execcmd(pline->left, input, pipes[1], how, 0);
+ }
+ pline->left = NULL;
+ zclose(pipes[1]);
+ if (pline->right) {
+ /* if another execpline() is invoked because the command is *
+ * a list it must know that we're already in a pipeline */
+ list_pipe = 1;
+ execpline2(pline->right, how, pipes[0], output, last1);
+ list_pipe = old_list_pipe;
+ zclose(pipes[0]);
+ subsh_close = -1;
+ }
+ }
+
+ lineno = oldlineno;
+}
+
+/* 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 **) ncalloc((countlinknodes(list) + 4) * sizeof(char *));
+ if (isset(XTRACE)) {
+ if (!doneps4)
+ fprintf(stderr, "%s", (prompt4) ? prompt4 : "");
+
+ for (node = firstnode(list); node; incnode(node)) {
+ *ptr++ = (char *)getdata(node);
+ zputs(getdata(node), stderr);
+ if (nextnode(node))
+ fputc(' ', stderr);
+ }
+ fputc('\n', stderr);
+ fflush(stderr);
+ } else {
+ for (node = firstnode(list); node; incnode(node))
+ *ptr++ = (char *)getdata(node);
+ }
+ *ptr = NULL;
+ return (argv);
+}
+
+/**/
+void
+untokenize(char *s)
+{
+ for (; *s; s++)
+ if (itok(*s))
+ if (*s == Nularg)
+ chuck(s--);
+ else
+ *s = ztokens[*s - Pound];
+}
+
+/* Open a file for writing redicection */
+
+/**/
+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)
+{
+ struct multio *mn = mfds[fd];
+ char buf[TCBUFSIZE];
+ int len, i;
+
+ if (fd < 0 || !mfds[fd] || mfds[fd]->ct < 2)
+ return;
+ if (zfork()) {
+ for (i = 0; i < mn->ct; i++)
+ zclose(mn->fds[i]);
+ zclose(mn->pipe);
+ mn->ct = 1;
+ mn->fds[0] = fd;
+ return;
+ }
+ /* pid == 0 */
+ closeallelse(mn);
+ if (mn->rflag) {
+ /* tee process */
+ while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0)
+ for (i = 0; i < mn->ct; i++)
+ write(mn->fds[i], buf, len);
+ } else {
+ /* cat process */
+ for (i = 0; i < mn->ct; i++)
+ while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0)
+ write(mn->pipe, buf, len);
+ }
+ _exit(0);
+}
+
+/* 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;
+
+ for (i = 0; i < OPEN_MAX; 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. */
+
+/**/
+static void
+addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag)
+{
+ int pipes[2];
+
+ if (!mfds[fd1] || unset(MULTIOS)) {
+ if(!mfds[fd1]) { /* starting a new multio */
+ mfds[fd1] = (struct multio *) alloc(sizeof(struct multio));
+ if (!forked && save[fd1] == -2)
+ save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1);
+ }
+ 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", NULL, fd1);
+ return;
+ }
+ if (mfds[fd1]->ct == 1) { /* split the stream */
+ mfds[fd1]->fds[0] = movefd(fd1);
+ mfds[fd1]->fds[1] = movefd(fd2);
+ mpipe(pipes);
+ mfds[fd1]->pipe = pipes[1 - rflag];
+ redup(pipes[rflag], fd1);
+ mfds[fd1]->ct = 2;
+ } else { /* add another fd to an already split stream */
+ 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);
+ }
+ mfds[fd1]->fds[mfds[fd1]->ct++] = movefd(fd2);
+ }
+ }
+ if (subsh_close >= 0 && !fdtable[subsh_close])
+ subsh_close = -1;
+}
+
+/**/
+static void
+addvars(LinkList l, int export)
+{
+ Varasg v;
+ LinkList vl;
+ int xtr;
+ char **arr, **ptr;
+
+ xtr = isset(XTRACE);
+ if (xtr && nonempty(l)) {
+ fprintf(stderr, "%s", prompt4 ? prompt4 : "");
+ doneps4 = 1;
+ }
+
+ while (nonempty(l)) {
+ v = (Varasg) ugetnode(l);
+ singsub(&v->name);
+ if (errflag)
+ return;
+ untokenize(v->name);
+ if (xtr)
+ fprintf(stderr, "%s=", v->name);
+ if (v->type == PM_SCALAR) {
+ vl = newlinklist();
+ addlinknode(vl, v->str);
+ } else
+ vl = v->arr;
+ prefork(vl, v->type == PM_SCALAR ? 7 : 3);
+ if (errflag)
+ return;
+ if (isset(GLOBASSIGN) || v->type != PM_SCALAR)
+ globlist(vl);
+ if (errflag)
+ return;
+ if (v->type == PM_SCALAR && (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)
+ fprintf(stderr, "%s ", val);
+ if (export) {
+ if (export < 0) {
+ /* We are going to fork so do not bother freeing this */
+ pm = (Param) paramtab->removenode(paramtab, v->name);
+ if (isset(RESTRICTED) && (pm->flags & PM_RESTRICTED)) {
+ zerr("%s: restricted", pm->nam, 0);
+ zsfree(val);
+ return;
+ }
+ }
+ allexp = opts[ALLEXPORT];
+ opts[ALLEXPORT] = 1;
+ pm = setsparam(v->name, val);
+ opts[ALLEXPORT] = allexp;
+ } else
+ pm = setsparam(v->name, val);
+ if (errflag)
+ return;
+ continue;
+ }
+ ptr = arr = (char **) zalloc(sizeof(char **) * (countlinknodes(vl) + 1));
+
+ while (nonempty(vl))
+ *ptr++ = ztrdup((char *) ugetnode(vl));
+
+ *ptr = NULL;
+ if (xtr) {
+ fprintf(stderr, "( ");
+ for (ptr = arr; *ptr; ptr++)
+ fprintf(stderr, "%s ", *ptr);
+ fprintf(stderr, ") ");
+ }
+ setaparam(v->name, arr);
+ if (errflag)
+ return;
+ }
+}
+
+/**/
+static void
+execcmd(Cmd cmd, int input, int output, int how, int last1)
+{
+ HashNode hn = NULL;
+ LinkNode node;
+ Redir fn;
+ struct multio *mfds[10];
+ char *text;
+ int save[10];
+ int fil, dfil, is_cursh, type, i;
+ int nullexec = 0, assign = 0, forked = 0;
+ int is_shfunc = 0, is_builtin = 0, is_exec = 0;
+ /* Various flags to the command. */
+ int cflags = 0, checked = 0;
+
+ doneps4 = 0;
+ args = cmd->args;
+ type = cmd->type;
+
+ 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 == SIMPLE && nonempty(args) &&
+ *(char *)peekfirst(args) == '%') {
+ 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 == SIMPLE && (how & Z_SYNC) &&
+ nonempty(args) && empty(cmd->redir) && !input &&
+ !nextnode(firstnode(args))) {
+ if (unset(NOTIFY))
+ scanjobs();
+ if (findjobnam(peekfirst(args)) != -1)
+ pushnode(args, dupstring("fg"));
+ }
+
+ /* 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 == SIMPLE) {
+ while (nonempty(args)) {
+ char *cmdarg = (char *) peekfirst(args);
+ checked = !has_token(cmdarg);
+ if (!checked)
+ break;
+ 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;
+ }
+ if (!(hn->flags & BINF_PREFIX)) {
+ is_builtin = 1;
+#ifdef DYNAMIC
+ /* autoload the builtin if necessary */
+ if (!((Builtin) hn)->handlerfunc) {
+ load_module(((Builtin) hn)->optstr);
+ hn = builtintab->getnode(builtintab, cmdarg);
+ }
+#endif
+ assign = (hn->flags & BINF_MAGICEQUALS);
+ break;
+ }
+ cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
+ cflags |= hn->flags;
+ uremnode(args, firstnode(args));
+ hn = NULL;
+ checked = 0;
+ if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
+ break;
+ }
+ }
+
+ /* Do prefork substitutions */
+ prefork(args, assign ? 2 : isset(MAGICEQUALSUBST));
+
+ if (type == SIMPLE) {
+ int unglobbed = 0;
+
+ for (;;) {
+ char *cmdarg;
+
+ if (!(cflags & BINF_NOGLOB))
+ while (!checked && !errflag && nonempty(args) &&
+ has_token((char *) peekfirst(args)))
+ glob(args, firstnode(args));
+ 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 == 2)
+ cmd->flags |= CFLAG_EXEC;
+
+ /* Empty command */
+ if (empty(args)) {
+ if (nonempty(cmd->redir)) {
+ if (cmd->flags & CFLAG_EXEC) {
+ /* Was this "exec < foobar"? */
+ nullexec = 1;
+ break;
+ } else if (!nullcmd || !*nullcmd ||
+ (cflags & BINF_PREFIX)) {
+ zerr("redirection with no command", NULL, 0);
+ errflag = lastval = 1;
+ return;
+ } else if (readnullcmd && *readnullcmd &&
+ ((Redir) peekfirst(cmd->redir))->type == READ &&
+ !nextnode(firstnode(cmd->redir))) {
+ addlinknode(args, dupstring(readnullcmd));
+ } else
+ addlinknode(args, dupstring(nullcmd));
+ } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) {
+ lastval = 0;
+ return;
+ } else {
+ cmdoutval = 0;
+ addvars(cmd->vars, 0);
+ if (errflag)
+ lastval = errflag;
+ else
+ lastval = cmdoutval;
+ if (isset(XTRACE)) {
+ fputc('\n', stderr);
+ fflush(stderr);
+ }
+ return;
+ }
+ } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) &&
+ (cmd->flags & CFLAG_EXEC)) {
+ zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0);
+ lastval = 1;
+ return;
+ }
+
+ if (errflag || checked ||
+ (unset(POSIXBUILTINS) && (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) {
+ zerr("no such builtin: %s", cmdarg, 0);
+ errflag = lastval = 1;
+ return;
+ }
+ break;
+ }
+ if (!(hn->flags & BINF_PREFIX)) {
+ is_builtin = 1;
+#ifdef DYNAMIC
+ /* autoload the builtin if necessary */
+ if (!((Builtin) hn)->handlerfunc)
+ load_module(((Builtin) hn)->optstr);
+#endif
+ break;
+ }
+ cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
+ cflags |= hn->flags;
+ uremnode(args, firstnode(args));
+ hn = NULL;
+ }
+ }
+
+ if (errflag) {
+ lastval = 1;
+ return;
+ }
+
+ /* Get the text associated with this command. */
+ if (jobbing || (how & Z_TIMED))
+ text = getjobtext((void *) cmd);
+ else
+ text = NULL;
+
+ /* Set up special parameter $_ */
+ zsfree(underscore);
+ if (nonempty(args)
+ && (underscore = ztrdup((char *) getdata(lastnode(args)))))
+ untokenize(underscore);
+ else
+ underscore = ztrdup("");
+
+ /* Warn about "rm *" */
+ if (type == SIMPLE && interact && unset(RMSTARSILENT)
+ && isset(SHINSTDIN) && 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))
+ uremnode(args, node);
+ } else if (l > 2 && s[l - 2] == '/' && s[l - 1] == Star) {
+ char t = s[l - 2];
+
+ s[l - 2] = 0;
+ if (!checkrmall(s))
+ uremnode(args, node);
+ s[l - 2] = t;
+ }
+ }
+ if (!nextnode(firstnode(args)))
+ errflag = 1;
+ }
+
+ if (errflag) {
+ lastval = 1;
+ return;
+ }
+
+ if (type == SIMPLE && !nullexec) {
+ char *s;
+ char trycd = (isset(AUTOCD) && isset(SHINSTDIN)
+ && empty(cmd->redir) && !empty(args)
+ && !nextnode(firstnode(args))
+ && *(char *)peekfirst(args));
+
+ DPUTS(empty(args), "BUG: empty(args) in exec.c");
+ if (!hn) {
+ /* Resolve external commands */
+ char *cmdarg = (char *) peekfirst(args);
+
+ hn = cmdnamtab->getnode(cmdnamtab, cmdarg);
+ if (hn && trycd && !isreallycom((Cmdnam)hn)) {
+ cmdnamtab->removenode(cmdnamtab, cmdarg);
+ cmdnamtab->freenode(hn);
+ hn = NULL;
+ }
+ if (!hn && isset(HASHCMDS) && strcmp(cmdarg, "..")) {
+ for (s = cmdarg; *s && *s != '/'; s++);
+ if (!*s)
+ hn = (HashNode) hashcmd(cmdarg, pathchecked);
+ }
+ }
+
+ /* 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("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 || (type >= CURSH) || nullexec);
+
+ /**************************************************************************
+ * Do we need to fork? We need to fork if: *
+ * 1) The command is supposed to run in the background. (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 when the command is the last stage in a subshell, or is the *
+ * last command after the option `-c'. *
+ * 2) We are not trapping EXIT or ZERR. *
+ * 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 ((how & Z_ASYNC) || (!(cmd->flags & CFLAG_EXEC) &&
+ (((is_builtin || is_shfunc) && output) ||
+ (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] ||
+ sigtrapped[SIGEXIT] || havefiles()))))) {
+
+ pid_t pid;
+ int synch[2];
+ char dummy;
+
+ child_block();
+ pipe(synch);
+
+ if ((pid = zfork()) == -1) {
+ close(synch[0]);
+ close(synch[1]);
+ return;
+ } if (pid) {
+ close(synch[1]);
+ read(synch[0], &dummy, 1);
+ close(synch[0]);
+#ifdef PATH_DEV_FD
+ closem(2);
+#endif
+ if (how & Z_ASYNC) {
+ lastpid = (long) pid;
+ } else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) {
+ /* search for STTY=... */
+ while (nonempty(cmd->vars))
+ if (!strcmp(((Varasg) ugetnode(cmd->vars))->name, "STTY")) {
+ jobtab[thisjob].stty_in_env = 1;
+ break;
+ }
+ }
+ addproc(pid, text);
+ return;
+ }
+ /* pid == 0 */
+ close(synch[0]);
+ entersubsh(how, type != SUBSH && !(how & Z_ASYNC) ? 2 : 1, 0);
+ close(synch[1]);
+ forked = 1;
+ if (sigtrapped[SIGINT] & ZSIG_IGNORED)
+ holdintr();
+#ifdef HAVE_NICE
+ /* Check if we should run background jobs at a lower priority. */
+ if ((how & Z_ASYNC) && isset(BGNICE))
+ nice(5);
+#endif /* HAVE_NICE */
+
+ } 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;
+ } 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 (!(cflags & BINF_NOGLOB))
+ globlist(args);
+ if (errflag) {
+ lastval = 1;
+ goto err;
+ }
+
+ /* Add pipeline input/output to mnodes */
+ if (input)
+ addfd(forked, save, mfds, 0, input, 0);
+ if (output)
+ addfd(forked, save, mfds, 1, output, 1);
+
+ /* Do process substitutions */
+ spawnpipes(cmd->redir);
+
+ /* Do io redirections */
+ while (nonempty(cmd->redir)) {
+ fn = (Redir) ugetnode(cmd->redir);
+ DPUTS(fn->type == HEREDOC || fn->type == HEREDOCDASH,
+ "BUG: unexpanded here document");
+ if (fn->type == INPIPE) {
+ if (fn->fd2 == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fn->fd2, 0);
+ } else if (fn->type == OUTPIPE) {
+ if (fn->fd2 == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fn->fd2, 1);
+ } else {
+ if (fn->type != HERESTR && xpandredir(fn, cmd->redir))
+ continue;
+ if (errflag) {
+ closemnodes(mfds);
+ fixfds(save);
+ execerr();
+ }
+ if (isset(RESTRICTED) && IS_WRITE_FILE(fn->type)) {
+ zerr("writing redirection not allowed in restricted mode", NULL, 0);
+ execerr();
+ }
+ if (unset(EXECOPT))
+ continue;
+ switch(fn->type) {
+ case HERESTR:
+ fil = getherestr(fn);
+ if (fil == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ if (errno != EINTR)
+ zerr("%e", NULL, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, 0);
+ break;
+ case READ:
+ case READWRITE:
+ if (fn->type == 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)
+ zerr("%e: %s", fn->name, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, 0);
+ /* If this is 'exec < file', read from stdin, *
+ * not terminal, unless `file' is a terminal. */
+ if (nullexec && fn->fd1 == 0 && isset(SHINSTDIN) && interact)
+ init_io();
+ break;
+ case CLOSE:
+ if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2)
+ save[fn->fd1] = movefd(fn->fd1);
+ closemn(mfds, fn->fd1);
+ zclose(fn->fd1);
+ break;
+ case MERGEIN:
+ case MERGEOUT:
+ if(fn->fd2 < 10)
+ closemn(mfds, fn->fd2);
+ fil = dup(fn->fd2);
+ if (fil == -1) {
+ char fdstr[4];
+
+ closemnodes(mfds);
+ fixfds(save);
+ sprintf(fdstr, "%d", fn->fd2);
+ zerr("%s: %e", fdstr, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT);
+ break;
+ default:
+ if (IS_APPEND_REDIR(fn->type))
+ fil = open(unmeta(fn->name),
+ (unset(CLOBBER) && !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 = dup(fil);
+ else
+ dfil = 0;
+ if (fil == -1 || dfil == -1) {
+ if(fil != -1)
+ close(fil);
+ closemnodes(mfds);
+ fixfds(save);
+ if (errno != EINTR)
+ zerr("%e: %s", fn->name, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, 1);
+ if(IS_ERROR_REDIR(fn->type))
+ addfd(forked, save, mfds, 2, dfil, 1);
+ break;
+ }
+ }
+ }
+
+ /* We are done with redirection. close the mnodes, *
+ * spawning tee/cat processes as necessary. */
+ for (i = 0; i < 10; i++)
+ closemn(mfds, i);
+
+ if (nullexec) {
+ for (i = 0; i < 10; i++)
+ if (save[i] != -2)
+ zclose(save[i]);
+ /*
+ * Here we specifically *don't* restore the original fd's
+ * before returning.
+ */
+ return;
+ }
+
+ if (isset(EXECOPT) && !errflag) {
+ /*
+ * 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)
+ entersubsh(how, type != SUBSH ? 2 : 1, 1);
+ if (type >= CURSH) {
+ static int (*func[]) _((Cmd)) = {
+ execcursh, exectime, execfuncdef, execfor, execwhile,
+ execrepeat, execif, execcase, execselect, execcond,
+ execarith, execautofn
+ };
+
+ if (last1 == 1)
+ cmd->flags |= CFLAG_EXEC;
+ lastval = (func[type - CURSH]) (cmd);
+ } else if (is_builtin || is_shfunc) {
+ LinkList restorelist = 0, removelist = 0;
+ /* builtin or shell function */
+
+ if (!forked && ((cflags & BINF_COMMAND) ||
+ (unset(POSIXBUILTINS) && !assign) ||
+ (isset(POSIXBUILTINS) && !is_shfunc &&
+ !(hn->flags & BINF_PSPECIAL))))
+ save_params(cmd, &restorelist, &removelist);
+
+ if (cmd->vars) {
+ /* Export this if the command is a shell function,
+ * but not if it's a builtin.
+ */
+ addvars(cmd->vars, is_shfunc);
+ if (errflag) {
+ restore_params(restorelist, removelist);
+ lastval = 1;
+ fixfds(save);
+ return;
+ }
+ }
+
+ if (is_shfunc) {
+ /* It's a shell function */
+#ifdef PATH_DEV_FD
+ int i;
+
+ for (i = 10; i <= max_zsh_fd; i++)
+ if (fdtable[i] > 1)
+ fdtable[i]++;
+#endif
+ if (subsh_close >= 0)
+ zclose(subsh_close);
+ subsh_close = -1;
+ execshfunc(cmd, (Shfunc) hn);
+#ifdef PATH_DEV_FD
+ for (i = 10; i <= max_zsh_fd; i++)
+ if (fdtable[i] > 1)
+ if (--(fdtable[i]) <= 2)
+ zclose(i);
+#endif
+ } else {
+ /* It's a builtin */
+ if (forked)
+ closem(1);
+ lastval = execbuiltin(args, (Builtin) hn);
+#ifdef PATH_DEV_FD
+ closem(2);
+#endif
+ if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && lastval && !subsh) {
+ fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
+ }
+ fflush(stdout);
+ if (save[1] == -2) {
+ if (ferror(stdout)) {
+ zerr("write error: %e", NULL, errno);
+ clearerr(stdout);
+ errflag = 0;
+ }
+ } else
+ clearerr(stdout);
+ }
+
+ if (cmd->flags & CFLAG_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(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
+ exit(lastval);
+ }
+
+ restore_params(restorelist, removelist);
+
+ } else {
+ if (cmd->flags & CFLAG_EXEC) {
+ setiparam("SHLVL", --shlvl);
+ /* If we are exec'ing a command, and we are not *
+ * in a subshell, then save the history file. */
+ if (!subsh && isset(RCS) && interact && !nohistsave)
+ savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
+ }
+ if (type == SIMPLE) {
+ if (cmd->vars) {
+ addvars(cmd->vars, -1);
+ if (errflag)
+ _exit(1);
+ }
+ closem(1);
+ if (coprocin)
+ zclose(coprocin);
+ if (coprocout)
+ zclose(coprocout);
+#ifdef HAVE_GETRLIMIT
+ if (!forked)
+ setlimits(NULL);
+#endif
+ execute((Cmdnam) hn, cflags & BINF_DASH);
+ } else { /* ( ... ) */
+ DPUTS(cmd->vars && nonempty(cmd->vars),
+ "BUG: assigment before complex command");
+ list_pipe = 0;
+ if (subsh_close >= 0)
+ zclose(subsh_close);
+ subsh_close = -1;
+ /* If we're forked (and we should be), no need to return */
+ DPUTS(last1 != 1 && !forked, "BUG: not exiting?");
+ execlist(cmd->u.list, 0, 1);
+ }
+ }
+ }
+
+ err:
+ if (forked)
+ _exit(lastval);
+ fixfds(save);
+}
+
+/* Arrange to have variables restored. */
+
+/**/
+static void
+save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p)
+{
+ Param pm;
+ LinkNode node;
+ char *s;
+
+ MUSTUSEHEAP("save_params()");
+
+ *restore_p = newlinklist();
+ *remove_p = newlinklist();
+
+ for (node = firstnode(cmd->vars); node; incnode(node)) {
+ s = ((Varasg) getdata(node))->name;
+ if ((pm = (Param) paramtab->getnode(paramtab, s))) {
+ if (!(pm->flags & PM_SPECIAL)) {
+ paramtab->removenode(paramtab, s);
+ } else if (!(pm->flags & PM_READONLY) &&
+ (unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) {
+ Param tpm = (Param) alloc(sizeof *tpm);
+
+ tpm->nam = s;
+ tpm->flags = pm->flags;
+ switch (PM_TYPE(pm->flags)) {
+ case PM_SCALAR:
+ tpm->u.str = ztrdup(pm->gets.cfn(pm));
+ break;
+ case PM_INTEGER:
+ tpm->u.val = pm->gets.ifn(pm);
+ break;
+ case PM_ARRAY:
+ PERMALLOC {
+ tpm->u.arr = arrdup(pm->gets.afn(pm));
+ } LASTALLOC;
+ break;
+ }
+ pm = tpm;
+ }
+ addlinknode(*remove_p, s);
+ addlinknode(*restore_p, pm);
+ } else {
+ addlinknode(*remove_p, s);
+ }
+ }
+}
+
+/* Restore saved parameters after executing a shfunc or builtin */
+
+/**/
+static void
+restore_params(LinkList restorelist, LinkList removelist)
+{
+ Param pm;
+ char *s;
+
+ if (removelist) {
+ /* remove temporary parameters */
+ while ((s = (char *) ugetnode(removelist))) {
+ if ((pm = (Param) paramtab->getnode(paramtab, s)) &&
+ !(pm->flags & PM_SPECIAL)) {
+ pm->flags &= ~PM_READONLY;
+ unsetparam_pm(pm, 0, 0);
+ }
+ }
+ }
+
+ if (restorelist) {
+ /* restore saved parameters */
+ while ((pm = (Param) ugetnode(restorelist))) {
+ if (pm->flags & PM_SPECIAL) {
+ Param tpm = (Param) paramtab->getnode(paramtab, pm->nam);
+
+ DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) ||
+ !(pm->flags & PM_SPECIAL),
+ "BUG: in restoring special parameters");
+ tpm->flags = pm->flags;
+ switch (PM_TYPE(pm->flags)) {
+ case PM_SCALAR:
+ tpm->sets.cfn(tpm, pm->u.str);
+ break;
+ case PM_INTEGER:
+ tpm->sets.ifn(tpm, pm->u.val);
+ break;
+ case PM_ARRAY:
+ tpm->sets.afn(tpm, pm->u.arr);
+ break;
+ }
+ } else
+ paramtab->addnode(paramtab, pm->nam, pm);
+ if (pm->flags & PM_EXPORTED)
+ pm->env = addenv(pm->nam, getsparam(pm->nam));
+ }
+ }
+}
+
+/* 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;
+}
+
+/**/
+static void
+entersubsh(int how, int cl, int fake)
+{
+ int sig;
+
+ if (cl != 2)
+ for (sig = 0; sig < VSIGCOUNT; sig++)
+ if (!(sigtrapped[sig] & ZSIG_FUNC))
+ unsettrap(sig);
+ if (unset(MONITOR)) {
+ if (how & Z_ASYNC) {
+ settrap(SIGINT, NULL);
+ settrap(SIGQUIT, NULL);
+ if (isatty(0)) {
+ close(0);
+ if (open("/dev/null", O_RDWR | O_NOCTTY)) {
+ zerr("can't open /dev/null: %e", NULL, errno);
+ _exit(1);
+ }
+ }
+ }
+ } else if (thisjob != -1 && cl) {
+ if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) {
+ if (kill(jobtab[list_pipe_job].gleader, 0) == -1 ||
+ setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) {
+ jobtab[list_pipe_job].gleader =
+ jobtab[thisjob].gleader = mypgrp;
+ setpgrp(0L, mypgrp);
+
+ if (how & Z_SYNC)
+ attachtty(jobtab[thisjob].gleader);
+ }
+ }
+ else if (!jobtab[thisjob].gleader ||
+ (setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
+ 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 (how & Z_SYNC)
+ attachtty(jobtab[thisjob].gleader);
+ }
+ }
+ if (!fake)
+ subsh = 1;
+ if (SHTTY != -1) {
+ zclose(SHTTY);
+ SHTTY = -1;
+ }
+ if (isset(MONITOR)) {
+ signal_default(SIGTTOU);
+ signal_default(SIGTTIN);
+ signal_default(SIGTSTP);
+ }
+ if (interact) {
+ signal_default(SIGTERM);
+ if (!(sigtrapped[SIGINT] & ZSIG_IGNORED))
+ signal_default(SIGINT);
+ }
+ if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED))
+ signal_default(SIGQUIT);
+ opts[MONITOR] = opts[USEZLE] = 0;
+ zleactive = 0;
+ if (cl)
+ clearjobtab();
+ times(&shtms);
+}
+
+/* close internal shell fds */
+
+/**/
+void
+closem(int how)
+{
+ int i;
+
+ for (i = 10; i <= max_zsh_fd; i++)
+ if (fdtable[i] && (!how || fdtable[i] == how))
+ zclose(i);
+}
+
+/* convert here document into a here string */
+
+/**/
+char *
+gethere(char *str, int typ)
+{
+ char *buf;
+ int bsiz, qt = 0, strip = 0;
+ char *s, *t, *bptr, c;
+
+ for (s = str; *s; s++)
+ if (INULL(*s)) {
+ *s = Nularg;
+ qt = 1;
+ }
+ untokenize(str);
+ if (typ == HEREDOCDASH) {
+ strip = 1;
+ while (*str == '\t')
+ str++;
+ }
+ bptr = buf = zalloc(bsiz = 256);
+ for (;;) {
+ t = bptr;
+
+ while ((c = hgetc()) == '\t' && strip)
+ ;
+ for (;;) {
+ if (bptr == buf + bsiz) {
+ buf = realloc(buf, 2 * bsiz);
+ t = buf + bsiz - (bptr - t);
+ bptr = buf + bsiz;
+ bsiz *= 2;
+ }
+ if (lexstop || c == '\n')
+ break;
+ *bptr++ = c;
+ c = hgetc();
+ }
+ *bptr = '\0';
+ if (!strcmp(t, str))
+ break;
+ if (lexstop) {
+ t = bptr;
+ break;
+ }
+ *bptr++ = '\n';
+ }
+ if (t > buf && t[-1] == '\n')
+ t--;
+ *t = '\0';
+ if (!qt)
+ parsestr(buf);
+ s = dupstring(buf);
+ zfree(buf, bsiz);
+ return s;
+}
+
+/* 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);
+ t[len++] = '\n';
+ s = gettempname();
+ if (!s || (fd = open(s, O_CREAT|O_WRONLY|O_EXCL|O_NOCTTY, 0600)) == -1)
+ return -1;
+ write(fd, t, len);
+ close(fd);
+ fd = open(s, O_RDONLY | O_NOCTTY);
+ unlink(s);
+ return fd;
+}
+
+/* $(...) */
+
+/**/
+LinkList
+getoutput(char *cmd, int qt)
+{
+ List list;
+ int pipes[2];
+ pid_t pid;
+ Cmd c;
+ Redir r;
+
+ if (!(list = parse_string(cmd)))
+ return NULL;
+ if (list != &dummy_list && !list->right && !list->left->flags &&
+ list->left->type == END && list->left->left->type == END &&
+ (c = list->left->left->left)->type == SIMPLE && empty(c->args) &&
+ empty(c->vars) && nonempty(c->redir) &&
+ !nextnode(firstnode(c->redir)) &&
+ (r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 &&
+ r->type == READ) {
+ /* $(< word) */
+ int stream;
+ char *s = r->name;
+
+ singsub(&s);
+ if (errflag)
+ return NULL;
+ untokenize(s);
+ if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
+ zerr("%e: %s", s, errno);
+ return NULL;
+ }
+ return readoutput(stream, qt);
+ }
+
+ mpipe(pipes);
+ child_block();
+ cmdoutval = 0;
+ if ((cmdoutpid = pid = zfork()) == -1) {
+ /* fork error */
+ zclose(pipes[0]);
+ zclose(pipes[1]);
+ errflag = 1;
+ cmdoutpid = 0;
+ child_unblock();
+ return NULL;
+ } else if (pid) {
+ LinkList retval;
+
+ zclose(pipes[1]);
+ retval = readoutput(pipes[0], qt);
+ fdtable[pipes[0]] = 0;
+ child_suspend(0); /* unblocks */
+ lastval = cmdoutval;
+ return retval;
+ }
+
+ /* pid == 0 */
+ child_unblock();
+ zclose(pipes[0]);
+ redup(pipes[1], 1);
+ opts[MONITOR] = 0;
+ entersubsh(Z_SYNC, 1, 0);
+ execlist(list, 0, 1);
+ close(1);
+ _exit(lastval);
+ zerr("exit returned in child!!", NULL, 0);
+ kill(getpid(), SIGKILL);
+ return NULL;
+}
+
+/* read output of command substitution */
+
+/**/
+static LinkList
+readoutput(int in, int qt)
+{
+ LinkList ret;
+ char *buf, *ptr;
+ int bsiz, c, cnt = 0;
+ FILE *fin;
+
+ fin = fdopen(in, "r");
+ ret = newlinklist();
+ ptr = buf = (char *) ncalloc(bsiz = 64);
+ 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 = (char *) ncalloc(bsiz *= 2);
+
+ memcpy(pp, buf, cnt - 1);
+ ptr = (buf = pp) + cnt - 1;
+ }
+ *ptr++ = c;
+ }
+ 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);
+
+ while (*words) {
+ if (isset(GLOBSUBST))
+ tokenize(*words);
+ addlinknode(ret, *words++);
+ }
+ }
+ return ret;
+}
+
+/**/
+static List
+parsecmd(char *cmd)
+{
+ char *str;
+ List list;
+
+ for (str = cmd + 2; *str && *str != Outpar; str++);
+ if (!*str || cmd[1] != Inpar) {
+ zerr("oops.", NULL, 0);
+ return NULL;
+ }
+ *str = '\0';
+ if (str[1] || !(list = parse_string(cmd + 2))) {
+ zerr("parse error in process substitution", NULL, 0);
+ return NULL;
+ }
+ return list;
+}
+
+/* =(...) */
+
+/**/
+char *
+getoutputfile(char *cmd)
+{
+ pid_t pid;
+ char *nam;
+ List list;
+ int fd;
+
+ if (thisjob == -1)
+ return NULL;
+ if (!(list = parsecmd(cmd)))
+ return NULL;
+ if (!(nam = gettempname()))
+ return NULL;
+
+ nam = ztrdup(nam);
+ PERMALLOC {
+ if (!jobtab[thisjob].filelist)
+ jobtab[thisjob].filelist = newlinklist();
+ addlinknode(jobtab[thisjob].filelist, nam);
+ } LASTALLOC;
+ child_block();
+ fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
+
+ if (fd < 0 || (cmdoutpid = pid = zfork()) == -1) {
+ /* fork or open error */
+ child_unblock();
+ return nam;
+ } else if (pid) {
+ int os;
+
+ close(fd);
+ os = jobtab[thisjob].stat;
+ waitforpid(pid);
+ cmdoutval = 0;
+ jobtab[thisjob].stat = os;
+ return nam;
+ }
+
+ /* pid == 0 */
+ redup(fd, 1);
+ opts[MONITOR] = 0;
+ entersubsh(Z_SYNC, 1, 0);
+ execlist(list, 0, 1);
+ close(1);
+ _exit(lastval);
+ zerr("exit returned in child!!", NULL, 0);
+ 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();
+
+# ifdef HAVE_MKFIFO
+ if (mkfifo(tnam, 0600) < 0)
+# else
+ if (mknod(tnam, 0010600, 0) < 0)
+# endif
+ return NULL;
+ return tnam;
+}
+#endif /* ! PATH_DEV_FD && HAVE_FIFOS */
+
+/* <(...) or >(...) */
+
+/**/
+char *
+getproc(char *cmd)
+{
+#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD)
+ zerr("doesn't look like your system supports FIFOs.", NULL, 0);
+ return NULL;
+#else
+ List list;
+ int out = *cmd == Inang;
+ char *pnam;
+#ifndef PATH_DEV_FD
+ int fd;
+#else
+ int pipes[2];
+#endif
+
+ if (thisjob == -1)
+ return NULL;
+#ifndef PATH_DEV_FD
+ if (!(pnam = namedpipe()))
+ return NULL;
+#else
+ pnam = ncalloc(strlen(PATH_DEV_FD) + 6);
+#endif
+ if (!(list = parsecmd(cmd)))
+ return NULL;
+#ifndef PATH_DEV_FD
+ PERMALLOC {
+ if (!jobtab[thisjob].filelist)
+ jobtab[thisjob].filelist = newlinklist();
+ addlinknode(jobtab[thisjob].filelist, ztrdup(pnam));
+ } LASTALLOC;
+ if (zfork()) {
+#else
+ mpipe(pipes);
+ if (zfork()) {
+ sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]);
+ zclose(pipes[out]);
+ fdtable[pipes[!out]] = 2;
+#endif
+ return pnam;
+ }
+#ifndef PATH_DEV_FD
+ closem(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(Z_ASYNC, 1, 0);
+ redup(fd, out);
+#else
+ entersubsh(Z_ASYNC, 1, 0);
+ redup(pipes[out], out);
+ closem(0); /* this closes pipes[!out] as well */
+#endif
+ execlist(list, 0, 1);
+ zclose(out);
+ _exit(lastval);
+ return NULL;
+#endif /* HAVE_FIFOS and PATH_DEV_FD not defined */
+}
+
+/* > >(...) or < <(...) (does not use named pipes) */
+
+/**/
+static int
+getpipe(char *cmd)
+{
+ List list;
+ int pipes[2], out = *cmd == Inang;
+
+ if (!(list = parsecmd(cmd)))
+ return -1;
+ mpipe(pipes);
+ if (zfork()) {
+ zclose(pipes[out]);
+ return pipes[!out];
+ }
+ entersubsh(Z_ASYNC, 1, 0);
+ redup(pipes[out], out);
+ closem(0); /* this closes pipes[!out] as well */
+ execlist(list, 0, 1);
+ _exit(lastval);
+ return 0;
+}
+
+/* open pipes with fds >= 10 */
+
+/**/
+static void
+mpipe(int *pp)
+{
+ pipe(pp);
+ pp[0] = movefd(pp[0]);
+ pp[1] = movefd(pp[1]);
+}
+
+/* Do process substitution with redirection */
+
+/**/
+static void
+spawnpipes(LinkList l)
+{
+ LinkNode n;
+ Redir f;
+ char *str;
+
+ n = firstnode(l);
+ for (; n; incnode(n)) {
+ f = (Redir) getdata(n);
+ if (f->type == OUTPIPE || f->type == INPIPE) {
+ str = f->name;
+ f->fd2 = getpipe(str);
+ }
+ }
+}
+
+/* evaluate a [[ ... ]] */
+
+/**/
+static int
+execcond(Cmd cmd)
+{
+ return !evalcond(cmd->u.cond);
+}
+
+/* evaluate a ((...)) arithmetic command */
+
+/**/
+static int
+execarith(Cmd cmd)
+{
+ char *e;
+ long val = 0;
+
+ while ((e = (char *) ugetnode(cmd->args)))
+ val = matheval(e);
+ errflag = 0;
+ return !val;
+}
+
+/* perform time ... command */
+
+/**/
+static int
+exectime(Cmd cmd)
+{
+ int jb;
+
+ jb = thisjob;
+ if (!cmd->u.pline) {
+ shelltime();
+ return 0;
+ }
+ execpline(cmd->u.pline, Z_TIMED|Z_SYNC, 0);
+ thisjob = jb;
+ return lastval;
+}
+
+/* Define a shell function */
+
+/**/
+static int
+execfuncdef(Cmd cmd)
+{
+ Shfunc shf;
+ char *s;
+ int signum;
+
+ PERMALLOC {
+ while ((s = (char *) ugetnode(cmd->args))) {
+ shf = (Shfunc) zalloc(sizeof *shf);
+ shf->funcdef = (List) dupstruct(cmd->u.list);
+ shf->flags = 0;
+
+ /* is this shell function a signal trap? */
+ if (!strncmp(s, "TRAP", 4) && (signum = getsignum(s + 4)) != -1) {
+ if (settrap(signum, shf->funcdef)) {
+ freestruct(shf->funcdef);
+ zfree(shf, sizeof *shf);
+ LASTALLOC_RETURN 1;
+ }
+ sigtrapped[signum] |= ZSIG_FUNC;
+ }
+ shfunctab->addnode(shfunctab, ztrdup(s), shf);
+ }
+ } LASTALLOC;
+ if(isset(HISTNOFUNCTIONS))
+ remhist();
+ return 0;
+}
+
+/* Main entry point to execute a shell function. */
+
+/**/
+static void
+execshfunc(Cmd cmd, Shfunc shf)
+{
+ LinkList last_file_list = NULL;
+
+ if (errflag)
+ return;
+
+ if (!list_pipe) {
+ /* 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);
+ }
+
+ doshfunc(shf->funcdef, cmd->args, shf->flags, 0);
+
+ if (!list_pipe)
+ deletefilelist(last_file_list);
+}
+
+/* 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. */
+
+/**/
+static int
+execautofn(Cmd cmd)
+{
+ Shfunc shf = cmd->u.autofn->shf;
+ List l = getfpfunc(shf->nam);
+ if(l == &dummy_list) {
+ zerr("%s: function definition file not found", shf->nam, 0);
+ return 1;
+ }
+ if(isset(KSHAUTOLOAD)) {
+ VARARR(char, n, strlen(shf->nam) + 1);
+ strcpy(n, shf->nam);
+ execlist(l, 1, 0);
+ shf = (Shfunc) shfunctab->getnode(shfunctab, n);
+ if(!shf || (shf->flags & PM_UNDEFINED)) {
+ zerr("%s: function not defined by file", n, 0);
+ return 1;
+ }
+ } else {
+ freestruct(shf->funcdef);
+ PERMALLOC {
+ shf->funcdef = dupstruct(stripkshdef(l, shf->nam));
+ } LASTALLOC;
+ shf->flags &= ~PM_UNDEFINED;
+ }
+ HEAPALLOC {
+ execlist(dupstruct(shf->funcdef), 1, 0);
+ } LASTALLOC;
+ return lastval;
+}
+
+/* execute a shell function */
+
+/**/
+void
+doshfunc(List list, LinkList doshargs, int flags, int noreturnval)
+/* If noreturnval is nonzero, then reset the current return *
+ * value (lastval) to its value before the shell function *
+ * was executed. */
+{
+ char **tab, **x, *oargv0 = NULL;
+ int xexittr, newexittr, oldzoptind, oldlastval;
+ char *ou;
+ void *xexitfn, *newexitfn;
+ char saveopts[OPT_SIZE];
+ int obreaks = breaks;
+
+ HEAPALLOC {
+ pushheap();
+ if (trapreturn < 0)
+ trapreturn--;
+ oldlastval = lastval;
+ xexittr = sigtrapped[SIGEXIT];
+ if (xexittr & ZSIG_FUNC)
+ xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
+ else
+ xexitfn = sigfuncs[SIGEXIT];
+ sigtrapped[SIGEXIT] = 0;
+ sigfuncs[SIGEXIT] = NULL;
+ tab = pparams;
+ oldzoptind = zoptind;
+ zoptind = 1;
+
+ /* 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(saveopts, opts, sizeof(opts));
+
+ if (flags & PM_TAGGED)
+ opts[XTRACE] = 1;
+ opts[PRINTEXITVALUE] = 0;
+ if (doshargs) {
+ LinkNode node;
+
+ node = doshargs->first;
+ pparams = x = (char **) zcalloc(((sizeof *x) * (1 + countlinknodes(doshargs))));
+ if (isset(FUNCTIONARGZERO)) {
+ oargv0 = argzero;
+ argzero = ztrdup((char *) node->dat);
+ }
+ node = node->next;
+ for (; node; node = node->next, x++)
+ *x = ztrdup((char *) node->dat);
+ } else {
+ pparams = (char **) zcalloc(sizeof *pparams);
+ if (isset(FUNCTIONARGZERO)) {
+ oargv0 = argzero;
+ argzero = ztrdup(argzero);
+ }
+ }
+ startparamscope();
+ ou = underscore;
+ underscore = ztrdup(underscore);
+ execlist(dupstruct(list), 1, 0);
+ zsfree(underscore);
+ underscore = ou;
+ endparamscope();
+
+ if (retflag) {
+ retflag = 0;
+ breaks = obreaks;
+ }
+ freearray(pparams);
+ if (oargv0) {
+ zsfree(argzero);
+ argzero = oargv0;
+ }
+ zoptind = oldzoptind;
+ pparams = tab;
+
+ if (isset(LOCALOPTIONS)) {
+ /* restore all shell options except PRIVILEGED and RESTRICTED */
+ saveopts[PRIVILEGED] = opts[PRIVILEGED];
+ saveopts[RESTRICTED] = opts[RESTRICTED];
+ memcpy(opts, saveopts, sizeof(opts));
+ } else {
+ /* just restore a couple. */
+ opts[XTRACE] = saveopts[XTRACE];
+ opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
+ opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
+ }
+
+ /*
+ * The trap '...' EXIT runs in the environment of the caller,
+ * so remember it here but run it after resetting the
+ * traps for the parent.
+ */
+ newexittr = sigtrapped[SIGEXIT];
+ newexitfn = sigfuncs[SIGEXIT];
+ if (newexittr & ZSIG_FUNC)
+ shfunctab->removenode(shfunctab, "TRAPEXIT");
+
+ sigtrapped[SIGEXIT] = xexittr;
+ if (xexittr & ZSIG_FUNC) {
+ shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn);
+ sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef;
+ } else
+ sigfuncs[SIGEXIT] = (List) xexitfn;
+
+ if (newexitfn) {
+ dotrapargs(SIGEXIT, &newexittr, newexitfn);
+ freestruct(newexitfn);
+ }
+
+ if (trapreturn < -1)
+ trapreturn++;
+ if (noreturnval)
+ lastval = oldlastval;
+ popheap();
+ } LASTALLOC;
+}
+
+/* Search fpath for an undefined function. Finds the file, and returns the *
+ * list of its contents. */
+
+/**/
+static List
+getfpfunc(char *s)
+{
+ char **pp, buf[PATH_MAX];
+ off_t len;
+ char *d;
+ List r;
+ int fd;
+
+ pp = 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);
+ unmetafy(buf, NULL);
+ if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) {
+ if ((len = lseek(fd, 0, 2)) != -1) {
+ lseek(fd, 0, 0);
+ d = (char *) zcalloc(len + 1);
+ if (read(fd, d, len) == len) {
+ close(fd);
+ d = metafy(d, len, META_REALLOC);
+ HEAPALLOC {
+ r = parse_string(d);
+ } LASTALLOC;
+ zfree(d, len + 1);
+ return r;
+ } else {
+ zfree(d, len + 1);
+ close(fd);
+ }
+ } else {
+ close(fd);
+ }
+ }
+ }
+ return &dummy_list;
+}
+
+/* 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. */
+
+/**/
+static List
+stripkshdef(List l, char *name)
+{
+ Sublist s;
+ Pline p;
+ Cmd c;
+ if(!l)
+ return NULL;
+ if(l->type != Z_SYNC || l->right)
+ return l;
+ s = l->left;
+ if(s->flags || s->right)
+ return l;
+ p = s->left;
+ if(p->right)
+ return l;
+ c = p->left;
+ if(c->type != FUNCDEF || c->flags ||
+ nonempty(c->redir) || nonempty(c->vars) ||
+ empty(c->args) || lastnode(c->args) != firstnode(c->args) ||
+ strcmp(name, peekfirst(c->args)))
+ return l;
+ return c->u.list;
+}
+
+/* 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], **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 = unmeta(s);
+
+ return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode));
+}
+
+/**/
+void
+execsave(void)
+{
+ struct execstack *es;
+
+ es = (struct execstack *) malloc(sizeof(struct execstack));
+ es->args = args;
+ 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->trapreturn = trapreturn;
+ es->noerrs = noerrs;
+ es->subsh_close = subsh_close;
+ es->underscore = underscore;
+ underscore = ztrdup(underscore);
+ es->next = exstack;
+ exstack = es;
+ noerrs = cmdoutpid = 0;
+}
+
+/**/
+void
+execrestore(void)
+{
+ struct execstack *en;
+
+ DPUTS(!exstack, "BUG: execrestore() without execsave()");
+ args = exstack->args;
+ list_pipe_pid = exstack->list_pipe_pid;
+ nowait = exstack->nowait;
+ pline_level = exstack->pline_level;
+ list_pipe_child = exstack->list_pipe_child;
+ list_pipe_job = exstack->list_pipe_job;
+ strcpy(list_pipe_text, exstack->list_pipe_text);
+ lastval = exstack->lastval;
+ noeval = exstack->noeval;
+ badcshglob = exstack->badcshglob;
+ cmdoutpid = exstack->cmdoutpid;
+ cmdoutval = exstack->cmdoutval;
+ trapreturn = exstack->trapreturn;
+ noerrs = exstack->noerrs;
+ subsh_close = exstack->subsh_close;
+ zsfree(underscore);
+ underscore = exstack->underscore;
+ en = exstack->next;
+ free(exstack);
+ exstack = en;
+}