diff --git a/Src/params.c b/Src/params.c index c9f4b3017..85eaee609 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2144,7 +2144,10 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) int isvarat; isvarat = (t[0] == '@' && !t[1]); - pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); + if (flags & SCANPM_NONAMEREF) + pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); + else + pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); if (sav) *s = sav; *pptr = s; @@ -2155,7 +2158,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); - if (pm->node.flags & PM_NAMEREF) { + if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) { char *refname = GETREFNAME(pm); if (refname && *refname) { /* only happens for namerefs pointing to array elements */ diff --git a/Src/subst.c b/Src/subst.c index 7a4b433bc..a86005340 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1818,14 +1818,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Use for the (k) flag. Goes down into the parameter code, * sometimes. */ - char hkeys = 0; + int hkeys = 0; /* * Used for the (v) flag, ditto. Not quite sure why they're * separate, but the tradition seems to be that things only * get combined when that makes the result more obscure rather * than less. */ - char hvals = 0; + int hvals = 0; /* * Whether we had to evaluate a subexpression, i.e. an * internal ${...} or $(...) or plain $pm. We almost don't @@ -1870,8 +1870,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, INAMESPC, 1) == s && *s != '#' && c != Pound && - !IS_DASH(c) && + if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s && + *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && c != '*' && c != Star && c != '@' && c != '{' && @@ -1891,15 +1891,30 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, s++; /* * In ksh emulation a leading `!' is a special flag working - * sort of like our (k). + * sort of like our (k). This is true only for arrays or + * associative arrays and only with subscripts [*] or [@], + * so zsh's implementation is approximate. For namerefs + * in ksh, ${!ref} substitues the parameter name at the + * end of any chain of references, rather than the value. + * * TODO: this is one of very few cases tied directly to * the emulation mode rather than an option. Since ksh * doesn't have parameter flags it might be neater to * handle this with the ^, =, ~ stuff, below. */ if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) { - hkeys = SCANPM_WANTKEYS; + hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF; s++; + /* There's a slew of other special bash meanings of parameter + * references that start with "!": + * ${!name} == ${(P)name} (when name is not a nameref) + * ${!name*} == ${(k)parameters[(I)name*]} + * ${!name@} == ${(@k)parameters[(I)name*]} + * ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too) + * ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh) + * + * See also workers/34390, workers/34397, workers/34408. + */ } else if (c == '(' || c == Inpar) { char *t, sav; int tt = 0; @@ -2154,10 +2169,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, escapes = 1; break; + case '!': + if ((hkeys|hvals) & ~SCANPM_NONAMEREF) + goto flagerr; + hkeys = SCANPM_NONAMEREF; + break; case 'k': + if (hkeys & ~SCANPM_WANTKEYS) + goto flagerr; hkeys = SCANPM_WANTKEYS; break; case 'v': + if (hvals & ~SCANPM_WANTKEYS) + goto flagerr; hvals = SCANPM_WANTVALS; break; @@ -2308,7 +2332,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * Look for special unparenthesised flags. * TODO: could make these able to appear inside parentheses, too, - * i.e. ${(^)...} etc. + * i.e. ${(^)...} etc., but ${(~)...} already has another meaning. */ for (;;) { if ((c = *s) == '^' || c == Hat) { diff --git a/Src/zsh.h b/Src/zsh.h index 0de1f7afb..f3a777045 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1964,6 +1964,7 @@ struct tieddata { #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ #define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ #define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ +#define SCANPM_NONAMEREF (1<<13) /* named references are not followed */ /* "$foo[@]"-style substitution * Only sign bit is significant