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