Subject: bug#8565: gzip/bzip2/output-hook support for split

From: Jim Meyering <[email-masked]>
Date: Sat, 30 Apr 2011 16:29:45 +0200

Karl Heuer wrote:
>>Karl, this is a good time to add the feature.
>>If you don't have time to handle the remaining bits (test,
>>doc and NEWS), let us know and we will take care of the rest.
>
> I'd better let you do it; I've got too many other items in my
> queue at the moment.

Hi Karl,

FYI, I've nearly finished with split-filter-related changes.
I posted a slightly modified version of your patch and a few
follow-on changes:

  http://thread.gmane.org/gmane.comp.gnu.coreutils.general/1138

And one more below.

I noticed that you've added a lot of "errno != EPIPE" tests.
And for a good reason, as you explained here:

  /* When filtering, closure of one pipe must not terminate the process,
     as there may still be other streams expecting input from us.  */
  sigemptyset (&newblocked);
  if (filter_command)
    {
      struct sigaction act;
      sigaction (SIGPIPE, NULL, &act);
      if (act.sa_handler != SIG_IGN)
        sigaddset (&newblocked, SIGPIPE);
    }
  sigprocmask (SIG_BLOCK, &newblocked, &oldblocked);

However, I presume you intended that change only
when filter_command is non-NULL.  I would not want to
suppress an error if somehow I accidentally ran a
classic split command with a non-default signal handler
and I sent it a SIGPIPE signal just as it was writing to
a file I cared about.

>From baf68e2dfb8ccfbfa797917e818551b12e129aef Mon Sep 17 00:00:00 2001
From: Jim Meyering <[email-masked]>
Date: Sat, 30 Apr 2011 16:08:04 +0200
Subject: [PATCH] split: factor out all new EPIPE tests; exempt EPIPE only
 with --filter

* src/split.c (ignorable): New function, to encapsulate EPIPE test.
Ignore failure with errno == EPIPE only when --filter=CMD is specified.
Replace all comparisons like && errno != EPIPE with
&& ! ignorable (errno).
---
 src/split.c |   28 ++++++++++++++++++----------
 1 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/split.c b/src/split.c
index 66f44fc..05315e6 100644
--- a/src/split.c
+++ b/src/split.c
@@ -131,6 +131,13 @@ static struct option const longopts[] =
   {NULL, 0, NULL, 0}
 };

+/* Return true if the errno value, ERR, is ignorable.  */
+static inline bool
+ignorable (int err)
+{
+  return filter_command && err == EPIPE;
+}
+
 static void
 set_suffix_length (uintmax_t n_units, enum Split_type split_type)
 {
@@ -346,7 +353,7 @@ create (const char *name)
 static void
 closeout (FILE *fp, int fd, pid_t pid, char const *name)
 {
-  if (fp != NULL && fclose (fp) != 0 && errno != EPIPE)
+  if (fp != NULL && fclose (fp) != 0 && ! ignorable (errno))
     error (EXIT_FAILURE, errno, "%s", name);
   if (fd >= 0)
     {
@@ -412,7 +419,7 @@ cwrite (bool new_file_flag, const char *bp, size_t bytes)
       if ((output_desc = create (outfile)) < 0)
         error (EXIT_FAILURE, errno, "%s", outfile);
     }
-  if (full_write (output_desc, bp, bytes) != bytes && errno != EPIPE)
+  if (full_write (output_desc, bp, bytes) != bytes && ! ignorable (errno))
     error (EXIT_FAILURE, errno, "%s", outfile);
 }

@@ -635,7 +642,7 @@ lines_chunk_split (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
                  large chunks from an existing file, so it's more efficient
                  to write out directly.  */
               if (full_write (STDOUT_FILENO, bp, to_write) != to_write
-                  && errno != EPIPE)
+                  && ! ignorable (errno))
                 error (EXIT_FAILURE, errno, "%s", _("write error"));
             }
           else
@@ -698,7 +705,8 @@ bytes_chunk_extract (uintmax_t k, uintmax_t n, char *buf, size_t bufsize,
         error (EXIT_FAILURE, errno, "%s", infile);
       else if (n_read == 0)
         break; /* eof.  */
-      if (full_write (STDOUT_FILENO, buf, n_read) != n_read && errno != EPIPE)
+      if (full_write (STDOUT_FILENO, buf, n_read) != n_read
+          && ! ignorable (errno))
         error (EXIT_FAILURE, errno, "%s", quote ("-"));
       start += n_read;
     }
@@ -772,7 +780,7 @@ ofile_open (of_t *files, size_t i_check, size_t nfiles)
                 error (EXIT_FAILURE, errno, "%s", files[i_check].of_name);
             }

-          if (fclose (files[i_reopen].ofile) != 0 && errno != EPIPE)
+          if (fclose (files[i_reopen].ofile) != 0 && ! ignorable (errno))
             error (EXIT_FAILURE, errno, "%s", files[i_reopen].of_name);
           files[i_reopen].ofile = NULL;
           files[i_reopen].ofd = OFD_APPEND;
@@ -856,11 +864,11 @@ lines_rr (uintmax_t k, uintmax_t n, char *buf, size_t bufsize)
               if (line_no == k && unbuffered)
                 {
                   if (full_write (STDOUT_FILENO, bp, to_write) != to_write
-                      && errno != EPIPE)
+                      && ! ignorable (errno))
                     error (EXIT_FAILURE, errno, "%s", _("write error"));
                 }
               else if (line_no == k && fwrite (bp, to_write, 1, stdout) != 1
-                       && errno != EPIPE)
+                       && ! ignorable (errno))
                 {
                   clearerr (stdout); /* To silence close_stdout().  */
                   error (EXIT_FAILURE, errno, "%s", _("write error"));
@@ -877,15 +885,15 @@ lines_rr (uintmax_t k, uintmax_t n, char *buf, size_t bufsize)
                   /* 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
-                      && errno != EPIPE)
+                      && ! ignorable (errno))
                     error (EXIT_FAILURE, errno, "%s", files[i_file].of_name);
                 }
               else if (fwrite (bp, to_write, 1, files[i_file].ofile) != 1
-                       && errno != EPIPE)
+                       && ! ignorable (errno))
                 error (EXIT_FAILURE, errno, "%s", files[i_file].of_name);
               if (file_limit)
                 {
-                  if (fclose (files[i_file].ofile) != 0 && errno != EPIPE)
+                  if (fclose (files[i_file].ofile) != 0 && ! ignorable (errno))
                     error (EXIT_FAILURE, errno, "%s", files[i_file].of_name);
                   files[i_file].ofile = NULL;
                   files[i_file].ofd = OFD_APPEND;
--
1.7.5.134.g1c08b