From b6d6582270c9de08a514647d49ee7f27fdb74cbc Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 07:12:28 +0000 Subject: [PATCH 1/7] fix: use buffered read to prevent hanging on infinite streams like /dev/zero --- src/uu/fold/src/fold.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 2eb97933180..36f717249cc 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -572,20 +572,22 @@ fn fold_file( mode: WidthMode, writer: &mut W, ) -> UResult<()> { - let mut line = Vec::new(); let mut output = Vec::new(); let mut col_count = 0; let mut last_space = None; + let mut buffer = [0u8; 8192]; loop { - if file - .read_until(NL, &mut line) - .map_err_context(|| translate!("fold-error-readline"))? - == 0 - { + let n = file + .read(&mut buffer) + .map_err_context(|| translate!("fold-error-read"))?; + + if n == 0 { break; } + let chunk = &buffer[..n]; + let mut ctx = FoldContext { spaces, width, @@ -596,12 +598,15 @@ fn fold_file( last_space: &mut last_space, }; - match std::str::from_utf8(&line) { + match std::str::from_utf8(chunk) { Ok(s) => process_utf8_line(s, &mut ctx)?, - Err(_) => process_non_utf8_line(&line, &mut ctx)?, + Err(_) => process_non_utf8_line(chunk, &mut ctx)?, } - line.clear(); + if !output.is_empty() { + writer.write_all(&output)?; + output.clear(); + } } if !output.is_empty() { From cb20a5f307ad94171eb57df39b88473644d26620 Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 07:18:09 +0000 Subject: [PATCH 2/7] tests: add regression test for char device input --- tests/by-util/test_fold.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/by-util/test_fold.rs b/tests/by-util/test_fold.rs index 9497044c910..7d6c13596c9 100644 --- a/tests/by-util/test_fold.rs +++ b/tests/by-util/test_fold.rs @@ -632,3 +632,13 @@ fn test_fullwidth_characters() { .succeeds() .stdout_is(format!("{e_fullwidth}\n{e_fullwidth}")); } + +#[cfg(unix)] +#[test] +fn test_fold_dev_null() { + new_ucmd!() + .arg("/dev/null") + .succeeds() + .no_stderr() + .stdout_only(""); +} From a4116f640cfc0504ad12480ab3cd906351aac150 Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 11:12:17 +0000 Subject: [PATCH 3/7] fix: use buffered read to prevent hanging on infinite streams like /dev/zero also fixed failure testcases --- src/uu/fold/src/fold.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 36f717249cc..e98bcaba6c6 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -576,17 +576,23 @@ fn fold_file( let mut col_count = 0; let mut last_space = None; - let mut buffer = [0u8; 8192]; loop { - let n = file - .read(&mut buffer) + let buffer = file.fill_buf() .map_err_context(|| translate!("fold-error-read"))?; - if n == 0 { + if buffer.is_empty() { break; } - let chunk = &buffer[..n]; + let len = buffer.len(); + let mut consume_len = len; + + if let Some(pos) = buffer.iter().position(|&b| b == b'\n') { + consume_len = pos + 1; + } + let chunk = buffer[..consume_len].to_vec(); + + file.consume(consume_len); let mut ctx = FoldContext { spaces, @@ -598,9 +604,9 @@ fn fold_file( last_space: &mut last_space, }; - match std::str::from_utf8(chunk) { + match std::str::from_utf8(&chunk) { Ok(s) => process_utf8_line(s, &mut ctx)?, - Err(_) => process_non_utf8_line(chunk, &mut ctx)?, + Err(_) => process_non_utf8_line(&chunk, &mut ctx)?, } if !output.is_empty() { @@ -609,10 +615,5 @@ fn fold_file( } } - if !output.is_empty() { - writer.write_all(&output)?; - output.clear(); - } - Ok(()) -} +} \ No newline at end of file From bf64d00425b9f76ab6c801c23e9b10a5230e3a0f Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 11:21:37 +0000 Subject: [PATCH 4/7] fix: use buffered read to prevent hanging on infinite streams like /dev/zero also fixed failure testcases after cargo fmt --- src/uu/fold/src/fold.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index e98bcaba6c6..b60515f08c9 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -577,7 +577,8 @@ fn fold_file( let mut last_space = None; loop { - let buffer = file.fill_buf() + let buffer = file + .fill_buf() .map_err_context(|| translate!("fold-error-read"))?; if buffer.is_empty() { @@ -589,7 +590,7 @@ fn fold_file( if let Some(pos) = buffer.iter().position(|&b| b == b'\n') { consume_len = pos + 1; - } + } let chunk = buffer[..consume_len].to_vec(); file.consume(consume_len); @@ -616,4 +617,4 @@ fn fold_file( } Ok(()) -} \ No newline at end of file +} From 4048d4f92790fe5979c2e4a82b47c160c825553d Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 17:29:00 +0000 Subject: [PATCH 5/7] fixed the performance regression hopefully --- src/uu/fold/src/fold.rs | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index b60515f08c9..1aedb6422ad 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -577,8 +577,7 @@ fn fold_file( let mut last_space = None; loop { - let buffer = file - .fill_buf() + let buffer = file.fill_buf() .map_err_context(|| translate!("fold-error-read"))?; if buffer.is_empty() { @@ -590,31 +589,39 @@ fn fold_file( if let Some(pos) = buffer.iter().position(|&b| b == b'\n') { consume_len = pos + 1; + } else { + if let Err(e) = std::str::from_utf8(buffer) { + if e.error_len().is_none() { + let valid = e.valid_up_to(); + if valid > 0 { + consume_len = valid; + } + } + } } - let chunk = buffer[..consume_len].to_vec(); - file.consume(consume_len); + { + let chunk = &buffer[..consume_len]; - let mut ctx = FoldContext { - spaces, - width, - mode, - writer, - output: &mut output, - col_count: &mut col_count, - last_space: &mut last_space, - }; + let mut ctx = FoldContext { + spaces, width, mode, writer, + output: &mut output, + col_count: &mut col_count, + last_space: &mut last_space, + }; - match std::str::from_utf8(&chunk) { - Ok(s) => process_utf8_line(s, &mut ctx)?, - Err(_) => process_non_utf8_line(&chunk, &mut ctx)?, - } + match std::str::from_utf8(chunk) { + Ok(s) => process_utf8_line(s, &mut ctx)?, + Err(_) => process_non_utf8_line(chunk, &mut ctx)?, + } + } + file.consume(consume_len); if !output.is_empty() { writer.write_all(&output)?; - output.clear(); + output.clear(); // Keeps capacity, avoiding re-allocation } } Ok(()) -} +} \ No newline at end of file From 12f900d0368aa3e6d174e135856f00b50cad02df Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 17:29:34 +0000 Subject: [PATCH 6/7] fixed the performance regression hopefully and cargo fmt --- src/uu/fold/src/fold.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 1aedb6422ad..45e1f3313d8 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -577,7 +577,8 @@ fn fold_file( let mut last_space = None; loop { - let buffer = file.fill_buf() + let buffer = file + .fill_buf() .map_err_context(|| translate!("fold-error-read"))?; if buffer.is_empty() { @@ -604,7 +605,10 @@ fn fold_file( let chunk = &buffer[..consume_len]; let mut ctx = FoldContext { - spaces, width, mode, writer, + spaces, + width, + mode, + writer, output: &mut output, col_count: &mut col_count, last_space: &mut last_space, @@ -614,7 +618,7 @@ fn fold_file( Ok(s) => process_utf8_line(s, &mut ctx)?, Err(_) => process_non_utf8_line(chunk, &mut ctx)?, } - } + } file.consume(consume_len); if !output.is_empty() { @@ -624,4 +628,4 @@ fn fold_file( } Ok(()) -} \ No newline at end of file +} From 9a67924e9cab75bd6f5ffe2e1e7f5a118302bc33 Mon Sep 17 00:00:00 2001 From: Saathwik Dasari Date: Thu, 1 Jan 2026 17:34:59 +0000 Subject: [PATCH 7/7] fixed the code quality failure --- src/uu/fold/src/fold.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/uu/fold/src/fold.rs b/src/uu/fold/src/fold.rs index 45e1f3313d8..f0a8c005965 100644 --- a/src/uu/fold/src/fold.rs +++ b/src/uu/fold/src/fold.rs @@ -590,13 +590,11 @@ fn fold_file( if let Some(pos) = buffer.iter().position(|&b| b == b'\n') { consume_len = pos + 1; - } else { - if let Err(e) = std::str::from_utf8(buffer) { - if e.error_len().is_none() { - let valid = e.valid_up_to(); - if valid > 0 { - consume_len = valid; - } + } else if let Err(e) = std::str::from_utf8(buffer) { + if e.error_len().is_none() { + let valid = e.valid_up_to(); + if valid > 0 { + consume_len = valid; } } }