Files
shed/src/fern.rs

128 lines
2.8 KiB
Rust

#![allow(
clippy::derivable_impls,
clippy::tabs_in_doc_comments,
clippy::while_let_on_iterator
)]
pub mod prelude;
pub mod libsh;
pub mod prompt;
pub mod procio;
pub mod parse;
pub mod expand;
pub mod state;
pub mod builtin;
pub mod jobs;
pub mod signal;
pub mod getopt;
pub mod shopt;
#[cfg(test)]
pub mod tests;
use crate::libsh::sys::{save_termios, set_termios};
use crate::parse::execute::exec_input;
use crate::signal::sig_setup;
use crate::state::source_rc;
use crate::prelude::*;
use clap::Parser;
use shopt::FernEditMode;
use state::{read_shopts, read_vars, write_shopts, write_vars};
#[derive(Parser,Debug)]
struct FernArgs {
script: Option<String>,
#[arg(trailing_var_arg = true)]
script_args: Vec<String>,
#[arg(long)]
version: bool
}
/// Force evaluation of lazily-initialized values early in shell startup.
///
/// In particular, this ensures that the variable table is initialized, which populates
/// environment variables from the system. If this initialization is deferred too long,
/// features like prompt expansion may fail due to missing environment variables.
///
/// This function triggers initialization by calling `read_vars` with a no-op closure,
/// which forces access to the variable table and causes its `LazyLock` constructor to run.
fn kickstart_lazy_evals() {
read_vars(|_| {});
}
fn main() {
kickstart_lazy_evals();
let args = FernArgs::parse();
if args.version {
println!("fern {}", env!("CARGO_PKG_VERSION"));
return;
}
if let Some(path) = args.script {
run_script(path, args.script_args);
} else {
fern_interactive();
}
}
fn run_script<P: AsRef<Path>>(path: P, args: Vec<String>) {
let path = path.as_ref();
if !path.is_file() {
eprintln!("fern: Failed to open input file: {}", path.display());
exit(1);
}
let Ok(input) = fs::read_to_string(path) else {
eprintln!("fern: Failed to read input file: {}", path.display());
exit(1);
};
write_vars(|v| v.bpush_arg(path.to_string_lossy().to_string()));
for arg in args {
write_vars(|v| v.bpush_arg(arg))
}
if let Err(e) = exec_input(input,None) {
eprintln!("{e}");
exit(1);
}
}
fn fern_interactive() {
save_termios();
set_termios();
sig_setup();
if let Err(e) = source_rc() {
eprintln!("{e}");
}
let mut readline_err_count: u32 = 0;
loop { // Main loop
let edit_mode = write_shopts(|opt| opt.query("prompt.edit_mode"))
.unwrap()
.map(|mode| mode.parse::<FernEditMode>().unwrap_or_default())
.unwrap();
let input = match prompt::read_line(edit_mode) {
Ok(line) => {
readline_err_count = 0;
line
}
Err(e) => {
eprintln!("{e}");
readline_err_count += 1;
if readline_err_count == 20 {
eprintln!("reached maximum readline error count, exiting");
break
} else {
continue
}
}
};
if let Err(e) = exec_input(input,None) {
eprintln!("{e}");
}
}
}