@@ -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;
22
23
use crate :: sys:: fs:: { File , OpenOptions } ;
23
24
use crate :: sys:: handle:: Handle ;
24
25
use crate :: sys:: path;
25
26
use crate :: sys:: pipe:: { self , AnonPipe } ;
26
27
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,8 +269,13 @@ impl Command {
269
269
None
270
270
} ;
271
271
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) ?;
272
277
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 ) ?;
274
279
cmd_str. push ( 0 ) ; // add null terminator
275
280
276
281
// stolen from the libuv code.
@@ -309,7 +314,6 @@ impl Command {
309
314
si. hStdOutput = stdout. as_raw_handle ( ) ;
310
315
si. hStdError = stderr. as_raw_handle ( ) ;
311
316
312
- let program = to_u16s ( & program) ?;
313
317
unsafe {
314
318
cvt ( c:: CreateProcessW (
315
319
program. as_ptr ( ) ,
@@ -730,7 +734,12 @@ enum Quote {
730
734
731
735
// Produces a wide string *without terminating null*; returns an error if
732
736
// `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 > > {
734
743
// Encode the command and arguments in a command line string such
735
744
// that the spawned process may recover them using CommandLineToArgvW.
736
745
let mut cmd: Vec < u16 > = Vec :: new ( ) ;
@@ -739,17 +748,18 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu
739
748
// need to add an extra pair of quotes surrounding the whole command line
740
749
// so they are properly passed on to the script.
741
750
// 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 ) ;
746
751
if is_batch_file {
747
752
cmd. push ( b'"' as u16 ) ;
748
753
}
749
754
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
+
753
763
for arg in args {
754
764
cmd. push ( ' ' as u16 ) ;
755
765
let ( arg, quote) = match arg {
0 commit comments