summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--Doc/Zsh/builtins.yo4
-rw-r--r--Src/Modules/parameter.c2
-rw-r--r--Src/builtin.c50
-rw-r--r--Src/exec.c107
-rw-r--r--Src/hashtable.c9
-rw-r--r--Src/init.c44
-rw-r--r--Src/options.c18
-rw-r--r--Src/parse.c2
-rw-r--r--Src/signals.c5
-rw-r--r--Src/zsh.h30
-rw-r--r--Test/B07emulate.ztst46
12 files changed, 287 insertions, 41 deletions
diff --git a/ChangeLog b/ChangeLog
index 5a69f0174..4b1508a47 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2012-10-11 Peter Stephenson <p.w.stephenson@ntlworld.com>
+
+ * 30726: Doc/Zsh/builtins.yo, Src/builtin.c, Src/exec.c,
+ Src/hashtable.c, Src/init.c, Src/options.c, Src/parse.c,
+ Src/signals.c, Src/zsh.h, Src/Modules/parameter.c,
+ Test/B07emulate.ztst: extend 30722 to handle the case
+ where shell options passed to the emulate command need
+ propagating to sticky emulation.
+
2012-10-11 Peter Stephenson <pws@csr.com>
* 30724: Src/exec.c, Src/jobs.c: shell code optimised to use
@@ -260,5 +269,5 @@
*****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.5744 $
+* $Revision: 1.5745 $
*****************************************************
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index f7924a072..37319c9b0 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -462,6 +462,10 @@ sitem(4.)(The presence or absence of the tt(-R) switch 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.)
+sitem(5.)(Difference in shell options supplied in addition to the
+basic emulation also mean the sticky emulations are different, so for
+example `tt(emulate zsh -c)' and `tt(emulate zsh -o cbases -c)' are
+treated as distinct sticky emulations.)
endsitem()
)
findex(enable)
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index f19dfce4f..a029c9cb4 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -289,7 +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;
+ shfunc_set_sticky(shf);
if (!strncmp(name, "TRAP", 4) &&
(sn = getsignum(name + 4)) != -1) {
diff --git a/Src/builtin.c b/Src/builtin.c
index 5cb643f23..8a83df711 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2944,7 +2944,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
shf = (Shfunc) zshcalloc(sizeof *shf);
shf->node.flags = on;
shf->funcdef = mkautofn(shf);
- shf->emulation = sticky_emulation;
+ shfunc_set_sticky(shf);
shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
if (signum != -1) {
@@ -5007,11 +5007,15 @@ 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, savesticky_emulation, savehackchar;
+ int saveemulation, savehackchar;
int ret = 1, new_emulation;
- char saveopts[OPT_SIZE], new_opts[OPT_SIZE], savesticky_opts[OPT_SIZE];
+ char saveopts[OPT_SIZE], new_opts[OPT_SIZE];
char *cmd = 0;
const char *shname = *argv;
+ LinkList optlist;
+ LinkNode optnode;
+ Emulation_options save_sticky;
+ OptIndex *on_ptr, *off_ptr;
/* without arguments just print current emulation */
if (!shname) {
@@ -5055,7 +5059,8 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
memcpy(new_opts, opts, sizeof(opts));
savehackchar = keyboardhackchar;
emulate(shname, OPT_ISSET(ops,'R'), &new_emulation, new_opts);
- if (parseopts("emulate", &argv, new_opts, &cmd)) {
+ optlist = newlinklist();
+ if (parseopts("emulate", &argv, new_opts, &cmd, optlist)) {
ret = 1;
goto restore;
}
@@ -5081,15 +5086,40 @@ bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
} else
return 0;
- savesticky_emulation = sticky_emulation;
- sticky_emulation = emulation;
- memcpy(savesticky_opts, sticky_opts, sizeof(opts));
- memcpy(sticky_opts, opts, sizeof(opts));
+ save_sticky = sticky;
+ sticky = hcalloc(sizeof(*sticky));
+ sticky->emulation = emulation;
+ for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
+ /* Data is index into new_opts */
+ char *optptr = (char *)getdata(optnode);
+ if (*optptr)
+ sticky->n_on_opts++;
+ else
+ sticky->n_off_opts++;
+ }
+ if (sticky->n_on_opts)
+ on_ptr = sticky->on_opts =
+ zhalloc(sticky->n_on_opts * sizeof(*sticky->on_opts));
+ else
+ on_ptr = NULL;
+ if (sticky->n_off_opts)
+ off_ptr = sticky->off_opts = zhalloc(sticky->n_off_opts *
+ sizeof(*sticky->off_opts));
+ else
+ off_ptr = NULL;
+ for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
+ /* Data is index into new_opts */
+ char *optptr = (char *)getdata(optnode);
+ int optno = optptr - new_opts;
+ if (*optptr)
+ *on_ptr++ = optno;
+ else
+ *off_ptr++ = optno;
+ }
ret = eval(argv);
- sticky_emulation = savesticky_emulation;
+ sticky = save_sticky;
emulation = saveemulation;
memcpy(opts, saveopts, sizeof(opts));
- memcpy(sticky_opts, savesticky_opts, sizeof(opts));
restore:
keyboardhackchar = savehackchar;
inittyptab(); /* restore banghist */
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];
diff --git a/Src/hashtable.c b/Src/hashtable.c
index b472e40b9..ef187927b 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -888,6 +888,15 @@ freeshfuncnode(HashNode hn)
if (shf->funcdef)
freeeprog(shf->funcdef);
zsfree(shf->filename);
+ if (shf->sticky) {
+ if (shf->sticky->n_on_opts)
+ zfree(shf->sticky->on_opts,
+ shf->sticky->n_on_opts * sizeof(*shf->sticky->on_opts));
+ if (shf->sticky->n_off_opts)
+ zfree(shf->sticky->off_opts,
+ shf->sticky->n_off_opts * sizeof(*shf->sticky->off_opts));
+ zfree(shf->sticky, sizeof(*shf->sticky));
+ }
zfree(shf, sizeof(struct shfunc));
}
diff --git a/Src/init.c b/Src/init.c
index 8f6c0ec6d..6c2ba13e4 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -246,7 +246,7 @@ parseargs(char **argv, char **runscript)
opts[SHINSTDIN] = 0;
opts[SINGLECOMMAND] = 0;
- if (parseopts(NULL, &argv, opts, &cmd))
+ if (parseopts(NULL, &argv, opts, &cmd, NULL))
exit(1);
paramlist = znewlinklist();
@@ -277,15 +277,37 @@ parseargs(char **argv, char **runscript)
argzero = ztrdup(argzero);
}
+/* Insert into list in order of pointer value */
+
+/**/
+static void
+parseopts_insert(LinkList optlist, void *ptr)
+{
+ LinkNode node;
+
+ for (node = firstnode(optlist); node; incnode(node)) {
+ if (ptr < getdata(node)) {
+ insertlinknode(optlist, prevnode(node), ptr);
+ return;
+ }
+ }
+
+ addlinknode(optlist, ptr);
+}
+
/*
* Parse shell options.
* If nam is not NULL, this is called from a command; don't
* exit on failure.
+ *
+ * If optlist is not NULL, it used to form a list of pointers
+ * into new_opts indicating which options have been changed.
*/
/**/
mod_export int
-parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
+parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
+ LinkList optlist)
{
int optionbreak = 0;
int action, optno;
@@ -364,8 +386,12 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
restricted = action;
} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
WARN_OPTION("can't change option: %s", *argv);
- } else if (dosetopt(optno, action, !nam, new_opts) && nam) {
- WARN_OPTION("can't change option: %s", *argv);
+ } else {
+ if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ WARN_OPTION("can't change option: %s", *argv);
+ } else if (optlist) {
+ parseopts_insert(optlist, new_opts+optno);
+ }
}
break;
} else if (isspace(STOUC(**argv))) {
@@ -385,8 +411,12 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
restricted = action;
} else if ((optno == EMACSMODE || optno == VIMODE) && nam) {
WARN_OPTION("can't change option: %s", *argv);
- } else if (dosetopt(optno, action, !nam, new_opts) && nam) {
- WARN_OPTION("can't change option: -%c", **argv);
+ } else {
+ if (dosetopt(optno, action, !nam, new_opts) && nam) {
+ WARN_OPTION("can't change option: -%c", **argv);
+ } else if (optlist) {
+ parseopts_insert(optlist, new_opts+optno);
+ }
}
}
}
@@ -396,7 +426,7 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp)
if (*cmdp) {
if (!*argv) {
WARN_OPTION("string expected after -%s", *cmdp);
- exit(1);
+ return 1;
}
*cmdp = *argv++;
}
diff --git a/Src/options.c b/Src/options.c
index 87e9abe2d..80fef3d00 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -35,29 +35,21 @@
/**/
mod_export int emulation;
-/* current sticky emulation: 0 means none */
+/* current sticky emulation: sticky = NULL means none */
/**/
-mod_export int sticky_emulation;
+mod_export Emulation_options sticky;
/* the options; e.g. if opts[SHGLOB] != 0, SH_GLOB is turned on */
-
+
/**/
mod_export char opts[OPT_SIZE];
-/*
- * the options that need setting for current sticky emulation, if any:
- * same format as opts.
- */
-
-/**/
-mod_export char sticky_opts[OPT_SIZE];
-
/* Option name hash table */
/**/
mod_export HashTable optiontab;
-
+
/* The canonical option name table */
#define OPT_CSH EMULATE_CSH
@@ -786,7 +778,7 @@ dosetopt(int optno, int value, int force, char *new_opts)
return -1;
#endif /* GETPWNAM_FAKED */
} else if ((optno == EMACSMODE || optno == VIMODE) && value) {
- if (sticky_emulation)
+ if (sticky && sticky->emulation)
return -1;
zleentry(ZLE_CMD_SET_KEYMAP, optno);
new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
diff --git a/Src/parse.c b/Src/parse.c
index 8d2878cd7..096faa072 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -3482,7 +3482,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;
+ shf->sticky = NULL;
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 ad688094b..046ee6a4a 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -755,7 +755,10 @@ 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->sticky) {
+ newshf->sticky = sticky_emulation_dup(shf->sticky, 0);
+ } else
+ newshf->sticky = 0;
if (shf->node.flags & PM_UNDEFINED)
newshf->funcdef->shf = newshf;
}
diff --git a/Src/zsh.h b/Src/zsh.h
index fee27ac10..e51572bcf 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -407,6 +407,7 @@ typedef struct cmdnam *Cmdnam;
typedef struct complist *Complist;
typedef struct conddef *Conddef;
typedef struct dirsav *Dirsav;
+typedef struct emulation_options *Emulation_options;
typedef struct features *Features;
typedef struct feature_enables *Feature_enables;
typedef struct funcstack *Funcstack;
@@ -1099,7 +1100,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 */
+ Emulation_options sticky; /* sticky emulation definitions, if any */
};
/* Shell function context types. */
@@ -2104,6 +2105,12 @@ enum {
OPT_SIZE
};
+/*
+ * Size required to fit an option number.
+ * If OPT_SIZE goes above 256 this will need to expand.
+ */
+typedef unsigned char OptIndex;
+
#undef isset
#define isset(X) (opts[X])
#define unset(X) (!opts[X])
@@ -2112,6 +2119,27 @@ enum {
#define jobbing (isset(MONITOR))
#define islogin (isset(LOGINSHELL))
+/*
+ * Record of emulation and options that need to be set
+ * for a full "emulate".
+ */
+struct emulation_options {
+ /* The emulation itself */
+ int emulation;
+ /* The number of options in on_opts. */
+ int n_on_opts;
+ /* The number of options in off_opts. */
+ int n_off_opts;
+ /*
+ * Array of options to be turned on.
+ * Only options specified explicitly in the emulate command
+ * are recorded. Null if n_on_opts is zero.
+ */
+ OptIndex *on_opts;
+ /* Array of options to be turned off, similar. */
+ OptIndex *off_opts;
+};
+
/***********************************************/
/* Definitions for terminal and display control */
/***********************************************/
diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 569640bb4..315206a20 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -201,3 +201,49 @@
emulate zsh -o fixallmybugs 'print This was executed, bad'
1:emulate -c with incorrect options
?(eval):emulate:1: no such option: fixallmybugs
+
+ emulate zsh -c '
+ func() { [[ -o extendedglob ]] || print extendedglob is off }
+ '
+ func
+ emulate zsh -o extendedglob -c '
+ func() { [[ -o extendedglob ]] && print extendedglob is on }
+ '
+ func
+0:options specified alongside emulation are also sticky
+>extendedglob is off
+>extendedglob is on
+
+ emulate zsh -o extendedglob -c '
+ func_inner() { setopt nobareglobqual }
+ '
+ emulate zsh -o extendedglob -c '
+ func_outer() {
+ func_inner
+ [[ -o bareglobqual ]] || print bareglobqual was turned off
+ [[ -o extendedglob ]] && print extendedglob is on, though
+ }
+ '
+ [[ -o extendedglob ]] || print extendedglob is initially off
+ func_outer
+0:options propagate between identical emulations
+>extendedglob is initially off
+>bareglobqual was turned off
+>extendedglob is on, though
+
+ emulate zsh -o extendedglob -c '
+ func_inner() { setopt nobareglobqual }
+ '
+ emulate zsh -o extendedglob -o cbases -c '
+ func_outer() {
+ func_inner
+ [[ -o bareglobqual ]] && print bareglobqual is still on
+ [[ -o extendedglob ]] && print extendedglob is on, too
+ }
+ '
+ [[ -o extendedglob ]] || print extendedglob is initially off
+ func_outer
+0:options do not propagate between different emulations
+>extendedglob is initially off
+>bareglobqual is still on
+>extendedglob is on, too