]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
tail: allow >=2**64 in traditional form
authorPaul Eggert <eggert@cs.ucla.edu>
Mon, 28 Jul 2025 18:24:38 +0000 (11:24 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 4 Aug 2025 02:48:04 +0000 (19:48 -0700)
This better matches the treatment of POSIX form,
e.g., ‘tail +Nc’ is now like ‘tail -c +N’ even when N is large.
* src/tail.c: Don’t include xstrtol.h.
(parse_obsolete_option): Treat numbers greater than UINTMAX_MAX as
if they are UINTMAX_MAX.  Parse the number by hand with saturating
arithmetic; nowadays that’s simpler than using xstrtoumax.  There
is no need for a diagnostic now, as the error cannot happen any more.
* tests/tail/tail.pl (obs-plus-c3): New test.

src/tail.c
tests/tail/tail.pl

index 8dd03a7c4d826973b20223d94baba80f78e32f4d..3efe0b7a3ee98b5ee57b8c7d118fbc339751ec02 100644 (file)
@@ -47,7 +47,6 @@
 #include "xbinary-io.h"
 #include "xdectoint.h"
 #include "xnanosleep.h"
-#include "xstrtol.h"
 
 #if HAVE_INOTIFY
 # include "hash.h"
@@ -2114,10 +2113,6 @@ tail_file (struct File_spec *f, uintmax_t n_files, uintmax_t n_units)
 static bool
 parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
 {
-  char const *p;
-  char const *n_string;
-  char const *n_string_end;
-  int default_count = DEFAULT_N_LINES;
   bool t_from_start;
   bool t_count_lines = true;
   bool t_forever = false;
@@ -2132,7 +2127,7 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
   int posix_ver = posix2_version ();
   bool obsolete_usage = posix_ver < 200112;
   bool traditional_usage = obsolete_usage || 200809 <= posix_ver;
-  p = argv[1];
+  char const *p = argv[1];
 
   switch (*p++)
     {
@@ -2159,14 +2154,16 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
       break;
     }
 
-  n_string = p;
-  while (c_isdigit (*p))
-    p++;
-  n_string_end = p;
+  uintmax_t n;
+  if (!c_isdigit (*p))
+    n = DEFAULT_N_LINES;
+  else
+    for (n = 0; c_isdigit (*p); p++)
+      n = ckd_mul (&n, n, 10) || ckd_add (&n, n, *p - '0') ? UINTMAX_MAX : n;
 
   switch (*p)
     {
-    case 'b': default_count *= 512; FALLTHROUGH;
+    case 'b': n = ckd_mul (&n, n, 512) ? UINTMAX_MAX : n; FALLTHROUGH;
     case 'c': t_count_lines = false; FALLTHROUGH;
     case 'l': p++; break;
     }
@@ -2180,13 +2177,7 @@ parse_obsolete_option (int argc, char * const *argv, uintmax_t *n_units)
   if (*p)
     return false;
 
-  if (n_string == n_string_end)
-    *n_units = default_count;
-  else if ((xstrtoumax (n_string, nullptr, 10, n_units, "b")
-            & ~LONGINT_INVALID_SUFFIX_CHAR)
-           != LONGINT_OK)
-    error (EXIT_FAILURE, errno, "%s: %s", _("invalid number"),
-           quote (argv[1]));
+  *n_units = n;
 
   /* Set globals.  */
   from_start = t_from_start;
index f972f3e2bfe81a2a796ac96d997697f2737d18ca..eeddc6b0743a93d964f318fdeeb570078ae01d99 100755 (executable)
@@ -29,6 +29,7 @@ my @tv = (
 #
 ['obs-plus-c1', '+2c', 'abcd', 'bcd', 0],
 ['obs-plus-c2', '+8c', 'abcd', '', 0],
+['obs-plus-c3', '+999999999999999999999999999999999999999999c', 'abcd', '', 0],
 ['obs-c3', '-1c', 'abcd', 'd', 0],
 ['obs-c4', '-9c', 'abcd', 'abcd', 0],
 ['obs-c5', '-12c', 'x' . ('y' x 12) . 'z', ('y' x 11) . 'z', 0],