From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6733 invoked by alias); 8 Jan 2014 12:23:59 -0000 Mailing-List: contact zsh-users-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Users List List-Post: List-Help: X-Seq: 18298 Received: (qmail 14846 invoked from network); 8 Jan 2014 12:23:42 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-4.9 required=5.0 tests=BAYES_00,LONGWORDS, RCVD_IN_DNSWL_HI,SPF_HELO_PASS autolearn=ham version=3.3.2 X-AuditID: cbfec7f4-b7f796d000005a13-cb-52cd40efb77e Date: Wed, 08 Jan 2014 12:13:34 +0000 From: Peter Stephenson To: zsh-users@zsh.org Subject: Re: RFE: Brace expansion with single characters Message-id: <20140108121334.3c13484a@pwslap01u.europe.root.pri> In-reply-to: <52CC7653.9080709@dserodio.net> References: <52CC7653.9080709@dserodio.net> Organization: Samsung Cambridge Solution Centre X-Mailer: Claws Mail 3.7.9 (GTK+ 2.22.0; i386-redhat-linux-gnu) MIME-version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7bit X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuphluLIzCtJLcpLzFFi42I5/e/4Fd33DmeDDNqOqFrsOLmS0YHRY9XB D0wBjFFcNimpOZllqUX6dglcGT+6l7IUbDao+HXyL3sD4y7VLkZODgkBE4kpc+ezQthiEhfu rWfrYuTiEBJYyiixZ9tUdiiHSeLSrt/sIFUsAqoS3058YgOx2QQMJaZums0IYosIiEosX7EZ rEZYwELi6NwPzCA2r4C9RMPFPqANHBycAjoSy9aLg4SFBLQl3l06AVbCL6AvcfXvJyaII+wl Zl45wwjRKijxY/I9FhCbWUBLYvO2JlYIW15i85q3zBMYBWYhKZuFpGwWkrIFjMyrGEVTS5ML ipPScw31ihNzi0vz0vWS83M3MUJC8MsOxsXHrA4xCnAwKvHw3lA7EyTEmlhWXJl7iFGCg1lJ hFdP+WyQEG9KYmVValF+fFFpTmrxIUYmDk6pBkaTyuMRHy8cPSaUMmPyg3sbQ2+5fTl0z1nm /s4Lsz3XuQVsPhn31PephMujT8nbcv6s3HSw86mXqpBj3fd4h26WOrNDe14/f1eelRES8kKo vavvLdO9VeZdU15s8Zi4PCkg7FXAwbVKJu2bii+vyslqru/RnTRp82u//Afu0xazyxz4u+/s KhNxJZbijERDLeai4kQAF9KV+x8CAAA= On Tue, 07 Jan 2014 19:49:07 -0200 Daniel Serodio 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 {..}. + * 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