@@ -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
}
@@ -536,44 +548,63 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
536
548
}
537
549
}
538
550
551
+ enum Quote {
552
+ // Every arg is quoted
553
+ Always ,
554
+ // Whitespace and empty args are quoted
555
+ Auto ,
556
+ // Arg appended without any changes (#29494)
557
+ Never ,
558
+ }
559
+
539
560
// Produces a wide string *without terminating null*; returns an error if
540
561
// `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 > > {
562
+ fn make_command_line ( prog : & OsStr , args : & [ Arg ] , force_quotes : bool ) -> io:: Result < Vec < u16 > > {
542
563
// Encode the command and arguments in a command line string such
543
564
// that the spawned process may recover them using CommandLineToArgvW.
544
565
let mut cmd: Vec < u16 > = Vec :: new ( ) ;
545
566
// Always quote the program name so CreateProcess doesn't interpret args as
546
567
// part of the name if the binary wasn't found first time.
547
- append_arg ( & mut cmd, prog, true ) ?;
568
+ append_arg ( & mut cmd, prog, Quote :: Always ) ?;
548
569
for arg in args {
549
570
cmd. push ( ' ' as u16 ) ;
550
- append_arg ( & mut cmd, arg, force_quotes) ?;
571
+ let ( arg, quote) = match arg {
572
+ Arg :: Regular ( arg) => ( arg, if force_quotes { Quote :: Always } else { Quote :: Auto } ) ,
573
+ Arg :: Raw ( arg) => ( arg, Quote :: Never ) ,
574
+ } ;
575
+ append_arg ( & mut cmd, arg, quote) ?;
551
576
}
552
577
return Ok ( cmd) ;
553
578
554
- fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , force_quotes : bool ) -> io:: Result < ( ) > {
579
+ fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , quote : Quote ) -> io:: Result < ( ) > {
555
580
// If an argument has 0 characters then we need to quote it to ensure
556
581
// that it actually gets passed through on the command line or otherwise
557
582
// it will be dropped entirely when parsed on the other end.
558
583
ensure_no_nuls ( arg) ?;
559
584
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 ( ) ;
585
+ let ( quote, escape) = match quote {
586
+ Quote :: Always => ( true , true ) ,
587
+ Quote :: Auto => {
588
+ ( arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' ) || arg_bytes. is_empty ( ) , true )
589
+ }
590
+ Quote :: Never => ( false , false ) ,
591
+ } ;
563
592
if quote {
564
593
cmd. push ( '"' as u16 ) ;
565
594
}
566
595
567
596
let mut backslashes: usize = 0 ;
568
597
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 ) ) ;
598
+ if escape {
599
+ if x == '\\' as u16 {
600
+ backslashes += 1 ;
601
+ } else {
602
+ if x == '"' as u16 {
603
+ // Add n+1 backslashes to total 2n+1 before internal '"'.
604
+ cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
605
+ }
606
+ backslashes = 0 ;
575
607
}
576
- backslashes = 0 ;
577
608
}
578
609
cmd. push ( x) ;
579
610
}
@@ -626,13 +657,15 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
626
657
}
627
658
628
659
pub struct CommandArgs < ' a > {
629
- iter : crate :: slice:: Iter < ' a , OsString > ,
660
+ iter : crate :: slice:: Iter < ' a , Arg > ,
630
661
}
631
662
632
663
impl < ' a > Iterator for CommandArgs < ' a > {
633
664
type Item = & ' a OsStr ;
634
665
fn next ( & mut self ) -> Option < & ' a OsStr > {
635
- self . iter . next ( ) . map ( |s| s. as_ref ( ) )
666
+ self . iter . next ( ) . map ( |arg| match arg {
667
+ Arg :: Regular ( s) | Arg :: Raw ( s) => s. as_ref ( ) ,
668
+ } )
636
669
}
637
670
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
638
671
self . iter . size_hint ( )
0 commit comments