@@ -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;
2223use crate :: sys:: fs:: { File , OpenOptions } ;
2324use crate :: sys:: handle:: Handle ;
2425use crate :: sys:: path;
2526use crate :: sys:: pipe:: { self , AnonPipe } ;
2627use 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,8 +269,13 @@ impl Command {
269269 None
270270 } ;
271271 let program = resolve_exe ( & self . program , || env:: var_os ( "PATH" ) , child_paths) ?;
272+ let is_batch_file = program
273+ . extension ( )
274+ . map ( |ext| ext. eq_ignore_ascii_case ( "cmd" ) || ext. eq_ignore_ascii_case ( "bat" ) )
275+ . unwrap_or ( false ) ;
276+ let program = path:: maybe_verbatim ( & program) ?;
272277 let mut cmd_str =
273- make_command_line ( program. as_os_str ( ) , & self . args , self . force_quotes_enabled ) ?;
278+ make_command_line ( & program, & self . args , self . force_quotes_enabled , is_batch_file ) ?;
274279 cmd_str. push ( 0 ) ; // add null terminator
275280
276281 // stolen from the libuv code.
@@ -309,7 +314,6 @@ impl Command {
309314 si. hStdOutput = stdout. as_raw_handle ( ) ;
310315 si. hStdError = stderr. as_raw_handle ( ) ;
311316
312- let program = to_u16s ( & program) ?;
313317 unsafe {
314318 cvt ( c:: CreateProcessW (
315319 program. as_ptr ( ) ,
@@ -730,7 +734,12 @@ enum Quote {
730734
731735// Produces a wide string *without terminating null*; returns an error if
732736// `prog` or any of the `args` contain a nul.
733- fn make_command_line ( prog : & OsStr , args : & [ Arg ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
737+ fn make_command_line (
738+ prog : & [ u16 ] ,
739+ args : & [ Arg ] ,
740+ force_quotes : bool ,
741+ is_batch_file : bool ,
742+ ) -> io:: Result < Vec < u16 > > {
734743 // Encode the command and arguments in a command line string such
735744 // that the spawned process may recover them using CommandLineToArgvW.
736745 let mut cmd: Vec < u16 > = Vec :: new ( ) ;
@@ -739,17 +748,18 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu
739748 // need to add an extra pair of quotes surrounding the whole command line
740749 // so they are properly passed on to the script.
741750 // 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 ) ;
746751 if is_batch_file {
747752 cmd. push ( b'"' as u16 ) ;
748753 }
749754
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 ) ?;
755+ // Always quote the program name so CreateProcess to avoid ambiguity when
756+ // the child process parses its arguments.
757+ // Note that quotes aren't escaped here because they can't be used in arg0.
758+ // But that's ok because file paths can't contain quotes.
759+ cmd. push ( b'"' as u16 ) ;
760+ cmd. extend_from_slice ( prog. strip_suffix ( & [ 0 ] ) . unwrap_or ( prog) ) ;
761+ cmd. push ( b'"' as u16 ) ;
762+
753763 for arg in args {
754764 cmd. push ( ' ' as u16 ) ;
755765 let ( arg, quote) = match arg {
0 commit comments