summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-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
9 files changed, 227 insertions, 40 deletions
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 */
/***********************************************/