about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog12
-rw-r--r--Doc/Zsh/builtins.yo60
-rw-r--r--Src/Modules/newuser.c2
-rw-r--r--Src/Modules/parameter.c1
-rw-r--r--Src/builtin.c34
-rw-r--r--Src/exec.c34
-rw-r--r--Src/hashtable.c2
-rw-r--r--Src/init.c15
-rw-r--r--Src/mkbltnmlst.sh2
-rw-r--r--Src/options.c26
-rw-r--r--Src/params.c8
-rw-r--r--Src/parse.c1
-rw-r--r--Src/signals.c3
-rw-r--r--Src/subst.c2
-rw-r--r--Src/zsh.h15
-rw-r--r--Test/B07emulate.ztst174
16 files changed, 322 insertions, 69 deletions
diff --git a/ChangeLog b/ChangeLog
index efef75d7f..20cf6ea4d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2009-02-11  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 26556: Doc/Zsh/builtins.yo: documentation for 26546.
+
+	* 26546: Src/builtin.c, Src/exec.c, Src/hashtable.c, Src/init.c,
+	Src/mkbltnmlst.sh, Src/options.c, Src/params.c, Src/parse.c,
+	Src/signals.c, Src/subst.c, Src/zsh.h, Src/Modules/newuser.c,
+	Src/Modules/parameter.c, Test/B07emulate.ztst: sticky emulation
+	for functions defined within emualate ... -c ... environments.
+
 2009-02-11  Peter Stephenson  <pws@csr.com>
 
 	* unposted: Functions/Calendar/age: accidentally committed a
@@ -11157,5 +11167,5 @@
 
 *****************************************************
 * This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.4562 $                         
+* $Revision: 1.4563 $                         
 *****************************************************
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 4487ee175..1d74d7f49 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -356,10 +356,6 @@ noderef(Compatibility)
 )\
 .
 
-If tt(-c) tt(arg) is given, evaluate tt(arg) after temporary setting
-requested emulation. Emulation and all options will be restored to their
-original values before tt(emulate) returns.
-
 If the tt(-R) option is given, all options
 are reset to their default value corresponding to the specified emulation
 mode, except for certain options describing the interactive
@@ -370,6 +366,62 @@ well, causing the effects of the tt(emulate) command and any tt(setopt) and
 tt(trap) commands to be local to the immediately surrounding shell
 function, if any; normally these options are turned off in all emulation
 modes except tt(ksh). The tt(-L) and tt(-c) are mutually exclusive.
