From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24256 invoked by alias); 1 Jun 2015 15:39:35 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 35353 Received: (qmail 25272 invoked from network); 1 Jun 2015 15:39:32 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,T_HDRS_LCASE, T_MANY_HDRS_LCASE autolearn=ham autolearn_force=no version=3.4.0 X-AuditID: cbfec7f4-f79c56d0000012ee-ac-556c7cb12ede Date: Mon, 01 Jun 2015 16:39:26 +0100 From: Peter Stephenson To: Zsh Hackers' List Subject: PATCH: expand tabs Message-id: <20150601163926.55ddfc47@pwslap01u.europe.root.pri> 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+NgFlrNLMWRmVeSWpSXmKPExsVy+t/xa7oba3JCDSZ0C1ocbH7I5MDoserg B6YAxigum5TUnMyy1CJ9uwSujPaLE9kKumwqns7Zz9LAeEq3i5GTQ0LAROLrxk9sELaYxIV7 64FsLg4hgaWMEt1P90M5M5gkphy/xQjhbGWU2LRpL1gLi4CqxLmPW1lAbDYBQ4mpm2YDFXFw iAhoS7R/FAMJCwtIS7SfWgFWzitgL/H75QJ2EJtfQF/i6t9PTBCb7SVmXjnDCFEjKPFj8j2w kcwCWhKbtzWxQtjyEpvXvGUGsYUE1CVu3N3NPoFRYBaSlllIWmYhaVnAyLyKUTS1NLmgOCk9 11CvODG3uDQvXS85P3cTIyQIv+xgXHzM6hCjAAejEg+vQGd2qBBrYllxZe4hRgkOZiURXquK nFAh3pTEyqrUovz4otKc1OJDjNIcLErivHN3vQ8REkhPLEnNTk0tSC2CyTJxcEo1MBoecQj7 Mr/Zz4BH+o9MeqD7jv/eZ+JNcl4ue8Ap+E/ObIdrTOGDd/MCVlx+/8zlibbWka6VCrz9XzdJ 1qzrSGUzXrrzyN37F2X/N51ze/1y6vddUT/47slZPtIuDzxmvcBN5EaS4rmpnUwFq2ZE+6xo Mn91aZrx2pcXsh965UVLX7yTeNNw5ywlluKMREMt5qLiRADs1bIDPgIAAA== I thought it was about time print had the ability to expand tabs internally. The main intended use for this is to get reasonably formatted function bodies from shell builtins and special variables without having to pipe through an external command. It could be attached to a whence option with a bit of wiring in hashtable.c and support for metafied strings. diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 1fcc7c2..6fa603a 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1106,7 +1106,7 @@ tt(popd) that do not change the environment seen by an interactive user. ) findex(print) xitem(tt(print )[ tt(-abcDilmnNoOpPrsSz) ] [ tt(-u) var(n) ] [ tt(-f) var(format) ] [ tt(-C) var(cols) ]) -item(SPACES()[ tt(-R) [ tt(-en) ]] [ var(arg) ... ])( +item(SPACES()[ tt(-xX) var(tab-stop) ] [ tt(-R) [ tt(-en) ]] [ var(arg) ... ])( With the `tt(-f)' option the arguments are printed as described by tt(printf). With no flags or with the flag `tt(-)', the arguments are printed on the standard output as described by tt(echo), with the following differences: @@ -1201,6 +1201,27 @@ tt(HIST_LEX_WORDS) option active. item(tt(-u) var(n))( Print the arguments to file descriptor var(n). ) +item(tt(-x) var(tab-stop))( +Expand leading tabs on each line of output in the printed string +assuming a tab stop every var(tab-stop) characters. This is appropriate +for formatting code that may be indented with tabs. Note that leading +tabs of any argument to print, not just the first, are expanded, even if +tt(print) is using spaces to separate arguments (the column count +is maintained across arguments but may be incorrect on output +owing to previous unexpanded tabs). + +The start of the output of each print command is assumed to be aligned +with a tab stop. Widths of multibyte characters are handled if the +option tt(MULTIBYTE) is in effect. This option is ignored if other +formatting options are in effect, namely column alignment or +tt(printf) style, or if output is to a special location such as shell +history or the command line editor. +) +item(tt(-X) var(tab-stop))( +This is similar to tt(-x), except that all tabs in the printed string +are expanded. This is appropriate if tabs in the arguments are +being used to produce a table format. +) item(tt(-z))( Push the arguments onto the editing buffer stack, separated by spaces. ) diff --git a/Src/builtin.c b/Src/builtin.c index 9358e8b..4b08146 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -99,7 +99,7 @@ static struct builtin builtins[] = #endif BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL), - BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:z-", NULL), + BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:x:X:z-", NULL), BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, NULL), BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL), BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), @@ -4208,11 +4208,40 @@ bin_print(char *name, char **args, Options ops, int func) return 0; } - for (; *args; args++, len++) { - fwrite(*args, *len, 1, fout); - if (args[1]) - fputc(OPT_ISSET(ops,'l') ? '\n' : - OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + if (OPT_HASARG(ops, 'x') || OPT_HASARG(ops, 'X')) { + char *eptr; + int expand, startpos = 0; + int all = OPT_HASARG(ops, 'X'); + char *xarg = all ? OPT_ARG(ops, 'X') : OPT_ARG(ops, 'x'); + + expand = (int)zstrtol(xarg, &eptr, 10); + if (*eptr || expand <= 0) { + zwarnnam(name, "positive integer expected after -%c: %s", 'x', + xarg); + return 1; + } + for (; *args; args++, len++) { + startpos = zexpandtabs(*args, *len, expand, startpos, fout, + all); + if (args[1]) { + if (OPT_ISSET(ops, 'l')) { + fputc('\n', fout); + startpos = 0; + } else if (OPT_ISSET(ops,'N')) { + fputc('\0', fout); + } else { + fputc(' ', fout); + startpos++; + } + } + } + } else { + for (; *args; args++, len++) { + fwrite(*args, *len, 1, fout); + if (args[1]) + fputc(OPT_ISSET(ops,'l') ? '\n' : + OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + } } if (!(OPT_ISSET(ops,'n') || nnl)) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); diff --git a/Src/utils.c b/Src/utils.c index 271c800..ddfe480 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4964,6 +4964,108 @@ metacharlenconv(const char *x, int *c) /**/ #endif /* MULTIBYTE_SUPPORT */ +/* + * Expand tabs to given width, with given starting position on line. + * len is length of unmetafied string in bytes. + * Output to fout. + * Return the end position on the line, i.e. if this is 0 modulo width + * the next character is aligned with a tab stop. + * + * If all is set, all tabs are expanded, else only leading tabs. + */ + +/**/ +mod_export int +zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout, + int all) +{ + int at_start = 1; + +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + size_t ret; + wchar_t wc; + + memset(&mbs, 0, sizeof(mbs)); +#endif + + while (len) { + if (*s == '\t') { + if (all || at_start) { + s++; + len--; + if (width <= 0 || !(startpos % width)) { + /* always output at least one space */ + fputc(' ', fout); + startpos++; + } + if (width <= 0) + continue; /* paranoia */ + while (startpos % width) { + fputc(' ', fout); + startpos++; + } + } else { + /* + * Leave tab alone. + * Guess width to apply... we might get this wrong. + * This is only needed if there's a following string + * that needs tabs expanding, which is unusual. + */ + startpos += width - startpos % width; + s++; + len--; + fputc('\t', fout); + } + continue; + } else if (*s == '\n' || *s == '\r') { + fputc(*s, fout); + s++; + len--; + startpos = 0; + at_start = 1; + continue; + } + + at_start = 0; +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + const char *sstart = s; + ret = mbrtowc(&wc, s, len, &mbs); + if (ret == MB_INVALID) { + /* Assume single character per character */ + memset(&mbs, 0, sizeof(mbs)); + s++; + len--; + } else if (ret == MB_INCOMPLETE) { + /* incomplete at end --- assume likewise, best we've got */ + s++; + len--; + } else { + s += ret; + len -= (int)ret; + } + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + startpos++; + } else { + int wcw = WCWIDTH(wc); + if (wcw > 0) /* paranoia */ + startpos += wcw; + } + fwrite(sstart, s - sstart, 1, fout); + + continue; + } +#endif /* MULTIBYTE_SUPPORT */ + fputc(*s, fout); + s++; + len--; + startpos++; + } + + return startpos; +} + /* check for special characters in the string */ /**/ diff --git a/Test/B03print.ztst b/Test/B03print.ztst index 48574c2..54d6350 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -284,3 +284,16 @@ >610062 >6100 >61 + + foo=$'one\ttwo\tthree\tfour\n' + foo+=$'\tone\ttwo\tthree\tfour\n' + foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour' + print -x4 $foo + print -X4 $foo +0:Tab expansion by print +>one two three four +> one two three four +> one two three four +>one two three four +> one two three four +> one two three four