From: Paul Eggert Date: Sat, 10 Aug 2024 20:00:33 +0000 (-0700) Subject: maint: distinguish EOVERFLOW vs ERANGE better X-Git-Tag: v9.6~193 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=495a35311c0e2fb5b88201b6f2a247750de56579;p=thirdparty%2Fcoreutils.git maint: distinguish EOVERFLOW vs ERANGE better Also, prepare for allowing some arguments to overflow without that being an error. * gl/lib/xdectoint.c: Do not include stddef.h, since we no longer use ‘unreachable’. (xnumtoimax, xnumtoumax, __xnumtoint): New arg FLAGS. All callers changed. Stop using __xdectoint_signed. All definers removed. * gl/lib/xdectoint.h (XTOINT_MIN_QUIET, XTOINT_MAX_QUIET) (XTOINT_MIN_RANGE, XTOINT_MAX_RANGE): New flag constants. * src/fmt.c (main): * src/fold.c (main): * src/nl.c (main): * src/pr.c (getoptnum): * src/split.c (main): Use XTOINT_MIN_RANGE and XTOINT_MAX_RANGE if appropriate. * src/pr.c (getoptnum): Return int rather than returning void and storing through int *. * src/stty.c (apply_settings): Use ckd_add to check for overflow instead of doing it by hand. (integer_arg): Accept and return uintmax_t, not unsigned long. --- diff --git a/gl/lib/xdectoimax.c b/gl/lib/xdectoimax.c index d4bb18fa13..4b0a33b752 100644 --- a/gl/lib/xdectoimax.c +++ b/gl/lib/xdectoimax.c @@ -2,5 +2,4 @@ #define __xnumtoint xnumtoimax #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 index 8a7efbcdaf..bb38431f39 100644 --- a/gl/lib/xdectoint.c +++ b/gl/lib/xdectoint.c @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -29,50 +28,56 @@ #include /* Parse numeric string N_STR of base BASE, and return the value. - Exit on parse error or if MIN or MAX are exceeded. + The value is between MIN and MAX. Strings can have multiplicative SUFFIXES if specified. - ERR is printed along with N_STR on error. */ + On a parse error or out-of-range number, diagnose with N_STR and ERR, and + exit with status ERR_EXIT if nonzero, EXIT_FAILURE otherwise. + However, if FLAGS & XTOINT_MIN_QUIET, do not diagnose or exit + for too-low numbers; return MIN and set errno instead. + Similarly for XTOINT_MAX_QUIET and too-high numbers and MAX. + The errno value and diagnostic for out-of-range values depend on + whether FLAGS & XTOINT_MIN_RANGE and FLAGS & XTOINT_MAX_RANGE are set. */ __xdectoint_t __xnumtoint (char const *n_str, int base, __xdectoint_t min, __xdectoint_t max, - char const *suffixes, char const *err, int err_exit) + char const *suffixes, char const *err, int err_exit, + int flags) { - strtol_error s_err; + __xdectoint_t tnum, r; + strtol_error s_err = __xstrtol (n_str, nullptr, base, &tnum, suffixes); - __xdectoint_t tnum; - s_err = __xstrtol (n_str, nullptr, base, &tnum, suffixes); + /* Errno value to report if there is an overflow. */ + int overflow_errno; - if (s_err == LONGINT_OK) + if (tnum < min) { - 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; - } + r = min; + overflow_errno = flags & XTOINT_MIN_RANGE ? ERANGE : EOVERFLOW; + if (s_err == LONGINT_OK) + s_err = LONGINT_OVERFLOW; } - 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) + else if (max < tnum) { - /* EINVAL error message is redundant in this context. */ - error (err_exit ? err_exit : EXIT_FAILURE, errno == EINVAL ? 0 : errno, - "%s: %s", err, quote (n_str)); - unreachable (); + r = max; + overflow_errno = flags & XTOINT_MAX_RANGE ? ERANGE : EOVERFLOW; + if (s_err == LONGINT_OK) + s_err = LONGINT_OVERFLOW; } + else + { + r = tnum; + overflow_errno = EOVERFLOW; + } + + int e = s_err == LONGINT_OVERFLOW ? overflow_errno : 0; + + if (! (s_err == LONGINT_OK + || (s_err == LONGINT_OVERFLOW + && flags & (tnum < 0 ? XTOINT_MIN_QUIET : XTOINT_MAX_QUIET)))) + error (err_exit ? err_exit : EXIT_FAILURE, e, "%s: %s", err, quote (n_str)); - return tnum; + errno = e; + return r; } /* Parse decimal string N_STR, and return the value. @@ -84,5 +89,5 @@ __xdectoint_t __xdectoint (char const *n_str, __xdectoint_t min, __xdectoint_t max, char const *suffixes, char const *err, int err_exit) { - return __xnumtoint (n_str, 10, min, max, suffixes, err, err_exit); + return __xnumtoint (n_str, 10, min, max, suffixes, err, err_exit, 0); } diff --git a/gl/lib/xdectoint.h b/gl/lib/xdectoint.h index 4550474b4d..2c738753af 100644 --- a/gl/lib/xdectoint.h +++ b/gl/lib/xdectoint.h @@ -16,17 +16,36 @@ along with this program. If not, see . */ #ifndef XDECTOINT_H_ -# define XDECTOINT_H_ 1 +#define XDECTOINT_H_ 1 -# include +#include -# define _DECLARE_XDECTOINT(name, type) \ +/* Flags for xnumtoimax and xnumtoumax. They can be ORed togethar. */ +enum + { + /* If the number is less than MIN, do not diagnose the problem; + instead, return MIN and set errno to EOVERFLOW or ERANGE. */ + XTOINT_MIN_QUIET = 1 << 0, + + /* Likewise for the MAX argument. */ + XTOINT_MAX_QUIET = 1 << 1, + + /* The MIN argument is imposed by the caller, not by the type of + the result. This causes the function to use ERANGE rather + than EOVERFLOW behavior when issuing diagnostics or setting errno. */ + XTOINT_MIN_RANGE = 1 << 2, + + /* Likewise for the MAX argument. */ + XTOINT_MAX_RANGE = 1 << 3 + }; + +#define _DECLARE_XDECTOINT(name, type) \ type name (char const *n_str, type min, type max, \ char const *suffixes, char const *err, int err_exit) \ _GL_ATTRIBUTE_NONNULL ((1, 5)); -# define _DECLARE_XNUMTOINT(name, type) \ +#define _DECLARE_XNUMTOINT(name, type) \ type name (char const *n_str, int base, type min, type max, \ - char const *suffixes, char const *err, int err_exit) \ + char const *suffixes, char const *err, int err_exit, int flags) \ _GL_ATTRIBUTE_NONNULL ((1, 6)); _DECLARE_XDECTOINT (xdectoimax, intmax_t) diff --git a/gl/lib/xdectoumax.c b/gl/lib/xdectoumax.c index 412ddf962d..4b43fa406f 100644 --- a/gl/lib/xdectoumax.c +++ b/gl/lib/xdectoumax.c @@ -2,5 +2,4 @@ #define __xnumtoint xnumtoumax #define __xdectoint_t uintmax_t #define __xstrtol xstrtoumax -#define __xdectoint_signed 0 #include "xdectoint.c" diff --git a/src/fmt.c b/src/fmt.c index 73ef64ee40..59086f35e0 100644 --- a/src/fmt.c +++ b/src/fmt.c @@ -395,8 +395,8 @@ main (int argc, char **argv) { /* Limit max_width to MAXCHARS / 2; otherwise, the resulting output can be quite ugly. */ - max_width = xdectoumax (max_width_option, 0, MAXCHARS / 2, "", - _("invalid width"), 0); + max_width = xnumtoumax (max_width_option, 10, 0, MAXCHARS / 2, "", + _("invalid width"), 0, XTOINT_MAX_RANGE); } if (goal_width_option) diff --git a/src/fold.c b/src/fold.c index 941ad11c61..7231e87742 100644 --- a/src/fold.c +++ b/src/fold.c @@ -279,8 +279,9 @@ main (int argc, char **argv) } FALLTHROUGH; case 'w': /* Line width. */ - width = xdectoumax (optarg, 1, SIZE_MAX - TAB_WIDTH - 1, "", - _("invalid number of columns"), 0); + width = xnumtoumax (optarg, 10, 1, SIZE_MAX - TAB_WIDTH - 1, "", + _("invalid number of columns"), 0, + XTOINT_MIN_RANGE | XTOINT_MAX_RANGE); break; case_GETOPT_HELP_CHAR; diff --git a/src/ls.c b/src/ls.c index f7ffe1086e..de4b02a201 100644 --- a/src/ls.c +++ b/src/ls.c @@ -2109,7 +2109,7 @@ decode_switches (int argc, char **argv) case 'T': tabsize_opt = xnumtoumax (optarg, 0, 0, MIN (PTRDIFF_MAX, SIZE_MAX), - "", _("invalid tab size"), LS_FAILURE); + "", _("invalid tab size"), LS_FAILURE, 0); break; case 'U': diff --git a/src/nl.c b/src/nl.c index f77a2aa9e8..b30579b4b3 100644 --- a/src/nl.c +++ b/src/nl.c @@ -531,15 +531,17 @@ main (int argc, char **argv) reset_numbers = false; break; case 'l': - blank_join = xdectoimax (optarg, 1, INTMAX_MAX, "", - _("invalid line number of blank lines"), 0); + blank_join = xnumtoimax (optarg, 10, 1, INTMAX_MAX, "", + _("invalid line number of blank lines"), + 0, XTOINT_MIN_RANGE); break; case 's': separator_str = optarg; break; case 'w': - lineno_width = xdectoimax (optarg, 1, INT_MAX, "", - _("invalid line number field width"), 0); + lineno_width = xnumtoimax (optarg, 10, 1, INT_MAX, "", + _("invalid line number field width"), + 0, XTOINT_MIN_RANGE); break; case 'n': if (STREQ (optarg, "ln")) diff --git a/src/pr.c b/src/pr.c index 09c6fa8acf..88a330bc0c 100644 --- a/src/pr.c +++ b/src/pr.c @@ -425,8 +425,7 @@ 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 (char const *n_str, int min, int *num, - char const *errfmt); +static int getoptnum (char const *n_str, int min, char const *errfmt); static void getoptarg (char *arg, char switch_char, char *character, int *number); static void print_files (int number_of_files, char **av); @@ -838,7 +837,7 @@ first_last_page (int oi, char c, char const *pages) static void parse_column_count (char const *s) { - getoptnum (s, 1, &columns, _("invalid number of columns")); + columns = getoptnum (s, 1, _("invalid number of columns")); explicit_columns = true; } @@ -975,8 +974,9 @@ main (int argc, char **argv) join_lines = true; break; case 'l': - getoptnum (optarg, 1, &lines_per_page, - _("'-l PAGE_LENGTH' invalid number of lines")); + lines_per_page + = getoptnum (optarg, 1, + _("'-l PAGE_LENGTH' invalid number of lines")); break; case 'm': parallel_files = true; @@ -990,12 +990,13 @@ main (int argc, char **argv) break; case 'N': skip_count = false; - getoptnum (optarg, INT_MIN, &start_line_num, - _("'-N NUMBER' invalid starting line number")); + start_line_num + = getoptnum (optarg, INT_MIN, + _("'-N NUMBER' invalid starting line number")); break; case 'o': - getoptnum (optarg, 0, &chars_per_margin, - _("'-o MARGIN' invalid line offset")); + chars_per_margin = getoptnum (optarg, 0, + _("'-o MARGIN' invalid line offset")); break; case 'r': ignore_failed_opens = true; @@ -1030,9 +1031,9 @@ main (int argc, char **argv) old_options = true; old_w = true; { - int tmp_cpl; - getoptnum (optarg, 1, &tmp_cpl, - _("'-w PAGE_WIDTH' invalid number of characters")); + int tmp_cpl + = getoptnum (optarg, 1, + _("'-w PAGE_WIDTH' invalid number of characters")); if (! truncate_lines) chars_per_line = tmp_cpl; } @@ -1040,8 +1041,9 @@ main (int argc, char **argv) case 'W': old_w = false; /* dominates -w */ truncate_lines = true; - getoptnum (optarg, 1, &chars_per_line, - _("'-W PAGE_WIDTH' invalid number of characters")); + chars_per_line + = getoptnum (optarg, 1, + _("'-W PAGE_WIDTH' invalid number of characters")); break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -1151,11 +1153,11 @@ main (int argc, char **argv) /* Parse numeric arguments, ensuring MIN <= number <= INT_MAX. */ -static void -getoptnum (char const *n_str, int min, int *num, char const *err) +static int +getoptnum (char const *n_str, int min, char const *err) { - intmax_t tnum = xdectoimax (n_str, min, INT_MAX, "", err, 0); - *num = tnum; + return xnumtoimax (n_str, 10, min, INT_MAX, "", err, 0, + min <= 0 ? 0 : XTOINT_MIN_RANGE); } /* Parse options of the form -scNNN. diff --git a/src/shred.c b/src/shred.c index ecb207a8b4..b7cd289b1a 100644 --- a/src/shred.c +++ b/src/shred.c @@ -1210,7 +1210,7 @@ main (int argc, char **argv) case 's': flags.size = xnumtoumax (optarg, 0, 0, OFF_T_MAX, "cbBkKMGTPEZYRQ0", - _("invalid file size"), 0); + _("invalid file size"), 0, 0); break; case 'v': diff --git a/src/split.c b/src/split.c index f82a7f74b4..47abce848a 100644 --- a/src/split.c +++ b/src/split.c @@ -1536,10 +1536,11 @@ main (int argc, char **argv) break; case IO_BLKSIZE_OPTION: - in_blk_size = xdectoumax (optarg, 1, + in_blk_size = xnumtoumax (optarg, 10, 1, MIN (SYS_BUFSIZE_MAX, MIN (IDX_MAX, SIZE_MAX) - 1), - multipliers, _("invalid IO block size"), 0); + multipliers, _("invalid IO block size"), + 0, XTOINT_MIN_RANGE); break; case VERBOSE_OPTION: diff --git a/src/stty.c b/src/stty.c index c3cd75cc5c..95091744aa 100644 --- a/src/stty.c +++ b/src/stty.c @@ -442,7 +442,7 @@ static int screen_columns (void); static bool set_mode (struct mode_info const *info, bool reversed, struct termios *mode); static bool eq_mode (struct termios *mode1, struct termios *mode2); -static unsigned long int integer_arg (char const *s, unsigned long int max); +static uintmax_t integer_arg (char const *s, uintmax_t max); static speed_t string_to_baud (char const *arg); static tcflag_t *mode_type_flag (enum mode_type type, struct termios *mode); static void display_all (struct termios *mode, char const *device_name); @@ -1243,12 +1243,11 @@ apply_settings (bool checking, char const *device_name, #ifdef HAVE_C_LINE else if (STREQ (arg, "line")) { - unsigned long int value; check_argument (arg); ++k; - mode->c_line = value = integer_arg (settings[k], ULONG_MAX); - if (mode->c_line != value) - error (0, 0, _("invalid line discipline %s"), + uintmax_t value = integer_arg (settings[k], UINTMAX_MAX); + if (ckd_add (&mode->c_line, value, 0)) + error (0, EOVERFLOW, _("invalid line discipline %s"), quote (settings[k])); *require_set_attr = true; } @@ -2359,8 +2358,9 @@ visible (cc_t ch) but allowing octal and hex numbers as in C. Reject values larger than MAXVAL. */ -static unsigned long int -integer_arg (char const *s, unsigned long int maxval) +static uintmax_t +integer_arg (char const *s, uintmax_t maxval) { - return xnumtoumax (s, 0, 0, maxval, "bB", _("invalid integer argument"), 0); + return xnumtoumax (s, 0, 0, maxval, "bB", _("invalid integer argument"), + 0, 0); }