1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7
8use tokio::sync::Mutex;
9
10use crate::{
11 ExecutionControlFlow, ExecutionResult, builtins, env::ShellEnvironment, error, extensions,
12 functions, interfaces, jobs, keywords, openfiles, options::RuntimeOptions, pathcache,
13 wellknownvars,
14};
15
16pub type KeyBindingsHelper = Arc<Mutex<dyn interfaces::KeyBindings>>;
18
19pub type ShellFd = i32;
21
22mod builder;
29mod builtin_registry;
30mod callstack;
31mod completion;
32mod env;
33mod execution;
34mod expansion;
35mod fs;
36mod funcs;
37mod history;
38mod initscripts;
39mod io;
40mod job_control;
41mod parsing;
42mod prompts;
43mod readline;
44mod state;
45mod traps;
46
47pub use builder::{CreateOptions, ShellBuilder, ShellBuilderState};
48pub use initscripts::{ProfileLoadBehavior, RcLoadBehavior};
49pub use state::ShellState;
50
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct Shell<SE: extensions::ShellExtensions = extensions::DefaultShellExtensions> {
60 #[cfg_attr(feature = "serde", serde(skip, default = "default_error_formatter"))]
62 error_formatter: SE::ErrorFormatter,
63
64 traps: crate::traps::TrapHandlerConfig,
66
67 open_files: openfiles::OpenFiles,
69
70 working_dir: PathBuf,
72
73 env: ShellEnvironment,
75
76 funcs: functions::FunctionEnv,
78
79 options: RuntimeOptions,
81
82 #[cfg_attr(feature = "serde", serde(skip))]
85 jobs: jobs::JobManager,
86
87 aliases: HashMap<String, String>,
89
90 last_exit_status: u8,
92
93 last_exit_status_change_count: usize,
95
96 last_pipeline_statuses: Vec<u8>,
98
99 depth: usize,
101
102 name: Option<String>,
104
105 args: Vec<String>,
107
108 version: Option<String>,
110
111 product_display_str: Option<String>,
113
114 call_stack: crate::callstack::CallStack,
116
117 directory_stack: Vec<PathBuf>,
119
120 completion_config: crate::completion::Config,
122
123 #[cfg_attr(feature = "serde", serde(skip))]
125 builtins: HashMap<String, builtins::Registration<SE>>,
126
127 program_location_cache: pathcache::PathCache,
129
130 last_stopwatch_time: std::time::SystemTime,
132
133 last_stopwatch_offset: u32,
135
136 #[cfg_attr(feature = "serde", serde(skip))]
138 parser_impl: crate::parser::ParserImpl,
139
140 #[cfg_attr(feature = "serde", serde(skip))]
142 key_bindings: Option<KeyBindingsHelper>,
143
144 history: Option<crate::history::History>,
146}
147
148impl<SE: extensions::ShellExtensions> Clone for Shell<SE> {
149 fn clone(&self) -> Self {
150 Self {
151 error_formatter: self.error_formatter.clone(),
152 traps: self.traps.clone(),
153 open_files: self.open_files.clone(),
154 working_dir: self.working_dir.clone(),
155 env: self.env.clone(),
156 funcs: self.funcs.clone(),
157 options: self.options.clone(),
158 jobs: jobs::JobManager::new(),
159 aliases: self.aliases.clone(),
160 last_exit_status: self.last_exit_status,
161 last_exit_status_change_count: self.last_exit_status_change_count,
162 last_pipeline_statuses: self.last_pipeline_statuses.clone(),
163 name: self.name.clone(),
164 args: self.args.clone(),
165 version: self.version.clone(),
166 product_display_str: self.product_display_str.clone(),
167 call_stack: {
168 let mut cs = self.call_stack.clone();
172 cs.clear_active_trap_signals();
173 cs
174 },
175 directory_stack: self.directory_stack.clone(),
176 completion_config: self.completion_config.clone(),
177 builtins: self.builtins.clone(),
178 program_location_cache: self.program_location_cache.clone(),
179 last_stopwatch_time: self.last_stopwatch_time,
180 last_stopwatch_offset: self.last_stopwatch_offset,
181 parser_impl: self.parser_impl,
182 key_bindings: self.key_bindings.clone(),
183 history: self.history.clone(),
184 depth: self.depth + 1,
185 }
186 }
187}
188
189impl<SE: extensions::ShellExtensions> AsRef<Self> for Shell<SE> {
190 fn as_ref(&self) -> &Self {
191 self
192 }
193}
194
195impl<SE: extensions::ShellExtensions> AsMut<Self> for Shell<SE> {
196 fn as_mut(&mut self) -> &mut Self {
197 self
198 }
199}
200
201impl<SE: extensions::ShellExtensions> Shell<SE> {
202 pub(crate) fn new(options: CreateOptions<SE>) -> Result<Self, error::Error> {
209 let runtime_options = RuntimeOptions::defaults_from(&options);
211
212 let mut shell = Self {
214 error_formatter: options.error_formatter,
215 open_files: openfiles::OpenFiles::new(),
216 options: runtime_options,
217 name: options.shell_name,
218 args: options.shell_args.unwrap_or_default(),
219 version: options.shell_version,
220 product_display_str: options.shell_product_display_str,
221 working_dir: options.working_dir.map_or_else(std::env::current_dir, Ok)?,
222 builtins: options.builtins,
223 parser_impl: options.parser,
224 key_bindings: options.key_bindings,
225 ..Self::default()
226 };
227
228 shell.open_files.update_from(options.fds.into_iter());
230
231 shell.options.extended_globbing = true;
234
235 if !options.do_not_inherit_env {
237 wellknownvars::inherit_env_vars(&mut shell)?;
238 }
239
240 if !options.skip_well_known_vars {
242 wellknownvars::init_well_known_vars(&mut shell)?;
243 }
244
245 for (var_name, var_value) in options.vars {
247 shell.env.set_global(var_name, var_value)?;
248 }
249
250 if shell.options.enable_command_history {
252 shell.history = shell
253 .load_history()
254 .unwrap_or_default()
255 .or_else(|| Some(crate::history::History::default()));
256 }
257
258 Ok(shell)
259 }
260}
261
262impl<SE: extensions::ShellExtensions> Shell<SE> {
263 pub fn increment_interactive_line_offset(&mut self, delta: usize) {
270 self.call_stack.increment_current_line_offset(delta);
271 }
272
273 pub fn set_current_cmd(&mut self, cmd: &impl brush_parser::ast::Node) {
275 self.call_stack
276 .set_current_pos(cmd.location().map(|span| span.start));
277 }
278
279 pub(crate) fn update_last_arg_variable(&mut self, last_arg: Option<String>) {
290 if self
295 .env
296 .get_using_policy("_", crate::env::EnvironmentLookup::Anywhere)
297 .is_some_and(|v| v.is_readonly())
298 {
299 return;
300 }
301
302 let value = last_arg.unwrap_or_default();
307 let _ = self
308 .env
309 .set_global("_", crate::variables::ShellVariable::new(value));
310 }
311
312 pub const fn apply_errexit_if_enabled(&self, result: &mut ExecutionResult) {
319 if self.options.exit_on_nonzero_command_exit
320 && !result.is_success()
321 && result.is_normal_flow()
322 {
323 result.next_control_flow = ExecutionControlFlow::ExitShell;
324 }
325 }
326
327 pub(crate) fn get_keywords(&self) -> Vec<&str> {
329 if self.options.sh_mode {
330 keywords::SH_MODE_KEYWORDS.iter().copied().collect()
331 } else {
332 keywords::KEYWORDS.iter().copied().collect()
333 }
334 }
335
336 pub fn is_keyword(&self, s: &str) -> bool {
342 if self.options.sh_mode {
343 keywords::SH_MODE_KEYWORDS.contains(s)
344 } else {
345 keywords::KEYWORDS.contains(s)
346 }
347 }
348
349 pub(crate) const fn last_exit_status_change_count(&self) -> usize {
350 self.last_exit_status_change_count
351 }
352}
353
354#[inherent::inherent]
355impl<SE: extensions::ShellExtensions> ShellState for Shell<SE> {
356 pub fn is_subshell(&self) -> bool {
358 self.depth > 0
359 }
360
361 pub fn last_stopwatch_time(&self) -> std::time::SystemTime {
363 self.last_stopwatch_time
364 }
365
366 pub fn last_stopwatch_offset(&self) -> u32 {
368 self.last_stopwatch_offset
369 }
370
371 pub fn env(&self) -> &ShellEnvironment {
373 &self.env
374 }
375
376 pub fn env_mut(&mut self) -> &mut ShellEnvironment {
378 &mut self.env
379 }
380
381 pub fn options(&self) -> &RuntimeOptions {
383 &self.options
384 }
385
386 pub fn options_mut(&mut self) -> &mut RuntimeOptions {
388 &mut self.options
389 }
390
391 pub fn aliases(&self) -> &HashMap<String, String> {
393 &self.aliases
394 }
395
396 pub fn aliases_mut(&mut self) -> &mut HashMap<String, String> {
398 &mut self.aliases
399 }
400
401 pub fn jobs(&self) -> &jobs::JobManager {
403 &self.jobs
404 }
405
406 pub fn jobs_mut(&mut self) -> &mut jobs::JobManager {
408 &mut self.jobs
409 }
410
411 pub fn traps(&self) -> &crate::traps::TrapHandlerConfig {
413 &self.traps
414 }
415
416 pub fn traps_mut(&mut self) -> &mut crate::traps::TrapHandlerConfig {
418 &mut self.traps
419 }
420
421 pub fn directory_stack(&self) -> &[PathBuf] {
423 &self.directory_stack
424 }
425
426 pub fn directory_stack_mut(&mut self) -> &mut Vec<PathBuf> {
428 &mut self.directory_stack
429 }
430
431 pub fn last_pipeline_statuses(&self) -> &[u8] {
433 &self.last_pipeline_statuses
434 }
435
436 pub fn last_pipeline_statuses_mut(&mut self) -> &mut Vec<u8> {
438 &mut self.last_pipeline_statuses
439 }
440
441 pub fn program_location_cache(&self) -> &pathcache::PathCache {
443 &self.program_location_cache
444 }
445
446 pub fn program_location_cache_mut(&mut self) -> &mut pathcache::PathCache {
448 &mut self.program_location_cache
449 }
450
451 pub fn completion_config(&self) -> &crate::completion::Config {
453 &self.completion_config
454 }
455
456 pub fn completion_config_mut(&mut self) -> &mut crate::completion::Config {
458 &mut self.completion_config
459 }
460
461 pub fn open_files(&self) -> &openfiles::OpenFiles {
463 &self.open_files
464 }
465
466 pub fn open_files_mut(&mut self) -> &mut openfiles::OpenFiles {
468 &mut self.open_files
469 }
470
471 pub fn current_shell_name(&self) -> Option<Cow<'_, str>> {
474 for frame in self.call_stack.iter() {
475 if frame.frame_type.is_run_script() {
477 return Some(frame.frame_type.name());
478 }
479 }
480
481 self.name.as_deref().map(|name| name.into())
482 }
483
484 pub fn depth(&self) -> usize {
486 self.depth
487 }
488
489 pub fn call_stack(&self) -> &crate::callstack::CallStack {
491 &self.call_stack
492 }
493
494 pub fn history(&self) -> Option<&crate::history::History> {
496 self.history.as_ref()
497 }
498
499 pub fn history_mut(&mut self) -> Option<&mut crate::history::History> {
501 self.history.as_mut()
502 }
503
504 pub fn version(&self) -> Option<&str> {
506 self.version.as_deref()
507 }
508
509 pub fn last_exit_status(&self) -> u8 {
511 self.last_exit_status
512 }
513
514 pub fn set_last_exit_status(&mut self, status: u8) {
516 self.last_exit_status = status;
517 self.last_exit_status_change_count += 1;
518 }
519
520 pub fn key_bindings(&self) -> Option<&KeyBindingsHelper> {
522 self.key_bindings.as_ref()
523 }
524
525 pub fn set_key_bindings(&mut self, key_bindings: Option<KeyBindingsHelper>) {
527 self.key_bindings = key_bindings;
528 }
529
530 pub fn working_dir(&self) -> &Path {
532 &self.working_dir
533 }
534
535 pub(crate) fn working_dir_mut(&mut self) -> &mut PathBuf {
538 &mut self.working_dir
539 }
540
541 pub fn product_display_str(&self) -> Option<&str> {
543 self.product_display_str.as_deref()
544 }
545}
546
547#[cfg(feature = "serde")]
548fn default_error_formatter<EF: extensions::ErrorFormatter>() -> EF {
549 EF::default()
550}