about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Builtins/rlimits.c17
-rw-r--r--Src/Makefile.in8
-rw-r--r--Src/Modules/zftp.c11
-rw-r--r--Src/Zle/zle_misc.c5
-rw-r--r--Src/Zle/zle_tricky.c91
-rw-r--r--Src/builtin.c63
-rw-r--r--Src/cond.c91
-rw-r--r--Src/exec.c396
-rw-r--r--Src/glob.c24
-rw-r--r--Src/hashtable.c4
-rw-r--r--Src/hist.c110
-rw-r--r--Src/init.c31
-rw-r--r--Src/jobs.c35
-rw-r--r--Src/lex.c76
-rw-r--r--Src/linklist.c4
-rw-r--r--Src/loop.c64
-rw-r--r--Src/math.c14
-rw-r--r--Src/mem.c55
-rw-r--r--Src/params.c21
-rw-r--r--Src/parse.c56
-rw-r--r--Src/signals.c2
-rw-r--r--Src/subst.c4
-rw-r--r--Src/text.c36
-rw-r--r--Src/utils.c641
-rw-r--r--Src/zsh.export3
-rw-r--r--Src/zsh.h14
-rw-r--r--Src/zsh.mdd11
27 files changed, 966 insertions, 921 deletions
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index f8167a106..505b024e4 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -222,8 +222,21 @@ printulimit(int lim, int hard, int head)
     /* display the limit */
     if (limit == RLIM_INFINITY)
 	printf("unlimited\n");
-    else
-	printf("%ld\n", (long)limit);
+    else {
+# ifdef RLIM_T_IS_QUAD_T
+	printf("%qd\n", limit);
+# else
+#  ifdef RLIM_T_IS_LONG_LONG
+	printf("%lld\n", limit);
+#  else
+#   ifdef RLIM_T_IS_UNSIGNED
+	printf("%lu\n", limit);
+#   else
+	printf("%ld\n", limit);
+#   endif /* RLIM_T_IS_UNSIGNED */
+#  endif /* RLIM_T_IS_LONG_LONG */
+# endif /* RLIM_T_IS_QUAD_T */
+    }
 }
 
 /* limit: set or show resource limits.  The variable hard indicates *
diff --git a/Src/Makefile.in b/Src/Makefile.in
index dd96aaf90..e98a696e4 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -205,8 +205,12 @@ clean: clean-modules
 distclean: distclean-modules
 realclean: realclean-modules
 
-mostlyclean-modules clean-modules distclean-modules realclean-modules: Makemod
-	@$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`
+# Don't remake Makemod just to delete things, even if it doesn't exist.
+mostlyclean-modules clean-modules distclean-modules realclean-modules:
+	if test -f Makemod; then \
+	  @$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \
+	fi; \
+	exit 0
 
 @CLEAN_MK@
 
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 873617e95..63bca854c 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -230,7 +230,7 @@ static char *lastmsg, lastcodestr[4];
 static int lastcode;
 
 /* flag for remote system is UNIX --- useful to know as things are simpler */
-static int zfis_unix, zfpassive_conn;
+static int zfpassive_conn;
 
 /* remote system has size, mdtm commands */
 enum {
@@ -1786,7 +1786,6 @@ zftp_open(char *name, char **args, int flags)
 	return 1;
     }
 
-    zfis_unix = 0;
     zfhas_size = zfhas_mdtm = ZFCP_UNKN;
     zdfd = -1;
     /* initial status: open, ASCII data, stream mode 'n' stuff */
@@ -2039,14 +2038,12 @@ zftp_login(char *name, char **args, int flags)
 		/*
 		 * Use binary for transfers.  This simple test saves much
 		 * hassle for all concerned, particularly me.
+		 *
+		 * We could set this based just on the UNIX part,
+		 * but I don't really know the consequences of that.
 		 */
 		zfstatus |= ZFST_IMAG;
-		zfis_unix = 1;
 	    }
-	    /*
-	     * we could set zfis_unix based just on the UNIX part,
-	     * but I don't really know the consequences of that.
-	     */
 	    zfsetparam("ZFTP_SYSTEM", systype, ZFPM_READONLY);
 	}
 	zfstatus |= ZFST_SYST;
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index c12c5894b..e2ccfbc46 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -788,9 +788,8 @@ static char *suffixfunc;
 void
 makesuffix(int n)
 {
-    suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] =
-	suffixlen[';'] = suffixlen['|'] = suffixlen['&'] = 
-	suffixlen['<'] = suffixlen['>'] = n;
+    suffixlen[256] = suffixlen[' '] = suffixlen['\t'] = suffixlen['\n'] = 
+	suffixlen[';'] = suffixlen['&'] = suffixlen['|'] = n;
 }
 
 /* Set up suffix for parameter names: the last n characters are a suffix *
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 281fe211d..8c2ae7bb6 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -681,12 +681,11 @@ cmphaswilds(char *str)
 /* Check if we have to complete a parameter name. */
 
 static char *
