@@ -144,7 +144,7 @@ pub fn do_add_to_path() -> Result<()> {
144144 _apply_new_path ( new_path)
145145}
146146
147- fn _apply_new_path ( new_path : Option < String > ) -> Result < ( ) > {
147+ fn _apply_new_path ( new_path : Option < Vec < u16 > > ) -> Result < ( ) > {
148148 use std:: ptr;
149149 use winapi:: shared:: minwindef:: * ;
150150 use winapi:: um:: winuser:: {
@@ -169,7 +169,7 @@ fn _apply_new_path(new_path: Option<String>) -> Result<()> {
169169 . chain_err ( || ErrorKind :: PermissionDenied ) ?;
170170 } else {
171171 let reg_value = RegValue {
172- bytes : utils:: string_to_winreg_bytes ( & new_path) ,
172+ bytes : utils:: string_to_winreg_bytes ( new_path) ,
173173 vtype : RegType :: REG_EXPAND_SZ ,
174174 } ;
175175 environment
@@ -194,9 +194,9 @@ fn _apply_new_path(new_path: Option<String>) -> Result<()> {
194194}
195195
196196// Get the windows PATH variable out of the registry as a String. If
197- // this returns None then the PATH variable is not unicode and we
197+ // this returns None then the PATH variable is not a string and we
198198// should not mess with it.
199- fn get_windows_path_var ( ) -> Result < Option < String > > {
199+ fn get_windows_path_var ( ) -> Result < Option < Vec < u16 > > > {
200200 use std:: io;
201201 use winreg:: enums:: { HKEY_CURRENT_USER , KEY_READ , KEY_WRITE } ;
202202 use winreg:: RegKey ;
@@ -212,63 +212,72 @@ fn get_windows_path_var() -> Result<Option<String>> {
212212 if let Some ( s) = utils:: string_from_winreg_value ( & val) {
213213 Ok ( Some ( s) )
214214 } else {
215- warn ! ( "the registry key HKEY_CURRENT_USER\\ Environment\\ PATH does not contain valid Unicode. \
216- Not modifying the PATH variable") ;
215+ warn ! (
216+ "the registry key HKEY_CURRENT_USER\\ Environment\\ PATH is not a string. \
217+ Not modifying the PATH variable"
218+ ) ;
217219 Ok ( None )
218220 }
219221 }
220- Err ( ref e) if e. kind ( ) == io:: ErrorKind :: NotFound => Ok ( Some ( String :: new ( ) ) ) ,
222+ Err ( ref e) if e. kind ( ) == io:: ErrorKind :: NotFound => Ok ( Some ( Vec :: new ( ) ) ) ,
221223 Err ( e) => Err ( e) . chain_err ( || ErrorKind :: WindowsUninstallMadness ) ,
222224 }
223225}
224226
225227// Returns None if the existing old_path does not need changing, otherwise
226228// prepends the path_str to old_path, handling empty old_path appropriately.
227- fn _add_to_path ( old_path : & str , path_str : String ) -> Option < String > {
229+ fn _add_to_path ( old_path : Vec < u16 > , path_str : Vec < u16 > ) -> Option < Vec < u16 > > {
228230 if old_path. is_empty ( ) {
229231 Some ( path_str)
230- } else if old_path. contains ( & path_str) {
232+ } else if old_path
233+ . windows ( path_str. len ( ) )
234+ . any ( |path| path == path_str)
235+ {
231236 None
232237 } else {
233238 let mut new_path = path_str;
234- new_path. push_str ( ";" ) ;
235- new_path. push_str ( & old_path) ;
239+ new_path. push ( b';' as u16 ) ;
240+ new_path. extend_from_slice ( & old_path) ;
236241 Some ( new_path)
237242 }
238243}
239244
240245// Returns None if the existing old_path does not need changing
241- fn _remove_from_path ( old_path : & str , path_str : String ) -> Option < String > {
242- let idx = old_path. find ( & path_str) ?;
246+ fn _remove_from_path ( old_path : Vec < u16 > , path_str : Vec < u16 > ) -> Option < Vec < u16 > > {
247+ let idx = old_path
248+ . windows ( path_str. len ( ) )
249+ . position ( |path| path == path_str) ?;
243250 // If there's a trailing semicolon (likely, since we probably added one
244251 // during install), include that in the substring to remove. We don't search
245252 // for that to find the string, because if its the last string in the path,
246253 // there may not be.
247254 let mut len = path_str. len ( ) ;
248- if old_path. as_bytes ( ) . get ( idx + path_str. len ( ) ) == Some ( & b';' ) {
255+ if old_path. get ( idx + path_str. len ( ) ) == Some ( & ( b';' as u16 ) ) {
249256 len += 1 ;
250257 }
251258
252- let mut new_path = old_path[ ..idx] . to_string ( ) ;
253- new_path. push_str ( & old_path[ idx + len..] ) ;
259+ let mut new_path = old_path[ ..idx] . to_owned ( ) ;
260+ new_path. extend_from_slice ( & old_path[ idx + len..] ) ;
254261 // Don't leave a trailing ; though, we don't want an empty string in the
255262 // path.
256- if new_path. ends_with ( ';' ) {
263+ if new_path. last ( ) == Some ( & ( b ';' as u16 ) ) {
257264 new_path. pop ( ) ;
258265 }
259266 Some ( new_path)
260267}
261268
262- fn _with_path_cargo_home_bin < F > ( f : F ) -> Result < Option < String > >
269+ fn _with_path_cargo_home_bin < F > ( f : F ) -> Result < Option < Vec < u16 > > >
263270where
264- F : FnOnce ( & str , String ) -> Option < String > ,
271+ F : FnOnce ( Vec < u16 > , Vec < u16 > ) -> Option < Vec < u16 > > ,
265272{
273+ use std:: ffi:: OsString ;
274+ use std:: os:: windows:: ffi:: OsStrExt ;
275+
266276 let windows_path = get_windows_path_var ( ) ?;
267- let path_str = utils:: cargo_home ( ) ?
268- . join ( "bin" )
269- . to_string_lossy ( )
270- . into_owned ( ) ;
271- Ok ( windows_path. and_then ( |old_path| f ( & old_path, path_str) ) )
277+ let mut path_str = utils:: cargo_home ( ) ?;
278+ path_str. push ( "bin" ) ;
279+ Ok ( windows_path
280+ . and_then ( |old_path| f ( old_path, OsString :: from ( path_str) . encode_wide ( ) . collect ( ) ) ) )
272281}
273282
274283pub fn do_remove_from_path ( ) -> Result < ( ) > {
@@ -402,26 +411,33 @@ pub fn delete_rustup_and_cargo_home() -> Result<()> {
402411
403412#[ cfg( test) ]
404413mod tests {
414+ use std:: ffi:: OsString ;
415+ use std:: os:: windows:: ffi:: OsStrExt ;
416+
405417 use winreg:: enums:: { RegType , HKEY_CURRENT_USER , KEY_READ , KEY_WRITE } ;
406418 use winreg:: { RegKey , RegValue } ;
407419
408420 use crate :: currentprocess;
409421 use crate :: test:: with_saved_path;
410422 use crate :: utils:: utils;
411423
424+ fn wide ( str : & str ) -> Vec < u16 > {
425+ OsString :: from ( str) . encode_wide ( ) . collect ( )
426+ }
427+
412428 #[ test]
413429 fn windows_install_does_not_add_path_twice ( ) {
414430 assert_eq ! (
415431 None ,
416432 super :: _add_to_path(
417- r"c:\users\example\.cargo\bin;foo" ,
418- r"c:\users\example\.cargo\bin" . into ( )
433+ wide ( r"c:\users\example\.cargo\bin;foo" ) ,
434+ wide ( r"c:\users\example\.cargo\bin" )
419435 )
420436 ) ;
421437 }
422438
423439 #[ test]
424- fn windows_doesnt_mess_with_a_non_unicode_path ( ) {
440+ fn windows_doesnt_mess_with_a_non_string_path ( ) {
425441 // This writes an error, so we want a sink for it.
426442 let tp = Box :: new ( currentprocess:: TestProcess {
427443 vars : [ ( "HOME" . to_string ( ) , "/unused" . to_string ( ) ) ]
@@ -437,23 +453,19 @@ mod tests {
437453 . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
438454 . unwrap ( ) ;
439455 let reg_value = RegValue {
440- bytes : vec ! [
441- 0x00 , 0xD8 , // leading surrogate
442- 0x01 , 0x01 , // bogus trailing surrogate
443- 0x00 , 0x00 ,
444- ] , // null
445- vtype : RegType :: REG_EXPAND_SZ ,
456+ bytes : vec ! [ 0x00 , 0xD8 , 0x01 , 0x01 , 0x00 , 0x00 ] ,
457+ vtype : RegType :: REG_BINARY ,
446458 } ;
447459 environment. set_raw_value ( "PATH" , & reg_value) . unwrap ( ) ;
448460 // Ok(None) signals no change to the PATH setting layer
449- fn panic ( _ : & str , _ : String ) -> Option < String > {
450- panic ! ( "called" ) ;
451- }
452- assert_eq ! ( None , super :: _with_path_cargo_home_bin ( panic ) . unwrap ( ) ) ;
461+ assert_eq ! (
462+ None ,
463+ super :: _with_path_cargo_home_bin ( |_ , _| panic! ( "called" ) ) . unwrap ( )
464+ ) ;
453465 } )
454466 } ) ;
455467 assert_eq ! (
456- r"warning: the registry key HKEY_CURRENT_USER\Environment\PATH does not contain valid Unicode . Not modifying the PATH variable
468+ r"warning: the registry key HKEY_CURRENT_USER\Environment\PATH is not a string . Not modifying the PATH variable
457469" ,
458470 String :: from_utf8( tp. get_stderr( ) ) . unwrap( )
459471 ) ;
@@ -474,15 +486,15 @@ mod tests {
474486 {
475487 // Can't compare the Results as Eq isn't derived; thanks error-chain.
476488 #![ allow( clippy:: unit_cmp) ]
477- assert_eq ! ( ( ) , super :: _apply_new_path( Some ( "foo" . into ( ) ) ) . unwrap( ) ) ;
489+ assert_eq ! ( ( ) , super :: _apply_new_path( Some ( wide ( "foo" ) ) ) . unwrap( ) ) ;
478490 }
479491 let root = RegKey :: predef ( HKEY_CURRENT_USER ) ;
480492 let environment = root
481493 . open_subkey_with_flags ( "Environment" , KEY_READ | KEY_WRITE )
482494 . unwrap ( ) ;
483495 let path = environment. get_raw_value ( "PATH" ) . unwrap ( ) ;
484496 assert_eq ! ( path. vtype, RegType :: REG_EXPAND_SZ ) ;
485- assert_eq ! ( utils:: string_to_winreg_bytes( "foo" ) , & path. bytes[ ..] ) ;
497+ assert_eq ! ( utils:: string_to_winreg_bytes( wide ( "foo" ) ) , & path. bytes[ ..] ) ;
486498 } )
487499 } ) ;
488500 }
@@ -503,7 +515,7 @@ mod tests {
503515 . set_raw_value (
504516 "PATH" ,
505517 & RegValue {
506- bytes : utils:: string_to_winreg_bytes ( "foo" ) ,
518+ bytes : utils:: string_to_winreg_bytes ( wide ( "foo" ) ) ,
507519 vtype : RegType :: REG_EXPAND_SZ ,
508520 } ,
509521 )
@@ -512,7 +524,7 @@ mod tests {
512524 {
513525 // Can't compare the Results as Eq isn't derived; thanks error-chain.
514526 #![ allow( clippy:: unit_cmp) ]
515- assert_eq ! ( ( ) , super :: _apply_new_path( Some ( "" . into ( ) ) ) . unwrap( ) ) ;
527+ assert_eq ! ( ( ) , super :: _apply_new_path( Some ( Vec :: new ( ) ) ) . unwrap( ) ) ;
516528 }
517529 let reg_value = environment. get_raw_value ( "PATH" ) ;
518530 match reg_value {
@@ -536,18 +548,18 @@ mod tests {
536548 . unwrap ( ) ;
537549 environment. delete_value ( "PATH" ) . unwrap ( ) ;
538550
539- assert_eq ! ( Some ( "" . into ( ) ) , super :: get_windows_path_var( ) . unwrap( ) ) ;
551+ assert_eq ! ( Some ( Vec :: new ( ) ) , super :: get_windows_path_var( ) . unwrap( ) ) ;
540552 } )
541553 } ) ;
542554 }
543555
544556 #[ test]
545557 fn windows_uninstall_removes_semicolon_from_path_prefix ( ) {
546558 assert_eq ! (
547- "foo" ,
559+ wide ( "foo" ) ,
548560 super :: _remove_from_path(
549- r"c:\users\example\.cargo\bin;foo" ,
550- r"c:\users\example\.cargo\bin" . into ( )
561+ wide ( r"c:\users\example\.cargo\bin;foo" ) ,
562+ wide ( r"c:\users\example\.cargo\bin" ) ,
551563 )
552564 . unwrap( )
553565 )
@@ -556,10 +568,10 @@ mod tests {
556568 #[ test]
557569 fn windows_uninstall_removes_semicolon_from_path_suffix ( ) {
558570 assert_eq ! (
559- "foo" ,
571+ wide ( "foo" ) ,
560572 super :: _remove_from_path(
561- r"foo;c:\users\example\.cargo\bin" ,
562- r"c:\users\example\.cargo\bin" . into ( )
573+ wide ( r"foo;c:\users\example\.cargo\bin" ) ,
574+ wide ( r"c:\users\example\.cargo\bin" ) ,
563575 )
564576 . unwrap( )
565577 )
0 commit comments