about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBart Schaefer <schaefer@zsh.org>2023-03-05 14:06:25 -0800
committerBart Schaefer <schaefer@zsh.org>2023-03-05 14:06:25 -0800
commit4bc1f6e0d2b60976b9c0412d72097ee1c812139b (patch)
tree807478c154429da44718f4e2f7354693e98db4c7
parenta9ba1662165823a0303a03fdeddb2ce4ca3814e5 (diff)
downloadzsh-4bc1f6e0d2b60976b9c0412d72097ee1c812139b.tar.gz
zsh-4bc1f6e0d2b60976b9c0412d72097ee1c812139b.tar.xz
zsh-4bc1f6e0d2b60976b9c0412d72097ee1c812139b.zip
51484: Extend named reference handling for special parameters, improve doc.
-rw-r--r--ChangeLog3
-rw-r--r--Doc/Zsh/builtins.yo2
-rw-r--r--Src/params.c108
3 files changed, 69 insertions, 44 deletions
diff --git a/ChangeLog b/ChangeLog
index 38a0395d6..30e9850e5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2023-03-05  Bart Schaefer  <schaefer@zsh.org>
 
+	* 51484: Src/builtins.yo Src/params.c: Extend named reference
+	handling for special parameters, improve doc.
+
 	* 51483: Src/Zle/compcore.c, Src/Zle/zle_tricky.c, Src/lex.c,
 	Src/params.c, Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h:
 	Enable assignment and expansion of parameters with ksh-like
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 92917c06c..5393cb149 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1924,6 +1924,8 @@ redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi(        )))
 xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmrtux) ] \
 [ {tt(PLUS())|tt(-)}tt(EFLRZip) [ var(n) ] ])
 xitem(SPACES()[ tt(+) ] [ var(name)[tt(=)var(value)] ... ])
+xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(n) ] \
+[ tt(-gr) ] [ var(name)[tt(=)var(value)] ... ])
 xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglrux) ] [ {tt(PLUS())|tt(-)}tt(LRZp) [ var(n) ] ])
 xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])
 item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) ] [ var(name) ... ])(
diff --git a/Src/params.c b/Src/params.c
index d3b6a7d43..c9f4b3017 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -475,6 +475,15 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
 	((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \
 		 !(V)->pm->node.nam || !*(V)->pm->node.nam))
 
+/*
+ * For named references.  Simple named references are just like scalars
+ * for efficiency, but special named references need get/set functions.
+ */
+#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ?	\
+			(PM)->gsu.s->getfn(PM) : (PM)->u.str)
+#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ?		\
+			  (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S)))
+
 static Param argvparam;
 
 /* "parameter table" - hash table containing the parameters
@@ -520,7 +529,7 @@ getparamnode(HashTable ht, const char *nam)
     HashNode hn = gethashnode2(ht, nam);
     Param pm = (Param) hn;
 
-    if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) {
+    if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) {
 	char *mn = dupstring(pm->u.str);
 
 	(void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL :
@@ -1002,12 +1011,13 @@ createparam(char *name, int flags)
 	    struct asgment stop;
 	    stop.flags = PM_NAMEREF | (flags & PM_LOCAL);
 	    stop.name = oldpm->node.nam;
-	    stop.value.scalar = oldpm->u.str;
+	    stop.value.scalar = GETREFNAME(oldpm);
 	    lastpm = (Param)resolve_nameref(oldpm, &stop);
 	    if (lastpm) {
 		if (lastpm->node.flags & PM_NAMEREF) {
-		    if (lastpm->u.str && *(lastpm->u.str)) {
-			name = lastpm->u.str;
+		    char *refname = GETREFNAME(lastpm);
+		    if (refname && *refname) {
+			name = refname;
 			oldpm = NULL;
 		    } else {
 			if (!(lastpm->node.flags & PM_READONLY))
@@ -2145,25 +2155,28 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	    memset(v, 0, sizeof(*v));
 	else
 	    v = (Value) hcalloc(sizeof *v);
-	if ((pm->node.flags & PM_NAMEREF) && pm->u.str && *(pm->u.str)) {
-	    /* only happens for namerefs pointing to array elements */
-	    char *ref = dupstring(pm->u.str);
-	    char *ss = pm->width ? ref + pm->width : NULL;
-	    if (ss) {
-		sav = *ss;
-		*ss = 0;
+	if (pm->node.flags & PM_NAMEREF) {
+	    char *refname = GETREFNAME(pm);
+	    if (refname && *refname) {
+		/* only happens for namerefs pointing to array elements */
+		char *ref = dupstring(refname);
+		char *ss = pm->width ? ref + pm->width : NULL;
+		if (ss) {
+		    sav = *ss;
+		    *ss = 0;
+		}
+		Param p1 = (Param)gethashnode2(paramtab, ref);
+		if (!(p1 && (pm = upscope(p1, pm->base))) ||
+		    ((pm->node.flags & PM_UNSET) &&
+		     !(pm->node.flags & PM_DECLARED)))
+		    return NULL;
+		if (ss) {
+		    flags |= SCANPM_NOEXEC;
+		    *ss = sav;
+		    s = dyncat(ss,*pptr);
+		} else
+		    s = *pptr;
 	    }
-	    Param p1 = (Param)gethashnode2(paramtab, ref);
-	    if (!(p1 && (pm = upscope(p1, pm->base))) ||
-		((pm->node.flags & PM_UNSET) &&
-		!(pm->node.flags & PM_DECLARED)))
-		return NULL;
-	    if (ss) {
-		flags |= SCANPM_NOEXEC;
-		*ss = sav;
-		s = dyncat(ss,*pptr);
-	    } else
-		s = *pptr;
 	}
 	if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) {
 	    /* Overload v->isarr as the flag bits for hashed arrays. */
@@ -3648,7 +3661,7 @@ mod_export Param
 setiparam_no_convert(char *s, zlong val)
 {
     /*
-     * If the target is already an integer, thisgets converted
+     * If the target is already an integer, this gets converted
      * back.  Low technology rules.
      */
     char buf[BDIGBUFSIZE];
@@ -6115,22 +6128,23 @@ resolve_nameref(Param pm, const Asgment stop)
     const char *seek = stop ? stop->value.scalar : NULL;
 
     if (pm && (pm->node.flags & PM_NAMEREF)) {
-	if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) {
+	char *refname = GETREFNAME(pm);
+	if (pm->node.flags & (PM_UNSET|PM_TAGGED)) {
 	    /* Semaphore with createparam() */
 	    pm->node.flags &= ~PM_UNSET;
 	    if (pm->node.flags & PM_NEWREF)	/* See setloopvar() */
 		return NULL;
-	    if (pm->u.str && *(pm->u.str) && (pm->node.flags & PM_TAGGED))
+	    if (refname && *refname && (pm->node.flags & PM_TAGGED))
 		pm->node.flags |= PM_SELFREF;	/* See setscope() */
 	    return (HashNode) pm;
-	} else if (pm->u.str) {
+	} else if (refname) {
 	    if ((pm->node.flags & PM_TAGGED) ||
-		(stop && strcmp(pm->u.str, stop->name) == 0)) {
-		/* zwarnnam(pm->u.str, "invalid self reference"); */
+		(stop && strcmp(refname, stop->name) == 0)) {
+		/* zwarnnam(refname, "invalid self reference"); */
 		return stop ? (HashNode)pm : NULL;
 	    }
-	    if (*(pm->u.str))
-		seek = pm->u.str;
+	    if (*refname)
+		seek = refname;
 	}
     }
     else if (pm && !(stop && (stop->flags & PM_NAMEREF)))
@@ -6180,8 +6194,13 @@ setloopvar(char *name, char *value)
   Param pm = (Param) gethashnode2(realparamtab, name);
 
   if (pm && (pm->node.flags & PM_NAMEREF)) {
+      if (pm->node.flags & PM_READONLY) {
+	  /* Bash error is: "%s: readonly variable" */
+	  zerr("read-only reference: %s", pm->node.nam);
+	  return;
+      }
       pm->base = pm->width = 0;
-      pm->u.str = ztrdup(value);
+      SETREFNAME(pm, ztrdup(value));
       pm->node.flags &= ~PM_UNSET;
       pm->node.flags |= PM_NEWREF;
       setscope(pm);
@@ -6197,7 +6216,8 @@ setscope(Param pm)
     if (pm->node.flags & PM_NAMEREF) {
 	Param basepm;
 	struct asgment stop;
-	char *t = pm->u.str ? itype_end(pm->u.str, INAMESPC, 0) : NULL;
+	char *refname = GETREFNAME(pm);
+	char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL;
 
 	/* Temporarily change nameref to array parameter itself */
 	if (t && *t == '[')
@@ -6211,7 +6231,7 @@ setscope(Param pm)
 	    stop.flags |= PM_LOCAL;
 	basepm = (Param)resolve_nameref(pm, &stop);
 	if (t) {
-	    pm->width = t - pm->u.str;
+	    pm->width = t - refname;
 	    *t = '[';
 	}
 	if (basepm) {
@@ -6220,23 +6240,23 @@ setscope(Param pm)
 		    if (pm->node.flags & PM_SELFREF) {
 			/* Loop signalled by resolve_nameref() */
 			if (upscope(pm, pm->base) == pm) {
-			    zerr("%s: invalid self reference", pm->u.str);
+			    zerr("%s: invalid self reference", refname);
 			    unsetparam_pm(pm, 0, 1);
 			    return;
 			}
 			pm->node.flags &= ~PM_SELFREF;
 		    } else if (pm->base == pm->level) {
-			if (pm->u.str && *(pm->u.str) &&
-			    strcmp(pm->node.nam, pm->u.str) == 0) {
-			    zerr("%s: invalid self reference", pm->u.str);
+			if (refname && *refname &&
+			    strcmp(pm->node.nam, refname) == 0) {
+			    zerr("%s: invalid self reference", refname);
 			    unsetparam_pm(pm, 0, 1);
 			    return;
 			}
 		    }
-		} else if (basepm->u.str) {
+		} else if ((t = GETREFNAME(basepm))) {
 		    if (basepm->base <= basepm->level &&
-			strcmp(pm->node.nam, basepm->u.str) == 0) {
-			zerr("%s: invalid self reference", pm->u.str);
+			strcmp(pm->node.nam, t) == 0) {
+			zerr("%s: invalid self reference", refname);
 			unsetparam_pm(pm, 0, 1);
 			return;
 		    }
@@ -6251,11 +6271,11 @@ setscope(Param pm)
 		unsetparam_pm(pm, 0, 1);
 	    } else if (isset(WARNNESTEDVAR))
 		zwarn("reference %s in enclosing scope set to local variable %s",
-		      pm->node.nam, pm->u.str);
+		      pm->node.nam, refname);
 	}
-	if (pm->u.str && upscope(pm, pm->base) == pm &&
-	    strcmp(pm->node.nam, pm->u.str) == 0) {
-	    zerr("%s: invalid self reference", pm->u.str);
+	if (refname && upscope(pm, pm->base) == pm &&
+	    strcmp(pm->node.nam, refname) == 0) {
+	    zerr("%s: invalid self reference", refname);
 	    unsetparam_pm(pm, 0, 1);
 	}
     }