>From dba01874bc7ee3ee2c2be1656d6594c115992dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Tue, 16 Dec 2014 12:36:39 +0000 Subject: [PATCH] diagnose too-large numbers better Following on from commit v8.23-82-gaddae94, consistently diagnose numbers that are too large, so as to distinguish from other errors, and make the limits obvious. * gl/modules/xdectoint: A new module implementing xdecto[iu]max(), which handles the common case of parsing a bounded integer and exiiting with a diagnostic on error. * gl/lib/xdectoimax.c: The signed variant. * gl/lib/xdectoint.c: The parameterized implementation. * gl/lib/xdectoint.h: The interface. * gl/lib/xdectoumax.c: The unsigned variant. * bootstrap.conf: Reference the new module. * cfg.mk (exclude_file_name_regexp--sc_require_config_h_first): Exclude the parmeterized templates. * src/csplit.c: Output EOVERFLOW or ERANGE errors if appropriate. * src/fmt.c: Likewise. * src/fold.c: Likewise. * src/head.c: Likewise. * src/ls.c: Likewise. * src/nl.c: Likewise. * src/nproc.c: Likewise. * src/shred.c: Likewise. * src/shuf.c: Likewise. * src/stdbuf.c: Likewise. * src/stty.c: Likewise. * src/tail.c: Likewise. * src/truncate.c: Likewise. * src/split.c: Likewise. * src/pr.c: Likewise. * tests/pr/pr-tests.pl: Adjust to avoid matching errno diagnostic. * tests/fmt/base.pl: Likewise. * tests/split/l-chunk.sh: Likewise. * tests/misc/shred-negative.sh: Likewise. * tests/misc/tail.pl: Likewise. Also remove the redundant existing ERR_SUBST from test err-6. --- bootstrap.conf | 1 + cfg.mk | 2 +- gl/lib/xdectoimax.c | 5 +++ gl/lib/xdectoint.c | 73 +++++++++++++++++++++++++++++++++ gl/lib/xdectoint.h | 30 ++++++++++++++ gl/lib/xdectoumax.c | 5 +++ gl/modules/xdectoint | 29 +++++++++++++ src/csplit.c | 8 ++-- src/fmt.c | 18 +++----- src/fold.c | 13 ++---- src/head.c | 32 ++++----------- src/ls.c | 25 ++++------- src/nl.c | 46 +++++---------------- src/nproc.c | 8 +--- src/pr.c | 98 ++++++++++++++++---------------------------- src/shred.c | 29 ++++--------- src/shuf.c | 19 ++++----- src/split.c | 89 +++++++++++++--------------------------- src/stdbuf.c | 4 +- src/stty.c | 9 +--- src/tail.c | 43 +++++-------------- src/truncate.c | 36 +++------------- tests/fmt/base.pl | 6 ++- tests/misc/shred-negative.sh | 2 +- tests/misc/tail.pl | 12 +++--- tests/pr/pr-tests.pl | 10 +++-- tests/split/l-chunk.sh | 7 ++-- 27 files changed, 300 insertions(+), 359 deletions(-) create mode 100644 gl/lib/xdectoimax.c create mode 100644 gl/lib/xdectoint.c create mode 100644 gl/lib/xdectoint.h create mode 100644 gl/lib/xdectoumax.c create mode 100644 gl/modules/xdectoint diff --git a/bootstrap.conf b/bootstrap.conf index c0b5f02..7706c5a 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -273,6 +273,7 @@ gnulib_modules=" xprintf xprintf-posix xreadlink + xdectoint xstrtod xstrtoimax xstrtol diff --git a/cfg.mk b/cfg.mk index ee444ba..5a96acf 100644 --- a/cfg.mk +++ b/cfg.mk @@ -607,7 +607,7 @@ exclude_file_name_regexp--sc_system_h_headers = \ _src = (false|lbracket|ls-(dir|ls|vdir)|tac-pipe|uname-(arch|uname)) exclude_file_name_regexp--sc_require_config_h_first = \ - (^lib/buffer-lcm\.c|src/$(_src)\.c)$$ + (^lib/buffer-lcm\.c|gl/lib/xdecto.max\.c|src/$(_src)\.c)$$ exclude_file_name_regexp--sc_require_config_h = \ $(exclude_file_name_regexp--sc_require_config_h_first) diff --git a/gl/lib/xdectoimax.c b/gl/lib/xdectoimax.c new file mode 100644 index 0000000..d81d058 --- /dev/null +++ b/gl/lib/xdectoimax.c @@ -0,0 +1,5 @@ +#define __xdectoint xdectoimax +#define __xdectoint_t intmax_t +#define __xstrtol xstrtoimax +#define __xdectoint_signed 1 +#include "xdectoint.c" diff --git a/gl/lib/xdectoint.c b/gl/lib/xdectoint.c new file mode 100644 index 0000000..0c7ed87 --- /dev/null +++ b/gl/lib/xdectoint.c @@ -0,0 +1,73 @@ +/* Convert decimal strings with bounds checking and exit on error. + + Copyright (C) 2014 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "xdectoint.h" + +#include +#include +#include + +#include "error.h" +#include "quote.h" +#include "xstrtol.h" + +/* Parse decimal string N_STR, and output to NUM. + Exit on parse error or if MIN or MAX are exceeded. + Strings can have multiplicative SUFFIXES if specified. + ERR is printed along with N_STR on error. */ + +__xdectoint_t +__xdectoint (const char *n_str, __xdectoint_t min, __xdectoint_t max, + const char *suffixes, const char *err, int err_exit) +{ + strtol_error s_err; + + __xdectoint_t tnum; + s_err = __xstrtol (n_str, NULL, 10, &tnum, suffixes); + + if (s_err == LONGINT_OK) + { + if (tnum < min || max < tnum) + { + s_err = LONGINT_OVERFLOW; + /* Use have the INT range as a heuristic to distinguish + type overflow rather than other min/max limits. */ + if (tnum > INT_MAX/2) + errno = EOVERFLOW; +#if __xdectoint_signed + else if (tnum < INT_MIN/2) + errno = EOVERFLOW; +#endif + else + errno = ERANGE; + } + } + else if (s_err == LONGINT_OVERFLOW) + errno = EOVERFLOW; + else if (s_err == LONGINT_INVALID_SUFFIX_CHAR_WITH_OVERFLOW) + errno = 0; /* Don't show ERANGE errors for invalid numbers. */ + + if (s_err != LONGINT_OK) + { + error (err_exit ? err_exit : EXIT_FAILURE, errno, + "%s: %s", err, quote (n_str)); + } + + return tnum; +} diff --git a/gl/lib/xdectoint.h b/gl/lib/xdectoint.h new file mode 100644 index 0000000..0a83b80 --- /dev/null +++ b/gl/lib/xdectoint.h @@ -0,0 +1,30 @@ +/* Convert decimal strings with bounds checking and exit on error. + + Copyright (C) 2014 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef XDECTOINT_H_ +# define XDECTOINT_H_ 1 + +# include + +# define _DECLARE_XDECTOINT(name, type) \ + type name (const char *n_str, type min, type max, \ + const char *suffixes, const char *err, int err_exit); + +_DECLARE_XDECTOINT (xdectoimax, intmax_t) +_DECLARE_XDECTOINT (xdectoumax, uintmax_t) + +#endif /* not XDECTOINT_H_ */ diff --git a/gl/lib/xdectoumax.c b/gl/lib/xdectoumax.c new file mode 100644 index 0000000..f9d9e05 --- /dev/null +++ b/gl/lib/xdectoumax.c @@ -0,0 +1,5 @@ +#define __xdectoint xdectoumax +#define __xdectoint_t uintmax_t +#define __xstrtol xstrtoumax +#define __xdectoint_signed 0 +#include "xdectoint.c" diff --git a/gl/modules/xdectoint b/gl/modules/xdectoint new file mode 100644 index 0000000..cbd3726 --- /dev/null +++ b/gl/modules/xdectoint @@ -0,0 +1,29 @@ +Description: +Convert decimal string to '[u]intmax_t', with bounds checking and exit on error + +Files: +lib/xdectoint.h +lib/xdectoint.c +lib/xdectoimax.c +lib/xdectoumax.c + +Depends-on: +error +errno +quote +xstrtoimax +xstrtoumax + +configure.ac: + +Makefile.am: +lib_SOURCES += xdectoimax.c xdectoumax.c + +Include: +"xdectoint.h" + +License: +GPL + +Maintainer: +all diff --git a/src/csplit.c b/src/csplit.c index af72d8f..15c4771 100644 --- a/src/csplit.c +++ b/src/csplit.c @@ -33,6 +33,7 @@ #include "quote.h" #include "safe-read.h" #include "stdio--.h" +#include "xdectoint.h" #include "xstrtol.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -1332,7 +1333,6 @@ int main (int argc, char **argv) { int optc; - unsigned long int val; initialize_main (&argc, &argv); set_program_name (argv[0]); @@ -1366,10 +1366,8 @@ main (int argc, char **argv) break; case 'n': - if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK - || MIN (INT_MAX, SIZE_MAX) < val) - error (EXIT_FAILURE, 0, _("%s: invalid number"), optarg); - digits = val; + digits = xdectoimax (optarg, 0, MIN (INT_MAX, SIZE_MAX), "", + _("invalid number"), 0); break; case 's': diff --git a/src/fmt.c b/src/fmt.c index f235da9..e3719d2 100644 --- a/src/fmt.c +++ b/src/fmt.c @@ -30,7 +30,7 @@ #include "error.h" #include "fadvise.h" #include "quote.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "fmt" @@ -399,23 +399,15 @@ main (int argc, char **argv) { /* Limit max_width to MAXCHARS / 2; otherwise, the resulting output can be quite ugly. */ - unsigned long int tmp; - if (! (xstrtoul (max_width_option, NULL, 10, &tmp, "") == LONGINT_OK - && tmp <= MAXCHARS / 2)) - error (EXIT_FAILURE, 0, _("invalid width: %s"), - quote (max_width_option)); - max_width = tmp; + max_width = xdectoumax (max_width_option, 0, MAXCHARS / 2, "", + _("invalid width"), 0); } if (goal_width_option) { /* Limit goal_width to max_width. */ - unsigned long int tmp; - if (! (xstrtoul (goal_width_option, NULL, 10, &tmp, "") == LONGINT_OK - && tmp <= max_width)) - error (EXIT_FAILURE, 0, _("invalid width: %s"), - quote (goal_width_option)); - goal_width = tmp; + goal_width = xdectoumax (goal_width_option, 0, max_width, "", + _("invalid width"), 0); if (max_width_option == NULL) max_width = goal_width + 10; } diff --git a/src/fold.c b/src/fold.c index 3bc9ba2..20cb61e 100644 --- a/src/fold.c +++ b/src/fold.c @@ -25,8 +25,7 @@ #include "system.h" #include "error.h" #include "fadvise.h" -#include "quote.h" -#include "xstrtol.h" +#include "xdectoint.h" #define TAB_WIDTH 8 @@ -280,14 +279,8 @@ main (int argc, char **argv) } /* Fall through. */ case 'w': /* Line width. */ - { - unsigned long int tmp_ulong; - if (! (xstrtoul (optarg, NULL, 10, &tmp_ulong, "") == LONGINT_OK - && 0 < tmp_ulong && tmp_ulong < SIZE_MAX - TAB_WIDTH)) - error (EXIT_FAILURE, 0, - _("invalid number of columns: %s"), quote (optarg)); - width = tmp_ulong; - } + width = xdectoumax (optarg, 1, SIZE_MAX - TAB_WIDTH - 1, "", + _("invalid number of columns"), 0); break; case_GETOPT_HELP_CHAR; diff --git a/src/head.c b/src/head.c index 2782f8e..da49378 100644 --- a/src/head.c +++ b/src/head.c @@ -38,7 +38,7 @@ #include "safe-read.h" #include "stat-size.h" #include "xfreopen.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "head" @@ -893,7 +893,7 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines, return ok; } -/* Convert a string of decimal digits, N_STRING, with an optional suffinx +/* Convert a string of decimal digits, N_STRING, with an optional suffix to an integral value. Upon successful conversion, return that value. If it cannot be converted, give a diagnostic and exit. COUNT_LINES indicates whether N_STRING is a number of bytes or a number @@ -902,27 +902,9 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines, static uintmax_t string_to_integer (bool count_lines, const char *n_string) { - strtol_error s_err; - uintmax_t n; - - s_err = xstrtoumax (n_string, NULL, 10, &n, "bkKmMGTPEZY0"); - - if (s_err == LONGINT_OVERFLOW) - { - error (EXIT_FAILURE, 0, - _("%s: %s is so large that it is not representable"), n_string, - count_lines ? _("number of lines") : _("number of bytes")); - } - - if (s_err != LONGINT_OK) - { - error (EXIT_FAILURE, 0, "%s: %s", n_string, - (count_lines - ? _("invalid number of lines") - : _("invalid number of bytes"))); - } - - return n; + return xdectoumax (n_string, 0, UINTMAX_MAX, "bkKmMGTPEZY0", + count_lines ? _("invalid number of lines") + : _("invalid number of bytes"), 0); } int @@ -1076,8 +1058,8 @@ main (int argc, char **argv) if ( ! count_lines && elide_from_end && OFF_T_MAX < n_units) { char umax_buf[INT_BUFSIZE_BOUND (n_units)]; - error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), - umaxtostr (n_units, umax_buf)); + error (EXIT_FAILURE, EOVERFLOW, _("invalid number of bytes: %s"), + quote (umaxtostr (n_units, umax_buf))); } file_list = (optind < argc diff --git a/src/ls.c b/src/ls.c index 5bae06f..9f7098d 100644 --- a/src/ls.c +++ b/src/ls.c @@ -105,6 +105,7 @@ #include "stat-size.h" #include "stat-time.h" #include "strftime.h" +#include "xdectoint.h" #include "xstrtol.h" #include "areadlink.h" #include "mbsalign.h" @@ -1742,15 +1743,9 @@ decode_switches (int argc, char **argv) break; case 'w': - { - unsigned long int tmp_ulong; - if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK - || ! (0 < tmp_ulong && tmp_ulong <= SIZE_MAX)) - error (LS_FAILURE, 0, _("invalid line width: %s"), - quotearg (optarg)); - line_length = tmp_ulong; - break; - } + line_length = xdectoumax (optarg, 1, SIZE_MAX, "", + _("invalid line width"), LS_FAILURE); + break; case 'x': format = horizontal; @@ -1816,15 +1811,9 @@ decode_switches (int argc, char **argv) break; case 'T': - { - unsigned long int tmp_ulong; - if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK - || SIZE_MAX < tmp_ulong) - error (LS_FAILURE, 0, _("invalid tab size: %s"), - quotearg (optarg)); - tabsize = tmp_ulong; - break; - } + tabsize = xdectoumax (optarg, 0, SIZE_MAX, "", + _("invalid tab size"), LS_FAILURE); + break; case 'U': sort_type = sort_none; diff --git a/src/nl.c b/src/nl.c index 7fc1134..5c34162 100644 --- a/src/nl.c +++ b/src/nl.c @@ -31,7 +31,7 @@ #include "fadvise.h" #include "linebuffer.h" #include "quote.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "nl" @@ -497,53 +497,27 @@ main (int argc, char **argv) } break; case 'v': - if (xstrtoimax (optarg, NULL, 10, &starting_line_number, "") - != LONGINT_OK) - { - error (0, 0, _("invalid starting line number: %s"), - quote (optarg)); - ok = false; - } + starting_line_number = xdectoimax (optarg, INTMAX_MIN, INTMAX_MAX, "", + _("invalid starting line number"), + 0); break; case 'i': - if (! (xstrtoimax (optarg, NULL, 10, &page_incr, "") == LONGINT_OK - && 0 < page_incr)) - { - error (0, 0, _("invalid line number increment: %s"), - quote (optarg)); - ok = false; - } + page_incr = xdectoimax (optarg, 1, INTMAX_MAX, "", + _("invalid line number increment"), 0); break; case 'p': reset_numbers = false; break; case 'l': - if (! (xstrtoimax (optarg, NULL, 10, &blank_join, "") == LONGINT_OK - && 0 < blank_join)) - { - error (0, 0, _("invalid number of blank lines: %s"), - quote (optarg)); - ok = false; - } + blank_join = xdectoimax (optarg, 1, INTMAX_MAX, "", + _("invalid line number of blank lines"), 0); break; case 's': separator_str = optarg; break; case 'w': - { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long <= 0 || tmp_long > INT_MAX) - { - error (0, 0, _("invalid line number field width: %s"), - quote (optarg)); - ok = false; - } - else - { - lineno_width = tmp_long; - } - } + lineno_width = xdectoimax (optarg, 1, INT_MAX, "", + _("invalid line number field width"), 0); break; case 'n': if (STREQ (optarg, "ln")) diff --git a/src/nproc.c b/src/nproc.c index e1bc1d0..4ba146a 100644 --- a/src/nproc.c +++ b/src/nproc.c @@ -25,7 +25,7 @@ #include "error.h" #include "nproc.h" #include "quote.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "nproc" @@ -102,11 +102,7 @@ main (int argc, char **argv) break; case IGNORE_OPTION: - if (xstrtoul (optarg, NULL, 10, &ignore, "") != LONGINT_OK) - { - error (0, 0, _("%s: invalid number to ignore"), optarg); - usage (EXIT_FAILURE); - } + ignore = xdectoumax (optarg, 0, ULONG_MAX, "", _("invalid number"),0); break; default: diff --git a/src/pr.c b/src/pr.c index 5299649..d6823b5 100644 --- a/src/pr.c +++ b/src/pr.c @@ -322,6 +322,7 @@ #include "stdio--.h" #include "strftime.h" #include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "pr" @@ -424,6 +425,8 @@ static bool skip_to_page (uintmax_t page); static void print_header (void); static void pad_across_to (int position); static void add_line_number (COLUMN *p); +static void getoptnum (const char *n_str, int min, int *num, + const char *errfmt); static void getoptarg (char *arg, char switch_char, char *character, int *number); static void print_files (int number_of_files, char **av); @@ -820,18 +823,12 @@ first_last_page (int oi, char c, char const *pages) /* Parse column count string S, and if it's valid (1 or larger and within range of the type of 'columns') set the global variables - columns and explicit_columns and return true. - Otherwise, exit with a diagnostic. */ + columns and explicit_columns. Otherwise, exit with a diagnostic. */ + static void parse_column_count (char const *s) { - long int tmp_long; - if (xstrtol (s, NULL, 10, &tmp_long, "") != LONGINT_OK - || !(1 <= tmp_long && tmp_long <= INT_MAX)) - error (EXIT_FAILURE, 0, - _("invalid number of columns: %s"), quote (s)); - - columns = tmp_long; + getoptnum (s, 1, &columns, _("invalid number of columns")); explicit_columns = true; } @@ -966,18 +963,9 @@ main (int argc, char **argv) join_lines = true; break; case 'l': - { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long <= 0 || tmp_long > INT_MAX) - { - error (EXIT_FAILURE, 0, - _("'-l PAGE_LENGTH' invalid number of lines: %s"), - quote (optarg)); - } - lines_per_page = tmp_long; - break; - } + getoptnum (optarg, 1, &lines_per_page, + _("'-l PAGE_LENGTH' invalid number of lines")); + break; case 'm': parallel_files = true; storing_columns = false; @@ -990,28 +978,13 @@ main (int argc, char **argv) break; case 'N': skip_count = false; - { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long > INT_MAX) - { - error (EXIT_FAILURE, 0, - _("'-N NUMBER' invalid starting line number: %s"), - quote (optarg)); - } - start_line_num = tmp_long; - break; - } + getoptnum (optarg, INT_MIN, &start_line_num, + _("'-N NUMBER' invalid starting line number")); + break; case 'o': - { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long < 0 || tmp_long > INT_MAX) - error (EXIT_FAILURE, 0, - _("'-o MARGIN' invalid line offset: %s"), quote (optarg)); - chars_per_margin = tmp_long; - break; - } + getoptnum (optarg, 0, &chars_per_margin, + _("'-o MARGIN' invalid line offset")); + break; case 'r': ignore_failed_opens = true; break; @@ -1045,29 +1018,19 @@ main (int argc, char **argv) old_options = true; old_w = true; { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long <= 0 || tmp_long > INT_MAX) - error (EXIT_FAILURE, 0, - _("'-w PAGE_WIDTH' invalid number of characters: %s"), - quote (optarg)); - if (!truncate_lines) - chars_per_line = tmp_long; - break; + int tmp_cpl; + getoptnum (optarg, 1, &tmp_cpl, + _("'-w PAGE_WIDTH' invalid number of characters")); + if (! truncate_lines) + chars_per_line = tmp_cpl; } + break; case 'W': old_w = false; /* dominates -w */ truncate_lines = true; - { - long int tmp_long; - if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long <= 0 || tmp_long > INT_MAX) - error (EXIT_FAILURE, 0, - _("'-W PAGE_WIDTH' invalid number of characters: %s"), - quote (optarg)); - chars_per_line = tmp_long; - break; - } + getoptnum (optarg, 1, &chars_per_line, + _("'-W PAGE_WIDTH' invalid number of characters")); + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -1173,6 +1136,15 @@ main (int argc, char **argv) return failed_opens ? EXIT_FAILURE : EXIT_SUCCESS; } +/* Parse numeric arguments, ensuring MIN <= number <= INT_MAX. */ + +static void +getoptnum (const char *n_str, int min, int *num, const char *err) +{ + intmax_t tnum = xdectoimax (n_str, min, INT_MAX, "", err, 0); + *num = tnum; +} + /* Parse options of the form -scNNN. Example: -nck, where 'n' is the option, c is the optional number @@ -1188,9 +1160,9 @@ getoptarg (char *arg, char switch_char, char *character, int *number) { long int tmp_long; if (xstrtol (arg, NULL, 10, &tmp_long, "") != LONGINT_OK - || tmp_long <= 0 || tmp_long > INT_MAX) + || tmp_long <= 0 || INT_MAX < tmp_long) { - error (0, 0, + error (0, INT_MAX < tmp_long ? EOVERFLOW : errno, _("'-%c' extra characters or invalid number in the argument: %s"), switch_char, quote (arg)); usage (EXIT_FAILURE); diff --git a/src/shred.c b/src/shred.c index b235b29..efbd3d0 100644 --- a/src/shred.c +++ b/src/shred.c @@ -86,7 +86,7 @@ #include "system.h" #include "argmatch.h" -#include "xstrtol.h" +#include "xdectoint.h" #include "error.h" #include "fcntl--.h" #include "human.h" @@ -1228,16 +1228,10 @@ main (int argc, char **argv) break; case 'n': - { - uintmax_t tmp; - if (xstrtoumax (optarg, NULL, 10, &tmp, NULL) != LONGINT_OK - || MIN (ULONG_MAX, SIZE_MAX / sizeof (int)) <= tmp) - { - error (EXIT_FAILURE, 0, _("%s: invalid number of passes"), - quotearg_colon (optarg)); - } - flags.n_iterations = tmp; - } + flags.n_iterations = xdectoumax (optarg, 0, + MIN (ULONG_MAX, + SIZE_MAX / sizeof (int)), "", + _("invalid number of passes"), 0); break; case RANDOM_SOURCE_OPTION: @@ -1255,17 +1249,8 @@ main (int argc, char **argv) break; case 's': - { - uintmax_t tmp; - if ((xstrtoumax (optarg, NULL, 0, &tmp, "cbBkKMGTPEZY0") - != LONGINT_OK) - || OFF_T_MAX < tmp) - { - error (EXIT_FAILURE, 0, _("%s: invalid file size"), - quotearg_colon (optarg)); - } - flags.size = tmp; - } + flags.size = xdectoumax (optarg, 0, OFF_T_MAX, "cbBkKMGTPEZY0", + _("invalid file size"), 0); break; case 'v': diff --git a/src/shuf.c b/src/shuf.c index df0092b..0678c3d 100644 --- a/src/shuf.c +++ b/src/shuf.c @@ -32,6 +32,7 @@ #include "randperm.h" #include "read-file.h" #include "stdio--.h" +#include "xdectoint.h" #include "xstrtol.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -422,7 +423,6 @@ main (int argc, char **argv) case 'i': { - unsigned long int argval = 0; char *p = strchr (optarg, '-'); char const *hi_optarg = optarg; bool invalid = !p; @@ -434,22 +434,19 @@ main (int argc, char **argv) if (p) { *p = '\0'; - invalid = ((xstrtoul (optarg, NULL, 10, &argval, NULL) - != LONGINT_OK) - || SIZE_MAX < argval); + lo_input = xdectoumax (optarg, 0, SIZE_MAX, "", + _("invalid input range"), 0); *p = '-'; - lo_input = argval; hi_optarg = p + 1; } - invalid |= ((xstrtoul (hi_optarg, NULL, 10, &argval, NULL) - != LONGINT_OK) - || SIZE_MAX < argval); - hi_input = argval; + hi_input = xdectoumax (hi_optarg, 0, SIZE_MAX, "", + _("invalid input range"), 0); + n_lines = hi_input - lo_input + 1; invalid |= ((lo_input <= hi_input) == (n_lines == 0)); if (invalid) - error (EXIT_FAILURE, 0, _("invalid input range %s"), + error (EXIT_FAILURE, errno, "%s: %s", _("invalid input range"), quote (optarg)); } break; @@ -462,7 +459,7 @@ main (int argc, char **argv) if (e == LONGINT_OK) head_lines = MIN (head_lines, argval); else if (e != LONGINT_OVERFLOW) - error (EXIT_FAILURE, 0, _("invalid line count %s"), + error (EXIT_FAILURE, 0, _("invalid line count: %s"), quote (optarg)); } break; diff --git a/src/split.c b/src/split.c index 0eec3ec..dd96a17 100644 --- a/src/split.c +++ b/src/split.c @@ -39,7 +39,7 @@ #include "safe-read.h" #include "sig2str.h" #include "xfreopen.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "split" @@ -1174,19 +1174,20 @@ no_filters: } \ while (0) + /* Parse K/N syntax of chunk options. */ static void parse_chunk (uintmax_t *k_units, uintmax_t *n_units, char *slash) { - *slash = '\0'; - if (xstrtoumax (slash + 1, NULL, 10, n_units, "") != LONGINT_OK - || *n_units == 0) - error (EXIT_FAILURE, 0, _("%s: invalid number of chunks"), slash + 1); - if (slash != optarg /* a leading number is specified. */ - && (xstrtoumax (optarg, NULL, 10, k_units, "") != LONGINT_OK - || *k_units == 0 || *n_units < *k_units)) - error (EXIT_FAILURE, 0, _("%s: invalid chunk number"), optarg); + *n_units = xdectoumax (slash + 1, 1, UINTMAX_MAX, "", + _("invalid number of chunks"), 0); + if (slash != optarg) /* a leading number is specified. */ + { + *slash = '\0'; + *k_units = xdectoumax (optarg, 1, *n_units, "", + _("invalid chunk number"), 0); + } } @@ -1197,7 +1198,7 @@ main (int argc, char **argv) size_t in_blk_size = 0; /* optimal block size of input file device */ size_t page_size = getpagesize (); uintmax_t k_units = 0; - uintmax_t n_units; + uintmax_t n_units = 0; static char const multipliers[] = "bEGKkMmPTYZ0"; int c; @@ -1231,16 +1232,8 @@ main (int argc, char **argv) switch (c) { case 'a': - { - unsigned long tmp; - if (xstrtoul (optarg, NULL, 10, &tmp, "") != LONGINT_OK - || SIZE_MAX / sizeof (size_t) < tmp) - { - error (0, 0, _("%s: invalid suffix length"), optarg); - usage (EXIT_FAILURE); - } - suffix_length = tmp; - } + suffix_length = xdectoumax (optarg, 0, SIZE_MAX / sizeof (size_t), + "", _("invalid suffix length"), 0); break; case ADDITIONAL_SUFFIX_OPTION: @@ -1258,46 +1251,27 @@ main (int argc, char **argv) if (split_type != type_undef) FAIL_ONLY_ONE_WAY (); split_type = type_bytes; - if (xstrtoumax (optarg, NULL, 10, &n_units, multipliers) != LONGINT_OK - || n_units == 0) - { - error (0, 0, _("%s: invalid number of bytes"), optarg); - usage (EXIT_FAILURE); - } - /* If input is a pipe, we could get more data than is possible - to write to a single file, so indicate that immediately - rather than having possibly future invocations fail. */ - if (OFF_T_MAX < n_units) - error (EXIT_FAILURE, EFBIG, - _("%s: invalid number of bytes"), optarg); - + /* Limit to OFF_T_MAX, becaue if input is a pipe, we could get more + data than is possible to write to a single file, so indicate that + immediately rather than having possibly future invocations fail. */ + n_units = xdectoumax (optarg, 1, OFF_T_MAX, multipliers, + _("invalid number of bytes"), 0); break; case 'l': if (split_type != type_undef) FAIL_ONLY_ONE_WAY (); split_type = type_lines; - if (xstrtoumax (optarg, NULL, 10, &n_units, "") != LONGINT_OK - || n_units == 0) - { - error (0, 0, _("%s: invalid number of lines"), optarg); - usage (EXIT_FAILURE); - } + n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "", + _("invalid number of lines"), 0); break; case 'C': if (split_type != type_undef) FAIL_ONLY_ONE_WAY (); split_type = type_byteslines; - if (xstrtoumax (optarg, NULL, 10, &n_units, multipliers) != LONGINT_OK - || n_units == 0 || SIZE_MAX < n_units) - { - error (0, 0, _("%s: invalid number of bytes"), optarg); - usage (EXIT_FAILURE); - } - if (OFF_T_MAX < n_units) - error (EXIT_FAILURE, EFBIG, - _("%s: invalid number of bytes"), optarg); + n_units = xdectoumax (optarg, 1, MIN (SIZE_MAX, OFF_T_MAX), + multipliers, _("invalid number of bytes"), 0); break; case 'n': @@ -1320,9 +1294,9 @@ main (int argc, char **argv) split_type = type_chunk_bytes; if ((slash = strchr (optarg, '/'))) parse_chunk (&k_units, &n_units, slash); - else if (xstrtoumax (optarg, NULL, 10, &n_units, "") != LONGINT_OK - || n_units == 0) - error (EXIT_FAILURE, 0, _("%s: invalid number of chunks"), optarg); + else + n_units = xdectoumax (optarg, 1, UINTMAX_MAX, "", + _("invalid number of chunks"), 0); break; case 'u': @@ -1388,15 +1362,8 @@ main (int argc, char **argv) break; case IO_BLKSIZE_OPTION: - { - uintmax_t tmp_blk_size; - if (xstrtoumax (optarg, NULL, 10, &tmp_blk_size, - multipliers) != LONGINT_OK - || tmp_blk_size == 0 || SIZE_MAX - page_size < tmp_blk_size) - error (0, 0, _("%s: invalid IO block size"), optarg); - else - in_blk_size = tmp_blk_size; - } + in_blk_size = xdectoumax (optarg, 1, SIZE_MAX - page_size, + multipliers, _("invalid IO block size"), 0); break; case VERBOSE_OPTION: @@ -1427,7 +1394,7 @@ main (int argc, char **argv) if (n_units == 0) { - error (0, 0, _("%s: invalid number of lines"), "0"); + error (0, 0, "%s: %s", _("invalid number of lines"), quote ("0")); usage (EXIT_FAILURE); } diff --git a/src/stdbuf.c b/src/stdbuf.c index 3fb5d3c..94a7a4f 100644 --- a/src/stdbuf.c +++ b/src/stdbuf.c @@ -66,7 +66,7 @@ parse_size (char const *str, size_t *size) { uintmax_t tmp_size; enum strtol_error e = xstrtoumax (str, NULL, 10, &tmp_size, "EGkKMPTYZ0"); - if (e == LONGINT_OK && tmp_size > SIZE_MAX) + if (e == LONGINT_OK && SIZE_MAX < tmp_size) e = LONGINT_OVERFLOW; if (e == LONGINT_OK) @@ -76,7 +76,7 @@ parse_size (char const *str, size_t *size) return 0; } - errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : 0); + errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : errno); return -1; } diff --git a/src/stty.c b/src/stty.c index 42ec3b4..2cc2a22 100644 --- a/src/stty.c +++ b/src/stty.c @@ -58,6 +58,7 @@ #include "error.h" #include "fd-reopen.h" #include "quote.h" +#include "xdectoint.h" #include "xstrtol.h" /* The official name of this program (e.g., no 'g' prefix). */ @@ -2087,11 +2088,5 @@ visible (cc_t ch) static unsigned long int integer_arg (const char *s, unsigned long int maxval) { - unsigned long int value; - if (xstrtoul (s, NULL, 0, &value, "bB") != LONGINT_OK || maxval < value) - { - error (0, 0, _("invalid integer argument %s"), quote (s)); - usage (EXIT_FAILURE); - } - return value; + return xdectoumax (s, 0, maxval, "bB", _("invalid integer argument"), 0); } diff --git a/src/tail.c b/src/tail.c index 4c5f943..3bc2302 100644 --- a/src/tail.c +++ b/src/tail.c @@ -44,6 +44,7 @@ #include "stat-time.h" #include "xfreopen.h" #include "xnanosleep.h" +#include "xdectoint.h" #include "xstrtol.h" #include "xstrtod.h" @@ -1949,7 +1950,7 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units) else if ((xstrtoumax (n_string, NULL, 10, n_units, "b") & ~LONGINT_INVALID_SUFFIX_CHAR) != LONGINT_OK) - error (EXIT_FAILURE, 0, _("number in %s is too large"), quote (argv[1])); + error (EXIT_FAILURE, errno, _("invalid number %s"), quote (argv[1])); /* Set globals. */ from_start = t_from_start; @@ -1986,17 +1987,10 @@ parse_options (int argc, char **argv, else if (*optarg == '-') ++optarg; - { - strtol_error s_err; - s_err = xstrtoumax (optarg, NULL, 10, n_units, "bkKmMGTPEZY0"); - if (s_err != LONGINT_OK) - { - error (EXIT_FAILURE, 0, "%s: %s", optarg, - (c == 'n' - ? _("invalid number of lines") - : _("invalid number of bytes"))); - } - } + *n_units = xdectoumax (optarg, 0, UINTMAX_MAX, "bkKmMGTPEZY0", + count_lines + ? _("invalid number of lines") + : _("invalid number of bytes"), 0); break; case 'f': @@ -2015,15 +2009,9 @@ parse_options (int argc, char **argv, case MAX_UNCHANGED_STATS_OPTION: /* --max-unchanged-stats=N */ - if (xstrtoumax (optarg, NULL, 10, - &max_n_unchanged_stats_between_opens, - "") - != LONGINT_OK) - { - error (EXIT_FAILURE, 0, - _("%s: invalid maximum number of unchanged stats between opens"), - optarg); - } + max_n_unchanged_stats_between_opens = + xdectoumax (optarg, 0, UINTMAX_MAX, "", + _("invalid maximum number of unchanged stats between opens"), 0); break; case DISABLE_INOTIFY_OPTION: @@ -2031,16 +2019,7 @@ parse_options (int argc, char **argv, break; case PID_OPTION: - { - strtol_error s_err; - unsigned long int tmp_ulong; - s_err = xstrtoul (optarg, NULL, 10, &tmp_ulong, ""); - if (s_err != LONGINT_OK || tmp_ulong > PID_T_MAX) - { - error (EXIT_FAILURE, 0, _("%s: invalid PID"), optarg); - } - pid = tmp_ulong; - } + pid = xdectoumax (optarg, 0, PID_T_MAX, "", _("invalid PID"), 0); break; case PRESUME_INPUT_PIPE_OPTION: @@ -2056,7 +2035,7 @@ parse_options (int argc, char **argv, double s; if (! (xstrtod (optarg, NULL, &s, c_strtod) && 0 <= s)) error (EXIT_FAILURE, 0, - _("%s: invalid number of seconds"), optarg); + _("invalid number of seconds: %s"), quote (optarg)); *sleep_interval = s; } break; diff --git a/src/truncate.c b/src/truncate.c index f06668d..e4ef5e7 100644 --- a/src/truncate.c +++ b/src/truncate.c @@ -29,7 +29,7 @@ #include "error.h" #include "quote.h" #include "stat-size.h" -#include "xstrtol.h" +#include "xdectoint.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "truncate" @@ -59,33 +59,6 @@ static struct option const longopts[] = typedef enum { rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t; -/* Set size to the value of STR, interpreted as a decimal integer, - optionally multiplied by various values. - Return -1 on error, 0 on success. - - This supports dd BLOCK size suffixes + lowercase g,t,m for bsd compat - Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */ -static int -parse_len (char const *str, off_t *size) -{ - enum strtol_error e; - intmax_t tmp_size; - e = xstrtoimax (str, NULL, 10, &tmp_size, "EgGkKmMPtTYZ0"); - if (e == LONGINT_OK - && !(OFF_T_MIN <= tmp_size && tmp_size <= OFF_T_MAX)) - e = LONGINT_OVERFLOW; - - if (e == LONGINT_OK) - { - errno = 0; - *size = tmp_size; - return 0; - } - - errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : 0); - return -1; -} - void usage (int status) { @@ -306,9 +279,10 @@ main (int argc, char **argv) } rel_mode = rm_rel; } - if (parse_len (optarg, &size) == -1) - error (EXIT_FAILURE, errno, _("invalid number %s"), - quote (optarg)); + /* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat. + Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */ + size = xdectoimax (optarg, OFF_T_MIN, OFF_T_MAX, "EgGkKmMPtTYZ0", + _("Invalid number"), 0); /* Rounding to multiple of 0 is nonsensical */ if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0) error (EXIT_FAILURE, 0, _("division by zero")); diff --git a/tests/fmt/base.pl b/tests/fmt/base.pl index c6b9787..8751119 100755 --- a/tests/fmt/base.pl +++ b/tests/fmt/base.pl @@ -26,9 +26,11 @@ my @Tests = {IN=> "ça\nçb\n"}, {OUT=>"ça b\n"}], ['wide-1', '-w 32768', - {ERR => "fmt: invalid width: '32768'\n"}, {EXIT => 1}], + {ERR => "fmt: invalid width: '32768'"}, {EXIT => 1}, + {ERR_SUBST => 's/:[^:]*$//'}], ['wide-2', '-w 2147483647', - {ERR => "fmt: invalid width: '2147483647'\n"}, {EXIT => 1}], + {ERR => "fmt: invalid width: '2147483647'"}, {EXIT => 1}, + {ERR_SUBST => 's/:[^:]*$//'}], ['bad-suffix', '-72x', {IN=> ''}, {ERR => "fmt: invalid width: '72x'\n"}, {EXIT => 1}], ['no-file', 'no-such-file', diff --git a/tests/misc/shred-negative.sh b/tests/misc/shred-negative.sh index 86cbf3e..9de1eaf 100755 --- a/tests/misc/shred-negative.sh +++ b/tests/misc/shred-negative.sh @@ -19,7 +19,7 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ shred -echo 'shred: -2: invalid file size' > exp || framework_failure_ +echo "shred: invalid file size: '-2'" > exp || framework_failure_ echo 1234 > f || framework_failure_ shred -s-2 f 2>err && fail=1 diff --git a/tests/misc/tail.pl b/tests/misc/tail.pl index 6798eb0..adeff68 100755 --- a/tests/misc/tail.pl +++ b/tests/misc/tail.pl @@ -19,7 +19,7 @@ use strict; my $prog = 'tail'; -my $normalize_filename = {ERR_SUBST => 's/^$prog: .*?:/$prog: -:/'}; +my $normalize_strerror = 's/:[^:]*$//'; # Turn off localization of executable's output. @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; @@ -59,7 +59,7 @@ my @tv = ( "$prog: cannot open '+cl' for reading: No such file or directory\n"], ['err-2', '-cl', '', '', 1, - "$prog: l: invalid number of bytes\n"], + "$prog: invalid number of bytes: 'l'\n"], ['err-3', '+2cz', '', '', 1, "$prog: cannot open '+2cz' for reading: No such file or directory\n"], @@ -72,9 +72,9 @@ my @tv = ( # the diagnostic: 'tail: 99999999999999999999: invalid number of bytes' # on all systems... probably, for now, maybe. ['err-5', '-c99999999999999999999', '', '', 1, - "$prog: 99999999999999999999: invalid number of bytes\n"], + "$prog: invalid number of bytes: '99999999999999999999'", $normalize_strerror], ['err-6', '-c --', '', '', 1, - "$prog: -: invalid number of bytes\n", $normalize_filename], + "$prog: invalid number of bytes: '-'\n"], # Same as -n 10 ['minus-1', '-', '', '', 0], @@ -106,10 +106,10 @@ my @Tests; foreach my $t (@tv) { - my ($test_name, $flags, $in, $exp, $ret, $err_msg) = @$t; + my ($test_name, $flags, $in, $exp, $ret, $err_msg, $err_sub) = @$t; my $e = [$test_name, $flags, {IN=>$in}, {OUT=>$exp}]; $ret - and push @$e, {EXIT=>$ret}, {ERR=>$err_msg}; + and push @$e, {EXIT=>$ret}, {ERR=>$err_msg}, {ERR_SUBST=>$err_sub}; $test_name =~ /^(obs-plus-|minus-)/ and push @$e, {ENV=>'_POSIX2_VERSION=199209'}; diff --git a/tests/pr/pr-tests.pl b/tests/pr/pr-tests.pl index 10947ac..cc2ef38 100755 --- a/tests/pr/pr-tests.pl +++ b/tests/pr/pr-tests.pl @@ -385,9 +385,11 @@ my @tv = ( ['col-long', '-W3 -t -1 --columns=2', "a\nb\nc\n", "a c\nb\n", 0], # Make sure these fail. ['col-0', '-0', '', '', 1, - "$prog: invalid number of columns: '0'\n"], + "$prog: invalid number of columns: '0'", + 's/:[^:]*$//'], ['col-inval', '-'.'9'x100, '', '', 1, - "$prog: invalid number of columns: '". ('9'x100) ."'\n"], + "$prog: invalid number of columns: '". ('9'x100) ."'", + 's/:[^:]*$//'], # Before coreutils-5.3.1, --pages=1:-1 would be treated like # --pages=1:18446744073709551615. @@ -427,7 +429,7 @@ my $common_option_prefix = '--date-format="-- Date/Time --" -h x'; my @Tests; foreach my $t (@tv) { - my ($test_name, $flags, $in, $exp, $ret, $err_msg) = @$t; + my ($test_name, $flags, $in, $exp, $ret, $err_msg, $err_sub) = @$t; my $new_ent = [$test_name, $common_option_prefix, $flags]; if (!ref $in) { @@ -454,7 +456,7 @@ foreach my $t (@tv) } } $ret - and push @$new_ent, {EXIT=>$ret}, {ERR=>$err_msg}; + and push @$new_ent, {EXIT=>$ret}, {ERR=>$err_msg}, {ERR_SUBST=>$err_sub}; push @Tests, $new_ent; } diff --git a/tests/split/l-chunk.sh b/tests/split/l-chunk.sh index 792c556..39ee4dc 100755 --- a/tests/split/l-chunk.sh +++ b/tests/split/l-chunk.sh @@ -20,7 +20,7 @@ print_ver_ split # invalid number of chunks -echo 'split: 1o: invalid number of chunks' > exp +echo "split: invalid number of chunks: '1o'" > exp split -n l/1o 2>err && fail=1 compare exp err || fail=1 @@ -53,8 +53,9 @@ lines=\ printf "%s" "$lines" | tr '~' '\n' > in || framework_failure_ -echo 'split: 16: invalid chunk number' > exp -split -n l/16/15 in 2>err && fail=1 +echo "split: invalid chunk number: '16'" > exp +split -n l/16/15 in 2>err.t && fail=1 +sed 's/:[^:]*$//' < err.t > err || framework_failure_ compare exp err || fail=1 printf '%s' "\ -- 2.1.0