From 672663e1b0afd68a10d991527fd5021c40c99acc Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Fri, 8 Jan 2016 13:55:12 -0500 Subject: numfmt: add the -z,--zero-terminated option * doc/coreutils.texi (numfmt invocation): Reference the description. * src/numfmt.c: Parameterize '\n' references. * tests/misc/numfmt.pl: Add tests for character and field processing. * NEWS: Mention the new feature. --- NEWS | 2 +- doc/coreutils.texi | 3 +++ src/numfmt.c | 31 +++++++++++++++++++++++-------- tests/misc/numfmt.pl | 29 +++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 9f48415de..1c214d520 100644 --- a/NEWS +++ b/NEWS @@ -33,7 +33,7 @@ GNU coreutils NEWS -*- outline -*- ** New features - comm, cut, head, paste, tail now have the -z,--zero-terminated option, and + comm,cut,head,numfmt,paste,tail now have the -z,--zero-terminated option, and tac --separator accepts an empty argument, to work with NUL delimited items. dd now summarizes sizes in --human-readable format too, not just --si. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 01d76ec2c..f7bdc4274 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -17171,6 +17171,9 @@ the output numbers represent other units (e.g. to represent @samp{4,000,000} bytes in blocks of 1KB, use @samp{--to=si --to-unit=1000}). Suffixes are handled as with @samp{--from=auto}. +@optZeroTerminated +@newlineFieldSeparator + @end table @subsection Possible @var{unit}s: diff --git a/src/numfmt.c b/src/numfmt.c index 5d38cbdcf..223f2a26b 100644 --- a/src/numfmt.c +++ b/src/numfmt.c @@ -147,6 +147,7 @@ static struct option const longopts[] = {"header", optional_argument, NULL, HEADER_OPTION}, {"format", required_argument, NULL, FORMAT_OPTION}, {"invalid", required_argument, NULL, INVALID_OPTION}, + {"zero-terminated", no_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -189,8 +190,13 @@ static int conv_exit_code = EXIT_CONVERSION_WARNINGS; /* auto-pad each line based on skipped whitespace. */ static int auto_padding = 0; static mbs_align_t padding_alignment = MBS_ALIGN_RIGHT; + +/* field delimiter */ static int delimiter = DELIMITER_DEFAULT; +/* line delimiter. */ +static unsigned char line_delim = '\n'; + /* if non-zero, the first 'header' lines from STDIN are skipped. */ static uintmax_t header = 0; @@ -205,6 +211,7 @@ static int decimal_point_length; /* debugging for developers. Enables devmsg(). */ static bool dev_debug = false; + static inline int default_scale_base (enum scale_type scale) { @@ -934,7 +941,9 @@ Reformat NUMBER(s), or the numbers from standard input if none are specified.\n\ fputs (_("\ --to-unit=N the output unit size (instead of the default 1)\n\ "), stdout); - + fputs (_("\ + -z, --zero-terminated line delimiter is NUL, not newline\n\ +"), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -1329,10 +1338,10 @@ next_field (char **line) else { /* keep any space prefix in the returned field */ - while (*field_end && isblank (to_uchar (*field_end))) + while (*field_end && field_sep (*field_end)) ++field_end; - while (*field_end && !isblank (to_uchar (*field_end))) + while (*field_end && ! field_sep (*field_end)) ++field_end; } @@ -1420,7 +1429,7 @@ process_line (char *line, bool newline) } if (newline) - putchar ('\n'); + putchar (line_delim); return valid_number; } @@ -1451,7 +1460,7 @@ main (int argc, char **argv) while (true) { - int c = getopt_long (argc, argv, "d:", longopts, NULL); + int c = getopt_long (argc, argv, "d:z", longopts, NULL); if (c == -1) break; @@ -1512,6 +1521,10 @@ main (int argc, char **argv) delimiter = optarg[0]; break; + case 'z': + line_delim = '\0'; + break; + case SUFFIX_OPTION: suffix = optarg; break; @@ -1599,12 +1612,14 @@ main (int argc, char **argv) size_t line_allocated = 0; ssize_t len; - while (header-- && getline (&line, &line_allocated, stdin) > 0) + while (header-- && getdelim (&line, &line_allocated, + line_delim, stdin) > 0) fputs (line, stdout); - while ((len = getline (&line, &line_allocated, stdin)) > 0) + while ((len = getdelim (&line, &line_allocated, + line_delim, stdin)) > 0) { - bool newline = line[len - 1] == '\n'; + bool newline = line[len - 1] == line_delim; if (newline) line[len - 1] = '\0'; valid_numbers &= process_line (line, newline); diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl index 451bb34ab..3f2d6cc66 100755 --- a/tests/misc/numfmt.pl +++ b/tests/misc/numfmt.pl @@ -804,6 +804,32 @@ my @Tests = {EXIT => 2}], ); +# test null-terminated lines +my @NullDelim_Tests = + ( + # Input from STDIN + ['z1', '-z --to=iec', + {IN_PIPE => "1025\x002048\x00"}, {OUT=>"1.1K\x002.0K\x00"}], + + # Input from the commandline - terminated by NULL vs NL + ['z3', ' --to=iec 1024', {OUT=>"1.0K\n"}], + ['z2', '-z --to=iec 1024', {OUT=>"1.0K\x00"}], + + # Input from STDIN, with fields + ['z4', '-z --field=3 --to=si', + {IN_PIPE => "A B 1001 C\x00" . + "D E 2002 F\x00"}, + {OUT => "A B 1.1K C\x00" . + "D E 2.1K F\x00"}], + + # Input from STDIN, with fields and embedded NL + ['z5', '-z --field=3 --to=si', + {IN_PIPE => "A\nB 1001 C\x00" . + "D E\n2002 F\x00"}, + {OUT => "A B 1.1K C\x00" . + "D E 2.1K F\x00"}], + ); + my @Limit_Tests = ( # Large Values @@ -1080,6 +1106,9 @@ foreach $t (@Tests) } } +# Add test for null-terminated lines (after adjusting the OUT string, above). +push @Tests, @NullDelim_Tests; + my $save_temps = $ENV{SAVE_TEMPS}; my $verbose = $ENV{VERBOSE}; -- cgit v1.2.3-70-g09d2