]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
expand,unexpand: add support for incremental tab stops
authorJacob Keller <jacob.e.keller@intel.com>
Tue, 28 Mar 2017 22:49:50 +0000 (15:49 -0700)
committerPádraig Brady <P@draigBrady.com>
Sun, 2 Apr 2017 23:35:08 +0000 (16:35 -0700)
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.

NEWS
doc/coreutils.texi
src/expand-common.c
tests/misc/expand.pl

diff --git a/NEWS b/NEWS
index 47d237be9b7f835d25aab4674cc4400b2d65902d..7f2d895eb8921ec260305e3545d69d98539028c4 100644 (file)
--- 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.
 
index b8e24aa56f9d115af5a24688bde8e3dfc9b18503..c22e0761578095bed5d1e97e87dd556158fbbc9f 100644 (file)
@@ -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
 
index e1b549fab1010c4806d3729a79ec714d8f3d4821..05e5bec2d8940aa8c49709522f2c6f384f769018 100644 (file)
@@ -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;
 }
index b04d2e72db6bc2f0c047a0a918c45d1b4e4e0c87..7fe783042523dba77ede5da559f5e96ba9743e60 100755 (executable)
@@ -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"}],