1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-01-01 05:16:05 +01:00

30726: make shell options passed to emulate stick along with the emulation

This commit is contained in:
Peter Stephenson 2012-10-11 20:14:01 +00:00
parent ad92cb3203
commit 4b86cc48f7
12 changed files with 287 additions and 41 deletions

View file

@ -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 $
*****************************************************

View file

@ -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)

View file

@ -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) {

View file

@ -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 */

View file

@ -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. */
/**/
@ -4478,6 +4518,45 @@ loadautofn(Shfunc shf, int fksh, int autol)
return shf;
}
/*
* 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
*
@ -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];

View file

@ -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));
}

View file

@ -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++;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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 */
/***********************************************/

View file

@ -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