zsh-workers
 help / color / mirror / code / Atom feed
From: Bart Schaefer <schaefer@brasslantern.com>
To: Zsh hackers list <zsh-workers@zsh.org>
Subject: [PATCH] (take two?) typeset array[position=index]=value / unset hash[$stuff]
Date: Tue, 1 Jun 2021 19:51:32 -0700	[thread overview]
Message-ID: <CAH+w=7Z4RTLsEujWFyvDHZ+GVwiRcSk+p15qgDEPD0dbQwaxOw@mail.gmail.com> (raw)
In-Reply-To: <CAH+w=7a+M7nTTqzmve+SZHwtEeRPTicQB5ZRy2XO6er4UhMb1w@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1944 bytes --]

On Tue, Jun 1, 2021 at 9:05 AM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> [...] I should at least examine whether getasg() ought to be using
> parse_subscript() even though the corresponding parse.c block is using
> skipparens().

I've decided parse_subscript() is appropriate there, so that's
included below.  If anyone sees any problem with that, please point it
out.

> Whereas most subscript flags are meaningless for unset, we might
> consider supporting (e) there.  I'm not sure whether that would fully
> address the problem with the other characters, though.

Turns out it does seem to fix that, so that's the direction I've gone.
More discussion below.

> The issue with the empty key seems merely to be that the subscript
> validity test for associative arrays never changed from the one for
> plain arrays.

To maintain error-equivalent backward compatibility I didn't "fix"
this, instead, hash[(e)] (or hash[(e)''] if you think that more
readable) is required in order to unset the element with the empty
key.

> [...] I'm not sure what backward-compatibility would be broken?  Do you
> mean that scripts that already have work-arounds for the current issue
> might stop working?

The one compatibility issue with the foregoing is this:

Current pre-patch zsh:
% typeset -A zz
% bad='(e)bang'
% zz[$bad]=x
t% typeset -p zz
typeset -A zz=( ['(e)bang']=x )
% unset zz\["$bad"\]
% typeset -p zz
typeset -A zz=( )

With the patch, the "(e)" appearing in the value of $bad becomes a
subscript flag, because $bad is expanded before "unset" parses:
% zz[$bad]=x
% typeset -p zz
typeset -A zz=( ['(e)bang']=x )
% unset zz\["$bad"\]
% typeset -p zz
typeset -A zz=( ['(e)bang']=x )

You have to double the flag:
% unset zz\["(e)$bad"\]
% typeset -p zz
typeset -A zz=( )

Is that a small enough incompatibility for this to be acceptable?

I've included doc updates, but not tests.  If anyone else would like
to take a stab at those?

