From 94a26e170ecda3d62d3194166a3e90a37c16de58 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 4 Apr 2025 10:42:31 +0200 Subject: [PATCH] uucore: parser: parse_time: Handle infinity and nan There were some missing corner cases when handling infinity and nan: - inf/infinity can be capitalized - nan must always be rejected, even if a suffix is provided Also, return Duration::MAX with infinite values, just for consistency (num.fract() returns 0 for infinity so technically we were just short of that). Add unit tests too. Fixes some of #7475. --- .../src/lib/features/parser/parse_time.rs | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/uucore/src/lib/features/parser/parse_time.rs b/src/uucore/src/lib/features/parser/parse_time.rs index 085dfac81..e0b4a6dc7 100644 --- a/src/uucore/src/lib/features/parser/parse_time.rs +++ b/src/uucore/src/lib/features/parser/parse_time.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (vars) NANOS numstr +// spell-checker:ignore (vars) NANOS numstr infinityh INFD nans nanh //! Parsing a duration from a string. //! //! Use the [`from_str`] function to parse a [`Duration`] from a string. @@ -58,22 +58,23 @@ pub fn from_str(string: &str) -> Result { 'h' => (slice, 60 * 60), 'd' => (slice, 60 * 60 * 24), val if !val.is_alphabetic() => (string, 1), - _ => { - if string == "inf" || string == "infinity" { - ("inf", 1) - } else { - return Err(format!("invalid time interval {}", string.quote())); - } - } + _ => match string.to_ascii_lowercase().as_str() { + "inf" | "infinity" => ("inf", 1), + _ => return Err(format!("invalid time interval {}", string.quote())), + }, }; let num = numstr .parse::() .map_err(|e| format!("invalid time interval {}: {}", string.quote(), e))?; - if num < 0. { + if num < 0. || num.is_nan() { return Err(format!("invalid time interval {}", string.quote())); } + if num.is_infinite() { + return Ok(Duration::MAX); + } + const NANOS_PER_SEC: u32 = 1_000_000_000; let whole_secs = num.trunc(); let nanos = (num.fract() * (NANOS_PER_SEC as f64)).trunc(); @@ -127,6 +128,24 @@ mod tests { assert!(from_str("-1").is_err()); } + #[test] + fn test_infinity() { + assert_eq!(from_str("inf"), Ok(Duration::MAX)); + assert_eq!(from_str("infinity"), Ok(Duration::MAX)); + assert_eq!(from_str("infinityh"), Ok(Duration::MAX)); + assert_eq!(from_str("INF"), Ok(Duration::MAX)); + assert_eq!(from_str("INFs"), Ok(Duration::MAX)); + } + + #[test] + fn test_nan() { + assert!(from_str("nan").is_err()); + assert!(from_str("nans").is_err()); + assert!(from_str("-nanh").is_err()); + assert!(from_str("NAN").is_err()); + assert!(from_str("-NAN").is_err()); + } + /// Test that capital letters are not allowed in suffixes. #[test] fn test_no_capital_letters() { @@ -134,5 +153,6 @@ mod tests { assert!(from_str("1M").is_err()); assert!(from_str("1H").is_err()); assert!(from_str("1D").is_err()); + assert!(from_str("INFD").is_err()); } }