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