zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: parser (was: Re: PATCH: Improved _mailboxes)
@ 2000-02-23 13:21 Sven Wischnowsky
  2000-02-23 16:45 ` Bart Schaefer
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Sven Wischnowsky @ 2000-02-23 13:21 UTC (permalink / raw)
  To: zsh-workers


Peter Stephenson wrote:

> Frankly, as regards 3.1.* versions, my policy has generally been to put in
> pretty much anything that doesn't cause immediate and obvious problems
> straight away, rather than delaying it.  There's a big enough user base for
> *-dev-* that the bad problems get sorted out pretty quickly; and zsh is
> complicated enough that the less bad problems often need the exposure of
> the 3.1.x releases to show up at all.  So I shouldn't be shy.  As I said
> yesterday, things my end are going to run to a halt for a few weeks.

Well, then...


Here is the first of the two. As I said, this mainly makes the parser
create wordcode directly, no more extra compilation phase.


The other optimisations (no particular order):

- Lists and sublists that are executed completely in the shell (as
  found out by the parser) are marked and the execution code then
  avoids calling execpline() and execcmd() for them.
- Strings are tested if they contain tokens by the parser and if they
  have tokens a flag is stored for them. The execution code then tries 
  to avoid duplicating strings without tokens, expanding them, calling 
  singsub() on them and so on.
- The functions fetchvalue() and getvalue() now get a struct value
  pointer. If that is non-zero, these structs are used instead of
  allocating a new struct.
- execbuiltin() allocates the array of arguments on the stack.
- The pwd for jobs is only built when the $PWD changes. Otherwise
  there is just a NULL pointer in the job table.
- evalcond() is a bit less recursive (for && and ||). Just a bit of
  tail-recursion elimination.
- ecgetlist() uses a new function newsizedlist() to allocate a list
  with a known length at once (including all nodes).
- In some (very few) places lists are allocated on the stack instead
  of in heap memory. Most importantly in singsub() and multisub().
- In some (again, very few) places tests were copied from the
  functions called to the call sites (and only for functions that were 
  called very often but almost never passed the test).
- Some of the basic signal functions (like signal_block()) have been
  turned into macros if possible on the machine/OS type and there is a 
  signal-mask for the SIGCHLD signal calculated in init.c and
  otherwise just copied and used (saving lots of calls to
  signal_mask()).

(Some of these patches together can significantly speed up the
execution of things like the completion functions where a lot of
code is executed completely in the shell and the parser can find that
out. On the linux box I wrote this on I had to compile with -O[23] to
see the real effect, though.)


Ok. Tell me what you think of these. I can always remove some of the
optimisations if you think they are too weird.


Bye
 Sven

P.S.: If a day without a patch is like a day without sunshine, what
      does this say about a patch with 6136 lines?

diff -ru ../z.old/Src/Modules/parameter.c Src/Modules/parameter.c
--- ../z.old/Src/Modules/parameter.c	Wed Feb 23 09:02:55 2000
+++ Src/Modules/parameter.c	Wed Feb 23 14:00:06 2000
@@ -1318,7 +1318,7 @@
 {
     char *ret;
 
-    ret = dupstring(jobtab[job].pwd);
+    ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
     return ret;
 }
 
diff -ru ../z.old/Src/Zle/computil.c Src/Zle/computil.c
--- ../z.old/Src/Zle/computil.c	Wed Feb 23 09:02:52 2000
+++ Src/Zle/computil.c	Wed Feb 23 11:54:54 2000
@@ -2117,6 +2117,7 @@
 bin_compquote(char *nam, char **args, char *ops, int func)
 {
     char *name;
+    struct value vbuf;
     Value v;
 
     /* Anything to do? */
@@ -2128,7 +2129,7 @@
 
     while ((name = *args++)) {
 	name = dupstring(name);
-	if ((v = getvalue(&name, 0))) {
+	if ((v = getvalue(&vbuf, &name, 0))) {
 	    switch (PM_TYPE(v->pm->flags)) {
 	    case PM_SCALAR:
 		{
diff -ru ../z.old/Src/Zle/zle_main.c Src/Zle/zle_main.c
--- ../z.old/Src/Zle/zle_main.c	Wed Feb 23 09:02:53 2000
+++ Src/Zle/zle_main.c	Wed Feb 23 11:54:54 2000
@@ -732,6 +732,7 @@
 bin_vared(char *name, char **args, char *ops, int func)
 {
     char *s, *t, *ova = varedarg;
+    struct value vbuf;
     Value v;
     Param pm = 0;
     int create = 0, ifl;
@@ -809,7 +810,7 @@
     }
     /* handle non-existent parameter */
     s = args[0];
-    v = fetchvalue(&s, (!create || type == PM_SCALAR),
+    v = fetchvalue(&vbuf, &s, (!create || type == PM_SCALAR),
 		   SCANPM_WANTKEYS|SCANPM_WANTVALS|SCANPM_MATCHMANY);
     if (!v && !create) {
 	zwarnnam(name, "no such variable: %s", args[0], 0);
diff -ru ../z.old/Src/builtin.c Src/builtin.c
--- ../z.old/Src/builtin.c	Wed Feb 23 09:02:45 2000
+++ Src/builtin.c	Wed Feb 23 12:40:40 2000
@@ -209,7 +209,7 @@
 execbuiltin(LinkList args, Builtin bn)
 {
     LinkNode n;
-    char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr;
+    char ops[MAX_OPS], *arg, *pp, *name, *optstr;
     char *oxarg, *xarg = NULL;
     char typenumstr[] = TYPESET_OPTNUM;
     int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0;
@@ -330,37 +330,42 @@
 	while (n)
 	    argc++, incnode(n);
     }
-    /* Get the actual arguments, into argv.  Oargv saves the *
-     * beginning of the array for later reference.           */
-    oargv = argv = (char **)ncalloc(sizeof(char **) * (argc + 1));
-    if ((*argv++ = arg))
-	while ((*argv++ = (char *)ugetnode(args)));
-    argv = oargv;
-    if (errflag) {
-	errflag = 0;
-	return 1;
-    }
+    {
+	VARARR(char *, argarr, (argc + 1));
+	char **argv, **oargv;
+
+	/* Get the actual arguments, into argv.  Oargv saves the *
+	 * beginning of the array for later reference.           */
+	oargv = argv = argarr;
+	if ((*argv++ = arg))
+	    while ((*argv++ = (char *)ugetnode(args)));
+	argv = oargv;
+	if (errflag) {
+	    errflag = 0;
+	    return 1;
+	}
 
-    /* check that the argument count lies within the specified bounds */
-    if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
-	zwarnnam(name, (argc < bn->minargs)
-		? "not enough arguments" : "too many arguments", NULL, 0);
-	return 1;
-    }
+	/* check that the argument count lies within the specified bounds */
+	if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
+	    zwarnnam(name, (argc < bn->minargs)
+		     ? "not enough arguments" : "too many arguments", NULL, 0);
+	    return 1;
+	}
 
-    /* display execution trace information, if required */
-    if (xtr) {
-	printprompt4();
-	fprintf(xtrerr, "%s", name);
-	if (xarg)
-	    fprintf(xtrerr, " %s", xarg);
-	while (*oargv)
-	    fprintf(xtrerr, " %s", *oargv++);
-	fputc('\n', xtrerr);
-	fflush(xtrerr);
+	/* display execution trace information, if required */
+	if (xtr) {
+	    printprompt4();
+	    fprintf(xtrerr, "%s", name);
+	    if (xarg)
+		fprintf(xtrerr, " %s", xarg);
+	    while (*oargv)
+		fprintf(xtrerr, " %s", *oargv++);
+	    fputc('\n', xtrerr);
+	    fflush(xtrerr);
+	}
+	/* call the handler function, and return its return value */
+	return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
     }
-    /* call the handler function, and return its return value */
-    return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid);
 }
 
 /* Enable/disable an element in one of the internal hash tables.  *
@@ -704,12 +709,14 @@
     cd_new_pwd(func, dir);
 
     if (stat(unmeta(pwd), &st1) < 0) {
+	setjobpwd();
 	zsfree(pwd);
 	pwd = metafy(zgetcwd(), -1, META_DUP);
     } else if (stat(".", &st2) < 0)
 	chdir(unmeta(pwd));
     else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
 	if (chasinglinks) {
+	    setjobpwd();
 	    zsfree(pwd);
 	    pwd = metafy(zgetcwd(), -1, META_DUP);
 	} else {
@@ -1004,6 +1011,7 @@
     current (i.e. new) pwd */
     zsfree(oldpwd);
     oldpwd = pwd;
+    setjobpwd();
     pwd = new_pwd;
     set_pwd_env();
 
@@ -2154,7 +2162,7 @@
     p->pats = NULL;
     p->heap = 0;
 
-    p->prog[0] = WCB_LIST(Z_SYNC | Z_END);
+    p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0);
     p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3);
     p->prog[2] = WCB_PIPE(WC_PIPE_END, 0);
     p->prog[3] = WCB_AUTOFN();
diff -ru ../z.old/Src/cond.c Src/cond.c
--- ../z.old/Src/cond.c	Wed Feb 23 09:02:45 2000
+++ Src/cond.c	Wed Feb 23 11:54:55 2000
@@ -42,10 +42,17 @@
 evalcond(Estate state)
 {
     struct stat *st;
-    char *left, *right = NULL;
-    Wordcode pcode = state->pc++;
-    wordcode code = *pcode;
-    int ctype = WC_COND_TYPE(code);
+    char *left, *right;
+    Wordcode pcode;
+    wordcode code;
+    int ctype, htok = 0;
+
+ rec:
+
+    left = right = NULL;
+    pcode = state->pc++;
+    code = *pcode;
+    ctype = WC_COND_TYPE(code);
 
     switch (ctype) {
     case COND_NOT:
@@ -56,7 +63,7 @@
 	if (evalcond(state)) {
 	    if (tracingcond)
 		fprintf(xtrerr, " %s", condstr[ctype]);
-	    return evalcond(state);
+	    goto rec;
 	} else {
 	    state->pc = pcode + (WC_COND_SKIP(code) + 1);
 	    return 0;
@@ -65,7 +72,7 @@
 	if (!evalcond(state)) {
 	    if (tracingcond)
 		fprintf(xtrerr, " %s", condstr[ctype]);
-	    return evalcond(state);
+	    goto rec;
 	} else {
 	    state->pc = pcode + (WC_COND_SKIP(code) + 1);
 	    return 1;
@@ -74,16 +81,16 @@
     case COND_MODI:
 	{
 	    Conddef cd;
-	    char *name = ecgetstr(state, 0), **strs;
+	    char *name = ecgetstr(state, EC_NODUP, NULL), **strs;
 	    int l = WC_COND_SKIP(code);
 
 	    if (ctype == COND_MOD)
-		strs = ecgetarr(state, l, 1);
+		strs = ecgetarr(state, l, EC_DUP, NULL);
 	    else {
 		char *sbuf[3];
 
-		sbuf[0] = ecgetstr(state, 0);
-		sbuf[1] = ecgetstr(state, 0);
+		sbuf[0] = ecgetstr(state, EC_NODUP, NULL);
+		sbuf[1] = ecgetstr(state, EC_NODUP, NULL);
 		sbuf[2] = NULL;
 
 		strs = arrdup(sbuf);
@@ -120,19 +127,23 @@
 	    return 0;
 	}
     }
-    left = ecgetstr(state, 1);
-    singsub(&left);
-    untokenize(left);
+    left = ecgetstr(state, EC_DUPTOK, &htok);
+    if (htok) {
+	singsub(&left);
+	untokenize(left);
+    }
     if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
-	right = ecgetstr(state, 1);
-	singsub(&right);
-	untokenize(right);
+	right = ecgetstr(state, EC_DUPTOK, &htok);
+	if (htok) {
+	    singsub(&right);
+	    untokenize(right);
+	}
     }
     if (tracingcond) {
 	if (ctype < COND_MOD) {
 	    char *rt = (char *) right;
 	    if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
-		rt = dupstring(ecrawstr(state->prog, state->pc));
+		rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
 		singsub(&rt);
 		untokenize(rt);
 	    }
@@ -191,8 +202,10 @@
 		char *opat;
 		int save;
 
-		right = opat = dupstring(ecrawstr(state->prog, state->pc));
-		singsub(&right);
+		right = opat = dupstring(ecrawstr(state->prog, state->pc,
+						  &htok));
+		if (htok)
+		    singsub(&right);
 		save = (!state->prog->heap &&
 			!strcmp(opat, right) && pprog != dummy_patprog2);
 
@@ -370,10 +383,11 @@
 {
     char *s = args[num];
 
-    singsub(&s);
-    if (!raw)
-	untokenize(s);
-
+    if (has_token(s)) {
+	singsub(&s);
+	if (!raw)
+	    untokenize(s);
+    }
     return s;
 }
 
@@ -383,9 +397,10 @@
 {
     char *s = args[num];
 
-    singsub(&s);
-    untokenize(s);
-
+    if (has_token(s)) {
+	singsub(&s);
+	untokenize(s);
+    }
     return mathevali(s);
 }
 
diff -ru ../z.old/Src/exec.c Src/exec.c
--- ../z.old/Src/exec.c	Wed Feb 23 09:02:45 2000
+++ Src/exec.c	Wed Feb 23 14:08:48 2000
@@ -134,6 +134,14 @@
 static LinkList args;
 static int doneps4;
 
+/* Execution functions. */
+
+static int (*execfuncs[]) _((Estate, int)) = {
+    execcursh, exectime, execfuncdef, execfor, execselect,
+    execwhile, execrepeat, execcase, execif, execcond,
+    execarith, execautofn
+};
+
 /* parse string into a list */
 
 /**/
@@ -713,6 +721,33 @@
     execlist(&s, dont_change_job, exiting);
 }
 
+/* Execute a simplified command. This is used to execute things that
+ * will run completely in the shell, so that we can by-pass all that
+ * nasty job-handling and redirection stuff in execpline and execcmd. */
+
+/**/
+static int
+execsimple(Estate state)
+{
+    wordcode code = *state->pc++;
+
+    if (code)
+	lineno = code - 1;
+
+    code = wc_code(*state->pc++);
+
+    if (code == WC_ASSIGN) {
+	cmdoutval = 0;
+	addvars(state, state->pc - 1, 0);
+	if (isset(XTRACE)) {
+	    fputc('\n', xtrerr);
+	    fflush(xtrerr);
+	}
+	return (lastval = (errflag ? errflag : cmdoutval));
+    } else
+	return (lastval = (execfuncs[code - WC_CURSH])(state, 0));
+}
+
 /* Main routine for executing a list.                                *
  * exiting means that the (sub)shell we are in is a definite goner   *
  * after the current list is finished, so we may be able to exec the *
@@ -749,11 +784,18 @@
      * semi-colon or ampersand (`sublists').               */
     code = *state->pc++;
     while (wc_code(code) == WC_LIST && !breaks && !retflag) {
+	ltype = WC_LIST_TYPE(code);
+	csp = cmdsp;
+
+	if (ltype & Z_SIMPLE) {
+	    next = state->pc + WC_LIST_SKIP(code);
+	    execsimple(state);
+	    state->pc = next;
+	    goto sublist_done;
+	}
 	/* Reset donetrap:  this ensures that a trap is only *
 	 * called once for each sublist that fails.          */
 	donetrap = 0;
-	csp = cmdsp;
-	ltype = WC_LIST_TYPE(code);
 
 	/* Loop through code followed by &&, ||, or end of sublist. */
 	code = *state->pc++;
@@ -764,14 +806,19 @@
 	    switch (WC_SUBLIST_TYPE(code)) {
 	    case WC_SUBLIST_END:
 		/* End of sublist; just execute, ignoring status. */
-		execpline(state, code, ltype, (ltype & Z_END) && exiting);
+		if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE)
+		    execsimple(state);
+		else
+		    execpline(state, code, ltype, (ltype & Z_END) && exiting);
 		state->pc = next;
 		goto sublist_done;
 		break;
 	    case WC_SUBLIST_AND:
 		/* If the return code is non-zero, we skip pipelines until *
 		 * we find a sublist followed by ORNEXT.                   */
-		if ((ret = execpline(state, code, Z_SYNC, 0))) {
+		if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
+			    execsimple(state) :
+			    execpline(state, code, Z_SYNC, 0)))) {
 		    state->pc = next;
 		    code = *state->pc++;
 		    next = state->pc + WC_SUBLIST_SKIP(code);
@@ -794,7 +841,9 @@
 	    case WC_SUBLIST_OR:
 		/* If the return code is zero, we skip pipelines until *
 		 * we find a sublist followed by ANDNEXT.              */
-		if (!(ret = execpline(state, code, Z_SYNC, 0))) {
+		if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ?
+			     execsimple(state) :
+			     execpline(state, code, Z_SYNC, 0)))) {
 		    state->pc = next;
 		    code = *state->pc++;
 		    next = state->pc + WC_SUBLIST_SKIP(code);
@@ -1190,6 +1239,7 @@
     /* A bigger argv is necessary for executing scripts */
     ptr = argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) *
 				       sizeof(char *));
+
     if (isset(XTRACE)) {
 	if (!doneps4)
 	    printprompt4();
@@ -1390,10 +1440,11 @@
 addvars(Estate state, Wordcode pc, int export)
 {
     LinkList vl;
-    int xtr, isstr;
+    int xtr, isstr, htok = 0;
     char **arr, **ptr, *name;
     Wordcode opc = state->pc;
     wordcode ac;
+    local_list1(svl);
 
     xtr = isset(XTRACE);
     if (xtr) {
@@ -1402,17 +1453,18 @@
     }
     state->pc = pc;
     while (wc_code(ac = *state->pc++) == WC_ASSIGN) {
-	name = ecgetstr(state, 1);
-	untokenize(name);
+	name = ecgetstr(state, EC_DUPTOK, &htok);
+	if (htok)
+	    untokenize(name);
 	if (xtr)
 	    fprintf(xtrerr, "%s=", name);
 	if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) {
-	    vl = newlinklist();
-	    addlinknode(vl, ecgetstr(state, 1));
+	    init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok));
+	    vl = &svl;
 	} else
-	    vl = ecgetlist(state, WC_ASSIGN_NUM(ac), 1);
+	    vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok);
 
-	if (vl) {
+	if (vl && htok) {
 	    prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) :
 			 PF_ASSIGN));
 	    if (errflag) {
@@ -1490,17 +1542,19 @@
 setunderscore(char *str)
 {
     if (str && *str) {
-	int l = strlen(str) + 1;
+	int l = strlen(str) + 1, nl = (l + 31) & ~31;
 
-	if (l > underscorelen || l < (underscorelen >> 2)) {
+	if (nl > underscorelen || (underscorelen - nl) > 64) {
 	    zfree(underscore, underscorelen);
-	    underscore = (char *) zalloc(underscorelen = l);
+	    underscore = (char *) zalloc(underscorelen = nl);
 	}
 	strcpy(underscore, str);
 	underscoreused = l;
     } else {
-	zfree(underscore, underscorelen);
-	underscore = (char *) zalloc(underscorelen = 32);
+	if (underscorelen > 128) {
+	    zfree(underscore, underscorelen);
+	    underscore = (char *) zalloc(underscorelen = 32);
+	}
 	*underscore = '\0';
 	underscoreused = 1;
     }
@@ -1537,7 +1591,7 @@
     struct multio *mfds[10];
     char *text;
     int save[10];
-    int fil, dfil, is_cursh, type, do_exec = 0, i;
+    int fil, dfil, is_cursh, type, do_exec = 0, i, htok = 0;
     int nullexec = 0, assign = 0, forked = 0;
     int is_shfunc = 0, is_builtin = 0, is_exec = 0;
     /* Various flags to the command. */
@@ -1560,8 +1614,11 @@
 
     type = wc_code(code);
 
+    /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
+     * But for that we would need to check/change all builtins so that
+     * they don't modify their argument strings. */
     args = (type == WC_SIMPLE ?
-	    ecgetlist(state, WC_SIMPLE_ARGC(code), 1) : NULL);
+	    ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
 
     for (i = 0; i < 10; i++) {
 	save[i] = -2;
@@ -1633,7 +1690,7 @@
 
     /* Do prefork substitutions */
     esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0;
-    if (args)
+    if (args && htok)
 	prefork(args, esprefork);
 
     if (type == WC_SIMPLE) {
@@ -1890,7 +1947,7 @@
 		wordcode ac;
 
 		while (wc_code(ac = *p) == WC_ASSIGN) {
-		    if (!strcmp(ecrawstr(state->prog, p + 1), "STTY")) {
+		    if (!strcmp(ecrawstr(state->prog, p + 1, NULL), "STTY")) {
 			jobtab[thisjob].stty_in_env = 1;
 			break;
 		    }
@@ -1926,7 +1983,7 @@
 	is_exec = 1;
     }
 
-    if ((esglob = !(cflags & BINF_NOGLOB)) && args) {
+    if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
 	LinkList oargs = args;
 	globlist(args, 0);
 	args = oargs;
@@ -2087,7 +2144,8 @@
     /* We are done with redirection.  close the mnodes, *
      * spawning tee/cat processes as necessary.         */
     for (i = 0; i < 10; i++)
-	closemn(mfds, i);
+	if (mfds[i] && mfds[i]->ct >= 2)
+	    closemn(mfds, i);
 
     if (nullexec) {
 	if (nullexec == 1) {
@@ -2120,15 +2178,9 @@
 	if (is_exec)
 	    entersubsh(how, (type != WC_SUBSH) ? 2 : 1, 1);
 	if (type >= WC_CURSH) {
-	    static int (*func[]) _((Estate, int)) = {
-		execcursh, exectime, execfuncdef, execfor, execselect,
-		execwhile, execrepeat, execcase, execif, execcond,
-		execarith, execautofn
-	    };
-
 	    if (last1 == 1)
 		do_exec = 1;
-	    lastval = (func[type - WC_CURSH])(state, do_exec);
+	    lastval = (execfuncs[type - WC_CURSH])(state, do_exec);
 	} else if (is_builtin || is_shfunc) {
 	    LinkList restorelist = 0, removelist = 0;
 	    /* builtin or shell function */
@@ -2136,16 +2188,20 @@
 	    if (!forked && ((cflags & BINF_COMMAND) ||
 			    (unset(POSIXBUILTINS) && !assign) ||
 			    (isset(POSIXBUILTINS) && !is_shfunc &&
-			     !(hn->flags & BINF_PSPECIAL))))
-		save_params(state, varspc, &restorelist, &removelist);
-
+			     !(hn->flags & BINF_PSPECIAL)))) {
+		if (varspc)
+		    save_params(state, varspc, &restorelist, &removelist);
+		else
+		    restorelist = removelist = NULL;
+	    }
 	    if (varspc) {
 		/* Export this if the command is a shell function,
 		 * but not if it's a builtin.
 		 */
 		addvars(state, varspc, is_shfunc);
 		if (errflag) {
-		    restore_params(restorelist, removelist);
+		    if (restorelist)
+			restore_params(restorelist, removelist);
 		    lastval = 1;
 		    fixfds(save);
 		    goto done;
@@ -2204,7 +2260,8 @@
 		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 		exit(lastval);
 	    }
-	    restore_params(restorelist, removelist);
+	    if (restorelist)
+		restore_params(restorelist, removelist);
 
 	} else {
 	    if (!forked)
@@ -2271,15 +2328,11 @@
 
     MUSTUSEHEAP("save_params()");
     
-    if (!pc) {
-	*restore_p = *remove_p = NULL;
-	return;
-    }
     *restore_p = newlinklist();
     *remove_p = newlinklist();
 
     while (wc_code(ac = *pc) == WC_ASSIGN) {
-	s = ecrawstr(state->prog, pc + 1);
+	s = ecrawstr(state->prog, pc + 1, NULL);
 	if ((pm = (Param) paramtab->getnode(paramtab, s))) {
 	    if (!(pm->flags & PM_SPECIAL)) {
 		paramtab->removenode(paramtab, s);
@@ -2309,14 +2362,12 @@
     Param pm;
     char *s;
 
-    if (removelist) {
-	/* remove temporary parameters */
-	while ((s = (char *) ugetnode(removelist))) {
-	    if ((pm = (Param) paramtab->getnode(paramtab, s)) &&
-		!(pm->flags & PM_SPECIAL)) {
-		pm->flags &= ~PM_READONLY;
-		unsetparam_pm(pm, 0, 0);
-	    }
+    /* remove temporary parameters */
+    while ((s = (char *) ugetnode(removelist))) {
+	if ((pm = (Param) paramtab->getnode(paramtab, s)) &&
+	    !(pm->flags & PM_SPECIAL)) {
+	    pm->flags &= ~PM_READONLY;
+	    unsetparam_pm(pm, 0, 0);
 	}
     }
 
@@ -2560,7 +2611,7 @@
 	wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) {
 	/* $(< word) */
 	int stream;
-	char *s = dupstring(ecrawstr(prog, pc + 5));
+	char *s = dupstring(ecrawstr(prog, pc + 5, NULL));
 
 	singsub(&s);
 	if (errflag)
@@ -2908,13 +2959,15 @@
 {
     char *e;
     zlong val = 0;
+    int htok = 0;
 
     if (isset(XTRACE)) {
 	printprompt4();
 	fprintf(xtrerr, "((");
     }
-    e = ecgetstr(state, 1);
-    singsub(&e);
+    e = ecgetstr(state, EC_DUPTOK, &htok);
+    if (htok)
+	singsub(&e);
     if (isset(XTRACE))
 	fprintf(xtrerr, " %s", e);
 
@@ -2954,21 +3007,22 @@
 {
     Shfunc shf;
     char *s;
-    int signum, nprg, npats, len, plen, i;
+    int signum, nprg, npats, len, plen, i, htok = 0;
     Wordcode beg = state->pc, end;
     Eprog prog;
     Patprog *pp;
     LinkList names;
 
     end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
-    names = ecgetlist(state, *state->pc++, 1);
+    names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
     nprg = *state->pc++ - 4;
     npats = *state->pc++;
 
     plen = (end - state->pc) * sizeof(wordcode);
     len = plen + (npats * sizeof(Patprog));
 
-    execsubst(names);
+    if (htok)
+	execsubst(names);
 
     PERMALLOC {
 	while ((s = (char *) ugetnode(names))) {
@@ -3346,7 +3400,7 @@
 	return prog;
     code = *pc++;
     if (wc_code(code) != WC_FUNCDEF ||
-	*pc != 1 || strcmp(name, ecrawstr(prog, pc + 1)))
+	*pc != 1 || strcmp(name, ecrawstr(prog, pc + 1, NULL)))
 	return prog;
 
     {
diff -ru ../z.old/Src/glob.c Src/glob.c
--- ../z.old/Src/glob.c	Wed Feb 23 09:02:45 2000
+++ Src/glob.c	Wed Feb 23 11:54:56 2000
@@ -1482,15 +1482,15 @@
     if (isset(BRACECCL)) {
 	/* In this case, any properly formed brace expression  *
 	 * will match and expand to the characters in between. */
-	int bc;
+	int bc, c;
 
-	for (bc = 0; *str; ++str)
-	    if (*str == Inbrace) {
+	for (bc = 0; (c = *str); ++str)
+	    if (c == Inbrace) {
 		if (!bc && str[1] == Outbrace)
 		    *str++ = '{', *str = '}';
 		else
 		    bc++;
-	    } else if (*str == Outbrace) {
+	    } else if (c == Outbrace) {
 		if (!bc)
 		    *str = '}';
 		else if (!--bc)
@@ -1568,24 +1568,23 @@
 int
 xpandredir(struct redir *fn, LinkList tab)
 {
-    LinkList fake;
     char *nam;
     struct redir *ff;
     int ret = 0;
+    local_list1(fake);
 
     /* Stick the name in a list... */
-    fake = newlinklist();
-    addlinknode(fake, fn->name);
+    init_list1(fake, fn->name);
     /* ...which undergoes all the usual shell expansions */
-    prefork(fake, isset(MULTIOS) ? 0 : PF_SINGLE);
+    prefork(&fake, isset(MULTIOS) ? 0 : PF_SINGLE);
     /* Globbing is only done for multios. */
     if (!errflag && isset(MULTIOS))
-	globlist(fake, 0);
+	globlist(&fake, 0);
     if (errflag)
 	return 0;
-    if (nonempty(fake) && !nextnode(firstnode(fake))) {
+    if (nonempty(&fake) && !nextnode(firstnode(&fake))) {
 	/* Just one match, the usual case. */
-	char *s = peekfirst(fake);
+	char *s = peekfirst(&fake);
 	fn->name = s;
 	untokenize(s);
 	if (fn->type == MERGEIN || fn->type == MERGEOUT) {
@@ -1609,7 +1608,7 @@
     else {
 	if (fn->type == MERGEOUT)
 	    fn->type = ERRWRITE;
-	while ((nam = (char *)ugetnode(fake))) {
+	while ((nam = (char *)ugetnode(&fake))) {
 	    /* Loop over matches, duplicating the *
 	     * redirection for each file found.   */
 	    ff = (struct redir *)alloc(sizeof *ff);
diff -ru ../z.old/Src/hashtable.c Src/hashtable.c
--- ../z.old/Src/hashtable.c	Wed Feb 23 09:02:45 2000
+++ Src/hashtable.c	Wed Feb 23 11:54:56 2000
@@ -80,10 +80,10 @@
 mod_export unsigned
 hasher(char *str)
 {
-    unsigned hashval = 0;
+    unsigned hashval = 0, c;
 
-    while (*str)
-	hashval += (hashval << 5) + *(unsigned char *)str++;
+    while ((c = *((unsigned char *) str++)))
+	hashval += (hashval << 5) + c;
 
     return hashval;
 }
diff -ru ../z.old/Src/init.c Src/init.c
--- ../z.old/Src/init.c	Wed Feb 23 09:02:46 2000
+++ Src/init.c	Wed Feb 23 14:12:42 2000
@@ -82,6 +82,11 @@
 /**/
 mod_export int (*getkeyptr) _((int));
 
+/* SIGCHLD mask */
+
+/**/
+mod_export sigset_t sigchld_mask;
+
 #ifdef DEBUG
 /* depth of allocation type stack */
 
@@ -761,6 +766,8 @@
 void
 init_signals(void)
 {
+    sigchld_mask = signal_mask(SIGCHLD);
+
     intr();
 
 #ifndef QDEBUG
diff -ru ../z.old/Src/jobs.c Src/jobs.c
--- ../z.old/Src/jobs.c	Wed Feb 23 09:02:46 2000
+++ Src/jobs.c	Wed Feb 23 11:54:57 2000
@@ -54,7 +54,7 @@
  
 /**/
 mod_export struct job jobtab[MAXJOB];
- 
+
 /* shell timings */
  
 /**/
@@ -556,6 +556,7 @@
 static int
 should_report_time(Job j)
 {
+    struct value vbuf;
     Value v;
     char *s = "REPORTTIME";
     int reporttime;
@@ -565,7 +566,8 @@
 	return 1;
 
     HEAPALLOC {
-	if (!(v = getvalue(&s, 0)) || (reporttime = getintvalue(v)) < 0) {
+	if (!(v = getvalue(&vbuf, &s, 0)) ||
+	    (reporttime = getintvalue(v)) < 0) {
 	    LASTALLOC_RETURN 0;
 	}
     } LASTALLOC;
@@ -717,7 +719,8 @@
  * the directory where the job is running, otherwise the current directory
  */
 
-    if ((lng & 4) || (interact && job == thisjob && strcmp(jn->pwd, pwd))) {
+    if ((lng & 4) || (interact && job == thisjob &&
+		      jn->pwd && strcmp(jn->pwd, pwd))) {
 	fprintf(shout, "(pwd %s: ", (lng & 4) ? "" : "now");
 	fprintdir((lng & 4) ? jn->pwd : pwd, shout);
 	fprintf(shout, ")\n");
@@ -774,7 +777,9 @@
 
     if (jn->ty)
 	zfree(jn->ty, sizeof(struct ttyinfo));
-
+    if (jn->pwd)
+	zsfree(jn->pwd);
+    jn->pwd = NULL;
     if (jn->stat & STAT_WASSUPER)
 	deletejob(jobtab + jn->other);
     jn->gleader = jn->other = 0;
@@ -945,11 +950,8 @@
     for (i = 1; i < MAXJOB; i++)
 	if (!jobtab[i].stat) {
 	    jobtab[i].stat = STAT_INUSE;
-	    if (strlen(pwd) >= PATH_MAX) {
-		memcpy(jobtab[i].pwd, pwd, PATH_MAX);
-		jobtab[i].pwd[PATH_MAX] = '\0';
-	    } else
-		strcpy(jobtab[i].pwd, pwd);
+	    if (jobtab[i].pwd)
+		zsfree(jobtab[i].pwd);
 	    jobtab[i].gleader = 0;
 	    return i;
 	}
@@ -958,6 +960,21 @@
     return -1;
 }
 
+/**/
+void
+setjobpwd(void)
+{
+    int i, l;
+
+    for (i = 1; i < MAXJOB; i++)
+	if (jobtab[i].stat && !jobtab[i].pwd) {
+	    if ((l = strlen(pwd)) >= PATH_MAX)
+		jobtab[i].pwd = ztrdup(pwd + l - PATH_MAX);
+	    else
+		jobtab[i].pwd = ztrdup(pwd);
+	}
+}
+
 /* print pids for & */
 
 /**/
@@ -1302,7 +1319,7 @@
 		/* for bg and fg -- show the job we are operating on */
 		printjob(jobtab + job, (stopped) ? -1 : 0, 1);
 	    if (func != BIN_BG) {		/* fg or wait */
-		if (strcmp(jobtab[job].pwd, pwd)) {
+		if (jobtab[job].pwd && strcmp(jobtab[job].pwd, pwd)) {
 		    fprintf(shout, "(pwd : ");
 		    fprintdir(jobtab[job].pwd, shout);
 		    fprintf(shout, ")\n");
diff -ru ../z.old/Src/lex.c Src/lex.c
--- ../z.old/Src/lex.c	Wed Feb 23 09:02:46 2000
+++ Src/lex.c	Wed Feb 23 11:54:57 2000
@@ -191,6 +191,11 @@
     void (*hwend) _((void));
     void (*addtoline) _((int));
 
+    int eclen, ecused, ecfree, ecnpats;
+    Wordcode ecbuf;
+    Eccstr ecstrs;
+    int ecsoffs;
+
     unsigned char *cstack;
     int csp;
 };
@@ -243,6 +248,13 @@
     ls->hwbegin = hwbegin;
     ls->hwend = hwend;
     ls->addtoline = addtoline;
+    ls->eclen = eclen;
+    ls->ecused = ecused;
+    ls->ecfree = ecfree;
+    ls->ecnpats = ecnpats;
+    ls->ecbuf = ecbuf;
+    ls->ecstrs = ecstrs;
+    ls->ecsoffs = ecsoffs;
     cmdsp = 0;
     inredir = 0;
     hdocs = NULL;
@@ -295,6 +307,13 @@
     hwbegin = lstack->hwbegin;
     hwend = lstack->hwend;
     addtoline = lstack->addtoline;
+    eclen = lstack->eclen;
+    ecused = lstack->ecused;
+    ecfree = lstack->ecfree;
+    ecnpats = lstack->ecnpats;
+    ecbuf = lstack->ecbuf;
+    ecstrs = lstack->ecstrs;
+    ecsoffs = lstack->ecsoffs;
     hlinesz = lstack->hlinesz;
     errflag = 0;
 
@@ -315,15 +334,17 @@
     if (tok == NEWLIN || tok == ENDINPUT) {
 	while (hdocs) {
 	    struct heredocs *next = hdocs->next;
+	    char *name;
 
 	    hwbegin(0);
-	    cmdpush(hdocs->rd->type == HEREDOC ? CS_HEREDOC : CS_HEREDOCD);
+	    cmdpush(WC_REDIR_TYPE(*(hdocs->pc)) == HEREDOC ?
+		    CS_HEREDOC : CS_HEREDOCD);
 	    STOPHIST
-	    hdocs->rd->name = gethere(hdocs->rd->name, hdocs->rd->type);
+	    name = gethere(hdocs->str, WC_REDIR_TYPE(*hdocs->pc));
 	    ALLOWHIST
 	    cmdpop();
 	    hwend();
-	    hdocs->rd->type = HERESTR;
+	    setheredoc(hdocs->pc, HERESTR, name);
 	    zfree(hdocs, sizeof(struct heredocs));
 	    hdocs = next;
 	}
@@ -1458,52 +1479,62 @@
 	yytext = tokstrings[tok];
 
 	return 0;
-    }
-
-    if (has_token(tokstr)) {
-	char *p, *t;
-
-	yytext = p = ncalloc(strlen(tokstr) + 1);
-	for (t = tokstr; (*p++ = itok(*t) ? ztokens[*t++ - Pound] : *t++););
-    } else
-	yytext = tokstr;
+    } else {
+	VARARR(char, copy, (strlen(tokstr) + 1));
 
-    if (zleparse && !(inbufflags & INP_ALIAS)) {
-	int zp = zleparse;
+	if (has_token(tokstr)) {
+	    char *p, *t;
 
-	gotword();
-	if (zp == 1 && !zleparse) {
-	    return 0;
+	    yytext = p = copy;
+	    for (t = tokstr;
+		 (*p++ = itok(*t) ? ztokens[*t++ - Pound] : *t++););
+	} else
+	    yytext = tokstr;
+
+	if (zleparse && !(inbufflags & INP_ALIAS)) {
+	    int zp = zleparse;
+
+	    gotword();
+	    if (zp == 1 && !zleparse) {
+		if (yytext == copy)
+		    yytext = tokstr;
+		return 0;
+	    }
 	}
-    }
 
-    if (tok == STRING) {
-	/* Check for an alias */
-	an = noaliases ? NULL : (Alias) aliastab->getnode(aliastab, yytext);
-	if (an && !an->inuse && ((an->flags & ALIAS_GLOBAL) || incmdpos ||
-	     inalmore)) {
-	    inpush(an->text, INP_ALIAS, an);
-	    /* remove from history if it begins with space */
-	    if (isset(HISTIGNORESPACE) && an->text[0] == ' ')
-		remhist();
-	    lexstop = 0;
-	    return 1;
+	if (tok == STRING) {
+	    /* Check for an alias */
+	    an = noaliases ? NULL :
+		(Alias) aliastab->getnode(aliastab, yytext);
+	    if (an && !an->inuse && ((an->flags & ALIAS_GLOBAL) || incmdpos ||
+				     inalmore)) {
+		inpush(an->text, INP_ALIAS, an);
+		/* remove from history if it begins with space */
+		if (isset(HISTIGNORESPACE) && an->text[0] == ' ')
+		    remhist();
+		lexstop = 0;
+		if (yytext == copy)
+		    yytext = tokstr;
+		return 1;
+	    }
+
+	    /* Then check for a reserved word */
+	    if ((incmdpos ||
+		 (unset(IGNOREBRACES) && yytext[0] == '}' && !yytext[1])) &&
+		(rw = (Reswd) reswdtab->getnode(reswdtab, yytext))) {
+		tok = rw->token;
+		if (tok == DINBRACK)
+		    incond = 1;
+	    } else if (incond && !strcmp(yytext, "]]")) {
+		tok = DOUTBRACK;
+		incond = 0;
+	    } else if (incond && yytext[0] == '!' && !yytext[1])
+		tok = BANG;
 	}
-
-	/* Then check for a reserved word */
-	if ((incmdpos ||
-	     (unset(IGNOREBRACES) && yytext[0] == '}' && !yytext[1])) &&
-	    (rw = (Reswd) reswdtab->getnode(reswdtab, yytext))) {
-	    tok = rw->token;
-	    if (tok == DINBRACK)
-		incond = 1;
-	} else if (incond && !strcmp(yytext, "]]")) {
-	    tok = DOUTBRACK;
-	    incond = 0;
-	} else if (incond && yytext[0] == '!' && !yytext[1])
-	    tok = BANG;
+	inalmore = 0;
+	if (yytext == copy)
+	    yytext = tokstr;
     }
-    inalmore = 0;
     return 0;
 }
 
diff -ru ../z.old/Src/linklist.c Src/linklist.c
--- ../z.old/Src/linklist.c	Wed Feb 23 09:02:46 2000
+++ Src/linklist.c	Wed Feb 23 11:54:57 2000
@@ -220,3 +220,26 @@
     l->last->next = 0;
 }
 
+/**/
+LinkList
+newsizedlist(int size)
+{
+    LinkList list;
+    LinkNode node;
+
+    MUSTUSEHEAP("newsizedlist()");
+
+    list = (LinkList) zhalloc(sizeof(struct linklist) +
+			      (size * sizeof(struct linknode)));
+
+    list->first = (LinkNode) (list + 1);
+    for (node = list->first; size; size--, node++) {
+	node->last = node - 1;
+	node->next = node + 1;
+    }
+    list->last = node - 1;
+    list->first->last = (LinkNode) list;
+    node[-1].next = NULL;
+
+    return list;
+}
diff -ru ../z.old/Src/loop.c Src/loop.c
--- ../z.old/Src/loop.c	Wed Feb 23 09:02:47 2000
+++ Src/loop.c	Wed Feb 23 11:54:57 2000
@@ -51,12 +51,12 @@
 {
     Wordcode end, loop;
     wordcode code = state->pc[-1];
-    int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND);
+    int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0;
     char *name, *str, *cond = NULL, *advance = NULL;
     zlong val = 0;
     LinkList args = NULL;
 
-    name = ecgetstr(state, 0);
+    name = ecgetstr(state, EC_NODUP, NULL);
     end = state->pc + WC_FOR_SKIP(code);
 
     if (iscond) {
@@ -75,14 +75,17 @@
 	    state->pc = end;
 	    return lastval = errflag;
 	}
-	cond = ecgetstr(state, 0);
-	advance = ecgetstr(state, 0);
+	cond = ecgetstr(state, EC_NODUP, &ctok);
+	advance = ecgetstr(state, EC_NODUP, &atok);
     } else if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
-	if (!(args = ecgetlist(state, *state->pc++, 1))) {
+	int htok = 0;
+
+	if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
 	    state->pc = end;
 	    return 0;
 	}
-	execsubst(args);
+	if (htok)
+	    execsubst(args);
     } else {
 	char **x;
 
@@ -97,8 +100,11 @@
     loop = state->pc;
     for (;;) {
 	if (iscond) {
-	    str = dupstring(cond);
-	    singsub(&str);
+	    if (ctok) {
+		str = dupstring(cond);
+		singsub(&str);
+	    } else
+		str = cond;
 	    if (!errflag) {
 		while (iblank(*str))
 		    str++;
@@ -141,13 +147,16 @@
 	if (retflag)
 	    break;
 	if (iscond && !errflag) {
-	    str = dupstring(advance);
+	    if (atok) {
+		str = dupstring(advance);
+		singsub(&str);
+	    } else
+		str = advance;
 	    if (isset(XTRACE)) {
 		printprompt4();
 		fprintf(xtrerr, "%s\n", str);
 		fflush(xtrerr);
 	    }
-	    singsub(&str);
 	    if (!errflag)
 		matheval(str);
 	}
@@ -179,7 +188,7 @@
     LinkList args;
 
     end = state->pc + WC_FOR_SKIP(code);
-    name = ecgetstr(state, 0);
+    name = ecgetstr(state, EC_NODUP, NULL);
 
     if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) {
 	char **x;
@@ -188,11 +197,14 @@
 	for (x = pparams; *x; x++)
 	    addlinknode(args, dupstring(*x));
     } else {
-	if (!(args = ecgetlist(state, *state->pc++, 1))) {
+	int htok = 0;
+
+	if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
 	    state->pc = end;
 	    return 0;
 	}
-	execsubst(args);
+	if (htok)
+	    execsubst(args);
     }
     if (!args || empty(args)) {
 	state->pc = end;
@@ -391,14 +403,15 @@
 {
     Wordcode end, loop;
     wordcode code = state->pc[-1];
-    int count;
+    int count, htok = 0;
     char *tmp;
 
     end = state->pc + WC_REPEAT_SKIP(code);
 
     lastval = 0;
-    tmp = ecgetstr(state, 1);
-    singsub(&tmp);
+    tmp = ecgetstr(state, EC_DUPTOK, &htok);
+    if (htok)
+	singsub(&tmp);
     count = atoi(tmp);
     pushheap();
     cmdpush(CS_REPEAT);
@@ -487,7 +500,7 @@
 
     end = state->pc + WC_CASE_SKIP(code);
 
-    word = ecgetstr(state, 1);
+    word = ecgetstr(state, EC_DUP, NULL);
     singsub(&word);
     untokenize(word);
     lastval = 0;
@@ -509,7 +522,7 @@
 	if (isset(XTRACE)) {
 	    char *pat2, *opat;
 
-	    opat = pat = ecgetstr(state, 1);
+	    opat = pat = ecgetstr(state, EC_DUP, NULL);
 	    singsub(&pat);
 	    save = (!state->prog->heap &&
 		    !strcmp(pat, opat) && *spprog != dummy_patprog2);
@@ -529,9 +542,12 @@
 	if (!pprog) {
 	    if (!pat) {
 		char *opat;
+		int htok = 0;
 
-		opat = pat = dupstring(ecrawstr(state->prog, state->pc - 2));
-		singsub(&pat);
+		opat = pat = dupstring(ecrawstr(state->prog,
+						state->pc - 2, &htok));
+		if (htok)
+		    singsub(&pat);
 		save = (!state->prog->heap &&
 			!strcmp(pat, opat) && *spprog != dummy_patprog2);
 	    }
diff -ru ../z.old/Src/params.c Src/params.c
--- ../z.old/Src/params.c	Wed Feb 23 09:02:47 2000
+++ Src/params.c	Wed Feb 23 11:54:58 2000
@@ -747,8 +747,8 @@
 getarg(char **str, int *inv, Value v, int a2, zlong *w)
 {
     int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
-    int keymatch = 0;
-    char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt;
+    int keymatch = 0, needtok = 0;
+    char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c;
     zlong num = 1, beg = 0, r = 0;
     Patprog pprog = NULL;
 
@@ -870,21 +870,25 @@
 	*inv = ind;
     }
 
-    for (t=s, i=0;
-	 *t && ((*t != ']' && *t != Outbrack && (ishash || *t != ',')) || i); t++)
-	if (*t == '[' || *t == Inbrack)
+    for (t = s, i = 0;
+	 (c = *t) && ((c != ']' && c != Outbrack &&
+		       (ishash || c != ',')) || i); t++) {
+	if (c == '[' || c == Inbrack)
 	    i++;
-	else if (*t == ']' || *t == Outbrack)
+	else if (c == ']' || c == Outbrack)
 	    i--;
-
-    if (!*t)
+	if (ispecial(c))
+	    needtok = 1;
+    }
+    if (!c)
 	return 0;
     s = dupstrpfx(s, t - s);
     *str = tt = t;
-    if (parsestr(s))
-	return 0;
-    singsub(&s);
-
+    if (needtok) {
+	if (parsestr(s))
+	    return 0;
+	singsub(&s);
+    }
     if (!rev) {
 	if (ishash) {
 	    HashTable ht = v->pm->gets.hfn(v->pm);
@@ -1177,43 +1181,42 @@
 
 /**/
 mod_export Value
-getvalue(char **pptr, int bracks)
+getvalue(Value v, char **pptr, int bracks)
 {
-  return fetchvalue(pptr, bracks, 0);
+  return fetchvalue(v, pptr, bracks, 0);
 }
 
 /**/
 mod_export Value
-fetchvalue(char **pptr, int bracks, int flags)
+fetchvalue(Value v, char **pptr, int bracks, int flags)
 {
     char *s, *t;
-    char sav;
-    Value v;
+    char sav, c;
     int ppar = 0;
 
     s = t = *pptr;
 
-    if (idigit(*s)) {
+    if (idigit(c = *s)) {
 	if (bracks >= 0)
 	    ppar = zstrtol(s, &s, 10);
 	else
 	    ppar = *s++ - '0';
     }
-    else if (iident(*s))
+    else if (iident(c))
 	while (iident(*s))
 	    s++;
-    else if (*s == Quest)
+    else if (c == Quest)
 	*s++ = '?';
-    else if (*s == Pound)
+    else if (c == Pound)
 	*s++ = '#';
-    else if (*s == String)
+    else if (c == String)
 	*s++ = '$';
-    else if (*s == Qstring)
+    else if (c == Qstring)
 	*s++ = '$';
-    else if (*s == Star)
+    else if (c == Star)
 	*s++ = '*';
-    else if (*s == '#' || *s == '-' || *s == '?' || *s == '$' ||
-	     *s == '_' || *s == '!' || *s == '@' || *s == '*')
+    else if (c == '#' || c == '-' || c == '?' || c == '$' ||
+	     c == '!' || c == '@' || c == '*')
 	s++;
     else
 	return NULL;
@@ -1221,7 +1224,10 @@
     if ((sav = *s))
 	*s = '\0';
     if (ppar) {
-	v = (Value) hcalloc(sizeof *v);
+	if (v)
+	    memset(v, 0, sizeof(*v));
+	else
+	    v = (Value) hcalloc(sizeof *v);
 	v->pm = argvparam;
 	v->inv = 0;
 	v->a = v->b = ppar - 1;
@@ -1231,14 +1237,17 @@
 	Param pm;
 	int isvarat;
 
-        isvarat = !strcmp(t, "@");
+        isvarat = (t[0] == '@' && !t[1]);
 	pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t);
 	if (sav)
 	    *s = sav;
 	*pptr = s;
 	if (!pm || (pm->flags & PM_UNSET))
 	    return NULL;
-	v = (Value) hcalloc(sizeof *v);
+	if (v)
+	    memset(v, 0, sizeof(*v));
+	else
+	    v = (Value) hcalloc(sizeof *v);
 	if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)) {
 	    /* Overload v->isarr as the flag bits for hashed arrays. */
 	    v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
@@ -1628,9 +1637,10 @@
 mod_export zlong
 getiparam(char *s)
 {
+    struct value vbuf;
     Value v;
 
-    if (!(v = getvalue(&s, 1)))
+    if (!(v = getvalue(&vbuf, &s, 1)))
 	return 0;
     return getintvalue(v);
 }
@@ -1641,8 +1651,10 @@
 mnumber
 getnparam(char *s)
 {
+    struct value vbuf;
     Value v;
-    if (!(v = getvalue(&s, 1))) {
+
+    if (!(v = getvalue(&vbuf, &s, 1))) {
 	mnumber mn;
 	mn.type = MN_INTEGER;
 	mn.u.l = 0;
@@ -1657,9 +1669,10 @@
 mod_export char *
 getsparam(char *s)
 {
+    struct value vbuf;
     Value v;
 
-    if (!(v = getvalue(&s, 0)))
+    if (!(v = getvalue(&vbuf, &s, 0)))
 	return NULL;
     return getstrvalue(v);
 }
@@ -1670,9 +1683,10 @@
 mod_export char **
 getaparam(char *s)
 {
+    struct value vbuf;
     Value v;
 
-    if (!idigit(*s) && (v = getvalue(&s, 0)) &&
+    if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
 	PM_TYPE(v->pm->flags) == PM_ARRAY)
 	return v->pm->gets.afn(v->pm);
     return NULL;
@@ -1684,9 +1698,10 @@
 mod_export char **
 gethparam(char *s)
 {
+    struct value vbuf;
     Value v;
 
-    if (!idigit(*s) && (v = getvalue(&s, 0)) &&
+    if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) &&
 	PM_TYPE(v->pm->flags) == PM_HASHED)
 	return paramvalarr(v->pm->gets.hfn(v->pm), SCANPM_WANTVALS);
     return NULL;
@@ -1696,6 +1711,7 @@
 mod_export Param
 setsparam(char *s, char *val)
 {
+    struct value vbuf;
     Value v;
     char *t = s;
     char *ss;
@@ -1708,12 +1724,12 @@
     }
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
-	if (!(v = getvalue(&s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1)))
 	    createparam(t, PM_ARRAY);
 	*ss = '[';
 	v = NULL;
     } else {
-	if (!(v = getvalue(&s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1)))
 	    createparam(t, PM_SCALAR);
 	else if ((PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
 		 !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) {
@@ -1722,7 +1738,7 @@
 	    v = NULL;
 	}
     }
-    if (!v && !(v = getvalue(&t, 1))) {
+    if (!v && !(v = getvalue(&vbuf, &t, 1))) {
 	zsfree(val);
 	return NULL;
     }
@@ -1734,6 +1750,7 @@
 mod_export Param
 setaparam(char *s, char **val)
 {
+    struct value vbuf;
     Value v;
     char *t = s;
     char *ss;
@@ -1746,7 +1763,7 @@
     }
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
-	if (!(v = getvalue(&s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1)))
 	    createparam(t, PM_ARRAY);
 	*ss = '[';
 	if (v && PM_TYPE(v->pm->flags) == PM_HASHED) {
@@ -1757,7 +1774,7 @@
 	}
 	v = NULL;
     } else {
-	if (!(v = fetchvalue(&s, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
 	    createparam(t, PM_ARRAY);
 	else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
 		 !(v->pm->flags & (PM_SPECIAL|PM_TIED))) {
@@ -1768,7 +1785,7 @@
 	}
     }
     if (!v)
-	if (!(v = fetchvalue(&t, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING)))
 	    return NULL;
     setarrvalue(v, val);
     return v->pm;
@@ -1778,6 +1795,7 @@
 mod_export Param
 sethparam(char *s, char **val)
 {
+    struct value vbuf;
     Value v;
     char *t = s;
 
@@ -1793,7 +1811,7 @@
 	errflag = 1;
 	return NULL;
     } else {
-	if (!(v = fetchvalue(&s, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
 	    createparam(t, PM_HASHED);
 	else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) &&
 		 !(v->pm->flags & PM_SPECIAL)) {
@@ -1803,7 +1821,7 @@
 	}
     }
     if (!v)
-	if (!(v = fetchvalue(&t, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING)))
 	    return NULL;
     setarrvalue(v, val);
     return v->pm;
@@ -1813,6 +1831,7 @@
 mod_export Param
 setiparam(char *s, zlong val)
 {
+    struct value vbuf;
     Value v;
     char *t = s;
     Param pm;
@@ -1823,7 +1842,7 @@
 	errflag = 1;
 	return NULL;
     }
-    if (!(v = getvalue(&s, 1))) {
+    if (!(v = getvalue(&vbuf, &s, 1))) {
 	pm = createparam(t, PM_INTEGER);
 	DPUTS(!pm, "BUG: parameter not created");
 	pm->u.val = val;
@@ -1844,6 +1863,7 @@
 Param
 setnparam(char *s, mnumber val)
 {
+    struct value vbuf;
     Value v;
     char *t = s;
     Param pm;
@@ -1853,7 +1873,7 @@
 	errflag = 1;
 	return NULL;
     }
-    if (!(v = getvalue(&s, 1))) {
+    if (!(v = getvalue(&vbuf, &s, 1))) {
 	pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER
 			 : PM_FFLOAT);
 	DPUTS(!pm, "BUG: parameter not created");
diff -ru ../z.old/Src/parse.c Src/parse.c
--- ../z.old/Src/parse.c	Wed Feb 23 09:02:48 2000
+++ Src/parse.c	Wed Feb 23 14:20:50 2000
@@ -28,182 +28,6 @@
  */
 
 #include "zsh.mdh"
-
-/********************************/
-/* Definitions for syntax trees */
-/********************************/
-
-typedef struct cond      *Cond;
-typedef struct cmd       *Cmd;
-typedef struct pline     *Pline;
-typedef struct sublist   *Sublist;
-typedef struct list      *List;
-typedef struct forcmd    *Forcmd;
-typedef struct autofn    *AutoFn;
-typedef struct varasg    *Varasg;
-
-
-/* struct list, struct sublist, struct pline, etc.  all fit the form *
- * of this structure and are used interchangably. The ptrs may hold  *
- * integers or pointers, depending on the type of the node.          */
-
-/* Generic node structure for syntax trees */
-struct node {
-    int ntype;			/* node type */
-};
-
-#define N_LIST    0
-#define N_SUBLIST 1
-#define N_PLINE   2
-#define N_CMD     3
-#define N_REDIR   4
-#define N_COND    5
-#define N_FOR     6
-#define N_CASE    7
-#define N_IF      8
-#define N_WHILE   9
-#define N_VARASG 10
-#define N_AUTOFN 11
-#define N_COUNT  12
-
-/* values for types[4] */
-
-#define NT_EMPTY 0
-#define NT_NODE  1
-#define NT_STR   2
-#define NT_PAT   3
-#define NT_LIST  4
-#define NT_ARR   8
-
-#define NT_TYPE(T) ((T) & 0xff)
-#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))
-
-/* tree element for lists */
-
-struct list {
-    int ntype;			/* node type */
-    int type;
-    Sublist left;
-    List right;
-};
-
-/* tree element for sublists */
-
-struct sublist {
-    int ntype;			/* node type */
-    int type;
-    int flags;			/* see PFLAGs below */
-    Pline left;
-    Sublist right;
-};
-
-#define ORNEXT  10		/* || */
-#define ANDNEXT 11		/* && */
-
-#define PFLAG_NOT     1		/* ! ... */
-#define PFLAG_COPROC 32		/* coproc ... */
-
-/* tree element for pipes */
-
-struct pline {
-    int ntype;			/* node type */
-    int type;
-    Cmd left;
-    Pline right;
-};
-
-#define END	0		/* pnode *right is null                     */
-#define PIPE	1		/* pnode *right is the rest of the pipeline */
-
-/* tree element for commands */
-
-struct cmd {
-    int ntype;			/* node type */
-    int type;
-    int flags;			/* see CFLAGs below             */
-    int lineno;			/* lineno of script for command */
-    union {
-	List list;		/* for SUBSH/CURSH/SHFUNC       */
-	Forcmd forcmd;
-	struct casecmd *casecmd;
-	struct ifcmd *ifcmd;
-	struct whilecmd *whilecmd;
-	Sublist pline;
-	Cond cond;
-	AutoFn autofn;
-	void *generic;
-    } u;
-    LinkList args;		/* command & argmument List (char *'s)   */
-    LinkList redir;		/* i/o redirections (struct redir *'s)   */
-    LinkList vars;		/* param assignments (struct varasg *'s) */
-};
-
-/* cmd types */
-#define SIMPLE   0
-#define SUBSH    1
-#define CURSH    2
-#define ZCTIME   3
-#define FUNCDEF  4
-#define CFOR     5
-#define CWHILE   6
-#define CREPEAT  7
-#define CIF      8
-#define CCASE    9
-#define CSELECT 10
-#define COND    11
-#define CARITH  12
-
-/* tree element for conditionals */
-
-struct cond {
-    int ntype;			/* node type */
-    int type;		/* can be cond_type, or a single */
-			/* letter (-a, -b, ...)          */
-    void *left, *right;
-};
-
-struct forcmd {			/* for/select */
-/* Cmd->args contains list of words to loop thru */
-    int ntype;			/* node type */
-    int inflag;			/* if there is an in ... clause       */
-    char *name;			/* initializer or parameter name      */
-    char *condition;		/* arithmetic terminating condition   */
-    char *advance;		/* evaluated after each loop          */
-    List list;			/* list to look through for each name */
-};
-
-struct casecmd {
-/* Cmd->args contains word to test */
-    int ntype;			/* node type */
-    char **pats;		/* pattern strings */
-    List *lists;		/* list to execute */
-};
-
-struct ifcmd {
-    int ntype;			/* node type */
-    List *ifls;
-    List *thenls;
-};
-
-struct whilecmd {
-    int ntype;			/* node type */
-    int cond;			/* 0 for while, 1 for until            */
-    List cont;			/* condition                           */
-    List loop;			/* list to execute until condition met */
-};
-
-/* variable assignment tree element */
-
-struct varasg {
-    int ntype;			/* node type */
-    int type;			/* nonzero means array                   */
-    char *name;
-    char *str;			/* should've been a union here.  oh well */
-    LinkList arr;
-};
-
 #include "parse.pro"
 
 /* != 0 if we are about to read a command word */
@@ -241,191 +65,543 @@
 /**/
 struct heredocs *hdocs;
  
-/* used in arrays of lists instead of NULL pointers */
- 
-/**/
-static struct list dummy_list;
 
-#define YYERROR  { tok = LEXERR; return NULL; }
-#define YYERRORV { tok = LEXERR; return; }
+#define YYERROR(O)  { tok = LEXERR; ecused = (O); return 0; }
+#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; }
 #define COND_ERROR(X,Y) do { \
   zwarn(X,Y,0); \
   herrflush(); \
   if (noerrs != 2) \
     errflag = 1; \
-  YYERROR \
+  YYERROR(ecused) \
 } while(0)
 
-#define make_list()     allocnode(sizeof(struct list), N_LIST)
-#define make_sublist()  allocnode(sizeof(struct sublist), N_SUBLIST)
-#define make_pline()    allocnode(sizeof(struct pline), N_PLINE)
-#define make_cmd()      allocnode(sizeof(struct cmd), N_CMD)
-#define make_forcmd()   allocnode(sizeof(struct forcmd), N_FOR)
-#define make_casecmd()  allocnode(sizeof(struct casecmd), N_CASE)
-#define make_ifcmd()    allocnode(sizeof(struct ifcmd), N_IF)
-#define make_whilecmd() allocnode(sizeof(struct whilecmd), N_WHILE)
-#define make_varnode()  allocnode(sizeof(struct varasg), N_VARASG)
-#define make_cond()     allocnode(sizeof(struct cond), N_COND)
-
-static void *
-allocnode(size_t s, int t)
-{
-    struct node *r = (struct node *) hcalloc(s);
-
-    r->ntype = t;
-
-    return (void *) r;
-}
 
-/*
- * event	: ENDINPUT
- *			| SEPER
- *			| sublist [ SEPER | AMPER | AMPERBANG ]
+/* 
+ * Word code.
+ *
+ * For now we simply post-process the syntax tree produced by the
+ * parser. We compile it into a struct eprog. Some day the parser
+ * above should be changed to emit the word code directly.
+ *
+ * Word code layout:
+ *
+ *   WC_END
+ *     - end of program code
+ *
+ *   WC_LIST
+ *     - data contains type (sync, ...)
+ *     - follwed by code for this list
+ *     - if not (type & Z_END), followed by next WC_LIST
+ *
+ *   WC_SUBLIST
+ *     - data contains type (&&, ||, END) and flags (coprog, not)
+ *     - followed by code for sublist
+ *     - if not (type == END), followed by next WC_SUBLIST
+ *
+ *   WC_PIPE
+ *     - data contains type (end, mid) and LINENO
+ *     - if not (type == END), followed by offset to next WC_PIPE
+ *     - followed by command
+ *     - if not (type == END), followed by next WC_PIPE
+ *
+ *   WC_REDIR
+ *     - must precede command-code (or WC_ASSIGN)
+ *     - data contains type (<, >, ...)
+ *     - followed by fd1 and name from struct redir
+ *
+ *   WC_ASSIGN
+ *     - data contains type (scalar, array) and number of array-elements
+ *     - followed by name and value
+ *
+ *   WC_SIMPLE
+ *     - data contains the number of arguments (plus command)
+ *     - followed by strings
+ *
+ *   WC_SUBSH
+ *     - data unused
+ *     - followed by list
+ *
+ *   WC_CURSH
+ *     - data unused
+ *     - followed by list
+ *
+ *   WC_TIMED
+ *     - data contains type (followed by pipe or not)
+ *     - if (type == PIPE), followed by pipe
+ *
+ *   WC_FUNCDEF
+ *     - data contains offset to after body-strings
+ *     - followed by number of names
+ *     - followed by names
+ *     - followed by number of codes for body
+ *     - followed by number of patterns for body
+ *     - follwoed by codes for body
+ *     - followed by strings for body
+ *
+ *   WC_FOR
+ *     - data contains type (list, ...) and offset to after body
+ *     - if (type == COND), followed by init, cond, advance expressions
+ *     - else if (type == PPARAM), followed by param name
+ *     - else if (type == LIST), followed by param name, num strings, strings
+ *     - followed by body
+ *
+ *   WC_SELECT
+ *     - data contains type (list, ...) and offset to after body
+ *     - if (type == PPARAM), followed by param name
+ *     - else if (type == LIST), followed by param name, num strings, strings
+ *     - followed by body
+ *
+ *   WC_WHILE
+ *     - data contains type (while, until) and ofsset to after body
+ *     - followed by condition
+ *     - followed by body
+ *
+ *   WC_REPEAT
+ *     - data contains offset to after body
+ *     - followed by number-string
+ *     - followed by body
+ *
+ *   WC_CASE
+ *     - first CASE is always of type HEAD, data contains offset to esac
+ *     - after that CASEs of type OR (;;) and AND (;&), data is offset to
+ *       next case
+ *     - each OR/AND case is followed by pattern, pattern-number, list
+ *
+ *   WC_IF
+ *     - first IF is of type HEAD, data contains offset to fi
+ *     - after that IFs of type IF, ELIF, ELSE, data is offset to next
+ *     - each non-HEAD is followed by condition (only IF, ELIF) and body
+ *
+ *   WC_COND
+ *     - data contains type
+ *     - if (type == AND/OR), data contains offset to after this one,
+ *       followed by two CONDs
+ *     - else if (type == NOT), followed by COND
+ *     - else if (type == MOD), followed by name and strings
+ *     - else if (type == MODI), followed by name, left, right
+ *     - else if (type == STR[N]EQ), followed by left, right, pattern-number
+ *     - else if (has two args) followed by left, right
+ *     - else followed by string
+ *
+ *   WC_ARITH
+ *     - followed by string (there's only one)
+ *
+ *   WC_AUTOFN
+ *     - only used by the autoload builtin
+ *
+ * Lists and sublists may also be simplified, indicated by the presence
+ * of the Z_SIMPLE or WC_SUBLIST_SIMPLE flags. In this case they are only
+ * followed by a slot containing the line number, not by a WC_SUBLIST or
+ * WC_PIPE, respectively. The real advantage of simplified lists and
+ * sublists is that they can be executed faster, see exec.c. In the
+ * parser, the test if a list can be simplified is done quite simply
+ * by passing a int* around which gets set to non-zero if the thing
+ * just parsed is `complex', i.e. may need to be run by forking or 
+ * some such.
+ *
+ * In each of the above, strings are encoded as one word code. For empty
+ * strings this is the bit pattern 11x, the lowest bit is non-zero if the
+ * string contains tokens and zero otherwise (this is true for the other
+ * ways to encode strings, too). For short strings (one to three
+ * characters), this is the marker 01x with the 24 bits above that
+ * containing the characters. Longer strings are encoded as the offset
+ * into the strs character array stored in the eprog struct shifted by
+ * two and ored with the bit pattern 0x.
+ * The ecstr() function that adds the code for a string uses a simple
+ * list of strings already added so that long strings are encoded only
+ * once.
+ *
+ * Note also that in the eprog struct the pattern, code, and string
+ * arrays all point to the same memory block.
+ *
+ *
+ * To make things even faster in future versions, we could not only 
+ * test if the strings contain tokens, but instead what kind of
+ * expansions need to be done on strings. In the execution code we
+ * could then use these flags for a specialized version of prefork()
+ * to avoid a lot of string parsing and some more string duplication.
  */
-/**/
-Eprog
-parse_event(void)
-{
-    List ret;
-    tok = ENDINPUT;
-    incmdpos = 1;
-    yylex();
-    return ((ret = par_event()) ? execompile(ret) : NULL);
-}
 
 /**/
-static List
-par_event(void)
-{
-    Sublist sl;
-    List l = NULL;
+int eclen, ecused, ecfree, ecnpats;
+/**/
+Wordcode ecbuf;
+/**/
+Eccstr ecstrs;
+/**/
+int ecsoffs;
 
-    while (tok == SEPER) {
-	if (isnewlin > 0)
-	    return NULL;
-	yylex();
-    }
-    if (tok == ENDINPUT)
-	return NULL;
-    if ((sl = par_sublist())) {
-	if (tok == ENDINPUT) {
-	    l = (List) make_list();
-	    l->type = Z_SYNC;
-	    l->left = sl;
-	} else if (tok == SEPER) {
-	    l = (List) make_list();
-	    l->type = Z_SYNC;
-	    l->left = sl;
-	    if (isnewlin <= 0)
-		yylex();
-	} else if (tok == AMPER) {
-	    l = (List) make_list();
-	    l->type = Z_ASYNC;
-	    l->left = sl;
-	    yylex();
-	} else if (tok == AMPERBANG) {
-	    l = (List) make_list();
-	    l->type = Z_ASYNC | Z_DISOWN;
-	    l->left = sl;
-	    yylex();
-	} else
-	    l = NULL;
-    }
-    if (!l) {
-	if (errflag) {
-	    yyerror(0);
-	    return NULL;
-	}
-	yyerror(1);
-	herrflush();
-	if (noerrs != 2)
-	    errflag = 1;
-	return NULL;
-    } else {
-	l->right = par_event();
-    }
-    return l;
-}
+/* Make at least n bytes free (aligned to sizeof(wordcode)). */
 
-/**/
-mod_export Eprog
-parse_list(void)
+static int
+ecspace(int n)
 {
-    List ret;
+    n = (n + sizeof(wordcode) - 1) / sizeof(wordcode);
 
-    tok = ENDINPUT;
-    incmdpos = 1;
-    yylex();
-    ret = par_list();
-#if 0
-    if (tok == LEXERR)
-#endif
-    if (tok != ENDINPUT)
-    {
-	yyerror(0);
-	return NULL;
+    if (ecfree < n) {
+	int a = (n > 256 ? n : 256);
+
+	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
+				    (eclen + a) * sizeof(wordcode));
+	eclen += a;
+	ecfree += a;
     }
-    return execompile(ret);
+    ecused += n;
+    ecfree -= n;
+
+    return ecused - 1;
 }
 
-/**/
-mod_export Eprog
-parse_cond(void)
+/* Insert n free code-slots at position p. */
+
+static void
+ecispace(int p, int n)
 {
-    Cond c = par_cond();
+    int m;
 
-    if (!c)
-	return NULL;
+    if (ecfree < n) {
+	int a = (n > 256 ? n : 256);
 
-    return execompile((List) c);
+	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
+				    (eclen + a) * sizeof(wordcode));
+	eclen += a;
+	ecfree += a;
+    }
+    if ((m = ecused - p) > 0)
+	memmove(ecbuf + p + n, ecbuf + p, m * sizeof(wordcode));
+    ecused += n;
 }
 
-/*
- * list	: { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ]
- */
+/* Add one wordcode. */
 
-/**/
-static List
-par_list(void)
-{
-    Sublist sl;
-    List l = NULL;
+static int
+ecadd(wordcode c)
+{
+    if (ecfree < 1) {
+	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
+				    (eclen + 256) * sizeof(wordcode));
+	eclen += 256;
+	ecfree += 256;
+    }
+    ecbuf[ecused] = c;
+    ecused++;
+    ecfree--;
+
+    return ecused - 1;
+}
+
+/* Delete a wordcode. */
+
+static void
+ecdel(int p)
+{
+    int n = ecused - p - 1;
+
+    if (n > 0)
+	memmove(ecbuf + p, ecbuf + p + 1, n * sizeof(wordcode));
+    ecused--;
+}
+
+/* Build the wordcode for a string. */
+
+static wordcode
+ecstrcode(char *s)
+{
+    int l, t = has_token(s);
+
+    if ((l = strlen(s) + 1) && l <= 4) {
+	wordcode c = (t ? 3 : 2);
+	switch (l) {
+	case 4: c |= ((wordcode) STOUC(s[2])) << 19;
+	case 3: c |= ((wordcode) STOUC(s[1])) << 11;
+	case 2: c |= ((wordcode) STOUC(s[0])) <<  3; break;
+	case 1: c = (t ? 7 : 6); break;
+	}
+	return c;
+    } else {
+	Eccstr p, q = NULL;
+
+	for (p = ecstrs; p; q = p, p = p->next)
+	    if (!strcmp(s, p->str))
+		return p->offs;
+
+	p = (Eccstr) zhalloc(sizeof(*p));
+	p->next = NULL;
+	if (q)
+	    q->next = p;
+	else
+	    ecstrs = p;
+	p->offs = (ecsoffs << 2) | (t ? 1 : 0);
+	p->str = s;
+	ecsoffs += l;
+
+	return p->offs;
+    }
+}
+
+static int
+ecstr(char *s)
+{
+    return ecadd(ecstrcode(s));
+}
+
+
+#define par_save_list(C) \
+    do { \
+        int eu = ecused; \
+        par_list(C); \
+        if (eu == ecused) ecadd(WCB_END()); \
+    } while (0)
+#define par_save_list1(C) \
+    do { \
+        int eu = ecused; \
+        par_list1(C); \
+        if (eu == ecused) ecadd(WCB_END()); \
+    } while (0)
+
+
+/* Initialise wordcode buffer. */
+
+static void
+init_parse(void)
+{
+    ecbuf = (Wordcode) zhalloc((eclen = ecfree = 256) * sizeof(wordcode));
+    ecused = 0;
+    ecstrs = NULL;
+    ecsoffs = ecnpats = 0;
+}
+
+/* Build eprog. */
+
+static Eprog
+bld_eprog(void)
+{
+    Eprog ret;
+    Eccstr p;
+    char *q;
+    int l;
+
+    ecadd(WCB_END());
+
+    ret = (Eprog) zhalloc(sizeof(*ret));
+    ret->len = ((ecnpats * sizeof(Patprog)) +
+		(ecused * sizeof(wordcode)) +
+		ecsoffs);
+    ret->npats = ecnpats;
+    ret->pats = (Patprog *) zhalloc(ret->len);
+    ret->prog = (Wordcode) (ret->pats + ecnpats);
+    ret->strs = (char *) (ret->prog + ecused);
+    ret->shf = NULL;
+    ret->heap = 1;
+    for (l = 0; l < ecnpats; l++)
+	ret->pats[l] = dummy_patprog1;
+    memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode));
+    for (p = ecstrs, q = ret->strs; p; p = p->next, q += l) {
+	l = strlen(p->str) + 1;
+	memcpy(q, p->str, l);
+    }
+    return ret;
+}
+
+/*
+ * event	: ENDINPUT
+ *			| SEPER
+ *			| sublist [ SEPER | AMPER | AMPERBANG ]
+ */
+
+/**/
+Eprog
+parse_event(void)
+{
+    tok = ENDINPUT;
+    incmdpos = 1;
+    yylex();
+    init_parse();
+    return ((par_event()) ? bld_eprog() : NULL);
+}
+
+/**/
+static int
+par_event(void)
+{
+    int r = 0, p, c = 0;
+
+    while (tok == SEPER) {
+	if (isnewlin > 0)
+	    return 0;
+	yylex();
+    }
+    if (tok == ENDINPUT)
+	return 0;
+
+    p = ecadd(0);
+
+    if (par_sublist(&c)) {
+	if (tok == ENDINPUT) {
+	    set_list_code(p, Z_SYNC, c);
+	    r = 1;
+	} else if (tok == SEPER) {
+	    set_list_code(p, Z_SYNC, c);
+	    if (isnewlin <= 0)
+		yylex();
+	    r = 1;
+	} else if (tok == AMPER) {
+	    set_list_code(p, Z_ASYNC, c);
+	    yylex();
+	    r = 1;
+	} else if (tok == AMPERBANG) {
+	    set_list_code(p, (Z_ASYNC | Z_DISOWN), c);
+	    yylex();
+	    r = 1;
+	}
+    }
+    if (!r) {
+	if (errflag) {
+	    yyerror(0);
+	    ecused--;
+	    return 0;
+	}
+	yyerror(1);
+	herrflush();
+	if (noerrs != 2)
+	    errflag = 1;
+	ecused--;
+	return 0;
+    } else {
+	int oec = ecused;
+
+	par_event();
+	if (ecused == oec)
+	    ecbuf[p] |= wc_bdata(Z_END);
+    }
+    return 1;
+}
+
+/**/
+mod_export Eprog
+parse_list(void)
+{
+    int c = 0;
+
+    tok = ENDINPUT;
+    incmdpos = 1;
+    yylex();
+    init_parse();
+    par_list(&c);
+#if 0 
+   if (tok == LEXERR)
+#endif
+   if (tok != ENDINPUT) {
+	yyerror(0);
+	return NULL;
+    }
+    return bld_eprog();
+}
+
+/**/
+mod_export Eprog
+parse_cond(void)
+{
+    init_parse();
+
+    if (!par_cond())
+	return NULL;
+
+    return bld_eprog();
+}
+
+/* This adds a list wordcode. The important bit about this is that it also
+ * tries to optimise this to a Z_SIMPLE list code. */
+
+/**/
+static void
+set_list_code(int p, int type, int complex)
+{
+    if (!complex && (type == Z_SYNC || type == (Z_SYNC | Z_END)) &&
+	WC_SUBLIST_TYPE(ecbuf[p + 1]) == WC_SUBLIST_END) {
+	int ispipe = !(WC_SUBLIST_FLAGS(ecbuf[p + 1]) & WC_SUBLIST_SIMPLE);
+	ecbuf[p] = WCB_LIST((type | Z_SIMPLE), ecused - 2 - p);
+	ecdel(p + 1);
+	if (ispipe)
+	    ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]);
+    } else
+	ecbuf[p] = WCB_LIST(type, 0);
+}
+
+/* The same for sublists. */
+
+/**/
+static void
+set_sublist_code(int p, int type, int flags, int skip, int complex)
+{
+    if (complex)
+	ecbuf[p] = WCB_SUBLIST(type, flags, skip);
+    else {
+	ecbuf[p] = WCB_SUBLIST(type, (flags | WC_SUBLIST_SIMPLE), skip);
+	ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]);
+    }
+}
+
+/*
+ * list	: { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ]
+ */
+
+/**/
+static int
+par_list(int *complex)
+{
+    int p, lp = -1, c;
+
+ rec:
 
     while (tok == SEPER)
 	yylex();
-    if ((sl = par_sublist())) {
+
+    p = ecadd(0);
+    c = 0;
+
+    if (par_sublist(&c)) {
+	*complex |= c;
 	if (tok == SEPER || tok == AMPER || tok == AMPERBANG) {
-	    l = (List) make_list();
-	    l->left = sl;
-	    l->type = (tok == SEPER) ? Z_SYNC :
-		(tok == AMPER) ? Z_ASYNC : Z_ASYNC | Z_DISOWN;
+	    if (tok != SEPER)
+		*complex = 1;
+	    set_list_code(p, ((tok == SEPER) ? Z_SYNC :
+			      (tok == AMPER) ? Z_ASYNC :
+			      (Z_ASYNC | Z_DISOWN)), c);
 	    incmdpos = 1;
 	    do {
 		yylex();
 	    } while (tok == SEPER);
-	    l->right = par_list();
-	} else {
-	    l = (List) make_list();
-	    l->left = sl;
-	    l->type = Z_SYNC;
+	    lp = p;
+	    goto rec;
+	} else
+	    set_list_code(p, (Z_SYNC | Z_END), c);
+	return 1;
+    } else {
+	ecused--;
+	if (lp >= 0) {
+	    ecbuf[lp] |= wc_bdata(Z_END);
+	    return 1;
 	}
+	return 0;
     }
-    return l;
 }
 
 /**/
-static List
-par_list1(void)
+static int
+par_list1(int *complex)
 {
-    Sublist sl;
-    List l = NULL;
+    int p = ecadd(0), c = 0;
 
-    if ((sl = par_sublist())) {
-	l = (List) make_list();
-	l->type = Z_SYNC;
-	l->left = sl;
+    if (par_sublist(&c)) {
+	set_list_code(p, (Z_SYNC | Z_END), c);
+	*complex |= c;
+	return 1;
+    } else {
+	ecused--;
+	return 0;
     }
-    return l;
 }
 
 /*
@@ -433,24 +609,37 @@
  */
 
 /**/
-static Sublist
-par_sublist(void)
+static int
+par_sublist(int *complex)
 {
-    Sublist sl;
+    int f, p, c = 0;
+
+    p = ecadd(0);
 
-    if ((sl = par_sublist2()))
+    if ((f = par_sublist2(&c)) != -1) {
+	int e = ecused;
+
+	*complex |= c;
 	if (tok == DBAR || tok == DAMPER) {
-	    int qtok = tok;
+	    int qtok = tok, sl;
 
 	    cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND);
 	    yylex();
 	    while (tok == SEPER)
 		yylex();
-	    sl->right = par_sublist();
-	    sl->type = (qtok == DBAR) ? ORNEXT : ANDNEXT;
+	    sl = par_sublist(complex);
+	    set_sublist_code(p, (sl ? (qtok == DBAR ?
+				       WC_SUBLIST_OR : WC_SUBLIST_AND) :
+				 WC_SUBLIST_END),
+			     f, (e - 1 - p), c);
 	    cmdpop();
-	}
-    return sl;
+	} else
+	    set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c);
+	return 1;
+    } else {
+	ecused--;
+	return 0;
+    }
 }
 
 /*
@@ -458,24 +647,24 @@
  */
 
 /**/
-static Sublist
-par_sublist2(void)
+static int
+par_sublist2(int *complex)
 {
-    Sublist sl;
-    Pline p;
+    int f = 0;
 
-    sl = (Sublist) make_sublist();
     if (tok == COPROC) {
-	sl->flags |= PFLAG_COPROC;
+	*complex = 1;
+	f |= WC_SUBLIST_COPROC;
 	yylex();
     } else if (tok == BANG) {
-	sl->flags |= PFLAG_NOT;
+	*complex = 1;
+	f |= WC_SUBLIST_NOT;
 	yylex();
     }
-    if (!(p = par_pline()) && !sl->flags)
-	return NULL;
-    sl->left = p;
-    return sl;
+    if (!par_pline(complex) && !f)
+	return -1;
+
+    return f;
 }
 
 /*
@@ -483,51 +672,52 @@
  */
 
 /**/
-static Pline
-par_pline(void)
+static int
+par_pline(int *complex)
 {
-    Cmd c;
-    Pline p, p2;
+    int p, line = lineno;
 
-    if (!(c = par_cmd()))
-	return NULL;
+    p = ecadd(0);
+
+    if (!par_cmd(complex)) {
+	ecused--;
+	return 0;
+    }
     if (tok == BAR) {
+	*complex = 1;
 	cmdpush(CS_PIPE);
 	yylex();
 	while (tok == SEPER)
 	    yylex();
-	p2 = par_pline();
+	ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0));
+	ecispace(p + 1, 1);
+	ecbuf[p + 1] = ecused - 1 - p;
+	par_pline(complex);
 	cmdpop();
-	p = (Pline) make_pline();
-	p->left = c;
-	p->right = p2;
-	p->type = PIPE;
-	return p;
+	return 1;
     } else if (tok == BARAMP) {
-	struct redir *rdr = (struct redir *)
-	    allocnode(sizeof(struct redir), N_REDIR);
+	int r;
+
+	for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3);
 
-	rdr->type = MERGEOUT;
-	rdr->fd1 = 2;
-	rdr->name = dupstring("1");
-	if (!c->redir)
-	    c->redir = newlinklist();
-	addlinknode(c->redir, rdr);
+	ecispace(r, 3);
+	p += 3;
+	ecbuf[r] = WCB_REDIR(MERGEOUT);
+	ecbuf[r + 1] = 2;
+	ecbuf[r + 2] = ecstrcode("1");
 
+	*complex = 1;
 	cmdpush(CS_ERRPIPE);
 	yylex();
-	p2 = par_pline();
+	ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0));
+	ecispace(p + 1, 1);
+	ecbuf[p + 1] = ecused - 1 - p;
+	par_pline(complex);
 	cmdpop();
-	p = (Pline) make_pline();
-	p->left = c;
-	p->right = p2;
-	p->type = PIPE;
-	return p;
+	return 1;
     } else {
-	p = (Pline) make_pline();
-	p->left = c;
-	p->type = END;
-	return p;
+	ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0));
+	return 1;
     }
 }
 
@@ -537,105 +727,116 @@
  */
 
 /**/
-static Cmd
-par_cmd(void)
+static int
+par_cmd(int *complex)
 {
-    Cmd c;
+    int r, nr = 0;
+
+    r = ecused;
 
-    c = (Cmd) make_cmd();
-    c->lineno = lineno;
-    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;
+	*complex = 1;
+	while (IS_REDIROP(tok)) {
+	    nr++;
+	    par_redir(&r);
+	}
+    }
     switch (tok) {
     case FOR:
 	cmdpush(CS_FOR);
-	par_for(c);
+	par_for(complex);
 	cmdpop();
 	break;
     case FOREACH:
 	cmdpush(CS_FOREACH);
-	par_for(c);
+	par_for(complex);
 	cmdpop();
 	break;
     case SELECT:
+	*complex = 1;
 	cmdpush(CS_SELECT);
-	par_for(c);
+	par_for(complex);
 	cmdpop();
 	break;
     case CASE:
 	cmdpush(CS_CASE);
-	par_case(c);
+	par_case(complex);
 	cmdpop();
 	break;
     case IF:
-	par_if(c);
+	par_if(complex);
 	break;
     case WHILE:
 	cmdpush(CS_WHILE);
-	par_while(c);
+	par_while(complex);
 	cmdpop();
 	break;
     case UNTIL:
 	cmdpush(CS_UNTIL);
-	par_while(c);
+	par_while(complex);
 	cmdpop();
 	break;
     case REPEAT:
 	cmdpush(CS_REPEAT);
-	par_repeat(c);
+	par_repeat(complex);
 	cmdpop();
 	break;
     case INPAR:
+	*complex = 1;
 	cmdpush(CS_SUBSH);
-	par_subsh(c);
+	par_subsh(complex);
 	cmdpop();
 	break;
     case INBRACE:
 	cmdpush(CS_CURSH);
-	par_subsh(c);
+	par_subsh(complex);
 	cmdpop();
 	break;
     case FUNC:
 	cmdpush(CS_FUNCDEF);
-	par_funcdef(c);
+	par_funcdef();
 	cmdpop();
 	break;
     case TIME:
-	par_time(c);
+	*complex = 1;
+	par_time();
 	break;
     case DINBRACK:
 	cmdpush(CS_COND);
-	par_dinbrack(c);
+	par_dinbrack();
 	cmdpop();
 	break;
     case DINPAR:
-	c->type = CARITH;
-	if (!c->args)
-	    c->args = newlinklist();
-	addlinknode(c->args, tokstr);
+	ecadd(WCB_ARITH());
+	ecstr(tokstr);
 	yylex();
 	break;
     default:
-	if (!par_simple(c))
-	    return NULL;
+	{
+	    int sr;
+
+	    if (!(sr = par_simple(complex, nr))) {
+		if (!nr)
+		    return 0;
+	    } else {
+		/* Three codes per redirection. */
+		if (sr > 1) {
+		    *complex = 1;
+		    r += (sr - 1) * 3;
+		}
+	    }
+	}
 	break;
     }
     if (IS_REDIROP(tok)) {
-	if (!c->redir)
-	    c->redir = newlinklist();
+	*complex = 1;
 	while (IS_REDIROP(tok))
-	    par_redir(c->redir);
+	    par_redir(&r);
     }
     incmdpos = 1;
     incasepat = 0;
     incond = 0;
-    return c;
+    return 1;
 }
 
 /*
@@ -646,86 +847,95 @@
 
 /**/
 static void
-par_for(Cmd c)
+par_for(int *complex)
 {
-    Forcmd f;
-    int csh = (tok == FOREACH);
+    int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT);
+    int type;
+
+    p = ecadd(0);
 
-    f = (Forcmd) make_forcmd();
-    c->type = (tok == SELECT) ? CSELECT : CFOR;
     incmdpos = 0;
     infor = tok == FOR ? 2 : 0;
     yylex();
     if (tok == DINPAR) {
 	yylex();
 	if (tok != DINPAR)
-	    YYERRORV;
-	f->name = tokstr;
+	    YYERRORV(oecused);
+	ecstr(tokstr);
 	yylex();
 	if (tok != DINPAR)
-	    YYERRORV;
-	f->condition = tokstr;
+	    YYERRORV(oecused);
+	ecstr(tokstr);
 	yylex();
 	if (tok != DOUTPAR)
-	    YYERRORV;
-	f->advance = tokstr;
+	    YYERRORV(oecused);
+	ecstr(tokstr);
 	infor = 0;
 	incmdpos = 1;
 	yylex();
+	type = WC_FOR_COND;
     } else {
 	infor = 0;
 	if (tok != STRING || !isident(tokstr))
-	    YYERRORV;
-	f->name = tokstr;
+	    YYERRORV(oecused);
+	ecstr(tokstr);
 	incmdpos = 1;
 	yylex();
 	if (tok == STRING && !strcmp(tokstr, "in")) {
-	    f->inflag = 1;
+	    int np, n;
+
 	    incmdpos = 0;
 	    yylex();
-	    if (!c->args)
-		c->args = newlinklist();
-	    c->args = par_wordlist();
+	    np = ecadd(0);
+	    n = par_wordlist();
 	    if (tok != SEPER)
-		YYERRORV;
+		YYERRORV(oecused);
+	    ecbuf[np] = n;
+	    type = (sel ? WC_SELECT_LIST : WC_FOR_LIST);
 	} else if (tok == INPAR) {
-	    f->inflag = 1;
+	    int np, n;
+
 	    incmdpos = 0;
 	    yylex();
-	    if (!c->args)
-		c->args = newlinklist();
-	    c->args = par_nl_wordlist();
+	    np = ecadd(0);
+	    n = par_nl_wordlist();
 	    if (tok != OUTPAR)
-		YYERRORV;
+		YYERRORV(oecused);
+	    ecbuf[np] = n;
 	    incmdpos = 1;
 	    yylex();
-	}
+	    type = (sel ? WC_SELECT_LIST : WC_FOR_LIST);
+	} else
+	    type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM);
     }
     incmdpos = 1;
     while (tok == SEPER)
 	yylex();
     if (tok == DO) {
 	yylex();
-	f->list = par_list();
+	par_save_list(complex);
 	if (tok != DONE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (tok == INBRACE) {
 	yylex();
-	f->list = par_list();
+	par_save_list(complex);
 	if (tok != OUTBRACE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (csh || isset(CSHJUNKIELOOPS)) {
-	f->list = par_list();
+	par_save_list(complex);
 	if (tok != ZEND)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (unset(SHORTLOOPS)) {
-	YYERRORV;
+	YYERRORV(oecused);
     } else
-	f->list = par_list1();
-    c->u.forcmd = f;
+	par_save_list1(complex);
+
+    ecbuf[p] = (sel ?
+		WCB_SELECT(type, ecused - 1 - p) :
+		WCB_FOR(type, ecused - 1 - p));
 }
 
 /*
@@ -737,35 +947,29 @@
 
 /**/
 static void
-par_case(Cmd c)
+par_case(int *complex)
 {
-    int brflag;
-    LinkList pats, lists;
-    int n = 1;
-    char **pp;
-    List *ll;
-    LinkNode no;
-    struct casecmd *cc;
+    int oecused = ecused, brflag, p, pp, n = 1, type;
+
+    p = ecadd(0);
 
-    c->type = CCASE;
     incmdpos = 0;
     yylex();
     if (tok != STRING)
-	YYERRORV;
-    pats = newlinklist();
-    addlinknode(pats, tokstr);
+	YYERRORV(oecused);
+    ecstr(tokstr);
+
     incmdpos = 1;
     yylex();
     while (tok == SEPER)
 	yylex();
     if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE)
-	YYERRORV;
+	YYERRORV(oecused);
     brflag = (tok == INBRACE);
     incasepat = 1;
     incmdpos = 0;
     yylex();
-    cc = c->u.casecmd = (struct casecmd *)make_casecmd();
-    lists = newlinklist();
+
     for (;;) {
 	char *str;
 
@@ -774,14 +978,13 @@
 	if (tok == OUTBRACE)
 	    break;
 	if (tok != STRING)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	if (!strcmp(tokstr, "esac"))
 	    break;
-	str = ncalloc(strlen(tokstr) + 2);
-	*str = ';';
-	strcpy(str + 1, tokstr);
+	str = dupstring(tokstr);
 	incasepat = 0;
 	incmdpos = 1;
+	type = WC_CASE_OR;
 	for (;;) {
 	    yylex();
 	    if (tok == OUTPAR) {
@@ -803,12 +1006,12 @@
 	    } else {
 		int sl = strlen(str);
 
-		if (str[sl - 1] != Bar) {
+		if (!sl || str[sl - 1] != Bar) {
 		    /* POSIX allows (foo*) patterns */
 		    int pct;
 		    char *s;
 
-		    for (s = str + 1, pct = 0; *s; s++) {
+		    for (s = str, pct = 0; *s; s++) {
 			if (*s == Inpar)
 			    pct++;
 			if (!pct)
@@ -819,26 +1022,26 @@
 				    chuck(s+1);
 			    if (*s == Bar || *s == Outpar)
 				while (iblank(s[-1]) &&
-				       (s < str+2 || s[-2] != Meta))
+				       (s < str + 1 || s[-2] != Meta))
 				    chuck(--s);
 			}
 			if (*s == Outpar)
 			    pct--;
 		    }
-		    if (*s || pct || s == str + 1)
-			YYERRORV;
+		    if (*s || pct || s == str)
+			YYERRORV(oecused);
 		    /* Simplify pattern by removing surrounding (...) */
 		    sl = strlen(str);
-		    DPUTS(str[1] != Inpar || str[sl-1] != Outpar,
+		    DPUTS(*str != Inpar || str[sl - 1] != Outpar,
 			  "BUG: strange case pattern");
-		    str[sl-1] = '\0';
-		    chuck(str+1);
+		    str[sl - 1] = '\0';
+		    chuck(str);
 		    break;
 		} else {
 		    char *str2;
 
 		    if (tok != STRING)
-			YYERRORV;
+			YYERRORV(oecused);
 		    str2 = ncalloc(sl + strlen(tokstr) + 1);
 		    strcpy(str2, str);
 		    strcpy(str2 + sl, tokstr);
@@ -846,35 +1049,26 @@
 		}
 	    }
 	}
-	addlinknode(pats, str);
-	addlinknode(lists, par_list());
+	pp = ecadd(0);
+	ecstr(str);
+	ecadd(ecnpats++);
+	par_save_list(complex);
 	n++;
+	if (tok == SEMIAMP)
+	    type = WC_CASE_AND;
+	ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp);
 	if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag))
 	    break;
-	if(tok == SEMIAMP)
-	    *str = '&';
-	else if (tok != DSEMI)
-	    YYERRORV;
+	if (tok != DSEMI && tok != SEMIAMP)
+	    YYERRORV(oecused);
 	incasepat = 1;
 	incmdpos = 0;
 	yylex();
     }
-
     incmdpos = 1;
     yylex();
 
-    cc->pats = (char **) alloc((n + 1) * sizeof(char *));
-
-    for (pp = cc->pats, no = firstnode(pats);
-	 no; incnode(no))
-	*pp++ = (char *)getdata(no);
-    *pp = NULL;
-
-    cc->lists = (List *) alloc((n + 1) * sizeof(List));
-    for (ll = cc->lists, no = firstnode(lists); no; incnode(no), ll++)
-	if (!(*ll = (List) getdata(no)))
-	    *ll = &dummy_list;
-    *ll = NULL;
+    ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p);
 }
 
 /*
@@ -886,20 +1080,13 @@
 
 /**/
 static void
-par_if(Cmd c)
+par_if(int *complex)
 {
-    struct ifcmd *i;
-    int xtok;
+    int oecused = ecused, xtok, p, pp, type, usebrace = 0;
     unsigned char nc;
-    LinkList ifsl, thensl;
-    LinkNode no;
-    int ni = 0, nt = 0, usebrace = 0;
-    List l, *ll;
 
-    ifsl = newlinklist();
-    thensl = newlinklist();
+    p = ecadd(0);
 
-    c->type = CIF;
     for (;;) {
 	xtok = tok;
 	cmdpush(xtok == IF ? CS_IF : CS_ELIF);
@@ -912,10 +1099,11 @@
 	    yylex();
 	if (!(xtok == IF || xtok == ELIF)) {
 	    cmdpop();
-	    YYERRORV;
+	    YYERRORV(oecused);
 	}
-	addlinknode(ifsl, par_list());
-	ni++;
+	pp = ecadd(0);
+	type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF);
+	par_save_list(complex);
 	incmdpos = 1;
 	while (tok == SEPER)
 	    yylex();
@@ -926,79 +1114,63 @@
 	    cmdpop();
 	    cmdpush(nc);
 	    yylex();
-	    addlinknode(thensl, par_list());
-	    nt++;
+	    par_save_list(complex);
+	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
 	    incmdpos = 1;
 	    cmdpop();
-	} else {
-	    if (tok == INBRACE) {
-		usebrace = 1;
-		cmdpop();
-		cmdpush(nc);
-		yylex();
-		l = par_list();
-		if (tok != OUTBRACE) {
-		    cmdpop();
-		    YYERRORV;
-		}
-		addlinknode(thensl, l);
-		nt++;
-		yylex();
-		incmdpos = 1;
-		if (tok == SEPER)
-		    break;
-		cmdpop();
-	    } else if (unset(SHORTLOOPS)) {
+	} else if (tok == INBRACE) {
+	    usebrace = 1;
+	    cmdpop();
+	    cmdpush(nc);
+	    yylex();
+	    par_save_list(complex);
+	    if (tok != OUTBRACE) {
 		cmdpop();
-		YYERRORV;
-	    } else {
-		cmdpop();
-		cmdpush(nc);
-		addlinknode(thensl, par_list1());
-		nt++;
-		incmdpos = 1;
-		break;
+		YYERRORV(oecused);
 	    }
+	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
+	    yylex();
+	    incmdpos = 1;
+	    if (tok == SEPER)
+		break;
+	    cmdpop();
+	} else if (unset(SHORTLOOPS)) {
+	    cmdpop();
+	    YYERRORV(oecused);
+	} else {
+	    cmdpop();
+	    cmdpush(nc);
+	    par_save_list1(complex);
+	    ecbuf[pp] = WCB_IF(type, ecused - 1 - pp);
+	    incmdpos = 1;
+	    break;
 	}
     }
     cmdpop();
     if (xtok == ELSE) {
+	pp = ecadd(0);
 	cmdpush(CS_ELSE);
 	while (tok == SEPER)
 	    yylex();
 	if (tok == INBRACE && usebrace) {
 	    yylex();
-	    l = par_list();
+	    par_list(complex);
 	    if (tok != OUTBRACE) {
 		cmdpop();
-		YYERRORV;
+		YYERRORV(oecused);
 	    }
 	} else {
-	    l = par_list();
+	    par_list(complex);
 	    if (tok != FI) {
 		cmdpop();
-		YYERRORV;
+		YYERRORV(oecused);
 	    }
 	}
-	addlinknode(thensl, l);
-	nt++;
+	ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp);
 	yylex();
 	cmdpop();
     }
-    i = (struct ifcmd *)make_ifcmd();
-    i->ifls = (List *) alloc((ni + 1) * sizeof(List));
-    i->thenls = (List *) alloc((nt + 1) * sizeof(List));
-
-    for (ll = i->ifls, no = firstnode(ifsl); no; incnode(no), ll++)
-	if (!(*ll = (List) getdata(no)))
-	    *ll = &dummy_list;
-    *ll = NULL;
-    for (ll = i->thenls, no = firstnode(thensl); no; incnode(no), ll++)
-	if (!(*ll = (List) getdata(no)))
-	    *ll = &dummy_list;
-    *ll = NULL;
-
-    c->u.ifcmd = i;
+    ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p);
 }
 
 /*
@@ -1008,37 +1180,38 @@
 
 /**/
 static void
-par_while(Cmd c)
+par_while(int *complex)
 {
-    struct whilecmd *w;
+    int oecused = ecused, p;
+    int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE);
 
-    c->type = CWHILE;
-    w = c->u.whilecmd = (struct whilecmd *)make_whilecmd();
-    w->cond = (tok == UNTIL);
+    p = ecadd(0);
     yylex();
-    w->cont = par_list();
+    par_save_list(complex);
     incmdpos = 1;
     while (tok == SEPER)
 	yylex();
     if (tok == DO) {
 	yylex();
-	w->loop = par_list();
+	par_save_list(complex);
 	if (tok != DONE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (tok == INBRACE) {
 	yylex();
-	w->loop = par_list();
+	par_save_list(complex);
 	if (tok != OUTBRACE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (isset(CSHJUNKIELOOPS)) {
-	w->loop = par_list();
+	par_save_list(complex);
 	if (tok != ZEND)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else
-	YYERRORV;
+	YYERRORV(oecused);
+
+    ecbuf[p] = WCB_WHILE(type, ecused - 1 - p);
 }
 
 /*
@@ -1047,41 +1220,44 @@
 
 /**/
 static void
-par_repeat(Cmd c)
+par_repeat(int *complex)
 {
-    c->type = CREPEAT;
+    int oecused = ecused, p;
+
+    p = ecadd(0);
+
     incmdpos = 0;
     yylex();
     if (tok != STRING)
-	YYERRORV;
-    if (!c->args)
-	c->args = newlinklist();
-    addlinknode(c->args, tokstr);
+	YYERRORV(oecused);
+    ecstr(tokstr);
     incmdpos = 1;
     yylex();
     while (tok == SEPER)
 	yylex();
     if (tok == DO) {
 	yylex();
-	c->u.list = par_list();
+	par_save_list(complex);
 	if (tok != DONE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (tok == INBRACE) {
 	yylex();
-	c->u.list = par_list();
+	par_save_list(complex);
 	if (tok != OUTBRACE)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (isset(CSHJUNKIELOOPS)) {
-	c->u.list = par_list();
+	par_save_list(complex);
 	if (tok != ZEND)
-	    YYERRORV;
+	    YYERRORV(oecused);
 	yylex();
     } else if (unset(SHORTLOOPS)) {
-	YYERRORV;
+	YYERRORV(oecused);
     } else
-	c->u.list = par_list1();
+	par_save_list1(complex);
+
+    ecbuf[p] = WCB_REPEAT(ecused - 1 - p);
 }
 
 /*
@@ -1090,13 +1266,15 @@
 
 /**/
 static void
-par_subsh(Cmd c)
+par_subsh(int *complex)
 {
-    c->type = (tok == INPAR) ? SUBSH : CURSH;
+    int oecused = ecused, otok = tok;
+
+    ecadd(tok == INPAR ? WCB_SUBSH() : WCB_CURSH());
     yylex();
-    c->u.list = par_list();
-    if (tok != ((c->type == SUBSH) ? OUTPAR : OUTBRACE))
-	YYERRORV;
+    par_save_list(complex);
+    if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE))
+	YYERRORV(oecused);
     incmdpos = 1;
     yylex();
 }
@@ -1108,43 +1286,88 @@
 
 /**/
 static void
-par_funcdef(Cmd c)
+par_funcdef(void)
 {
-    int oldlineno = lineno;
+    int oecused = ecused, oldlineno = lineno, num = 0, sbeg, onp, p, c = 0;
+    Eccstr ostrs;
+
     lineno = 0;
     nocorrect = 1;
     incmdpos = 0;
     yylex();
-    c->type = FUNCDEF;
-    c->args = newlinklist();
+
+    p = ecadd(0);
+    ecadd(0);
+
     incmdpos = 1;
     while (tok == STRING) {
 	if (*tokstr == Inbrace && !tokstr[1]) {
 	    tok = INBRACE;
 	    break;
 	}
-	addlinknode(c->args, tokstr);
+	ecstr(tokstr);
+	num++;
 	yylex();
     }
+    ecadd(0);
+    ecadd(0);
+
     nocorrect = 0;
     if (tok == INOUTPAR)
 	yylex();
     while (tok == SEPER)
 	yylex();
+
+    sbeg = ecsoffs;
+    ecsoffs = 0;
+    ostrs = ecstrs;
+    ecstrs = NULL;
+    onp = ecnpats;
+    ecnpats = 0;
+
     if (tok == INBRACE) {
 	yylex();
-	c->u.list = par_list();
+	par_save_list(&c);
 	if (tok != OUTBRACE) {
 	    lineno += oldlineno;
-	    YYERRORV;
+	    ecsoffs = sbeg;
+	    ecstrs = ostrs;
+	    ecnpats = onp;
+	    YYERRORV(oecused);
 	}
 	yylex();
     } else if (unset(SHORTLOOPS)) {
 	lineno += oldlineno;
-	YYERRORV;
+	ecsoffs = sbeg;
+	ecstrs = ostrs;
+	ecnpats = onp;
+	YYERRORV(oecused);
     } else
-	c->u.list = par_list1();
+	par_save_list1(&c);
+
+    ecbuf[p + num + 2] = ecused - num - p;
+    ecbuf[p + num + 3] = ecnpats;
+    ecbuf[p + 1] = num;
+
+    if (ecsoffs) {
+	int beg = ecused, l;
+	Eccstr sp;
+	char *sq;
+
+	ecspace(ecsoffs);
+
+	for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp;
+	     sp = sp->next, sq += l) {
+	    l = strlen(sp->str) + 1;
+	    memcpy(sq, sp->str, l);
+	}
+    }
     lineno += oldlineno;
+    ecsoffs = sbeg;
+    ecstrs = ostrs;
+    ecnpats = onp;
+
+    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
 }
 
 /*
@@ -1153,11 +1376,17 @@
 
 /**/
 static void
-par_time(Cmd c)
+par_time(void)
 {
+    int p, f, c = 0;
+
     yylex();
-    c->type = ZCTIME;
-    c->u.pline = par_sublist2();
+
+    p = ecadd(0);
+    ecadd(0);
+    f = par_sublist2(&c);
+    ecbuf[p] = WCB_TIMED((p + 1 == ecused) ? WC_TIMED_EMPTY : WC_TIMED_PIPE);
+    set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c);
 }
 
 /*
@@ -1166,15 +1395,16 @@
 
 /**/
 static void
-par_dinbrack(Cmd c)
+par_dinbrack(void)
 {
-    c->type = COND;
+    int oecused = ecused;
+
     incond = 1;
     incmdpos = 0;
     yylex();
-    c->u.cond = par_cond();
+    par_cond();
     if (tok != DOUTBRACK)
-	YYERRORV;
+	YYERRORV(oecused);
     incond = 0;
     incmdpos = 1;
     yylex();
@@ -1187,293 +1417,157 @@
  */
 
 /**/
-static Cmd
-par_simple(Cmd c)
+static int
+par_simple(int *complex, int nr)
 {
-    int isnull = 1;
+    int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
+    int c = *complex;
 
-    c->type = SIMPLE;
+    r = ecused;
     for (;;) {
-	if (tok == NOCORRECT)
+	if (tok == NOCORRECT) {
+	    *complex = c = 1;
 	    nocorrect = 1;
-	else if (tok == ENVSTRING) {
-	    struct varasg *v = (struct varasg *)make_varnode();
-	    char *p;
+	} else if (tok == ENVSTRING) {
+	    char *p, *name, *str;
 
-	    v->type = PM_SCALAR;
-	    v->name = tokstr;
+	    ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, 0));
+	    name = tokstr;
 	    for (p = tokstr; *p && *p != Inbrack && *p != '='; p++);
 	    if (*p == Inbrack && !skipparens(Inbrack, Outbrack, &p) &&
 		*p == '=') {
 		*p = '\0';
-		v->str = p + 1;
+		str = p + 1;
 	    } else
-		equalsplit(tokstr, &v->str);
-	    if (!c->vars)
-		c->vars = newlinklist();
-	    addlinknode(c->vars, v);
+		equalsplit(tokstr, &str);
+	    ecstr(name);
+	    ecstr(str);
 	    isnull = 0;
 	} else if (tok == ENVARRAY) {
-	    struct varasg *v = (struct varasg *)make_varnode();
-	    int oldcmdpos = incmdpos;
+	    int oldcmdpos = incmdpos, n;
 
-	    v->type = PM_ARRAY;
+	    p = ecadd(0);
 	    incmdpos = 0;
-	    v->name = tokstr;
+	    ecstr(tokstr);
 	    cmdpush(CS_ARRAY);
 	    yylex();
-	    v->arr = par_nl_wordlist();
+	    n = par_nl_wordlist();
+	    ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, n);
 	    cmdpop();
 	    if (tok != OUTPAR)
-		YYERROR;
+		YYERROR(oecused);
 	    incmdpos = oldcmdpos;
-	    if (!c->vars)
-		c->vars = newlinklist();
-	    addlinknode(c->vars, v);
 	    isnull = 0;
 	} else
 	    break;
 	yylex();
     }
     if (tok == AMPER || tok == AMPERBANG)
-	YYERROR;
+	YYERROR(oecused);
+
+    p = ecadd(WCB_SIMPLE(0));
+
     for (;;) {
 	if (tok == STRING) {
+	    *complex = 1;
 	    incmdpos = 0;
-	    if (!c->args)
-		c->args = newlinklist();
-	    addlinknode(c->args, tokstr);
+	    ecstr(tokstr);
+	    argc++;
 	    yylex();
 	} else if (IS_REDIROP(tok)) {
-	    if (!c->redir)
-		c->redir = newlinklist();
-	    par_redir(c->redir);
+	    *complex = c = 1;
+	    par_redir(&r);
+	    p += 3;		/* 3 codes per redirection */
+	    sr++;
 	} else if (tok == INOUTPAR) {
-	    int oldlineno = lineno;
+	    int oldlineno = lineno, sbeg, onp;
+	    Eccstr ostrs;
+
+	    *complex = c;
 	    lineno = 0;
 	    incmdpos = 1;
 	    cmdpush(CS_FUNCDEF);
 	    yylex();
 	    while (tok == SEPER)
 		yylex();
+
+	    ecispace(p + 1, 1);
+	    ecbuf[p + 1] = argc;
+	    ecadd(0);
+	    ecadd(0);
+
+	    sbeg = ecsoffs;
+	    ecsoffs = 0;
+	    ostrs = ecstrs;
+	    ecstrs = NULL;
+	    onp = ecnpats;
+	    ecnpats = 0;
+
 	    if (tok == INBRACE) {
+		int c = 0;
+
 		yylex();
-		c->u.list = par_list();
+		par_list(&c);
 		if (tok != OUTBRACE) {
 		    cmdpop();
 		    lineno += oldlineno;
-		    YYERROR;
+		    ecsoffs = sbeg;
+		    ecstrs = ostrs;
+		    ecnpats = onp;
+		    YYERROR(oecused);
 		}
 		yylex();
 	    } else {
-		List l;
-		Sublist sl;
-		Pline pl;
-
-		l = (List) allocnode(sizeof(*l), N_LIST);
-		l->type = Z_SYNC;
-		l->left = sl = (Sublist) allocnode(sizeof(*sl), N_SUBLIST);
-		sl->type = END;
-		sl->left = pl = (Pline) allocnode(sizeof(*pl), N_PLINE);
-		pl->type = END;
-		pl->left = par_cmd();
-		c->u.list = l;
-	    }
-	    cmdpop();
-	    c->type = FUNCDEF;
-	    lineno += oldlineno;
-	} else
-	    break;
-	isnull = 0;
-    }
-    if (isnull && (!c->redir || empty(c->redir)))
-	return NULL;
-    incmdpos = 1;
-    return c;
-}
-
-/*
- * condlex is yylex for normal parsing, but is altered to allow
- * the test builtin to use par_cond.
- */
-
-/**/
-void (*condlex) _((void)) = yylex;
-
-/*
- * cond	: cond_1 { SEPER } [ DBAR { SEPER } cond ]
- */
-
-/**/
-static Cond
-par_cond(void)
-{
-    Cond c, c2;
+		int ll, sl, c = 0;
 
-    c = par_cond_1();
-    while (tok == SEPER)
-	condlex();
-    if (tok == DBAR) {
-	condlex();
-	while (tok == SEPER)
-	    condlex();
-	c2 = (Cond) make_cond();
-	c2->left = (void *) c;
-	c2->right = (void *) par_cond();
-	c2->type = COND_OR;
-	return c2;
-    }
-    return c;
-}
+		ll = ecadd(0);
+		sl = ecadd(0);
 
-/*
- * cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ]
- */
+		par_cmd(&c);
 
-/**/
-static Cond
-par_cond_1(void)
-{
-    Cond c, c2;
+		set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c);
+		set_list_code(ll, (Z_SYNC | Z_END), c);
+	    }
+	    cmdpop();
 
-    c = par_cond_2();
-    while (tok == SEPER)
-	condlex();
-    if (tok == DAMPER) {
-	condlex();
-	while (tok == SEPER)
-	    condlex();
-	c2 = (Cond) make_cond();
-	c2->left = (void *) c;
-	c2->right = (void *) par_cond_1();
-	c2->type = COND_AND;
-	return c2;
-    }
-    return c;
-}
+	    ecbuf[p + argc + 2] = ecused - argc - p;
+	    ecbuf[p + argc + 3] = ecnpats;
 
-/*
- * cond_2	: BANG cond_2
-				| INPAR { SEPER } cond_2 { SEPER } OUTPAR
-				| STRING STRING STRING
-				| STRING STRING
-				| STRING ( INANG | OUTANG ) STRING
- */
+	    if (ecsoffs) {
+		int beg = ecused, l;
+		Eccstr sp;
+		char *sq;
+
+		ecspace(ecsoffs);
+
+		for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp;
+		     sp = sp->next, sq += l) {
+		    l = strlen(sp->str) + 1;
+		    memcpy(sq, sp->str, l);
+		}
+	    }
+	    lineno += oldlineno;
+	    ecsoffs = sbeg;
+	    ecstrs = ostrs;
+	    ecnpats = onp;
 
-/**/
-static Cond
-par_cond_2(void)
-{
-    Cond c, c2;
-    char *s1, *s2, *s3;
-    int dble = 0;
+	    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
 
-    if (condlex == testlex) {
-	/* See the description of test in POSIX 1003.2 */
-	if (tok == NULLTOK)
-	    /* no arguments: false */
-	    return par_cond_double(dupstring("-n"), dupstring(""));
-	if (!*testargs) {
-	    /* one argument: [ foo ] is equivalent to [ -n foo ] */
-	    s1 = tokstr;
-	    condlex();
-	    return par_cond_double(dupstring("-n"), s1);
-	}
-	if (testargs[1] && !testargs[2]) {
-	    /* three arguments: if the second argument is a binary operator, *
-	     * perform that binary test on the first and the trird argument  */
-	    if (!strcmp(*testargs, "=")  ||
-		!strcmp(*testargs, "==") ||
-		!strcmp(*testargs, "!=") ||
-		(**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
-		s1 = tokstr;
-		condlex();
-		s2 = tokstr;
-		condlex();
-		s3 = tokstr;
-		condlex();
-		return par_cond_triple(s1, s2, s3);
-	    }
-	}
-    }
-    if (tok == BANG) {
-	condlex();
-	c = par_cond_2();
-	c2 = (Cond) make_cond();
-	c2->left = (void *) c;
-	c2->type = COND_NOT;
-	return c2;
-    }
-    if (tok == INPAR) {
-	condlex();
-	while (tok == SEPER)
-	    condlex();
-	c = par_cond();
-	while (tok == SEPER)
-	    condlex();
-	if (tok != OUTPAR)
-	    YYERROR;
-	condlex();
-	return c;
-    }
-    if (tok != STRING) {
-	if (tok && tok != LEXERR && condlex == testlex) {
-	    s1 = tokstr;
-	    condlex();
-	    return par_cond_double("-n", s1);
+	    isfunc = 1;
 	} else
-	    YYERROR;
-    }
-    s1 = tokstr;
-    if (condlex == testlex)
-	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
-		  && !s1[2]);
-    condlex();
-    if (tok == INANG || tok == OUTANG) {
-	int xtok = tok;
-	condlex();
-	if (tok != STRING)
-	    YYERROR;
-	s3 = tokstr;
-	condlex();
-	c = (Cond) make_cond();
-	c->left = (void *) s1;
-	c->right = (void *) s3;
-	c->type = (xtok == INANG) ? COND_STRLT : COND_STRGTR;
-	c->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
-	return c;
+	    break;
+	isnull = 0;
     }
-    if (tok != STRING) {
-	if (tok != LEXERR && condlex == testlex) {
-	    if (!dble)
-		return par_cond_double("-n", s1);
-	    else if (!strcmp(s1, "-t"))
-		return par_cond_double(s1, "1");
-	} else
-	    YYERROR;
+    if (isnull && !(sr + nr)) {
+	ecused = p;
+	return 0;
     }
-    s2 = tokstr;
-    incond++;			/* parentheses do globbing */
-    condlex();
-    incond--;			/* parentheses do grouping */
-    if (tok == STRING && !dble) {
-	s3 = tokstr;
-	condlex();
-	if (tok == STRING) {
-	    LinkList l = newlinklist();
+    incmdpos = 1;
 
-	    addlinknode(l, s2);
-	    addlinknode(l, s3);
+    if (!isfunc)
+	ecbuf[p] = WCB_SIMPLE(argc);
 
-	    while (tok == STRING) {
-		addlinknode(l, tokstr);
-		condlex();
-	    }
-	    return par_cond_multi(s1, l);
-	} else
-	    return par_cond_triple(s1, s2, s3);
-    } else
-	return par_cond_double(s1, s2);
+    return sr + 1;
 }
 
 /*
@@ -1500,32 +1594,31 @@
 
 /**/
 static void
-par_redir(LinkList l)
+par_redir(int *rp)
 {
-    struct redir *fn = (struct redir *)
-	allocnode(sizeof(struct redir), N_REDIR);
-    int oldcmdpos, oldnc;
+    int r = *rp, type, fd1, oldcmdpos, oldnc;
+    char *name;
 
     oldcmdpos = incmdpos;
     incmdpos = 0;
     oldnc = nocorrect;
     if (tok != INANG && tok != INOUTANG)
 	nocorrect = 1;
-    fn->type = redirtab[tok - OUTANG];
-    fn->fd1 = tokfd;
+    type = redirtab[tok - OUTANG];
+    fd1 = tokfd;
     yylex();
     if (tok != STRING && tok != ENVSTRING)
-	YYERRORV;
+	YYERRORV(ecused);
     incmdpos = oldcmdpos;
     nocorrect = oldnc;
 
     /* assign default fd */
-    if (fn->fd1 == -1)
-	fn->fd1 = IS_READFD(fn->type) ? 0 : 1;
+    if (fd1 == -1)
+	fd1 = IS_READFD(type) ? 0 : 1;
 
-    fn->name = tokstr;
+    name = tokstr;
 
-    switch (fn->type) {
+    switch (type) {
     case HEREDOC:
     case HEREDOCDASH: {
 	/* <<[-] name */
@@ -1534,31 +1627,56 @@
 	for (hd = &hdocs; *hd; hd = &(*hd)->next);
 	*hd = zalloc(sizeof(struct heredocs));
 	(*hd)->next = NULL;
-	(*hd)->rd = fn;
-	break;
+	(*hd)->pc = ecbuf + r;
+	(*hd)->str = tokstr;
+
+	/* If we ever need more than three codes (or less), we have to change
+	 * the factors in par_cmd() and par_simple(), too. */
+	ecispace(r, 3);
+	*rp = r + 3;
+	ecbuf[r] = WCB_REDIR(type);
+	ecbuf[r + 1] = fd1;
+
+	yylex();
+	return;
     }
     case WRITE:
     case WRITENOW:
 	if (tokstr[0] == Outang && tokstr[1] == Inpar)
 	    /* > >(...) */
-	    fn->type = OUTPIPE;
+	    type = OUTPIPE;
 	else if (tokstr[0] == Inang && tokstr[1] == Inpar)
-	    YYERRORV;
+	    YYERRORV(ecused);
 	break;
     case READ:
 	if (tokstr[0] == Inang && tokstr[1] == Inpar)
 	    /* < <(...) */
-	    fn->type = INPIPE;
+	    type = INPIPE;
 	else if (tokstr[0] == Outang && tokstr[1] == Inpar)
-	    YYERRORV;
+	    YYERRORV(ecused);
 	break;
     case READWRITE:
 	if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
-	    fn->type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
+	    type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
 	break;
     }
     yylex();
-    addlinknode(l, fn);
+
+    /* If we ever need more than three codes (or less), we have to change
+     * the factors in par_cmd() and par_simple(), too. */
+    ecispace(r, 3);
+    *rp = r + 3;
+    ecbuf[r] = WCB_REDIR(type);
+    ecbuf[r + 1] = fd1;
+    ecbuf[r + 2] = ecstrcode(name);
+}
+
+/**/
+void
+setheredoc(Wordcode pc, int type, char *str)
+{
+    pc[0] = WCB_REDIR(type);
+    pc[2] = ecstrcode(str);
 }
 
 /*
@@ -1566,17 +1684,16 @@
  */
 
 /**/
-static LinkList
+static int
 par_wordlist(void)
 {
-    LinkList l;
-
-    l = newlinklist();
+    int num = 0;
     while (tok == STRING) {
-	addlinknode(l, tokstr);
+	ecstr(tokstr);
+	num++;
 	yylex();
     }
-    return l;
+    return num;
 }
 
 /*
@@ -1584,788 +1701,309 @@
  */
 
 /**/
-static LinkList
+static int
 par_nl_wordlist(void)
 {
-    LinkList l;
+    int num = 0;
 
-    l = newlinklist();
     while (tok == STRING || tok == SEPER) {
-	if (tok != SEPER)
-	    addlinknode(l, tokstr);
+	if (tok != SEPER) {
+	    ecstr(tokstr);
+	    num++;
+	}
 	yylex();
     }
-    return l;
+    return num;
 }
 
-/**/
-static Cond
-par_cond_double(char *a, char *b)
-{
-    Cond n = (Cond) make_cond();
-
-    n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
-    n->left = (void *) b;
-    if (a[0] != '-' || !a[1])
-	COND_ERROR("parse error: condition expected: %s", a);
-    else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1)
-	n->type = a[1];
-    else {
-	char *d[2];
-
-	n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
-	n->type = COND_MOD;
-	n->left = (void *) a;
-	d[0] = b;
-	d[1] = NULL;
-	n->right = (void *) arrdup(d);
-    }
-    return n;
-}
+/*
+ * condlex is yylex for normal parsing, but is altered to allow
+ * the test builtin to use par_cond.
+ */
 
 /**/
-static int
-get_cond_num(char *tst)
-{
-    static char *condstrs[] =
-    {
-	"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
-    };
-    int t0;
+void (*condlex) _((void)) = yylex;
 
-    for (t0 = 0; condstrs[t0]; t0++)
-	if (!strcmp(condstrs[t0], tst))
-	    return t0;
-    return -1;
-}
+/*
+ * cond	: cond_1 { SEPER } [ DBAR { SEPER } cond ]
+ */
 
 /**/
-static Cond
-par_cond_triple(char *a, char *b, char *c)
-{
-    Cond n = (Cond) make_cond();
-    int t0;
-
-    n->left = (void *) a;
-    n->right = (void *) c;
-    if ((b[0] == Equals || b[0] == '=') &&
-	(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) {
-	n->ntype = NT_SET(N_COND, NT_STR, NT_STR, NT_PAT, 0);
-	n->type = COND_STREQ;
-    } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) {
-	n->ntype = NT_SET(N_COND, NT_STR, NT_STR, NT_PAT, 0);
-	n->type = COND_STRNEQ;
-    } else if (b[0] == '-') {
-	if ((t0 = get_cond_num(b + 1)) > -1) {
-	    n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
-	    n->type = t0 + COND_NT;
-	} else {
-	    char *d[3];
-
-	    n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
-	    n->type = COND_MODI;
-	    n->left = (void *) b;
-	    d[0] = a;
-	    d[1] = c;
-	    d[2] = NULL;
-	    n->right = (void *) arrdup(d);
-	}
-    } else if (a[0] == '-' && a[1]) {
-	char *d[3];
-
-	n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
-	n->type = COND_MOD;
-	n->left = (void *) a;
-	d[0] = b;
-	d[1] = c;
-	d[2] = NULL;
-	n->right = (void *) arrdup(d);
-    } else
-	COND_ERROR("condition expected: %s", b);
-    return n;
-}
-
-/**/
-static Cond
-par_cond_multi(char *a, LinkList l)
-{
-    Cond n = (Cond) make_cond();
-
-    n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
-    if (a[0] != '-' || !a[1])
-	COND_ERROR("condition expected: %s", a);
-    else {
-	n->type = COND_MOD;
-	n->left = (void *) a;
-	n->right = (void *) listarr(l);
-    }
-    return n;
-}
-
-/**/
-static void
-yyerror(int noerr)
-{
-    int t0;
-
-    for (t0 = 0; t0 != 20; t0++)
-	if (!yytext || !yytext[t0] || yytext[t0] == '\n')
-	    break;
-    if (t0 == 20)
-	zwarn("parse error near `%l...'", yytext, 20);
-    else if (t0)
-	zwarn("parse error near `%l'", yytext, t0);
-    else
-	zwarn("parse error", NULL, 0);
-    if (!noerr && noerrs != 2)
-	errflag = 1;
-}
-
-/* 
- * Word code.
- *
- * For now we simply post-process the syntax tree produced by the
- * parser. We compile it into a struct eprog. Some day the parser
- * above should be changed to emit the word code directly.
- *
- * Word code layout:
- *
- *   WC_END
- *     - end of program code
- *
- *   WC_LIST
- *     - data contains type (sync, ...)
- *     - follwed by code for this list
- *     - if not (type & Z_END), followed by next WC_LIST
- *
- *   WC_SUBLIST
- *     - data contains type (&&, ||, END) and flags (coprog, not)
- *     - followed by code for sublist
- *     - if not (type == END), followed by next WC_SUBLIST
- *
- *   WC_PIPE
- *     - data contains type (end, mid) and LINENO
- *     - if not (type == END), followed by offset to next WC_PIPE
- *     - followed by command
- *     - if not (type == END), followed by next WC_PIPE
- *
- *   WC_REDIR
- *     - must precede command-code (or WC_ASSIGN)
- *     - data contains type (<, >, ...)
- *     - followed by fd1 and name from struct redir
- *
- *   WC_ASSIGN
- *     - data contains type (scalar, array) and number of array-elements
- *     - followed by name and value
- *
- *   WC_SIMPLE
- *     - data contains the number of arguments (plus command)
- *     - followed by strings
- *
- *   WC_SUBSH
- *     - data unused
- *     - followed by list
- *
- *   WC_CURSH
- *     - data unused
- *     - followed by list
- *
- *   WC_TIMED
- *     - data contains type (followed by pipe or not)
- *     - if (type == PIPE), followed by pipe
- *
- *   WC_FUNCDEF
- *     - data contains offset to after body-strings
- *     - followed by number of names
- *     - followed by names
- *     - followed by number of codes for body
- *     - followed by number of patterns for body
- *     - follwoed by codes for body
- *     - followed by strings for body
- *
- *   WC_FOR
- *     - data contains type (list, ...) and offset to after body
- *     - if (type == COND), followed by init, cond, advance expressions
- *     - else if (type == PPARAM), followed by param name
- *     - else if (type == LIST), followed by param name, num strings, strings
- *     - followed by body
- *
- *   WC_SELECT
- *     - data contains type (list, ...) and offset to after body
- *     - if (type == PPARAM), followed by param name
- *     - else if (type == LIST), followed by param name, num strings, strings
- *     - followed by body
- *
- *   WC_WHILE
- *     - data contains type (while, until) and ofsset to after body
- *     - followed by condition
- *     - followed by body
- *
- *   WC_REPEAT
- *     - data contains offset to after body
- *     - followed by number-string
- *     - followed by body
- *
- *   WC_CASE
- *     - first CASE is always of type HEAD, data contains offset to esac
- *     - after that CASEs of type OR (;;) and AND (;&), data is offset to
- *       next case
- *     - each OR/AND case is followed by pattern, pattern-number, list
- *
- *   WC_IF
- *     - first IF is of type HEAD, data contains offset to fi
- *     - after that IFs of type IF, ELIF, ELSE, data is offset to next
- *     - each non-HEAD is followed by condition (only IF, ELIF) and body
- *
- *   WC_COND
- *     - data contains type
- *     - if (type == AND/OR), data contains offset to after this one,
- *       followed by two CONDs
- *     - else if (type == NOT), followed by COND
- *     - else if (type == MOD), followed by name and strings
- *     - else if (type == MODI), followed by name, left, right
- *     - else if (type == STR[N]EQ), followed by left, right, pattern-number
- *     - else if (has two args) followed by left, right
- *     - else followed by string
- *
- *   WC_ARITH
- *     - followed by string (there's only one)
- *
- *   WC_AUTOFN
- *     - only used by the autoload builtin
- *
- * In each of the above, strings are encoded as one word code. For empty
- * strings this is the bit pattern 0xfe000000. For short strings (one to
- * three characters), this is the marker 0xff000000 with the lower three
- * bytes containing the characters. Longer strings are encoded as the
- * offset into the strs character array stored in the eprog struct.
- * The ecstr() function that adds the code for a string uses a simple
- * list of strings already added so that long strings are encoded only
- * once.
- *
- * Note also that in the eprog struct the pattern, code, and string
- * arrays all point to the same memory block.
- */
-
-static int eclen, ecused, ecfree, ecnpats;
-static Wordcode ecbuf;
-
-typedef struct eccstr *Eccstr;
-
-struct eccstr {
-    Eccstr next;
-    char *str;
-    wordcode offs;
-};
-
-static Eccstr ecstrs;
-static int ecsoffs;
-
-/* Make at least n bytes free (aligned to sizeof(wordcode)). */
-
 static int
-ecspace(int n)
-{
-    n = (n + sizeof(wordcode) - 1) / sizeof(wordcode);
-
-    if (ecfree < n) {
-	int a = (n > 256 ? n : 256);
-
-	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
-				    (eclen + a) * sizeof(wordcode));
-	eclen += a;
-	ecfree += a;
-    }
-    ecused += n;
-    ecfree -= n;
-
-    return ecused - 1;
-}
-
-/* Add one wordcode. */
-
-static int
-ecadd(wordcode c)
-{
-    if (ecfree < 1) {
-	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
-				    (eclen + 256) * sizeof(wordcode));
-	eclen += 256;
-	ecfree += 256;
-    }
-    ecbuf[ecused] = c;
-    ecused++;
-    ecfree--;
-
-    return ecused - 1;
-}
-
-/* Add a string and the wordcode for it. */
-
-static int
-ecstr(char *s)
-{
-    int l;
-
-    if ((l = strlen(s) + 1) && l <= 4) {
-	wordcode c = 0xff000000;
-	switch (l) {
-	case 4: c |= ((wordcode) STOUC(s[2])) << 16;
-	case 3: c |= ((wordcode) STOUC(s[1])) <<  8;
-	case 2: c |= ((wordcode) STOUC(s[0])); break;
-	case 1: c = 0xfe000000;   break;
-	}
-	return ecadd(c);
-    } else {
-	Eccstr p, q = NULL;
-
-	for (p = ecstrs; p; q = p, p = p->next)
-	    if (!strcmp(s, p->str))
-		return ecadd(p->offs);
-
-	p = (Eccstr) zhalloc(sizeof(*p));
-	p->next = NULL;
-	if (q)
-	    q->next = p;
-	else
-	    ecstrs = p;
-	p->offs = ecsoffs;
-	p->str = s;
-	ecsoffs += l;
-
-	return ecadd(p->offs);
-    }
-}
-
-#define ec(N) ecomp((struct node *) (N))
-#define ecsave(N) \
-  do { int u = ecused; ec(N); if (u == ecused) ecadd(WCB_END()); } while (0)
-
-#define _Cond(X) ((Cond) (X))
-#define _Cmd(X) ((Cmd) (X))
-#define _Pline(X) ((Pline) (X))
-#define _Sublist(X) ((Sublist) (X))
-#define _List(X) ((List) (X))
-#define _casecmd(X) ((struct casecmd *) (X))
-#define _ifcmd(X) ((struct ifcmd *) (X))
-#define _whilecmd(X) ((struct whilecmd *) (X))
-
-#define cont(N) do { n = (struct node *) (N); goto rec; } while (0)
-
-/* Compile a node. */
-
-static void
-ecomp(struct node *n)
-{
-    int p, c;
-
- rec:
-
-    if (!n || ((List) n) == &dummy_list)
-	return;
-
-    switch (NT_TYPE(n->ntype)) {
-    case N_LIST:
-	ecadd(WCB_LIST(_List(n)->type | (_List(n)->right ? 0 : Z_END)));
-	if (_List(n)->right) {
-	    ec(_List(n)->left);
-	    cont(_List(n)->right);
-	} else
-	    cont(_List(n)->left);
-	break;
-    case N_SUBLIST:
-	p = ecadd(0);
-	ec(_Sublist(n)->left);
-	ecbuf[p] = WCB_SUBLIST((_Sublist(n)->right ?
-				((_Sublist(n)->type == ORNEXT) ?
-				 WC_SUBLIST_OR : WC_SUBLIST_AND) :
-				WC_SUBLIST_END),
-			       (((_Sublist(n)->flags & PFLAG_NOT) ?
-				 WC_SUBLIST_NOT : 0) |
-				((_Sublist(n)->flags & PFLAG_COPROC) ?
-				 WC_SUBLIST_COPROC : 0)),
-			       (ecused - 1 - p));
-	if (_Sublist(n)->right)
-	    cont(_Sublist(n)->right);
-	break;
-    case N_PLINE:
-	ecadd(WCB_PIPE((_Pline(n)->right ? WC_PIPE_MID : WC_PIPE_END),
-		       (_Cmd(_Pline(n)->left)->lineno >= 0 ?
-			_Cmd(_Pline(n)->left)->lineno + 1 : 0)));
-	if (_Pline(n)->right) {
-	    p = ecadd(0);
-	    ec(_Pline(n)->left);
-	    ecbuf[p] = (wordcode) (ecused - p);
-	    cont(_Pline(n)->right);
-	} else
-	    cont(_Pline(n)->left);
-	break;
-    case N_CMD:
-	{
-	    Cmd nn = _Cmd(n);
-
-	    /* Note that the execution and text code require that the
-	     * redirs and assignments are in exactly this order and that
-	     * they are before the command. */
-
-	    ecredirs(nn->redir);
-
-	    switch (nn->type) {
-	    case SIMPLE:
-		{
-		    int num = 0;
-
-		    ecassigns(nn->vars);
-		    p = ecadd(0);
-
-		    if (nn->args) {
-			LinkNode ap;
-
-			for (ap = firstnode(nn->args); ap;
-			     incnode(ap), num++)
-			    ecstr((char *) getdata(ap));
-		    }
-		    ecbuf[p] = WCB_SIMPLE(num);
-		}
-		break;
-	    case SUBSH:
-		ecadd(WCB_SUBSH());
-		ecsave(nn->u.list);
-		break;
-	    case ZCTIME:
-		ecadd(WCB_TIMED(nn->u.pline ? WC_TIMED_PIPE : WC_TIMED_EMPTY));
-		if (nn->u.pline)
-		    ec(nn->u.pline);
-		break;
-	    case FUNCDEF:
-		{
-		    LinkNode np;
-		    int num, sbeg, onp;
-		    Eccstr ostrs;
-
-		    /* Defined functions and their strings are stored
-		     * inline. */
-
-		    p = ecadd(0);
-		    ecadd(0);
-
-		    for (np = firstnode(nn->args), num = 0; np;
-			 incnode(np), num++)
-			ecstr((char *) getdata(np));
-
-		    ecadd(0);
-		    ecadd(0);
-
-		    sbeg = ecsoffs;
-		    ecsoffs = 0;
-		    ostrs = ecstrs;
-		    ecstrs = NULL;
-		    onp = ecnpats;
-		    ecnpats = 0;
-
-		    ecsave(nn->u.list);
-
-		    ecbuf[p + num + 2] = ecused - num - p;
-		    ecbuf[p + num + 3] = ecnpats;
-		    ecbuf[p + 1] = num;
-
-		    if (ecsoffs) {
-			int beg = ecused, l;
-			Eccstr sp;
-			char *sq;
-
-			ecspace(ecsoffs);
-
-			for (sp = ecstrs, sq = (char *) (ecbuf + beg); sp;
-			     sp = sp->next, sq += l) {
-			    l = strlen(sp->str) + 1;
-			    memcpy(sq, sp->str, l);
-			}
-		    }
-		    ecsoffs = sbeg;
-		    ecstrs = ostrs;
-		    ecnpats = onp;
-
-		    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
-		}
-		break;
-	    case CURSH:
-		ecadd(WCB_CURSH());
-		ecsave(nn->u.list);
-		break;
-	    case CFOR:
-		{
-		    int type;
-
-		    p = ecadd(0);
-		    ecstr(nn->u.forcmd->name);
-
-		    if (nn->u.forcmd->condition) {
-			type = WC_FOR_COND;
-			ecstr(nn->u.forcmd->condition);
-			ecstr(nn->u.forcmd->advance);
-		    } else {
-			if (nn->args) {
-			    LinkNode fp;
-			    int num;
-
-			    type = WC_FOR_LIST;
-
-			    ecadd(0);
-
-			    for (fp = firstnode(nn->args), num = 0; fp;
-				 incnode(fp), num++)
-				ecstr((char *) getdata(fp));
-
-			    ecbuf[p + 2] = num;
-			} else
-			    type = WC_FOR_PPARAM;
-		    }
-		    ecsave(nn->u.forcmd->list);
-
-		    ecbuf[p] = WCB_FOR(type, ecused - 1 - p);
-		}
-		break;
-	    case CSELECT:
-		{
-		    int type;
+par_cond(void)
+{
+    int p = ecused, r;
 
-		    p = ecadd(0);
-		    ecstr(nn->u.forcmd->name);
+    r = par_cond_1();
+    while (tok == SEPER)
+	condlex();
+    if (tok == DBAR) {
+	condlex();
+	while (tok == SEPER)
+	    condlex();
+	ecispace(p, 1);
+	par_cond();
+	ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p);
+	return 1;
+    }
+    return r;
+}
 
-		    if (nn->args) {
-			LinkNode fp;
-			int num;
+/*
+ * cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ]
+ */
 
-			type = WC_SELECT_LIST;
-			ecadd(0);
+/**/
+static int
+par_cond_1(void)
+{
+    int r, p = ecused;
 
-			for (fp = firstnode(nn->args), num = 0; fp;
-			     incnode(fp), num++)
-			    ecstr((char *) getdata(fp));
+    r = par_cond_2();
+    while (tok == SEPER)
+	condlex();
+    if (tok == DAMPER) {
+	condlex();
+	while (tok == SEPER)
+	    condlex();
+	ecispace(p, 1);
+	par_cond_1();
+	ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p);
+	return 1;
+    }
+    return r;
+}
 
-			ecbuf[p + 2] = num;
-		    } else
-			type = WC_SELECT_PPARAM;
+/*
+ * cond_2	: BANG cond_2
+				| INPAR { SEPER } cond_2 { SEPER } OUTPAR
+				| STRING STRING STRING
+				| STRING STRING
+				| STRING ( INANG | OUTANG ) STRING
+ */
 
-		    ecsave(nn->u.forcmd->list);
+/**/
+static int
+par_cond_2(void)
+{
+    char *s1, *s2, *s3;
+    int dble = 0;
 
-		    ecbuf[p] = WCB_SELECT(type, ecused - 1 - p);
-		}
-		break;
-	    case CIF:
-		{
-		    List *i, *t;
-		    int type = WC_IF_IF;
-
-		    c = ecadd(0);
-
-		    for (i = nn->u.ifcmd->ifls, t = nn->u.ifcmd->thenls;
-			 *i; i++, t++) {
-			p = ecadd(0);
-			ecsave(*i);
-			ecsave(*t);
-			ecbuf[p] = WCB_IF(type, ecused - 1 - p);
-			type = WC_IF_ELIF;
-		    }
-		    if (*t) {
-			p = ecadd(0);
-			ecsave(*t);
-			ecbuf[p] = WCB_IF(WC_IF_ELSE, ecused - 1 - p);
-		    }
-		    ecbuf[c] = WCB_IF(WC_IF_HEAD, ecused - 1 - c);
-		}
-		break;
-	    case CCASE:
-		{
-		    List *l;
-		    char **pp = nn->u.casecmd->pats;
-
-		    p = ecadd(0);
-		    ecstr(*pp++);
-
-		    for (l = nn->u.casecmd->lists; l && *l; l++, pp++) {
-			c = ecadd(0);
-			ecstr(*pp + 1);
-			ecadd(ecnpats++);
-			ecsave(*l);
-			ecbuf[c] = WCB_CASE((**pp == ';' ?
-					     WC_CASE_OR : WC_CASE_AND),
-					    ecused - 1 - c);
-		    }
-		    ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p);
-		}
-		break;
-	    case COND:
-		eccond(nn->u.cond);
-		break;
-	    case CARITH:
-		ecadd(WCB_ARITH());
-		ecstr((char *) getdata(firstnode(nn->args)));
-		break;
-	    case CREPEAT:
-		p = ecadd(0);
-		ecstr((char *) getdata(firstnode(nn->args)));
-		ecsave(nn->u.list);
-		ecbuf[p] = WCB_REPEAT(ecused - 1 - p);
-		break;
-	    case CWHILE:
-		p = ecadd(0);
-		ecsave(nn->u.whilecmd->cont);
-		ecsave(nn->u.whilecmd->loop);
-		ecbuf[p] = WCB_WHILE((nn->u.whilecmd->cond ?
-				      WC_WHILE_UNTIL : WC_WHILE_WHILE),
-				     ecused - 1 - p);
-		break;
+    if (condlex == testlex) {
+	/* See the description of test in POSIX 1003.2 */
+	if (tok == NULLTOK)
+	    /* no arguments: false */
+	    return par_cond_double(dupstring("-n"), dupstring(""));
+	if (!*testargs) {
+	    /* one argument: [ foo ] is equivalent to [ -n foo ] */
+	    s1 = tokstr;
+	    condlex();
+	    return par_cond_double(dupstring("-n"), s1);
+	}
+	if (testargs[1] && !testargs[2]) {
+	    /* three arguments: if the second argument is a binary operator, *
+	     * perform that binary test on the first and the trird argument  */
+	    if (!strcmp(*testargs, "=")  ||
+		!strcmp(*testargs, "==") ||
+		!strcmp(*testargs, "!=") ||
+		(**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
+		s1 = tokstr;
+		condlex();
+		s2 = tokstr;
+		condlex();
+		s3 = tokstr;
+		condlex();
+		return par_cond_triple(s1, s2, s3);
 	    }
 	}
-	break;
-    case N_COND:
-	eccond((Cond) n);
-	break;
-#ifdef DEBUG
-    default:
-	dputs("BUG: node type not handled in ecomp().");
-	break;
-#endif
     }
-}
+    if (tok == BANG) {
+	condlex();
+	ecadd(WCB_COND(COND_NOT, 0));
+	return par_cond_2();
+    }
+    if (tok == INPAR) {
+	int r;
 
-/**/
-static void
-ecredirs(LinkList l)
-{
-    LinkNode n;
-    Redir f;
+	condlex();
+	while (tok == SEPER)
+	    condlex();
+	r = par_cond();
+	while (tok == SEPER)
+	    condlex();
+	if (tok != OUTPAR)
+	    YYERROR(ecused);
+	condlex();
+	return r;
+    }
+    if (tok != STRING) {
+	if (tok && tok != LEXERR && condlex == testlex) {
+	    s1 = tokstr;
+	    condlex();
+	    return par_cond_double("-n", s1);
+	} else
+	    YYERROR(ecused);
+    }
+    s1 = tokstr;
+    if (condlex == testlex)
+	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
+		  && !s1[2]);
+    condlex();
+    if (tok == INANG || tok == OUTANG) {
+	int xtok = tok;
+	condlex();
+	if (tok != STRING)
+	    YYERROR(ecused);
+	s3 = tokstr;
+	condlex();
+	ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0));
+	ecstr(s1);
+	ecstr(s3);
+	return 1;
+    }
+    if (tok != STRING) {
+	if (tok != LEXERR && condlex == testlex) {
+	    if (!dble)
+		return par_cond_double("-n", s1);
+	    else if (!strcmp(s1, "-t"))
+		return par_cond_double(s1, "1");
+	} else
+	    YYERROR(ecused);
+    }
+    s2 = tokstr;
+    incond++;			/* parentheses do globbing */
+    condlex();
+    incond--;			/* parentheses do grouping */
+    if (tok == STRING && !dble) {
+	s3 = tokstr;
+	condlex();
+	if (tok == STRING) {
+	    LinkList l = newlinklist();
 
-    if (!l)
-	return;
+	    addlinknode(l, s2);
+	    addlinknode(l, s3);
 
-    for (n = firstnode(l); n; incnode(n)) {
-	f = (Redir) getdata(n);
+	    while (tok == STRING) {
+		addlinknode(l, tokstr);
+		condlex();
+	    }
+	    return par_cond_multi(s1, l);
+	} else
+	    return par_cond_triple(s1, s2, s3);
+    } else
+	return par_cond_double(s1, s2);
+}
 
-	ecadd(WCB_REDIR(f->type));
-	ecadd(f->fd1);
-	ecstr(f->name);
+/**/
+static int
+par_cond_double(char *a, char *b)
+{
+    if (a[0] != '-' || !a[1])
+	COND_ERROR("parse error: condition expected: %s", a);
+    else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) {
+	ecadd(WCB_COND(a[1], 0));
+	ecstr(b);
+    } else {
+	ecadd(WCB_COND(COND_MOD, 1));
+	ecstr(a);
+	ecstr(b);
     }
+    return 1;
 }
 
 /**/
-static void
-ecassigns(LinkList l)
+static int
+get_cond_num(char *tst)
 {
-    int p;
-    LinkNode n;
-    Varasg v;
-
-    if (!l)
-	return;
+    static char *condstrs[] =
+    {
+	"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
+    };
+    int t0;
 
-    for (n = firstnode(l); n; incnode(n)) {
-	v = (Varasg) getdata(n);
+    for (t0 = 0; condstrs[t0]; t0++)
+	if (!strcmp(condstrs[t0], tst))
+	    return t0;
+    return -1;
+}
 
-	p = ecadd(0);
-	ecstr(v->name);
+/**/
+static int
+par_cond_triple(char *a, char *b, char *c)
+{
+    int t0;
 
-	if (PM_TYPE(v->type) == PM_ARRAY) {
-	    LinkNode vp;
-	    int num;
-
-	    for (vp = firstnode(v->arr), num = 0; vp; incnode(vp), num++)
-		ecstr((char *) getdata(vp));
-	    ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, num);
+    if ((b[0] == Equals || b[0] == '=') &&
+	(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) {
+	ecadd(WCB_COND(COND_STREQ, 0));
+	ecstr(a);
+	ecstr(c);
+	ecadd(ecnpats++);
+    } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) {
+	ecadd(WCB_COND(COND_STRNEQ, 0));
+	ecstr(a);
+	ecstr(c);
+	ecadd(ecnpats++);
+    } else if (b[0] == '-') {
+	if ((t0 = get_cond_num(b + 1)) > -1) {
+	    ecadd(WCB_COND(t0 + COND_NT, 0));
+	    ecstr(a);
+	    ecstr(c);
 	} else {
-	    ecstr(v->str);
-	    ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_SCALAR, 0);
+	    ecadd(WCB_COND(COND_MODI, 0));
+	    ecstr(b);
+	    ecstr(a);
+	    ecstr(c);
 	}
-    }
+    } else if (a[0] == '-' && a[1]) {
+	ecadd(WCB_COND(COND_MOD, 2));
+	ecstr(a);
+	ecstr(b);
+	ecstr(c);
+    } else
+	COND_ERROR("condition expected: %s", b);
+
+    return 1;
 }
 
 /**/
-static void
-eccond(Cond c)
+static int
+par_cond_multi(char *a, LinkList l)
 {
-    int p;
-
-    switch (c->type) {
-    case COND_NOT:
-	ecadd(WCB_COND(COND_NOT, 0));
-	eccond(c->left);
-	break;
-    case COND_AND:
-    case COND_OR:
-	p = ecadd(0);
-	eccond(c->left);
-	eccond(c->right);
-	ecbuf[p] = WCB_COND(c->type, ecused - 1 - p);
-	break;
-    case COND_MOD:
-	{
-	    char **pp;
-	    int num;
+    if (a[0] != '-' || !a[1])
+	COND_ERROR("condition expected: %s", a);
+    else {
+	LinkNode n;
 
-	    p = ecadd(0);
-	    ecstr((char *) c->left);
-	    for (pp = (char **) c->right, num = 0; *pp; pp++, num++)
-		ecstr(*pp);
-	    ecbuf[p] = WCB_COND(COND_MOD, num);
-	}
-	break;
-    case COND_MODI:
-	ecadd(WCB_COND(COND_MODI, 0));
-	ecstr((char *) c->left);
-	ecstr(((char **) c->right)[0]);
-	ecstr(((char **) c->right)[1]);
-	break;
-    default:
-	ecadd(WCB_COND(c->type, 0));
-	ecstr((char *) c->left);
-	if (c->type <= COND_GE) {
-	    ecstr((char *) c->right);
-	    if (c->type == COND_STREQ || c->type == COND_STRNEQ)
-		ecadd(ecnpats++);
-	}
-	break;
+	ecadd(WCB_COND(COND_MOD, countlinknodes(l)));
+	ecstr(a);
+	for (n = firstnode(l); n; incnode(n))
+	    ecstr((char *) getdata(n));
     }
+    return 1;
 }
 
 /**/
-static Eprog
-execompile(List list)
+static void
+yyerror(int noerr)
 {
-    Eprog ret;
-    Eccstr p;
-    char *q;
-    int l;
-
-    MUSTUSEHEAP("execompile");
-
-    ecbuf = (Wordcode) zhalloc((eclen = ecfree = 256) * sizeof(wordcode));
-    ecused = 0;
-    ecstrs = NULL;
-    ecsoffs = ecnpats = 0;
+    int t0;
+    char *t;
 
-    ec(list);
-    ecadd(WCB_END());
+    if ((t = dupstring(yytext)))
+	untokenize(t);
 
-    ret = (Eprog) zhalloc(sizeof(*ret));
-    ret->len = ((ecnpats * sizeof(Patprog)) +
-		(ecused * sizeof(wordcode)) +
-		ecsoffs);
-    ret->npats = ecnpats;
-    ret->pats = (Patprog *) zhalloc(ret->len);
-    ret->prog = (Wordcode) (ret->pats + ecnpats);
-    ret->strs = (char *) (ret->prog + ecused);
-    ret->shf = NULL;
-    ret->heap = 1;
-    for (l = 0; l < ecnpats; l++)
-	ret->pats[l] = dummy_patprog1;
-    memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode));
-    for (p = ecstrs, q = ret->strs; p; p = p->next, q += l) {
-	l = strlen(p->str) + 1;
-	memcpy(q, p->str, l);
-    }
-    return ret;
+    for (t0 = 0; t0 != 20; t0++)
+	if (!t || !t[t0] || t[t0] == '\n')
+	    break;
+    if (t0 == 20)
+	zwarn("parse error near `%l...'", t, 20);
+    else if (t0)
+	zwarn("parse error near `%l'", t, t0);
+    else
+	zwarn("parse error", NULL, 0);
+    if (!noerr && noerrs != 2)
+	errflag = 1;
 }
 
 /**/
@@ -2425,75 +2063,94 @@
 
 /**/
 char *
-ecgetstr(Estate s, int dup)
+ecgetstr(Estate s, int dup, int *tok)
 {
     static char buf[4];
     wordcode c = *s->pc++;
     char *r;
 
-    if (c == 0xfe000000)
+    if (c == 6 || c == 7)
 	r = "";
-    else if (c >= 0xff000000) {
-	buf[0] = (char) (c & 0xff);
-	buf[1] = (char) ((c >>  8) & 0xff);
-	buf[2] = (char) ((c >> 16) & 0xff);
+    else if (c & 2) {
+	buf[0] = (char) ((c >>  3) & 0xff);
+	buf[1] = (char) ((c >> 11) & 0xff);
+	buf[2] = (char) ((c >> 19) & 0xff);
 	buf[3] = '\0';
 	r = dupstring(buf);
-	dup = 0;
-    } else
-	r = s->strs + c;
-
-    return (dup ? dupstring(r) : r);
+	dup = EC_NODUP;
+    } else {
+	r = s->strs + (c >> 2);
+    }
+    if (tok)
+	*tok = (c & 1);
+    return ((dup == EC_DUP || (dup && (c & 1)))  ? dupstring(r) : r);
 }
 
 /**/
 char *
-ecrawstr(Eprog p, Wordcode pc)
+ecrawstr(Eprog p, Wordcode pc, int *tok)
 {
     static char buf[4];
     wordcode c = *pc;
 
-    if (c == 0xfe000000)
+    if (c == 6 || c == 7) {
+	if (tok)
+	    *tok = (c & 1);
 	return "";
-    else if (c >= 0xff000000) {
-	buf[0] = (char) (c & 0xff);
-	buf[1] = (char) ((c >>  8) & 0xff);
-	buf[2] = (char) ((c >> 16) & 0xff);
+    } else if (c & 2) {
+	buf[0] = (char) ((c >>  3) & 0xff);
+	buf[1] = (char) ((c >> 11) & 0xff);
+	buf[2] = (char) ((c >> 19) & 0xff);
 	buf[3] = '\0';
+	if (tok)
+	    *tok = (c & 1);
 	return buf;
-    } else
-	return p->strs + c;
+    } else {
+	if (tok)
+	    *tok = (c & 1);
+	return p->strs + (c >> 2);
+    }
 }
 
 /**/
 char **
-ecgetarr(Estate s, int num, int dup)
+ecgetarr(Estate s, int num, int dup, int *tok)
 {
     char **ret, **rp;
+    int tf = 0, tmp = 0;
 
     ret = rp = (char **) zhalloc((num + 1) * sizeof(char *));
 
-    while (num--)
-	*rp++ = ecgetstr(s, dup);
+    while (num--) {
+	*rp++ = ecgetstr(s, dup, &tmp);
+	tf |=  tmp;
+    }
     *rp = NULL;
+    if (tok)
+	*tok = tf;
 
     return ret;
 }
 
 /**/
 LinkList
-ecgetlist(Estate s, int num, int dup)
+ecgetlist(Estate s, int num, int dup, int *tok)
 {
     if (num) {
 	LinkList ret;
+	int i, tf = 0, tmp = 0;
 
-	ret = newlinklist();
-
-	while (num--)
-	    addlinknode(ret, ecgetstr(s, dup));
-
+	ret = newsizedlist(num);
+	for (i = 0; i < num; i++) {
+	    setsizednode(ret, i, ecgetstr(s, dup, &tmp));
+	    tf |= tmp;
+	}
+	if (tok)
+	    *tok = tf;
 	return ret;
     }
+    if (tok)
+	*tok = 0;
     return NULL;
 }
 
@@ -2509,7 +2166,7 @@
 
 	r->type = WC_REDIR_TYPE(code);
 	r->fd1 = *s->pc++;
-	r->name = ecgetstr(s, 1);
+	r->name = ecgetstr(s, EC_DUP, NULL);
 
 	addlinknode(ret, r);
 
diff -ru ../z.old/Src/signals.c Src/signals.c
--- ../z.old/Src/signals.c	Wed Feb 23 09:02:48 2000
+++ Src/signals.c	Wed Feb 23 11:54:58 2000
@@ -177,20 +177,25 @@
 
 /* Block the signals in the given signal *
  * set. Return the old signal set.       */
- 
+
+/**/
+#ifdef POSIX_SIGNALS
+
+/**/
+mod_export sigset_t dummy_sigset1, dummy_sigset2;
+
+/**/
+#else
+
 /**/
+#ifndef BSD_SIGNALS
+
 sigset_t
 signal_block(sigset_t set)
 {
     sigset_t oset;
  
-#ifdef POSIX_SIGNALS
-    sigprocmask(SIG_BLOCK, &set, &oset);
-#else
-# ifdef BSD_SIGNALS
-    oset = sigblock(set);
-# else
-#  ifdef SYSV_SIGNALS
+#ifdef SYSV_SIGNALS
     int i;
  
     oset = blocked_set;
@@ -200,7 +205,7 @@
             sighold(i);
         }
     }
-#  else  /* NO_SIGNAL_BLOCKING */
+#else  /* NO_SIGNAL_BLOCKING */
 /* We will just ignore signals if the system doesn't have *
  * the ability to block them.                             */
     int i;
@@ -212,25 +217,27 @@
             signal_ignore(i);
         }
    }
-#  endif /* SYSV_SIGNALS  */
-# endif  /* BSD_SIGNALS   */
-#endif   /* POSIX_SIGNALS */
+#endif /* SYSV_SIGNALS  */
  
     return oset;
 }
 
+/**/
+#endif /* BSD_SIGNALS */
+
+/**/
+#endif /* POSIX_SIGNALS */
+
 /* Unblock the signals in the given signal *
  * set. Return the old signal set.         */
 
-/**/
+#ifndef POSIX_SIGNALS
+
 sigset_t
 signal_unblock(sigset_t set)
 {
     sigset_t oset;
  
-#ifdef POSIX_SIGNALS
-    sigprocmask(SIG_UNBLOCK, &set, &oset);
-#else
 # ifdef BSD_SIGNALS
     sigfillset(&oset);
     oset = sigsetmask(oset);
@@ -260,10 +267,11 @@
    }
 #  endif /* SYSV_SIGNALS  */
 # endif  /* BSD_SIGNALS   */
-#endif   /* POSIX_SIGNALS */
  
     return oset;
 }
+
+#endif   /* POSIX_SIGNALS */
 
 /* set the process signal mask to *
  * be the given signal mask       */
diff -ru ../z.old/Src/signals.h Src/signals.h
--- ../z.old/Src/signals.h	Wed Feb 23 09:02:48 2000
+++ Src/signals.h	Wed Feb 23 11:54:58 2000
@@ -56,8 +56,8 @@
 # define sigismember(s,n)  ((*(s) & (1 << ((n) - 1))) != 0)
 #endif   /* ifndef POSIX_SIGNALS */
  
-#define child_block()      signal_block(signal_mask(SIGCHLD))
-#define child_unblock()    signal_unblock(signal_mask(SIGCHLD))
+#define child_block()      signal_block(sigchld_mask)
+#define child_unblock()    signal_unblock(sigchld_mask)
 #define child_suspend(S)   signal_suspend(SIGCHLD, S)
 
 /* ignore a signal */
@@ -92,3 +92,29 @@
 	} \
     } \
 } while (0)
+
+
+/* Make some signal functions faster. */
+
+#ifdef POSIX_SIGNALS
+#define signal_block(S) \
+    ((dummy_sigset1 = (S)), \
+     sigprocmask(SIG_BLOCK, &dummy_sigset1, &dummy_sigset2), \
+     dummy_sigset2)
+#else
+# ifdef BSD_SIGNALS
+#define signal_block(S) sigblock(S)
+# else
+extern sigset_t signal_block _((sigset_t));
+# endif  /* BSD_SIGNALS   */
+#endif   /* POSIX_SIGNALS */
+
+#ifdef POSIX_SIGNALS
+#define signal_unblock(S) \
+    ((dummy_sigset1 = (S)), \
+     sigprocmask(SIG_UNBLOCK, &dummy_sigset1, &dummy_sigset2), \
+     dummy_sigset2)
+#else
+extern sigset_t signal_unblock _((sigset_t));
+#endif   /* POSIX_SIGNALS */
+
diff -ru ../z.old/Src/subst.c Src/subst.c
--- ../z.old/Src/subst.c	Wed Feb 23 09:02:49 2000
+++ Src/subst.c	Wed Feb 23 11:54:59 2000
@@ -53,12 +53,12 @@
 
     MUSTUSEHEAP("prefork");
     for (node = firstnode(list); node; incnode(node)) {
-	char *str;
+	char *str, c;
 
 	str = (char *)getdata(node);
-	if ((*str == Inang || *str == Outang || *str == Equals) &&
+	if (((c = *str) == Inang || c == Outang || c == Equals) &&
 	    str[1] == Inpar) {
-	    if (*str == Inang || *str == Outang)
+	    if (c == Inang || c == Outang)
 		setdata(node, (void *) getproc(str));	/* <(...) or >(...) */
 	    else
 		setdata(node, (void *) getoutputfile(str));	/* =(...) */
@@ -94,16 +94,16 @@
 {
     int qt;
     char *str3 = (char *)getdata(node);
-    char *str  = str3;
+    char *str  = str3, c;
 
-    while (!errflag && *str) {
-	if ((qt = *str == Qstring) || *str == String) {
-	    if (str[1] == Inpar) {
+    while (!errflag && (c = *str)) {
+	if ((qt = c == Qstring) || c == String) {
+	    if ((c = str[1]) == Inpar) {
 		if (!qt)
 		    mult_isarr = 1;
 		str++;
 		goto comsub;
-	    } else if (str[1] == Inbrack) {
+	    } else if (c == Inbrack) {
 		/* $[...] */
 		char *str2 = str;
 		str2++;
@@ -115,7 +115,7 @@
 		str = arithsubst(str + 2, &str3, str2);
 		setdata(node, (void *) str3);
 		continue;
-	    } else if (str[1] == Snull) {
+	    } else if (c == Snull) {
 		str = getkeystring(str, NULL, 4, NULL);
 		continue;
 	    } else {
@@ -125,14 +125,14 @@
 		str3 = (char *)getdata(node);
 		continue;
 	    }
-	} else if ((qt = *str == Qtick) || *str == Tick)
+	} else if ((qt = c == Qtick) || c == Tick)
 	  comsub: {
 	    LinkList pl;
 	    char *s, *str2 = str;
 	    char endchar;
 	    int l1, l2;
 
-	    if (*str == Inpar) {
+	    if (c == Inpar) {
 		endchar = Outpar;
 		str[-1] = '\0';
 #ifdef DEBUG
@@ -143,7 +143,7 @@
 #endif
 		str--;
 	    } else {
-		endchar = *str;
+		endchar = c;
 		*str = '\0';
 
 		while (*++str != endchar)
@@ -164,12 +164,12 @@
 	     * be left unchanged.  Note that the lexer doesn't tokenize   *
 	     * the body of a command substitution so if there are some    *
 	     * tokens here they are from a ${(e)~...} substitution.       */
-	    for (str = str2; *++str; )
-		if (itok(*str) && *str != Nularg &&
-		    !(endchar != Outpar && *str == Bnull &&
+	    for (str = str2; (c = *++str); )
+		if (itok(c) && c != Nularg &&
+		    !(endchar != Outpar && c == Bnull &&
 		      (str[1] == '$' || str[1] == '\\' || str[1] == '`' ||
 		       (qt && str[1] == '"'))))
-		    *str = ztokens[*str - Pound];
+		    *str = ztokens[c - Pound];
 	    str++;
 	    if (!(pl = getoutput(str2 + 1, qt || ssub))) {
 		zerr("parse error in command substitution", NULL, 0);
@@ -231,15 +231,15 @@
 mod_export void
 singsub(char **s)
 {
-    LinkList foo;
+    local_list1(foo);
 
-    foo = newlinklist();
-    addlinknode(foo, *s);
-    prefork(foo, PF_SINGLE);
+    init_list1(foo, *s);
+
+    prefork(&foo, PF_SINGLE);
     if (errflag)
 	return;
-    *s = (char *) ugetnode(foo);
-    DPUTS(nonempty(foo), "BUG: singsub() produced more than one word!");
+    *s = (char *) ugetnode(&foo);
+    DPUTS(nonempty(&foo), "BUG: singsub() produced more than one word!");
 }
 
 /* Perform substitution on a single word. Unlike with singsub, the      *
@@ -259,24 +259,23 @@
 static int
 multsub(char **s, char ***a, int *isarr, char *sep)
 {
-    LinkList foo;
     int l, omi = mult_isarr;
     char **r, **p;
+    local_list1(foo);
 
     mult_isarr = 0;
-    foo = newlinklist();
-    addlinknode(foo, *s);
-    prefork(foo, 0);
+    init_list1(foo, *s);
+    prefork(&foo, 0);
     if (errflag) {
 	if (isarr)
 	    *isarr = 0;
 	mult_isarr = omi;
 	return 0;
     }
-    if ((l = countlinknodes(foo))) {
+    if ((l = countlinknodes(&foo))) {
 	p = r = ncalloc((l + 1) * sizeof(char*));
-	while (nonempty(foo))
-	    *p++ = (char *)ugetnode(foo);
+	while (nonempty(&foo))
+	    *p++ = (char *)ugetnode(&foo);
 	*p = NULL;
 	if (a && mult_isarr) {
 	    *a = r;
@@ -291,7 +290,7 @@
 	return 0;
     }
     if (l)
-	*s = (char *) ugetnode(foo);
+	*s = (char *) ugetnode(&foo);
     else
 	*s = dupstring("");
     if (isarr)
@@ -423,20 +422,27 @@
 
 /**/
 static char *
-strcatsub(char **d, char *pb, char *pe, char *src, int l, char *s, int glbsub)
+strcatsub(char **d, char *pb, char *pe, char *src, int l, char *s, int glbsub,
+	  int copied)
 {
+    char *dest;
     int pl = pe - pb;
-    char *dest = ncalloc(pl + l + (s ? strlen(s) : 0) + 1);
 
-    *d = dest;
-    strncpy(dest, pb, pl);
-    dest += pl;
-    strcpy(dest, src);
-    if (glbsub)
-	tokenize(dest);
-    dest += l;
-    if (s)
-	strcpy(dest, s);
+    if (!pl && (!s || !*s)) {
+	dest = (*d = (copied ? src : dupstring(src)));
+	if (glbsub)
+	    tokenize(dest);
+    } else {
+	*d = dest = ncalloc(pl + l + (s ? strlen(s) : 0) + 1);
+	strncpy(dest, pb, pl);
+	dest += pl;
+	strcpy(dest, src);
+	if (glbsub)
+	    tokenize(dest);
+	dest += l;
+	if (s)
+	    strcpy(dest, s);
+    }
     return dest;
 }
 
@@ -719,7 +725,7 @@
 LinkNode
 paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 {
-    char *aptr = *str;
+    char *aptr = *str, c, cc;
     char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n);
     int colf;			/* != 0 means we found a colon after the name */
     int isarr = 0;
@@ -733,6 +739,7 @@
     int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
     char *val = NULL, **aval = NULL;
     unsigned int fwidth = 0;
+    struct value vbuf;
     Value v = NULL;
     int flags = 0;
     int flnum = 0;
@@ -756,24 +763,24 @@
     int subexp;
 
     *s++ = '\0';
-    if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
-	*s != '!' && *s != '$' && *s != String && *s != Qstring &&
-	*s != '?' && *s != Quest && *s != '_' &&
-	*s != '*' && *s != Star && *s != '@' && *s != '{' &&
-	*s != Inbrace && *s != '=' && *s != Equals && *s != Hat &&
-	*s != '^' && *s != '~' && *s != Tilde && *s != '+') {
+    if (!ialnum(c = *s) && c != '#' && c != Pound && c != '-' &&
+	c != '!' && c != '$' && c != String && c != Qstring &&
+	c != '?' && c != Quest && c != '_' &&
+	c != '*' && c != Star && c != '@' && c != '{' &&
+	c != Inbrace && c != '=' && c != Equals && c != Hat &&
+	c != '^' && c != '~' && c != Tilde && c != '+') {
 	s[-1] = '$';
 	*str = s;
 	return n;
     }
-    DPUTS(*s == '{', "BUG: inbrace == '{' in paramsubst()");
-    if (*s == Inbrace) {
+    DPUTS(c == '{', "BUG: inbrace == '{' in paramsubst()");
+    if (c == Inbrace) {
 	inbrace = 1;
 	s++;
-	if (*s == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
+	if ((c = *s) == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
 	    hkeys = SCANPM_WANTKEYS;
 	    s++;
-	} else if (*s == '(' || *s == Inpar) {
+	} else if (c == '(' || c == Inpar) {
 	    char *t, sav;
 	    int tt = 0;
 	    zlong num;
@@ -788,8 +795,8 @@
 		}\
 	    }
 
-	    for (s++; *s != ')' && *s != Outpar; s++, tt = 0) {
-		switch (*s) {
+	    for (s++; (c = *s) != ')' && c != Outpar; s++, tt = 0) {
+		switch (c) {
 		case ')':
 		case Outpar:
 		    break;
@@ -979,31 +986,31 @@
 	postmul = " ";
 
     for (;;) {
-	if (*s == '^' || *s == Hat) {
-	    if (*++s == '^' || *s == Hat) {
+	if ((c = *s) == '^' || c == Hat) {
+	    if ((c = *++s) == '^' || c == Hat) {
 		plan9 = 0;
 		s++;
 	    } else
 		plan9 = 1;
-	} else if (*s == '=' || *s == Equals) {
-	    if (*++s == '=' || *s == Equals) {
+	} else if ((c = *s) == '=' || c == Equals) {
+	    if ((c = *++s) == '=' || c == Equals) {
 		spbreak = 0;
 		s++;
 	    } else
 		spbreak = 1;
-	} else if ((*s == '#' || *s == Pound) &&
-		   (iident(s[1])
-		    || s[1] == '*' || s[1] == Star || s[1] == '@'
-		    || s[1] == '-' || (s[1] == ':' && s[2] == '-')
-		    || (isstring(s[1]) && (s[2] == Inbrace || s[2] == Inpar))))
+	} else if ((c == '#' || c == Pound) &&
+		   (iident(cc = s[1])
+		    || cc == '*' || cc == Star || cc == '@'
+		    || cc == '-' || (cc == ':' && s[2] == '-')
+		    || (isstring(cc) && (s[2] == Inbrace || s[2] == Inpar))))
 	    getlen = 1 + whichlen, s++;
-	else if (*s == '~' || *s == Tilde) {
-	    if (*++s == '~' || *s == Tilde) {
+	else if (c == '~' || c == Tilde) {
+	    if ((c = *++s) == '~' || c == Tilde) {
 		globsubst = 0;
 		s++;
 	    } else
 		globsubst = 1;
-	} else if (*s == '+') {
+	} else if (c == '+') {
 	    if (iident(s[1]) || (aspar && isstring(s[1]) &&
 				 (s[2] == Inbrace || s[2] == Inpar)))
 		chkset = 1, s++;
@@ -1043,7 +1050,7 @@
 	    s++;
 	v = (Value) NULL;
     } else if (aspar) {
-	if ((v = getvalue(&s, 1))) {
+	if ((v = getvalue(&vbuf, &s, 1))) {
 	    val = idbeg = getstrvalue(v);
 	    subexp = 1;
 	} else
@@ -1052,8 +1059,9 @@
     if (!subexp || aspar) {
 	char *ov = val;
 
-	if (!(v = fetchvalue((subexp ? &ov : &s), (wantt ? -1 :
-				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
+	if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s),
+			     (wantt ? -1 :
+			      ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
 			     hkeys|hvals|(arrasg ? SCANPM_ASSIGNING : 0))) ||
 	    (v->pm && (v->pm->flags & PM_UNSET)))
 	    vunset = 1;
@@ -1199,13 +1207,13 @@
 
 		case PM_LOWER:
 		    t = val;
-		    for (; *t; t++)
-			*t = tulower(*t);
+		    for (; (c = *t); t++)
+			*t = tulower(c);
 		    break;
 		case PM_UPPER:
 		    t = val;
-		    for (; *t; t++)
-			*t = tuupper(*t);
+		    for (; (c = *t); t++)
+			*t = tuupper(c);
 		    break;
 		}
 	    }
@@ -1236,12 +1244,10 @@
     fstr = s;
     if (inbrace) {
 	int bct;
-	for (bct = 1;; fstr++) {
-	    if (!*fstr)
-		break;
-	    else if (*fstr == Inbrace)
+	for (bct = 1; (c = *fstr); fstr++) {
+	    if (c == Inbrace)
 		bct++;
-	    else if (*fstr == Outbrace && !--bct)
+	    else if (c == Outbrace && !--bct)
 		break;
 	}
 
@@ -1250,29 +1256,29 @@
 	    zerr("closing brace expected", NULL, 0);
 	    return NULL;
 	}
-	if (*fstr)
+	if (c)
 	    *fstr++ = '\0';
     }
 
     /* Check for ${..?..} or ${..=..} or one of those. *
      * Only works if the name is in braces.            */
 
-    if (inbrace && (*s == '-' ||
-		    *s == '+' ||
-		    *s == ':' ||
-		    *s == '=' || *s == Equals ||
-		    *s == '%' ||
-		    *s == '#' || *s == Pound ||
-		    *s == '?' || *s == Quest ||
-		    *s == '/')) {
+    if (inbrace && ((c = *s) == '-' ||
+		    c == '+' ||
+		    c == ':' ||
+		    c == '=' || c == Equals ||
+		    c == '%' ||
+		    c == '#' || c == Pound ||
+		    c == '?' || c == Quest ||
+		    c == '/')) {
 
 	if (!flnum)
 	    flnum++;
-	if (*s == '%')
+	if (c == '%')
 	    flags |= SUB_END;
 
 	/* Check for ${..%%..} or ${..##..} */
-	if ((*s == '%' || *s == '#' || *s == Pound) && *s == s[1]) {
+	if ((c == '%' || c == '#' || c == Pound) && c == s[1]) {
 	    s++;
 	    /* we have %%, not %, or ##, not # */
 	    flags |= SUB_LONG;
@@ -1285,17 +1291,17 @@
 	     * indicates shortest substring; else look for longest.
 	     */
 	    flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG;
-	    if (*s == '/') {
+	    if ((c = *s) == '/') {
 		/* doubled, so replace all occurrences */
 		flags |= SUB_GLOBAL;
 		s++;
 	    }
 	    /* Check for anchored substitution */
-	    if (*s == '%') {
+	    if (c == '%') {
 		/* anchor at tail */
 		flags |= SUB_END;
 		s++;
-	    } else if (*s == '#' || *s == Pound) {
+	    } else if (c == '#' || c == Pound) {
 		/* anchor at head: this is the `normal' case in getmatch */
 		s++;
 	    } else
@@ -1311,8 +1317,8 @@
 	     * double quotes the Bnull isn't there, so it's not
 	     * consistent.
 	     */
-	    for (ptr = s; *ptr && *ptr != '/'; ptr++)
-		if (*ptr == '\\' && ptr[1] == '/')
+	    for (ptr = s; (c = *ptr) && c != '/'; ptr++)
+		if (c == '\\' && ptr[1] == '/')
 		    chuck(ptr);
 	    replstr = (*ptr && ptr[1]) ? ptr+1 : "";
 	    *ptr = '\0';
@@ -1453,12 +1459,12 @@
 
 		singsub(&s);
 		if (t == '/' && (flags & SUB_SUBSTR)) {
-		    if (*s == '#' || *s == '%') {
+		    if ((c = *s) == '#' || c == '%') {
 			flags &= ~SUB_SUBSTR;
-			if (*s == '%')
+			if (c == '%')
 			    flags |= SUB_END;
 			s++;
-		    } else if (*s == '\\') {
+		    } else if (c == '\\') {
 			s++;
 		    }
 		}
@@ -1508,6 +1514,7 @@
 		    }
 		    s = ss;
 		}
+		copied = 1;
 		if (inbrace && *s) {
 		    if (*s == ':' && !imeta(s[1]))
 			zerr("unrecognized modifier `%c'", NULL, s[1]);
@@ -1696,7 +1703,6 @@
 		    int pre = quotetype != 3 ? 1 : 2;
 		    int sl;
 		    char *tmp;
-
 		    tmp = bslashquote(val, NULL, quotetype);
 		    sl = strlen(tmp);
 		    val = (char *) zhalloc(pre + sl + 2);
@@ -1769,15 +1775,15 @@
 		qsort(aval, i, sizeof(char *), sortfn[sortit-1]);
 	}
 	if (plan9) {
-	    LinkList tl = newlinklist();
 	    LinkNode tn;
+	    local_list1(tl);
 
 	    *--fstr = Marker;
-	    addlinknode(tl, fstr);
-	    if (!eval && !stringsubst(tl, firstnode(tl), ssub))
+	    init_list1(tl, fstr);
+	    if (!eval && !stringsubst(&tl, firstnode(&tl), ssub))
 		return NULL;
 	    *str = aptr;
-	    tn = firstnode(tl);
+	    tn = firstnode(&tl);
 	    while ((x = *aval++)) {
 		if (prenum || postnum)
 		    x = dopadding(x, prenum, postnum, preone, postone,
@@ -1785,10 +1791,11 @@
 		if (eval && subst_parse_str(x, (qt && !nojoin)))
 		    return NULL;
 		xlen = strlen(x);
-		for (tn = firstnode(tl);
+		for (tn = firstnode(&tl);
 		     tn && *(y = (char *) getdata(tn)) == Marker;
 		     incnode(tn)) {
-		    strcatsub(&y, ostr, aptr, x, xlen, y + 1, globsubst);
+		    strcatsub(&y, ostr, aptr, x, xlen, y + 1, globsubst,
+			      copied);
 		    if (qt && !*y && isarr != 2)
 			y = dupstring(nulstring);
 		    if (plan9)
@@ -1820,7 +1827,7 @@
 	    if (eval && subst_parse_str(x, (qt && !nojoin)))
 		return NULL;
 	    xlen = strlen(x);
-	    strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst);
+	    strcatsub(&y, ostr, aptr, x, xlen, NULL, globsubst, copied);
 	    if (qt && !*y && isarr != 2)
 		y = dupstring(nulstring);
 	    setdata(n, (void *) y);
@@ -1851,7 +1858,7 @@
 	    if (eval && subst_parse_str(x, (qt && !nojoin)))
 		return NULL;
 	    xlen = strlen(x);
-	    *str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst);
+	    *str = strcatsub(&y, aptr, aptr, x, xlen, fstr, globsubst, copied);
 	    if (qt && !*y && isarr != 2)
 		y = dupstring(nulstring);
 	    insertlinknode(l, n, (void *) y), incnode(n);
@@ -1870,7 +1877,7 @@
 	if (eval && subst_parse_str(x, (qt && !nojoin)))
 	    return NULL;
 	xlen = strlen(x);
-	*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst);
+	*str = strcatsub(&y, ostr, aptr, x, xlen, fstr, globsubst, copied);
 	if (qt && !*y)
 	    y = dupstring(nulstring);
 	setdata(n, (void *) y);
diff -ru ../z.old/Src/text.c Src/text.c
--- ../z.old/Src/text.c	Wed Feb 23 09:02:49 2000
+++ Src/text.c	Wed Feb 23 11:54:59 2000
@@ -78,7 +78,7 @@
 {
     if (num) {
 	while (num--) {
-	    taddstr(ecgetstr(state, 0));
+	    taddstr(ecgetstr(state, EC_NODUP, NULL));
 	    taddchr(' ');
 	}
 	tptr--;
@@ -243,7 +243,7 @@
 	switch (wc_code(code)) {
 	case WC_LIST:
 	    if (!s) {
-		tpush(code, (WC_LIST_TYPE(code) & Z_END));
+		s = tpush(code, (WC_LIST_TYPE(code) & Z_END));
 		stack = 0;
 	    } else {
 		if (WC_LIST_TYPE(code) & Z_ASYNC) {
@@ -260,6 +260,8 @@
 		    s->pop = (WC_LIST_TYPE(s->code) & Z_END);
 		}
 	    }
+	    if (!stack && (WC_LIST_TYPE(s->code) & Z_SIMPLE))
+		state->pc++;
 	    break;
 	case WC_SUBLIST:
 	    if (!s) {
@@ -306,14 +308,14 @@
 	    }
 	    break;
 	case WC_ASSIGN:
-	    taddstr(ecgetstr(state, 0));
+	    taddstr(ecgetstr(state, EC_NODUP, NULL));
 	    taddchr('=');
 	    if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
 		taddchr('(');
 		taddlist(state, WC_ASSIGN_NUM(code));
 		taddstr(") ");
 	    } else {
-		taddstr(ecgetstr(state, 0));
+		taddstr(ecgetstr(state, EC_NODUP, NULL));
 		taddchr(' ');
 	    }
 	    break;
@@ -391,14 +393,14 @@
 		taddstr("for ");
 		if (WC_FOR_TYPE(code) == WC_FOR_COND) {
 		    taddstr("((");
-		    taddstr(ecgetstr(state, 0));
+		    taddstr(ecgetstr(state, EC_NODUP, NULL));
 		    taddstr("; ");
-		    taddstr(ecgetstr(state, 0));
+		    taddstr(ecgetstr(state, EC_NODUP, NULL));
 		    taddstr("; ");
-		    taddstr(ecgetstr(state, 0));
+		    taddstr(ecgetstr(state, EC_NODUP, NULL));
 		    taddstr(")) do");
 		} else {
-		    taddstr(ecgetstr(state, 0));
+		    taddstr(ecgetstr(state, EC_NODUP, NULL));
 		    if (WC_FOR_TYPE(code) == WC_FOR_LIST) {
 			taddstr(" in ");
 			taddlist(state, *state->pc++);
@@ -419,7 +421,7 @@
 	case WC_SELECT:
 	    if (!s) {
 		taddstr("select ");
-		taddstr(ecgetstr(state, 0));
+		taddstr(ecgetstr(state, EC_NODUP, NULL));
 		if (WC_SELECT_TYPE(code) == WC_SELECT_LIST) {
 		    taddstr(" in ");
 		    taddlist(state, *state->pc++);
@@ -457,7 +459,7 @@
 	case WC_REPEAT:
 	    if (!s) {
 		taddstr("repeat ");
-		taddstr(ecgetstr(state, 0));
+		taddstr(ecgetstr(state, EC_NODUP, NULL));
 		taddnl();
 		taddstr("do");
 		tindent++;
@@ -475,7 +477,7 @@
 		Wordcode end = state->pc + WC_CASE_SKIP(code);
 
 		taddstr("case ");
-		taddstr(ecgetstr(state, 0));
+		taddstr(ecgetstr(state, EC_NODUP, NULL));
 		taddstr(" in");
 
 		if (state->pc >= end) {
@@ -492,7 +494,7 @@
 		    else
 			taddchr(' ');
 		    code = *state->pc++;
-		    taddstr(ecgetstr(state, 0));
+		    taddstr(ecgetstr(state, EC_NODUP, NULL));
 		    state->pc++;
 		    taddstr(") ");
 		    tindent++;
@@ -508,7 +510,7 @@
 		else
 		    taddchr(' ');
 		code = *state->pc++;
-		taddstr(ecgetstr(state, 0));
+		taddstr(ecgetstr(state, EC_NODUP, NULL));
 		state->pc++;
 		taddstr(") ");
 		tindent++;
@@ -638,31 +640,31 @@
 			}
 			break;
 		    case COND_MOD:
-			taddstr(ecgetstr(state, 0));
+			taddstr(ecgetstr(state, EC_NODUP, NULL));
 			taddchr(' ');
 			taddlist(state, WC_COND_SKIP(code));
 			stack = 1;
 			break;
 		    case COND_MODI:
 			{
-			    char *name = ecgetstr(state, 0);
+			    char *name = ecgetstr(state, EC_NODUP, NULL);
 
-			    taddstr(ecgetstr(state, 0));
+			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			    taddchr(' ');
 			    taddstr(name);
 			    taddchr(' ');
-			    taddstr(ecgetstr(state, 0));
+			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			    stack = 1;
 			}
 			break;
 		    default:
 			if (ctype <= COND_GE) {
 			    /* Binary test: `a = b' etc. */
-			    taddstr(ecgetstr(state, 0));
+			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			    taddstr(" ");
 			    taddstr(c1[ctype - COND_STREQ]);
 			    taddstr(" ");
-			    taddstr(ecgetstr(state, 0));
+			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			    if (ctype == COND_STREQ ||
 				ctype == COND_STRNEQ)
 				state->pc++;
@@ -675,7 +677,7 @@
 			    c2[2] = ' ';
 			    c2[3] = '\0';
 			    taddstr(c2);
-			    taddstr(ecgetstr(state, 0));
+			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			}
 			stack = 1;
 			break;
@@ -685,7 +687,7 @@
 	    break;
 	case WC_ARITH:
 	    taddstr("((");
-	    taddstr(ecgetstr(state, 0));
+	    taddstr(ecgetstr(state, EC_NODUP, NULL));
 	    taddstr("))");
 	    stack = 1;
 	    break;
diff -ru ../z.old/Src/version.h Src/version.h
--- ../z.old/Src/version.h	Wed Feb 23 09:02:49 2000
+++ Src/version.h	Wed Feb 23 11:58:38 2000
@@ -1 +1 @@
-#define ZSH_VERSION "3.1.6-dev-18"
+#define ZSH_VERSION "3.1.6-dev-19"
diff -ru ../z.old/Src/zsh.h Src/zsh.h
--- ../z.old/Src/zsh.h	Wed Feb 23 09:02:49 2000
+++ Src/zsh.h	Wed Feb 23 11:54:59 2000
@@ -352,7 +352,25 @@
 #define pushnode(X,Y) insertlinknode(X,(LinkNode) X,Y)
 #define incnode(X) (X = nextnode(X))
 #define firsthist() (hist_ring? hist_ring->down->histnum : curhist)
+#define setsizednode(X,Y,Z) ((X)->first[(Y)].dat = (void *) (Z))
 
+/* stack allocated linked lists */
+
+#define local_list0(N) struct linklist N
+#define init_list0(N) \
+    do { \
+        (N).first = NULL; \
+        (N).last = (LinkNode) &(N); \
+    } while (0)
+#define local_list1(N) struct linklist N; struct linknode __n0
+#define init_list1(N,V0) \
+    do { \
+        (N).first = &__n0; \
+        (N).last = &__n0; \
+        __n0.next = NULL; \
+        __n0.last = (LinkNode) &(N); \
+        __n0.dat = (void *) (V0); \
+    } while (0)
 
 /********************************/
 /* Definitions for syntax trees */
@@ -364,9 +382,10 @@
 #define Z_SYNC	 (1<<1)	/* run this sublist synchronously       (;)  */
 #define Z_ASYNC  (1<<2)	/* run this sublist asynchronously      (&)  */
 #define Z_DISOWN (1<<3)	/* run this sublist without job control (&|) */
+/* (1<<4) is used for Z_END, see the wordcode definitions */
+/* (1<<5) is used for Z_SIMPLE, see the wordcode definitions */
 
-/* flags for command modifiers */
-#define CFLAG_EXEC	(1<<0)	/* exec ...    */
+/* Condition types. */
 
 #define COND_NOT    0
 #define COND_AND    1
@@ -481,11 +500,24 @@
     char *strs;			/* strings from prog */
 };
 
+typedef struct eccstr *Eccstr;
+
+struct eccstr {
+    Eccstr next;
+    char *str;
+    wordcode offs;
+};
+
+#define EC_NODUP  0
+#define EC_DUP    1
+#define EC_DUPTOK 2
+
 #define WC_CODEBITS 5
 
 #define wc_code(C)   ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1)))
 #define wc_data(C)   ((C) >> WC_CODEBITS)
-#define wc_bld(C, D) (((wordcode) (C)) | (((wordcode) (D)) << WC_CODEBITS))
+#define wc_bdata(D)  ((D) << WC_CODEBITS)
+#define wc_bld(C,D)  (((wordcode) (C)) | (((wordcode) (D)) << WC_CODEBITS))
 
 #define WC_END      0
 #define WC_LIST     1
@@ -512,17 +544,20 @@
 
 #define WC_LIST_TYPE(C)     wc_data(C)
 #define Z_END               (1<<4) 
-#define WCB_LIST(T)         wc_bld(WC_LIST, (T))
+#define Z_SIMPLE            (1<<5)
+#define WC_LIST_SKIP(C)     (wc_data(C) >> 6)
+#define WCB_LIST(T,O)       wc_bld(WC_LIST, ((T) | ((O) << 6)))
 
 #define WC_SUBLIST_TYPE(C)  (wc_data(C) & ((wordcode) 3))
 #define WC_SUBLIST_END      0
 #define WC_SUBLIST_AND      1
 #define WC_SUBLIST_OR       2
-#define WC_SUBLIST_FLAGS(C) (wc_data(C) & ((wordcode) 12))
+#define WC_SUBLIST_FLAGS(C) (wc_data(C) & ((wordcode) 0x1c))
 #define WC_SUBLIST_COPROC   4
 #define WC_SUBLIST_NOT      8
-#define WC_SUBLIST_SKIP(C)  (wc_data(C) >> 4)
-#define WCB_SUBLIST(T,F,O)  wc_bld(WC_SUBLIST, ((T) | (F) | ((O) << 4)))
+#define WC_SUBLIST_SIMPLE  16
+#define WC_SUBLIST_SKIP(C)  (wc_data(C) >> 5)
+#define WCB_SUBLIST(T,F,O)  wc_bld(WC_SUBLIST, ((T) | (F) | ((O) << 5)))
 
 #define WC_PIPE_TYPE(C)     (wc_data(C) & ((wordcode) 1))
 #define WC_PIPE_END         0
@@ -612,7 +647,7 @@
     pid_t gleader;		/* process group leader of this job  */
     pid_t other;		/* subjob id or subshell pid         */
     int stat;                   /* see STATs below                   */
-    char pwd[PATH_MAX + 1];	/* current working dir of shell when *
+    char *pwd;			/* 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 */
@@ -683,7 +718,8 @@
 
 struct heredocs {
     struct heredocs *next;
-    Redir rd;
+    Wordcode pc;
+    char *str;
 };
 
 struct dirsav {
@@ -968,7 +1004,7 @@
 #define GF_BACKREF	0x0400
 #define GF_MATCHREF	0x0800
 
-/* Dummy Patprog pointers. Used mainly in executions trees, but the
+/* Dummy Patprog pointers. Used mainly in executable code, but the
  * pattern code needs to know about it, too. */
 
 #define dummy_patprog1 ((Patprog) 1)
@@ -1475,14 +1511,19 @@
 /****************************************/
 
 #define CMDSTACKSZ 256
-#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < CMDSTACKSZ)) {;} else cmdstack[cmdsp++]=(X)
+#define cmdpush(X) do { \
+                       if (cmdsp >= 0 && cmdsp < CMDSTACKSZ) \
+                           cmdstack[cmdsp++]=(X); \
+                   } while (0)
 #ifdef DEBUG
-# define cmdpop()  if (cmdsp <= 0) { \
-			fputs("BUG: cmdstack empty\n", stderr); \
-			fflush(stderr); \
-		   } else cmdsp--
+# define cmdpop()  do { \
+                       if (cmdsp <= 0) { \
+			   fputs("BUG: cmdstack empty\n", stderr); \
+			   fflush(stderr); \
+		       } else cmdsp--; \
+                   } while (0)
 #else
-# define cmdpop()   if (cmdsp <= 0) {;} else cmdsp--
+# define cmdpop()   do { if (cmdsp > 0) cmdsp--; } while (0)
 #endif
 
 #define CS_FOR          0

--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
  2000-02-23 13:21 PATCH: parser (was: Re: PATCH: Improved _mailboxes) Sven Wischnowsky
@ 2000-02-23 16:45 ` Bart Schaefer
  2000-02-23 18:58 ` Peter Stephenson
  2000-02-24  7:47 ` Andrej Borsenkow
  2 siblings, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2000-02-23 16:45 UTC (permalink / raw)
  To: Sven Wischnowsky, zsh-workers

On Feb 23,  2:21pm, Sven Wischnowsky wrote:
} Subject: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
}
} - Lists and sublists that are executed completely in the shell (as
}   found out by the parser) are marked and the execution code then
}   avoids calling execpline() and execcmd() for them.

That ought to make some debugging chores easier ... I was worried that
this would break the xtrace redirections test, but redirecions must be
considered "not completely in the shell."

} Ok. Tell me what you think of these. I can always remove some of the
} optimisations if you think they are too weird.

Optimization is weird almost by definition.

} P.S.: If a day without a patch is like a day without sunshine, what
}       does this say about a patch with 6136 lines?

In the U.S. there's a saying, "A hundred and ten degrees in the shade."
(Farenheit, that is.  "43 degrees ..." doesn't have the same oomph.)

Of course, with an 8000+ -line patch right behind it ....

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
  2000-02-23 13:21 PATCH: parser (was: Re: PATCH: Improved _mailboxes) Sven Wischnowsky
  2000-02-23 16:45 ` Bart Schaefer
@ 2000-02-23 18:58 ` Peter Stephenson
  2000-02-24  7:47 ` Andrej Borsenkow
  2 siblings, 0 replies; 10+ messages in thread
From: Peter Stephenson @ 2000-02-23 18:58 UTC (permalink / raw)
  To: zsh-workers

This will probably be my last email for a while...

Sven Wischnowsky wrote:
> Here is the first of the two. As I said, this mainly makes the parser
> create wordcode directly, no more extra compilation phase.

I'm getting a core dump in zfinit from my .zshrc (after both patches, in
fact).  It's in text.c.  Actually, there seem to be two bugs there.  One
happens when a function is empty and you run `which' or equivalent:
getpermtext() calls gettext2() without realising that the prog.len is zero,
so it's only going to find garbage.  This must be because the code for
end-of-text is not being added, or if it was removed it's not being tested
enough.

I can't offhand see what's causing the second, but this seems to tickle it
on my system (although I only tried this after masking out the previous one
by putting an if (prog.len) before the call to gettext2()):

  chpwd() {
    if [[ ${+ZFTP_USER} = 1 && -n $ZFTP_USER ]]; then
      zftp_chpwd
    else
      [[ -t 1 && -t 2 ]] && header -P "%m:  %~";
    fi
  }
  which chpwd

causes a crash with a backtrace as follows, although it's probably more
useful to know that it seems to go wrong at the start of the second `[['.

#0  0x808d07f in taddstr (s=0x1e97c27d <Address 0x1e97c27d out of bounds>)
    at text.c:60
#1  0x808d154 in taddlist (state=0x7fffe000, num=3721) at text.c:81
#2  0x808d746 in gettext2 (state=0x7fffe000) at text.c:368
#3  0x808d246 in getpermtext (prog=0x80cd968, c=0x1) at text.c:126
#4  0x80667f8 in printshfuncnode (hn=0x80cd8e8, printflags=16)
    at hashtable.c:882
#5  0x80551aa in bin_whence (nam=0x2aac07d8 "which", argv=0x7ffff080, 
    ops=0x7ffff0d0 "", func=0) at builtin.c:2346
#6  0x8051bbd in execbuiltin (args=0x2aac07b8, bn=0x809d898) at builtin.c:367
#7  0x805df93 in execcmd (state=0x7ffff354, input=0, output=0, how=18, last1=2)
    at exec.c:2235
#8  0x805b9ef in execpline2 (state=0x7ffff354, pcode=579, how=18, input=0, 
    output=0, last1=0) at exec.c:1172
#9  0x805b235 in execpline (state=0x7ffff354, slcode=4098, how=18, last1=0)
    at exec.c:965
#10 0x805ad98 in execlist (state=0x7ffff354, dont_change_job=0, exiting=0)
    at exec.c:812
#11 0x805ab7a in execode (p=0x7fffe000, dont_change_job=0, exiting=135061469)
    at exec.c:721
#12 0x806a744 in loop (toplevel=1, justonce=0) at init.c:144
#13 0x80512ee in main (argc=2, argv=0x7ffff3f4) at ./main.c:86

-- 
Peter Stephenson <pws@pwstephenson.fsnet.co.uk>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
  2000-02-23 13:21 PATCH: parser (was: Re: PATCH: Improved _mailboxes) Sven Wischnowsky
  2000-02-23 16:45 ` Bart Schaefer
  2000-02-23 18:58 ` Peter Stephenson
@ 2000-02-24  7:47 ` Andrej Borsenkow
  2 siblings, 0 replies; 10+ messages in thread
From: Andrej Borsenkow @ 2000-02-24  7:47 UTC (permalink / raw)
  To: Sven Wischnowsky, zsh-workers

>
> Here is the first of the two. As I said, this mainly makes the parser
> create wordcode directly, no more extra compilation phase.
>
>

Sounds really interesting ... Two questions.

Is the code position-independent?
Is now code-compiler separated from code-interpreter?

The main reason for these questions - is it possible to precompile Zsh
function, store it and then execute directly? This may be intersted in many
cases - primary use is completion. Precompile completion functions; put
byte-code in single file; mmap this file. I do not know about speed increase
(if any) - but it should dramatically reduce RAM footprint on multiuser
systems. Currently every shell compiles every function on it's own and it
goes in private memory - and that is real RAM (O.K., it is real swap on some
systems :-) mmap'ing precompiled byte-code would mean, that just a single
copy exists.

It may be useful to generalize it to allow byte-code be the contents of
variable. Then Zsh could simply execute the content of

mapfile[/path/to/precompiled/file] ... may be, not as directly - with
something like imaginal zcompiler module

zcodeload file

/andrej



^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
  2000-02-25  8:41 Sven Wischnowsky
@ 2000-02-25  9:55 ` Andrej Borsenkow
  0 siblings, 0 replies; 10+ messages in thread
From: Andrej Borsenkow @ 2000-02-25  9:55 UTC (permalink / raw)
  To: Sven Wischnowsky, zsh-workers

>
> Bart Schaefer wrote:
>
> > On Feb 24, 10:07am, Sven Wischnowsky wrote:
> > } Subject: RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
> > }
> > }
> > } Andrej Borsenkow wrote:
> > }
> > } > zcodeload file
> >
> > Let's not do that, shall we?  Let's stick with autoload and have a file
> > suffix convention, like emacs' .el and .elc, or something.  Heck, there
> > could even be separate fpath and compiled_fpath or ...
>
> I was wondering what to do when the directory isn't writable... but a
> $COMPILED_FPATH containing one directory would be enough. Hm. Do you
> want to say that you actually like the idea? Making everything ready
> for the mmap would be quite simple. The only problem I can see is that
> we would need to have a wordcode-verifier (but, of course, that can be
> done). That's yet another reason for having only a scalar containing
> only one directory name (so $COMPILED_FDIR might be a better name) --
> save compiled functions only if that is set and names an existing,
> writable directory. Users would set it to a directory in their account
> so that others can't trick them into using evil code.
>

Ehh ... not sure, I really did mean it all :-) Do you suggest compiling
functions on the fly and storing byte code externally? And mmaping every
single function? Just some points.

- at least on my system (and it is pretty much standard SVR4) process memory
is the list of segments. Every mmap results in adding address segment. To
resolve virtual address, system needs to search this list. mmaping 30-40
functions will add corresponding number of segments - not only is it slow,
but due to alignment restrictions it is going to waste virtual memory.

- I actually meant, that precompiled (standard) functions are installed in
default system location. This all was intended only for those functions,
that come with zsh distribution and can be considered "read only". Doing
this for arbitrary function (or any piece of code) has obvious problem of
keeping two in sync. I am not sure, if it worth troubles.

- in other words, my intention was to have single file with byte code for
distributed functions (that can be included into distribution or generated
as part of build). This will basically predefine all functions, making them
"part" of zsh binary  - without need to autoload it. If user wants to
override them - he can always define function again. (actually, step further
is to load them in memory and dump executable. Sounds familiar, does not it
:-)

/andrej


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
@ 2000-02-25  8:41 Sven Wischnowsky
  2000-02-25  9:55 ` Andrej Borsenkow
  0 siblings, 1 reply; 10+ messages in thread
From: Sven Wischnowsky @ 2000-02-25  8:41 UTC (permalink / raw)
  To: zsh-workers


Bart Schaefer wrote:

> On Feb 24, 10:07am, Sven Wischnowsky wrote:
> } Subject: RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
> }
> } 
> } Andrej Borsenkow wrote:
> } 
> } > zcodeload file
> 
> Let's not do that, shall we?  Let's stick with autoload and have a file
> suffix convention, like emacs' .el and .elc, or something.  Heck, there
> could even be separate fpath and compiled_fpath or ...

I was wondering what to do when the directory isn't writable... but a
$COMPILED_FPATH containing one directory would be enough. Hm. Do you
want to say that you actually like the idea? Making everything ready
for the mmap would be quite simple. The only problem I can see is that 
we would need to have a wordcode-verifier (but, of course, that can be 
done). That's yet another reason for having only a scalar containing
only one directory name (so $COMPILED_FDIR might be a better name) --
save compiled functions only if that is set and names an existing,
writable directory. Users would set it to a directory in their account 
so that others can't trick them into using evil code.

> } All this also makes me think about a way to allow multiple zsh's to
> } share other memory bits (like the command table and so on). How
> } portable is anonymous shared mmap or shared mmap on /dev/null?
> 
> Do we really want to go down the road of having e.g. zmodload in one
> zsh suddenly make new builtins available to another zsh?  I don't want
> the behavior of a script that's running in the background to change
> because of something I loaded into my foreground shell ...

Should be configurable, of course. And to be turned on explicitly. If
at all...

Bye
 Sven


--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
  2000-02-24  9:07 Sven Wischnowsky
@ 2000-02-24 18:08 ` Bart Schaefer
  0 siblings, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2000-02-24 18:08 UTC (permalink / raw)
  To: zsh-workers

On Feb 24, 10:07am, Sven Wischnowsky wrote:
} Subject: RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
}
} 
} Andrej Borsenkow wrote:
} 
} > zcodeload file

Let's not do that, shall we?  Let's stick with autoload and have a file
suffix convention, like emacs' .el and .elc, or something.  Heck, there
could even be separate fpath and compiled_fpath or ...

} All this also makes me think about a way to allow multiple zsh's to
} share other memory bits (like the command table and so on). How
} portable is anonymous shared mmap or shared mmap on /dev/null?

Do we really want to go down the road of having e.g. zmodload in one
zsh suddenly make new builtins available to another zsh?  I don't want
the behavior of a script that's running in the background to change
because of something I loaded into my foreground shell ...

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
@ 2000-02-24 10:03 Sven Wischnowsky
  0 siblings, 0 replies; 10+ messages in thread
From: Sven Wischnowsky @ 2000-02-24 10:03 UTC (permalink / raw)
  To: zsh-workers


Peter Stephenson wrote:

> This will probably be my last email for a while...
> 
> Sven Wischnowsky wrote:
> > Here is the first of the two. As I said, this mainly makes the parser
> > create wordcode directly, no more extra compilation phase.
> 
> I'm getting a core dump in zfinit from my .zshrc (after both patches, in
> fact).  It's in text.c.  Actually, there seem to be two bugs there.  One
> happens when a function is empty and you run `which' or equivalent:
> getpermtext() calls gettext2() without realising that the prog.len is zero,
> so it's only going to find garbage.  This must be because the code for
> end-of-text is not being added, or if it was removed it's not being tested
> enough.

Oops. There were only three places where I forgot to use par_save_list()
instead of par_list(). That's fixed now.

> I can't offhand see what's causing the second, but this seems to tickle it
> on my system (although I only tried this after masking out the previous one
> by putting an if (prog.len) before the call to gettext2()):
> 
>   chpwd() {
>     if [[ ${+ZFTP_USER} = 1 && -n $ZFTP_USER ]]; then
>       zftp_chpwd
>     else
>       [[ -t 1 && -t 2 ]] && header -P "%m:  %~";
>     fi
>   }
>   which chpwd
> 
> causes a crash with a backtrace as follows, although it's probably more
> useful to know that it seems to go wrong at the start of the second `[['.

The problem was that I forgot to tell text.c about simplified sublists.


Bye
 Sven

diff -ru ../z.old/Src/parse.c Src/parse.c
--- ../z.old/Src/parse.c	Thu Feb 24 10:46:25 2000
+++ Src/parse.c	Thu Feb 24 10:54:02 2000
@@ -1154,13 +1154,13 @@
 	    yylex();
 	if (tok == INBRACE && usebrace) {
 	    yylex();
-	    par_list(complex);
+	    par_save_list(complex);
 	    if (tok != OUTBRACE) {
 		cmdpop();
 		YYERRORV(oecused);
 	    }
 	} else {
-	    par_list(complex);
+	    par_save_list(complex);
 	    if (tok != FI) {
 		cmdpop();
 		YYERRORV(oecused);
@@ -1507,7 +1507,7 @@
 		int c = 0;
 
 		yylex();
-		par_list(&c);
+		par_save_list(&c);
 		if (tok != OUTBRACE) {
 		    cmdpop();
 		    lineno += oldlineno;
diff -ru ../z.old/Src/text.c Src/text.c
--- ../z.old/Src/text.c	Thu Feb 24 10:46:26 2000
+++ Src/text.c	Thu Feb 24 10:58:46 2000
@@ -269,7 +269,7 @@
 		    taddstr("! ");
 		if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_COPROC)
 		    taddstr("coproc ");
-		tpush(code, (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END));
+		s = tpush(code, (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END));
 	    } else {
 		if (!(stack = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END))) {
 		    taddstr((WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) ?
@@ -282,6 +282,8 @@
 			taddstr("coproc ");
 		}
 	    }
+	    if (!stack && (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_SIMPLE))
+		state->pc++;
 	    break;
 	case WC_PIPE:
 	    if (!s) {

--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
@ 2000-02-24  9:07 Sven Wischnowsky
  2000-02-24 18:08 ` Bart Schaefer
  0 siblings, 1 reply; 10+ messages in thread
From: Sven Wischnowsky @ 2000-02-24  9:07 UTC (permalink / raw)
  To: zsh-workers


Andrej Borsenkow wrote:

> > Here is the first of the two. As I said, this mainly makes the parser
> > create wordcode directly, no more extra compilation phase.
> 
> Sounds really interesting ... Two questions.
> 
> Is the code position-independent?

Yes, I took care of that to make copying easier and allow other things 
(memory compaction?).

> Is now code-compiler separated from code-interpreter?

There is now nothing I would really call a compiler anymore.

> The main reason for these questions - is it possible to precompile Zsh
> function, store it and then execute directly? This may be intersted in many
> cases - primary use is completion. Precompile completion functions; put
> byte-code in single file; mmap this file. I do not know about speed increase
> (if any) - but it should dramatically reduce RAM footprint on multiuser
> systems. Currently every shell compiles every function on it's own and it
> goes in private memory - and that is real RAM (O.K., it is real swap on some
> systems :-) mmap'ing precompiled byte-code would mean, that just a single
> copy exists.

That would indeed be possible. The wordcode is position- and machine-
independent.

> It may be useful to generalize it to allow byte-code be the contents of
> variable. Then Zsh could simply execute the content of
> 
> mapfile[/path/to/precompiled/file] ... may be, not as directly - with
> something like imaginal zcompiler module
> 
> zcodeload file

All this also makes me think about a way to allow multiple zsh's to
share other memory bits (like the command table and so on). How
portable is anonymous shared mmap or shared mmap on /dev/null?

Bye
 Sven


--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
@ 2000-02-24  8:54 Sven Wischnowsky
  0 siblings, 0 replies; 10+ messages in thread
From: Sven Wischnowsky @ 2000-02-24  8:54 UTC (permalink / raw)
  To: zsh-workers


Bart Schaefer wrote:

> On Feb 23,  2:21pm, Sven Wischnowsky wrote:
> } Subject: PATCH: parser (was: Re: PATCH: Improved _mailboxes)
> }
> } - Lists and sublists that are executed completely in the shell (as
> }   found out by the parser) are marked and the execution code then
> }   avoids calling execpline() and execcmd() for them.
> 
> That ought to make some debugging chores easier ... I was worried that
> this would break the xtrace redirections test, but redirecions must be
> considered "not completely in the shell."

Right. I wanted to make the most common case fast and redirections
aren't used that often in most scripts/functions.

Bye
 Sven


--
Sven Wischnowsky                         wischnow@informatik.hu-berlin.de


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2000-02-25  9:56 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-02-23 13:21 PATCH: parser (was: Re: PATCH: Improved _mailboxes) Sven Wischnowsky
2000-02-23 16:45 ` Bart Schaefer
2000-02-23 18:58 ` Peter Stephenson
2000-02-24  7:47 ` Andrej Borsenkow
2000-02-24  8:54 Sven Wischnowsky
2000-02-24  9:07 Sven Wischnowsky
2000-02-24 18:08 ` Bart Schaefer
2000-02-24 10:03 Sven Wischnowsky
2000-02-25  8:41 Sven Wischnowsky
2000-02-25  9:55 ` Andrej Borsenkow

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).