Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

"typeset -p" and no_GLOBAL_EXPORT, other misc.



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.
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


Messages sorted by: Reverse Date, Date, Thread, Author