Subject: Fix 'shuf -n 0x' and similar problems

From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 15:06:18 -0700

I installed into GNU coreutils the attached series of patches, to fix 
problems like 'shuf -n 0x' where shuf did not diagnose the trailing 'x', 
along with some other stuff I noticed while looking into the problem.
>From d4cbfaeca1e838a0d0373adfbd133a9f5eaa8e87 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 11:34:56 -0700
Subject: [PATCH 1/5] build: re-enable type-limits checking

* configure.ac: When --enable-gcc-warnings is used, omit
-Wno-type-limits.  The need for -Wno-type-limits has passed, now
that intprops.h uses builtin primitives for GCC 5 and later, given
that recent GCCs issue type-limits warnings only for non-constant
expressions.  --enable-gcc-warnings is not intended for use with
old compilers, so we can drop -Wno-type-limits now.
---
 configure.ac | 2 --
 1 file changed, 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index d90c710e3..292ae0bf2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -134,7 +134,6 @@ if test "$gl_gcc_warnings" = yes; then
   nw="$nw -Wswitch-enum"            # Too many warnings for now
   nw="$nw -Wswitch-default"         # Too many warnings for now
   nw="$nw -Wstack-protector"        # not worth working around
-  nw="$nw -Wtype-limits"            # False alarms for portable code
   nw="$nw -Wformat-overflow=2"      # False alarms due to GCC bug 80776
   nw="$nw -Wformat-truncation=2"    # False alarm in ls.c, probably related
   # things I might fix soon:
@@ -155,7 +154,6 @@ if test "$gl_gcc_warnings" = yes; then
     gl_WARN_ADD([$w])
   done
   gl_WARN_ADD([-Wno-sign-compare])     # Too many warnings for now
-  gl_WARN_ADD([-Wno-type-limits])      # False alarms for portable code
   gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
   gl_WARN_ADD([-Wno-format-nonliteral])
 
-- 
2.21.0

>From 6778871f67c6a66aacc76b0a63ff26c8b72dce87 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 12:52:52 -0700
Subject: [PATCH 2/5] =?UTF-8?q?build:=20don=E2=80=99t=20worry=20about=20lo?=
 =?UTF-8?q?gical-op=20checking?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* configure.ac: Remove code tailoring --enable-gcc-warnings
to GCC 4.7 and earlier, as developers no longer need to worry
about GCCs that old.
---
 configure.ac | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/configure.ac b/configure.ac
index 292ae0bf2..18c5a99bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,7 +128,6 @@ if test "$gl_gcc_warnings" = yes; then
   nw="$nw -Wunreachable-code"       # Too many warnings for now
   nw="$nw -Wpadded"                 # Our structs are not padded
   nw="$nw -Wredundant-decls"        # openat.h declares e.g., mkdirat
-  nw="$nw -Wlogical-op"             # Too many warnings until GCC 4.8.0
   nw="$nw -Wformat-nonliteral"      # who.c and pinky.c strftime uses
   nw="$nw -Wnested-externs"         # use of XARGMATCH/verify_function__
   nw="$nw -Wswitch-enum"            # Too many warnings for now
@@ -157,11 +156,6 @@ if test "$gl_gcc_warnings" = yes; then
   gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
   gl_WARN_ADD([-Wno-format-nonliteral])
 
