diff options
| author | Philippe Altherr <philippe.altherr@gmail.com> | 2026-06-09 20:41:40 -0700 |
|---|---|---|
| committer | Bart Schaefer <schaefer@zsh.org> | 2026-06-09 20:47:57 -0700 |
| commit | 934b1551092546b7902392c4ccf91a87dbb24343 (patch) | |
| tree | a5abad92d50da0e1ee44760faba85e53895a11e0 | |
| parent | 54722: Fix nameref issues in builtin "unset" (diff) | |
| download | zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar.gz zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar.bz2 zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar.lz zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar.xz zsh-934b1551092546b7902392c4ccf91a87dbb24343.tar.zst zsh-934b1551092546b7902392c4ccf91a87dbb24343.zip | |
54727: Implement references to positional parameters
| -rw-r--r-- | Src/params.c | 67 | ||||
| -rw-r--r-- | Test/K01nameref.ztst | 95 |
2 files changed, 150 insertions, 12 deletions
diff --git a/Src/params.c b/Src/params.c index f838fa73f..e5bbe9cb9 100644 --- a/Src/params.c +++ b/Src/params.c @@ -229,6 +229,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 = @@ -489,6 +491,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 @@ -861,6 +865,9 @@ createparamtable(void) } argvparam = (Param) &argvparam_pm; + argnparams = zshcalloc(8 * sizeof(Param)); + argnparams_size = 8; + argnparams[0] = (Param) paramtab->getnode(paramtab, "0"); noerrs = 2; @@ -4903,6 +4910,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' */ /**/ @@ -6295,6 +6345,23 @@ resolve_nameref_rec(Param pm, const Param stop, int keep_lastref) if ((pm = loadparamnode(paramtab, upscope(pm, ref), refname)) && pm != stop && !(pm->node.flags & PM_UNSET)) pm = resolve_nameref_rec(pm, stop, keep_lastref); + } 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 0336a507d..6cfe6d1d0 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -1419,24 +1419,25 @@ 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 2 02 \! \? \$ - _ do - edgelocal $edge - edgeupper $edge + edgelocal $edge foo + edgeupper $edge foo done unfunction edgelocal edgeupper 0q:references to builtin specials -F:BUG:References to 1, 2, ... are supported but don't work (always expand to "") >typeset -n x=argv ->argv +>argv foo >typeset -n x=ARGC ->1 +>2 >typeset -n x=0 >edgelocal ->typeset -n x=1 -> ->typeset -n x=01 -> +>typeset -n x=00 +>edgelocal +>typeset -n x=2 +>foo +>typeset -n x=02 +>foo >typeset -n x=! >0 >typeset -un x=! @@ -1466,8 +1467,78 @@ F:BUG:References to 1, 2, ... are supported but don't work (always expand to "") ?edgelocal: invalid variable name: # ?edgeupper: invalid variable name: # ?edgeupper: invalid variable name: 0 -?edgeupper: invalid variable name: 1 -?edgeupper: invalid variable name: 01 +?edgeupper: invalid variable name: 00 +?edgeupper: invalid variable name: 2 +?edgeupper: invalid variable name: 02 + + 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 |
