1use std::{io::Read, path::Path};
4
5use crate::{
6 ExecutionControlFlow, ExecutionParameters, ExecutionResult, ProcessGroupPolicy, SourceInfo,
7 arithmetic::Evaluatable as _, callstack, error, interp::Execute as _, openfiles,
8 trace_categories,
9};
10
11impl<SE: crate::extensions::ShellExtensions> crate::Shell<SE> {
12 pub fn default_exec_params(&self) -> ExecutionParameters {
14 let mut params = ExecutionParameters::default();
15
16 params.process_group_policy = if self.options.enable_job_control {
17 ProcessGroupPolicy::NewProcessGroup
18 } else {
19 ProcessGroupPolicy::SameProcessGroup
20 };
21
22 params
23 }
24
25 pub(super) async fn source_if_exists(
26 &mut self,
27 path: impl AsRef<Path>,
28 params: &ExecutionParameters,
29 ) -> Result<bool, error::Error> {
30 let path = path.as_ref();
31 if path.exists() {
32 self.source_script(path, std::iter::empty::<String>(), params)
33 .await?;
34 Ok(true)
35 } else {
36 tracing::debug!("skipping non-existent file: {}", path.display());
37 Ok(false)
38 }
39 }
40
41 pub async fn source_script<S: Into<String>, P: AsRef<Path>, I: Iterator<Item = S>>(
49 &mut self,
50 path: P,
51 args: I,
52 params: &ExecutionParameters,
53 ) -> Result<ExecutionResult, error::Error> {
54 self.parse_and_execute_script_file(
55 path.as_ref(),
56 args,
57 params,
58 callstack::ScriptCallType::Source,
59 )
60 .await
61 }
62
63 async fn parse_and_execute_script_file<
72 S: Into<String>,
73 P: AsRef<Path>,
74 I: Iterator<Item = S>,
75 >(
76 &mut self,
77 path: P,
78 args: I,
79 params: &ExecutionParameters,
80 call_type: callstack::ScriptCallType,
81 ) -> Result<ExecutionResult, error::Error> {
82 let path = path.as_ref();
83 tracing::debug!("sourcing: {}", path.display());
84
85 let mut options = std::fs::File::options();
86 options.read(true);
87
88 let opened_file: openfiles::OpenFile = self
89 .open_file(&options, path, params)
90 .map_err(|e| error::ErrorKind::FailedSourcingFile(path.to_owned(), e))?;
91
92 if opened_file.is_dir() {
93 return Err(error::ErrorKind::FailedSourcingFile(
94 path.to_owned(),
95 std::io::Error::from(std::io::ErrorKind::IsADirectory),
96 )
97 .into());
98 }
99
100 let source_info = crate::SourceInfo::from(path.to_owned());
101
102 let mut result = self
103 .source_file(opened_file, &source_info, args, params, call_type)
104 .await?;
105
106 if matches!(
110 result.next_control_flow,
111 ExecutionControlFlow::ReturnFromFunctionOrScript
112 ) {
113 result.next_control_flow = ExecutionControlFlow::Normal;
114 }
115
116 Ok(result)
117 }
118
119 async fn source_file<F: Read, S: Into<String>, I: Iterator<Item = S>>(
129 &mut self,
130 file: F,
131 source_info: &crate::SourceInfo,
132 args: I,
133 params: &ExecutionParameters,
134 call_type: callstack::ScriptCallType,
135 ) -> Result<ExecutionResult, error::Error> {
136 let mut reader = std::io::BufReader::new(file);
137 let mut parser = brush_parser::Parser::new(&mut reader, &self.parser_options());
138
139 tracing::debug!(target: trace_categories::PARSE, "Parsing sourced file: {}", source_info.source);
140 let parse_result = parser.parse_program();
141
142 let script_positional_args = args.map(Into::into);
143
144 self.call_stack
145 .push_script(call_type, source_info, script_positional_args);
146
147 let result = self
148 .run_parsed_result(parse_result, source_info, params)
149 .await;
150
151 self.call_stack.pop();
152
153 result
154 }
155
156 pub async fn run_string<S: Into<String>>(
164 &mut self,
165 command: S,
166 source_info: &crate::SourceInfo,
167 params: &ExecutionParameters,
168 ) -> Result<ExecutionResult, error::Error> {
169 let parse_result = self.parse_string(command);
170 self.run_parsed_result(parse_result, source_info, params)
171 .await
172 }
173
174 pub async fn run_dash_c_command<S: Into<String>>(
185 &mut self,
186 command: S,
187 ) -> Result<ExecutionResult, error::Error> {
188 self.start_command_string_mode();
189
190 let params = self.default_exec_params();
192 let source_info = SourceInfo::from("-c");
193 let result = self.run_string(command, &source_info, ¶ms).await?;
194
195 self.end_command_string_mode()?;
196
197 let _ = self.on_exit().await;
199
200 Ok(result)
201 }
202
203 pub async fn run_script<S: Into<String>, P: AsRef<Path>, I: Iterator<Item = S>>(
214 &mut self,
215 script_path: P,
216 args: I,
217 ) -> Result<ExecutionResult, error::Error> {
218 let params = self.default_exec_params();
219 let result = self
220 .parse_and_execute_script_file(
221 script_path.as_ref(),
222 args,
223 ¶ms,
224 callstack::ScriptCallType::Run,
225 )
226 .await?;
227
228 let _ = self.on_exit().await;
230
231 Ok(result)
232 }
233
234 pub(crate) async fn run_parsed_result(
235 &mut self,
236 parse_result: Result<brush_parser::ast::Program, brush_parser::ParseError>,
237 source_info: &crate::SourceInfo,
238 params: &ExecutionParameters,
239 ) -> Result<ExecutionResult, error::Error> {
240 let result = match parse_result {
242 Ok(prog) => self.run_program(prog, params).await,
243 Err(parse_err) => Err(error::Error::from(error::ErrorKind::ParseError(
244 parse_err,
245 source_info.clone(),
246 ))
247 .into_fatal()),
248 };
249
250 match result {
252 Ok(result) => Ok(result),
253 Err(err) => {
254 let _ = self.display_error(&mut params.stderr(self), &err);
255
256 let result = err.into_result(self);
257 self.set_last_exit_status(result.exit_code.into());
258
259 Ok(result)
260 }
261 }
262 }
263
264 pub async fn run_program(
271 &mut self,
272 program: brush_parser::ast::Program,
273 params: &ExecutionParameters,
274 ) -> Result<ExecutionResult, error::Error> {
275 program.execute(self, params).await
276 }
277
278 pub fn eval_arithmetic(
280 &mut self,
281 expr: &brush_parser::ast::ArithmeticExpr,
282 ) -> Result<i64, error::Error> {
283 Ok(expr.eval(self)?)
284 }
285}