From 636c2bb7ae9444551d900d50829eb87414a23f8f Mon Sep 17 00:00:00 2001 From: zhitkoff Date: Mon, 4 Sep 2023 12:05:26 -0400 Subject: [PATCH] uucore: parse_size_max and split --- src/uu/split/src/split.rs | 28 ++++++++-------------- src/uucore/src/lib/parser/parse_size.rs | 32 +++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index d76bfb2de..e39f6e93f 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -21,7 +21,7 @@ use std::io::{stdin, BufRead, BufReader, BufWriter, ErrorKind, Read, Write}; use std::path::Path; use uucore::display::Quotable; use uucore::error::{FromIo, UIoError, UResult, USimpleError, UUsageError}; -use uucore::parse_size::{parse_size, ParseSizeError}; +use uucore::parse_size::{parse_size_max, ParseSizeError}; use uucore::uio_error; use uucore::{format_usage, help_about, help_section, help_usage}; @@ -419,8 +419,7 @@ impl NumberType { let parts: Vec<&str> = s.split('/').collect(); match &parts[..] { [n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; if num_chunks > 0 { Ok(Self::Bytes(num_chunks)) @@ -429,32 +428,26 @@ impl NumberType { } } ["l", n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::Lines(num_chunks)) } ["l", k_str, n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = k_str - .parse() + let chunk_number = parse_size_max(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; Ok(Self::KthLines(chunk_number, num_chunks)) } ["r", n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; Ok(Self::RoundRobin(num_chunks)) } ["r", k_str, n_str] => { - let num_chunks = n_str - .parse() + let num_chunks = parse_size_max(n_str) .map_err(|_| NumberTypeError::NumberOfChunks(n_str.to_string()))?; - let chunk_number = k_str - .parse() + let chunk_number = parse_size_max(k_str) .map_err(|_| NumberTypeError::ChunkNumber(k_str.to_string()))?; Ok(Self::KthRoundRobin(chunk_number, num_chunks)) } @@ -523,7 +516,7 @@ impl Strategy { error: fn(ParseSizeError) -> StrategyError, ) -> Result { let s = matches.get_one::(option).unwrap(); - let n = parse_size(s).map_err(error)?; + let n = parse_size_max(s).map_err(error)?; if n > 0 { Ok(strategy(n)) } else { @@ -542,7 +535,7 @@ impl Strategy { matches.value_source(OPT_NUMBER) == Some(ValueSource::CommandLine), ) { (Some(v), false, false, false, false) => { - let v = parse_size(v).map_err(|_| { + let v = parse_size_max(v).map_err(|_| { StrategyError::Lines(ParseSizeError::ParseFailure(v.to_string())) })?; Ok(Self::Lines(v)) @@ -687,7 +680,6 @@ impl Settings { if additional_suffix.contains('/') { return Err(SettingsError::SuffixContainsSeparator(additional_suffix)); } - let strategy = Strategy::from(matches, obs_lines).map_err(SettingsError::Strategy)?; let (suffix_type, suffix_start) = suffix_type_from(matches)?; let suffix_length_str = matches.get_one::(OPT_SUFFIX_LENGTH).unwrap(); diff --git a/src/uucore/src/lib/parser/parse_size.rs b/src/uucore/src/lib/parser/parse_size.rs index 5f64afcd8..63039e609 100644 --- a/src/uucore/src/lib/parser/parse_size.rs +++ b/src/uucore/src/lib/parser/parse_size.rs @@ -7,6 +7,7 @@ use std::error::Error; use std::fmt; +use std::num::IntErrorKind; use crate::display::Quotable; @@ -201,8 +202,10 @@ impl<'parser> Parser<'parser> { radix: u32, original_size: &str, ) -> Result { - u64::from_str_radix(numeric_string, radix) - .map_err(|_| ParseSizeError::ParseFailure(original_size.to_string())) + u64::from_str_radix(numeric_string, radix).map_err(|e| match e.kind() { + IntErrorKind::PosOverflow => ParseSizeError::size_too_big(original_size), + _ => ParseSizeError::ParseFailure(original_size.to_string()), + }) } } @@ -232,6 +235,23 @@ pub fn parse_size(size: &str) -> Result { Parser::default().parse(size) } +/// Same as `parse_size()`, except returns `u64::MAX` on overflow +/// GNU lib/coreutils include similar functionality +/// and GNU test suite checks this behavior for some utils +pub fn parse_size_max(size: &str) -> Result { + let result = Parser::default().parse(size); + match result { + Ok(_) => result, + Err(error) => { + if let ParseSizeError::SizeTooBig(_) = error { + Ok(u64::MAX) + } else { + Err(error) + } + } + } +} + #[derive(Debug, PartialEq, Eq)] pub enum ParseSizeError { InvalidSuffix(String), // Suffix @@ -392,6 +412,14 @@ mod tests { ); } + #[test] + #[cfg(not(target_pointer_width = "128"))] + fn overflow_to_max_x64() { + assert_eq!(Ok(u64::MAX), parse_size_max("18446744073709551616")); + assert_eq!(Ok(u64::MAX), parse_size_max("10000000000000000000000")); + assert_eq!(Ok(u64::MAX), parse_size_max("1Y")); + } + #[test] fn invalid_suffix() { let test_strings = ["5mib", "1eb", "1H"];