summaryrefslogtreecommitdiff
path: root/Src/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/subst.c')
-rw-r--r--Src/subst.c38
1 files changed, 31 insertions, 7 deletions
diff --git a/Src/subst.c b/Src/subst.c
index 7a4b433bc..974d6171e 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_WANTVALS)
+ 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) {