1use itertools::Itertools;
4
5use crate::{CreateOptions, namedoptions};
6
7#[derive(Clone, Default)]
9#[expect(clippy::module_name_repetitions)]
10pub struct RuntimeOptions {
11 pub export_variables_on_modification: bool,
15 pub notify_job_termination_immediately: bool,
17 pub exit_on_nonzero_command_exit: bool,
19 pub disable_filename_globbing: bool,
21 pub remember_command_locations: bool,
23 pub place_all_assignment_args_in_command_env: bool,
25 pub enable_job_control: bool,
27 pub do_not_execute_commands: bool,
29 pub real_effective_uid_mismatch: bool,
31 pub exit_after_one_command: bool,
33 pub treat_unset_variables_as_error: bool,
35 pub print_shell_input_lines: bool,
37 pub print_commands_and_arguments: bool,
39 pub perform_brace_expansion: bool,
41 pub disallow_overwriting_regular_files_via_output_redirection: bool,
43 pub shell_functions_inherit_err_trap: bool,
45 pub enable_bang_style_history_substitution: bool,
47 pub do_not_resolve_symlinks_when_changing_dir: bool,
49 pub shell_functions_inherit_debug_and_return_traps: bool,
51
52 pub emacs_mode: bool,
56 pub enable_command_history: bool,
58 pub ignore_eof: bool,
60 pub return_first_failure_from_pipeline: bool,
62 pub posix_mode: bool,
64 pub vi_mode: bool,
66
67 pub assoc_expand_once: bool,
71 pub auto_cd: bool,
73 pub cdable_vars: bool,
75 pub cd_autocorrect_spelling: bool,
77 pub check_hashtable_before_command_exec: bool,
79 pub check_jobs_before_exit: bool,
81 pub check_window_size_after_external_commands: bool,
83 pub save_multiline_cmds_in_history: bool,
85 pub compat31: bool,
87 pub compat32: bool,
89 pub compat40: bool,
91 pub compat41: bool,
93 pub compat42: bool,
95 pub compat43: bool,
97 pub compat44: bool,
99 pub quote_all_metachars_in_completion: bool,
101 pub expand_dir_names_on_completion: bool,
103 pub autocorrect_dir_spelling_on_completion: bool,
105 pub glob_matches_dotfiles: bool,
107 pub exit_on_exec_fail: bool,
109 pub expand_aliases: bool,
111 pub enable_debugger: bool,
113 pub extended_globbing: bool,
115 pub extquote: bool,
117 pub fail_expansion_on_globs_without_match: bool,
119 pub force_fignore: bool,
121 pub glob_ranges_use_c_locale: bool,
123 pub enable_star_star_glob: bool,
125 pub errors_in_gnu_format: bool,
127 pub append_to_history_file: bool,
129 pub allow_reedit_failed_history_subst: bool,
131 pub allow_modifying_history_substitution: bool,
133 pub enable_hostname_completion: bool,
135 pub send_sighup_to_all_jobs_on_exit: bool,
137 pub command_subst_inherits_errexit: bool,
139 pub interactive_comments: bool,
141 pub run_last_pipeline_cmd_in_current_shell: bool,
143 pub embed_newlines_in_multiline_cmds_in_history: bool,
145 pub local_vars_inherit_value_and_attrs: bool,
147 pub localvar_unset: bool,
149 pub login_shell: bool,
151 pub mail_warn: bool,
153 pub no_empty_cmd_completion: bool,
155 pub case_insensitive_pathname_expansion: bool,
157 pub case_insensitive_conditionals: bool,
159 pub expand_non_matching_patterns_to_null: bool,
161 pub programmable_completion: bool,
163 pub programmable_completion_alias: bool,
165 pub expand_prompt_strings: bool,
167 pub restricted_shell: bool,
169 pub shift_verbose: bool,
171 pub source_builtin_searches_path: bool,
173 pub echo_builtin_expands_escape_sequences: bool,
175
176 pub interactive: bool,
180 pub read_commands_from_stdin: bool,
182 pub sh_mode: bool,
184 pub max_function_call_depth: Option<usize>,
186}
187
188impl RuntimeOptions {
189 pub fn defaults_from(create_options: &CreateOptions) -> Self {
195 let mut options = Self {
197 interactive: create_options.interactive,
198 disallow_overwriting_regular_files_via_output_redirection: create_options
199 .disallow_overwriting_regular_files_via_output_redirection,
200 do_not_execute_commands: create_options.do_not_execute_commands,
201 enable_command_history: create_options.interactive,
202 enable_job_control: create_options.interactive,
203 exit_after_one_command: create_options.exit_after_one_command,
204 read_commands_from_stdin: create_options.read_commands_from_stdin,
205 sh_mode: create_options.sh_mode,
206 posix_mode: create_options.posix,
207 print_commands_and_arguments: create_options.print_commands_and_arguments,
208 print_shell_input_lines: create_options.verbose,
209 remember_command_locations: true,
210 check_window_size_after_external_commands: true,
211 save_multiline_cmds_in_history: true,
212 extquote: true,
213 force_fignore: true,
214 enable_hostname_completion: true,
215 interactive_comments: true,
216 expand_prompt_strings: true,
217 source_builtin_searches_path: true,
218 perform_brace_expansion: true,
219 quote_all_metachars_in_completion: true,
220 programmable_completion: true,
221 glob_ranges_use_c_locale: true,
222 max_function_call_depth: create_options.max_function_call_depth,
223 ..Self::default()
224 };
225
226 if create_options.interactive {
228 options.enable_bang_style_history_substitution = true;
229 options.emacs_mode = !create_options.no_editing;
230 options.expand_aliases = true;
231 }
232
233 for enabled_option in &create_options.enabled_options {
235 if let Some(option) = namedoptions::options(namedoptions::ShellOptionKind::SetO)
236 .get(enabled_option.as_str())
237 {
238 option.set(&mut options, true);
239 }
240 }
241 for disabled_option in &create_options.disabled_options {
242 if let Some(option) = namedoptions::options(namedoptions::ShellOptionKind::SetO)
243 .get(disabled_option.as_str())
244 {
245 option.set(&mut options, false);
246 }
247 }
248
249 for enabled_option in &create_options.enabled_shopt_options {
251 if let Some(shopt_option) = namedoptions::options(namedoptions::ShellOptionKind::Shopt)
252 .get(enabled_option.as_str())
253 {
254 shopt_option.set(&mut options, true);
255 }
256 }
257 for disabled_option in &create_options.disabled_shopt_options {
258 if let Some(shopt_option) = namedoptions::options(namedoptions::ShellOptionKind::Shopt)
259 .get(disabled_option.as_str())
260 {
261 shopt_option.set(&mut options, false);
262 }
263 }
264
265 options
266 }
267
268 pub fn option_flags(&self) -> String {
270 let mut cs = vec![];
271
272 for o in namedoptions::options(namedoptions::ShellOptionKind::Set).iter() {
273 if o.definition.get(self) {
274 cs.push(o.name.chars().next().unwrap());
275 }
276 }
277
278 cs.sort_by(|a, b| {
280 if a == b {
281 std::cmp::Ordering::Equal
282 } else if *a == 's' {
283 std::cmp::Ordering::Greater
284 } else if *b == 's' {
285 std::cmp::Ordering::Less
286 } else if a.is_ascii_lowercase() && b.is_ascii_uppercase() {
287 std::cmp::Ordering::Less
288 } else if a.is_ascii_uppercase() && b.is_ascii_lowercase() {
289 std::cmp::Ordering::Greater
290 } else {
291 a.cmp(b)
292 }
293 });
294
295 cs.into_iter().collect()
296 }
297
298 pub fn seto_optstr(&self) -> String {
300 let mut cs = vec![];
301
302 for option in namedoptions::options(namedoptions::ShellOptionKind::SetO).iter() {
303 if option.definition.get(self) {
304 cs.push(option.name);
305 }
306 }
307
308 cs.sort_unstable();
309 cs.into_iter().join(":")
310 }
311
312 pub fn shopt_optstr(&self) -> String {
314 let mut cs = vec![];
315
316 for option in namedoptions::options(namedoptions::ShellOptionKind::Shopt).iter() {
317 if option.definition.get(self) {
318 cs.push(option.name);
319 }
320 }
321
322 cs.sort_unstable();
323 cs.into_iter().join(":")
324 }
325}