summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--Doc/Zsh/builtins.yo23
-rw-r--r--Src/builtin.c41
-rw-r--r--Src/utils.c102
-rw-r--r--Test/B03print.ztst13
5 files changed, 177 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index bf10bedc2..090a9be24 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2015-06-02 Peter Stephenson <p.stephenson@samsung.com>
+
+ * 35353: Doc/Zsh/builtins.yo, Src/builtin.c, Src/utils.c,
+ Test/B03print.ztst: print -x and print -X expand tabs.
+
2015-06-02 Oliver Kiddle <opk@zsh.org>
* 35356: Completion/Unix/Type/_ttys, Completion/Unix/Command/_ps,
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 1fcc7c2b7..6fa603ac8 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 9358e8b1f..4b081468d 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 271c800fd..7409dc876 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 48574c227..54d6350cf 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