1pub mod startup;
26use self::startup::args::Parse;
29use self::startup::init_file::run_rcfile;
30use self::startup::input::prepare_input;
31use std::cell::RefCell;
32use std::ops::ControlFlow::{Break, Continue};
33use yash_env::Env;
34use yash_env::RealSystem;
35use yash_env::System;
36use yash_env::option::{Interactive, On};
37use yash_env::signal;
38use yash_env::system::{Disposition, Errno, SystemEx as _};
39use yash_executor::Executor;
40use yash_semantics::trap::run_exit_trap;
41use yash_semantics::{Divert, ExitStatus};
42use yash_semantics::{interactive_read_eval_loop, read_eval_loop};
43
44async fn print_version(env: &mut Env) {
45 let version = env!("CARGO_PKG_VERSION");
46 let result = yash_builtin::common::output(env, &format!("yash {version}\n")).await;
47 env.exit_status = result.exit_status();
48}
49
50#[allow(clippy::await_holding_refcell_ref)]
52async fn run_as_shell_process(env: &mut Env) {
53 let run = match self::startup::args::parse(std::env::args()) {
55 Ok(Parse::Help) => todo!("print help"),
56 Ok(Parse::Version) => return print_version(env).await,
57 Ok(Parse::Run(run)) => run,
58 Err(e) => {
59 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
60 env.system.print_error(&format!("{arg0}: {e}\n")).await;
61 env.exit_status = ExitStatus::ERROR;
62 return;
63 }
64 };
65
66 env.variables.extend_env(std::env::vars());
68
69 let work = self::startup::configure_environment(env, run);
70
71 let is_interactive = env.options.get(Interactive) == On;
72
73 run_rcfile(env, work.rcfile).await;
76
77 let ref_env = RefCell::new(env);
79 let lexer = match prepare_input(&ref_env, &work.source) {
80 Ok(lexer) => lexer,
81 Err(e) => {
82 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
83 let message = format!("{arg0}: {e}\n");
84 let mut env = ref_env.borrow_mut();
89 env.system.print_error(&message).await;
90 env.exit_status = match e.errno {
91 Errno::ENOENT | Errno::ENOTDIR | Errno::EILSEQ => ExitStatus::NOT_FOUND,
92 _ => ExitStatus::NOEXEC,
93 };
94 return;
95 }
96 };
97
98 let result = if is_interactive {
100 interactive_read_eval_loop(&ref_env, &mut { lexer }).await
101 } else {
102 read_eval_loop(&ref_env, &mut { lexer }).await
103 };
104
105 let env = ref_env.into_inner();
106 env.apply_result(result);
107
108 match result {
109 Continue(())
110 | Break(Divert::Continue { .. })
111 | Break(Divert::Break { .. })
112 | Break(Divert::Return(_))
113 | Break(Divert::Interrupt(_))
114 | Break(Divert::Exit(_)) => run_exit_trap(env).await,
115 Break(Divert::Abort(_)) => (),
116 }
117}
118
119pub fn main() -> ! {
120 let system = unsafe { RealSystem::new() };
123 let mut env = Env::with_system(Box::new(system));
124
125 let sigpipe = env
129 .system
130 .signal_number_from_name(signal::Name::Pipe)
131 .unwrap();
132 _ = env.system.sigaction(sigpipe, Disposition::Default);
133
134 let system = env.system.clone();
135 let executor = Executor::new();
136 let task = Box::pin(async {
137 run_as_shell_process(&mut env).await;
138 env.system.exit_or_raise(env.exit_status).await;
139 });
140 unsafe { executor.spawn_pinned(task) }
143 loop {
144 executor.run_until_stalled();
145 system.select(false).ok();
146 }
147}