From bfd89d6506c6f009bc4c321dba6083f54a2e43a8 Mon Sep 17 00:00:00 2001 From: mattsu Date: Thu, 20 Nov 2025 21:22:16 +0900 Subject: [PATCH 1/6] fix: ignore NUL bytes in sort debug output alignment calculations Replaced direct length checks with filtered counts excluding b'\0' in debug underline and indentation output to prevent misalignment from embedded NUL characters, which are often stripped during inspection. Added comprehensive tests for various sort modes and inputs, including NUL byte scenarios, to verify correct debug annotations. --- src/uu/sort/src/sort.rs | 9 ++- tests/by-util/test_sort.rs | 156 +++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index ec9ab5b9305..7b9a12b397a 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -671,14 +671,19 @@ impl<'a> Line<'a> { _ => {} } + // Don't let embedded NUL bytes influence column alignment in the + // debug underline output, since they are often filtered out (e.g. + // via `tr -d '\0'`) before inspection. let select = &line[..selection.start]; - write!(writer, "{}", " ".repeat(select.len()))?; + let indent = select.iter().filter(|&&c| c != b'\0').count(); + write!(writer, "{}", " ".repeat(indent))?; if selection.is_empty() { writeln!(writer, "{}", translate!("sort-error-no-match-for-key"))?; } else { let select = &line[selection]; - writeln!(writer, "{}", "_".repeat(select.len()))?; + let underline_len = select.iter().filter(|&&c| c != b'\0').count(); + writeln!(writer, "{}", "_".repeat(underline_len))?; } } diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 8bce9d69cb4..92560b16e91 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -6,10 +6,12 @@ // spell-checker:ignore (words) ints (linux) NOFILE #![allow(clippy::cast_possible_wrap)] +use std::env; use std::time::Duration; use uutests::at_and_ucmd; use uutests::new_ucmd; +use uutests::util::TestScenario; fn test_helper(file_name: &str, possible_args: &[&str]) { for args in possible_args { @@ -1871,6 +1873,160 @@ fn test_argument_suggestion_colors_enabled() { } } +#[test] +fn test_debug_key_annotations() { + let ts = TestScenario::new("sort"); + + let number = |input: &str| -> String { + input + .split_terminator('\n') + .enumerate() + .map(|(idx, line)| format!("{}\t{line}\n", idx + 1)) + .collect() + }; + + let run_sort = |args: &[&str], input: &str| -> String { + ts.ucmd() + .args(args) + .pipe_in(input) + .succeeds() + .stdout_move_str() + }; + + let mut output = String::new(); + for mode in ["n", "h", "g"] { + output.push_str(&run_sort(&["-s", &format!("-k2{mode}"), "--debug"], "1\n\n44\n33\n2\n")); + output.push_str(&run_sort( + &["-s", &format!("-k1.3{mode}"), "--debug"], + "1\n\n44\n33\n2\n", + )); + output.push_str(&run_sort(&["-s", &format!("-k1{mode}"), "--debug"], "1\n\n44\n33\n2\n")); + output.push_str(&run_sort( + &["-s", "-k2g", "--debug"], + &number("2\n\n1\n"), + )); + } + + output.push_str(&run_sort(&["-s", "-k1M", "--debug"], "FEB\n\nJAN\n")); + output.push_str(&run_sort(&["-s", "-k2,2M", "--debug"], "FEB\n\nJAN\n")); + output.push_str(&run_sort(&["-s", "-k1M", "--debug"], "FEB\nJAZZ\n\nJAN\n")); + output.push_str(&run_sort( + &["-s", "-k2,2M", "--debug"], + &number("FEB\nJAZZ\n\nJAN\n"), + )); + output.push_str(&run_sort(&["-s", "-k1M", "--debug"], "FEB\nJANZ\n\nJAN\n")); + output.push_str(&run_sort( + &["-s", "-k2,2M", "--debug"], + &number("FEB\nJANZ\n\nJAN\n"), + )); + + output.push_str(&run_sort(&["-s", "-g", "--debug"], " 1.2ignore\n 1.1e4ignore\n")); + output.push_str(&run_sort(&["-s", "-d", "--debug"], "\tb\n\t\ta\n")); + output.push_str(&run_sort(&["-s", "-k2,2", "--debug"], "a\n\n")); + output.push_str(&run_sort(&["-s", "-k1", "--debug"], "b\na\n")); + output.push_str(&run_sort( + &["-s", "--debug", "-k1,1h"], + "-0\n1\n-2\n--Mi-1\n-3\n-0\n", + )); + output.push_str(&run_sort(&["-b", "--debug"], " 1\n1\n")); + output.push_str(&run_sort(&["-s", "-b", "--debug"], " 1\n1\n")); + output.push_str(&run_sort(&["--debug"], " 1\n1\n")); + output.push_str(&run_sort(&["-s", "-k1n", "--debug"], "2,5\n2.4\n")); + output.push_str(&run_sort(&["-s", "-k1n", "--debug"], "2.,,3\n2.4\n")); + output.push_str(&run_sort(&["-s", "-k1n", "--debug"], "2,,3\n2.4\n")); + output.push_str(&run_sort( + &["-s", "-n", "-z", "--debug"], + concat!("1a\0", "2b\0"), + )); + + let mut zero_mix = ts + .ucmd() + .args(&["-s", "-k2b,2", "--debug"]) + .pipe_in("\0\ta\n") + .succeeds() + .stdout_move_bytes(); + zero_mix.retain(|b| *b != 0); + output.push_str(&String::from_utf8(zero_mix).unwrap()); + + output.push_str(&run_sort(&["-s", "-k2.4b,2.3n", "--debug"], "A\tchr10\nB\tchr1\n")); + output.push_str(&run_sort(&["-s", "-k1.2b", "--debug"], "1 2\n1 3\n")); + + assert_eq!( + output, + ts.fixtures.read("debug_key_annotation.expected") + ); + + if let Ok(locale_fr_utf8) = env::var("LOCALE_FR_UTF8") { + if locale_fr_utf8 != "none" { + let probe = ts + .ucmd() + .args(&["-g", "--debug", "/dev/null"]) + .env("LC_NUMERIC", &locale_fr_utf8) + .env("LC_MESSAGES", "C") + .run(); + if probe + .stderr_str() + .contains("numbers use .*,.* as a decimal point") + { + let mut locale_output = String::new(); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", "C") + .args(&["--debug", "-k2g", "-k1b,1"]) + .pipe_in(" 1²---++3 1,234 Mi\n") + .succeeds() + .stdout_move_str(), + ); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", &locale_fr_utf8) + .args(&["--debug", "-k2g", "-k1b,1"]) + .pipe_in(" 1²---++3 1,234 Mi\n") + .succeeds() + .stdout_move_str(), + ); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", &locale_fr_utf8) + .args(&[ + "--debug", + "-k1,1n", + "-k1,1g", + "-k1,1h", + "-k2,2n", + "-k2,2g", + "-k2,2h", + "-k3,3n", + "-k3,3g", + "-k3,3h", + ]) + .pipe_in("+1234 1234Gi 1,234M\n") + .succeeds() + .stdout_move_str(), + ); + + let normalized = locale_output + .lines() + .map(|line| { + if line.starts_with("^^ ") { + "^ no match for key".to_string() + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") + + "\n"; + + assert_eq!( + normalized, + ts.fixtures.read("debug_key_annotation_locale.expected") + ); + } + } + } +} + #[test] fn test_color_environment_variables() { // Test different color environment variable combinations From 62c92994eaf71bea44e2ae69243155f617ab51a5 Mon Sep 17 00:00:00 2001 From: mattsu Date: Mon, 24 Nov 2025 12:07:28 +0900 Subject: [PATCH 2/6] feat: add locale-aware tests for sort debug key annotations Split the existing `test_debug_key_annotations` into two tests: one for basic functionality and another for locale-specific behavior to handle conditional execution based on environment variables. Extracted a new helper function `debug_key_annotation_output` to generate debug output, improving test modularity and reducing code duplication. This enhances test coverage for debug key annotations in different numeric locales. --- tests/by-util/test_sort.rs | 155 +++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 92560b16e91..772330d1acf 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1876,7 +1876,87 @@ fn test_argument_suggestion_colors_enabled() { #[test] fn test_debug_key_annotations() { let ts = TestScenario::new("sort"); + let output = debug_key_annotation_output(&ts); + assert_eq!(output, ts.fixtures.read("debug_key_annotation.expected")); +} + +#[test] +fn test_debug_key_annotations_locale() { + let ts = TestScenario::new("sort"); + + if let Ok(locale_fr_utf8) = env::var("LOCALE_FR_UTF8") { + if locale_fr_utf8 != "none" { + let probe = ts + .ucmd() + .args(&["-g", "--debug", "/dev/null"]) + .env("LC_NUMERIC", &locale_fr_utf8) + .env("LC_MESSAGES", "C") + .run(); + if probe + .stderr_str() + .contains("numbers use .*,.* as a decimal point") + { + let mut locale_output = String::new(); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", "C") + .args(&["--debug", "-k2g", "-k1b,1"]) + .pipe_in(" 1²---++3 1,234 Mi\n") + .succeeds() + .stdout_move_str(), + ); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", &locale_fr_utf8) + .args(&["--debug", "-k2g", "-k1b,1"]) + .pipe_in(" 1²---++3 1,234 Mi\n") + .succeeds() + .stdout_move_str(), + ); + locale_output.push_str( + &ts.ucmd() + .env("LC_ALL", &locale_fr_utf8) + .args(&[ + "--debug", + "-k1,1n", + "-k1,1g", + "-k1,1h", + "-k2,2n", + "-k2,2g", + "-k2,2h", + "-k3,3n", + "-k3,3g", + "-k3,3h", + ]) + .pipe_in("+1234 1234Gi 1,234M\n") + .succeeds() + .stdout_move_str(), + ); + + let normalized = locale_output + .lines() + .map(|line| { + if line.starts_with("^^ ") { + "^ no match for key".to_string() + } else { + line.to_string() + } + }) + .collect::>() + .join("\n") + + "\n"; + + assert_eq!( + normalized, + ts.fixtures.read("debug_key_annotation_locale.expected") + ); + } + } + } +} + +fn debug_key_annotation_output(ts: &TestScenario) -> String { let number = |input: &str| -> String { input .split_terminator('\n') @@ -1951,80 +2031,7 @@ fn test_debug_key_annotations() { output.push_str(&run_sort(&["-s", "-k2.4b,2.3n", "--debug"], "A\tchr10\nB\tchr1\n")); output.push_str(&run_sort(&["-s", "-k1.2b", "--debug"], "1 2\n1 3\n")); - assert_eq!( - output, - ts.fixtures.read("debug_key_annotation.expected") - ); - - if let Ok(locale_fr_utf8) = env::var("LOCALE_FR_UTF8") { - if locale_fr_utf8 != "none" { - let probe = ts - .ucmd() - .args(&["-g", "--debug", "/dev/null"]) - .env("LC_NUMERIC", &locale_fr_utf8) - .env("LC_MESSAGES", "C") - .run(); - if probe - .stderr_str() - .contains("numbers use .*,.* as a decimal point") - { - let mut locale_output = String::new(); - locale_output.push_str( - &ts.ucmd() - .env("LC_ALL", "C") - .args(&["--debug", "-k2g", "-k1b,1"]) - .pipe_in(" 1²---++3 1,234 Mi\n") - .succeeds() - .stdout_move_str(), - ); - locale_output.push_str( - &ts.ucmd() - .env("LC_ALL", &locale_fr_utf8) - .args(&["--debug", "-k2g", "-k1b,1"]) - .pipe_in(" 1²---++3 1,234 Mi\n") - .succeeds() - .stdout_move_str(), - ); - locale_output.push_str( - &ts.ucmd() - .env("LC_ALL", &locale_fr_utf8) - .args(&[ - "--debug", - "-k1,1n", - "-k1,1g", - "-k1,1h", - "-k2,2n", - "-k2,2g", - "-k2,2h", - "-k3,3n", - "-k3,3g", - "-k3,3h", - ]) - .pipe_in("+1234 1234Gi 1,234M\n") - .succeeds() - .stdout_move_str(), - ); - - let normalized = locale_output - .lines() - .map(|line| { - if line.starts_with("^^ ") { - "^ no match for key".to_string() - } else { - line.to_string() - } - }) - .collect::>() - .join("\n") - + "\n"; - - assert_eq!( - normalized, - ts.fixtures.read("debug_key_annotation_locale.expected") - ); - } - } - } + output } #[test] From 55b788410986917c1c01e9efeca381ab8f8e9ef6 Mon Sep 17 00:00:00 2001 From: mattsu Date: Mon, 24 Nov 2025 13:49:57 +0900 Subject: [PATCH 3/6] refactor(test): optimize string building in debug_key_annotation_output for efficiency Rework the `number` helper function to use a mutable String buffer with `writeln!` macro instead of collecting intermediary vectors with `map` and `collect`. This reduces allocations and improves performance in test output generation, building the numbered output directly without extra string concatenations. --- tests/by-util/test_sort.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 772330d1acf..a1e5e6677dd 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -7,6 +7,7 @@ #![allow(clippy::cast_possible_wrap)] use std::env; +use std::fmt::Write as FmtWrite; use std::time::Duration; use uutests::at_and_ucmd; @@ -1958,11 +1959,12 @@ fn test_debug_key_annotations_locale() { fn debug_key_annotation_output(ts: &TestScenario) -> String { let number = |input: &str| -> String { - input - .split_terminator('\n') - .enumerate() - .map(|(idx, line)| format!("{}\t{line}\n", idx + 1)) - .collect() + let mut out = String::new(); + for (idx, line) in input.split_terminator('\n').enumerate() { + // build efficiently without collecting intermediary Strings + writeln!(&mut out, "{}\t{line}", idx + 1).unwrap(); + } + out }; let run_sort = |args: &[&str], input: &str| -> String { From d7989cc7da35f1363c6ffd42e456bc4360f55231 Mon Sep 17 00:00:00 2001 From: mattsu Date: Mon, 24 Nov 2025 13:52:01 +0900 Subject: [PATCH 4/6] refactor(tests): improve formatting and readability in sort test helpers - Reformatted command-line arguments in test_debug_key_annotations_locale to fit on fewer lines - Wrapped run_sort calls in debug_key_annotation_output for better code structure - Minor reordering of output.push_str blocks for consistency and clarity --- tests/by-util/test_sort.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index a1e5e6677dd..d97f8638a53 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1919,16 +1919,8 @@ fn test_debug_key_annotations_locale() { &ts.ucmd() .env("LC_ALL", &locale_fr_utf8) .args(&[ - "--debug", - "-k1,1n", - "-k1,1g", - "-k1,1h", - "-k2,2n", - "-k2,2g", - "-k2,2h", - "-k3,3n", - "-k3,3g", - "-k3,3h", + "--debug", "-k1,1n", "-k1,1g", "-k1,1h", "-k2,2n", "-k2,2g", "-k2,2h", + "-k3,3n", "-k3,3g", "-k3,3h", ]) .pipe_in("+1234 1234Gi 1,234M\n") .succeeds() @@ -1977,16 +1969,19 @@ fn debug_key_annotation_output(ts: &TestScenario) -> String { let mut output = String::new(); for mode in ["n", "h", "g"] { - output.push_str(&run_sort(&["-s", &format!("-k2{mode}"), "--debug"], "1\n\n44\n33\n2\n")); + output.push_str(&run_sort( + &["-s", &format!("-k2{mode}"), "--debug"], + "1\n\n44\n33\n2\n", + )); output.push_str(&run_sort( &["-s", &format!("-k1.3{mode}"), "--debug"], "1\n\n44\n33\n2\n", )); - output.push_str(&run_sort(&["-s", &format!("-k1{mode}"), "--debug"], "1\n\n44\n33\n2\n")); output.push_str(&run_sort( - &["-s", "-k2g", "--debug"], - &number("2\n\n1\n"), + &["-s", &format!("-k1{mode}"), "--debug"], + "1\n\n44\n33\n2\n", )); + output.push_str(&run_sort(&["-s", "-k2g", "--debug"], &number("2\n\n1\n"))); } output.push_str(&run_sort(&["-s", "-k1M", "--debug"], "FEB\n\nJAN\n")); @@ -2002,7 +1997,10 @@ fn debug_key_annotation_output(ts: &TestScenario) -> String { &number("FEB\nJANZ\n\nJAN\n"), )); - output.push_str(&run_sort(&["-s", "-g", "--debug"], " 1.2ignore\n 1.1e4ignore\n")); + output.push_str(&run_sort( + &["-s", "-g", "--debug"], + " 1.2ignore\n 1.1e4ignore\n", + )); output.push_str(&run_sort(&["-s", "-d", "--debug"], "\tb\n\t\ta\n")); output.push_str(&run_sort(&["-s", "-k2,2", "--debug"], "a\n\n")); output.push_str(&run_sort(&["-s", "-k1", "--debug"], "b\na\n")); @@ -2030,7 +2028,10 @@ fn debug_key_annotation_output(ts: &TestScenario) -> String { zero_mix.retain(|b| *b != 0); output.push_str(&String::from_utf8(zero_mix).unwrap()); - output.push_str(&run_sort(&["-s", "-k2.4b,2.3n", "--debug"], "A\tchr10\nB\tchr1\n")); + output.push_str(&run_sort( + &["-s", "-k2.4b,2.3n", "--debug"], + "A\tchr10\nB\tchr1\n", + )); output.push_str(&run_sort(&["-s", "-k1.2b", "--debug"], "1 2\n1 3\n")); output From b0f6290860696bc271f9d027170871abcc548971 Mon Sep 17 00:00:00 2001 From: mattsu Date: Mon, 24 Nov 2025 14:02:39 +0900 Subject: [PATCH 5/6] refactor(test): embed expected debug key annotation outputs as constants Replace fixture file reads with inline constants in test functions for debug key annotations and locale variants. This makes the tests more self-contained by removing dependencies on external fixture files. --- tests/by-util/test_sort.rs | 250 ++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 5 deletions(-) diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index d97f8638a53..7dc66d40789 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1879,7 +1879,7 @@ fn test_debug_key_annotations() { let ts = TestScenario::new("sort"); let output = debug_key_annotation_output(&ts); - assert_eq!(output, ts.fixtures.read("debug_key_annotation.expected")); + assert_eq!(output, EXPECTED_DEBUG_KEY_ANNOTATION); } #[test] @@ -1940,10 +1940,7 @@ fn test_debug_key_annotations_locale() { .join("\n") + "\n"; - assert_eq!( - normalized, - ts.fixtures.read("debug_key_annotation_locale.expected") - ); + assert_eq!(normalized, EXPECTED_DEBUG_KEY_ANNOTATION_LOCALE); } } } @@ -2037,6 +2034,249 @@ fn debug_key_annotation_output(ts: &TestScenario) -> String { output } +const EXPECTED_DEBUG_KEY_ANNOTATION: &str = r#"1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key +1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key + +^ no match for key +1 +_ +2 +_ +33 +__ +44 +__ +2> + ^ no match for key +3>1 + _ +1>2 + _ +1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key +1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key + +^ no match for key +1 +_ +2 +_ +33 +__ +44 +__ +2> + ^ no match for key +3>1 + _ +1>2 + _ +1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key +1 + ^ no match for key + +^ no match for key +44 + ^ no match for key +33 + ^ no match for key +2 + ^ no match for key + +^ no match for key +1 +_ +2 +_ +33 +__ +44 +__ +2> + ^ no match for key +3>1 + _ +1>2 + _ + +^ no match for key +JAN +___ +FEB +___ +FEB + ^ no match for key + +^ no match for key +JAN + ^ no match for key +JAZZ +^ no match for key + +^ no match for key +JAN +___ +FEB +___ +2>JAZZ + ^ no match for key +3> + ^ no match for key +4>JAN + ___ +1>FEB + ___ + +^ no match for key +JANZ +___ +JAN +___ +FEB +___ +3> + ^ no match for key +2>JANZ + ___ +4>JAN + ___ +1>FEB + ___ + 1.2ignore + ___ + 1.1e4ignore + _____ +>>a +___ +>b +__ +a + ^ no match for key + +^ no match for key +a +_ +b +_ +-3 +__ +-2 +__ +-0 +__ +--Mi-1 +^ no match for key +-0 +__ +1 +_ + 1 + _ +__ +1 +_ +_ + 1 + _ +1 +_ + 1 +__ +1 +_ +2,5 +_ +2.4 +___ +2.,,3 +__ +2.4 +___ +2,,3 +_ +2.4 +___ +1a +_ +2b +_ +>a + _ +A>chr10 + ^ no match for key +B>chr1 + ^ no match for key +1 2 + __ +1 3 + __ +"#; + +const EXPECTED_DEBUG_KEY_ANNOTATION_LOCALE: &str = r#" 1²---++3 1,234 Mi + _ + _________ +________________________ + 1²---++3 1,234 Mi + _____ + ________ +_______________________ ++1234 1234Gi 1,234M +^ no match for key +_____ +^ no match for key + ____ + ____ + _____ + _____ + _____ + ______ +___________________ +"#; + #[test] fn test_color_environment_variables() { // Test different color environment variable combinations From dd59b593b9e8e934a8d4791e30d273d884c5dadf Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 6 Dec 2025 08:57:28 +0900 Subject: [PATCH 6/6] feat(sort): extract count_non_null_bytes for debug alignment Add a helper function `count_non_null_bytes` to count bytes in a slice while ignoring embedded NULs. This improves code reusability and is used in debug underline output to ensure proper alignment by filtering NUL characters that may be present in selection strings. Replaces inline counting logic in two locations within the `Line` implementation. --- src/uu/sort/src/sort.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 7b9a12b397a..def2cfcb0b9 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -222,6 +222,11 @@ impl SortMode { } } +/// Return the length of the byte slice while ignoring embedded NULs (used for debug underline alignment). +fn count_non_null_bytes(bytes: &[u8]) -> usize { + bytes.iter().filter(|&&c| c != b'\0').count() +} + pub struct Output { file: Option<(OsString, File)>, } @@ -675,14 +680,14 @@ impl<'a> Line<'a> { // debug underline output, since they are often filtered out (e.g. // via `tr -d '\0'`) before inspection. let select = &line[..selection.start]; - let indent = select.iter().filter(|&&c| c != b'\0').count(); + let indent = count_non_null_bytes(select); write!(writer, "{}", " ".repeat(indent))?; if selection.is_empty() { writeln!(writer, "{}", translate!("sort-error-no-match-for-key"))?; } else { let select = &line[selection]; - let underline_len = select.iter().filter(|&&c| c != b'\0').count(); + let underline_len = count_non_null_bytes(select); writeln!(writer, "{}", "_".repeat(underline_len))?; } }