brush_core/
namedoptions.rs

1//! Defines shell options.
2
3use std::collections::HashMap;
4use std::sync::LazyLock;
5
6use crate::options::RuntimeOptions;
7
8type OptionGetter = fn(shell: &RuntimeOptions) -> bool;
9type OptionSetter = fn(shell: &mut RuntimeOptions, value: bool) -> ();
10
11/// Defines an option.
12pub struct ShellOptionDef {
13    /// Getter function that retrieves the current value of the option.
14    getter: OptionGetter,
15    /// Setter function that may be used to set the current value of the option.
16    setter: OptionSetter,
17}
18
19impl ShellOptionDef {
20    /// Constructs a new option definition.
21    ///
22    /// # Arguments
23    ///
24    /// * `getter` - A function that retrieves the current value of the option.
25    /// * `setter` - A function that sets the current value of the option.
26    fn new(getter: OptionGetter, setter: OptionSetter) -> Self {
27        Self { getter, setter }
28    }
29
30    /// Retrieves the current value of this option from the given runtime options.
31    ///
32    /// # Arguments
33    ///
34    /// * `options` - The runtime options to retrieve the value from.
35    pub fn get(&self, options: &RuntimeOptions) -> bool {
36        (self.getter)(options)
37    }
38
39    /// Sets the value of this option in the given runtime options.
40    ///
41    /// # Arguments
42    ///
43    /// * `options` - The runtime options to modify.
44    /// * `value` - The new value to set for the option.
45    pub fn set(&self, options: &mut RuntimeOptions, value: bool) {
46        (self.setter)(options, value);
47    }
48}
49
50/// Describes a shell option.
51pub struct ShellOption {
52    /// The name of the option.
53    pub name: &'static str,
54    /// The definition of the option.
55    pub definition: &'static ShellOptionDef,
56}
57
58/// Describes a set of shell options.
59pub struct ShellOptionSet {
60    inner: &'static HashMap<&'static str, ShellOptionDef>,
61}
62
63/// Kind of shell option.
64#[derive(Clone, Copy)]
65pub enum ShellOptionKind {
66    /// `set` option.
67    Set,
68    /// `set -o` option.
69    SetO,
70    /// `shopt` option.
71    Shopt,
72}
73
74/// Returns the options for the given shell option kind.
75///
76/// # Arguments
77///
78/// * `kind` - The kind of shell options to retrieve.
79pub fn options(kind: ShellOptionKind) -> ShellOptionSet {
80    match kind {
81        ShellOptionKind::Set => ShellOptionSet {
82            inner: &SET_OPTIONS,
83        },
84        ShellOptionKind::SetO => ShellOptionSet {
85            inner: &SET_O_OPTIONS,
86        },
87        ShellOptionKind::Shopt => ShellOptionSet {
88            inner: &SHOPT_OPTIONS,
89        },
90    }
91}
92
93impl ShellOptionSet {
94    /// Returns an iterator over the options defined in this set.
95    pub fn iter(&self) -> impl Iterator<Item = ShellOption> {
96        self.inner
97            .iter()
98            .map(|(&name, definition)| ShellOption { name, definition })
99    }
100
101    /// Returns the option with the given name, if it exists.
102    ///
103    /// # Arguments
104    ///
105    /// * `name` - The name of the option to retrieve.
106    pub fn get(&self, name: &str) -> Option<&'static ShellOptionDef> {
107        self.inner.get(name)
108    }
109}
110
111static SET_OPTIONS: LazyLock<HashMap<&'static str, ShellOptionDef>> = LazyLock::new(|| {
112    HashMap::from([
113        (
114            "a",
115            ShellOptionDef::new(
116                |options| options.export_variables_on_modification,
117                |options, value| options.export_variables_on_modification = value,
118            ),
119        ),
120        (
121            "b",
122            ShellOptionDef::new(
123                |options| options.notify_job_termination_immediately,
124                |options, value| options.notify_job_termination_immediately = value,
125            ),
126        ),
127        (
128            "e",
129            ShellOptionDef::new(
130                |options| options.exit_on_nonzero_command_exit,
131                |options, value| options.exit_on_nonzero_command_exit = value,
132            ),
133        ),
134        (
135            "f",
136            ShellOptionDef::new(
137                |options| options.disable_filename_globbing,
138                |options, value| options.disable_filename_globbing = value,
139            ),
140        ),
141        (
142            "h",
143            ShellOptionDef::new(
144                |options| options.remember_command_locations,
145                |options, value| options.remember_command_locations = value,
146            ),
147        ),
148        (
149            "i",
150            ShellOptionDef::new(
151                |options| options.interactive,
152                |options, value| options.interactive = value,
153            ),
154        ),
155        (
156            "k",
157            ShellOptionDef::new(
158                |options| options.place_all_assignment_args_in_command_env,
159                |options, value| options.place_all_assignment_args_in_command_env = value,
160            ),
161        ),
162        (
163            "m",
164            ShellOptionDef::new(
165                |options| options.enable_job_control,
166                |options, value| options.enable_job_control = value,
167            ),
168        ),
169        (
170            "n",
171            ShellOptionDef::new(
172                |options| options.do_not_execute_commands,
173                |options, value| options.do_not_execute_commands = value,
174            ),
175        ),
176        (
177            "p",
178            ShellOptionDef::new(
179                |options| options.real_effective_uid_mismatch,
180                |options, value| options.real_effective_uid_mismatch = value,
181            ),
182        ),
183        (
184            "t",
185            ShellOptionDef::new(
186                |options| options.exit_after_one_command,
187                |options, value| options.exit_after_one_command = value,
188            ),
189        ),
190        (
191            "u",
192            ShellOptionDef::new(
193                |options| options.treat_unset_variables_as_error,
194                |options, value| options.treat_unset_variables_as_error = value,
195            ),
196        ),
197        (
198            "v",
199            ShellOptionDef::new(
200                |options| options.print_shell_input_lines,
201                |options, value| options.print_shell_input_lines = value,
202            ),
203        ),
204        (
205            "x",
206            ShellOptionDef::new(
207                |options| options.print_commands_and_arguments,
208                |options, value| options.print_commands_and_arguments = value,
209            ),
210        ),
211        (
212            "B",
213            ShellOptionDef::new(
214                |options| options.perform_brace_expansion,
215                |options, value| options.perform_brace_expansion = value,
216            ),
217        ),
218        (
219            "C",
220            ShellOptionDef::new(
221                |options| options.disallow_overwriting_regular_files_via_output_redirection,
222                |options, value| {
223                    options.disallow_overwriting_regular_files_via_output_redirection = value;
224                },
225            ),
226        ),
227        (
228            "E",
229            ShellOptionDef::new(
230                |options| options.shell_functions_inherit_err_trap,
231                |options, value| options.shell_functions_inherit_err_trap = value,
232            ),
233        ),
234        (
235            "H",
236            ShellOptionDef::new(
237                |options| options.enable_bang_style_history_substitution,
238                |options, value| options.enable_bang_style_history_substitution = value,
239            ),
240        ),
241        (
242            "P",
243            ShellOptionDef::new(
244                |options| options.do_not_resolve_symlinks_when_changing_dir,
245                |options, value| options.do_not_resolve_symlinks_when_changing_dir = value,
246            ),
247        ),
248        (
249            "T",
250            ShellOptionDef::new(
251                |options| options.shell_functions_inherit_debug_and_return_traps,
252                |options, value| options.shell_functions_inherit_debug_and_return_traps = value,
253            ),
254        ),
255        (
256            "s",
257            ShellOptionDef::new(
258                |options| options.read_commands_from_stdin,
259                |options, value| options.read_commands_from_stdin = value,
260            ),
261        ),
262    ])
263});
264
265static SET_O_OPTIONS: LazyLock<HashMap<&'static str, ShellOptionDef>> = LazyLock::new(|| {
266    HashMap::from([
267        (
268            "allexport",
269            ShellOptionDef::new(
270                |options| options.export_variables_on_modification,
271                |options, value| options.export_variables_on_modification = value,
272            ),
273        ),
274        (
275            "braceexpand",
276            ShellOptionDef::new(
277                |options| options.perform_brace_expansion,
278                |options, value| options.perform_brace_expansion = value,
279            ),
280        ),
281        (
282            "emacs",
283            ShellOptionDef::new(
284                |options| options.emacs_mode,
285                |options, value| options.emacs_mode = value,
286            ),
287        ),
288        (
289            "errexit",
290            ShellOptionDef::new(
291                |options| options.exit_on_nonzero_command_exit,
292                |options, value| options.exit_on_nonzero_command_exit = value,
293            ),
294        ),
295        (
296            "errtrace",
297            ShellOptionDef::new(
298                |options| options.shell_functions_inherit_err_trap,
299                |options, value| options.shell_functions_inherit_err_trap = value,
300            ),
301        ),
302        (
303            "functrace",
304            ShellOptionDef::new(
305                |options| options.shell_functions_inherit_debug_and_return_traps,
306                |options, value| options.shell_functions_inherit_debug_and_return_traps = value,
307            ),
308        ),
309        (
310            "hashall",
311            ShellOptionDef::new(
312                |options| options.remember_command_locations,
313                |options, value| options.remember_command_locations = value,
314            ),
315        ),
316        (
317            "histexpand",
318            ShellOptionDef::new(
319                |options| options.enable_bang_style_history_substitution,
320                |options, value| options.enable_bang_style_history_substitution = value,
321            ),
322        ),
323        (
324            "history",
325            ShellOptionDef::new(
326                |options| options.enable_command_history,
327                |options, value| options.enable_command_history = value,
328            ),
329        ),
330        (
331            "ignoreeof",
332            ShellOptionDef::new(
333                |options| options.ignore_eof,
334                |options, value| options.ignore_eof = value,
335            ),
336        ),
337        (
338            "interactive-comments",
339            ShellOptionDef::new(
340                |options| options.interactive_comments,
341                |options, value| options.interactive_comments = value,
342            ),
343        ),
344        (
345            "keyword",
346            ShellOptionDef::new(
347                |options| options.place_all_assignment_args_in_command_env,
348                |options, value| options.place_all_assignment_args_in_command_env = value,
349            ),
350        ),
351        (
352            "monitor",
353            ShellOptionDef::new(
354                |options| options.enable_job_control,
355                |options, value| options.enable_job_control = value,
356            ),
357        ),
358        (
359            "noclobber",
360            ShellOptionDef::new(
361                |options| options.disallow_overwriting_regular_files_via_output_redirection,
362                |options, value| {
363                    options.disallow_overwriting_regular_files_via_output_redirection = value;
364                },
365            ),
366        ),
367        (
368            "noexec",
369            ShellOptionDef::new(
370                |options| options.do_not_execute_commands,
371                |options, value| options.do_not_execute_commands = value,
372            ),
373        ),
374        (
375            "noglob",
376            ShellOptionDef::new(
377                |options| options.disable_filename_globbing,
378                |options, value| options.disable_filename_globbing = value,
379            ),
380        ),
381        ("nolog", ShellOptionDef::new(|_| false, |_, _| ())),
382        (
383            "notify",
384            ShellOptionDef::new(
385                |options| options.notify_job_termination_immediately,
386                |options, value| options.notify_job_termination_immediately = value,
387            ),
388        ),
389        (
390            "nounset",
391            ShellOptionDef::new(
392                |options| options.treat_unset_variables_as_error,
393                |options, value| options.treat_unset_variables_as_error = value,
394            ),
395        ),
396        (
397            "onecmd",
398            ShellOptionDef::new(
399                |options| options.exit_after_one_command,
400                |options, value| options.exit_after_one_command = value,
401            ),
402        ),
403        (
404            "physical",
405            ShellOptionDef::new(
406                |options| options.do_not_resolve_symlinks_when_changing_dir,
407                |options, value| options.do_not_resolve_symlinks_when_changing_dir = value,
408            ),
409        ),
410        (
411            "pipefail",
412            ShellOptionDef::new(
413                |options| options.return_first_failure_from_pipeline,
414                |options, value| options.return_first_failure_from_pipeline = value,
415            ),
416        ),
417        (
418            "posix",
419            ShellOptionDef::new(
420                |options| options.posix_mode,
421                |options, value| options.posix_mode = value,
422            ),
423        ),
424        (
425            "privileged",
426            ShellOptionDef::new(
427                |options| options.real_effective_uid_mismatch,
428                |options, value| options.real_effective_uid_mismatch = value,
429            ),
430        ),
431        (
432            "verbose",
433            ShellOptionDef::new(
434                |options| options.print_shell_input_lines,
435                |options, value| options.print_shell_input_lines = value,
436            ),
437        ),
438        (
439            "vi",
440            ShellOptionDef::new(
441                |options| options.vi_mode,
442                |options, value| options.vi_mode = value,
443            ),
444        ),
445        (
446            "xtrace",
447            ShellOptionDef::new(
448                |options| options.print_commands_and_arguments,
449                |options, value| options.print_commands_and_arguments = value,
450            ),
451        ),
452    ])
453});
454
455static SHOPT_OPTIONS: LazyLock<HashMap<&'static str, ShellOptionDef>> = LazyLock::new(|| {
456    HashMap::from([
457        (
458            "autocd",
459            ShellOptionDef::new(
460                |options| options.auto_cd,
461                |options, value| options.auto_cd = value,
462            ),
463        ),
464        (
465            "assoc_expand_once",
466            ShellOptionDef::new(
467                |options| options.assoc_expand_once,
468                |options, value| options.assoc_expand_once = value,
469            ),
470        ),
471        (
472            "cdable_vars",
473            ShellOptionDef::new(
474                |options| options.cdable_vars,
475                |options, value| options.cdable_vars = value,
476            ),
477        ),
478        (
479            "cdspell",
480            ShellOptionDef::new(
481                |options| options.cd_autocorrect_spelling,
482                |options, value| options.cd_autocorrect_spelling = value,
483            ),
484        ),
485        (
486            "checkhash",
487            ShellOptionDef::new(
488                |options| options.check_hashtable_before_command_exec,
489                |options, value| options.check_hashtable_before_command_exec = value,
490            ),
491        ),
492        (
493            "checkjobs",
494            ShellOptionDef::new(
495                |options| options.check_jobs_before_exit,
496                |options, value| options.check_jobs_before_exit = value,
497            ),
498        ),
499        (
500            "checkwinsize",
501            ShellOptionDef::new(
502                |options| options.check_window_size_after_external_commands,
503                |options, value| options.check_window_size_after_external_commands = value,
504            ),
505        ),
506        (
507            "cmdhist",
508            ShellOptionDef::new(
509                |options| options.save_multiline_cmds_in_history,
510                |options, value| options.save_multiline_cmds_in_history = value,
511            ),
512        ),
513        (
514            "compat31",
515            ShellOptionDef::new(
516                |options| options.compat31,
517                |options, value| options.compat31 = value,
518            ),
519        ),
520        (
521            "compat32",
522            ShellOptionDef::new(
523                |options| options.compat32,
524                |options, value| options.compat32 = value,
525            ),
526        ),
527        (
528            "compat40",
529            ShellOptionDef::new(
530                |options| options.compat40,
531                |options, value| options.compat40 = value,
532            ),
533        ),
534        (
535            "compat41",
536            ShellOptionDef::new(
537                |options| options.compat41,
538                |options, value| options.compat41 = value,
539            ),
540        ),
541        (
542            "compat42",
543            ShellOptionDef::new(
544                |options| options.compat42,
545                |options, value| options.compat42 = value,
546            ),
547        ),
548        (
549            "compat43",
550            ShellOptionDef::new(
551                |options| options.compat43,
552                |options, value| options.compat43 = value,
553            ),
554        ),
555        (
556            "compat44",
557            ShellOptionDef::new(
558                |options| options.compat44,
559                |options, value| options.compat44 = value,
560            ),
561        ),
562        (
563            "complete_fullquote",
564            ShellOptionDef::new(
565                |options| options.quote_all_metachars_in_completion,
566                |options, value| options.quote_all_metachars_in_completion = value,
567            ),
568        ),
569        (
570            "direxpand",
571            ShellOptionDef::new(
572                |options| options.expand_dir_names_on_completion,
573                |options, value| options.expand_dir_names_on_completion = value,
574            ),
575        ),
576        (
577            "dirspell",
578            ShellOptionDef::new(
579                |options| options.autocorrect_dir_spelling_on_completion,
580                |options, value| options.autocorrect_dir_spelling_on_completion = value,
581            ),
582        ),
583        (
584            "dotglob",
585            ShellOptionDef::new(
586                |options| options.glob_matches_dotfiles,
587                |options, value| options.glob_matches_dotfiles = value,
588            ),
589        ),
590        (
591            "execfail",
592            ShellOptionDef::new(
593                |options| options.exit_on_exec_fail,
594                |options, value| options.exit_on_exec_fail = value,
595            ),
596        ),
597        (
598            "expand_aliases",
599            ShellOptionDef::new(
600                |options| options.expand_aliases,
601                |options, value| options.expand_aliases = value,
602            ),
603        ),
604        (
605            "extdebug",
606            ShellOptionDef::new(
607                |options| options.enable_debugger,
608                |options, value| options.enable_debugger = value,
609            ),
610        ),
611        (
612            "extglob",
613            ShellOptionDef::new(
614                |options| options.extended_globbing,
615                |options, value| options.extended_globbing = value,
616            ),
617        ),
618        (
619            "extquote",
620            ShellOptionDef::new(
621                |options| options.extquote,
622                |options, value| options.extquote = value,
623            ),
624        ),
625        (
626            "failglob",
627            ShellOptionDef::new(
628                |options| options.fail_expansion_on_globs_without_match,
629                |options, value| options.fail_expansion_on_globs_without_match = value,
630            ),
631        ),
632        (
633            "force_fignore",
634            ShellOptionDef::new(
635                |options| options.force_fignore,
636                |options, value| options.force_fignore = value,
637            ),
638        ),
639        (
640            "globasciiranges",
641            ShellOptionDef::new(
642                |options| options.glob_ranges_use_c_locale,
643                |options, value| options.glob_ranges_use_c_locale = value,
644            ),
645        ),
646        (
647            "globstar",
648            ShellOptionDef::new(
649                |options| options.enable_star_star_glob,
650                |options, value| options.enable_star_star_glob = value,
651            ),
652        ),
653        (
654            "gnu_errfmt",
655            ShellOptionDef::new(
656                |options| options.errors_in_gnu_format,
657                |options, value| options.errors_in_gnu_format = value,
658            ),
659        ),
660        (
661            "histappend",
662            ShellOptionDef::new(
663                |options| options.append_to_history_file,
664                |options, value| options.append_to_history_file = value,
665            ),
666        ),
667        (
668            "histreedit",
669            ShellOptionDef::new(
670                |options| options.allow_reedit_failed_history_subst,
671                |options, value| options.allow_reedit_failed_history_subst = value,
672            ),
673        ),
674        (
675            "histverify",
676            ShellOptionDef::new(
677                |options| options.allow_modifying_history_substitution,
678                |options, value| options.allow_modifying_history_substitution = value,
679            ),
680        ),
681        (
682            "hostcomplete",
683            ShellOptionDef::new(
684                |options| options.enable_hostname_completion,
685                |options, value| options.enable_hostname_completion = value,
686            ),
687        ),
688        (
689            "huponexit",
690            ShellOptionDef::new(
691                |options| options.send_sighup_to_all_jobs_on_exit,
692                |options, value| options.send_sighup_to_all_jobs_on_exit = value,
693            ),
694        ),
695        (
696            "inherit_errexit",
697            ShellOptionDef::new(
698                |options| options.command_subst_inherits_errexit,
699                |options, value| options.command_subst_inherits_errexit = value,
700            ),
701        ),
702        (
703            "interactive_comments",
704            ShellOptionDef::new(
705                |options| options.interactive_comments,
706                |options, value| options.interactive_comments = value,
707            ),
708        ),
709        (
710            "lastpipe",
711            ShellOptionDef::new(
712                |options| options.run_last_pipeline_cmd_in_current_shell,
713                |options, value| options.run_last_pipeline_cmd_in_current_shell = value,
714            ),
715        ),
716        (
717            "lithist",
718            ShellOptionDef::new(
719                |options| options.embed_newlines_in_multiline_cmds_in_history,
720                |options, value| options.embed_newlines_in_multiline_cmds_in_history = value,
721            ),
722        ),
723        (
724            "localvar_inherit",
725            ShellOptionDef::new(
726                |options| options.local_vars_inherit_value_and_attrs,
727                |options, value| options.local_vars_inherit_value_and_attrs = value,
728            ),
729        ),
730        (
731            "localvar_unset",
732            ShellOptionDef::new(
733                |options| options.localvar_unset,
734                |options, value| options.localvar_unset = value,
735            ),
736        ),
737        (
738            "login_shell",
739            ShellOptionDef::new(
740                |options| options.login_shell,
741                |options, value| options.login_shell = value,
742            ),
743        ),
744        (
745            "mailwarn",
746            ShellOptionDef::new(
747                |options| options.mail_warn,
748                |options, value| options.mail_warn = value,
749            ),
750        ),
751        (
752            "no_empty_cmd_completion",
753            ShellOptionDef::new(
754                |options| options.no_empty_cmd_completion,
755                |options, value| options.no_empty_cmd_completion = value,
756            ),
757        ),
758        (
759            "nocaseglob",
760            ShellOptionDef::new(
761                |options| options.case_insensitive_pathname_expansion,
762                |options, value| options.case_insensitive_pathname_expansion = value,
763            ),
764        ),
765        (
766            "nocasematch",
767            ShellOptionDef::new(
768                |options| options.case_insensitive_conditionals,
769                |options, value| options.case_insensitive_conditionals = value,
770            ),
771        ),
772        (
773            "nullglob",
774            ShellOptionDef::new(
775                |options| options.expand_non_matching_patterns_to_null,
776                |options, value| options.expand_non_matching_patterns_to_null = value,
777            ),
778        ),
779        (
780            "progcomp",
781            ShellOptionDef::new(
782                |options| options.programmable_completion,
783                |options, value| options.programmable_completion = value,
784            ),
785        ),
786        (
787            "progcomp_alias",
788            ShellOptionDef::new(
789                |options| options.programmable_completion_alias,
790                |options, value| options.programmable_completion_alias = value,
791            ),
792        ),
793        (
794            "promptvars",
795            ShellOptionDef::new(
796                |options| options.expand_prompt_strings,
797                |options, value| options.expand_prompt_strings = value,
798            ),
799        ),
800        (
801            "restricted_shell",
802            ShellOptionDef::new(
803                |options| options.restricted_shell,
804                |options, value| options.restricted_shell = value,
805            ),
806        ),
807        (
808            "shift_verbose",
809            ShellOptionDef::new(
810                |options| options.shift_verbose,
811                |options, value| options.shift_verbose = value,
812            ),
813        ),
814        (
815            "sourcepath",
816            ShellOptionDef::new(
817                |options| options.source_builtin_searches_path,
818                |options, value| options.source_builtin_searches_path = value,
819            ),
820        ),
821        (
822            "xpg_echo",
823            ShellOptionDef::new(
824                |options| options.echo_builtin_expands_escape_sequences,
825                |options, value| options.echo_builtin_expands_escape_sequences = value,
826            ),
827        ),
828    ])
829});