about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <p.w.stephenson@ntlworld.com>2015-01-08 21:39:26 +0000
committerPeter Stephenson <p.w.stephenson@ntlworld.com>2015-01-09 21:33:39 +0000
commitcfd91eac0732da8ece012ca4ab051d928a85c9dd (patch)
tree68ef32e118a060de0c7685c848e0267e64cf5137 /Src
parent6291d38848680b84252799d9e33110bca842efe8 (diff)
downloadzsh-cfd91eac0732da8ece012ca4ab051d928a85c9dd.tar.gz
zsh-cfd91eac0732da8ece012ca4ab051d928a85c9dd.tar.xz
zsh-cfd91eac0732da8ece012ca4ab051d928a85c9dd.zip
Rearrange context saving.
Variables are now associated with the module that declares them, being
initialised and saved/restored there.  However, as many variables are
used for communication between modules, many of them are set in multiple
places, so the assignment is ambiguous.
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/compcore.c4
-rw-r--r--Src/Zle/compctl.c8
-rw-r--r--Src/Zle/textobjects.c4
-rw-r--r--Src/Zle/zle_tricky.c24
-rw-r--r--Src/builtin.c8
-rw-r--r--Src/context.c116
-rw-r--r--Src/exec.c8
-rw-r--r--Src/hist.c88
-rw-r--r--Src/init.c4
-rw-r--r--Src/lex.c321
-rw-r--r--Src/parse.c83
-rw-r--r--Src/signals.c4
-rw-r--r--Src/zsh.h65
-rw-r--r--Src/zsh.mdd3
14 files changed, 441 insertions, 299 deletions
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index f5056058a..000f9da2a 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1524,7 +1524,7 @@ set_comp_sep(void)
     ol = zlemetaline;
     addedx = 1;
     noerrs = 1;
-    lexsave();
+    zcontext_save();
     lexflags = LEXFLAGS_ZLE;
     /*
      * tl is the length of the temporary string including
@@ -1673,7 +1673,7 @@ set_comp_sep(void)
     inpop();
     errflag &= ~ERRFLAG_ERROR;
     noerrs = ne;
-    lexrestore();
+    zcontext_restore();
     wb = owb;
     we = owe;
     zlemetaline = ol;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 2a80e6c84..43dd4e2a9 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2795,7 +2795,7 @@ sep_comp_string(char *ss, char *s, int noffs)
      * get the words we have to expand.                        */
     addedx = 1;
     noerrs = 1;
-    lexsave();
+    zcontext_save();
     lexflags = LEXFLAGS_ZLE;
     tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
     strcpy(tmp, ss);
@@ -2849,7 +2849,7 @@ sep_comp_string(char *ss, char *s, int noffs)
     inpop();
     errflag &= ~ERRFLAG_ERROR;
     noerrs = ne;
-    lexrestore();
+    zcontext_restore();
     wb = owb;
     we = owe;
     zlemetacs = ocs;
@@ -3707,7 +3707,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	/* Put the string in the lexer buffer and call the lexer to *
 	 * get the words we have to expand.                        */
-	lexsave();
+	zcontext_save();
 	lexflags = LEXFLAGS_ZLE;
 	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
 	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
@@ -3726,7 +3726,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	strinend();
 	inpop();
 	errflag &= ~ERRFLAG_ERROR;
-	lexrestore();
+	zcontext_restore();
 	/* Fine, now do full expansion. */
 	prefork(foo, 0);
 	if (!errflag) {
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index 37d2c0ad9..9b3277a97 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -241,7 +241,7 @@ selectargument(UNUSED(char **args))
 
     addedx = 0;
     noerrs = 1;
-    lexsave();
+    zcontext_save();
     lexflags = LEXFLAGS_ACTIVE;
     linein = zlegetline(&ll, &cs);
     zlemetall = ll;
@@ -277,7 +277,7 @@ selectargument(UNUSED(char **args))
     inpop();
     errflag &= ~ERRFLAG_ERROR;
     noerrs = ne;
-    lexrestore();
+    zcontext_restore();
     zlemetacs = ocs;
     wb = owb;
     we = owe;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 950c22f38..f18ad170e 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -698,7 +698,7 @@ docomplete(int lst)
     freeheap();
     /* Save the lexer state, in case the completion code uses the lexer *
      * somewhere (e.g. when processing a compctl -s flag).              */
-    lexsave();
+    zcontext_save();
     if (inwhat == IN_ENV)
 	lincmd = 0;
     if (s) {
@@ -868,7 +868,7 @@ docomplete(int lst)
     } else
 	ret = 1;
     /* Reset the lexer state, pop the heap. */
-    lexrestore();
+    zcontext_restore();
     popheap();
 
     dat[0] = lst;
@@ -1164,7 +1164,7 @@ get_comp_string(void)
     varname = NULL;
     insubscr = 0;
     clwpos = -1;
-    lexsave();
+    zcontext_save();
     lexflags = LEXFLAGS_ZLE;
     inpush(dupstrspace(linptr), 0, NULL);
     strinbeg(0);
@@ -1422,7 +1422,7 @@ get_comp_string(void)
 		zlemetall -= parend;
 		zlemetaline[zlemetall + addedx] = '\0';
 	    }
