mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 15:22:38 +00:00
commit
91078cfcb3
2 changed files with 201 additions and 12 deletions
|
@ -10,6 +10,7 @@ extern crate uucore;
|
||||||
|
|
||||||
use chrono::prelude::DateTime;
|
use chrono::prelude::DateTime;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
use clap::ArgMatches;
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -63,6 +64,7 @@ mod options {
|
||||||
pub const TIME_STYLE: &str = "time-style";
|
pub const TIME_STYLE: &str = "time-style";
|
||||||
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
pub const ONE_FILE_SYSTEM: &str = "one-file-system";
|
||||||
pub const DEREFERENCE: &str = "dereference";
|
pub const DEREFERENCE: &str = "dereference";
|
||||||
|
pub const INODES: &str = "inodes";
|
||||||
pub const FILE: &str = "FILE";
|
pub const FILE: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +91,7 @@ struct Options {
|
||||||
separate_dirs: bool,
|
separate_dirs: bool,
|
||||||
one_file_system: bool,
|
one_file_system: bool,
|
||||||
dereference: bool,
|
dereference: bool,
|
||||||
|
inodes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
|
@ -102,6 +105,7 @@ struct Stat {
|
||||||
is_dir: bool,
|
is_dir: bool,
|
||||||
size: u64,
|
size: u64,
|
||||||
blocks: u64,
|
blocks: u64,
|
||||||
|
inodes: u64,
|
||||||
inode: Option<FileInfo>,
|
inode: Option<FileInfo>,
|
||||||
created: Option<u64>,
|
created: Option<u64>,
|
||||||
accessed: u64,
|
accessed: u64,
|
||||||
|
@ -127,6 +131,7 @@ impl Stat {
|
||||||
is_dir: metadata.is_dir(),
|
is_dir: metadata.is_dir(),
|
||||||
size: metadata.len(),
|
size: metadata.len(),
|
||||||
blocks: metadata.blocks() as u64,
|
blocks: metadata.blocks() as u64,
|
||||||
|
inodes: 1,
|
||||||
inode: Some(file_info),
|
inode: Some(file_info),
|
||||||
created: birth_u64(&metadata),
|
created: birth_u64(&metadata),
|
||||||
accessed: metadata.atime() as u64,
|
accessed: metadata.atime() as u64,
|
||||||
|
@ -144,6 +149,7 @@ impl Stat {
|
||||||
size: metadata.len(),
|
size: metadata.len(),
|
||||||
blocks: size_on_disk / 1024 * 2,
|
blocks: size_on_disk / 1024 * 2,
|
||||||
inode: file_info,
|
inode: file_info,
|
||||||
|
inodes: 1,
|
||||||
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
created: windows_creation_time_to_unix_time(metadata.creation_time()),
|
||||||
accessed: windows_time_to_unix_time(metadata.last_access_time()),
|
accessed: windows_time_to_unix_time(metadata.last_access_time()),
|
||||||
modified: windows_time_to_unix_time(metadata.last_write_time()),
|
modified: windows_time_to_unix_time(metadata.last_write_time()),
|
||||||
|
@ -257,6 +263,18 @@ fn read_block_size(s: Option<&str>) -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn choose_size(matches: &ArgMatches, stat: &Stat) -> u64 {
|
||||||
|
if matches.is_present(options::INODES) {
|
||||||
|
stat.inodes
|
||||||
|
} else if matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES) {
|
||||||
|
stat.size
|
||||||
|
} else {
|
||||||
|
// The st_blocks field indicates the number of blocks allocated to the file, 512-byte units.
|
||||||
|
// See: http://linux.die.net/man/2/stat
|
||||||
|
stat.blocks * 512
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this takes `my_stat` to avoid having to stat files multiple times.
|
// this takes `my_stat` to avoid having to stat files multiple times.
|
||||||
// XXX: this should use the impl Trait return type when it is stabilized
|
// XXX: this should use the impl Trait return type when it is stabilized
|
||||||
fn du(
|
fn du(
|
||||||
|
@ -307,6 +325,7 @@ fn du(
|
||||||
} else {
|
} else {
|
||||||
my_stat.size += this_stat.size;
|
my_stat.size += this_stat.size;
|
||||||
my_stat.blocks += this_stat.blocks;
|
my_stat.blocks += this_stat.blocks;
|
||||||
|
my_stat.inodes += 1;
|
||||||
if options.all {
|
if options.all {
|
||||||
stats.push(this_stat);
|
stats.push(this_stat);
|
||||||
}
|
}
|
||||||
|
@ -330,6 +349,7 @@ fn du(
|
||||||
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
|
if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
|
||||||
my_stat.size += stat.size;
|
my_stat.size += stat.size;
|
||||||
my_stat.blocks += stat.blocks;
|
my_stat.blocks += stat.blocks;
|
||||||
|
my_stat.inodes += stat.inodes;
|
||||||
}
|
}
|
||||||
options
|
options
|
||||||
.max_depth
|
.max_depth
|
||||||
|
@ -413,6 +433,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
separate_dirs: matches.is_present(options::SEPARATE_DIRS),
|
||||||
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
one_file_system: matches.is_present(options::ONE_FILE_SYSTEM),
|
||||||
dereference: matches.is_present(options::DEREFERENCE),
|
dereference: matches.is_present(options::DEREFERENCE),
|
||||||
|
inodes: matches.is_present(options::INODES),
|
||||||
};
|
};
|
||||||
|
|
||||||
let files = match matches.value_of(options::FILE) {
|
let files = match matches.value_of(options::FILE) {
|
||||||
|
@ -420,6 +441,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
None => vec!["."],
|
None => vec!["."],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if options.inodes
|
||||||
|
&& (matches.is_present(options::APPARENT_SIZE) || matches.is_present(options::BYTES))
|
||||||
|
{
|
||||||
|
show_warning!("options --apparent-size and -b are ineffective with --inodes")
|
||||||
|
}
|
||||||
|
|
||||||
let block_size = u64::try_from(read_block_size(matches.value_of(options::BLOCK_SIZE))).unwrap();
|
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| {
|
let threshold = matches.value_of(options::THRESHOLD).map(|s| {
|
||||||
|
@ -445,7 +472,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
convert_size_other
|
convert_size_other
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let convert_size = |size| convert_size_fn(size, multiplier, block_size);
|
let convert_size = |size: u64| {
|
||||||
|
if options.inodes {
|
||||||
|
size.to_string()
|
||||||
|
} else {
|
||||||
|
convert_size_fn(size, multiplier, block_size)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let time_format_str = match matches.value_of("time-style") {
|
let time_format_str = match matches.value_of("time-style") {
|
||||||
Some(s) => match s {
|
Some(s) => match s {
|
||||||
|
@ -488,15 +521,7 @@ Try '{} --help' for more information.",
|
||||||
let (_, len) = iter.size_hint();
|
let (_, len) = iter.size_hint();
|
||||||
let len = len.unwrap();
|
let len = len.unwrap();
|
||||||
for (index, stat) in iter.enumerate() {
|
for (index, stat) in iter.enumerate() {
|
||||||
let size = if matches.is_present(options::APPARENT_SIZE)
|
let size = choose_size(&matches, &stat);
|
||||||
|| matches.is_present(options::BYTES)
|
|
||||||
{
|
|
||||||
stat.size
|
|
||||||
} else {
|
|
||||||
// C's stat is such that each block is assume to be 512 bytes
|
|
||||||
// See: http://linux.die.net/man/2/stat
|
|
||||||
stat.blocks * 512
|
|
||||||
};
|
|
||||||
|
|
||||||
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
|
if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -629,8 +654,8 @@ pub fn uu_app() -> App<'static, 'static> {
|
||||||
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
|
.help("print sizes in human readable format (e.g., 1K 234M 2G)")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("inodes")
|
Arg::with_name(options::INODES)
|
||||||
.long("inodes")
|
.long(options::INODES)
|
||||||
.help(
|
.help(
|
||||||
"list inode usage information instead of block usage like --block-size=1K"
|
"list inode usage information instead of block usage like --block-size=1K"
|
||||||
)
|
)
|
||||||
|
|
|
@ -292,6 +292,77 @@ fn _du_dereference(s: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_du_inodes_basic() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let result = scene.ucmd().arg("--inodes").succeeds();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let result_reference = scene.cmd("du").arg("--inodes").run();
|
||||||
|
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
_du_inodes_basic(result.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn _du_inodes_basic(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"2\t.\\subdir\\deeper\\deeper_dir
|
||||||
|
4\t.\\subdir\\deeper
|
||||||
|
3\t.\\subdir\\links
|
||||||
|
8\t.\\subdir
|
||||||
|
11\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn _du_inodes_basic(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"2\t./subdir/deeper/deeper_dir
|
||||||
|
4\t./subdir/deeper
|
||||||
|
3\t./subdir/links
|
||||||
|
8\t./subdir
|
||||||
|
11\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_du_inodes() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--summarize")
|
||||||
|
.arg("--inodes")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_only("11\t.\n");
|
||||||
|
|
||||||
|
let result = scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--separate-dirs")
|
||||||
|
.arg("--inodes")
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
result.stdout_contains("3\t.\\subdir\\links\n");
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
result.stdout_contains("3\t./subdir/links\n");
|
||||||
|
result.stdout_contains("3\t.\n");
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let result_reference = scene.cmd("du").arg("--separate-dirs").arg("--inodes").run();
|
||||||
|
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_du_h_flag_empty_file() {
|
fn test_du_h_flag_empty_file() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
@ -419,3 +490,96 @@ fn test_du_threshold() {
|
||||||
.stdout_does_not_contain("links")
|
.stdout_does_not_contain("links")
|
||||||
.stdout_contains("deeper_dir");
|
.stdout_contains("deeper_dir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_du_apparent_size() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let result = scene.ucmd().arg("--apparent-size").succeeds();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let result_reference = scene.cmd("du").arg("--apparent-size").run();
|
||||||
|
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
_du_apparent_size(result.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn _du_apparent_size(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"1\t.\\subdir\\deeper\\deeper_dir
|
||||||
|
1\t.\\subdir\\deeper
|
||||||
|
6\t.\\subdir\\links
|
||||||
|
6\t.\\subdir
|
||||||
|
6\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(target_vendor = "apple")]
|
||||||
|
fn _du_apparent_size(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"1\t./subdir/deeper/deeper_dir
|
||||||
|
1\t./subdir/deeper
|
||||||
|
6\t./subdir/links
|
||||||
|
6\t./subdir
|
||||||
|
6\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
fn _du_apparent_size(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"1\t./subdir/deeper/deeper_dir
|
||||||
|
2\t./subdir/deeper
|
||||||
|
6\t./subdir/links
|
||||||
|
8\t./subdir
|
||||||
|
8\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(all(
|
||||||
|
not(target_vendor = "apple"),
|
||||||
|
not(target_os = "windows"),
|
||||||
|
not(target_os = "freebsd")
|
||||||
|
))]
|
||||||
|
fn _du_apparent_size(s: &str) {
|
||||||
|
assert_eq!(
|
||||||
|
s,
|
||||||
|
"5\t./subdir/deeper/deeper_dir
|
||||||
|
9\t./subdir/deeper
|
||||||
|
10\t./subdir/links
|
||||||
|
22\t./subdir
|
||||||
|
26\t.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_du_bytes() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
let result = scene.ucmd().arg("--bytes").succeeds();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let result_reference = scene.cmd("du").arg("--bytes").run();
|
||||||
|
assert_eq!(result.stdout_str(), result_reference.stdout_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
result.stdout_contains("5145\t.\\subdir\n");
|
||||||
|
#[cfg(target_vendor = "apple")]
|
||||||
|
result.stdout_contains("5625\t./subdir\n");
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
result.stdout_contains("7193\t./subdir\n");
|
||||||
|
#[cfg(all(
|
||||||
|
not(target_vendor = "apple"),
|
||||||
|
not(target_os = "windows"),
|
||||||
|
not(target_os = "freebsd")
|
||||||
|
))]
|
||||||
|
result.stdout_contains("21529\t./subdir\n");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue