about summary refs log tree commit diff
path: root/Src/exec.c
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>2000-01-17 18:25:25 +0000
committerTanaka Akira <akr@users.sourceforge.net>2000-01-17 18:25:25 +0000
commit63acd1060174cd9d3595abb3fe71174c595e8131 (patch)
tree5885596a27556a0d5bfd104ec18848d0c05ce759 /Src/exec.c
parentcf10e6052445bc5460e86fc9621dec4cd4b9cb02 (diff)
downloadzsh-63acd1060174cd9d3595abb3fe71174c595e8131.tar.gz
zsh-63acd1060174cd9d3595abb3fe71174c595e8131.tar.xz
zsh-63acd1060174cd9d3595abb3fe71174c595e8131.zip
zsh-workers/9332
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c666
1 files changed, 397 insertions, 269 deletions
diff --git a/Src/exec.c b/Src/exec.c
index 0c87f4500..b076f4af9 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -132,22 +132,22 @@ static int doneps4;
 /* parse string into a list */
 
 /**/
-mod_export List
+mod_export Eprog
 parse_string(char *s, int ln)
 {
-    List l;
+    Eprog p;
     int oldlineno = lineno;
 
     lexsave();
     inpush(s, (ln ? INP_LINENO : 0), NULL);
     strinbeg(0);
     lineno = ln ? 1 : -1;
-    l = parse_list();
+    p = parse_list();
     lineno = oldlineno;
     strinend();
     inpop();
     lexrestore();
-    return l;
+    return p;
 }
 
 /**/
@@ -300,12 +300,12 @@ static char list_pipe_text[JOBTEXTSIZE];
 
 /**/
 static int
