From: Daniel Shahaf <d.s@daniel.shahaf.name>
To: zsh-workers@zsh.org
Subject: LOCAL_VARS option ?
Date: Thu, 19 Jan 2017 06:54:08 +0000 [thread overview]
Message-ID: <20170119065408.GA5534@fujitsu.shahaf.local2> (raw)
[-- Attachment #1: Type: text/plain, Size: 339 bytes --]
Phil suggested on IRC a LOCAL_VARS option that has the effect of making
all newly-declared variables local; e.g.,
% unset x y
% () { setopt localvars; x=42; typeset -g y=43 }
% echo $+x $+y
0 1
%
I'm attaching a proof of concept patch (work in progress; see top of the
attachment for known issues), but WDYT of the the general concept?
[-- Attachment #2: local_vars-v1-wip.diff --]
[-- Type: text/x-diff, Size: 9511 bytes --]
[[[
WIP: LOCAL_VARS option
Proof of concept. Known issues:
1. Interaction with 'emulate -L' (see TODO below).
2. There's a block in assignstrvalue() that I don't know whether needs
changing or not (see TODO below).
3. Currently, LOCAL_VARS overrides 'typeset -g', which is silly. This
shouldn't be hard to fix by having bin_typeset() propagate a "leave
PM_LOCAL unset" flag to createparam(). (Currently, createparam() is
called with flags=PM_LOCAL for 'typeset' and with flags=0 both from
'typeset -g' and from callers other than bin_typeset().)
Finally, I'm not that familiar with the *param() C functions so I could
easily have overlooked something.
]]]
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 3a3130a..ac93810 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -552,6 +552,8 @@ function, if any; normally these options are turned off in all emulation
modes except tt(ksh). The tt(-L) switch is mutually exclusive with the
use of tt(-c) in var(flags).
+em(TODO): should tt(emulate -L) set tt(LOCAL_VARS)?
+
If there is a single argument and the tt(-l) switch is given, the
options that would be set or unset (the latter indicated with the prefix
`tt(no)') are listed. tt(-l) can be combined with tt(-L) or tt(-R) and
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 434b710..46e5de5 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1705,8 +1705,9 @@ item(tt(LOCAL_OPTIONS) <K>)(
If this option is set at the point of return from a shell function,
most options (including this one) which were in force upon entry to
the function are restored; options that are not restored are
-tt(PRIVILEGED) and tt(RESTRICTED). Otherwise, only this option,
-and the tt(LOCAL_LOOPS), tt(XTRACE) and tt(PRINT_EXIT_VALUE) options are
+tt(PRIVILEGED) and tt(RESTRICTED). Otherwise, only the options
+tt(LOCAL_LOOPS), tt(LOCAL_OPTIONS), tt(LOCAL_VARS), tt(PRINT_EXIT_VALUE),
+and tt(XTRACE) are
restored. Hence if this is explicitly unset by a shell function the
other options in force at the point of return will remain so.
A shell function can also guarantee itself a known shell configuration
@@ -1745,6 +1746,18 @@ fn+LPAR()RPAR() { setopt localtraps; trap '' INT; sleep 3; })
will restore normal handling of tt(SIGINT) after the function exits.
)
+pindex(LOCAL_VARS)
+pindex(NO_LOCAL_VARS)
+pindex(LOCALVARS)
+pindex(NOLOCALVARS)
+item(tt(LOCAL_VARS))(
+Whilst this option is set, any shell construct that declares a new shell
+parameter will default to making that parameter local to the current function.
+Declaring a parameter that is visible to outer scopes is still possible with
+tt(typeset -g) and tt(typeset -x).
+
+This option overrides tt(ALL_EXPORT).
+)
pindex(MULTI_FUNC_DEF)
pindex(NO_MULTI_FUNC_DEF)
pindex(MULTIFUNCDEF)
diff --git a/Src/builtin.c b/Src/builtin.c
index 3b5b2c4..d4b290e 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5779,6 +5779,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
if (opt_L)
cmdopts[LOCALOPTIONS] = cmdopts[LOCALTRAPS] =
cmdopts[LOCALPATTERNS] = 1;
+ /* XXX LOCALVARS */
if (opt_l) {
list_emulate_options(cmdopts, opt_R);
return 0;
@@ -5831,6 +5832,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
} else {
if (opt_L)
opts[LOCALOPTIONS] = opts[LOCALTRAPS] = opts[LOCALPATTERNS] = 1;
+ /* XXX LOCALVARS */
return 0;
}
diff --git a/Src/exec.c b/Src/exec.c
index d3538c3..f812205 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2429,7 +2429,6 @@ addvars(Estate state, Wordcode pc, int addflags)
if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) {
Param pm;
char *val;
- int allexp;
if (empty(vl))
val = ztrdup("");
@@ -2442,6 +2441,8 @@ addvars(Estate state, Wordcode pc, int addflags)
fputc(' ', xtrerr);
}
if ((addflags & ADDVAR_EXPORT) && !strchr(name, '[')) {
+ int old_LOCALVARS;
+ int allexp;
if ((addflags & ADDVAR_RESTRICT) && isset(RESTRICTED) &&
(pm = (Param) paramtab->removenode(paramtab, name)) &&
(pm->node.flags & PM_RESTRICTED)) {
@@ -2455,11 +2456,14 @@ addvars(Estate state, Wordcode pc, int addflags)
STTYval = ztrdup(val);
}
allexp = opts[ALLEXPORT];
+ old_LOCALVARS = opts[LOCALVARS];
opts[ALLEXPORT] = 1;
+ opts[LOCALVARS] = 0;
if (isset(KSHARRAYS))
unsetparam(name);
pm = assignsparam(name, val, myflags);
opts[ALLEXPORT] = allexp;
+ opts[LOCALVARS] = old_LOCALVARS;
} else
pm = assignsparam(name, val, myflags);
if (errflag) {
@@ -5532,6 +5536,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
opts[XTRACE] = saveopts[XTRACE];
opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
+ opts[LOCALVARS] = saveopts[LOCALVARS];
opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
}
diff --git a/Src/options.c b/Src/options.c
index 4729ba5..ef7c91c 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -186,6 +186,7 @@ static struct optname optns[] = {
{{NULL, "localloops", OPT_EMULATE}, LOCALLOOPS},
{{NULL, "localpatterns", OPT_EMULATE}, LOCALPATTERNS},
{{NULL, "localtraps", OPT_EMULATE|OPT_KSH}, LOCALTRAPS},
+{{NULL, "localvars", 0}, LOCALVARS},
{{NULL, "login", OPT_SPECIAL}, LOGINSHELL},
{{NULL, "longlistjobs", 0}, LONGLISTJOBS},
{{NULL, "magicequalsubst", OPT_EMULATE}, MAGICEQUALSUBST},
diff --git a/Src/params.c b/Src/params.c
index 946fdd1..30a6027 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -975,13 +975,22 @@ createparam(char *name, int flags)
paramtab->addnode(paramtab, ztrdup(name), pm);
}
- if (isset(ALLEXPORT) && !(flags & PM_HASHELEM))
+ if (isset(ALLEXPORT) && !(flags & PM_HASHELEM) &&
+ unset(LOCALVARS))
flags |= PM_EXPORTED;
} else {
pm = (Param) hcalloc(sizeof *pm);
pm->node.nam = nulstring;
}
- pm->node.flags = flags & ~PM_LOCAL;
+ pm->node.flags = flags;
+ if ((flags & PM_HASHELEM) || (flags & PM_EXPORTED))
+ pm->node.flags &= ~PM_LOCAL;
+ else if (isset(LOCALVARS)) {
+ pm->node.flags |= PM_LOCAL;
+ pm->level = locallevel;
+ }
+ else
+ pm->node.flags &= ~PM_LOCAL;
if(!(pm->node.flags & PM_SPECIAL))
assigngetset(pm);
@@ -2581,10 +2590,14 @@ assignstrvalue(Value v, char *val, int flags)
}
break;
}
+ /* TODO LOCALVARS: should isset(LOCALVARS) be checked here?
+ * (This block updates $PWD during cd.)
+ */
if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) &&
- !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
+ (unset(ALLEXPORT) || (v->pm->node.flags & PM_HASHELEM))) ||
(v->pm->node.flags & PM_ARRAY) || v->pm->ename)
return;
+ DPUTS1(0, "exporting %s", v->pm->node.nam);
export_param(v->pm);
}
@@ -4658,6 +4671,11 @@ pipestatsetfn(UNUSED(Param pm), char **x)
numpipestats = 0;
}
+/*
+ * If the shell scalar parameter s is exported, then set the corresponding
+ * environment variable to the array 't' joined by the tied array's joinchar.
+ */
+
/**/
void
arrfixenv(char *s, char **t)
diff --git a/Src/zsh.h b/Src/zsh.h
index deefdba..3ab0415 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2334,6 +2334,7 @@ enum {
LOCALOPTIONS,
LOCALPATTERNS,
LOCALTRAPS,
+ LOCALVARS,
LOGINSHELL,
LONGLISTJOBS,
MAGICEQUALSUBST,
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index 45df9f5..c9dc2bc 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -1213,3 +1213,50 @@
?(anon):4: `break' active at end of function scope
?(anon):4: `break' active at end of function scope
?(anon):4: `break' active at end of function scope
+
+ (unset x; () { x=42 }; echo "scalar born, unset: $+x")
+ (unset x; () { setopt localvars; x=42 }; echo "scalar born, set: $+x")
+ (local x; () { x=42 }; echo "scalar exists, unset: $+x")
+ (local x; () { setopt localvars; x=42 }; echo "scalar exists, set: $+x")
+ (unset x; () { setopt localvars; x=42 sh -c 'printf %s $x'; }; echo " in child, and now: $+x")
+0:LOCAL_VARS, scalars
+>scalar born, unset: 1
+>scalar born, set: 0
+>scalar exists, unset: 1
+>scalar exists, set: 1
+>42 in child, and now: 0
+
+ (unset x; () { x[5]=42 }; echo "array born, unset: $+x")
+ (unset x; () { setopt localvars; x[5]=42 }; echo "array born, set: $+x")
+ (typeset -a x; () { x[5]=42 }; echo "array exists, set: $+x ${+x[5]}")
+ (typeset -a x; () { setopt localvars; x[5]=42 }; echo "array exists, unset: $+x ${+x[5]}")
+0:LOCAL_VARS, classic arrays
+>array born, unset: 1
+>array born, set: 0
+>array exists, set: 1 1
+>array exists, unset: 1 1
+
+ (unset x; () { : ${(AA)=x::=foo bar}; }; echo "assoc born, unset: $+x")
+ (unset x; () { setopt localvars; : ${(AA)=x::=foo bar}; }; echo "assoc born, set: $+x")
+ (typeset -A x; () { x[foo]=42 }; echo "assoc exists, unset: $+x ${+x[foo]}")
+ (typeset -A x; () { setopt localvars; x[foo]=42 }; echo "assoc exists, set: $+x ${+x[foo]}")
+0:LOCAL_VARS, associative arrays
+>assoc born, unset: 1
+>assoc born, set: 0
+>assoc exists, unset: 1 1
+>assoc exists, set: 1 1
+
+# don't use LOCAL_TRAPS when testing it
+ () {
+ local flags
+ for flags in {,a,A}{g,x}; do
+ (unset x; () { typeset -$flags x; setopt localvars; }; echo $+x)
+ done
+ }
+0:LOCAL_VARS with -g/-x
+>1
+>1
+>1
+>1
+>1
+>1
next reply other threads:[~2017-01-19 6:57 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20170119070023epcas3p17d787fb31e7c04d5bcf2231020769b5f@epcas3p1.samsung.com>
2017-01-19 6:54 ` Daniel Shahaf [this message]
2017-01-19 9:43 ` Jens Elkner
2017-01-19 9:45 ` Peter Stephenson
2017-01-19 15:47 ` Bart Schaefer
2017-01-19 16:08 ` Peter Stephenson
2017-01-20 5:01 ` Bart Schaefer
2017-01-20 17:19 ` Peter Stephenson
2017-01-22 18:45 ` Bart Schaefer
2017-01-22 19:00 ` Peter Stephenson
2017-01-23 10:09 ` Peter Stephenson
2017-01-23 11:20 ` Daniel Shahaf
2017-01-23 11:37 ` Peter Stephenson
2017-01-25 5:50 ` Daniel Shahaf
2017-01-25 9:24 ` Peter Stephenson
2017-01-25 19:32 ` Daniel Shahaf
2017-01-25 21:50 ` Bart Schaefer
2017-01-29 21:21 ` Daniel Shahaf
2017-01-26 19:43 ` Peter Stephenson
2017-01-26 20:04 ` Peter Stephenson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170119065408.GA5534@fujitsu.shahaf.local2 \
--to=d.s@daniel.shahaf.name \
--cc=zsh-workers@zsh.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/zsh/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).