about summary refs log tree commit diff
path: root/Src/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/parse.c')
-rw-r--r--Src/parse.c2753
1 files changed, 2207 insertions, 546 deletions
diff --git a/Src/parse.c b/Src/parse.c
index d42be2f2f..3ffed46d7 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -33,17 +33,17 @@
 /* != 0 if we are about to read a command word */
  
 /**/
-int incmdpos;
+mod_export int incmdpos;
  
 /* != 0 if we are in the middle of a [[ ... ]] */
  
 /**/
-int incond;
+mod_export int incond;
  
 /* != 0 if we are after a redirection (for ctxtlex only) */
  
 /**/
-int inredir;
+mod_export int inredir;
  
 /* != 0 if we are about to read a case pattern */
  
@@ -65,107 +65,477 @@ int infor;
 /**/
 struct heredocs *hdocs;
  
-/* used in arrays of lists instead of NULL pointers */
- 
+
+#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(ecused) \
+} while(0)
+
+
+/* 
+ * 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
+ *     - followed by number of names
+ *     - followed by names
+ *     - followed by offset to first string
+ *     - followed by length of string table
+ *     - 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.
+ */
+
+/**/
+int eclen, ecused, ecnpats;
+/**/
+Wordcode ecbuf;
+/**/
+Eccstr ecstrs;
 /**/
-struct list dummy_list;
+int ecsoffs, ecssub, ecnfunc;
 
-#define YYERROR  { tok = LEXERR; return NULL; }
-#define YYERRORV { tok = LEXERR; return; }
-#define COND_ERROR(X,Y) do{herrflush();zerr(X,Y,0);YYERROR}while(0)
+/* Adjust pointers in here-doc structs. */
 
-#define make_list()     allocnode(N_LIST)
-#define make_sublist()  allocnode(N_SUBLIST)
-#define make_pline()    allocnode(N_PLINE)
-#define make_cmd()      allocnode(N_CMD)
-#define make_forcmd()   allocnode(N_FOR)
-#define make_casecmd()  allocnode(N_CASE)
-#define make_ifcmd()    allocnode(N_IF)
-#define make_whilecmd() allocnode(N_WHILE)
-#define make_varnode()  allocnode(N_VARASG)
-#define make_cond()     allocnode(N_COND)
+static void
+ecadjusthere(int p, int d)
+{
+    struct heredocs *h;
+
+    for (h = hdocs; h; h = h->next)
+	if (h->pc >= p)
+	    h->pc += d;
+}
+
+/* Insert n free code-slots at position p. */
+
+static void
+ecispace(int p, int n)
+{
+    int m;
+
+    if ((eclen - ecused) < n) {
+	int a = (n > 256 ? n : 256);
+
+	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
+				    (eclen + a) * sizeof(wordcode));
+	eclen += a;
+    }
+    if ((m = ecused - p) > 0)
+	memmove(ecbuf + p + n, ecbuf + p, m * sizeof(wordcode));
+    ecused += n;
+    ecadjusthere(p, n);
+}
+
+/* Add one wordcode. */
+
+static int
+ecadd(wordcode c)
+{
+    if ((eclen - ecused) < 1) {
+	ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode),
+				    (eclen + 256) * sizeof(wordcode));
+	eclen += 256;
+    }
+    ecbuf[ecused] = c;
+    ecused++;
+
+    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--;
+    ecadjusthere(p, -1);
+}
+
+/* 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 (p->nfunc == ecnfunc && !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 - ecssub) << 2) | (t ? 1 : 0);
+	p->str = s;
+	p->nfunc = ecnfunc;
+	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 = 256) * sizeof(wordcode));
+    ecused = 0;
+    ecstrs = NULL;
+    ecsoffs = ecnpats = 0;
+    ecssub = 0;
+    ecnfunc = 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->flags = EF_HEAP;
+    ret->dump = NULL;
+    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 ]
  */
+
 /**/
-List
+Eprog
 parse_event(void)
 {
     tok = ENDINPUT;
     incmdpos = 1;
     yylex();
-    return par_event();
+    init_parse();
+    return ((par_event()) ? bld_eprog() : NULL);
 }
 
 /**/
-static List
+static int
 par_event(void)
 {
-    Sublist sl;
-    List l = NULL;
+    int r = 0, p, c = 0;
 
     while (tok == SEPER) {
 	if (isnewlin > 0)
-	    return NULL;
+	    return 0;
 	yylex();
     }
     if (tok == ENDINPUT)
-	return NULL;
-    if ((sl = par_sublist()))
+	return 0;
+
+    p = ecadd(0);
+
+    if (par_sublist(&c)) {
 	if (tok == ENDINPUT) {
-	    l = (List) make_list();
-	    l->type = Z_SYNC;
-	    l->left = sl;
+	    set_list_code(p, Z_SYNC, c);
+	    r = 1;
 	} else if (tok == SEPER) {
-	    l = (List) make_list();
-	    l->type = Z_SYNC;
-	    l->left = sl;
+	    set_list_code(p, Z_SYNC, c);
 	    if (isnewlin <= 0)
 		yylex();
+	    r = 1;
 	} else if (tok == AMPER) {
-	    l = (List) make_list();
-	    l->type = Z_ASYNC;
-	    l->left = sl;
+	    set_list_code(p, Z_ASYNC, c);
 	    yylex();
+	    r = 1;
 	} else if (tok == AMPERBANG) {
-	    l = (List) make_list();
-	    l->type = Z_ASYNC | Z_DISOWN;
-	    l->left = sl;
+	    set_list_code(p, (Z_ASYNC | Z_DISOWN), c);
 	    yylex();
-	} else
-	    l = NULL;
-    if (!l) {
+	    r = 1;
+	}
+    }
+    if (!r) {
 	if (errflag) {
-	    yyerror();
-	    return NULL;
+	    yyerror(0);
+	    ecused--;
+	    return 0;
 	}
+	yyerror(1);
 	herrflush();
-	yyerror();
-	return NULL;
+	if (noerrs != 2)
+	    errflag = 1;
+	ecused--;
+	return 0;
     } else {
-	l->right = par_event();
+	int oec = ecused;
+
+	par_event();
+	if (ecused == oec)
+	    ecbuf[p] |= wc_bdata(Z_END);
     }
-    return l;
+    return 1;
 }
 
 /**/
-List
+mod_export Eprog
 parse_list(void)
 {
-    List ret;
+    int c = 0;
 
     tok = ENDINPUT;
     incmdpos = 1;
     yylex();
-    ret = par_list();
-    if (tok == LEXERR) {
-	yyerror();
+    init_parse();
+    par_list(&c);
+#if 0 
+   if (tok == LEXERR)
+#endif
+   if (tok != ENDINPUT) {
+	yyerror(0);
 	return NULL;
     }
-    return ret;
+    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]);
+    }
 }
 
 /*
@@ -173,46 +543,60 @@ parse_list(void)
  */
 
 /**/
-static List
-par_list(void)
+static int
+par_list(int *complex)
 {
-    Sublist sl;
-    List l = NULL;
+    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 l;
+	return 0;
+    }
 }
 
 /**/
-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;
 }
 
 /*
@@ -220,24 +604,37 @@ par_list1(void)
  */
 
 /**/
-static Sublist
-par_sublist(void)
+static int
+par_sublist(int *complex)
 {
-    Sublist sl;
+    int f, p, c = 0;
 
-    if ((sl = par_sublist2()))
+    p = ecadd(0);
+
+    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;
+    }
 }
 
 /*
@@ -245,24 +642,24 @@ par_sublist(void)
  */
 
 /**/
-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;
 }
 
 /*
@@ -270,48 +667,53 @@ par_sublist2(void)
  */
 
 /**/
-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(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");
-	addlinknode(c->redir, rdr);
+	ecispace(r, 3);
+	ecbuf[r] = WCB_REDIR(MERGEOUT);
+	ecbuf[r + 1] = 2;
+	ecbuf[r + 2] = ecstrcode("1");
 
+	*complex = 1;
 	cmdpush(CS_ERRPIPE);
 	yylex();
-	p2 = par_pline();
+	while (tok == SEPER)
+	    yylex();
+	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;
     }
 }
 
@@ -321,96 +723,116 @@ par_pline(void)
  */
 
 /**/
-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 = newlinklist();
-    c->redir = newlinklist();
-    c->vars = newlinklist();
-    while (IS_REDIROP(tok))
-	par_redir(c->redir);
+    if (IS_REDIROP(tok)) {
+	*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;
-	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;
     }
-    while (IS_REDIROP(tok))
-	par_redir(c->redir);
+    if (IS_REDIROP(tok)) {
+	*complex = 1;
+	while (IS_REDIROP(tok))
+	    par_redir(&r);
+    }
     incmdpos = 1;
     incasepat = 0;
     incond = 0;
-    return c;
+    return 1;
 }
 
 /*
@@ -421,82 +843,95 @@ par_cmd(void)
 
 /**/
 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();
-	    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();
-	    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));
 }
 
 /*
@@ -508,35 +943,29 @@ par_for(Cmd c)
 
 /**/
 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;
 
@@ -545,14 +974,13 @@ par_case(Cmd c)
 	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) {
@@ -566,7 +994,7 @@ par_case(Cmd c)
 
 		incasepat = 1;
 		incmdpos = 0;
-		str2 = ncalloc(sl + 2);
+		str2 = hcalloc(sl + 2);
 		strcpy(str2, str);
 		str2[sl] = Bar;
 		str2[sl+1] = '\0';
@@ -574,12 +1002,12 @@ par_case(Cmd c)
 	    } 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)
@@ -590,54 +1018,53 @@ par_case(Cmd c)
 				    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 != Inpar || str[sl - 1] != Outpar,
+			  "BUG: strange case pattern");
+		    str[sl - 1] = '\0';
+		    chuck(str);
 		    break;
 		} else {
 		    char *str2;
 
 		    if (tok != STRING)
-			YYERRORV;
-		    str2 = ncalloc(sl + strlen(tokstr) + 1);
+			YYERRORV(oecused);
+		    str2 = hcalloc(sl + strlen(tokstr) + 1);
 		    strcpy(str2, str);
 		    strcpy(str2 + sl, tokstr);
 		    str = str2;
 		}
 	    }
 	}
-	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);
 }
 
 /*
@@ -649,20 +1076,13 @@ par_case(Cmd c)
 
 /**/
 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);
@@ -675,10 +1095,11 @@ par_if(Cmd c)
 	    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();
@@ -689,79 +1110,63 @@ par_if(Cmd c)
 	    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)) {
-		cmdpop();
-		YYERRORV;
-	    } else {
+	} else if (tok == INBRACE) {
+	    usebrace = 1;
+	    cmdpop();
+	    cmdpush(nc);
+	    yylex();
+	    par_save_list(complex);
+	    if (tok != OUTBRACE) {
 		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_save_list(complex);
 	    if (tok != OUTBRACE) {
 		cmdpop();
-		YYERRORV;
+		YYERRORV(oecused);
 	    }
 	} else {
-	    l = par_list();
+	    par_save_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);
 }
 
 /*
@@ -771,37 +1176,38 @@ par_if(Cmd c)
 
 /**/
 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);
 }
 
 /*
@@ -810,39 +1216,44 @@ par_while(Cmd c)
 
 /**/
 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;
-    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);
 }
 
 /*
@@ -851,13 +1262,18 @@ par_repeat(Cmd c)
 
 /**/
 static void
-par_subsh(Cmd c)
+par_subsh(int *complex)
 {
-    c->type = (tok == INPAR) ? SUBSH : CURSH;
+    int oecused = ecused, otok = tok, p;
+
+    p = ecadd(0);
     yylex();
-    c->u.list = par_list();
-    if (tok != ((c->type == SUBSH) ? OUTPAR : OUTBRACE))
-	YYERRORV;
+    par_list(complex);
+    ecadd(WCB_END());
+    if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE))
+	YYERRORV(oecused);
+    ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) :
+		WCB_CURSH(ecused - 1 - p));
     incmdpos = 1;
     yylex();
 }
@@ -869,37 +1285,74 @@ par_subsh(Cmd c)
 
 /**/
 static void
-par_funcdef(Cmd c)
+par_funcdef(void)
 {
+    int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0;
+    int so, oecssub = ecssub;
+
+    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);
+    ecadd(0);
+
     nocorrect = 0;
     if (tok == INOUTPAR)
 	yylex();
     while (tok == SEPER)
 	yylex();
+
+    ecnfunc++;
+    ecssub = so = ecsoffs;
+    onp = ecnpats;
+    ecnpats = 0;
+
     if (tok == INBRACE) {
 	yylex();
-	c->u.list = par_list();
-	if (tok != OUTBRACE)
-	    YYERRORV;
+	par_list(&c);
+	if (tok != OUTBRACE) {
+	    lineno += oldlineno;
+	    ecnpats = onp;
+	    ecssub = oecssub;
+	    YYERRORV(oecused);
+	}
 	yylex();
     } else if (unset(SHORTLOOPS)) {
-	YYERRORV;
+	lineno += oldlineno;
+	ecnpats = onp;
+	ecssub = oecssub;
+	YYERRORV(oecused);
     } else
-	c->u.list = par_list1();
+	par_list1(&c);
+
+    ecadd(WCB_END());
+    ecbuf[p + num + 2] = so - oecssub;
+    ecbuf[p + num + 3] = ecsoffs - so;
+    ecbuf[p + num + 4] = ecnpats;
+    ecbuf[p + 1] = num;
+
+    lineno += oldlineno;
+    ecnpats = onp;
+    ecssub = oecssub;
+    ecnfunc++;
+
+    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
 }
 
 /*
@@ -908,11 +1361,17 @@ par_funcdef(Cmd c)
 
 /**/
 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);
 }
 
 /*
@@ -921,15 +1380,16 @@ par_time(Cmd c)
 
 /**/
 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();
@@ -942,77 +1402,290 @@ par_dinbrack(Cmd c)
  */
 
 /**/
-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();
-
-	    v->type = PM_SCALAR;
-	    equalsplit(v->name = tokstr, &v->str);
-	    addlinknode(c->vars, v);
+	} else if (tok == ENVSTRING) {
+	    char *p, *name, *str;
+
+	    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';
+		str = p + 1;
+	    } else
+		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;
-	    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;
-	    addlinknode(c->args, tokstr);
+	    ecstr(tokstr);
+	    argc++;
 	    yylex();
 	} else if (IS_REDIROP(tok)) {
-	    par_redir(c->redir);
+	    *complex = c = 1;
+	    par_redir(&r);
+	    p += 3;		/* 3 codes per redirection */
+	    sr++;
 	} else if (tok == INOUTPAR) {
+	    int oldlineno = lineno, onp, so, oecssub = ecssub;
+
+	    *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);
+	    ecadd(0);
+
+	    ecnfunc++;
+	    ecssub = so = ecsoffs;
+	    onp = ecnpats;
+	    ecnpats = 0;
+
 	    if (tok == INBRACE) {
+		int c = 0;
+
 		yylex();
-		c->u.list = par_list();
+		par_list(&c);
 		if (tok != OUTBRACE) {
 		    cmdpop();
-		    YYERROR;
+		    lineno += oldlineno;
+		    ecnpats = onp;
+		    ecssub = oecssub;
+		    YYERROR(oecused);
 		}
 		yylex();
-	    } else
-		c->u.list = (List) expandstruct((struct node *) par_cmd(), N_LIST);
+	    } else {
+		int ll, sl, c = 0;
+
+		ll = ecadd(0);
+		sl = ecadd(0);
+
+		par_cmd(&c);
+
+		set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c);
+		set_list_code(ll, (Z_SYNC | Z_END), c);
+	    }
 	    cmdpop();
