* "typeset -p" and no_GLOBAL_EXPORT, other misc. @ 2024-03-12 4:13 Bart Schaefer 2024-03-12 8:49 ` Stephane Chazelas 0 siblings, 1 reply; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 4:13 UTC (permalink / raw) To: Zsh hackers list [-- Attachment #1: Type: text/plain, Size: 5238 bytes --] I alluded to this on zsh-users. If you "emulate ksh" and then attempt "typeset -p" (especially "typeset -p -m") the results are somewhat unpredictable, but among the possible effects is that instead of printing parameter names/values, typeset instead attempts to create a local name for every existing global name. In part this invovles the GLOBAL_EXPORT option which is set by default in zsh mode. When it is unset, the PM_LOCAL flag is turned on in cases where it should not be, or at least in cases where typeset_single() doesn't know how to deal with it and bypasses the value printing code to fall through to the assignment / attribute modifying code. It doesn't actually succeed in doing anything at all, but if compiled with ZSH_DEBUG it spits out a lot of repetitions of BUG: -p not handled The doc for GLOBAL_EXPORT says: If this option is set, passing the -x flag to the builtins declare, float, integer, readonly and typeset (but not local) will also set the -g flag; hence parameters exported to the environment will not be made local to the enclosing function, unless they were already or the flag +g is given explicitly. If the option is unset, exported parameters will be made local in just the same way as any other parameter. The code that implements this is: if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || OPT_PLUS(ops,'g') || *name == 'l' || (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) on |= PM_LOCAL; If we unroll that and apply a little boolean algebra: if (*name == 'l' || OPT_PLUS(ops,'g')) on |= PM_LOCAL; else if (OPT_ISSET(ops,'g')) /* Do nothing */; /* Or strangely: on &= ~PM_LOCAL */ else if (!isset(GLOBALEXPORT)) on |= PM_LOCAL; else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m'))) on |= PM_LOCAL; This demonstrates that GLOBALEXPORT (when not set) affects a lot more than just the -x flag. In the "strangely" branch noted above, PM_LOCAL is often still set, but it's state doesn't seem to matter. Anyway, to make this almost match the doc, I can rearrange it to be: if (*name == 'l' || OPT_PLUS(ops,'g')) on |= PM_LOCAL; else { if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m'))) on |= PM_LOCAL; else if (OPT_MINUS(ops,'x')) { if (isset(GLOBALEXPORT)) ops->ind['g'] = 1; else if (locallevel) on |= PM_LOCAL; } if (OPT_MINUS(ops,'g')) on &= ~PM_LOCAL; } This fixes most cases but it's still possible to generate the "BUG" message by explicitly enabling PM_LOCAL: % () { typeset -p +g -m \* } And in that case it somehow wipes out all the prompt-related parameters (PS1, PS2, etc.). The answer to that seems to be to never go past the "if (usepm)" branch in typeset_single() when the 'p' option is set, but that leaves cases that seem like they should work but where "usepm" is turned off too soon. So I've attempted to fix that too. The remaining thing, which I have not been able to fix, is the tangled meanings of [+|-][pgm] ... some combinations that it seems should display something display nothing, and some combinations display what seems like too much. This is because some decisions are made in bin_typeset() and others are deferred to typeset_single(). For example: % () { typeset +m f\* } array tied FIGNORE fignore array tied FPATH fpath undefined funcfiletrace undefined funcsourcetrace undefined funcstack undefined functions undefined functions_source undefined functrace But add -p and (trimming some long output): % () { typeset -p +m f\* } typeset -g -aT FIGNORE fignore=( ) typeset -g -aT FPATH fpath=( /usr/local/share/zsh/... ) That works exactly the same with -p -m which I guess is OK. However ... % () { local foo=bar; typeset -p -m f\* } typeset -g -aT FIGNORE fignore=( ) typeset -g -aT FPATH fpath=( /usr/local/share/zsh/... ) typeset foo=bar % () { local foo=bar; typeset -p +m f\* } typeset -g -aT FIGNORE fignore=( ) typeset foo=bar typeset -g -aT FPATH fpath=( /usr/local/share/zsh/... ) The order of printing changes between -/+ if there's a local that matches? Those two cases also don't matter for +p with either state of m, but then there's [+/-]g ... it seems like (inside a function where -g matters): typeset -p -g ... should print only globals and skip locals, but that doesn't happen. Also typeset +p +g ... should have the same effect, but it does something else. And typeset -p +g ... should print only locals, but it prints everything except in typeset -p +g -m ... which does print only locals, whereas typeset -p +g +m ... prints nothing. And you'd think that typeset +p -g ... would be the same as -p +g, but in fact it also prints everything (ignoring -m entirely), but becomes the same if +m is added. On top of this are differences between [-/+]m with and without a pattern argument. To even start fixing this mess, we'd have to explain what a function considers "local" and what "global". Are all "inherited" scopes global, so local is the current scope only? Gaah. Anyway the attached at least prevents munging the parameter space when the intention is only to display it. [-- Attachment #2: typeset+-pgm.txt --] [-- Type: text/plain, Size: 278 bytes --] diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst index d6a5588df..b9a2f9f37 100644 --- a/Test/D10nofork.ztst +++ b/Test/D10nofork.ztst @@ -7,6 +7,8 @@ purr() { print -r -- "$@" } purl() { print -rl -- "$@" } + setopt ignoreclosebraces + %test REPLY=OUTER ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 4:13 "typeset -p" and no_GLOBAL_EXPORT, other misc Bart Schaefer @ 2024-03-12 8:49 ` Stephane Chazelas 2024-03-12 18:32 ` Bart Schaefer 0 siblings, 1 reply; 13+ messages in thread From: Stephane Chazelas @ 2024-03-12 8:49 UTC (permalink / raw) To: Bart Schaefer; +Cc: Zsh hackers list 2024-03-11 21:13:32 -0700, Bart Schaefer: [...] > To even start fixing this mess, we'd have to explain what a function > considers "local" and what "global". Are all "inherited" scopes > global, so local is the current scope only? Gaah. [...] IMO, typeset -g should only mean: don't make it local, do not instantiate a new variable in the current scope. And we certainly don't want bash's typeset -g behaviour where it means *the outermost scope*, which in shells with dynamic scoping is essentially what "global" means, but it's not particularly useful to single it out. For instance, in bash: bash-5.3$ integer() { typeset -gi -- "$@"; } bash-5.3$ integer i=1+1 bash-5.3$ echo $i 2 fine, but: bash-5.3$ f() { local i; integer i=2+2; echo "$i"; } bash-5.3$ f bash-5.3$ echo $i 4 That "integer" changed the type of the global (outer-most) i variable instead of that of its caller. IOW, most of the usages of "typeset -g" like with that "integer" above is when you want to change attributes of a variable (set the type with typeset which bash renamed to declare) without wanting to make it local, like export or readonly do in Bourne-like shells (except zsh for the latter), not change the type of the variable in the outermost scope which I can't think why you would want to. In mksh or yash, readonly var is like typeset -gr var. In bash, readonly var is like mksh/yash's typeset -gr var, but unlike its own typeset -gr: ~$ bash -c 'b() { local a=1; a; echo $a; }; a() { readonly a=2; echo $a; }; a=0; b; echo $a' 2 2 0 ~$ bash -c 'b() { local a=1; a; echo $a; }; a() { typeset -gr a=2; echo $a; }; a=0; b; echo $a' 1 1 2 ~$ mksh -c 'b() { local a=1; a; echo $a; }; a() { readonly a=2; echo $a; }; a=0; b; echo $a' 2 2 0 ~$ mksh -c 'b() { local a=1; a; echo $a; }; a() { typeset -gr a=2; echo $a; }; a=0; b; echo $a' 2 2 0 In zsh, readonly var, when not emulating other shells is more like typeset -r: ~$ zsh -c 'b() { local a=1; a; echo $a; }; a() { typeset -gr a=2; echo $a; }; a=0; b; echo $a' 2 2 0 ~$ zsh -c 'b() { local a=1; a; echo $a; }; a() { readonly a=2; echo $a; }; a=0; b; echo $a' 2 1 0 ~$ zsh -c 'b() { local a=1; a; echo $a; }; a() { typeset -r a=2; echo $a; }; a=0; b; echo $a' 2 1 0 ksh93 does static scoping, so things are different there anyway, there's only a global scope and one local scope not a stack of them, so -g can mean "global" without ambiguity and functions cannot access the variables of their caller unless they're exported. ~$ ksh -c 'function b { typeset a=1; a; echo $a; }; function a { typeset -gr a=2; echo $a; }; a=0; b; echo $a' 2 1 2 ~$ ksh -c 'function b { typeset a=1; a; echo $a; }; function a { readonly a=2; echo $a; }; a=0; b; echo $a' 2 1 2 ~$ ksh -c 'function b { typeset -x a=1; a; echo $a; }; function a { readonly a=2; echo $a; }; a=0; b; echo $a' 2 1 0 ~$ ksh -c 'function b { typeset -x a=1; a; echo $a; a=3; }; function a { readonly a=2; echo $a; }; a=0; b; echo $a' 2 1 0 readonly is still not equivalent to typeset -gr as it affects the variable in the scope where it currently exists (whether local, global or exported from an ancestor), not only in the global scope: $ ksh -c 'function a { typeset a=1; typeset -gr a; echo $a; }; a=0; a; echo $a; a=2' 1 0 ksh: a: is read only $ ksh -c 'function a { typeset a=1; readonly a; echo $a; }; a=0; a; echo $a; a=2' 1 0 -- Stephane ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 8:49 ` Stephane Chazelas @ 2024-03-12 18:32 ` Bart Schaefer 2024-03-12 20:06 ` Stephane Chazelas 2024-03-12 20:26 ` Stephane Chazelas 0 siblings, 2 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 18:32 UTC (permalink / raw) To: Zsh hackers list On Tue, Mar 12, 2024 at 1:49 AM Stephane Chazelas <stephane@chazelas.org> wrote: > > IMO, typeset -g should only mean: don't make it local, do not > instantiate a new variable in the current scope. As I think I mentioned elsewhere, zsh's "typeset -g" (in the absence of -p) only applies to newly instantiated parameters -- it will still find parameters in the current (or enclosing) scope if they already exist. Which typically means it creates a parameter in the outermost scope, but I suppose also answers some of my other questions, e.g., +p -g should still find everything including locals and is not necessarily equivalent to -p +g. > bash-5.3$ f() { local i; integer i=2+2; echo "$i"; } > bash-5.3$ f > > bash-5.3$ echo $i > 4 > > That "integer" changed the type of the global (outer-most) i > variable instead of that of its caller. Looks like "integer" et al. in bash actually search for the parameter using the type (which corresponds to what zsh attempts to do when using -p), rather searching for the parameter name and then altering the type (which zsh without my patch sometimes does by accident). Does it always use outermost scope or does it just use the "nearest" integer (in this example) that it finds? > In zsh, readonly var, when not emulating other shells is more > like typeset -r: It should in fact be exactly like "typeset -r", and export should be exactly like "typeset +x". > ksh93 does static scoping As someone else pointed out elsewhere, this depends on whether you do foo() { ...; } or function foo { ...; } but really, I don't care, as we don't emulate this bit anyway. Aside: Shouldn't IGNORE_CLOSE_BRACES be set in ksh emulation? It currently is not. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 18:32 ` Bart Schaefer @ 2024-03-12 20:06 ` Stephane Chazelas 2024-03-12 20:31 ` Bart Schaefer 2024-03-12 20:26 ` Stephane Chazelas 1 sibling, 1 reply; 13+ messages in thread From: Stephane Chazelas @ 2024-03-12 20:06 UTC (permalink / raw) To: Bart Schaefer; +Cc: Zsh hackers list 2024-03-12 11:32:41 -0700, Bart Schaefer: [...] > > bash-5.3$ f() { local i; integer i=2+2; echo "$i"; } > > bash-5.3$ f > > > > bash-5.3$ echo $i > > 4 > > > > That "integer" changed the type of the global (outer-most) i > > variable instead of that of its caller. > > Looks like "integer" et al. in bash actually search for the parameter > using the type (which corresponds to what zsh attempts to do when > using -p), rather searching for the parameter name and then altering > the type (which zsh without my patch sometimes does by accident). > > Does it always use outermost scope or does it just use the "nearest" > integer (in this example) that it finds? Sorry, you're missing my point. bash doesn't have an "integer" builtin. I was defining a: integer() { typeset -gi "$@"; } *function* to demonstrate the undesired behaviour of bash's typeset -g. [...] > > ksh93 does static scoping > > As someone else pointed out elsewhere, this depends on whether you do > foo() { ...; } > or > function foo { ...; } > but really, I don't care, as we don't emulate this bit anyway. Yes, Bourne-style functions in ksh93 do no scoping at all whether static or dynamic. If you use "typeset" in a Bourne-style function in ksh93, that will affect or instantiate the variable in the inner-most Korn-style function in the call stack, or the global scope if there are only Bourne-style functions in the call stack. Calling a Bourne-style function in ksh in effect is like dumping the body of that function on the spot when it comes to scoping. That has its uses to work around the strictness of the Korn-style statically scoped functions. You could do for instance: int() typeset -i "$@" there. > Aside: Shouldn't IGNORE_CLOSE_BRACES be set in ksh emulation? It > currently is not. I'd say $ zsh --emulate ksh -c 'echo go}' zsh:1: parse error near `}' $ zsh --emulate ksh -o ignoreclosebraces -c 'echo go}' go } Are both wrong for ksh compatibility. $ zsh --emulate sh -c 'echo go}' go} ~$ zsh --emulate sh -c 'set -o' | grep brace braceccl off noignorebraces off ignoreclosebraces off ~$ zsh --emulate ksh -c 'set -o' | grep brace braceccl off ignorebraces off ignoreclosebraces off $ zsh --emulate ksh -o ignorebraces -c 'echo go}' go} But that disables braceexpansion (which is or is not enabled by default on ksh depending on the implementation, version, how it was compiled and the setting of the braceexpand (and posix in ksh93u+m) options). Newer versions of the POSIX spec allow (but don't require nor specify) brace expansion, so it doesn't need to be disabled in sh emulation any longer (but doesn't need to be enabled either). Same for {fd}> ... See https://www.austingroupbugs.net/view.php?id=1193 -- Stephane ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:06 ` Stephane Chazelas @ 2024-03-12 20:31 ` Bart Schaefer 2024-03-12 20:48 ` Stephane Chazelas 2024-03-12 21:02 ` Bart Schaefer 0 siblings, 2 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 20:31 UTC (permalink / raw) To: Zsh hackers list On Tue, Mar 12, 2024 at 1:06 PM Stephane Chazelas <stephane@chazelas.org> wrote: > > 2024-03-12 11:32:41 -0700, Bart Schaefer: > > > > Looks like "integer" et al. in bash actually search for the parameter > > using the type [...] > > > > Does it always use outermost scope or does it just use the "nearest" > > integer (in this example) that it finds? > > Sorry, you're missing my point. bash doesn't have an "integer" > builtin. Fine, but my point was that the type and name are both used to search for the parameter. If you instead wrote: $ f() { typeset -i i; integer i=2+2; echo "$i"; } Would that still find the global $i instead of "the $i in f"? > > Aside: Shouldn't IGNORE_CLOSE_BRACES be set in ksh emulation? It > > currently is not. > > I'd say > > $ zsh --emulate ksh -c 'echo go}' > zsh:1: parse error near `}' > $ zsh --emulate ksh -o ignoreclosebraces -c 'echo go}' > go } Also beside the point, which is that the first of these three is wrong: % zsh --emulate ksh -c '{ echo go }' go % zsh --emulate ksh -o ignoreclosebraces -c '{ echo go }' zsh:1: parse error near `}' zsh --emulate ksh -o ignoreclosebraces -c '{ echo go; }' go ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:31 ` Bart Schaefer @ 2024-03-12 20:48 ` Stephane Chazelas 2024-03-12 21:02 ` Bart Schaefer 1 sibling, 0 replies; 13+ messages in thread From: Stephane Chazelas @ 2024-03-12 20:48 UTC (permalink / raw) To: Bart Schaefer; +Cc: Zsh hackers list 2024-03-12 13:31:42 -0700, Bart Schaefer: > On Tue, Mar 12, 2024 at 1:06 PM Stephane Chazelas <stephane@chazelas.org> wrote: > > > > 2024-03-12 11:32:41 -0700, Bart Schaefer: > > > > > > Looks like "integer" et al. in bash actually search for the parameter > > > using the type [...] > > > > > > Does it always use outermost scope or does it just use the "nearest" > > > integer (in this example) that it finds? > > > > Sorry, you're missing my point. bash doesn't have an "integer" > > builtin. > > Fine, but my point was that the type and name are both used to search > for the parameter. If you instead wrote: > > $ f() { typeset -i i; integer i=2+2; echo "$i"; } > > Would that still find the global $i instead of "the $i in f"? My point was that the "typeset -gi i=2+2" that my integer function does affects the variable at the outer-most scope in bash, so typeset -g is not non-local-typeset there, but more like typeset-at-the-outermost-scope. Makes sense in that the "g" is meant for "global" and that "outermost scope" for a shell with dynamic scoping is likely the closest you can get to a "global" scope but in practice it's not useful because in a shell with dynamic scoping there's generally no good reason to want to single-out the outer-most scope as more special than any other scope in the call stack while there are often good reasons for "typeset" to just "set the type" of existing variable without instantiating a new variable in the current scope. Which is why I'm saying that bash's behaviour is undesirable and that we don't want to go there, and that zsh should carry on as it currently does in that regard (similar to what mksh and yash do, two other shells with dynamic scoping). > > > Aside: Shouldn't IGNORE_CLOSE_BRACES be set in ksh > > > emulation? It currently is not. > > > > I'd say > > > > $ zsh --emulate ksh -c 'echo go}' zsh:1: parse error near > > `}' $ zsh --emulate ksh -o ignoreclosebraces -c 'echo go}' > > go } > > Also beside the point, which is that the first of these three is wrong: > > % zsh --emulate ksh -c '{ echo go }' > go > % zsh --emulate ksh -o ignoreclosebraces -c '{ echo go }' > zsh:1: parse error near `}' > zsh --emulate ksh -o ignoreclosebraces -c '{ echo go; }' > go What I'm saying is that while ignoreclosebraces helps, it's not *enough* to give you ksh compatibility. AFAICT, to get ksh compatibility, } must be treated literally unless part of brace expansion {fd}> redirs, ${...} expansions. -- Stephane ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:31 ` Bart Schaefer 2024-03-12 20:48 ` Stephane Chazelas @ 2024-03-12 21:02 ` Bart Schaefer 1 sibling, 0 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 21:02 UTC (permalink / raw) To: Zsh hackers list On Tue, Mar 12, 2024 at 1:31 PM Bart Schaefer <schaefer@brasslantern.com> wrote: > > $ f() { typeset -i i; integer i=2+2; echo "$i"; } > > Would that still find the global $i instead of "the $i in f"? For bash 5.0 at least, the answer is "yes". I don't have a newer one handy to try. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 18:32 ` Bart Schaefer 2024-03-12 20:06 ` Stephane Chazelas @ 2024-03-12 20:26 ` Stephane Chazelas 2024-03-12 20:38 ` Bart Schaefer 1 sibling, 1 reply; 13+ messages in thread From: Stephane Chazelas @ 2024-03-12 20:26 UTC (permalink / raw) To: Bart Schaefer; +Cc: Zsh hackers list 2024-03-12 11:32:41 -0700, Bart Schaefer: [...] > > In zsh, readonly var, when not emulating other shells is more > > like typeset -r: > > It should in fact be exactly like "typeset -r", and export should be > exactly like "typeset +x". [...] ITYM typeset -x, but I find that export / typeset -x seem to behave like typeset -gx in that they don't make the variable local. ~$ zsh -c 'a() { export a; a=2; echo $a; }; a=1; a; echo $a' 2 2 ~$ zsh -c 'a() { typeset -x a; a=2; echo $a; }; a=1; a; echo $a' 2 2 ~$ zsh -c 'a() { typeset -gx a; a=2; echo $a; }; a=1; a; echo $a' 2 2 $ zsh -c 'a() { export a; a=3; typeset -p a; }; b() { local a=2; a; typeset -p a; }; a=1; b; typeset -p a' export -x a=3 typeset -x a=3 typeset a=1 So in that way, they're different from readonly/typeset -r That export -x seems bogus BTW as export doesn't accept the -x option. To have a local export variable, it seems you need typeset var; typeset -x var or typeset var; export var instead of typeset -x var. $ zsh -c 'a() { typeset a; typeset -x a; a=3; typeset -p a; }; b() { local a=2; a; typeset -p a; }; a=1; b; typeset -p a' typeset -x a=3 typeset a=2 typeset a=1 -- Stephane ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:26 ` Stephane Chazelas @ 2024-03-12 20:38 ` Bart Schaefer 2024-03-12 20:58 ` Stephane Chazelas ` (2 more replies) 0 siblings, 3 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 20:38 UTC (permalink / raw) To: Zsh hackers list On Tue, Mar 12, 2024 at 1:26 PM Stephane Chazelas <stephane@chazelas.org> wrote: > > ITYM typeset -x Yes, sorry, typo. > but I find that export / typeset -x seem to > behave like typeset -gx in that they don't make the variable > local. Yes, that's GLOBAL_EXPORT in action, as in the subject of this thread. They're still exactly like each other, I didn't say they were like some other shell's export or that they acted like -r. > $ zsh -c 'a() { export a; a=3; typeset -p a; }; b() { local a=2; a; typeset -p a; }; a=1; b; typeset -p a' > export -x a=3 > typeset -x a=3 > typeset a=1 > > That export -x seems bogus BTW as export doesn't accept the -x > option. Indeed, that's a bug in the printparamnode() routine. > To have a local export variable, it seems you need > typeset var; typeset -x var or typeset var; export var instead > of typeset -x var. Just typeset +g -x var will do it, or unsetopt GLOBAL_EXPORT (which is dangerous without the patch at the start of this thread). ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:38 ` Bart Schaefer @ 2024-03-12 20:58 ` Stephane Chazelas 2024-03-12 21:03 ` Bart Schaefer 2024-03-13 0:52 ` {PATCH] (for real this time) " Bart Schaefer 2024-03-14 2:11 ` [PATCH] More "typeset -p" and GLOBAL_EXPORT Bart Schaefer 2 siblings, 1 reply; 13+ messages in thread From: Stephane Chazelas @ 2024-03-12 20:58 UTC (permalink / raw) To: Bart Schaefer; +Cc: Zsh hackers list 2024-03-12 13:38:48 -0700, Bart Schaefer: [...] > > but I find that export / typeset -x seem to > > behave like typeset -gx in that they don't make the variable > > local. > > Yes, that's GLOBAL_EXPORT in action, as in the subject of this thread. > They're still exactly like each other, I didn't say they were like > some other shell's export or that they acted like -r. Sorry, most of your initial post went way over my head, I just reacted to the last sentence to warn against the "bash way" in this regard and make sure that -g remains more for "non-local" than "global" (whatever that means such as outermost-scope in bash). [...] > > To have a local export variable, it seems you need > > typeset var; typeset -x var or typeset var; export var instead > > of typeset -x var. > > Just > typeset +g -x var [...] Thanks. Also fine with me for +g to cancel an implicit -g that would mean non-local. -- Stephane ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:58 ` Stephane Chazelas @ 2024-03-12 21:03 ` Bart Schaefer 0 siblings, 0 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-12 21:03 UTC (permalink / raw) To: Zsh hackers list On Tue, Mar 12, 2024 at 1:58 PM Stephane Chazelas <stephane@chazelas.org> wrote: > > Also fine with me for +g to cancel an implicit -g that would > mean non-local. That's good, because that bit is not changing. ^ permalink raw reply [flat|nested] 13+ messages in thread
* {PATCH] (for real this time) Re: "typeset -p" and no_GLOBAL_EXPORT, other misc. 2024-03-12 20:38 ` Bart Schaefer 2024-03-12 20:58 ` Stephane Chazelas @ 2024-03-13 0:52 ` Bart Schaefer 2024-03-14 2:11 ` [PATCH] More "typeset -p" and GLOBAL_EXPORT Bart Schaefer 2 siblings, 0 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-13 0:52 UTC (permalink / raw) To: Zsh hackers list [-- Attachment #1: Type: text/plain, Size: 334 bytes --] On Tue, Mar 12, 2024 at 1:38 PM Bart Schaefer <schaefer@brasslantern.com> wrote: > > Just > typeset +g -x var > will do it, or unsetopt GLOBAL_EXPORT (which is dangerous without the > patch at the start of this thread). Dangit, just noticed that the wrong file got attached to that first message. Here's the right one. [-- Attachment #2: typeset+-pgm.txt --] [-- Type: text/plain, Size: 2993 bytes --] diff --git a/Src/builtin.c b/Src/builtin.c index 829b899f8..7bfb1ce1d 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2059,7 +2059,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' * flags of an unset parameter. */ - usepm = pm && (!(pm->node.flags & PM_UNSET) || + usepm = pm && (!(pm->node.flags & PM_UNSET) || OPT_ISSET(ops, 'p') || (isset(POSIXBUILTINS) && (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); @@ -2195,7 +2195,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0) newspecial = NS_SECONDS; - if (isset(POSIXBUILTINS)) { + if (isset(POSIXBUILTINS) && !OPT_ISSET(ops,'p')) { /* * Stricter rules about retaining readonly attribute in this case. */ @@ -2224,7 +2224,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, * ii. we are creating a new local parameter */ if (usepm) { - if (OPT_MINUS(ops,'p') && on && !(on & pm->node.flags)) + if (OPT_MINUS(ops,'p') && on && + !((on & pm->node.flags) || ((on & PM_LOCAL) && pm->level))) return NULL; else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags)) return NULL; @@ -2339,7 +2340,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; pm->node.flags |= (on & PM_READONLY); return pm; - } + } else if (OPT_ISSET(ops,'p')) + return NULL; /* Nothing to print */ if ((asg->flags & ASG_ARRAY) ? !(on & (PM_ARRAY|PM_HASHED)) : @@ -2686,6 +2688,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g')) ops->ind['g'] = 1; +#if 0 + /* "local" rejects -m, this should too ... what about +m ? */ + if (locallevel && OPT_MINUS(ops, 'm') && + !(OPT_MINUS(ops, 'g') || OPT_ISSET(ops, 'p'))) { + zerrnam(name, "bad option: -m"); + return 1; + } +#endif + /* Translate the options into PM_* flags. * * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ @@ -2788,10 +2799,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) return 0; } - if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || - OPT_PLUS(ops,'g') || *name == 'l' || - (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) - on |= PM_LOCAL; + /* Using *name here is cheating, "local" allows no -g option */ + if ((*name == 'l' || OPT_PLUS(ops,'g'))) + on |= PM_LOCAL; + else if (!OPT_ISSET(ops,'g')) { + if (OPT_MINUS(ops, 'x')) { + if (isset(GLOBALEXPORT)) + ops->ind['g'] = 1; + else if (locallevel) + on |= PM_LOCAL; + } else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m'))) + on |= PM_LOCAL; + } if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) { Param apm; ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] More "typeset -p" and GLOBAL_EXPORT 2024-03-12 20:38 ` Bart Schaefer 2024-03-12 20:58 ` Stephane Chazelas 2024-03-13 0:52 ` {PATCH] (for real this time) " Bart Schaefer @ 2024-03-14 2:11 ` Bart Schaefer 2 siblings, 0 replies; 13+ messages in thread From: Bart Schaefer @ 2024-03-14 2:11 UTC (permalink / raw) To: Zsh hackers list [-- Attachment #1: Type: text/plain, Size: 2205 bytes --] On Tue, Mar 12, 2024 at 1:38 PM Bart Schaefer <schaefer@brasslantern.com> wrote: > > On Tue, Mar 12, 2024 at 1:26 PM Stephane Chazelas <stephane@chazelas.org> wrote: > > > > $ zsh -c 'a() { export a; a=3; typeset -p a; }; b() { local a=2; a; typeset -p a; }; a=1; b; typeset -p a' > > export -x a=3 > > typeset -x a=3 > > typeset a=1 > > > > That export -x seems bogus BTW as export doesn't accept the -x > > option. > > Indeed, that's a bug in the printparamnode() routine. That's not the only bug. As demonstrated by B02typeset.ztst in the test no array/hash in POSIX export/readonly -p with a declaration in a function of local -rx zsh_exported_readonly_scalar=1 the output of "export -p" is typeset -rx zsh_exported_readonly_scalar=1 However, "local" is supposed to supersede the GLOBAL_EXPORT option, so that should have output one of typeset +g -rx zsh_exported_readonly_scalar=1 or local -rx zsh_exported_readonly_scalar=1 The attached patch changes this (using "local"). The POSIX mode output -- export zsh_exported_readonly_scalar=1 -- is explained by a comment: * The zsh variants of export -p/readonly -p also report other * flags to indicate other attributes or scope. The POSIX variants * don't. There is also an AFAICT insurmountable problem: innie() { typeset -p $1 } outie() { local -x $1; innie $1 } outie var displays export var='' That is "correct" in that var is not local to outie, but that statement cannot be evaluated to restore the state of $var anywhere other than from functions called by innie. Conversely: innie() { print ${(tP)1} } outie var displays scalar-local-export Which is also "correct" but fibs about $var being local to innie. Finally we have innie() { typeset +m $1 } outie var which agrees with the (t) flag by reporting local exported var And innie() { typeset -m $1 } outie var yielding var='' No mention of export at all. These are all long-standing behaviors and haven't caused anyone a problem yet that I know of, so I'm not suggesting anything change, just pointing out that this patch doesn't address them. [-- Attachment #2: printparamflags.txt --] [-- Type: text/plain, Size: 2733 bytes --] diff --git a/Src/params.c b/Src/params.c index 973df3fe5..f65aa1e80 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6044,6 +6044,7 @@ printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; Param peer = NULL; + int altname = 0; if (!(p->node.flags & PM_HASHELEM) && !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.') @@ -6089,16 +6090,26 @@ printparamnode(HashNode hn, int printflags) if (printflags & PRINT_POSIX_EXPORT) { if (!(p->node.flags & PM_EXPORTED)) return; + altname = 'x'; printf("export "); } else if (printflags & PRINT_POSIX_READONLY) { if (!(p->node.flags & PM_READONLY)) return; + altname = 'r'; printf("readonly "); - } else if (locallevel && p->level >= locallevel) { - printf("typeset "); /* printf("local "); */ } else if ((p->node.flags & PM_EXPORTED) && !(p->node.flags & (PM_ARRAY|PM_HASHED))) { - printf("export "); + if (p->level && p->level >= locallevel) + printf("local "); + else { + altname = 'x'; + printf("export "); + } + } else if (locallevel && p->level >= locallevel) { + if (p->node.flags & PM_EXPORTED) + printf("local "); + else + printf("typeset "); /* printf("local "); */ } else if (locallevel) { printf("typeset -g "); } else @@ -6112,6 +6123,10 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; + + if (altname && altname == pmptr->typeflag) + continue; + if (pmptr->flags & PMTF_TEST_LEVEL) { if (p->level) { /* diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index d90f17d13..914eea92b 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -311,7 +311,7 @@ print $OUTER 0:Export of tied parameters >i:n:n:e:r ->typeset -xT OUTER outer=( i n n e r ) +>local -xT OUTER outer=( i n n e r ) >typeset -aT OUTER outer=( i n n e r ) >OUTER=i:n:n:e:r >outer=( i n n e r ) @@ -1099,12 +1099,12 @@ } 0: no array/hash in POSIX export/readonly -p >zsh: ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 >sh: >export zsh_exported_readonly_scalar=1 >readonly zsh_exported_readonly_scalar=1 ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2024-03-14 2:11 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-03-12 4:13 "typeset -p" and no_GLOBAL_EXPORT, other misc Bart Schaefer 2024-03-12 8:49 ` Stephane Chazelas 2024-03-12 18:32 ` Bart Schaefer 2024-03-12 20:06 ` Stephane Chazelas 2024-03-12 20:31 ` Bart Schaefer 2024-03-12 20:48 ` Stephane Chazelas 2024-03-12 21:02 ` Bart Schaefer 2024-03-12 20:26 ` Stephane Chazelas 2024-03-12 20:38 ` Bart Schaefer 2024-03-12 20:58 ` Stephane Chazelas 2024-03-12 21:03 ` Bart Schaefer 2024-03-13 0:52 ` {PATCH] (for real this time) " Bart Schaefer 2024-03-14 2:11 ` [PATCH] More "typeset -p" and GLOBAL_EXPORT Bart Schaefer
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).