yash_cli/startup.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2023 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Shell startup
18
19use self::args::{Run, Source, Work};
20use yash_builtin::BUILTINS;
21use yash_env::Env;
22use yash_env::System;
23use yash_env::io::Fd;
24use yash_env::option::Option::{Interactive, Monitor, Stdin};
25use yash_env::option::State::On;
26
27pub mod args;
28pub mod init_file;
29pub mod input;
30
31/// Tests whether the shell should be implicitly interactive.
32///
33/// As per POSIX, "if the shell reads commands from the standard input and the
34/// shell's standard input and standard error are attached to a terminal, the
35/// shell is considered to be interactive." This function implements this rule.
36///
37/// This function returns `false` if the interactive option is explicitly
38/// specified in the command line arguments to honor the user's intent.
39pub fn auto_interactive<S: System>(system: &S, run: &Run) -> bool {
40 if run.work.source != Source::Stdin {
41 return false;
42 }
43 if run.options.iter().any(|&(o, _)| o == Interactive) {
44 return false;
45 }
46 system.isatty(Fd::STDIN) && system.isatty(Fd::STDERR)
47}
48
49/// Get the environment ready for performing the work.
50///
51/// This function takes the parsed command-line arguments and applies them to
52/// the environment. It also sets up signal dispositions and prepares built-ins
53/// and variables. The function returns the work to be performed, which is
54/// extracted from the `run` argument.
55///
56/// This function is _pure_ in that all system calls are performed by the
57/// `System` trait object (`env.system`).
58pub fn configure_environment(env: &mut Env, run: Run) -> Work {
59 // Apply the parsed options to the environment
60 if auto_interactive(&env.system, &run) {
61 env.options.set(Interactive, On);
62 }
63 if run.work.source == self::args::Source::Stdin {
64 env.options.set(Stdin, On);
65 }
66 for &(option, state) in &run.options {
67 env.options.set(option, state);
68 }
69 if env.options.get(Interactive) == On && !run.options.iter().any(|&(o, _)| o == Monitor) {
70 env.options.set(Monitor, On);
71 }
72
73 // Apply the parsed operands to the environment
74 env.arg0 = run.arg0;
75 env.variables.positional_params_mut().values = run.positional_params;
76
77 // Configure internal dispositions for signals
78 if env.options.get(Interactive) == On {
79 env.traps
80 .enable_internal_dispositions_for_terminators(&mut env.system)
81 .ok();
82 if env.options.get(Monitor) == On {
83 env.traps
84 .enable_internal_dispositions_for_stoppers(&mut env.system)
85 .ok();
86 }
87 }
88
89 // Make sure the shell is in the foreground if job control is enabled
90 if env.options.get(Monitor) == On {
91 // Ignore failures as we can still proceed even if we can't get into the foreground
92 env.ensure_foreground().ok();
93 }
94
95 // Prepare built-ins
96 env.builtins.extend(BUILTINS.iter().cloned());
97
98 // Prepare variables
99 env.init_variables();
100
101 run.work
102}