diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 76ef483bf31..8c028b05613 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -194,64 +194,46 @@ fn fold_file_bytewise( let mut line = Vec::new(); loop { - if file - .read_until(NL, &mut line) - .map_err_context(|| translate!("fold-error-readline"))? - == 0 - { - break; + // 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); } - if line == [NL] { - output.write_all(&[NL])?; - line.clear(); - continue; + // The width exceeds the line read so far if we have + // reached EOF. + if width > line.len() { + output.write_all(&line)?; + break; } - 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; - } + let chunk = &line[..width]; + let newline_end = chunk.iter().position(|c| NL.eq(c)).map(|v| v + 1); - i += slice.len(); + let space_end = chunk + .iter() + .rposition(|c| spaces && c.is_ascii_whitespace() && !CR.eq(c)) + .map(|v| v + 1); - let at_eol = i >= len; + let end = newline_end.or(space_end).unwrap_or(width); + let slice = &line[..end]; - if at_eol { - output.write_all(slice)?; - } else { - output.write_all(slice)?; - output.write_all(&[NL])?; - } + 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(); + line.drain(..end); } - Ok(()) } diff --git a/tests/by-util/test_fold.rs b/tests/by-util/test_fold.rs index c371ba4ca58..2d863ffa43f 100644 --- a/tests/by-util/test_fold.rs +++ b/tests/by-util/test_fold.rs @@ -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!()