-execcursh(Cmd cmd, LinkList args, int flags)
+execcursh(Estate state, int do_exec)
 {
     if (!list_pipe && thisjob != list_pipe_job)
 	deletejob(jobtab + thisjob);
     cmdpush(CS_CURSH);
-    execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
+    execlist(state, 1, do_exec);
     cmdpop();
 
     return lastval;
@@ -686,14 +686,27 @@ hashcmd(char *arg0, char **pp)
 mod_export void
 execstring(char *s, int dont_change_job, int exiting)
 {
-    List list;
+    Eprog prog;
 
     pushheap();
-    if ((list = parse_string(s, 0)))
-	execlist(list, dont_change_job, exiting);
+    if ((prog = parse_string(s, 0)))
+	execode(prog, dont_change_job, exiting);
     popheap();
 }
 
+/**/
+void
+execode(Eprog p, int dont_change_job, int exiting)
+{
+    struct estate s;
+
+    s.prog = p;
+    s.pc = p->prog;
+    s.strs = p->strs;
+
+    execlist(&s, dont_change_job, exiting);
+}
+
 /* 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 *
@@ -703,11 +716,12 @@ execstring(char *s, int dont_change_job, int exiting)
 
 /**/
 void
-execlist(List list, int dont_change_job, int exiting)
+execlist(Estate state, int dont_change_job, int exiting)
 {
-    Sublist slist;
     static int donetrap;
-    int ret, cj, csp;
+    Wordcode next;
+    wordcode code;
+    int ret, cj, csp, ltype;
     int old_pline_level, old_list_pipe, oldlineno;
     /*
      * ERREXIT only forces the shell to exit if the last command in a &&
@@ -727,31 +741,41 @@ execlist(List list, int dont_change_job, int exiting)
 
     /* Loop over all sets of comands separated by newline, *
      * semi-colon or ampersand (`sublists').               */
-    while (list && list != &dummy_list && !breaks && !retflag) {
+    code = *state->pc++;
+    while (wc_code(code) == WC_LIST && !breaks && !retflag) {
 	/* Reset donetrap:  this ensures that a trap is only *
 	 * called once for each sublist that fails.          */
 	donetrap = 0;
-	slist = list->left;
 	csp = cmdsp;
+	ltype = WC_LIST_TYPE(code);
 
 	/* Loop through code followed by &&, ||, or end of sublist. */
-	while (slist) {
+	code = *state->pc++;
+	while (wc_code(code) == WC_SUBLIST) {
+	    next = state->pc + WC_SUBLIST_SKIP(code);
 	    if (!oldnoerrexit)
-		noerrexit = (slist->type != END);
-	    switch (slist->type) {
-	    case END:
+		noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END);
+	    switch (WC_SUBLIST_TYPE(code)) {
+	    case WC_SUBLIST_END:
 		/* End of sublist; just execute, ignoring status. */
-		execpline(slist, list->type, !list->right && exiting);
+		execpline(state, code, ltype, (ltype & Z_END) && exiting);
+		state->pc = next;
 		goto sublist_done;
 		break;
-	    case ANDNEXT:
+	    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(slist, Z_SYNC, 0))) {
-		    while ((slist = slist->right))
-			if (slist->type == ORNEXT)
-			    break;
-		    if (!slist) {
+		if ((ret = execpline(state, code, Z_SYNC, 0))) {
+		    state->pc = next;
+		    code = *state->pc++;
+		    next = state->pc + WC_SUBLIST_SKIP(code);
+		    while (wc_code(code) == WC_SUBLIST &&
+			   WC_SUBLIST_TYPE(code) == WC_SUBLIST_AND) {
+			state->pc = next;
+			code = *state->pc++;
+			next = state->pc + WC_SUBLIST_SKIP(code);
+		    }
+		    if (wc_code(code) != WC_SUBLIST) {
 			/* We've skipped to the end of the list, not executing *
 			 * the final pipeline, so don't perform error handling *
 			 * for this sublist.                                   */
@@ -761,26 +785,34 @@ execlist(List list, int dont_change_job, int exiting)
 		}
 		cmdpush(CS_CMDAND);
 		break;
-	    case ORNEXT:
+	    case WC_SUBLIST_OR:
 		/* If the return code is zero, we skip pipelines until *
 		 * we find a sublist followed by ANDNEXT.              */
-		if (!(ret = execpline(slist, Z_SYNC, 0))) {
-		    while ((slist = slist->right))
-			if (slist->type == ANDNEXT)
-			    break;
-		    if (!slist) {
+		if (!(ret = execpline(state, code, Z_SYNC, 0))) {
+		    state->pc = next;
+		    code = *state->pc++;
+		    next = state->pc + WC_SUBLIST_SKIP(code);
+		    while (wc_code(code) == WC_SUBLIST &&
+			   WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) {
+			state->pc = next;
+			code = *state->pc++;
+			next = state->pc + WC_SUBLIST_SKIP(code);
+		    }
+		    if (wc_code(code) != WC_SUBLIST) {
 			/* We've skipped to the end of the list, not executing *
 			 * the final pipeline, so don't perform error handling *
 			 * for this sublist.                                   */
 			donetrap = 1;
 			goto sublist_done;
-		     }
+		    }
 		}
 		cmdpush(CS_CMDOR);
 		break;
 	    }
-	    slist = slist->right;
+	    state->pc = next;
+	    code = *state->pc++;
 	}
+	state->pc--;
 sublist_done:
 
 	cmdsp = csp;
@@ -806,9 +838,12 @@ sublist_done:
 		    exit(lastval);
 	    }
 	}
-
-	list = list->right;
+	if (ltype & Z_END)
+	    break;
+	code = *state->pc++;
     }
+    if (wc_code(code) == WC_END)
+	state->pc--;
 
     pline_level = old_pline_level;
     list_pipe = old_list_pipe;
@@ -829,15 +864,17 @@ sublist_done:
 
 /**/
 static int
-execpline(Sublist l, int how, int last1)
+execpline(Estate state, wordcode slcode, int how, int last1)
 {
     int ipipe[2], opipe[2];
     int pj, newjob;
     int old_simple_pline = simple_pline;
+    int slflags = WC_SUBLIST_FLAGS(slcode);
+    wordcode code = *state->pc++;
     static int lastwj, lpforked;
 
-    if (!l->left)
-	return lastval = (l->flags & PFLAG_NOT) != 0;
+    if (wc_code(code) != WC_PIPE)
+	return lastval = (slflags & WC_SUBLIST_NOT) != 0;
 
     pj = thisjob;
     ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0;
@@ -846,10 +883,11 @@ execpline(Sublist l, int how, int last1)
     /* get free entry in job table and initialize it */
     if ((thisjob = newjob = initjob()) == -1)
 	return 1;
+
     if (how & Z_TIMED)
 	jobtab[thisjob].stat |= STAT_TIMED;
 
-    if (l->flags & PFLAG_COPROC) {
+    if (slflags & WC_SUBLIST_COPROC) {
 	how = Z_ASYNC;
 	if (coprocin >= 0) {
 	    zclose(coprocin);
@@ -869,15 +907,15 @@ execpline(Sublist l, int how, int last1)
 	list_pipe_job = newjob;
         list_pipe_pid = 0;
 	nowait = 0;
-	simple_pline = (l->left->type == END);
+	simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END);
     }
     lastwj = lpforked = 0;
-    execpline2(l->left, how, opipe[0], ipipe[1], last1);
+    execpline2(state, code, how, opipe[0], ipipe[1], last1);
     pline_level--;
     if (how & Z_ASYNC) {
 	lastwj = newjob;
 	jobtab[thisjob].stat |= STAT_NOSTTY;
-	if (l->flags & PFLAG_COPROC) {
+	if (slflags & WC_SUBLIST_COPROC) {
 	    zclose(ipipe[1]);
 	    zclose(opipe[0]);
 	}
@@ -1027,7 +1065,7 @@ execpline(Sublist l, int how, int last1)
 	    thisjob = pj;
 
 	}
-	if (l->flags & PFLAG_NOT)
+	if (slflags & WC_SUBLIST_NOT)
 	    lastval = !lastval;
     }
     if (!pline_level)
@@ -1041,7 +1079,8 @@ static int subsh_close = -1;
 
 /**/
 static void
-execpline2(Pline pline, int how, int input, int output, int last1)
+execpline2(Estate state, wordcode pcode,
+	   int how, int input, int output, int last1)
 {
     pid_t pid;
     int pipes[2];
@@ -1049,26 +1088,31 @@ execpline2(Pline pline, int how, int input, int output, int last1)
     if (breaks || retflag)
 	return;
 
-    if (pline->left->lineno >= 0)
-	lineno = pline->left->lineno;
+    if (WC_PIPE_LINENO(pcode))
+	lineno = WC_PIPE_LINENO(pcode) - 1;
 
     if (pline_level == 1) {
 	if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel))
-	    strcpy(list_pipe_text, getjobtext((void *) pline->left));
+	    strcpy(list_pipe_text, getjobtext(state->prog, state->pc - 1));
 	else
 	    list_pipe_text[0] = '\0';
     }
-    if (pline->type == END)
-	execcmd(pline->left, input, output, how, last1 ? 1 : 2);
+    if (WC_PIPE_TYPE(pcode) == WC_PIPE_END)
+	execcmd(state, input, output, how, last1 ? 1 : 2);
     else {
 	int old_list_pipe = list_pipe;
+	Wordcode next = state->pc + (*state->pc);
+	wordcode code;
+
+	state->pc++;
+	code = *state->pc;
 
 	mpipe(pipes);
 
 	/* if we are doing "foo | bar" where foo is a current *
 	 * shell command, do foo in a subshell and do the     *
 	 * rest of the pipeline in the current shell.         */
-	if (pline->left->type >= CURSH && (how & Z_SYNC)) {
+	if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) {
 	    int synch[2];
 
 	    pipe(synch);
@@ -1079,7 +1123,7 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 	    } else if (pid) {
 		char dummy, *text;
 
-		text = getjobtext((void *) pline->left);
+		text = getjobtext(state->prog, state->pc - 2);
 		addproc(pid, text);
 		close(synch[1]);
 		read(synch[0], &dummy, 1);
@@ -1089,26 +1133,26 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 		close(synch[0]);
 		entersubsh(how, 2, 0);
 		close(synch[1]);
-		execcmd(pline->left, input, pipes[1], how, 0);
+		execcmd(state, input, pipes[1], how, 0);
 		_exit(lastval);
 	    }
 	} else {
 	    /* otherwise just do the pipeline normally. */
 	    subsh_close = pipes[0];
-	    execcmd(pline->left, input, pipes[1], how, 0);
+	    execcmd(state, input, pipes[1], how, 0);
 	}
 	zclose(pipes[1]);
-	if (pline->right) {
-	    /* if another execpline() is invoked because the command is *
-	     * a list it must know that we're already in a pipeline     */
-	    cmdpush(CS_PIPE);
-	    list_pipe = 1;
-	    execpline2(pline->right, how, pipes[0], output, last1);
-	    list_pipe = old_list_pipe;
-	    cmdpop();
-	    zclose(pipes[0]);
-	    subsh_close = -1;
-	}
+	state->pc = next;
+
+	/* if another execpline() is invoked because the command is *
+	 * a list it must know that we're already in a pipeline     */
+	cmdpush(CS_PIPE);
+	list_pipe = 1;
+	execpline2(state, *state->pc++, how, pipes[0], output, last1);
+	list_pipe = old_list_pipe;
+	cmdpop();
+	zclose(pipes[0]);
+	subsh_close = -1;
     }
 }
 
@@ -1122,8 +1166,8 @@ makecline(LinkList list)
     char **argv, **ptr;
 
     /* A bigger argv is necessary for executing scripts */
-    ptr  =
-    argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *));
+    ptr = argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) *
+				       sizeof(char *));
     if (isset(XTRACE)) {
 	if (!doneps4)
 	    printprompt4();
@@ -1321,42 +1365,46 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag)
 
 /**/
 static void
