pub mod startup;
use self::startup::args::Parse;
use self::startup::init_file::run_rcfile;
use self::startup::input::prepare_input;
use std::cell::RefCell;
use std::ops::ControlFlow::{Break, Continue};
use std::rc::Rc;
use yash_env::Env;
use yash_env::RealSystem;
use yash_env::option::{Interactive, On};
use yash_env::semantics::{Divert, ExitStatus, exit_or_raise};
use yash_env::system::resource::GetRlimit;
use yash_env::system::{
Chdir, Disposition, Errno, Fcntl, GetCwd, GetUid, Isatty, Sigaction, Signals, Sysconf,
TcGetPgrp, Times, Umask, Write,
};
use yash_semantics::trap::run_exit_trap;
use yash_semantics::{Runtime, interactive_read_eval_loop, read_eval_loop};
async fn print_version<S>(env: &mut Env<S>)
where
S: Fcntl + Isatty + Write,
{
let version = env!("CARGO_PKG_VERSION");
let result = yash_builtin::common::output(env, &format!("yash {version}\n")).await;
env.exit_status = result.exit_status();
}
#[allow(clippy::await_holding_refcell_ref)]
async fn run_as_shell_process<S>(env: &mut Env<S>)
where
S: Chdir
+ GetCwd
+ GetRlimit
+ GetUid
+ Runtime
+ Sysconf
+ TcGetPgrp
+ Times
+ Umask
+ 'static,
{
let run = match self::startup::args::parse(std::env::args()) {
Ok(Parse::Help) => todo!("print help"),
Ok(Parse::Version) => return print_version(env).await,
Ok(Parse::Run(run)) => run,
Err(e) => {
let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
env.system.print_error(&format!("{arg0}: {e}\n")).await;
env.exit_status = ExitStatus::ERROR;
return;
}
};
env.variables.extend_env(std::env::vars());
let work = self::startup::configure_environment(env, run).await;
let is_interactive = env.options.get(Interactive) == On;
run_rcfile(env, work.rcfile).await;
let ref_env = RefCell::new(env);
let lexer = match prepare_input(&ref_env, &work.source).await {
Ok(lexer) => lexer,
Err(e) => {
let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
let message = format!("{arg0}: {e}\n");
let mut env = ref_env.borrow_mut();
env.system.print_error(&message).await;
env.exit_status = match e.errno {
Errno::ENOENT | Errno::ENOTDIR | Errno::EILSEQ => ExitStatus::NOT_FOUND,
_ => ExitStatus::NOEXEC,
};
return;
}
};
let result = if is_interactive {
interactive_read_eval_loop(&ref_env, &mut { lexer }).await
} else {
read_eval_loop(&ref_env, &mut { lexer }).await
};
let env = ref_env.into_inner();
env.apply_result(result);
match result {
Continue(())
| Break(Divert::Continue { .. })
| Break(Divert::Break { .. })
| Break(Divert::Return(_))
| Break(Divert::Interrupt(_))
| Break(Divert::Exit(_)) => run_exit_trap(env).await,
Break(Divert::Abort(_)) => (),
}
}
pub fn main() -> ! {
let system = unsafe { RealSystem::new() };
let mut env = Env::with_system(system);
env.system
.sigaction(RealSystem::SIGPIPE, Disposition::Default)
.ok();
let system = Rc::clone(&env.system);
let task = async {
run_as_shell_process(&mut env).await;
exit_or_raise(&env.system, env.exit_status).await
};
system.run_real(task)
}