Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 31 additions & 49 deletions src/uu/fold/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
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(())
}

Expand Down
16 changes: 16 additions & 0 deletions tests/by-util/test_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
Expand Down
Loading