-check_param(char *s, int set, char **ep)
+check_param(char *s, int set, int test)
 {
     char *p;
-    int bq = 0, eq = 0, i;
 
-    if (!ep)
+    if (!test)
 	ispar = parq = eparq = 0;
     /* Try to find a `$'. */
     for (p = s + offs; p > s && *p != String && *p != Qstring; p--);
@@ -726,9 +725,9 @@ check_param(char *s, int set, char **ep)
 
 	e = b;
 	if (br) {
-	    while (*e == (ep ? Dnull : '"'))
-		e++, parq++, bq++;
-	    if (!ep)
+	    while (*e == (test ? Dnull : '"'))
+		e++, parq++;
+	    if (!test)
 		b = e;
 	}
 	/* Find the end of the name. */
@@ -749,14 +748,12 @@ check_param(char *s, int set, char **ep)
 	if (offs <= e - s && offs >= b - s && n <= 0) {
 	    if (br) {
 		p = e;
-		while (*p == (ep ? Dnull : '"'))
-		    p++, parq--, eparq++, eq++;
+		while (*p == (test ? Dnull : '"'))
+		    p++, parq--, eparq++;
 	    }
 	    /* It is. */
-	    if (ep) {
-		*ep = e;
+	    if (test)
 		return b;
-	    }
 	    /* If we were called from makecomplistflags(), we have to set the
 	     * global variables. */
 
@@ -765,21 +762,12 @@ check_param(char *s, int set, char **ep)
 		    mflags |= CMF_PARBR;
 
 		/* Get the prefix (anything up to the character before the name). */
-		for (i = eq, p = e; i; i--, p++)
-		    *p = '.';
-		isuf = quotename(e, NULL);
-		for (i = eq, p = isuf; i; i--, p++)
-		    *p = '"';
+		isuf = dupstring(e);
+		untokenize(isuf);
 		*e = '\0';
 		ripre = dupstring(s);
 		ripre[b - s] = '\0';
-		for (i = bq, p = ripre + (b - s) - 1; i; i--, p--)
-		    *p = '.';
-		ipre = quotename(ripre, NULL);
-		for (i = bq, p = ripre + strlen(ripre) - 1; i; i--, p--)
-		    *p = '"';
-		for (i = bq, p = ipre + strlen(ipre) - 1; i; i--, p--)
-		    *p = '"';
+		ipre = dupstring(ripre);
 		untokenize(ipre);
 	    }
 	    else
@@ -1231,8 +1219,7 @@ get_comp_string(void)
 	clwpos = -1;
 	lexsave();
 	inpush(dupstrspace((char *) linptr), 0, NULL);
-	strinbeg();
-	stophist = 2;
+	strinbeg(0);
 	i = tt0 = cp = rd = ins = oins = linarr = parct = ia = 0;
 
 	/* This loop is possibly the wrong way to do this.  It goes through *
@@ -1535,11 +1522,12 @@ get_comp_string(void)
 	/* This variable will hold the current word in quoted form. */
 	qword = ztrdup(s);
 	offs = cs - wb;
-	if ((p = check_param(s, 0, &tt))) {
-	    for (; *p == Dnull; p++)
-		*p = '"';
-	    for (; *tt == Dnull; tt++)
-		*tt = '"';
+	if ((p = check_param(s, 0, 1))) {
+	    for (p = s; *p; p++)
+		if (*p == Dnull)
+		    *p = '"';
+		else if (*p == Snull)
+		    *p = '\'';
 	}
 	if (*s == Snull || *s == Dnull) {
 	    char *q = (*s == Snull ? "'" : "\""), *n = tricat(qipre, q, "");
@@ -3427,7 +3415,7 @@ add_match_data(int alt, char *str, Cline line,
     Aminfo ai = (alt ? fainfo : ainfo);
     int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
 
-    palen = salen = qipl = ipl = pl = ppl = isl = psl = 0;
+    palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
 
     DPUTS(!line, "BUG: add_match_data() without cline");
 
@@ -4369,10 +4357,10 @@ docompletion(char *s, int lst, int incmd)
 	    invalidatelist();
 
 	/* Print the explanation strings if needed. */
-	if (!showinglist && validlist && nmatches != 1) {
+	if (!showinglist && validlist && usemenu != 2 && nmatches != 1) {
 	    Cmgroup g = amatches;
 	    Cexpl *e;
-	    int up = 0, tr = 1;
+	    int up = 0, tr = 1, nn = 0;
 
 	    if (!nmatches)
 		feep();
@@ -4385,7 +4373,12 @@ docompletion(char *s, int lst, int incmd)
 				trashzle();
 				tr = 0;
 			    }
+			    if (nn) {
+				up++;
+				putc('\n', shout);
+			    }
 			    up += printfmt((*e)->str, (*e)->count, 1);
+			    nn = 1;
 			}
 			e++;
 		    }
@@ -4915,6 +4908,9 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     int ois = instring, oib = inbackt;
     char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
 
+    swb = swe = soffs = 0;
+    ns = NULL;
+
     /* Put the string in the lexer buffer and call the lexer to *
      * get the words we have to expand.                        */
     zleparse = 1;
@@ -4930,8 +4926,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
-    strinbeg();
-    stophist = 2;
+    strinbeg(0);
     noaliases = 1;
     do {
 	ctxtlex();
@@ -4979,11 +4974,21 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     ll = oll;
     if (cur < 0 || i < 1)
 	return 1;
+    owb = offs;
+    offs = soffs;
+    if ((p = check_param(ns, 0, 1))) {
+	for (p = ns; *p; p++)
+	    if (*p == Dnull)
+		*p = '"';
+	    else if (*p == Snull)
+		*p = '\'';
+    }
+    offs = owb;
     if (*ns == Snull || *ns == Dnull) {
 	instring = (*ns == Snull ? 1 : 2);
 	inbackt = 0;
 	swb++;
-	if (ns[strlen(ns) - 1] == *ns)
+	if (ns[strlen(ns) - 1] == *ns && ns[1])
 	    swe--;
 	autoq = (*ns == Snull ? '\'' : '"');
     } else {
@@ -5028,7 +5033,8 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
 	int olws = clwsize, olwn = clwnum, olwp = clwpos;
 	int obr = brange, oer = erange, oof = offs;
-	
+	unsigned long occ = ccont;
+
 	clwsize = clwnum = countlinknodes(foo);
 	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
 	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
@@ -5043,7 +5049,9 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	qipre = qp;
 	qisuf = qs;
 	offs = soffs;
+	ccont = CC_CCCONT;
 	makecomplistcmd(ns, !clwpos, CFN_FIRST);
+	ccont = occ;
 	offs = oof;
 	zsfree(cmdstr);
 	cmdstr = os;
@@ -6341,7 +6349,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
 	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
 	inpush(tmpbuf, 0, NULL);
-	strinbeg();
+	strinbeg(0);
 	noaliases = 1;
 	do {
 	    ctxtlex();
@@ -6502,7 +6510,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	int oldn = clwnum, oldp = clwpos;
 	unsigned long occ = ccont;
 	
-	ccont = 0;
+	ccont = CC_CCCONT;
 	
 	/* So we restrict the words-array. */
 	if (brange >= clwnum)
@@ -8099,14 +8107,13 @@ doexpandhist(void)
 	lexsave();
 	/* We push ol as it will remain unchanged */
 	inpush((char *) ol, 0, NULL);
-	strinbeg();
+	strinbeg(1);
 	noaliases = 1;
 	noerrs = 1;
 	exlast = inbufct;
 	do {
 	    ctxtlex();
 	} while (tok != ENDINPUT && tok != LEXERR);
-	stophist = 2;
 	while (!lexstop)
 	    hgetc();
 	/* We have to save errflags because it's reset in lexrestore. Since  *
@@ -8178,7 +8185,7 @@ getcurcmd(void)
 	metafy_line();
 	inpush(dupstrspace((char *) line), 0, NULL);
 	unmetafy_line();
-	strinbeg();
+	strinbeg(1);
 	pushheap();
 	do {
 	    curlincmd = incmdpos;
diff --git a/Src/builtin.c b/Src/builtin.c
index 4c9b159dd..d74d9cd88 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -43,7 +43,7 @@ static struct builtin builtins[] =
     BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
     BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("alias", BINF_MAGICEQUALS, bin_alias, 0, -1, 0, "Lgmr", NULL),
-    BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "t", "u"),
+    BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "tU", "u"),
     BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
     BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
     BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
@@ -64,7 +64,7 @@ static struct builtin builtins[] =
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
-    BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtu", NULL),
+    BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
     BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "dfmrv", NULL),
@@ -209,7 +209,7 @@ execbuiltin(LinkList args, Builtin bn)
     LinkNode n;
     char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
     char *oxarg, *xarg = NULL;
-    int flags, sense, argc = 0, execop;
+    int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
 
     /* initialise some static variables */
     auxdata = NULL;
@@ -250,12 +250,21 @@ execbuiltin(LinkList args, Builtin bn)
 		    break;
 	    }
 	    /* save the options in xarg, for execution tracing */
-	    if (xarg) {
-		oxarg = tricat(xarg, " ", arg);
-		zsfree(xarg);
-		xarg = oxarg;
-	    } else
-		xarg = ztrdup(arg);
+	    if (xtr) {
+		if (xarg) {
+		    int l = strlen(arg) + lxarg + 1;
+
+		    oxarg = zhalloc(l + 1);
+		    strcpy(oxarg, xarg);
+		    oxarg[lxarg] = ' ';
+		    strcpy(oxarg + lxarg + 1, arg);
+		    xarg = oxarg;
+		    lxarg = l + 1;
+		} else {
+		    xarg = dupstring(arg);
+		    lxarg = strlen(xarg);
+		}
+	    }
 	    /* handle -- or - (ops['-']), and + (ops['-'] and ops['+']) */
 	    if (arg[1] == '-')
 		arg++;
@@ -283,7 +292,6 @@ execbuiltin(LinkList args, Builtin bn)
 		if(*arg == Meta)
 		    *++arg ^= 32;
 		zerr("bad option: -%c", NULL, *arg);
-		zsfree(xarg);
 		return 1;
 	    }
 	    arg = (char *) ugetnode(args);
@@ -330,7 +338,6 @@ execbuiltin(LinkList args, Builtin bn)
 	while ((*argv++ = (char *)ugetnode(args)));
     argv = oargv;
     if (errflag) {
-	zsfree(xarg);
 	errflag = 0;
 	return 1;
     }
@@ -339,12 +346,11 @@ execbuiltin(LinkList args, Builtin bn)
     if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
 	zwarnnam(name, (argc < bn->minargs)
 		? "not enough arguments" : "too many arguments", NULL, 0);
-	zsfree(xarg);
 	return 1;
     }
 
     /* display execution trace information, if required */
-    if (isset(XTRACE)) {
+    if (xtr) {
 	fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name);
 	if (xarg)
 	    fprintf(stderr, " %s", xarg);
@@ -353,7 +359,6 @@ execbuiltin(LinkList args, Builtin bn)
 	fputc('\n', stderr);
 	fflush(stderr);
     }
-    zsfree(xarg);
     /* call the handler function, and return its return value */
     return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
 }
@@ -1824,17 +1829,18 @@ bin_functions(char *name, char **argv, char *ops, int func)
     int on = 0, off = 0;
 
     /* Do we have any flags defined? */
-    if (ops['u'] || ops['t']) {
-	if (ops['u'] == 1)
-	    on |= PM_UNDEFINED;
-	else if (ops['u'] == 2)
-	    off |= PM_UNDEFINED;
-
-	if (ops['t'] == 1)
-	    on |= PM_TAGGED;
-	else if (ops['t'] == 2)
-	    off |= PM_TAGGED;
-    }
+    if (ops['u'] == 1)
+	on |= PM_UNDEFINED;
+    else if (ops['u'] == 2)
+	off |= PM_UNDEFINED;
+    if (ops['U'] == 1)
+	on |= PM_UNALIASED|PM_UNDEFINED;
+    else if (ops['U'] == 2)
+	off |= PM_UNALIASED;
+    if (ops['t'] == 1)
+	on |= PM_TAGGED;
+    else if (ops['t'] == 2)
+	off |= PM_TAGGED;
 
     if (off & PM_UNDEFINED) {
 	zwarnnam(name, "invalid option(s)", NULL, 0);
@@ -1845,6 +1851,8 @@ bin_functions(char *name, char **argv, char *ops, int func)
      * are given, we will print only functions containing these  *
      * flags, else we'll print them all.                         */
     if (!*argv) {
+	if (ops['U'] && !ops['u'])
+	    on &= ~PM_UNDEFINED;
 	scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, 0);
 	return 0;
     }
@@ -3078,8 +3086,7 @@ bin_eval(char *nam, char **argv, char *ops, int func)
     List list;
 
     inpush(zjoin(argv, ' '), 0, NULL);
-    strinbeg();
-    stophist = 2;
+    strinbeg(0);
     list = parse_list();
     strinend();
     inpop();
@@ -3584,7 +3591,7 @@ bin_trap(char *name, char **argv, char *ops, int func)
 		if (!sigfuncs[sig])
 		    printf("trap -- '' %s\n", sigs[sig]);
 		else {
-		    s = getpermtext((void *) dupstruct((void *) sigfuncs[sig]));
+		    s = getpermtext((void *) sigfuncs[sig]);
 		    printf("trap -- ");
 		    quotedzputs(s, stdout);
 		    printf(" %s\n", sigs[sig]);
diff --git a/Src/cond.c b/Src/cond.c
index a8387a454..98deea2bf 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -42,6 +42,7 @@ int
 evalcond(Cond c)
 {
     struct stat *st;
+    char *left, *right = NULL;
 
     switch (c->type) {
     case COND_NOT:
@@ -103,107 +104,109 @@ evalcond(Cond c)
 	    return 0;
 	}
     }
-    singsub((char **)&c->left);
-    untokenize(c->left);
+    left = dupstring((char *) c->left);
+    singsub(&left);
+    untokenize(left);
     if (c->right) {
-	singsub((char **)&c->right);
+	right = dupstring((char *) c->right);
+	singsub(&right);
 	if (c->type != COND_STREQ && c->type != COND_STRNEQ)
-	    untokenize(c->right);
+	    untokenize(right);
     }
 
     if (tracingcond) {
 	if (c->type < COND_MOD) {
-	    char *rt = (char *)c->right;
+	    char *rt = (char *)right;
 	    if (c->type == COND_STREQ || c->type == COND_STRNEQ) {
 		rt = dupstring(rt);
 		untokenize(rt);
 	    }
-	    fprintf(stderr, " %s %s %s", (char *)c->left, condstr[c->type],
+	    fprintf(stderr, " %s %s %s", (char *)left, condstr[c->type],
 		    rt);
 	} else
-	    fprintf(stderr, " -%c %s", c->type, (char *)c->left);
+	    fprintf(stderr, " -%c %s", c->type, (char *)left);
     }
 
     switch (c->type) {
     case COND_STREQ:
-	return matchpat(c->left, c->right);
+	return matchpat(left, right);
     case COND_STRNEQ:
-	return !matchpat(c->left, c->right);
+	return !matchpat(left, right);
     case COND_STRLT:
-	return strcmp(c->left, c->right) < 0;
+	return strcmp(left, right) < 0;
     case COND_STRGTR:
-	return strcmp(c->left, c->right) > 0;
+	return strcmp(left, right) > 0;
     case 'e':
     case 'a':
-	return (doaccess(c->left, F_OK));
+	return (doaccess(left, F_OK));
     case 'b':
-	return (S_ISBLK(dostat(c->left)));
+	return (S_ISBLK(dostat(left)));
     case 'c':
-	return (S_ISCHR(dostat(c->left)));
+	return (S_ISCHR(dostat(left)));
     case 'd':
-	return (S_ISDIR(dostat(c->left)));
+	return (S_ISDIR(dostat(left)));
     case 'f':
-	return (S_ISREG(dostat(c->left)));
+	return (S_ISREG(dostat(left)));
     case 'g':
-	return (!!(dostat(c->left) & S_ISGID));
+	return (!!(dostat(left) & S_ISGID));
     case 'k':
-	return (!!(dostat(c->left) & S_ISVTX));
+	return (!!(dostat(left) & S_ISVTX));
     case 'n':
-	return (!!strlen(c->left));
+	return (!!strlen(left));
     case 'o':
-	return (optison(c->left));
+	return (optison(left));
     case 'p':
-	return (S_ISFIFO(dostat(c->left)));
+	return (S_ISFIFO(dostat(left)));
     case 'r':
-	return (doaccess(c->left, R_OK));
+	return (doaccess(left, R_OK));
     case 's':
-	return ((st = getstat(c->left)) && !!(st->st_size));
+	return ((st = getstat(left)) && !!(st->st_size));
     case 'S':
-	return (S_ISSOCK(dostat(c->left)));
+	return (S_ISSOCK(dostat(left)));
     case 'u':
-	return (!!(dostat(c->left) & S_ISUID));
+	return (!!(dostat(left) & S_ISUID));
     case 'w':
-	return (doaccess(c->left, W_OK));
+	return (doaccess(left, W_OK));
     case 'x':
 	if (privasserted()) {
-	    mode_t mode = dostat(c->left);
+	    mode_t mode = dostat(left);
 	    return (mode & S_IXUGO) || S_ISDIR(mode);
 	}
-	return doaccess(c->left, X_OK);
+	return doaccess(left, X_OK);
     case 'z':
-	return (!strlen(c->left));
+	return (!strlen(left));
     case 'h':
     case 'L':
-	return (S_ISLNK(dolstat(c->left)));
+	return (S_ISLNK(dolstat(left)));
     case 'O':
-	return ((st = getstat(c->left)) && st->st_uid == geteuid());
+	return ((st = getstat(left)) && st->st_uid == geteuid());
     case 'G':
-	return ((st = getstat(c->left)) && st->st_gid == getegid());
+	return ((st = getstat(left)) && st->st_gid == getegid());
     case 'N':
-	return ((st = getstat(c->left)) && st->st_atime <= st->st_mtime);
+	return ((st = getstat(left)) && st->st_atime <= st->st_mtime);
     case 't':
-	return isatty(matheval(c->left));
+	return isatty(matheval(left));
     case COND_EQ:
-	return matheval(c->left) == matheval(c->right);
+	return matheval(left) == matheval(right);
     case COND_NE:
-	return matheval(c->left) != matheval(c->right);
+	return matheval(left) != matheval(right);
     case COND_LT:
-	return matheval(c->left) < matheval(c->right);
+	return matheval(left) < matheval(right);
     case COND_GT:
-	return matheval(c->left) > matheval(c->right);
+	return matheval(left) > matheval(right);
     case COND_LE:
-	return matheval(c->left) <= matheval(c->right);
+	return matheval(left) <= matheval(right);
     case COND_GE:
-	return matheval(c->left) >= matheval(c->right);
+	return matheval(left) >= matheval(right);
     case COND_NT:
     case COND_OT:
 	{
 	    time_t a;
 
-	    if (!(st = getstat(c->left)))
+	    if (!(st = getstat(left)))
 		return 0;
 	    a = st->st_mtime;
-	    if (!(st = getstat(c->right)))
+	    if (!(st = getstat(right)))
 		return 0;
 	    return (c->type == COND_NT) ? a > st->st_mtime : a < st->st_mtime;
 	}
@@ -212,11 +215,11 @@ evalcond(Cond c)
 	    dev_t d;
 	    ino_t i;
 
-	    if (!(st = getstat(c->left)))
+	    if (!(st = getstat(left)))
 		return 0;
 	    d = st->st_dev;
 	    i = st->st_ino;
-	    if (!(st = getstat(c->right)))
+	    if (!(st = getstat(right)))
 		return 0;
 	    return d == st->st_dev && i == st->st_ino;
 	}
diff --git a/Src/exec.c b/Src/exec.c
index 952dfbb78..5cfe3e3ef 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -139,8 +139,7 @@ parse_string(char *s)
 
     lexsave();
     inpush(s, 0, NULL);
-    strinbeg();
-    stophist = 2;
+    strinbeg(0);
     l = parse_list();
     strinend();
     inpop();
@@ -298,12 +297,12 @@ static char list_pipe_text[JOBTEXTSIZE];
 
 /**/
 static int
-execcursh(Cmd cmd)
+execcursh(Cmd cmd, LinkList args, int flags)
 {
     if (!list_pipe)
 	deletejob(jobtab + thisjob);
-    execlist(cmd->u.list, 1, cmd->flags & CFLAG_EXEC);
-    cmd->u.list = NULL;
+    execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
+
     return lastval;
 }
 
@@ -718,7 +717,6 @@ execlist(List list, int dont_change_job, int exiting)
 	/* Reset donetrap:  this ensures that a trap is only *
 	 * called once for each sublist that fails.          */
 	donetrap = 0;
-	simplifyright(list);
 	slist = list->left;
 
 	/* Loop through code followed by &&, ||, or end of sublist. */
@@ -1009,11 +1007,13 @@ execpline2(Pline pline, int how, int input, int output, int last1)
     lineno = pline->left->lineno;
 
     if (pline_level == 1)
-	strcpy(list_pipe_text, getjobtext((void *) pline->left));
-    if (pline->type == END) {
+	if (!sfcontext)
+	    strcpy(list_pipe_text, getjobtext((void *) pline->left));
+	else
+	    list_pipe_text[0] = '\0';
+    if (pline->type == END)
 	execcmd(pline->left, input, output, how, last1 ? 1 : 2);
-	pline->left = NULL;
-    } else {
+    else {
 	int old_list_pipe = list_pipe;
 
 	mpipe(pipes);
@@ -1046,11 +1046,10 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 		_exit(lastval);
 	    }
 	} else {
-	/* otherwise just do the pipeline normally. */
+	    /* otherwise just do the pipeline normally. */
 	    subsh_close = pipes[0];
 	    execcmd(pline->left, input, pipes[1], how, 0);
 	}
-	pline->left = NULL;
 	zclose(pipes[1]);
 	if (pline->right) {
 	    /* if another execpline() is invoked because the command is *
@@ -1102,13 +1101,17 @@ makecline(LinkList list)
 void
 untokenize(char *s)
 {
-    for (; *s; s++)
-	if (itok(*s)) {
-	    if (*s == Nularg)
-		chuck(s--);
-	    else
-		*s = ztokens[*s - Pound];
-	}
+    if (*s) {
+	char *p = s, c;
+
+	while ((c = *s++))
+	    if (itok(c)) {
+		if (c != Nularg)
+		    *p++ = ztokens[c - Pound];
+	    } else
+		*p++ = c;
+	*p = '\0';
+    }
 }
 
 /* Open a file for writing redicection */
@@ -1152,34 +1155,34 @@ clobber_open(struct redir *f)
 static void
 closemn(struct multio **mfds, int fd)
 {
-    struct multio *mn = mfds[fd];
-    char buf[TCBUFSIZE];
-    int len, i;
+    if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) {
+	struct multio *mn = mfds[fd];
+	char buf[TCBUFSIZE];
+	int len, i;
 
-    if (fd < 0 || !mfds[fd] || mfds[fd]->ct < 2)
-	return;
-    if (zfork()) {
-	for (i = 0; i < mn->ct; i++)
-	    zclose(mn->fds[i]);
-	zclose(mn->pipe);
-	mn->ct = 1;
-	mn->fds[0] = fd;
-	return;
-    }
-    /* pid == 0 */
-    closeallelse(mn);
-    if (mn->rflag) {
-	/* tee process */
-	while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0)
+	if (zfork()) {
 	    for (i = 0; i < mn->ct; i++)
-		write(mn->fds[i], buf, len);
-    } else {
-	/* cat process */
-	for (i = 0; i < mn->ct; i++)
-	    while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0)
-		write(mn->pipe, buf, len);
+		zclose(mn->fds[i]);
+	    zclose(mn->pipe);
+	    mn->ct = 1;
+	    mn->fds[0] = fd;
+	    return;
+	}
+	/* pid == 0 */
+	closeallelse(mn);
+	if (mn->rflag) {
+	    /* tee process */
+	    while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0)
+		for (i = 0; i < mn->ct; i++)
+		    write(mn->fds[i], buf, len);
+	} else {
+	    /* cat process */
+	    for (i = 0; i < mn->ct; i++)
+		while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0)
+		    write(mn->pipe, buf, len);
+	}
+	_exit(0);
     }
-    _exit(0);
 }
 
 /* close all the mnodes (failure) */
@@ -1273,9 +1276,10 @@ static void
 addvars(LinkList l, int export)
 {
     Varasg v;
+    LinkNode n;
     LinkList vl;
     int xtr;
-    char **arr, **ptr;
+    char **arr, **ptr, *name;
 
     xtr = isset(XTRACE);
     if (xtr && nonempty(l)) {
@@ -1283,26 +1287,30 @@ addvars(LinkList l, int export)
 	doneps4 = 1;
     }
 
-    while (nonempty(l)) {
-	v = (Varasg) ugetnode(l);
-	singsub(&v->name);
+    for (n = firstnode(l); n; incnode(n)) {
+	v = (Varasg) getdata(n);
+	name = dupstring(v->name);
+	singsub(&name);
 	if (errflag)
 	    return;
-	untokenize(v->name);
+	untokenize(name);
 	if (xtr)
-	    fprintf(stderr, "%s=", v->name);
+	    fprintf(stderr, "%s=", name);
 	if (v->type == PM_SCALAR) {
 	    vl = newlinklist();
-	    addlinknode(vl, v->str);
+	    addlinknode(vl, dupstring(v->str));
 	} else
-	    vl = v->arr;
-	prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) : PF_ASSIGN);
-	if (errflag)
-	    return;
-	if (isset(GLOBASSIGN) || v->type != PM_SCALAR)
-	    globlist(vl);
-	if (errflag)
-	    return;
+	    vl = listdup(v->arr);
+	if (vl) {
+	    prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) :
+		                               PF_ASSIGN);
+	    if (errflag)
+		return;
+	    if (isset(GLOBASSIGN) || v->type != PM_SCALAR)
+		globlist(vl);
+	    if (errflag)
+		return;
+	}
 	if (v->type == PM_SCALAR && (empty(vl) || !nextnode(firstnode(vl)))) {
 	    Param pm;
 	    char *val;
@@ -1319,7 +1327,7 @@ addvars(LinkList l, int export)
 	    if (export) {
 		if (export < 0) {
 		    /* We are going to fork so do not bother freeing this */
-		    pm = (Param) paramtab->removenode(paramtab, v->name);
+		    pm = (Param) paramtab->removenode(paramtab, name);
 		    if (isset(RESTRICTED) && (pm->flags & PM_RESTRICTED)) {
 			zerr("%s: restricted", pm->nam, 0);
 			zsfree(val);
@@ -1328,18 +1336,22 @@ addvars(LinkList l, int export)
 		}
 		allexp = opts[ALLEXPORT];
 		opts[ALLEXPORT] = 1;
-		pm = setsparam(v->name, val);
+		pm = setsparam(name, val);
 		opts[ALLEXPORT] = allexp;
 	    } else
-		pm = setsparam(v->name, val);
+		pm = setsparam(name, val);
 	    if (errflag)
 		return;
 	    continue;
 	}
-	ptr = arr = (char **) zalloc(sizeof(char **) * (countlinknodes(vl) + 1));
+	if (vl) {
+	    ptr = arr = (char **) zalloc(sizeof(char **) *
+					 (countlinknodes(vl) + 1));
 
-	while (nonempty(vl))
-	    *ptr++ = ztrdup((char *) ugetnode(vl));
+	    while (nonempty(vl))
+		*ptr++ = ztrdup((char *) ugetnode(vl));
+	} else
+	    ptr = arr = (char **) zalloc(sizeof(char **));
 
 	*ptr = NULL;
 	if (xtr) {
@@ -1348,7 +1360,7 @@ addvars(LinkList l, int export)
 		fprintf(stderr, "%s ", *ptr);
 	    fprintf(stderr, ") ");
 	}
-	setaparam(v->name, arr);
+	setaparam(name, arr);
 	if (errflag)
 	    return;
     }
