diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index b7c53eb72..e466b8afe 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -28,6 +28,7 @@ use std::os::windows::io::AsRawHandle; #[cfg(windows)] use std::path::Path; use std::path::PathBuf; +use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::InvalidEncodingHandling; @@ -56,6 +57,7 @@ mod options { pub const BLOCK_SIZE_1M: &str = "m"; pub const SEPARATE_DIRS: &str = "S"; pub const SUMMARIZE: &str = "s"; + pub const THRESHOLD: &str = "threshold"; pub const SI: &str = "si"; pub const TIME: &str = "time"; pub const TIME_STYLE: &str = "time-style"; @@ -510,6 +512,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(options::ONE_FILE_SYSTEM) .help("skip directories on different file systems") ) + .arg( + Arg::with_name(options::THRESHOLD) + .short("t") + .long(options::THRESHOLD) + .alias("th") + .value_name("SIZE") + .number_of_values(1) + .allow_hyphen_values(true) + .help("exclude entries smaller than SIZE if positive, \ + or entries greater than SIZE if negative") + ) // .arg( // Arg::with_name("") // .short("x") @@ -586,6 +599,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap(); + let threshold = matches.value_of(options::THRESHOLD).map(|s| { + Threshold::from_str(s) + .unwrap_or_else(|e| crash!(1, "{}", format_error_message(e, s, options::THRESHOLD))) + }); + let multiplier: u64 = if matches.is_present(options::SI) { 1000 } else { @@ -654,6 +672,11 @@ Try '{} --help' for more information.", // See: http://linux.die.net/man/2/stat stat.blocks * 512 }; + + if threshold.map_or(false, |threshold| threshold.should_exclude(size)) { + continue; + } + if matches.is_present(options::TIME) { let tm = { let secs = { @@ -720,6 +743,37 @@ Try '{} --help' for more information.", 0 } +#[derive(Clone, Copy)] +enum Threshold { + Lower(u64), + Upper(u64), +} + +impl FromStr for Threshold { + type Err = ParseSizeError; + + fn from_str(s: &str) -> std::result::Result { + let offset = if s.starts_with(&['-', '+'][..]) { 1 } else { 0 }; + + let size = u64::try_from(parse_size(&s[offset..])?).unwrap(); + + if s.starts_with('-') { + Ok(Threshold::Upper(size)) + } else { + Ok(Threshold::Lower(size)) + } + } +} + +impl Threshold { + fn should_exclude(&self, size: u64) -> bool { + match *self { + Threshold::Upper(threshold) => size > threshold, + Threshold::Lower(threshold) => size < threshold, + } + } +} + fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String { // NOTE: // GNU's du echos affected flag, -B or --block-size (-t or --threshold), depending user's selection diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 8d1267423..93875ae51 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -80,19 +80,22 @@ fn _du_basics_subdir(s: &str) { #[test] fn test_du_invalid_size() { - new_ucmd!() - .arg("--block-size=1fb4t") - .arg("/tmp") - .fails() - .code_is(1) - .stderr_only("du: invalid --block-size argument '1fb4t'"); - #[cfg(not(target_pointer_width = "128"))] - new_ucmd!() - .arg("--block-size=1Y") - .arg("/tmp") - .fails() - .code_is(1) - .stderr_only("du: --block-size argument '1Y' too large"); + let args = &["block-size", "threshold"]; + for s in args { + new_ucmd!() + .arg(format!("--{}=1fb4t", s)) + .arg("/tmp") + .fails() + .code_is(1) + .stderr_only(format!("du: invalid --{} argument '1fb4t'", s)); + #[cfg(not(target_pointer_width = "128"))] + new_ucmd!() + .arg(format!("--{}=1Y", s)) + .arg("/tmp") + .fails() + .code_is(1) + .stderr_only(format!("du: --{} argument '1Y' too large", s)); + } } #[test] @@ -351,3 +354,24 @@ fn test_du_one_file_system() { } _du_basics_subdir(result.stdout_str()); } + +#[test] +fn test_du_threshold() { + let scene = TestScenario::new(util_name!()); + + let threshold = if cfg!(windows) { "7K" } else { "10K" }; + + scene + .ucmd() + .arg(format!("--threshold={}", threshold)) + .succeeds() + .stdout_contains("links") + .stdout_does_not_contain("deeper"); + + scene + .ucmd() + .arg(format!("--threshold=-{}", threshold)) + .succeeds() + .stdout_does_not_contain("links") + .stdout_contains("deeper"); +}