-	    lexrestore();
+	    zcontext_restore();
 	    tt = NULL;
 	    goto start;
 	}
@@ -1496,12 +1496,12 @@ get_comp_string(void)
 	if (tmp) {
 	    tmp = NULL;
 	    linptr = zlemetaline;
-	    lexrestore();
+	    zcontext_restore();
 	    addedx = 0;
 	    goto start;
 	}
 	noaliases = ona;
-	lexrestore();
+	zcontext_restore();
 	return NULL;
     }
 
@@ -2151,7 +2151,7 @@ get_comp_string(void)
 	    offs = boffs;
 	}
     }
-    lexrestore();
+    zcontext_restore();
 
     return (char *)s;
 }
@@ -2791,7 +2791,7 @@ doexpandhist(void)
     expanding = 1;
     excs = zlemetacs;
     zlemetall = zlemetacs = 0;
-    lexsave();
+    zcontext_save();
     /* We push ol as it will remain unchanged */
     inpush(ol, 0, NULL);
     strinbeg(1);
@@ -2803,7 +2803,7 @@ doexpandhist(void)
     } while (tok != ENDINPUT && tok != LEXERR);
     while (!lexstop)
 	hgetc();
-    /* We have to save errflags because it's reset in lexrestore. Since  *
+    /* We have to save errflags because it's reset in zcontext_restore. Since  *
      * noerrs was set to 1 errflag is true if there was a habort() which *
      * means that the expanded string is unusable.                       */
     err = errflag;
@@ -2811,7 +2811,7 @@ doexpandhist(void)
     noaliases = ona;
     strinend();
     inpop();
-    lexrestore();
+    zcontext_restore();
     expanding = 0;
 
     if (!err) {
@@ -2910,7 +2910,7 @@ getcurcmd(void)
     int curlincmd;
     char *s = NULL;
 
-    lexsave();
+    zcontext_save();
     lexflags = LEXFLAGS_ZLE;
     metafy_line();
     inpush(dupstrspace(zlemetaline), 0, NULL);
@@ -2934,7 +2934,7 @@ getcurcmd(void)
     inpop();
     errflag &= ~ERRFLAG_ERROR;
     unmetafy_line();
-    lexrestore();
+    zcontext_restore();
 
     return s;
 }
diff --git a/Src/builtin.c b/Src/builtin.c
index d2108264f..8abe728db 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -6102,7 +6102,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
 	}
     }
 
-    lexsave();
+    zcontext_save();
     testargs = argv;
     tok = NULLTOK;
     condlex = testlex;
@@ -6112,16 +6112,16 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func)
 
     if (errflag) {
 	errflag &= ~ERRFLAG_ERROR;
-	lexrestore();
+	zcontext_restore();
 	return 1;
     }
 
     if (!prog || tok == LEXERR) {
 	zwarnnam(name, tokstr ? "parse error" : "argument expected");
-	lexrestore();
+	zcontext_restore();
 	return 1;
     }
