* src/split.c (of_t): Add the done member.
(lines_rr): Set the done member for the file if the filter has an
ignorable error. Don't attempt subsequent writes if the done member for
the file is set.
* tests/split/filter.sh: Add a test case.
* NEWS: Mention the bug fix.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-NO8YC6GS.
---
NEWS | 5 +++++
src/split.c | 22 ++++++++++++----------
tests/split/filter.sh | 14 ++++++++++++++
3 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/NEWS b/NEWS
index ffe134e76..9960c2299 100644
--- a/NEWS
+++ b/NEWS
@@ -205,6 +205,11 @@ GNU coreutils NEWS -*-
outline -*-
with a newline.
[bug introduced in coreutils-9.2]
+ 'split --number=r/N --filter' no longer stops reading as soon as any one of
+ the filters stops accepting input. Now it continues until all filters stop
+ accepting input or the entire input is processed.
+ [bug introduced with --filter option in coreutils-8.13]
+
'stty' again rejects empty speed arguments.
Previously such arguments were accepted as value 0.
[bug introduced in coreutils-9.8]
diff --git a/src/split.c b/src/split.c
index 71a097264..ea410dc9f 100644
--- a/src/split.c
+++ b/src/split.c
@@ -1131,6 +1131,9 @@ typedef struct of_info
int ofd;
FILE *ofile;
pid_t opid;
+
+ /* True if the filter no longer accepts input. */
+ bool done;
} of_t;
enum
@@ -1250,6 +1253,7 @@ lines_rr (intmax_t k, intmax_t n, char *buf, idx_t
bufsize, of_t **filesp)
files[i_file].ofd = OFD_NEW;
files[i_file].ofile = NULL;
files[i_file].opid = 0;
+ files[i_file].done = false;
}
i_file = 0;
file_limit = false;
@@ -1299,22 +1303,20 @@ lines_rr (intmax_t k, intmax_t n, char *buf, idx_t
bufsize, of_t **filesp)
{
/* Secure file descriptor. */
file_limit |= ofile_open (files, i_file, n);
- if (unbuffered)
+ if (! files[i_file].done)
{
/* Note writing to fd, rather than flushing the FILE gives
an 8% performance benefit, due to reduced data copying.
*/
- if (full_write (files[i_file].ofd, bp, to_write) != to_write
- && ! ignorable (errno))
+ if (unbuffered
+ ? full_write (files[i_file].ofd, bp, to_write) ==
to_write
+ : fwrite (bp, to_write, 1, files[i_file].ofile) == 1)
+ wrote = true;
+ else if (ignorable (errno))
+ files[i_file].done = true;
+ else
error (EXIT_FAILURE, errno, "%s",
quotef (files[i_file].of_name));
}
- else if (fwrite (bp, to_write, 1, files[i_file].ofile) != 1
- && ! ignorable (errno))
- error (EXIT_FAILURE, errno, "%s",
- quotef (files[i_file].of_name));
-
- if (! ignorable (errno))
- wrote = true;
if (file_limit)
{
diff --git a/tests/split/filter.sh b/tests/split/filter.sh
index ba8f5e447..90ee3a5f2 100755
--- a/tests/split/filter.sh
+++ b/tests/split/filter.sh
@@ -71,6 +71,20 @@ for mode in '' 'r/'; do
done
done
+seq 100 >in &&
+echo 1 >exp-xaa &&
+sed -n 'n;p' in >exp-xab || framework_failure_
+for mode in '' -u; do
+ rm -f x?? || framework_failure_
+ split $mode -n r/2 \
+ --filter='case $FILE in xaa) head -n1 ;; *) cat ;; esac >$FILE' in \
+ >out 2>err || fail=1
+ compare /dev/null out || fail=1
+ compare /dev/null err || fail=1
+ compare exp-xaa xaa || fail=1
+ compare exp-xab xab || fail=1
+done
+
# Ensure that "endless" input _is_ processed for unbounded number of filters
for buf in 1000 1000000; do
returns_ 124 timeout .5 sh -c \
--
2.56.0