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

Re: [PATCH] Restrict named directories to scalar parameters.



Aside:  I suggest adding PS1="" to the input of zsh -fis for the Test/K01 patch.

What is the advantage? It doesn't seem to make any difference.



Sorry, the following is way too long for these two small patches but hopefully it will make it clear where I'm coming from and let you pinpoint what, if anything, you disagree with.

Here is my understanding of named directories minus the features not relevant for the current discussion.

Zsh maintains a table of named directories that maps directory names to directories. The table is accessible via the parameter nameddirs. The table supports two features:
Once a parameter P named N is flagged with PM_NAMEDDIR, each time it is updated with a new value D, the pair (N, D) is added to the table of named directories if D starts with "/" and otherwise any entry whose name is N is removed from the table.

One can distinguish two kinds of named directories:
  • Hash-based: When a named directory N is defined with the "hash" command (e.g. "hash -d foo=/foo123), then it only lives in the table of named directories; no parameter named N is needed and defining or updating one before or after the call to "hash" has no effect.
  • Parameter-based: When a named directory is defined by first defining a parameter P named N and then expanding the filename ~N (e.g., "foo=/foo123; : ~foo"), then it's backed by the parameter P; any update of the value of the parameter P also updates the named directory N.
With a parameter-based named directory N, one could expect that ~N always expands to the same as $N but that isn't true if one calls "hash" after defining the named directory:

% zsh -fic 'foo=/foo1; : ~foo; hash -d foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo1 ~foo=/foo2

One could argue that the call to "hash" turned the parameter-based named directory into a hash-based one but that isn't the case either. Indeed, updating the parameter after calling "hash" still updates the named directory:

% zsh -fic 'foo=/foo1; : ~foo; hash -d foo=/foo2; foo=/foo3; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo3 ~foo=/foo3

To me this looks more like a bug than a feature. It could be addressed either by updating the value of the parameter or by removing the PM_NAMEDDIR flag from the parameter when "hash" is called. The former would ensure that $N and ~N remain in sync, the latter would turn the parameter-based named directory into a true hash-based one.

My assumption is that for a parameter-based named directory N, it is in principle expected that ~N and $N remain in sync. The example above shows that this can be violated. However, to the best of my knowledge, it is true as long as one never uses "hash" to modify parameter-based named directories.

When parameters are searched for the expansion of ~N (because there isn't yet any N in the table of named directories), the search is restricted to scalar parameters. One could argue that the search should be expanded to named references that refer to a scalar parameter. This was actually the case before workers/54475. However that broke the assumption above; even in the absence of calls to "hash", ~N and $N could get out of sync:

% zsh -fic 'var=/foo1; typeset -n foo=var; : ~foo; foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo2 ~foo=/foo1

Fixing this without extra infrastructure would be way too expensive; every update of a scalar parameter would have to scan the whole parameter table to check whether there is a named reference flagged with PM_NAMEDDIR that refers to it. The extra infrastructure looks way too overblown for such a small feature. To me, restricting parameter-based named directories to scalar parameters looks like the most pragmatic approach.

Directory values

When parameters are searched for the expansion of ~N, only parameters whose expansion starts with a "/" are considered:

% zsh -fic 'foo=no-slash; : ~foo; printf "~foo=%s\n" ~foo'
zsh:1: no such user or named directory: foo


When parameters flagged with PM_NAMEDDIR are updated with a value that doesn't start with a "/", the corresponding named directory is removed from the table instead of being updated with the new value:

% zsh -fic 'foo=/foo1; : ~foo; printf "~foo=%s\n" ~foo; foo=no-slash; printf "~foo=%s\n" ~foo'
~foo=/foo1
zsh:1: no such user or named directory: foo

If the "hash" command is used to define or update a named directory, then surprisingly all values are accepted:

% zsh -fic 'hash -d foo=no-slash; printf "~foo=%s\n" ~foo'
~foo=no-slash


It's unclear to me whether the fact that the "hash" command doesn't restrict values to ones that start with a "/" is a bug or a feature.

AUTO_NAME_DIRS option

The aim of the AUTO_NAME_DIRS option is to avoid the need to expand ~N in order to create a parameter-based named directory N; one can simply define the parameter N.

A side-effect of the AUTO_NAME_DIRS option is that it makes it possible to turn a hash-based named directory N into a parameter-based one. Indeed, if a parameter named N is defined after the call to "hash", it will override the value specified by the "hash" command and any further updates of the parameter named N will be reflected in the named directory N:

% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=/foo2; printf "\$foo=%s ~foo=%s\n" $foo ~foo'
$foo=/foo2 ~foo=/foo2

The documentation of the AUTO_NAME_DIRS option states that only parameters whose value is an absolute directory become named directories. However, the current implementation promotes all scalar parameters and all named references to named directories (i.e., flags them with PM_NAMEDDIR) whenever their value is initialized or updated.

As discussed above, parameter-based named directories backed by named references aren't supported by the current implementation because it can't ensure that ~N and $N remain in sync. An additional issue is that the code that promotes named references to named directories initializes the named directory with the name of the referred parameter instead of initializing it with the value of the referred parameter.

A better description of the patch workers/54759 is that it prevents the promotion of named references to named directories. Given the current shortcomings and given the fact that named references can never be initialized with values that start with "/", the only noticeable effect of the patch is that initializing a named reference N after creating a hash-based named directory N, will no longer remove the named directory:

% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset -n foo=var; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=()            # Before patch
nameddirs=(foo /foo1)   # After patch

A better description of the patch workers/54760 is that it prevents the promotion to named directories of (scalar) parameters whose value doesn't start with a "/".

When AUTO_NAME_DIRS is disabled, declaring, initializing and/or updating a parameter never has any effect on a hash-based named directory:

% zsh -fic 'hash -d foo=/foo1; foo=/foo2; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)

When AUTO_NAME_DIRS is enabled, the same is still true for any non-scalar and non-nameref parameter:

% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=(/foo2); : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)
% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset -i foo=1/2; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)

It is even true for scalar declarations with no initialization:

% zsh -fic 'setopt autonamedirs; hash -d foo=/foo1; typeset foo; : ~foo; ; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=(foo /foo1)

Given the above and given the fact that the documentation of the AUTO_NAME_DIRS option states that only parameters whose value is an absolute directory become named directories, it seems reasonable to think that initializing or updating a scalar parameter with a value that is not a directory (i.e., that doesn't start with a "/") should leave any existing named directory unchanged:

% zsh-dev -fic 'setopt autonamedirs; hash -d foo=/foo1; foo=foo2; echo "nameddirs=(${(kv)nameddirs})"'
nameddirs=()            # Before patch
nameddirs=(foo /foo1)
   # After patch

and should not flag the parameter with PM_NAMEDDIR:

% zsh -fic 'setopt autonamedirs; foo=foo1; hash -d foo=/foo2; setopt +o autonamedirs; foo=foo3; echo "nameddirs=(${(kv)nameddirs})"'    
nameddirs=()
            # Before patch
nameddirs=(foo /foo2)   # After patch

In other words, only scalar parameters that effectively store absolute directory names are ever promoted to named directories.

Philippe



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