-addvars(LinkList l, int export)
+addvars(Estate state, Wordcode pc, int export)
 {
-    Varasg v;
-    LinkNode n;
     LinkList vl;
-    int xtr;
+    int xtr, isstr;
     char **arr, **ptr, *name;
+    Wordcode opc = state->pc;
+    wordcode ac;
 
     xtr = isset(XTRACE);
-    if (xtr && nonempty(l)) {
+    if (xtr) {
 	printprompt4();
 	doneps4 = 1;
     }
-
-    for (n = firstnode(l); n; incnode(n)) {
-	v = (Varasg) getdata(n);
-	name = dupstring(v->name);
+    state->pc = pc;
+    while (wc_code(ac = *state->pc++) == WC_ASSIGN) {
+	name = ecgetstr(state, 1);
 	untokenize(name);
 	if (xtr)
 	    fprintf(stderr, "%s=", name);
-	if (v->type == PM_SCALAR) {
+	if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) {
 	    vl = newlinklist();
-	    addlinknode(vl, dupstring(v->str));
+	    addlinknode(vl, ecgetstr(state, 1));
 	} else
-	    vl = listdup(v->arr);
+	    vl = ecgetlist(state, WC_ASSIGN_NUM(ac), 1);
+
 	if (vl) {
-	    prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) :
-		                               PF_ASSIGN);
-	    if (errflag)
+	    prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) :
+			 PF_ASSIGN));
+	    if (errflag) {
+		state->pc = opc;
 		return;
-	    if (isset(GLOBASSIGN) || v->type != PM_SCALAR)
+	    }
+	    if (isset(GLOBASSIGN) || !isstr)
 		globlist(vl);
-	    if (errflag)
+	    if (errflag) {
+		state->pc = opc;
 		return;
+	    }
 	}
-	if (v->type == PM_SCALAR && (empty(vl) || !nextnode(firstnode(vl)))) {
+	if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) {
 	    Param pm;
 	    char *val;
 	    int allexp;
@@ -1375,6 +1423,7 @@ addvars(LinkList l, int export)
 		    (pm->flags & PM_RESTRICTED)) {
 		    zerr("%s: restricted", pm->nam, 0);
 		    zsfree(val);
+		    state->pc = opc;
 		    return;
 		}
 		allexp = opts[ALLEXPORT];
@@ -1383,8 +1432,10 @@ addvars(LinkList l, int export)
 		opts[ALLEXPORT] = allexp;
 	    } else
 		pm = setsparam(name, val);
-	    if (errflag)
+	    if (errflag) {
+		state->pc = opc;
 		return;
+	    }
 	    continue;
 	}
 	if (vl) {
@@ -1404,9 +1455,12 @@ addvars(LinkList l, int export)
 	    fprintf(stderr, ") ");
 	}
 	setaparam(name, arr);
-	if (errflag)
+	if (errflag) {
+	    state->pc = opc;
 	    return;
+	}
     }
+    state->pc = opc;
 }
 
 /**/
@@ -1430,9 +1484,25 @@ setunderscore(char *str)
     }
 }
 
+static int esprefork, esglob;
+
+/**/
+void
+execsubst(LinkList strs)
+{
+    if (strs) {
+	prefork(strs, esprefork);
+	if (esglob) {
+	    LinkList ostrs = strs;
+	    globlist(strs);
+	    strs = ostrs;
+	}
+    }
+}
+
 /**/
 static void
