yash_cli/
startup.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// This file is part of yash, an extended POSIX shell.
// Copyright (C) 2023 WATANABE Yuki
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Shell startup

use self::args::{Run, Source, Work};
use yash_builtin::BUILTINS;
use yash_env::io::Fd;
use yash_env::option::Option::{Interactive, Monitor, Stdin};
use yash_env::option::State::On;
use yash_env::Env;
use yash_env::System;

pub mod args;
pub mod init_file;
pub mod input;

/// Tests whether the shell should be implicitly interactive.
///
/// As per POSIX, "if there are no operands and the shell's standard input and
/// standard error are attached to a terminal, the shell is considered to be
/// interactive." This function implements this rule.
pub fn auto_interactive<S: System>(system: &S, run: &Run) -> bool {
    if run.work.source != Source::Stdin {
        return false;
    }
    if run.options.iter().any(|&(o, _)| o == Interactive) {
        return false;
    }
    if !run.positional_params.is_empty() {
        return false;
    }
    system.isatty(Fd::STDIN) && system.isatty(Fd::STDERR)
}

/// Get the environment ready for performing the work.
///
/// This function takes the parsed command-line arguments and applies them to
/// the environment. It also sets up signal dispositions and prepares built-ins
/// and variables. The function returns the work to be performed, which is
/// extracted from the `run` argument.
///
/// This function is _pure_ in that all system calls are performed by the
/// `System` trait object (`env.system`).
pub fn configure_environment(env: &mut Env, run: Run) -> Work {
    // Apply the parsed options to the environment
    if auto_interactive(&env.system, &run) {
        env.options.set(Interactive, On);
    }
    if run.work.source == self::args::Source::Stdin {
        env.options.set(Stdin, On);
    }
    for &(option, state) in &run.options {
        env.options.set(option, state);
    }
    if env.options.get(Interactive) == On && !run.options.iter().any(|&(o, _)| o == Monitor) {
        env.options.set(Monitor, On);
    }

    // Apply the parsed operands to the environment
    env.arg0 = run.arg0;
    env.variables.positional_params_mut().values = run.positional_params;

    // Configure internal dispositions for signals
    if env.options.get(Interactive) == On {
        env.traps
            .enable_internal_dispositions_for_terminators(&mut env.system)
            .ok();
        if env.options.get(Monitor) == On {
            env.traps
                .enable_internal_dispositions_for_stoppers(&mut env.system)
                .ok();
        }
    }

    // Prepare built-ins
    env.builtins.extend(BUILTINS.iter().cloned());

    // Prepare variables
    env.init_variables();

    run.work
}