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