-  # Enable this warning only with gcc-4.8 and newer.  Before that
-  # bounds checking as done in truncate.c was incorrectly flagged.
-  # See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43772
-  gl_GCC_VERSION_IFELSE([4], [8], [gl_WARN_ADD([-Wlogical-op])])
-
   # clang is unduly picky about some things.
   AC_CACHE_CHECK([whether the compiler is clang], [utils_cv_clang],
     [AC_COMPILE_IFELSE(
-- 
2.21.0

>From 95c705bdc03c89cdf774f90ee68452ebd46b400a Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 12:58:07 -0700
Subject: [PATCH 3/5] shuf: improve randperm overflow checking
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* gl/lib/randperm.c: Include randperm.h first, since it’s the API.
Include stdint.h, count-leading-zeros.h, verify.h.
(floor_lg): Rename from ceil_log (which was not actually
implementing the ceiling!) and implement the floor using
count_leading_zeros.
(randperm_bound): Use floor_lg, not ceil_log.  Use uintmax_t
instead of size_t in case the size gets large on a 32-bit host.
* gl/modules/randperm (Depends-on): Add count-leading-zeros, stdint.
---
 gl/lib/randperm.c   | 27 ++++++++++++++++-----------
 gl/modules/randperm |  2 ++
 2 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/gl/lib/randperm.c b/gl/lib/randperm.c
index ce69222e9..b079aba33 100644
--- a/gl/lib/randperm.c
+++ b/gl/lib/randperm.c
@@ -19,24 +19,29 @@
 
 #include <config.h>
 
-#include "hash.h"
 #include "randperm.h"
 
 #include <limits.h>
+#include <stdint.h>
 #include <stdlib.h>
 
+#include "count-leading-zeros.h"
+#include "hash.h"
+#include "verify.h"
 #include "xalloc.h"
 
-/* Return the ceiling of the log base 2 of N.  If N is zero, return
-   an unspecified value.  */
+/* Return the floor of the log base 2 of N.  If N is zero, return -1.  */
 
-static size_t _GL_ATTRIBUTE_CONST
-ceil_lg (size_t n)
+static int _GL_ATTRIBUTE_CONST
+floor_lg (size_t n)
 {
-  size_t b = 0;
-  for (n--; n != 0; n /= 2)
-    b++;
-  return b;
+  verify (SIZE_WIDTH <= ULLONG_WIDTH);
+  return (n == 0 ? -1
+          : SIZE_WIDTH <= UINT_WIDTH
+          ? UINT_WIDTH - 1 - count_leading_zeros (n)
+          : SIZE_WIDTH <= ULONG_WIDTH
+          ? ULONG_WIDTH - 1 - count_leading_zeros_l (n)
+          : ULLONG_WIDTH - 1 - count_leading_zeros_ll (n));
 }
 
 /* Return an upper bound on the number of random bytes needed to
@@ -48,10 +53,10 @@ randperm_bound (size_t h, size_t n)
 {
   /* Upper bound on number of bits needed to generate the first number
      of the permutation.  */
-  size_t lg_n = ceil_lg (n);
+  uintmax_t lg_n = floor_lg (n) + 1;
 
   /* Upper bound on number of bits needed to generated the first H elements.  */
-  size_t ar = lg_n * h;
+  uintmax_t ar = lg_n * h;
 
   /* Convert the bit count to a byte count.  */
   size_t bound = (ar + CHAR_BIT - 1) / CHAR_BIT;
diff --git a/gl/modules/randperm b/gl/modules/randperm
index 1fa261290..e3b347140 100644
--- a/gl/modules/randperm
+++ b/gl/modules/randperm
@@ -6,7 +6,9 @@ lib/randperm.c
 lib/randperm.h
 
 Depends-on:
+count-leading-zeros
 randint
+stdint
 xalloc
 hash
 
-- 
2.21.0

>From 9e324d0ed790a455078da0f593b0502c0533f7c5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 14:31:52 -0700
Subject: [PATCH 4/5] stdbuf: improve size checking
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* bootstrap.conf (gnulib_modules): Add minmax.
* src/libstdbuf.c: Include stdint.h, minmax.h.
(apply_mode): Don’t assume SIZE_MAX <= ULONG_MAX.
Improve checking for invalid sizes.
---
 bootstrap.conf  |  1 +
 src/libstdbuf.c | 37 ++++++++++++++++++++-----------------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/bootstrap.conf b/bootstrap.conf
index 018bc4eb3..de795757c 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -165,6 +165,7 @@ gnulib_modules="
   memcmp2
   mempcpy
   memrchr
+  minmax
   mgetgroups
   mkancesdirs
   mkdir
diff --git a/src/libstdbuf.c b/src/libstdbuf.c
index 4d8131de5..999e365ae 100644
--- a/src/libstdbuf.c
+++ b/src/libstdbuf.c
@@ -18,7 +18,9 @@
 
 #include <config.h>
 #include <stdio.h>
+#include <stdint.h>
 #include "system.h"
+#include "minmax.h"
 
 /* Deactivate config.h's "rpl_"-prefixed definition of malloc,
    since we don't link gnulib here, and the replacement isn't
@@ -90,7 +92,7 @@ apply_mode (FILE *stream, const char *mode)
 {
   char *buf = NULL;
   int setvbuf_mode;
-  size_t size = 0;
+  uintmax_t size = 0;
 
   if (*mode == '0')
     setvbuf_mode = _IONBF;
@@ -99,27 +101,28 @@ apply_mode (FILE *stream, const char *mode)
   else
     {
       setvbuf_mode = _IOFBF;
-      verify (SIZE_MAX <= ULONG_MAX);
-      size = strtoul (mode, NULL, 10);
-      if (size > 0)
-        {
-          if (!(buf = malloc (size)))   /* will be freed by fclose()  */
-            {
-              /* We could defer the allocation to libc, however since
-                 glibc currently ignores the combination of NULL buffer
-                 with non zero size, we'll fail here.  */
-              fprintf (stderr,
-                       _("failed to allocate a %" PRIuMAX
-                         " byte stdio buffer\n"), (uintmax_t) size);
-              return;
-            }
-        }
-      else
+      char *mode_end;
+      size = strtoumax (mode, &mode_end, 10);
+      if (size == 0 || *mode_end)
         {
           fprintf (stderr, _("invalid buffering mode %s for %s\n"),
                    mode, fileno_to_name (fileno (stream)));
           return;
         }
+
+      buf = size <= SIZE_MAX ? malloc (size) : NULL;
+      if (!buf)
+        {
+          /* We could defer the allocation to libc, however since
+             glibc currently ignores the combination of NULL buffer
+             with non zero size, we'll fail here.  */
+          fprintf (stderr,
+                   _("failed to allocate a %" PRIuMAX
+                     " byte stdio buffer\n"),
+                   size);
+          return;
+        }
+      /* buf will be freed by fclose.  */
     }
 
   if (setvbuf (stream, buf, setvbuf_mode, size) != 0)
-- 
2.21.0

>From 931d7173277f803f96ebb46605c0c6d2684fa98d Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 22 Oct 2019 14:55:24 -0700
Subject: [PATCH 5/5] all: improve parsing of numeric arguments

This addresses a longstanding "update all callers" FIXME in
lib/xstrtol.c, by having programs check that numbers do not
have unknown suffixes.  The problem was also reported for
'shuf' by my student Maggie Huang while reimplementing a shuf
subset in Python as an exercise in UCLA Computer Science 35L:
https://web.cs.ucla.edu/classes/fall19/cs35L/assign/assign3.html
This patch also improves the portability of the code to unusual
platforms where ULONG_MAX < SIZE_MAX.
* NEWS: Mention user-visible changes.
* src/chgrp.c (parse_group):
* src/chroot.c (parse_additional_groups):
* src/du.c (main):
* src/install.c (get_ids):
* src/join.c (string_to_join_field):
* src/ls.c (decode_switches):
* src/md5sum.c (split_3):
* src/shuf.c (main):
* src/sort.c (specify_nthreads):
* src/uniq.c (size_opt, main):
Use uintmax_t instead of unsigned long, for portability
to oddball platforms where unsigned long is not wide enough.
* src/du.c (main):
* src/expr.c (mpz_init_set_str) [!HAVE_GMP]:
* src/install.c (get_ids):
* src/ls.c (decode_switches):
* src/mknod.c (main):
* src/ptx.c (main):
* src/shuf.c (main):
* src/sort.c (specify_nmerge, specify_nthreads):
Reject numbers with suffixes.
* src/md5sum.c (split_3): Simplify.
---
 NEWS          |  7 +++++++
 src/chgrp.c   |  4 ++--
 src/chroot.c  |  5 +++--
 src/du.c      |  8 ++++----
 src/expr.c    |  2 +-
 src/install.c |  8 ++++----
 src/join.c    |  5 ++---
 src/ls.c      | 19 ++++++++-----------
 src/md5sum.c  | 21 +++++++++------------
 src/mknod.c   |  4 ++--
 src/ptx.c     |  4 ++--
 src/shuf.c    |  4 ++--
 src/sort.c    |  6 +++---
 src/uniq.c    |  9 ++++-----
 14 files changed, 53 insertions(+), 53 deletions(-)

diff --git a/NEWS b/NEWS
index 476c02aed..300a799ae 100644
--- a/NEWS
+++ b/NEWS
@@ -43,6 +43,13 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** Changes in behavior
 
+  Several programs now check that numbers end properly.  For example,
+  'du -d 1x' now reports an error instead of silently ignoring the 'x'.
+  Affected programs and options include du -d, expr's numeric operands
+  on non-GMP builds, install -g and -o, ls's TABSIZE environment
+  variable, mknod b and c, ptx -g and -w, shuf -n, and sort --batch-size
+  and --parallel.
+
   date now parses military time zones in accordance with common usage:
     "A" to "M"  are equivalent to UTC+1 to UTC+12
     "N" to "Y"  are equivalent to UTC-1 to UTC-12
diff --git a/src/chgrp.c b/src/chgrp.c
index dfd39ae5e..b7393eef6 100644
--- a/src/chgrp.c
+++ b/src/chgrp.c
@@ -87,8 +87,8 @@ parse_group (const char *name)
         gid = grp->gr_gid;
       else
         {
-          unsigned long int tmp;
-          if (! (xstrtoul (name, NULL, 10, &tmp, "") == LONGINT_OK
+          uintmax_t tmp;
+          if (! (xstrtoumax (name, NULL, 10, &tmp, "") == LONGINT_OK
                  && tmp <= GID_T_MAX))
             die (EXIT_FAILURE, 0, _("invalid group: %s"),
                  quote (name));
diff --git a/src/chroot.c b/src/chroot.c
index 42ea5fa92..ea55e7527 100644
--- a/src/chroot.c
+++ b/src/chroot.c
@@ -106,9 +106,10 @@ parse_additional_groups (char const *groups, GETGROUPS_T **pgids,
   for (tmp = strtok (buffer, ","); tmp; tmp = strtok (NULL, ","))
     {
       struct group *g;
-      unsigned long int value;
+      uintmax_t value;
 
-      if (xstrtoul (tmp, NULL, 10, &value, "") == LONGINT_OK && value <= MAXGID)
+      if (xstrtoumax (tmp, NULL, 10, &value, "") == LONGINT_OK
+          && value <= MAXGID)
         {
           while (isspace (to_uchar (*tmp)))
             tmp++;
diff --git a/src/du.c b/src/du.c
index 43c348159..a44b22e46 100644
--- a/src/du.c
+++ b/src/du.c
@@ -807,12 +807,12 @@ main (int argc, char **argv)
 
         case 'd':		/* --max-depth=N */
           {
-            unsigned long int tmp_ulong;
-            if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
-                && tmp_ulong <= SIZE_MAX)
+            uintmax_t tmp;
+            if (xstrtoumax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
+                && tmp <= SIZE_MAX)
               {
                 max_depth_specified = true;
-                max_depth = tmp_ulong;
+                max_depth = tmp;
               }
             else
               {
diff --git a/src/expr.c b/src/expr.c
index 3ce61b203..08d7277ec 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -60,7 +60,7 @@ static void mpz_init_set_ui (mpz_t z, unsigned long int i) { z[0] = i; }
 static int
 mpz_init_set_str (mpz_t z, char *s, int base)
 {
-  return xstrtoimax (s, NULL, base, z, NULL) == LONGINT_OK ? 0 : -1;
+  return xstrtoimax (s, NULL, base, z, "") == LONGINT_OK ? 0 : -1;
 }
 static void
 mpz_add (mpz_t r, mpz_t a0, mpz_t b0)
diff --git a/src/install.c b/src/install.c
index bde69c994..8b02be965 100644
--- a/src/install.c
+++ b/src/install.c
@@ -583,8 +583,8 @@ get_ids (void)
       pw = getpwnam (owner_name);
       if (pw == NULL)
         {
-          unsigned long int tmp;
-          if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
+          uintmax_t tmp;
+          if (xstrtoumax (owner_name, NULL, 0, &tmp, "") != LONGINT_OK
               || UID_T_MAX < tmp)
             die (EXIT_FAILURE, 0, _("invalid user %s"),
                  quote (owner_name));
@@ -602,8 +602,8 @@ get_ids (void)
       gr = getgrnam (group_name);
       if (gr == NULL)
         {
-          unsigned long int tmp;
-          if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
+          uintmax_t tmp;
+          if (xstrtoumax (group_name, NULL, 0, &tmp, "") != LONGINT_OK
               || GID_T_MAX < tmp)
             die (EXIT_FAILURE, 0, _("invalid group %s"),
                  quote (group_name));
diff --git a/src/join.c b/src/join.c
index dd0ce42bc..64156a22c 100644
--- a/src/join.c
+++ b/src/join.c
@@ -839,10 +839,9 @@ static size_t
 string_to_join_field (char const *str)
 {
   size_t result;
-  unsigned long int val;
-  verify (SIZE_MAX <= ULONG_MAX);
+  uintmax_t val;
 
-  strtol_error s_err = xstrtoul (str, NULL, 10, &val, "");
+  strtol_error s_err = xstrtoumax (str, NULL, 10, &val, "");
   if (s_err == LONGINT_OVERFLOW || (s_err == LONGINT_OK && SIZE_MAX < val))
     val = SIZE_MAX;
   else if (s_err != LONGINT_OK || val == 0)
diff --git a/src/ls.c b/src/ls.c
index 034087f26..d02653b48 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -1902,18 +1902,15 @@ decode_switches (int argc, char **argv)
     tabsize = 8;
     if (p)
       {
-        unsigned long int tmp_ulong;
-        if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
-            && tmp_ulong <= SIZE_MAX)
-          {
-            tabsize = tmp_ulong;
-          }
+        uintmax_t tmp;
+        if (xstrtoumax (p, NULL, 0, &tmp, "") == LONGINT_OK
+            && tmp <= SIZE_MAX)
+          tabsize = tmp;
         else
-          {
-            error (0, 0,
-             _("ignoring invalid tab size in environment variable TABSIZE: %s"),
-                   quote (p));
-          }
+          error (0, 0,
+                 _("ignoring invalid tab size in environment variable TABSIZE:"
+                   " %s"),
+                 quote (p));
       }
   }
 
diff --git a/src/md5sum.c b/src/md5sum.c
index f75b6de02..de847ccb2 100644
--- a/src/md5sum.c
+++ b/src/md5sum.c
@@ -450,26 +450,23 @@ split_3 (char *s, size_t s_len,
       ptrdiff_t algo = argmatch (algo_name, algorithm_out_string, NULL, 0);
       if (algo < 0)
         return false;
-      else
-        b2_algorithm = algo;
+      b2_algorithm = algo;
       if (openssl_format)
         s[--i] = '(';
 
+      b2_length = blake2_max_len[b2_algorithm] * 8;
       if (length_specified)
         {
-          unsigned long int tmp_ulong;
-          if (xstrtoul (s + i, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
-              && 0 < tmp_ulong && tmp_ulong <= blake2_max_len[b2_algorithm] * 8
-              && tmp_ulong % 8 == 0)
-            b2_length = tmp_ulong;
-          else
+          uintmax_t length;
+          char *siend;
+          if (! (xstrtoumax (s + i, &siend, 0, &length, NULL) == LONGINT_OK
+                 && 0 < length && length <= b2_length
+                 && length % 8 == 0))
             return false;
 
-          while (ISDIGIT (s[i]))
-            ++i;
+          i = siend - s;
+          b2_length = length;
         }
-      else
-        b2_length = blake2_max_len[b2_algorithm] * 8;
 
       digest_hex_bytes = b2_length / 4;
 #endif
diff --git a/src/mknod.c b/src/mknod.c
index 7f7cbc739..3f430af46 100644
--- a/src/mknod.c
+++ b/src/mknod.c
@@ -230,12 +230,12 @@ main (int argc, char **argv)
         uintmax_t i_major, i_minor;
         dev_t device;
 
-        if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
+        if (xstrtoumax (s_major, NULL, 0, &i_major, "") != LONGINT_OK
             || i_major != (major_t) i_major)
           die (EXIT_FAILURE, 0,
                _("invalid major device number %s"), quote (s_major));
 
-        if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
+        if (xstrtoumax (s_minor, NULL, 0, &i_minor, "") != LONGINT_OK
             || i_minor != (minor_t) i_minor)
           die (EXIT_FAILURE, 0,
                _("invalid minor device number %s"), quote (s_minor));
diff --git a/src/ptx.c b/src/ptx.c
index 3f2344055..779b78c90 100644
--- a/src/ptx.c
+++ b/src/ptx.c
@@ -1940,7 +1940,7 @@ main (int argc, char **argv)
         case 'g':
           {
             intmax_t tmp;
-            if (! (xstrtoimax (optarg, NULL, 0, &tmp, NULL) == LONGINT_OK
+            if (! (xstrtoimax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
                    && 0 < tmp && tmp <= PTRDIFF_MAX))
               die (EXIT_FAILURE, 0, _("invalid gap width: %s"),
                    quote (optarg));
@@ -1967,7 +1967,7 @@ main (int argc, char **argv)
         case 'w':
           {
             intmax_t tmp;
-            if (! (xstrtoimax (optarg, NULL, 0, &tmp, NULL) == LONGINT_OK
+            if (! (xstrtoimax (optarg, NULL, 0, &tmp, "") == LONGINT_OK
                    && 0 < tmp && tmp <= PTRDIFF_MAX))
               die (EXIT_FAILURE, 0, _("invalid line width: %s"),
                    quote (optarg));
diff --git a/src/shuf.c b/src/shuf.c
index 6a1aa0158..37a5a5554 100644
--- a/src/shuf.c
+++ b/src/shuf.c
@@ -440,8 +440,8 @@ main (int argc, char **argv)
 
       case 'n':
         {
-          unsigned long int argval;
-          strtol_error e = xstrtoul (optarg, NULL, 10, &argval, NULL);
+          uintmax_t argval;
+          strtol_error e = xstrtoumax (optarg, NULL, 10, &argval, "");
 
           if (e == LONGINT_OK)
             head_lines = MIN (head_lines, argval);
diff --git a/src/sort.c b/src/sort.c
index 360a1f140..b2336894f 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -1330,7 +1330,7 @@ specify_nmerge (int oi, char c, char const *s)
 {
   uintmax_t n;
   struct rlimit rlimit;
-  enum strtol_error e = xstrtoumax (s, NULL, 10, &n, NULL);
+  enum strtol_error e = xstrtoumax (s, NULL, 10, &n, "");
 
   /* Try to find out how many file descriptors we'll be able
      to open.  We need at least nmerge + 3 (STDIN_FILENO,
@@ -1443,8 +1443,8 @@ specify_sort_size (int oi, char c, char const *s)
 static size_t
 specify_nthreads (int oi, char c, char const *s)
 {
-  unsigned long int nthreads;
-  enum strtol_error e = xstrtoul (s, NULL, 10, &nthreads, "");
+  uintmax_t nthreads;
+  enum strtol_error e = xstrtoumax (s, NULL, 10, &nthreads, "");
   if (e == LONGINT_OVERFLOW)
     return SIZE_MAX;
   if (e != LONGINT_OK)
diff --git a/src/uniq.c b/src/uniq.c
index 9600ec094..836473624 100644
--- a/src/uniq.c
+++ b/src/uniq.c
@@ -240,10 +240,9 @@ strict_posix2 (void)
 static size_t
 size_opt (char const *opt, char const *msgid)
 {
-  unsigned long int size;
-  verify (SIZE_MAX <= ULONG_MAX);
+  uintmax_t size;
 
-  switch (xstrtoul (opt, NULL, 10, &size, ""))
+  switch (xstrtoumax (opt, NULL, 10, &size, ""))
     {
     case LONGINT_OK:
     case LONGINT_OVERFLOW:
@@ -539,10 +538,10 @@ main (int argc, char **argv)
         {
         case 1:
           {
-            unsigned long int size;
+            uintmax_t size;
             if (optarg[0] == '+'
                 && ! strict_posix2 ()
-                && xstrtoul (optarg, NULL, 10, &size, "") == LONGINT_OK
+                && xstrtoumax (optarg, NULL, 10, &size, "") == LONGINT_OK
                 && size <= SIZE_MAX)
               skip_chars = size;
             else if (nfiles == 2)
-- 
2.21.0