-execcmd(Cmd cmd, int input, int output, int how, int last1)
+execcmd(Estate state, int input, int output, int how, int last1)
 {
     HashNode hn = NULL;
     LinkNode node;
@@ -1440,19 +1510,31 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     struct multio *mfds[10];
     char *text;
     int save[10];
-    int fil, dfil, is_cursh, type, flags, i;
+    int fil, dfil, is_cursh, type, do_exec = 0, i;
     int nullexec = 0, assign = 0, forked = 0;
     int is_shfunc = 0, is_builtin = 0, is_exec = 0;
     /* Various flags to the command. */
     int cflags = 0, checked = 0;
-    LinkList vars, redir;
+    LinkList redir;
+    wordcode code;
+    Wordcode beg = state->pc, varspc;
 
     doneps4 = 0;
-    args = listdup(cmd->args);
-    type = cmd->type;
-    flags = cmd->flags;
-    redir = dupheaplist(cmd->redir);
-    vars = cmd->vars;
+    redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
+    if (wc_code(*state->pc) == WC_ASSIGN) {
+	varspc = state->pc;
+	while (wc_code((code = *state->pc)) == WC_ASSIGN)
+	    state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
+			  3 : WC_ASSIGN_NUM(code) + 2);
+    } else
+	varspc = NULL;
+
+    code = *state->pc++;
+
+    type = wc_code(code);
+
+    args = (type == WC_SIMPLE ?
+	    ecgetlist(state, WC_SIMPLE_ARGC(code), 1) : NULL);
 
     for (i = 0; i < 10; i++) {
 	save[i] = -2;
@@ -1461,7 +1543,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 
     /* If the command begins with `%', then assume it is a *
      * reference to a job in the job table.                */
-    if (type == SIMPLE && args && nonempty(args) &&
+    if (type == WC_SIMPLE && args && nonempty(args) &&
 	*(char *)peekfirst(args) == '%') {
 	pushnode(args, dupstring((how & Z_DISOWN)
 				 ? "disown" : (how & Z_ASYNC) ? "bg" : "fg"));
@@ -1472,9 +1554,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * any redirections, then check if it matches as a prefix of a   *
      * job currently in the job table.  If it does, then we treat it *
      * as a command to resume this job.                              */
-    if (isset(AUTORESUME) && type == SIMPLE && (how & Z_SYNC) &&
-	args && nonempty(args) &&
-	(!cmd->redir || empty(cmd->redir)) && !input &&
+    if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) &&
+	args && nonempty(args) && (!redir || empty(redir)) && !input &&
 	!nextnode(firstnode(args))) {
 	if (unset(NOTIFY))
 	    scanjobs();
@@ -1487,7 +1568,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * command if it contains some tokens (e.g. x=ex; ${x}port), so this *
      * only works in simple cases.  has_token() is called to make sure   *
      * this really is a simple case.                                     */
-    if (type == SIMPLE) {
+    if (type == WC_SIMPLE) {
 	while (args && nonempty(args)) {
 	    char *cmdarg = (char *) peekfirst(args);
 	    checked = !has_token(cmdarg);
@@ -1524,10 +1605,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     }
 
     /* Do prefork substitutions */
+    esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0;
     if (args)
-	prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0);
+	prefork(args, esprefork);
 
-    if (type == SIMPLE) {
+    if (type == WC_SIMPLE) {
 	int unglobbed = 0;
 
 	for (;;) {
@@ -1546,16 +1628,16 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	    /* Current shell should not fork unless the *
 	     * exec occurs at the end of a pipeline.    */
 	    if ((cflags & BINF_EXEC) && last1)
-		flags |= CFLAG_EXEC;
+		do_exec = 1;
 
 	    /* Empty command */
 	    if (!args || empty(args)) {
 		if (redir && nonempty(redir)) {
-		    if (flags & CFLAG_EXEC) {
+		    if (do_exec) {
 			/* Was this "exec < foobar"? */
 			nullexec = 1;
 			break;
-		    } else if (vars && nonempty(vars)) {
+		    } else if (varspc) {
 			nullexec = 2;
 			break;
 		    } else if (!nullcmd || !*nullcmd ||
@@ -1579,8 +1661,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    return;
 		} else {
 		    cmdoutval = 0;
-		    if (vars)
-			addvars(vars, 0);
+		    if (varspc)
+			addvars(state, varspc, 0);
 		    if (errflag)
 			lastval = errflag;
 		    else
@@ -1591,9 +1673,9 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    }
 		    return;
 		}
-	    } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) &&
-		       (flags & CFLAG_EXEC)) {
-		zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0);
+	    } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
+		zerrnam("exec", "%s: restricted",
+			(char *) getdata(firstnode(args)), 0);
 		lastval = 1;
 		return;
 	    }
@@ -1641,7 +1723,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     /* Get the text associated with this command. */
     if ((how & Z_ASYNC) ||
 	(!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
-	text = getjobtext((void *) cmd);
+	text = getjobtext(state->prog, beg);
     else
 	text = NULL;
 
@@ -1650,10 +1732,9 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : "");
 
     /* Warn about "rm *" */
-    if (type == SIMPLE && interact && unset(RMSTARSILENT)
-	&& isset(SHINSTDIN) && args && nonempty(args)
-	&& nextnode(firstnode(args))
-	&& !strcmp(peekfirst(args), "rm")) {
+    if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) &&
+	isset(SHINSTDIN) && args && nonempty(args) &&
+	nextnode(firstnode(args)) && !strcmp(peekfirst(args), "rm")) {
 	LinkNode node, next;
 
 	for (node = nextnode(firstnode(args)); node && !errflag; node = next) {
@@ -1682,12 +1763,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	return;
     }
 
-    if (type == SIMPLE && !nullexec) {
+    if (type == WC_SIMPLE && !nullexec) {
 	char *s;
-	char trycd = (isset(AUTOCD) && isset(SHINSTDIN)
-		      && (!redir || empty(redir)) && args && !empty(args)
-		      && !nextnode(firstnode(args))
-		      && *(char *)peekfirst(args));
+	char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
+		      (!redir || empty(redir)) && args && !empty(args) &&
+		      !nextnode(firstnode(args)) && *(char *)peekfirst(args));
 
 	DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c");
 	if (!hn) {
@@ -1724,7 +1804,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     }
 
     /* This is nonzero if the command is a current shell procedure? */
-    is_cursh = (is_builtin || is_shfunc || (type >= CURSH) || nullexec);
+    is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH);
 
     /**************************************************************************
      * Do we need to fork?  We need to fork if:                               *
@@ -1747,10 +1827,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
      * current shell.                                                         *
      **************************************************************************/
 
-    if ((how & Z_ASYNC) || (!(flags & CFLAG_EXEC) &&
-       (((is_builtin || is_shfunc) && output) ||
-       (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] ||
-        sigtrapped[SIGEXIT] || havefiles()))))) {
+    if ((how & Z_ASYNC) ||
+	(!do_exec &&
+	 (((is_builtin || is_shfunc) && output) ||
+	  (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] ||
+			 sigtrapped[SIGEXIT] || havefiles()))))) {
 
 	pid_t pid;
 	int synch[2];
@@ -1772,23 +1853,26 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 #endif
 	    if (how & Z_ASYNC) {
 		lastpid = (zlong) pid;
-	    } else if (!jobtab[thisjob].stty_in_env &&
-		       vars && nonempty(vars)) {
+	    } else if (!jobtab[thisjob].stty_in_env && varspc) {
 		/* search for STTY=... */
-		LinkNode n;
+		Wordcode p = varspc;
+		wordcode ac;
 
-		for (n = firstnode(vars); n; incnode(n))
-		    if (!strcmp(((Varasg) getdata(n))->name, "STTY")) {
+		while (wc_code(ac = *p) == WC_ASSIGN) {
+		    if (!strcmp(ecrawstr(state->prog, p + 1), "STTY")) {
 			jobtab[thisjob].stty_in_env = 1;
 			break;
 		    }
+		    p += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
+			  3 : WC_ASSIGN_NUM(ac) + 2);
+		}
 	    }
 	    addproc(pid, text);
 	    return;
 	}
 	/* pid == 0 */
 	close(synch[0]);
-	entersubsh(how, type != SUBSH && !(how & Z_ASYNC) ? 2 : 1, 0);
+	entersubsh(how, (type != WC_SUBSH) && !(how & Z_ASYNC) ? 2 : 1, 0);
 	close(synch[1]);
 	forked = 1;
 	if (sigtrapped[SIGINT] & ZSIG_IGNORED)
@@ -1811,7 +1895,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	is_exec = 1;
     }
 
-    if (args && !(cflags & BINF_NOGLOB)) {
+    if (args && (esglob = !(cflags & BINF_NOGLOB))) {
 	LinkList oargs = args;
 	globlist(args);
 	args=oargs;
@@ -1980,8 +2064,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	 * If nullexec is 2, we have variables to add with the redirections
 	 * in place.
 	 */
-	if (vars)
-	    addvars(vars, 0);
+	if (varspc)
+	    addvars(state, varspc, 0);
 	lastval = errflag ? errflag : cmdoutval;
 	if (isset(XTRACE)) {
 	    fputc('\n', stderr);
@@ -1994,17 +2078,17 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 	 * exit) in case there is an error return.
 	 */
 	if (is_exec)
-	    entersubsh(how, type != SUBSH ? 2 : 1, 1);
-	if (type >= CURSH) {
-	    static int (*func[]) _((Cmd, LinkList, int)) = {
-		execcursh, exectime, execfuncdef, execfor, execwhile,
-		execrepeat, execif, execcase, execselect, execcond,
+	    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)
-		flags |= CFLAG_EXEC;
-	    lastval = (func[type - CURSH]) (cmd, args, flags);
+		do_exec = 1;
+	    lastval = (func[type - WC_CURSH])(state, do_exec);
 	} else if (is_builtin || is_shfunc) {
 	    LinkList restorelist = 0, removelist = 0;
 	    /* builtin or shell function */
@@ -2013,13 +2097,13 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 			    (unset(POSIXBUILTINS) && !assign) ||
 			    (isset(POSIXBUILTINS) && !is_shfunc &&
 			     !(hn->flags & BINF_PSPECIAL))))
-		save_params(cmd, &restorelist, &removelist);
+		save_params(state, varspc, &restorelist, &removelist);
 
-	    if (vars) {
+	    if (varspc) {
 		/* Export this if the command is a shell function,
 		 * but not if it's a builtin.
 		 */
-		addvars(vars, is_shfunc);
+		addvars(state, varspc, is_shfunc);
 		if (errflag) {
 		    restore_params(restorelist, removelist);
 		    lastval = 1;
@@ -2041,7 +2125,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		if (subsh_close >= 0)
 		    zclose(subsh_close);
 		subsh_close = -1;
-		execshfunc(cmd, (Shfunc) hn, args);
+		execshfunc((Shfunc) hn, args);
 #ifdef PATH_DEV_FD
 		for (i = 10; i <= max_zsh_fd; i++)
 		    if (fdtable[i] > 1)
@@ -2070,7 +2154,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    clearerr(stdout);
 	    }
 
-	    if (flags & CFLAG_EXEC) {
+	    if (do_exec) {
 		if (subsh)
 		    _exit(lastval);
 
@@ -2080,21 +2164,20 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 		exit(lastval);
 	    }
-
 	    restore_params(restorelist, removelist);
 
 	} else {
 	    if (!forked)
 		setiparam("SHLVL", --shlvl);
-	    if (flags & CFLAG_EXEC) {
+	    if (do_exec) {
 		/* If we are exec'ing a command, and we are not *
 		 * in a subshell, then save the history file.   */
 		if (!subsh && isset(RCS) && interact && !nohistsave)
 		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 	    }
-	    if (type == SIMPLE) {
-		if (vars) {
-		    addvars(vars, -1);
+	    if (type == WC_SIMPLE) {
+		if (varspc) {
+		    addvars(state, varspc, -1);
 		    if (errflag)
 			_exit(1);
 		}
@@ -2109,7 +2192,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 #endif
 		execute((Cmdnam) hn, cflags & BINF_DASH);
 	    } else {		/* ( ... ) */
-		DPUTS(vars && nonempty(vars),
+		DPUTS(varspc,
 		      "BUG: assigment before complex command");
 		list_pipe = 0;
 		if (subsh_close >= 0)
@@ -2117,7 +2200,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
                 subsh_close = -1;
 		/* If we're forked (and we should be), no need to return */
 		DPUTS(last1 != 1 && !forked, "BUG: not exiting?");
-		execlist(cmd->u.list, 0, 1);
+		execlist(state, 0, 1);
 	    }
 	}
     }
@@ -2132,23 +2215,23 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 
 /**/
 static void
-save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p)
+save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
 {
     Param pm;
-    LinkNode node;
     char *s;
+    wordcode ac;
 
     MUSTUSEHEAP("save_params()");
     
-    if (!cmd->vars) {
+    if (!pc) {
 	*restore_p = *remove_p = NULL;
 	return;
     }
     *restore_p = newlinklist();
     *remove_p = newlinklist();
 
-    for (node = firstnode(cmd->vars); node; incnode(node)) {
-	s = ((Varasg) getdata(node))->name;
+    while (wc_code(ac = *pc) == WC_ASSIGN) {
+	s = ecrawstr(state->prog, pc + 1);
 	if ((pm = (Param) paramtab->getnode(paramtab, s))) {
 	    if (!(pm->flags & PM_SPECIAL)) {
 		paramtab->removenode(paramtab, s);
@@ -2161,9 +2244,11 @@ save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p)
 	    }
 	    addlinknode(*remove_p, s);
 	    addlinknode(*restore_p, pm);
-	} else {
+	} else
 	    addlinknode(*remove_p, s);
-	}
+
+	pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
+	       3 : WC_ASSIGN_NUM(ac) + 2);
     }
 }
 
@@ -2408,25 +2493,27 @@ getherestr(struct redir *fn)
 LinkList
 getoutput(char *cmd, int qt)
 {
-    List list;
+    Eprog prog;
     int pipes[2];
     pid_t pid;
-    Cmd c;
-    Redir r;
+    Wordcode pc;
 
-    if (!(list = parse_string(cmd, 0)))
+    if (!(prog = parse_string(cmd, 0)))
 	return NULL;
-    if (list != &dummy_list && !list->right && !list->left->flags &&
-	list->left->type == END && list->left->left->type == END &&
-	(c = list->left->left->left)->type == SIMPLE && 
-	(!c->args || empty(c->args)) &&
-	(!c->vars || empty(c->vars)) && c->redir && nonempty(c->redir) &&
-	!nextnode(firstnode(c->redir)) &&
-	(r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 &&
-	r->type == READ) {
+
+    pc = prog->prog;
+    if (prog != &dummy_eprog &&
+	wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) &&
+	wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) &&
+	WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END &&
+	wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END &&
+	wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == READ && 
+	!pc[4] &&
+	wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6]) &&
+	wc_code(pc[7]) == WC_END) {
 	/* $(< word) */
 	int stream;
-	char *s = r->name;
+	char *s = dupstring(ecrawstr(prog, pc + 5));
 
 	singsub(&s);
 	if (errflag)
@@ -2438,7 +2525,6 @@ getoutput(char *cmd, int qt)
 	}
 	return readoutput(stream, qt);
     }
-
     mpipe(pipes);
     child_block();
     cmdoutval = 0;
@@ -2460,14 +2546,13 @@ getoutput(char *cmd, int qt)
 	lastval = cmdoutval;
 	return retval;
     }
-
     /* pid == 0 */
     child_unblock();
     zclose(pipes[0]);
     redup(pipes[1], 1);
     opts[MONITOR] = 0;
     entersubsh(Z_SYNC, 1, 0);
-    execlist(list, 0, 1);
+    execode(prog, 0, 1);
     close(1);
     _exit(lastval);
     zerr("exit returned in child!!", NULL, 0);
@@ -2531,11 +2616,11 @@ readoutput(int in, int qt)
 }
 
 /**/
-static List
+static Eprog
 parsecmd(char *cmd)
 {
     char *str;
-    List list;
+    Eprog prog;
 
     for (str = cmd + 2; *str && *str != Outpar; str++);
     if (!*str || cmd[1] != Inpar) {
@@ -2543,11 +2628,11 @@ parsecmd(char *cmd)
 	return NULL;
     }
     *str = '\0';
-    if (str[1] || !(list = parse_string(cmd + 2, 0))) {
+    if (str[1] || !(prog = parse_string(cmd + 2, 0))) {
 	zerr("parse error in process substitution", NULL, 0);
 	return NULL;
     }
-    return list;
+    return prog;
 }
 
 /* =(...) */
@@ -2558,12 +2643,12 @@ getoutputfile(char *cmd)
 {
     pid_t pid;
     char *nam;
-    List list;
+    Eprog prog;
     int fd;
 
     if (thisjob == -1)
 	return NULL;
-    if (!(list = parsecmd(cmd)))
+    if (!(prog = parsecmd(cmd)))
 	return NULL;
     if (!(nam = gettempname()))
 	return NULL;
@@ -2596,7 +2681,7 @@ getoutputfile(char *cmd)
     redup(fd, 1);
     opts[MONITOR] = 0;
     entersubsh(Z_SYNC, 1, 0);
-    execlist(list, 0, 1);
+    execode(prog, 0, 1);
     close(1);
     _exit(lastval);
     zerr("exit returned in child!!", NULL, 0);
@@ -2632,7 +2717,7 @@ getproc(char *cmd)
     zerr("doesn't look like your system supports FIFOs.", NULL, 0);
     return NULL;
 #else
-    List list;
+    Eprog prog;
     int out = *cmd == Inang;
     char *pnam;
 #ifndef PATH_DEV_FD
@@ -2649,7 +2734,7 @@ getproc(char *cmd)
 #else
     pnam = ncalloc(strlen(PATH_DEV_FD) + 6);
 #endif
-    if (!(list = parsecmd(cmd)))
+    if (!(prog = parsecmd(cmd)))
 	return NULL;
 #ifndef PATH_DEV_FD
     PERMALLOC {
@@ -2681,7 +2766,7 @@ getproc(char *cmd)
     redup(pipes[out], out);
     closem(0);   /* this closes pipes[!out] as well */
 #endif
-    execlist(list, 0, 1);
+    execode(prog, 0, 1);
     zclose(out);
     _exit(lastval);
     return NULL;
@@ -2694,10 +2779,10 @@ getproc(char *cmd)
 static int
 getpipe(char *cmd)
 {
-    List list;
+    Eprog prog;
     int pipes[2], out = *cmd == Inang;
 
-    if (!(list = parsecmd(cmd)))
+    if (!(prog = parsecmd(cmd)))
 	return -1;
     mpipe(pipes);
     if (zfork()) {
@@ -2707,7 +2792,7 @@ getpipe(char *cmd)
     entersubsh(Z_ASYNC, 1, 0);
     redup(pipes[out], out);
     closem(0);	/* this closes pipes[!out] as well */
-    execlist(list, 0, 1);
+    execode(prog, 0, 1);
     _exit(lastval);
     return 0;
 }
@@ -2749,15 +2834,17 @@ extern int tracingcond;
 
 /**/
 static int
-execcond(Cmd cmd, LinkList args, int flags)
+execcond(Estate state, int do_exec)
 {
     int stat;
+
+    state->pc--;
     if (isset(XTRACE)) {
 	printprompt4();
 	fprintf(stderr, "[[");
 	tracingcond++;
     }
-    stat = !evalcond(cmd->u.cond);
+    stat = !evalcond(state);
     if (isset(XTRACE)) {
 	fprintf(stderr, " ]]\n");
 	fflush(stderr);
@@ -2770,7 +2857,7 @@ execcond(Cmd cmd, LinkList args, int flags)
 
 /**/
 static int
-execarith(Cmd cmd, LinkList args, int flags)
+execarith(Estate state, int do_exec)
 {
     char *e;
     zlong val = 0;
@@ -2779,12 +2866,13 @@ execarith(Cmd cmd, LinkList args, int flags)
 	printprompt4();
 	fprintf(stderr, "((");
     }
-    if (args)
-	while ((e = (char *) ugetnode(args))) {
-	    if (isset(XTRACE))
-		fprintf(stderr, " %s", e);
-	    val = mathevali(e);
-	}
+    e = ecgetstr(state, 1);
+    singsub(&e);
+    if (isset(XTRACE))
+	fprintf(stderr, " %s", e);
+
+    val = mathevali(e);
+
     if (isset(XTRACE)) {
 	fprintf(stderr, " ))\n");
 	fflush(stderr);
@@ -2797,16 +2885,16 @@ execarith(Cmd cmd, LinkList args, int flags)
 
 /**/
 static int
-exectime(Cmd cmd, LinkList args, int flags)
+exectime(Estate state, int do_exec)
 {
     int jb;
 
     jb = thisjob;
-    if (!cmd->u.pline) {
+    if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
 	shelltime();
 	return 0;
     }
-    execpline(cmd->u.pline, Z_TIMED|Z_SYNC, 0);
+    execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
     thisjob = jb;
     return lastval;
 }
@@ -2815,35 +2903,63 @@ exectime(Cmd cmd, LinkList args, int flags)
 
 /**/
 static int
-execfuncdef(Cmd cmd, LinkList args, int flags)
+execfuncdef(Estate state, int do_exec)
 {
     Shfunc shf;
     char *s;
-    int signum;
+    int signum, nprg, npats, num, len, plen, i;
+    Wordcode beg = state->pc, end, names;
+    Eprog prog;
+    Patprog *pp;
+
+    end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
+    num = state->pc[0];
+    names = state->pc + 1;
+    nprg = state->pc[1 + num] - 4;
+    npats = state->pc[2 + num];
+
+    state->pc += num + 3;
+
+    plen = (end - state->pc) * sizeof(wordcode);
+    len = plen + (npats * sizeof(Patprog));
+
+    PERMALLOC {
+	while (num--) {
+	    s = ecrawstr(state->prog, names++);
+	    prog = (Eprog) zalloc(sizeof(*prog));
+	    prog->heap = 0;
+	    prog->len = len;
+	    prog->npats = npats;
+	    prog->pats = pp = (Patprog *) zalloc(len);
+	    prog->prog = (Wordcode) (prog->pats + npats);
+	    for (i = npats; i--; pp++)
+		*pp = dummy_patprog1;
+	    memcpy(prog->prog, state->pc, plen);
+	    prog->strs = (char *) (prog->prog + nprg);
+	    prog->shf = NULL;
+
+	    shf = (Shfunc) zalloc(sizeof(*shf));
+	    shf->funcdef = prog;
+	    shf->flags = 0;
+
+	    /* is this shell function a signal trap? */
+	    if (!strncmp(s, "TRAP", 4) &&
+		(signum = getsignum(s + 4)) != -1) {
+		if (settrap(signum, shf->funcdef)) {
+		    freeeprog(shf->funcdef);
+		    zfree(shf, sizeof(*shf));
+		    state->pc = end;
+		    LASTALLOC_RETURN 1;
+		}
+		sigtrapped[signum] |= ZSIG_FUNC;
+	    }
+	    shfunctab->addnode(shfunctab, ztrdup(s), shf);
+	}
+    } LASTALLOC;
 
-    if (args) {
-	PERMALLOC {
-	    while ((s = (char *) ugetnode(args))) {
-		shf = (Shfunc) zalloc(sizeof *shf);
-		shf->funcdef = (List) dupstruct(cmd->u.list);
-		shf->flags = 0;
-
-		/* is this shell function a signal trap? */
-		if (!strncmp(s, "TRAP", 4) &&
-		    (signum = getsignum(s + 4)) != -1) {
-		    if (settrap(signum, shf->funcdef)) {
-			freestruct(shf->funcdef);
-			zfree(shf, sizeof *shf);
-			LASTALLOC_RETURN 1;
-		    }
-		    sigtrapped[signum] |= ZSIG_FUNC;
-  		}
-		shfunctab->addnode(shfunctab, ztrdup(s), shf);
-  	    }
-	} LASTALLOC;
-    }
     if(isset(HISTNOFUNCTIONS))
 	remhist();
+    state->pc = end;
     return 0;
 }
 
@@ -2851,7 +2967,7 @@ execfuncdef(Cmd cmd, LinkList args, int flags)
 
 /**/
 static void
-execshfunc(Cmd cmd, Shfunc shf, LinkList args)
+execshfunc(Shfunc shf, LinkList args)
 {
     LinkList last_file_list = NULL;
     unsigned char *ocs;
@@ -2903,27 +3019,29 @@ execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 
 /**/
 static int
-execautofn(Cmd cmd, LinkList args, int flags)
+execautofn(Estate state, int do_exec)
 {
-    Shfunc shf = cmd->u.autofn->shf;
+    Shfunc shf = state->prog->shf;
     int noalias = noaliases;
-    List l;
+    Eprog prog;
 
     pushheap();
 
     noaliases = (shf->flags & PM_UNALIASED);
-    l = getfpfunc(shf->nam);
+    prog = getfpfunc(shf->nam);
     noaliases = noalias;
 
-    if (l == &dummy_list) {
+    if (prog == &dummy_eprog) {
 	zerr("%s: function definition file not found", shf->nam, 0);
 	popheap();
 	return 1;
     }
+    if (!prog)
+	prog = &dummy_eprog;
     if (isset(KSHAUTOLOAD)) {
 	VARARR(char, n, strlen(shf->nam) + 1);
 	strcpy(n, shf->nam);
-	execlist(l, 1, 0);
+	execode(prog, 1, 0);
 	shf = (Shfunc) shfunctab->getnode(shfunctab, n);
 	if(!shf || (shf->flags & PM_UNDEFINED)) {
 	    zerr("%s: function not defined by file", n, 0);
@@ -2931,15 +3049,15 @@ execautofn(Cmd cmd, LinkList args, int flags)
 	    return 1;
 	}
     } else {
-	freestruct(shf->funcdef);
+	freeeprog(shf->funcdef);
 	PERMALLOC {
-	    shf->funcdef = dupstruct(stripkshdef(l, shf->nam));
+	    shf->funcdef = dupeprog(stripkshdef(prog, shf->nam));
 	} LASTALLOC;
 	shf->flags &= ~PM_UNDEFINED;
     }
     popheap();
 
-    execlist(shf->funcdef, 1, 0);
+    execode(shf->funcdef, 1, 0);
     return lastval;
 }
 
@@ -2950,20 +3068,20 @@ loadautofn(Shfunc shf)
     /* Copied from execautofn() -- should consolidate someday */
 
     int noalias = noaliases;
-    List l;
+    Eprog prog;
 
     pushheap();
 
     noaliases = (shf->flags & PM_UNALIASED);
-    l = getfpfunc(shf->nam);
+    prog = getfpfunc(shf->nam);
     noaliases = noalias;
 
-    if (l == &dummy_list) {
+    if (prog == &dummy_eprog) {
 	zerr("%s: function definition file not found", shf->nam, 0);
 	return 1;
     }
     PERMALLOC {
-	shf->funcdef = dupstruct(stripkshdef(l, shf->nam));
+	shf->funcdef = dupeprog(stripkshdef(prog, shf->nam));
     } LASTALLOC;
     shf->flags &= ~PM_UNDEFINED;
 
@@ -2976,7 +3094,7 @@ loadautofn(Shfunc shf)
 
 /**/
 mod_export void
-doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
+doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
 /* If noreturnval is nonzero, then reset the current return *
  * value (lastval) to its value before the shell function   *
  * was executed.                                            */
@@ -3030,7 +3148,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 		argzero = ztrdup(argzero);
 	    }
 	}
-	runshfunc(list, wrappers, dupstring(name));
+	runshfunc(prog, wrappers, dupstring(name));
 	if (retflag) {
 	    retflag = 0;
 	    breaks = obreaks;
@@ -3073,7 +3191,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 
 /**/
 mod_export void
-runshfunc(List list, FuncWrap wrap, char *name)
+runshfunc(Eprog prog, FuncWrap wrap, char *name)
 {
     int cont;
     VARARR(char, ou, underscoreused);
@@ -3082,7 +3200,7 @@ runshfunc(List list, FuncWrap wrap, char *name)
 
     while (wrap) {
 	wrap->module->wrapper++;
-	cont = wrap->handler(list, wrap->next, name);
+	cont = wrap->handler(prog, wrap->next, name);
 	wrap->module->wrapper--;
 
 	if (!wrap->module->wrapper &&
@@ -3094,7 +3212,7 @@ runshfunc(List list, FuncWrap wrap, char *name)
 	wrap = wrap->next;
     }
     startparamscope();
-    execlist(list, 1, 0);
+    execode(prog, 1, 0);
     setunderscore(ou);
     endparamscope();
 }
@@ -3103,13 +3221,13 @@ runshfunc(List list, FuncWrap wrap, char *name)
  * list of its contents.                                                    */
 
 /**/
-static List
+static Eprog
 getfpfunc(char *s)
 {
     char **pp, buf[PATH_MAX];
     off_t len;
     char *d;
-    List r;
+    Eprog r;
     int fd;
 
     pp = fpath;
@@ -3140,12 +3258,11 @@ getfpfunc(char *s)
 		    close(fd);
 
 		zfree(d, len + 1);
-	    } else {
+	    } else
 		close(fd);
-	    }
 	}
     }
-    return &dummy_list;
+    return &dummy_eprog;
 }
 
 /* Handle the most common type of ksh-style autoloading, when doing a      *
@@ -3155,30 +3272,41 @@ getfpfunc(char *s)
  * contents of that definition.  Otherwise, use the entire file.           */
 
 /**/
-static List
-stripkshdef(List l, char *name)
+static Eprog
+stripkshdef(Eprog prog, char *name)
 {
-    Sublist s;
-    Pline p;
-    Cmd c;
-    if(!l)
+    Wordcode pc = prog->prog;
+    wordcode code;
+    Eprog ret;
+
+    if (!prog)
 	return NULL;
-    if(l->type != Z_SYNC || l->right)
-	return l;
-    s = l->left;
-    if(s->flags || s->right)
-	return l;
-    p = s->left;
-    if(p->right)
-	return l;
-    c = p->left;
-    if (c->type != FUNCDEF || c->flags ||
-	(c->redir && nonempty(c->redir)) || (c->vars && nonempty(c->vars)) ||
-	!c->args || empty(c->args) ||
-	lastnode(c->args) != firstnode(c->args) ||
-	strcmp(name, peekfirst(c->args)))
-	return l;
-    return c->u.list;
+    code = *pc++;
+    if (wc_code(code) != WC_LIST ||
+	(WC_LIST_TYPE(code) & (Z_SYNC|Z_END)) != (Z_SYNC|Z_END))
+	return prog;
+    code = *pc++;
+    if (wc_code(code) != WC_SUBLIST ||
+	WC_SUBLIST_FLAGS(code) || WC_SUBLIST_TYPE(code) != WC_SUBLIST_END)
+	return prog;
+    code = *pc++;
+    if (wc_code(code) != WC_PIPE || WC_PIPE_TYPE(code) != WC_PIPE_END)
+	return prog;
+    code = *pc++;
+    if (wc_code(code) != WC_FUNCDEF ||
+	*pc != 1 || strcmp(name, ecrawstr(prog, pc + 1)))
+	return prog;
+
+    ret = (Eprog) zhalloc(sizeof(*prog));
+    ret->len = (WC_FUNCDEF_SKIP(code) - 3) * sizeof(wordcode);
+    ret->prog = pc + 3;
+    ret->strs = (char *) (pc + pc[3]);
+    ret->shf = NULL;
+    ret->pats = prog->pats;
+    ret->npats = prog->npats;
+    ret->heap = 1;
+
+    return ret;
 }
 
 /* check to see if AUTOCD applies here */