[-- Attachment #2: hash_subscripts.txt --]
[-- Type: text/plain, Size: 2913 bytes --]

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index f8e11d79d..07d8f6ac9 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -2411,6 +2411,8 @@ but the previous value will still reappear when the scope ends.
 Individual elements of associative array parameters may be unset by using
 subscript syntax on var(name), which should be quoted (or the entire command
 prefixed with tt(noglob)) to protect the subscript from filename generation.
+The `tt(LPAR())tt(e)tt(RPAR())' subscript flag may be used to require strict
+interpretation of characters in the key.
 
 If the tt(-m) flag is specified the arguments are taken as patterns (should
 be quoted) and all parameters with matching names are unset.  Note that this
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index dc28a45ae..03547ef3f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -443,7 +443,9 @@ not inhibited.
 
 This flag can also be used to force tt(*) or tt(@) to be interpreted as
 a single key rather than as a reference to all values.  It may be used
-for either purpose on the left side of an assignment.
+for either purpose on the left side of an assignment.  This flag may
+also be used in the subscript of an argument to tt(unset) to disable
+interpretation of special characters and quoting.
 )
 enditem()
 
diff --git a/Src/builtin.c b/Src/builtin.c
index a16fddcb7..7aff354cc 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1933,10 +1933,14 @@ getasg(char ***argvp, LinkList assigns)
     asg.flags = 0;
 
     /* search for `=' */
-    for (; *s && *s != '='; s++);
+    for (; *s && *s != '[' && *s != '=' /* && *s != '+' */; s++);
+    if (s > asg.name && *s == '[') {
+	char *se = parse_subscript(s + 1, 1, ']');
+	if (se && *se == ']') s = se + 1;
+    }
 
     /* found `=', so return with a value */
-    if (*s) {
+    if (*s && *s == '=') {
 	*s = '\0';
 	asg.value.scalar = s + 1;
     } else {
@@ -3726,11 +3730,16 @@ bin_unset(char *name, char **argv, Options ops, int func)
 	if (ss) {
 	    char *sse;
 	    *ss = 0;
+	    /* parse_subscript() doesn't handle empty brackets - should it? */
 	    if ((sse = parse_subscript(ss+1, 1, ']'))) {
 		*sse = 0;
-		subscript = dupstring(ss+1);
+		if (ss[1] == '(' && ss[2] == 'e' && ss[3] == ')') {
+		    subscript = dupstring(ss+4);
+		} else {
+		    subscript = dupstring(ss+1);
+		    remnulargs(subscript);
+		}
 		*sse = ']';
-		remnulargs(subscript);
 		untokenize(subscript);
 	    }
 	}
@@ -3763,6 +3772,12 @@ bin_unset(char *name, char **argv, Options ops, int func)
 	    } else if (PM_TYPE(pm->node.flags) == PM_SCALAR ||
 		       PM_TYPE(pm->node.flags) == PM_ARRAY) {
 		struct value vbuf;
+		if (!*subscript) {
+		    *ss = '[';
+		    zerrnam(name, "%s: invalid parameter name", s);
+		    returnval = 1;
+		    continue;
+		}
 		vbuf.isarr = (PM_TYPE(pm->node.flags) == PM_ARRAY ?
 			      SCANPM_ARRONLY : 0);
 		vbuf.pm = pm;

  reply	other threads:[~2021-06-02  2:52 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-12-16 21:10 regexp-replace and ^, word boundary or look-behind operators Stephane Chazelas
2019-12-16 21:27 ` Stephane Chazelas
2019-12-17  7:38   ` Stephane Chazelas
2019-12-17 11:11     ` [PATCH] " Stephane Chazelas
2019-12-18  0:22       ` Daniel Shahaf
2019-12-18  8:31         ` Stephane Chazelas
2020-01-01 14:03         ` [PATCH v2] " Stephane Chazelas
2021-04-30  6:11           ` Stephane Chazelas
2021-04-30 23:13             ` Bart Schaefer
2021-05-05 11:45               ` [PATCH v3] regexp-replace and ^, word boundary or look-behind operators (and more) Stephane Chazelas
2021-05-31  0:58                 ` Lawrence Velázquez
2021-05-31 18:18                 ` Bart Schaefer
2021-05-31 21:37                   ` [PATCH] (?) typeset array[position=index]=value Bart Schaefer
2021-06-01  5:32                     ` Stephane Chazelas
2021-06-01 16:05                       ` Bart Schaefer
2021-06-02  2:51                         ` Bart Schaefer [this message]
2021-06-02 10:06                           ` [PATCH] (take two?) typeset array[position=index]=value / unset hash[$stuff] Stephane Chazelas
2021-06-02 14:52                             ` Bart Schaefer
2021-06-02 16:02                               ` Stephane Chazelas
2021-06-02  9:11                         ` [PATCH] (?) typeset array[position=index]=value Stephane Chazelas
2021-06-02 13:34                           ` Daniel Shahaf
2021-06-02 14:20                             ` Stephane Chazelas
2021-06-02 15:59                               ` Bart Schaefer
2021-06-03  2:04                                 ` [PATCH (not final)] (take three?) unset "array[$anything]" Bart Schaefer
2021-06-03  2:42                                   ` Bart Schaefer
2021-06-03  6:12                                     ` Bart Schaefer
2021-06-03  8:54                                       ` Peter Stephenson
2021-06-03 13:13                                         ` Stephane Chazelas
2021-06-03 14:41                                           ` Peter Stephenson
2021-06-04 19:25                                             ` Bart Schaefer
2021-06-05 18:18                                               ` Peter Stephenson
2021-06-09 23:31                                                 ` Bart Schaefer
2021-06-13 16:51                                                   ` Peter Stephenson
2021-06-13 18:04                                                     ` Bart Schaefer
2021-06-13 19:48                                                       ` Peter Stephenson
2021-06-13 21:44                                                         ` Bart Schaefer
2021-06-14  7:19                                                           ` Stephane Chazelas
2021-06-03 18:12                                           ` Bart Schaefer
2021-06-04  8:02                                             ` Stephane Chazelas
2021-06-04 18:36                                               ` Bart Schaefer
2021-06-04 20:21                                                 ` Stephane Chazelas
2021-06-05  0:20                                                   ` Bart Schaefer
2021-06-05 17:05                                                     ` Stephane Chazelas
2021-06-10  0:14                                                       ` Square brackets in command position Bart Schaefer
2021-06-03  6:05                                   ` [PATCH (not final)] (take three?) unset "array[$anything]" Stephane Chazelas
2021-06-03  6:43                                     ` Bart Schaefer
2021-06-03  7:31                                       ` Stephane Chazelas
2021-06-10  0:21                         ` [PATCH] (?) typeset array[position=index]=value Bart Schaefer
2021-06-05  4:29                     ` Mikael Magnusson
2021-06-05  5:49                       ` Bart Schaefer
2021-06-05 11:06                         ` Mikael Magnusson
2021-06-05 16:22                           ` Bart Schaefer
2021-06-18 10:53                         ` Mikael Magnusson
2024-03-08 15:30                 ` [PATCH v3] regexp-replace and ^, word boundary or look-behind operators (and more) Stephane Chazelas
2024-03-09  8:41                   ` [PATCH v5] " Stephane Chazelas
2024-03-09  9:21                     ` MBEGIN when =~ finds bytes inside characters (Was: [PATCH v5] regexp-replace and ^, word boundary or look-behind operators (and more).) Stephane Chazelas
2024-03-09 13:03                   ` [PATCH v3] regexp-replace and ^, word boundary or look-behind operators (and more) Stephane Chazelas
2024-03-10 19:52                     ` [PATCH v6] " Stephane Chazelas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH+w=7Z4RTLsEujWFyvDHZ+GVwiRcSk+p15qgDEPD0dbQwaxOw@mail.gmail.com' \
    --to=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).