diff options
Diffstat (limited to 'src/numfmt.c')
-rw-r--r-- | src/numfmt.c | 552 |
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; } |