about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--Doc/Zsh/expn.yo13
-rw-r--r--Src/glob.c71
-rw-r--r--Test/D09brace.ztst47
4 files changed, 122 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index 0d001fa9a..212ac68f0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2010-12-05  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
+	* Mikael: 28474, 28478: Doc/Zsh/expn.yo, Src/glob.c,
+	Test/D09brace.ztst: extended {START..END..STEP} syntax.
+
 	* 28476: Test/.distfiles, Test/D09brace.ztst: new set of tests
 	for brace expansion.
 
@@ -13893,5 +13896,5 @@
 
 *****************************************************
 * This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.5138 $
+* $Revision: 1.5139 $
 *****************************************************
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 0c26a7ca5..bae3736a9 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1419,9 +1419,20 @@ where var(n1) and var(n2) are integers,
 is expanded to every number between
 var(n1) and var(n2) inclusive.  If either number begins with a
 zero, all the resulting numbers will be padded with leading zeroes to
-that minimum width.  If the numbers are in decreasing order the
+that minimum width, but for negative numbers the tt(-) character is also
+included in the width.  If the numbers are in decreasing order the
 resulting sequence will also be in decreasing order.
 
+An expression of the form `tt({)var(n1)tt(..)var(n2)tt(..)var(n3)tt(})',
+where var(n1), var(n2), and var(n3) are integers,
+is expanded as above, but only every var(n3)th number starting from var(n1)
+is output.  If var(n3) is negative the numbers are output in reverse order,
+this is slightly different from simply swapping var(n1) and var(n2) in the case
+that the step var(n3) doesn't evenly divide the range.  Zero padding can be
+specified in any of the three numbers, specifying it in the third can be useful
+to pad for example `tt({-99..100..01})' which is not possible to specify by putting a
+0 on either of the first two numbers (i.e. pad to two characters).
+
 If a brace expression matches none of the above forms, it is left
 unchanged, unless the option tt(BRACE_CCL) (an abbreviation for `brace
 character class') is set.
diff --git a/Src/glob.c b/Src/glob.c
index c552e6cf1..5f6813589 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1924,14 +1924,29 @@ hasbraces(char *str)
 	case Inbrace:
 	    if (!lbr) {
 		lbr = str - 1;
+		if (*str == '-')
+		    str++;
 		while (idigit(*str))
 		    str++;
 		if (*str == '.' && str[1] == '.') {
-		    str++;
-		    while (idigit(*++str));
+		    str++; str++;
+		    if (*str == '-')
+			str++;
+		    while (idigit(*str))
+			str++;
 		    if (*str == Outbrace &&
 			(idigit(lbr[1]) || idigit(str[-1])))
 			return 1;
+		    else if (*str == '.' && str[1] == '.') {
+			str++; str++;
+			if (*str == '-')
+			    str++;
+			while (idigit(*str))
+			    str++;
+			if (*str == Outbrace &&
+			    (idigit(lbr[1]) || idigit(str[-1])))
+			    return 1;
+		    }
 		}
 	    } else {
 		char *s = --str;
@@ -2061,18 +2076,20 @@ xpandbraces(LinkList list, LinkNode *np)
 	} else if (bc == 1) {
 	    if (*str2 == Comma)
 		++comma;	/* we have {foo,bar} */
-	    else if (*str2 == '.' && str2[1] == '.')
+	    else if (*str2 == '.' && str2[1] == '.') {
 		dotdot++;	/* we have {num1..num2} */
+		++str2;
+	    }
 	}
     DPUTS(bc, "BUG: unmatched brace in xpandbraces()");
     if (!comma && dotdot) {
 	/* Expand range like 0..10 numerically: comma or recursive
 	   brace expansion take precedence. */
-	char *dots, *p;
+	char *dots, *p, *dots2 = NULL;
 	LinkNode olast = last;
 	/* Get the first number of the range */
-	int rstart = zstrtol(str+1,&dots,10), rend = 0, err = 0, rev = 0;
-	int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2;
+	int rstart = zstrtol(str+1,&dots,10), rend = 0, err = 0, rev = 0, rincr = 1;
+	int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2, wid3 = 0;
 	int strp = str - str3;
 
 	if (dots == str + 1 || *dots != '.' || dots[1] != '.')
@@ -2080,23 +2097,53 @@ xpandbraces(LinkList list, LinkNode *np)
 	else {
 	    /* Get the last number of the range */
 	    rend = zstrtol(dots+2,&p,10);
-	    if (p == dots+2 || p != str2)
+	    if (p == dots+2)
 		err++;
+	    /* check for {num1..num2..incr} */
+	    if (p != str2) {
+		wid2 = (p - dots) - 2;
+		dots2 = p;
+		if (dotdot == 2 && *p == '.' && p[1] == '.') {
+		    rincr = zstrtol(p+2, &p, 10);
+		    wid3 = p - dots2 - 2;
+		    if (p != str2 || !rincr)
+			err++;
+		} else
+		    err++;
+	    }
 	}
 	if (!err) {
 	    /* If either no. begins with a zero, pad the output with   *
-	     * zeroes. Otherwise, choose a min width to suppress them. */
-	    int minw = (str[1] == '0') ? wid1 : (dots[2] == '0' ) ? wid2 :
-		(wid2 > wid1) ? wid1 : wid2;
+	     * zeroes. Otherwise, set min width to 0 to suppress them.
+	     * str+1 is the first number in the range, dots+2 the last,
+	     * and dots2+2 is the increment if that's given. */
+	    /* TODO: sorry about this */
+	    int minw = (str[1] == '0' || (str[1] == '-' && str[2] == '0'))
+		       ? wid1
+		       : (dots[2] == '0' || (dots[2] == '-' && dots[3] == '0'))
+		       ? wid2
+		       : (dots2 && (dots2[2] == '0' ||
+				    (dots2[2] == '-' && dots2[3] == '0')))
+		       ? wid3
+		       : 0;
+	    if (rincr < 0) {
+		/* Handle negative increment */
+		rincr = -rincr;
+		rev = !rev;
+	    }
 	    if (rstart > rend) {
 		/* Handle decreasing ranges correctly. */
 		int rt = rend;
 		rend = rstart;
 		rstart = rt;
-		rev = 1;
+		rev = !rev;
+	    } else if (rincr > 1) {
+		/* when incr > 1, range is aligned to the highest number of str1,
+		 * compensate for this so that it is aligned to the first number */
+		rend -= (rend - rstart) % rincr;
 	    }
 	    uremnode(list, node);
-	    for (; rend >= rstart; rend--) {
+	    for (; rend >= rstart; rend -= rincr) {
 		/* Node added in at end, so do highest first */
 		p = dupstring(str3);
 		sprintf(p + strp, "%0*d", minw, rend);
diff --git a/Test/D09brace.ztst b/Test/D09brace.ztst
index ff2eb522c..d0ec93cd3 100644
--- a/Test/D09brace.ztst
+++ b/Test/D09brace.ztst
@@ -50,3 +50,50 @@
   print X{4..1}Y
 0:Numeric range expansion, decreasing
 >X4Y X3Y X2Y X1Y
+
+  print X{1..4}{1..4}Y
+0:Numeric range expansion, combined braces
+>X11Y X12Y X13Y X14Y X21Y X22Y X23Y X24Y X31Y X32Y X33Y X34Y X41Y X42Y X43Y X44Y
+
+  print X{-4..4}Y
+0:Numeric range expansion, negative numbers (1)
+>X-4Y X-3Y X-2Y X-1Y X0Y X1Y X2Y X3Y X4Y
+
+  print X{4..-4}Y
+0:Numeric range expansion, negative numbers (2)
+>X4Y X3Y X2Y X1Y X0Y X-1Y X-2Y X-3Y X-4Y
+
+  print X{004..-4..2}Y
+0:Numeric range expansion, stepping and padding (1)
+>X004Y X002Y X000Y X-02Y X-04Y
+
+  print X{4..-4..02}Y
+0:Numeric range expansion, stepping and padding (1)
+>X04Y X02Y X00Y X-2Y X-4Y
+
+  print X{1..32..3}Y
+0:Numeric range expansion, step alignment (1)
+>X1Y X4Y X7Y X10Y X13Y X16Y X19Y X22Y X25Y X28Y X31Y
+
+  print X{1..32..-3}Y
+0:Numeric range expansion, step alignment (2)
+>X31Y X28Y X25Y X22Y X19Y X16Y X13Y X10Y X7Y X4Y X1Y
+
+  print X{32..1..3}Y
+0:Numeric range expansion, step alignment (3)
+>X32Y X29Y X26Y X23Y X20Y X17Y X14Y X11Y X8Y X5Y X2Y
+
+  print X{32..1..-3}Y
+0:Numeric range expansion, step alignment (4)
+>X2Y X5Y X8Y X11Y X14Y X17Y X20Y X23Y X26Y X29Y X32Y
+
+  setopt brace_ccl
+  print X{za-q521}Y
+  unsetopt brace_ccl
+0:BRACE_CCL on
+>X1Y X2Y X5Y XaY XbY XcY XdY XeY XfY XgY XhY XiY XjY XkY XlY XmY XnY XoY XpY XqY XzY
+
+  print X{za-q521}Y
+0:BRACE_CCL off
+>X{za-q521}Y
+