Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: support negative LEN in ${VAR:OFFSET:LEN}



Updated with your docs and error messages.

% echo ${path:10:-5}
zsh: substring expression: 8 < 10
% echo ${PATH:150:-150}
zsh: substring expression: 85 < 150

---
 Doc/Zsh/expn.yo        |   12 ++++++++----
 Src/subst.c            |   40 +++++++++++++++++++++++++++++++---------
 Test/D04parameter.ztst |   14 ++++++++++++++
 3 files changed, 53 insertions(+), 13 deletions(-)

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 3a1c372..5d69dbd 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -588,7 +588,7 @@ remove the non-matched elements).
 xitem(tt(${)var(name)tt(:)var(offset)tt(}))
 item(tt(${)var(name)tt(:)var(offset)tt(:)var(length)tt(}))(
 This syntax gives effects similar to parameter subscripting
-in the form tt($)var(name)tt({)var(start)tt(,)var(end)tt(}), but is
+in the form tt($)var(name)tt([)var(start)tt(,)var(end)tt(]), but is
 compatible with other shells; note that both var(offset) and var(length)
 are interpreted differently from the components of a subscript.
 
@@ -608,8 +608,12 @@ the option tt(KSH_ARRAYS).
 A negative offset counts backwards from the end of the scalar or array,
 so that -1 corresponds to the last character or element, and so on.
 
-var(length) is always treated directly as a length and hence may not be
-negative.  The option tt(MULTIBYTE) is obeyed, i.e. the offset and length
+When positive, var(length) counts from the var(offset) position
+toward the end of the scalar or array.  When negative, var(length)
+counts back from the end.  If this results in a position smaller
+than var(offset), a diagnostic is printed and nothing is substituted.
+
+The option tt(MULTIBYTE) is obeyed, i.e. the offset and length
 count multibyte characters where appropriate.
 
 var(offset) and var(length) undergo the same set of shell substitutions
@@ -635,7 +639,7 @@ tt(${)var(name)tt(:-)var(word)tt(}) form of substitution.  Instead, a space
 may be inserted before the tt(-).  Furthermore, neither var(offset) nor
 var(length) may begin with an alphabetic character or tt(&) as these are
 used to indicate history-style modifiers.  To substitute a value from a
-variable, the recommended approach is to proceed it with a tt($) as this
+variable, the recommended approach is to precede it with a tt($) as this
 signifies the intention (parameter substitution can easily be rendered
 unreadable); however, as arithmetic substitution is performed, the
 expression tt(${var: offs}) does work, retrieving the offset from
diff --git a/Src/subst.c b/Src/subst.c
index 723bb25..ce4dbe6 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2834,7 +2834,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	    char *check_offset = check_colon_subscript(s, &check_offset2);
 	    if (check_offset) {
 		zlong offset = mathevali(check_offset);
-		zlong length = (zlong)-1;
+		zlong length;
+		int length_set = 0;
 		int offset_hack_argzero = 0;
 		if (errflag)
 		    return NULL;
@@ -2849,14 +2850,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			zerr("invalid length: %s", check_offset);
 			return NULL;
 		    }
-                    if (check_offset) {
+		    if (check_offset) {
 			length = mathevali(check_offset);
+			length_set = 1;
 			if (errflag)
 			    return NULL;
-			if (length < (zlong)0) {
-			    zerr("invalid length: %s", check_offset);
-			    return NULL;
-			}
 		    }
 		}
 		if (horrible_offset_hack) {
@@ -2884,8 +2882,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    }
 		    if (offset_hack_argzero)
 			alen++;
-		    if (length < 0)
-		      length = alen;
+		    if (length_set) {
+			if (length < 0)
+			    length += alen - offset;
+			if (length < 0) {
+			    zerr("substring expression: %d < %d",
+			         length + offset, offset);
+			    return NULL;
+			}
+		    } else
+			length = alen;
 		    if (offset > alen)
 			offset = alen;
 		    if (offset + length > alen)
@@ -2904,6 +2910,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    aval = newarr;
 		} else {
 		    char *sptr, *eptr;
+		    int given_offset;
 		    if (offset < 0) {
 			MB_METACHARINIT();
 			for (sptr = val; *sptr; ) {
@@ -2913,12 +2920,27 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			if (offset < 0)
 			    offset = 0;
 		    }
+		    given_offset = offset;
 		    MB_METACHARINIT();
+		    if (length_set && length < 0)
+			length -= offset;
 		    for (sptr = val; *sptr && offset; ) {
 			sptr += MB_METACHARLEN(sptr);
 			offset--;
 		    }
-		    if (length >= 0) {
+		    if (length_set) {
+			if (length < 0) {
+			    MB_METACHARINIT();
+			    for (eptr = val; *eptr; ) {
+				eptr += MB_METACHARLEN(eptr);
+				length++;
+			    }
+			    if (length < 0) {
+				zerr("substring expression: %d < %d",
+				     length + given_offset, given_offset);
+				return NULL;
+			    }
+			}
 			for (eptr = sptr; *eptr && length; ) {
 			    eptr += MB_METACHARLEN(eptr);
 			    length--;
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 3646245..b91caaa 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -1346,6 +1346,7 @@
    print ${foo:$(echo 3 + 3):`echo 4 - 3`}
    print ${foo: -1}
    print ${foo: -10}
+   print ${foo:5:-2}
 0:Bash-style offsets, scalar
 >456789
 >56789
@@ -1357,6 +1358,7 @@
 >7
 >9
 >123456789
+>67
 
    foo=(1 2 3 4 5 6 7 8 9)
    print ${foo:3}
@@ -1369,6 +1371,7 @@
    print ${foo:$(echo 3 + 3):`echo 4 - 3`}
    print ${foo: -1}
    print ${foo: -10}
+   print ${foo:5:-2}
 0:Bash-style offsets, array
 >4 5 6 7 8 9
 >5 6 7 8 9
@@ -1380,6 +1383,7 @@
 >7
 >9
 >1 2 3 4 5 6 7 8 9
+>6 7
 
    testfn() {
      emulate -L sh
@@ -1418,3 +1422,13 @@
    print ${str:0:}
 1:Regression test for missing length after offset
 ?(eval):2: unrecognized modifier
+
+   foo="123456789"
+   print ${foo:5:-6}
+1:Regression test for total length < 0 in string
+?(eval):2: substring expression: 3 < 5
+
+   foo=(1 2 3 4 5 6 7 8 9)
+   print ${foo:5:-6}
+1:Regression test for total length < 0 in array
+?(eval):2: substring expression: 3 < 5
-- 
1.7.4-rc1



Messages sorted by: Reverse Date, Date, Thread, Author