1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

rm: Added descend messages for interactive mode Fixes #3817 (#3931)

Co-authored-by: Terts Diepraam <terts.diepraam@gmail.com>
This commit is contained in:
Pat Laster 2022-10-05 06:35:31 -05:00 committed by GitHub
parent e523a56dab
commit 493a2628d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 2 deletions

View file

@ -303,13 +303,36 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
}
} else {
let mut dirs: VecDeque<DirEntry> = VecDeque::new();
// The Paths to not descend into. We need to this because WalkDir doesn't have a way, afaik, to not descend into a directory
// So we have to just ignore paths as they come up if they start with a path we aren't descending into
let mut not_descended: Vec<PathBuf> = Vec::new();
for entry in WalkDir::new(path) {
'outer: for entry in WalkDir::new(path) {
match entry {
Ok(entry) => {
if options.interactive == InteractiveMode::Always {
for not_descend in &not_descended {
if entry.path().starts_with(not_descend) {
// We don't need to continue the rest of code in this loop if we are in a directory we don't want to descend into
continue 'outer;
}
}
}
let file_type = entry.file_type();
if file_type.is_dir() {
dirs.push_back(entry);
// If we are in Interactive Mode Always and the directory isn't empty we ask if we should descend else we push this directory onto dirs vector
if options.interactive == InteractiveMode::Always
&& fs::read_dir(entry.path()).unwrap().count() != 0
{
// If we don't descend we push this directory onto our not_descended vector else we push this directory onto dirs vector
if prompt_descend(entry.path()) {
dirs.push_back(entry);
} else {
not_descended.push(entry.path().to_path_buf());
}
} else {
dirs.push_back(entry);
}
} else {
had_err = remove_file(entry.path(), options).bitor(had_err);
}
@ -447,6 +470,10 @@ fn prompt_write_protected(path: &Path, is_dir: bool, options: &Options) -> bool
}
}
fn prompt_descend(path: &Path) -> bool {
prompt(&(format!("rm: descend into directory {}? ", path.quote())))
}
fn prompt_file(path: &Path, is_dir: bool) -> bool {
if is_dir {
prompt(&(format!("rm: remove directory {}? ", path.quote())))

View file

@ -353,6 +353,53 @@ fn test_rm_interactive_never() {
assert!(!at.file_exists(file_2));
}
#[test]
fn test_rm_descend_directory() {
// This test descends into each directory and deletes the files and folders inside of them
// This test will have the rm process asks 6 question and us answering Y to them will delete all the files and folders
use std::io::Write;
use std::process::Child;
// Needed for talking with stdin on platforms where CRLF or LF matters
const END_OF_LINE: &str = if cfg!(windows) { "\r\n" } else { "\n" };
let yes = format!("y{}", END_OF_LINE);
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let file_1 = "a/at.txt";
let file_2 = "a/b/bt.txt";
at.mkdir_all("a/b/");
at.touch(file_1);
at.touch(file_2);
let mut child: Child = scene.ucmd().arg("-ri").arg("a").run_no_wait();
// Needed so that we can talk to the rm program
let mut child_stdin = child.stdin.take().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child_stdin.write_all(yes.as_bytes()).unwrap();
child_stdin.flush().unwrap();
child.wait_with_output().unwrap();
assert!(!at.dir_exists("a/b"));
assert!(!at.dir_exists("a"));
assert!(!at.file_exists(file_1));
assert!(!at.file_exists(file_2));
}
#[test]
#[ignore = "issue #3722"]
fn test_rm_directory_rights_rm1() {