mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-24 10:23:31 +08:00
expand,unexpand: support specifying a trailing tab size
* doc/coreutils.texi (expand invocation): Document the feature. (unexpand invocation): Likewise. * src/expand-common.c (extend_size): A new global to use when the last tab stop is prefixed by '/'. (set_extend_size): A new function to validate and set the new extend_size global. (parse_tab_stops): Call set_extend_size() for '/' prefixes. (finalize_tab_stops): Ensure a single specified '/' is treated like a standard tabsize, but also ensure that when '/' is specified with a single other entry that we process as a list rather than a tab size. (get_next_tab_stop): Use the tab size if set, for items after the user specified tab position list. * tests/misc/expand.pl: Add test cases * NEWS: Mention the new feature. Fixes http://bugs.gnu.org/25540
This commit is contained in:
parent
9404382f6b
commit
b1413b6011
6
NEWS
6
NEWS
@ -58,6 +58,12 @@ GNU coreutils NEWS -*- outline -*-
|
||||
'cp -fl A B' no longer remove B before creating the new link.
|
||||
That is, there is no longer a brief moment when B does not exist.
|
||||
|
||||
** New features
|
||||
|
||||
expand and unexpand now support specifying a tab size to use
|
||||
after explicitly specified tab stops, by prefixing the last
|
||||
specified number like --tabs=2,4,/8.
|
||||
|
||||
|
||||
* Noteworthy changes in release 8.26 (2016-11-30) [stable]
|
||||
|
||||
|
@ -6695,8 +6695,16 @@ The program accepts the following options. Also see @ref{Common options}.
|
||||
If only one tab stop is given, set the tabs @var{tab1} spaces apart
|
||||
(default is 8). Otherwise, set the tabs at columns @var{tab1},
|
||||
@var{tab2}, @dots{} (numbered from 0), and replace any tabs beyond the
|
||||
last tab stop given with single spaces. Tab stops can be separated by
|
||||
blanks as well as by commas.
|
||||
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.
|
||||
@end macro
|
||||
@gnuExpandTabs
|
||||
|
||||
|
||||
For compatibility, GNU @command{expand} also accepts the obsolete
|
||||
option syntax, @option{-@var{t1}[,@var{t2}]@dots{}}. New scripts
|
||||
@ -6748,8 +6756,10 @@ The program accepts the following options. Also see @ref{Common options}.
|
||||
If only one tab stop is given, set the tabs @var{tab1} columns apart
|
||||
instead of the default 8. Otherwise, set the tabs at columns
|
||||
@var{tab1}, @var{tab2}, @dots{} (numbered from 0), and leave blanks
|
||||
beyond the tab stops given unchanged. Tab stops can be separated by
|
||||
blanks as well as by commas. This option implies the @option{-a} option.
|
||||
beyond the tab stops given unchanged.
|
||||
@gnuExpandTabs
|
||||
|
||||
This option implies the @option{-a} option.
|
||||
|
||||
For compatibility, GNU @command{unexpand} supports the obsolete option syntax,
|
||||
@option{-@var{tab1}[,@var{tab2}]@dots{}}, where tab stops must be
|
||||
|
@ -34,6 +34,9 @@ bool convert_entire_line = false;
|
||||
/* If nonzero, the size of all tab stops. If zero, use 'tab_list' instead. */
|
||||
static uintmax_t tab_size = 0;
|
||||
|
||||
/* If nonzero, the size of all tab stops after the last specifed. */
|
||||
static uintmax_t extend_size = 0;
|
||||
|
||||
/* The maximum distance between tab stops. */
|
||||
size_t max_column_width;
|
||||
|
||||
@ -85,14 +88,32 @@ add_tab_stop (uintmax_t tabval)
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
set_extend_size (uintmax_t tabval)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
if (extend_size)
|
||||
{
|
||||
error (0, 0,
|
||||
_("'/' specifier only allowed"
|
||||
" with the last value"));
|
||||
ok = false;
|
||||
}
|
||||
extend_size = tabval;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Add the comma or blank separated list of tab stops STOPS
|
||||
to the list of tab stops. */
|
||||
extern void
|
||||
parse_tab_stops (char const *stops)
|
||||
{
|
||||
bool have_tabval = false;
|
||||
uintmax_t tabval IF_LINT ( = 0);
|
||||
char const *num_start IF_LINT ( = NULL);
|
||||
uintmax_t tabval = 0;
|
||||
bool extend_tabval = false;
|
||||
char const *num_start = NULL;
|
||||
bool ok = true;
|
||||
|
||||
for (; *stops; stops++)
|
||||
@ -100,9 +121,31 @@ parse_tab_stops (char const *stops)
|
||||
if (*stops == ',' || isblank (to_uchar (*stops)))
|
||||
{
|
||||
if (have_tabval)
|
||||
{
|
||||
if (extend_tabval)
|
||||
{
|
||||
if (! set_extend_size (tabval))
|
||||
{
|
||||
extend_tabval = false;
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
add_tab_stop (tabval);
|
||||
}
|
||||
have_tabval = false;
|
||||
}
|
||||
else if (*stops == '/')
|
||||
{
|
||||
if (have_tabval)
|
||||
{
|
||||
error (0, 0, _("'/' specifier not at start of number: %s"),
|
||||
quote (stops));
|
||||
ok = false;
|
||||
}
|
||||
extend_tabval = true;
|
||||
}
|
||||
else if (ISDIGIT (*stops))
|
||||
{
|
||||
if (!have_tabval)
|
||||
@ -132,11 +175,16 @@ parse_tab_stops (char const *stops)
|
||||
}
|
||||
}
|
||||
|
||||
if (have_tabval)
|
||||
{
|
||||
if (extend_tabval)
|
||||
ok &= set_extend_size (tabval);
|
||||
else
|
||||
add_tab_stop (tabval);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
exit (EXIT_FAILURE);
|
||||
|
||||
if (have_tabval)
|
||||
add_tab_stop (tabval);
|
||||
}
|
||||
|
||||
/* Check that the list of tab stops TABS, with ENTRIES entries,
|
||||
@ -172,8 +220,8 @@ finalize_tab_stops (void)
|
||||
validate_tab_stops (tab_list, first_free_tab);
|
||||
|
||||
if (first_free_tab == 0)
|
||||
tab_size = max_column_width = 8;
|
||||
else if (first_free_tab == 1)
|
||||
tab_size = max_column_width = extend_size ? extend_size : 8;
|
||||
else if (first_free_tab == 1 && ! extend_size)
|
||||
tab_size = tab_list[0];
|
||||
else
|
||||
tab_size = 0;
|
||||
@ -199,6 +247,10 @@ get_next_tab_column (const uintmax_t column, size_t* tab_index,
|
||||
return tab;
|
||||
}
|
||||
|
||||
/* relative last tab - return multiples of it */
|
||||
if (extend_size)
|
||||
return column + (extend_size - column % extend_size);
|
||||
|
||||
*last_tab = true;
|
||||
return 0;
|
||||
}
|
||||
|
@ -39,9 +39,10 @@ my @Tests =
|
||||
['t5', '--tabs=""', {IN=>"a\tb\tc"}, {OUT=>"a b c"}],
|
||||
['t6', '--tabs=","', {IN=>"a\tb\tc"}, {OUT=>"a b c"}],
|
||||
['t7', '--tabs=" "', {IN=>"a\tb\tc"}, {OUT=>"a b c"}],
|
||||
['t8', '--tabs="/"', {IN=>"a\tb\tc"}, {OUT=>"a b c"}],
|
||||
|
||||
# Input field wider than the specified tab list
|
||||
['t8', '--tabs=6,9', {IN=>"a\tbbbbbbbbbbbbb\tc"},
|
||||
['if', '--tabs=6,9', {IN=>"a\tbbbbbbbbbbbbb\tc"},
|
||||
{OUT=>"a bbbbbbbbbbbbb c"}],
|
||||
|
||||
['i1', '--tabs=3 -i', {IN=>"\ta\tb"}, {OUT=>" a\tb"}],
|
||||
@ -138,6 +139,17 @@ my @Tests =
|
||||
{OUT=>"1 2 3 4 5\n" .
|
||||
"a bHELLO\b\b\b c d e\n"}],
|
||||
|
||||
# Test the trailing '/' feature which specifies the
|
||||
# tab size to use after the last specified stop
|
||||
['trail1', '--tabs=1,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
|
||||
['trail2', '--tabs=2,/5', {IN=>"\ta\tb\tc"}, {OUT=>" a b c"}],
|
||||
['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"}],
|
||||
['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 errors
|
||||
['e1', '--tabs="a"', {IN=>''}, {OUT=>''}, {EXIT=>1},
|
||||
@ -148,6 +160,12 @@ my @Tests =
|
||||
{ERR => "$prog: tab size cannot be 0\n"}],
|
||||
['e4', '--tabs=3,3', {IN=>''}, {OUT=>''}, {EXIT=>1},
|
||||
{ERR => "$prog: tab sizes must be ascending\n"}],
|
||||
['e5', '--tabs=/3,6,8', {IN=>''}, {OUT=>''}, {EXIT=>1},
|
||||
{ERR => "$prog: '/' specifier only allowed with the last value\n"}],
|
||||
['e6', '-t/3 -t/6', {IN=>''}, {OUT=>''}, {EXIT=>1},
|
||||
{ERR => "$prog: '/' specifier only allowed with the last value\n"}],
|
||||
['e7', '--tabs=3/', {IN=>''}, {OUT=>''}, {EXIT=>1},
|
||||
{ERR => "$prog: '/' specifier not at start of number: '/'\n"}],
|
||||
);
|
||||
|
||||
my $save_temps = $ENV{DEBUG};
|
||||
|
Loading…
Reference in New Issue
Block a user