zsh-workers
 help / color / mirror / code / Atom feed
* "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 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: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: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: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: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: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 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).