-	    c->type = FUNCDEF;
+
+	    ecadd(WCB_END());
+	    ecbuf[p + argc + 2] = so - oecssub;
+	    ecbuf[p + argc + 3] = ecsoffs - so;
+	    ecbuf[p + argc + 4] = ecnpats;
+
+	    lineno += oldlineno;
+	    ecnpats = onp;
+	    ecssub = oecssub;
+	    ecnfunc++;
+
+	    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
+
+	    isfunc = 1;
 	} else
 	    break;
 	isnull = 0;
     }
-    if (isnull && empty(c->redir))
-	return NULL;
+    if (isnull && !(sr + nr)) {
+	ecused = p;
+	return 0;
+    }
     incmdpos = 1;
-    return c;
+
+    if (!isfunc)
+	ecbuf[p] = WCB_SIMPLE(argc);
+
+    return sr + 1;
+}
+
+/*
+ * redir	: ( OUTANG | ... | TRINANG ) STRING
+ */
+
+static int redirtab[TRINANG - OUTANG + 1] = {
+    WRITE,
+    WRITENOW,
+    APP,
+    APPNOW,
+    READ,
+    READWRITE,
+    HEREDOC,
+    HEREDOCDASH,
+    MERGEIN,
+    MERGEOUT,
+    ERRWRITE,
+    ERRWRITENOW,
+    ERRAPP,
+    ERRAPPNOW,
+    HERESTR,
+};
+
+/**/
+static void
+par_redir(int *rp)
+{
+    int r = *rp, type, fd1, oldcmdpos, oldnc;
+    char *name;
+
+    oldcmdpos = incmdpos;
+    incmdpos = 0;
+    oldnc = nocorrect;
+    if (tok != INANG && tok != INOUTANG)
+	nocorrect = 1;
+    type = redirtab[tok - OUTANG];
+    fd1 = tokfd;
+    yylex();
+    if (tok != STRING && tok != ENVSTRING)
+	YYERRORV(ecused);
+    incmdpos = oldcmdpos;
+    nocorrect = oldnc;
+
+    /* assign default fd */
+    if (fd1 == -1)
+	fd1 = IS_READFD(type) ? 0 : 1;
+
+    name = tokstr;
+
+    switch (type) {
+    case HEREDOC:
+    case HEREDOCDASH: {
+	/* <<[-] name */
+	struct heredocs **hd;
+
+	/* 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;
+
+	for (hd = &hdocs; *hd; hd = &(*hd)->next);
+	*hd = zalloc(sizeof(struct heredocs));
+	(*hd)->next = NULL;
+	(*hd)->type = type;
+	(*hd)->pc = r;
+	(*hd)->str = tokstr;
+
+	yylex();
+	return;
+    }
+    case WRITE:
+    case WRITENOW:
+	if (tokstr[0] == Outang && tokstr[1] == Inpar)
+	    /* > >(...) */
+	    type = OUTPIPE;
+	else if (tokstr[0] == Inang && tokstr[1] == Inpar)
+	    YYERRORV(ecused);
+	break;
+    case READ:
+	if (tokstr[0] == Inang && tokstr[1] == Inpar)
+	    /* < <(...) */
+	    type = INPIPE;
+	else if (tokstr[0] == Outang && tokstr[1] == Inpar)
+	    YYERRORV(ecused);
+	break;
+    case READWRITE:
+	if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
+	    type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
+	break;
+    }
+    yylex();
+
+    /* 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(int pc, int type, char *str)
+{
+    ecbuf[pc] = WCB_REDIR(type);
+    ecbuf[pc + 2] = ecstrcode(str);
+}
+
+/*
+ * wordlist	: { STRING }
+ */
+
+/**/
+static int
+par_wordlist(void)
+{
+    int num = 0;
+    while (tok == STRING) {
+	ecstr(tokstr);
+	num++;
+	yylex();
+    }
+    return num;
+}
+
+/*
+ * nl_wordlist	: { STRING | SEPER }
+ */
+
+/**/
+static int
+par_nl_wordlist(void)
+{
+    int num = 0;
+
+    while (tok == STRING || tok == SEPER) {
+	if (tok != SEPER) {
+	    ecstr(tokstr);
+	    num++;
+	}
+	yylex();
+    }
+    return num;
 }
 
 /*
@@ -1028,25 +1701,24 @@ void (*condlex) _((void)) = yylex;
  */
 
 /**/
-Cond
+static int
 par_cond(void)
 {
-    Cond c, c2;
+    int p = ecused, r;
 
-    c = par_cond_1();
+    r = 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;
+	ecispace(p, 1);
+	par_cond();
+	ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p);
+	return 1;
     }
-    return c;
+    return r;
 }
 
 /*
@@ -1054,25 +1726,24 @@ par_cond(void)
  */
 
 /**/
-static Cond
+static int
 par_cond_1(void)
 {
-    Cond c, c2;
+    int r, p = ecused;
 
-    c = par_cond_2();
+    r = 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;
+	ecispace(p, 1);
+	par_cond_1();
+	ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p);
+	return 1;
     }
-    return c;
+    return r;
 }
 
 /*
@@ -1084,10 +1755,9 @@ par_cond_1(void)
  */
 
 /**/
-static Cond
+static int
 par_cond_2(void)
 {
-    Cond c, c2;
     char *s1, *s2, *s3;
     int dble = 0;
 
@@ -1121,31 +1791,31 @@ par_cond_2(void)
     }
     if (tok == BANG) {
 	condlex();
-	c = par_cond_2();
-	c2 = (Cond) make_cond();
-	c2->left = (void *) c;
-	c2->type = COND_NOT;
-	return c2;
+	ecadd(WCB_COND(COND_NOT, 0));
+	return par_cond_2();
     }
     if (tok == INPAR) {
+	int r;
+
 	condlex();
 	while (tok == SEPER)
 	    condlex();
-	c = par_cond();
+	r = par_cond();
 	while (tok == SEPER)
 	    condlex();
 	if (tok != OUTPAR)
-	    YYERROR;
+	    YYERROR(ecused);
 	condlex();
-	return c;
+	return r;
     }
-    if (tok != STRING)
+    if (tok != STRING) {
 	if (tok && tok != LEXERR && condlex == testlex) {
 	    s1 = tokstr;
 	    condlex();
 	    return par_cond_double("-n", s1);
 	} else
-	    YYERROR;
+	    YYERROR(ecused);
+    }
     s1 = tokstr;
     if (condlex == testlex)
 	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
@@ -1155,24 +1825,23 @@ par_cond_2(void)
 	int xtok = tok;
 	condlex();
 	if (tok != STRING)
-	    YYERROR;
+	    YYERROR(ecused);
 	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;
+	ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0));
+	ecstr(s1);
+	ecstr(s3);
+	return 1;
     }
-    if (tok != STRING)
+    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;
+	    YYERROR(ecused);
+    }
     s2 = tokstr;
     incond++;			/* parentheses do globbing */
     condlex();
