aboutsummaryrefslogtreecommitdiffstats
path: root/src/numfmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/numfmt.c')
-rw-r--r--src/numfmt.c194
1 files changed, 86 insertions, 108 deletions
diff --git a/src/numfmt.c b/src/numfmt.c
index ca2eaffc3..a242f0efb 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -17,13 +17,14 @@
#include <config.h>
#include <float.h>
#include <getopt.h>
+#include <stdckdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <langinfo.h>
-#include "mbsalign.h"
#include "argmatch.h"
#include "c-ctype.h"
+#include "mbswidth.h"
#include "quote.h"
#include "system.h"
#include "xstrtol.h"
@@ -173,9 +174,9 @@ static uintmax_t from_unit_size = 1;
static uintmax_t to_unit_size = 1;
static int grouping = 0;
static char *padding_buffer = nullptr;
-static size_t padding_buffer_size = 0;
-static long int padding_width = 0;
-static long int zero_padding_width = 0;
+static idx_t padding_buffer_size = 0;
+static intmax_t padding_width = 0;
+static int zero_padding_width = 0;
static long int user_precision = -1;
static char const *format_str = nullptr;
static char *format_str_prefix = nullptr;
@@ -187,7 +188,6 @@ 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;
@@ -728,19 +728,17 @@ simple_strtod_fatal (enum simple_strtod_error err, char const *input_str)
error (conv_exit_code, 0, gettext (msgid), quote (input_str));
}
-/* Convert VAL to a human format string in BUF. */
-static void
+/* Convert VAL to a human format string using PRECISION in BUF of size
+ BUF_SIZE. Use SCALE, GROUP, and ROUND to format. Return
+ the number of bytes needed to represent VAL. If this number is not
+ less than BUF_SIZE, the buffer is too small; if it is negative, the
+ formatting failed for some reason. */
+static int
double_to_human (long double val, int precision,
- char *buf, size_t buf_size,
+ char *buf, idx_t buf_size,
enum scale_type scale, int group, enum round_type round)
{
- int num_size;
- char fmt[64];
- static_assert ((INT_BUFSIZE_BOUND (zero_padding_width)
- + INT_BUFSIZE_BOUND (precision)
- + 10 /* for %.Lf etc. */)
- < sizeof fmt);
-
+ char fmt[sizeof "%'0.*Lfi%s%s%s" + INT_STRLEN_BOUND (zero_padding_width)];
char *pfmt = fmt;
*pfmt++ = '%';
@@ -748,7 +746,7 @@ double_to_human (long double val, int precision,
*pfmt++ = '\'';
if (zero_padding_width)
- pfmt += snprintf (pfmt, sizeof (fmt) - 2, "0%ld", zero_padding_width);
+ pfmt += sprintf (pfmt, "0%d", zero_padding_width);
devmsg ("double_to_human:\n");
@@ -762,13 +760,10 @@ double_to_human (long double val, int precision,
" no scaling, returning (grouped) value: %'.*Lf\n" :
" no scaling, returning value: %.*Lf\n", precision, val);
- stpcpy (pfmt, ".*Lf");
+ strcpy (pfmt, ".*Lf%s");
- 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;
+ return snprintf (buf, buf_size, fmt, precision, val,
+ suffix ? suffix : "");
}
/* Scaling requested by user. */
@@ -810,23 +805,14 @@ double_to_human (long double val, int precision,
devmsg (" after rounding, value=%Lf * %0.f ^ %d\n", val, scale_base, power);
- stpcpy (pfmt, ".*Lf%s");
+ strcpy (pfmt, ".*Lf%s%s%s");
int prec = user_precision == -1 ? show_decimal_point : user_precision;
- /* buf_size - 1 used here to ensure place for possible scale_IEC_I suffix. */
- num_size = snprintf (buf, buf_size - 1, fmt, prec, 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 - num_size - 1);
-
- devmsg (" returning value: %s\n", quote (buf));
-
- return;
+ return snprintf (buf, buf_size, fmt, prec, val,
+ suffix_power_char (power),
+ &"i"[! (scale == scale_IEC_I && 0 < power)],
+ suffix ? suffix : "");
}
/* Convert a string of decimal digits, N_STRING, with an optional suffix
@@ -876,17 +862,6 @@ unit_to_umax (char const *n_string)
return n;
}
-
-static void
-setup_padding_buffer (size_t min_size)
-{
- if (padding_buffer_size > min_size)
- return;
-
- padding_buffer_size = min_size + 1;
- padding_buffer = xrealloc (padding_buffer, padding_buffer_size);
-}
-
void
usage (int status)
{
@@ -1052,7 +1027,7 @@ Examples:\n\
NOTES:
1. This function sets the global variables:
- padding_width, padding_alignment, grouping,
+ padding_width, grouping,
format_str_prefix, format_str_suffix
2. The function aborts on any errors. */
static void
@@ -1061,7 +1036,6 @@ parse_format_string (char const *fmt)
size_t i;
size_t prefix_len = 0;
size_t suffix_pos;
- long int pad = 0;
char *endptr = nullptr;
bool zero_padding = false;
@@ -1092,30 +1066,25 @@ parse_format_string (char const *fmt)
break;
}
- errno = 0;
- pad = strtol (fmt + i, &endptr, 10);
- if (errno == ERANGE || pad < -LONG_MAX)
- error (EXIT_FAILURE, 0,
- _("invalid format %s (width overflow)"), quote (fmt));
+ intmax_t pad = strtoimax (fmt + i, &endptr, 10);
- if (endptr != (fmt + i) && pad != 0)
+ if (pad != 0)
{
if (debug && padding_width && !(zero_padding && pad > 0))
error (0, 0, _("--format padding overriding --padding"));
+ /* Set padding width and alignment. On overflow, set widths to
+ large values that cause later code to avoid undefined behavior
+ and fail at a reasonable point. */
if (pad < 0)
- {
- padding_alignment = MBS_ALIGN_LEFT;
- padding_width = -pad;
- }
+ padding_width = pad;
else
{
if (zero_padding)
- zero_padding_width = pad;
+ zero_padding_width = MIN (pad, INT_MAX);
else
padding_width = pad;
}
-
}
i = endptr - fmt;
@@ -1159,11 +1128,10 @@ parse_format_string (char const *fmt)
format_str_suffix = xstrdup (fmt + suffix_pos);
devmsg ("format String:\n input: %s\n grouping: %s\n"
- " padding width: %ld\n alignment: %s\n"
+ " padding width: %jd\n"
" prefix: %s\n suffix: %s\n",
quote_n (0, fmt), (grouping) ? "yes" : "no",
padding_width,
- (padding_alignment == MBS_ALIGN_LEFT) ? "Left" : "Right",
quote_n (1, format_str_prefix ? format_str_prefix : ""),
quote_n (2, format_str_suffix ? format_str_suffix : ""));
}
@@ -1203,12 +1171,11 @@ parse_human_number (char const *str, long double /*output */ *value,
/* Print the given VAL, using the requested representation.
The number is printed to STDOUT, with padding and alignment. */
-static int
-prepare_padded_number (const long double val, size_t precision)
+static bool
+prepare_padded_number (const long double val, size_t precision,
+ intmax_t *padding)
{
/* Generate Output. */
- char buf[128];
-
size_t precision_used = user_precision == -1 ? precision : user_precision;
/* Can't reliably print too-large values without auto-scaling. */
@@ -1229,7 +1196,7 @@ prepare_padded_number (const long double val, size_t precision)
_("value too large to be printed: '%Lg'"
" (consider using --to)"), val);
}
- return 0;
+ return false;
}
if (x > MAX_ACCEPTABLE_DIGITS - 1)
@@ -1237,42 +1204,65 @@ prepare_padded_number (const long double val, size_t precision)
if (inval_style != inval_ignore)
error (conv_exit_code, 0, _("value too large to be printed: '%Lg'"
" (cannot handle values > 999Q)"), val);
- return 0;
+ return false;
}
- double_to_human (val, precision_used, buf, sizeof (buf),
- scale_to, grouping, round_style);
- if (suffix)
- strncat (buf, suffix, sizeof (buf) - strlen (buf) -1);
+ while (true)
+ {
+ int numlen = double_to_human (val, precision_used,
+ padding_buffer, padding_buffer_size,
+ scale_to, grouping, round_style);
+ ptrdiff_t growth;
+ if (numlen < 0 || ckd_sub (&growth, numlen, padding_buffer_size - 1))
+ error (EXIT_FAILURE, 0,
+ _("failed to prepare value '%Lf' for printing"), val);
+ if (growth <= 0)
+ break;
+ padding_buffer = xpalloc (padding_buffer, &padding_buffer_size,
+ growth, -1, 1);
+ }
devmsg ("formatting output:\n value: %Lf\n humanized: %s\n",
- val, quote (buf));
+ val, quote (padding_buffer));
- if (padding_width && strlen (buf) < padding_width)
+ intmax_t pad = 0;
+ if (padding_width)
{
- size_t w = padding_width;
- mbsalign (buf, padding_buffer, padding_buffer_size, &w,
- padding_alignment, MBA_UNIBYTE_ONLY);
-
- devmsg (" After padding: %s\n", quote (padding_buffer));
- }
- else
- {
- setup_padding_buffer (strlen (buf) + 1);
- strcpy (padding_buffer, buf);
+ int buf_width = mbswidth (padding_buffer,
+ MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE);
+ if (0 <= buf_width)
+ {
+ if (padding_width < 0)
+ {
+ if (padding_width < -buf_width)
+ pad = padding_width + buf_width;
+ }
+ else
+ {
+ if (buf_width < padding_width)
+ pad = padding_width - buf_width;
+ }
+ }
}
- return 1;
+ *padding = pad;
+ return true;
}
static void
-print_padded_number (void)
+print_padded_number (intmax_t padding)
{
if (format_str_prefix)
fputs (format_str_prefix, stdout);
+ for (intmax_t p = padding; 0 < p; p--)
+ putchar (' ');
+
fputs (padding_buffer, stdout);
+ for (intmax_t p = padding; p < 0; p++)
+ putchar (' ');
+
if (format_str_suffix)
fputs (format_str_suffix, stdout);
}
@@ -1305,16 +1295,8 @@ process_suffixed_number (char *text, long double *result,
/* setup auto-padding. */
if (auto_padding)
{
- if (text < p || field > 1)
- {
- padding_width = strlen (text);
- setup_padding_buffer (padding_width);
- }
- else
- {
- padding_width = 0;
- }
- devmsg ("setting Auto-Padding to %ld characters\n", padding_width);
+ padding_width = text < p || 1 < field ? strlen (text) : 0;
+ devmsg ("setting Auto-Padding to %jd characters\n", padding_width);
}
long double val = 0;
@@ -1393,11 +1375,12 @@ process_field (char *text, uintmax_t field)
valid_number =
process_suffixed_number (text, &val, &precision, field);
+ intmax_t padding;
if (valid_number)
- valid_number = prepare_padded_number (val, precision);
+ valid_number = prepare_padded_number (val, precision, &padding);
if (valid_number)
- print_padded_number ();
+ print_padded_number (padding);
else
fputs (text, stdout);
}
@@ -1508,15 +1491,12 @@ main (int argc, char **argv)
break;
case PADDING_OPTION:
- if (xstrtol (optarg, nullptr, 10, &padding_width, "") != LONGINT_OK
- || padding_width == 0 || padding_width < -LONG_MAX)
+ if (((xstrtoimax (optarg, nullptr, 10, &padding_width, "")
+ & ~LONGINT_OVERFLOW)
+ != LONGINT_OK)
+ || padding_width == 0)
error (EXIT_FAILURE, 0, _("invalid padding value %s"),
quote (optarg));
- if (padding_width < 0)
- {
- padding_alignment = MBS_ALIGN_LEFT;
- padding_width = -padding_width;
- }
/* TODO: We probably want to apply a specific --padding
to --header lines too. */
break;
@@ -1605,8 +1585,6 @@ main (int argc, char **argv)
error (0, 0, _("grouping has no effect in this locale"));
}
-
- setup_padding_buffer (padding_width);
auto_padding = (padding_width == 0 && delimiter == DELIMITER_DEFAULT);
if (inval_style != inval_abort)