From cb11d2ab5e1911739004b0359ae4f5f6a643acf0 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 10 Aug 2024 19:02:44 -0700 Subject: [PATCH] tail: support counts > 2**64 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * src/tail.c (tail_lines): If skipping all input, use lseek if possible. (parse_options): Allow counts to exceed 2**64. (main): Don’t subtract 1 from UINTMAX_MAX, since it stands for infinity in this context. (main): Also don’t read anything when given infinite elisions. * tests/tail/tail.pl: Adjust to match new behavior. Rename err-5 test to big-c and expect the invocation to succeed, since ‘tail -c99999999999999999999’ now succeeds instead of (unnecessarily) failing. --- NEWS | 3 ++- src/tail.c | 41 +++++++++++++++++++++++------------------ tests/tail/tail.pl | 8 ++------ 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/NEWS b/NEWS index dde9719db1..8e65fbaf5f 100644 --- a/NEWS +++ b/NEWS @@ -34,7 +34,8 @@ GNU coreutils NEWS -*- outline -*- ** Improvements - 'head -c NUM', 'head -n NUM', 'nl -l NUM' and 'nproc --ignore NUM' + 'head -c NUM', 'head -n NUM', 'nl -l NUM', 'nproc --ignore NUM', + 'tail -c NUM', 'tail -n NUM', and 'tail --max-unchanged-stats NUM’ no longer fail merely because NUM stands for 2**64 or more. sort operates more efficiently when used on pseudo files with diff --git a/src/tail.c b/src/tail.c index f373f768e1..df13b2c90f 100644 --- a/src/tail.c +++ b/src/tail.c @@ -1932,10 +1932,17 @@ tail_lines (char const *pretty_filename, int fd, uintmax_t n_lines, if (from_start) { - int t = start_lines (pretty_filename, fd, n_lines, read_pos); - if (t) - return t < 0; - *read_pos += dump_remainder (false, pretty_filename, fd, COPY_TO_EOF); + /* If skipping all input use lseek if possible, for speed. */ + off_t pos; + if (n_lines == UINTMAX_MAX && 0 <= (pos = lseek (fd, SEEK_END, 0))) + *read_pos = pos; + else + { + int t = start_lines (pretty_filename, fd, n_lines, read_pos); + if (t) + return t < 0; + *read_pos += dump_remainder (false, pretty_filename, fd, COPY_TO_EOF); + } } else { @@ -2213,10 +2220,11 @@ parse_options (int argc, char **argv, else if (*optarg == '-') ++optarg; - *n_units = xdectoumax (optarg, 0, UINTMAX_MAX, "bkKmMGTPEZYRQ0", - count_lines - ? _("invalid number of lines") - : _("invalid number of bytes"), 0); + *n_units = xnumtoumax (optarg, 10, 0, UINTMAX_MAX, "bkKmMGTPEZYRQ0", + (count_lines + ? _("invalid number of lines") + : _("invalid number of bytes")) + , 0, XTOINT_MAX_QUIET); break; case 'f': @@ -2236,8 +2244,10 @@ parse_options (int argc, char **argv, case MAX_UNCHANGED_STATS_OPTION: /* --max-unchanged-stats=N */ max_n_unchanged_stats_between_opens = - xdectoumax (optarg, 0, UINTMAX_MAX, "", - _("invalid maximum number of unchanged stats between opens"), 0); + xnumtoumax (optarg, 10, 0, UINTMAX_MAX, "", + _("invalid maximum number of unchanged stats" + " between opens"), + 0, XTOINT_MAX_QUIET); break; case DISABLE_INOTIFY_OPTION: @@ -2351,8 +2361,7 @@ main (int argc, char **argv) enum header_mode header_mode = multiple_files; bool ok = true; /* If from_start, the number of items to skip before printing; otherwise, - the number of items at the end of the file to print. Although the type - is signed, the value is never negative. */ + the number of items at the end of the file to print. */ uintmax_t n_units = DEFAULT_N_LINES; size_t n_files; char **file; @@ -2388,11 +2397,7 @@ main (int argc, char **argv) /* To start printing with item N_UNITS from the start of the file, skip N_UNITS - 1 items. 'tail -n +0' is actually meaningless, but for Unix compatibility it's treated the same as 'tail -n +1'. */ - if (from_start) - { - if (n_units) - --n_units; - } + n_units -= from_start && 0 < n_units && n_units < UINTMAX_MAX; if (optind < argc) { @@ -2436,7 +2441,7 @@ main (int argc, char **argv) } /* Don't read anything if we'll never output anything. */ - if (! n_units && ! forever && ! from_start) + if (! forever && n_units == (from_start ? UINTMAX_MAX : 0)) return EXIT_SUCCESS; F = xnmalloc (n_files, sizeof *F); diff --git a/tests/tail/tail.pl b/tests/tail/tail.pl index f46a97aa4e..d79c42b9a3 100755 --- a/tests/tail/tail.pl +++ b/tests/tail/tail.pl @@ -68,15 +68,11 @@ my @tv = ( ['err-4', '-2cX', '', '', 1, "$prog: option used in invalid context -- 2\n"], -# Since the number is larger than 2^64, this should provoke -# the diagnostic: 'tail: 99999999999999999999: invalid number of bytes' -# on all systems... probably, for now, maybe. -['err-5', '-c99999999999999999999', '', '', 1, - "$prog: invalid number of bytes: '99999999999999999999'\n", - $normalize_strerror], ['err-6', '-c --', '', '', 1, "$prog: invalid number of bytes: '-'\n", $normalize_strerror], +['big-c', '-c99999999999999999999', '', '', 0], + # Same as -n 10 ['minus-1', '-', '', '', 0], ['minus-2', '-', "x\n" . ("y\n" x 10) . 'z', ("y\n" x 9) . 'z', 0], -- 2.47.2