@@ -137,7 +137,7 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
137137
138138pub struct Command {
139139 program : OsString ,
140- args : Vec < OsString > ,
140+ args : Vec < Arg > ,
141141 env : CommandEnv ,
142142 cwd : Option < OsString > ,
143143 flags : u32 ,
@@ -161,6 +161,14 @@ pub struct StdioPipes {
161161 pub stderr : Option < AnonPipe > ,
162162}
163163
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+
164172impl Command {
165173 pub fn new ( program : & OsStr ) -> Command {
166174 Command {
@@ -178,7 +186,7 @@ impl Command {
178186 }
179187
180188 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 ( ) ) )
182190 }
183191 pub fn env_mut ( & mut self ) -> & mut CommandEnv {
184192 & mut self . env
@@ -203,6 +211,10 @@ impl Command {
203211 self . force_quotes_enabled = enabled;
204212 }
205213
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+
206218 pub fn get_program ( & self ) -> & OsStr {
207219 & self . program
208220 }
@@ -315,9 +327,13 @@ impl Command {
315327
316328impl fmt:: Debug for Command {
317329 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
318- write ! ( f , "{:?}" , self . program) ?;
330+ self . program . fmt ( f ) ?;
319331 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+ } ?;
321337 }
322338 Ok ( ( ) )
323339 }
@@ -536,44 +552,63 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
536552 }
537553}
538554
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+
539564// Produces a wide string *without terminating null*; returns an error if
540565// `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 > > {
542567 // Encode the command and arguments in a command line string such
543568 // that the spawned process may recover them using CommandLineToArgvW.
544569 let mut cmd: Vec < u16 > = Vec :: new ( ) ;
545570 // Always quote the program name so CreateProcess doesn't interpret args as
546571 // 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 ) ?;
548573 for arg in args {
549574 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) ?;
551580 }
552581 return Ok ( cmd) ;
553582
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 < ( ) > {
555584 // If an argument has 0 characters then we need to quote it to ensure
556585 // that it actually gets passed through on the command line or otherwise
557586 // it will be dropped entirely when parsed on the other end.
558587 ensure_no_nuls ( arg) ?;
559588 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+ } ;
563596 if quote {
564597 cmd. push ( '"' as u16 ) ;
565598 }
566599
567600 let mut backslashes: usize = 0 ;
568601 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 ;
575611 }
576- backslashes = 0 ;
577612 }
578613 cmd. push ( x) ;
579614 }
@@ -626,13 +661,15 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
626661}
627662
628663pub struct CommandArgs < ' a > {
629- iter : crate :: slice:: Iter < ' a , OsString > ,
664+ iter : crate :: slice:: Iter < ' a , Arg > ,
630665}
631666
632667impl < ' a > Iterator for CommandArgs < ' a > {
633668 type Item = & ' a OsStr ;
634669 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+ } )
636673 }
637674 fn size_hint ( & self ) -> ( usize , Option < usize > ) {
638675 self . iter . size_hint ( )
0 commit comments