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