@@ -19,12 +19,12 @@ use crate::path::{Path, PathBuf};
19
19
use crate :: ptr;
20
20
use crate :: sys:: c;
21
21
use crate :: sys:: c:: NonZeroDWORD ;
22
- use crate :: sys:: cvt;
23
22
use crate :: sys:: fs:: { File , OpenOptions } ;
24
23
use crate :: sys:: handle:: Handle ;
25
24
use crate :: sys:: path;
26
25
use crate :: sys:: pipe:: { self , AnonPipe } ;
27
26
use crate :: sys:: stdio;
27
+ use crate :: sys:: { cvt, to_u16s} ;
28
28
use crate :: sys_common:: mutex:: StaticMutex ;
29
29
use crate :: sys_common:: process:: { CommandEnv , CommandEnvs } ;
30
30
use crate :: sys_common:: { AsInner , IntoInner } ;
@@ -269,13 +269,8 @@ impl Command {
269
269
None
270
270
} ;
271
271
let program = resolve_exe ( & self . program , || env:: var_os ( "PATH" ) , child_paths) ?;
272
- // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd"
273
- let is_batch_file = matches ! (
274
- program. len( ) . checked_sub( 5 ) . and_then( |i| program. get( i..) ) ,
275
- Some ( [ 46 , 98 | 66 , 97 | 65 , 116 | 84 , 0 ] | [ 46 , 99 | 67 , 109 | 77 , 100 | 68 , 0 ] )
276
- ) ;
277
272
let mut cmd_str =
278
- make_command_line ( & program, & self . args , self . force_quotes_enabled , is_batch_file ) ?;
273
+ make_command_line ( program. as_os_str ( ) , & self . args , self . force_quotes_enabled ) ?;
279
274
cmd_str. push ( 0 ) ; // add null terminator
280
275
281
276
// stolen from the libuv code.
@@ -314,6 +309,7 @@ impl Command {
314
309
si. hStdOutput = stdout. as_raw_handle ( ) ;
315
310
si. hStdError = stderr. as_raw_handle ( ) ;
316
311
312
+ let program = to_u16s ( & program) ?;
317
313
unsafe {
318
314
cvt ( c:: CreateProcessW (
319
315
program. as_ptr ( ) ,
@@ -370,7 +366,7 @@ fn resolve_exe<'a>(
370
366
exe_path : & ' a OsStr ,
371
367
parent_paths : impl FnOnce ( ) -> Option < OsString > ,
372
368
child_paths : Option < & OsStr > ,
373
- ) -> io:: Result < Vec < u16 > > {
369
+ ) -> io:: Result < PathBuf > {
374
370
// Early return if there is no filename.
375
371
if exe_path. is_empty ( ) || path:: has_trailing_slash ( exe_path) {
376
372
return Err ( io:: const_io_error!(
@@ -392,19 +388,19 @@ fn resolve_exe<'a>(
392
388
if has_exe_suffix {
393
389
// The application name is a path to a `.exe` file.
394
390
// Let `CreateProcessW` figure out if it exists or not.
395
- return path :: maybe_verbatim ( Path :: new ( exe_path) ) ;
391
+ return Ok ( exe_path. into ( ) ) ;
396
392
}
397
393
let mut path = PathBuf :: from ( exe_path) ;
398
394
399
395
// Append `.exe` if not already there.
400
396
path = path:: append_suffix ( path, EXE_SUFFIX . as_ref ( ) ) ;
401
- if let Some ( path ) = program_exists ( & path) {
397
+ if program_exists ( & path) {
402
398
return Ok ( path) ;
403
399
} else {
404
400
// It's ok to use `set_extension` here because the intent is to
405
401
// remove the extension that was just added.
406
402
path. set_extension ( "" ) ;
407
- return path :: maybe_verbatim ( & path) ;
403
+ return Ok ( path) ;
408
404
}
409
405
} else {
410
406
ensure_no_nuls ( exe_path) ?;
@@ -419,7 +415,7 @@ fn resolve_exe<'a>(
419
415
if !has_extension {
420
416
path. set_extension ( EXE_EXTENSION ) ;
421
417
}
422
- program_exists ( & path)
418
+ if program_exists ( & path) { Some ( path ) } else { None }
423
419
} ) ;
424
420
if let Some ( path) = result {
425
421
return Ok ( path) ;
@@ -435,10 +431,10 @@ fn search_paths<Paths, Exists>(
435
431
parent_paths : Paths ,
436
432
child_paths : Option < & OsStr > ,
437
433
mut exists : Exists ,
438
- ) -> Option < Vec < u16 > >
434
+ ) -> Option < PathBuf >
439
435
where
440
436
Paths : FnOnce ( ) -> Option < OsString > ,
441
- Exists : FnMut ( PathBuf ) -> Option < Vec < u16 > > ,
437
+ Exists : FnMut ( PathBuf ) -> Option < PathBuf > ,
442
438
{
443
439
// 1. Child paths
444
440
// This is for consistency with Rust's historic behaviour.
@@ -490,18 +486,17 @@ where
490
486
}
491
487
492
488
/// Check if a file exists without following symlinks.
493
- fn program_exists ( path : & Path ) -> Option < Vec < u16 > > {
489
+ fn program_exists ( path : & Path ) -> bool {
494
490
unsafe {
495
- let path = path:: maybe_verbatim ( path) . ok ( ) ?;
496
- // Getting attributes using `GetFileAttributesW` does not follow symlinks
497
- // and it will almost always be successful if the link exists.
498
- // There are some exceptions for special system files (e.g. the pagefile)
499
- // but these are not executable.
500
- if c:: GetFileAttributesW ( path. as_ptr ( ) ) == c:: INVALID_FILE_ATTRIBUTES {
501
- None
502
- } else {
503
- Some ( path)
504
- }
491
+ to_u16s ( path)
492
+ . map ( |path| {
493
+ // Getting attributes using `GetFileAttributesW` does not follow symlinks
494
+ // and it will almost always be successful if the link exists.
495
+ // There are some exceptions for special system files (e.g. the pagefile)
496
+ // but these are not executable.
497
+ c:: GetFileAttributesW ( path. as_ptr ( ) ) != c:: INVALID_FILE_ATTRIBUTES
498
+ } )
499
+ . unwrap_or ( false )
505
500
}
506
501
}
507
502
@@ -735,12 +730,7 @@ enum Quote {
735
730
736
731
// Produces a wide string *without terminating null*; returns an error if
737
732
// `prog` or any of the `args` contain a nul.
738
- fn make_command_line (
739
- prog : & [ u16 ] ,
740
- args : & [ Arg ] ,
741
- force_quotes : bool ,
742
- is_batch_file : bool ,
743
- ) -> io:: Result < Vec < u16 > > {
733
+ fn make_command_line ( prog : & OsStr , args : & [ Arg ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
744
734
// Encode the command and arguments in a command line string such
745
735
// that the spawned process may recover them using CommandLineToArgvW.
746
736
let mut cmd: Vec < u16 > = Vec :: new ( ) ;
@@ -749,18 +739,17 @@ fn make_command_line(
749
739
// need to add an extra pair of quotes surrounding the whole command line
750
740
// so they are properly passed on to the script.
751
741
// See issue #91991.
742
+ let is_batch_file = Path :: new ( prog)
743
+ . extension ( )
744
+ . map ( |ext| ext. eq_ignore_ascii_case ( "cmd" ) || ext. eq_ignore_ascii_case ( "bat" ) )
745
+ . unwrap_or ( false ) ;
752
746
if is_batch_file {
753
747
cmd. push ( b'"' as u16 ) ;
754
748
}
755
749
756
- // Always quote the program name so CreateProcess to avoid ambiguity when
757
- // the child process parses its arguments.
758
- // Note that quotes aren't escaped here because they can't be used in arg0.
759
- // But that's ok because file paths can't contain quotes.
760
- cmd. push ( b'"' as u16 ) ;
761
- cmd. extend_from_slice ( prog. strip_suffix ( & [ 0 ] ) . unwrap_or ( prog) ) ;
762
- cmd. push ( b'"' as u16 ) ;
763
-
750
+ // Always quote the program name so CreateProcess doesn't interpret args as
751
+ // part of the name if the binary wasn't found first time.
752
+ append_arg ( & mut cmd, prog, Quote :: Always ) ?;
764
753
for arg in args {
765
754
cmd. push ( ' ' as u16 ) ;
766
755
let ( arg, quote) = match arg {
0 commit comments