-    lexrestore();
+    zcontext_restore();
 
     if (*curtestarg) {
 	zwarnnam(name, "too many arguments");
diff --git a/Src/context.c b/Src/context.c
new file mode 100644
index 000000000..bd8d191bf
--- /dev/null
+++ b/Src/context.c
@@ -0,0 +1,116 @@
+/*
+ * context.c - context save and restore
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+/*
+ * This short file provides a home for the stack of saved contexts.
+ * The actions for saving and restoring are encapsulated within
+ * individual modules.
+ */
+
+#include "zsh.mdh"
+#include "context.pro"
+
+struct context_stack {
+    struct context_stack *next;
+
+    struct hist_stack hist_stack;
+    struct lex_stack lex_stack;
+    struct parse_stack parse_stack;
+};
+
+static struct context_stack *cstack;
+
+/* save some or all of current context */
+
+/**/
+mod_export void
+zcontext_save_partial(int parts)
+{
+    struct context_stack *cs;
+
+    cs = (struct context_stack *)malloc(sizeof(struct context_stack));
+
+    if (parts & ZCONTEXT_HIST) {
+	hist_context_save(&cs->hist_stack, !cstack);
+    }
+    if (parts & ZCONTEXT_LEX) {
+	lex_context_save(&cs->lex_stack, !cstack);
+    }
+    if (parts & ZCONTEXT_PARSE) {
+	parse_context_save(&cs->parse_stack, !cstack);
+    }
+
+    cs->next = cstack;
+    cstack = cs;
+}
+
+/* save context in full */
+
+/**/
+mod_export void
+zcontext_save(void)
+{
+    zcontext_save_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
+
+/* restore context or part thereof */
+
+/**/
+mod_export void
+zcontext_restore_partial(int parts)
+{
+    struct context_stack *cs = cstack;
+
+    DPUTS(!cstack, "BUG: zcontext_restore() without zcontext_save()");
+
+    queue_signals();
+    cstack = cstack->next;
+
+    if (parts & ZCONTEXT_HIST) {
+	hist_context_restore(&cs->hist_stack, !cstack);
+    }
+    if (parts & ZCONTEXT_LEX) {
+	lex_context_restore(&cs->lex_stack, !cstack);
+    }
+    if (parts & ZCONTEXT_PARSE) {
+	parse_context_restore(&cs->parse_stack, !cstack);
+    }
+
+    free(cs);
+
+    unqueue_signals();
+}
+
+/* restore full context */
+
+/**/
+mod_export void
+zcontext_restore(void)
+{
+    zcontext_restore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+}
diff --git a/Src/exec.c b/Src/exec.c
index ab9291024..7b6495113 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -217,7 +217,7 @@ parse_string(char *s, int reset_lineno)
     Eprog p;
     zlong oldlineno;
 
-    lexsave();
+    zcontext_save();
     inpush(s, INP_LINENO, NULL);
     strinbeg(0);
     oldlineno = lineno;
@@ -229,7 +229,7 @@ parse_string(char *s, int reset_lineno)
 	lastval = 1;
     strinend();
     inpop();
-    lexrestore();
+    zcontext_restore();
     return p;
 }
 
@@ -3349,9 +3349,9 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		 * The copy uses the wordcode parsing area, so save and
 		 * restore state.
 		 */
-		lexsave();
+		zcontext_save();
 		redir_prog = eccopyredirs(&s);
-		lexrestore();
+		zcontext_restore();
 	    } else
 		redir_prog = NULL;
 	    
diff --git a/Src/hist.c b/Src/hist.c
index e65d78bfd..447f00e84 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -222,6 +222,85 @@ static int histsave_stack_pos = 0;
 
 static zlong histfile_linect;
 
+/* save history context */
+
+/**/
+void
+hist_context_save(struct hist_stack *hs, int toplevel)
+{
+    if (toplevel) {
+	/* top level, make this version visible to ZLE */
+	zle_chline = chline;
+	/* ensure line stored is NULL-terminated */
+	if (hptr)
+	    *hptr = '\0';
+    }
+    hs->histactive = histactive;
+    hs->histdone = histdone;
+    hs->stophist = stophist;
+    hs->hline = chline;
+    hs->hptr = hptr;
+    hs->chwords = chwords;
+    hs->chwordlen = chwordlen;
+    hs->chwordpos = chwordpos;
+    hs->hwgetword = hwgetword;
+    hs->hgetc = hgetc;
+    hs->hungetc = hungetc;
+    hs->hwaddc = hwaddc;
+    hs->hwbegin = hwbegin;
+    hs->hwend = hwend;
+    hs->addtoline = addtoline;
+    hs->hlinesz = hlinesz;
+    /*
+     * We save and restore the command stack with history
+     * as it's visible to the user interactively, so if
+     * we're preserving history state we'll continue to
+     * show the current set of commands from input.
+     */
+    hs->cstack = cmdstack;
+    hs->csp = cmdsp;
+
+    stophist = 0;
+    chline = NULL;
+    hptr = NULL;
+    histactive = 0;
+    cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
+    cmdsp = 0;
+}
+
+/**/
+void
+hist_context_restore(const struct hist_stack *hs, int toplevel)
+{
+    if (toplevel) {
+	/* Back to top level: don't need special ZLE value */
+	DPUTS(hs->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
+	zle_chline = NULL;
+    }
+    histactive = hs->histactive;
+    histdone = hs->histdone;
+    stophist = hs->stophist;
+    chline = hs->hline;
+    hptr = hs->hptr;
+    chwords = hs->chwords;
+    chwordlen = hs->chwordlen;
+    chwordpos = hs->chwordpos;
+    hwgetword = hs->hwgetword;
+    hgetc = hs->hgetc;
+    hungetc = hs->hungetc;
+    hwaddc = hs->hwaddc;
+    hwbegin = hs->hwbegin;
+    hwend = hs->hwend;
+    addtoline = hs->addtoline;
+    hlinesz = hs->hlinesz;
+    if (cmdstack)
+	zfree(cmdstack, CMDSTACKSZ);
+    cmdstack = hs->cstack;
+    cmdsp = hs->csp;
+}
+
+/* restore history context */
+
 /* add a character to the current history word */
 
 static void
@@ -815,6 +894,11 @@ strinbeg(int dohist)
     strin++;
     hbegin(dohist);
     lexinit();
+    /*
+     * Also initialise some variables owned by the parser but
+     * used for communication between the parser and lexer.
+     */
+    init_parse_status();
 }
 
 /* done reading a string */
