@@ -24,6 +24,10 @@ use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
2424use rt:: rtio;
2525use c_str:: CString ;
2626use 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