From d5caa0d9d88c7d19cee46b0a4137397ffa7c5642 Mon Sep 17 00:00:00 2001 From: vulppine Date: Sat, 2 Oct 2021 08:15:25 -0700 Subject: [PATCH 1/4] seq: Adds hexadecimal integer parsing --- src/uu/seq/src/seq.rs | 76 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index aac8f2280..594796641 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -88,30 +88,76 @@ impl FromStr for Number { if s.starts_with('+') { s = &s[1..]; } - - match s.parse::() { - Ok(n) => { - // If `s` is '-0', then `parse()` returns - // `BigInt::zero()`, but we need to return - // `Number::MinusZero` instead. - if n == BigInt::zero() && s.starts_with('-') { - Ok(Number::MinusZero) - } else { - Ok(Number::BigInt(n)) - } + let is_neg = s.starts_with('-'); + let is_hex = { + // GNU 20.11.2 - Parsing of Floats + match s.find("0x") { + Some(i) => (true, i), + None => match s.find("0X") { + Some(i) => (true, i), + None => (false, 0), + }, } - Err(_) => match s.parse::() { - Ok(value) if value.is_nan() => Err(format!( + }; + + match is_hex { + (true, i) => match i <= 1 { + false => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + true => match &s.as_bytes()[i + 2] { + b'-' | b'+' => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + // TODO: hexadecimal floating point parsing (see #2660) + b'.' => Err(format!( + "NotImplemented: hexadecimal floating point numbers: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + _ => { + let num = BigInt::from_str_radix(&s[i + 2..], 16) + .map_err(|_| format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + ))?; + match (is_neg, num == BigInt::zero()) { + (true, true) => Ok(Number::MinusZero), + (true, false) => Ok(Number::BigInt(-num)), + (false, _) => Ok(Number::BigInt(num)), + } + } + }, + }, + (false, _) => match s.parse::() { + Ok(n) => { + // If `s` is '-0', then `parse()` returns + // `BigInt::zero()`, but we need to return + // `Number::MinusZero` instead. + if n == BigInt::zero() && is_neg { + Ok(Number::MinusZero) + } else { + Ok(Number::BigInt(n)) + } + } + Err(_) => match s.parse::() { + Ok(value) if value.is_nan() => Err(format!( "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", s.quote(), uucore::execution_phrase(), )), - Ok(value) => Ok(Number::F64(value)), - Err(_) => Err(format!( + Ok(value) => Ok(Number::F64(value)), + Err(_) => Err(format!( "invalid floating point argument: {}\nTry '{} --help' for more information.", s.quote(), uucore::execution_phrase(), )), + }, }, } } From aad0682a404814a03d47efb8bcb88c7b14f8bcdb Mon Sep 17 00:00:00 2001 From: vulppine Date: Sat, 2 Oct 2021 08:46:09 -0700 Subject: [PATCH 2/4] seq: Adds testing for hexadecimal integer parsing --- tests/by-util/test_seq.rs | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 27b5f99bc..2d2ea5344 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -1,6 +1,46 @@ use crate::common::util::*; use std::io::Read; +#[test] +fn test_hex_rejects_posneg_after_identifier() { + new_ucmd!() + .args(&["0x-123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '0x-123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["0x+123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '0x+123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["-0x-123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '-0x-123ABC'") + .stderr_contains("for more information."); + new_ucmd!() + .args(&["-0x+123ABC"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '-0x+123ABC'") + .stderr_contains("for more information."); +} + +#[test] +fn test_hex_lowercase_uppercase() { + new_ucmd!() + .args(&["0xa", "0xA"]) + .succeeds() + .stdout_is("10\n"); + new_ucmd!() + .args(&["0Xa", "0XA"]) + .succeeds() + .stdout_is("10\n"); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!()); From 4e1f945e86e99e350f3f3e12db8057d19401acc2 Mon Sep 17 00:00:00 2001 From: vulppine Date: Sun, 3 Oct 2021 09:50:49 -0700 Subject: [PATCH 3/4] seq: Adds testing for large hex numbers --- tests/by-util/test_seq.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 2d2ea5344..7136f5e76 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -2,7 +2,7 @@ use crate::common::util::*; use std::io::Read; #[test] -fn test_hex_rejects_posneg_after_identifier() { +fn test_hex_rejects_sign_after_identifier() { new_ucmd!() .args(&["0x-123ABC"]) .fails() @@ -41,6 +41,19 @@ fn test_hex_lowercase_uppercase() { .stdout_is("10\n"); } +#[test] +fn test_hex_big_number() { + new_ucmd!() + .args(&[ + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x100000000000000000000000000000000", + ]) + .succeeds() + .stdout_is( + "340282366920938463463374607431768211455\n340282366920938463463374607431768211456\n", + ); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!()); From cddd40b4e1766a32d3f46f010e64dc07c2fb1fad Mon Sep 17 00:00:00 2001 From: vulppine Date: Tue, 5 Oct 2021 18:41:28 -0700 Subject: [PATCH 4/4] seq: Updates hex parse readability, adds hex test --- src/uu/seq/src/seq.rs | 29 +++++++++-------------------- tests/by-util/test_seq.rs | 10 ++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 594796641..a76a23c4e 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -89,25 +89,9 @@ impl FromStr for Number { s = &s[1..]; } let is_neg = s.starts_with('-'); - let is_hex = { - // GNU 20.11.2 - Parsing of Floats - match s.find("0x") { - Some(i) => (true, i), - None => match s.find("0X") { - Some(i) => (true, i), - None => (false, 0), - }, - } - }; - match is_hex { - (true, i) => match i <= 1 { - false => Err(format!( - "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", - s.quote(), - uucore::execution_phrase(), - )), - true => match &s.as_bytes()[i + 2] { + match s.to_lowercase().find("0x") { + Some(i) if i <= 1 => match &s.as_bytes()[i + 2] { b'-' | b'+' => Err(format!( "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", s.quote(), @@ -133,8 +117,13 @@ impl FromStr for Number { } } }, - }, - (false, _) => match s.parse::() { + Some(_) => Err(format!( + "invalid hexadecimal argument: {}\nTry '{} --help' for more information.", + s.quote(), + uucore::execution_phrase(), + )), + + None => match s.parse::() { Ok(n) => { // If `s` is '-0', then `parse()` returns // `BigInt::zero()`, but we need to return diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 7136f5e76..6ed3cb67d 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -54,6 +54,16 @@ fn test_hex_big_number() { ); } +#[test] +fn test_hex_identifier_in_wrong_place() { + new_ucmd!() + .args(&["1234ABCD0x"]) + .fails() + .no_stdout() + .stderr_contains("invalid hexadecimal argument: '1234ABCD0x'") + .stderr_contains("for more information."); +} + #[test] fn test_rejects_nan() { let ts = TestScenario::new(util_name!());