@@ -2992,7 +3076,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
     opts[RCQUOTES] = 0;
     addedx = 0;
     noerrs = 1;
-    lexsave();
+    zcontext_save();
     lexflags = flags | LEXFLAGS_ACTIVE;
     /*
      * Are we handling comments?
@@ -3189,7 +3273,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags)
     errflag &= ~ERRFLAG_ERROR;
     nocomments = onc;
     noerrs = ne;
-    lexrestore();
+    zcontext_restore();
     zlemetacs = ocs;
     zlemetall = oll;
     wb = owb;
diff --git a/Src/init.c b/Src/init.c
index 080fc8561..e7d86feac 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -107,7 +107,7 @@ loop(int toplevel, int justonce)
 
     pushheap();
     if (!toplevel)
-	lexsave();
+	zcontext_save();
     for (;;) {
 	freeheap();
 	if (stophist == 3)	/* re-entry via preprompt() */
@@ -227,7 +227,7 @@ loop(int toplevel, int justonce)
     }
     err = errflag;
     if (!toplevel)
-	lexrestore();
+	zcontext_restore();
     popheap();
 
     if (err)
diff --git a/Src/lex.c b/Src/lex.c
index d440f3d70..69441b28d 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -203,263 +203,64 @@ static int dbparens;
 static int len = 0, bsiz = 256;
 static char *bptr;
 
-struct lexstack {
-    struct lexstack *next;
-
-    int incmdpos;
-    int incond;
-    int incasepat;
-    int dbparens;
-    int isfirstln;
-    int isfirstch;
-    int histactive;
-    int histdone;
-    int lexflags;
-    int stophist;
-    int hlinesz;
-    char *hline;
-    char *hptr;
-    enum lextok tok;
-    int isnewlin;
-    char *tokstr;
-    char *zshlextext;
-    char *bptr;
-    int bsiz;
-    int len;
-    int lex_add_raw;
-    char *tokstr_raw;
-    char *bptr_raw;
-    int bsiz_raw;
-    int len_raw;
-    short *chwords;
-    int chwordlen;
-    int chwordpos;
-    int hwgetword;
-    int lexstop;
-    struct heredocs *hdocs;
-    int (*hgetc) _((void));
-    void (*hungetc) _((int));
-    void (*hwaddc) _((int));
-    void (*hwbegin) _((int));
-    void (*hwend) _((void));
-    void (*addtoline) _((int));
-
-    int eclen, ecused, ecnpats;
-    Wordcode ecbuf;
-    Eccstr ecstrs;
-    int ecsoffs, ecssub, ecnfunc;
-
-    unsigned char *cstack;
-    int csp;
-    zlong toklineno;
-};
-
-static struct lexstack *lstack = NULL;
-
-/* save the context or parts thereof */
-
-/* is this a hack or what? */
+/* save lexical context */
 
 /**/