@@ -1180,200 +1849,1192 @@ par_cond_2(void)
     if (tok == STRING && !dble) {
 	s3 = tokstr;
 	condlex();
-	return par_cond_triple(s1, s2, s3);
+	if (tok == STRING) {
+	    LinkList l = newlinklist();
+
+	    addlinknode(l, s2);
+	    addlinknode(l, s3);
+
+	    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);
 }
 
-/*
- * redir	: ( OUTANG | ... | TRINANG ) STRING
- */
+/**/
+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 int redirtab[TRINANG - OUTANG + 1] = {
-    WRITE,
-    WRITENOW,
-    APP,
-    APPNOW,
-    READ,
-    READWRITE,
-    HEREDOC,
-    HEREDOCDASH,
-    MERGEIN,
-    MERGEOUT,
-    ERRWRITE,
-    ERRWRITENOW,
-    ERRAPP,
-    ERRAPPNOW,
-    HERESTR,
-};
+/**/
+static int
+get_cond_num(char *tst)
+{
+    static char *condstrs[] =
+    {
+	"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
+    };
+    int t0;
+
+    for (t0 = 0; condstrs[t0]; t0++)
+	if (!strcmp(condstrs[t0], tst))
+	    return t0;
+    return -1;
+}
+
+/**/
+static int
+par_cond_triple(char *a, char *b, char *c)
+{
+    int t0;
+
+    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 {
+	    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 int
+par_cond_multi(char *a, LinkList l)
+{
+    if (a[0] != '-' || !a[1])
+	COND_ERROR("condition expected: %s", a);
+    else {
+	LinkNode n;
+
+	ecadd(WCB_COND(COND_MOD, countlinknodes(l)));
+	ecstr(a);
+	for (n = firstnode(l); n; incnode(n))
+	    ecstr((char *) getdata(n));
+    }
+    return 1;
+}
 
 /**/
 static void
-par_redir(LinkList l)
+yyerror(int noerr)
 {
-    struct redir *fn = (struct redir *)allocnode(N_REDIR);
-    int oldcmdpos, oldnc;
+    int t0;
+    char *t;
 
-    oldcmdpos = incmdpos;
-    incmdpos = 0;
-    oldnc = nocorrect;
-    if (tok != INANG && tok != INOUTANG)
-	nocorrect = 1;
-    fn->type = redirtab[tok - OUTANG];
-    fn->fd1 = tokfd;
-    yylex();
-    if (tok != STRING && tok != ENVSTRING)
-	YYERRORV;
-    incmdpos = oldcmdpos;
-    nocorrect = oldnc;
+    if ((t = dupstring(yytext)))
+	untokenize(t);
 
-    /* assign default fd */
-    if (fn->fd1 == -1)
-	fn->fd1 = IS_READFD(fn->type) ? 0 : 1;
+    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;
+}
 
-    fn->name = tokstr;
+/**/
+mod_export Eprog
+dupeprog(Eprog p, int heap)
+{
+    Eprog r;
+    int i;
+    Patprog *pp;
 
-    switch (fn->type) {
-    case HEREDOC:
-    case HEREDOCDASH: {
-	/* <<[-] name */
-	struct heredocs **hd;
+    if (p == &dummy_eprog)
+	return p;
 
-	for (hd = &hdocs; *hd; hd = &(*hd)->next);
-	*hd = zalloc(sizeof(struct heredocs));
-	(*hd)->next = NULL;
-	(*hd)->rd = fn;
-	break;
+    r = (heap ? (Eprog) zhalloc(sizeof(*r)) : (Eprog) zalloc(sizeof(*r)));
+    r->flags = (heap ? EF_HEAP : EF_REAL) | (p->flags & EF_RUN);
+    r->dump = NULL;
+    r->len = p->len;
+    r->npats = p->npats;
+    pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) :
+		    (Patprog *) zcalloc(r->len));
+    r->prog = (Wordcode) (r->pats + r->npats);
+    r->strs = ((char *) r->prog) + (p->strs - ((char *) p->prog));
+    memcpy(r->prog, p->prog, r->len - (p->npats * sizeof(Patprog)));
+    r->shf = NULL;
+
+    for (i = r->npats; i--; pp++)
+	*pp = dummy_patprog1;
+
+    return r;
+}
+
+static LinkList eprog_free;
+
+/**/
+mod_export void
+freeeprog(Eprog p)
+{
+    if (p && p != &dummy_eprog)
+	zaddlinknode(eprog_free, p);
+}
+
+/**/
+void
+freeeprogs(void)
+{
+    Eprog p;
+    int i;
+    Patprog *pp;
+
+    while ((p = (Eprog) getlinknode(eprog_free))) {
+	for (i = p->npats, pp = p->pats; i--; pp++)
+	    freepatprog(*pp);
+	if (p->dump) {
+	    decrdumpcount(p->dump);
+	    zfree(p->pats, p->npats * sizeof(Patprog));
+	} else
+	    zfree(p->pats, p->len);
+	zfree(p, sizeof(*p));
     }
-    case WRITE:
-    case WRITENOW:
-	if (tokstr[0] == Outang && tokstr[1] == Inpar)
-	    /* > >(...) */
-	    fn->type = OUTPIPE;
-	else if (tokstr[0] == Inang && tokstr[1] == Inpar)
-	    YYERRORV;
-	break;
-    case READ:
-	if (tokstr[0] == Inang && tokstr[1] == Inpar)
-	    /* < <(...) */
-	    fn->type = INPIPE;
-	else if (tokstr[0] == Outang && tokstr[1] == Inpar)
-	    YYERRORV;
-	break;
-    case READWRITE:
-	if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar)
-	    fn->type = tokstr[0] == Inang ? INPIPE : OUTPIPE;
-	break;
+}
+
+/**/
+char *
+ecgetstr(Estate s, int dup, int *tok)
+{
+    static char buf[4];
+    wordcode c = *s->pc++;
+    char *r;
+
+    if (c == 6 || c == 7)
+	r = "";
+    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 = EC_NODUP;
+    } else {
+	r = s->strs + (c >> 2);
     }
-    yylex();
-    addlinknode(l, fn);
+    if (tok)
+	*tok = (c & 1);
+
+    /*** Since function dump files are mapped read-only, avoiding to
+     *   to duplicate strings when they don't contain tokens may fail
+     *   when one of the many utility functions happens to write to
+     *   one of the strings (without really modifying it).
+     *   If that happens to you and you don't feel like debugging it,
+     *   just change the line below to:
+     *
+     *     return (dup ? dupstring(r) : r);
+     */
+
+    return ((dup == EC_DUP || (dup && (c & 1)))  ? dupstring(r) : r);
 }
 
-/*
- * wordlist	: { STRING }
- */
+/**/
+char *
+ecrawstr(Eprog p, Wordcode pc, int *tok)
+{
+    static char buf[4];
+    wordcode c = *pc;
+
+    if (c == 6 || c == 7) {
+	if (tok)
+	    *tok = (c & 1);
+	return "";
+    } 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 {
+	if (tok)
+	    *tok = (c & 1);
+	return p->strs + (c >> 2);
+    }
+}
 
 /**/
