--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/openenvutils/commandshell/shell/src/exec.c Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,4273 @@
+// exec.c - command execution
+//
+// © Portions Copyright (c) Symbian Software Ltd 2007 - 2008. All rights reserved.
+//
+/*
+ * 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"
+
+#ifdef __SYMBIAN32__
+#ifdef __WINSCW__
+#pragma warn_unusedarg off
+#pragma warn_possunwant off
+#endif//__WINSCW__
+#endif//__SYMBIAN32__
+
+#ifdef __SYMBIAN32__
+#include <pthread.h>
+#include <sys/param.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include "dummy.h"
+#endif //__SYMBIAN32__
+
+#ifdef __SYMBIAN32__
+typedef struct
+ {
+ Eprog p;
+ int dont_change_job;
+ int exiting;
+ } st_exec;
+#endif // __SYMBIAN32__
+
+/* used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. */
+
+/**/
+int noerrexit;
+
+/* suppress error messages */
+
+/**/
+mod_export int noerrs;
+
+/* do not save history on exec and exit */
+
+/**/
+int nohistsave;
+
+/* error/break flag */
+
+/**/
+mod_export 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 */
+
+/**/
+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. */
+
+/**/
+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 */
+
+/**/
+mod_export int coprocin;
+
+/* output fd from the coprocess */
+
+/**/
+mod_export int coprocout;
+
+/* != 0 if the line editor is active */
+
+/**/
+mod_export int zleactive;
+
+/* pid of process undergoing 'process substitution' */
+
+/**/
+pid_t cmdoutpid;
+
+/* exit status of process undergoing 'process substitution' */
+
+/**/
+int 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 functions currently active. */
+
+/**/
+mod_export Funcstack funcstack;
+
+#define execerr() if (!forked) { lastval = 1; goto done; } else _exit(1)
+
+static LinkList args;
+static int doneps4;
+static char *STTYval;
+
+/* Execution functions. */
+
+static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
+ execcursh, exectime, execfuncdef, 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(0, 0, bin_whence, 0, -1, BIN_COMMAND, "vV", NULL);
+
+
+/* parse string into a list */
+
+/**/
+mod_export Eprog
+parse_string(char *s)
+{
+ Eprog p;
+ int oldlineno = lineno;
+
+ lexsave();
+ inpush(s, INP_LINENO, NULL);
+ strinbeg(0);
+ lineno = 1;
+ p = parse_list();
+ lineno = oldlineno;
+ if (tok == LEXERR && !lastval)
+ lastval = 1;
+ strinend();
+ inpop();
+ lexrestore();
+ 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", NULL, errno);
+ 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 */
+
+/**/
+#ifndef __SYMBIAN32__
+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", NULL, 0);
+ return -1;
+ }
+ if (tv)
+ gettimeofday(tv, &dummy_tz);
+ 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;
+}
+#endif //__SYMBIAN32__
+
+/*
+ * 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++;
+
+ if (!list_pipe && thisjob != list_pipe_job && !hasprocs(thisjob))
+ deletejob(jobtab + thisjob);
+ cmdpush(CS_CURSH);
+ execlist(state, 1, do_exec);
+ cmdpop();
+
+ state->pc = end;
+
+ return lastval;
+}
+
+/* execve after handling $_ and #! */
+
+#define POUNDBANGLIMIT 64
+
+/**/
+static int
+zexecve(char *pth, char **argv)
+{
+ int eno =0;
+#ifndef __SYMBIAN32__
+ static char buf[PATH_MAX * 2];
+ 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);
+ closedumps();
+ 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')
+ 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 (*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);
+#endif//__SYMBIAN32__
+
+ 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(UNUSED(Cmdnam cmdname), int dash, int defpath)
+{
+ Cmdnam cn;
+ 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, '/') || defpath)) {
+ 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 ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) {
+ LinkList exargs = args;
+ char *t = tricat("stty", " ", s);
+
+ STTYval = 0; /* this prevents infinite recursion */
+ zsfree(s);
+ args = NULL;
+ execstring(t, 1, 0);
+ zsfree(t);
+ args = exargs;
+ } 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));
+ delenvvalue(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);
+ closem(3);
+ 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((errno == EACCES || errno == ENOEXEC) ? 126 : 127);
+ }
+ break;
+ }
+
+ /* for command -p, search the default path */
+ if (defpath) {
+ char *s, pbuf[PATH_MAX];
+ char *dptr, *pe, *ps = DEFAULT_PATH;
+
+ for(;ps;ps = pe ? pe+1 : NULL) {
+ pe = strchr(ps, ':');
+ if (*ps == '/') {
+ s = pbuf;
+ if (pe)
+ struncpy(&s, ps, pe-ps);
+ else
+ strucpy(&s, ps);
+ *s++ = '/';
+ if ((s - pbuf) + strlen(arg0) >= PATH_MAX)
+ continue;
+ strucpy(&s, arg0);
+ if (iscom(pbuf))
+ break;
+ }
+ }
+
+ if (!ps) {
+ zerr("command not found: %s", arg0, 0);
+ _exit(127);
+ }
+
+ ee = zexecve(pbuf, argv);
+
+ 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], *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((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).
+ */
+
+/**/
+mod_export char *
+findcmd(char *arg0, int docopy)
+{
+ 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 == '/') {
+ RET_IF_COM(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);
+ RET_IF_COM(buf);
+ }
+ strcpy(nn, cn->u.name ? *(cn->u.name) : "");
+ strcat(nn, "/");
+ strcat(nn, cn->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;
+}
+
+/**/
+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->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->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;
+}
+
+/**/
+mod_export 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) zshcalloc(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 */
+
+/**/
+mod_export void
+execstring(char *s, int dont_change_job, int exiting)
+{
+ Eprog prog;
+
+ pushheap();
+ if ((prog = parse_string(s)))
+ execode(prog, dont_change_job, exiting);
+ popheap();
+}
+
+
+#ifdef __SYMBIAN32__
+mod_export
+void* execode_wrap(void* any)
+ {
+ st_exec *st=(st_exec*)any;
+ if(st)
+ execode(st->p, st->dont_change_job, st->exiting);
+ return NULL;
+ }
+#endif //__SYMBIAN32__
+
+/**/
+mod_export void
+execode(Eprog p, int dont_change_job, int exiting)
+{
+ struct estate s;
+
+ 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 */
+}
+
+/* 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;
+
+ if (errflag)
+ return (lastval = 1);
+
+ /* In evaluated traps, don't modify the line number. */
+ if ((!intrap || trapisfunc) && !ineval && code)
+ lineno = code - 1;
+
+ code = wc_code(*state->pc++);
+
+ if (code == WC_ASSIGN) {
+ cmdoutval = 0;
+ addvars(state, state->pc - 1, 0);
+ if (isset(XTRACE)) {
+ fputc('\n', xtrerr);
+ fflush(xtrerr);
+ }
+ lv = (lastval ? lastval : cmdoutval);
+ } else
+ lv = (execfuncs[code - WC_CURSH])(state, 0);
+
+ 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, 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;
+
+ cj = thisjob;
+ old_pline_level = pline_level;
+ old_list_pipe = list_pipe;
+ oldlineno = lineno;
+
+ if (sourcelevel && unset(SHINSTDIN))
+ pline_level = list_pipe = 0;
+
+ /* Loop over all sets of comands separated by newline, *
+ * semi-colon or ampersand (`sublists'). */
+ code = *state->pc++;
+ while (wc_code(code) == WC_LIST && !breaks && !retflag) {
+ ltype = WC_LIST_TYPE(code);
+ csp = cmdsp;
+
+ if (ltype & Z_SIMPLE) {
+ next = state->pc + WC_LIST_SKIP(code);
+ execsimple(state);
+ state->pc = next;
+ goto sublist_done;
+ }
+ /* Reset donetrap: this ensures that a trap is only *
+ * called once for each sublist that fails. */
+ donetrap = 0;
+
+ /* Loop through code followed by &&, ||, or end of sublist. */
+ code = *state->pc++;
+ while (wc_code(code) == WC_SUBLIST) {
+ next = state->pc + WC_SUBLIST_SKIP(code);
+ if (!oldnoerrexit)
+ noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END);
+ 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. */
+ donetrap = 1;
+ goto sublist_done;
+ } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
+ 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. */
+ donetrap = 1;
+ goto sublist_done;
+ } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
+ 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:
+
+ noerrexit = oldnoerrexit;
+
+ if (sigtrapped[SIGDEBUG]) {
+ exiting = donetrap;
+ ret = lastval;
+ dotrap(SIGDEBUG);
+ lastval = ret;
+ donetrap = exiting;
+ noerrexit = oldnoerrexit;
+ }
+
+ 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 (!noerrexit && !donetrap) {
+ if (sigtrapped[SIGZERR] && lastval) {
+ dotrap(SIGZERR);
+ donetrap = 1;
+ }
+ if (lastval) {
+ int errreturn = isset(ERRRETURN) &&
+ (isset(INTERACTIVE) || locallevel || sourcelevel);
+ int errexit = isset(ERREXIT) ||
+ (isset(ERRRETURN) && !errreturn);
+ 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;
+ lineno = oldlineno;
+ 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(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;
+
+ 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(), so this
+ * is also the only place where we can expand the job table
+ * under us.
+ */
+ if ((thisjob = newjob = initjob()) == -1) {
+ child_unblock();
+ 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);
+ }
+ mpipe(ipipe);
+ mpipe(opipe);
+ coprocin = ipipe[0];
+ coprocout = opipe[1];
+ fdtable[coprocin] = fdtable[coprocout] = 0;
+ }
+ /* 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) {
+ deletejob(jobtab + thisjob);
+ thisjob = -1;
+ }
+ else
+ spawnjob();
+ child_unblock();
+ return 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) {
+ 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;
+ }
+ 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;
+ printjob(jn, !!isset(LONGLISTJOBS), 1);
+ }
+ else if (newjob != list_pipe_job)
+ deletejob(jn);
+ 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();
+ child_block();
+ } else
+ updated = 0;
+ if (!updated &&
+ list_pipe_job && hasprocs(list_pipe_job) &&
+ !(jobtab[list_pipe_job].stat & STAT_STOPPED)) {
+ child_unblock();
+ child_block();
+ }
+ 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)))) {
+#ifndef __SYMBIAN32__
+ pid_t pid;
+ int synch[2];
+ struct timeval bgtime;
+
+ pipe(synch);
+
+ if ((pid = zfork(&bgtime)) == -1) {
+ trashzle();
+ close(synch[0]);
+ close(synch[1]);
+ fprintf(stderr, "zsh: job can't be suspended\n");
+ fflush(stderr);
+ makerunning(jn);
+ killjb(jn, SIGCONT);
+ thisjob = newjob;
+ }
+ else if (pid) {
+ char dummy;
+
+ lpforked =
+ (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
+ list_pipe_pid = pid;
+ list_pipe_start = bgtime;
+ nowait = errflag = 1;
+ breaks = loops;
+ close(synch[1]);
+ read(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 = pid;
+ }
+ if ((list_pipe || last1) && hasprocs(list_pipe_job))
+ killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
+ break;
+ }
+ else {
+ close(synch[0]);
+ entersubsh(Z_ASYNC, 0, 0, 0);
+ if (jobtab[list_pipe_job].procs) {
+ if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
+ == -1) {
+ setpgrp(0L, mypgrp = getpid());
+ }
+ } else
+ setpgrp(0L, mypgrp = getpid());
+ close(synch[1]);
+ kill(getpid(), SIGSTOP);
+ list_pipe = 0;
+ list_pipe_child = 1;
+ opts[INTERACTIVE] = 0;
+ if (errbrk_saved) {
+ errflag = prev_errflag;
+ breaks = prev_breaks;
+ }
+ break;
+ }
+#else
+ if ((list_pipe || last1) && hasprocs(list_pipe_job))
+ killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
+ break;
+#endif // __SYMBIAN32__
+ }
+ 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))) {
+ deletejob(jn);
+ 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);
+ thisjob = pj;
+
+ }
+ if (slflags & WC_SUBLIST_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(Estate state, wordcode pcode,
+ int how, int input, int output, int last1)
+{
+ int pipes[2];
+ if (breaks || retflag)
+ return;
+
+ /* In evaluated traps, don't modify the line number. */
+ if ((!intrap || trapisfunc) && !ineval && WC_PIPE_LINENO(pcode))
+ lineno = WC_PIPE_LINENO(pcode) - 1;
+
+ if (pline_level == 1) {
+ if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel))
+ 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)
+
+#ifndef __SYMBIAN32__
+ execcmd(state, input, output, how, last1 ? 1 : 2);
+#else
+ execcmd(state, input, 0, how, last1 ? 1 : 2);
+#endif
+ else {
+ int old_list_pipe = list_pipe;
+ Wordcode next = state->pc + (*state->pc), pc;
+ wordcode code;
+
+ state->pc++;
+ for (pc = state->pc; wc_code(code = *pc) == WC_REDIR; pc += 3);
+
+ 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 (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) {
+#ifndef __SYMBIAN32__
+
+ int synch[2];
+ pid_t pid;
+ struct timeval bgtime;
+
+ pipe(synch);
+ if ((pid = zfork(&bgtime)) == -1) {
+ close(synch[0]);
+ close(synch[1]);
+ } else if (pid) {
+ char dummy, *text;
+
+ text = getjobtext(state->prog, state->pc);
+ addproc(pid, text, 0, &bgtime);
+ close(synch[1]);
+ read(synch[0], &dummy, 1);
+ close(synch[0]);
+ } else {
+ zclose(pipes[0]);
+ close(synch[0]);
+ entersubsh(how, 2, 0, 0);
+ close(synch[1]);
+ execcmd(state, input, pipes[1], how, 0);
+ _exit(lastval);
+ }
+#else
+ execcmd(state, input, pipes[1], how, 0);
+#endif//__SYMBIAN32__
+ } else {
+ /* otherwise just do the pipeline normally. */
+#ifndef __SYMBIAN32__
+ subsh_close = pipes[0];
+ execcmd(state, input, pipes[1], how, 0);
+#else
+ execcmd(state, 1, 1, how, 0);
+#endif
+ }
+#ifndef __SYMBIAN32__
+ zclose(pipes[1]);
+#endif
+ 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;
+#ifndef __SYMBIAN32__
+ execpline2(state, *state->pc++, how, pipes[0], output, last1);
+#else
+ execpline2(state, *state->pc++, how, 1, output, last1);
+#endif
+ list_pipe = old_list_pipe;
+ cmdpop();
+#ifndef __SYMBIAN32__
+ zclose(pipes[0]);
+#endif
+ subsh_close = -1;
+ }
+}
+
+/* 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;
+ }
+ }
+}
+
+/* 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)
+{
+ if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) {
+ struct multio *mn = mfds[fd];
+ int i;
+
+#ifndef __SYMBIAN32__
+ char buf[TCBUFSIZE];
+ pid_t pid;
+ int len;
+ struct timeval bgtime;
+
+ if ((pid = zfork(&bgtime))) {
+ for (i = 0; i < mn->ct; i++)
+ zclose(mn->fds[i]);
+ zclose(mn->pipe);
+ if (pid == -1) {
+ mfds[fd] = NULL;
+ return;
+ }
+ mn->ct = 1;
+ mn->fds[0] = fd;
+ addproc(pid, NULL, 1, &bgtime);
+ return;
+ }
+ /* pid == 0 */
+ 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(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(mn->pipe, buf, len);
+ }
+ }
+ _exit(0);
+#else
+ for (i = 0; i < mn->ct; i++)
+ zclose(mn->fds[i]);
+
+ zclose(mn->pipe);
+ mn->ct = 1;
+ mn->fds[0] = fd;
+ return;
+#endif
+ } else if (fd >= 0)
+ 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 = zopenmax();
+
+ 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. */
+
+/**/
+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 *) zhalloc(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(Estate state, Wordcode pc, int export)
+{
+ LinkList vl;
+ int xtr, isstr, htok = 0;
+ char **arr, **ptr, *name;
+ Wordcode opc = state->pc;
+ wordcode ac;
+ local_list1(svl);
+
+ xtr = isset(XTRACE);
+ if (xtr) {
+ printprompt4();
+ doneps4 = 1;
+ }
+ state->pc = pc;
+ while (wc_code(ac = *state->pc++) == WC_ASSIGN) {
+ name = ecgetstr(state, EC_DUPTOK, &htok);
+ if (htok)
+ untokenize(name);
+ 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 (vl && htok) {
+ prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) :
+ PF_ASSIGN));
+ if (errflag) {
+ state->pc = opc;
+ return;
+ }
+ if (isset(GLOBASSIGN) || !isstr)
+ globlist(vl, 0);
+ 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 (export && !strchr(name, '[')) {
+ if (export < 0 && isset(RESTRICTED) &&
+ (pm = (Param) paramtab->removenode(paramtab, name)) &&
+ (pm->flags & PM_RESTRICTED)) {
+ zerr("%s: restricted", pm->nam, 0);
+ zsfree(val);
+ state->pc = opc;
+ return;
+ }
+ if (strcmp(name, "STTY") == 0) {
+ zsfree(STTYval);
+ STTYval = ztrdup(val);
+ }
+ allexp = opts[ALLEXPORT];
+ opts[ALLEXPORT] = 1;
+ pm = assignsparam(name, val,
+ WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
+ opts[ALLEXPORT] = allexp;
+ } else
+ pm = assignsparam(name, val,
+ WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
+ 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, WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC);
+ if (errflag) {
+ state->pc = opc;
+ return;
+ }
+ }
+ state->pc = opc;
+}
+
+/**/
+void
+setunderscore(char *str)
+{
+ if (str && *str) {
+ int l = strlen(str) + 1, nl = (l + 31) & ~31;
+
+ if (nl > underscorelen || (underscorelen - nl) > 64) {
+ zfree(underscore, underscorelen);
+ underscore = (char *) zalloc(underscorelen = nl);
+ }
+ strcpy(underscore, str);
+ underscoreused = l;
+ } else {
+ if (underscorelen > 128) {
+ zfree(underscore, underscorelen);
+ underscore = (char *) zalloc(underscorelen = 32);
+ }
+ *underscore = '\0';
+ underscoreused = 1;
+ }
+}
+
+/* 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);
+ if (esglob) {
+ LinkList ostrs = strs;
+ globlist(strs, 0);
+ strs = ostrs;
+ }
+ }
+}
+#ifdef __SYMBIAN32__
+extern int handlePipeCmds(char*, pid_t, int fds[3], int);
+extern void clearfds(void);
+#endif
+
+/**/
+static void
+execcmd(Estate state, 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, do_exec = 0, i, htok = 0;
+ int nullexec = 0, assign = 0, forked = 0;
+ int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
+ /* Various flags to the command. */
+ int cflags = 0, checked = 0, oautocont = opts[AUTOCONTINUE];
+ LinkList redir;
+ wordcode code;
+ Wordcode beg = state->pc, varspc;
+ FILE *oxtrerr = xtrerr;
+ static int op_fd=-1;
+ int thread_created=0;
+
+ doneps4 = 0;
+ redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
+ if (wc_code(*state->pc) == WC_ASSIGN) {
+ 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
+ varspc = NULL;
+
+ code = *state->pc++;
+
+ type = wc_code(code);
+
+ /* 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. */
+ args = (type == WC_SIMPLE ?
+ ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
+
+ 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 && args && nonempty(args) &&
+ *(char *)peekfirst(args) == '%') {
+ if (how & Z_DISOWN)
+ 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"));
+ }
+
+ /* 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) {
+ while (args && 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;
+
+ /* autoload the builtin if necessary */
+ if (!((Builtin) hn)->handlerfunc) {
+ load_module(((Builtin) hn)->optstr);
+ hn = builtintab->getnode(builtintab, cmdarg);
+ }
+ assign = (hn && (hn->flags & BINF_MAGICEQUALS));
+ break;
+ }
+ cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
+ cflags |= hn->flags;
+ checked = 0;
+ if (cflags & BINF_COMMAND && nextnode(firstnode(args))) {
+ /* check for options to command builtin */
+ char *next = (char *) getdata(nextnode(firstnode(args)));
+ char *cmdopt;
+ if (next && *next == '-' && strlen(next) == 2 &&
+ (cmdopt = strchr("pvV", next[1])))
+ {
+ if (*cmdopt == 'p') {
+ uremnode(args, firstnode(args));
+ use_defpath = 1;
+ if (nextnode(firstnode(args)))
+ next = (char *) getdata(nextnode(firstnode(args)));
+ } else {
+ hn = (HashNode)&commandbn;
+ is_builtin = 1;
+ break;
+ }
+ }
+ if (!strcmp(next, "--"))
+ uremnode(args, firstnode(args));
+ }
+ uremnode(args, firstnode(args));
+ hn = NULL;
+ if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
+ break;
+ }
+ }
+
+ /* Do prefork substitutions */
+ esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0;
+ if (args && htok)
+ prefork(args, esprefork);
+
+ if (type == WC_SIMPLE) {
+ 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", NULL, 0);
+ errflag = lastval = 1;
+ 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;
+ return;
+ } else {
+ cmdoutval = 0;
+ if (varspc)
+ addvars(state, varspc, 0);
+ if (errflag)
+ lastval = errflag;
+ else
+ lastval = cmdoutval;
+ if (isset(XTRACE)) {
+ fputc('\n', xtrerr);
+ fflush(xtrerr);
+ }
+ return;
+ }
+ } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
+ zerrnam("exec", "%s: restricted",
+ (char *) getdata(firstnode(args)), 0);
+ lastval = 1;
+ 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
+ */
+ if (errflag || checked || is_builtin ||
+ (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) {
+ zwarn("no such builtin: %s", cmdarg, 0);
+ lastval = 1;
+ opts[AUTOCONTINUE] = oautocont;
+ return;
+ }
+ break;
+ }
+ if (!(hn->flags & BINF_PREFIX)) {
+ is_builtin = 1;
+
+ /* autoload the builtin if necessary */
+ if (!((Builtin) hn)->handlerfunc) {
+ load_module(((Builtin) hn)->optstr);
+ hn = builtintab->getnode(builtintab, cmdarg);
+ }
+ break;
+ }
+ cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
+ cflags |= hn->flags;
+ uremnode(args, firstnode(args));
+ hn = NULL;
+ }
+ }
+
+ if (errflag) {
+ lastval = 1;
+ opts[AUTOCONTINUE] = oautocont;
+ return;
+ }
+
+ /* Get the text associated with this command. */
+ if ((how & Z_ASYNC) ||
+ (!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
+ text = getjobtext(state->prog, beg);
+ else
+ text = NULL;
+
+ /* Set up special parameter $_ */
+
+ 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))
+ 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;
+ opts[AUTOCONTINUE] = oautocont;
+ return;
+ }
+
+ if (type == WC_SIMPLE && !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");
+#ifndef __SYMBIAN32__
+ 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)->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);
+ }
+ }
+#endif //__SYMBIAN32__
+ /* 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 || nullexec || type >= WC_CURSH);
+
+ /**************************************************************************
+ * 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 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 ((how & Z_ASYNC) ||
+ (!do_exec &&
+ (((is_builtin || is_shfunc) && output) ||
+ (!is_cursh && (last1 != 1 || nsigtrapped || havefiles()))))) {
+
+// 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 ((esglob = !(cflags & BINF_NOGLOB)) && args && 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 (!(xtrerr = fdopen(movefd(dup(fileno(stderr))), "w")))
+ xtrerr = stderr;
+ else
+ fdtable[fileno(xtrerr)] = 3;
+ }
+
+ /* Add pipeline input/output to mnodes */
+#ifndef __SYMBIAN32__
+ if (input)
+ {
+ addfd(forked, save, mfds, 0, input, 0);
+ }
+
+ if (output)
+ {
+ addfd(forked, save, mfds, 1, output, 1);
+ }
+#endif
+ /* 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 (fn->fd2 == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fn->fd2, 0);
+ } else if (fn->type == REDIR_OUTPIPE) {
+ if (fn->fd2 == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fn->fd2, 1);
+ } else {
+ 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", NULL, 0);
+ execerr();
+ }
+ if (unset(EXECOPT))
+ continue;
+ switch(fn->type) {
+ case REDIR_HERESTR:
+ fil = getherestr(fn);
+ if (fil == -1) {
+ closemnodes(mfds);
+ fixfds(save);
+ if (errno != EINTR)
+ zwarn("%e", NULL, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, 0);
+ break;
+ case REDIR_READ:
+ case REDIR_READWRITE:
+ 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", 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 == 1 && fn->fd1 == 0 &&
+ isset(SHINSTDIN) && interact && !zleactive)
+ init_io();
+ break;
+ case REDIR_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 REDIR_MERGEIN:
+ case REDIR_MERGEOUT:
+ if (fn->fd2 < 10)
+ closemn(mfds, fn->fd2);
+ if (fn->fd2 > 9 &&
+ (fdtable[fn->fd2] ||
+ 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 = dup(fd);
+ }
+ if (fil == -1) {
+ char fdstr[4];
+
+ closemnodes(mfds);
+ fixfds(save);
+ if (fn->fd2 != -2)
+ sprintf(fdstr, "%d", fn->fd2);
+ zwarn("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, errno);
+ execerr();
+ }
+ addfd(forked, save, mfds, fn->fd1, fil, fn->type == REDIR_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)
+ zwarn("%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++)
+ if (mfds[i] && mfds[i]->ct >= 2)
+ closemn(mfds, i);
+
+ if (nullexec) {
+ 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 nullexec is 2, we have variables to add with the redirections
+ * in place.
+ */
+ if (varspc)
+ addvars(state, varspc, 0);
+ lastval = errflag ? errflag : cmdoutval;
+ if (isset(XTRACE)) {
+ fputc('\n', xtrerr);
+ fflush(xtrerr);
+ }
+ } else 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 != WC_SUBSH) ? 2 : 1, 1,
+ (do_exec || (type >= WC_CURSH && last1 == 1))
+ && !forked);
+ if (type >= WC_CURSH) {
+ if (last1 == 1)
+ do_exec = 1;
+ lastval = (execfuncs[type - WC_CURSH])(state, do_exec);
+ } 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)))) {
+ if (varspc)
+ save_params(state, varspc, &restorelist, &removelist);
+ else
+ restorelist = removelist = NULL;
+ }
+ if (varspc) {
+ /* Export this if the command is a shell function,
+ * but not if it's a builtin.
+ */
+ addvars(state, varspc, is_shfunc);
+ if (errflag) {
+ if (restorelist)
+ restore_params(restorelist, removelist);
+ lastval = 1;
+ fixfds(save);
+ goto done;
+ }
+ }
+
+ 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((Shfunc) hn, args);
+#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, input, output);
+
+#ifdef PATH_DEV_FD
+ closem(2);
+#endif
+ fflush(stdout);
+#ifdef __SYMBIAN32__
+ fflush(stderr);
+#endif
+ if (save[1] == -2) {
+ if (ferror(stdout)) {
+ zwarn("write error: %e", NULL, errno);
+ clearerr(stdout);
+ }
+ } else
+ clearerr(stdout);
+ }
+ if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
+ lastval && !subsh) {
+ fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
+ }
+
+ 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 {
+ pid_t pid;
+ int fds[3];
+ struct timeval bgtime;
+ struct timezone dummy_tz;
+ char* arg0 = (char *) peekfirst(args);
+
+ gettimeofday(&bgtime, &dummy_tz);
+
+ if (!forked)
+ setiparam("SHLVL", --shlvl);
+ if (do_exec) {
+ /* 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(NULL, 1, HFILE_USE_OPTIONS);
+ }
+ if (varspc) {
+ addvars(state, varspc, -1);
+ if (errflag)
+ _exit(1);
+ }
+ closem(1);
+ if (coprocin)
+ zclose(coprocin);
+ if (coprocout)
+ zclose(coprocout);
+#ifdef HAVE_GETRLIMIT
+ if (!forked)
+ setlimits(NULL);
+#endif
+ if (how & Z_ASYNC) {
+ zsfree(STTYval);
+ STTYval = 0;
+ }
+
+#ifdef __SYMBIAN32__
+ {
+ //find and execute the external command
+ const int data_size=512;
+ char data[data_size];
+ int read_cnt=0;
+ char** argv;
+ fd_set readfds;
+ int status=-1;
+ size_t oldcmdlen=0, newcmdlen=0;
+ struct timeval tv;
+ char *pcmd=NULL;
+ char* pfile=NULL;
+
+ //generate full path
+ argv=makecline(args);
+ pfile=argv[0];
+
+ //generate commands
+ while(++argv && *argv)
+ {
+ newcmdlen=strlen(*argv)+newcmdlen+1;
+ if(!pcmd)
+ pcmd=(char*)calloc(newcmdlen+1, sizeof(char));
+ else
+ pcmd=(char*)realloc(pcmd, newcmdlen+1);
+
+ sprintf(pcmd+oldcmdlen, "%s ", *argv);
+ oldcmdlen=newcmdlen;
+ }
+
+ //execute the external command
+ if ( (pid=popen3(pfile, pcmd, environ, fds)) == -1)
+ {
+ zerr("command not found: %s", arg0, 0);
+ opts[AUTOCONTINUE] = oautocont;
+ clearfds();
+ free(pcmd);
+ lastval=127;
+ return;
+ }
+ free(pcmd);
+
+ if(input || output)
+ {
+ handlePipeCmds(pfile, pid, fds, output);
+ }
+ else
+ {
+ //read the output from the executed command
+ for(;;)
+ {
+ int wait_pid=waitpid(pid, &status, WNOHANG);
+ if(pid == wait_pid || wait_pid == -1) //dont wait for the child to terminate
+ {
+ int max= MAX(fds[1], fds[2]);
+
+ FD_ZERO(&readfds);
+ FD_SET(fds[1], &readfds);
+ FD_SET(fds[2], &readfds);
+
+ //read available data, if child is terminated already.
+ for(;;)
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ if(select(max+1, &readfds, NULL, NULL, &tv) <=0)
+ break;
+ else
+ {
+ if(FD_ISSET(fds[1], &readfds))
+ {
+ memset(&data[0], 0, 512);
+ read_cnt=read(fds[1], &data[0], 512);
+ if(read_cnt>0)
+ write(1, data, read_cnt);
+ }
+
+ if(FD_ISSET(fds[2], &readfds))
+ {
+ memset(&data[0], 0, 512);
+ read_cnt=read(fds[2], &data[0], 512);
+ if(read_cnt>0)
+ write(2, data, read_cnt);
+ }
+ }
+ }
+ //Updating the Error status.
+ if(WIFEXITED(status))
+ lastval = WEXITSTATUS(status);
+ else if(WIFTERMINATED(status))
+ lastval = WTERMINATESTATUS(status);
+ else if(WIFPANICED(status))
+ lastval = WPANICCODE(status);
+ break;
+ }
+ else
+ {
+ int max= MAX(0, MAX(fds[1], fds[2]));
+
+ FD_ZERO(&readfds);
+ FD_SET(fds[1], &readfds);
+ FD_SET(fds[2], &readfds);
+ FD_SET(0, &readfds);
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ if(select(max+1, &readfds, NULL, NULL, &tv) < 0)
+ break;
+
+ else
+ {
+ if(FD_ISSET(fds[1], &readfds))
+ {
+ memset(&data[0], 0, 512);
+ read_cnt=read(fds[1], &data[0], 512);
+ if(read_cnt>0)
+ write(1, data, read_cnt);
+ }
+
+ if(FD_ISSET(fds[2], &readfds))
+ {
+ memset(&data[0], 0, 512);
+ read_cnt=read(fds[2], &data[0], 512);
+ if(read_cnt>0)
+ write(2, data, read_cnt);
+ }
+
+ if(FD_ISSET(0, &readfds))
+ {
+ memset(&data[0], 0, 512);
+ read_cnt=read(0, &data[0], 512);
+ if(read_cnt>0)
+ write(fds[0], data, read_cnt);
+ }
+ }//else
+
+ } //else
+ }//for (;;)
+ close(fds[0]);
+ close(fds[1]);
+ close(fds[2]);
+ }
+ }
+#endif
+ 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);
+ }
+ }
+ opts[AUTOCONTINUE] = oautocont;
+ fixfds(save);
+ return;
+ }
+ }
+
+ err:
+ if (forked)
+ _exit(lastval);
+ fixfds(save);
+
+ done:
+ if (xtrerr != oxtrerr) {
+ fil = fileno(xtrerr);
+ fclose(xtrerr);
+ xtrerr = oxtrerr;
+ zclose(fil);
+ }
+
+ zsfree(STTYval);
+ STTYval = 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))) {
+ if (pm->env)
+ delenv(pm);
+ if (!(pm->flags & PM_SPECIAL)) {
+ paramtab->removenode(paramtab, s);
+ } else if (!(pm->flags & PM_READONLY) &&
+ (unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) {
+ Param tpm = (Param) hcalloc(sizeof *tpm);
+ tpm->nam = pm->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->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->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->nam, pm);
+ if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->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;
+}
+
+/**/
+int
+forklevel;
+
+/**/
+static void
+entersubsh(int how, int cl, int fake, int revertpgrp)
+{
+ int sig, monitor;
+
+ if (cl != 2)
+ for (sig = 0; sig < VSIGCOUNT; sig++)
+ if (!(sigtrapped[sig] & ZSIG_FUNC))
+ unsettrap(sig);
+ if (!(monitor = isset(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 (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 (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 (revertpgrp && getpid() == mypgrp)
+ release_pgrp();
+ if (SHTTY != -1) {
+ shout = NULL;
+ 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(monitor);
+ get_usage();
+ forklevel = locallevel;
+}
+
+/* close internal shell fds */
+
+/**/
+mod_export 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)) {
+ qt = 1;
+ break;
+ }
+ quotesubst(str);
+ untokenize(str);
+ if (typ == REDIR_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) {
+ int ef = errflag;
+
+ parsestr(buf);
+
+ if (!errflag)
+ errflag = ef;
+ }
+ 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';
+ if ((fd = gettempfile(NULL, 1, &s)) < 0)
+ 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)
+{
+ Eprog prog;
+ int pipes[2];
+
+#ifndef __SYMBIAN32__
+ pid_t pid;
+#endif
+ Wordcode pc;
+ int fd;
+ LinkList retval;
+ if (!(prog = parse_string(cmd)))
+ return NULL;
+
+ 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_READ &&
+ !pc[4] &&
+ wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) {
+ /* $(< word) */
+ int stream;
+ char *s = dupstring(ecrawstr(prog, pc + 5, NULL));
+
+ 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);
+ }
+#ifndef __SYMBIAN32__
+ mpipe(pipes);
+ child_block();
+ cmdoutval = 0;
+ if ((cmdoutpid = pid = zfork(NULL)) == -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;
+ waitforpid(pid); /* unblocks */
+ lastval = cmdoutval;
+ return retval;
+ }
+ /* pid == 0 */
+ child_unblock();
+ zclose(pipes[0]);
+ redup(pipes[1], 1);
+ opts[MONITOR] = 0;
+ entersubsh(Z_SYNC, 1, 0, 0);
+ cmdpush(CS_CMDSUBST);
+
+ cmdpop();
+ close(1);
+ _exit(lastval);
+ zerr("exit returned in child!!", NULL, 0);
+ kill(getpid(), SIGKILL);
+#endif // __SYMBIAN32__
+
+ //execute the command within the same process space
+ mpipe(pipes);
+ fd = dup(1); /* Preserve stdout */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ redup(pipes[1], 1);
+ execode(prog, 0, 1);
+
+ retval=readoutput(pipes[0], qt);
+ redup(fd, 1);
+ zclose(pipes[0]);
+ zclose(pipes[1]);
+ close(fd);
+
+ return retval;
+}
+
+/* read output of command substitution */
+
+/**/
+mod_export LinkList
+readoutput(int in, int qt)
+{
+ LinkList ret;
+ char *buf, *ptr;
+ int bsiz, cnt = 0;
+ char c;
+ FILE *fin;
+#ifdef __SYMBIAN32__
+ struct timeval tv;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(in, &readfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 1;
+
+ fin = fdopen(in, "r");
+ ret = newlinklist();
+ ptr = buf = (char *) hcalloc(bsiz = 64);
+ if(select(in+1, &readfds, NULL, NULL, &tv)==0)
+ {
+ goto dont_read;
+ }
+#endif //__SYMBIAN32__
+
+ while ((read(in, &c, 1) >0) || errno == EINTR) {
+#ifndef __SYMBIAN32__
+ if (c == EOF) {
+ errno = 0;
+ clearerr(fin);
+ continue;
+ }
+#endif
+ if (imeta(c)) {
+ *ptr++ = Meta;
+ c ^= 32;
+ cnt++;
+ }
+ if (++cnt >= bsiz) {
+ char *pp = (char *) hcalloc(bsiz *= 2);
+
+ memcpy(pp, buf, cnt - 1);
+ ptr = (buf = pp) + cnt - 1;
+ }
+ *ptr++ = c;
+#ifndef __SYMBIAN32__
+ if (ptr[-1]=='\n')
+ {
+ break;
+ }
+#else
+ FD_ZERO(&readfds);
+ FD_SET(in, &readfds);
+ if(select(in+1, &readfds, NULL, NULL, &tv)==0)
+ {
+ break;
+ }
+#endif
+ }
+
+dont_read:
+ 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 *str;
+ Eprog prog;
+
+ for (str = cmd + 2; *str && *str != Outpar; str++);
+ if (!*str || cmd[1] != Inpar) {
+ zerr("oops.", NULL, 0);
+ return NULL;
+ }
+ *str = '\0';
+ if (str[1] || !(prog = parse_string(cmd + 2))) {
+ zerr("parse error in process substitution", NULL, 0);
+ return NULL;
+ }
+ return prog;
+}
+
+/* =(...) */
+
+/**/
+char *
+getoutputfile(char *cmd)
+{
+ Eprog prog;
+
+#ifndef __SYMBIAN32__
+ pid_t pid;
+ char *nam;
+ int fd;
+
+ if (thisjob == -1)
+ return NULL;
+ if (!(prog = parsecmd(cmd)))
+ return NULL;
+ if (!(nam = gettempname(NULL, 0)))
+ return NULL;
+
+ if (!jobtab[thisjob].filelist)
+ jobtab[thisjob].filelist = znewlinklist();
+ zaddlinknode(jobtab[thisjob].filelist, nam);
+
+ child_block();
+ fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
+
+ if (fd < 0 || (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);
+ cmdoutval = 0;
+ jobtab[thisjob].stat = os;
+ return nam;
+ }
+
+ /* pid == 0 */
+ redup(fd, 1);
+ opts[MONITOR] = 0;
+ entersubsh(Z_SYNC, 1, 0, 0);
+ cmdpush(CS_CMDSUBST);
+ execode(prog, 0, 1);
+ cmdpop();
+ close(1);
+ _exit(lastval);
+ zerr("exit returned in child!!", NULL, 0);
+ kill(getpid(), SIGKILL);
+ return NULL;
+#else
+ st_exec stexec;
+ pthread_t testThread;
+
+ if (!(prog = parsecmd(cmd)))
+ return NULL;
+
+ stexec.dont_change_job=0;
+ stexec.exiting=1;
+ stexec.p=prog;
+
+ pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
+ return NULL;
+#endif
+}
+
+#if !defined(PATH_DEV_FD) && defined(HAVE_FIFOS)
+/* get a temporary named pipe */
+
+static char *
+namedpipe(void)
+{
+ char *tnam = gettempname(NULL, 1);
+
+# 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
+ Eprog prog;
+ int out = *cmd == Inang;
+
+#ifndef __SYMBIAN32__
+ char *pnam;
+ pid_t pid;
+ struct timeval bgtime;
+
+#ifndef PATH_DEV_FD
+ int fd;
+ if (thisjob == -1)
+ return NULL;
+ if (!(pnam = namedpipe()))
+ return NULL;
+ if (!(prog = parsecmd(cmd)))
+ return NULL;
+ if (!jobtab[thisjob].filelist)
+ jobtab[thisjob].filelist = znewlinklist();
+ zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam));
+
+ if ((pid = zfork(&bgtime))) {
+ if (pid == -1)
+ return NULL;
+ if (!out)
+ addproc(pid, NULL, 1, &bgtime);
+ return pnam;
+ }
+ 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, 0);
+ redup(fd, out);
+#else /* PATH_DEV_FD */
+ int pipes[2];
+
+ if (thisjob == -1)
+ return NULL;
+ pnam = hcalloc(strlen(PATH_DEV_FD) + 6);
+ if (!(prog = parsecmd(cmd)))
+ return NULL;
+ mpipe(pipes);
+ if ((pid = zfork(&bgtime))) {
+ sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]);
+ zclose(pipes[out]);
+ if (pid == -1)
+ {
+ zclose(pipes[!out]);
+ return NULL;
+ }
+ fdtable[pipes[!out]] = 2;
+ if (!out)
+ {
+ addproc(pid, NULL, 1, &bgtime);
+ }
+ return pnam;
+ }
+ entersubsh(Z_ASYNC, 1, 0, 0);
+ redup(pipes[out], out);
+ closem(0); /* this closes pipes[!out] as well */
+
+#endif /* PATH_DEV_FD */
+
+ cmdpush(CS_CMDSUBST);
+ execode(prog, 0, 1);
+ cmdpop();
+ zclose(out);
+ _exit(lastval);
+ return NULL;
+#else /*__SYMBIAN32__*/
+ st_exec stexec;
+ pthread_t testThread;
+
+ if (!(prog = parsecmd(cmd)))
+ return NULL;
+
+ stexec.dont_change_job=0;
+ stexec.exiting=1;
+ stexec.p=prog;
+
+ pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
+ return NULL;
+#endif // __SYMBIAN32__
+#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 out = *cmd == Inang;
+
+
+#ifndef __SYMBIAN32__
+ pid_t pid;
+ struct timeval bgtime;
+ int pipes[2];
+
+ if (!(prog = parsecmd(cmd)))
+ return -1;
+
+ mpipe(pipes);
+ if ((pid = zfork(&bgtime))) {
+ zclose(pipes[out]);
+ if (pid == -1) {
+ zclose(pipes[!out]);
+ return -1;
+ }
+ if (!nullexec)
+ addproc(pid, NULL, 1, &bgtime);
+ return pipes[!out];
+ }
+ entersubsh(Z_ASYNC, 1, 0, 0);
+ redup(pipes[out], out);
+ closem(0); /* this closes pipes[!out] as well */
+ cmdpush(CS_CMDSUBST);
+ execode(prog, 0, 1);
+ cmdpop();
+ _exit(lastval);
+#else
+ st_exec stexec;
+ pthread_t testThread;
+
+ if (!(prog = parsecmd(cmd)))
+ return -1;
+
+ stexec.dont_change_job=0;
+ stexec.exiting=1;
+ stexec.p=prog;
+
+ pthread_create(&testThread, NULL, &execode_wrap, (void*)&stexec);
+#endif //__SYMBIAN32__
+ 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
+ *
+ * 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 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);
+ }
+ }
+}
+
+extern int tracingcond;
+
+/* 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 = 1;
+ 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);
+ }
+ errflag = 0;
+ /* 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 int
+execfuncdef(Estate state, UNUSED(int do_exec))
+{
+ Shfunc shf;
+ char *s;
+ int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0;
+ Wordcode beg = state->pc, end;
+ Eprog prog;
+ Patprog *pp;
+ LinkList names;
+
+ end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
+ if (!(names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
+ state->pc = end;
+ return 0;
+ }
+ 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)
+ execsubst(names);
+
+ while ((s = (char *) ugetnode(names))) {
+ prog = (Eprog) zalloc(sizeof(*prog));
+ prog->npats = npats;
+ prog->nref = 1; /* allocated from permanent storage */
+ prog->len = len;
+ if (state->prog->dump) {
+ prog->flags = EF_MAP;
+ incrdumpcount(state->prog->dump);
+ prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
+ prog->prog = state->pc;
+ prog->strs = state->strs + sbeg;
+ prog->dump = state->prog->dump;
+ } 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->flags = 0;
+
+ /* is this shell function a signal trap? */
+ if (!strncmp(s, "TRAP", 4) &&
+ (signum = getsignum(s + 4)) != -1) {
+ if (settrap(signum, shf->funcdef)) {
+ freeeprog(shf->funcdef);
+ zfree(shf, sizeof(*shf));
+ state->pc = end;
+ return 1;
+ }
+ sigtrapped[signum] |= ZSIG_FUNC;
+
+ /*
+ * Remove the old node explicitly in case it has
+ * an alternative name
+ */
+ removetrapnode(signum);
+ }
+ shfunctab->addnode(shfunctab, ztrdup(s), shf);
+ }
+ state->pc = end;
+ return 0;
+}
+
+/* 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;
+
+ if (!list_pipe && 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);
+ }
+
+ 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);
+ }
+ ocs = cmdstack;
+ ocsp = cmdsp;
+ cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
+ cmdsp = 0;
+ if ((osfc = sfcontext) == SFC_NONE)
+ sfcontext = SFC_DIRECT;
+ doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0);
+ sfcontext = osfc;
+ free(cmdstack);
+ cmdstack = ocs;
+ cmdsp = ocsp;
+
+ 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(Estate state, UNUSED(int do_exec))
+{
+ Shfunc shf;
+ char *oldscriptname;
+
+ if (!(shf = loadautofn(state->prog->shf, 1, 0)))
+ return 1;
+
+ oldscriptname = scriptname;
+ scriptname = dupstring(shf->nam);
+ execode(shf->funcdef, 1, 0);
+ scriptname = oldscriptname;
+
+ return lastval;
+}
+
+/**/
+Shfunc
+loadautofn(Shfunc shf, int fksh, int autol)
+{
+ int noalias = noaliases, ksh = 1;
+ Eprog prog;
+
+ pushheap();
+
+ noaliases = (shf->flags & PM_UNALIASED);
+ prog = getfpfunc(shf->nam, &ksh);
+ noaliases = noalias;
+
+ if (ksh == 1) {
+ ksh = fksh;
+ if (ksh == 1)
+ ksh = (shf->flags & PM_KSHSTORED) ? 2 :
+ (shf->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->nam, 0);
+ locallevel++;
+ popheap();
+ return NULL;
+ }
+ if (!prog)
+ 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->flags &= ~PM_UNDEFINED;
+ } else {
+ VARARR(char, n, strlen(shf->nam) + 1);
+ strcpy(n, shf->nam);
+ execode(prog, 1, 0);
+ shf = (Shfunc) shfunctab->getnode(shfunctab, n);
+ if (!shf || (shf->flags & PM_UNDEFINED)) {
+ /* We're not actually in the function; decrement locallevel */
+ locallevel--;
+ zwarn("%s: function not defined by file", n, 0);
+ locallevel++;
+ popheap();
+ return NULL;
+ }
+ }
+ } else {
+ freeeprog(shf->funcdef);
+ if (prog->flags & EF_MAP)
+ shf->funcdef = stripkshdef(prog, shf->nam);
+ else
+ shf->funcdef = dupeprog(stripkshdef(prog, shf->nam), 0);
+ shf->flags &= ~PM_UNDEFINED;
+ }
+ popheap();
+
+ return shf;
+}
+
+/*
+ * execute a shell 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(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
+{
+ char **tab, **x, *oargv0;
+ int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
+ int *oldpipestats = NULL;
+ char saveopts[OPT_SIZE], *oldscriptname = scriptname, *fname = dupstring(name);
+ int obreaks;
+ struct funcstack fstack;
+#ifdef MAX_FUNCTION_DEPTH
+ static int funcdepth;
+#endif
+
+ pushheap();
+
+ oargv0 = NULL;
+ obreaks = breaks;;
+ if (trapreturn < 0)
+ trapreturn--;
+ oldlastval = lastval;
+ oldnumpipestats = numpipestats;
+ if (noreturnval) {
+ /*
+ * Easiest to use the heap here since we're bracketed
+ * immediately by a pushheap/popheap pair.
+ */
+ size_t bytes = sizeof(int)*numpipestats;
+ oldpipestats = (int *)zhalloc(bytes);
+ memcpy(oldpipestats, pipestats, bytes);
+ }
+
+ starttrapscope();
+
+ tab = pparams;
+ if (!(flags & PM_UNDEFINED))
+ scriptname = dupstring(name);
+ oldzoptind = zoptind;
+ zoptind = 1;
+ oldoptcind = optcind;
+ 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(saveopts, opts, sizeof(opts));
+
+ if (flags & PM_TAGGED)
+ opts[XTRACE] = 1;
+ opts[PRINTEXITVALUE] = 0;
+ if (doshargs) {
+ LinkNode node;
+
+ node = doshargs->first;
+ pparams = x = (char **) zshcalloc(((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 **) zshcalloc(sizeof *pparams);
+ if (isset(FUNCTIONARGZERO)) {
+ oargv0 = argzero;
+ argzero = ztrdup(argzero);
+ }
+ }
+#ifdef MAX_FUNCTION_DEPTH
+ if(++funcdepth > MAX_FUNCTION_DEPTH)
+ {
+ zerr("maximum nested function level reached", NULL, 0);
+ goto undoshfunc;
+ }
+#endif
+ fstack.name = dupstring(name);
+ fstack.prev = funcstack;
+ funcstack = &fstack;
+
+ if (prog->flags & EF_RUN) {
+ Shfunc shf;
+
+ prog->flags &= ~EF_RUN;
+
+ runshfunc(prog, NULL, fstack.name);
+
+ if (!(shf = (Shfunc) shfunctab->getnode(shfunctab,
+ (name = fname)))) {
+ zwarn("%s: function not defined by file", name, 0);
+ if (noreturnval)
+ errflag = 1;
+ else
+ lastval = 1;
+ goto doneshfunc;
+ }
+ prog = shf->funcdef;
+ }
+ runshfunc(prog, wrappers, fstack.name);
+ doneshfunc:
+ funcstack = fstack.prev;
+#ifdef MAX_FUNCTION_DEPTH
+ undoshfunc:
+ --funcdepth;
+#endif
+ if (retflag) {
+ retflag = 0;
+ breaks = obreaks;
+ }
+ freearray(pparams);
+ if (oargv0) {
+ zsfree(argzero);
+ argzero = oargv0;
+ }
+ pparams = tab;
+ optcind = oldoptcind;
+ zoptind = oldzoptind;
+ scriptname = oldscriptname;
+
+ 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];
+ }
+
+ endtrapscope();
+
+ if (trapreturn < -1)
+ trapreturn++;
+ ret = lastval;
+ if (noreturnval) {
+ lastval = oldlastval;
+ numpipestats = oldnumpipestats;
+ memcpy(pipestats, oldpipestats, sizeof(int)*numpipestats);
+ }
+ popheap();
+
+ if (exit_pending) {
+ if (locallevel) {
+ /* 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;
+ VARARR(char, ou, underscoreused);
+
+ memcpy(ou, underscore, underscoreused);
+
+ while (wrap) {
+ wrap->module->wrapper++;
+ cont = wrap->handler(prog, wrap->next, name);
+ wrap->module->wrapper--;
+
+ if (!wrap->module->wrapper &&
+ (wrap->module->flags & MOD_UNLOAD))
+ unload_module(wrap->module, NULL);
+
+ if (!cont)
+ return;
+ wrap = wrap->next;
+ }
+ startparamscope();
+ execode(prog, 1, 0);
+ setunderscore(ou);
+ endparamscope();
+}
+
+/* Search fpath for an undefined function. Finds the file, and returns the *
+ * list of its contents. */
+
+/**/
+Eprog
+getfpfunc(char *s, int *ksh)
+{
+ char **pp, buf[PATH_MAX];
+ off_t len;
+ off_t rlen;
+ char *d;
+ Eprog 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);
+ if ((r = try_dump_file(*pp, s, buf, ksh)))
+ return r;
+ unmetafy(buf, NULL);
+ if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) {
+ if ((len = lseek(fd, 0, 2)) != -1) {
+ 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);
+ scriptname = oldscriptname;
+
+ zfree(d, len + 1);
+
+ return r;
+ } else
+ close(fd);
+
+ zfree(d, len + 1);
+ } else
+ close(fd);
+ }
+ }
+ return &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 = prog->prog;
+ wordcode code;
+
+ if (!prog)
+ return NULL;
+ 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 || strcmp(name, ecrawstr(prog, pc + 1, NULL)))
+ 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], **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 *) 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 = 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;
+ setunderscore(exstack->underscore);
+ zsfree(exstack->underscore);
+ en = exstack->next;
+ free(exstack);
+ exstack = en;
+}