-mod_export void
-lexsave_partial(int parts)
-{
-    struct lexstack *ls;
-
-    ls = (struct lexstack *)malloc(sizeof(struct lexstack));
-
-    if (parts & ZCONTEXT_LEX) {
-	ls->incmdpos = incmdpos;
-	ls->incond = incond;
-	ls->incasepat = incasepat;
-	ls->dbparens = dbparens;
-	ls->isfirstln = isfirstln;
-	ls->isfirstch = isfirstch;
-	ls->lexflags = lexflags;
-
-	ls->tok = tok;
-	ls->isnewlin = isnewlin;
-	ls->tokstr = tokstr;
-	ls->zshlextext = zshlextext;
-	ls->bptr = bptr;
-	ls->bsiz = bsiz;
-	ls->len = len;
-	ls->lex_add_raw = lex_add_raw;
-	ls->tokstr_raw = tokstr_raw;
-	ls->bptr_raw = bptr_raw;
-	ls->bsiz_raw = bsiz_raw;
-	ls->len_raw = len_raw;
-	ls->lexstop = lexstop;
-	ls->toklineno = toklineno;
-
-	tokstr = zshlextext = bptr = NULL;
-	bsiz = 256;
-	tokstr_raw = bptr_raw = NULL;
-	bsiz_raw = len_raw = lex_add_raw = 0;
-
-	inredir = 0;
-    }
-    if (parts & ZCONTEXT_HIST) {
-	if (!lstack) {
-	    /* top level, make this version visible to ZLE */
-	    zle_chline = chline;
-	    /* ensure line stored is NULL-terminated */
-	    if (hptr)
-		*hptr = '\0';
-	}
-	ls->histactive = histactive;
-	ls->histdone = histdone;
-	ls->stophist = stophist;
-	ls->hline = chline;
-	ls->hptr = hptr;
-	ls->chwords = chwords;
-	ls->chwordlen = chwordlen;
-	ls->chwordpos = chwordpos;
-	ls->hwgetword = hwgetword;
-	ls->hgetc = hgetc;
-	ls->hungetc = hungetc;
-	ls->hwaddc = hwaddc;
-	ls->hwbegin = hwbegin;
-	ls->hwend = hwend;
-	ls->addtoline = addtoline;
-	ls->hlinesz = hlinesz;
-	/*
-	 * We save and restore the command stack with history
-	 * as it's visible to the user interactively, so if
-	 * we're preserving history state we'll continue to
-	 * show the current set of commands from input.
-	 */
-	ls->cstack = cmdstack;
-	ls->csp = cmdsp;
-
-	stophist = 0;
-	chline = NULL;
-	hptr = NULL;
-	histactive = 0;
-	cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
-	cmdsp = 0;
-    }
-    if (parts & ZCONTEXT_PARSE) {
-	ls->hdocs = hdocs;
-	ls->eclen = eclen;
-	ls->ecused = ecused;
-	ls->ecnpats = ecnpats;
-	ls->ecbuf = ecbuf;
-	ls->ecstrs = ecstrs;
-	ls->ecsoffs = ecsoffs;
-	ls->ecssub = ecssub;
-	ls->ecnfunc = ecnfunc;
-	ecbuf = NULL;
-	hdocs = NULL;
-    }
-
-    ls->next = lstack;
-    lstack = ls;
-}
-
-/* save context in full */
-
-/**/
-mod_export void
-lexsave(void)
-{
-    lexsave_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
-}
-
-/* restore context or part therefore */
-
-/**/
-mod_export void
-lexrestore_partial(int parts)
+void
+lex_context_save(struct lex_stack *ls, int toplevel)
 {
-    struct lexstack *ln = lstack;
-
-    DPUTS(!lstack, "BUG: lexrestore() without lexsave()");
-
-    queue_signals();
-    lstack = lstack->next;
-
-    if (parts & ZCONTEXT_LEX) {
-	incmdpos = ln->incmdpos;
-	incond = ln->incond;
-	incasepat = ln->incasepat;
-	dbparens = ln->dbparens;
-	isfirstln = ln->isfirstln;
-	isfirstch = ln->isfirstch;
-	lexflags = ln->lexflags;
-	tok = ln->tok;
-	isnewlin = ln->isnewlin;
-	tokstr = ln->tokstr;
-	zshlextext = ln->zshlextext;
-	bptr = ln->bptr;
-	bsiz = ln->bsiz;
-	len = ln->len;
-	lex_add_raw = ln->lex_add_raw;
-	tokstr_raw = ln->tokstr_raw;
-	bptr_raw = ln->bptr_raw;
-	bsiz_raw = ln->bsiz_raw;
-	len_raw = ln->len_raw;
-	lexstop = ln->lexstop;
-	toklineno = ln->toklineno;
-    }
-
-    if (parts & ZCONTEXT_HIST) {
-	if (!lstack) {
-	    /* Back to top level: don't need special ZLE value */
-	    DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE");
-	    zle_chline = NULL;
-	}
-	histactive = ln->histactive;
-	histdone = ln->histdone;
-	stophist = ln->stophist;
-	chline = ln->hline;
-	hptr = ln->hptr;
-	chwords = ln->chwords;
-	chwordlen = ln->chwordlen;
-	chwordpos = ln->chwordpos;
-	hwgetword = ln->hwgetword;
-	hgetc = ln->hgetc;
-	hungetc = ln->hungetc;
-	hwaddc = ln->hwaddc;
-	hwbegin = ln->hwbegin;
-	hwend = ln->hwend;
-	addtoline = ln->addtoline;
-	hlinesz = ln->hlinesz;
-	if (cmdstack)
-	    zfree(cmdstack, CMDSTACKSZ);
-	cmdstack = ln->cstack;
-	cmdsp = ln->csp;
-    }
-
-    if (parts & ZCONTEXT_PARSE) {
-	if (ecbuf)
-	    zfree(ecbuf, eclen);
-
-	hdocs = ln->hdocs;
-	eclen = ln->eclen;
-	ecused = ln->ecused;
-	ecnpats = ln->ecnpats;
-	ecbuf = ln->ecbuf;
-	ecstrs = ln->ecstrs;
-	ecsoffs = ln->ecsoffs;
-	ecssub = ln->ecssub;
-	ecnfunc = ln->ecnfunc;
-
-	errflag &= ~ERRFLAG_ERROR;
-    }
-
-    free(ln);
-
-    unqueue_signals();
+    (void)toplevel;
+
+    ls->dbparens = dbparens;
+    ls->isfirstln = isfirstln;
+    ls->isfirstch = isfirstch;
+    ls->lexflags = lexflags;
+
+    ls->tok = tok;
+    ls->tokstr = tokstr;
+    ls->zshlextext = zshlextext;
+    ls->bptr = bptr;
+    ls->bsiz = bsiz;
+    ls->len = len;
+    ls->lex_add_raw = lex_add_raw;
+    ls->tokstr_raw = tokstr_raw;
+    ls->bptr_raw = bptr_raw;
+    ls->bsiz_raw = bsiz_raw;
+    ls->len_raw = len_raw;
+    ls->lexstop = lexstop;
+    ls->toklineno = toklineno;
+
+    tokstr = zshlextext = bptr = NULL;
+    bsiz = 256;
+    tokstr_raw = bptr_raw = NULL;
+    bsiz_raw = len_raw = lex_add_raw = 0;
 }
 
