@@ -137,7 +137,7 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
137
137
138
138
pub struct Command {
139
139
program : OsString ,
140
- args : Vec < OsString > ,
140
+ args : Vec < Arg > ,
141
141
env : CommandEnv ,
142
142
cwd : Option < OsString > ,
143
143
flags : u32 ,
@@ -161,6 +161,14 @@ pub struct StdioPipes {
161
161
pub stderr : Option < AnonPipe > ,
162
162
}
163
163
164
+ #[ derive( Debug ) ]
165
+ enum Arg {
166
+ /// Add quotes (if needed)
167
+ Regular ( OsString ) ,
168
+ /// Append raw string without quoting
169
+ Raw ( OsString ) ,
170
+ }
171
+
164
172
impl Command {
165
173
pub fn new ( program : & OsStr ) -> Command {
166
174
Command {
@@ -178,7 +186,7 @@ impl Command {
178
186
}
179
187
180
188
pub fn arg ( & mut self , arg : & OsStr ) {
181
- self . args . push ( arg. to_os_string ( ) )
189
+ self . args . push ( Arg :: Regular ( arg. to_os_string ( ) ) )
182
190
}
183
191
pub fn env_mut ( & mut self ) -> & mut CommandEnv {
184
192
& mut self . env
@@ -203,6 +211,10 @@ impl Command {
203
211
self . force_quotes_enabled = enabled;
204
212
}
205
213
214
+ pub fn raw_arg ( & mut self , command_str_to_append : & OsStr ) {
215
+ self . args . push ( Arg :: Raw ( command_str_to_append. to_os_string ( ) ) )
216
+ }
217
+
206
218
pub fn get_program ( & self ) -> & OsStr {
207
219
& self . program
208
220
}
@@ -315,9 +327,13 @@ impl Command {
315
327
316
328
impl fmt:: Debug for Command {
317
329
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
318
- write ! ( f , "{:?}" , self . program) ?;
330
+ self . program . fmt ( f ) ?;
319
331
for arg in & self . args {
320
- write ! ( f, " {:?}" , arg) ?;
332
+ f. write_str ( " " ) ?;
333
+ match arg {
334
+ Arg :: Regular ( s) => s. fmt ( f) ,
335
+ Arg :: Raw ( s) => f. write_str ( & s. to_string_lossy ( ) ) ,
336
+ } ?;
321
337
}
322
338
Ok ( ( ) )
323
339
}
@@ -536,44 +552,63 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
536
552
}
537
553
}
538
554
555
+ enum Quote {
556
+ // Every arg is quoted
557
+ Always ,
558
+ // Whitespace and empty args are quoted
559
+ Auto ,
560
+ // Arg appended without any changes (#29494)
561
+ Never ,
562
+ }
563
+
539
564
// Produces a wide string *without terminating null*; returns an error if
540
565
// `prog` or any of the `args` contain a nul.
541
- fn make_command_line ( prog : & OsStr , args : & [ OsString ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
566
+ fn make_command_line ( prog : & OsStr , args : & [ Arg ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
542
567
// Encode the command and arguments in a command line string such
543
568
// that the spawned process may recover them using CommandLineToArgvW.
544
569
let mut cmd: Vec < u16 > = Vec :: new ( ) ;
545
570
// Always quote the program name so CreateProcess doesn't interpret args as
546
571
// part of the name if the binary wasn't found first time.
547
- append_arg ( & mut cmd, prog, true ) ?;
572
+ append_arg ( & mut cmd, prog, Quote :: Always ) ?;
548
573
for arg in args {
549
574
cmd. push ( ' ' as u16 ) ;
550
- append_arg ( & mut cmd, arg, force_quotes) ?;
575
+ let ( arg, quote) = match arg {
576
+ Arg :: Regular ( arg) => ( arg, if force_quotes { Quote :: Always } else { Quote :: Auto } ) ,
577
+ Arg :: Raw ( arg) => ( arg, Quote :: Never ) ,
578
+ } ;
579
+ append_arg ( & mut cmd, arg, quote) ?;
551
580
}
552
581
return Ok ( cmd) ;
553
582
554
- fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , force_quotes : bool ) -> io:: Result < ( ) > {
583
+ fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , quote : Quote ) -> io:: Result < ( ) > {
555
584
// If an argument has 0 characters then we need to quote it to ensure
556
585
// that it actually gets passed through on the command line or otherwise
557
586
// it will be dropped entirely when parsed on the other end.
558
587
ensure_no_nuls ( arg) ?;
559
588
let arg_bytes = & arg. as_inner ( ) . inner . as_inner ( ) ;
560
- let quote = force_quotes
561
- || arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' )
562
- || arg_bytes. is_empty ( ) ;
589
+ let ( quote, escape) = match quote {
590
+ Quote :: Always => ( true , true ) ,
591
+ Quote :: Auto => {
592
+ ( arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' ) || arg_bytes. is_empty ( ) , true )
593
+ }
594
+ Quote :: Never => ( false , false ) ,
595
+ } ;
563
596
if quote {
564
597
cmd. push ( '"' as u16 ) ;
565
598
}
566
599
567
600
let mut backslashes: usize = 0 ;
568
601
for x in arg. encode_wide ( ) {
569
- if x == '\\' as u16 {
570
- backslashes += 1 ;
571
- } else {
572
- if x == '"' as u16 {
573
- // Add n+1 backslashes to total 2n+1 before internal '"'.
574
- cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
602
+ if escape {
603
+ if x == '\\' as u16 {
604
+ backslashes += 1 ;
605
+ } else {
606
+ if x == '"' as u16 {
607
+ // Add n+1 backslashes to total 2n+1 before internal '"'.
608
+ cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
609
+ }
610
+ backslashes = 0 ;
575
611
}
576
- backslashes = 0 ;
577
612
}
578
613
cmd. push ( x) ;
579
614
}
@@ -626,13 +661,15 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
626
661
}
627
662
628
663
pub struct CommandArgs < ' a > {
629
- iter : crate :: slice:: Iter < ' a , OsString > ,
664
+ iter : crate :: slice:: Iter < ' a , Arg > ,
630
665
}
631
666
632
667
impl < ' a > Iterator for CommandArgs < ' a > {
633
668
type Item = & ' a OsStr ;
634
669
fn next ( & mut self ) -> Option < & ' a OsStr > {
635
- self . iter . next ( ) . map ( |s| s. as_ref ( ) )
670
+ self . iter . next ( ) . map ( |arg| match arg {
671
+ Arg :: Regular ( s) | Arg :: Raw ( s) => s. as_ref ( ) ,
672
+ } )
636
673
}
637
674
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
638
675
self . iter . size_hint ( )
0 commit comments