@@ -17,6 +17,7 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt};
17
17
use crate :: os:: windows:: io:: { AsRawHandle , FromRawHandle , IntoRawHandle } ;
18
18
use crate :: path:: { Path , PathBuf } ;
19
19
use crate :: ptr;
20
+ use crate :: sys:: args:: { self , Arg } ;
20
21
use crate :: sys:: c;
21
22
use crate :: sys:: c:: NonZeroDWORD ;
22
23
use crate :: sys:: cvt;
@@ -27,7 +28,7 @@ use crate::sys::pipe::{self, AnonPipe};
27
28
use crate :: sys:: stdio;
28
29
use crate :: sys_common:: mutex:: StaticMutex ;
29
30
use crate :: sys_common:: process:: { CommandEnv , CommandEnvs } ;
30
- use crate :: sys_common:: { AsInner , IntoInner } ;
31
+ use crate :: sys_common:: IntoInner ;
31
32
32
33
use libc:: { c_void, EXIT_FAILURE , EXIT_SUCCESS } ;
33
34
@@ -147,7 +148,7 @@ impl AsRef<OsStr> for EnvKey {
147
148
}
148
149
}
149
150
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 > {
151
152
if str. as_ref ( ) . encode_wide ( ) . any ( |b| b == 0 ) {
152
153
Err ( io:: const_io_error!( ErrorKind :: InvalidInput , "nul byte found in provided data" ) )
153
154
} else {
@@ -182,14 +183,6 @@ pub struct StdioPipes {
182
183
pub stderr : Option < AnonPipe > ,
183
184
}
184
185
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
-
193
186
impl Command {
194
187
pub fn new ( program : & OsStr ) -> Command {
195
188
Command {
@@ -275,8 +268,19 @@ impl Command {
275
268
program. len( ) . checked_sub( 5 ) . and_then( |i| program. get( i..) ) ,
276
269
Some ( [ 46 , 98 | 66 , 97 | 65 , 116 | 84 , 0 ] | [ 46 , 99 | 67 , 109 | 77 , 100 | 68 , 0 ] )
277
270
) ;
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
+ } ;
280
284
cmd_str. push ( 0 ) ; // add null terminator
281
285
282
286
// stolen from the libuv code.
@@ -730,96 +734,36 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
730
734
}
731
735
}
732
736
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
-
742
737
// Produces a wide string *without terminating null*; returns an error if
743
738
// `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 > > {
750
740
// Encode the command and arguments in a command line string such
751
741
// that the spawned process may recover them using CommandLineToArgvW.
752
742
let mut cmd: Vec < u16 > = Vec :: new ( ) ;
753
743
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
-
762
744
// Always quote the program name so CreateProcess to avoid ambiguity when
763
745
// the child process parses its arguments.
764
746
// Note that quotes aren't escaped here because they can't be used in arg0.
765
747
// But that's ok because file paths can't contain quotes.
766
748
cmd. push ( b'"' as u16 ) ;
767
- cmd. extend_from_slice ( prog . strip_suffix ( & [ 0 ] ) . unwrap_or ( prog ) ) ;
749
+ cmd. extend ( argv0 . encode_wide ( ) ) ;
768
750
cmd. push ( b'"' as u16 ) ;
769
751
770
752
for arg in args {
771
753
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) ?;
822
755
}
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)
823
767
}
824
768
825
769
fn make_envp ( maybe_env : Option < BTreeMap < EnvKey , OsString > > ) -> io:: Result < ( * mut c_void , Vec < u16 > ) > {
0 commit comments