Actually this wasn't quite right in the edge case
of continuous illegal multi-byte chars in the input.
I.e. the following didn't exit:

  tr '\0' '\303' < /dev/zero |
  (ulimit -v 8000 && src/fold) > /dev/full

The simple fix would be to move the "if ferror(stdout)"
check after the next_line: label, but then I noticed
that mcel_scan() leaves errno set to EILSEQ
which would result in a confusing error.

Note the EILSEQ is from the underlying mbrtowc/mbrtoc32 call.
I'm half wondering should mcel_scan() restore errno,
as err==-1 implies EILSEQ on glibc at least?

Anyway there aren't that many write calls,
so I used a small wrapper in the attached
to immediately exit upon any write error.

I'll update tests/fold/fold-zero-width.sh before pushing,
as the bounded memory check looks invalid there anyway,
so I'll check \0, \n, \303 in a loop there.

cheers,
Padraig
From 0b12eadb191dc5ffe3bc968c9f6d4c7006b31774 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Tue, 16 Sep 2025 16:45:02 +0100
Subject: [PATCH] fold: fix write error checks with invalid multi-byte input

* src/fold.c (write_out): A new helper to check all writes.
(fold-file): Use write_out() for all writes.
---
 src/fold.c | 30 ++++++++++++++++--------------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/fold.c b/src/fold.c
index a2ca953d4..708ef63de 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -133,6 +133,14 @@ adjust_column (size_t column, mcel_t g)
   return column;
 }
 
+static void
+write_out (char const *line, size_t line_len, bool newline)
+{
+  if (fwrite (line, sizeof (char), line_len, stdout) != line_len
+      || (newline && putchar ('\n') < 0))
+    write_error ();
+}
+
 /* Fold file FILENAME, or standard input if FILENAME is "-",
    to stdout, with maximum line length WIDTH.
    Return true if successful.  */
@@ -192,8 +200,7 @@ fold_file (char const *filename, size_t width)
             }
           if (g.ch == '\n')
             {
-              fwrite (line_out, sizeof (char), offset_out, stdout);
-              putchar ('\n');
+              write_out (line_out, offset_out, /*newline=*/ true);
               column = offset_out = 0;
               continue;
             }
@@ -226,8 +233,7 @@ fold_file (char const *filename, size_t width)
                     {
                       logical_end += space_length;
                       /* Found a blank.  Don't output the part after it. */
-                      fwrite (line_out, sizeof (char), logical_end, stdout);
-                      putchar ('\n');
+                      write_out (line_out, logical_end, /*newline=*/ true);
                       /* Move the remainder to the beginning of the next line.
                          The areas being copied here might overlap. */
                       memmove (line_out, line_out + logical_end,
@@ -253,8 +259,7 @@ fold_file (char const *filename, size_t width)
                   continue;
                 }
 
-              fwrite (line_out, sizeof (char), offset_out, stdout);
-              putchar ('\n');
+              write_out (line_out, offset_out, /*newline=*/ true);
               column = offset_out = 0;
               goto rescan;
             }
@@ -263,22 +268,19 @@ fold_file (char const *filename, size_t width)
              zero.  */
           if (sizeof line_out <= offset_out + g.len)
             {
-              fwrite (line_out, sizeof (char), offset_out, stdout);
+              write_out (line_out, offset_out, /*newline=*/ false);
               offset_out = 0;
             }
 
           memcpy (line_out + offset_out, p, g.len);
           offset_out += g.len;
         }
-      if (feof (istream))
-        break;
-
-      if (ferror (stdout))
-        write_error ();
-
       /* We read a full buffer of complete characters.  */
       offset_in = 0;
 
+      if (feof (istream))
+        break;
+
     next_line:;
     }
 
@@ -287,7 +289,7 @@ fold_file (char const *filename, size_t width)
     saved_errno = 0;
 
   if (offset_out)
-    fwrite (line_out, sizeof (char), offset_out, stdout);
+    write_out (line_out, offset_out, /*newline=*/ false);
 
   if (STREQ (filename, "-"))
     clearerr (istream);
-- 
2.50.1

Reply via email to