1
1
use r_efi::protocols::simple_text_output;
2
2
3
3
use super::helpers;
4
+ use crate::collections::BTreeMap;
4
5
pub use crate::ffi::OsString as EnvKey;
5
6
use crate::ffi::{OsStr, OsString};
6
7
use crate::num::{NonZero, NonZeroI32};
@@ -21,6 +22,7 @@ pub struct Command {
21
22
args: Vec<OsString>,
22
23
stdout: Option<Stdio>,
23
24
stderr: Option<Stdio>,
25
+ env: CommandEnv,
24
26
}
25
27
26
28
// passed back to std::process with the pipes connected to the child, if any
@@ -40,15 +42,21 @@ pub enum Stdio {
40
42
41
43
impl Command {
42
44
pub fn new(program: &OsStr) -> Command {
43
- Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None }
45
+ Command {
46
+ prog: program.to_os_string(),
47
+ args: Vec::new(),
48
+ stdout: None,
49
+ stderr: None,
50
+ env: Default::default(),
51
+ }
44
52
}
45
53
46
54
pub fn arg(&mut self, arg: &OsStr) {
47
55
self.args.push(arg.to_os_string());
48
56
}
49
57
50
58
pub fn env_mut(&mut self) -> &mut CommandEnv {
51
- panic!("unsupported")
59
+ &mut self.env
52
60
}
53
61
54
62
pub fn cwd(&mut self, _dir: &OsStr) {
@@ -76,7 +84,7 @@ impl Command {
76
84
}
77
85
78
86
pub fn get_envs(&self) -> CommandEnvs<'_> {
79
- panic!("unsupported" )
87
+ self.env.iter( )
80
88
}
81
89
82
90
pub fn get_current_dir(&self) -> Option<&Path> {
@@ -140,8 +148,30 @@ impl Command {
140
148
cmd.stderr_inherit()
141
149
};
142
150
151
+ let env = env_changes(&self.env);
152
+
153
+ // Set any new vars
154
+ if let Some(e) = &env {
155
+ for (k, (_, v)) in e {
156
+ match v {
157
+ Some(v) => crate::env::set_var(k, v),
158
+ None => crate::env::remove_var(k),
159
+ }
160
+ }
161
+ }
162
+
143
163
let stat = cmd.start_image()?;
144
164
165
+ // Rollback any env changes
166
+ if let Some(e) = env {
167
+ for (k, (v, _)) in e {
168
+ match v {
169
+ Some(v) => crate::env::set_var(k, v),
170
+ None => crate::env::remove_var(k),
171
+ }
172
+ }
173
+ }
174
+
145
175
let stdout = cmd.stdout()?;
146
176
let stderr = cmd.stderr()?;
147
177
@@ -725,3 +755,32 @@ mod uefi_command_internal {
725
755
res.into_boxed_slice()
726
756
}
727
757
}
758
+
759
+ /// Create a map of environment variable changes. Allows efficient setting and rolling back of
760
+ /// enviroment variable changes.
761
+ ///
762
+ /// Entry: (Old Value, New Value)
763
+ fn env_changes(env: &CommandEnv) -> Option<BTreeMap<EnvKey, (Option<OsString>, Option<OsString>)>> {
764
+ if env.is_unchanged() {
765
+ return None;
766
+ }
767
+
768
+ let mut result = BTreeMap::<EnvKey, (Option<OsString>, Option<OsString>)>::new();
769
+
770
+ // Check if we want to clear all prior variables
771
+ if env.does_clear() {
772
+ for (k, v) in crate::env::vars_os() {
773
+ result.insert(k.into(), (Some(v), None));
774
+ }
775
+ }
776
+
777
+ for (k, v) in env.iter() {
778
+ let v: Option<OsString> = v.map(Into::into);
779
+ result
780
+ .entry(k.into())
781
+ .and_modify(|cur| *cur = (cur.0.clone(), v.clone()))
782
+ .or_insert((crate::env::var_os(k), v));
783
+ }
784
+
785
+ Some(result)
786
+ }
0 commit comments