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