@@ -24,6 +24,9 @@ use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
2424use rt:: rtio;
2525use c_str:: CString ;
2626use collections:: HashMap ;
27+ use std:: hash:: Hash ;
28+ use std:: hash:: sip:: SipState ;
29+ use clone:: Clone ;
2730
2831/// Signal a process to exit, without forcibly killing it. Corresponds to
2932/// SIGTERM on unix platforms.
@@ -78,8 +81,56 @@ pub struct Process {
7881 pub extra_io : Vec < Option < io:: PipeStream > > ,
7982}
8083
84+ /// A representation of environment variable name
85+ /// It compares case-insensitive on Windows and case-sensitive everywhere else.
86+ #[ cfg( not( windows) ) ]
87+ #[ deriving( PartialEq , Eq , Hash , Clone , Show ) ]
88+ struct EnvKey ( CString ) ;
89+
90+ #[ doc( hidden) ]
91+ #[ cfg( windows) ]
92+ #[ deriving( Eq , Clone , Show ) ]
93+ struct EnvKey ( CString ) ;
94+
95+ #[ cfg( windows) ]
96+ impl Hash for EnvKey {
97+ fn hash ( & self , state : & mut SipState ) {
98+ let & EnvKey ( ref x) = self ;
99+ match x. as_str ( ) {
100+ Some ( s) => for ch in s. chars ( ) {
101+ ( ch as u8 as char ) . to_lowercase ( ) . hash ( state) ;
102+ } ,
103+ None => x. hash ( state)
104+ }
105+ }
106+ }
107+
108+ #[ cfg( windows) ]
109+ impl PartialEq for EnvKey {
110+ fn eq ( & self , other : & EnvKey ) -> bool {
111+ let & EnvKey ( ref x) = self ;
112+ let & EnvKey ( ref y) = other;
113+ match ( x. as_str ( ) , y. as_str ( ) ) {
114+ ( Some ( xs) , Some ( ys) ) => {
115+ if xs. len ( ) != ys. len ( ) {
116+ return false
117+ } else {
118+ for ( xch, ych) in xs. chars ( ) . zip ( ys. chars ( ) ) {
119+ if xch. to_lowercase ( ) != ych. to_lowercase ( ) {
120+ return false ;
121+ }
122+ }
123+ return true ;
124+ }
125+ } ,
126+ // If either is not a valid utf8 string, just compare them byte-wise
127+ _ => return x. eq ( y)
128+ }
129+ }
130+ }
131+
81132/// A HashMap representation of environment variables.
82- pub type EnvMap = HashMap < CString , CString > ;
133+ pub type EnvMap = HashMap < EnvKey , CString > ;
83134
84135/// The `Command` type acts as a process builder, providing fine-grained control
85136/// over how a new process should be spawned. A default configuration can be
@@ -161,14 +212,14 @@ impl Command {
161212 self
162213 }
163214 // Get a mutable borrow of the environment variable map for this `Command`.
164- fn get_env_map < ' a > ( & ' a mut self ) -> & ' a mut EnvMap {
215+ fn get_env_map < ' a > ( & ' a mut self ) -> & ' a mut EnvMap {
165216 match self . env {
166217 Some ( ref mut map) => map,
167218 None => {
168219 // if the env is currently just inheriting from the parent's,
169220 // materialize the parent's env into a hashtable.
170221 self . env = Some ( os:: env_as_bytes ( ) . into_iter ( )
171- . map ( |( k, v) | ( k. as_slice ( ) . to_c_str ( ) ,
222+ . map ( |( k, v) | ( EnvKey ( k. as_slice ( ) . to_c_str ( ) ) ,
172223 v. as_slice ( ) . to_c_str ( ) ) )
173224 . collect ( ) ) ;
174225 self . env . as_mut ( ) . unwrap ( )
@@ -177,15 +228,18 @@ impl Command {
177228 }
178229
179230 /// Inserts or updates an environment variable mapping.
231+ ///
232+ /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
233+ /// and case-sensitive on all other platforms.
180234 pub fn env < ' a , T : ToCStr , U : ToCStr > ( & ' a mut self , key : T , val : U )
181235 -> & ' a mut Command {
182- self . get_env_map ( ) . insert ( key. to_c_str ( ) , val. to_c_str ( ) ) ;
236+ self . get_env_map ( ) . insert ( EnvKey ( key. to_c_str ( ) ) , val. to_c_str ( ) ) ;
183237 self
184238 }
185239
186240 /// Removes an environment variable mapping.
187241 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 ( ) ) ;
242+ self . get_env_map ( ) . remove ( & EnvKey ( key. to_c_str ( ) ) ) ;
189243 self
190244 }
191245
@@ -195,7 +249,7 @@ impl Command {
195249 /// variable, the *rightmost* instance will determine the value.
196250 pub fn env_set_all < ' a , T : ToCStr , U : ToCStr > ( & ' a mut self , env : & [ ( T , U ) ] )
197251 -> & ' a mut Command {
198- self . env = Some ( env. iter ( ) . map ( |& ( ref k, ref v) | ( k. to_c_str ( ) , v. to_c_str ( ) ) )
252+ self . env = Some ( env. iter ( ) . map ( |& ( ref k, ref v) | ( EnvKey ( k. to_c_str ( ) ) , v. to_c_str ( ) ) )
199253 . collect ( ) ) ;
200254 self
201255 }
@@ -273,7 +327,9 @@ impl Command {
273327 let env = match self . env {
274328 None => None ,
275329 Some ( ref env_map) =>
276- Some ( env_map. iter ( ) . collect :: < Vec < _ > > ( ) )
330+ Some ( env_map. iter ( )
331+ . map ( |( & EnvKey ( ref key) , val) | ( key, val) )
332+ . collect :: < Vec < _ > > ( ) )
277333 } ;
278334 let cfg = ProcessConfig {
279335 program : & self . program ,
@@ -1039,4 +1095,16 @@ mod tests {
10391095 assert!( cmd. status( ) . unwrap( ) . success( ) ) ;
10401096 assert!( fdes. inner_write( "extra write\n " . as_bytes( ) ) . is_ok( ) ) ;
10411097 } )
1098+
1099+ #[ test]
1100+ #[ cfg( windows) ]
1101+ fn env_map_keys_ci ( ) {
1102+ use super :: EnvKey ;
1103+ let mut cmd = Command :: new ( "" ) ;
1104+ cmd. env ( "path" , "foo" ) ;
1105+ cmd. env ( "Path" , "bar" ) ;
1106+ let env = & cmd. env . unwrap ( ) ;
1107+ let val = env. find ( & EnvKey ( "PATH" . to_c_str ( ) ) ) ;
1108+ assert ! ( val. unwrap( ) == & "bar" . to_c_str( ) ) ;
1109+ }
10421110}
0 commit comments