Skip to content

Commit cfe0154

Browse files
committed
numfmt: fix error message with unit separator and trailing garbage
GNU test: numfmt.pl unit-sep-22-fail
1 parent 21c141f commit cfe0154

1 file changed

Lines changed: 45 additions & 7 deletions

File tree

src/uu/numfmt/src/format.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fn find_valid_number_with_suffix(s: &str, unit: Unit) -> Option<&str> {
7070
}
7171
}
7272

73-
fn detailed_error_message(s: &str, unit: Unit) -> Option<String> {
73+
fn detailed_error_message(s: &str, unit: Unit, unit_separator: &str) -> Option<String> {
7474
if s.is_empty() {
7575
return Some(translate!("numfmt-error-invalid-number-empty"));
7676
}
@@ -87,6 +87,41 @@ fn detailed_error_message(s: &str, unit: Unit) -> Option<String> {
8787
return Some(translate!("numfmt-error-invalid-number", "input" => s.quote()));
8888
}
8989

90+
// When a unit separator is in use, the valid part may extend beyond the
91+
// contiguous number+suffix found by find_valid_number_with_suffix.
92+
// For example "5 K Field2" with unit_separator=" " has valid_part="5" but
93+
// the real valid prefix is "5 K"; the trailing " Field2" is the garbage.
94+
let valid_end =
95+
if !unit_separator.is_empty() && valid_part == find_numeric_beginning(s).unwrap_or("") {
96+
let rest = &s[valid_part.len()..];
97+
if let Some(after_sep) = rest.strip_prefix(unit_separator) {
98+
// Check if the next char is a valid suffix letter
99+
let mut chars = after_sep.chars();
100+
let has_suffix = chars
101+
.next()
102+
.is_some_and(|c| RawSuffix::try_from(&c).is_ok());
103+
if has_suffix {
104+
let suffix_char_len = 1;
105+
let with_i =
106+
chars.next() == Some('i') && [Unit::Auto, Unit::Iec(true)].contains(&unit);
107+
let suffix_len = if with_i {
108+
suffix_char_len + 1
109+
} else {
110+
suffix_char_len
111+
};
112+
valid_part.len() + unit_separator.len() + suffix_len
113+
} else {
114+
valid_part.len()
115+
}
116+
} else {
117+
valid_part.len()
118+
}
119+
} else {
120+
valid_part.len()
121+
};
122+
123+
let valid_part = &s[..valid_end];
124+
90125
if valid_part != s && valid_part.parse::<f64>().is_ok() {
91126
return match s.chars().nth(valid_part.len()) {
92127
Some('+' | '-') => {
@@ -100,9 +135,10 @@ fn detailed_error_message(s: &str, unit: Unit) -> Option<String> {
100135
};
101136
}
102137

103-
if valid_part != s && valid_part.parse::<f64>().is_err() {
138+
if valid_part != s {
139+
let trailing = s[valid_part.len()..].trim_start();
104140
return Some(
105-
translate!("numfmt-error-invalid-specific-suffix", "input" => s.quote(), "suffix" => s[valid_part.len()..].quote()),
141+
translate!("numfmt-error-invalid-specific-suffix", "input" => s.quote(), "suffix" => trailing.quote()),
106142
);
107143
}
108144
None
@@ -367,7 +403,9 @@ fn transform_from(s: &str, opts: &TransformOptions, options: &NumfmtOptions) ->
367403
&options.unit_separator,
368404
options.explicit_unit_separator,
369405
)
370-
.map_err(|original| detailed_error_message(s, opts.from).unwrap_or(original))?;
406+
.map_err(|original| {
407+
detailed_error_message(s, opts.from, &options.unit_separator).unwrap_or(original)
408+
})?;
371409
let i = i * (opts.from_unit as f64);
372410

373411
remove_suffix(i, suffix, opts.from).map(|n| {
@@ -818,20 +856,20 @@ mod tests {
818856

819857
#[test]
820858
fn test_detailed_error_message() {
821-
let result = detailed_error_message("123i", Unit::Auto);
859+
let result = detailed_error_message("123i", Unit::Auto, "");
822860
assert!(result.is_some());
823861
let error = result.unwrap();
824862
assert!(error.contains("numfmt-error-invalid-suffix") || error.contains("invalid suffix"));
825863

826-
let result = detailed_error_message("5MF", Unit::Auto);
864+
let result = detailed_error_message("5MF", Unit::Auto, "");
827865
assert!(result.is_some());
828866
let error = result.unwrap();
829867
assert!(
830868
error.contains("numfmt-error-invalid-specific-suffix")
831869
|| error.contains("invalid suffix")
832870
);
833871

834-
let result = detailed_error_message("5KM", Unit::Auto);
872+
let result = detailed_error_message("5KM", Unit::Auto, "");
835873
assert!(result.is_some());
836874
let error = result.unwrap();
837875
assert!(

0 commit comments

Comments
 (0)