From 21193d7f0128dc66f4c41c7b06d1d20c5b9339fc Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 27 Feb 2010 00:18:13 +0000 Subject: users/14905 + 14906: problems with :s in parameter expansion --- ChangeLog | 8 +++++++- Doc/Zsh/expn.yo | 22 ++++++++++++---------- Src/subst.c | 31 ++++++++++++++++++++++++++++--- Test/D04parameter.ztst | 26 ++++++++++++++++++++++++++ 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 + + * 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 * 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 -- cgit 1.4.1