From 25b12c5d1139de9b7527a1fa2b722fe19c46f462 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 5 Dec 2010 21:07:48 +0000 Subject: 28474, 28478: extended {START..END..STEP} syntax --- ChangeLog | 5 +++- Doc/Zsh/expn.yo | 13 +++++++++- Src/glob.c | 71 +++++++++++++++++++++++++++++++++++++++++++++--------- Test/D09brace.ztst | 47 ++++++++++++++++++++++++++++++++++++ 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 + * 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 + -- cgit 1.4.1