summaryrefslogtreecommitdiff
path: root/Src/Modules/param_private.c
diff options
context:
space:
mode:
authorJoe Rayhawk <jrayhawk@fairlystable.org>2025-04-30 02:07:56 -0700
committerJoe Rayhawk <jrayhawk@fairlystable.org>2025-04-30 02:07:56 -0700
commit26e09889646be3ea65b4a3dfeda26213e4bb6a27 (patch)
tree4f3c73a9416bf47ad7e125383d23cf42879e38d7 /Src/Modules/param_private.c
parent841bce705a58b04220b1f257abcc00ae71cbdbdc (diff)
parent001cba48ce3b964cf01fb3e2af54b20eacbc9bf5 (diff)
downloadzsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.tar.gz
zsh-26e09889646be3ea65b4a3dfeda26213e4bb6a27.zip
Merge branch 'upstream' into debian
Diffstat (limited to 'Src/Modules/param_private.c')
-rw-r--r--Src/Modules/param_private.c151
1 files changed, 118 insertions, 33 deletions
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index c53839152..044617190 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -87,12 +87,55 @@ makeprivate(HashNode hn, UNUSED(int flags))
((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL &&
/* typeset_single() line 2300 discards PM_REMOVABLE -- why? */
!is_private(pm->old))))) {
- zwarnnam("private", "can't change scope of existing param: %s",
- pm->node.nam);
- makeprivate_error = 1;
+ if (is_private(pm->old)) {
+ if (pm->old->node.flags & PM_READONLY) {
+ zerr("read-only variable: %s", pm->node.nam);
+ makeprivate_error = 1;
+ } else if ((pm->node.flags | pm->old->node.flags) ==
+ pm->old->node.flags) {
+ /* private called twice on same parameter */
+ Param tpm = pm;
+ pm = pm->old;
+ --locallevel;
+ /* why have a union if we need this switch anyway? */
+ switch (PM_TYPE(pm->node.flags)) {
+ case PM_SCALAR:
+ pm->gsu.s->setfn(pm, tpm->u.str);
+ tpm->u.str = NULL;
+ break;
+ case PM_INTEGER:
+ pm->gsu.i->setfn(pm, tpm->u.val);
+ break;
+ case PM_EFLOAT:
+ case PM_FFLOAT:
+ pm->gsu.f->setfn(pm, tpm->u.dval);
+ break;
+ case PM_ARRAY:
+ pm->gsu.a->setfn(pm, tpm->u.arr);
+ tpm->u.arr = NULL;
+ break;
+ case PM_HASHED:
+ pm->gsu.h->setfn(pm, tpm->u.hash);
+ tpm->u.hash = NULL;
+ break;
+ }
+ ++locallevel;
+ if (!(tpm->node.flags & PM_UNSET))
+ pm->node.flags &= ~PM_UNSET;
+ } else {
+ zerrnam("private",
+ "can't change type of private param: %s",
+ pm->node.nam);
+ makeprivate_error = 1;
+ }
+ } else {
+ zerrnam("private", "can't change scope of existing param: %s",
+ pm->node.nam);
+ makeprivate_error = 1;
+ }
return;
}
- struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure));
+ struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure));
switch (PM_TYPE(pm->node.flags)) {
case PM_SCALAR:
gsu->g = (void *)(pm->gsu.s);
@@ -122,6 +165,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
break;
default:
makeprivate_error = 1;
+ zfree(gsu, sizeof(struct gsu_closure));
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
@@ -229,7 +273,9 @@ setfn_error(Param pm)
* calling the original unsetfn. This assures that if the old unsetfn
* wants to use its getfn or setfn, they're unconditionally present.
* The "explicit" flag indicates that "unset" was called, if zero the
- * parameter is going out of scope (see params.c).
+ * parameter is going out of scope (see params.c). PM_DECLARED is
+ * asserted as if TYPESET_TO_UNSET were in use so that the private
+ * parameter is re-used rather than re-created when assigned again.
*
*/
@@ -252,7 +298,7 @@ pps_setfn(Param pm, char *x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s);
GsuScalar gsu = (GsuScalar)(c->g);
- if (locallevel == pm->level)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -267,8 +313,11 @@ pps_unsetfn(Param pm, int explicit)
pm->gsu.s = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.s = (GsuScalar)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -289,7 +338,7 @@ ppi_setfn(Param pm, zlong x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i);
GsuInteger gsu = (GsuInteger)(c->g);
- if (locallevel == pm->level)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -304,8 +353,11 @@ ppi_unsetfn(Param pm, int explicit)
pm->gsu.i = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.i = (GsuInteger)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -326,7 +378,7 @@ ppf_setfn(Param pm, double x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f);
GsuFloat gsu = (GsuFloat)(c->g);
- if (locallevel == pm->level)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -341,8 +393,11 @@ ppf_unsetfn(Param pm, int explicit)
pm->gsu.f = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.f = (GsuFloat)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/**/
@@ -364,7 +419,7 @@ ppa_setfn(Param pm, char **x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a);
GsuArray gsu = (GsuArray)(c->g);
- if (locallevel == pm->level)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -379,8 +434,11 @@ ppa_unsetfn(Param pm, int explicit)
pm->gsu.a = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.a = (GsuArray)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
static HashTable emptytable;
@@ -403,7 +461,7 @@ pph_setfn(Param pm, HashTable x)
{
struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h);
GsuHash gsu = (GsuHash)(c->g);
- if (locallevel == pm->level)
+ if (locallevel == pm->level || locallevel > private_wraplevel)
gsu->setfn(pm, x);
else
setfn_error(pm);
@@ -418,8 +476,11 @@ pph_unsetfn(Param pm, int explicit)
pm->gsu.h = gsu;
if (locallevel <= pm->level)
gsu->unsetfn(pm, explicit);
- if (explicit)
+ if (explicit) {
+ pm->node.flags |= PM_DECLARED;
pm->gsu.h = (GsuHash)c;
+ } else
+ zfree(c, sizeof(struct gsu_closure));
}
/*
@@ -479,18 +540,19 @@ static struct funcwrap wrapper[] = {
};
/**/
+static int private_wraplevel = 0;
+
+/**/
static int
wrap_private(Eprog prog, FuncWrap w, char *name)
{
- static int wraplevel = 0;
-
- if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
- int owl = wraplevel;
- wraplevel = locallevel;
+ if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) {
+ int owl = private_wraplevel;
+ private_wraplevel = locallevel;
scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET);
runshfunc(prog, w, name);
scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0);
- wraplevel = owl;
+ private_wraplevel = owl;
return 0;
}
return 1;
@@ -502,27 +564,50 @@ static GetNodeFunc getparamnode;
static HashNode
getprivatenode(HashTable ht, const char *nam)
{
- HashNode hn = getparamnode(ht, nam);
+ /* getparamnode() would follow namerefs, we must not do that here */
+ HashNode hn = gethashnode2(ht, nam);
Param pm = (Param) hn;
- while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) {
+ /* autoload has precedence over nameref, so getparamnode() */
+ if (pm && (pm->node.flags & PM_AUTOLOAD)) {
+ hn = getparamnode(ht, nam);
+ pm = (Param) hn;
+ /* how would an autoloaded private behave? return here? */
+ }
+ while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) {
+ if (pm->level == private_wraplevel + 1) {
+ /* Variable is in the current function scope */
+ break;
+ }
+#if 0
if (!(pm->node.flags & PM_UNSET)) {
/*
* private parameters are always marked PM_UNSET before we
- * increment locallevel, so the only way we get here is
- * when createparam() wants a new parameter that is not at
- * the current locallevel and it has therefore cleared the
- * PM_UNSET flag.
+ * increment locallevel, so there are three possible ways
+ * to get here:
+ * 1) createparam() wants a new parameter that is not at
+ * the current locallevel and it has therefore cleared the
+ * PM_UNSET flag
+ * 2) locallevel has been incremented (startparamscope())
+ * outside the usual function call stack (private_wraplevel)
+ * 3) dynamic scoping is fetching a value from a surrounding
+ * scope, we don't know if that's for assign or just expand
+ * The first of those is now caught in createparam() when
+ * testing PM_RO_BY_DESIGN and the second occurs only in
+ * nofork substitution or handling of ZLE specials. If the
+ * third is an assignment, the GSU setfn rejects it.
*/
DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope");
- setfn_error(pm);
- /*
- * TODO: instead of throwing an error here, create a global
- * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL.
- */
}
+#endif
pm = pm->old;
}
+
+ /* resolve nameref after skipping private parameters */
+ if (pm && (pm->node.flags & PM_NAMEREF) &&
+ (pm->u.str || (pm->node.flags & PM_UNSET)))
+ pm = (Param) resolve_nameref(pm, NULL);
+
return (HashNode)pm;
}
@@ -561,7 +646,7 @@ printprivatenode(HashNode hn, int printflags)
static struct builtin bintab[] = {
/* Copied from BUILTIN("local"), "P" added */
- BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P")
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmrtux", "P")
};
static struct features module_features = {