From 79a076e46afc895e471c49dcf02cbbb900edf50b Mon Sep 17 00:00:00 2001 From: FidelSch Date: Tue, 3 Mar 2026 12:02:22 -0300 Subject: [PATCH] fix(numfmt): fix escape_line to handle non-printable ASCII bytes in error messages --- src/uu/numfmt/src/format.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 81fb85647..57abc8458 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -452,18 +452,24 @@ fn format_string( )) } -/// Encodes a byte slice as a string, representing non-UTF-8 bytes as octal escapes. +/// Encodes a byte slice as a string, representing non-UTF-8 bytes and non-printable ASCII +/// bytes as octal escapes. Valid UTF-8 multi-byte characters pass through unchanged. /// Used to safely format invalid input in error messages. pub(crate) fn escape_line(line: &[u8]) -> String { - line.iter() - .map(|&b| { - if b.is_ascii_graphic() || b.is_ascii_whitespace() { - (b as char).to_string() + let mut result = String::new(); + for chunk in line.utf8_chunks() { + for c in chunk.valid().chars() { + if c.is_ascii() && !c.is_ascii_graphic() && !c.is_ascii_whitespace() { + result.push_str(&format!("\\{:03o}", c as u8)); } else { - format!("\\{b:03o}") + result.push(c); } - }) - .collect() + } + for &b in chunk.invalid() { + result.push_str(&format!("\\{b:03o}")); + } + } + result } fn split_bytes<'a>(input: &'a [u8], delim: &'a [u8]) -> impl Iterator {