Change:
The following program demonstrates the problem: When compiled and executed on WIndows, it randomly displays either 'foo' or 'BAR'. This happens because process::Command represents environment block as a HasMap<CString, CString, so it treats "foo" and "FOO" as different variables, whereas Windows considers them to be the same one. When child process is spawned, both get into the environment block, however Windows keeps only the last one.

Discussion:
use rt::rtio; use c_str::CString; use collections::HashMap; <ins> use hash::Hash; use clone::Clone; #[cfg(windows)] use std::hash::sip::SipState; </ins> /// Signal a process to exit, without forcibly killing it. Corresponds to /// SIGTERM on unix platforms.

pub extra_io: Vec<Option<io::PipeStream>>, } <ins> /// A representation of environment variable name /// It compares case-insensitive on Windows and case-sensitive everywhere else. #[cfg(not(windows))] #[deriving(PartialEq, Eq, Hash, Clone, Show)] struct EnvKey(CString); #[doc(hidden)] #[cfg(windows)] #[deriving(Eq, Clone, Show)] struct EnvKey(CString); #[cfg(windows)] impl Hash for EnvKey { fn hash(&self, state: &mut SipState) { let &EnvKey(ref x) = self; match x.as_str() { Some(s) => for ch in s.chars() { (ch as u8 as char).to_lowercase().hash(state); }, None => x.hash(state) } } } #[cfg(windows)] impl PartialEq for EnvKey { fn eq(&self, other: &EnvKey) -> bool { let &EnvKey(ref x) = self; let &EnvKey(ref y) = other; match (x.as_str(), y.as_str()) { (Some(xs), Some(ys)) => { if xs.len() != ys.len() { return false } else { for (xch, ych) in xs.chars().zip(ys.chars()) { if xch.to_lowercase() != ych.to_lowercase() { return false; } } return true; } }, // If either is not a valid utf8 string, just compare them byte-wise _ => return x.eq(y) } } } </ins> /// A HashMap representation of environment variables. <del> pub type EnvMap = HashMap<CString, CString>; </del> <ins> pub type EnvMap = HashMap<EnvKey, CString>; </ins> /// The `Command` type acts as a process builder, providing fine-grained control /// over how a new process should be spawned. A default configuration can be

self } // Get a mutable borrow of the environment variable map for this `Command`. <del> fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap { </del> <ins> fn get_env_map<'a>(&'a mut self) -> &'a mut  EnvMap { </ins> match self.env { Some(ref mut map) => map, None => { // if the env is currently just inheriting from the parent's, // materialize the parent's env into a hashtable. self.env = Some(os::env_as_bytes().into_iter() <del> .map(|(k, v)| (k.as_slice().to_c_str(), </del> <ins> .map(|(k, v)| (EnvKey(k.as_slice().to_c_str()), </ins> v.as_slice().to_c_str())) .collect()); self.env.as_mut().unwrap()

} /// Inserts or updates an environment variable mapping. <ins> /// /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, /// and case-sensitive on all other platforms. </ins> pub fn env<'a, T: ToCStr, U: ToCStr>(&'a mut self, key: T, val: U) -> &'a mut Command { <del> self.get_env_map().insert(key.to_c_str(), val.to_c_str()); </del> <ins> self.get_env_map().insert(EnvKey(key.to_c_str()), val.to_c_str()); </ins> self } /// Removes an environment variable mapping. pub fn env_remove<'a, T: ToCStr>(&'a mut self, key: T) -> &'a mut Command { <del> self.get_env_map().remove(&key.to_c_str()); </del> <ins> self.get_env_map().remove(&EnvKey(key.to_c_str())); </ins> self }

/// variable, the *rightmost* instance will determine the value. pub fn env_set_all<'a, T: ToCStr, U: ToCStr>(&'a mut self, env: &[(T,U)]) -> &'a mut Command { <del> self.env = Some(env.iter().map(|&(ref k, ref v)| (k.to_c_str(), v.to_c_str())) </del> <ins> self.env = Some(env.iter().map(|&(ref k, ref v)| (EnvKey(k.to_c_str()), v.to_c_str())) </ins> .collect()); self }

let env = match self.env { None => None, Some(ref env_map) => <del> Some(env_map.iter().collect::<Vec<_>>()) </del> <ins> Some(env_map.iter() .map(|(&EnvKey(ref key), val)| (key, val)) .collect::<Vec<_>>()) </ins> }; let cfg = ProcessConfig { program: &self.program,

assert!(cmd.status().unwrap().success()); assert!(fdes.inner_write("extra writen".as_bytes()).is_ok()); }) <ins> #[test] #[cfg(windows)] fn env_map_keys_ci() { use super::EnvKey; let mut cmd = Command::new(""); cmd.env("path", "foo"); cmd.env("Path", "bar"); let env = &cmd.env.unwrap(); let val = env.find(&EnvKey("PATH".to_c_str())); assert!(val.unwrap() == &"bar".to_c_str()); } </ins> }