Skip to content

Commit

Permalink
du: implement files0-from
Browse files Browse the repository at this point in the history
Should make tests/du/files0-from-dir pass
  • Loading branch information
sylvestre committed Dec 24, 2023
1 parent f2ca22e commit afbdaa3
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 8 deletions.
59 changes: 52 additions & 7 deletions src/uu/du/src/du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ mod options {
pub const INODES: &str = "inodes";
pub const EXCLUDE: &str = "exclude";
pub const EXCLUDE_FROM: &str = "exclude-from";
pub const FILES0_FROM: &str = "files0-from";
pub const VERBOSE: &str = "verbose";
pub const FILE: &str = "FILE";
}
Expand Down Expand Up @@ -587,6 +588,37 @@ pub fn div_ceil(a: u64, b: u64) -> u64 {
(a + b - 1) / b
}

fn read_files_from(file_name: &str) -> Result<Vec<PathBuf>, std::io::Error> {
let reader: Box<dyn BufRead> = if file_name == "-" {
// Read from standard input
Box::new(BufReader::new(std::io::stdin()))
} else {
// First, check if the file_name is a directory
let path = PathBuf::from(file_name);
if path.is_dir() {
// Return an error if it's a directory
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("{}: read error: Is a directory", file_name),
));
}

// Read from a file
Box::new(BufReader::new(File::open(file_name)?))
};

let mut paths = Vec::new();

for line in reader.split(b'\0') {
let path = line?;
if !path.is_empty() {
paths.push(PathBuf::from(String::from_utf8_lossy(&path).to_string()));
}
}

Ok(paths)
}

#[uucore::main]
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Expand All @@ -601,13 +633,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
summarize,
)?;

let files = match matches.get_one::<String>(options::FILE) {
Some(_) => matches
.get_many::<String>(options::FILE)
.unwrap()
.map(PathBuf::from)
.collect(),
None => vec![PathBuf::from(".")],
let files = if let Some(file_from) = matches.get_one::<String>(options::FILES0_FROM) {
// Read file paths from the specified file, separated by null characters
read_files_from(file_from)?
} else {
match matches.get_one::<String>(options::FILE) {
Some(_) => matches
.get_many::<String>(options::FILE)
.unwrap()
.map(PathBuf::from)
.collect(),
None => vec![PathBuf::from(".")],
}
};

let time = matches.contains_id(options::TIME).then(|| {
Expand Down Expand Up @@ -954,6 +991,14 @@ pub fn uu_app() -> Command {
.help("exclude files that match any pattern in FILE")
.action(ArgAction::Append)
)
.arg(
Arg::new(options::FILES0_FROM)
.long("files0-from")
.value_name("FILE")
.value_hint(clap::ValueHint::FilePath)
.help("summarize device usage of the NUL-terminated file names specified in file F; if F is -, then read names from standard input")
.action(ArgAction::Append)
)
.arg(
Arg::new(options::TIME)
.long(options::TIME)
Expand Down
60 changes: 59 additions & 1 deletion tests/by-util/test_du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink
// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink testfile1 testfile2 filelist testdir
#[cfg(not(windows))]
use regex::Regex;
#[cfg(not(windows))]
Expand Down Expand Up @@ -935,3 +935,61 @@ fn test_du_symlink_multiple_fail() {
assert_eq!(result.code(), 1);
result.stdout_contains("4\tfile1\n");
}

#[test]
// Disable on Windows because of different path separators and handling of null characters
#[cfg(not(target_os = "windows"))]
fn test_du_files0_from() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;

let mut file1 = at.make_file("testfile1");
file1.write_all(b"content1").unwrap();
let mut file2 = at.make_file("testfile2");
file2.write_all(b"content2").unwrap();

at.mkdir_all("testdir");
let mut file3 = at.make_file("testdir/testfile3");
file3.write_all(b"content3").unwrap();

let mut file_list = at.make_file("filelist");
write!(file_list, "testfile1\0testfile2\0testdir\0").unwrap();

ts.ucmd()
.arg("--files0-from=filelist")
.succeeds()
.stdout_contains("testfile1")
.stdout_contains("testfile2")
.stdout_contains("testdir");
}

#[test]
fn test_du_files0_from_stdin() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;

let mut file1 = at.make_file("testfile1");
file1.write_all(b"content1").unwrap();
let mut file2 = at.make_file("testfile2");
file2.write_all(b"content2").unwrap();

let input = "testfile1\0testfile2\0";

ts.ucmd()
.arg("--files0-from=-")
.pipe_in(input)
.succeeds()
.stdout_contains("testfile1")
.stdout_contains("testfile2");
}

#[test]
fn test_du_files0_from_dir() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;

at.mkdir("dir");

let result = ts.ucmd().arg("--files0-from=dir").fails();
assert_eq!(result.stderr_str(), "du: dir: read error: Is a directory\n");
}

0 comments on commit afbdaa3

Please sign in to comment.