* 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


Reply via email to