[PATCH] Fix saving and restoring of subscripted inline assignments
Philippe Altherr <[email protected]> Fri, 10 Jul 2026 13:44:53 +0200
| Newsgroups | gmane.comp.shells.zsh.devel |
|---|---|
| Message-ID | <CAGdYchsePaCCbZomQAwFWW5pZyDUSP+WQmk8gEW1CmNZi5jmAQ@mail.gmail.com> |
When an external command is run with a subscripted inline assignment, the
assigned parameter is restored to its previous value after the command
returns:
% export var=12345
% var[2,4]=ABC printenv var
1ABC5
% typeset -p var
export var=12345
The same isn't true for builtins and user defined functions:
% myprintenv() { echo ${(P)1} }
% var[2,4]=ABC myprintenv var
1ABC5
% typeset -p var
export var=1ABC5
The former works because the parameter is only assigned in the forked
process used to run the external command. Thus the value of the parameter
remains unchanged in the main shell.
In the case of the latter, everything runs in the same process. There is
code to save
<https://github.com/zsh-users/zsh/blob/489436767786ec8c8e16436d00ff3c7c4ce0a380/Src/exec.c#L4467>
and restore
<https://github.com/zsh-users/zsh/blob/489436767786ec8c8e16436d00ff3c7c4ce0a380/Src/exec.c#L4521>
parameters modified by inline assignments but that code doesn't account for
the fact that the assigned parameters may be subscripted. In the example
above, it tries to save and restore the parameter var[2,4], instead of the
parameter var, which naturally fails. This looks more like a bug than a
feature. The patch below fixes it by taking into account the possible
presence of subscripts.
- Fix saving and restoring of subscripted inline assignments
<https://github.com/zsh-users/zsh/compare/master...paltherr:zsh:fix-subscripted-inline-assignments>
Philippe
fix-subscripted-inline-assignments.txt
(text/plain, 1.5 KB)
diff --git a/Src/exec.c b/Src/exec.c
index 17899262d..20caf40d1 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4467,14 +4467,16 @@ static void
save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
{
Param pm;
- char *s;
wordcode ac;
*restore_p = newlinklist();
*remove_p = newlinklist();
while (wc_code(ac = *pc) == WC_ASSIGN) {
- s = ecrawstr(state->prog, pc + 1, NULL);
+ char *s = ecrawstr(state->prog, pc + 1, NULL);
+ char *ss = itype_end(s, INAMESPC, 0);
+ int slen = *ss == '[' || *ss == Inbrack ? ss - s : strlen(s);
+ addlinknode(*remove_p, s = dupstring_wlen(s, slen));
if ((pm = (Param) paramtab->getnode(paramtab, s))) {
Param tpm = NULL;
if (pm->env)
@@ -4503,11 +4505,9 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p)
tpm->node.nam = pm->node.nam;
copyparam(tpm, pm, 1);
}
- addlinknode(*remove_p, dupstring(s));
if (tpm)
addlinknode(*restore_p, tpm);
- } else
- addlinknode(*remove_p, dupstring(s));
+ }
pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ?
3 : WC_ASSIGN_NUM(ac) + 2);
diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst
index d653d75b3..3cb73a3fe 100644
--- a/Test/A06assign.ztst
+++ b/Test/A06assign.ztst
@@ -539,12 +539,16 @@
call
HELLO=${HELLO}liness call
call
+ HELLO[2,4]=oo call
+ call
unset HELLO
0:save and restore when using original value in temporary
>world
>universe
>world
>worldliness
+>world
+>wood
>world
(integer i n x