summary refs log tree commit diff
path: root/Src/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c107
1 files changed, 101 insertions, 6 deletions
diff --git a/Src/exec.c b/Src/exec.c
index b2224cfb3..74b14d54d 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4267,7 +4267,7 @@ execfuncdef(Estate state, UNUSED(int do_exec))
 	shf->node.flags = 0;
 	shf->filename = ztrdup(scriptfilename);
 	shf->lineno = lineno;
-	shf->emulation = sticky_emulation;
+	shfunc_set_sticky(shf);
 
 	if (!names) {
 	    /*
@@ -4319,6 +4319,46 @@ execfuncdef(Estate state, UNUSED(int do_exec))
     return ret;
 }
 
+/* Duplicate a sticky emulation */
+
+/**/
+
+mod_export Emulation_options
+sticky_emulation_dup(Emulation_options src, int useheap)
+{
+    Emulation_options newsticky = useheap ?
+	hcalloc(sizeof(*src)) : zshcalloc(sizeof(*src));
+    newsticky->emulation = src->emulation;
+    if (src->n_on_opts) {
+	size_t sz = src->n_on_opts * sizeof(*src->on_opts);
+	newsticky->n_on_opts = src->n_on_opts;
+	newsticky->on_opts = useheap ? zhalloc(sz) : zalloc(sz);
+	memcpy(newsticky->on_opts, src->on_opts, sz);
+    }
+    if (src->n_off_opts) {
+	size_t sz = src->n_off_opts * sizeof(*src->off_opts);
+	newsticky->n_off_opts = src->n_off_opts;
+	newsticky->off_opts = useheap ? zhalloc(sz) : zalloc(sz);
+	memcpy(newsticky->off_opts, src->off_opts, sz);
+    }
+
+    return newsticky;
+}
+
+/* Set the sticky emulation attributes for a shell function */
+
+/**/
+
+mod_export void
+shfunc_set_sticky(Shfunc shf)
+{
+    if (sticky)
+	shf->sticky = sticky_emulation_dup(sticky, 0);
+    else
+	shf->sticky = NULL;
+}
+
+
 /* Main entry point to execute a shell function. */
 
 /**/
@@ -4479,6 +4519,45 @@ loadautofn(Shfunc shf, int fksh, int autol)
 }
 
 /*
+ * Check if a sticky emulation differs from the current one.
+ */
+
+/**/
+
+int sticky_emulation_differs(Emulation_options sticky2)
+{
+    /* If no new sticky emulation, not a different emulation */
+    if (!sticky2)
+	return 0;
+    /* If no current sticky emulation, different */
+    if (!sticky)
+	return 1;
+    /* If basic emulation different, different */
+    if (sticky->emulation != sticky2->emulation)
+	return 1;
+    /* If differing numbers of options, different */
+    if (sticky->n_on_opts != sticky2->n_on_opts ||
+	sticky->n_off_opts != sticky2->n_off_opts)
+	return 1;
+    /*
+     * We need to compare option arrays, if non-null.
+     * We made parseopts() create the list of options in option
+     * order to make this easy.
+     */
+    /* If different options turned on, different */
+    if (sticky->n_on_opts &&
+	memcmp(sticky->on_opts, sticky2->on_opts,
+	       sticky->n_on_opts * sizeof(*sticky->on_opts)) != 0)
+	return 1;
+    /* If different options turned on, different */
+    if (sticky->n_off_opts &&
+	memcmp(sticky->off_opts, sticky2->off_opts,
+	       sticky->n_off_opts * sizeof(*sticky->off_opts)) != 0)
+	return 1;
+    return 0;
+}
+
+/*
  * execute a shell function
  *
  * name is the name of the function
@@ -4507,10 +4586,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
     char *name = shfunc->node.nam;
     int flags = shfunc->node.flags, ooflags;
     char *fname = dupstring(name);
-    int obreaks, saveemulation, savesticky_emulation, restore_sticky;
+    int obreaks, saveemulation, restore_sticky;
     Eprog prog;
     struct funcstack fstack;
     static int oflags;
+    Emulation_options save_sticky = NULL;
 #ifdef MAX_FUNCTION_DEPTH
     static int funcdepth;
 #endif
@@ -4548,9 +4628,9 @@ 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;
+    save_sticky = sticky;
 
-    if (shfunc->emulation && sticky_emulation != shfunc->emulation) {
+    if (sticky_emulation_differs(shfunc->sticky)) {
 	/*
 	 * Function is marked for sticky emulation.
 	 * Enable it now.
@@ -4563,9 +4643,24 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	 *
 	 * This propagates the sticky emulation to subfunctions.
 	 */
-	emulation = sticky_emulation = shfunc->emulation;
+	sticky = sticky_emulation_dup(shfunc->sticky, 1);
+	emulation = sticky->emulation;
 	restore_sticky = 1;
 	installemulation(emulation, opts);
+	if (sticky->n_on_opts) {
+	    OptIndex *onptr;
+	    for (onptr = sticky->on_opts;
+		 onptr < sticky->on_opts + sticky->n_on_opts;
+		 onptr++)
+		opts[*onptr] = 1;
+	}
+	if (sticky->n_off_opts) {
+	    OptIndex *offptr;
+	    for (offptr = sticky->off_opts;
+		 offptr < sticky->off_opts + sticky->n_off_opts;
+		 offptr++)
+		opts[*offptr] = 0;
+	}
     } else
 	restore_sticky = 0;
 
@@ -4674,7 +4769,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	 */
 	memcpy(opts, saveopts, sizeof(opts));
 	emulation = saveemulation;
-	sticky_emulation = savesticky_emulation;
+	sticky = save_sticky;
     } else if (isset(LOCALOPTIONS)) {
 	/* restore all shell options except PRIVILEGED and RESTRICTED */
 	saveopts[PRIVILEGED] = opts[PRIVILEGED];