Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
LOCAL_VARS option ?
- X-seq: zsh-workers 40383
- From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: LOCAL_VARS option ?
- Date: Thu, 19 Jan 2017 06:54:08 +0000
- Dkim-signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= daniel.shahaf.name; h=content-type:date:from:message-id :mime-version:subject:to:x-me-sender:x-me-sender:x-sasl-enc :x-sasl-enc; s=mesmtp; bh=jqommBdZqgdJ7hVnOohf5GUeWjk=; b=nUsGnR z+km6uh6SSyjV0MzsPJStJOd9SRPXNzwkut+GeAH5f8EHexy5ugznepS3btfH/gA qvuBaPwfc/kFC3JM2WJ1wvvUYPETf81FWhlLHjzBklx/axz+JXLHtXFY+KFEEifu L0I81vvs29PVn9R99gHT+gymKFDznkDhfyhbE=
- Dkim-signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=content-type:date:from:message-id :mime-version:subject:to:x-me-sender:x-me-sender:x-sasl-enc :x-sasl-enc; s=smtpout; bh=jqommBdZqgdJ7hVnOohf5GUeWjk=; b=kt26b QGwcgviN4EYOzlP4bP7ROQsO4LuEKHbQDu3TYnUG1+gao7qdjnTsJGZmHFiS9xSU PyoIdIb+sLbUfJr+/UDDUYcqV7FGJtRzMJprI8wgBWPEKp5n8rlBjvWyPnjNCu/N Oue1O1PaTxHGtQ03vPRN4Qhybhmj7P+JuVnsHI=
- List-help: <mailto:zsh-workers-help@zsh.org>
- List-id: Zsh Workers List <zsh-workers.zsh.org>
- List-post: <mailto:zsh-workers@zsh.org>
- Mailing-list: contact zsh-workers-help@xxxxxxx; run by ezmlm
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?
[[[
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
Messages sorted by:
Reverse Date,
Date,
Thread,
Author