mirror of
https://github.com/uutils/coreutils.git
synced 2025-07-07 21:45:01 +00:00
Merge pull request #7760 from Qelxiros/7670-tail-hex-formatting
tail hex parsing, remove fundu dependency
This commit is contained in:
parent
0125bbc2b4
commit
044b33d8cb
10 changed files with 125 additions and 91 deletions
|
@ -20,7 +20,6 @@ exacl
|
|||
filetime
|
||||
formatteriteminfo
|
||||
fsext
|
||||
fundu
|
||||
getopts
|
||||
getrandom
|
||||
globset
|
||||
|
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -1030,21 +1030,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fundu"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ce12752fc64f35be3d53e0a57017cd30970f0cffd73f62c791837d8845badbd"
|
||||
dependencies = [
|
||||
"fundu-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fundu-core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e463452e2d8b7600d38dcea1ed819773a57f0d710691bfc78db3961bd3f4c3ba"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
|
@ -3372,7 +3357,6 @@ name = "uu_tail"
|
|||
version = "0.0.30"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fundu",
|
||||
"libc",
|
||||
"memchr",
|
||||
"notify",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coreutils (uutils)
|
||||
# * see the repository LICENSE, README, and CONTRIBUTING files for more information
|
||||
|
||||
# spell-checker:ignore (libs) bigdecimal datetime serde bincode fundu gethostid kqueue libselinux mangen memmap procfs uuhelp startswith constness expl
|
||||
# spell-checker:ignore (libs) bigdecimal datetime serde bincode gethostid kqueue libselinux mangen memmap procfs uuhelp startswith constness expl
|
||||
|
||||
[package]
|
||||
name = "coreutils"
|
||||
|
@ -295,7 +295,6 @@ filetime = "0.2.23"
|
|||
fnv = "1.0.7"
|
||||
fs_extra = "1.3.0"
|
||||
fts-sys = "0.2.16"
|
||||
fundu = "2.0.0"
|
||||
gcd = "2.3"
|
||||
glob = "0.3.1"
|
||||
half = "2.4.1"
|
||||
|
|
|
@ -5,6 +5,7 @@ use uucore::parser::parse_time;
|
|||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(s) = std::str::from_utf8(data) {
|
||||
_ = parse_time::from_str(s);
|
||||
_ = parse_time::from_str(s, true);
|
||||
_ = parse_time::from_str(s, false);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@ fn sleep(args: &[&str]) -> UResult<()> {
|
|||
|
||||
let sleep_dur = args
|
||||
.iter()
|
||||
.filter_map(|input| match parse_time::from_str(input) {
|
||||
.filter_map(|input| match parse_time::from_str(input, true) {
|
||||
Ok(duration) => Some(duration),
|
||||
Err(error) => {
|
||||
arg_error = true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# spell-checker:ignore (libs) kqueue fundu
|
||||
# spell-checker:ignore (libs) kqueue
|
||||
[package]
|
||||
name = "uu_tail"
|
||||
description = "tail ~ (uutils) display the last lines of input"
|
||||
|
@ -25,7 +25,6 @@ memchr = { workspace = true }
|
|||
notify = { workspace = true }
|
||||
uucore = { workspace = true, features = ["parser"] }
|
||||
same-file = { workspace = true }
|
||||
fundu = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { workspace = true, features = [
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (ToDO) kqueue Signum fundu
|
||||
// spell-checker:ignore (ToDO) kqueue Signum
|
||||
|
||||
use crate::paths::Input;
|
||||
use crate::{Quotable, parse, platform};
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
|
||||
use fundu::{DurationParser, SaturatingInto};
|
||||
use same_file::Handle;
|
||||
use std::ffi::OsString;
|
||||
use std::io::IsTerminal;
|
||||
use std::time::Duration;
|
||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||
use uucore::parser::parse_size::{ParseSizeError, parse_size_u64};
|
||||
use uucore::parser::parse_time;
|
||||
use uucore::parser::shortcut_value_parser::ShortcutValueParser;
|
||||
use uucore::{format_usage, help_about, help_usage, show_warning};
|
||||
|
||||
|
@ -228,22 +228,9 @@ impl Settings {
|
|||
};
|
||||
|
||||
if let Some(source) = matches.get_one::<String>(options::SLEEP_INT) {
|
||||
// Advantage of `fundu` over `Duration::(try_)from_secs_f64(source.parse().unwrap())`:
|
||||
// * doesn't panic on errors like `Duration::from_secs_f64` would.
|
||||
// * no precision loss, rounding errors or other floating point problems.
|
||||
// * evaluates to `Duration::MAX` if the parsed number would have exceeded
|
||||
// `DURATION::MAX` or `infinity` was given
|
||||
// * not applied here but it supports customizable time units and provides better error
|
||||
// messages
|
||||
settings.sleep_sec = match DurationParser::without_time_units().parse(source) {
|
||||
Ok(duration) => SaturatingInto::<Duration>::saturating_into(duration),
|
||||
Err(_) => {
|
||||
return Err(UUsageError::new(
|
||||
1,
|
||||
format!("invalid number of seconds: '{source}'"),
|
||||
));
|
||||
}
|
||||
}
|
||||
settings.sleep_sec = parse_time::from_str(source, false).map_err(|_| {
|
||||
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(s) = matches.get_one::<String>(options::MAX_UNCHANGED_STATS) {
|
||||
|
|
|
@ -71,17 +71,15 @@ impl Config {
|
|||
|
||||
let kill_after = match options.get_one::<String>(options::KILL_AFTER) {
|
||||
None => None,
|
||||
Some(kill_after) => match parse_time::from_str(kill_after) {
|
||||
Some(kill_after) => match parse_time::from_str(kill_after, true) {
|
||||
Ok(k) => Some(k),
|
||||
Err(err) => return Err(UUsageError::new(ExitStatus::TimeoutFailed.into(), err)),
|
||||
},
|
||||
};
|
||||
|
||||
let duration =
|
||||
match parse_time::from_str(options.get_one::<String>(options::DURATION).unwrap()) {
|
||||
Ok(duration) => duration,
|
||||
Err(err) => return Err(UUsageError::new(ExitStatus::TimeoutFailed.into(), err)),
|
||||
};
|
||||
parse_time::from_str(options.get_one::<String>(options::DURATION).unwrap(), true)
|
||||
.map_err(|err| UUsageError::new(ExitStatus::TimeoutFailed.into(), err))?;
|
||||
|
||||
let preserve_status: bool = options.get_flag(options::PRESERVE_STATUS);
|
||||
let foreground = options.get_flag(options::FOREGROUND);
|
||||
|
|
|
@ -25,7 +25,7 @@ use std::time::Duration;
|
|||
/// one hundred twenty three seconds or "4.5d" meaning four and a half
|
||||
/// days. If no unit is specified, the unit is assumed to be seconds.
|
||||
///
|
||||
/// The only allowed suffixes are
|
||||
/// If `allow_suffixes` is true, the allowed suffixes are
|
||||
///
|
||||
/// * "s" for seconds,
|
||||
/// * "m" for minutes,
|
||||
|
@ -48,10 +48,12 @@ use std::time::Duration;
|
|||
/// ```rust
|
||||
/// use std::time::Duration;
|
||||
/// use uucore::parser::parse_time::from_str;
|
||||
/// assert_eq!(from_str("123"), Ok(Duration::from_secs(123)));
|
||||
/// assert_eq!(from_str("2d"), Ok(Duration::from_secs(60 * 60 * 24 * 2)));
|
||||
/// assert_eq!(from_str("123", true), Ok(Duration::from_secs(123)));
|
||||
/// assert_eq!(from_str("123", false), Ok(Duration::from_secs(123)));
|
||||
/// assert_eq!(from_str("2d", true), Ok(Duration::from_secs(60 * 60 * 24 * 2)));
|
||||
/// assert!(from_str("2d", false).is_err());
|
||||
/// ```
|
||||
pub fn from_str(string: &str) -> Result<Duration, String> {
|
||||
pub fn from_str(string: &str, allow_suffixes: bool) -> Result<Duration, String> {
|
||||
// TODO: Switch to Duration::NANOSECOND if that ever becomes stable
|
||||
// https://github.com/rust-lang/rust/issues/57391
|
||||
const NANOSECOND_DURATION: Duration = Duration::from_nanos(1);
|
||||
|
@ -63,7 +65,11 @@ pub fn from_str(string: &str) -> Result<Duration, String> {
|
|||
let num = match num_parser::parse(
|
||||
string,
|
||||
ParseTarget::Duration,
|
||||
&[('s', 1), ('m', 60), ('h', 60 * 60), ('d', 60 * 60 * 24)],
|
||||
if allow_suffixes {
|
||||
&[('s', 1), ('m', 60), ('h', 60 * 60), ('d', 60 * 60 * 24)]
|
||||
} else {
|
||||
&[]
|
||||
},
|
||||
) {
|
||||
Ok(ebd) | Err(ExtendedParserError::Overflow(ebd)) => ebd,
|
||||
Err(ExtendedParserError::Underflow(_)) => return Ok(NANOSECOND_DURATION),
|
||||
|
@ -105,20 +111,26 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_no_units() {
|
||||
assert_eq!(from_str("123"), Ok(Duration::from_secs(123)));
|
||||
assert_eq!(from_str("123", true), Ok(Duration::from_secs(123)));
|
||||
assert_eq!(from_str("123", false), Ok(Duration::from_secs(123)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_units() {
|
||||
assert_eq!(from_str("2d"), Ok(Duration::from_secs(60 * 60 * 24 * 2)));
|
||||
assert_eq!(
|
||||
from_str("2d", true),
|
||||
Ok(Duration::from_secs(60 * 60 * 24 * 2))
|
||||
);
|
||||
assert!(from_str("2d", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overflow() {
|
||||
// u64 seconds overflow (in Duration)
|
||||
assert_eq!(from_str("9223372036854775808d"), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("9223372036854775808d", true), Ok(Duration::MAX));
|
||||
// ExtendedBigDecimal overflow
|
||||
assert_eq!(from_str("1e92233720368547758080"), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("1e92233720368547758080", false), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("1e92233720368547758080", false), Ok(Duration::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -128,87 +140,143 @@ mod tests {
|
|||
const NANOSECOND_DURATION: Duration = Duration::from_nanos(1);
|
||||
|
||||
// ExtendedBigDecimal underflow
|
||||
assert_eq!(from_str("1e-92233720368547758080"), Ok(NANOSECOND_DURATION));
|
||||
// nanoseconds underflow (in Duration)
|
||||
assert_eq!(from_str("0.0000000001"), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-10"), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("9e-10"), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-9"), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1.9e-9"), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("2e-9"), Ok(Duration::from_nanos(2)));
|
||||
assert_eq!(
|
||||
from_str("1e-92233720368547758080", true),
|
||||
Ok(NANOSECOND_DURATION)
|
||||
);
|
||||
// nanoseconds underflow (in Duration, true)
|
||||
assert_eq!(from_str("0.0000000001", true), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-10", true), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("9e-10", true), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-9", true), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1.9e-9", true), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("2e-9", true), Ok(Duration::from_nanos(2)));
|
||||
|
||||
// ExtendedBigDecimal underflow
|
||||
assert_eq!(
|
||||
from_str("1e-92233720368547758080", false),
|
||||
Ok(NANOSECOND_DURATION)
|
||||
);
|
||||
// nanoseconds underflow (in Duration, false)
|
||||
assert_eq!(from_str("0.0000000001", false), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-10", false), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("9e-10", false), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1e-9", false), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("1.9e-9", false), Ok(NANOSECOND_DURATION));
|
||||
assert_eq!(from_str("2e-9", false), Ok(Duration::from_nanos(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero() {
|
||||
assert_eq!(from_str("0e-9"), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0e-100"), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0e-92233720368547758080"), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0.000000000000000000000"), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0e-9", true), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0e-100", true), Ok(Duration::ZERO));
|
||||
assert_eq!(
|
||||
from_str("0e-92233720368547758080", true),
|
||||
Ok(Duration::ZERO)
|
||||
);
|
||||
assert_eq!(
|
||||
from_str("0.000000000000000000000", true),
|
||||
Ok(Duration::ZERO)
|
||||
);
|
||||
|
||||
assert_eq!(from_str("0e-9", false), Ok(Duration::ZERO));
|
||||
assert_eq!(from_str("0e-100", false), Ok(Duration::ZERO));
|
||||
assert_eq!(
|
||||
from_str("0e-92233720368547758080", false),
|
||||
Ok(Duration::ZERO)
|
||||
);
|
||||
assert_eq!(
|
||||
from_str("0.000000000000000000000", false),
|
||||
Ok(Duration::ZERO)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_float() {
|
||||
assert_eq!(
|
||||
from_str("0x1.1p-1"),
|
||||
from_str("0x1.1p-1", true),
|
||||
Ok(Duration::from_secs_f64(0.53125f64))
|
||||
);
|
||||
assert_eq!(
|
||||
from_str("0x1.1p-1d"),
|
||||
from_str("0x1.1p-1", false),
|
||||
Ok(Duration::from_secs_f64(0.53125f64))
|
||||
);
|
||||
assert_eq!(
|
||||
from_str("0x1.1p-1d", true),
|
||||
Ok(Duration::from_secs_f64(0.53125f64 * 3600.0 * 24.0))
|
||||
);
|
||||
assert_eq!(from_str("0xfh"), Ok(Duration::from_secs(15 * 3600)));
|
||||
assert_eq!(from_str("0xfh", true), Ok(Duration::from_secs(15 * 3600)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_empty() {
|
||||
assert!(from_str("").is_err());
|
||||
assert!(from_str("", true).is_err());
|
||||
assert!(from_str("", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_invalid_unit() {
|
||||
assert!(from_str("123X").is_err());
|
||||
assert!(from_str("123X", true).is_err());
|
||||
assert!(from_str("123X", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_multi_bytes_characters() {
|
||||
assert!(from_str("10€").is_err());
|
||||
assert!(from_str("10€", true).is_err());
|
||||
assert!(from_str("10€", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_invalid_magnitude() {
|
||||
assert!(from_str("12abc3s").is_err());
|
||||
assert!(from_str("12abc3s", true).is_err());
|
||||
assert!(from_str("12abc3s", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_only_point() {
|
||||
assert!(from_str(".", true).is_err());
|
||||
assert!(from_str(".", false).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative() {
|
||||
assert!(from_str("-1").is_err());
|
||||
assert!(from_str("-1", true).is_err());
|
||||
assert!(from_str("-1", false).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));
|
||||
assert_eq!(from_str("inf", true), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("infinity", true), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("infinityh", true), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("INF", true), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("INFs", true), Ok(Duration::MAX));
|
||||
|
||||
assert_eq!(from_str("inf", false), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("infinity", false), Ok(Duration::MAX));
|
||||
assert_eq!(from_str("INF", false), 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());
|
||||
assert!(from_str("nan", true).is_err());
|
||||
assert!(from_str("nans", true).is_err());
|
||||
assert!(from_str("-nanh", true).is_err());
|
||||
assert!(from_str("NAN", true).is_err());
|
||||
assert!(from_str("-NAN", true).is_err());
|
||||
|
||||
assert!(from_str("nan", false).is_err());
|
||||
assert!(from_str("NAN", false).is_err());
|
||||
assert!(from_str("-NAN", false).is_err());
|
||||
}
|
||||
|
||||
/// Test that capital letters are not allowed in suffixes.
|
||||
#[test]
|
||||
fn test_no_capital_letters() {
|
||||
assert!(from_str("1S").is_err());
|
||||
assert!(from_str("1M").is_err());
|
||||
assert!(from_str("1H").is_err());
|
||||
assert!(from_str("1D").is_err());
|
||||
assert!(from_str("INFD").is_err());
|
||||
assert!(from_str("1S", true).is_err());
|
||||
assert!(from_str("1M", true).is_err());
|
||||
assert!(from_str("1H", true).is_err());
|
||||
assert!(from_str("1D", true).is_err());
|
||||
assert!(from_str("INFD", true).is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4444,7 +4444,6 @@ fn test_follow_when_files_are_pointing_to_same_relative_file_and_file_stays_same
|
|||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::exponent_exceed_float_max("1.0e100000")]
|
||||
#[case::underscore_delimiter("1_000")]
|
||||
#[case::only_point(".")]
|
||||
#[case::space_in_primes("' '")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue