Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/uu/sort/src/numeric_str_cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub struct NumInfo {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct NumInfoParseSettings {
pub accept_si_units: bool,
/// Also accept lowercase SI units, as with `-f`.
pub fold_si_units: bool,
pub thousands_separator: Option<u8>,
pub decimal_pt: Option<u8>,
}
Expand All @@ -39,6 +41,7 @@ impl Default for NumInfoParseSettings {
fn default() -> Self {
Self {
accept_si_units: false,
fold_si_units: false,
thousands_separator: None,
decimal_pt: Some(b'.'),
}
Expand Down Expand Up @@ -81,8 +84,13 @@ impl NumInfo {

if Self::is_invalid_char(char, &mut had_decimal_pt, parse_settings) {
return if let Some(start) = start {
let unit = if parse_settings.fold_si_units {
char.to_ascii_uppercase()
} else {
char
};
let has_si_unit =
parse_settings.accept_si_units && b"kKMGTPEZYRQ".contains(&char);
parse_settings.accept_si_units && b"kKMGTPEZYRQ".contains(&unit);
(
Self { exponent, sign },
start..if has_si_unit { idx + 1 } else { idx },
Expand Down Expand Up @@ -165,14 +173,21 @@ impl NumInfo {
}
}

/// A lowercase unit other than `k` is only part of the range with `-f`, so it is
/// read as the uppercase one.
fn get_unit(unit: Option<u8>) -> u8 {
match unit {
Some(b'k') => 1,
Some(u) => b"KMGTPEZYRQ"
.iter()
.position(|&c| c == u)
.map_or(0, |pos| (pos + 1) as u8),
None => 0,
Some(b'k' | b'K') => 1,
Some(b'M' | b'm') => 2,
Some(b'G' | b'g') => 3,
Some(b'T' | b't') => 4,
Some(b'P' | b'p') => 5,
Some(b'E' | b'e') => 6,
Some(b'Z' | b'z') => 7,
Some(b'Y' | b'y') => 8,
Some(b'R' | b'r') => 9,
Some(b'Q' | b'q') => 10,
_ => 0,
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/uu/sort/src/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,10 @@ impl Default for NumericLocaleSettings {
}

impl NumericLocaleSettings {
fn num_info_settings(self, accept_si_units: bool) -> NumInfoParseSettings {
fn num_info_settings(self, accept_si_units: bool, fold_si_units: bool) -> NumInfoParseSettings {
NumInfoParseSettings {
accept_si_units,
fold_si_units,
thousands_separator: self.thousands_sep,
decimal_pt: self.decimal_pt,
}
Expand Down Expand Up @@ -788,9 +789,10 @@ impl<'a> Line<'a> {
match selector.settings.mode {
SortMode::Numeric | SortMode::HumanNumeric => {
// find out which range is used for numeric comparisons
// GNU marks a lowercase unit as not part of the key, even with -f.
let mut parse_settings = settings
.numeric_locale
.num_info_settings(selector.settings.mode == SortMode::HumanNumeric);
.num_info_settings(selector.settings.mode == SortMode::HumanNumeric, false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please add a --debug -h -f case to the test so this stays aligned with GNU?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sylvestre Added test_human_numeric_ignore_case_debug, which checks the --debug -h -f output against GNU's.

// Debug annotations should ignore thousands separators to match GNU output.
parse_settings.thousands_separator = None;
let (_, num_range) =
Expand Down Expand Up @@ -1369,7 +1371,10 @@ impl FieldSelector {
// Parse NumInfo for this number.
let (info, num_range) = NumInfo::parse(
range_str,
&numeric_locale.num_info_settings(self.settings.mode == SortMode::HumanNumeric),
&numeric_locale.num_info_settings(
self.settings.mode == SortMode::HumanNumeric,
self.settings.ignore_case,
),
);
// Shorten the range to what we need to pass to numeric_str_cmp later.
range_str = &range_str[num_range];
Expand Down
38 changes: 38 additions & 0 deletions tests/by-util/test_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,44 @@ fn test_human_block_sizes2() {
}
}

#[test]
fn test_human_numeric_ignore_case() {
// With -f, lowercase units other than k are read as the uppercase ones
for args in [&["-h", "-f"][..], &["-hf"], &["-k1,1hf"]] {
new_ucmd!()
.args(args)
.pipe_in("2m\n1e\n3g\n1K\n-1m\n")
.succeeds()
.stdout_only("-1m\n1K\n2m\n3g\n1e\n");
}
// Without -f (a key with its own options does not inherit the global
// one) they are not units
for args in [&["-h"][..], &["-f", "-k1h"]] {
new_ucmd!()
.args(args)
.pipe_in("2m\n1e\n3g\n1K\n-1m\n")
.succeeds()
.stdout_only("-1m\n1e\n2m\n3g\n1K\n");
}
}

#[test]
fn test_human_numeric_ignore_case_debug() {
// GNU sorts by the folded unit but does not mark a lowercase unit as part of the key
new_ucmd!()
.env("LC_ALL", "C")
.args(&["--debug", "-h", "-f"])
.pipe_in("2m\n1e\n3g\n1K\n-1m\n")
.succeeds()
.stdout_is(concat!(
"-1m\n__\n___\n",
"1K\n__\n__\n",
"2m\n_\n__\n",
"3g\n_\n__\n",
"1e\n_\n__\n",
));
}

#[test]
fn test_human_numeric_zero_stable() {
let input = "0M\n0K\n-0K\n-P\n-0M\n";
Expand Down
Loading