+
+If tt(-c) tt(arg) is given, evaluate tt(arg) while the requested
+emulation is temporarily in effect.  The emulation and all options will
+be restored to their original values before tt(emulate) returns.  The
+tt(-R) flag may be used.
+
+Use of tt(-c) enables `sticky' emulation mode for functions defined
+within the evaluated expression:  the emulation mode is associated
+thereafter with the function so that whenever the function is executed
+the emulation (respecting the tt(-R) flag, if present) and all
+options are set before entry to the function, and restored after exit.
+If the function is called when the sticky emulation is already in
+effect, either within an `tt(emulate) var(shell) tt(-c)' expression or
+within another function with the same sticky emulation, entry and exit
+from the function do not cause options to be altered (except due to
+standard processing such as the tt(LOCAL_OPTIONS) option).
+
+For example:
+
+example(emulate sh -c 'fni+LPAR()RPAR() { setopt cshnullglob; }
+fno+LPAR()RPAR() { fni; }'
+fno
+)
+
+The two functions tt(fni) and tt(fno) are defined with sticky tt(sh)
+emulation.  tt(fno) is then executed, causing options associated
+with emulations to be set to their values in tt(sh).  tt(fni) then
+calls tt(fno); because tt(fno) is also marked for sticky tt(sh)
+emulation, no option changes take place on entry to or exit from it.
+Hence the option tt(cshnullglob), turned off by tt(sh) emulation, will
+be turned on within tt(fni) and remain on on return to tt(fno).  On exit
+from tt(fno), the emulation mode and all options will be restored to the
+state they were in before entry to the temporary emulation.
+
+The documentation above is typically sufficient for the intended
+purpose of executing code designed for other shells in a suitable
+environment.  More detailed rules follow.
+startsitem()
+sitem(1.)(The sticky emulation environment provided by `tt(emulate)
+var(shell) tt(-c)' is identical to that provided by entry to
+a function marked for sticky emulation as a consequence of being
+defined in such an environment.  Hence, for example, the sticky
+emulation is inherited by subfunctions defined within functions
+with sticky emulation.)
+sitem(2.)(No change of options takes place on entry to or exit from
+functions that are not marked for sticky emulation, other than those
+that would normally take place, even if those functions are called
+within sticky emulation.)
+sitem(3.)(No special handling is provided for functions marked for
+tt(autoload) nor for functions present in wordcode created by
+the tt(zcompile) command.)
+sitem(4.)(The presence or absence of the tt(-R) flag to tt(emulate)
+corresponds to different sticky emulation modes, so for example
+`tt(emulate sh -c)', `tt(emulate -R sh -c)' and `tt(emulate csh -c)'
+are treated as three distinct sticky emulations.)
+endsitem()
 )
 findex(enable)
 cindex(enabling commands)
diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c
index e19c34df3..cc020d5bd 100644
--- a/Src/Modules/newuser.c
+++ b/Src/Modules/newuser.c
@@ -78,7 +78,7 @@ boot_(UNUSED(Module m))
 	0 };
     const char **sp;
 
-    if (emulation != EMULATE_ZSH)
+    if (!EMULATION(EMULATE_ZSH))
 	return 0;
 
     if (!dotdir) {
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index e3fb909ea..38b462001 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -289,6 +289,7 @@ setfunction(char *name, char *val, int dis)
     shf = (Shfunc) zshcalloc(sizeof(*shf));
     shf->funcdef = dupeprog(prog, 0);
     shf->node.flags = dis;
+    shf->emulation = sticky_emulation;
 
     if (!strncmp(name, "TRAP", 4) &&
 	(sn = getsignum(name + 4)) != -1) {
diff --git a/Src/builtin.c b/Src/builtin.c
index 99af38e3a..b402d56e2 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -536,7 +536,7 @@ bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 
     /* Obsolescent sh compatibility: set - is the same as set +xv *
      * and set - args is the same as set +xv -- args              */
-    if (emulation != EMULATE_ZSH && *args && **args == '-' && !args[0][1]) {
+    if (!EMULATION(EMULATE_ZSH) && *args && **args == '-' && !args[0][1]) {
 	dosetopt(VERBOSE, 0, 0);
 	dosetopt(XTRACE, 0, 0);
 	if (!args[1])
@@ -2861,6 +2861,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    shf = (Shfunc) zshcalloc(sizeof *shf);
 	    shf->node.flags = on;
 	    shf->funcdef = mkautofn(shf);
+	    /* No sticky emulation for autoloaded functions */
+	    shf->emulation = 0;
 	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
 
 	    if (signum != -1) {
@@ -4834,21 +4836,38 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
 {
     int opt_L = OPT_ISSET(ops, 'L');
     int opt_R = OPT_ISSET(ops, 'R');
-    int saveemulation ;
+    int saveemulation, savesticky_emulation;
     int ret;
     char saveopts[OPT_SIZE];
 
     /* without arguments just print current emulation */
     if (!*argv) {
+	const char *shname;
+
 	if (opt_L || opt_R) {
 	    zwarnnam("emulate", "not enough arguments");
 	    return 1;
 	}
 
-	printf("%s\n", emulation == EMULATE_CSH ? "csh" :
-		       emulation == EMULATE_KSH ? "ksh" :
-		       emulation == EMULATE_SH  ? "sh" :
-		       "zsh");
+	switch(SHELL_EMULATION()) {
+	case EMULATE_CSH:
+	    shname = "csh";
+	    break;
+
+	case EMULATE_KSH:
+	    shname = "ksh";
+	    break;
+
+	case EMULATE_SH:
+	    shname = "sh";
+	    break;
+
+	default:
+	    shname = "zsh";
+	    break;
+	}
+
+	printf("%s\n", shname);
 	return 0;
     }
 
@@ -4880,9 +4899,12 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
 
     memcpy(saveopts, opts, sizeof(opts));
     saveemulation = emulation;
+    savesticky_emulation = sticky_emulation;
     emulate(*argv, OPT_ISSET(ops,'R'));
+    sticky_emulation = emulation;
     ret = eval(argv+2);
     memcpy(opts, saveopts, sizeof(opts));
+    sticky_emulation = savesticky_emulation;
     emulation = saveemulation;
     return ret;
 }
diff --git a/Src/exec.c b/Src/exec.c
index 5aec655a2..b86e5350c 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3999,6 +3999,7 @@ execfuncdef(Estate state, UNUSED(int do_exec))
 	shf->node.flags = 0;
 	shf->filename = ztrdup(scriptfilename);
 	shf->lineno = lineno;
+	shf->emulation = sticky_emulation;
 
 	if (!names) {
 	    /*
@@ -4221,7 +4222,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     char *name = shfunc->node.nam;
     int flags = shfunc->node.flags;
     char *fname = dupstring(name);
-    int obreaks, saveemulation ;
+    int obreaks, saveemulation, savesticky_emulation, restore_sticky;
     Eprog prog;
     struct funcstack fstack;
 #ifdef MAX_FUNCTION_DEPTH
@@ -4261,6 +4262,26 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
      * function we need to restore the original options on exit.   */
     memcpy(saveopts, opts, sizeof(opts));
     saveemulation = emulation;
+    savesticky_emulation = sticky_emulation;
+
+    if (shfunc->emulation && sticky_emulation != shfunc->emulation) {
+	/*
+	 * Function is marked for sticky emulation.
+	 * Enable it now.
+	 *
+	 * We deliberately do not do this if the sticky emulation
+	 * in effect is the same as that requested.  This enables
+	 * option setting naturally within emulation environments.
+	 * Note that a difference in EMULATE_FULLY (emulate with
+	 * or without -R) counts as a different environment.
+	 *
+	 * This propagates the sticky emulation to subfunctions.
+	 */
+	emulation = sticky_emulation = shfunc->emulation;
+	restore_sticky = 1;
+	installemulation();
+    } else
+	restore_sticky = 0;
 
     if (flags & PM_TAGGED)
 	opts[XTRACE] = 1;
@@ -4349,7 +4370,16 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     zoptind = oldzoptind;
     scriptname = oldscriptname;
 
-    if (isset(LOCALOPTIONS)) {
+    if (restore_sticky) {
+	/*
+	 * If we switched to an emulation environment just for
+	 * this function, we interpret the option and emulation
+	 * switch as being a firewall between environments.
+	 */
+	memcpy(opts, saveopts, sizeof(opts));
+	emulation = saveemulation;
+	sticky_emulation = savesticky_emulation;
+    } else if (isset(LOCALOPTIONS)) {
 	/* restore all shell options except PRIVILEGED and RESTRICTED */
 	saveopts[PRIVILEGED] = opts[PRIVILEGED];
 	saveopts[RESTRICTED] = opts[RESTRICTED];
diff --git a/Src/hashtable.c b/Src/hashtable.c
index e12aca852..2eb70c3b0 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -588,7 +588,7 @@ mod_export HashTable cmdnamtab;
  
 /**/
 mod_export char **pathchecked;
- 
+
 /* Create a new command hash table */
  
 /**/
diff --git a/Src/init.c b/Src/init.c
index 215514f6d..341446cb9 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -775,7 +775,7 @@ setupvals(void)
     if(unset(INTERACTIVE)) {
 	prompt = ztrdup("");
 	prompt2 = ztrdup("");
-    } else if (emulation == EMULATE_KSH || emulation == EMULATE_SH) {
+    } else if (EMULATION(EMULATE_KSH|EMULATE_SH)) {
 	prompt  = ztrdup(privasserted() ? "# " : "$ ");
 	prompt2 = ztrdup("> ");
     } else {
@@ -783,7 +783,7 @@ setupvals(void)
 	prompt2 = ztrdup("%_> ");
     }
     prompt3 = ztrdup("?# ");
-    prompt4 = (emulation == EMULATE_KSH || emulation == EMULATE_SH)
+    prompt4 = EMULATION(EMULATE_KSH|EMULATE_SH)
 	? ztrdup("+ ") : ztrdup("+%N:%i> ");
     sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? ");
 
@@ -811,14 +811,14 @@ setupvals(void)
     /* Get password entry and set info for `USERNAME' */
 #ifdef USE_GETPWUID
     if ((pswd = getpwuid(cached_uid))) {
-	if (emulation == EMULATE_ZSH)
+	if (EMULATION(EMULATE_ZSH))
 	    home = metafy(pswd->pw_dir, -1, META_DUP);
 	cached_username = ztrdup(pswd->pw_name);
     }
     else
 #endif /* USE_GETPWUID */
     {
-	if (emulation == EMULATE_ZSH)
+	if (EMULATION(EMULATE_ZSH))
 	    home = ztrdup("/");
 	cached_username = ztrdup("");
     }
@@ -828,7 +828,7 @@ setupvals(void)
      * In non-native emulations HOME must come from the environment;
      * we're not allowed to set it locally.
      */
-    if (emulation == EMULATE_ZSH)
+    if (EMULATION(EMULATE_ZSH))
 	ptr = home;
     else
 	ptr = zgetenv("HOME");
@@ -954,7 +954,7 @@ run_init_scripts(void)
 {
     noerrexit = -1;
 
-    if (emulation == EMULATE_KSH || emulation == EMULATE_SH) {
+    if (EMULATION(EMULATE_KSH|EMULATE_SH)) {
 	if (islogin)
 	    source("/etc/profile");
 	if (unset(PRIVILEGED)) {
@@ -1160,8 +1160,7 @@ sourcehome(char *s)
     char *h;
 
     queue_signals();
-    if (emulation == EMULATE_SH || emulation == EMULATE_KSH ||
-	!(h = getsparam("ZDOTDIR"))) {
+    if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam("ZDOTDIR"))) {
 	h = home;
 	if (!h)
 	    return;
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index 005e2ef81..d9c5b13e9 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -40,7 +40,7 @@ for x_mod in $x_mods; do
     unset moddeps autofeatures
     . $srcdir/../$modfile
     if test "x$autofeatures" != x; then
-	echo "  if (emulation == EMULATE_ZSH) {"
+	echo "  if (EMULATION(EMULATE_ZSH)) {"
 	echo "    char *features[] = { "
 	for feature in $autofeatures; do
 	    echo "      \"$feature\","
diff --git a/Src/options.c b/Src/options.c
index 3c7f88048..f852ec830 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -35,6 +35,11 @@
 /**/
 mod_export int emulation;
  
+/* current sticky emulation:  0 means none */
+
+/**/
+mod_export int sticky_emulation;
+
 /* the options; e.g. if opts[SHGLOB] != 0, SH_GLOB is turned on */
  
 /**/
@@ -58,9 +63,12 @@ mod_export HashTable optiontab;
 #define OPT_NONBOURNE	(OPT_ALL & ~OPT_BOURNE)
 #define OPT_NONZSH	(OPT_ALL & ~OPT_ZSH)
 
-#define OPT_EMULATE	(1<<5)	/* option is relevant to emulation */
-#define OPT_SPECIAL	(1<<6)	/* option should never be set by emulate() */
-#define OPT_ALIAS	(1<<7)	/* option is an alias to an other option */
+/* option is relevant to emulation */
+#define OPT_EMULATE	(EMULATE_UNUSED)
+/* option should never be set by emulate() */
+#define OPT_SPECIAL	(EMULATE_UNUSED<<1)
+/* option is an alias to an other option */
+#define OPT_ALIAS	(EMULATE_UNUSED<<2)
 
 #define defset(X) (!!((X)->node.flags & emulation))
 
@@ -477,6 +485,14 @@ setemulate(HashNode hn, int fully)
 
 /**/
 void
+installemulation(void)
+{
+    scanhashtable(optiontab, 0, 0, 0, setemulate,
+		  !!(emulation & EMULATE_FULLY));
+}
+
+/**/
+void
 emulate(const char *zsh_name, int fully)
 {
     char ch = *zsh_name;
@@ -494,7 +510,9 @@ emulate(const char *zsh_name, int fully)
     else
 	emulation = EMULATE_ZSH;
 
-    scanhashtable(optiontab, 0, 0, 0, setemulate, fully);
+    if (fully)
+	emulation |= EMULATE_FULLY;
+    installemulation();
 }
 
 /* setopt, unsetopt */
diff --git a/Src/params.c b/Src/params.c
index 1e902e871..6a7ab0fa6 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -642,7 +642,7 @@ createparamtable(void)
     /* Add the special parameters to the hash table */
     for (ip = special_params; ip->node.nam; ip++)
 	paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
-    if (emulation != EMULATE_SH && emulation != EMULATE_KSH)
+    if (!EMULATION(EMULATE_SH|EMULATE_KSH))
 	while ((++ip)->node.nam)
 	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
 
@@ -720,7 +720,7 @@ createparamtable(void)
 #endif
     opts[ALLEXPORT] = oae;
 
-    if (emulation == EMULATE_ZSH)
+    if (EMULATION(EMULATE_ZSH))
     {
 	/*
 	 * For native emulation we always set the variable home
@@ -1881,7 +1881,7 @@ getstrvalue(Value v)
     switch(PM_TYPE(v->pm->node.flags)) {
     case PM_HASHED:
 	/* (!v->isarr) should be impossible unless emulating ksh */
-	if (!v->isarr && emulation == EMULATE_KSH) {
+	if (!v->isarr && EMULATION(EMULATE_KSH)) {
 	    s = dupstring("[0]");
 	    if (getindex(&s, v, 0) == 0)
 		s = getstrvalue(v);
@@ -2164,7 +2164,7 @@ export_param(Param pm)
 
     if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
 #if 0	/* Requires changes elsewhere in params.c and builtin.c */
-	if (emulation == EMULATE_KSH /* isset(KSHARRAYS) */) {
+	if (EMULATION(EMULATE_KSH) /* isset(KSHARRAYS) */) {
 	    struct value v;
 	    v.isarr = 1;
 	    v.flags = 0;
diff --git a/Src/parse.c b/Src/parse.c
index 722809a78..59459870a 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -3415,6 +3415,7 @@ dump_autoload(char *nam, char *file, int on, Options ops, int func)
 	shf = (Shfunc) zshcalloc(sizeof *shf);
 	shf->node.flags = on;
 	shf->funcdef = mkautofn(shf);
+	shf->emulation = 0;
 	shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf);
 	if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->node.nam, ops, func))
 	    ret = 1;
diff --git a/Src/signals.c b/Src/signals.c
index ac5ffaa21..5d1797f2f 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -706,6 +706,7 @@ dosavetrap(int sig, int level)
 	    newshf->node.flags = shf->node.flags;
 	    newshf->funcdef = dupeprog(shf->funcdef, 0);
 	    newshf->filename = ztrdup(shf->filename);
+	    newshf->emulation = shf->emulation;
 	    if (shf->node.flags & PM_UNDEFINED)
 		newshf->funcdef->shf = newshf;
 	}
@@ -1201,7 +1202,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
 	/* return triggered */
 	retflag = 1;
     } else {
-	if (traperr && emulation != EMULATE_SH)
+	if (traperr && !EMULATION(EMULATE_SH))
 	    lastval = 1;
 	if (try_tryflag)
 	    errflag = traperr;
diff --git a/Src/subst.c b/Src/subst.c
index 42f880965..89e9e46eb 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1534,7 +1534,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	 * doesn't have parameter flags it might be neater to
 	 * handle this with the ^, =, ~ stuff, below.
 	 */
-	if ((c = *s) == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
+	if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) {
 	    hkeys = SCANPM_WANTKEYS;
 	    s++;
 	} else if (c == '(' || c == Inpar) {
diff --git a/Src/zsh.h b/Src/zsh.h
index 36755c719..159806f2c 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1067,6 +1067,7 @@ struct shfunc {
     char *filename;             /* Name of file located in */
     zlong lineno;		/* line number in above file */
     Eprog funcdef;		/* function definition    */
+    int emulation;		/* sticky emulation for function */
 };
 
 /* Shell function context types. */
@@ -1790,6 +1791,20 @@ struct histent {
 #define EMULATE_SH   (1<<3) /* Bourne shell */
 #define EMULATE_ZSH  (1<<4) /* `native' mode */
 
+/* Test for a shell emulation.  Use this rather than emulation directly. */
+#define EMULATION(X)	(emulation & (X))
+
+/* Return only base shell emulation field. */
+#define SHELL_EMULATION()	(emulation & ((1<<5)-1))
+
+/* Additional flags */
+
+#define EMULATE_FULLY (1<<5) /* "emulate -R" in effect */
+/*
+ * Higher bits are used in options.c, record lowest unused bit...
+ */
+#define EMULATE_UNUSED (1<<6)
+
 /* option indices */
 
 enum {
diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 57eea3806..dec809ea4 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -3,72 +3,176 @@
 %prep
 
   isset() { 
+    print -n "${1}: "
     if [[ -o $1 ]]; then print yes; else print no; fi
   }
   showopts() {
-      # Set for Bourne shell emulation
-      isset shwordsplit
-      # Set in native mode and unless "emulate -R" is in use
-      isset banghist
+    # Set for Bourne shell emulation
+    isset shwordsplit
+    # Set in native mode and unless "emulate -R" is in use
+    isset banghist
+  }
+  cshowopts() {
+    showopts
+    # Show a csh option, too
+    isset cshnullglob
   }
 
 %test
 
-  (showopts
+  (print Before
+  showopts
   fn() {
      emulate sh
   }
   fn
+   print After
   showopts)
 0:Basic use of emulate
->no
->yes
->yes
->yes
+>Before
+>shwordsplit: no
+>banghist: yes
+>After
+>shwordsplit: yes
+>banghist: yes
 
   fn() {
     emulate -L sh
+    print During
     showopts
   }
+  print Before
   showopts
   fn
+  print After
   showopts
 0:Use of emulate -L
->no
->yes
->yes
->yes
->no
->yes
+>Before
+>shwordsplit: no
+>banghist: yes
+>During
+>shwordsplit: yes
+>banghist: yes
+>After
+>shwordsplit: no
+>banghist: yes
 
-  (showopts
+  (print Before
+  showopts
   emulate -R sh
+  print After
   showopts)
 0:Use of emulate -R
->no
->yes
->yes
->no
+>Before
+>shwordsplit: no
+>banghist: yes
+>After
+>shwordsplit: yes
+>banghist: no
 
+  print Before
   showopts
-  emulate sh -c 'showopts'
+  emulate sh -c 'print During; showopts'
+  print After
   showopts
 0:Use of emulate -c
->no
->yes
->yes
->yes
->no
->yes
-
+>Before
+>shwordsplit: no
+>banghist: yes
+>During
+>shwordsplit: yes
+>banghist: yes
+>After
+>shwordsplit: no
+>banghist: yes
 
+  print Before
   showopts
-  emulate -R sh -c 'showopts'
+  emulate -R sh -c 'print During; showopts'
+  print After
   showopts
 0:Use of emulate -R -c
->no
->yes
->yes
->no
->no
->yes
+>Before
+>shwordsplit: no
+>banghist: yes
+>During
+>shwordsplit: yes
+>banghist: no
+>After
+>shwordsplit: no
+>banghist: yes
+
+  print Before
+  showopts
+  emulate -R sh -c 'shshowopts() { showopts; }'
+  print After definition
+  showopts
+  print In sticky emulation
+  shshowopts
+  print After sticky emulation
+  showopts
+0:Basic sticky function emulation
+>Before
+>shwordsplit: no
+>banghist: yes
+>After definition
+>shwordsplit: no
+>banghist: yes
+>In sticky emulation
+>shwordsplit: yes
+>banghist: no
+>After sticky emulation
+>shwordsplit: no
+>banghist: yes
+
+  print Before
+  cshowopts
+  emulate -R sh -c 'shshowopts() { cshowopts; }'
+  emulate csh -c 'cshshowopts() {
+    cshowopts
+    print In nested sh emulation
+    shshowopts
+  }'
+  print After definition
+  cshowopts
+  print In sticky csh emulation
+  cshshowopts
+  print After sticky emulation
+  cshowopts
+0:Basic sticky function emulation
+>Before
+>shwordsplit: no
+>banghist: yes
+>cshnullglob: no
+>After definition
+>shwordsplit: no
+>banghist: yes
+>cshnullglob: no
+>In sticky csh emulation
+>shwordsplit: no
+>banghist: yes
+>cshnullglob: yes
+>In nested sh emulation
+>shwordsplit: yes
+>banghist: no
+>cshnullglob: no
+>After sticky emulation
+>shwordsplit: no
+>banghist: yes
+>cshnullglob: no
+
+  isalp() { if [[ -o alwayslastprompt ]]; then print on; else print off; fi; }
+  emulate sh -c 'shfunc_inner() { setopt alwayslastprompt; }'
+  emulate csh -c 'cshfunc_inner() { setopt alwayslastprompt; }'
+  emulate sh -c 'shfunc_outer() {
+    unsetopt alwayslastprompt;
+    shfunc_inner;
+    isalp
+    unsetopt alwayslastprompt
+    cshfunc_inner
+    isalp
+  }'
+  shfunc_outer
+0:Sticky emulation not triggered if sticky emulation unchanged
+>on
+>off