Skip to content

Commit 3638225

Browse files
committed
auto merge of #17249 : vadimcn/rust/env-keys, r=alexcrichton
Closes #16937
2 parents 35be9b8 + 8b84911 commit 3638225

File tree

1 file changed

+76
-7
lines changed

1 file changed

+76
-7
lines changed

src/libstd/io/process.rs

+76-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
2424
use rt::rtio;
2525
use c_str::CString;
2626
use collections::HashMap;
27+
use hash::Hash;
28+
use clone::Clone;
29+
#[cfg(windows)]
30+
use std::hash::sip::SipState;
2731

2832
/// Signal a process to exit, without forcibly killing it. Corresponds to
2933
/// SIGTERM on unix platforms.
@@ -78,8 +82,56 @@ pub struct Process {
7882
pub extra_io: Vec<Option<io::PipeStream>>,
7983
}
8084

85+
/// A representation of environment variable name
86+
/// It compares case-insensitive on Windows and case-sensitive everywhere else.
87+
#[cfg(not(windows))]
88+
#[deriving(PartialEq, Eq, Hash, Clone, Show)]
89+
struct EnvKey(CString);
90+
91+
#[doc(hidden)]
92+
#[cfg(windows)]
93+
#[deriving(Eq, Clone, Show)]
94+
struct EnvKey(CString);
95+
96+
#[cfg(windows)]
97+
impl Hash for EnvKey {
98+
fn hash(&self, state: &mut SipState) {
99+
let &EnvKey(ref x) = self;
100+
match x.as_str() {
101+
Some(s) => for ch in s.chars() {
102+
(ch as u8 as char).to_lowercase().hash(state);
103+
},
104+
None => x.hash(state)
105+
}
106+
}
107+
}
108+
109+
#[cfg(windows)]
110+
impl PartialEq for EnvKey {
111+
fn eq(&self, other: &EnvKey) -> bool {
112+
let &EnvKey(ref x) = self;
113+
let &EnvKey(ref y) = other;
114+
match (x.as_str(), y.as_str()) {
115+
(Some(xs), Some(ys)) => {
116+
if xs.len() != ys.len() {
117+
return false
118+
} else {
119+
for (xch, ych) in xs.chars().zip(ys.chars()) {
120+
if xch.to_lowercase() != ych.to_lowercase() {
121+
return false;
122+
}
123+
}
124+
return true;
125+
}
126+
},
127+
// If either is not a valid utf8 string, just compare them byte-wise
128+
_ => return x.eq(y)
129+
}
130+
}
131+
}
132+
81133
/// A HashMap representation of environment variables.
82-
pub type EnvMap = HashMap<CString, CString>;
134+
pub type EnvMap = HashMap<EnvKey, CString>;
83135

84136
/// The `Command` type acts as a process builder, providing fine-grained control
85137
/// over how a new process should be spawned. A default configuration can be
@@ -161,14 +213,14 @@ impl Command {
161213
self
162214
}
163215
// Get a mutable borrow of the environment variable map for this `Command`.
164-
fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
216+
fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
165217
match self.env {
166218
Some(ref mut map) => map,
167219
None => {
168220
// if the env is currently just inheriting from the parent's,
169221
// materialize the parent's env into a hashtable.
170222
self.env = Some(os::env_as_bytes().into_iter()
171-
.map(|(k, v)| (k.as_slice().to_c_str(),
223+
.map(|(k, v)| (EnvKey(k.as_slice().to_c_str()),
172224
v.as_slice().to_c_str()))
173225
.collect());
174226
self.env.as_mut().unwrap()
@@ -177,15 +229,18 @@ impl Command {
177229
}
178230

179231
/// Inserts or updates an environment variable mapping.
232+
///
233+
/// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
234+
/// and case-sensitive on all other platforms.
180235
pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U)
181236
-> &'a mut Command {
182-
self.get_env_map().insert(key.to_c_str(), val.to_c_str());
237+
self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str());
183238
self
184239
}
185240

186241
/// Removes an environment variable mapping.
187242
pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command {
188-
self.get_env_map().remove(&key.to_c_str());
243+
self.get_env_map().remove(&EnvKey(key.to_c_str()));
189244
self
190245
}
191246

@@ -195,7 +250,7 @@ impl Command {
195250
/// variable, the *rightmost* instance will determine the value.
196251
pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)])
197252
-> &'a mut Command {
198-
self.env = Some(env.iter().map(|&(ref k, ref v)| (k.to_c_str(), v.to_c_str()))
253+
self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str()))
199254
.collect());
200255
self
201256
}
@@ -273,7 +328,9 @@ impl Command {
273328
let env = match self.env {
274329
None => None,
275330
Some(ref env_map) =>
276-
Some(env_map.iter().collect::<Vec<_>>())
331+
Some(env_map.iter()
332+
.map(|(&EnvKey(ref key), val)| (key, val))
333+
.collect::<Vec<_>>())
277334
};
278335
let cfg = ProcessConfig {
279336
program: &self.program,
@@ -1039,4 +1096,16 @@ mod tests {
10391096
assert!(cmd.status().unwrap().success());
10401097
assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
10411098
})
1099+
1100+
#[test]
1101+
#[cfg(windows)]
1102+
fn env_map_keys_ci() {
1103+
use super::EnvKey;
1104+
let mut cmd = Command::new("");
1105+
cmd.env("path", "foo");
1106+
cmd.env("Path", "bar");
1107+
let env = &cmd.env.unwrap();
1108+
let val = env.find(&EnvKey("PATH".to_c_str()));
1109+
assert!(val.unwrap() == &"bar".to_c_str());
1110+
}
10421111
}

0 commit comments

Comments
 (0)