diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 2f24cff..10fa660 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -10,7 +10,7 @@ use crate::{ archive, commands::warn_user_about_loading_zip_in_memory, extension::{split_first_compression_format, CompressionFormat::*, Extension}, - utils::{user_wants_to_continue, FileVisibilityPolicy}, + utils::{io::lock_and_flush_output_stdio, user_wants_to_continue, FileVisibilityPolicy}, QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; @@ -104,8 +104,11 @@ pub fn compress_files( } Zip => { if !formats.is_empty() { - warn_user_about_loading_zip_in_memory(); + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_zip_in_memory(); if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? { return Ok(false); } @@ -132,8 +135,11 @@ pub fn compress_files( } SevenZip => { if !formats.is_empty() { - warn_user_about_loading_sevenz_in_memory(); + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_sevenz_in_memory(); if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? { return Ok(false); } diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 63c42d8..57ed969 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -13,7 +13,9 @@ use crate::{ CompressionFormat::{self, *}, Extension, }, - utils::{self, logger::info_accessible, nice_directory_display, user_wants_to_continue}, + utils::{ + self, io::lock_and_flush_output_stdio, logger::info_accessible, nice_directory_display, user_wants_to_continue, + }, QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; @@ -122,8 +124,11 @@ pub fn decompress_file( } Zip => { if formats.len() > 1 { - warn_user_about_loading_zip_in_memory(); + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_zip_in_memory(); if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? { return Ok(()); } @@ -169,8 +174,11 @@ pub fn decompress_file( } SevenZip => { if formats.len() > 1 { - warn_user_about_loading_sevenz_in_memory(); + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_sevenz_in_memory(); if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? { return Ok(()); } diff --git a/src/commands/list.rs b/src/commands/list.rs index 05e37c3..d33ced5 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -9,7 +9,7 @@ use crate::{ commands::warn_user_about_loading_zip_in_memory, extension::CompressionFormat::{self, *}, list::{self, FileInArchive, ListOptions}, - utils::user_wants_to_continue, + utils::{io::lock_and_flush_output_stdio, user_wants_to_continue}, QuestionAction, QuestionPolicy, BUFFER_CAPACITY, }; @@ -65,8 +65,11 @@ pub fn list_archive_contents( Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))), Zip => { if formats.len() > 1 { - warn_user_about_loading_zip_in_memory(); + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_zip_in_memory(); if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { return Ok(()); } @@ -94,6 +97,10 @@ pub fn list_archive_contents( } SevenZip => { if formats.len() > 1 { + // Locking necessary to guarantee that warning and question + // messages stay adjacent + let _locks = lock_and_flush_output_stdio(); + warn_user_about_loading_zip_in_memory(); if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { return Ok(()); diff --git a/src/commands/mod.rs b/src/commands/mod.rs index d15c5f9..a868f4d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -16,32 +16,28 @@ use crate::{ error::{Error, FinalError}, extension::{self, parse_format}, list::ListOptions, - utils::{ - self, - logger::{info_accessible, warning}, - to_utf, EscapedPathDisplay, FileVisibilityPolicy, - }, + utils::{self, colors::*, logger::info_accessible, to_utf, EscapedPathDisplay, FileVisibilityPolicy}, CliArgs, QuestionPolicy, }; /// Warn the user that (de)compressing this .zip archive might freeze their system. fn warn_user_about_loading_zip_in_memory() { - const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ - \tThe format '.zip' is limited and cannot be (de)compressed using encoding streams.\n\ - \tWhen using '.zip' with other formats, (de)compression must be done in-memory\n\ - \tCareful, you might run out of RAM if the archive is too large!"; + const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n \ + The format '.zip' is limited by design and cannot be (de)compressed with encoding streams.\n \ + When chaining '.zip' with other formats, all (de)compression needs to be done in-memory\n \ + Careful, you might run out of RAM if the archive is too large!"; - warning(ZIP_IN_MEMORY_LIMITATION_WARNING.to_string()); + eprintln!("{}[WARNING]{}: {ZIP_IN_MEMORY_LIMITATION_WARNING}", *ORANGE, *RESET); } /// Warn the user that (de)compressing this .7z archive might freeze their system. fn warn_user_about_loading_sevenz_in_memory() { - const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ - \tThe format '.7z' is limited and cannot be (de)compressed using encoding streams.\n\ - \tWhen using '.7z' with other formats, (de)compression must be done in-memory\n\ - \tCareful, you might run out of RAM if the archive is too large!"; + const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n \ + The format '.7z' is limited by design and cannot be (de)compressed with encoding streams.\n \ + When chaining '.7z' with other formats, all (de)compression needs to be done in-memory\n \ + Careful, you might run out of RAM if the archive is too large!"; - warning(SEVENZ_IN_MEMORY_LIMITATION_WARNING.to_string()); + eprintln!("{}[WARNING]{}: {SEVENZ_IN_MEMORY_LIMITATION_WARNING}", *ORANGE, *RESET); } /// This function checks what command needs to be run and performs A LOT of ahead-of-time checks diff --git a/src/utils/io.rs b/src/utils/io.rs new file mode 100644 index 0000000..7e05d84 --- /dev/null +++ b/src/utils/io.rs @@ -0,0 +1,12 @@ +use std::io::{self, stderr, stdout, StderrLock, StdoutLock, Write}; + +type StdioOutputLocks = (StdoutLock<'static>, StderrLock<'static>); + +pub fn lock_and_flush_output_stdio() -> io::Result { + let mut stdout = stdout().lock(); + stdout.flush()?; + let mut stderr = stderr().lock(); + stderr.flush()?; + + Ok((stdout, stderr)) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f8fd350..dce133d 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -7,6 +7,7 @@ pub mod colors; mod file_visibility; mod formatting; mod fs; +pub mod io; pub mod logger; mod question; diff --git a/src/utils/question.rs b/src/utils/question.rs index c548258..f828ee8 100644 --- a/src/utils/question.rs +++ b/src/utils/question.rs @@ -5,7 +5,7 @@ use std::{ borrow::Cow, - io::{self, Write}, + io::{stdin, BufRead}, path::Path, }; @@ -15,7 +15,7 @@ use super::{strip_cur_dir, to_utf}; use crate::{ accessible::is_running_in_accessible_mode, error::{Error, FinalError, Result}, - utils::{self, colors}, + utils::{self, colors, io::lock_and_flush_output_stdio}, }; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -121,10 +121,13 @@ impl<'a> Confirmation<'a> { (Some(placeholder), Some(subs)) => Cow::Owned(self.prompt.replace(placeholder, subs)), }; + let _locks = lock_and_flush_output_stdio()?; + let mut stdin_lock = stdin().lock(); + // Ask the same question to end while no valid answers are given loop { if is_running_in_accessible_mode() { - print!( + eprintln!( "{} {}yes{}/{}no{}: ", message, *colors::GREEN, @@ -133,7 +136,7 @@ impl<'a> Confirmation<'a> { *colors::RESET ); } else { - print!( + eprintln!( "{} [{}Y{}/{}n{}] ", message, *colors::GREEN, @@ -142,11 +145,9 @@ impl<'a> Confirmation<'a> { *colors::RESET ); } - let _stdout_lock = io::stdout().lock(); - io::stdout().flush()?; let mut answer = String::new(); - let bytes_read = io::stdin().read_line(&mut answer)?; + let bytes_read = stdin_lock.read_line(&mut answer)?; if bytes_read == 0 { let error = FinalError::with_title("Unexpected EOF when asking question.")