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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Init script support for shells.
use std::path::PathBuf;
use crate::{Shell, error, extensions, interp};
/// Behavior for loading profile files.
#[derive(Default)]
pub enum ProfileLoadBehavior {
/// Load the default profile files.
#[default]
LoadDefault,
/// Skip loading profile files.
Skip,
}
impl ProfileLoadBehavior {
/// Returns whether profile loading should be skipped.
pub const fn skip(&self) -> bool {
matches!(self, Self::Skip)
}
}
/// Behavior for loading rc files.
#[derive(Default)]
pub enum RcLoadBehavior {
/// Load the default rc files.
#[default]
LoadDefault,
/// Load a custom rc file; do not load defaults.
LoadCustom(PathBuf),
/// Skip loading rc files.
Skip,
}
impl RcLoadBehavior {
/// Returns whether rc loading should be skipped.
pub const fn skip(&self) -> bool {
matches!(self, Self::Skip)
}
}
impl<SE: extensions::ShellExtensions> Shell<SE> {
/// Loads and executes standard shell configuration files (i.e., rc and profile).
///
/// # Arguments
///
/// * `profile_behavior` - Behavior for loading profile files.
/// * `rc_behavior` - Behavior for loading rc files.
pub async fn load_config(
&mut self,
profile_behavior: &ProfileLoadBehavior,
rc_behavior: &RcLoadBehavior,
) -> Result<(), error::Error> {
let mut params = self.default_exec_params();
params.process_group_policy = interp::ProcessGroupPolicy::SameProcessGroup;
if self.options.login_shell {
// --noprofile means skip this.
if matches!(profile_behavior, ProfileLoadBehavior::Skip) {
return Ok(());
}
//
// Source the system profile if it exists.
//
// Next source the first of these that exists and is readable (if any):
// * ~/.bash_profile
// * ~/.bash_login
// * ~/.profile
//
if let Some(system_profile) = crate::sys::fs::get_system_profile_path() {
self.source_if_exists(system_profile, ¶ms).await?;
}
if let Some(home_path) = self.home_dir() {
if self.options.sh_mode {
self.source_if_exists(home_path.join(".profile").as_path(), ¶ms)
.await?;
} else {
if !self
.source_if_exists(home_path.join(".bash_profile").as_path(), ¶ms)
.await?
{
if !self
.source_if_exists(home_path.join(".bash_login").as_path(), ¶ms)
.await?
{
self.source_if_exists(home_path.join(".profile").as_path(), ¶ms)
.await?;
}
}
}
}
} else {
if self.options.interactive {
match rc_behavior {
_ if self.options.sh_mode => (),
RcLoadBehavior::Skip => (),
RcLoadBehavior::LoadCustom(rc_file) => {
// If an explicit rc file is provided, source it.
self.source_if_exists(rc_file, ¶ms).await?;
}
RcLoadBehavior::LoadDefault => {
//
// Otherwise, for non-login interactive shells, load in this order:
//
// system rc file (e.g. /etc/bash.bashrc on Unix)
// ~/.bashrc
//
if let Some(system_rc) = crate::sys::fs::get_system_rc_path() {
self.source_if_exists(system_rc, ¶ms).await?;
}
if let Some(home_path) = self.home_dir() {
self.source_if_exists(home_path.join(".bashrc").as_path(), ¶ms)
.await?;
self.source_if_exists(home_path.join(".brushrc").as_path(), ¶ms)
.await?;
}
}
}
} else {
let env_var_name = if self.options.sh_mode {
"ENV"
} else {
"BASH_ENV"
};
if self.env.is_set(env_var_name) {
//
// TODO(well-known-vars): look at $ENV/BASH_ENV; source its expansion if that
// file exists
//
return error::unimp(
"load config from $ENV/BASH_ENV for non-interactive, non-login shell",
);
}
}
}
Ok(())
}
}