Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: RFE: Brace expansion with single characters
On Tue, 07 Jan 2014 19:49:07 -0200
Daniel Serodio <dserodio@xxxxxxxxxxxx> wrote:
> As a long time zsh user, I was surprised to find a bash feature missing
> from zsh: on bash, brace expansion also works with single characters,
> while on zsh only digits.
>
> Please consider this request for enhancement.
As I said elsewhere, this looks straightforward and the tests I've added
suggest I haven't screwed up too badly.
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 5ba3e21..8bbe780 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1539,6 +1539,16 @@ 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).
+An expression of the form `tt({)var(c1)tt(..)var(c2)tt(})', where
+var(c1) and var(c2) are single characters (which may be multibyte
+characters), is expanded to every character in the range from var(c1) to
+var(c2) in whatever character sequence is used internally. For
+characters with code points below 128 this is US ASCII (this is the only
+case most users will need). If any intervening character is not
+printable, appropriate quotation is used to render it printable.
+If the character sequence is reversed, the output is in reverse
+order, e.g. `tt({d..a})' is substituted as `tt(d c b a)'.
+
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 e0d0cf6..f10c0ef 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1895,6 +1895,8 @@ hasbraces(char *str)
switch (*str++) {
case Inbrace:
if (!lbr) {
+ if (bracechardots(str-1, NULL, NULL, NULL))
+ return 1;
lbr = str - 1;
if (*str == '-')
str++;
@@ -2027,6 +2029,55 @@ xpandredir(struct redir *fn, LinkList redirtab)
return ret;
}
+/*
+ * Check for a brace expansion of the form {<char>..<char>}.
+ * On input str must be positioned at an Inbrace, but the sequence
+ * of characters beyond that has not necessarily been checked.
+ * Return 1 if found else 0.
+ *
+ * The other parameters are optionaland if the function returns 1 are
+ * used to return:
+ * - *c1p: the first character in the expansion.
+ * - *c2p: the final character in the expansion.
+ * - *pnext: pointer to the character after the closing brace.
+ */
+
+/**/
+static int
+bracechardots(char *str, convchar_t *c1p, convchar_t *c2p, char **pnextp)
+{
+ convchar_t cstart, cend;
+ char *pnext = str + 1, *pconv, convstr[2];
+ if (itok(*pnext)) {
+ convstr[0] = ztokens[*pnext - Pound];
+ convstr[1] = '\0';
+ pconv = convstr;
+ } else
+ pconv = pnext;
+ MB_METACHARINIT();
+ pnext += MB_METACHARLENCONV(pconv, &cstart);
+ if (cstart == WEOF || pnext[0] != '.' || pnext[1] != '.')
+ return 0;
+ pnext += 2;
+ if (itok(*pnext)) {
+ convstr[0] = ztokens[*pnext - Pound];
+ convstr[1] = '\0';
+ pconv = convstr;
+ } else
+ pconv = pnext;
+ MB_METACHARINIT();
+ pnext += MB_METACHARLENCONV(pconv, &cend);
+ if (cend == WEOF || *pnext != Outbrace)
+ return 0;
+ if (c1p)
+ *c1p = cstart;
+ if (c2p)
+ *c2p = cend;
+ if (pnextp)
+ *pnextp = pnext+1;
+ return 1;
+}
+
/* brace expansion */
/**/
@@ -2060,10 +2111,63 @@ xpandbraces(LinkList list, LinkNode *np)
char *dots, *p, *dots2 = NULL;
LinkNode olast = last;
/* Get the first number of the range */
- zlong rstart = zstrtol(str+1,&dots,10), rend = 0;
+ zlong rstart, rend;
int err = 0, rev = 0, rincr = 1;
- int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2, wid3 = 0;
- int strp = str - str3;
+ int wid1, wid2, wid3, strp;
+ convchar_t cstart, cend;
+ char *pnext;
+
+ if (bracechardots(str, &cstart, &cend, &pnext)) {
+ int lenalloc;
+ /*
+ * This is a character range.
+ */
+ if (cend < cstart) {
+ convchar_t ctmp = cend;
+ cend = cstart;
+ cstart = ctmp;
+ rev = 1;
+ }
+ uremnode(list, node);
+ strp = str - str3;
+ lenalloc = strlen(str3) + 1;
+ for (; cend >= cstart; cend--) {
+#ifdef MULTIBYTE_SUPPORT
+ char *ncptr;
+ int nclen;
+ mb_metacharinit();
+ ncptr = wcs_nicechar(cend, NULL, NULL);
+ /*
+ * Be paranoid about allowing enough space
+ * for metafied multibyte characters. This
+ * saves us having to do bug-prone calculations.
+ */
+ nclen = strlen(ncptr);
+ p = zhalloc(lenalloc + nclen);
+ memcpy(p, str3, strp);
+ memcpy(p + strp, ncptr, nclen);
+ strcpy(p + strp + nclen, str2 + 1);
+#else
+ p = zhalloc(lenalloc);
+ memcpy(p, str3, strp);
+ sprintf(p + strp, "%c", cend);
+ strcat(p + strp, str2 + 1);
+#endif
+ insertlinknode(list, last, p);
+ if (rev) /* decreasing: add in reverse order. */
+ last = nextnode(last);
+ }
+ *np = nextnode(olast);
+ return;
+ }
+
+ /* Get the first number of the range */
+ rstart = zstrtol(str+1,&dots,10);
+ rend = 0;
+ wid1 = (dots - str) - 1;
+ wid2 = (str2 - dots) - 2;
+ wid3 = 0;
+ strp = str - str3;
if (dots == str + 1 || *dots != '.' || dots[1] != '.')
err++;
diff --git a/Test/D09brace.ztst b/Test/D09brace.ztst
index d0ec93c..3e667a8 100644
--- a/Test/D09brace.ztst
+++ b/Test/D09brace.ztst
@@ -97,3 +97,18 @@
0:BRACE_CCL off
>X{za-q521}Y
+ print -r hey{a..j}there
+0:{char..char} ranges, simple case
+>heyathere heybthere heycthere heydthere heyethere heyfthere heygthere heyhthere heyithere heyjthere
+
+ print -r gosh{1,{Z..a},2}cripes
+0:{char..char} ranges, ASCII ordering
+>gosh1cripes goshZcripes gosh[cripes gosh\cripes gosh]cripes gosh^cripes gosh_cripes gosh`cripes goshacripes gosh2cripes
+
+ print -r crumbs{y..p}ooh
+0:{char..char} ranges, reverse
+>crumbsyooh crumbsxooh crumbswooh crumbsvooh crumbsuooh crumbstooh crumbssooh crumbsrooh crumbsqooh crumbspooh
+
+ print -r left{[..]}right
+0:{char..char} ranges with tokenized characters
+>left[right left\right left]right
pws
Messages sorted by:
Reverse Date,
Date,
Thread,
Author