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