Implemented a new builtin and improved error handling
This commit is contained in:
55
src/fern.rs
55
src/fern.rs
@@ -11,6 +11,7 @@ pub mod signal;
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
pub mod getopt;
|
||||
pub mod shopt;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -22,8 +23,25 @@ use state::{source_rc, write_meta};
|
||||
use termios::{LocalFlags, Termios};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub static mut SAVED_TERMIOS: Option<Option<Termios>> = None;
|
||||
|
||||
/// The previous state of the terminal options.
|
||||
///
|
||||
/// This variable stores the terminal settings at the start of the program and restores them when the program exits.
|
||||
/// It is initialized exactly once at the start of the program and accessed exactly once at the end of the program.
|
||||
/// It will not be mutated or accessed under any other circumstances.
|
||||
///
|
||||
/// This ended up being necessary because wrapping Termios in a thread-safe way was unreasonably tricky.
|
||||
///
|
||||
/// The possible states of this variable are:
|
||||
/// - `None`: The terminal options have not been set yet (before initialization).
|
||||
/// - `Some(None)`: There were no terminal options to save (i.e., no terminal input detected).
|
||||
/// - `Some(Some(Termios))`: The terminal options (as `Termios`) have been saved.
|
||||
///
|
||||
/// **Important:** This static variable is mutable and accessed via unsafe code. It is only safe to use because:
|
||||
/// - It is set once during program startup and accessed once during program exit.
|
||||
/// - It is not mutated or accessed after the initial setup and final read.
|
||||
///
|
||||
/// **Caution:** Future changes to this code should respect these constraints to ensure safety. Modifying or accessing this variable outside the defined lifecycle could lead to undefined behavior.
|
||||
pub(crate) static mut SAVED_TERMIOS: Option<Option<Termios>> = None;
|
||||
|
||||
pub fn save_termios() {
|
||||
unsafe {
|
||||
@@ -37,15 +55,15 @@ pub fn save_termios() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(static_mut_refs)]
|
||||
pub fn get_saved_termios() -> Option<Termios> {
|
||||
unsafe {
|
||||
// This is only used when the shell exits so it's fine
|
||||
// SAVED_TERMIOS is only mutated once at the start as well
|
||||
SAVED_TERMIOS.clone().flatten()
|
||||
}
|
||||
pub unsafe fn get_saved_termios() -> Option<Termios> {
|
||||
// SAVED_TERMIOS should *only ever* be set once and accessed once
|
||||
// Set at the start of the program, and accessed during the exit of the program to reset the termios.
|
||||
// Do not use this variable anywhere else
|
||||
SAVED_TERMIOS.clone().flatten()
|
||||
}
|
||||
|
||||
/// Set termios to not echo control characters, like ^Z for instance
|
||||
fn set_termios() {
|
||||
if isatty(std::io::stdin().as_raw_fd()).unwrap() {
|
||||
let mut termios = termios::tcgetattr(std::io::stdin()).unwrap();
|
||||
@@ -73,12 +91,24 @@ fn main() {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
|
||||
loop {
|
||||
const MAX_READLINE_ERRORS: u32 = 5;
|
||||
let mut readline_err_count: u32 = 0;
|
||||
|
||||
loop { // Main loop
|
||||
let input = match prompt::read_line() {
|
||||
Ok(line) => line,
|
||||
Ok(line) => {
|
||||
readline_err_count = 0;
|
||||
line
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
continue
|
||||
readline_err_count += 1;
|
||||
if readline_err_count == MAX_READLINE_ERRORS {
|
||||
eprintln!("reached maximum readline error count, exiting");
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,4 +116,5 @@ fn main() {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user