From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 834 invoked by alias); 19 Jan 2017 06:57:57 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 40383 Received: (qmail 16274 invoked from network); 19 Jan 2017 06:57:57 -0000 X-Qmail-Scanner-Diagnostics: from out1-smtp.messagingengine.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(66.111.4.25):SA:0(-0.7/5.0):. Processed in 1.474117 secs); 19 Jan 2017 06:57:57 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-0.7 required=5.0 tests=RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.1 X-Envelope-From: d.s@daniel.shahaf.name X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: none (ns1.primenet.com.au: domain at daniel.shahaf.name does not designate permitted sender hosts) 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= X-ME-Sender: X-Sasl-enc: rxVJh7C35QL7xqBYLLfuFy1r6okDfVOifHfqfYbSZAWZ 1484809064 Date: Thu, 19 Jan 2017 06:54:08 +0000 From: Daniel Shahaf To: zsh-workers@zsh.org Subject: LOCAL_VARS option ? Message-ID: <20170119065408.GA5534@fujitsu.shahaf.local2> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="LQksG6bCIzRHxTLp" Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) --LQksG6bCIzRHxTLp Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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? --LQksG6bCIzRHxTLp Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="local_vars-v1-wip.diff" [[[ 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) )( 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 --LQksG6bCIzRHxTLp--