summaryrefslogtreecommitdiff
path: root/src/numfmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/numfmt.c')
-rw-r--r--src/numfmt.c552
1 files changed, 394 insertions, 158 deletions
diff --git a/src/numfmt.c b/src/numfmt.c
index 206866ac..35c5c5b9 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -1,5 +1,5 @@
/* Reformat numbers like 11505426432 to the more human-readable 11G
- Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,12 @@
#include "system.h"
#include "xstrtol.h"
#include "xstrndup.h"
+#include "gl_linked_list.h"
+#include "gl_xlist.h"
+
+#if HAVE_FPSETPREC
+# include <ieeefp.h>
+#endif
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "numfmt"
@@ -151,7 +157,7 @@ enum { DELIMITER_DEFAULT = CHAR_MAX + 1 };
/* Maximum number of digits we can safely handle
without precision loss, if scaling is 'none'. */
-enum { MAX_UNSCALED_DIGITS = 18 };
+enum { MAX_UNSCALED_DIGITS = LDBL_DIG };
/* Maximum number of digits we can work with.
This is equivalent to 999Y.
@@ -161,8 +167,8 @@ enum { MAX_ACCEPTABLE_DIGITS = 27 };
static enum scale_type scale_from = scale_none;
static enum scale_type scale_to = scale_none;
-static enum round_type _round = round_from_zero;
-static enum inval_type _invalid = inval_abort;
+static enum round_type round_style = round_from_zero;
+static enum inval_type inval_style = inval_abort;
static const char *suffix = NULL;
static uintmax_t from_unit_size = 1;
static uintmax_t to_unit_size = 1;
@@ -171,6 +177,7 @@ 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 long int user_precision = -1;
static const char *format_str = NULL;
static char *format_str_prefix = NULL;
static char *format_str_suffix = NULL;
@@ -182,7 +189,10 @@ 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;
-static long int field = 1;
+static bool all_fields = false;
+static size_t all_fields_after = 0;
+static size_t all_fields_before = 0;
+static gl_list_t field_list;
static int delimiter = DELIMITER_DEFAULT;
/* if non-zero, the first 'header' lines from STDIN are skipped. */
@@ -382,30 +392,41 @@ simple_round_nearest (long double val)
return val < 0 ? val - 0.5 : val + 0.5;
}
-static inline intmax_t
+static inline long double _GL_ATTRIBUTE_CONST
simple_round (long double val, enum round_type t)
{
+ intmax_t rval;
+ intmax_t intmax_mul = val / INTMAX_MAX;
+ val -= (long double) INTMAX_MAX * intmax_mul;
+
switch (t)
{
case round_ceiling:
- return simple_round_ceiling (val);
+ rval = simple_round_ceiling (val);
+ break;
case round_floor:
- return simple_round_floor (val);
+ rval = simple_round_floor (val);
+ break;
case round_from_zero:
- return simple_round_from_zero (val);
+ rval = simple_round_from_zero (val);
+ break;
case round_to_zero:
- return simple_round_to_zero (val);
+ rval = simple_round_to_zero (val);
+ break;
case round_nearest:
- return simple_round_nearest (val);
+ rval = simple_round_nearest (val);
+ break;
default:
/* to silence the compiler - this should never happen. */
return 0;
}
+
+ return (long double) INTMAX_MAX * intmax_mul + rval;
}
enum simple_strtod_error
@@ -445,6 +466,7 @@ simple_strtod_int (const char *input_str,
long double val = 0;
unsigned int digits = 0;
+ bool found_digit = false;
if (*input_str == '-')
{
@@ -459,10 +481,14 @@ simple_strtod_int (const char *input_str,
{
int digit = (**endptr) - '0';
+ found_digit = true;
+
+ if (val || digit)
+ digits++;
+
if (digits > MAX_UNSCALED_DIGITS)
e = SSE_OK_PRECISION_LOSS;
- ++digits;
if (digits > MAX_ACCEPTABLE_DIGITS)
return SSE_OVERFLOW;
@@ -471,7 +497,8 @@ simple_strtod_int (const char *input_str,
++(*endptr);
}
- if (digits == 0)
+ if (! found_digit
+ && ! STREQ_LEN (*endptr, decimal_point, decimal_point_length))
return SSE_INVALID_NUMBER;
if (*negative)
val = -val;
@@ -513,7 +540,6 @@ simple_strtod_float (const char *input_str,
if (e != SSE_OK && e != SSE_OK_PRECISION_LOSS)
return e;
-
/* optional decimal point + fraction. */
if (STREQ_LEN (*endptr, decimal_point, decimal_point_length))
{
@@ -536,6 +562,8 @@ simple_strtod_float (const char *input_str,
val_frac = ((long double) val_frac) / powerld (10, exponent);
+ /* TODO: detect loss of precision (only really 18 digits
+ of precision across all digits (before and after '.')). */
if (value)
{
if (negative)
@@ -565,7 +593,7 @@ simple_strtod_float (const char *input_str,
Returns:
SSE_OK - valid number.
- SSE_OK_PRECISION_LOSS - if more than 18 digits were used.
+ SSE_OK_PRECISION_LOSS - if more than LDBL_DIG digits were used.
SSE_OVERFLOW - if more than 27 digits (999Y) were used.
SSE_INVALID_NUMBER - if no digits were found.
SSE_VALID_BUT_FORBIDDEN_SUFFIX
@@ -580,9 +608,12 @@ simple_strtod_human (const char *input_str,
/* 'scale_auto' is checked below. */
int scale_base = default_scale_base (allowed_scaling);
- devmsg ("simple_strtod_human:\n input string: %s\n "
- "locale decimal-point: %s\n",
- quote_n (0, input_str), quote_n (1, decimal_point));
+ devmsg ("simple_strtod_human:\n input string: %s\n"
+ " locale decimal-point: %s\n"
+ " MAX_UNSCALED_DIGITS: %d\n",
+ quote_n (0, input_str),
+ quote_n (1, decimal_point),
+ MAX_UNSCALED_DIGITS);
enum simple_strtod_error e =
simple_strtod_float (input_str, endptr, value, precision);
@@ -677,7 +708,7 @@ simple_strtod_fatal (enum simple_strtod_error err, char const *input_str)
}
- if (_invalid != inval_ignore)
+ if (inval_style != inval_ignore)
error (conv_exit_code, 0, gettext (msgid), quote (input_str));
}
@@ -729,18 +760,23 @@ double_to_human (long double val, int precision,
/* Normalize val to scale. */
unsigned int power = 0;
val = expld (val, scale_base, &power);
- devmsg (" scaled value to %Lf * %0.f ^ %d\n", val, scale_base, power);
+ devmsg (" scaled value to %Lf * %0.f ^ %u\n", val, scale_base, power);
/* Perform rounding. */
- int ten_or_less = 0;
- if (absld (val) < 10)
+ unsigned int power_adjust = 0;
+ if (user_precision != -1)
+ power_adjust = MIN (power * 3, user_precision);
+ else if (absld (val) < 10)
{
/* for values less than 10, we allow one decimal-point digit,
so adjust before rounding. */
- ten_or_less = 1;
- val *= 10;
+ power_adjust = 1;
}
+
+ val *= powerld (10, power_adjust);
val = simple_round (val, round);
+ val /= powerld (10, power_adjust);
+
/* two special cases after rounding:
1. a "999.99" can turn into 1000 - so scale down
2. a "9.99" can turn into 10 - so don't display decimal-point. */
@@ -749,20 +785,21 @@ double_to_human (long double val, int precision,
val /= scale_base;
power++;
}
- if (ten_or_less)
- val /= 10;
/* should "7.0" be printed as "7" ?
if removing the ".0" is preferred, enable the fourth condition. */
int show_decimal_point = (val != 0) && (absld (val) < 10) && (power > 0);
/* && (absld (val) > simple_round_floor (val))) */
- devmsg (" after rounding, value=%Lf * %0.f ^ %d\n", val, scale_base, power);
+ devmsg (" after rounding, value=%Lf * %0.f ^ %u\n", val, scale_base, power);
- stpcpy (pfmt, show_decimal_point ? ".1Lf%s" : ".0Lf%s");
+ stpcpy (pfmt, ".*Lf%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, val, suffix_power_char (power));
+ 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);
@@ -776,19 +813,48 @@ double_to_human (long double val, int precision,
}
/* Convert a string of decimal digits, N_STRING, with an optional suffix
- to an integral value. Upon successful conversion, return that value.
+ to an integral value. Suffixes are handled as with --from=auto.
+ Upon successful conversion, return that value.
If it cannot be converted, give a diagnostic and exit. */
static uintmax_t
unit_to_umax (const char *n_string)
{
strtol_error s_err;
+ const char *c_string = n_string;
+ char *t_string = NULL;
+ size_t n_len = strlen (n_string);
char *end = NULL;
uintmax_t n;
+ const char *suffixes = "KMGTPEZY";
+
+ /* Adjust suffixes so K=1000, Ki=1024, KiB=invalid. */
+ if (n_len && ! c_isdigit (n_string[n_len - 1]))
+ {
+ t_string = xmalloc (n_len + 2);
+ end = t_string + n_len - 1;
+ memcpy (t_string, n_string, n_len);
- s_err = xstrtoumax (n_string, &end, 10, &n, "KMGTPEZY");
+ if (*end == 'i' && 2 <= n_len && ! c_isdigit (*(end - 1)))
+ *end = '\0';
+ else
+ {
+ *++end = 'B';
+ *++end = '\0';
+ suffixes = "KMGTPEZY0";
+ }
+
+ c_string = t_string;
+ }
+
+ s_err = xstrtoumax (c_string, &end, 10, &n, suffixes);
if (s_err != LONGINT_OK || *end || n == 0)
- error (EXIT_FAILURE, 0, _("invalid unit size: %s"), quote (n_string));
+ {
+ free (t_string);
+ error (EXIT_FAILURE, 0, _("invalid unit size: %s"), quote (n_string));
+ }
+
+ free (t_string);
return n;
}
@@ -825,7 +891,8 @@ Reformat NUMBER(s), or the numbers from standard input if none are specified.\n\
-d, --delimiter=X use X instead of whitespace for field delimiter\n\
"), stdout);
fputs (_("\
- --field=N replace the number in input field N (default is 1)\n\
+ --field=FIELDS replace the numbers in these input fields (default=1)\n\
+ see FIELDS below\n\
"), stdout);
fputs (_("\
--format=FORMAT use printf style floating-point FORMAT;\n\
@@ -904,10 +971,21 @@ UNIT options:\n"), stdout);
...\n"), stdout);
fputs (_("\n\
+FIELDS supports cut(1) style field ranges:\n\
+ N N'th field, counted from 1\n\
+ N- from N'th field, to end of line\n\
+ N-M from N'th to M'th field (inclusive)\n\
+ -M from first to M'th field (inclusive)\n\
+ - all fields\n\
+Multiple fields/ranges can be separated with commas\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 zero (%010f) width\n\
will zero pad the number. Optional negative values (%-10f) will left align.\n\
+Optional precision (%.1f) will override the input determined precision.\n\
"), stdout);
printf (_("\n\
@@ -931,14 +1009,14 @@ Examples:\n\
-> \"1000\"\n\
$ echo 1K | %s --from=iec\n\
-> \"1024\"\n\
- $ df | %s --header --field 2 --to=si\n\
- $ ls -l | %s --header --field 5 --to=iec\n\
+ $ df -B1 | %s --header --field 2-4 --to=si\n\
+ $ ls -l | %s --header --field 5 --to=iec\n\
$ ls -lh | %s --header --field 5 --from=iec --padding=10\n\
$ ls -lh | %s --header --field 5 --from=iec --format %%10f\n"),
program_name, program_name, program_name,
program_name, program_name, program_name,
program_name, program_name, program_name);
- emit_ancillary_info ();
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -951,7 +1029,6 @@ Examples:\n\
Only a limited subset of printf(3) syntax is supported.
TODO:
- support .precision
support %e %g etc. rather than just %f
NOTES:
@@ -1005,7 +1082,7 @@ 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"));
+ error (0, 0, _("--format padding overriding --padding"));
if (pad < 0)
{
@@ -1026,9 +1103,28 @@ parse_format_string (char const *fmt)
if (fmt[i] == '\0')
error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
+ if (fmt[i] == '.')
+ {
+ i++;
+ errno = 0;
+ user_precision = strtol (fmt + i, &endptr, 10);
+ if (errno == ERANGE || user_precision < 0 || SIZE_MAX < user_precision
+ || isblank (fmt[i]) || fmt[i] == '+')
+ {
+ /* Note we disallow negative user_precision to be
+ consistent with printf(1). POSIX states that
+ negative precision is only supported (and ignored)
+ when used with '.*f'. glibc at least will malform
+ output when passed a direct negative precision. */
+ error (EXIT_FAILURE, 0,
+ _("invalid precision in format %s"), quote (fmt));
+ }
+ i = endptr - fmt;
+ }
+
if (fmt[i] != 'f')
error (EXIT_FAILURE, 0, _("invalid format %s,"
- " directive must be %%[0]['][-][N]f"),
+ " directive must be %%[0]['][-][N][.][N]f"),
quote (fmt));
i++;
suffix_pos = i;
@@ -1077,7 +1173,7 @@ parse_human_number (const char *str, long double /*output */ *value,
if (ptr && *ptr != '\0')
{
- if (_invalid != inval_ignore)
+ if (inval_style != inval_ignore)
error (conv_exit_code, 0, _("invalid suffix in input %s: %s"),
quote_n (0, str), quote_n (1, ptr));
e = SSE_INVALID_SUFFIX;
@@ -1094,27 +1190,39 @@ prepare_padded_number (const long double val, size_t precision)
/* 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. */
unsigned int x;
expld (val, 10, &x);
- if (scale_to == scale_none && x > MAX_UNSCALED_DIGITS)
+
+ if (scale_to == scale_none
+ && x + precision_used > MAX_UNSCALED_DIGITS)
{
- if (_invalid != inval_ignore)
- error (conv_exit_code, 0, _("value too large to be printed: '%Lg'"
- " (consider using --to)"), val);
+ if (inval_style != inval_ignore)
+ {
+ if (precision_used)
+ error (conv_exit_code, 0,
+ _("value/precision too large to be printed: '%Lg/%"PRIuMAX"'"
+ " (consider using --to)"), val, (uintmax_t)precision_used);
+ else
+ error (conv_exit_code, 0,
+ _("value too large to be printed: '%Lg'"
+ " (consider using --to)"), val);
+ }
return 0;
}
if (x > MAX_ACCEPTABLE_DIGITS - 1)
{
- if (_invalid != inval_ignore)
+ if (inval_style != inval_ignore)
error (conv_exit_code, 0, _("value too large to be printed: '%Lg'"
" (cannot handle values > 999Y)"), val);
return 0;
}
- double_to_human (val, precision, buf, sizeof (buf), scale_to, grouping,
- _round);
+ double_to_human (val, precision_used, buf, sizeof (buf),
+ scale_to, grouping, round_style);
if (suffix)
strncat (buf, suffix, sizeof (buf) - strlen (buf) -1);
@@ -1153,7 +1261,8 @@ print_padded_number (void)
/* Converts the TEXT number string to the requested representation,
and handles automatic suffix addition. */
static int
-process_suffixed_number (char *text, long double *result, size_t *precision)
+process_suffixed_number (char *text, long double *result,
+ size_t *precision, long int field)
{
if (suffix && strlen (text) > strlen (suffix))
{
@@ -1204,139 +1313,253 @@ process_suffixed_number (char *text, long double *result, size_t *precision)
return (e == SSE_OK || e == SSE_OK_PRECISION_LOSS);
}
-/* Skip the requested number of fields in the input string.
- Returns a pointer to the *delimiter* of the requested field,
- or a pointer to NUL (if reached the end of the string). */
-static inline char * _GL_ATTRIBUTE_PURE
-skip_fields (char *buf, int fields)
+typedef struct range_pair
{
- char *ptr = buf;
- if (delimiter != DELIMITER_DEFAULT)
- {
- if (*ptr == delimiter)
- fields--;
- while (*ptr && fields--)
- {
- while (*ptr && *ptr == delimiter)
- ++ptr;
- while (*ptr && *ptr != delimiter)
- ++ptr;
- }
- }
- else
- while (*ptr && fields--)
- {
- while (*ptr && isblank (to_uchar (*ptr)))
- ++ptr;
- while (*ptr && !isblank (to_uchar (*ptr)))
- ++ptr;
- }
- return ptr;
+ size_t lo;
+ size_t hi;
+} range_pair_t;
+
+static int
+sort_field (const void *elt1, const void *elt2)
+{
+ range_pair_t* rp1 = (range_pair_t*) elt1;
+ range_pair_t* rp2 = (range_pair_t*) elt2;
+
+ if (rp1->lo < rp2->lo)
+ return -1;
+
+ return rp1->lo > rp2->lo;
}
-/* Parse a delimited string, and extracts the requested field.
- NOTE: the input buffer is modified.
+static int
+match_field (const void *elt1, const void *elt2)
+{
+ range_pair_t* rp = (range_pair_t*) elt1;
+ size_t field = *(size_t*) elt2;
- TODO:
- Maybe support multiple fields, though can always pipe output
- into another numfmt to process other fields.
- Maybe default to processing all fields rather than just first?
+ if (rp->lo <= field && field <= rp->hi)
+ return 0;
+
+ if (rp->lo < field)
+ return -1;
+
+ return 1;
+}
- Output:
- _PREFIX, _DATA, _SUFFIX will point to the relevant positions
- in the input string, or be NULL if such a part doesn't exist. */
static void
-extract_fields (char *line, int _field,
- char ** _prefix, char ** _data, char ** _suffix)
+free_field (const void *elt)
{
- char *ptr = line;
- *_prefix = NULL;
- *_data = NULL;
- *_suffix = NULL;
+ void *p = (void *)elt;
+ free (p);
+}
- devmsg ("extracting Fields:\n input: %s\n field: %d\n",
- quote (line), _field);
+/* Add the specified fields to field_list.
+ The format recognized is similar to cut.
+ TODO: Refactor the more performant cut implementation
+ for use by both utilities. */
+static void
+parse_field_arg (char *arg)
+{
+
+ char *start, *end;
+ range_pair_t *rp;
+ size_t field_val;
+ size_t range_val = 0;
+
+ start = end = arg;
- if (field > 1)
+ if (STREQ (arg, "-"))
{
- /* skip the requested number of fields. */
- *_prefix = line;
- ptr = skip_fields (line, field - 1);
- if (*ptr == '\0')
- {
- /* not enough fields in the input - print warning? */
- devmsg (" TOO FEW FIELDS!\n prefix: %s\n", quote (*_prefix));
- return;
- }
+ all_fields = true;
+
+ return;
+ }
+
+ if (*start == '-')
+ {
+ /* range -M */
+ ++start;
+
+ all_fields_before = strtol (start, &end, 10);
+
+ if (start == end || all_fields_before <=0)
+ error (EXIT_FAILURE, 0, _("invalid field value %s"),
+ quote (start));
+
+ return;
+ }
+
+ field_list = gl_list_create_empty (GL_LINKED_LIST,
+ NULL, NULL, free_field, false);
+
+ while (*end != '\0') {
+ field_val = strtol (start, &end, 10);
+
+ if (start == end || field_val <=0)
+ error (EXIT_FAILURE, 0, _("invalid field value %s"),
+ quote (start));
+
+ if (! range_val)
+ {
+ /* field N */
+ rp = xmalloc (sizeof (*rp));
+ rp->lo = rp->hi = field_val;
+ gl_sortedlist_add (field_list, sort_field, rp);
+ }
+ else
+ {
+ /* range N-M
+ The last field was the start of the field range. The current
+ field is the end of the field range. We already added the
+ start field, so increment and add all the fields through
+ range end. */
+ if (field_val < range_val)
+ error (EXIT_FAILURE, 0, _("invalid decreasing range"));
+ rp = xmalloc (sizeof (*rp));
+ rp->lo = range_val + 1;
+ rp->hi = field_val;
+ gl_sortedlist_add (field_list, sort_field, rp);
+
+ range_val = 0;
+ }
+
+ switch (*end) {
+ case ',':
+ /* discrete field separator */
+ ++end;
+ start = end;
+ break;
- *ptr = '\0';
- ++ptr;
+ case '-':
+ /* field range separator */
+ ++end;
+ start = end;
+ range_val = field_val;
+ break;
}
+ }
- *_data = ptr;
- *_suffix = skip_fields (*_data, 1);
- if (**_suffix)
+ if (range_val)
{
- /* there is a suffix (i.e. the field is not the last on the line),
- so null-terminate the _data before it. */
- **_suffix = '\0';
- ++(*_suffix);
+ /* range N-
+ range_val was not reset indicating ARG
+ ended with a trailing '-' */
+ all_fields_after = range_val;
+ }
+}
+
+/* Return a pointer to the beginning of the next field in line.
+ The line pointer is moved to the end of the next field. */
+static char*
+next_field (char **line)
+{
+ char *field_start = *line;
+ char *field_end = field_start;
+
+ if (delimiter != DELIMITER_DEFAULT)
+ {
+ if (*field_start != delimiter)
+ {
+ while (*field_end && *field_end != delimiter)
+ ++field_end;
+ }
+ /* else empty field */
}
else
- *_suffix = NULL;
+ {
+ /* keep any space prefix in the returned field */
+ while (*field_end && isblank (to_uchar (*field_end)))
+ ++field_end;
- devmsg (" prefix: %s\n number: %s\n suffix: %s\n",
- quote_n (0, *_prefix ? *_prefix : ""),
- quote_n (1, *_data),
- quote_n (2, *_suffix ? *_suffix : ""));
+ while (*field_end && !isblank (to_uchar (*field_end)))
+ ++field_end;
+ }
+
+ *line = field_end;
+ return field_start;
}
+static bool
+include_field (size_t field)
+{
+ if (all_fields)
+ return true;
-/* Convert a number in a given line of text.
- NEWLINE specifies whether to output a '\n' for this "line". */
-static int
-process_line (char *line, bool newline)
+ if (all_fields_after && all_fields_after <= field)
+ return true;
+
+ if (all_fields_before && field <= all_fields_before)
+ return true;
+
+ /* default to field 1 */
+ if (! field_list)
+ return field == 1;
+
+ return gl_sortedlist_search (field_list, match_field, &field);
+}
+
+/* Convert and output the given field. If it is not included in the set
+ of fields to process just output the original */
+static bool
+process_field (char *text, size_t field)
{
- char *pre, *num, *suf;
long double val = 0;
size_t precision = 0;
- int valid_number = 0;
-
- extract_fields (line, field, &pre, &num, &suf);
- if (!num)
- if (_invalid != inval_ignore)
- error (conv_exit_code, 0, _("input line is too short, "
- "no numbers found to convert in field %ld"),
- field);
+ bool valid_number = true;
- if (num)
+ if (include_field (field))
{
- valid_number = process_suffixed_number (num, &val, &precision);
+ valid_number =
+ process_suffixed_number (text, &val, &precision, field);
+
if (valid_number)
valid_number = prepare_padded_number (val, precision);
+
+ if (valid_number)
+ print_padded_number ();
+ else
+ fputs (text, stdout);
}
+ else
+ fputs (text, stdout);
- if (pre)
- fputs (pre, stdout);
+ return valid_number;
+}
- if (pre && num)
- fputc ((delimiter == DELIMITER_DEFAULT) ? ' ' : delimiter, stdout);
+/* Convert number in a given line of text.
+ NEWLINE specifies whether to output a '\n' for this "line". */
+static int
+process_line (char *line, bool newline)
+{
+ char *next;
+ size_t field = 0;
+ bool valid_number = true;
- if (valid_number)
- {
- print_padded_number ();
- }
- else
- {
- if (num)
- fputs (num, stdout);
- }
+ while (true) {
+ ++field;
+ next = next_field (&line);
- if (suf)
- {
- fputc ((delimiter == DELIMITER_DEFAULT) ? ' ' : delimiter, stdout);
- fputs (suf, stdout);
- }
+ if (*line != '\0')
+ {
+ /* nul terminate the current field string and process */
+ *line = '\0';
+
+ if (! process_field (next, field))
+ valid_number = false;
+
+ fputc ((delimiter == DELIMITER_DEFAULT) ?
+ ' ' : delimiter, stdout);
+ ++line;
+ }
+ else
+ {
+ /* end of the line, process the last field and finish */
+ if (! process_field (next, field))
+ valid_number = false;
+
+ break;
+ }
+ }
if (newline)
putchar ('\n');
@@ -1355,6 +1578,11 @@ main (int argc, char **argv)
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+#if HAVE_FPSETPREC
+ /* Enabled extended precision if needed. */
+ fpsetprec (FP_PE);
+#endif
+
decimal_point = nl_langinfo (RADIXCHAR);
if (decimal_point == NULL || strlen (decimal_point) == 0)
decimal_point = ".";
@@ -1390,7 +1618,7 @@ main (int argc, char **argv)
break;
case ROUND_OPTION:
- _round = XARGMATCH ("--round", optarg, round_args, round_types);
+ round_style = XARGMATCH ("--round", optarg, round_args, round_types);
break;
case GROUPING_OPTION:
@@ -1412,10 +1640,12 @@ main (int argc, char **argv)
break;
case FIELD_OPTION:
- if (xstrtol (optarg, NULL, 10, &field, "") != LONGINT_OK
- || field <= 0)
- error (EXIT_FAILURE, 0, _("invalid field value %s"),
- quote (optarg));
+ if (all_fields || all_fields_before || all_fields_after || field_list)
+ {
+ error (EXIT_FAILURE, 0,
+ _("multiple field specifications"));
+ }
+ parse_field_arg (optarg);
break;
case 'd':
@@ -1458,7 +1688,8 @@ main (int argc, char **argv)
break;
case INVALID_OPTION:
- _invalid = XARGMATCH ("--invalid", optarg, inval_args, inval_types);
+ inval_style = XARGMATCH ("--invalid", optarg,
+ inval_args, inval_types);
break;
case_GETOPT_HELP_CHAR;
@@ -1492,7 +1723,7 @@ main (int argc, char **argv)
setup_padding_buffer (padding_width);
auto_padding = (padding_width == 0 && delimiter == DELIMITER_DEFAULT);
- if (_invalid != inval_abort)
+ if (inval_style != inval_abort)
conv_exit_code = 0;
if (argc > optind)
@@ -1526,17 +1757,22 @@ main (int argc, char **argv)
error (0, errno, _("error reading input"));
}
+#ifdef lint
free (padding_buffer);
free (format_str_prefix);
free (format_str_suffix);
+ if (field_list)
+ gl_list_free (field_list);
+#endif
if (debug && !valid_numbers)
error (0, 0, _("failed to convert some of the input numbers"));
int exit_status = EXIT_SUCCESS;
- if (!valid_numbers && _invalid != inval_warn && _invalid != inval_ignore)
+ if (!valid_numbers
+ && inval_style != inval_warn && inval_style != inval_ignore)
exit_status = EXIT_CONVERSION_WARNINGS;
- exit (exit_status);
+ return exit_status;
}