2020-09-27 03:52:13 +13:00
|
|
|
use czkawka_core::common_traits::*;
|
2020-09-23 22:17:19 +12:00
|
|
|
use czkawka_core::*;
|
2020-08-27 06:49:43 +12:00
|
|
|
use std::{env, process};
|
|
|
|
|
2020-08-26 21:17:16 +12:00
|
|
|
fn main() {
|
2020-08-27 06:49:43 +12:00
|
|
|
// Parse argument
|
2020-09-23 22:17:19 +12:00
|
|
|
let all_arguments: Vec<String> = env::args().skip(1).collect(); // Not need to check program name
|
2020-08-30 05:11:55 +12:00
|
|
|
let mut commands_arguments: Vec<String> = Vec::new();
|
|
|
|
|
2020-09-23 22:17:19 +12:00
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
println!("{:?}", all_arguments);
|
2020-08-27 06:49:43 +12:00
|
|
|
|
2020-08-31 00:00:22 +12:00
|
|
|
// No arguments, so we print help to allow user to learn more about program
|
|
|
|
if all_arguments.is_empty() {
|
|
|
|
print_help();
|
|
|
|
process::exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assigning commands with arguments
|
|
|
|
let mut arguments: Vec<ArgumentsPair> = Vec::new();
|
|
|
|
|
2020-09-01 02:15:56 +12:00
|
|
|
for argument in all_arguments {
|
|
|
|
if argument.starts_with("--") {
|
|
|
|
commands_arguments.push(argument);
|
|
|
|
} else if argument.starts_with('-') {
|
2020-09-12 01:52:06 +12:00
|
|
|
let a: ArgumentsPair = ArgumentsPair { command: argument, argument: Option::None };
|
2020-09-01 02:15:56 +12:00
|
|
|
arguments.push(a);
|
|
|
|
} else {
|
|
|
|
if arguments.is_empty() {
|
|
|
|
println!("FATAL ERROR: Trying to use {} without any arguments(like -i -e -delete)", argument);
|
2020-08-31 00:00:22 +12:00
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-09-01 02:15:56 +12:00
|
|
|
if arguments[arguments.len() - 1].argument != Option::None {
|
2020-09-26 23:48:53 +12:00
|
|
|
println!(
|
|
|
|
"FATAL ERROR: Trying set second parameter \"{}\" for \"{}\" which already have this parameter \"{}\" ",
|
|
|
|
argument,
|
|
|
|
arguments[arguments.len() - 1].command,
|
|
|
|
arguments[arguments.len() - 1].argument.as_ref().unwrap()
|
|
|
|
); // This may be changed in future to support 2 or more attributes with space
|
2020-09-01 02:15:56 +12:00
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
let last_element = arguments.len() - 1;
|
|
|
|
arguments[last_element].argument = Option::from(argument);
|
2020-08-30 05:11:55 +12:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 17:32:37 +12:00
|
|
|
#[cfg(debug_assertions)]
|
|
|
|
for a in &arguments {
|
|
|
|
println!(
|
|
|
|
"Argument number {} - {}",
|
|
|
|
a.command,
|
|
|
|
match &a.argument {
|
|
|
|
Some(t) => t.clone(),
|
|
|
|
None => "NO_ARGUMENT".to_string(),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
|
2020-08-30 05:11:55 +12:00
|
|
|
if commands_arguments.is_empty() {
|
2020-08-31 00:00:22 +12:00
|
|
|
println! {"FATAL ERROR: Missing type of app which you want to run, please read help for more info."};
|
2020-08-30 05:11:55 +12:00
|
|
|
process::exit(0);
|
|
|
|
}
|
|
|
|
match commands_arguments[0].as_ref() {
|
2020-08-31 00:00:22 +12:00
|
|
|
"--d" => {
|
|
|
|
let mut df = duplicate::DuplicateFinder::new();
|
2020-08-30 05:11:55 +12:00
|
|
|
|
2020-08-31 00:00:22 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-i") {
|
2020-09-27 03:52:13 +13:00
|
|
|
df.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
|
2020-08-31 00:00:22 +12:00
|
|
|
} else {
|
|
|
|
println!("FATAL ERROR: Parameter -i with set of included files is required.");
|
2020-08-27 06:49:43 +12:00
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-e") {
|
2020-09-27 03:52:13 +13:00
|
|
|
df.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
|
2020-08-30 02:12:20 +12:00
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-s") {
|
2020-09-01 02:15:56 +12:00
|
|
|
let min_size = match ArgumentsPair::get_argument(&arguments, "-s", false).parse::<u64>() {
|
2020-09-12 01:52:06 +12:00
|
|
|
Ok(t) => {
|
|
|
|
if t == 0 {
|
|
|
|
println!("ERROR: Minimum file size must be at least 1 byte.");
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
t
|
|
|
|
}
|
|
|
|
}
|
2020-08-30 02:12:20 +12:00
|
|
|
Err(_) => {
|
2020-09-12 01:52:06 +12:00
|
|
|
println!("FATAL ERROR: \"{}\" is not valid file size(allowed range <1,u64::max>)", ArgumentsPair::get_argument(&arguments, "-s", false));
|
2020-08-30 02:12:20 +12:00
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
df.set_min_file_size(min_size);
|
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-x") {
|
2020-09-01 02:15:56 +12:00
|
|
|
df.set_allowed_extensions(ArgumentsPair::get_argument(&arguments, "-x", false));
|
2020-08-30 02:12:20 +12:00
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-k") {
|
2020-09-01 02:15:56 +12:00
|
|
|
df.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
|
2020-08-31 00:00:22 +12:00
|
|
|
}
|
2020-09-16 05:17:13 +12:00
|
|
|
|
2020-09-12 23:25:23 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-o") {
|
|
|
|
df.set_recursive_search(false);
|
|
|
|
}
|
2020-09-25 08:25:24 +12:00
|
|
|
df.set_check_method(duplicate::CheckingMethod::Hash); // Default
|
2020-08-31 00:00:22 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-l") {
|
2020-09-01 02:15:56 +12:00
|
|
|
let argument_name = ArgumentsPair::get_argument(&arguments, "-l", false).to_lowercase();
|
|
|
|
if argument_name == "size" {
|
2020-09-25 08:25:24 +12:00
|
|
|
df.set_check_method(duplicate::CheckingMethod::Size);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else if argument_name == "hash" {
|
2020-09-25 08:25:24 +12:00
|
|
|
df.set_check_method(duplicate::CheckingMethod::Hash);
|
2020-08-31 00:00:22 +12:00
|
|
|
} else {
|
|
|
|
println!("-l can only have values hash or size");
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-08-30 02:12:20 +12:00
|
|
|
}
|
2020-09-01 02:15:56 +12:00
|
|
|
|
2020-09-17 22:07:58 +12:00
|
|
|
df.set_delete_method(duplicate::DeleteMethod::None);
|
2020-09-01 02:15:56 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-delete") {
|
|
|
|
let argument_name = ArgumentsPair::get_argument(&arguments, "-delete", true).to_lowercase();
|
|
|
|
if argument_name == "aen" {
|
2020-09-17 22:07:58 +12:00
|
|
|
df.set_delete_method(duplicate::DeleteMethod::AllExceptNewest);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else if argument_name == "aeo" {
|
2020-09-17 22:07:58 +12:00
|
|
|
df.set_delete_method(duplicate::DeleteMethod::AllExceptOldest);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else if argument_name == "on" {
|
2020-09-17 22:07:58 +12:00
|
|
|
df.set_delete_method(duplicate::DeleteMethod::OneNewest);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else if argument_name == "oo" {
|
2020-09-17 22:07:58 +12:00
|
|
|
df.set_delete_method(duplicate::DeleteMethod::OneOldest);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else if argument_name == "" {
|
2020-09-17 22:07:58 +12:00
|
|
|
// Default
|
|
|
|
df.set_delete_method(duplicate::DeleteMethod::AllExceptOldest);
|
2020-09-01 02:15:56 +12:00
|
|
|
} else {
|
|
|
|
println!(
|
|
|
|
"Invalid argument {} for command -delete, available arguments - aen(All except newest one), aeo(All except oldest one), on(Only one newest), oo(Only one oldest)",
|
|
|
|
argument_name
|
|
|
|
);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-17 22:07:58 +12:00
|
|
|
df.find_duplicates();
|
2020-09-12 01:52:06 +12:00
|
|
|
|
2020-09-18 20:23:49 +12:00
|
|
|
#[allow(clippy::collapsible_if)]
|
2020-09-16 05:17:13 +12:00
|
|
|
if ArgumentsPair::has_command(&arguments, "-f") {
|
2020-09-18 20:23:49 +12:00
|
|
|
if !df.save_results_to_file(&ArgumentsPair::get_argument(&arguments, "-f", false)) {
|
|
|
|
df.get_text_messages().print_messages();
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-09-16 05:17:13 +12:00
|
|
|
}
|
|
|
|
|
2020-09-19 18:20:49 +12:00
|
|
|
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
|
2020-09-27 03:52:13 +13:00
|
|
|
df.print_results();
|
2020-09-19 18:20:49 +12:00
|
|
|
|
2020-09-18 20:23:49 +12:00
|
|
|
df.get_text_messages().print_messages();
|
2020-09-01 05:37:30 +12:00
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
"--h" | "--help" => {
|
2020-08-30 05:11:55 +12:00
|
|
|
print_help();
|
2020-09-01 05:37:30 +12:00
|
|
|
}
|
|
|
|
"--e" => {
|
|
|
|
let mut ef = empty_folder::EmptyFolder::new();
|
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-i") {
|
2020-09-27 03:52:13 +13:00
|
|
|
ef.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
|
2020-09-01 05:37:30 +12:00
|
|
|
} else {
|
|
|
|
println!("FATAL ERROR: Parameter -i with set of included files is required.");
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-09-18 21:11:08 +12:00
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-delete") {
|
|
|
|
ef.set_delete_folder(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
ef.find_empty_folders();
|
|
|
|
|
2020-09-18 20:23:49 +12:00
|
|
|
#[allow(clippy::collapsible_if)]
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-f") {
|
|
|
|
if !ef.save_results_to_file(&ArgumentsPair::get_argument(&arguments, "-f", false)) {
|
|
|
|
ef.get_text_messages().print_messages();
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-09-01 05:37:30 +12:00
|
|
|
}
|
2020-09-01 02:26:14 +12:00
|
|
|
|
2020-09-19 18:20:49 +12:00
|
|
|
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
|
2020-09-27 03:52:13 +13:00
|
|
|
ef.print_results();
|
2020-09-01 05:37:30 +12:00
|
|
|
}
|
2020-09-26 23:48:53 +12:00
|
|
|
"--b" => {
|
|
|
|
let mut bf = big_file::BigFile::new();
|
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-i") {
|
2020-09-27 03:52:13 +13:00
|
|
|
bf.set_included_directory(ArgumentsPair::get_argument(&arguments, "-i", false));
|
2020-09-26 23:48:53 +12:00
|
|
|
} else {
|
|
|
|
println!("FATAL ERROR: Parameter -i with set of included files is required.");
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-e") {
|
2020-09-27 03:52:13 +13:00
|
|
|
bf.set_excluded_directory(ArgumentsPair::get_argument(&arguments, "-e", false));
|
2020-09-26 23:48:53 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-s") {
|
|
|
|
let number_of_files = match ArgumentsPair::get_argument(&arguments, "-s", false).parse::<usize>() {
|
|
|
|
Ok(t) => {
|
|
|
|
if t == 0 {
|
|
|
|
println!("ERROR: Minimum one biggest file must be showed..");
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
t
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
println!("FATAL ERROR: \"{}\" is not valid number of files to show(allowed range <1,usize::max>)", ArgumentsPair::get_argument(&arguments, "-s", false));
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
bf.set_number_of_files_to_check(number_of_files);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-x") {
|
|
|
|
bf.set_allowed_extensions(ArgumentsPair::get_argument(&arguments, "-x", false));
|
|
|
|
}
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-k") {
|
|
|
|
bf.set_excluded_items(ArgumentsPair::get_argument(&arguments, "-k", false));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-o") {
|
|
|
|
bf.set_recursive_search(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bf.find_big_files();
|
|
|
|
|
|
|
|
#[allow(clippy::collapsible_if)]
|
|
|
|
if ArgumentsPair::has_command(&arguments, "-f") {
|
|
|
|
if !bf.save_results_to_file(&ArgumentsPair::get_argument(&arguments, "-f", false)) {
|
|
|
|
bf.get_text_messages().print_messages();
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(debug_assertions))] // This will show too much probably unnecessary data to debug, comment line only if needed
|
2020-09-27 03:52:13 +13:00
|
|
|
bf.print_results();
|
2020-09-26 23:48:53 +12:00
|
|
|
|
|
|
|
bf.get_text_messages().print_messages();
|
|
|
|
}
|
2020-09-23 22:17:19 +12:00
|
|
|
"--version" | "v" => {
|
|
|
|
println!("Czkawka CLI {}", CZKAWKA_VERSION);
|
|
|
|
process::exit(0);
|
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
argum => {
|
|
|
|
println!("FATAL ERROR: \"{}\" argument is not supported, check help for more info.", argum);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
2020-08-27 06:49:43 +12:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print_help() {
|
2020-08-31 03:18:04 +12:00
|
|
|
println!(
|
2020-09-01 02:15:56 +12:00
|
|
|
r###"
|
|
|
|
Usage of Czkawka:
|
|
|
|
|
|
|
|
## Main arguments:
|
|
|
|
--h / --help - prints help, also works without any arguments
|
2020-09-23 22:17:19 +12:00
|
|
|
|
|
|
|
Usage example:
|
|
|
|
czkawka --help
|
|
|
|
czkawka
|
|
|
|
|
2020-09-01 02:15:56 +12:00
|
|
|
|
2020-09-26 23:48:53 +12:00
|
|
|
--d <-i directory_to_search> [-e exclude_directories = ""] [-k excluded_items = ""] [-s min_size = 1024] [-x allowed_extension = ""] [-l type_of_search = "hash"] [-o] [-f file_to_save = "results.txt"] [-delete = "aeo"] - search for duplicates files
|
2020-09-01 02:15:56 +12:00
|
|
|
-i directory_to_search - list of directories which should will be searched like /home/rafal
|
|
|
|
-e exclude_directories - list of directories which will be excluded from search.
|
2020-09-12 08:32:17 +12:00
|
|
|
-k excluded_items - list of excluded items which contains * wildcard(may be slow)
|
2020-09-16 05:17:13 +12:00
|
|
|
-o - this options prevents from recursive check of folders
|
|
|
|
-f file_to_save - saves results to file
|
2020-09-01 02:15:56 +12:00
|
|
|
-s min_size - minimum size of checked files in bytes, assigning bigger value may speed up searching.
|
2020-09-12 08:32:17 +12:00
|
|
|
-x allowed_extension - list of checked extension, e.g. "jpg,mp4" will allow to check "book.jpg" and "car.mp4" but not roman.png. There are also helpful macros which allow to easy use a typcal extension like IMAGE("jpg,kra,gif,png,bmp,tiff,webp,hdr,svg") or TEXT("txt,doc,docx,odt,rtf")
|
2020-09-01 02:26:14 +12:00
|
|
|
-l type_of_search - allows to use fastest which takes into account only size, and more accurate which check if file contnet is same(hashes).
|
|
|
|
-delete - delete found files, by default remove all except the most oldest one, it can take arguments: aen(All except newest one), aeo(All except oldest one), on(Only one newest), oo(Only one oldest)
|
2020-09-23 22:17:19 +12:00
|
|
|
|
|
|
|
Usage example:
|
|
|
|
czkawka --d -i "/home/rafal/,/home/szczekacz" -e "/home/rafal/Pulpit,/home/rafal/Obrazy" -s 25 -x "7z,rar,IMAGE" -l "size" -delete
|
|
|
|
czkawka --d -i "/etc/,/mnt/Miecz" -s 1000 -x "VIDEO" -l "hash" -o
|
|
|
|
czkawka --d -i "/var/" -k "/var/l*b/,/var/lo*,*tmp"
|
|
|
|
czkawka --d -i "/etc/" -delete "aeo"
|
|
|
|
|
2020-09-04 03:33:43 +12:00
|
|
|
|
2020-09-18 17:32:37 +12:00
|
|
|
--e <-i directory_to_search> [-e exclude_directories = ""] [-o] [-f file_to_save] [-delete] - option to find and delete empty folders
|
2020-09-01 02:26:14 +12:00
|
|
|
-i directory_to_search - list of directories which should will be searched like /home/rafal
|
|
|
|
-e exclude_directories - list of directories which will be excluded from search.
|
2020-09-18 17:32:37 +12:00
|
|
|
-f file_to_save - saves results to file
|
2020-09-01 02:26:14 +12:00
|
|
|
-delete - delete found empty folders
|
2020-09-23 22:17:19 +12:00
|
|
|
|
|
|
|
Usage example:
|
|
|
|
czkawka --e -i "/home/rafal/rr, /home/gateway" -e "/home/rafal/rr/2" -delete
|
|
|
|
|
2020-09-26 23:48:53 +12:00
|
|
|
--b <-i directory_to_search> [-e exclude_directories = ""] [-k excluded_items = ""] [-s number_of_files = 50] [-x allowed_extension = ""] [-o] [-f file_to_save = "results.txt"]
|
|
|
|
-i directory_to_search - list of directories which should will be searched like /home/rafal
|
|
|
|
-e exclude_directories - list of directories which will be excluded from search.
|
|
|
|
-k excluded_items - list of excluded items which contains * wildcard(may be slow)
|
|
|
|
-o - this options prevents from recursive check of folders
|
|
|
|
-f file_to_save - saves results to file
|
|
|
|
-s number_of_files - number of showed the biggest files.
|
|
|
|
-x allowed_extension - list of checked extension, e.g. "jpg,mp4" will allow to check "book.jpg" and "car.mp4" but not roman.png. There are also helpful macros which allow to easy use a typcal extension like IMAGE("jpg,kra,gif,png,bmp,tiff,webp,hdr,svg") or TEXT("txt,doc,docx,odt,rtf")
|
|
|
|
|
2020-09-23 22:17:19 +12:00
|
|
|
--version / --v - prints program name and version
|
|
|
|
|
2020-09-01 02:15:56 +12:00
|
|
|
"###
|
2020-08-31 03:18:04 +12:00
|
|
|
);
|
2020-08-26 21:17:16 +12:00
|
|
|
}
|
2020-08-31 00:00:22 +12:00
|
|
|
|
|
|
|
struct ArgumentsPair {
|
|
|
|
command: String,
|
2020-09-01 02:15:56 +12:00
|
|
|
argument: Option<String>,
|
2020-08-31 00:00:22 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ArgumentsPair {
|
|
|
|
pub fn has_command(ar: &[ArgumentsPair], command: &str) -> bool {
|
|
|
|
for a in ar {
|
|
|
|
if a.command == command {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2020-09-01 02:15:56 +12:00
|
|
|
pub fn get_argument(ar: &[ArgumentsPair], command: &str, can_be_empty: bool) -> String {
|
2020-08-31 00:00:22 +12:00
|
|
|
for a in ar {
|
|
|
|
if a.command == command {
|
2020-09-01 02:15:56 +12:00
|
|
|
if !can_be_empty && a.argument == Option::None {
|
|
|
|
println!("FATAL ERROR: {} commands should have argument passed", command);
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
return match &a.argument {
|
|
|
|
Some(t) => t.clone(),
|
|
|
|
None => "".to_string(),
|
|
|
|
};
|
2020-08-31 00:00:22 +12:00
|
|
|
}
|
|
|
|
}
|
2020-09-12 01:52:06 +12:00
|
|
|
panic!("INTERNAL ERROR: Get argument should always return value");
|
2020-08-31 00:00:22 +12:00
|
|
|
}
|
|
|
|
}
|