-/* complete restore context */
+/* restore lexical context */
 
 /**/
 mod_export void
-lexrestore(void)
+lex_context_restore(const struct lex_stack *ls, int toplevel)
 {
-    lexrestore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE);
+    (void)toplevel;
+
+    dbparens = ls->dbparens;
+    isfirstln = ls->isfirstln;
+    isfirstch = ls->isfirstch;
+    lexflags = ls->lexflags;
+    tok = ls->tok;
+    tokstr = ls->tokstr;
+    zshlextext = ls->zshlextext;
+    bptr = ls->bptr;
+    bsiz = ls->bsiz;
+    len = ls->len;
+    lex_add_raw = ls->lex_add_raw;
+    tokstr_raw = ls->tokstr_raw;
+    bptr_raw = ls->bptr_raw;
+    bsiz_raw = ls->bsiz_raw;
+    len_raw = ls->len_raw;
+    lexstop = ls->lexstop;
+    toklineno = ls->toklineno;
 }
 
 /**/
@@ -634,9 +435,7 @@ initlextabs(void)
 void
 lexinit(void)
 {
-    incond = incasepat = nocorrect =
-    infor = dbparens = lexstop = 0;
-    incmdpos = 1;
+    nocorrect = dbparens = lexstop = 0;
     tok = ENDINPUT;
 }
 
@@ -1725,7 +1524,7 @@ parsestrnoerr(char *s)
 {
     int l = strlen(s), err;
 
-    lexsave();
+    zcontext_save();
     untokenize(s);
     inpush(dupstring(s), 0, NULL);
     strinbeg(0);
@@ -1737,7 +1536,7 @@ parsestrnoerr(char *s)
     strinend();
     inpop();
     DPUTS(cmdsp, "BUG: parsestr: cmdstack not empty.");
-    lexrestore();
+    zcontext_restore();
     return err;
 }
 
@@ -1756,7 +1555,7 @@ parse_subscript(char *s, int sub, int endchar)
 
     if (!*s || *s == endchar)
 	return 0;
-    lexsave();
+    zcontext_save();
     untokenize(t = dupstring(s));
     inpush(t, 0, NULL);
     strinbeg(0);
@@ -1776,7 +1575,7 @@ parse_subscript(char *s, int sub, int endchar)
     strinend();
     inpop();
     DPUTS(cmdsp, "BUG: parse_subscript: cmdstack not empty.");
-    lexrestore();
+    zcontext_restore();
     return s;
 }
 
@@ -1794,7 +1593,7 @@ parse_subst_string(char *s)
 
     if (!*s || !strcmp(s, nulstring))
 	return 0;
-    lexsave();
+    zcontext_save();
     untokenize(s);
     inpush(dupstring(s), 0, NULL);
     strinbeg(0);
@@ -1807,7 +1606,7 @@ parse_subst_string(char *s)
     strinend();
     inpop();
     DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty.");
-    lexrestore();
+    zcontext_restore();
     /* Keep any interrupt error status */
     errflag = err | (errflag & ERRFLAG_INT);
     if (ctok == LEXERR) {
@@ -1817,7 +1616,7 @@ parse_subst_string(char *s)
 #ifdef DEBUG
     /*
      * Historical note: we used to check here for olen (the value of len
-     * before lexrestore()) == l, but that's not necessarily the case if
+     * before zcontext_restore()) == l, but that's not necessarily the case if
      * we stripped an RCQUOTE.
      */
     if (ctok != STRING || (errflag && !noerrs)) {
@@ -2047,7 +1846,7 @@ skipcomm(void)
 	new_len = len;
 	new_bsiz = bsiz;
 
-	lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+	zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
     } else {
 	/*
 	 * Set up for nested command subsitution, however
@@ -2063,7 +1862,7 @@ skipcomm(void)
 	new_len = len_raw;
 	new_bsiz = bsiz_raw;
 
-	lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+	zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
     }
     tokstr_raw = new_tokstr;
     bsiz_raw = new_bsiz;
@@ -2090,7 +1889,7 @@ skipcomm(void)
      */
     new_lexstop = lexstop;
 
-    lexrestore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
+    zcontext_restore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE);
 
     if (lex_add_raw) {
 	/*
diff --git a/Src/parse.c b/Src/parse.c
index fa37ca3d9..0b54a904d 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -31,7 +31,7 @@
 #include "parse.pro"
 
 /* != 0 if we are about to read a command word */
- 
+
 /**/
 mod_export int incmdpos;
 
@@ -242,6 +242,67 @@ int ecsoffs, ecssub, ecnfunc;
 #define EC_DOUBLE_THRESHOLD  32768
 #define EC_INCREMENT         1024
 
+/* save parse context */
+
+/**/
+void
+parse_context_save(struct parse_stack *ps, int toplevel)
+{
+    (void)toplevel;
+
+    ps->incmdpos = incmdpos;
+    ps->aliasspaceflag = aliasspaceflag;
+    ps->incond = incond;
+    ps->inredir = inredir;
+    ps->incasepat = incasepat;
+    ps->isnewlin = isnewlin;
+    ps->infor = infor;
+
+    ps->hdocs = hdocs;
+    ps->eclen = eclen;
+    ps->ecused = ecused;
+    ps->ecnpats = ecnpats;
+    ps->ecbuf = ecbuf;
+    ps->ecstrs = ecstrs;
+    ps->ecsoffs = ecsoffs;
+    ps->ecssub = ecssub;
+    ps->ecnfunc = ecnfunc;
+    ecbuf = NULL;
+    hdocs = NULL;
+}
+
+/* restore parse context */
+
+/**/
+void
+parse_context_restore(const struct parse_stack *ps, int toplevel)
+{
+    (void)toplevel;
+
+    if (ecbuf)
+	zfree(ecbuf, eclen);
+
+    incmdpos = ps->incmdpos;
+    aliasspaceflag = ps->aliasspaceflag;
+    incond = ps->incond;
+    inredir = ps->inredir;
+    incasepat = ps->incasepat;
+    incasepat = ps->incasepat;
+    isnewlin = ps->isnewlin;
+    infor = ps->infor;
+
+    hdocs = ps->hdocs;
+    eclen = ps->eclen;
+    ecused = ps->ecused;
+    ecnpats = ps->ecnpats;
+    ecbuf = ps->ecbuf;
+    ecstrs = ps->ecstrs;
+    ecsoffs = ps->ecsoffs;
+    ecssub = ps->ecssub;
+    ecnfunc = ps->ecnfunc;
+
+    errflag &= ~ERRFLAG_ERROR;
+}
 
 /* Adjust pointers in here-doc structs. */
 
@@ -359,6 +420,21 @@ ecstrcode(char *s)
     } while (0)
 
 
+/**/
+mod_export void
+init_parse_status(void)
+{
+    /*
+     * These variables are currently declared by the parser, so we
+     * initialise them here.  Possibly they are more naturally declared
+     * by the lexical anaylser; however, as they are used for signalling
+     * between the two it's a bit ambiguous.  We clear them when
+     * using the lexical analyser for strings as well as here.
+     */
+    incasepat = incond = inredir = infor = 0;
+    incmdpos = 1;
+}
+
 /* Initialise wordcode buffer. */
 
 /**/
@@ -373,6 +449,8 @@ init_parse(void)
     ecsoffs = ecnpats = 0;
     ecssub = 0;
     ecnfunc = 0;
+
+    init_parse_status();
 }
 
 /* Build eprog. */
