fold: fix crash when reading from pseudo-devices (#11314)

* fold: fix crash when reading from pseudo-devices

squashme: wraparound struct for fold
squashme: fix crash when reading from pseudo-devices
Fixes #11291

* tests/fold: add tests to read from pseudo device streams

* tests/fold: kill child process before making asserts

* tests/fold: test fold on pseudo devices only for bsds and linux
This commit is contained in:
Himadri Bhattacharjee
2026-06-07 14:55:52 +05:30
committed by GitHub
parent e55b67489b
commit e6a6cb6399
2 changed files with 50 additions and 52 deletions
+34 -52
View File
@@ -194,64 +194,46 @@ fn fold_file_bytewise<T: Read, W: Write>(
let mut line = Vec::new();
loop {
if file
.read_until(NL, &mut line)
.map_err_context(|| translate!("fold-error-readline"))?
== 0
{
// Ensures that our line always has enough bytes to either
// come across a newline, a whitespace or just wrap around.
while width > line.len() {
let buf = file
.fill_buf()
.map_err_context(|| translate!("fold-error-readline"))?;
if buf.is_empty() {
break;
}
line.extend_from_slice(buf);
let len = buf.len();
file.consume(len);
}
// The width exceeds the line read so far if we have
// reached EOF.
if width > line.len() {
output.write_all(&line)?;
break;
}
if line == [NL] {
let chunk = &line[..width];
let newline_end = chunk.iter().position(|c| NL.eq(c)).map(|v| v + 1);
let space_end = chunk
.iter()
.rposition(|c| spaces && c.is_ascii_whitespace() && !CR.eq(c))
.map(|v| v + 1);
let end = newline_end.or(space_end).unwrap_or(width);
let slice = &line[..end];
output.write_all(slice)?;
let slice_ends_without_newline = line[end - 1] != NL;
let no_newline_follows_slice = line.get(end).is_some_and(|c| *c != NL);
if slice_ends_without_newline && no_newline_follows_slice {
output.write_all(&[NL])?;
line.clear();
continue;
}
let len = line.len();
let mut i = 0;
while i < len {
let width = if len - i >= width { width } else { len - i };
let slice = {
let slice = &line[i..i + width];
if spaces && i + width < len {
match slice
.iter()
.enumerate()
.rev()
.find(|(_, c)| c.is_ascii_whitespace() && **c != CR)
{
Some((m, _)) => &slice[..=m],
None => slice,
}
} else {
slice
}
};
// Don't duplicate trailing newlines: if the slice is "\n", the
// previous iteration folded just before the end of the line and
// has already printed this newline.
if slice == [NL] {
break;
}
i += slice.len();
let at_eol = i >= len;
if at_eol {
output.write_all(slice)?;
} else {
output.write_all(slice)?;
output.write_all(&[NL])?;
}
}
line.clear();
line.drain(..end);
}
Ok(())
}
+16
View File
@@ -889,6 +889,22 @@ fn test_bytewise_carriage_return_is_not_word_boundary() {
.succeeds()
.stdout_is("fizz\rb\nuzz\rfi\nzzbuzz"); // spell-checker:disable-line
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))]
#[test]
fn test_bytewise_read_from_pseudo_device() {
let mut child = new_ucmd!().arg("-b").arg("/dev/zero").run_no_wait();
child.make_assertion_with_delay(100).is_alive();
child
.kill()
.make_assertion()
.with_all_output()
.stdout_contains_bytes(b"\x00\x0a")
.no_stderr();
}
#[test]
fn test_obsolete_syntax() {
new_ucmd!()