From ea0bd72dd84a783355969e0a9a1584ce7b1ea08b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:16:31 -0800 Subject: 51485: module for several ksh93 features, mostly enabled only in ksh emulation. --- Src/Modules/ksh93.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 Src/Modules/ksh93.c (limited to 'Src/Modules/ksh93.c') diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c new file mode 100644 index 000000000..9dc75c93c --- /dev/null +++ b/Src/Modules/ksh93.c @@ -0,0 +1,265 @@ +/* + * ksh93.c - support for more ksh93 features + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2022 Barton E. Schaefer + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Barton E. Schaefer or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, or + * consequential damages arising out of the use of this software and its + * documentation, even if Barton E. Schaefer and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Barton E. Schaefer and the Zsh Development Group + * specifically disclaim any warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose. + * The software provided hereunder is on an "as is" basis, and + * Barton E. Schaefer and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +#include "ksh93.mdh" +#include "ksh93.pro" + +/* Implementing "namespace" requires creating a new keword. Hrm. */ + +/* + * Standard module configuration/linkage + */ + +static struct builtin bintab[] = { + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gr", "n") +}; + +#include "zsh.mdh" + +static void +edcharsetfn(Param pm, char *x) +{ + /* + * To make this work like ksh, we must intercept $KEYS before the widget + * is looked up, so that changing the key sequence causes a different + * widget to be substituted. Somewhat similar to "bindkey -s". + * + * Ksh93 adds SIGKEYBD to the trap list for this purpose. + */ + ; +} + +static char ** +matchgetfn(Param pm) +{ + char **zsh_match = getaparam("match"); + + /* For this to work accurately, ksh emulation should always imply + * that the (#m) and (#b) extendedglob operators are enabled. + * + * When we have a 0th element (ksharrays), it is $MATCH. Elements + * 1st and larger mirror the $match array. + */ + + if (pm->u.arr) + freearray(pm->u.arr); + if (zsh_match && *zsh_match) { + if (isset(KSHARRAYS)) { + char **ap = + (char **) zalloc(sizeof(char *) * (arrlen(zsh_match)+1)); + pm->u.arr = ap; + *ap++ = ztrdup(getsparam("MATCH")); + while (*zsh_match) + *ap = ztrdup(*zsh_match++); + } else + pm->u.arr = zarrdup(zsh_match); + } else if (isset(KSHARRAYS)) { + pm->u.arr = mkarray(ztrdup(getsparam("MATCH"))); + } else + pm->u.arr = NULL; + + return arrgetfn(pm); +} + +static const struct gsu_scalar constant_gsu = + { strgetfn, NULL, nullunsetfn }; + +static const struct gsu_scalar sh_edchar_gsu = + { strvargetfn, edcharsetfn, nullunsetfn }; +static const struct gsu_scalar sh_edmode_gsu = + { strgetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_array sh_match_gsu = + { matchgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_scalar sh_name_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_scalar sh_subscript_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; + +static char *sh_name; +static char *sh_subscript; +static char *sh_edchar; +static char sh_edmode[2]; + +/* + * Some parameters listed here do not appear in ksh93.mdd autofeatures + * because they are only instantiated by ksh93_wrapper() below. This + * obviously includes those commented out here. + */ +static struct paramdef partab[] = { + PARAMDEF(".sh.command", PM_NAMEREF|PM_READONLY, "ZSH_DEBUG_CMD", &constant_gsu), + PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, &sh_edchar, &sh_edchar_gsu), + PARAMDEF(".sh.edcol", PM_NAMEREF|PM_READONLY, "CURSOR", &constant_gsu), + PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_edmode, &sh_edmode_gsu), + PARAMDEF(".sh.edtext", PM_NAMEREF|PM_READONLY, "BUFFER", &constant_gsu), + PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu), + /* PARAMDEF(".sh.fun", PM_SCALAR|PM_UNSET, NULL, &constant_gsu), */ + /* PARAMDEF(".sh.level", PM_INTEGER|PM_UNSET, NULL, &constant_gsu), */ + PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu), + PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu), + PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_name, &sh_name_gsu), + PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_subscript, &sh_subscript_gsu), + PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu), + /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */ + PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu) +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +static int +ksh93_wrapper(Eprog prog, FuncWrap w, char *name) +{ + Funcstack f; + Param pm; + zlong num = funcstack->prev ? getiparam(".sh.level") : 0; + + if (!EMULATION(EMULATE_KSH)) + return 1; + + if (num == 0) + for (f = funcstack; f; f = f->prev, num++); + else + num++; + + queue_signals(); + ++locallevel; /* Make these local */ + if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; /* Why is this necessary? */ + setiparam(".sh.level", num); + } + if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setsparam(".sh.fun", ztrdup(name)); + pm->node.flags |= PM_READONLY; + } + if (zleactive) { + extern mod_import_variable char *curkeymapname; /* XXX */ + extern mod_import_variable char *varedarg; /* XXX */ + /* How to distinguish emacs bindings? */ + if (curkeymapname && strcmp(curkeymapname, "main") == 0) + strcpy(sh_edmode, "\e"); + else + strcpy(sh_edmode, ""); + if (!sh_edchar) + sh_edchar = dupstring(getsparam("KEYS")); + if (varedarg) { + char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0); + if (ie && *ie) { + *ie++ = '\0'; + /* Assume bin_vared has validated subscript */ + sh_subscript = dupstring(ie); + ie = sh_subscript + strlen(sh_subscript); + *--ie = '\0'; + } else + sh_subscript = NULL; + if ((pm = createparam(".sh.value", PM_LOCAL|PM_NAMEREF|PM_UNSET))) { + pm->level = locallevel; + setloopvar(".sh.value", "BUFFER"); /* Hack */ + } + } else + sh_name = sh_subscript = NULL; + } else { + sh_edchar = sh_name = sh_subscript = NULL; + strcpy(sh_edmode, ""); + /* TODO: + * - disciplines + * - special handling of .sh.value in math + */ + } + --locallevel; + unqueue_signals(); + + return 1; +} + +static struct funcwrap wrapper[] = { + WRAPDEF(ksh93_wrapper), +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(Module m) +{ + return addwrapper(m, wrapper); +} + +/**/ +int +cleanup_(Module m) +{ + struct paramdef *p; + + deletewrapper(m, wrapper); + + /* Clean up namerefs, otherwise deleteparamdef() is confused */ + for (p = partab; p < partab + sizeof(partab)/sizeof(*partab); ++p) { + if (p->flags & PM_NAMEREF) { + HashNode hn = gethashnode2(paramtab, p->name); + if (hn) + ((Param)hn)->node.flags &= ~PM_NAMEREF; + } + } + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} -- cgit v1.2.3 From 25dceb1dea4ee1c23dcc02f3f7f5c0bf345c492c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 11 Mar 2023 13:20:21 -0800 Subject: 51557: Clarify availability of ksh-mode parameters, improve vi-mode detection. --- ChangeLog | 5 +++++ Doc/Zsh/mod_ksh93.yo | 8 +++++--- Src/Modules/ksh93.c | 5 +++-- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index f15072191..cc2890b2f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-03-11 Bart Schaefer + + * 51557: Doc/zsh/mod_ksh93.yo, Src/Modules/ksh93.c: Clarify + availability of ksh-mode parameters, improve vi-mode detection. + 2023-03-07 Bart Schaefer * 51534: Util/printdefines: update for recent changes diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index d58b979b9..99dab385f 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -24,8 +24,10 @@ enditem() subsect(Ksh Parameters) cindex(parameters, ksh) -Parameters supplied by this module that are marked with `' below are -available only in ksh emulation. +Parameters supplied by this module that are marked with `' below +are available only when ksh emulation is active before entry to the +shell function, that is, `tt(emulate ksh)' locally to a function does +not populate these parameters for that function. startitem() vindex(.sh.command) @@ -45,7 +47,7 @@ A named reference to the ZLE special parameter `tt(CURSOR)'. vindex(.sh.edmode) item(tt(.sh.edmode) )( In a ZLE widget, this parameter has the value tt(ESC) (tt($'\e')) if the -`tt(main)' keymap is selected, and the empty string otherwise. This is +tt(VI) option is set and the `tt(main)' keymap is selected. This is intended for use with vi-mode key bindings (`tt(bindkey -v)'). In a future revision, assigning `tt(.sh.edchar=${.sh.edmode})' is expected to initiate `tt(vicmd)' mode when `tt(viins)' is active, and do diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 9dc75c93c..51999dd71 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -168,8 +168,9 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) if (zleactive) { extern mod_import_variable char *curkeymapname; /* XXX */ extern mod_import_variable char *varedarg; /* XXX */ - /* How to distinguish emacs bindings? */ - if (curkeymapname && strcmp(curkeymapname, "main") == 0) + /* bindkey -v forces VIMODE so this test is as good as any */ + if (curkeymapname && isset(VIMODE) && + strcmp(curkeymapname, "main") == 0) strcpy(sh_edmode, "\e"); else strcpy(sh_edmode, ""); -- cgit v1.2.3 From 25f5618b17cf5e9b5add518dffd512fe8503c6b2 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 13 Dec 2023 11:27:32 +0100 Subject: 52382: avoid the non-standard \e in C code, preferring \033 --- ChangeLog | 4 ++++ Src/Modules/ksh93.c | 2 +- Src/Zle/zle_utils.c | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index 84334a0af..ca505ceea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-12-13 Oliver Kiddle + + * 52382: Src/Modules/ksh93.c, Src/Zle/zle_utils.c: avoid \e in C code + 2023-12-09 Bart Schaefer * 52366 + fix typo: Completion/Unix/Command/_ant: rename diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 51999dd71..9af5e1d69 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -171,7 +171,7 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) /* bindkey -v forces VIMODE so this test is as good as any */ if (curkeymapname && isset(VIMODE) && strcmp(curkeymapname, "main") == 0) - strcpy(sh_edmode, "\e"); + strcpy(sh_edmode, "\033"); else strcpy(sh_edmode, ""); if (!sh_edchar) diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 51bb43339..e2b86e863 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -977,7 +977,7 @@ cuttext(ZLE_STRING_T line, int ct, int flags) unmetafy(mbcut, &cutll); mbcut = base64_encode(mbcut, cutll); - fprintf(shout, "\e]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', + fprintf(shout, "\033]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', mbcut); } else if (zmod.flags & MOD_VIBUF) { struct cutbuffer *b = &vibuf[zmod.vibuf]; -- cgit v1.2.3 From 610b18875ad9f4498a57e9af6903bcac3b14ff46 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:07:01 -0800 Subject: 52650 plus minor fixes: add -u for named references pointing to "upper" scope --- ChangeLog | 9 +++++++++ Doc/Zsh/builtins.yo | 8 ++++++-- Doc/Zsh/expn.yo | 19 +++++++++++++++++++ Doc/Zsh/func.yo | 13 ++++++++----- Doc/Zsh/mod_ksh93.yo | 2 +- Etc/FAQ.yo | 7 +++++++ Src/Modules/ksh93.c | 2 +- Src/builtin.c | 2 +- Src/exec.c | 22 +++++++++++++++++++++- Src/params.c | 32 +++++++++++++++++++------------ Test/K01nameref.ztst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++--- Test/V10private.ztst | 4 ++-- 12 files changed, 145 insertions(+), 28 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index 0dd7268d1..2d4d9922e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-03-04 Bart Schaefer + + * 52650 plus minor fixes: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, + Doc/Zsh/func.yo, Doc/Zsh/mod_ksh93.yo, Etc/FAQ.yo, + Src/Modules/ksh93.c, Src/builtin.c, Src/exec.c, Src/params.c, + Test/D04parameter.ztst, Test/K01nameref.ztst, + Test/V10private.ztst: add -u option for named references that + point to the "upper" scope, failed assignments have status 1 + 2024-03-05 Oliver Kiddle * 52646: Completion/Zsh/Type/_ps1234, Doc/Zsh/compsys.yo, diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 6318053d8..7a9684ac8 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2052,13 +2052,17 @@ cindex(named reference) cindex(reference, named) The flag tt(-n) creates a em(named reference) to another parameter. The second parameter need not exist at the time the reference is -created. Only tt(-g) and tt(-r) may be used in conjunction with +created. Only tt(-g), tt(-u), and tt(-r) may be used in conjunction with tt(-n). The var(name) so created may not be an array element nor use a subscript, but the var(value) assigned may be any valid parameter name syntax, even a subscripted array element (including an associative array element) or an array slice, which is evaluated when the named reference is expanded. It is an error for a named reference to refer -to itself, even indirectly through a chain of references. +to itself, even indirectly through a chain of references. When tt(-u) +is applied to a named reference, the parameter identified by var(value) +is always found in the calling function scope rather than the current +local scope. In this case, if there is no such parameter in the calling +scope, assignments to the named reference may fail, setting tt($?) to 1. See ifzman(zmanref(zshexpn))ifnzman(noderef(Parameter Expansion)) and ifzman(zmanref(zshparam))ifnzman(noderef(Parameters)) for details of the behavior of named references. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 2acfd08c9..183ca6e03 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1600,6 +1600,25 @@ example(tt(before local: OUTER) tt(by reference: OUTER) tt(after func: RESULT)) +To force a named reference to refer to the outer scope, even if a local +has already been declared, add the tt(-u) option when declaring the +named reference. In this case var(rname) should already exist in the +outer scope, otherwise the behavior of assignment through var(pname) +is not defined and may change the scope of the reference or fail with +a status of 1. Example of correct usage: +ifzman() +example(tt(caller=OUTER) +tt(func LPAR()RPAR() {) +tt( print before local: $caller) +tt( local caller=INNER) +tt( print after local: $caller) +tt( typeset -n -u outer=$1) +tt( print by reference: $outer) +tt( outer=RESULT) +tt(}) +tt(func caller) +tt(print after func: $caller)) + Note, however, that named references to em(special) parameters acquire the behavior of the special parameter, regardless of the scope where the reference is declared. diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index d4914df7a..7b71e34e9 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -31,10 +31,12 @@ referent parameter is in scope, and as early as possible in the function if the reference is to a parameter in a calling scope. A typical use of named references is to pass the name -of the referent as a positional parameter. For example, +of the referent as a positional parameter. In this case it is +good practice to use the tt(-u) option to reference the calling +scope. For example, ifzman() example(pop+LPAR()RPAR() { - local -n ref=$1 + local -nu ref=$1 local last=$ref[$#ref] ref[$#ref]=LPAR()RPAR() print -r -- $last @@ -43,9 +45,10 @@ array=LPAR() a list of five values RPAR() pop array) prints the word `tt(values)' and shortens `tt($array)' to -`tt(LPAR() a list of five RPAR())'. There are no local parameters in -tt(pop) at the time `tt(ref=$1)' is assigned, so `tt(ref)' becomes a -reference to `tt(array)' in the caller. +`tt(LPAR() a list of five RPAR())'. With tt(-nu), `tt(ref)' becomes a +reference to `tt(array)' in the caller. There are no local parameters in +tt(pop) at the time `tt(ref=$1)' is assigned, so in this example tt(-u) +could have been omitted, but it makes the intention clear. Functions execute in the same process as the caller and share all files diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index 9cd708d10..7508758aa 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -12,7 +12,7 @@ The single builtin provided by this module is: startitem() findex(nameref) cindex(named references, creating) -item(tt(nameref) [ tt(-r) ] var(pname)[tt(=)var(rname)])( +item(tt(nameref) [ tt(-gur) ] var(pname)[tt(=)var(rname)])( Equivalent to tt(typeset -n )var(pname)tt(=)var(rname) However, tt(nameref) is a builtin command rather than a reserved word, diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo index 145ef02c9..4a86050e6 100644 --- a/Etc/FAQ.yo +++ b/Etc/FAQ.yo @@ -1025,6 +1025,13 @@ label(210) HIT:SPOT ) + Dynamic scoping applies to named references, so for example a named + reference declared in global scope may be used in function scopes. + In ksh, local parameters have static scope, so named references in + zsh may have side-effects that do not occur in ksh. To limit those + effects, mytt(zmodload zsh/param/private) and declare all named + references mytt(private). + Named references may be used in zsh versions later than 5.9. sect(What is zsh's support for non-forking command substitution?) diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 9af5e1d69..6760cbca0 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -38,7 +38,7 @@ */ static struct builtin bintab[] = { - BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gr", "n") + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gur", "n") }; #include "zsh.mdh" diff --git a/Src/builtin.c b/Src/builtin.c index 83144677b..ba9cb03c2 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2699,7 +2699,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) off |= bit; } if (OPT_MINUS(ops,'n')) { - if ((on|off) & ~PM_READONLY) { + if ((on|off) & ~(PM_READONLY|PM_UPPER)) { zwarnnam(name, "no other attributes allowed with -n"); return 1; } diff --git a/Src/exec.c b/Src/exec.c index d85adbea5..0231bc361 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2604,6 +2604,17 @@ addvars(Estate state, Wordcode pc, int addflags) opts[ALLEXPORT] = allexp; } else pm = assignsparam(name, val, myflags); + if (!pm) { + lastval = 1; + /* + * This is cheating but some exec functions propagate + * assignment status only from command substitution + * + * zerr("%s: assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; @@ -2628,7 +2639,16 @@ addvars(Estate state, Wordcode pc, int addflags) } fprintf(xtrerr, ") "); } - assignaparam(name, arr, myflags); + if (!assignaparam(name, arr, myflags)) { + lastval = 1; + /* + * See above RE "cheating" + * + * zerr("%s: array assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; diff --git a/Src/params.c b/Src/params.c index e83e4aa5e..263cd0c52 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1060,8 +1060,7 @@ createparam(char *name, int flags) "BUG: local parameter is not unset"); oldpm = lastpm; } - } else - flags |= PM_NAMEREF; + } } DPUTS(oldpm && oldpm->level > locallevel, @@ -6267,10 +6266,12 @@ resolve_nameref(Param pm, const Asgment stop) } } else if ((hn = gethashnode2(realparamtab, seek))) { if (pm) { - if (!(stop && (stop->flags & (PM_LOCAL)))) - hn = (HashNode)upscope((Param)hn, - ((pm->node.flags & PM_NAMEREF) ? - pm->base : ((Param)hn)->level)); + if (!(stop && (stop->flags & (PM_LOCAL)))) { + int scope = ((pm->node.flags & PM_NAMEREF) ? + ((pm->node.flags & PM_UPPER) ? -1 : + pm->base) : ((Param)hn)->level); + hn = (HashNode)upscope((Param)hn, scope); + } /* user can't tag a nameref, safe for loop detection */ pm->node.flags |= PM_TAGGED; } @@ -6316,11 +6317,13 @@ setloopvar(char *name, char *value) static void setscope(Param pm) { - if (pm->node.flags & PM_NAMEREF) { + queue_signals(); + if (pm->node.flags & PM_NAMEREF) do { Param basepm; struct asgment stop; char *refname = GETREFNAME(pm); char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL; + int q = queue_signal_level(); /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6330,9 +6333,11 @@ setscope(Param pm) stop.name = ""; stop.value.scalar = NULL; stop.flags = PM_NAMEREF; - if (locallevel) + if (locallevel && !(pm->node.flags & PM_UPPER)) stop.flags |= PM_LOCAL; + dont_queue_signals(); /* Prevent unkillable loops */ basepm = (Param)resolve_nameref(pm, &stop); + restore_queue_signals(q); if (t) { pm->width = t - refname; *t = '['; @@ -6345,7 +6350,7 @@ setscope(Param pm) if (upscope(pm, pm->base) == pm) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } pm->node.flags &= ~PM_SELFREF; } else if (pm->base == pm->level) { @@ -6353,7 +6358,7 @@ setscope(Param pm) strcmp(pm->node.nam, refname) == 0) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } } } else if ((t = GETREFNAME(basepm))) { @@ -6361,7 +6366,7 @@ setscope(Param pm) strcmp(pm->node.nam, t) == 0) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } } } else @@ -6381,7 +6386,8 @@ setscope(Param pm) zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); } - } + } while (0); + unqueue_signals(); } /**/ @@ -6393,6 +6399,8 @@ upscope(Param pm, int reflevel) pm = up; up = up->old; } + if (reflevel < 0 && locallevel > 0) + return pm->level == locallevel ? up : pm; return pm; } diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index ff48e2289..47d32697c 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -492,7 +492,6 @@ F:unexpected side-effects of previous tests } typeset -p ptr2 1:up-reference part 5, stacked namerefs, end not in scope -F:What is the correct behavior for the scope of ptr1? >typeset -n ptr1=ptr2 >typeset -n ptr2 >ptr1=ptr2 @@ -529,6 +528,49 @@ F:Same test, should part 5 output look like this? >typeset -n ptr2 >typeset ptr2=val + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer && print -u2 assignment expected to fail + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +1:up-reference part 7, upscope namerefs, end not in scope +>ptr1=var +>ptr2=var +>ptr1= +>ptr2=inner +*?*typeset*: no such variable: var +*?*typeset*: no such variable: var + + typeset var + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer || print -u2 assignment expected to succeed + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +0:up-reference part 8, upscope namerefs, end in scope +>ptr1=var +>ptr2=var +>ptr1=outer +>ptr2=inner +>typeset -g var=outer +>typeset var=outer + if zmodload zsh/parameter; then () { zmodload -u zsh/parameter @@ -539,7 +581,7 @@ F:Same test, should part 5 output look like this? } else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test' fi -0:up-reference part 3, autoloading with hidden special +0:up-reference part 9, autoloading with hidden special >nameref-local-nameref-local >typeset -h parameters @@ -762,12 +804,17 @@ F:relies on global TYPESET_TO_UNSET in %prep bar=xx typeset -n foo=bar - () { typeset -n foo; foo=zz; foo=zz; print $bar $zz } + () { + typeset -n foo; foo=zz + foo=zz || print -u2 foo: assignment failed + print $bar $zz + } () { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz } 0:regression: local nameref may not in-scope a global parameter F:previously this could create an infinite recursion and crash >xx >xx zz +*?*foo: assignment failed typeset -nm foo=bar 1:create nameref by pattern match not allowed diff --git a/Test/V10private.ztst b/Test/V10private.ztst index efa346002..ed51316f3 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -378,7 +378,7 @@ F:Here ptr1 finds private ptr2 by scope mismatch typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val # This is a silent no-op, why? + ptr1=val || print -u2 ptr1: assignment failed typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -388,7 +388,6 @@ F:Here ptr1 finds private ptr2 by scope mismatch 1:up-reference for private namerefs, end not in scope F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch -F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 >typeset -hn ptr2='' >ptr1=ptr2 @@ -396,6 +395,7 @@ F:Assignment silently fails, is that correct? >ptr2= >typeset -n ptr1=ptr2 >typeset -hn ptr2='' +*?*ptr1: assignment failed *?*no such variable: ptr2 typeset ptr2 -- cgit v1.2.3 From ea5a5d6ec430cad1a73f3179fcc018c90a315c35 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 9 Mar 2024 22:02:57 -0800 Subject: 52725: updated named reference semantics --- ChangeLog | 4 ++++ Src/Modules/ksh93.c | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 12 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index 620c74fcf..a2609a1cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-03-09 Bart Schaefer + + * 52725: Src/Modules/ksh93.c: updated named reference semantics + 2024-03-09 Stephane Chazelas * 52721: fix metafication and regexp/subject confusion in diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 6760cbca0..8d10317dc 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -113,18 +113,17 @@ static char sh_edmode[2]; * obviously includes those commented out here. */ static struct paramdef partab[] = { - PARAMDEF(".sh.command", PM_NAMEREF|PM_READONLY, "ZSH_DEBUG_CMD", &constant_gsu), - PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, &sh_edchar, &sh_edchar_gsu), - PARAMDEF(".sh.edcol", PM_NAMEREF|PM_READONLY, "CURSOR", &constant_gsu), - PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_edmode, &sh_edmode_gsu), - PARAMDEF(".sh.edtext", PM_NAMEREF|PM_READONLY, "BUFFER", &constant_gsu), + PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, + &sh_edchar, &sh_edchar_gsu), + PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_edmode, &sh_edmode_gsu), PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu), - /* PARAMDEF(".sh.fun", PM_SCALAR|PM_UNSET, NULL, &constant_gsu), */ - /* PARAMDEF(".sh.level", PM_INTEGER|PM_UNSET, NULL, &constant_gsu), */ PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu), PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu), - PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_name, &sh_name_gsu), - PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_subscript, &sh_subscript_gsu), + PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_name, &sh_name_gsu), + PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_subscript, &sh_subscript_gsu), PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu), /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */ PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu) @@ -156,15 +155,35 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) queue_signals(); ++locallevel; /* Make these local */ - if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { +#define LOCAL_NAMEREF (PM_LOCAL|PM_UNSET|PM_NAMEREF) + if ((pm = createparam(".sh.command", LOCAL_NAMEREF))) { pm->level = locallevel; /* Why is this necessary? */ - setiparam(".sh.level", num); + /* Force scoping by assignent hack */ + setloopvar(".sh.command", "ZSH_DEBUG_CMD"); + pm->node.flags |= PM_READONLY; } + /* .sh.edchar is in partab and below */ + if (zleactive && (pm = createparam(".sh.edcol", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edcol", "CURSOR"); + pm->node.flags |= (PM_NAMEREF|PM_READONLY); + } + /* .sh.edmode is in partab and below */ + if (zleactive && (pm = createparam(".sh.edtext", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edtext", "BUFFER"); + pm->node.flags |= PM_READONLY; + } + if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) { pm->level = locallevel; setsparam(".sh.fun", ztrdup(name)); pm->node.flags |= PM_READONLY; } + if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setiparam(".sh.level", num); + } if (zleactive) { extern mod_import_variable char *curkeymapname; /* XXX */ extern mod_import_variable char *varedarg; /* XXX */ @@ -186,9 +205,10 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) *--ie = '\0'; } else sh_subscript = NULL; - if ((pm = createparam(".sh.value", PM_LOCAL|PM_NAMEREF|PM_UNSET))) { + if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) { pm->level = locallevel; setloopvar(".sh.value", "BUFFER"); /* Hack */ + pm->node.flags |= PM_READONLY; } } else sh_name = sh_subscript = NULL; -- cgit v1.2.3 From 8adfbfc1f05200d566b9830232d0dabf82c84283 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 12 Mar 2024 18:02:38 -0700 Subject: unposted: "typeset -p" has problems with special parameters having NULL values --- ChangeLog | 3 +++ Src/Modules/ksh93.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index c8935fb61..389054f14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-12 Bart Schaefer + * unposted: Src/Modules/ksh93.c: "typeset -p" has problems with + special parameters having NULL values, use a dummy static instead. + * 52742: Src/builtin.c: fix bad interactions of "typeset -p" with GLOBAL_EXPORT, plus some other inconsistencies. diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 8d10317dc..3206c11f3 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -102,9 +102,10 @@ static const struct gsu_scalar sh_name_gsu = static const struct gsu_scalar sh_subscript_gsu = { strvargetfn, nullstrsetfn, nullunsetfn }; -static char *sh_name; -static char *sh_subscript; -static char *sh_edchar; +static char sh_unsetval[2]; /* Dummy to treat as NULL */ +static char *sh_name = sh_unsetval; +static char *sh_subscript = sh_unsetval; +static char *sh_edchar = sh_unsetval; static char sh_edmode[2]; /* @@ -193,7 +194,7 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) strcpy(sh_edmode, "\033"); else strcpy(sh_edmode, ""); - if (!sh_edchar) + if (sh_edchar == sh_unsetval) sh_edchar = dupstring(getsparam("KEYS")); if (varedarg) { char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0); @@ -204,16 +205,16 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) ie = sh_subscript + strlen(sh_subscript); *--ie = '\0'; } else - sh_subscript = NULL; + sh_subscript = sh_unsetval; if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) { pm->level = locallevel; setloopvar(".sh.value", "BUFFER"); /* Hack */ pm->node.flags |= PM_READONLY; } } else - sh_name = sh_subscript = NULL; + sh_name = sh_subscript = sh_unsetval; } else { - sh_edchar = sh_name = sh_subscript = NULL; + sh_edchar = sh_name = sh_subscript = sh_unsetval; strcpy(sh_edmode, ""); /* TODO: * - disciplines -- cgit v1.2.3 From 20990fa7e4bad34bd7e3c145f93f19cb811e8856 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 30 Jan 2025 12:51:37 +0100 Subject: 53337: allow nameref -p --- ChangeLog | 2 ++ Doc/Zsh/mod_ksh93.yo | 2 +- Src/Modules/ksh93.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Src/Modules/ksh93.c') diff --git a/ChangeLog b/ChangeLog index 19e5b2497..3ddb16ace 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2025-01-30 Oliver Kiddle + * 53337: Doc/Zsh/mod_ksh93.yo, Src/Modules/ksh93.c: allow nameref -p + * 53336: Doc/Makefile.in, Etc/Makefile.in: avoid GNU make specific use of $< in a non-inference rule diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index 7508758aa..7d22064ee 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -12,7 +12,7 @@ The single builtin provided by this module is: startitem() findex(nameref) cindex(named references, creating) -item(tt(nameref) [ tt(-gur) ] var(pname)[tt(=)var(rname)])( +item(tt(nameref) [ tt(-gpur) ] var(pname)[tt(=)var(rname)])( Equivalent to tt(typeset -n )var(pname)tt(=)var(rname) However, tt(nameref) is a builtin command rather than a reserved word, diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 3206c11f3..fa0785cda 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -38,7 +38,7 @@ */ static struct builtin bintab[] = { - BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gur", "n") + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gpru", "n") }; #include "zsh.mdh" -- cgit v1.2.3