diff --git a/src/uucore/src/lib/features/parser/num_parser.rs b/src/uucore/src/lib/features/parser/num_parser.rs index c072b8702..6f220d282 100644 --- a/src/uucore/src/lib/features/parser/num_parser.rs +++ b/src/uucore/src/lib/features/parser/num_parser.rs @@ -165,23 +165,24 @@ impl ExtendedParser for u64 { ExtendedBigDecimal::BigDecimal(bd) => { let (digits, scale) = bd.into_bigint_and_scale(); if scale == 0 { - let negative = digits.sign() == Sign::Minus; + let (sign, digits) = digits.into_parts(); + match u64::try_from(digits) { - Ok(i) => Ok(i), - _ => Err(ExtendedParserError::Overflow(if negative { - // TODO: We should wrap around here #7488 - 0 - } else { - u64::MAX - })), + Ok(i) => { + if sign == Sign::Minus { + Ok(!i + 1) + } else { + Ok(i) + } + } + _ => Err(ExtendedParserError::Overflow(u64::MAX)), } } else { // Should not happen. Err(ExtendedParserError::NotNumeric) } } - // TODO: Handle -0 too #7488 - // No other case should not happen. + ExtendedBigDecimal::MinusZero => Ok(0), _ => Err(ExtendedParserError::NotNumeric), } } @@ -500,10 +501,28 @@ mod tests { fn test_decimal_u64() { assert_eq!(Ok(123), u64::extended_parse("123")); assert_eq!(Ok(u64::MAX), u64::extended_parse(&format!("{}", u64::MAX))); - // TODO: We should wrap around here #7488 + assert_eq!(Ok(0), u64::extended_parse("-0")); + assert_eq!(Ok(u64::MAX), u64::extended_parse("-1")); + assert_eq!( + Ok(u64::MAX / 2 + 1), + u64::extended_parse("-9223372036854775808") // i64::MIN + ); + assert_eq!( + Ok(1123372036854675616), + u64::extended_parse("-17323372036854876000") // 2*i64::MIN + ); + assert_eq!(Ok(1), u64::extended_parse("-18446744073709551615")); // -u64::MAX assert!(matches!( - u64::extended_parse("-123"), - Err(ExtendedParserError::Overflow(0)) + u64::extended_parse("-18446744073709551616"), // -u64::MAX - 1 + Err(ExtendedParserError::Overflow(u64::MAX)) + )); + assert!(matches!( + u64::extended_parse("-92233720368547758150"), + Err(ExtendedParserError::Overflow(u64::MAX)) + )); + assert!(matches!( + u64::extended_parse("-170141183460469231731687303715884105729"), + Err(ExtendedParserError::Overflow(u64::MAX)) )); assert!(matches!( u64::extended_parse(""), diff --git a/tests/by-util/test_printf.rs b/tests/by-util/test_printf.rs index 61e14608a..48abb513b 100644 --- a/tests/by-util/test_printf.rs +++ b/tests/by-util/test_printf.rs @@ -2,6 +2,8 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. + +// spell-checker:ignore fffffffffffffffc use uutests::new_ucmd; use uutests::util::TestScenario; use uutests::util_name; @@ -668,6 +670,41 @@ fn partial_integer() { .stderr_is("printf: '42x23': value not completely converted\n"); } +#[test] +fn unsigned_hex_negative_wraparound() { + new_ucmd!() + .args(&["%x", "-0b100"]) + .succeeds() + .stdout_only("fffffffffffffffc"); + + new_ucmd!() + .args(&["%x", "-0100"]) + .succeeds() + .stdout_only("ffffffffffffffc0"); + + new_ucmd!() + .args(&["%x", "-100"]) + .succeeds() + .stdout_only("ffffffffffffff9c"); + + new_ucmd!() + .args(&["%x", "-0x100"]) + .succeeds() + .stdout_only("ffffffffffffff00"); + + new_ucmd!() + .args(&["%x", "-92233720368547758150"]) + .fails_with_code(1) + .stdout_is("ffffffffffffffff") + .stderr_is("printf: '-92233720368547758150': Numerical result out of range\n"); + + new_ucmd!() + .args(&["%u", "-1002233720368547758150"]) + .fails_with_code(1) + .stdout_is("18446744073709551615") + .stderr_is("printf: '-1002233720368547758150': Numerical result out of range\n"); +} + #[test] fn test_overflow() { new_ucmd!()