Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: [PATCH] Implement references to positional parameters
- X-seq: zsh-workers 54365
- From: Philippe Altherr <philippe.altherr@xxxxxxxxx>
- To: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: Re: [PATCH] Implement references to positional parameters
- Date: Thu, 16 Apr 2026 16:42:46 +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-20240605; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :dkim-signature; bh=W6DtFwlHzIZpuQycQ6Xi8P8Jja3Cx11l6pmzbHD+iuY=; fh=BgAYDYpL6Ne/A5nWEMVJiHiBtrz8Imz3uf26RDwgQX4=; b=Jq4Yvji6HDcf/4cBnfS9L6PoV/TmnzFzf0Z+BITxTH0fWPepyxFIXGSLG82FQ6TX0h f4PwP7VyOPzAbyBrsYFIokGBo5azmZEmDoIunw2APCdPNX9WZ02q3R3R59TdCqkkLfFU qQ/4SXQRyX0z58D8K3JQ4sxHMExQBZSKZnBSkUhEayyRRCuZur8SA3d+B6mhxpZpodU6 PcIIAm22jgdiXUiuKlQRjV9YCEU/XX+eK6jS1+8C2fp3lyH7s1cJ2iqTS6iCJrH7zBsD TRAbJxdfs4KoFSf+27heDR0AJmfWq/PEeZfGY4M+mbW8WU7+xkHgdE0ibTmSlFiovSbA PqMw==; darn=zsh.org
- Arc-seal: i=1; a=rsa-sha256; t=1776350578; cv=none; d=google.com; s=arc-20240605; b=VPQ6NpdXZy9tWLd8eTIgzWNBV8rDEbkAgmSdsC37hQH0Ngi7DkHQEBu3YNUELCaQ53 UXNuoOHRmduW7z66+FVKBQGH9bqO4zAfQrBW30RyBNuGchu0mJ6XDqSvqPbjh6xu/jCO ou/vMxpaWh+ac5gqD1hoD+tndKDX+tEb45+86UWirjhQDnOo3iLSURjpXhEzKy/V5ukO pRuRnJVTywPFg0Uy2AXU9rt7yHk0yIrGL6ZmDdfdogbuDxkIdoopk3IaBjtJU2392lUs +OY21njQHZoe/rb4m6PfqkRiAds5f1zpce/6hD4l6PprONsKBJAWgelZCeG2oo0l6OW5 i+ig==
- Archived-at: <https://zsh.org/workers/54365>
- In-reply-to: <CAGdYcht3HsZKEkzj7_czPdoXUAnWA0cv7iht3nPFzh3qNiBCXQ@mail.gmail.com>
- List-id: <zsh-workers.zsh.org>
- References: <CAGdYcht3HsZKEkzj7_czPdoXUAnWA0cv7iht3nPFzh3qNiBCXQ@mail.gmail.com>
diff --git a/Src/params.c b/Src/params.c
index 319fadfe5..99cfe6ef8 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -224,6 +224,8 @@ static const struct gsu_integer ttyidle_gsu =
static const struct gsu_scalar argzero_gsu =
{ argzerogetfn, argzerosetfn, nullunsetfn };
+static const struct gsu_scalar argn_gsu =
+{ argngetfn, argnsetfn, argnunsetfn };
static const struct gsu_scalar username_gsu =
{ usernamegetfn, usernamesetfn, stdunsetfn };
static const struct gsu_scalar dash_gsu =
@@ -484,6 +486,8 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
(zsfree((PM)->u.str), (PM)->u.str = (S)))
static Param argvparam;
+static Param *argnparams;
+static size_t argnparams_size;
/*
* Lists of references to nested variables ("Param" instances) indexed
@@ -856,6 +860,9 @@ createparamtable(void)
}
argvparam = (Param) &argvparam_pm;
+ argnparams = zshcalloc(8 * sizeof(Param));
+ argnparams_size = 8;
+ argnparams[0] = (Param) paramtab->getnode(paramtab, "0");
noerrs = 2;
@@ -4986,6 +4993,49 @@ argzerogetfn(UNUSED(Param pm))
return argzero;
}
+/* Function to get value for positional parameters */
+
+/**/
+static char *
+argngetfn(Param pm)
+{
+ return arrlen_gt(pparams, pm->u.val - 1) ?
+ pparams[pm->u.val - 1] : (char *) hcalloc(1);
+}
+
+/* Function to set value for positional parameters */
+
+/**/
+static void
+argnsetfn(Param pm, char *x)
+{
+ int len = arrlen(pparams);
+ int ppar = pm->u.val;
+ if (ppar <= len)
+ zsfree(pparams[ppar - 1]);
+ else if (x) {
+ int i;
+ pparams = (char **) zrealloc(pparams, sizeof(char *) * ppar + 1);
+ for (i = len; i < ppar - 1; i++)
+ pparams[i] = ztrdup("");
+ pparams[ppar] = 0;
+ }
+ if (x) {
+ pparams[ppar - 1] = ztrdup(x);
+ zsfree(x);
+ } else if (ppar <= len)
+ memmove(pparams + ppar - 1, pparams + ppar, (len - ppar + 1) * sizeof(char *));
+}
+
+/* Function to unset positional parameters */
+
+/**/
+static void
+argnunsetfn(Param pm, UNUSED(int exp))
+{
+ argnsetfn(pm, NULL);
+}
+
/* Function to get value for special parameter `HISTSIZE' */
/**/
@@ -6388,6 +6438,23 @@ resolve_nameref_rec(Param pm, const Param stop, int keep_lastref)
pm = resolve_nameref_rec(pm, stop, keep_lastref);
ref->node.flags &= ~PM_TAGGED;
}
+ } else if (idigit(*refname)) {
+ int ppar = zstrtol(refname, NULL, 10);
+ if (ppar >= argnparams_size) {
+ size_t old_size = argnparams_size;
+ size_t new_size = argnparams_size = MAX(2 * old_size, ppar);
+ argnparams = zrealloc(argnparams, new_size * sizeof(Param));
+ memset(argnparams + old_size, 0,
+ (new_size - old_size) * sizeof(Param));
+ }
+ if (!(pm = argnparams[ppar])) {
+ pm = argnparams[ppar] = zshcalloc(sizeof(*pm));
+ pm->node.nam = zalloc(snprintf(NULL, 0, "%d", ppar) + 1);
+ sprintf(pm->node.nam, "%d", ppar);
+ pm->node.flags = PM_SCALAR | PM_SPECIAL;
+ pm->u.val = ppar;
+ pm->gsu.s = &argn_gsu;
+ }
} else if (keep_lastref)
pm = ref;
unqueue_signals();
diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst
index f772d583f..68f56a398 100644
--- a/Test/K01nameref.ztst
+++ b/Test/K01nameref.ztst
@@ -1339,14 +1339,14 @@ F:previously this could create an infinite recursion and crash
edgelocal() ( local -n x=$1; typeset -p x; print -r $x )
edgeupper() ( local -nu x=$1; typeset -p x; print -r $x )
- for edge in argv ARGC \@ \* \# 0 1 01 \! \? - _
+ for edge in argv ARGC \@ \* \# 0 00 1 01 \! \? - _
do
edgelocal $edge
edgelocal "$edge""[1]"
edgeupper $edge
done
0:references to builtin specials
-F:Subscripting on 1 01 ! ? - should print first character but do not
+F:Subscripting on 00 1 01 ! ? - should print first character but do not
>typeset -n x=argv
>argv
>typeset -n x='argv[1]'
@@ -1359,12 +1359,16 @@ F:Subscripting on 1 01 ! ? - should print first character but do not
>edgelocal
>typeset -n x='0[1]'
>e
->typeset -n x=1
+>typeset -n x=00
+>edgelocal
+>typeset -n x='00[1]'
>
+>typeset -n x=1
+>1
>typeset -n x='1[1]'
>
>typeset -n x=01
->
+>01
>typeset -n x='01[1]'
>
>typeset -n x=!
@@ -1403,6 +1407,7 @@ F:Subscripting on 1 01 ! ? - should print first character but do not
?edgelocal: invalid name reference: #[1]
?edgeupper: invalid name reference: #
?edgeupper: invalid name reference: 0
+?edgeupper: invalid name reference: 00
?edgeupper: invalid name reference: 1
?edgeupper: invalid name reference: 01
@@ -1418,6 +1423,75 @@ F:$$[1] reference should print the first digit of $$ but prints nothing
>$$[1]
>$$
+ tst() {
+ typeset -n ref0=0 2>&1
+ typeset -n ref00=00 2>&1
+ typeset -n ref2=2 2>&1
+ typeset -n ref02=02 2>&1
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02
+ ref0=TST
+ ref2=BBB
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02
+ ref00=Tst
+ ref02=Bbb
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02; typeset -p -- 0 00 2 02 2>&1
+ unset ref0
+ unset ref2
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02; typeset -p -- 0 00 2 02 2>&1
+ ref0=tst
+ ref2=bbb
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02; typeset -p -- 0 00 2 02 2>&1
+ { ref0=(TST TST) 2>&1 } always { TRY_BLOCK_ERROR=0 }
+ { ref2=(BBB BBB) 2>&1 } always { TRY_BLOCK_ERROR=0 }
+ echo ${(q)0} ${(q)*} - $ref0 - $ref00 - $ref2 - $ref02; typeset -p -- 0 00 2 02 2>&1
+ typeset -n ref5=5 2>&1
+ ref5=zzz
+ echo ${(q)0} ${(q)*}
+ unset ref2
+ echo ${(q)0} ${(q)*}
+ unset ref2
+ echo ${(q)0} ${(q)*}
+ unset ref2
+ echo ${(q)0} ${(q)*}
+ unset ref2
+ echo ${(q)0} ${(q)*}
+ unset ref2
+ echo ${(q)0} ${(q)*}
+ }
+ tst aaa bbb ccc
+ unfunction tst
+0:references to positional parameters
+>tst aaa bbb ccc - tst - tst - bbb - bbb
+>TST aaa BBB ccc - TST - TST - BBB - BBB
+>Tst aaa Bbb ccc - Tst - Tst - Bbb - Bbb
+>tst:typeset:11: no such variable: 00
+>tst:typeset:11: no such variable: 2
+>tst:typeset:11: no such variable: 02
+>typeset -g 0=Tst
+>Tst aaa ccc - Tst - Tst - ccc - ccc
+>tst:typeset:14: no such variable: 00
+>tst:typeset:14: no such variable: 2
+>tst:typeset:14: no such variable: 02
+>typeset -g 0=Tst
+>tst aaa bbb - tst - tst - bbb - bbb
+>tst:typeset:17: no such variable: 00
+>tst:typeset:17: no such variable: 2
+>tst:typeset:17: no such variable: 02
+>typeset -g 0=tst
+>tst:18: 0: attempt to assign array value to non-array
+>tst:19: 2: attempt to assign array value to non-array
+>tst aaa bbb - tst - tst - bbb - bbb
+>tst:typeset:20: no such variable: 00
+>tst:typeset:20: no such variable: 2
+>tst:typeset:20: no such variable: 02
+>typeset -g 0=tst
+>tst aaa bbb '' '' zzz
+>tst aaa '' '' zzz
+>tst aaa '' zzz
+>tst aaa zzz
+>tst aaa
+>tst aaa
+
#
# The following tests are run in interactive mode, using PS1 as an
# assignable special with side-effects. This crashed at one time.
Messages sorted by:
Reverse Date,
Date,
Thread,
Author