]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
tail: support counts > 2**64
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 11 Aug 2024 02:02:44 +0000 (19:02 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 11 Aug 2024 02:30:01 +0000 (19:30 -0700)
* 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
src/tail.c
tests/tail/tail.pl

diff --git a/NEWS b/NEWS
index dde9719db161220d7f20c76c373a929b71d59812..8e65fbaf5ff9076c2933b74fdd8cbfd027afa883 100644 (file)
--- 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
index f373f768e1da5430afa24d625b861238dc438be8..df13b2c90f52c8f0c047d4cd998b44939ae76543 100644 (file)
@@ -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);
index f46a97aa4ed69f336eb30a6cb96c3925f365a537..d79c42b9a3bd39546cd7bc4764a32071c9b46e4b 100755 (executable)
@@ -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],