-static LinkList
-par_wordlist(void)
+char **
+ecgetarr(Estate s, int num, int dup, int *tok)
 {
-    LinkList l;
+    char **ret, **rp;
+    int tf = 0, tmp = 0;
 
-    l = newlinklist();
-    while (tok == STRING) {
-	addlinknode(l, tokstr);
-	yylex();
+    ret = rp = (char **) zhalloc((num + 1) * sizeof(char *));
+
+    while (num--) {
+	*rp++ = ecgetstr(s, dup, &tmp);
+	tf |=  tmp;
     }
-    return l;
+    *rp = NULL;
+    if (tok)
+	*tok = tf;
+
+    return ret;
 }
 
-/*
- * nl_wordlist	: { STRING | SEPER }
+/**/
+LinkList
+ecgetlist(Estate s, int num, int dup, int *tok)
+{
+    if (num) {
+	LinkList ret;
+	int i, tf = 0, tmp = 0;
+
+	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;
+}
+
+/**/
+LinkList
+ecgetredirs(Estate s)
+{
+    LinkList ret = newlinklist();
+    wordcode code = *s->pc++;
+
+    while (wc_code(code) == WC_REDIR) {
+	Redir r = (Redir) zhalloc(sizeof(*r));
+
+	r->type = WC_REDIR_TYPE(code);
+	r->fd1 = *s->pc++;
+	r->name = ecgetstr(s, EC_DUP, NULL);
+
+	addlinknode(ret, r);
+
+	code = *s->pc++;
+    }
+    s->pc--;
+
+    return ret;
+}
+
+/**/
+mod_export struct eprog dummy_eprog;
+
+static wordcode dummy_eprog_code;
+
+/**/
+void
+init_eprog(void)
+{
+    dummy_eprog_code = WCB_END();
+    dummy_eprog.len = sizeof(wordcode);
+    dummy_eprog.prog = &dummy_eprog_code;
+    dummy_eprog.strs = NULL;
+
+    eprog_free = znewlinklist();
+}
+
+/* Code for function dump files.
+ *
+ * Dump files consist of a header and the function bodies (the wordcode
+ * plus the string table) and that twice: once for the byte-order of the
+ * host the file was created on and once for the other byte-order. The
+ * header describes where the beginning of the `other' version is and it
+ * is up to the shell reading the file to decide which version it needs.
+ * This is done by checking if the first word is FD_MAGIC (then the 
+ * shell reading the file has the same byte order as the one that created
+ * the file) or if it is FD_OMAGIC, then the `other' version has to be
+ * read.
+ * The header is the magic number, a word containing the flags (if the
+ * file should be mapped or read and if this header is the `other' one),
+ * the version string in a field of 40 characters and the descriptions
+ * for the functions in the dump file.
+ * Each description consists of a struct fdhead followed by the name,
+ * aligned to sizeof(wordcode) (i.e. 4 bytes).
  */
 
+#include "version.h"
+
+#define FD_EXT ".zwc"
+#define FD_MINMAP 4096
+
+#define FD_PRELEN 12
+#define FD_MAGIC  0x02030405
+#define FD_OMAGIC 0x05040302
+
+#define FDF_MAP   1
+#define FDF_OTHER 2
+
+typedef struct fdhead *FDHead;
+
+struct fdhead {
+    wordcode start;		/* offset to function definition */
+    wordcode len;		/* length of wordcode/strings */
+    wordcode npats;		/* number of patterns needed */
+    wordcode strs;		/* offset to strings */
+    wordcode hlen;		/* header length (incl. name) */
+    wordcode flags;		/* flags and offset to name tail */
+};
+
+#define fdheaderlen(f) (((Wordcode) (f))[FD_PRELEN])
+
+#define fdmagic(f)       (((Wordcode) (f))[0])
+#define fdsetbyte(f,i,v) \
+    ((((unsigned char *) (((Wordcode) (f)) + 1))[i]) = ((unsigned char) (v)))
+#define fdbyte(f,i)      ((wordcode) (((unsigned char *) (((Wordcode) (f)) + 1))[i]))
+#define fdflags(f)       fdbyte(f, 0)
+#define fdsetflags(f,v)  fdsetbyte(f, 0, v)
+#define fdother(f)       (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16))
+#define fdsetother(f, o) \
+    do { \
+        fdsetbyte(f, 1, ((o) & 0xff)); \
+        fdsetbyte(f, 2, (((o) >> 8) & 0xff)); \
+        fdsetbyte(f, 3, (((o) >> 16) & 0xff)); \
+    } while (0)
+#define fdversion(f)     ((char *) ((f) + 2))
+
+#define firstfdhead(f) ((FDHead) (((Wordcode) (f)) + FD_PRELEN))
+#define nextfdhead(f)  ((FDHead) (((Wordcode) (f)) + (f)->hlen))
+
+#define fdhflags(f)      (((FDHead) (f))->flags)
+#define fdhtail(f)       (((FDHead) (f))->flags >> 2)
+#define fdhbldflags(f,t) ((f) | ((t) << 2))
+
+#define FDHF_KSHLOAD 1
+#define FDHF_ZSHLOAD 2
+
+#define fdname(f)      ((char *) (((FDHead) (f)) + 1))
+
+/* This is used when building wordcode files. */
+
+typedef struct wcfunc *WCFunc;
+
+struct wcfunc {
+    char *name;
+    Eprog prog;
+    int flags;
+};
+
+/* Try to find the description for the given function name. */
+
+static FDHead
+dump_find_func(Wordcode h, char *name)
+{
+    FDHead n, e = (FDHead) (h + fdheaderlen(h));
+
+    for (n = firstfdhead(h); n < e; n = nextfdhead(n))
+	if (!strcmp(name, fdname(n) + fdhtail(n)))
+	    return n;
+
+    return NULL;
+}
+
 /**/
-static LinkList
-par_nl_wordlist(void)
+int
+bin_zcompile(char *nam, char **args, char *ops, int func)
 {
-    LinkList l;
+    int map, flags;
+    char *dump;
+
+    if ((ops['k'] && ops['z']) || (ops['R'] && ops['M']) ||
+	(ops['c'] && (ops['U'] || ops['k'] || ops['z'])) ||
+	(!(ops['c'] || ops['a']) && ops['m'])) {
+	zwarnnam(nam, "illegal combination of options", NULL, 0);
+	return 1;
+    }
+    if ((ops['c'] || ops['a']) && isset(KSHAUTOLOAD))
+	zwarnnam(nam, "functions will use zsh style autoloading", NULL, 0);
 
-    l = newlinklist();
-    while (tok == STRING || tok == SEPER) {
-	if (tok != SEPER)
-	    addlinknode(l, tokstr);
-	yylex();
+    flags = (ops['k'] ? FDHF_KSHLOAD :
+	     (ops['z'] ? FDHF_ZSHLOAD : 0));
+
+    if (ops['t']) {
+	Wordcode f;
+
+	if (!*args) {
+	    zwarnnam(nam, "too few arguments", NULL, 0);
+	    return 1;
+	}
+	if (!(f = load_dump_header(*args)) &&
+	    !(f = load_dump_header(dyncat(*args, FD_EXT)))) {
+	    zwarnnam(nam, "invalid dump file: %s", *args, 0);
+	    return 1;
+	}
+	if (args[1]) {
+	    for (args++; *args; args++)
+		if (!dump_find_func(f, *args))
+		    return 1;
+	    return 0;
+	} else {
+	    FDHead h, e = (FDHead) (f + fdheaderlen(f));
+
+	    printf("function dump file (%s) for zsh-%s\n",
+		   ((fdflags(f) & FDF_MAP) ? "mapped" : "read"), fdversion(f));
+	    for (h = firstfdhead(f); h < e; h = nextfdhead(h))
+		printf("%s\n", fdname(h));
+	    return 0;
+	}
+    }
+    if (!*args) {
+	zwarnnam(nam, "too few arguments", NULL, 0);
+	return 1;
     }
-    return l;
+    map = (ops['M'] ? 2 : (ops['R'] ? 0 : 1));
+
+    if (!args[1] && !(ops['c'] || ops['a']))
+	return build_dump(nam, dyncat(*args, FD_EXT), args, ops['U'], map, flags);
+
+    dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT));
+
+    return ((ops['c'] || ops['a']) ?
+	    build_cur_dump(nam, dump, args + 1, ops['m'], map,
+			   (ops['c'] ? 1 : 0) | (ops['a'] ? 2 : 0)) :
+	    build_dump(nam, dump, args + 1, ops['U'], map, flags));
 }
 
+/* Load the header of a dump file. Returns NULL if the file isn't a
+ * valid dump file. */
+
 /**/
-static Cond
-par_cond_double(char *a, char *b)
+static Wordcode
+load_dump_header(char *name)
 {
-    Cond n = (Cond) make_cond();
+    int fd;
+    wordcode buf[FD_PRELEN + 1];
 
-    if (a[0] != '-' || !a[1] || a[2])
-	COND_ERROR("parse error: condition expected: %s", a);
-    n->left = (void *) b;
-    n->type = a[1];
-    n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
-    return n;
+    if ((fd = open(name, O_RDONLY)) < 0)
+	return NULL;
+
+    if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
+	((FD_PRELEN + 1) * sizeof(wordcode)) ||
+	(fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC) ||
+	strcmp(ZSH_VERSION, fdversion(buf))) {
+	close(fd);
+	return NULL;
+    } else {
+	int len;
+	Wordcode head;
+
+	if (fdmagic(buf) == FD_MAGIC) {
+	    len = fdheaderlen(buf) * sizeof(wordcode);
+	    head = (Wordcode) zhalloc(len);
+	}
+	else {
+	    int o = fdother(buf);
+
+	    if (lseek(fd, o, 0) == -1 ||
+		read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) !=
+		((FD_PRELEN + 1) * sizeof(wordcode))) {
+		close(fd);
+		return NULL;
+	    }
+	    len = fdheaderlen(buf) * sizeof(wordcode);
+	    head = (Wordcode) zhalloc(len);
+	}
+	memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode));
+
+	if (read(fd, head + (FD_PRELEN + 1),
+		 len - ((FD_PRELEN + 1) * sizeof(wordcode))) !=
+	    len - ((FD_PRELEN + 1) * sizeof(wordcode))) {
+	    close(fd);
+	    return NULL;
+	}
+	close(fd);
+	return head;
+    }
+}
+
+/* Swap the bytes in a wordcode. */
+
+static void
+fdswap(Wordcode p, int n)
+{
+    wordcode c;
+
+    for (; n--; p++) {
+	c = *p;
+	*p = (((c & 0xff) << 24) |
+	      ((c & 0xff00) << 8) |
+	      ((c & 0xff0000) >> 8) |
+	      ((c & 0xff000000) >> 24));
+    }
+}
+
+/* Write a dump file. */
+
+static void
+write_dump(int dfd, LinkList progs, int map, int hlen, int tlen)
+{
+    LinkNode node;
+    WCFunc wcf;
+    int other = 0, ohlen, tmp;
+    wordcode pre[FD_PRELEN];
+    char *tail, *n;
+    struct fdhead head;
+    Eprog prog;
+
+    if (map == 1)
+	map = (tlen >= FD_MINMAP);
+
+    for (ohlen = hlen; ; hlen = ohlen) {
+	fdmagic(pre) = (other ? FD_OMAGIC : FD_MAGIC);
+	fdsetflags(pre, ((map ? FDF_MAP : 0) | other));
+	fdsetother(pre, tlen);
+	strcpy(fdversion(pre), ZSH_VERSION);
+	write(dfd, pre, FD_PRELEN * sizeof(wordcode));
+
+	for (node = firstnode(progs); node; incnode(node)) {
+	    wcf = (WCFunc) getdata(node);
+	    n = wcf->name;
+	    prog = wcf->prog;
+	    head.start = hlen;
+	    hlen += (prog->len - (prog->npats * sizeof(Patprog)) +
+		     sizeof(wordcode) - 1) / sizeof(wordcode);
+	    head.len = prog->len - (prog->npats * sizeof(Patprog));
+	    head.npats = prog->npats;
+	    head.strs = prog->strs - ((char *) prog->prog);
+	    head.hlen = (sizeof(struct fdhead) / sizeof(wordcode)) +
+		(strlen(n) + sizeof(wordcode)) / sizeof(wordcode);
+	    if ((tail = strrchr(n, '/')))
+		tail++;
+	    else
+		tail = n;
+	    head.flags = fdhbldflags(wcf->flags, (tail - n));
+	    if (other)
+		fdswap((Wordcode) &head, sizeof(head) / sizeof(wordcode));
+	    write(dfd, &head, sizeof(head));
+	    tmp = strlen(n) + 1;
+	    write(dfd, n, tmp);
+	    if ((tmp &= (sizeof(wordcode) - 1)))
+		write(dfd, &head, sizeof(wordcode) - tmp);
+	}
+	for (node = firstnode(progs); node; incnode(node)) {
+	    prog = ((WCFunc) getdata(node))->prog;
+	    tmp = (prog->len - (prog->npats * sizeof(Patprog)) +
+		   sizeof(wordcode) - 1) / sizeof(wordcode);
+	    if (other)
+		fdswap(prog->prog, (((Wordcode) prog->strs) - prog->prog));
+	    write(dfd, prog->prog, tmp * sizeof(wordcode));
+	}
+	if (other)
+	    break;
+	other = FDF_OTHER;
+    }
 }
 
 /**/
 static int
