use std::io::IsTerminal;
use lz4::cli::args::{parse_args, ParsedArgs};
use lz4::cli::constants::{display_level, set_display_level, LZ4_EXTENSION};
use lz4::cli::help::wait_enter;
use lz4::cli::init::detect_alias;
use lz4::cli::op_mode::{determine_op_mode, OpMode};
use lz4::config::MULTITHREAD;
use lz4::io::{
compress_filename, compress_filename_legacy, compress_multiple_filenames,
compress_multiple_filenames_legacy, decompress_filename, decompress_multiple_filenames,
display_compressed_files_info, set_notification_level, STDIN_MARK, STDOUT_MARK,
};
fn run(args: ParsedArgs) -> i32 {
let mut prefs = args.prefs;
let mut op_mode = args.op_mode;
let c_level = args.c_level;
let c_level_last = args.c_level_last;
let legacy_format = args.legacy_format;
let force_stdout = args.force_stdout;
let main_pause = args.main_pause;
let mut multiple_inputs = args.multiple_inputs;
let nb_workers = args.nb_workers;
let mut input_filename: Option<String> = args.input_filename;
let mut output_filename: Option<String> = args.output_filename;
let dictionary_filename = args.dictionary_filename;
let mut in_file_names: Vec<String> = args.in_file_names;
let block_size = args.block_size;
let mut bench_config = args.bench_config;
let exe_name = args.exe_name;
#[cfg(feature = "recursive")]
let recursive = args.recursive;
let mut _output_filename_storage: Option<String> = None;
lz4::displaylevel!(
3,
"*** LZ4 v{} {}-bit {}, by {} ***\n",
lz4::LZ4_VERSION_STRING,
(std::mem::size_of::<*const ()>() * 8),
lz4::cli::constants::IO_MT,
lz4::cli::constants::AUTHOR
);
if !MULTITHREAD && nb_workers > 1 {
lz4::displaylevel!(
2,
"warning: this executable doesn't support multithreading \n"
);
}
if op_mode == OpMode::Compress || op_mode == OpMode::Bench {
lz4::displaylevel!(4, "Blocks size : {} KB\n", block_size >> 10);
}
if multiple_inputs {
if let Some(first) = in_file_names.first() {
input_filename = Some(first.clone());
}
#[cfg(feature = "recursive")]
if recursive {
use std::path::Path;
let paths: Vec<&Path> = in_file_names
.iter()
.map(|s| Path::new(s.as_str()))
.collect();
match lz4::util::create_file_list(&paths) {
Ok(list) => {
for (u, p) in list.iter().enumerate() {
lz4::displaylevel!(4, "{} {}\n", u, p.display());
}
in_file_names = list
.into_iter()
.map(|p| p.to_string_lossy().into_owned())
.collect();
}
Err(e) => {
eprintln!("lz4: {}", e);
return 1;
}
}
}
}
if let Some(ref dict) = dictionary_filename {
if dict.as_str() == STDIN_MARK && std::io::stdin().is_terminal() {
lz4::displaylevel!(1, "refusing to read from a console\n");
std::process::exit(1);
}
prefs.set_dictionary_filename(Some(dict.as_str()));
}
if op_mode == OpMode::Bench {
bench_config.set_notification_level(display_level());
let file_refs: Vec<&str> = in_file_names.iter().map(|s| s.as_str()).collect();
let result = lz4::bench::bench_files(
&file_refs,
c_level,
c_level_last,
dictionary_filename.as_deref(),
&bench_config,
);
if main_pause {
wait_enter();
}
return if result.is_ok() { 0 } else { 1 };
}
if op_mode == OpMode::Test {
prefs.set_test_mode(true);
output_filename = Some(lz4::io::NUL_MARK.to_owned());
op_mode = OpMode::Decompress;
}
let input_filename: String = input_filename.unwrap_or_else(|| STDIN_MARK.to_owned());
if input_filename == STDIN_MARK && std::io::stdin().is_terminal() {
lz4::displaylevel!(1, "refusing to read from a console\n");
std::process::exit(1);
}
if input_filename == STDIN_MARK && output_filename.is_none() {
output_filename = Some(STDOUT_MARK.to_owned());
}
if output_filename.is_none() && !multiple_inputs {
if op_mode == OpMode::Auto {
op_mode = determine_op_mode(&input_filename);
}
if op_mode == OpMode::Compress {
let out = format!("{}{}", input_filename, LZ4_EXTENSION);
lz4::displaylevel!(2, "Compressed filename will be : {} \n", out);
_output_filename_storage = Some(out.clone());
output_filename = Some(out);
} else if op_mode == OpMode::Decompress {
if let Some(base) = input_filename.strip_suffix(LZ4_EXTENSION) {
lz4::displaylevel!(2, "Decoding file {} \n", base);
_output_filename_storage = Some(base.to_owned());
output_filename = Some(base.to_owned());
} else {
lz4::displaylevel!(1, "Cannot determine an output filename \n");
lz4::cli::help::print_usage(&exe_name);
std::process::exit(1);
}
}
}
if op_mode == OpMode::List {
if !multiple_inputs {
in_file_names.push(input_filename.clone());
}
} else if !multiple_inputs {
}
let output_filename: String = output_filename.unwrap_or_else(|| "*\\dummy^!//".to_owned());
if output_filename == STDOUT_MARK
&& op_mode != OpMode::List
&& std::io::stdout().is_terminal()
&& !force_stdout
{
lz4::displaylevel!(1, "refusing to write to console without -c \n");
std::process::exit(1);
}
if output_filename == STDOUT_MARK && display_level() == 2 {
set_display_level(1);
}
if multiple_inputs && display_level() == 2 {
set_display_level(1);
}
if op_mode == OpMode::Auto {
op_mode = determine_op_mode(&input_filename);
}
set_notification_level(display_level() as i32);
if in_file_names.is_empty() {
multiple_inputs = false;
}
let operation_result: i32 = if op_mode == OpMode::Decompress {
if multiple_inputs {
let dec_extension: &str = if output_filename == STDOUT_MARK {
STDOUT_MARK
} else if output_filename == lz4::io::NUL_MARK {
lz4::io::NUL_MARK
} else {
LZ4_EXTENSION
};
let srcs: Vec<&str> = in_file_names.iter().map(|s| s.as_str()).collect();
match decompress_multiple_filenames(&srcs, dec_extension, &prefs) {
Ok(()) => 0,
Err(_) => 1,
}
} else {
match decompress_filename(&input_filename, &output_filename, &prefs) {
Ok(_) => 0,
Err(_) => 1,
}
}
} else if op_mode == OpMode::List {
let srcs: Vec<&str> = in_file_names.iter().map(|s| s.as_str()).collect();
match display_compressed_files_info(&srcs) {
Ok(()) => 0,
Err(_) => 1,
}
} else {
#[cfg(feature = "multithread")]
{
let mut nb = nb_workers;
if nb != 1 {
if nb == 0 {
nb = lz4::io::default_nb_workers() as usize;
}
let max = lz4::config::NB_WORKERS_MAX;
if nb > max {
lz4::displaylevel!(
3,
"Requested {} threads too large => automatically reduced to {} \n",
nb,
max
);
nb = max;
} else {
lz4::displaylevel!(3, "Using {} threads for compression \n", nb);
}
}
prefs.set_nb_workers(nb as i32);
}
if legacy_format {
lz4::displaylevel!(3, "! Generating LZ4 Legacy format (deprecated) ! \n");
if multiple_inputs {
let leg_ext: &str = if output_filename == STDOUT_MARK {
STDOUT_MARK
} else {
LZ4_EXTENSION
};
let srcs: Vec<&str> = in_file_names.iter().map(|s| s.as_str()).collect();
match compress_multiple_filenames_legacy(&srcs, leg_ext, c_level, &prefs) {
Ok(()) => 0,
Err(_) => 1,
}
} else {
match compress_filename_legacy(&input_filename, &output_filename, c_level, &prefs) {
Ok(_) => 0,
Err(_) => 1,
}
}
} else {
if multiple_inputs {
let comp_ext: &str = if output_filename == STDOUT_MARK {
STDOUT_MARK
} else {
LZ4_EXTENSION
};
let srcs: Vec<&str> = in_file_names.iter().map(|s| s.as_str()).collect();
match compress_multiple_filenames(&srcs, comp_ext, c_level, &prefs) {
Ok(missed) => missed as i32,
Err(_) => 1,
}
} else {
match compress_filename(&input_filename, &output_filename, c_level, &prefs) {
Ok(_) => 0,
Err(_) => 1,
}
}
}
};
if main_pause {
wait_enter();
}
operation_result
}
fn main() {
let argv0 = std::env::args().next().unwrap_or_else(|| "lz4".to_owned());
let init = detect_alias(&argv0);
let args = match parse_args(init) {
Ok(a) => a,
Err(e) => {
eprintln!("lz4: {}", e);
std::process::exit(1);
}
};
if args.exit_early {
std::process::exit(0);
}
let exit_code = run(args);
std::process::exit(exit_code);
}