@@ -65,6 +65,8 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
6565 }
6666}
6767
68+ // 32768 minus NUL plus starting space in our implementation
69+ const CMDLINE_MAX : usize = 32768 ;
6870pub struct Command {
6971 program : OsString ,
7072 args : Vec < OsString > ,
@@ -75,6 +77,8 @@ pub struct Command {
7577 stdin : Option < Stdio > ,
7678 stdout : Option < Stdio > ,
7779 stderr : Option < Stdio > ,
80+ cmdline : Vec < u16 > ,
81+ cmdline_error : Option < io:: Error > ,
7882}
7983
8084pub enum Stdio {
@@ -106,11 +110,32 @@ impl Command {
106110 stdin : None ,
107111 stdout : None ,
108112 stderr : None ,
113+ cmdline : Vec :: new ( ) ,
114+ cmdline_error : None ,
109115 }
110116 }
111117
118+ pub fn maybe_arg ( & mut self , arg : & OsStr ) -> io:: Result < ( ) > {
119+ self . args . push ( arg. to_os_string ( ) ) ;
120+ self . cmdline . push ( ' ' as u16 ) ;
121+ let result = append_arg ( & mut cmd, arg, false ) ;
122+ if result. is_err ( ) {
123+ self . cmdline . truncate ( self . cmdline . len ( ) - 1 ) ;
124+ result
125+ } else if self . cmdline . size ( ) >= CMDLINE_MAX {
126+ // Roll back oversized
127+ self . cmdline . truncate ( self . cmdline . len ( ) - 1 - result. unwrap ( ) ) ;
128+ Err ( io:: Error :: new ( ErrorKind :: InvalidInput , "oversized cmdline" ) )
129+ }
130+ Ok ( )
131+ }
112132 pub fn arg ( & mut self , arg : & OsStr ) {
113- self . args . push ( arg. to_os_string ( ) )
133+ if self . cmdline_error . is_none ( ) {
134+ let result = self . maybe_arg ( self , arg) ;
135+ if result. is_err ( ) {
136+ self . cmdline_error = Some ( result. expect_err ( ) ) ;
137+ }
138+ }
114139 }
115140 pub fn env_mut ( & mut self ) -> & mut CommandEnv {
116141 & mut self . env
@@ -136,34 +161,45 @@ impl Command {
136161 default : Stdio ,
137162 needs_stdin : bool ,
138163 ) -> io:: Result < ( Process , StdioPipes ) > {
164+ if self . cmdline_error . is_some ( ) {
165+ return self . cmdline_error . unwrap ( ) ;
166+ }
167+
139168 let maybe_env = self . env . capture_if_changed ( ) ;
140169 // To have the spawning semantics of unix/windows stay the same, we need
141170 // to read the *child's* PATH if one is provided. See #15149 for more
142171 // details.
143- let program = maybe_env. as_ref ( ) . and_then ( |env| {
144- if let Some ( v) = env. get ( OsStr :: new ( "PATH" ) ) {
145- // Split the value and test each path to see if the
146- // program exists.
147- for path in split_paths ( & v) {
148- let path = path
149- . join ( self . program . to_str ( ) . unwrap ( ) )
150- . with_extension ( env:: consts:: EXE_EXTENSION ) ;
151- if fs:: metadata ( & path) . is_ok ( ) {
152- return Some ( path. into_os_string ( ) ) ;
172+ let program = maybe_env
173+ . as_ref ( )
174+ . and_then ( |env| {
175+ if let Some ( v) = env. get ( OsStr :: new ( "PATH" ) ) {
176+ // Split the value and test each path to see if the
177+ // program exists.
178+ for path in split_paths ( & v) {
179+ let path = path
180+ . join ( self . program . to_str ( ) . unwrap ( ) )
181+ . with_extension ( env:: consts:: EXE_EXTENSION ) ;
182+ if fs:: metadata ( & path) . is_ok ( ) {
183+ return Some ( path. into_os_string ( ) ) ;
184+ }
153185 }
154186 }
155- }
156- None
157- } ) ;
187+ None
188+ } )
189+ . as_ref ( )
190+ . unwrap_or ( & self . program ) ;
191+
192+ // Prepare and terminate the application name and the cmdline
193+ // XXX: this won't work for 16-bit, might be preferable to do a extend_from_slice
194+ let program_str: Vec < u16 > = Vec :: new ( ) ;
195+ append_arg ( & mut program_str, program, true ) ?;
196+ program_str. push ( 0 ) ;
197+ self . cmdline . push ( 0 ) ;
158198
159199 let mut si = zeroed_startupinfo ( ) ;
160200 si. cb = mem:: size_of :: < c:: STARTUPINFO > ( ) as c:: DWORD ;
161201 si. dwFlags = c:: STARTF_USESTDHANDLES ;
162202
163- let program = program. as_ref ( ) . unwrap_or ( & self . program ) ;
164- let mut cmd_str = make_command_line ( program, & self . args ) ?;
165- cmd_str. push ( 0 ) ; // add null terminator
166-
167203 // stolen from the libuv code.
168204 let mut flags = self . flags | c:: CREATE_UNICODE_ENVIRONMENT ;
169205 if self . detach {
@@ -201,8 +237,8 @@ impl Command {
201237
202238 unsafe {
203239 cvt ( c:: CreateProcessW (
204- ptr :: null ( ) ,
205- cmd_str . as_mut_ptr ( ) ,
240+ program_str . as_mut_ptr ( ) ,
241+ self . cmdline . as_mut_ptr ( ) . offset ( 1 ) , // Skip the starting space
206242 ptr:: null_mut ( ) ,
207243 ptr:: null_mut ( ) ,
208244 c:: TRUE ,
@@ -221,6 +257,14 @@ impl Command {
221257
222258 Ok ( ( Process { handle : Handle :: new ( pi. hProcess ) } , pipes) )
223259 }
260+
261+ pub fn get_size ( & mut self ) -> io:: Result < usize > {
262+ let ( _, cmd_str) = self . prepare_command_line ( ) ?;
263+ Ok ( cmd_str. len ( ) )
264+ }
265+ pub fn check_size ( & mut self , _refresh : bool ) -> io:: Result < bool > {
266+ Ok ( self . get_size ( ) ? < 32767 )
267+ }
224268}
225269
226270impl fmt:: Debug for Command {
@@ -445,6 +489,44 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
445489 }
446490}
447491
492+ fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , force_quotes : bool ) -> io:: Result < usize > {
493+ let mut addsize: usize = 0 ;
494+ // If an argument has 0 characters then we need to quote it to ensure
495+ // that it actually gets passed through on the command line or otherwise
496+ // it will be dropped entirely when parsed on the other end.
497+ ensure_no_nuls ( arg) ?;
498+ let arg_bytes = & arg. as_inner ( ) . inner . as_inner ( ) ;
499+ let quote =
500+ force_quotes || arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' ) || arg_bytes. is_empty ( ) ;
501+ if quote {
502+ cmd. push ( '"' as u16 ) ;
503+ addsize += 1 ;
504+ }
505+
506+ let mut backslashes: usize = 0 ;
507+ for x in arg. encode_wide ( ) {
508+ if x == '\\' as u16 {
509+ backslashes += 1 ;
510+ } else {
511+ if x == '"' as u16 {
512+ // Add n+1 backslashes to total 2n+1 before internal '"'.
513+ cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
514+ addsize += backslashes + 1 ;
515+ }
516+ backslashes = 0 ;
517+ }
518+ cmd. push ( x) ;
519+ }
520+
521+ if quote {
522+ // Add n backslashes to total 2n before ending '"'.
523+ cmd. extend ( ( 0 ..backslashes) . map ( |_| '\\' as u16 ) ) ;
524+ cmd. push ( '"' as u16 ) ;
525+ addsize += backslashes + 1 ;
526+ }
527+ Ok ( addsize)
528+ }
529+
448530// Produces a wide string *without terminating null*; returns an error if
449531// `prog` or any of the `args` contain a nul.
450532fn make_command_line ( prog : & OsStr , args : & [ OsString ] ) -> io:: Result < Vec < u16 > > {
@@ -459,41 +541,6 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
459541 append_arg ( & mut cmd, arg, false ) ?;
460542 }
461543 return Ok ( cmd) ;
462-
463- fn append_arg ( cmd : & mut Vec < u16 > , arg : & OsStr , force_quotes : bool ) -> io:: Result < ( ) > {
464- // If an argument has 0 characters then we need to quote it to ensure
465- // that it actually gets passed through on the command line or otherwise
466- // it will be dropped entirely when parsed on the other end.
467- ensure_no_nuls ( arg) ?;
468- let arg_bytes = & arg. as_inner ( ) . inner . as_inner ( ) ;
469- let quote = force_quotes
470- || arg_bytes. iter ( ) . any ( |c| * c == b' ' || * c == b'\t' )
471- || arg_bytes. is_empty ( ) ;
472- if quote {
473- cmd. push ( '"' as u16 ) ;
474- }
475-
476- let mut backslashes: usize = 0 ;
477- for x in arg. encode_wide ( ) {
478- if x == '\\' as u16 {
479- backslashes += 1 ;
480- } else {
481- if x == '"' as u16 {
482- // Add n+1 backslashes to total 2n+1 before internal '"'.
483- cmd. extend ( ( 0 ..=backslashes) . map ( |_| '\\' as u16 ) ) ;
484- }
485- backslashes = 0 ;
486- }
487- cmd. push ( x) ;
488- }
489-
490- if quote {
491- // Add n backslashes to total 2n before ending '"'.
492- cmd. extend ( ( 0 ..backslashes) . map ( |_| '\\' as u16 ) ) ;
493- cmd. push ( '"' as u16 ) ;
494- }
495- Ok ( ( ) )
496- }
497544}
498545
499546fn make_envp ( maybe_env : Option < BTreeMap < EnvKey , OsString > > ) -> io:: Result < ( * mut c_void , Vec < u16 > ) > {
0 commit comments