1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-12-07 06:02:21 +01:00

26546, 26556: sticky emulation for functions defined in emulate ... -c ...

environments, plus documentation
This commit is contained in:
Peter Stephenson 2009-02-11 20:42:15 +00:00
parent 0d02cf343e
commit c7d8b0dfb8
16 changed files with 322 additions and 69 deletions

View file

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

View file

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

View file

@ -78,7 +78,7 @@ boot_(UNUSED(Module m))
0 };
const char **sp;
if (emulation != EMULATE_ZSH)
if (!EMULATION(EMULATE_ZSH))
return 0;
if (!dotdir) {

View file

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

View file

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

View file

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

View file

@ -588,7 +588,7 @@ mod_export HashTable cmdnamtab;
/**/
mod_export char **pathchecked;
/* Create a new command hash table */
/**/

View file

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

View file

@ -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\","

View file

@ -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))
@ -475,6 +483,14 @@ setemulate(HashNode hn, int fully)
opts[on->optno] = defset(on);
}
/**/
void
installemulation(void)
{
scanhashtable(optiontab, 0, 0, 0, setemulate,
!!(emulation & EMULATE_FULLY));
}
/**/
void
emulate(const char *zsh_name, int fully)
@ -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 */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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