diff --git a/crates/icy_board/src/menu_runner/login.rs b/crates/icy_board/src/menu_runner/login.rs index 0ff64b0d..8a0454ca 100644 --- a/crates/icy_board/src/menu_runner/login.rs +++ b/crates/icy_board/src/menu_runner/login.rs @@ -291,7 +291,8 @@ impl PcbBoardCommand { } } if settings.ask_xfer_protocol { - let protocol = self.state.ask_protocols("N".to_string()).await?; + let protocol = self.state.ask_protocols("N").await?; + self.state.new_line().await?; if !protocol.is_empty() { new_user.protocol = protocol; } else { @@ -656,7 +657,7 @@ impl PcbBoardCommand { self.state.new_line().await?; let date_formats = self.state.get_board().await.languages.date_formats.clone(); - self.state.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.state.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; let mut preview = String::new(); for (i, (disp_fmt, fmt)) in date_formats.iter().enumerate() { if fmt == cur_format { diff --git a/crates/icy_board_engine/src/data/ICBTEXT.toml b/crates/icy_board_engine/src/data/ICBTEXT.toml index c0a3dce6..4ee33bd1 100644 --- a/crates/icy_board_engine/src/data/ICBTEXT.toml +++ b/crates/icy_board_engine/src/data/ICBTEXT.toml @@ -1848,7 +1848,7 @@ style = "White" text = "Output File Name" style = "Red" -[BatchDownloadTime] +[_BatchDownloadTime] text = "Batch Download Time:~" style = "Yellow" diff --git a/crates/icy_board_engine/src/icy_board/icb_config.rs b/crates/icy_board_engine/src/icy_board/icb_config.rs index 10e49052..884c3f24 100644 --- a/crates/icy_board_engine/src/icy_board/icb_config.rs +++ b/crates/icy_board_engine/src/icy_board/icb_config.rs @@ -194,6 +194,43 @@ pub enum IcbColor { IcyEngine(Color), } +impl IcbColor { + pub fn dos_black() -> Self { + IcbColor::Dos(0x00) + } + pub fn dos_blue() -> Self { + IcbColor::Dos(0x00) + } + + pub fn dos_light_blue() -> Self { + IcbColor::Dos(0x09) + } + + pub fn dos_light_green() -> Self { + IcbColor::Dos(0x0A) + } + + pub fn dos_cyan() -> Self { + IcbColor::Dos(0x0B) + } + + pub fn dos_light_red() -> Self { + IcbColor::Dos(0x0C) + } + + pub fn dos_magenta() -> Self { + IcbColor::Dos(0x0D) + } + + pub fn dos_yellow() -> Self { + IcbColor::Dos(0x0E) + } + + pub fn dos_white() -> Self { + IcbColor::Dos(0x0F) + } +} + impl From<u8> for IcbColor { fn from(color: u8) -> Self { IcbColor::Dos(color) diff --git a/crates/icy_board_engine/src/icy_board/icb_text.rs b/crates/icy_board_engine/src/icy_board/icb_text.rs index 08a13624..fc05c834 100644 --- a/crates/icy_board_engine/src/icy_board/icb_text.rs +++ b/crates/icy_board_engine/src/icy_board/icb_text.rs @@ -979,7 +979,7 @@ pub enum IceText { /// `Output File Name` OutputFileName = 479, /// `Batch Download Time:~` - BatchDownloadTime = 480, + _BatchDownloadTime = 480, /// `Batch Download Size:~` BatchDownloadSize = 481, /// `Batch Protocol Type: ~` @@ -1633,13 +1633,13 @@ impl IcbTextStyle { pub fn to_color(&self) -> IcbColor { match self { IcbTextStyle::Plain => IcbColor::None, - IcbTextStyle::Red => IcbColor::Dos(12), - IcbTextStyle::Green => IcbColor::Dos(10), - IcbTextStyle::Yellow => IcbColor::Dos(14), - IcbTextStyle::Blue => IcbColor::Dos(9), - IcbTextStyle::Purple => IcbColor::Dos(13), - IcbTextStyle::Cyan => IcbColor::Dos(11), - IcbTextStyle::White => IcbColor::Dos(15), + IcbTextStyle::Red => IcbColor::dos_light_red(), + IcbTextStyle::Green => IcbColor::dos_light_green(), + IcbTextStyle::Yellow => IcbColor::dos_yellow(), + IcbTextStyle::Blue => IcbColor::dos_light_blue(), + IcbTextStyle::Purple => IcbColor::dos_magenta(), + IcbTextStyle::Cyan => IcbColor::dos_cyan(), + IcbTextStyle::White => IcbColor::dos_white(), } } diff --git a/crates/icy_board_engine/src/icy_board/state/functions.rs b/crates/icy_board_engine/src/icy_board/state/functions.rs index 75ae486d..5752cc3d 100644 --- a/crates/icy_board_engine/src/icy_board/state/functions.rs +++ b/crates/icy_board_engine/src/icy_board/state/functions.rs @@ -46,17 +46,6 @@ pub mod display_flags { pub const NOTBLANK: i32 = 0x02000; // same as 'AUTO' } -pub mod pcb_colors { - use crate::icy_board::icb_config::IcbColor; - - pub const BLUE: IcbColor = IcbColor::Dos(9); - pub const GREEN: IcbColor = IcbColor::Dos(10); - pub const CYAN: IcbColor = IcbColor::Dos(11); - pub const RED: IcbColor = IcbColor::Dos(12); - pub const MAGENTA: IcbColor = IcbColor::Dos(13); - pub const YELLOW: IcbColor = IcbColor::Dos(14); - pub const WHITE: IcbColor = IcbColor::Dos(15); -} const TXT_STOPCHAR: char = '_'; lazy_static::lazy_static! { @@ -233,7 +222,7 @@ impl IcyBoardState { let Ok(content) = fs::read(resolved_name) else { if display_error { self.bell().await?; - self.set_color(TerminalTarget::Both, pcb_colors::RED).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_red()).await?; self.print(TerminalTarget::Both, &format!("\r\n({}) is missing!\r\n\r\n", file_name.as_ref().display())) .await?; } diff --git a/crates/icy_board_engine/src/icy_board/state/menu_runner.rs b/crates/icy_board_engine/src/icy_board/state/menu_runner.rs index 0cb44340..85f2dad3 100644 --- a/crates/icy_board_engine/src/icy_board/state/menu_runner.rs +++ b/crates/icy_board_engine/src/icy_board/state/menu_runner.rs @@ -224,7 +224,7 @@ impl IcyBoardState { } CommandType::FlagFiles => { // FLAG - self.flag_files().await?; + self.flag_files_cmd(false).await?; } CommandType::EnterMessage => { // E diff --git a/crates/icy_board_engine/src/icy_board/state/mod.rs b/crates/icy_board_engine/src/icy_board/state/mod.rs index 830d39f5..803cbed5 100644 --- a/crates/icy_board_engine/src/icy_board/state/mod.rs +++ b/crates/icy_board_engine/src/icy_board/state/mod.rs @@ -1,5 +1,5 @@ use std::{ - collections::{HashMap, HashSet, VecDeque}, + collections::{HashMap, VecDeque}, fmt::Alignment, fs, path::{Path, PathBuf}, @@ -102,35 +102,16 @@ impl Default for DisplayOptions { pub struct TransferStatistics { pub downloaded_files: usize, pub downloaded_bytes: usize, + pub downloaded_cps: usize, + pub uploaded_files: usize, pub uploaded_bytes: usize, - - pub dl_transfer_time: usize, - pub ul_transfer_time: usize, + pub uploaded_cps: usize, } impl TransferStatistics { - pub fn get_cps_download(&self) -> usize { - if self.dl_transfer_time == 0 { - return 0; - } - self.downloaded_bytes / self.dl_transfer_time - } - - pub fn get_cps_upload(&self) -> usize { - if self.ul_transfer_time == 0 { - return 0; - } - self.uploaded_bytes / self.ul_transfer_time - } - pub fn get_cps_both(&self) -> usize { - let total_time = self.dl_transfer_time + self.ul_transfer_time; - if total_time == 0 { - return 0; - } - // actually correct - it's not the average, but the accumlated csp - (self.downloaded_bytes + self.uploaded_bytes) / total_time + (self.downloaded_cps + self.uploaded_cps) / 2 } } @@ -205,7 +186,7 @@ pub struct Session { // The maximum number of files in flagged_files pub batch_limit: usize, - pub flagged_files: HashSet<PathBuf>, + pub flagged_files: Vec<PathBuf>, } impl Session { @@ -250,7 +231,7 @@ impl Session { saved_color: IcbColor::Dos(7), sysop_name: "SYSOP".to_string(), - flagged_files: HashSet::new(), + flagged_files: Vec::new(), emsi: None, paged_sysop: false, bytes_remaining: 0, @@ -1027,7 +1008,7 @@ impl IcyBoardState { return Ok(()); } if ch.source == KeySource::Sysop { - self.set_color(TerminalTarget::Both, IcbColor::Dos(10)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; } else { self.reset_color(TerminalTarget::Both).await?; } @@ -1377,7 +1358,9 @@ impl IcyBoardState { "FIRSTU" => { result = self.session.get_first_name().to_uppercase(); } - "FNUM" => {} + "FNUM" => { + result = (self.session.flagged_files.len() + 1).to_string(); + } "FREESPACE" => {} "GFXMODE" => { result = match self.session.disp_options.grapics_mode { @@ -1478,7 +1461,7 @@ impl IcyBoardState { return None; } "PROLTR" | "PRODESC" | "PWXDATE" | "PWXDAYS" | "QOFF" | "QON" | "RATIOBYTES" | "RATIOFILES" => {} - "RCPS" => result = self.transfer_statistics.get_cps_upload().to_string(), + "RCPS" => result = self.transfer_statistics.uploaded_cps.to_string(), "RBYTES" => result = self.transfer_statistics.uploaded_bytes.to_string(), "RFILES" => result = self.transfer_statistics.uploaded_files.to_string(), "REAL" => { @@ -1491,7 +1474,7 @@ impl IcyBoardState { result = user.security_level.to_string() } } - "SCPS" => result = self.transfer_statistics.get_cps_download().to_string(), + "SCPS" => result = self.transfer_statistics.downloaded_cps.to_string(), "SBYTES" => result = self.transfer_statistics.downloaded_bytes.to_string(), "SFILES" => result = self.transfer_statistics.downloaded_files.to_string(), "SYSDATE" => { @@ -1799,7 +1782,7 @@ impl IcyBoardState { let pos = self.user_screen.caret.get_position(); self.set_activity(NodeStatus::NodeMessage).await; self.new_line().await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(15)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_white()).await?; self.println(TerminalTarget::Both, &"Broadcast:").await?; self.println(TerminalTarget::Both, &msg).await?; self.bell().await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/login.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/login.rs index 90a91b01..3ae2279d 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/login.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/login.rs @@ -279,7 +279,7 @@ impl IcyBoardState { } } if settings.ask_xfer_protocol { - let protocol = self.ask_protocols("N".to_string()).await?; + let protocol = self.ask_protocols("N").await?; if !protocol.is_empty() { new_user.protocol = protocol; } else { @@ -610,7 +610,7 @@ impl IcyBoardState { self.new_line().await?; let date_formats = self.get_board().await.languages.date_formats.clone(); - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; let mut preview = String::new(); for (i, (disp_fmt, fmt)) in date_formats.iter().enumerate() { if fmt == cur_format { diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/editor/mod.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/editor/mod.rs index 2ac8fecb..8ac7014a 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/editor/mod.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/editor/mod.rs @@ -122,7 +122,7 @@ impl EditState { let line: usize = self.read_line_number(IceText::DeleteLineNumber, state).await?; if line > 0 && (line as usize - 1) < self.msg.len() { self.print_divider(state).await?; - state.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + state.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; state.print(TerminalTarget::Both, &format!("{}: ", line)).await?; state.reset_color(TerminalTarget::Both).await?; state.println(TerminalTarget::Both, &self.msg[line - 1]).await?; @@ -194,7 +194,7 @@ impl EditState { let to_part = format!("{}{}", to_txt, self.to); let subj_part = format!("{}{} {}", subj_txt, self.subj, Local::now().format("%H:%M")); - state.set_color(TerminalTarget::Both, IcbColor::Dos(14)).await?; + state.set_color(TerminalTarget::Both, IcbColor::dos_yellow()).await?; state.println(TerminalTarget::Both, &format!("{:<38}{:<38}", to_part, subj_part)).await?; self.print_divider(state).await?; @@ -202,7 +202,7 @@ impl EditState { } async fn print_divider(&mut self, state: &mut IcyBoardState) -> Res<()> { - state.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + state.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; state.println(TerminalTarget::Both, &str::repeat("-", 79)).await?; state.reset_color(TerminalTarget::Both).await?; Ok(()) diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/filebrowser/more_prompt.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/filebrowser/more_prompt.rs index a0863dac..0bfeb9a1 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/filebrowser/more_prompt.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/filebrowser/more_prompt.rs @@ -27,7 +27,7 @@ impl IcyBoardState { self.session.push_tokens(&input); match self.session.tokens.pop_front().unwrap_or_default().to_ascii_uppercase().as_str() { "F" | "FL" | "FLA" | "FLAG" => { - self.flag_files().await?; + self.flag_files_cmd(false).await?; } "V" | "S" => { self.view_file().await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/fileview/mod.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/fileview/mod.rs index 70c1bf47..2795956b 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/mods/fileview/mod.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/mods/fileview/mod.rs @@ -70,7 +70,7 @@ impl IcyBoardState { .await?; self.println(TerminalTarget::Both, " Length Date Time Name").await?; self.println(TerminalTarget::Both, " ======== ========== ===== ======").await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; for info in &file_content { if self.session.disp_options.abort_printout { break; @@ -90,11 +90,11 @@ impl IcyBoardState { self.println(TerminalTarget::Both, &info.name).await?; len += info.size; } - self.set_color(TerminalTarget::Both, IcbColor::Dos(14)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_yellow()).await?; self.set_color(TerminalTarget::Both, colors.file_head.clone()).await?; self.println(TerminalTarget::Both, "--------- ------").await?; self.set_color(TerminalTarget::Both, colors.file_size).await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(15)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_white()).await?; self.println( TerminalTarget::Both, &format!("{:>9} {} files", humanize_bytes_decimal!(len).to_string(), file_content.len()), diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/d_download.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/d_download.rs index 7a8d6847..980e90fc 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/d_download.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/d_download.rs @@ -1,5 +1,11 @@ +use std::os::unix::fs::MetadataExt; use std::path::PathBuf; +use async_recursion::async_recursion; +use humanize_bytes::humanize_bytes_decimal; + +use crate::icy_board::icb_config::IcbColor; +use crate::icy_board::state::functions::MASK_NUM; use crate::{icy_board::state::IcyBoardState, Res}; use crate::{ icy_board::{icb_text::IceText, state::functions::display_flags}, @@ -8,76 +14,240 @@ use crate::{ impl IcyBoardState { pub async fn download(&mut self) -> Res<()> { - if self.session.flagged_files.is_empty() { - self.println(TerminalTarget::Both, "No files flagged for download.").await?; - self.new_line().await?; - self.press_enter().await?; - self.display_current_menu = true; + if !self.session.flagged_files.is_empty() { + let download_tagged = self + .input_field( + IceText::DownloadTagged, + 1, + "", + &"", + Some(self.session.yes_char.to_string()), + display_flags::NEWLINE | display_flags::UPCASE | display_flags::LFBEFORE | display_flags::YESNO | display_flags::FIELDLEN, + ) + .await?; + + if download_tagged == self.session.no_char.to_uppercase().to_string() { + self.new_line().await?; + self.press_enter().await?; + self.display_current_menu = true; + return Ok(()); + } } - let download_tagged = self - .input_field( - IceText::DownloadTagged, - 1, - "", - &"", - Some(self.session.yes_char.to_string()), - display_flags::NEWLINE | display_flags::UPCASE | display_flags::YESNO | display_flags::FIELDLEN, - ) - .await?; + self.flag_files_cmd(true).await?; - if download_tagged == self.session.no_char.to_uppercase().to_string() { - self.new_line().await?; + if self.session.flagged_files.is_empty() { self.press_enter().await?; self.display_current_menu = true; return Ok(()); } - let protocol_str: String = self.session.current_user.as_ref().unwrap().protocol.clone(); + let mut protocol_str: String = self.session.current_user.as_ref().unwrap().protocol.clone(); let mut protocol = None; - for p in &self.get_board().await.protocols.protocols { - if p.is_enabled && p.char_code == protocol_str { - protocol = Some(p.send_command.clone()); - break; + let mut p_descr = "None".to_string(); + + let mut goodbye_after_dl = false; + let mut do_dl = true; + loop { + for p in &self.get_board().await.protocols.protocols { + if p.is_enabled && p.char_code == protocol_str { + p_descr = p.description.clone(); + protocol = Some(p.send_command.clone()); + break; + } } - } - if let Some(protocol) = protocol { - let mut prot = protocol.create(); - let files: Vec<PathBuf> = self.session.flagged_files.drain().collect(); - for f in &files { - if !f.exists() { - log::error!("File not found: {:?}", f); - self.session.op_text = f.file_name().unwrap().to_string_lossy().to_string(); - self.display_text(IceText::NotFoundOnDisk, display_flags::NEWLINE).await?; - self.new_line().await?; - self.press_enter().await?; - return Ok(()); + let mut total_size = 0; + for path in &self.session.flagged_files { + if let Ok(data) = path.metadata() { + total_size += data.size(); } } - match prot.initiate_send(&mut *self.connection, &files).await { - Ok(mut state) => { - /* let mut c = BlockingConnection { - conn: &mut self.connection, - };*/ - while !state.is_finished { - if let Err(e) = prot.update_transfer(&mut *self.connection, &mut state).await { - log::error!("Error while updating file transfer with {:?} : {}", protocol, e); - self.display_text(IceText::TransferAborted, display_flags::NEWLINE).await?; - break; - } + self.display_text(IceText::BatchDownloadSize, display_flags::DEFAULT).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; + self.println(TerminalTarget::Both, &format!(" {}", humanize_bytes_decimal!(total_size).to_string())) + .await?; + + self.display_text(IceText::BatchProtocol, display_flags::DEFAULT).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; + self.println(TerminalTarget::Both, &p_descr).await?; + self.display_text(IceText::ReadyToSendBatch, display_flags::NEWLINE | display_flags::LFAFTER) + .await?; + + let input = self + .input_field( + IceText::GoodbyeAfterDownload, + 1, + &DL_LISTMASK, + &"", + None, + display_flags::NEWLINE | display_flags::UPCASE | display_flags::FIELDLEN, + ) + .await?; + + match input.as_str() { + "A" => { + do_dl = false; + break; + } + "E" => { + self.edit_dl_batch().await?; + } + "G" => { + goodbye_after_dl = true; + break; + } + "L" => { + self.list_dl_batch().await?; + } + "P" => { + let protocol = self.ask_protocols(&protocol_str).await?; + + if !protocol.is_empty() { + protocol_str = protocol; } } - Err(e) => { - log::error!("Error while initiating file transfer with {:?} : {}", protocol, e); - self.println(TerminalTarget::Both, &format!("Error: {}", e)).await?; + _ => { + break; } } - } else { - self.println(TerminalTarget::Both, "Protocol not found.").await?; + } + if do_dl { + self.display_text(IceText::SendingFiles, display_flags::NEWLINE).await?; + + if let Some(protocol) = &protocol { + let mut prot = protocol.create(); + let files: Vec<PathBuf> = self.session.flagged_files.drain(..).collect(); + for f in &files { + if !f.exists() { + log::error!("File not found: {:?}", f); + self.session.op_text = f.file_name().unwrap().to_string_lossy().to_string(); + self.display_text(IceText::NotFoundOnDisk, display_flags::NEWLINE).await?; + self.new_line().await?; + self.press_enter().await?; + return Ok(()); + } + } + match prot.initiate_send(&mut *self.connection, &files).await { + Ok(mut state) => { + while !state.is_finished { + if let Err(e) = prot.update_transfer(&mut *self.connection, &mut state).await { + log::error!("Error while updating file transfer with {:?} : {}", protocol, e); + self.display_text(IceText::TransferAborted, display_flags::NEWLINE).await?; + break; + } + } + self.display_text(IceText::BatchTransferEnded, display_flags::LFBEFORE).await?; + self.transfer_statistics.downloaded_bytes = state.send_state.total_bytes_transfered as usize; + self.transfer_statistics.downloaded_files = state.send_state.finished_files.len(); + self.transfer_statistics.downloaded_cps = state.send_state.get_bps() as usize; + self.display_text(IceText::BatchSend, display_flags::LFBEFORE).await?; + } + Err(e) => { + log::error!("Error while initiating file transfer with {:?} : {}", protocol, e); + self.println(TerminalTarget::Both, &format!("Error: {}", e)).await?; + } + } + } else { + self.println(TerminalTarget::Both, "Protocol not found.").await?; + } + + if goodbye_after_dl { + self.goodbye().await?; + } } self.new_line().await?; self.press_enter().await?; Ok(()) } + + #[async_recursion(?Send)] + async fn edit_dl_batch(&mut self) -> Res<()> { + self.new_line().await?; + + loop { + let input = self + .input_field( + IceText::EditBatch, + 1, + &DL_EDITMASK, + &"", + None, + display_flags::NEWLINE | display_flags::UPCASE | display_flags::FIELDLEN, + ) + .await?; + + match input.as_str() { + "A" => { + self.new_line().await?; + self.flag_files_cmd(true).await?; + } + "R" => { + self.remove_dl_batch().await?; + } + "L" => { + self.list_dl_batch().await?; + } + _ => { + break; + } + } + } + Ok(()) + } + + async fn remove_dl_batch(&mut self) -> Res<()> { + self.session.op_text = format!("1-{}", self.session.flagged_files.len()); + let input = self + .input_field( + IceText::RemoveFileNumber, + 16, + &MASK_NUM, + &"", + None, + display_flags::NEWLINE | display_flags::UPCASE | display_flags::LFBEFORE, + ) + .await?; + self.session.push_tokens(&input); + + let mut remove = Vec::new(); + while let Some(token) = self.session.tokens.pop_front() { + if let Ok(num) = token.parse::<usize>() { + if num == 0 { + continue; + } + if let Some(path) = &self.session.flagged_files.get(num - 1) { + remove.push(num - 1); + self.session.op_text = path.file_name().unwrap_or_default().to_string_lossy().to_string(); + self.display_text(IceText::RemovedFile, display_flags::NEWLINE).await?; + } + } + } + remove.sort_by(|a, b| b.cmp(a)); + for r in remove { + self.session.flagged_files.remove(r); + } + self.new_line().await?; + Ok(()) + } + async fn list_dl_batch(&mut self) -> Res<()> { + self.new_line().await?; + for (i, path) in self.session.flagged_files.clone().iter().enumerate() { + let size = if let Ok(data) = path.metadata() { data.size() } else { 0 }; + self.display_text(IceText::FileSelected, display_flags::DEFAULT).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; + + let number = format!("({})", i + 1); + self.print(TerminalTarget::Both, &format!("{number:<5}{:>8} ", humanize_bytes_decimal!(size).to_string())) + .await?; + self.println(TerminalTarget::Both, &format!("{}", path.file_name().unwrap_or_default().to_string_lossy())) + .await?; + } + self.new_line().await?; + + Ok(()) + } } + +const DL_LISTMASK: &str = "AEGLP"; +const DL_EDITMASK: &str = "ARL"; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/f_show_file_directories.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/f_show_file_directories.rs index 604e2f0d..c975f9b1 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/f_show_file_directories.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/f_show_file_directories.rs @@ -88,7 +88,7 @@ impl IcyBoardState { } } "F" | "FL" | "FLA" | "FLAG" => { - self.flag_files().await?; + self.flag_files_cmd(false).await?; } "L" => { self.find_files_cmd().await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/flag_flag_files.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/flag_flag_files.rs index 40e5e0a9..3d49fef3 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/flag_flag_files.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/flag_flag_files.rs @@ -10,18 +10,22 @@ use crate::{ }; impl IcyBoardState { - pub async fn flag_files(&mut self) -> Res<()> { + pub async fn flag_files_cmd(&mut self, show_flagged: bool) -> Res<()> { // flag let input = if let Some(token) = self.session.tokens.pop_front() { token } else { self.input_field( - IceText::FlagForDownload, + if show_flagged { + IceText::FileNameToDownloadBatch + } else { + IceText::FlagForDownload + }, 60, &MASK_ASCII, &"hlpflag", None, - display_flags::NEWLINE | display_flags::UPCASE | display_flags::LFAFTER | display_flags::HIGHASCII, + display_flags::NEWLINE | display_flags::UPCASE | display_flags::LFAFTER | display_flags::LFBEFORE | display_flags::HIGHASCII, ) .await? }; @@ -72,14 +76,15 @@ impl IcyBoardState { continue; } - if !self.session.flagged_files.insert(file) { + if self.session.flagged_files.contains(&file) { self.session.op_text = name; self.display_text(IceText::DuplicateBatchFile, display_flags::NEWLINE).await?; continue; } + self.session.flagged_files.push(file); let count = self.session.flagged_files.len(); - self.set_color(TerminalTarget::Both, IcbColor::Dos(10)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; let nr: String = format!("({})", count); self.println( TerminalTarget::Both, diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/l_find_files.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/l_find_files.rs index 83c53c07..a60dced7 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/l_find_files.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/l_find_files.rs @@ -106,7 +106,7 @@ impl IcyBoardState { self.display_text(IceText::ScanningDirectory, display_flags::DEFAULT).await?; self.print(TerminalTarget::Both, &format!(" {} ", area + 1)).await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(10)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; self.print(TerminalTarget::Both, &format!("({})", self.session.current_conference.directories[area].name)) .await?; self.new_line().await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/lang_set_language.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/lang_set_language.rs index 329089ab..5feba4e8 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/lang_set_language.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/lang_set_language.rs @@ -45,7 +45,7 @@ impl IcyBoardState { self.display_text(IceText::LanguageAvailable, display_flags::NEWLINE | display_flags::LFAFTER) .await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; for line in languages { self.print(TerminalTarget::Both, &line).await?; self.new_line().await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/q_quick_message_scan.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/q_quick_message_scan.rs index d922f545..3c05e2f2 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/q_quick_message_scan.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/q_quick_message_scan.rs @@ -102,7 +102,7 @@ impl IcyBoardState { self.display_text(IceText::QuickScanHeader, display_flags::NEWLINE).await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; for i in number..message_base.active_messages() { match message_base.read_header(i) { Ok(header) => { diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/s_take_survey.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/s_take_survey.rs index 21d6bab3..72e77b77 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/s_take_survey.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/s_take_survey.rs @@ -168,7 +168,7 @@ impl IcyBoardState { }; if txt.eq_ignore_ascii_case(&self.session.yes_char.to_string()) { for question in &lines[start_line..] { - self.set_color(TerminalTarget::Both, IcbColor::Dos(14)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_yellow()).await?; self.print(TerminalTarget::Both, question).await?; self.new_line().await?; self.reset_color(TerminalTarget::Both).await?; diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/t_set_transfer_protocol.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/t_set_transfer_protocol.rs index 0157006c..5da5c0e4 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/t_set_transfer_protocol.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/t_set_transfer_protocol.rs @@ -20,7 +20,7 @@ impl IcyBoardState { String::new() }; - let protocol = self.ask_protocols(cur_protocol).await?; + let protocol = self.ask_protocols(&cur_protocol).await?; if !protocol.is_empty() { let selected_protocol = protocol.to_ascii_uppercase(); @@ -35,7 +35,7 @@ impl IcyBoardState { if let Some(user) = &mut self.session.current_user { user.protocol = selected_protocol; } - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; self.print(TerminalTarget::Both, &txt).await?; self.new_line().await?; self.new_line().await?; @@ -45,7 +45,7 @@ impl IcyBoardState { Ok(()) } - pub async fn ask_protocols(&mut self, cur_protocol: String) -> Res<String> { + pub async fn ask_protocols(&mut self, cur_protocol: &str) -> Res<String> { let mut protocols = Vec::new(); self.new_line().await?; for protocol in self.get_board().await.protocols.iter() { @@ -71,7 +71,7 @@ impl IcyBoardState { )); } - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; for line in protocols { self.print(TerminalTarget::Both, &line).await?; self.new_line().await?; @@ -83,7 +83,7 @@ impl IcyBoardState { &MASK_ALNUM, "", Some(cur_protocol.to_string()), - display_flags::NEWLINE | display_flags::LFBEFORE | display_flags::LFAFTER | display_flags::UPCASE | display_flags::FIELDLEN, + display_flags::NEWLINE | display_flags::LFBEFORE | display_flags::UPCASE | display_flags::FIELDLEN, ) .await?; Ok(protocol) diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/w_write_settings.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/w_write_settings.rs index b09df06b..49d581eb 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/w_write_settings.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/w_write_settings.rs @@ -129,7 +129,8 @@ impl IcyBoardState { } if settings.ask_xfer_protocol { - let protocol = self.ask_protocols("N".to_string()).await?; + let protocol = self.ask_protocols("N").await?; + self.new_line().await?; if !protocol.is_empty() { new_user.protocol = protocol; } else { diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/who_display_nodes.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/who_display_nodes.rs index be02849f..996a58c4 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/who_display_nodes.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/who_display_nodes.rs @@ -22,7 +22,7 @@ impl IcyBoardState { } } } - self.set_color(TerminalTarget::Both, IcbColor::Dos(11)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_cyan()).await?; self.println(TerminalTarget::Both, &lines.join("\r\n")).await?; self.new_line().await?; Ok(()) diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/y_your_mail_scan.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/y_your_mail_scan.rs index 349e7cd3..e54d8128 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/y_your_mail_scan.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/y_your_mail_scan.rs @@ -148,14 +148,14 @@ impl IcyBoardState { } } if res.msg_to > 0 { - self.set_color(TerminalTarget::Both, IcbColor::Dos(15)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_white()).await?; } else { self.reset_color(TerminalTarget::Both).await?; } self.print(TerminalTarget::Both, &format!("{:>6}", res.msg_to)).await?; if res.msg_from > 0 { - self.set_color(TerminalTarget::Both, IcbColor::Dos(15)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_white()).await?; } else { self.reset_color(TerminalTarget::Both).await?; } diff --git a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/z_zippy_directory_scan.rs b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/z_zippy_directory_scan.rs index 06b85658..66fe7286 100644 --- a/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/z_zippy_directory_scan.rs +++ b/crates/icy_board_engine/src/icy_board/state/user_commands/pcb/z_zippy_directory_scan.rs @@ -102,7 +102,7 @@ impl IcyBoardState { self.display_text(IceText::ScanningDirectory, display_flags::DEFAULT).await?; self.print(TerminalTarget::Both, &format!(" {} ", area + 1)).await?; - self.set_color(TerminalTarget::Both, IcbColor::Dos(10)).await?; + self.set_color(TerminalTarget::Both, IcbColor::dos_light_green()).await?; self.print(TerminalTarget::Both, &format!("({})", self.session.current_conference.directories[area].name)) .await?; self.new_line().await?; diff --git a/crates/icy_board_engine/src/vm/statements/predefined_procedures.rs b/crates/icy_board_engine/src/vm/statements/predefined_procedures.rs index c06ba193..c5104107 100644 --- a/crates/icy_board_engine/src/vm/statements/predefined_procedures.rs +++ b/crates/icy_board_engine/src/vm/statements/predefined_procedures.rs @@ -256,7 +256,7 @@ pub async fn log(vm: &mut VirtualMachine<'_>, args: &[PPEExpr]) -> Res<()> { pub async fn input(vm: &mut VirtualMachine<'_>, args: &[PPEExpr]) -> Res<()> { let prompt = vm.eval_expr(&args[0]).await?.as_string(); - let color = IcbColor::Dos(14); + let color = IcbColor::dos_yellow(); let d = get_default_string(vm, &args[1]).await; let output = vm .icy_board_state