@@ -539,9 +617,8 @@ parse_list(void)
     int c = 0;
 
     tok = ENDINPUT;
-    incmdpos = 1;
-    zshlex();
     init_parse();
+    zshlex();
     par_list(&c);
     if (tok != ENDINPUT) {
         clear_hdocs();
diff --git a/Src/signals.c b/Src/signals.c
index 899f1217b..3950ad1a2 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -1210,7 +1210,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
     intrap++;
     *sigtr |= ZSIG_IGNORED;
 
-    lexsave();
+    zcontext_save();
     /* execsave will save the old trap_return and trap_state */
     execsave();
     breaks = retflag = 0;
@@ -1265,7 +1265,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
     new_trap_return = trap_return;
 
     execrestore();
-    lexrestore();
+    zcontext_restore();
 
     if (new_trap_state == TRAP_STATE_FORCE_RETURN &&
 	/* zero return from function isn't special */
diff --git a/Src/zsh.h b/Src/zsh.h
index 475b7824f..8fb4f977a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2691,6 +2691,71 @@ struct sortelt {
 
 typedef struct sortelt *SortElt;
 
+/*********************************************************/
+/* Structures to save and restore for individual modules */
+/*********************************************************/
+
+/* History */
+struct hist_stack {
+    int histactive;
+    int histdone;
+    int stophist;
+    int hlinesz;
+    char *hline;
+    char *hptr;
+    short *chwords;
+    int chwordlen;
+    int chwordpos;
+    int hwgetword;
+    int (*hgetc) _((void));
+    void (*hungetc) _((int));
+    void (*hwaddc) _((int));
+    void (*hwbegin) _((int));
+    void (*hwend) _((void));
+    void (*addtoline) _((int));
+    unsigned char *cstack;
+    int csp;
+};
+
+/* Lexical analyser */
+struct lex_stack {
+    int dbparens;
+    int isfirstln;
+    int isfirstch;
+    int lexflags;
+    enum lextok tok;
+    char *tokstr;
+    char *zshlextext;
+    char *bptr;
+    int bsiz;
+    int len;
+    int lex_add_raw;
+    char *tokstr_raw;
+    char *bptr_raw;
+    int bsiz_raw;
+    int len_raw;
+    int lexstop;
+    zlong toklineno;
+};
+
+/* Parser */
+struct parse_stack {
+    struct heredocs *hdocs;
+
+    int incmdpos;
+    int aliasspaceflag;
+    int incond;
+    int inredir;
+    int incasepat;
+    int isnewlin;
+    int infor;
+
+    int eclen, ecused, ecnpats;
+    Wordcode ecbuf;
+    Eccstr ecstrs;
+    int ecsoffs, ecssub, ecnfunc;
+};
+
 /************************/
 /* Flags to casemodifiy */
 /************************/
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 9a8c923f9..f0379d2d1 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -9,7 +9,8 @@ alwayslink=1
 
 # autobins not specified because of alwayslink
 
-objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o hashnameddir.o \
+objects="builtin.o compat.o cond.o context.o \
+exec.o glob.o hashtable.o hashnameddir.o \
 hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \
 mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \
 signames.o sort.o string.o subst.o text.o utils.o watch.o"