-get_cond_num(char *tst)
+build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
 {
-    static char *condstrs[] =
-    {
-	"nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL
-    };
-    int t0;
+    int dfd, fd, hlen, tlen, flen, ona = noaliases;
+    LinkList progs;
+    char *file;
+    Eprog prog;
+    WCFunc wcf;
+
+    if (!strsfx(FD_EXT, dump))
+	dump = dyncat(dump, FD_EXT);
+
+    if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) {
+	zwarnnam(nam, "can't write dump file: %s", dump, 0);
+	return 1;
+    }
+    progs = newlinklist();
+    noaliases = ali;
+
+    for (hlen = FD_PRELEN, tlen = 0; *files; files++) {
+	if (!strcmp(*files, "-k")) {
+	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD;
+	    continue;
+	} else if (!strcmp(*files, "-z")) {
+	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD;
+	    continue;
+	}
+	if ((fd = open(*files, O_RDONLY)) < 0 ||
+	    (flen = lseek(fd, 0, 2)) == -1) {
+	    if (fd >= 0)
+		close(fd);
+	    close(dfd);
+	    zwarnnam(nam, "can't open file: %s", *files, 0);
+	    noaliases = ona;
+	    unlink(dump);
+	    return 1;
+	}
+	file = (char *) zalloc(flen + 1);
+	file[flen] = '\0';
+	lseek(fd, 0, 0);
+	if (read(fd, file, flen) != flen) {
+	    close(fd);
+	    close(dfd);
+	    zfree(file, flen);
+	    zwarnnam(nam, "can't read file: %s", *files, 0);
+	    noaliases = ona;
+	    unlink(dump);
+	    return 1;
+	}
+	close(fd);
+	file = metafy(file, flen, META_REALLOC);
+
+	if (!(prog = parse_string(file, 1)) || errflag) {
+	    errflag = 0;
+	    close(dfd);
+	    zfree(file, flen);
+	    zwarnnam(nam, "can't read file: %s", *files, 0);
+	    noaliases = ona;
+	    unlink(dump);
+	    return 1;
+	}
+	zfree(file, flen);
 
-    for (t0 = 0; condstrs[t0]; t0++)
-	if (!strcmp(condstrs[t0], tst))
-	    return t0;
-    return -1;
+	wcf = (WCFunc) zhalloc(sizeof(*wcf));
+	wcf->name = *files;
+	wcf->prog = prog;
+	wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : flags);
+	addlinknode(progs, wcf);
+
+	flen = (strlen(*files) + sizeof(wordcode)) / sizeof(wordcode);
+	hlen += (sizeof(struct fdhead) / sizeof(wordcode)) + flen;
+
+	tlen += (prog->len - (prog->npats * sizeof(Patprog)) +
+		 sizeof(wordcode) - 1) / sizeof(wordcode);
+    }
+    noaliases = ona;
+
+    tlen = (tlen + hlen) * sizeof(wordcode);
+
+    write_dump(dfd, progs, map, hlen, tlen);
+
+    close(dfd);
+
+    return 0;
+}
+
+static int
+cur_add_func(char *nam, Shfunc shf, LinkList names, LinkList progs,
+	     int *hlen, int *tlen, int what)
+{
+    Eprog prog;
+    WCFunc wcf;
+
+    if (shf->flags & PM_UNDEFINED) {
+	int ona = noaliases;
+
+	if (!(what & 2)) {
+	    zwarnnam(nam, "function is not loaded: %s", shf->nam, 0);
+	    return 1;
+	}
+	noaliases = (shf->flags & PM_UNALIASED);
+	if (!(prog = getfpfunc(shf->nam, NULL)) || prog == &dummy_eprog) {
+	    noaliases = ona;
+	    zwarnnam(nam, "can't load function: %s", shf->nam, 0);
+	    return 1;
+	}
+	if (prog->dump)
+	    prog = dupeprog(prog, 1);
+	noaliases = ona;
+    } else {
+	if (!(what & 1)) {
+	    zwarnnam(nam, "function is already loaded: %s", shf->nam, 0);
+	    return 1;
+	}
+	prog = dupeprog(shf->funcdef, 1);
+    }
+    wcf = (WCFunc) zhalloc(sizeof(*wcf));
+    wcf->name = shf->nam;
+    wcf->prog = prog;
+    wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : FDHF_ZSHLOAD);
+    addlinknode(progs, wcf);
+    addlinknode(names, shf->nam);
+
+    *hlen += ((sizeof(struct fdhead) / sizeof(wordcode)) +
+	      ((strlen(shf->nam) + sizeof(wordcode)) / sizeof(wordcode)));
+    *tlen += (prog->len - (prog->npats * sizeof(Patprog)) +
+	      sizeof(wordcode) - 1) / sizeof(wordcode);
+
+    return 0;
 }
 
 /**/
