From bd2e33bb3419bc868d5046cec593d54440f12370 Mon Sep 17 00:00:00 2001 From: Yonatan Linik Date: Wed, 23 Jul 2025 19:16:12 +0300 Subject: [PATCH] sort: Support hexadecimal numbers/floats correctly Fixes https://github.com/uutils/coreutils/issues/8060 --- src/uu/sort/src/sort.rs | 18 +++++++++++++++--- tests/by-util/test_sort.rs | 12 ++++++++++++ tests/fixtures/sort/exponents_general.expected | 2 +- .../sort/exponents_general.expected.debug | 6 +++--- tests/fixtures/sort/exponents_general.txt | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 4928dd17d..5c97abba3 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -7,7 +7,7 @@ // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html // https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html -// spell-checker:ignore (misc) HFKJFK Mbdfhn getrlimit RLIMIT_NOFILE rlim bigdecimal extendedbigdecimal +// spell-checker:ignore (misc) HFKJFK Mbdfhn getrlimit RLIMIT_NOFILE rlim bigdecimal extendedbigdecimal hexdigit mod check; mod chunks; @@ -1834,15 +1834,27 @@ fn get_leading_gen(input: &str) -> Range { let mut had_e_notation = false; let mut had_decimal_pt = false; + let mut had_hex_notation: bool = false; while let Some((idx, c)) = char_indices.next() { - if c.is_ascii_digit() { + if had_hex_notation && c.is_ascii_hexdigit() { continue; } + + if c.is_ascii_digit() { + if c == '0' && matches!(char_indices.peek(), Some((_, 'x' | 'X'))) { + had_hex_notation = true; + char_indices.next(); + } + continue; + } + if c == DECIMAL_PT && !had_decimal_pt && !had_e_notation { had_decimal_pt = true; continue; } - if (c == 'e' || c == 'E') && !had_e_notation { + let is_decimal_e = (c == 'e' || c == 'E') && !had_hex_notation; + let is_hex_e = (c == 'p' || c == 'P') && had_hex_notation; + if (is_decimal_e || is_hex_e) && !had_e_notation { // we can only consume the 'e' if what follow is either a digit, or a sign followed by a digit. if let Some(&(_, next_char)) = char_indices.peek() { if (next_char == '+' || next_char == '-') diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 9a9022e29..657a3addd 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -1556,3 +1556,15 @@ fn test_g_arbitrary() { .succeeds() .stdout_is(output); } + +#[test] +// Test hexadecimal numbers (and hex floats) +fn test_g_float_hex() { + let input = "0x123\n0x0\n0x2p10\n0x9p-10\n"; + let output = "0x0\n0x9p-10\n0x123\n0x2p10\n"; + new_ucmd!() + .args(&["-g"]) + .pipe_in(input) + .succeeds() + .stdout_is(output); +} diff --git a/tests/fixtures/sort/exponents_general.expected b/tests/fixtures/sort/exponents_general.expected index 308a82e1e..0aa6e0751 100644 --- a/tests/fixtures/sort/exponents_general.expected +++ b/tests/fixtures/sort/exponents_general.expected @@ -8,11 +8,11 @@ -12e-5555.5 0b10 // binary not supported -0x10 // hexadecimal not supported, but it should be 55e-20 55e-20.10 5.5.5.5 10E +0x10 64e+ 99e- 1000EDKLD diff --git a/tests/fixtures/sort/exponents_general.expected.debug b/tests/fixtures/sort/exponents_general.expected.debug index a7238d10e..657a36159 100644 --- a/tests/fixtures/sort/exponents_general.expected.debug +++ b/tests/fixtures/sort/exponents_general.expected.debug @@ -28,9 +28,6 @@ ______________ 0b10 // binary not supported _ ____________________________ -0x10 // hexadecimal not supported, but it should be -_ -___________________________________________________ 55e-20 ______ ______ @@ -43,6 +40,9 @@ _______ 10E __ ___ +0x10 +____ +____ 64e+ __ ____ diff --git a/tests/fixtures/sort/exponents_general.txt b/tests/fixtures/sort/exponents_general.txt index 4a9bbba2e..f6b88b8f5 100644 --- a/tests/fixtures/sort/exponents_general.txt +++ b/tests/fixtures/sort/exponents_general.txt @@ -4,7 +4,7 @@ +100000 10000K78 -0x10 // hexadecimal not supported, but it should be +0x10 10E 0b10 // binary not supported 64e+