@@ -19,12 +19,12 @@ use crate::path::{Path, PathBuf};
1919use crate :: ptr;
2020use crate :: sys:: c;
2121use crate :: sys:: c:: NonZeroDWORD ;
22- use crate :: sys:: cvt;
2322use crate :: sys:: fs:: { File , OpenOptions } ;
2423use crate :: sys:: handle:: Handle ;
2524use crate :: sys:: path;
2625use crate :: sys:: pipe:: { self , AnonPipe } ;
2726use crate :: sys:: stdio;
27+ use crate :: sys:: { cvt, to_u16s} ;
2828use crate :: sys_common:: mutex:: StaticMutex ;
2929use crate :: sys_common:: process:: { CommandEnv , CommandEnvs } ;
3030use crate :: sys_common:: { AsInner , IntoInner } ;
@@ -269,13 +269,8 @@ impl Command {
269269 None
270270 } ;
271271 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- ) ;
277272 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 ) ?;
279274 cmd_str. push ( 0 ) ; // add null terminator
280275
281276 // stolen from the libuv code.
@@ -314,6 +309,7 @@ impl Command {
314309 si. hStdOutput = stdout. as_raw_handle ( ) ;
315310 si. hStdError = stderr. as_raw_handle ( ) ;
316311
312+ let program = to_u16s ( & program) ?;
317313 unsafe {
318314 cvt ( c:: CreateProcessW (
319315 program. as_ptr ( ) ,
@@ -370,7 +366,7 @@ fn resolve_exe<'a>(
370366 exe_path : & ' a OsStr ,
371367 parent_paths : impl FnOnce ( ) -> Option < OsString > ,
372368 child_paths : Option < & OsStr > ,
373- ) -> io:: Result < Vec < u16 > > {
369+ ) -> io:: Result < PathBuf > {
374370 // Early return if there is no filename.
375371 if exe_path. is_empty ( ) || path:: has_trailing_slash ( exe_path) {
376372 return Err ( io:: const_io_error!(
@@ -392,19 +388,19 @@ fn resolve_exe<'a>(
392388 if has_exe_suffix {
393389 // The application name is a path to a `.exe` file.
394390 // Let `CreateProcessW` figure out if it exists or not.
395- return path :: maybe_verbatim ( Path :: new ( exe_path) ) ;
391+ return Ok ( exe_path. into ( ) ) ;
396392 }
397393 let mut path = PathBuf :: from ( exe_path) ;
398394
399395 // Append `.exe` if not already there.
400396 path = path:: append_suffix ( path, EXE_SUFFIX . as_ref ( ) ) ;
401- if let Some ( path ) = program_exists ( & path) {
397+ if program_exists ( & path) {
402398 return Ok ( path) ;
403399 } else {
404400 // It's ok to use `set_extension` here because the intent is to
405401 // remove the extension that was just added.
406402 path. set_extension ( "" ) ;
407- return path :: maybe_verbatim ( & path) ;
403+ return Ok ( path) ;
408404 }
409405 } else {
410406 ensure_no_nuls ( exe_path) ?;
@@ -419,7 +415,7 @@ fn resolve_exe<'a>(
419415 if !has_extension {
420416 path. set_extension ( EXE_EXTENSION ) ;
421417 }
422- program_exists ( & path)
418+ if program_exists ( & path) { Some ( path ) } else { None }
423419 } ) ;
424420 if let Some ( path) = result {
425421 return Ok ( path) ;
@@ -435,10 +431,10 @@ fn search_paths<Paths, Exists>(
435431 parent_paths : Paths ,
436432 child_paths : Option < & OsStr > ,
437433 mut exists : Exists ,
438- ) -> Option < Vec < u16 > >
434+ ) -> Option < PathBuf >
439435where
440436 Paths : FnOnce ( ) -> Option < OsString > ,
441- Exists : FnMut ( PathBuf ) -> Option < Vec < u16 > > ,
437+ Exists : FnMut ( PathBuf ) -> Option < PathBuf > ,
442438{
443439 // 1. Child paths
444440 // This is for consistency with Rust's historic behaviour.
@@ -490,18 +486,17 @@ where
490486}
491487
492488/// Check if a file exists without following symlinks.
493- fn program_exists ( path : & Path ) -> Option < Vec < u16 > > {
489+ fn program_exists ( path : & Path ) -> bool {
494490 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 )
505500 }
506501}
507502
@@ -735,12 +730,7 @@ enum Quote {
735730
736731// Produces a wide string *without terminating null*; returns an error if
737732// `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 > > {
744734 // Encode the command and arguments in a command line string such
745735 // that the spawned process may recover them using CommandLineToArgvW.
746736 let mut cmd: Vec < u16 > = Vec :: new ( ) ;
@@ -749,18 +739,17 @@ fn make_command_line(
749739 // need to add an extra pair of quotes surrounding the whole command line
750740 // so they are properly passed on to the script.
751741 // 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 ) ;
752746 if is_batch_file {
753747 cmd. push ( b'"' as u16 ) ;
754748 }
755749
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 ) ?;
764753 for arg in args {
765754 cmd. push ( ' ' as u16 ) ;
766755 let ( arg, quote) = match arg {
0 commit comments