diff --git a/docs/src/extensions.md b/docs/src/extensions.md index 6a479ad6565..61ec4dc8e98 100644 --- a/docs/src/extensions.md +++ b/docs/src/extensions.md @@ -195,7 +195,7 @@ Similar to the proc-ps implementation and unlike GNU/Coreutils, `uptime` provide ## `base32/base64/basenc` -Just like on macOS, `base32/base64/basenc` provides `-D` to decode data. +Just like on macOS, `base32/base64/basenc` provides `-D` to decode data and `-o` to output to a file. ## `shred` diff --git a/src/uu/base32/locales/en-US.ftl b/src/uu/base32/locales/en-US.ftl index 925e5c70a25..35a008e6682 100644 --- a/src/uu/base32/locales/en-US.ftl +++ b/src/uu/base32/locales/en-US.ftl @@ -57,3 +57,4 @@ base-common-read-error = read error: {$error} base-common-help-decode = decode data base-common-help-ignore-garbage = when decoding, ignore non-alphabetic characters base-common-help-wrap = wrap encoded lines after COLS character (default {$default}, 0 to disable wrapping) +base-common-help-output-file = output to OUTPUT_FILE instead of stdout diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index fe13e46cc0c..fa9266bc231 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, Command}; use std::ffi::OsString; use std::fs::File; -use std::io::{self, ErrorKind, Read, Seek}; +use std::io::{self, BufWriter, ErrorKind, Read, Seek, Write}; use std::path::{Path, PathBuf}; use uucore::display::Quotable; use uucore::encoding::{ @@ -34,6 +34,7 @@ pub struct Config { pub ignore_garbage: bool, pub wrap_cols: Option, pub to_read: Option, + pub output_file: Option, } pub mod options { @@ -41,6 +42,7 @@ pub mod options { pub static WRAP: &str = "wrap"; pub static IGNORE_GARBAGE: &str = "ignore-garbage"; pub static FILE: &str = "file"; + pub static OUTPUT_FILE: &str = "output_file"; } impl Config { @@ -86,11 +88,17 @@ impl Config { }) .transpose()?; + let output_file = match options.get_one::(options::OUTPUT_FILE) { + Some(value) if value != "-" => Some(Path::new(value).to_owned()), + _ => None, + }; + Ok(Self { decode: options.get_flag(options::DECODE), ignore_garbage: options.get_flag(options::IGNORE_GARBAGE), wrap_cols, to_read, + output_file, }) } } @@ -138,6 +146,14 @@ pub fn base_app(about: &'static str, usage: &str) -> Command { .help(translate!("base-common-help-wrap", "default" => WRAP_DEFAULT)) .overrides_with(options::WRAP), ) + .arg( + Arg::new(options::OUTPUT_FILE) + .short('o') + .long(options::OUTPUT_FILE) + .help(translate!("base-common-help-output-file")) + .value_parser(clap::value_parser!(OsString)) + .value_hint(clap::ValueHint::FilePath), + ) // "multiple" arguments are used to check whether there is more than one // file passed in. .arg( @@ -194,7 +210,10 @@ pub fn handle_input(input: &mut R, format: Format, config: Confi get_supports_fast_decode_and_encode(format, config.decode, has_padding); let supports_fast_decode_and_encode_ref = supports_fast_decode_and_encode.as_ref(); - let mut stdout_lock = io::stdout().lock(); + let mut stdout_lock: Box = match &config.output_file { + Some(path) => Box::new(BufWriter::new(File::create(path)?)), + None => Box::new(io::stdout().lock()), + }; if config.decode { fast_decode::fast_decode( read, diff --git a/src/uu/base64/benches/base64_bench.rs b/src/uu/base64/benches/base64_bench.rs index b157af0c85b..b85904fd495 100644 --- a/src/uu/base64/benches/base64_bench.rs +++ b/src/uu/base64/benches/base64_bench.rs @@ -6,22 +6,24 @@ use divan::{Bencher, black_box}; use std::ffi::OsString; use uu_base64::uumain; -use uucore::benchmark::{create_test_file, run_util_function, text_data}; +use uucore::benchmark::{create_test_file, run_util_function, setup_test_file, text_data}; fn create_tmp_file(size_mb: usize) -> String { - let temp_dir = tempfile::tempdir().unwrap(); let data = text_data::generate_by_size(size_mb, 80); - let file_path = create_test_file(&data, temp_dir.path()); + let file_path = setup_test_file(&data); String::from(file_path.to_str().unwrap()) } /// Benchmark for base64 encoding #[divan::bench()] fn b64_encode_synthetic(bencher: Bencher) { - let file_path_str = &create_tmp_file(5_000); + let file_path_str = &create_tmp_file(50); bencher.bench(|| { - black_box(run_util_function(uumain, &[file_path_str])); + black_box(run_util_function( + uumain, + &["-o", "/dev/null", file_path_str], + )); }); } @@ -29,20 +31,25 @@ fn b64_encode_synthetic(bencher: Bencher) { #[divan::bench()] fn b64_decode_synthetic(bencher: Bencher) { let temp_dir = tempfile::tempdir().unwrap(); - let file_path_str = &create_tmp_file(5_000); + let file_path_str = &create_tmp_file(50); let in_file = create_test_file(b"", temp_dir.path()); let in_file_str = in_file.to_str().unwrap(); uumain( [ + OsString::from(uucore::util_name()), + OsString::from("-o"), + OsString::from(in_file_str), OsString::from(file_path_str), - OsString::from(format!(">{in_file_str}")), ] .iter() .map(|x| (*x).clone()), ); bencher.bench(|| { - black_box(run_util_function(uumain, &["-d", in_file_str])); + black_box(run_util_function( + uumain, + &["-d", "-o", "/dev/null", in_file_str], + )); }); } @@ -50,20 +57,25 @@ fn b64_decode_synthetic(bencher: Bencher) { #[divan::bench()] fn b64_decode_ignore_garbage_synthetic(bencher: Bencher) { let temp_dir = tempfile::tempdir().unwrap(); - let file_path_str = &create_tmp_file(5_000); + let file_path_str = &create_tmp_file(50); let in_file = create_test_file(b"", temp_dir.path()); let in_file_str = in_file.to_str().unwrap(); uumain( [ + OsString::from(uucore::util_name()), + OsString::from("-o"), + OsString::from(in_file_str), OsString::from(file_path_str), - OsString::from(format!(">{in_file_str}")), ] .iter() .map(|x| (*x).clone()), ); bencher.bench(|| { - black_box(run_util_function(uumain, &["-d", "-i", in_file_str])); + black_box(run_util_function( + uumain, + &["-d", "-i", "-o", "/dev/null", in_file_str], + )); }); }