@@ -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