mirror of
https://github.com/zsh-users/zsh
synced 2024-09-30 05:06:09 +00:00
51945: assorted documentation improvements, bug fixes, and new test
1) Document the behavior of "typeset -n existing_var" (via Jun T. comment) 2) Prohibit "typeset -nm pattern" because, well, it's insane. Add test. 3) Improve doc for ${(!)ref} including ${{t!)ref} (Jun T.) 4) Fix doc for how-to unset of a named ref (Jun T.) 5) Allow "typeset +r -n ref" and "typeset +r +n ref" (Jun T.) 6) Fix "typeset -r -n ref=param" to create readonly references 7) Avoid accidental removal of PM_UNSET flag (Jun T.) and update test 8) Fix "typeset -gn ref=value" and add a test for it 9) Add tests for read-only reference behavior 10) Fix infinite recursion when resolving scope of an unset local named reference, add test.
This commit is contained in:
parent
5ff23c2c6d
commit
baa19d2a85
|
@ -1,5 +1,10 @@
|
|||
2023-07-26 Bart Schaefer <schaefer@zsh.org>
|
||||
|
||||
* 51945: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo,
|
||||
Src/builtin.c, Src/params.c, Test/K01nameref.ztst: improve named
|
||||
references documentation, fixes for typeset -r and -g behavior,
|
||||
fix unset reference behavior including scoping crash, more tests
|
||||
|
||||
* Shohei YOSHIDA: 51979: Completion/Linux/Command/_free: Update
|
||||
free completion for procps-ng version 4.0.3
|
||||
|
||||
|
|
|
@ -2060,6 +2060,11 @@ function unless `tt(-g -n)' is specified, and any local parameter (of
|
|||
any type) with the same var(name) supplants a named reference from a
|
||||
surrounding scope.
|
||||
|
||||
A scalar parameter, including an existing named reference, may be
|
||||
converted to a new named reference by `tt(typeset -n )var(name)', so
|
||||
the `tt(-p)' option must be included to display the value of a
|
||||
specific named reference var(name).
|
||||
|
||||
If no attribute flags are given, and either no var(name) arguments are
|
||||
present or the flag tt(+m) is used, then each parameter name printed is
|
||||
preceded by a list of the attributes of that parameter (tt(array),
|
||||
|
@ -2104,7 +2109,8 @@ is not used in this case).
|
|||
|
||||
If the tt(+g) flag is combined with tt(-m), a new local parameter is
|
||||
created for every matching parameter that is not already local. Otherwise
|
||||
tt(-m) applies all other flags or assignments to the existing parameters.
|
||||
tt(-m) applies all other flags or assignments to the existing parameters,
|
||||
except that the tt(-n) option cannot create named references in this way.
|
||||
|
||||
Except when assignments are made with var(name)tt(=)var(value), using
|
||||
tt(+m) forces the matching parameters and their attributes to be printed,
|
||||
|
|
|
@ -987,6 +987,11 @@ means the same thing as the more readable `(tt(%%qqq))'. The
|
|||
following flags are supported:
|
||||
|
||||
startitem()
|
||||
item(tt(!))(
|
||||
When the parameter being expanded is a named reference, the reference
|
||||
itself is examined and thus is em(not) resolved to its referent. In
|
||||
ksh emulation, the parens around this flag are optional.
|
||||
)
|
||||
item(tt(#))(
|
||||
Evaluate the resulting words as numeric expressions and interpret
|
||||
these as character codes. Output the corresponding characters. Note
|
||||
|
@ -1245,7 +1250,8 @@ item(tt(hideval))(
|
|||
for parameters with the `hideval' flag (tt(-H))
|
||||
)
|
||||
item(tt(nameref))(
|
||||
for named references having an empty value (tt(-n))
|
||||
for named references (tt(typeset -n)) either having an empty value or
|
||||
when combined with `tt(!)' as in `tt(${LPAR()!t)tt(RPAR()var(rname)})'
|
||||
)
|
||||
item(tt(special))(
|
||||
for special parameters defined by the shell
|
||||
|
|
|
@ -672,9 +672,9 @@ 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,
|
||||
use either `tt(unset -n )var(pname)' or one of:
|
||||
use either `tt(unset -n )var(pname)' (preferred) or one of:
|
||||
ifzman()
|
||||
example(tt(typeset -n )var(pname)
|
||||
example(tt(typeset -n )var(pname=)
|
||||
tt(typeset +n )var(pname))
|
||||
|
||||
followed by
|
||||
|
|
|
@ -2248,10 +2248,14 @@ typeset_single(char *cname, char *pname, Param pm, int func,
|
|||
zerrnam(cname, "%s: restricted", pname);
|
||||
return pm;
|
||||
}
|
||||
if ((pm->node.flags & PM_READONLY) &&
|
||||
(pm->node.flags & PM_NAMEREF & off)) {
|
||||
zerrnam(cname, "%s: read-only reference", pname);
|
||||
return pm;
|
||||
if ((pm->node.flags & PM_READONLY) && !(off & PM_READONLY) &&
|
||||
/* It seems as though these checks should not be specific to
|
||||
* PM_NAMEREF, but changing that changes historic behavior */
|
||||
((on & PM_NAMEREF) != (pm->node.flags & PM_NAMEREF) ||
|
||||
(asg && (pm->node.flags & PM_NAMEREF)))) {
|
||||
zerrnam(cname, "%s: read-only %s", pname,
|
||||
(pm->node.flags & PM_NAMEREF) ? "reference" : "variable");
|
||||
return NULL;
|
||||
}
|
||||
if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) {
|
||||
Param apm;
|
||||
|
@ -2693,7 +2697,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
|
|||
off |= bit;
|
||||
}
|
||||
if (OPT_MINUS(ops,'n')) {
|
||||
if ((on & ~PM_READONLY)|off) {
|
||||
if ((on|off) & ~PM_READONLY) {
|
||||
zwarnnam(name, "no other attributes allowed with -n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -3021,6 +3025,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
|
|||
/* With the -m option, treat arguments as glob patterns */
|
||||
if (OPT_ISSET(ops,'m')) {
|
||||
if (!OPT_ISSET(ops,'p')) {
|
||||
if (on & PM_NAMEREF) {
|
||||
/* It's generally unwise to mass-change the types of
|
||||
* parameters, but for namerefs it would be fatal */
|
||||
unqueue_signals();
|
||||
zerrnam(name, "invalid reference");
|
||||
return 1;
|
||||
}
|
||||
if (!(on|roff))
|
||||
printflags |= PRINT_TYPE;
|
||||
if (!on)
|
||||
|
@ -3104,13 +3115,25 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
|
|||
}
|
||||
if (hn) {
|
||||
/* namerefs always start over fresh */
|
||||
if (((Param)hn)->level >= locallevel) {
|
||||
if (((Param)hn)->level >= locallevel ||
|
||||
(!(on & PM_LOCAL) && ((Param)hn)->level < locallevel)) {
|
||||
Param oldpm = (Param)hn;
|
||||
if (!asg->value.scalar && oldpm->u.str)
|
||||
if (!asg->value.scalar &&
|
||||
PM_TYPE(oldpm->node.flags) == PM_SCALAR &&
|
||||
oldpm->u.str)
|
||||
asg->value.scalar = dupstring(oldpm->u.str);
|
||||
unsetparam_pm((Param)hn, 0, 1);
|
||||
/* Defer read-only error to typeset_single() */
|
||||
if (!(hn->flags & PM_READONLY))
|
||||
unsetparam_pm(oldpm, 0, 1);
|
||||
}
|
||||
hn = NULL;
|
||||
/* Passing a NULL pm to typeset_single() makes the
|
||||
* nameref read-only before assignment, which breaks
|
||||
* typeset -rn ref=var
|
||||
* so this is special-cased to permit that action
|
||||
* like assign-at-create for other parameter types.
|
||||
*/
|
||||
if (!(hn->flags & PM_READONLY))
|
||||
hn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
Src/params.c
14
Src/params.c
|
@ -546,7 +546,7 @@ getparamnode(HashTable ht, const char *nam)
|
|||
}
|
||||
}
|
||||
|
||||
if (hn && ht == realparamtab)
|
||||
if (hn && ht == realparamtab && !(hn->flags & PM_UNSET))
|
||||
hn = resolve_nameref((Param)hn, NULL);
|
||||
return hn;
|
||||
}
|
||||
|
@ -3729,7 +3729,9 @@ unsetparam_pm(Param pm, int altflag, int exp)
|
|||
char *altremove;
|
||||
|
||||
if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) {
|
||||
zerr("read-only variable: %s", pm->node.nam);
|
||||
zerr("read-only %s: %s",
|
||||
(pm->node.flags & PM_NAMEREF) ? "reference" : "variable",
|
||||
pm->node.nam);
|
||||
return 1;
|
||||
}
|
||||
if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
|
||||
|
@ -6182,8 +6184,12 @@ resolve_nameref(Param pm, const Asgment stop)
|
|||
seek = refname;
|
||||
}
|
||||
}
|
||||
else if (pm && !(stop && (stop->flags & PM_NAMEREF)))
|
||||
return (HashNode)pm;
|
||||
else if (pm) {
|
||||
if (!(stop && (stop->flags & PM_NAMEREF)))
|
||||
return (HashNode)pm;
|
||||
if (!(pm->node.flags & PM_NAMEREF))
|
||||
return (pm->level < locallevel ? NULL : (HashNode)pm);
|
||||
}
|
||||
if (seek) {
|
||||
queue_signals();
|
||||
/* pm->width is the offset of any subscript */
|
||||
|
|
|
@ -515,7 +515,7 @@ F:Same test, should part 5 output look like this?
|
|||
>ptr1=val
|
||||
>ptr2=
|
||||
>typeset -n ptr1=ptr2
|
||||
>typeset -n ptr2=''
|
||||
>typeset -n ptr2
|
||||
>typeset ptr2=val
|
||||
|
||||
if zmodload zsh/parameter; then
|
||||
|
@ -694,4 +694,72 @@ F:Checking for a bug in zmodload that affects later tests
|
|||
F:runs in `setopt noexec` so $(...) returns nothing
|
||||
*?*bad math expression: empty string
|
||||
|
||||
unset -n ref
|
||||
typeset -n ref=GLOBAL
|
||||
() {
|
||||
typeset -gn ref=RESET
|
||||
}
|
||||
typeset -p ref
|
||||
0:reset global reference within function
|
||||
>typeset -n ref=RESET
|
||||
|
||||
unset -n ref
|
||||
typeset -rn ref=RO
|
||||
typeset -p ref
|
||||
(typeset -n ref=RW)
|
||||
print status: $? expected: 1
|
||||
typeset +r -n ref
|
||||
typeset -p ref
|
||||
typeset -r +n ref
|
||||
typeset -p ref
|
||||
(typeset -rn ref)
|
||||
print status: $? expected: 1
|
||||
typeset +r -n ref=RW # Assignment occurs after type change,
|
||||
typeset -p ref RO # so RO=RW here. Potentially confusing.
|
||||
typeset -r -n ref=RX # No type change, so referent changes ...
|
||||
typeset -p ref RO # ... and previous refererent does not.
|
||||
typeset +rn ref=RW # Here ref=RW, again type changed first.
|
||||
typeset -p ref
|
||||
0:add and remove readonly attribute with references
|
||||
>typeset -rn ref=RO
|
||||
*?*: ref: read-only reference
|
||||
>status: 1 expected: 1
|
||||
>typeset -n ref=RO
|
||||
>typeset -r ref=RO
|
||||
*?*: ref: read-only variable
|
||||
>status: 1 expected: 1
|
||||
>typeset -n ref=RO
|
||||
>typeset -g RO=RW
|
||||
>typeset -rn ref=RX
|
||||
>typeset -g RO=RW
|
||||
>typeset ref=RW
|
||||
|
||||
() {
|
||||
typeset -n r1 r2=
|
||||
typeset -p r1 r2
|
||||
print -- ${(!)r1-unset}
|
||||
print -- ${+r1}
|
||||
typeset -p r1
|
||||
}
|
||||
0:unset nameref remains unset when resolved
|
||||
F:relies on global TYPESET_TO_UNSET in %prep
|
||||
>typeset -n r1
|
||||
>typeset -n r2=''
|
||||
>unset
|
||||
>0
|
||||
>typeset -n r1
|
||||
|
||||
bar=xx
|
||||
typeset -n foo=bar
|
||||
() { typeset -n foo; foo=zz; foo=zz; print $bar $zz }
|
||||
() { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz }
|
||||
0:regression: local nameref may not in-scope a global parameter
|
||||
F:previously this could create an infinite recursion and crash
|
||||
>xx
|
||||
>xx zz
|
||||
|
||||
typeset -nm foo=bar
|
||||
1:create nameref by pattern match not allowed
|
||||
*?*typeset:1: invalid reference
|
||||
|
||||
%clean
|
||||
|
|
Loading…
Reference in a new issue