openenvutils/commandshell/shell/src/exec.c
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
--- /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;
+}