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