summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Doc/Zsh/builtins.yo16
-rw-r--r--Doc/Zsh/expn.yo5
-rw-r--r--Doc/Zsh/func.yo34
-rw-r--r--Doc/Zsh/grammar.yo3
-rw-r--r--Doc/Zsh/params.yo4
-rw-r--r--Test/K01nameref.ztst170
7 files changed, 218 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 2b2f2a08b..0cc33c7f1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2023-02-12 Bart Schaefer <schaefer@zsh.org>
+ * 51403: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/func.yo,
+ Doc/Zsh/grammar.yo, Doc/Zsh/params.yo, Test/K01nameref.ztst:
+ Tests and documentation for 51402, clean up some other tests.
+
* 51402: Src/builtin.c, Src/loop.c, Src/params.c, Src/zsh.h:
Add ksh/bash features (unset -n, for ref), readonly refs,
better error checking and messages, code injection safety,
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 97a82226b..92917c06c 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -2041,12 +2041,13 @@ cindex(named reference)
cindex(reference, named)
The flag tt(-n) creates a em(named reference) to another parameter.
The second parameter need not exist at the time the reference is
-created. No attributes except tt(-g) may be used in conjunction with
+created. Only tt(-g) and tt(-r) may be used in conjunction with
tt(-n). The var(name) so created may not be an array element nor use
a subscript, but the var(value) assigned may be any valid parameter
name syntax, even a subscripted array element (including an associative
array element) or an array slice, which is evaluated when the named
-reference is expanded.
+reference is expanded. It is an error for a named reference to refer
+to itself, even indirectly through a chain of references.
See ifzman(zmanref(zshexpn))ifnzman(noderef(Parameter Expansion)) and
ifzman(zmanref(zshparam))ifnzman(noderef(Parameters)) for details of the
behavior of named references.
@@ -2443,7 +2444,7 @@ with the command `tt(zmodload -F zsh/rlimits b:unlimit)'.
)
findex(unset)
cindex(parameters, unsetting)
-item(tt(unset) [ tt(-fmv) ] var(name) ...)(
+item(tt(unset) [ tt(-fmv) ] [ tt(-n) ] var(name) ...)(
Each named parameter is unset.
Local parameters remain local even if unset; they appear unset within scope,
but the previous value will still reappear when the scope ends.
@@ -2457,10 +2458,13 @@ be quoted) and all parameters with matching names are unset. Note that this
cannot be used when unsetting associative array elements, as the subscript
will be treated as part of the pattern.
-The tt(-v) flag specifies that var(name) refers to parameters. This is the
-default behaviour.
+The tt(-v) flag specifies that var(name) refers to parameters. This is
+the default behaviour. If the tt(-n) option is supplied, and
+var(name) is a a named reference, var(name) will be unset rather than
+the variable it references.
-tt(unset -f) is equivalent to tt(unfunction).
+tt(unset -f) is equivalent to tt(unfunction). The tt(-n) option has
+no effect with tt(-f).
)
findex(unsetopt)
cindex(options, unsetting)
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 8b1c69c55..ef01794e6 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1581,7 +1581,10 @@ is interpreted at the time tt(${)var(pname)tt(}) is expanded. Any
form of subscript is allowed, including those that select individual
elements, substrings of scalar strings, or multiple elements as with
array slices or the `tt((i))', `tt((I))', `tt((r))', `tt((R))' and
-`tt((w))' subscript flags.
+`tt((w))' subscript flags. However, the subscript is evaluated with
+the tt(NO_EXEC) option in effect, so command substitution and other
+similar constructs produce no output, although are not syntactically
+excluded.
When var(rname) is an array (but not an array element or slice), the
named reference may also be used in substitutions requiring an
diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo
index 12db3f56a..d4914df7a 100644
--- a/Doc/Zsh/func.yo
+++ b/Doc/Zsh/func.yo
@@ -13,6 +13,40 @@ Functions are executed like commands with the arguments
passed as positional parameters.
(See noderef(Command Execution).)
+Parameters declared by any of the `tt(typeset)' family of commands
+during the execution of a function become em(local) to the function
+unless the `tt(-g)' option is used. This is the em(scope) of the
+parameter, which extends dynamically to any other functions called by
+the declaring function. In most cases, local parameters take the
+place of any other parameter having the same name that was assigned or
+declared in an earlier function scope.
+(See noderef(Local Parameters).)
+
+A named parameter declared with the `tt(-n)' option to any of the
+`tt(typeset)' commands becomes a reference to a parameter in scope at
+the time of assignment to the named reference, which may be at a
+different call level than the declaring function. For this reason,
+it is good practice to declare a named reference as soon as the
+referent parameter is in scope, and as early as possible in the
+function if the reference is to a parameter in a calling scope.
+
+A typical use of named references is to pass the name
+of the referent as a positional parameter. For example,
+ifzman()
+example(pop+LPAR()RPAR() {
+ local -n ref=$1
+ local last=$ref[$#ref]
+ ref[$#ref]=LPAR()RPAR()
+ print -r -- $last
+}
+array=LPAR() a list of five values RPAR()
+pop array)
+
+prints the word `tt(values)' and shortens `tt($array)' to
+`tt(LPAR() a list of five RPAR())'. There are no local parameters in
+tt(pop) at the time `tt(ref=$1)' is assigned, so `tt(ref)' becomes a
+reference to `tt(array)' in the caller.
+
Functions execute in the same process as the caller and
share all files
and present working directory with the
diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index 9af211090..1b834f41a 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -187,6 +187,9 @@ Expand the list of var(word)s, and set the parameter
var(name) to each of them in turn, executing var(list)
each time. If the `tt(in) var(word)' is omitted,
use the positional parameters instead of the var(word)s.
+If any var(name) has been declared as a named reference,
+the corresponding var(word) is treated as the name of a
+parameter and var(name) is made a reference to that.
The var(term) consists of one or more newline or tt(;)
which terminate the var(word)s, and are optional when the
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index c7ecf0f64..946c00793 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -656,8 +656,8 @@ When a em(named reference) is created with `tt(typeset -n)', all uses
of var(pname) in assignments and expansions instead assign to or
expand var(rname). This also applies to `tt(unset )var(pname)' and to
most subsequent uses of `tt(typeset)' with the exception of
-`tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference
-it is necessary to use one of:
+`tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference,
+use either `tt(unset -n )var(pname)' or one of:
ifzman()
example(tt(typeset -n )var(pname)
tt(typeset +n )var(pname))
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index a663194a7..61c2b006a 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -25,6 +25,12 @@
0:assign nameref placeholder
>ptr=var
+ typeset ptr=var
+ typeset -n ptr
+ typeset -n
+0:convert scalar to nameref
+>ptr=var
+
typeset -n ptr=var
typeset +n ptr
typeset -p ptr
@@ -46,6 +52,11 @@ F:Other type changes are fatal errors, should this also be?
>typeset -n ptr=var
>typeset -t var
+ typeset -n ptr=var[2]
+ typeset -t ptr
+1:change type of referenced array element
+*?*var\[2\]: can't change type via subscript reference
+
typeset -n ptr[1]=var
1:illegal nameref name
*?*reference variable cannot be an array
@@ -93,6 +104,14 @@ F:Other type changes are fatal errors, should this also be?
typeset -n ptr=var
typeset var=value
+ unset -n ptr
+ typeset -p var ptr
+0:unset of the nameref itself
+F:If earlier tests change, might get "no such variable" here
+>typeset var=value
+
+ typeset -n ptr=var
+ typeset var=value
typeset -p ptr var
typeset ptr=newvalue
typeset -p ptr var
@@ -105,11 +124,11 @@ F:Other type changes are fatal errors, should this also be?
typeset -n ptr=var
ptr=value
typeset -p var ptr
- unset var # for next test
0:assign new scalar via nameref
>typeset -g var=value
>typeset -n ptr=var
+ unset var
typeset -n ptr=var
typeset var=(val1 val2)
typeset -p ptr var
@@ -132,11 +151,11 @@ F:unexpected side-effects of previous tests
typeset -n ptr=var
ptr=(val1 val2)
typeset -p var ptr
- unset var # for next test
0:assign new array via nameref
>typeset -g -a var=( val1 val2 )
>typeset -n ptr=var
+ unset var
typeset -n ptr2=var
typeset -n ptr1=ptr2
typeset var=value
@@ -178,12 +197,12 @@ F:unexpected side-effects of previous tests
typeset -n ptr1=ptr2
typeset ptr1=newvalue
typeset -p ptr1 ptr2 var
- unset var # for next test
0:typeset new parameter indirectly
>typeset -n ptr1=ptr2
>typeset -n ptr2=var
>typeset var=newvalue
+ unset var
typeset -n ptr2=var
typeset -n ptr1=ptr2
typeset var=value
@@ -219,19 +238,18 @@ F:unexpected side-effects of previous tests
typeset -n ptr1=ptr2
typeset ptr1=(val1 val2)
typeset -p ptr1 ptr2 var
- unset var # for next test
0:typeset new array indirectly
>typeset -n ptr1=ptr2
>typeset -n ptr2=var
>typeset -a var=( val1 val2 )
- typeset -p ptr1 ptr2 var
+ typeset -p ptr1 ptr2
1:check state of paramtab FOUR
F:unexpected side-effects of previous tests
*?*no such variable: ptr1
*?*no such variable: ptr2
-*?*no such variable: var
+ unset var
typeset -n ptr2=var
typeset -n ptr1=ptr2
ptr1=(val1 val2)
@@ -246,34 +264,59 @@ F:unexpected side-effects of previous tests
1:direct nameref loop not allowed
*?*invalid self reference
+ unset var
+ typeset -gn ptr1=var
+ typeset -p ptr1
+0:global reference to unset var
+>typeset -g -n ptr1=var
+
+ unset -n ptr1
+ typeset -gn ptr1
+ typeset -p ptr1
+ ptr1=ptr1
+1:global direct reference
+>typeset -g -n ptr1
+*?*invalid self reference
+
typeset -n ptr1=ptr2
typeset -n ptr2=ptr3
typeset -n ptr3=ptr1
1:indirect nameref loop not allowed
*?*invalid self reference
+ typeset -n ptr1 ptr2
+ ptr1=ptr2
+ ptr2=ptr1
+1:looping assignment not allowed
+*?*invalid self reference
+
+ unset -n ptr2
typeset -n ptr2='path[2]'
print -r -- $ptr2
0q:nameref to array element, no braces
>${path[2]}
+ unset -n ptr2
typeset -n ptr2='path[2]'
print -r -- ${ptr2}
0q:nameref to array element, with braces
>${path[2]}
+ unset -n ptr1
typeset -A hash=(x MISS y HIT)
typeset -n ptr1='hash[y]'
print -r -- $ptr1
0:nameref to hash element, no braces
>HIT
+ unset -n ptr1
typeset -A hash=(x MISS y HIT)
typeset -n ptr1='hash[y]'
print -r -- ${ptr1}
0:nameref to hash element, with braces
>HIT
+ unset -n ptr2
typeset -a ary=(1 2)
typeset -n ptr2='ary[2]'
ptr2=TWO
@@ -281,6 +324,7 @@ F:unexpected side-effects of previous tests
0:assign array element by nameref
>typeset -a ary=( 1 TWO )
+ unset -n ptr2
typeset -n ptr2='ary[2]'
ptr2=TWO
typeset -p ary
@@ -288,6 +332,7 @@ F:unexpected side-effects of previous tests
F:ksh93 does not implement this either
>typeset -a ary=( '' TWO )
+ unset -n ptr1
typeset -A hash=(x MISS y MISS)
typeset -n ptr1='hash[y]'
ptr1=HIT
@@ -295,6 +340,7 @@ F:ksh93 does not implement this either
0:assign to hash element by nameref
>typeset -A hash=( [x]=MISS [y]=HIT )
+ unset -n ptr1
typeset -A hash
typeset -n ptr1='hash[y]'
ptr1=HIT
@@ -303,10 +349,13 @@ F:ksh93 does not implement this either
F:ksh93 does not implement this either
>typeset -A hash=( [y]=HIT )
+ unset -n ptr1
typeset -n ptr1='not good'
1:invalid nameref
*?*invalid variable name: not good
+ unset -n ptr1
+ unset hash
typeset -A hash
typeset -n ptr1='hash[y]'
print ${ptr1::=HIT}
@@ -316,24 +365,25 @@ F:ksh93 does not implement this either
>typeset -n ptr1='hash[y]'
>typeset -A hash=( [y]=HIT )
+ unset -n ptr
+ unset gval
typeset -n ptr=gval
gval=global
() { local gval=local; print $ptr; typeset -p ptr gval }
- unset gval # for next test
0:up-reference part 1
>global
>typeset -g -n ptr=gval
>typeset gval=local
- typeset -p ptr ptr1 ptr2 val gval
+ typeset -p ptr ptr1 ptr2 val
1:check state of paramtab FIVE
F:unexpected side-effects of previous tests
*?*no such variable: ptr
*?*no such variable: ptr1
*?*no such variable: ptr2
*?*no such variable: val
-*?*no such variable: gval
+ unset gval
typeset -n ptr1=gval
typeset gval
() { typeset gval=local; ptr1=global }
@@ -509,4 +559,106 @@ F:Same test, should part 5 output look like this?
>bry[2]
>ipsum
+ unset -n ref
+ unset var
+ typeset -n ref=var
+ typeset var=GLOBAL
+ () {
+ typeset -n ref=$1
+ print -r $ref
+ ref=RESET
+ typeset -p ref var
+ } ref
+ typeset -p ref var
+0:local reference points to same-name global reference, part 1
+>GLOBAL
+>typeset -n ref=ref
+>typeset -g var=RESET
+>typeset -n ref=var
+>typeset var=RESET
+
+ unset -n ref
+ unset var
+ typeset -n ref=var
+ () {
+ typeset -n ref=$1
+ print -r $ref
+ ref=RESET
+ typeset -p ref var
+ } ref
+ typeset -p ref var
+0:local reference points to same-name global reference, part 2
+>
+>typeset -n ref=ref
+>typeset -g var=RESET
+>typeset -n ref=var
+>typeset -g var=RESET
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ typeset one=ONE
+ for ref in one ref two; do print -r $ref; done
+1:for-loop variable is a reference, part 1
+>ONE
+*?*ref: invalid self reference
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ () {
+ typeset one=ONE
+ for ref in one ref two; do print -r ${(t)ref}; done
+ }
+1:for-loop variable is a reference, part 2
+>scalar-local
+*?*ref: invalid self reference
+
+ unset -n ref
+ unset one var
+ typeset -n ref=var
+ () {
+ typeset one=ONE
+ typeset -n ref=ref
+ for ref in one ref two; do
+ typeset -p ref
+ print -r $ref
+ done
+ typeset -p ref
+ }
+ typeset -p ref
+0:for-loop variable is a reference, part 3
+>typeset -n ref=one
+>ONE
+>typeset -n ref=ref
+>
+>typeset -n ref=two
+>
+>typeset -n ref=two
+>typeset -n ref=var
+
+ unset -n ref
+ unset one
+ typeset -n ref
+ () {
+ setopt localoptions warn_nested_var
+ typeset one=ONE
+ for ref in one two; do print -r ${(t)ref}; done
+ typeset -n ref
+ for ref in one two; do print -r ${(t)ref}; done
+ }
+0:for-loop variable is a reference, part 4, warnings
+>scalar-local
+>
+>scalar-local
+>
+*?*ref: global reference to local variable: one
+
+ typeset -n ptr='ary[$(echo 2)]'
+ typeset -a ary=(one two three)
+ print $ptr
+1:attempt deferred command substitution in subscript
+F:runs in `setopt noexec` so $(...) returns nothing
+*?*bad math expression: empty string
+
%clean