On 2026-10-07 22:15, Collin Funk wrote:
Given the preceeding checks in that if statement, I suspect
Paul meant to write something like this:
...
-                  && 0 <= lseek (fd, hi_pos, SEEK_CUR))
+                  && 0 <= lseek (fd, hi_pos - current_pos, SEEK_CUR))

Yes, I should have written either that, or 'lseek (fd, hi_pos, SEEK_SET)'.

Looking into nearby code I see another bug. That "end_pos % (STP_BLKSIZE (&fstatus->st) + 
1)" is bogus: why add 1 to the blocksize? In the unlikely event that end_pos is a multiple of both 
page_size and of (STP_BLKSIZE (&fstatus->st) + 1), this will surely do the wrong thing.

Also, the two separate calls to lseek are awkward.

I installed the attached patch to fix all this. If someone has the energy to 
add a test case that would be nice.

Thanks, Scott, for reporting the bug.
From c3709a88f2dd60c4498547240ea147768bd75805 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Wed, 7 Oct 2026 23:14:19 -0700
Subject: [PATCH] wc: fix buffer boundary calculations

Problem reported by Scott Orme (Bug#82013).
* src/wc.c (wc): Fix calculations of file sizes vs page size
vs block size.
---
 NEWS      |  4 ++++
 THANKS.in |  1 +
 src/wc.c  | 27 ++++++++++++++++-----------
 3 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/NEWS b/NEWS
index 4126b4028..01b3e4dff 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   in GB18030 locales, and word counts in UTF-8 locales.
   [bug introduced in coreutils-9.5]
 
+  'wc -c' no longer miscounts bytes when stdin has a nonzero offset
+  into a seekable file whose size is a multiple of the page size.
+  [bug introduced in coreutils-8.24]
+
 ** New Features
 
   'env' and 'printenv' now support the --quoting-style option
diff --git a/THANKS.in b/THANKS.in
index 6a098897d..94669125b 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -577,6 +577,7 @@ Santiago Vila Doncel                [email protected]
 Savochkin Andrey Vladimirovich      [email protected]
 Scott Harrison                      [email protected]
 Scott Lurndal                       [email protected]
+Scott Orme                          [email protected]
 Sébastien Maret                     [email protected]
 Sergei Steshenko                    [email protected]
 Sergey Vlasov                       [email protected]
diff --git a/src/wc.c b/src/wc.c
index 272a905f5..d0aab941d 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -428,7 +428,7 @@ wc (int fd, char const *file_x, struct fstatus *fstatus)
             ;
           else if (end_pos % page_size)
             {
-              /* We only need special handling of /proc and /sys files etc.
+              /* We need special handling of /proc and /sys files etc. only
                  when they're a multiple of PAGE_SIZE.  In the common case
                  for files with st_size not a multiple of PAGE_SIZE,
                  it's more efficient and accurate to use st_size.
@@ -436,20 +436,25 @@ wc (int fd, char const *file_x, struct fstatus *fstatus)
                  Be careful here.  The current position may actually be
                  beyond the end of the file.  As in the example above.  */
 
-              bytes = end_pos < current_pos ? 0 : end_pos - current_pos;
-              if (bytes && 0 <= lseek (fd, bytes, SEEK_CUR))
-                skip_read = true;
-              else
-                bytes = 0;
+              if (current_pos < end_pos)
+                {
+                  bytes = end_pos - current_pos;
+                  skip_read = true;
+                }
             }
-          else
+          else if (0 < end_pos)
             {
-              off_t hi_pos = (end_pos
-                              - end_pos % (STP_BLKSIZE (&fstatus->st) + 1));
-              if (0 <= current_pos && current_pos < hi_pos
-                  && 0 <= lseek (fd, hi_pos, SEEK_CUR))
+              off_t ep_1 = end_pos - 1;
+              off_t hi_pos = ep_1 - ep_1 % STP_BLKSIZE (&fstatus->st);
+              if (current_pos < hi_pos)
                 bytes = hi_pos - current_pos;
             }
+
+          if (0 < bytes && lseek (fd, bytes, SEEK_CUR) < 0)
+            {
+              bytes = 0;
+              skip_read = false;
+            }
         }
 
       if (! skip_read)
-- 
2.53.0

Reply via email to