summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2014-04-30 15:05:15 +0100
committerPádraig Brady <P@draigBrady.com>2014-05-02 01:59:31 +0100
commit10a96524355775e6d8533555f185656c7f29075c (patch)
tree67c8aa976f39add33397afdeeaae67800d1bb55f
parent59eacf2764a522f017c8289be94162b6f5163e8f (diff)
downloadcoreutils-10a96524355775e6d8533555f185656c7f29075c.tar.xz
numfmt: support zero padding using --format="%010f"
* src/numfmt.c (setup_padding_buffer): Simplify the code by not explicitly dealing with heap exhaustion. (parse_format_string): Likewise. Handle multiple grouping modifiers as does the standard printf. Handle the new leading zero --format modifier. (double_to_human): Use more defensive coding against overwriting stack buffers. Honor the leading zeros width. (usage): Mention the leading zero --format modifier. (main): Allow --padding in combo with a --format (width), as the number of leading zeros are useful independent of the main field width. * doc/coreutils.texi (numfmt invocation): Likewise. * tests/misc/numfmt.pl: Add new test cases. * NEWS: Mention the improvement.
-rw-r--r--NEWS3
-rw-r--r--doc/coreutils.texi6
-rw-r--r--src/numfmt.c94
-rwxr-xr-xtests/misc/numfmt.pl25
4 files changed, 87 insertions, 41 deletions
diff --git a/NEWS b/NEWS
index 7855a4811..92ab59df4 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,9 @@ GNU coreutils NEWS -*- outline -*-
causing name look-up errors. Also look-ups are first done outside the chroot,
in case the look-up within the chroot fails due to library conflicts etc.
+ numfmt supports zero padding of numbers using the standard printf
+ syntax of a leading zero, for example --format="%010f".
+
shred now supports multiple passes on GNU/Linux tape devices by rewinding
the tape before each pass, avoids redundant writes to empty files,
uses direct I/O for all passes where possible, and attempts to clear
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 12002fcd4..a949ffcac 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -2293,10 +2293,12 @@ Convert the number in input field @var{n} (default: 1).
@item --format=@var{format}
@opindex --format
Use printf-style floating FORMAT string. The @var{format} string must contain
-one @samp{%f} directive, optionally with @samp{'}, @samp{-}, or width
+one @samp{%f} directive, optionally with @samp{'}, @samp{-}, @samp{0}, or width
modifiers. The @samp{'} modifier will enable @option{--grouping}, the @samp{-}
modifier will enable left-aligned @option{--padding} and the width modifier will
-enable right-aligned @option{--padding}.
+enable right-aligned @option{--padding}. The @samp{0} width modifier
+(without the @samp{-} modifier) will generate leading zeros on the number,
+up to the specified width.
@item --from=@var{unit}
@opindex --from
diff --git a/src/numfmt.c b/src/numfmt.c
index 63411f305..c7448752c 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -169,6 +169,7 @@ static int grouping = 0;
static char *padding_buffer = NULL;
static size_t padding_buffer_size = 0;
static long int padding_width = 0;
+static long int zero_padding_width = 0;
static const char *format_str = NULL;
static char *format_str_prefix = NULL;
static char *format_str_suffix = NULL;
@@ -272,7 +273,7 @@ suffix_power (const char suf)
}
static inline const char *
-suffix_power_character (unsigned int power)
+suffix_power_char (unsigned int power)
{
switch (power)
{
@@ -705,6 +706,21 @@ double_to_human (long double val, int precision,
char *buf, size_t buf_size,
enum scale_type scale, int group, enum round_type round)
{
+ int num_size;
+ char fmt[64];
+ verify (sizeof (fmt) > (INT_BUFSIZE_BOUND (zero_padding_width)
+ + INT_BUFSIZE_BOUND (precision)
+ + 10 /* for %.Lf etc. */));
+
+ char *pfmt = fmt;
+ *pfmt++ = '%';
+
+ if (group)
+ *pfmt++ = '\'';
+
+ if (zero_padding_width)
+ pfmt += snprintf (pfmt, sizeof (fmt) - 1, "0%ld", zero_padding_width);
+
devmsg ("double_to_human:\n");
if (scale == scale_none)
@@ -717,9 +733,10 @@ double_to_human (long double val, int precision,
" no scaling, returning (grouped) value: %'.*Lf\n" :
" no scaling, returning value: %.*Lf\n", precision, val);
- int i = snprintf (buf, buf_size, (group) ? "%'.*Lf" : "%.*Lf",
- precision, val);
- if (i < 0 || i >= (int) buf_size)
+ stpcpy (pfmt, ".*Lf");
+
+ num_size = snprintf (buf, buf_size, fmt, precision, val);
+ if (num_size < 0 || num_size >= (int) buf_size)
error (EXIT_FAILURE, 0,
_("failed to prepare value '%Lf' for printing"), val);
return;
@@ -761,11 +778,16 @@ double_to_human (long double val, int precision,
devmsg (" after rounding, value=%Lf * %0.f ^ %d\n", val, scale_base, power);
- snprintf (buf, buf_size, (show_decimal_point) ? "%.1Lf%s" : "%.0Lf%s",
- val, suffix_power_character (power));
+ stpcpy (pfmt, show_decimal_point ? ".1Lf%s" : ".0Lf%s");
+
+ /* buf_size - 1 used here to ensure place for possible scale_IEC_I suffix. */
+ num_size = snprintf (buf, buf_size - 1, fmt, val, suffix_power_char (power));
+ if (num_size < 0 || num_size >= (int) buf_size - 1)
+ error (EXIT_FAILURE, 0,
+ _("failed to prepare value '%Lf' for printing"), val);
if (scale == scale_IEC_I && power > 0)
- strncat (buf, "i", buf_size - strlen (buf) - 1);
+ strncat (buf, "i", buf_size - num_size - 1);
devmsg (" returning value: %s\n", quote (buf));
@@ -798,10 +820,7 @@ setup_padding_buffer (size_t min_size)
return;
padding_buffer_size = min_size + 1;
- padding_buffer = realloc (padding_buffer, padding_buffer_size);
- if (!padding_buffer)
- error (EXIT_FAILURE, 0, _("out of memory (requested %zu bytes)"),
- padding_buffer_size);
+ padding_buffer = xrealloc (padding_buffer, padding_buffer_size);
}
void
@@ -906,8 +925,8 @@ UNIT options:\n"), stdout);
fputs (_("\n\
FORMAT must be suitable for printing one floating-point argument '%f'.\n\
Optional quote (%'f) will enable --grouping (if supported by current locale).\n\
-Optional width value (%10f) will pad output. Optional negative width values\n\
-(%-10f) will left-pad output.\n\
+Optional width value (%10f) will pad output. Optional zero (%010f) width\n\
+will zero pad the number. Optional negative values (%-10f) will left align.\n\
"), stdout);
printf (_("\n\
@@ -967,6 +986,7 @@ parse_format_string (char const *fmt)
size_t suffix_pos;
long int pad = 0;
char *endptr = NULL;
+ bool zero_padding = false;
for (i = 0; !(fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
{
@@ -977,13 +997,24 @@ parse_format_string (char const *fmt)
}
i++;
- i += strspn (fmt + i, " ");
- if (fmt[i] == '\'')
+ while (true)
{
- grouping = 1;
- i++;
+ size_t skip = strspn (fmt + i, " ");
+ i += skip;
+ if (fmt[i] == '\'')
+ {
+ grouping = 1;
+ i++;
+ }
+ else if (fmt[i] == '0')
+ {
+ zero_padding = true;
+ i++;
+ }
+ else if (! skip)
+ break;
}
- i += strspn (fmt + i, " ");
+
errno = 0;
pad = strtol (fmt + i, &endptr, 10);
if (errno == ERANGE)
@@ -992,6 +1023,9 @@ parse_format_string (char const *fmt)
if (endptr != (fmt + i) && pad != 0)
{
+ if (debug && padding_width && !(zero_padding && pad > 0))
+ error (0, 0, _("--format padding overridding --padding"));
+
if (pad < 0)
{
padding_alignment = MBS_ALIGN_LEFT;
@@ -999,8 +1033,12 @@ parse_format_string (char const *fmt)
}
else
{
- padding_width = pad;
+ if (zero_padding)
+ zero_padding_width = pad;
+ else
+ padding_width = pad;
}
+
}
i = endptr - fmt;
@@ -1009,7 +1047,7 @@ parse_format_string (char const *fmt)
if (fmt[i] != 'f')
error (EXIT_FAILURE, 0, _("invalid format %s,"
- " directive must be %%['][-][N]f"),
+ " directive must be %%[0]['][-][N]f"),
quote (fmt));
i++;
suffix_pos = i;
@@ -1020,19 +1058,9 @@ parse_format_string (char const *fmt)
quote (fmt));
if (prefix_len)
- {
- format_str_prefix = xstrndup (fmt, prefix_len);
- if (!format_str_prefix)
- error (EXIT_FAILURE, 0, _("out of memory (requested %zu bytes)"),
- prefix_len + 1);
- }
+ format_str_prefix = xstrndup (fmt, prefix_len);
if (fmt[suffix_pos] != '\0')
- {
- format_str_suffix = strdup (fmt + suffix_pos);
- if (!format_str_suffix)
- error (EXIT_FAILURE, 0, _("out of memory (requested %zu bytes)"),
- strlen (fmt + suffix_pos));
- }
+ format_str_suffix = xstrdup (fmt + suffix_pos);
devmsg ("format String:\n input: %s\n grouping: %s\n"
" padding width: %ld\n alignment: %s\n"
@@ -1462,8 +1490,6 @@ main (int argc, char **argv)
if (format_str != NULL && grouping)
error (EXIT_FAILURE, 0, _("--grouping cannot be combined with --format"));
- if (format_str != NULL && padding_width > 0)
- error (EXIT_FAILURE, 0, _("--padding cannot be combined with --format"));
/* Warn about no-op. */
if (debug && scale_from == scale_none && scale_to == scale_none
diff --git a/tests/misc/numfmt.pl b/tests/misc/numfmt.pl
index ca3c89675..dfb4b2e0a 100755
--- a/tests/misc/numfmt.pl
+++ b/tests/misc/numfmt.pl
@@ -695,11 +695,11 @@ my @Tests =
{EXIT=>1}],
['fmt-err-4', '--format "%d"',
{ERR=>"$prog: invalid format '%d', " .
- "directive must be %['][-][N]f\n"},
+ "directive must be %[0]['][-][N]f\n"},
{EXIT=>1}],
['fmt-err-5', '--format "% -43 f"',
{ERR=>"$prog: invalid format '% -43 f', " .
- "directive must be %['][-][N]f\n"},
+ "directive must be %[0]['][-][N]f\n"},
{EXIT=>1}],
['fmt-err-6', '--format "%f %f"',
{ERR=>"$prog: format '%f %f' has too many % directives\n"},
@@ -708,9 +708,6 @@ my @Tests =
{ERR=>"$prog: invalid format '%123456789012345678901234567890f'".
" (width overflow)\n"},
{EXIT=>1}],
- ['fmt-err-8', '--format "%f" --padding 20',
- {ERR=>"$prog: --padding cannot be combined with --format\n"},
- {EXIT=>1}],
['fmt-err-9', '--format "%f" --grouping',
{ERR=>"$prog: --grouping cannot be combined with --format\n"},
{EXIT=>1}],
@@ -748,6 +745,17 @@ my @Tests =
['fmt-15', '--format "--%100000f--" --to=si 4200',
{OUT=>"--" . " " x 99996 . "4.2K--" }],
+ # --format padding overrides --padding
+ ['fmt-16', '--format="%6f" --padding=66 1234',{OUT=>" 1234"}],
+
+ # zero padding
+ ['fmt-17', '--format="%06f" 1234',{OUT=>"001234"}],
+ # also support spaces (which are ignored as spacing is handled separately)
+ ['fmt-18', '--format="%0 6f" 1234',{OUT=>"001234"}],
+ # handle generic padding in combination
+ ['fmt-22', '--format="%06f" --padding=7 1234',{OUT=>" 001234"}],
+ ['fmt-23', '--format="%06f" --padding=-7 1234',{OUT=>"001234 "}],
+
## Check all errors again, this time with --invalid=fail
## Input will be printed without conversion,
@@ -881,6 +889,13 @@ my @Locale_Tests =
['lcl-fmt-4', '--format "--%-10f--" --to=si 5000000',
{OUT=>"--5,0M --"},
{ENV=>"LC_ALL=$locale"}],
+ # handle zero/grouping in combination
+ ['lcl-fmt-5', '--format="%\'06f" 1234',{OUT=>"01 234"},
+ {ENV=>"LC_ALL=$locale"}],
+ ['lcl-fmt-6', '--format="%0\'6f" 1234',{OUT=>"01 234"},
+ {ENV=>"LC_ALL=$locale"}],
+ ['lcl-fmt-7', '--format="%0\'\'6f" 1234',{OUT=>"01 234"},
+ {ENV=>"LC_ALL=$locale"}],
);
if ($locale ne 'C')