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