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 {
36 (self.getter)(options)
37 }
38
39 pub fn set(&self, options: &mut RuntimeOptions, value: bool) {
46 (self.setter)(options, value);
47 }
48}
49
50pub struct ShellOption {
52 pub name: &'static str,
54 pub definition: &'static ShellOptionDef,
56}
57
58pub struct ShellOptionSet {
60 inner: &'static HashMap<&'static str, ShellOptionDef>,
61}
62
63#[derive(Clone, Copy)]
65pub enum ShellOptionKind {
66 Set,
68 SetO,
70 Shopt,
72}
73
74pub 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 pub fn iter(&self) -> impl Iterator<Item = ShellOption> {
96 self.inner
97 .iter()
98 .map(|(&name, definition)| ShellOption { name, definition })
99 }
100
101 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});