1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8use tokio::sync::Mutex;
9
10pub type ExternalCommandHook =
15 Arc<dyn Fn(&str, &[String]) -> Option<(String, Vec<String>)> + Send + Sync>;
16
17use crate::{
18 ExecutionControlFlow, ExecutionResult, builtins, env::ShellEnvironment, error, extensions,
19 functions, interfaces, jobs, keywords, openfiles, options::RuntimeOptions, pathcache,
20 wellknownvars,
21};
22
23pub type KeyBindingsHelper = Arc<Mutex<dyn interfaces::KeyBindings>>;
25
26pub type ShellFd = i32;
28
29mod builder;
36mod builtin_registry;
37mod callstack;
38mod completion;
39mod env;
40mod execution;
41mod expansion;
42mod fs;
43mod funcs;
44mod history;
45mod initscripts;
46mod io;
47mod job_control;
48mod parsing;
49mod prompts;
50mod readline;
51mod state;
52mod traps;
53
54pub use builder::{CreateOptions, ShellBuilder, ShellBuilderState};
55pub use initscripts::{ProfileLoadBehavior, RcLoadBehavior};
56pub use state::ShellState;
57
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67pub struct Shell<SE: extensions::ShellExtensions = extensions::DefaultShellExtensions> {
68 #[cfg_attr(feature = "serde", serde(skip, default = "default_error_formatter"))]
70 error_formatter: SE::ErrorFormatter,
71
72 traps: crate::traps::TrapHandlerConfig,
74
75 open_files: openfiles::OpenFiles,
77
78 working_dir: PathBuf,
80
81 env: ShellEnvironment,
83
84 funcs: functions::FunctionEnv,
86
87 options: RuntimeOptions,
89
90 #[cfg_attr(feature = "serde", serde(skip))]
93 jobs: jobs::JobManager,
94
95 aliases: HashMap<String, String>,
97
98 last_exit_status: u8,
100
101 last_exit_status_change_count: usize,
103
104 last_pipeline_statuses: Vec<u8>,
106
107 depth: usize,
109
110 name: Option<String>,
112
113 args: Vec<String>,
115
116 version: Option<String>,
118
119 product_display_str: Option<String>,
121
122 call_stack: crate::callstack::CallStack,
124
125 directory_stack: Vec<PathBuf>,
127
128 completion_config: crate::completion::Config,
130
131 #[cfg_attr(feature = "serde", serde(skip))]
133 builtins: HashMap<String, builtins::Registration<SE>>,
134
135 program_location_cache: pathcache::PathCache,
137
138 last_stopwatch_time: std::time::SystemTime,
140
141 last_stopwatch_offset: u32,
143
144 #[cfg_attr(feature = "serde", serde(skip))]
146 parser_impl: crate::parser::ParserImpl,
147
148 #[cfg_attr(feature = "serde", serde(skip))]
150 key_bindings: Option<KeyBindingsHelper>,
151
152 history: Option<crate::history::History>,
154
155 #[cfg_attr(feature = "serde", serde(skip))]
157 external_command_hook: Option<ExternalCommandHook>,
158}
159
160impl<SE: extensions::ShellExtensions> Clone for Shell<SE> {
161 fn clone(&self) -> Self {
162 Self {
163 error_formatter: self.error_formatter.clone(),
164 traps: self.traps.clone(),
165 open_files: self.open_files.clone(),
166 working_dir: self.working_dir.clone(),
167 env: self.env.clone(),
168 funcs: self.funcs.clone(),
169 options: self.options.clone(),
170 jobs: jobs::JobManager::new(),
171 aliases: self.aliases.clone(),
172 last_exit_status: self.last_exit_status,
173 last_exit_status_change_count: self.last_exit_status_change_count,
174 last_pipeline_statuses: self.last_pipeline_statuses.clone(),
175 name: self.name.clone(),
176 args: self.args.clone(),
177 version: self.version.clone(),
178 product_display_str: self.product_display_str.clone(),
179 call_stack: self.call_stack.clone(),
180 directory_stack: self.directory_stack.clone(),
181 completion_config: self.completion_config.clone(),
182 builtins: self.builtins.clone(),
183 program_location_cache: self.program_location_cache.clone(),
184 last_stopwatch_time: self.last_stopwatch_time,
185 last_stopwatch_offset: self.last_stopwatch_offset,
186 parser_impl: self.parser_impl,
187 key_bindings: self.key_bindings.clone(),
188 history: self.history.clone(),
189 external_command_hook: self.external_command_hook.clone(),
190 depth: self.depth + 1,
191 }
192 }
193}
194
195impl<SE: extensions::ShellExtensions> AsRef<Self> for Shell<SE> {
196 fn as_ref(&self) -> &Self {
197 self
198 }
199}
200
201impl<SE: extensions::ShellExtensions> AsMut<Self> for Shell<SE> {
202 fn as_mut(&mut self) -> &mut Self {
203 self
204 }
205}
206
207impl<SE: extensions::ShellExtensions> Shell<SE> {
208 pub(crate) fn new(options: CreateOptions<SE>) -> Result<Self, error::Error> {
215 let runtime_options = RuntimeOptions::defaults_from(&options);
217
218 let mut shell = Self {
220 error_formatter: options.error_formatter,
221 open_files: openfiles::OpenFiles::new(),
222 options: runtime_options,
223 name: options.shell_name,
224 args: options.shell_args.unwrap_or_default(),
225 version: options.shell_version,
226 product_display_str: options.shell_product_display_str,
227 working_dir: options.working_dir.map_or_else(std::env::current_dir, Ok)?,
228 builtins: options.builtins,
229 parser_impl: options.parser,
230 key_bindings: options.key_bindings,
231 external_command_hook: options.external_command_hook,
232 ..Self::default()
233 };
234
235 shell.open_files.update_from(options.fds.into_iter());
237
238 shell.options.extended_globbing = true;
241
242 if !options.do_not_inherit_env {
244 wellknownvars::inherit_env_vars(&mut shell)?;
245 }
246
247 if !options.skip_well_known_vars {
249 wellknownvars::init_well_known_vars(&mut shell)?;
250 }
251
252 for (var_name, var_value) in options.vars {
254 shell.env.set_global(var_name, var_value)?;
255 }
256
257 if shell.options.enable_command_history {
259 shell.history = shell
260 .load_history()
261 .unwrap_or_default()
262 .or_else(|| Some(crate::history::History::default()));
263 }
264
265 Ok(shell)
266 }
267}
268
269impl<SE: extensions::ShellExtensions> Shell<SE> {
270 pub fn increment_interactive_line_offset(&mut self, delta: usize) {
277 self.call_stack.increment_current_line_offset(delta);
278 }
279
280 pub fn set_current_cmd(&mut self, cmd: &impl brush_parser::ast::Node) {
282 self.call_stack
283 .set_current_pos(cmd.location().map(|span| span.start));
284 }
285
286 pub const fn apply_errexit_if_enabled(&self, result: &mut ExecutionResult) {
293 if self.options.exit_on_nonzero_command_exit
294 && !result.is_success()
295 && result.is_normal_flow()
296 {
297 result.next_control_flow = ExecutionControlFlow::ExitShell;
298 }
299 }
300
301 pub(crate) fn get_keywords(&self) -> Vec<&str> {
303 if self.options.sh_mode {
304 keywords::SH_MODE_KEYWORDS.iter().copied().collect()
305 } else {
306 keywords::KEYWORDS.iter().copied().collect()
307 }
308 }
309
310 pub fn is_keyword(&self, s: &str) -> bool {
316 if self.options.sh_mode {
317 keywords::SH_MODE_KEYWORDS.contains(s)
318 } else {
319 keywords::KEYWORDS.contains(s)
320 }
321 }
322
323 pub(crate) const fn last_exit_status_change_count(&self) -> usize {
324 self.last_exit_status_change_count
325 }
326
327 pub fn external_command_hook(&self) -> Option<&ExternalCommandHook> {
329 self.external_command_hook.as_ref()
330 }
331}
332
333#[inherent::inherent]
334impl<SE: extensions::ShellExtensions> ShellState for Shell<SE> {
335 pub fn is_subshell(&self) -> bool {
337 self.depth > 0
338 }
339
340 pub fn last_stopwatch_time(&self) -> std::time::SystemTime {
342 self.last_stopwatch_time
343 }
344
345 pub fn last_stopwatch_offset(&self) -> u32 {
347 self.last_stopwatch_offset
348 }
349
350 pub fn env(&self) -> &ShellEnvironment {
352 &self.env
353 }
354
355 pub fn env_mut(&mut self) -> &mut ShellEnvironment {
357 &mut self.env
358 }
359
360 pub fn options(&self) -> &RuntimeOptions {
362 &self.options
363 }
364
365 pub fn options_mut(&mut self) -> &mut RuntimeOptions {
367 &mut self.options
368 }
369
370 pub fn aliases(&self) -> &HashMap<String, String> {
372 &self.aliases
373 }
374
375 pub fn aliases_mut(&mut self) -> &mut HashMap<String, String> {
377 &mut self.aliases
378 }
379
380 pub fn jobs(&self) -> &jobs::JobManager {
382 &self.jobs
383 }
384
385 pub fn jobs_mut(&mut self) -> &mut jobs::JobManager {
387 &mut self.jobs
388 }
389
390 pub fn traps(&self) -> &crate::traps::TrapHandlerConfig {
392 &self.traps
393 }
394
395 pub fn traps_mut(&mut self) -> &mut crate::traps::TrapHandlerConfig {
397 &mut self.traps
398 }
399
400 pub fn directory_stack(&self) -> &[PathBuf] {
402 &self.directory_stack
403 }
404
405 pub fn directory_stack_mut(&mut self) -> &mut Vec<PathBuf> {
407 &mut self.directory_stack
408 }
409
410 pub fn last_pipeline_statuses(&self) -> &[u8] {
412 &self.last_pipeline_statuses
413 }
414
415 pub fn last_pipeline_statuses_mut(&mut self) -> &mut Vec<u8> {
417 &mut self.last_pipeline_statuses
418 }
419
420 pub fn program_location_cache(&self) -> &pathcache::PathCache {
422 &self.program_location_cache
423 }
424
425 pub fn program_location_cache_mut(&mut self) -> &mut pathcache::PathCache {
427 &mut self.program_location_cache
428 }
429
430 pub fn completion_config(&self) -> &crate::completion::Config {
432 &self.completion_config
433 }
434
435 pub fn completion_config_mut(&mut self) -> &mut crate::completion::Config {
437 &mut self.completion_config
438 }
439
440 pub fn open_files(&self) -> &openfiles::OpenFiles {
442 &self.open_files
443 }
444
445 pub fn open_files_mut(&mut self) -> &mut openfiles::OpenFiles {
447 &mut self.open_files
448 }
449
450 pub fn current_shell_name(&self) -> Option<Cow<'_, str>> {
453 for frame in self.call_stack.iter() {
454 if frame.frame_type.is_run_script() {
456 return Some(frame.frame_type.name());
457 }
458 }
459
460 self.name.as_deref().map(|name| name.into())
461 }
462
463 pub fn depth(&self) -> usize {
465 self.depth
466 }
467
468 pub fn call_stack(&self) -> &crate::callstack::CallStack {
470 &self.call_stack
471 }
472
473 pub fn history(&self) -> Option<&crate::history::History> {
475 self.history.as_ref()
476 }
477
478 pub fn history_mut(&mut self) -> Option<&mut crate::history::History> {
480 self.history.as_mut()
481 }
482
483 pub fn version(&self) -> Option<&str> {
485 self.version.as_deref()
486 }
487
488 pub fn last_exit_status(&self) -> u8 {
490 self.last_exit_status
491 }
492
493 pub fn set_last_exit_status(&mut self, status: u8) {
495 self.last_exit_status = status;
496 self.last_exit_status_change_count += 1;
497 }
498
499 pub fn key_bindings(&self) -> Option<&KeyBindingsHelper> {
501 self.key_bindings.as_ref()
502 }
503
504 pub fn set_key_bindings(&mut self, key_bindings: Option<KeyBindingsHelper>) {
506 self.key_bindings = key_bindings;
507 }
508
509 pub fn working_dir(&self) -> &Path {
511 &self.working_dir
512 }
513
514 pub(crate) fn working_dir_mut(&mut self) -> &mut PathBuf {
517 &mut self.working_dir
518 }
519
520 pub fn product_display_str(&self) -> Option<&str> {
522 self.product_display_str.as_deref()
523 }
524}
525
526#[cfg(feature = "serde")]
527fn default_error_formatter<EF: extensions::ErrorFormatter>() -> EF {
528 EF::default()
529}