From 7e3086c35428cc25bfa4f9eb1b33e46f12714f66 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 15 Jul 2020 22:49:34 +0200 Subject: [PATCH] Prompt to remove leftover files when removing dir --- tools/yabridgectl/Cargo.lock | 171 +++++++++++++++++++++++++++++++++ tools/yabridgectl/Cargo.toml | 1 + tools/yabridgectl/src/files.rs | 72 +++++++++----- tools/yabridgectl/src/main.rs | 44 +++++++-- 4 files changed, 259 insertions(+), 29 deletions(-) diff --git a/tools/yabridgectl/Cargo.lock b/tools/yabridgectl/Cargo.lock index 8c91f2d0..bbc71b40 100644 --- a/tools/yabridgectl/Cargo.lock +++ b/tools/yabridgectl/Cargo.lock @@ -9,6 +9,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "atty" version = "0.2.14" @@ -26,12 +38,35 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "cc" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" + [[package]] name = "cfg-if" version = "0.1.10" @@ -82,6 +117,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -130,12 +171,44 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "dirs-next" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "heck" version = "0.3.1" @@ -185,6 +258,15 @@ version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -206,6 +288,19 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -257,6 +352,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "promptly" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99cfb0289110d969dd21637cfbc922584329bc9e5037c5e576325c615658509" +dependencies = [ + "rustyline", +] + [[package]] name = "quote" version = "1.0.7" @@ -291,6 +395,54 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustyline" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777" +dependencies = [ + "cfg-if", + "dirs-next", + "libc", + "log", + "memchr", + "nix", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi 0.3.9", +] + [[package]] name = "same-file" version = "1.0.6" @@ -418,6 +570,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + [[package]] name = "vec_map" version = "0.8.2" @@ -430,6 +588,12 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "walkdir" version = "2.3.1" @@ -441,6 +605,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "winapi" version = "0.2.8" @@ -498,6 +668,7 @@ dependencies = [ "clap", "colored", "lazy_static", + "promptly", "rayon", "serde", "serde_derive", diff --git a/tools/yabridgectl/Cargo.toml b/tools/yabridgectl/Cargo.toml index d3d7f579..af272b6f 100644 --- a/tools/yabridgectl/Cargo.toml +++ b/tools/yabridgectl/Cargo.toml @@ -14,6 +14,7 @@ aho-corasick = "0.7.13" colored = "2.0.0" clap = { version = "3.0.0-beta.1", features = ["wrap_help"] } lazy_static = "1.4.0" +promptly = "0.3.0" rayon = "1.3.1" serde = "1.0.114" serde_derive = "1.0.114" diff --git a/tools/yabridgectl/src/files.rs b/tools/yabridgectl/src/files.rs index 05c2dc9e..4be2ed63 100644 --- a/tools/yabridgectl/src/files.rs +++ b/tools/yabridgectl/src/files.rs @@ -99,28 +99,7 @@ impl FoundFile { /// the directory does not exist, or if `winedump` could not be found. pub fn index(directory: &Path) -> Result { // First we'll find all .dll and .so files in the directory - let mut dll_files: Vec = Vec::new(); - let mut so_files: Vec = Vec::new(); - // XXX: We're silently skipping directories and files we don't have permission to read. This - // sounds like the expected behavior, but I"m not entirely sure. - for entry in WalkDir::new(directory) - .follow_links(true) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|x| !x.file_type().is_dir()) - { - match entry.path().extension().and_then(|os| os.to_str()) { - Some("dll") => dll_files.push(entry.into_path()), - Some("so") => { - if entry.path_is_symlink() { - so_files.push(FoundFile::Symlink(entry.into_path())); - } else { - so_files.push(FoundFile::Regular(entry.into_path())); - } - } - _ => (), - } - } + let (dll_files, so_files) = find_files(directory); lazy_static! { static ref VST2_AUTOMATON: AhoCorasick = @@ -160,3 +139,52 @@ pub fn index(directory: &Path) -> Result { so_files, }) } + +/// THe same as [index()](index), but only report found `.so` files. This avoids unnecesarily +/// filtering the found `.dll` files. +pub fn index_so_files(directory: &Path) -> Vec { + let (_, so_files) = find_files(directory); + + so_files +} + +/// Find all `.dll` and `.so` files under a directory. The results are a pair of `(dll_files, +/// so_files)`. +fn find_files(directory: &Path) -> (Vec, Vec) { + let mut dll_files: Vec = Vec::new(); + let mut so_files: Vec = Vec::new(); + // XXX: We're silently skipping directories and files we don't have permission to read. This + // sounds like the expected behavior, but I"m not entirely sure. + for (file_idx, entry) in WalkDir::new(directory) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|x| !x.file_type().is_dir()) + .enumerate() + { + // This is a bit of an odd warning, but I can see it happening that someone adds their + // entire home directory by accident. Removing the home directory would cause yabridgectl to + // scan for leftover `.so` files, which would of course take an enternity. This warning will + // at least tell the user what's happening and that they can safely cancel the scan. + if file_idx == 100_000 { + eprintln!( + "Indexed over 100.000 files, press Ctrl+C to cancel this operation if this was not \ + intentional." + ) + } + + match entry.path().extension().and_then(|os| os.to_str()) { + Some("dll") => dll_files.push(entry.into_path()), + Some("so") => { + if entry.path_is_symlink() { + so_files.push(FoundFile::Symlink(entry.into_path())); + } else { + so_files.push(FoundFile::Regular(entry.into_path())); + } + } + _ => (), + } + } + + (dll_files, so_files) +} diff --git a/tools/yabridgectl/src/main.rs b/tools/yabridgectl/src/main.rs index 39bc276f..d4a50a0c 100644 --- a/tools/yabridgectl/src/main.rs +++ b/tools/yabridgectl/src/main.rs @@ -29,7 +29,6 @@ mod files; // TODO: Naming and descriptions could be made clearer // TODO: When creating copies, check whether `yabridge-host.exe` is in the PATH for the login shell -// TODO: Check for left over files when removing directory // TODO: Reward parts of the readme fn main() { @@ -142,10 +141,10 @@ fn main() { /// Add a direcotry to the plugin locations. Duplicates get ignord because we're using ordered sets. fn add_directory(config: &mut Config, path: PathBuf) { config.plugin_dirs.insert(path); - if let Err(err) = config.write() { + config.write().unwrap_or_else(|err| { eprintln!("Error while writing config file: {}", err); exit(1); - }; + }); } /// Remove a direcotry to the plugin locations. The path is assumed to be part of @@ -154,10 +153,41 @@ fn remove_directory(config: &mut Config, path: &Path) { // We've already verified that this path is in `config.plugin_dirs` // XXS: Would it be a good idea to warn about leftover .so files? config.plugin_dirs.remove(path); - if let Err(err) = config.write() { + config.write().unwrap_or_else(|err| { eprintln!("Error while writing config file: {}", err); exit(1); - }; + }); + + // Ask the user to remove any leftover files to prevent possible future problems and out of date + // copies + let orphan_files = files::index_so_files(path); + if !orphan_files.is_empty() { + println!( + "WARNING: Found {} leftover '.so' files still in this directory:", + orphan_files.len() + ); + + for file in &orphan_files { + println!("- {}", file.path().display()); + } + + match promptly::prompt_opt::( + "\nWould you like to remove these files? Entering anything other than YES will leave \ + these files intact", + ) { + Ok(Some(answer)) if answer == "YES" => { + for file in &orphan_files { + fs::remove_file(file.path()).unwrap_or_else(|err| { + eprintln!("Could not remove '{}': {}", file.path().display(), err); + exit(1); + }); + } + + println!("\nRemoved {} files.", orphan_files.len()); + } + _ => {} + } + } } /// List the plugin locations. @@ -229,10 +259,10 @@ fn set_settings(config: &mut Config, options: &ArgMatches) { Err(err) => err.exit(), } - if let Err(err) = config.write() { + config.write().unwrap_or_else(|err| { eprintln!("Error while writing config file: {}", err); exit(1); - }; + }); } /// Set up yabridge for all Windows VST2 plugins in the plugin directories. Will also remove orphan