@@ -24,6 +24,9 @@ use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
24
24
use rt:: rtio;
25
25
use c_str:: CString ;
26
26
use collections:: HashMap ;
27
+ use std:: hash:: Hash ;
28
+ use std:: hash:: sip:: SipState ;
29
+ use clone:: Clone ;
27
30
28
31
/// Signal a process to exit, without forcibly killing it. Corresponds to
29
32
/// SIGTERM on unix platforms.
@@ -78,8 +81,56 @@ pub struct Process {
78
81
pub extra_io : Vec < Option < io:: PipeStream > > ,
79
82
}
80
83
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
+
81
132
/// A HashMap representation of environment variables.
82
- pub type EnvMap = HashMap < CString , CString > ;
133
+ pub type EnvMap = HashMap < EnvKey , CString > ;
83
134
84
135
/// The `Command` type acts as a process builder, providing fine-grained control
85
136
/// over how a new process should be spawned. A default configuration can be
@@ -161,14 +212,14 @@ impl Command {
161
212
self
162
213
}
163
214
// 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 {
165
216
match self . env {
166
217
Some ( ref mut map) => map,
167
218
None => {
168
219
// if the env is currently just inheriting from the parent's,
169
220
// materialize the parent's env into a hashtable.
170
221
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 ( ) ) ,
172
223
v. as_slice ( ) . to_c_str ( ) ) )
173
224
. collect ( ) ) ;
174
225
self . env . as_mut ( ) . unwrap ( )
@@ -177,15 +228,18 @@ impl Command {
177
228
}
178
229
179
230
/// 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.
180
234
pub fn env < ' a , T : ToCStr , U : ToCStr > ( & ' a mut self , key : T , val : U )
181
235
-> & ' 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 ( ) ) ;
183
237
self
184
238
}
185
239
186
240
/// Removes an environment variable mapping.
187
241
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 ( ) ) ) ;
189
243
self
190
244
}
191
245
@@ -195,7 +249,7 @@ impl Command {
195
249
/// variable, the *rightmost* instance will determine the value.
196
250
pub fn env_set_all < ' a , T : ToCStr , U : ToCStr > ( & ' a mut self , env : & [ ( T , U ) ] )
197
251
-> & ' 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 ( ) ) )
199
253
. collect ( ) ) ;
200
254
self
201
255
}
@@ -273,7 +327,9 @@ impl Command {
273
327
let env = match self . env {
274
328
None => None ,
275
329
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 < _ > > ( ) )
277
333
} ;
278
334
let cfg = ProcessConfig {
279
335
program : & self . program ,
@@ -1039,4 +1095,16 @@ mod tests {
1039
1095
assert!( cmd. status( ) . unwrap( ) . success( ) ) ;
1040
1096
assert!( fdes. inner_write( "extra write\n " . as_bytes( ) ) . is_ok( ) ) ;
1041
1097
} )
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
+ }
1042
1110
}
0 commit comments