-static Cond
-par_cond_triple(char *a, char *b, char *c)
+static int
+build_cur_dump(char *nam, char *dump, char **names, int match, int map,
+	       int what)
 {
-    Cond n = (Cond) make_cond();
-    int t0;
+    int dfd, hlen, tlen;
+    LinkList progs, lnames;
+    Shfunc shf = NULL;
 
-    if ((b[0] == Equals || b[0] == '=') &&
-	(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2])))
-	n->type = COND_STREQ;
-    else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2])
-	n->type = COND_STRNEQ;
-    else if (b[0] == '-') {
-	if ((t0 = get_cond_num(b + 1)) > -1)
-	    n->type = t0 + COND_NT;
-	else
-	    COND_ERROR("unrecognized condition: %s", b);
+    if (!strsfx(FD_EXT, dump))
+	dump = dyncat(dump, FD_EXT);
+
+    if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) {
+	zwarnnam(nam, "can't write dump file: %s", dump, 0);
+	return 1;
+    }
+    progs = newlinklist();
+    lnames = newlinklist();
+
+    hlen = FD_PRELEN;
+    tlen = 0;
+
+    if (!*names) {
+	int i;
+	HashNode hn;
+
+	for (i = 0; i < shfunctab->hsize; i++)
+	    for (hn = shfunctab->nodes[i]; hn; hn = hn->next)
+		if (cur_add_func(nam, (Shfunc) hn, lnames, progs,
+				 &hlen, &tlen, what)) {
+		    errflag = 0;
+		    close(dfd);
+		    unlink(dump);
+		    return 1;
+		}
+    } else if (match) {
+	char *pat;
+	Patprog pprog;
+	int i;
+	HashNode hn;
+
+	for (; *names; names++) {
+	    tokenize(pat = dupstring(*names));
+	    if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) {
+		zwarnnam(nam, "bad pattern: %s", *names, 0);
+		close(dfd);
+		unlink(dump);
+		return 1;
+	    }
+	    for (i = 0; i < shfunctab->hsize; i++)
+		for (hn = shfunctab->nodes[i]; hn; hn = hn->next)
+		    if (!listcontains(lnames, hn->nam) &&
+			pattry(pprog, hn->nam) &&
+			cur_add_func(nam, (Shfunc) hn, lnames, progs,
+				     &hlen, &tlen, what)) {
+			errflag = 0;
+			close(dfd);
+			unlink(dump);
+			return 1;
+		    }
+	}
+    } else {
+	for (; *names; names++) {
+	    if (errflag ||
+		!(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) {
+		zwarnnam(nam, "unknown function: %s", *names, 0);
+		errflag = 0;
+		close(dfd);
+		unlink(dump);
+		return 1;
+	    }
+	    if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) {
+		errflag = 0;
+		close(dfd);
+		unlink(dump);
+		return 1;
+	    }
+	}
+    }
+    if (empty(progs)) {
+	zwarnnam(nam, "no functions", NULL, 0);
+	errflag = 0;
+	close(dfd);
+	unlink(dump);
+	return 1;
+    }
+    tlen = (tlen + hlen) * sizeof(wordcode);
+
+    write_dump(dfd, progs, map, hlen, tlen);
+
+    close(dfd);
+
+    return 0;
+}
+
+#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP)
+
+#include <sys/mman.h>
+
+#if defined(MAP_SHARED) && defined(PROT_READ)
+
+#define USE_MMAP 1
+
+#endif
+#endif
+
+#ifdef USE_MMAP
+
+/* List of dump files mapped. */
+
+static FuncDump dumps;
+
+/* Load a dump file (i.e. map it). */
+
+static void
+load_dump_file(char *dump, int other, int len)
+{
+    FuncDump d;
+    Wordcode addr;
+    int fd, off;
+
+    if (other) {
+	static size_t pgsz = 0;
+
+	if (!pgsz) {
+
+#ifdef _SC_PAGESIZE
+	    pgsz = sysconf(_SC_PAGESIZE);     /* SVR4 */
+#else
+# ifdef _SC_PAGE_SIZE
+	    pgsz = sysconf(_SC_PAGE_SIZE);    /* HPUX */
+# else
+	    pgsz = getpagesize();
+# endif
+#endif
+
+	    pgsz--;
+	}
+	off = len & ~pgsz;
     } else
-	COND_ERROR("condition expected: %s", b);
-    n->left = (void *) a;
-    n->right = (void *) c;
-    n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
-    return n;
+	off = 0;
+
+    if ((fd = open(dump, O_RDONLY)) < 0)
+	return;
+
+    fd = movefd(fd);
+
+    if ((addr = (Wordcode) mmap(NULL, len, PROT_READ, MAP_SHARED, fd, off)) ==
+	((Wordcode) -1)) {
+	close(fd);
+	return;
+    }
+    d = (FuncDump) zalloc(sizeof(*d));
+    d->next = dumps;
+    dumps = d;
+    d->name = ztrdup(dump);
+    d->fd = fd;
+    d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0);
+    d->addr = addr;
+    d->len = len;
+    d->count = 0;
 }
 
+#endif
+
+/* Try to load a function from one of the possible wordcode files for it.
+ * The first argument is a element of $fpath, the second one is the name
+ * of the function searched and the last one is the possible name for the
+ * uncompiled function file (<path>/<func>). */
+
 /**/