@@ -1364,15 +1376,19 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     struct multio *mfds[10];
     char *text;
     int save[10];
-    int fil, dfil, is_cursh, type, i;
+    int fil, dfil, is_cursh, type, flags, i;
     int nullexec = 0, assign = 0, forked = 0;
     int is_shfunc = 0, is_builtin = 0, is_exec = 0;
     /* Various flags to the command. */
     int cflags = 0, checked = 0;
+    LinkList vars, redir;
 
     doneps4 = 0;
-    args = cmd->args;
+    args = listdup(cmd->args);
     type = cmd->type;
+    flags = cmd->flags;
+    redir = dupheaplist(cmd->redir);
+    vars = cmd->vars;
 
     for (i = 0; i < 10; i++) {
 	save[i] = -2;
@@ -1381,7 +1397,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 
     /* If the command begins with `%', then assume it is a *
      * reference to a job in the job table.                */
-    if (type == SIMPLE && nonempty(args) &&
+    if (type == SIMPLE && args && nonempty(args) &&
 	*(char *)peekfirst(args) == '%') {
 	pushnode(args, dupstring((how & Z_DISOWN)
 				 ? "disown" : (how & Z_ASYNC) ? "bg" : "fg"));
@@ -1393,7 +1409,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * job currently in the job table.  If it does, then we treat it *
      * as a command to resume this job.                              */
     if (isset(AUTORESUME) && type == SIMPLE && (how & Z_SYNC) &&
-	nonempty(args) && empty(cmd->redir) && !input &&
+	args && nonempty(args) &&
+	(!cmd->redir || empty(cmd->redir)) && !input &&
 	!nextnode(firstnode(args))) {
 	if (unset(NOTIFY))
 	    scanjobs();
@@ -1407,7 +1424,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * only works in simple cases.  has_token() is called to make sure   *
      * this really is a simple case.                                     */
     if (type == SIMPLE) {
-	while (nonempty(args)) {
+	while (args && nonempty(args)) {
 	    char *cmdarg = (char *) peekfirst(args);
 	    checked = !has_token(cmdarg);
 	    if (!checked)
@@ -1444,7 +1461,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     }
 
     /* Do prefork substitutions */
-    prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0);
+    if (args)
+	prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0);
 
     if (type == SIMPLE) {
 	int unglobbed = 0;
@@ -1453,7 +1471,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	    char *cmdarg;
 
 	    if (!(cflags & BINF_NOGLOB))
-		while (!checked && !errflag && nonempty(args) &&
+		while (!checked && !errflag && args && nonempty(args) &&
 		       has_token((char *) peekfirst(args)))
 		    glob(args, firstnode(args));
 	    else if (!unglobbed) {
@@ -1465,12 +1483,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	    /* Current shell should not fork unless the *
 	     * exec occurs at the end of a pipeline.    */
 	    if ((cflags & BINF_EXEC) && last1 == 2)
-		cmd->flags |= CFLAG_EXEC;
+		flags |= CFLAG_EXEC;
 
 	    /* Empty command */
-	    if (empty(args)) {
-		if (nonempty(cmd->redir)) {
-		    if (cmd->flags & CFLAG_EXEC) {
+	    if (!args || empty(args)) {
+		if (redir && nonempty(redir)) {
+		    if (flags & CFLAG_EXEC) {
 			/* Was this "exec < foobar"? */
 			nullexec = 1;
 			break;
@@ -1480,17 +1498,23 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 			errflag = lastval = 1;
 			return;
 		    } else if (readnullcmd && *readnullcmd &&
-			       ((Redir) peekfirst(cmd->redir))->type == READ &&
-			       !nextnode(firstnode(cmd->redir))) {
+			       ((Redir) peekfirst(redir))->type == READ &&
+			       !nextnode(firstnode(redir))) {
+			if (!args)
+			    args = newlinklist();
 			addlinknode(args, dupstring(readnullcmd));
-		    } else
+		    } else {
+			if (!args)
+			    args = newlinklist();
 			addlinknode(args, dupstring(nullcmd));
+		    }
 		} else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) {
 		    lastval = 0;
 		    return;
 		} else {
 		    cmdoutval = 0;
-		    addvars(cmd->vars, 0);
+		    if (vars)
+			addvars(vars, 0);
 		    if (errflag)
 			lastval = errflag;
 		    else
@@ -1502,7 +1526,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    return;
 		}
 	    } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) &&
-		       (cmd->flags & CFLAG_EXEC)) {
+		       (flags & CFLAG_EXEC)) {
 		zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0);
 		lastval = 1;
 		return;
@@ -1548,22 +1572,32 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     }
 
     /* Get the text associated with this command. */
-    if (jobbing || (how & Z_TIMED))
+    if (!sfcontext && (jobbing || (how & Z_TIMED)))
 	text = getjobtext((void *) cmd);
     else
 	text = NULL;
 
     /* Set up special parameter $_ */
-    zsfree(underscore);
-    if (nonempty(args)
-	&& (underscore = ztrdup((char *) getdata(lastnode(args)))))
-	untokenize(underscore); 
-    else
-  	underscore = ztrdup("");
+    if (args && nonempty(args)) {
+	char *u = (char *) getdata(lastnode(args));
+
+	if (u) {
+	    int ul = strlen(u);
+
+	    if (ul >= underscorelen) {
+		zfree(underscore, underscorelen);
+		underscore = (char *) zalloc(underscorelen = ul + 32);
+	    }
+	    strcpy(underscore, u);
+	} else
+	    *underscore = '\0';
+    } else
+	*underscore = '\0';
 
     /* Warn about "rm *" */
     if (type == SIMPLE && interact && unset(RMSTARSILENT)
-	&& isset(SHINSTDIN) && nonempty(args) && nextnode(firstnode(args))
+	&& isset(SHINSTDIN) && args && nonempty(args)
+	&& nextnode(firstnode(args))
 	&& !strcmp(peekfirst(args), "rm")) {
 	LinkNode node, next;
 
@@ -1596,11 +1630,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     if (type == SIMPLE && !nullexec) {
 	char *s;
 	char trycd = (isset(AUTOCD) && isset(SHINSTDIN)
-		      && empty(cmd->redir) && !empty(args)
+		      && (!redir || empty(redir)) && args && !empty(args)
 		      && !nextnode(firstnode(args))
 		      && *(char *)peekfirst(args));
 
-	DPUTS(empty(args), "BUG: empty(args) in exec.c");
+	DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c");
 	if (!hn) {
 	    /* Resolve external commands */
 	    char *cmdarg = (char *) peekfirst(args);
@@ -1652,7 +1686,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * current shell.                                                         *
      **************************************************************************/
 
-    if ((how & Z_ASYNC) || (!(cmd->flags & CFLAG_EXEC) &&
+    if ((how & Z_ASYNC) || (!(flags & CFLAG_EXEC) &&
        (((is_builtin || is_shfunc) && output) ||
        (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] ||
         sigtrapped[SIGEXIT] || havefiles()))))) {
@@ -1677,10 +1711,13 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 #endif
 	    if (how & Z_ASYNC) {
 		lastpid = (zlong) pid;
-	    } else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) {
+	    } else if (!jobtab[thisjob].stty_in_env &&
+		       vars && nonempty(vars)) {
 		/* search for STTY=... */
-		while (nonempty(cmd->vars))
-		    if (!strcmp(((Varasg) ugetnode(cmd->vars))->name, "STTY")) {
+		LinkNode n;
+
+		for (n = firstnode(vars); n; incnode(n))
+		    if (!strcmp(((Varasg) getdata(n))->name, "STTY")) {
 			jobtab[thisjob].stty_in_env = 1;
 			break;
 		    }
@@ -1713,7 +1750,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	is_exec = 1;
     }
 
-    if (!(cflags & BINF_NOGLOB))
+    if (args && !(cflags & BINF_NOGLOB))
 	globlist(args);
     if (errflag) {
 	lastval = 1;
@@ -1727,11 +1764,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	addfd(forked, save, mfds, 1, output, 1);
 
     /* Do process substitutions */
-    spawnpipes(cmd->redir);
+    if (redir)
+	spawnpipes(redir);
 
     /* Do io redirections */
-    while (nonempty(cmd->redir)) {
-	fn = (Redir) ugetnode(cmd->redir);
+    while (redir && nonempty(redir)) {
+	fn = (Redir) ugetnode(redir);
 	DPUTS(fn->type == HEREDOC || fn->type == HEREDOCDASH,
 	      "BUG: unexpanded here document");
 	if (fn->type == INPIPE) {
@@ -1749,7 +1787,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	    }
 	    addfd(forked, save, mfds, fn->fd1, fn->fd2, 1);
 	} else {
-	    if (fn->type != HERESTR && xpandredir(fn, cmd->redir))
+	    if (fn->type != HERESTR && xpandredir(fn, redir))
 		continue;
 	    if (errflag) {
 		closemnodes(mfds);
@@ -1870,15 +1908,15 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	if (is_exec)
 	    entersubsh(how, type != SUBSH ? 2 : 1, 1);
 	if (type >= CURSH) {
-	    static int (*func[]) _((Cmd)) = {
+	    static int (*func[]) _((Cmd, LinkList, int)) = {
 		execcursh, exectime, execfuncdef, execfor, execwhile,
 		execrepeat, execif, execcase, execselect, execcond,
 		execarith, execautofn
 	    };
 
 	    if (last1 == 1)
-		cmd->flags |= CFLAG_EXEC;
-	    lastval = (func[type - CURSH]) (cmd);
+		flags |= CFLAG_EXEC;
+	    lastval = (func[type - CURSH]) (cmd, args, flags);
 	} else if (is_builtin || is_shfunc) {
 	    LinkList restorelist = 0, removelist = 0;
 	    /* builtin or shell function */
@@ -1889,11 +1927,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 			     !(hn->flags & BINF_PSPECIAL))))
 		save_params(cmd, &restorelist, &removelist);
 
-	    if (cmd->vars) {
+	    if (vars) {
 		/* Export this if the command is a shell function,
 		 * but not if it's a builtin.
 		 */
-		addvars(cmd->vars, is_shfunc);
+		addvars(vars, is_shfunc);
 		if (errflag) {
 		    restore_params(restorelist, removelist);
 		    lastval = 1;
@@ -1904,6 +1942,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 
 	    if (is_shfunc) {
 		/* It's a shell function */
+
 #ifdef PATH_DEV_FD
 		int i;
 
@@ -1914,7 +1953,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		if (subsh_close >= 0)
 		    zclose(subsh_close);
 		subsh_close = -1;
-		execshfunc(cmd, (Shfunc) hn);
+		execshfunc(cmd, (Shfunc) hn, args);
 #ifdef PATH_DEV_FD
 		for (i = 10; i <= max_zsh_fd; i++)
 		    if (fdtable[i] > 1)
@@ -1943,7 +1982,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    clearerr(stdout);
 	    }
 
-	    if (cmd->flags & CFLAG_EXEC) {
+	    if (flags & CFLAG_EXEC) {
 		if (subsh)
 		    _exit(lastval);
 
@@ -1957,7 +1996,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	    restore_params(restorelist, removelist);
 
 	} else {
-	    if (cmd->flags & CFLAG_EXEC) {
+	    if (flags & CFLAG_EXEC) {
 		setiparam("SHLVL", --shlvl);
 		/* If we are exec'ing a command, and we are not *
 		 * in a subshell, then save the history file.   */
@@ -1965,8 +2004,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 	    }
 	    if (type == SIMPLE) {
-		if (cmd->vars) {
-		    addvars(cmd->vars, -1);
+		if (vars) {
+		    addvars(vars, -1);
 		    if (errflag)
 			_exit(1);
 		}
@@ -1981,7 +2020,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 #endif
 		execute((Cmdnam) hn, cflags & BINF_DASH);
 	    } else {		/* ( ... ) */
-		DPUTS(cmd->vars && nonempty(cmd->vars),
+		DPUTS(vars && nonempty(vars),
 		      "BUG: assigment before complex command");
 		list_pipe = 0;
 		if (subsh_close >= 0)
@@ -2011,7 +2050,11 @@ save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p)
     char *s;
 
     MUSTUSEHEAP("save_params()");
-
+    
+    if (!cmd->vars) {
+	*restore_p = *remove_p = NULL;
+	return;
+    }
     *restore_p = newlinklist();
     *remove_p = newlinklist();
 
@@ -2283,8 +2326,9 @@ getoutput(char *cmd, int qt)
 	return NULL;
     if (list != &dummy_list && !list->right && !list->left->flags &&
 	list->left->type == END && list->left->left->type == END &&
-	(c = list->left->left->left)->type == SIMPLE && empty(c->args) &&
-	empty(c->vars) && nonempty(c->redir) &&
+	(c = list->left->left->left)->type == SIMPLE && 
+	(!c->args || empty(c->args)) &&
+	(!c->vars || empty(c->vars)) && c->redir && nonempty(c->redir) &&
 	!nextnode(firstnode(c->redir)) &&
 	(r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 &&
 	r->type == READ) {
@@ -2613,7 +2657,7 @@ extern int tracingcond;
 
 /**/
 static int
-execcond(Cmd cmd)
+execcond(Cmd cmd, LinkList args, int flags)
 {
     int stat;
     if (isset(XTRACE)) {
@@ -2633,18 +2677,19 @@ execcond(Cmd cmd)
 
 /**/
 static int
-execarith(Cmd cmd)
+execarith(Cmd cmd, LinkList args, int flags)
 {
     char *e;
     zlong val = 0;
 
     if (isset(XTRACE))
 	fprintf(stderr, "%s((", prompt4 ? prompt4 : "");
-    while ((e = (char *) ugetnode(cmd->args))) {
-	if (isset(XTRACE))
-	    fprintf(stderr, " %s", e);
-	val = matheval(e);
-    }
+    if (args)
+	while ((e = (char *) ugetnode(args))) {
+	    if (isset(XTRACE))
+		fprintf(stderr, " %s", e);
+	    val = matheval(e);
+	}
     if (isset(XTRACE)) {
 	fprintf(stderr, " ))\n");
 	fflush(stderr);
@@ -2657,7 +2702,7 @@ execarith(Cmd cmd)
 
 /**/
 static int
-exectime(Cmd cmd)
+exectime(Cmd cmd, LinkList args, int flags)
 {
     int jb;
 
@@ -2675,30 +2720,33 @@ exectime(Cmd cmd)
 
 /**/
 static int
-execfuncdef(Cmd cmd)
+execfuncdef(Cmd cmd, LinkList args, int flags)
 {
     Shfunc shf;
     char *s;
     int signum;
 
-    PERMALLOC {
-	while ((s = (char *) ugetnode(cmd->args))) {
-	    shf = (Shfunc) zalloc(sizeof *shf);
-	    shf->funcdef = (List) dupstruct(cmd->u.list);
-	    shf->flags = 0;
-
-	    /* is this shell function a signal trap? */
-	    if (!strncmp(s, "TRAP", 4) && (signum = getsignum(s + 4)) != -1) {
-		if (settrap(signum, shf->funcdef)) {
-		    freestruct(shf->funcdef);
-		    zfree(shf, sizeof *shf);
-		    LASTALLOC_RETURN 1;
-		}
-		sigtrapped[signum] |= ZSIG_FUNC;
-	    }
-	    shfunctab->addnode(shfunctab, ztrdup(s), shf);
-	}
-    } LASTALLOC;
+    if (args) {
+	PERMALLOC {
+	    while ((s = (char *) ugetnode(args))) {
+		shf = (Shfunc) zalloc(sizeof *shf);
+		shf->funcdef = (List) dupstruct(cmd->u.list);
+		shf->flags = 0;
+
+		/* is this shell function a signal trap? */
+		if (!strncmp(s, "TRAP", 4) &&
+		    (signum = getsignum(s + 4)) != -1) {
+		    if (settrap(signum, shf->funcdef)) {
+			freestruct(shf->funcdef);
+			zfree(shf, sizeof *shf);
+			LASTALLOC_RETURN 1;
+		    }
+		    sigtrapped[signum] |= ZSIG_FUNC;
+  		}
+		shfunctab->addnode(shfunctab, ztrdup(s), shf);
+  	    }
+	} LASTALLOC;
+    }
     if(isset(HISTNOFUNCTIONS))
 	remhist();
     return 0;
@@ -2708,7 +2756,7 @@ execfuncdef(Cmd cmd)
 
 /**/
 static void
-execshfunc(Cmd cmd, Shfunc shf)
+execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 {
     LinkList last_file_list = NULL;
 
@@ -2726,16 +2774,17 @@ execshfunc(Cmd cmd, Shfunc shf)
     if (isset(XTRACE)) {
 	LinkNode lptr;
 	fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4);
-	for (lptr = firstnode(cmd->args); lptr; incnode(lptr)) {
-	    if (lptr != firstnode(cmd->args))
-		fputc(' ', stderr);
-	    fprintf(stderr, "%s", (char *)getdata(lptr));
-	}
+	if (args)
+	    for (lptr = firstnode(args); lptr; incnode(lptr)) {
+		if (lptr != firstnode(args))
+		    fputc(' ', stderr);
+		fprintf(stderr, "%s", (char *)getdata(lptr));
+	    }
 	fputc('\n', stderr);
 	fflush(stderr);
     }
 
-    doshfunc(shf->nam, shf->funcdef, cmd->args, shf->flags, 0);
+    doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0);
 
     if (!list_pipe)
 	deletefilelist(last_file_list);
@@ -2749,10 +2798,16 @@ execshfunc(Cmd cmd, Shfunc shf)
 
 /**/
 static int
-execautofn(Cmd cmd)
+execautofn(Cmd cmd, LinkList args, int flags)
 {
     Shfunc shf = cmd->u.autofn->shf;
-    List l = getfpfunc(shf->nam);
+    int noalias = noaliases;
+    List l;
+
+    noaliases = (shf->flags & PM_UNALIASED);
+    l = getfpfunc(shf->nam);
+    noaliases = noalias;
+
     if(l == &dummy_list) {
 	zerr("%s: function definition file not found", shf->nam, 0);
 	return 1;
@@ -2773,9 +2828,7 @@ execautofn(Cmd cmd)
 	} LASTALLOC;
 	shf->flags &= ~PM_UNDEFINED;
     }
-    HEAPALLOC {
-	execlist(dupstruct(shf->funcdef), 1, 0);
-    } LASTALLOC;
+    execlist(shf->funcdef, 1, 0);
     return lastval;
 }
 
@@ -2822,7 +2875,8 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    LinkNode node;
 
 	    node = doshargs->first;
-	    pparams = x = (char **) zcalloc(((sizeof *x) * (1 + countlinknodes(doshargs))));
+	    pparams = x = (char **) zcalloc(((sizeof *x) *
+					     (1 + countlinknodes(doshargs))));
 	    if (isset(FUNCTIONARGZERO)) {
 		oargv0 = argzero;
 		argzero = ztrdup((char *) node->dat);
@@ -2901,7 +2955,7 @@ void
 runshfunc(List list, FuncWrap wrap, char *name)
 {
     int cont;
-    char *ou;
+    VARARR(char, ou, underscorelen);
 
     while (wrap) {
 	wrap->module->wrapper++;
@@ -2917,11 +2971,9 @@ runshfunc(List list, FuncWrap wrap, char *name)
 	wrap = wrap->next;
     }
     startparamscope();
-    ou = underscore;
-    underscore = ztrdup(underscore);
-    execlist(dupstruct(list), 1, 0);
-    zsfree(underscore);
-    underscore = ou;
+    strcpy(ou, underscore);
+    execlist(list, 1, 0);
+    strcpy(underscore, ou);
     endparamscope();
 }
 
@@ -2949,20 +3001,18 @@ getfpfunc(char *s)
 	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);
-		d = (char *) zcalloc(len + 1);
 		if (read(fd, d, len) == len) {
 		    close(fd);
+		    d[len] = '\0';
 		    d = metafy(d, len, META_REALLOC);
 		    HEAPALLOC {
 			r = parse_string(d);
 		    } LASTALLOC;
-		    zfree(d, len + 1);
 		    return r;
-		} else {
-		    zfree(d, len + 1);
+		} else
 		    close(fd);
-		}
 	    } else {
 		close(fd);
 	    }
@@ -2995,9 +3045,10 @@ stripkshdef(List l, char *name)
     if(p->right)
 	return l;
     c = p->left;
-    if(c->type != FUNCDEF || c->flags ||
-	nonempty(c->redir) || nonempty(c->vars) ||
-	empty(c->args) || lastnode(c->args) != firstnode(c->args) ||
+    if (c->type != FUNCDEF || c->flags ||
+	(c->redir && nonempty(c->redir)) || (c->vars && nonempty(c->vars)) ||
+	!c->args || empty(c->args) ||
+	lastnode(c->args) != firstnode(c->args) ||
 	strcmp(name, peekfirst(c->args)))
 	return l;
     return c->u.list;
@@ -3076,8 +3127,7 @@ execsave(void)
     es->trapreturn = trapreturn;
     es->noerrs = noerrs;
     es->subsh_close = subsh_close;
-    es->underscore = underscore;
-    underscore = ztrdup(underscore);
+    es->underscore = ztrdup(underscore);
     es->next = exstack;
     exstack = es;
     noerrs = cmdoutpid = 0;
@@ -3105,8 +3155,8 @@ execrestore(void)
     trapreturn = exstack->trapreturn;
     noerrs = exstack->noerrs;
     subsh_close = exstack->subsh_close;
-    zsfree(underscore);
-    underscore = exstack->underscore;
+    strcpy(underscore, exstack->underscore);
+    zsfree(exstack->underscore);
     en = exstack->next;
     free(exstack);
     exstack = en;
diff --git a/Src/glob.c b/Src/glob.c
index 35aa447de..93b497632 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -744,7 +744,7 @@ parsecomp(int gflag)
 		pptr++;
 	}
 
-	if (*pptr == Inpar && pptr[1] == Pound) {
+	if (*pptr == Inpar && pptr[1] == Pound && isset(EXTENDEDGLOB)) {
 	    /* Found some globbing flags */
 	    char *eptr = pptr;
 	    if (kshfunc != KF_NONE)
@@ -3432,17 +3432,17 @@ tokenize(char *s)
 void
 remnulargs(char *s)
 {
-    int nl = *s;
-    char *t = s;
-
-    while (*s)
-	if (INULL(*s))
-	    chuck(s);
-	else
-	    s++;
-    if (!*t && nl) {
-	t[0] = Nularg;
-	t[1] = '\0';
+    if (*s) {
+	char *t = s, *p = s, c;
+
+	while ((c = *s++))
+	    if (!INULL(c))
+		*p++ = c;
+	*p = '\0';
+	if (!*t) {
+	    t[0] = Nularg;
+	    t[1] = '\0';
+	}
     }
 }
 
diff --git a/Src/hashtable.c b/Src/hashtable.c
index b534d8ac1..e80461b4e 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -880,7 +880,7 @@ printshfuncnode(HashNode hn, int printflags)
 	return;
     }
  
-    t = getpermtext((void *) dupstruct((void *) f->funcdef));
+    t = getpermtext((void *) f->funcdef);
     quotedzputs(f->nam, stdout);
     printf(" () {\n\t");
     zputs(t, stdout);
@@ -1327,7 +1327,7 @@ addhistnode(HashTable ht, char *nam, void *nodeptr)
 {
     HashNode oldnode = addhashnode2(ht, nam, nodeptr);
     Histent he = (Histent)nodeptr;
-    if (oldnode && oldnode != nodeptr) {
+    if (oldnode && oldnode != (HashNode)nodeptr) {
 	if (he->flags & HIST_MAKEUNIQUE
 	 || (he->flags & HIST_FOREIGN && (Histent)oldnode == he->up)) {
 	    he->flags |= HIST_DUP;
diff --git a/Src/hist.c b/Src/hist.c
index 4d672deab..49dac724c 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -30,6 +30,27 @@
 #include "zsh.mdh"
 #include "hist.pro"
 
+/* Functions to call for getting/ungetting a character and for history
+ * word control. */
+
+/**/
+int (*hgetc) _((void));
+
+/**/
+void (*hungetc) _((int));
+
+/**/
+void (*hwaddc) _((int));
+
+/**/
+void (*hwbegin) _((int));
+
+/**/
+void (*hwend) _((void));
+
+/**/
+void (*addtoline) _((int));
+
 /* != 0 means history substitution is turned off */
  
 /**/
@@ -159,12 +180,11 @@ int hlinesz;
 /* default event (usually curhist-1, that is, "!!") */
  
 static int defev;
- 
+
 /* add a character to the current history word */
 
-/**/
-void
-hwaddc(int c)
+static void
+ihwaddc(int c)
 {
     /* Only if history line exists and lexing has not finished. */
     if (chline && !(errflag || lexstop)) {
@@ -182,7 +202,7 @@ hwaddc(int c)
 	if (hptr - chline >= hlinesz) {
 	    int oldsiz = hlinesz;
 
-	    chline = realloc(chline, hlinesz = oldsiz + 16);
+	    chline = realloc(chline, hlinesz = oldsiz + 64);
 	    hptr = chline + oldsiz;
 	}
     }
@@ -192,12 +212,12 @@ hwaddc(int c)
  * zsh expands history (see doexpandhist() in zle_tricky.c). It also     *
  * calculates the new cursor position after the expansion. It is called  *
  * from hgetc() and from gettok() in lex.c for characters in comments.   */
- 
+
 /**/
 void
-addtoline(int c)
+iaddtoline(int c)
 {
-    if (! expanding || lexstop)
+    if (!expanding || lexstop)
 	return;
     if (qbang && c == bangchar && stophist < 2) {
 	exlast--;
@@ -216,9 +236,8 @@ addtoline(int c)
     line[cs++] = itok(c) ? ztokens[c - Pound] : c;
 }
 
-/**/
-int
-hgetc(void)
+static int
+ihgetc(void)
 {
     int c = ingetc();
 
@@ -234,7 +253,7 @@ hgetc(void)
     }
     if ((inbufflags & INP_HIST) && !stophist) {
 	/* the current character c came from a history expansion          *
-	 * (inbufflags && INP_HIST) and history is not disabled           *
+	 * (inbufflags & INP_HIST) and history is not disabled            *
 	 * (e.g. we are not inside single quotes). In that case, \!       *
 	 * should be treated as ! (since this \! came from a previous     *
 	 * history line where \ was used to escape the bang). So if       *
@@ -606,9 +625,8 @@ histsubchar(int c)
 /* unget a char and remove it from chline. It can only be used *
  * to unget a character returned by hgetc.                     */
 
-/**/
-void
-hungetc(int c)
+static void
+ihungetc(int c)
 {
     int doit = 1;
 
@@ -641,10 +659,10 @@ hungetc(int c)
 
 /**/
 void
-strinbeg(void)
+strinbeg(int dohist)
 {
     strin++;
-    hbegin();
+    hbegin(dohist);
     lexinit();
 }
 
@@ -661,17 +679,49 @@ strinend(void)
     histdone = 0;
 }
 
+/* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
+ * they aren't needed */
+
+static void
+nohw(int c)
+{
+}
+
+static void
+nohwe(void)
+{
+}
+
 /* initialize the history mechanism */
 
 /**/
 void
-hbegin(void)
+hbegin(int dohist)
 {
     isfirstln = isfirstch = 1;
     errflag = histdone = spaceflag = 0;
-    stophist = (!interact || unset(BANGHIST) || unset(SHINSTDIN)) << 1;
-    chline = hptr = zcalloc(hlinesz = 16);
-    chwords = zalloc((chwordlen = 16)*sizeof(short));
+    stophist = (dohist ? ((!interact || unset(SHINSTDIN)) << 1) : 2);
+    if (stophist == 2 || (inbufflags & INP_ALIAS)) {
+	chline = hptr = NULL;
+	hlinesz = 0;
+	chwords = NULL;
+	chwordlen = 0;
+	hgetc = ingetc;
+	hungetc = inungetc;
+	hwaddc = nohw;
+	hwbegin = nohw;
+	hwend = nohwe;
+	addtoline = nohw;
+    } else {
+	chline = hptr = zcalloc(hlinesz = 64);
+	chwords = zalloc((chwordlen = 64) * sizeof(short));
+	hgetc = ihgetc;
+	hungetc = ihungetc;
+	hwaddc = ihwaddc;
+	hwbegin = ihwbegin;
+	hwend = ihwend;
+	addtoline = iaddtoline;
+    }
     chwordpos = 0;
 
     if (histactive & HA_JUNKED)
@@ -864,7 +914,8 @@ hend(void)
     int flag, save = 1;
     char *hf = getsparam("HISTFILE");
 
-    DPUTS(!chline, "BUG: chline is NULL in hend()");
+    DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
+	  "BUG: chline is NULL in hend()");
     if (histdone & HISTFLAG_SETTY)
 	settyinfo(&shttyinfo);
     if (!(histactive & HA_NOINC)) {
@@ -1005,8 +1056,10 @@ int hwgetword = -1;
 
 /**/
 void
-hwbegin(int offset)
+ihwbegin(int offset)
 {
+    if (stophist == 2 || strin)
+	return;
     if (chwordpos%2)
 	chwordpos--;	/* make sure we're on a word start, not end */
     /* If we're expanding an alias, we should overwrite the expansion
@@ -1023,15 +1076,18 @@ hwbegin(int offset)
 
 /**/
 void
-hwend(void)
+ihwend(void)
 {
+    if (stophist == 2 || strin)
+	return;
     if (chwordpos%2 && chline) {
 	/* end of word reached and we've already begun a word */
 	if (hptr > chline + chwords[chwordpos-1]) {
 	    chwords[chwordpos++] = hptr - chline;
 	    if (chwordpos >= chwordlen) {
 		chwords = (short *) realloc(chwords,
-					    (chwordlen += 16)*sizeof(short));
+					    (chwordlen += 32) * 
+					    sizeof(short));
 	    }
 	    if (hwgetword > -1) {
 		/* We want to reuse the current word position */
@@ -1606,7 +1662,7 @@ readhistfile(char *fn, int err, int readflags)
     else if (!lockhistfile(fn, 1))
 	return;
     if ((in = fopen(unmeta(fn), "r"))) {
-	nwordlist = 16;
+	nwordlist = 64;
 	wordlist = (short *)zalloc(nwordlist*sizeof(short));
 	bufsiz = 1024;
 	buf = zalloc(bufsiz);
@@ -1717,7 +1773,7 @@ readhistfile(char *fn, int err, int readflags)
 		if (*pt) {
 		    if (nwordpos >= nwordlist)
 			wordlist = (short *) realloc(wordlist,
-					(nwordlist += 16)*sizeof(short));
+					(nwordlist += 64)*sizeof(short));
 		    wordlist[nwordpos++] = pt - start;
 		    while (*pt && !inblank(*pt))
 			pt++;
diff --git a/Src/init.c b/Src/init.c
index f9e6574dd..89cbf2a8b 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -37,6 +37,14 @@
 /**/
 int noexitct = 0;
 
+/* buffer for $_ and its length */
+
+/**/
+char *underscore;
+
+/**/
+int underscorelen;
+
 /* what level of sourcing we are at */
  
 /**/
@@ -94,7 +102,7 @@ loop(int toplevel, int justonce)
 	    if (interact)
 		preprompt();
 	}
-	hbegin();		/* init history mech        */
+	hbegin(1);		/* init history mech        */
 	intr();			/* interrupts on            */
 	lexinit();              /* initialize lexical state */
 	if (!(list = parse_event())) {	/* if we couldn't parse a list */
@@ -128,6 +136,8 @@ loop(int toplevel, int justonce)
 	    if (stopmsg)	/* unset 'you have stopped jobs' flag */
 		stopmsg--;
 	    execlist(list, 0, 0);
+	    if (toplevel)
+		freestructs();
 	    tok = toksav;
 	    if (toplevel)
 		noexitct = 0;
@@ -550,8 +560,20 @@ setupvals(void)
     cdpath   = mkarray(NULL);
     manpath  = mkarray(NULL);
     fignore  = mkarray(NULL);
-#ifdef FUNCTION_DIR
-    fpath    = mkarray(ztrdup(FUNCTION_DIR));
+#ifdef FPATH_DIR
+# ifdef FPATH_SUBDIRS
+    {
+	char *fpath_subdirs[] = FPATH_SUBDIRS;
+	int len = sizeof(fpath_subdirs)/sizeof(char *), j;
+
+	fpath = zalloc((len+1)*sizeof(char *));
+	for (j = 0; j < len; j++)
+	    fpath[j] = tricat(FPATH_DIR, "/", fpath_subdirs[j]);
+	fpath[len] = NULL;
+    }
+# else
+    fpath    = mkarray(ztrdup(FPATH_DIR));
+# endif
 #else
     fpath    = mkarray(NULL);
 #endif
@@ -582,7 +604,8 @@ setupvals(void)
     ifs         = ztrdup(DEFAULT_IFS);
     wordchars   = ztrdup(DEFAULT_WORDCHARS);
     postedit    = ztrdup("");
-    underscore  = ztrdup("");
+    underscore  = (char *) zalloc(underscorelen = 32);
+    *underscore = '\0';
 
     zoptarg = ztrdup("");
     zoptind = 1;
diff --git a/Src/jobs.c b/Src/jobs.c
index 7b0eca6b6..31861e284 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -65,10 +65,6 @@ struct tms shtms;
 /**/
 int ttyfrozen;
  
-/* empty job structure for quick clearing of jobtab entries */
-
-static struct job zero;		/* static variables are initialized to zero */
-
 static struct timeval dtimeval, now;
 
 /* Diff two timevals for elapsed-time computations */
@@ -244,8 +240,11 @@ update_job(Job jn)
 		adjustwinsize(0);
 	    }
 	}
+    } else if (list_pipe && (val & 0200) && inforeground == 1) {
+	breaks = loops;
+	errflag = 1;
+	inerrflush();
     }
-
     if (somestopped && jn->stat & STAT_SUPERJOB)
 	return;
     jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED :
@@ -663,14 +662,16 @@ deletejob(Job jn)
 	nx = pn->next;
 	zfree(pn, sizeof(struct process));
     }
-    zsfree(jn->pwd);
-
     deletefilelist(jn->filelist);
 
     if (jn->ty)
 	zfree(jn->ty, sizeof(struct ttyinfo));
 
-    *jn = zero;
+    jn->gleader = jn->other = 0;
+    jn->stat = jn->stty_in_env = 0;
+    jn->procs = NULL;
+    jn->filelist = NULL;
+    jn->ty = NULL;
 }
 
 /* add a process to the current job */
@@ -831,7 +832,12 @@ waitjob(int job, int sig)
 void
 waitjobs(void)
 {
-    waitjob(thisjob, 0);
+    Job jn = jobtab + thisjob;
+
+    if (jn->procs)
+	waitjob(thisjob, 0);
+    else
+	deletejob(jn);
     thisjob = -1;
 }
 
@@ -843,12 +849,9 @@ clearjobtab(void)
 {
     int i;
 
-    for (i = 1; i < MAXJOB; i++) {
-	if (jobtab[i].pwd)
-	    zsfree(jobtab[i].pwd);
+    for (i = 1; i < MAXJOB; i++)
 	if (jobtab[i].ty)
 	    zfree(jobtab[i].ty, sizeof(struct ttyinfo));
-    }
 
     memset(jobtab, 0, sizeof(jobtab)); /* zero out table */
 }
@@ -864,7 +867,11 @@ initjob(void)
     for (i = 1; i < MAXJOB; i++)
 	if (!jobtab[i].stat) {
 	    jobtab[i].stat = STAT_INUSE;
-	    jobtab[i].pwd = ztrdup(pwd);
+	    if (strlen(pwd) >= PATH_MAX) {
+		memcpy(jobtab[i].pwd, pwd, PATH_MAX);
+		jobtab[i].pwd[PATH_MAX] = '\0';
+	    } else
+		strcpy(jobtab[i].pwd, pwd);
 	    jobtab[i].gleader = 0;
 	    return i;
 	}
diff --git a/Src/lex.c b/Src/lex.c
index 7371243a7..8dc836329 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -179,6 +179,12 @@ struct lexstack {
     int hwgetword;
     int lexstop;
     struct heredocs *hdocs;
+    int (*hgetc) _((void));
+    void (*hungetc) _((int));
+    void (*hwaddc) _((int));
+    void (*hwbegin) _((int));
+    void (*hwend) _((void));
+    void (*addtoline) _((int));
 
     unsigned char *cstack;
     int csp;
@@ -226,6 +232,12 @@ lexsave(void)
     ls->hwgetword = hwgetword;
     ls->lexstop = lexstop;
     ls->hdocs = hdocs;
+    ls->hgetc = hgetc;
+    ls->hungetc = hungetc;
+    ls->hwaddc = hwaddc;
+    ls->hwbegin = hwbegin;
+    ls->hwend = hwend;
+    ls->addtoline = addtoline;
     cmdsp = 0;
     inredir = 0;
     hdocs = NULL;
@@ -271,6 +283,12 @@ lexrestore(void)
     hwgetword = lstack->hwgetword;
     lexstop = lstack->lexstop;
     hdocs = lstack->hdocs;
+    hgetc = lstack->hgetc;
+    hungetc = lstack->hungetc;
+    hwaddc = lstack->hwaddc;
+    hwbegin = lstack->hwbegin;
+    hwend = lstack->hwend;
+    addtoline = lstack->addtoline;
     hlinesz = lstack->hlinesz;
     errflag = 0;
 
@@ -783,7 +801,7 @@ gettok(void)
 static int
 gettokstr(int c, int sub)
 {
-    int bct = 0, pct = 0, brct = 0;
+    int bct = 0, pct = 0, brct = 0, fdpar = 0;
     int intpos = 1, in_brace_param = 0;
     int peek, inquote;
 #ifdef DEBUG
@@ -798,8 +816,12 @@ gettokstr(int c, int sub)
     for (;;) {
 	int act;
 	int e;
+	int inbl = inblank(c);
+	
+	if (fdpar && !inbl && c != ')')
+	    fdpar = 0;
 
-	if (inblank(c) && !in_brace_param && !pct)
+	if (inbl && !in_brace_param && !pct)
 	    act = LX2_BREAK;
 	else {
 	    act = lexact2[STOUC(c)];
@@ -822,6 +844,12 @@ gettokstr(int c, int sub)
 	    add(Meta);
 	    break;
 	case LX2_OUTPAR:
+	    if (fdpar) {
+		/* this is a single word `(   )', treat as INOUTPAR */
+		add(c);
+		*bptr = '\0';
+		return INOUTPAR;
+	    }
 	    if ((sub || in_brace_param) && isset(SHGLOB))
 		break;
 	    if (!in_brace_param && !pct--) {
@@ -898,11 +926,40 @@ gettokstr(int c, int sub)
 		    e = hgetc();
 		    hungetc(e);
 		    lexstop = 0;
-		    if (e == ')' ||
-			(incmdpos && !brct && peek != ENVSTRING))
+		    /* For command words, parentheses are only
+		     * special at the start.  But now we're tokenising
+		     * the remaining string.  So I don't see what
+		     * the old incmdpos test here is for.
+		     *   pws 1999/6/8
+		     *
+		     * Oh, no.
+		     *  func1(   )
+		     * is a valid function definition in [k]sh.  The best
+		     * thing we can do, without really nasty lookahead tricks,
+		     * is break if we find a blank after a parenthesis.  At
+		     * least this can't happen inside braces or brackets.  We
+		     * only allow this with SHGLOB (set for both sh and ksh).
+		     *
+		     * Things like `print @( |foo)' should still
+		     * work, because [k]sh don't allow multiple words
+		     * in a function definition, so we only do this
+		     * in command position.
+		     *   pws 1999/6/14
+		     */
+		    if (e == ')' || (isset(SHGLOB) && inblank(e) && !bct &&
+				     !brct && !intpos && incmdpos))
 			goto brk;
 		}
-		pct++;
+		/*
+		 * This also handles the [k]sh `foo( )' function definition.
+		 * Maintain a variable fdpar, set as long as a single set of
+		 * parentheses contains only space.  Then if we get to the
+		 * closing parenthesis and it is still set, we can assume we
+		 * have a function definition.  Only do this at the start of
+		 * the word, since the (...) must be a separate token.
+		 */
+		if (!pct++ && isset(SHGLOB) && intpos && !bct && !brct)
+		    fdpar = 1;
 	    }
 	    c = Inpar;
 	    break;
@@ -1294,8 +1351,7 @@ parsestr(char *s)
 	lexsave();
 	untokenize(s);
 	inpush(dupstring(s), 0, NULL);
-	strinbeg();
-	stophist = 2;
+	strinbeg(0);
 	len = 0;
 	bptr = tokstr = s;
 	bsiz = l + 1;
@@ -1331,8 +1387,7 @@ parse_subst_string(char *s)
     lexsave();
     untokenize(s);
     inpush(dupstring(s), 0, NULL);
-    strinbeg();
-    stophist = 2;
+    strinbeg(0);
     len = 0;
     bptr = tokstr = s;
     bsiz = l + 1;
@@ -1377,8 +1432,7 @@ exalias(void)
 
     if (!tokstr) {
 	yytext = tokstrings[tok];
-	if (yytext)
-	    yytext = dupstring(yytext);
+
 	return 0;
     }
 
diff --git a/Src/linklist.c b/Src/linklist.c
index 62a962595..e3cb4d626 100644
--- a/Src/linklist.c
+++ b/Src/linklist.c
@@ -38,7 +38,7 @@ newlinklist(void)
 {
     LinkList list;
 
-    list = (LinkList) alloc(sizeof *list);
+    list = (LinkList) ncalloc(sizeof *list);
     list->first = NULL;
     list->last = (LinkNode) list;
     return list;
@@ -53,7 +53,7 @@ insertlinknode(LinkList list, LinkNode node, void *dat)
     LinkNode tmp, new;
 
     tmp = node->next;
-    node->next = new = (LinkNode) alloc(sizeof *tmp);
+    node->next = new = (LinkNode) ncalloc(sizeof *tmp);
     new->last = node;
     new->dat = dat;
     new->next = tmp;
diff --git a/Src/loop.c b/Src/loop.c
index 070173fa2..07a7a56b0 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -44,21 +44,18 @@ int contflag;
  
 /**/
 int breaks;
- 
+
 /**/
 int
-execfor(Cmd cmd)
+execfor(Cmd cmd, LinkList args, int flags)
 {
-    List list;
     Forcmd node;
     char *str;
-    int val = 0;
-    LinkList args;
+    zlong val = 0;
 
     node = cmd->u.forcmd;
-    args = cmd->args;
     if (node->condition) {
-	str = node->name;
+	str = dupstring(node->name);
 	singsub(&str);
 	if (!errflag)
 	    matheval(str);
@@ -69,7 +66,7 @@ execfor(Cmd cmd)
 
 	args = newlinklist();
 	for (x = pparams; *x; x++)
-	    addlinknode(args, ztrdup(*x));
+	    addlinknode(args, dupstring(*x));
     }
     lastval = 0;
     loops++;
@@ -95,13 +92,12 @@ execfor(Cmd cmd)
 	    if (!val)
 		break;
 	} else {
-	    str = (char *) ugetnode(args);
-	    if (!str)
+	    if (!args || !(str = (char *) ugetnode(args)))
 		break;
 	    setsparam(node->name, ztrdup(str));
 	}
-	list = (List) dupstruct(node->list);
-	execlist(list, 1, (cmd->flags & CFLAG_EXEC) && empty(args));
+	execlist(node->list, 1,
+		 (flags & CFLAG_EXEC) && args && empty(args));
 	if (breaks) {
 	    breaks--;
 	    if (breaks || !contflag)
@@ -129,27 +125,24 @@ execfor(Cmd cmd)
 
 /**/
 int
-execselect(Cmd cmd)
+execselect(Cmd cmd, LinkList args, int flags)
 {
-    List list;
     Forcmd node;
     char *str, *s;
-    LinkList args;
     LinkNode n;
     int i;
     FILE *inp;
     size_t more;
 
     node = cmd->u.forcmd;
-    args = cmd->args;
     if (!node->inflag) {
 	char **x;
 
 	args = newlinklist();
 	for (x = pparams; *x; x++)
-	    addlinknode(args, ztrdup(*x));
+	    addlinknode(args, dupstring(*x));
     }
-    if (empty(args))
+    if (!args || empty(args))
 	return 1;
     loops++;
     lastval = 0;
@@ -196,8 +189,7 @@ execselect(Cmd cmd)
 		str = "";
 	}
 	setsparam(node->name, ztrdup(str));
-	list = (List) dupstruct(node->list);
-	execlist(list, 1, 0);
+	execlist(node->list, 1, 0);
 	freeheap();
 	if (breaks) {
 	    breaks--;
@@ -278,9 +270,8 @@ selectlist(LinkList l, size_t start)
 
 /**/
 int
-execwhile(Cmd cmd)
+execwhile(Cmd cmd, LinkList args, int flags)
 {
-    List list;
     struct whilecmd *node;
     int olderrexit, oldval;
 
@@ -290,9 +281,8 @@ execwhile(Cmd cmd)
     pushheap();
     loops++;
     for (;;) {
-	list = (List) dupstruct(node->cont);
 	noerrexit = 1;
-	execlist(list, 1, 0);
+	execlist(node->cont, 1, 0);
 	noerrexit = olderrexit;
 	if (!((lastval == 0) ^ node->cond)) {
 	    if (breaks)
@@ -300,8 +290,7 @@ execwhile(Cmd cmd)
 	    lastval = oldval;
 	    break;
 	}
-	list = (List) dupstruct(node->loop);
-	execlist(list, 1, 0);
+	execlist(node->loop, 1, 0);
 	if (breaks) {
 	    breaks--;
 	    if (breaks || !contflag)
@@ -322,22 +311,20 @@ execwhile(Cmd cmd)
 
 /**/
 int
-execrepeat(Cmd cmd)
+execrepeat(Cmd cmd, LinkList args, int flags)
 {
-    List list;
     int count;
 
     lastval = 0;
-    if (empty(cmd->args) || nextnode(firstnode(cmd->args))) {
+    if (!args || empty(args) || nextnode(firstnode(args))) {
 	zerr("bad argument for repeat", NULL, 0);
 	return 1;
     }
-    count = atoi(peekfirst(cmd->args));
+    count = atoi(peekfirst(args));
     pushheap();
     loops++;
     while (count--) {
-	list = (List) dupstruct(cmd->u.list);
-	execlist(list, 1, 0);
+	execlist(cmd->u.list, 1, 0);
 	freeheap();
 	if (breaks) {
 	    breaks--;
@@ -357,7 +344,7 @@ execrepeat(Cmd cmd)
 
 /**/
 int
-execif(Cmd cmd)
+execif(Cmd cmd, LinkList args, int flags)
 {
     struct ifcmd *node;
     int olderrexit;
@@ -380,7 +367,7 @@ execif(Cmd cmd)
     noerrexit = olderrexit;
 
     if (*t)
-	execlist(*t, 1, cmd->flags & CFLAG_EXEC);
+	execlist(*t, 1, flags & CFLAG_EXEC);
     else
 	lastval = 0;
 
@@ -389,7 +376,7 @@ execif(Cmd cmd)
 
 /**/
 int
-execcase(Cmd cmd)
+execcase(Cmd cmd, LinkList args, int flags)
 {
     struct casecmd *node;
     char *word;
@@ -400,18 +387,18 @@ execcase(Cmd cmd)
     l = node->lists;
     p = node->pats;
 
-    word = *p++;
+    word = dupstring(*p++);
     singsub(&word);
     untokenize(word);
     lastval = 0;
 
     if (node) {
 	while (*p) {
-	    char *pat = *p + 1;
+	    char *pat = dupstring(*p + 1);
 	    singsub(&pat);
 	    if (matchpat(word, pat)) {
 		do {
-		    execlist(*l++, 1, **p == ';' && (cmd->flags & CFLAG_EXEC));
+		    execlist(*l++, 1, **p == ';' && (flags & CFLAG_EXEC));
 		} while(**p++ == '&' && *p);
 		break;
 	    }
@@ -421,4 +408,3 @@ execcase(Cmd cmd)
     }
     return lastval;
 }
-
diff --git a/Src/math.c b/Src/math.c
index efbf22e44..7e7e557ed 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -733,9 +733,9 @@ mathevall(char *s, int prek, char **ep)
     char *xptr;
     zlong xyyval;
     LV xyylval;
-    char **xlvals = 0;
+    char **xlvals = 0, *nlvals[LVCOUNT];
     int xsp;
-    struct mathvalue *xstack = 0;
+    struct mathvalue *xstack = 0, nstack[STACKSZ];
     zlong ret;
 
     xlastbase = xnoeval = xunary = xlvc = xyyval = xyylval = xsp = 0;
@@ -753,9 +753,10 @@ mathevall(char *s, int prek, char **ep)
 	xsp = sp;
 	xstack = stack;
     }
-    stack = (struct mathvalue *)zalloc(STACKSZ*sizeof(struct mathvalue));
+    stack = nstack;
     lastbase = -1;
-    lvals = (char **)zcalloc(LVCOUNT*sizeof(char *));
+    memset(nlvals, 0, LVCOUNT*sizeof(char *));
+    lvals = nlvals;
     lvc = 0;
     ptr = s;
     sp = -1;
@@ -769,8 +770,6 @@ mathevall(char *s, int prek, char **ep)
 
     ret = stack[0].val;
 
-    zfree(lvals, LVCOUNT*sizeof(char *));
-    zfree(stack, STACKSZ*sizeof(struct mathvalue));
     if (--mlevel) {
 	lastbase = xlastbase;
 	noeval = xnoeval;
@@ -827,7 +826,8 @@ mathevalarg(char *s, char **ss)
 static void
 mathparse(int pc)
 {
-    int q, otok, onoeval;
+    zlong q;
+    int otok, onoeval;
 
     if (errflag)
 	return;
diff --git a/Src/mem.c b/Src/mem.c
index 97ff7fdc2..7f3b4688f 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -97,7 +97,7 @@ static int h_m[1025], h_push, h_pop, h_free;
 #endif
 
 #define H_ISIZE  sizeof(zlong)
-#define HEAPSIZE (8192 - H_ISIZE)
+#define HEAPSIZE (16384 - H_ISIZE)
 #define HEAP_ARENA_SIZE (HEAPSIZE - sizeof(struct heap))
 #define HEAPFREE (16384 - H_ISIZE)
 
@@ -133,6 +133,10 @@ global_permalloc(void)
 
 static Heap heaps;
 
+/* first heap with free space, not always correct */
+
+static Heap fheap;
+
 /* Use new heaps from now on. This returns the old heap-list. */
 
 /**/
@@ -141,7 +145,7 @@ new_heaps(void)
 {
     Heap h = heaps;
 
-    heaps = NULL;
+    fheap = heaps = NULL;
 
     return h;
 }
@@ -160,6 +164,7 @@ old_heaps(Heap old)
 	zfree(h, sizeof(*h));
     }
     heaps = old;
+    fheap = NULL;
 }
 
 /* Temporarily switch to other heaps (or back again). */
@@ -171,6 +176,7 @@ switch_heaps(Heap new)
     Heap h = heaps;
 
     heaps = new;
+    fheap = NULL;
 
     return h;
 }
@@ -208,6 +214,8 @@ freeheap(void)
 #if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG)
     h_free++;
 #endif
+
+    fheap = NULL;
     for (h = heaps; h; h = hn) {
 	hn = h->next;
 	if (h->sp) {
@@ -215,6 +223,8 @@ freeheap(void)
 	    memset(arena(h) + h->sp->used, 0xff, h->used - h->sp->used);
 #endif
 	    h->used = h->sp->used;
+	    if (!fheap && h->used < HEAP_ARENA_SIZE)
+		fheap = h;
 	    hl = h;
 	} else
 	    zfree(h, HEAPSIZE);
@@ -238,6 +248,7 @@ popheap(void)
     h_pop++;
 #endif
 
+    fheap = NULL;
     for (h = heaps; h; h = hn) {
 	hn = h->next;
 	if ((hs = h->sp)) {
@@ -246,6 +257,8 @@ popheap(void)
 	    memset(arena(h) + hs->used, 0xff, h->used - hs->used);
 #endif
 	    h->used = hs->used;
+	    if (!fheap && h->used < HEAP_ARENA_SIZE)
+		fheap = h;
 	    zfree(hs, sizeof(*hs));
 
 	    hl = h;
@@ -275,13 +288,12 @@ zhalloc(size_t size)
 
     /* find a heap with enough free space */
 
-    for (h = heaps; h; h = h->next) {
+    for (h = (fheap ? fheap : heaps); h; h = h->next) {
 	if (HEAP_ARENA_SIZE >= (n = size + h->used)) {
 	    h->used = n;
 	    return arena(h) + n - size;
 	}
     }
-
     {
 	Heap hp;
         /* not found, allocate new heap */
@@ -311,6 +323,7 @@ zhalloc(size_t size)
 	    hp->next = h;
 	else
 	    heaps = h;
+	fheap = NULL;
 
 	unqueue_signals();
 	return arena(h);
@@ -361,6 +374,7 @@ hrealloc(char *p, size_t old, size_t new)
 		ph->next = h->next;
 	    else
 		heaps = h->next;
+	    fheap = NULL;
 	    zfree(h, HEAPSIZE);
 	    return NULL;
 	}
@@ -593,6 +607,16 @@ struct m_hdr {
 #define M_ISIZE (sizeof(zlong))
 #define M_MIN   (2 * M_ISIZE)
 
+/* M_FREE  is the number of bytes that have to be free before memory is
+ *         given back to the system
+ * M_KEEP  is the number of bytes that will be kept when memory is given
+ *         back; note that this has to be less than M_FREE
+ * M_ALLOC is the number of extra bytes to request from the system */
+
+#define M_FREE  65536
+#define M_KEEP  32768
+#define M_ALLOC M_KEEP
+
 /* a pointer to the last free block, a pointer to the free list (the blocks
    on this list are kept in order - lowest address first) */
 
@@ -623,13 +647,13 @@ static char *m_high, *m_low;
 
 
 #define M_SIDX(S)  ((S) / M_ISIZE)
-#define M_SNUM     50
+#define M_SNUM     128
 #define M_SLEN(M)  ((M)->len / M_SNUM)
 #define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) +  \
 		    sizeof(zlong) + sizeof(struct m_hdr *))
 #define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) -  \
 		     sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
-#define M_NSMALL 8
+#define M_NSMALL   13
 
 static struct m_hdr *m_small[M_NSMALL];
 
@@ -691,9 +715,9 @@ malloc(MALLOC_ARG_T size)
 	    m->used++;
 
 	    /* if all small blocks in this block are allocated, the block is 
-	       put at the end of the list blocks wth small blocks of this
+	       put at the end of the list blocks with small blocks of this
 	       size (i.e., we try to keep blocks with free blocks at the
-	       beginning of the list, to make the search faster */
+	       beginning of the list, to make the search faster) */
 
 	    if (m->used == M_SNUM && m->next) {
 		for (mt = m; mt->next; mt = mt->next);
@@ -753,15 +777,24 @@ malloc(MALLOC_ARG_T size)
 	for (mp = NULL, m = m_free; m && m->len < size; mp = m, m = m->next);
     }
     if (!m) {
+	long nal;
 	/* no matching free block was found, we have to request new
 	   memory from the system */
-	n = (size + M_HSIZE + m_pgsz - 1) & ~(m_pgsz - 1);
+	n = (size + M_HSIZE + M_ALLOC + m_pgsz - 1) & ~(m_pgsz - 1);
 
 	if (((char *)(m = (struct m_hdr *)sbrk(n))) == ((char *)-1)) {
 	    DPUTS(1, "MEM: allocation error at sbrk.");
 	    unqueue_signals();
 	    return NULL;
 	}
+	if ((nal = ((long)(char *)m) & (M_ALIGN-1))) {
+	    if ((char *)sbrk(M_ALIGN - nal) == (char *)-1) {
+		DPUTS(1, "MEM: allocation error at sbrk.");
+		unqueue_signals();
+		return NULL;
+	    }
+	    m = (struct m_hdr *) ((char *)m + (M_ALIGN - nal));
+	}
 	/* set m_low, for the check in free() */
 	if (!m_low)
 	    m_low = (char *)m;
@@ -1034,8 +1067,8 @@ zfree(void *p, int sz)
        and now there is more than one page size of memory, we can give
        it back to the system (and we do it ;-) */
     if ((((char *)m_lfree) + M_ISIZE + m_lfree->len) == m_high &&
-	m_lfree->len >= m_pgsz + M_MIN) {
-	long n = (m_lfree->len - M_MIN) & ~(m_pgsz - 1);
+	m_lfree->len >= m_pgsz + M_MIN + M_FREE) {
+	long n = (m_lfree->len - M_MIN - M_KEEP) & ~(m_pgsz - 1);
 
 	m_lfree->len -= n;
 	if (brk(m_high -= n) == -1) {
diff --git a/Src/params.c b/Src/params.c
index 75910c6fd..b9f3d33a0 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -52,7 +52,6 @@ char **pparams,		/* $argv        */
  
 /**/
 char *argzero,		/* $0           */
-     *underscore,	/* $_           */
      *home,		/* $HOME        */
      *hostnam,		/* $HOST        */
      *ifs,		/* $IFS         */
@@ -232,11 +231,6 @@ IPDEF9("manpath", &manpath, "MANPATH"),
 IPDEF9("psvar", &psvar, "PSVAR"),
 IPDEF9("watch", &watch, "WATCH"),
 
-/*TEST BEGIN*/
-#define IPDEF10(A) {NULL,A,PM_HASHED|PM_SPECIAL|PM_DONTIMPORT,BR((void *)0),SFN(hashsetfn),GFN(hashgetfn),stdunsetfn,0,NULL,NULL,NULL,0}
-IPDEF10("testhash"),
-/*TEST END*/
-
 #ifdef DYNAMIC
 IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
 #endif
@@ -723,16 +717,13 @@ isident(char *s)
 #endif
 }
 
-static char **garr;
-
 /**/
 static zlong
 getarg(char **str, int *inv, Value v, int a2, zlong *w)
 {
-    int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
-    int beg = 0, hasbeg = 0;
+    int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
     char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
-    zlong r = 0;
+    zlong num = 1, beg = 0, r = 0;
     Comp c;
 
     ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED);
@@ -1147,7 +1138,6 @@ fetchvalue(char **pptr, int bracks, int flags)
     int ppar = 0;
 
     s = t = *pptr;
-    garr = NULL;
 
     if (idigit(*s)) {
 	if (bracks >= 0)
@@ -1638,7 +1628,7 @@ setaparam(char *s, char **val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_ARRAY);
 	*ss = '[';
-	if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+	if (v && PM_TYPE(v->pm->flags) == PM_HASHED) {
 	    zerr("attempt to set slice of associative array", NULL, 0);
 	    freearray(val);
 	    errflag = 1;
@@ -2466,7 +2456,10 @@ wordcharssetfn(Param pm, char *x)
 char *
 underscoregetfn(Param pm)
 {
-    return underscore;
+    char *u = dupstring(underscore);
+
+    untokenize(u);
+    return u;
 }
 
 /* Function to get value for special parameter `TERM' */
diff --git a/Src/parse.c b/Src/parse.c
index 043aa0f0d..520181750 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -298,6 +298,8 @@ par_pline(void)
 	rdr->type = MERGEOUT;
 	rdr->fd1 = 2;
 	rdr->name = dupstring("1");
+	if (!c->redir)
+	    c->redir = newlinklist();
 	addlinknode(c->redir, rdr);
 
 	cmdpush(CS_ERRPIPE);
@@ -330,11 +332,14 @@ par_cmd(void)
 
     c = (Cmd) make_cmd();
     c->lineno = lineno;
-    c->args = newlinklist();
-    c->redir = newlinklist();
-    c->vars = newlinklist();
-    while (IS_REDIROP(tok))
-	par_redir(c->redir);
+    c->args = NULL;
+    c->vars = NULL;
+    if (IS_REDIROP(tok)) {
+	c->redir = newlinklist();
+	while (IS_REDIROP(tok))
+	    par_redir(c->redir);
+    } else
+	c->redir = NULL;
     switch (tok) {
     case FOR:
 	cmdpush(CS_FOR);
@@ -399,6 +404,8 @@ par_cmd(void)
 	break;
     case DINPAR:
 	c->type = CARITH;
+	if (!c->args)
+	    c->args = newlinklist();
 	addlinknode(c->args, tokstr);
 	yylex();
 	break;
@@ -407,8 +414,12 @@ par_cmd(void)
 	    return NULL;
 	break;
     }
-    while (IS_REDIROP(tok))
-	par_redir(c->redir);
+    if (IS_REDIROP(tok)) {
+	if (!c->redir)
+	    c->redir = newlinklist();
+	while (IS_REDIROP(tok))
+	    par_redir(c->redir);
+    }
     incmdpos = 1;
     incasepat = 0;
     incond = 0;
@@ -460,6 +471,8 @@ par_for(Cmd c)
 	    f->inflag = 1;
 	    incmdpos = 0;
 	    yylex();
+	    if (!c->args)
+		c->args = newlinklist();
 	    c->args = par_wordlist();
 	    if (tok != SEPER)
 		YYERRORV;
@@ -467,6 +480,8 @@ par_for(Cmd c)
 	    f->inflag = 1;
 	    incmdpos = 0;
 	    yylex();
+	    if (!c->args)
+		c->args = newlinklist();
 	    c->args = par_nl_wordlist();
 	    if (tok != OUTPAR)
 		YYERRORV;
@@ -819,6 +834,8 @@ par_repeat(Cmd c)
     yylex();
     if (tok != STRING)
 	YYERRORV;
+    if (!c->args)
+	c->args = newlinklist();
     addlinknode(c->args, tokstr);
     incmdpos = 1;
     yylex();
@@ -966,6 +983,8 @@ par_simple(Cmd c)
 		v->str = p + 1;
 	    } else
 		equalsplit(tokstr, &v->str);
+	    if (!c->vars)
+		c->vars = newlinklist();
 	    addlinknode(c->vars, v);
 	    isnull = 0;
 	} else if (tok == ENVARRAY) {
@@ -982,6 +1001,8 @@ par_simple(Cmd c)
 	    if (tok != OUTPAR)
 		YYERROR;
 	    incmdpos = oldcmdpos;
+	    if (!c->vars)
+		c->vars = newlinklist();
 	    addlinknode(c->vars, v);
 	    isnull = 0;
 	} else
@@ -993,9 +1014,13 @@ par_simple(Cmd c)
     for (;;) {
 	if (tok == STRING) {
 	    incmdpos = 0;
+	    if (!c->args)
+		c->args = newlinklist();
 	    addlinknode(c->args, tokstr);
 	    yylex();
 	} else if (IS_REDIROP(tok)) {
+	    if (!c->redir)
+		c->redir = newlinklist();
 	    par_redir(c->redir);
 	} else if (tok == INOUTPAR) {
 	    incmdpos = 1;
@@ -1011,15 +1036,26 @@ par_simple(Cmd c)
 		    YYERROR;
 		}
 		yylex();
-	    } else
-		c->u.list = (List) expandstruct((struct node *) par_cmd(), N_LIST);
+	    } else {
+		List l;
+		Sublist sl;
+		Pline pl;
+
+		l = (List) allocnode(N_LIST);
+		l->type = Z_SYNC;
+		l->left = sl = (Sublist) allocnode(N_SUBLIST);
+		sl->type = END;
+		sl->left = pl = (Pline) allocnode(N_PLINE);
+		pl->type = END;
+		pl->left = par_cmd();
+	    }
 	    cmdpop();
 	    c->type = FUNCDEF;
 	} else
 	    break;
 	isnull = 0;
     }
-    if (isnull && empty(c->redir))
+    if (isnull && (!c->redir || empty(c->redir)))
 	return NULL;
     incmdpos = 1;
     return c;
diff --git a/Src/signals.c b/Src/signals.c
index 3e655cbfd..f69da0140 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -719,7 +719,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
 	freelinklist(args, (FreeFunc) NULL);
 	zsfree(name);
     } else HEAPALLOC {
-	execlist(dupstruct(sigfn), 1, 0);
+	execlist(sigfn, 1, 0);
     } LASTALLOC;
     if (trapreturn > 0)
 	trapret = trapreturn;
diff --git a/Src/subst.c b/Src/subst.c
index 0017b205e..d85d73f58 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -53,9 +53,9 @@ prefork(LinkList list, int flags)
 
     MUSTUSEHEAP("prefork");
     for (node = firstnode(list); node; incnode(node)) {
-	char *str, *str3;
+	char *str;
 
-	str = str3 = (char *)getdata(node);
+	str = (char *)getdata(node);
 	if ((*str == Inang || *str == Outang || *str == Equals) &&
 	    str[1] == Inpar) {
 	    if (*str == Inang || *str == Outang)
diff --git a/Src/text.c b/Src/text.c
index d3eafaf9f..73b9088f2 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -169,7 +169,6 @@ gettext2(struct node *n)
 	    if (_List(n)->type & Z_DISOWN)
 		taddstr("|");
 	}
-	simplifyright(_List(n));
 	if (_List(n)->right) {
 	    if (tnewlins)
 		taddnl();
@@ -460,22 +459,23 @@ getsimptext(Cmd cmd)
 {
     LinkNode n;
 
-    for (n = firstnode(cmd->vars); n; incnode(n)) {
-	struct varasg *v = (struct varasg *)getdata(n);
-
-	taddstr(v->name);
-	taddchr('=');
-	if (PM_TYPE(v->type) == PM_ARRAY) {
-	    taddchr('(');
-	    taddlist(v->arr);
-	    taddstr(") ");
-	} else if (PM_TYPE(v->type) == PM_HASHED) {
-	    /* XXX */
-	} else {
-	    taddstr(v->str);
-	    taddchr(' ');
+    if (cmd->vars)
+	for (n = firstnode(cmd->vars); n; incnode(n)) {
+	    struct varasg *v = (struct varasg *)getdata(n);
+
+	    taddstr(v->name);
+	    taddchr('=');
+	    if (PM_TYPE(v->type) == PM_ARRAY) {
+		taddchr('(');
+		taddlist(v->arr);
+		taddstr(") ");
+	    } else if (PM_TYPE(v->type) == PM_HASHED) {
+		/* XXX */
+	    } else {
+		taddstr(v->str);
+		taddchr(' ');
+	    }
 	}
-    }
     taddlist(cmd->args);
 }
 
@@ -490,6 +490,8 @@ getredirs(Cmd cmd)
 	"<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">"
     };
 
+    if (!cmd->redir)
+	return;
     taddchr(' ');
     for (n = firstnode(cmd->redir); n; incnode(n)) {
 	struct redir *f = (struct redir *)getdata(n);
@@ -537,7 +539,7 @@ taddlist(LinkList l)
 {
     LinkNode n;
 
-    if (!(n = firstnode(l)))
+    if (!l || !(n = firstnode(l)))
 	return;
     for (; n; incnode(n)) {
 	taddstr(getdata(n));
diff --git a/Src/utils.c b/Src/utils.c
index 32588b4c3..ba4f3dc9d 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -853,61 +853,131 @@ int resetneeded;
 int winchanged;
 #endif
 
-/* check the size of the window and adjust if necessary. *
- * The value of from:					 *
- *   0: called from update_job or setupvals		 *
- *   1: called from the SIGWINCH handler		 *
- *   2: the user have just changed LINES manually	 *
- *   3: the user have just changed COLUMNS manually      */
-
-/**/
-void
-adjustwinsize(int from)
+static int
+adjustlines(int signalled)
 {
-    int oldcols = columns, oldrows = lines;
+    int oldlines = lines;
 
 #ifdef TIOCGWINSZ
-    static int userlines, usercols;
-
-    if (SHTTY == -1)
-	return;
-
-    if (from == 2)
-	userlines = lines > 0;
-    if (from == 3)
-	usercols = columns > 0;
-
-    if (!ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize)) {
-	if (!userlines || from == 1)
-	    lines = shttyinfo.winsize.ws_row;
-	if (!usercols || from == 1)
-	    columns = shttyinfo.winsize.ws_col;
+    if (signalled || lines <= 0)
+	lines = shttyinfo.winsize.ws_row;
+    else
+	shttyinfo.winsize.ws_row = lines;
+#endif /* TIOCGWINSZ */
+    if (lines <= 0) {
+	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ rows");
+	lines = tclines > 0 ? tclines : 24;
     }
-#endif   /* TIOCGWINSZ */
 
-    if (lines <= 0)
-	lines = tclines > 0 ? tclines : 24;
-    if (columns <= 0)
-	columns = tccolumns > 0 ? tccolumns : 80;
     if (lines > 2)
 	termflags &= ~TERM_SHORT;
     else
 	termflags |= TERM_SHORT;
+
+    return (lines != oldlines);
+}
+
+static int
+adjustcolumns(int signalled)
+{
+    int oldcolumns = columns;
+
+#ifdef TIOCGWINSZ
+    if (signalled || columns <= 0)
+	columns = shttyinfo.winsize.ws_col;
+    else
+	shttyinfo.winsize.ws_col = columns;
+#endif /* TIOCGWINSZ */
+    if (columns <= 0) {
+	DPUTS(signalled, "BUG: Impossible TIOCGWINSZ cols");
+	columns = tccolumns > 0 ? tccolumns : 80;
+    }
+
     if (columns > 2)
 	termflags &= ~TERM_NARROW;
     else
 	termflags |= TERM_NARROW;
 
+    return (columns != oldcolumns);
+}
+
+/* check the size of the window and adjust if necessary. *
+ * The value of from:					 *
+ *   0: called from update_job or setupvals		 *
+ *   1: called from the SIGWINCH handler		 *
+ *   2: called from the LINES parameter callback	 *
+ *   3: called from the COLUMNS parameter callback	 */
+
+/**/
+void
+adjustwinsize(int from)
+{
+    static int getwinsz = 1;
+    int ttyrows = shttyinfo.winsize.ws_row;
+    int ttycols = shttyinfo.winsize.ws_col;
+    int resetzle = 0;
+
+    if (getwinsz || from == 1) {
 #ifdef TIOCGWINSZ
-    if (interact && from >= 2) {
-	shttyinfo.winsize.ws_row = lines;
-	shttyinfo.winsize.ws_col = columns;
+	if (SHTTY == -1)
+	    return;
+	if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) {
+	    resetzle = (ttyrows != shttyinfo.winsize.ws_row ||
+			ttycols != shttyinfo.winsize.ws_col);
+	    if (from == 0 && resetzle && ttyrows && ttycols)
+		from = 1; /* Signal missed while a job owned the tty? */
+	    ttyrows = shttyinfo.winsize.ws_row;
+	    ttycols = shttyinfo.winsize.ws_col;
+	} else {
+	    /* Set to unknown on failure */
+	    shttyinfo.winsize.ws_row = 0;
+	    shttyinfo.winsize.ws_col = 0;
+	    resetzle = 1;
+	}
+#else
+	resetzle = from == 1;
+#endif /* TIOCGWINSZ */
+    } /* else
+	 return; */
+
+    switch (from) {
+    case 0:
+    case 1:
+	getwinsz = 0;
+	/* Calling setiparam() here calls this function recursively, but  *
+	 * because we've already called adjustlines() and adjustcolumns() *
+	 * here, recursive calls are no-ops unless a signal intervenes.   *
+	 * The commented "else return;" above might be a safe shortcut,   *
+	 * but I'm concerned about what happens on race conditions; e.g., *
+	 * suppose the user resizes his xterm during `eval $(resize)'?    */
+	if (adjustlines(from) && zgetenv("LINES"))
+	    setiparam("LINES", lines);
+	if (adjustcolumns(from) && zgetenv("COLUMNS"))
+	    setiparam("COLUMNS", columns);
+	getwinsz = 1;
+	break;
+    case 2:
+	resetzle = adjustlines(0);
+	break;
+    case 3:
+	resetzle = adjustcolumns(0);
+	break;
+    }
+
+#ifdef TIOCGWINSZ
+    if (interact && from >= 2 &&
+	(shttyinfo.winsize.ws_row != ttyrows ||
+	 shttyinfo.winsize.ws_col != ttycols)) {
+	/* shttyinfo.winsize is already set up correctly */
 	ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize);
     }
-#endif
+#endif /* TIOCGWINSZ */
 
-    if (zleactive && (from >= 2 || oldcols != columns || oldrows != lines)) {
-	resetneeded = winchanged = 1;
+    if (zleactive && resetzle) {
+#ifdef TIOCGWINSZ
+	winchanged =
+#endif /* TIOCGWINSZ */
+	    resetneeded = 1;
 	zrefresh();
     }
 }
@@ -1733,13 +1803,12 @@ char *
 sepjoin(char **s, char *sep)
 {
     char *r, *p, **t;
-    int l, sl, elide = 0;
+    int l, sl;
     char sepbuf[3];
 
     if (!*s)
 	return "";
     if (!sep) {
-	elide = 1;
 	sep = sepbuf;
 	sepbuf[0] = *ifs;
 	sepbuf[1] = *ifs == Meta ? ifs[1] ^ 32 : '\0';
@@ -1853,329 +1922,21 @@ allocnode(int type)
 {
     struct node *n;
 
-    n = (struct node *) alloc(sizetab[type]);
+    n = (struct node *) ncalloc(sizetab[type]);
     memset((void *) n, 0, sizetab[type]);
     n->ntype = flagtab[type];
-    if (useheap)
-	n->ntype |= NT_HEAP;
 
     return (void *) n;
 }
+ 
+/* duplicate a syntax tree */
 
 /**/
 void *
 dupstruct(void *a)
 {
-    struct node *n, *r;
-
-    n = (struct node *) a;
-    if (!a || ((List) a) == &dummy_list)
-	return (void *) a;
-
-    if ((n->ntype & NT_HEAP) && !useheap) {
-	HEAPALLOC {
-	    n = (struct node *) dupstruct2((void *) n);
-	} LASTALLOC;
-	n = simplifystruct(n);
-    }
-    r = (struct node *)dupstruct2((void *) n);
-
-    if (!(n->ntype & NT_HEAP) && useheap)
-	r = expandstruct(r, N_LIST);
-
-    return (void *) r;
-}
-
-/**/
-static struct node *
-simplifystruct(struct node *n)
-{
-    if (!n || ((List) n) == &dummy_list)
-	return n;
-
-    switch (NT_TYPE(n->ntype)) {
-    case N_LIST:
-	{
-	    List l = (List) n;
-
-	    l->left = (Sublist) simplifystruct((struct node *)l->left);
-	    if ((l->type & Z_SYNC) && !l->right)
-		return (struct node *)l->left;
-	}
-	break;
-    case N_SUBLIST:
-	{
-	    Sublist sl = (Sublist) n;
-
-	    sl->left = (Pline) simplifystruct((struct node *)sl->left);
-	    if (sl->type == END && !sl->flags && !sl->right)
-		return (struct node *)sl->left;
-	}
-	break;
-    case N_PLINE:
-	{
-	    Pline pl = (Pline) n;
-
-	    pl->left = (Cmd) simplifystruct((struct node *)pl->left);
-	    if (pl->type == END && !pl->right)
-		return (struct node *)pl->left;
-	}
-	break;
-    case N_CMD:
-	{
-	    Cmd c = (Cmd) n;
-	    int i = 0;
-
-	    if (empty(c->args))
-		c->args = NULL, i++;
-	    if (empty(c->redir))
-		c->redir = NULL, i++;
-	    if (empty(c->vars))
-		c->vars = NULL, i++;
-
-	    c->u.list = (List) simplifystruct((struct node *)c->u.list);
-	    if (i == 3 && !c->flags &&
-		(c->type == CWHILE || c->type == CIF ||
-		 c->type == COND))
-		return (struct node *)c->u.list;
-	}
-	break;
-    case N_FOR:
-	{
-	    Forcmd f = (Forcmd) n;
-
-	    f->list = (List) simplifystruct((struct node *)f->list);
-	}
-	break;
-    case N_CASE:
-	{
-	    struct casecmd *c = (struct casecmd *)n;
-	    List *l;
-
-	    for (l = c->lists; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	}
-	break;
-    case N_IF:
-	{
-	    struct ifcmd *i = (struct ifcmd *)n;
-	    List *l;
-
-	    for (l = i->ifls; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	    for (l = i->thenls; *l; l++)
-		*l = (List) simplifystruct((struct node *)*l);
-	}
-	break;
-    case N_WHILE:
-	{
-	    struct whilecmd *w = (struct whilecmd *)n;
-
-	    w->cont = (List) simplifystruct((struct node *)w->cont);
-	    w->loop = (List) simplifystruct((struct node *)w->loop);
-	}
-    }
-
-    return n;
-}
-
-/**/
-struct node *
-expandstruct(struct node *n, int exp)
-{
-    struct node *m;
-
-    if (!n || ((List) n) == &dummy_list)
-	return n;
-
-    if (exp != N_COUNT && exp != NT_TYPE(n->ntype)) {
-	switch (exp) {
-	case N_LIST:
-	    {
-		List l;
-
-		m = (struct node *) allocnode(N_LIST);
-		l = (List) m;
-		l->type = Z_SYNC;
-		l->left = (Sublist) expandstruct(n, N_SUBLIST);
-
-		return (struct node *)l;
-	    }
-	case N_SUBLIST:
-	    {
-		Sublist sl;
-
-		m = (struct node *) allocnode(N_SUBLIST);
-		sl = (Sublist) m;
-		sl->type = END;
-		sl->left = (Pline) expandstruct(n, N_PLINE);
-
-		return (struct node *)sl;
-	    }
-	case N_PLINE:
-	    {
-		Pline pl;
-
-		m = (struct node *) allocnode(N_PLINE);
-		pl = (Pline) m;
-		pl->type = END;
-		pl->left = (Cmd) expandstruct(n, N_CMD);
-
-		return (struct node *)pl;
-	    }
-	case N_CMD:
-	    {
-		Cmd c;
-
-		m = (struct node *) allocnode(N_CMD);
-		c = (Cmd) m;
-		switch (NT_TYPE(n->ntype)) {
-		case N_WHILE:
-		    c->type = CWHILE;
-		    break;
-		case N_IF:
-		    c->type = CIF;
-		    break;
-		case N_COND:
-		    c->type = COND;
-		}
-		c->u.list = (List) expandstruct(n, NT_TYPE(n->ntype));
-		c->args = newlinklist();
-		c->vars = newlinklist();
-		c->redir = newlinklist();
-
-		return (struct node *)c;
-	    }
-	}
-    } else
-	switch (NT_TYPE(n->ntype)) {
-	case N_LIST:
-	    {
-		List l = (List) n;
-
-		l->left = (Sublist) expandstruct((struct node *)l->left,
-						 N_SUBLIST);
-		l->right = (List) expandstruct((struct node *)l->right,
-					       N_LIST);
-	    }
-	    break;
-	case N_SUBLIST:
-	    {
-		Sublist sl = (Sublist) n;
-
-		sl->left = (Pline) expandstruct((struct node *)sl->left,
-						N_PLINE);
-		sl->right = (Sublist) expandstruct((struct node *)sl->right,
-						   N_SUBLIST);
-	    }
-	    break;
-	case N_PLINE:
-	    {
-		Pline pl = (Pline) n;
-
-		pl->left = (Cmd) expandstruct((struct node *)pl->left,
-					      N_CMD);
-		pl->right = (Pline) expandstruct((struct node *)pl->right,
-						 N_PLINE);
-	    }
-	    break;
-	case N_CMD:
-	    {
-		Cmd c = (Cmd) n;
-
-		if (!c->args)
-		    c->args = newlinklist();
-		if (!c->vars)
-		    c->vars = newlinklist();
-		if (!c->redir)
-		    c->redir = newlinklist();
-
-		switch (c->type) {
-		case CFOR:
-		case CSELECT:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_FOR);
-		    break;
-		case CWHILE:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_WHILE);
-		    break;
-		case CIF:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_IF);
-		    break;
-		case CCASE:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_CASE);
-		    break;
-		case COND:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_COND);
-		    break;
-		case ZCTIME:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_SUBLIST);
-		    break;
-		case AUTOFN:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_AUTOFN);
-		    break;
-		default:
-		    c->u.list = (List) expandstruct((struct node *)c->u.list,
-						    N_LIST);
-		}
-	    }
-	    break;
-	case N_FOR:
-	    {
-		Forcmd f = (Forcmd) n;
-
-		f->list = (List) expandstruct((struct node *)f->list,
-					      N_LIST);
-	    }
-	    break;
-	case N_CASE:
-	    {
-		struct casecmd *c = (struct casecmd *)n;
-		List *l;
-
-		for (l = c->lists; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-	    }
-	    break;
-	case N_IF:
-	    {
-		struct ifcmd *i = (struct ifcmd *)n;
-		List *l;
-
-		for (l = i->ifls; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-		for (l = i->thenls; *l; l++)
-		    *l = (List) expandstruct((struct node *)*l, N_LIST);
-	    }
-	    break;
-	case N_WHILE:
-	    {
-		struct whilecmd *w = (struct whilecmd *)n;
-
-		w->cont = (List) expandstruct((struct node *)w->cont,
-					      N_LIST);
-		w->loop = (List) expandstruct((struct node *)w->loop,
-					      N_LIST);
-	    }
-	}
-
-    return n;
-}
-
-/* duplicate a syntax tree */
-
-/**/
-static void *
-dupstruct2(void *a)
-{
     void **onodes, **nnodes, *ret, *n, *on;
-    int type, heap;
+    int type;
     size_t nodeoffs;
 
     if (!a || ((List) a) == &dummy_list)
@@ -2183,53 +1944,33 @@ dupstruct2(void *a)
     type = *(int *)a;
     ret = alloc(sizetab[NT_TYPE(type)]);
     memcpy(ret, a, nodeoffs = offstab[NT_TYPE(type)]);
-    *(int*)ret = (type & ~NT_HEAP) | (useheap ? NT_HEAP : 0);
     onodes = (void **) ((char *)a + nodeoffs);
     nnodes = (void **) ((char *)ret + nodeoffs);
-    heap = type & NT_HEAP;
     for (type = (type & 0xffff00) >> 4; (type >>= 4); *nnodes++ = n) {
 	if (!(on = *onodes++))
 	    n = NULL;
 	else {
 	    switch (type & 0xf) {
 	    case NT_NODE:
-		n = dupstruct2(on);
+		n = dupstruct(on);
 		break;
 	    case NT_STR:
 		n = dupstring(on);
 		break;
 	    case NT_LIST | NT_NODE:
-		if (heap) {
-		    if (useheap)
-			n =  duplist(on, (VFunc) dupstruct2);
-		    else
-			n = list2arr(on, (VFunc) dupstruct2);
-		}
-		else if (useheap)
-		    n = arr2list(on, (VFunc) dupstruct2);
-		else
-		    n = duparray(on, (VFunc) dupstruct2);
+		n = duplist(on, (VFunc) dupstruct);
 		break;
 	    case NT_LIST | NT_STR:
-		if (heap) {
-		    if (useheap)
-			n = duplist(on, (VFunc) dupstring);
-		    else
-			n = list2arr(on, (VFunc) ztrdup);
-		}
-		else if (useheap)
-		    n = arr2list(on, (VFunc) dupstring);
-		else
-		    n = duparray(on, (VFunc) ztrdup);
+		n = duplist(on, (VFunc) (useheap ? dupstring : ztrdup));
 		break;
 	    case NT_NODE | NT_ARR:
-		n = duparray(on, (VFunc) dupstruct2);
+		n = duparray(on, (VFunc) dupstruct);
 		break;
 	    case NT_STR | NT_ARR:
 		n = duparray(on, (VFunc) (useheap ? dupstring : ztrdup));
 		break;
 	    default:
-		DPUTS(1, "BUG: bad node type in dupstruct2()");
+		DPUTS(1, "BUG: bad node type in dupstruct()");
 		abort();
 	    }
 	}
@@ -2237,19 +1978,46 @@ dupstruct2(void *a)
     return ret;
 }
 
-/* free a syntax tree */
+/* Free a syntax tree. Now, freestruct() only registers everything that
+ * has to be freed. Later, freestructs() will be called to do the real
+ * work. This is to avoid having functions that are currently executed
+ * free themselves by re-defining themselves. */
+
+static LinkList freeslist = NULL;
 
 /**/
 void
 freestruct(void *a)
 {
-    void **nodes, *n;
-    int type, size;
-
     if (!a || ((List) a) == &dummy_list)
 	return;
 
-    type = * (int *) a;
+    PERMALLOC {
+	if (!freeslist)
+	    freeslist = newlinklist();
+	addlinknode(freeslist, a);
+    } LASTALLOC;
+}
+
+/**/
+void
+freestructs(void)
+{
+    void *a;
+
+    if (freeslist)
+	while ((a = getlinknode(freeslist)))
+	    ifreestruct(a);
+}
+
+/**/
+static void
+ifreestruct(void *a)
+{
+    void **nodes, *n;
+    int type, size;
+
+    type = *(int *) a;
     nodes = (void **) ((char *)a + offstab[NT_TYPE(type)]);
     size = sizetab[NT_TYPE(type)];
     for (type = (type & 0xffff00) >> 4; (type >>= 4);) {
@@ -2262,6 +2030,8 @@ freestruct(void *a)
 		zsfree((char *) n);
 		break;
 	    case NT_LIST | NT_NODE:
+		freelinklist((LinkList) n, (FreeFunc) freestruct);
+		break;
 	    case NT_NODE | NT_ARR:
 	    {
 		void **p = (void **) n;
@@ -2272,6 +2042,8 @@ freestruct(void *a)
 		break;
 	    }
 	    case NT_LIST | NT_STR:
+		freelinklist((LinkList) n, (FreeFunc) zsfree);
+		break;
 	    case NT_STR | NT_ARR:
 		freearray((char **) n);
 		break;
@@ -2287,58 +2059,46 @@ freestruct(void *a)
 }
 
 /**/
-static LinkList
-duplist(LinkList l, VFunc func)
+LinkList
+dupheaplist(LinkList l)
 {
-    LinkList ret;
-    LinkNode node;
-
-    ret = newlinklist();
-    for (node = firstnode(l); node; incnode(node))
-	addlinknode(ret, func(getdata(node)));
-    return ret;
-}
-
-/**/
-char **
-duparray(char **arr, VFunc func)
-{
-    char **ret, **rr;
-
-    ret = (char **) alloc((arrlen(arr) + 1) * sizeof(char *));
-    for (rr = ret; *arr;)
-	*rr++ = (char *)func(*arr++);
-    *rr = NULL;
+    if (!l)
+	return NULL;
 
-    return ret;
+    return duplist(l, (VFunc) dupstruct);
 }
 
 /**/
-static char **
-list2arr(LinkList l, VFunc func)
+static LinkList
+duplist(LinkList l, VFunc func)
 {
-    char **arr, **r;
-    LinkNode n;
-
-    arr = r = (char **) alloc((countlinknodes(l) + 1) * sizeof(char *));
+    if (l && nonempty(l)) {
+	LinkList ret;
+	LinkNode node;
 
-    for (n = firstnode(l); n; incnode(n))
-	*r++ = (char *)func(getdata(n));
-    *r = NULL;
-
-    return arr;
+	ret = newlinklist();
+	for (node = firstnode(l); node; incnode(node))
+	    addlinknode(ret, func(getdata(node)));
+	return ret;
+    }
+    return NULL;
 }
 
 /**/
-static LinkList
-arr2list(char **arr, VFunc func)
+char **
+duparray(char **arr, VFunc func)
 {
-    LinkList l = newlinklist();
+    if (arr && *arr) {
+	char **ret, **rr, *p;
 
-    while (*arr)
-	addlinknode(l, func(*arr++));
+	ret = (char **) alloc((arrlen(arr) + 1) * sizeof(char *));
+	for (rr = ret; (p = *arr++);)
+	    *rr++ = (char *)func(p);
+	*rr = NULL;
 
-    return l;
+	return ret;
+    }
+    return NULL;
 }
 
 /**/
@@ -2384,28 +2144,6 @@ equalsplit(char *s, char **t)
     return 0;
 }
 
-/* see if the right side of a list is trivial */
-
-/**/
-void
-simplifyright(List l)
-{
-    Cmd c;
-
-    if (l == &dummy_list || !l->right)
-	return;
-    if (l->right->right || l->right->left->right ||
-	l->right->left->flags || l->right->left->left->right ||
-	l->left->flags)
-	return;
-    c = l->left->left->left;
-    if (c->type != SIMPLE || nonempty(c->args) || nonempty(c->redir)
-	|| nonempty(c->vars))
-	return;
-    l->right = NULL;
-    return;
-}
-
 /* the ztypes table */
 
 /**/
@@ -2470,6 +2208,25 @@ arrdup(char **s)
     return y;
 }
 
+/* Duplicate a list of strings. */
+
+/**/
+LinkList
+listdup(LinkList l)
+{
+    if (!l)
+	return NULL;
+    else {
+	LinkList r = newlinklist();
+	LinkNode n;
+
+	for (n = firstnode(l); n; incnode(n))
+	    addlinknode(r, dupstring((char *) getdata(n)));
+
+	return r;
+    }
+}
+
 /**/
 char **
 listarr(LinkList l)
diff --git a/Src/zsh.export b/Src/zsh.export
index ef9fca8c6..72779e24a 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -75,6 +75,7 @@ findcmd
 firsthist
 freearray
 freeheap
+freelinklist
 freestruct
 getaparam
 gethashnode
@@ -113,6 +114,7 @@ hist_skip_flags
 holdintr
 hptr
 hrealloc
+ifs
 inbufct
 incmdpos
 incond
@@ -179,7 +181,6 @@ path
 pathchecked
 popheap
 postedit
-pparams
 ppid
 prefork
 prepromptfns
diff --git a/Src/zsh.h b/Src/zsh.h
index 4d46beada..e7b6e5ae0 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -47,7 +47,11 @@
  */
 #ifdef ZSH_64_BIT_TYPE
 typedef ZSH_64_BIT_TYPE zlong;
-typedef unsigned ZSH_64_BIT_TYPE zulong;
+#ifdef ZSH_64_BIT_UTYPE
+typedef ZSH_64_BIT_UTYPE zulong;
+#else
+typedef unsigned zlong zulong;
+#endif
 #else
 typedef long zlong;
 typedef unsigned long zulong;
@@ -352,7 +356,6 @@ struct node {
 #define NT_N(T, N) (((T) >> (8 + (N) * 4)) & 0xf)
 #define NT_SET(T0, T1, T2, T3, T4) \
     ((T0) | ((T1) << 8) | ((T2) << 12) | ((T3) << 16) | ((T4) << 20))
-#define NT_HEAP   (1 << 30)
 
 /* tree element for lists */
 
@@ -605,7 +608,7 @@ struct job {
     pid_t gleader;		/* process group leader of this job  */
     pid_t other;		/* subjob id or subshell pid         */
     int stat;                   /* see STATs below                   */
-    char *pwd;			/* current working dir of shell when *
+    char pwd[PATH_MAX + 1];	/* current working dir of shell when *
 				 * this job was spawned              */
     struct process *procs;	/* list of processes                 */
     LinkList filelist;		/* list of files to delete when done */
@@ -948,7 +951,12 @@ struct param {
 #define PM_READONLY	(1<<8)	/* readonly                                   */
 #define PM_TAGGED	(1<<9)	/* tagged                                     */
 #define PM_EXPORTED	(1<<10)	/* exported                                   */
+
+/* The following are the same since they *
+ * both represent -U option to typeset   */
 #define PM_UNIQUE	(1<<11)	/* remove duplicates                          */
+#define PM_UNALIASED	(1<<11)	/* do not expand aliases when autoloading     */
+
 #define PM_TIED 	(1<<12)	/* array tied to colon-path or v.v. */
 #define PM_SPECIAL	(1<<13) /* special builtin parameter                  */
 #define PM_DONTIMPORT	(1<<14)	/* do not import this variable                */
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index bd9aabc2e..ef04658b1 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -31,7 +31,16 @@ version.h: $(sdir_top)/Config/version.mk
 zshpaths.h: FORCE Makemod
 	@echo '#define MODULE_DIR "'$(MODDIR)'"' > zshpaths.h.tmp
 	@if test x$(fndir) != xno; then \
-	  echo '#define FUNCTION_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
+	  echo '#define FPATH_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \
+	  if test x$(FUNCTIONS_SUBDIRS) != x -a \
+	  x$(FUNCTIONS_SUBDIRS) != xno; then \
+	    fpath_tmp="`for f in $$FUNCTIONS_INSTALL; do \
+	      echo $$f | sed s%/.*%%; \
+	    done | sort | uniq`"; \
+	    fpath_tmp="`echo $$fpath_tmp | sed 's/ /\", \"/g'`"; \
+	    echo "#define FPATH_SUBDIRS { \"$$fpath_tmp\" }" \
+	    >>zshpaths.h.tmp; \
+	  fi; \
 	fi
 	@if cmp -s zshpaths.h zshpaths.h.tmp; then \
 	    rm -f zshpaths.h.tmp; \