fix STDIN syncrhonization problems

This commit is contained in:
João Marcos P. Bezerra 2024-03-16 02:55:45 -03:00 committed by João Marcos
parent ccbdceac34
commit a3e5bac438
7 changed files with 61 additions and 30 deletions

View File

@ -10,7 +10,7 @@ use crate::{
archive, archive,
commands::warn_user_about_loading_zip_in_memory, commands::warn_user_about_loading_zip_in_memory,
extension::{split_first_compression_format, CompressionFormat::*, Extension}, 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, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -104,8 +104,11 @@ pub fn compress_files(
} }
Zip => { Zip => {
if !formats.is_empty() { 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)? { if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? {
return Ok(false); return Ok(false);
} }
@ -132,8 +135,11 @@ pub fn compress_files(
} }
SevenZip => { SevenZip => {
if !formats.is_empty() { 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)? { if !user_wants_to_continue(output_path, question_policy, QuestionAction::Compression)? {
return Ok(false); return Ok(false);
} }

View File

@ -13,7 +13,9 @@ use crate::{
CompressionFormat::{self, *}, CompressionFormat::{self, *},
Extension, 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, QuestionAction, QuestionPolicy, BUFFER_CAPACITY,
}; };
@ -122,8 +124,11 @@ pub fn decompress_file(
} }
Zip => { Zip => {
if formats.len() > 1 { 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)? { if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
} }
@ -169,8 +174,11 @@ pub fn decompress_file(
} }
SevenZip => { SevenZip => {
if formats.len() > 1 { 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)? { if !user_wants_to_continue(input_file_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
} }

View File

@ -9,7 +9,7 @@ use crate::{
commands::warn_user_about_loading_zip_in_memory, commands::warn_user_about_loading_zip_in_memory,
extension::CompressionFormat::{self, *}, extension::CompressionFormat::{self, *},
list::{self, FileInArchive, ListOptions}, list::{self, FileInArchive, ListOptions},
utils::user_wants_to_continue, utils::{io::lock_and_flush_output_stdio, user_wants_to_continue},
QuestionAction, QuestionPolicy, BUFFER_CAPACITY, 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))), Tar => Box::new(crate::archive::tar::list_archive(tar::Archive::new(reader))),
Zip => { Zip => {
if formats.len() > 1 { 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)? { if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());
} }
@ -94,6 +97,10 @@ pub fn list_archive_contents(
} }
SevenZip => { SevenZip => {
if formats.len() > 1 { 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(); warn_user_about_loading_zip_in_memory();
if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? { if !user_wants_to_continue(archive_path, question_policy, QuestionAction::Decompression)? {
return Ok(()); return Ok(());

View File

@ -16,32 +16,28 @@ use crate::{
error::{Error, FinalError}, error::{Error, FinalError},
extension::{self, parse_format}, extension::{self, parse_format},
list::ListOptions, list::ListOptions,
utils::{ utils::{self, colors::*, logger::info_accessible, to_utf, EscapedPathDisplay, FileVisibilityPolicy},
self,
logger::{info_accessible, warning},
to_utf, EscapedPathDisplay, FileVisibilityPolicy,
},
CliArgs, QuestionPolicy, CliArgs, QuestionPolicy,
}; };
/// Warn the user that (de)compressing this .zip archive might freeze their system. /// Warn the user that (de)compressing this .zip archive might freeze their system.
fn warn_user_about_loading_zip_in_memory() { fn warn_user_about_loading_zip_in_memory() {
const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ const ZIP_IN_MEMORY_LIMITATION_WARNING: &str = "\n \
\tThe format '.zip' is limited and cannot be (de)compressed using encoding streams.\n\ The format '.zip' is limited by design and cannot be (de)compressed with encoding streams.\n \
\tWhen using '.zip' with other formats, (de)compression must be done in-memory\n\ When chaining '.zip' with other formats, all (de)compression needs to be done in-memory\n \
\tCareful, you might run out of RAM if the archive is too large!"; 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. /// Warn the user that (de)compressing this .7z archive might freeze their system.
fn warn_user_about_loading_sevenz_in_memory() { fn warn_user_about_loading_sevenz_in_memory() {
const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n\ const SEVENZ_IN_MEMORY_LIMITATION_WARNING: &str = "\n \
\tThe format '.7z' is limited and cannot be (de)compressed using encoding streams.\n\ The format '.7z' is limited by design and cannot be (de)compressed with encoding streams.\n \
\tWhen using '.7z' with other formats, (de)compression must be done in-memory\n\ When chaining '.7z' with other formats, all (de)compression needs to be done in-memory\n \
\tCareful, you might run out of RAM if the archive is too large!"; 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 /// This function checks what command needs to be run and performs A LOT of ahead-of-time checks

12
src/utils/io.rs Normal file
View File

@ -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<StdioOutputLocks> {
let mut stdout = stdout().lock();
stdout.flush()?;
let mut stderr = stderr().lock();
stderr.flush()?;
Ok((stdout, stderr))
}

View File

@ -7,6 +7,7 @@ pub mod colors;
mod file_visibility; mod file_visibility;
mod formatting; mod formatting;
mod fs; mod fs;
pub mod io;
pub mod logger; pub mod logger;
mod question; mod question;

View File

@ -5,7 +5,7 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
io::{self, Write}, io::{stdin, BufRead},
path::Path, path::Path,
}; };
@ -15,7 +15,7 @@ use super::{strip_cur_dir, to_utf};
use crate::{ use crate::{
accessible::is_running_in_accessible_mode, accessible::is_running_in_accessible_mode,
error::{Error, FinalError, Result}, error::{Error, FinalError, Result},
utils::{self, colors}, utils::{self, colors, io::lock_and_flush_output_stdio},
}; };
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[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)), (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 // Ask the same question to end while no valid answers are given
loop { loop {
if is_running_in_accessible_mode() { if is_running_in_accessible_mode() {
print!( eprintln!(
"{} {}yes{}/{}no{}: ", "{} {}yes{}/{}no{}: ",
message, message,
*colors::GREEN, *colors::GREEN,
@ -133,7 +136,7 @@ impl<'a> Confirmation<'a> {
*colors::RESET *colors::RESET
); );
} else { } else {
print!( eprintln!(
"{} [{}Y{}/{}n{}] ", "{} [{}Y{}/{}n{}] ",
message, message,
*colors::GREEN, *colors::GREEN,
@ -142,11 +145,9 @@ impl<'a> Confirmation<'a> {
*colors::RESET *colors::RESET
); );
} }
let _stdout_lock = io::stdout().lock();
io::stdout().flush()?;
let mut answer = String::new(); 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 { if bytes_read == 0 {
let error = FinalError::with_title("Unexpected EOF when asking question.") let error = FinalError::with_title("Unexpected EOF when asking question.")