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 std::rc::Rc;
34use yash_env::Env;
35use yash_env::RealSystem;
36use yash_env::option::{Interactive, On};
37use yash_env::semantics::{Divert, ExitStatus, exit_or_raise};
38use yash_env::system::concurrency::WriteAll;
39use yash_env::system::resource::GetRlimit;
40use yash_env::system::{
41 Chdir, Concurrent, Disposition, Errno, GetCwd, GetUid, Isatty, Sigaction as _, Signals as _,
42 Sysconf, TcGetPgrp, Times, Umask, Write,
43};
44use yash_semantics::trap::run_exit_trap;
45use yash_semantics::{Runtime, interactive_read_eval_loop, read_eval_loop};
46
47async fn print_version<S>(env: &mut Env<S>)
48where
49 S: Isatty + WriteAll,
50{
51 let version = env!("CARGO_PKG_VERSION");
52 let result = yash_builtin::common::output(env, &format!("yash {version}\n")).await;
53 env.exit_status = result.exit_status();
54}
55
56#[allow(
57 clippy::await_holding_refcell_ref,
58 reason = "`print_error` does not run concurrently with the input decorators or read-eval loop"
59)]
60async fn run_as_shell_process<S>(env: &mut Env<S>)
61where
62 S: Chdir
63 + Clone
64 + GetCwd
65 + GetRlimit
66 + GetUid
67 + Runtime
68 + Sysconf
69 + TcGetPgrp
70 + Times
71 + Umask
72 + Write
73 + 'static,
74{
75 let run = match self::startup::args::parse(std::env::args()) {
77 Ok(Parse::Help) => todo!("print help"),
78 Ok(Parse::Version) => return print_version(env).await,
79 Ok(Parse::Run(run)) => run,
80 Err(e) => {
81 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
82 env.system.print_error(&format!("{arg0}: {e}\n")).await;
83 env.exit_status = ExitStatus::ERROR;
84 return;
85 }
86 };
87
88 env.variables.extend_env(std::env::vars());
90
91 let work = self::startup::configure_environment(env, run).await;
92
93 let is_interactive = env.options.get(Interactive) == On;
94
95 run_rcfile(env, work.rcfile).await;
98
99 let ref_env = RefCell::new(env);
101 let lexer = match prepare_input(&ref_env, &work.source).await {
102 Ok(lexer) => lexer,
103 Err(e) => {
104 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
105 let message = format!("{arg0}: {e}\n");
106 let mut env = ref_env.borrow_mut();
111 env.system.print_error(&message).await;
112 env.exit_status = match e.errno {
113 Errno::ENOENT | Errno::ENOTDIR | Errno::EILSEQ => ExitStatus::NOT_FOUND,
114 _ => ExitStatus::NOEXEC,
115 };
116 return;
117 }
118 };
119
120 let result = if is_interactive {
122 interactive_read_eval_loop(&ref_env, &mut { lexer }).await
123 } else {
124 read_eval_loop(&ref_env, &mut { lexer }).await
125 };
126
127 let env = ref_env.into_inner();
128 env.apply_result(result);
129
130 match result {
131 Continue(())
132 | Break(Divert::Continue { .. })
133 | Break(Divert::Break { .. })
134 | Break(Divert::Return(_))
135 | Break(Divert::Interrupt(_))
136 | Break(Divert::Exit(_)) => run_exit_trap(env).await,
137 Break(Divert::Abort(_)) => (),
138 }
139}
140
141pub fn main() -> ! {
142 let system = unsafe { RealSystem::new() };
145
146 system
150 .sigaction(RealSystem::SIGPIPE, Disposition::Default)
151 .ok();
152
153 let system = Rc::new(Concurrent::new(system));
154 let runner = Rc::clone(&system);
155 let task = async {
156 let mut env = Env::with_system(system);
157 run_as_shell_process(&mut env).await;
158 exit_or_raise(&env.system, env.exit_status).await
159 };
160 runner.run_real(task)
161}