Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] Don't export hidden parameters
- X-seq: zsh-workers 54987
- From: Philippe Altherr <philippe.altherr@xxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: [PATCH] Don't export hidden parameters
- Date: Sun, 19 Jul 2026 15:02:53 +0200
- Arc-authentication-results: i=1; mx.google.com; arc=none
- Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=to:subject:message-id:date:from:mime-version:dkim-signature; bh=7ChUR7w16mN/vXJ9yz/TQ9mr6fCdJfhVirUdeRTCvXI=; fh=BgAYDYpL6Ne/A5nWEMVJiHiBtrz8Imz3uf26RDwgQX4=; b=Us/kZvQ/gBk6C4ZxGpwyJNEtUJ6g2LOLUBhZq7ni72MGfXmsXCeE2rdy53rX2dqgLz IT78FZtTBNVHwy3JsZrElLsNhIzkS32ueFEt7svkpUOSYVTYVVzHjAzRN/vd0PLS3kVM 5MILUffkWc8Q0zlZJiRAfXLpWULpaz1bDYGteBs6s7KeZZTBVbxJMm//PuKfH8e1FSrA 1bhFut4/BMfSKwh0nk7e4eex9+sS6cqrv6RzTsVJ4dqc5pizrZelOCWYfzEn2PgOQMoQ 5tSxao4LpmPwAJB2/nSNBE9GbGmv1Shx6E+v9mW2ClCP5xGgRukQt8PZr2P/KPEE7H+U THVA==; darn=zsh.org
- Arc-seal: i=1; a=rsa-sha256; t=1784466185; cv=none; d=google.com; s=arc-20260327; b=GZcE8V4wMnFfcPChHQYhjKfEvwG17edjWARSrXpbIsnB9vJLZSl2wOroOAcIA/ny8f 0IcZ+l10Xs/BivyAfvERZJG5MhXBUYfTc1jyRNVILpFoNJbKEeN97EzN62UlPE7N7KKg 9dJCYsG2/6rJ1dsNFTIU/TlN8pJy5KuYNlbR4ZdE8wFpg4HLr1y9inPhfqVJrSDqJ30I OtpYBhyacmmiXjCOsarX1y1/LZBfEcSTtGxHjfXiN3RBfTz6HP4s+ZNm/ccVHb2KfrOF mFyvcUiZo7n98uCAyy5EUQ3HA8Iz/17d2hhdDId1MfmZxIavWulczsLD5UQnXF2DEkbI P8XQ==
- Archived-at: <https://zsh.org/workers/54987>
- List-id: <zsh-workers.zsh.org>
Whenever an -x flagged parameter is updated, the precomputed environment must be updated. However, if the updated -x flagged parameter is hidden by a same name local parameter, then the -x flagged parameter is not supposed to show up in the environment and therefore no environment update should be performed.
There are currently two ways to update hidden parameters. In both cases, the current implementation wrongly updates the precomputed environment with an entry that shouldn't be there.
One way relies on references:
% export VAR=foo
% typeset -n ref=VAR;
% () { local VAR=hide; ref=bar; printenv VAR }
bar
The other one relies on tied parameters:
% export -T VAR=aa:bb var
% () { local -x VAR=foo; var=(cc dd); printenv VAR }
cc
Some combinations of references and tied parameters require additional changes (see tests in the patch).
The steps in the GitHub patch exhibit why each change is needed.
Philippe
diff --git a/Src/builtin.c b/Src/builtin.c
index 2e3e5752a..7a19c051a 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2262,14 +2262,14 @@ typeset_single(char *cname, char *pname, Param pm, int func,
x = zarrdup(x);
(*pm->gsu.a->setfn)(pm, x);
} else if (pm->ename && x)
- arrfixenv(pm->ename, x);
+ arrfixenv(NULL, pm, x);
} else if (PM_TYPE(pm->node.flags) == PM_SCALAR && pm->ename &&
(apm =
(Param) paramtab->getnode(paramtab, pm->ename))) {
x = (*apm->gsu.a->getfn)(apm);
uniqarray(x);
if (x)
- arrfixenv(pm->node.nam, x);
+ arrfixenv(pm, NULL, x);
}
}
if (OPT_ISSET(ops,'p')) {
@@ -2292,7 +2292,8 @@ typeset_single(char *cname, char *pname, Param pm, int func,
}
if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
if (pm->node.flags & PM_EXPORTED) {
- if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg))
+ if (!(pm->node.flags & PM_UNSET) &&
+ !pm->env && !ASG_VALUEP(asg) && !ishidden(pm))
addenv(pm, getsparam(pname));
} else if (pm->env && !(pm->node.flags & PM_HASHELEM))
delenv(pm);
diff --git a/Src/params.c b/Src/params.c
index e111b28c7..700df1c47 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2838,7 +2838,8 @@ assignstrvalue(Value v, char *val, int flags)
!(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) ||
(v->pm->node.flags & PM_ARRAY) || v->pm->ename))
return;
- export_param(v->pm);
+ if (!ishidden(v->pm))
+ export_param(v->pm);
}
/**/
@@ -4027,7 +4028,7 @@ arrsetfn(Param pm, char **x)
uniqarray(x);
/* Arrays tied to colon-arrays may need to fix the environment */
if (pm->ename && x)
- arrfixenv(pm->ename, x);
+ arrfixenv(NULL, pm, x);
}
/* Function to get value of an association parameter */
@@ -4263,7 +4264,7 @@ arrvarsetfn(Param pm, char **x)
*dptr = x;
if (pm->ename) {
if (x)
- arrfixenv(pm->ename, x);
+ arrfixenv(NULL, pm, x);
else if (*dptr == path)
pathchecked = path;
}
@@ -4292,7 +4293,7 @@ colonarrsetfn(Param pm, char *x)
*dptr = colonsplit(x, pm->node.flags & PM_UNIQUE);
else
*dptr = mkarray(NULL);
- arrfixenv(pm->node.nam, *dptr);
+ arrfixenv(pm, NULL, *dptr);
zsfree(x);
}
@@ -4338,7 +4339,7 @@ tiedarrsetfn(Param pm, char *x)
} else
*dptr->arrptr = NULL;
if (pm->ename)
- arrfixenv(pm->node.nam, *dptr->arrptr);
+ arrfixenv(pm, NULL, *dptr->arrptr);
}
/**/
@@ -5276,18 +5277,29 @@ pipestatsetfn(UNUSED(Param pm), char **x)
numpipestats = 0;
}
+/* Updates the environment string of a tied scalar parameter.
+ *
+ * pm: the tied scalar parameter or NULL
+ * apm: the tied array parameter if pm is NULL or NULL
+ * t: the tied array elements
+ */
+
/**/
void
-arrfixenv(char *s, char **t)
+arrfixenv(Param pm, Param apm, char **t)
{
- Param pm;
int joinchar;
if (t == path)
cmdnamtab->emptytable(cmdnamtab);
- pm = (Param) paramtab->getnode(paramtab, s);
-
+
+ if (!pm &&
+ (!(pm = (Param) realparamtab->getnode(realparamtab, apm->ename)) ||
+ PM_TYPE(pm->node.flags) != PM_SCALAR ||
+ !pm->ename || strcmp(pm->ename, apm->node.nam)))
+ return;
+
/*
* Only one level of a parameter can be exported. Unless
* ALLEXPORT is set, this must be global.
@@ -5304,7 +5316,7 @@ arrfixenv(char *s, char **t)
* Do not "fix" parameters that were not exported
*/
- if (!(pm->node.flags & PM_EXPORTED))
+ if (!(pm->node.flags & PM_EXPORTED) || ishidden(pm))
return;
if (pm->node.flags & PM_SPECIAL)
@@ -6464,6 +6476,15 @@ upscope(Param pm, const Param ref)
return pm;
}
+/* Return whether the parameter is hidden by a same name local paramater. */
+
+/**/
+mod_export int
+ishidden(Param pm)
+{
+ return pm != (Param) realparamtab->getnode2(realparamtab, pm->node.nam);
+}
+
/**/
static int
valid_refname(char *val, int flags)
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index 952bbd30c..d49ff1c50 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -2348,4 +2348,51 @@ F:converting from association/array to string should work here too
>B:16#FF
>C:16#FF
+ show() {
+ printf "%2s: V1=%2s/%2s V2=%2s/%2s\n" \
+ $1 "$V1" "$(printenv V1)" "$V2" "$(printenv V2)"
+ }
+ local V1=A1
+ local -x V2=A2
+ local -n R1=V1 R2=V2
+ show @1
+ () {
+ local V1=B1
+ local -x V2=B2
+ show @2
+ export R1
+ R2=C2
+ show @3
+ }
+ show @4
+ unfunction show
+0:Hidden parameters aren't exported (part 1)
+>@1: V1=A1/ V2=A2/A2
+>@2: V1=B1/ V2=B2/B2
+>@3: V1=B1/ V2=B2/B2
+>@4: V1=A1/A1 V2=C2/C2
+
+ show() {
+ printf "%2s: V1=%-5s/%-5s V2=%-5s/%-5s V3=%-5s/%-5s\n" \
+ $1 "$V1" "$(printenv V1)" "$V2" "$(printenv V2)" "$V3" "$(printenv V3)"
+ }
+ local -x -T V1=A1:1A v1
+ local -x -T V2=A2:2A v2
+ local -n R2=V2 R3=V3
+ local -x -T R3=A3:3A v3
+ show @1
+ () {
+ local -x V1=B1 V2=B2 V3=B3
+ show @2
+ v1=(C1 1C) R2=C2:2C v3=(C3 3C)
+ show @3
+ }
+ show @4
+ unfunction show
+0:Hidden parameters aren't exported (part 2)
+>@1: V1=A1:1A/A1:1A V2=A2:2A/A2:2A V3=A3:3A/A3:3A
+>@2: V1=B1 /B1 V2=B2 /B2 V3=B3 /B3
+>@3: V1=B1 /B1 V2=B2 /B2 V3=B3 /B3
+>@4: V1=C1:1C/C1:1C V2=C2:2C/C2:2C V3=C3:3C/C3:3C
+
%clean
Messages sorted by:
Reverse Date,
Date,
Thread,
Author