@@ -17,6 +17,7 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt};
1717use crate :: os:: windows:: io:: { AsRawHandle , FromRawHandle , IntoRawHandle } ;
1818use crate :: path:: { Path , PathBuf } ;
1919use crate :: ptr;
20+ use crate :: sys:: args:: { self , Arg } ;
2021use crate :: sys:: c;
2122use crate :: sys:: c:: NonZeroDWORD ;
2223use crate :: sys:: cvt;
@@ -27,7 +28,7 @@ use crate::sys::pipe::{self, AnonPipe};
2728use crate :: sys:: stdio;
2829use crate :: sys_common:: mutex:: StaticMutex ;
2930use crate :: sys_common:: process:: { CommandEnv , CommandEnvs } ;
30- use crate :: sys_common:: { AsInner , IntoInner } ;
31+ use crate :: sys_common:: IntoInner ;
3132
3233use libc:: { c_void, EXIT_FAILURE , EXIT_SUCCESS } ;
3334
@@ -147,7 +148,7 @@ impl AsRef<OsStr> for EnvKey {
147148 }
148149}
149150
150- fn ensure_no_nuls < T : AsRef < OsStr > > ( str : T ) -> io:: Result < T > {
151+ pub ( crate ) fn ensure_no_nuls < T : AsRef < OsStr > > ( str : T ) -> io:: Result < T > {
151152 if str. as_ref ( ) . encode_wide ( ) . any ( |b| b == 0 ) {
152153 Err ( io:: const_io_error!( ErrorKind :: InvalidInput , "nul byte found in provided data" ) )
153154 } else {
@@ -182,14 +183,6 @@ pub struct StdioPipes {
182183 pub stderr : Option < AnonPipe > ,
183184}
184185
185- #[ derive( Debug ) ]
186- enum Arg {
187- /// Add quotes (if needed)
188- Regular ( OsString ) ,
189- /// Append raw string without quoting
190- Raw ( OsString ) ,
191- }
192-
193186impl Command {
194187 pub fn new ( program : & OsStr ) -> Command {
195188 Command {
@@ -275,8 +268,19 @@ impl Command {
275268 program. len( ) . checked_sub( 5 ) . and_then( |i| program. get( i..) ) ,
276269 Some ( [ 46 , 98 | 66 , 97 | 65 , 116 | 84 , 0 ] | [ 46 , 99 | 67 , 109 | 77 , 100 | 68 , 0 ] )
277270 ) ;
278- let mut cmd_str =
279- make_command_line ( & program, & self . args , self . force_quotes_enabled , is_batch_file) ?;
271+ let ( program, mut cmd_str) = if is_batch_file {
272+ (
273+ command_prompt ( ) ?,
274+ args:: make_bat_command_line (
275+ & args:: to_user_path ( program) ?,
276+ & self . args ,
277+ self . force_quotes_enabled ,
278+ ) ?,
279+ )
280+ } else {
281+ let cmd_str = make_command_line ( & self . program , & self . args , self . force_quotes_enabled ) ?;
282+ ( program, cmd_str)
283+ } ;
280284 cmd_str. push ( 0 ) ; // add null terminator
281285
282286 // stolen from the libuv code.
@@ -730,96 +734,36 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
730734 }
731735}
732736
733- enum Quote {
734- // Every arg is quoted
735- Always ,
736- // Whitespace and empty args are quoted
737- Auto ,
738- // Arg appended without any changes (#29494)
739- Never ,
740- }
741-
742737// Produces a wide string *without terminating null*; returns an error if
743738// `prog` or any of the `args` contain a nul.
744- fn make_command_line (
745- prog : & [ u16 ] ,
746- args : & [ Arg ] ,
747- force_quotes : bool ,
748- is_batch_file : bool ,
749- ) -> io:: Result < Vec < u16 > > {
739+ fn make_command_line ( argv0 : & OsStr , args : & [ Arg ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
750740 // Encode the command and arguments in a command line string such
751741 // that the spawned process may recover them using CommandLineToArgvW.
752742 let mut cmd: Vec < u16 > = Vec :: new ( ) ;
753743
754- // CreateFileW has special handling for .bat and .cmd files, which means we
755- // need to add an extra pair of quotes surrounding the whole command line
756- // so they are properly passed on to the script.
757- // See issue #91991.
758- if is_batch_file {
759- cmd. push ( b'"' as u16 ) ;
760- }
761-
762744 // Always quote the program name so CreateProcess to avoid ambiguity when
763745 // the child process parses its arguments.
764746 // Note that quotes aren't escaped here because they can't be used in arg0.
765747 // But that's ok because file paths can't contain quotes.
766748 cmd. push ( b'"' as u16 ) ;
767- cmd. extend_from_slice ( prog . strip_suffix ( & [ 0 ] ) . unwrap_or ( prog ) ) ;
749+ cmd. extend ( argv0 . encode_wide ( ) ) ;
768750 cmd. push ( b'"' as u16 ) ;
769751
770752 for arg in args {
771753 cmd. push ( ' ' as u16 ) ;
772- let ( arg, quote) = match arg {
773- Arg :: Regular ( arg) => ( arg, if force_quotes { Quote :: Always } else { Quote :: Auto } ) ,
774- Arg :: Raw ( arg) => ( arg, Quote :: Never ) ,
775- } ;
776- append_arg ( & mut cmd, arg, quote) ?;
777- }
778- if is_batch_file {
779- cmd. push ( b'"' as u16 ) ;
780- }
781- return Ok ( cmd) ;
782-
783- fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , quote : Quote ) -> io:: Result < ( ) > {
784- // If an argument has 0 characters then we need to quote it to ensure
785- // that it actually gets passed through on the command line or otherwise
786- // it will be dropped entirely when parsed on the other end.
787- ensure_no_nuls ( arg) ?;
788- let arg_bytes = & arg. as_inner ( ) . inner . as_inner ( ) ;
789- let ( quote, escape) = match quote {
790- Quote :: Always => ( true , true ) ,
791- Quote :: Auto => {
792- ( arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' ) || arg_bytes. is_empty ( ) , true )
793- }
794- Quote :: Never => ( false , false ) ,
795- } ;
796- if quote {
797- cmd. push ( '"' as u16 ) ;
798- }
799-
800- let mut backslashes: usize = 0 ;
801- for x in arg. encode_wide ( ) {
802- if escape {
803- if x == '\\' as u16 {
804- backslashes += 1 ;
805- } else {
806- if x == '"' as u16 {
807- // Add n+1 backslashes to total 2n+1 before internal '"'.
808- cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
809- }
810- backslashes = 0 ;
811- }
812- }
813- cmd. push ( x) ;
814- }
815-
816- if quote {
817- // Add n backslashes to total 2n before ending '"'.
818- cmd. extend ( ( 0 ..backslashes) . map ( |_| '\\' as u16 ) ) ;
819- cmd. push ( '"' as u16 ) ;
820- }
821- Ok ( ( ) )
754+ args:: append_arg ( & mut cmd, arg, force_quotes) ?;
822755 }
756+ Ok ( cmd)
757+ }
758+
759+ // Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
760+ fn command_prompt ( ) -> io:: Result < Vec < u16 > > {
761+ let mut system: Vec < u16 > = super :: fill_utf16_buf (
762+ |buf, size| unsafe { c:: GetSystemDirectoryW ( buf, size) } ,
763+ |buf| buf. into ( ) ,
764+ ) ?;
765+ system. extend ( "\\ cmd.exe" . encode_utf16 ( ) . chain ( [ 0 ] ) ) ;
766+ Ok ( system)
823767}
824768
825769fn make_envp ( maybe_env : Option < BTreeMap < EnvKey , OsString > > ) -> io:: Result < ( * mut c_void , Vec < u16 > ) > {
0 commit comments