From 1ab9cbbeca14ae0078bb4c5edf57cc989ac982d9 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 18 Oct 2026 18:59:25 +0000 Subject: [PATCH 1/2] fix(ls): respect line_ending in display_grid when using --zero with column formats When using --zero with -C or -x, lines were terminated by newline instead of NUL because display_grid did not take line_ending into account. Fixes #14774 --- src/uu/ls/src/display.rs | 14 ++++++++++++-- tests/by-util/test_ls.rs | 8 ++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/uu/ls/src/display.rs b/src/uu/ls/src/display.rs index 7dbd4db3a3f..aad86c21334 100644 --- a/src/uu/ls/src/display.rs +++ b/src/uu/ls/src/display.rs @@ -410,6 +410,7 @@ fn display_items_non_long( &mut state.out, quoted, config.tab_size, + config.line_ending, ), Format::Across => display_grid( names.into_iter(), @@ -418,6 +419,7 @@ fn display_items_non_long( &mut state.out, quoted, config.tab_size, + config.line_ending, ), Format::Commas => display_commas_format( names.into_iter(), @@ -497,6 +499,7 @@ fn display_grid( out: &mut BufWriter, quoted: bool, tab_size: usize, + line_ending: LineEnding, ) -> UResult<()> { if width == 0 { // If the width is 0 we print one single line @@ -509,7 +512,7 @@ fn display_grid( write_os_str(out, &name.displayed)?; } if printed_something { - writeln!(out)?; + write!(out, "{line_ending}")?; } } else { let names: Vec = { @@ -556,7 +559,14 @@ fn display_grid( width: width as usize, }, ); - write!(out, "{grid}")?; + match line_ending { + LineEnding::Newline => write!(out, "{grid}")?, + LineEnding::Nul => { + let grid_str = grid.to_string(); + let nul_str = grid_str.replace('\n', "\0"); + write!(out, "{nul_str}")?; + } + } } Ok(()) } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 23833bfc1d4..84d6facb769 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -1083,6 +1083,14 @@ fn test_ls_zero() { .succeeds() .stdout_only("0-test-zero, 2-test-zero, 3-test-zero\x00"); + for opt in ["-C", "-x"] { + scene + .ucmd() + .args(&["--zero", opt, "-w", "80"]) + .succeeds() + .stdout_only("0-test-zero\t2-test-zero\t3-test-zero\x00"); + } + scene .ucmd() .args(&["--zero", "--hide-control-chars"]) From 88ec8dfb55dfee9b2d2d9753c9ca8521226dd5d7 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 18 Oct 2026 22:51:07 +0000 Subject: [PATCH 2/2] fix(ls): replace only row terminators with NUL in display_grid and expand tests --- src/uu/ls/src/display.rs | 32 ++++++++++++++++++++++++++++---- tests/by-util/test_ls.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/uu/ls/src/display.rs b/src/uu/ls/src/display.rs index aad86c21334..467ec9d9d3e 100644 --- a/src/uu/ls/src/display.rs +++ b/src/uu/ls/src/display.rs @@ -550,7 +550,6 @@ fn display_grid( tab_size, }, }; - let grid = Grid::new( names, GridOptions { @@ -562,9 +561,34 @@ fn display_grid( match line_ending { LineEnding::Newline => write!(out, "{grid}")?, LineEnding::Nul => { - let grid_str = grid.to_string(); - let nul_str = grid_str.replace('\n', "\0"); - write!(out, "{nul_str}")?; + struct GridNulWriter<'a, W> { + out: &'a mut W, + io_error: Option, + } + impl FmtWrite for GridNulWriter<'_, W> { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + let res = if s == "\n" { + self.out.write_all(b"\0") + } else { + self.out.write_all(s.as_bytes()) + }; + if let Err(e) = res { + self.io_error = Some(e); + return Err(std::fmt::Error); + } + Ok(()) + } + } + let mut writer = GridNulWriter { + out, + io_error: None, + }; + if let Err(e) = write!(writer, "{grid}") { + if let Some(io_err) = writer.io_error { + return Err(io_err.into()); + } + return Err(std::io::Error::other(e).into()); + } } } } diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 84d6facb769..938082c26fb 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -1088,9 +1088,21 @@ fn test_ls_zero() { .ucmd() .args(&["--zero", opt, "-w", "80"]) .succeeds() - .stdout_only("0-test-zero\t2-test-zero\t3-test-zero\x00"); + .stdout_only("0-test-zero 2-test-zero 3-test-zero\x00"); } + scene + .ucmd() + .args(&["--zero", "-C", "-w", "30"]) + .succeeds() + .stdout_only("0-test-zero 3-test-zero\x002-test-zero\x00"); + + scene + .ucmd() + .args(&["--zero", "-x", "-w", "30"]) + .succeeds() + .stdout_only("0-test-zero 2-test-zero\x003-test-zero\x00"); + scene .ucmd() .args(&["--zero", "--hide-control-chars"]) @@ -1150,6 +1162,26 @@ fn test_ls_zero() { .succeeds() .stdout_only("0-test-zero, 1\ntest-zero, 2-test-zero, 3-test-zero\x00"); + for opt in ["-C", "-x"] { + scene + .ucmd() + .args(&["--zero", opt, "-w", "80"]) + .succeeds() + .stdout_only("0-test-zero 1\ntest-zero\t 2-test-zero 3-test-zero\x00"); + } + + scene + .ucmd() + .args(&["--zero", "-C", "-w", "30"]) + .succeeds() + .stdout_only("0-test-zero 2-test-zero\x001\ntest-zero 3-test-zero\x00"); + + scene + .ucmd() + .args(&["--zero", "-x", "-w", "30"]) + .succeeds() + .stdout_only("0-test-zero 1\ntest-zero\x002-test-zero 3-test-zero\x00"); + scene .ucmd() .args(&["--zero", "--hide-control-chars"])