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

extended braces syntax, {1..32..-03}



This patch adds the syntax {start..end..step} to brace expansion. It
also allows negative numbers to be used (this was allowed when
braceccl was set before, which worked inconsistently). It also fixes
up zero-padding with negative numbers. You can specify zeropadding in
the step, as well as a negative step. Using a negative step rather
than reversing the order makes a difference as shown here:

% echo {1..32..4}
1 5 9 13 17 21 25 29
% echo {1..32..-4}
29 25 21 17 13 9 5 1
% echo {32..1..4}
32 28 24 20 16 12 8 4
% echo {32..1..-4}
4 8 12 16 20 24 28 32

The basic syntax is stolen/borrowed from bash, but bash does not allow
negative steps.

Missing still is documentation, but I've had this lying around since
august so I figured I'd post it and see if anyone wants to change
anything first.

Zero-padding works like this:

% echo {01..4}
01 02 03 04
% echo {01..-4}
01 00 -1 -2 -3 -4
% echo {1..-04}
001 000 -01 -02 -03 -04
% echo {1..-4..01}
01 00 -1 -2 -3 -4
% echo {1..-4..-02}
-03 -01 001

Originally i had the same number of zeroes regardless of the minus
sign, but bash does it this way, so I figured I'd be consistent with
that.

The code is admittedly somewhat cryptic in places.

Here's a link to the patch,
http://mika.l3ib.org/patches/zsh-extended-braces.patch
And here's the patch

From 6fa4a778632320f234f0b9c1f477f5cd3a66a5ff Mon Sep 17 00:00:00 2001
From: Mikael Magnusson <mikachu@xxxxxxxxx>
Date: Wed, 7 Jul 2010 00:12:25 +0200
Subject: [PATCH] Add {1..10..2} syntax, allow negative numbers without
braceccl set

---
 Src/glob.c |   71 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/Src/glob.c b/Src/glob.c
index c552e6c..5f68135 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);
-- 
1.7.3



-- 
Mikael Magnusson



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