summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2010-02-27 00:18:13 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2010-02-27 00:18:13 +0000
commit21193d7f0128dc66f4c41c7b06d1d20c5b9339fc (patch)
tree63fef850d047a07a8bd31b5c8cca124f8e74297e
parent31123a118472ee4e4afbfae5dff904bb40113e65 (diff)
downloadzsh-21193d7f0128dc66f4c41c7b06d1d20c5b9339fc.tar.gz
zsh-21193d7f0128dc66f4c41c7b06d1d20c5b9339fc.zip
users/14905 + 14906: problems with :s in parameter expansion
-rw-r--r--ChangeLog8
-rw-r--r--Doc/Zsh/expn.yo22
-rw-r--r--Src/subst.c31
-rw-r--r--Test/D04parameter.ztst26
4 files changed, 73 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index e24788548..ed70e59f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2010-02-27 Peter Stephenson <p.w.stephenson@ntlworld.com>
+
+ * users/14905 (modified, see users/14096): Doc/Zsh/expn.yo,
+ Src/subst.c, Test/D04parameter.ztst: fix various problems
+ with :s modifier in parameters.
+
2010-02-26 Peter Stephenson <pws@csr.com>
* users/14902: Src/Modules/datetime.c: another go.
@@ -12843,5 +12849,5 @@
*****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.4920 $
+* $Revision: 1.4921 $
*****************************************************
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 6b020a280..7e55ff419 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -273,6 +273,8 @@ The forms `tt(gs/)var(l)tt(/)var(r)' and `tt(s/)var(l)tt(/)var(r)tt(/:G)'
perform global substitution, i.e. substitute every occurrence of var(r)
for var(l). Note that the tt(g) or tt(:G) must appear in exactly the
position shown.
+
+See further notes on this form of substitution below.
)
item(tt(&))(
Repeat the previous tt(s) substitution. Like tt(s), may be preceded
@@ -293,18 +295,18 @@ parameter expansion.
)
enditem()
-The tt(s/l/r/) substitution works as follows. By default the left-hand
-side of substitutions are not patterns, but character strings. Any
-character can be used as the delimiter in place of `tt(/)'. A
-backslash quotes the delimiter character. The character `tt(&)', in
-the right-hand-side var(r), is replaced by the text from the
+The tt(s/)var(l)tt(/)var(r)tt(/) substitution works as follows. By
+default the left-hand side of substitutions are not patterns, but
+character strings. Any character can be used as the delimiter in place
+of `tt(/)'. A backslash quotes the delimiter character. The character
+`tt(&)', in the right-hand-side var(r), is replaced by the text from the
left-hand-side var(l). The `tt(&)' can be quoted with a backslash. A
-null var(l) uses the previous string either from the previous var(l)
-or from the contextual scan string var(s) from `tt(!?)var(s)'. You can
+null var(l) uses the previous string either from the previous var(l) or
+from the contextual scan string var(s) from `tt(!?)var(s)'. You can
omit the rightmost delimiter if a newline immediately follows var(r);
-the rightmost `tt(?)' in a context scan can similarly be omitted.
-Note the same record of the last var(l) and var(r) is maintained
-across all forms of expansion.
+the rightmost `tt(?)' in a context scan can similarly be omitted. Note
+the same record of the last var(l) and var(r) is maintained across all
+forms of expansion.
If the option tt(HIST_SUBST_PATTERN) is set, var(l) is treated as
a pattern of the usual form described in
diff --git a/Src/subst.c b/Src/subst.c
index 462d0b3a0..a5ed289be 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3282,6 +3282,13 @@ modify(char **str, char **ptr)
ptr1 += charlen;
for (ptr2 = ptr1, charlen = 0; *ptr2; ptr2 += charlen) {
convchar_t del2;
+ if ((*ptr2 == Bnull || *ptr2 == '\\') && ptr2[1]) {
+ /* in double quotes, the backslash isn't tokenized */
+ if (*ptr2 == '\\')
+ *ptr2 = Bnull;
+ charlen = 2;
+ continue;
+ }
charlen = MB_METACHARLENCONV(ptr2, &del2);
#ifdef MULTIBYTE_SUPPORT
if (del2 == WEOF)
@@ -3301,6 +3308,13 @@ modify(char **str, char **ptr)
*ptr1end = '\0';
for (ptr3 = ptr2, charlen = 0; *ptr3; ptr3 += charlen) {
convchar_t del3;
+ if ((*ptr3 == Bnull || *ptr3 == '\\') && ptr3[1]) {
+ /* in double quotes, the backslash isn't tokenized */
+ if (*ptr3 == '\\')
+ *ptr3 = Bnull;
+ charlen = 2;
+ continue;
+ }
charlen = MB_METACHARLENCONV(ptr3, &del3);
#ifdef MULTIBYTE_SUPPORT
if (del3 == WEOF)
@@ -3326,9 +3340,20 @@ modify(char **str, char **ptr)
chuck(tt--);
if (!isset(HISTSUBSTPATTERN))
untokenize(hsubl);
- for (tt = hsubr = ztrdup(ptr2); *tt; tt++)
- if (inull(*tt) && *tt != Bnullkeep)
- chuck(tt--);
+ for (tt = hsubr = ztrdup(ptr2); *tt; tt++) {
+ if (inull(*tt) && *tt != Bnullkeep) {
+ if (*tt == Bnull && (tt[1] == '&' || tt[1] == '\\')) {
+ /*
+ * The substitution will treat \& and \\
+ * specially. We need to leave real \'s
+ * as the first character for this to work.
+ */
+ *tt = '\\';
+ } else {
+ chuck(tt--);
+ }
+ }
+ }
*ptr1end = sav1;
*ptr3 = sav;
*ptr = ptr3 - 1;
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 59fa3ac91..f81b7de1d 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -947,6 +947,32 @@
>/
>/
+ baz=foo/bar
+ zab=oof+rab
+ print ${baz:s/\//+/}
+ print "${baz:s/\//+/}"
+ print ${zab:s/+/\//}
+ print "${zab:s/+/\//}"
+0:Quoting of separator in substitution modifier
+>foo+bar
+>foo+bar
+>oof/rab
+>oof/rab
+
+ print -r ${${:-one/two}:s,/,X&Y,}
+ print -r ${${:-one/two}:s,/,X\&Y,}
+ print -r ${${:-one/two}:s,/,X\\&Y,}
+ print -r "${${:-one/two}:s,/,X&Y,}"
+ print -r "${${:-one/two}:s,/,X\&Y,}"
+ print -r "${${:-one/two}:s,/,X\\&Y,}"
+0:Quoting of ampersand in substitution modifier RHS
+>oneX/Ytwo
+>oneX&Ytwo
+>oneX\/Ytwo
+>oneX/Ytwo
+>oneX&Ytwo
+>oneX\/Ytwo
+
nully=($'a\0c' $'a\0b\0b' $'a\0b\0a' $'a\0b\0' $'a\0b' $'a\0' $'a')
for string in ${(o)nully}; do
for (( i = 1; i <= ${#string}; i++ )); do