From 0400116af8980ae38bc1c69e9cd434743a4bdd07 Mon Sep 17 00:00:00 2001 From: Cedric Erdelen Date: Mon, 2 Feb 2026 15:31:15 +0100 Subject: [PATCH] expand: use chunking read calls expand: Edgecase fix expand: spelling error in comment expand: use byte literals instead of hex values for better readability --- src/uu/expand/src/expand.rs | 40 +++++++++++-------- tests/by-util/test_expand.rs | 10 +++++ tests/fixtures/expand/new_line_in_chunk.txt | 9 +++++ .../expand/new_line_in_chunk_expected.txt | 9 +++++ 4 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 tests/fixtures/expand/new_line_in_chunk.txt create mode 100644 tests/fixtures/expand/new_line_in_chunk_expected.txt diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index 5acd4fac2..ec8a3704f 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, ArgMatches, Command}; use std::ffi::OsString; use std::fs::File; -use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout}; +use std::io::{BufReader, BufWriter, Read, Write, stdin, stdout}; use std::num::IntErrorKind; use std::path::Path; use std::str::from_utf8; @@ -411,11 +411,12 @@ fn write_tab_spaces( } } -fn expand_line( - buf: &mut Vec, +fn expand_buf( + buf: &[u8], output: &mut BufWriter, tabstops: &[usize], options: &Options, + col: &mut usize, ) -> std::io::Result<()> { use self::CharType::{Backspace, Other, Tab}; @@ -423,11 +424,12 @@ fn expand_line( // we can write the buffer directly without character-by-character processing if !buf.contains(&b'\t') && !buf.contains(&b'\x08') && (options.utf8 || !buf.contains(&b'\r')) { output.write_all(buf)?; - buf.truncate(0); + if let Some(n) = buf.iter().rposition(|&b| b == b'\n') { + *col = buf.len() - n - 1; + } return Ok(()); } - let mut col = 0; let mut byte = 0; let mut init = true; @@ -438,8 +440,8 @@ fn expand_line( match ctype { Tab => { // figure out how many spaces to the next tabstop - let nts = next_tabstop(tabstops, col, &options.remaining_mode); - col += nts; + let nts = next_tabstop(tabstops, *col, &options.remaining_mode); + *col += nts; // now dump out either spaces if we're expanding, or a literal tab if we're not if init || !options.iflag { @@ -449,25 +451,30 @@ fn expand_line( } } Backspace => { - col = col.saturating_sub(1); + *col = col.saturating_sub(1); // if we're writing anything other than a space, then we're // done with the line's leading spaces - if buf[byte] != 0x20 { + if buf[byte] != b' ' { init = false; } output.write_all(&buf[byte..byte + nbytes])?; } Other => { - col += cwidth; + *col += cwidth; // if we're writing anything other than a space, then we're // done with the line's leading spaces - if buf[byte] != 0x20 { + if buf[byte] != b' ' { init = false; } + if buf[byte] == b'\n' { + *col = 0; + init = true; + } + output.write_all(&buf[byte..byte + nbytes])?; } } @@ -475,8 +482,6 @@ fn expand_line( byte += nbytes; // advance the pointer } - buf.truncate(0); // clear the buffer - Ok(()) } @@ -485,14 +490,15 @@ fn expand_file( output: &mut BufWriter, options: &Options, ) -> UResult<()> { - let mut buf = Vec::new(); + let mut buf = [0u8; 4096]; let mut input = open(file)?; let ts = options.tabstops.as_ref(); + let mut col = 0; loop { - match input.read_until(b'\n', &mut buf) { + match input.read(&mut buf) { Ok(0) => break, - Ok(_) => { - expand_line(&mut buf, output, ts, options) + Ok(n) => { + expand_buf(&buf[..n], output, ts, options, &mut col) .map_err_context(|| translate!("expand-error-failed-to-write-output"))?; } Err(e) => return Err(e.map_err_context(|| file.maybe_quote().to_string())), diff --git a/tests/by-util/test_expand.rs b/tests/by-util/test_expand.rs index 78a0f6ae5..cd84a82d2 100644 --- a/tests/by-util/test_expand.rs +++ b/tests/by-util/test_expand.rs @@ -451,3 +451,13 @@ fn test_expand_non_utf8_paths() { .succeeds() .stdout_is("hello world\ntest line\n"); } + +#[test] +fn test_buffered_reads_new_line_no_tabs_in_first_chunk() { + // test includes tabs after 1 full chunk of no tabs with new line + // checks that the tabstop calculation is done to correct column + new_ucmd!() + .args(&["new_line_in_chunk.txt"]) + .succeeds() + .stdout_is_fixture("new_line_in_chunk_expected.txt"); +} diff --git a/tests/fixtures/expand/new_line_in_chunk.txt b/tests/fixtures/expand/new_line_in_chunk.txt new file mode 100644 index 000000000..9654789dc --- /dev/null +++ b/tests/fixtures/expand/new_line_in_chunk.txt @@ -0,0 +1,9 @@ +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000 0 000 000 00 0 0 0 0 + Lorem Ipsum test file + + + + +hello diff --git a/tests/fixtures/expand/new_line_in_chunk_expected.txt b/tests/fixtures/expand/new_line_in_chunk_expected.txt new file mode 100644 index 000000000..3997d70b6 --- /dev/null +++ b/tests/fixtures/expand/new_line_in_chunk_expected.txt @@ -0,0 +1,9 @@ +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +000000000000000000 0 000 000 00 0 0 0 0 + Lorem Ipsum test file + + + + +hello