-static void
-yyerror(void)
+Eprog
+try_dump_file(char *path, char *name, char *file, int *ksh)
 {
-    int t0;
+    Eprog prog;
+    struct stat std, stc, stn;
+    int rd, rc, rn;
+    char *dig, *wc;
+
+    if (strsfx(FD_EXT, path))
+	return check_dump_file(path, name, ksh);
+
+    dig = dyncat(path, FD_EXT);
+    wc = dyncat(file, FD_EXT);
+
+    rd = stat(dig, &std);
+    rc = stat(wc, &stc);
+    rn = stat(file, &stn);
+
+    /* See if there is a digest file for the directory, it is younger than
+     * both the uncompiled function file and its compiled version (or they
+     * don't exist) and the digest file contains the definition for the
+     * function. */
+    if (!rd &&
+	(rc || std.st_mtime > stc.st_mtime) &&
+	(rn || std.st_mtime > stn.st_mtime) &&
+	(prog = check_dump_file(dig, name, ksh)))
+	return prog;
+
+    /* No digest file. Now look for the per-function compiled file. */
+    if (!rc &&
+	(rn || stc.st_mtime > stn.st_mtime) &&
+	(prog = check_dump_file(wc, name, ksh)))
+	return prog;
+
+    /* No compiled file for the function. The caller (getfpfunc() will
+     * check if the directory contains the uncompiled file for it. */
+    return NULL;
+}
 
-    for (t0 = 0; t0 != 20; t0++)
-	if (!yytext || !yytext[t0] || yytext[t0] == '\n')
-	    break;
-    if (t0 == 20)
-	zerr("parse error near `%l...'", yytext, 20);
-    else if (t0)
-	zerr("parse error near `%l'", yytext, t0);
+/* Almost the same, but for sourced files. */
+
+/**/
+Eprog
+try_source_file(char *file)
+{
+    Eprog prog;
+    struct stat stc, stn;
+    int rc, rn;
+    char *wc, *tail;
+
+    if ((tail = strrchr(file, '/')))
+	tail++;
     else
-	zerr("parse error", NULL, 0);
+	tail = file;
+
+    if (strsfx(FD_EXT, file))
+	return check_dump_file(file, tail, NULL);
+
+    wc = dyncat(file, FD_EXT);
+
+    rc = stat(wc, &stc);
+    rn = stat(file, &stn);
+
+    if (!rc && (rn || stc.st_mtime > stn.st_mtime) &&
+	(prog = check_dump_file(wc, tail, NULL)))
+	return prog;
+
+    return NULL;
+}
+
+/* See if `file' names a wordcode dump file and that contains the
+ * definition for the function `name'. If so, return an eprog for it. */
+
+/**/
+static Eprog
+check_dump_file(char *file, char *name, int *ksh)
+{
+    int isrec = 0;
+    Wordcode d;
+    FDHead h;
+    FuncDump f;
+
+#ifdef USE_MMAP
+
+ rec:
+
+#endif
+
+    d = NULL;
+
+#ifdef USE_MMAP
+
+    for (f = dumps; f; f = f->next)
+	if (!strcmp(file, f->name)) {
+	    d = f->map;
+	    break;
+	}
+
+#else
+
+    f = NULL;
+
+#endif
+
+    if (!f && (isrec || !(d = load_dump_header(file))))
+	return NULL;
+
+    if ((h = dump_find_func(d, name))) {
+	/* Found the name. If the file is already mapped, return the eprog,
+	 * otherwise map it and just go up. */
+
+#ifdef USE_MMAP
+
+	if (f) {
+	    Eprog prog = (Eprog) zalloc(sizeof(*prog));
+	    Patprog *pp;
+	    int np;
+
+	    prog->flags = EF_MAP;
+	    prog->len = h->len;
+	    prog->npats = np = h->npats;
+	    prog->pats = pp = (Patprog *) zalloc(np * sizeof(Patprog));
+	    prog->prog = f->map + h->start;
+	    prog->strs = ((char *) prog->prog) + h->strs;
+	    prog->shf = NULL;
+	    prog->dump = f;
+
+	    incrdumpcount(f);
+
+	    while (np--)
+		*pp++ = dummy_patprog1;
+
+	    if (ksh)
+		*ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 :
+			((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1));
+
+	    return prog;
+	} else if (fdflags(d) & FDF_MAP) {
+	    load_dump_file(file, (fdflags(d) & FDF_OTHER), fdother(d));
+	    isrec = 1;
+	    goto rec;
+	} else
+
+#endif
+
+	    {
+	    Eprog prog;
+	    Patprog *pp;
+	    int np, fd, po = h->npats * sizeof(Patprog);
+
+	    if ((fd = open(file, O_RDONLY)) < 0 ||
+		lseek(fd, ((h->start * sizeof(wordcode)) +
+			   ((fdflags(d) & FDF_OTHER) ? fdother(d) : 0)), 0) < 0) {
+		if (fd >= 0)
+		    close(fd);
+		return NULL;
+	    }
+	    d = (Wordcode) zalloc(h->len + po);
+
+	    if (read(fd, ((char *) d) + po, h->len) != h->len) {
+		close(fd);
+		zfree(d, h->len);
+
+		return NULL;
+	    }
+	    close(fd);
+
+	    prog = (Eprog) zalloc(sizeof(*prog));
+
+	    prog->flags = EF_REAL;
+	    prog->len = h->len + po;
+	    prog->npats = np = h->npats;
+	    prog->pats = pp = (Patprog *) d;
+	    prog->prog = (Wordcode) (((char *) d) + po);
+	    prog->strs = ((char *) prog->prog) + h->strs;
+	    prog->shf = NULL;
+	    prog->dump = f;
+
+	    while (np--)
+		*pp++ = dummy_patprog1;
+
+	    if (ksh)
+		*ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 :
+			((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1));
+
+	    return prog;
+	}
+    }
+    return NULL;
+}
+
+#ifdef USE_MMAP
+
+/* Increment the reference counter for a dump file. */
+
+/**/
+void
+incrdumpcount(FuncDump f)
+{
+    f->count++;
+}
+
+/* Decrement the reference counter for a dump file. If zero, unmap the file. */
+
+/**/
+void
+decrdumpcount(FuncDump f)
+{
+    f->count--;
+    if (!f->count) {
+	FuncDump p, q;
+
+	for (q = NULL, p = dumps; p && p != f; q = p, p = p->next);
+	if (p) {
+	    if (q)
+		q->next = p->next;
+	    else
+		dumps = p->next;
+	    munmap((void *) f->addr, f->len);
+	    zclose(f->fd);
+	    zsfree(f->name);
+	    zfree(f, sizeof(*f));
+	}
+    }
+}
+
+#else
+
+void
+incrdumpcount(FuncDump f)
+{
+}
+
+void
+decrdumpcount(FuncDump f)
+{
+}
+
+#endif
+
+/**/
+int
+dump_autoload(char *file, int on, char *ops, int func)
+{
+    Wordcode h;
+    FDHead n, e;
+    Shfunc shf;
+    int ret = 0;
+
+    if (!strsfx(FD_EXT, file))
+	file = dyncat(file, FD_EXT);
+
+    if (!(h = load_dump_header(file)))
+	return 1;
+
+    for (n = firstfdhead(h), e = (FDHead) (h + fdheaderlen(h)); n < e;
+	 n = nextfdhead(n)) {
+	shf = (Shfunc) zcalloc(sizeof *shf);
+	shf->flags = on;
+	shf->funcdef = mkautofn(shf);
+	shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf);
+	if (ops['X'] && eval_autoload(shf, shf->nam, ops, func))
+	    ret = 1;
+    }
+    return ret;
 }