Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions src/uu/du/src/du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ enum Deref {
enum SizeFormat {
HumanDecimal,
HumanBinary,
BlockSize(u64),
BlockSize(u64, Option<String>),
}

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
Expand Down Expand Up @@ -301,19 +301,37 @@ fn get_file_info(path: &Path, _metadata: &Metadata) -> Option<FileInfo> {
result
}

fn read_block_size(s: Option<&str>) -> UResult<u64> {
fn read_block_size(s: Option<&str>) -> UResult<(u64, Option<String>)> {
let vars = ["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"];
if let Some(s) = s {
parse_size_u64(s)
.map(|block_size| (block_size, suffix_from_parsed_block_size(s)))
.map_err(|e| USimpleError::new(1, format_error_message(&e, s, options::BLOCK_SIZE)))
} else if let Some(bytes) =
parse_block_size::block_size_from_env(&["DU_BLOCK_SIZE", "BLOCK_SIZE", "BLOCKSIZE"]).found()
{
Ok(bytes)
} else if let Some(block_size) = parse_block_size::block_size_from_env(&vars).found() {
let suffix = vars
.into_iter()
.find_map(|var| env::var(var).ok())
.and_then(|value| suffix_from_parsed_block_size(&value));
Ok((block_size, suffix))
} else {
Ok(parse_block_size::default_block_size())
Ok((parse_block_size::default_block_size(), None))
}
}

fn suffix_from_parsed_block_size(s: &str) -> Option<String> {
let mut chars = s.chars();
let unit = chars.next()?.to_ascii_uppercase();
if !unit.is_ascii_alphabetic() || unit == 'B' {
return None;
}

Some(match chars.as_str() {
"" | "D" => unit.to_string(),
"B" if unit == 'K' => "kB".to_string(),
suffix => format!("{unit}{suffix}"),
})
}

#[cfg(all(unix, not(target_os = "redox")))]
fn time_from_raw_stat(
entry_stat: &uucore::safe_traversal::Metadata,
Expand Down Expand Up @@ -885,7 +903,7 @@ impl StatPrinter {
}

fn convert_size(&self, size: u64) -> String {
match self.size_format {
match &self.size_format {
SizeFormat::HumanDecimal => uucore::format::human::human_readable(
size,
uucore::format::human::SizeFormat::Decimal,
Expand All @@ -894,12 +912,16 @@ impl StatPrinter {
size,
uucore::format::human::SizeFormat::Binary,
),
SizeFormat::BlockSize(block_size) => {
SizeFormat::BlockSize(block_size, suffix) => {
if self.inodes {
// we ignore block size (-B) with --inodes
size.to_string()
} else {
size.div_ceil(block_size).to_string()
let blocks = size.div_ceil(*block_size);
match suffix {
Some(suffix) => format!("{blocks}{suffix}"),
None => blocks.to_string(),
}
}
}
}
Expand Down Expand Up @@ -994,27 +1016,27 @@ fn get_size_format_flag_arg_index_if_present(matches: &ArgMatches, arg: &str) ->

fn parse_block_size_arg_or_default_fallback(matches: &ArgMatches) -> UResult<SizeFormat> {
let block_size_str = matches.get_one::<String>(options::BLOCK_SIZE);
let block_size = read_block_size(block_size_str.map(AsRef::as_ref))?;
let (block_size, suffix) = read_block_size(block_size_str.map(AsRef::as_ref))?;
if block_size == 0 {
return Err(std::io::Error::other(translate!("du-error-invalid-block-size-argument", "option" => options::BLOCK_SIZE, "value" => block_size_str.map_or("???BUG", |v| v).quote()))
.into());
}
Ok(SizeFormat::BlockSize(block_size))
Ok(SizeFormat::BlockSize(block_size, suffix))
}

fn parse_size_format(matches: &ArgMatches) -> UResult<SizeFormat> {
let block_size_value_or_default_fallback = parse_block_size_arg_or_default_fallback(matches)?;
let candidates = [
(
SizeFormat::BlockSize(1),
SizeFormat::BlockSize(1, None),
get_size_format_flag_arg_index_if_present(matches, options::BYTES),
),
(
SizeFormat::BlockSize(1024),
SizeFormat::BlockSize(1024, None),
get_size_format_flag_arg_index_if_present(matches, options::BLOCK_SIZE_1K),
),
(
SizeFormat::BlockSize(1024 * 1024),
SizeFormat::BlockSize(1024 * 1024, None),
get_size_format_flag_arg_index_if_present(matches, options::BLOCK_SIZE_1M),
),
(
Expand Down Expand Up @@ -1601,7 +1623,7 @@ mod test_du {
fn test_read_block_size() {
let test_data = [Some("1024".to_string()), Some("K".to_string()), None];
for it in &test_data {
assert!(matches!(read_block_size(it.as_deref()), Ok(1024)));
assert!(matches!(read_block_size(it.as_deref()), Ok((1024, _))));
}
}
}
30 changes: 29 additions & 1 deletion tests/by-util/test_du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,34 @@ fn test_du_env_block_size_hierarchy() {
assert_eq!(expected, result2);
}

#[test]
fn test_du_suffix_only_block_size() {
let ts = TestScenario::new(util_name!());
let file = "file";
ts.fixtures.write(file, "x");

for (arg, env, expected) in [
(Some("-BM"), None, "1M"),
(Some("-B1M"), None, "1"),
(None, Some(("DU_BLOCK_SIZE", "M")), "1M"),
(None, Some(("BLOCK_SIZE", "KB")), "1kB"),
(None, Some(("BLOCKSIZE", "MiB")), "1MiB"),
(None, Some(("DU_BLOCK_SIZE", "1M")), "1"),
] {
let mut cmd = ts.ucmd();
if let Some(arg) = arg {
cmd.arg(arg);
}
if let Some((var, value)) = env {
cmd.env(var, value);
}
cmd.arg("--apparent-size")
.arg(file)
.succeeds()
.stdout_only(format!("{expected}\t{file}\n"));
}
}

#[test]
fn test_du_binary_block_size() {
let ts = TestScenario::new(util_name!());
Expand Down Expand Up @@ -2104,7 +2132,7 @@ fn test_du_inodes_blocksize_ineffective() {
let at = &ts.fixtures;
let fpath = at.plus("test.txt");
at.touch(fpath);
for method in ["-B3", "--block-size=3"] {
for method in ["-B3", "--block-size=3", "-BM"] {
// No warning
ts.ucmd()
.arg(method)
Expand Down
Loading