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::option::{Interactive, On};
36use yash_env::semantics::{Divert, ExitStatus, exit_or_raise};
37use yash_env::system::resource::GetRlimit;
38use yash_env::system::{
39 Chdir, Disposition, Errno, Fcntl, GetCwd, GetUid, Isatty, Sigaction, Signals, Sysconf,
40 TcGetPgrp, Times, Umask, Write,
41};
42use yash_executor::Executor;
43use yash_semantics::trap::run_exit_trap;
44use yash_semantics::{Runtime, interactive_read_eval_loop, read_eval_loop};
45
46async fn print_version<S>(env: &mut Env<S>)
47where
48 S: Fcntl + Isatty + Write,
49{
50 let version = env!("CARGO_PKG_VERSION");
51 let result = yash_builtin::common::output(env, &format!("yash {version}\n")).await;
52 env.exit_status = result.exit_status();
53}
54
55#[allow(clippy::await_holding_refcell_ref)]
57async fn run_as_shell_process<S>(env: &mut Env<S>)
58where
59 S: Chdir
60 + GetCwd
61 + GetRlimit
62 + GetUid
63 + Runtime
64 + Sysconf
65 + TcGetPgrp
66 + Times
67 + Umask
68 + 'static,
69{
70 let run = match self::startup::args::parse(std::env::args()) {
72 Ok(Parse::Help) => todo!("print help"),
73 Ok(Parse::Version) => return print_version(env).await,
74 Ok(Parse::Run(run)) => run,
75 Err(e) => {
76 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
77 env.system.print_error(&format!("{arg0}: {e}\n")).await;
78 env.exit_status = ExitStatus::ERROR;
79 return;
80 }
81 };
82
83 env.variables.extend_env(std::env::vars());
85
86 let work = self::startup::configure_environment(env, run).await;
87
88 let is_interactive = env.options.get(Interactive) == On;
89
90 run_rcfile(env, work.rcfile).await;
93
94 let ref_env = RefCell::new(env);
96 let lexer = match prepare_input(&ref_env, &work.source) {
97 Ok(lexer) => lexer,
98 Err(e) => {
99 let arg0 = std::env::args().next().unwrap_or_else(|| "yash".to_owned());
100 let message = format!("{arg0}: {e}\n");
101 let mut env = ref_env.borrow_mut();
106 env.system.print_error(&message).await;
107 env.exit_status = match e.errno {
108 Errno::ENOENT | Errno::ENOTDIR | Errno::EILSEQ => ExitStatus::NOT_FOUND,
109 _ => ExitStatus::NOEXEC,
110 };
111 return;
112 }
113 };
114
115 let result = if is_interactive {
117 interactive_read_eval_loop(&ref_env, &mut { lexer }).await
118 } else {
119 read_eval_loop(&ref_env, &mut { lexer }).await
120 };
121
122 let env = ref_env.into_inner();
123 env.apply_result(result);
124
125 match result {
126 Continue(())
127 | Break(Divert::Continue { .. })
128 | Break(Divert::Break { .. })
129 | Break(Divert::Return(_))
130 | Break(Divert::Interrupt(_))
131 | Break(Divert::Exit(_)) => run_exit_trap(env).await,
132 Break(Divert::Abort(_)) => (),
133 }
134}
135
136pub fn main() -> ! {
137 let system = unsafe { RealSystem::new() };
140 let mut env = Env::with_system(system);
141
142 env.system
146 .sigaction(RealSystem::SIGPIPE, Disposition::Default)
147 .ok();
148
149 let system = env.system.clone();
150 let executor = Executor::new();
151 let task = Box::pin(async {
152 run_as_shell_process(&mut env).await;
153 exit_or_raise(&env.system, env.exit_status).await
154 });
155 unsafe { executor.spawn_pinned(task) }
158 loop {
159 executor.run_until_stalled();
160 system.select(false).ok();
161 }
162}