summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Keller <jacob.e.keller@intel.com>2017-03-28 15:49:50 -0700
committerPádraig Brady <P@draigBrady.com>2017-04-02 16:35:08 -0700
commit9daef34543cc3267ec8e2cc22488bf202448ed84 (patch)
treee0cb2828dc5477fe61f9edf6db41c36edd350664
parentc7bcea1b78a581e85247d51980db22b7a3858f58 (diff)
downloadcoreutils-9daef34543cc3267ec8e2cc22488bf202448ed84.tar.gz
expand,unexpand: add support for incremental tab stops
Support --tabs="1,+8" which is equivalent to --tabs="1,9,17,..." useful for viewing unified diff output with its 1 character gutter for example. * doc/coreutils.texi ({expand,unexpand} invocation): Document, using diff processing as the example. * src/expand-common.c (set_increment_size): Update the new increment_size global. (parse_tab_stops): Handle the new '+' prefix. (finalize_tab_stops): Verify both '+' and '/' prefixes are not used together. * tests/misc/expand.pl: Add test cases. * NEWS: Mention the new feature.
-rw-r--r--NEWS4
-rw-r--r--doc/coreutils.texi8
-rw-r--r--src/expand-common.c60
-rwxr-xr-xtests/misc/expand.pl16
4 files changed, 86 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 47d237be9..7f2d895eb 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,10 @@ GNU coreutils NEWS -*- outline -*-
** New features
+ expand and unexpand now support specifying an offset for tab stops
+ by prefixing the last specified number like --tabs=1,+8 which is
+ useful for visualizing diff output for example.
+
split supports a new --hex-suffixes[=from] option to create files with
lower case hexadecimal suffixes, similar to the --numeric-suffixes option.
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b8e24aa56..c22e07615 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -7056,10 +7056,18 @@ If only one tab stop is given, set the tabs @var{tab1} spaces apart
last tab stop given with single spaces.
@macro gnuExpandTabs
Tab stops can be separated by blanks as well as by commas.
+
As a GNU extension the last @var{tab} specified can be prefixed
with a @samp{/} to indicate a tab size to use for remaining positions.
For example, @option{--tabs=2,4,/8} will set tab stops at position 2 and 4,
and every multiple of 8 after that.
+
+Also the last @var{tab} specified can be prefixed with a @samp{+} to indicate
+a tab size to use for remaining positions, offset from the final explicitly
+specified tab stop.
+For example, to ignore the 1 character gutter present in diff output,
+one can specify a 1 character offset using @option{--tabs=1,+8},
+which will set tab stops at positions 1,9,17,@dots{}
@end macro
@gnuExpandTabs
diff --git a/src/expand-common.c b/src/expand-common.c
index e1b549fab..05e5bec2d 100644
--- a/src/expand-common.c
+++ b/src/expand-common.c
@@ -38,6 +38,9 @@ static uintmax_t tab_size = 0;
/* If nonzero, the size of all tab stops after the last specifed. */
static uintmax_t extend_size = 0;
+/* If nonzero, an increment for additional tab stops after the last specified.*/
+static uintmax_t increment_size = 0;
+
/* The maximum distance between tab stops. */
size_t max_column_width;
@@ -106,6 +109,23 @@ set_extend_size (uintmax_t tabval)
return ok;
}
+static bool
+set_increment_size (uintmax_t tabval)
+{
+ bool ok = true;
+
+ if (increment_size)
+ {
+ error (0,0,
+ _("'+' specifier only allowed"
+ " with the last value"));
+ ok = false;
+ }
+ increment_size = tabval;
+
+ return ok;
+}
+
/* Add the comma or blank separated list of tab stops STOPS
to the list of tab stops. */
extern void
@@ -114,6 +134,7 @@ parse_tab_stops (char const *stops)
bool have_tabval = false;
uintmax_t tabval = 0;
bool extend_tabval = false;
+ bool increment_tabval = false;
char const *num_start = NULL;
bool ok = true;
@@ -131,6 +152,14 @@ parse_tab_stops (char const *stops)
break;
}
}
+ else if (increment_tabval)
+ {
+ if (! set_increment_size (tabval))
+ {
+ ok = false;
+ break;
+ }
+ }
else
add_tab_stop (tabval);
}
@@ -145,6 +174,18 @@ parse_tab_stops (char const *stops)
ok = false;
}
extend_tabval = true;
+ increment_tabval = false;
+ }
+ else if (*stops == '+')
+ {
+ if (have_tabval)
+ {
+ error (0, 0, _("'+' specifier not at start of number: %s"),
+ quote (stops));
+ ok = false;
+ }
+ increment_tabval = true;
+ extend_tabval = false;
}
else if (ISDIGIT (*stops))
{
@@ -179,6 +220,8 @@ parse_tab_stops (char const *stops)
{
if (extend_tabval)
ok &= set_extend_size (tabval);
+ else if (increment_tabval)
+ ok &= set_increment_size (tabval);
else
add_tab_stop (tabval);
}
@@ -204,6 +247,9 @@ validate_tab_stops (uintmax_t const *tabs, size_t entries)
die (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
prev_tab = tabs[i];
}
+
+ if (increment_size && extend_size)
+ die (EXIT_FAILURE, 0, _("'/' specifier is mutually exclusive with '+'"));
}
/* Called after all command-line options have been parsed,
@@ -220,8 +266,10 @@ finalize_tab_stops (void)
validate_tab_stops (tab_list, first_free_tab);
if (first_free_tab == 0)
- tab_size = max_column_width = extend_size ? extend_size : 8;
- else if (first_free_tab == 1 && ! extend_size)
+ tab_size = max_column_width = extend_size
+ ? extend_size : increment_size
+ ? increment_size : 8;
+ else if (first_free_tab == 1 && ! extend_size && ! increment_size)
tab_size = tab_list[0];
else
tab_size = 0;
@@ -251,6 +299,14 @@ get_next_tab_column (const uintmax_t column, size_t* tab_index,
if (extend_size)
return column + (extend_size - column % extend_size);
+ /* incremental last tab - add increment_size to the previous tab stop */
+ if (increment_size)
+ {
+ uintmax_t end_tab = tab_list[first_free_tab - 1];
+
+ return column + (increment_size - ((column - end_tab) % increment_size));
+ }
+
*last_tab = true;
return 0;
}
diff --git a/tests/misc/expand.pl b/tests/misc/expand.pl
index b04d2e72d..7fe783042 100755
--- a/tests/misc/expand.pl
+++ b/tests/misc/expand.pl
@@ -146,11 +146,27 @@ my @Tests =
['trail3', '--tabs=1,2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
['trail4', '--tabs=/5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
['trail5', '--tabs=//5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['trail5a','--tabs=+/5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
['trail6', '--tabs=/,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
['trail7', '--tabs=,/5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
['trail8', '--tabs=1 -t/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
['trail9', '--tab=1,2 -t/5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+ # Test incremental trailing '+' feature which specifies that
+ # tab stops should continue every increment
+ ['incre0', '--tab=1,+5', {IN=>"+\t\ta\tb"}, {OUT=>"+ a b"}],
+ ['incre1', '--tabs=1,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+ ['incre2', '--tabs=2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+ ['incre3', '--tabs=1,2,+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+ ['incre4', '--tabs=+5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['incre5', '--tabs=++5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['incre5a','--tabs=/+5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['incre6', '--tabs=+,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['incre7', '--tabs=,+5', {IN=>"\ta\tb"}, {OUT=>" a b"}],
+ ['incre8', '--tabs=1 -t+5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+ ['incre9', '--tab=1,2 -t+5',{IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
+
+
# Test errors
['e1', '--tabs="a"', {IN=>''}, {OUT=>''}, {EXIT=>1},
{ERR => "$prog: tab size contains invalid character(s): 'a'\n"}],