@@ -65,6 +65,8 @@ fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
65
65
}
66
66
}
67
67
68
+ // 32768 minus NUL plus starting space in our implementation
69
+ const CMDLINE_MAX : usize = 32768 ;
68
70
pub struct Command {
69
71
program : OsString ,
70
72
args : Vec < OsString > ,
@@ -75,6 +77,8 @@ pub struct Command {
75
77
stdin : Option < Stdio > ,
76
78
stdout : Option < Stdio > ,
77
79
stderr : Option < Stdio > ,
80
+ cmdline : Vec < u16 > ,
81
+ cmdline_error : Option < io:: Error > ,
78
82
}
79
83
80
84
pub enum Stdio {
@@ -106,11 +110,32 @@ impl Command {
106
110
stdin : None ,
107
111
stdout : None ,
108
112
stderr : None ,
113
+ cmdline : Vec :: new ( ) ,
114
+ cmdline_error : None ,
109
115
}
110
116
}
111
117
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
+ }
112
132
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
+ }
114
139
}
115
140
pub fn env_mut ( & mut self ) -> & mut CommandEnv {
116
141
& mut self . env
@@ -136,34 +161,45 @@ impl Command {
136
161
default : Stdio ,
137
162
needs_stdin : bool ,
138
163
) -> io:: Result < ( Process , StdioPipes ) > {
164
+ if self . cmdline_error . is_some ( ) {
165
+ return self . cmdline_error . unwrap ( ) ;
166
+ }
167
+
139
168
let maybe_env = self . env . capture_if_changed ( ) ;
140
169
// To have the spawning semantics of unix/windows stay the same, we need
141
170
// to read the *child's* PATH if one is provided. See #15149 for more
142
171
// 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
+ }
153
185
}
154
186
}
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 ) ;
158
198
159
199
let mut si = zeroed_startupinfo ( ) ;
160
200
si. cb = mem:: size_of :: < c:: STARTUPINFO > ( ) as c:: DWORD ;
161
201
si. dwFlags = c:: STARTF_USESTDHANDLES ;
162
202
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
-
167
203
// stolen from the libuv code.
168
204
let mut flags = self . flags | c:: CREATE_UNICODE_ENVIRONMENT ;
169
205
if self . detach {
@@ -201,8 +237,8 @@ impl Command {
201
237
202
238
unsafe {
203
239
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
206
242
ptr:: null_mut ( ) ,
207
243
ptr:: null_mut ( ) ,
208
244
c:: TRUE ,
@@ -221,6 +257,14 @@ impl Command {
221
257
222
258
Ok ( ( Process { handle : Handle :: new ( pi. hProcess ) } , pipes) )
223
259
}
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
+ }
224
268
}
225
269
226
270
impl fmt:: Debug for Command {
@@ -445,6 +489,44 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {
445
489
}
446
490
}
447
491
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
+
448
530
// Produces a wide string *without terminating null*; returns an error if
449
531
// `prog` or any of the `args` contain a nul.
450
532
fn 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>> {
459
541
append_arg ( & mut cmd, arg, false ) ?;
460
542
}
461
543
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
- }
497
544
}
498
545
499
546
fn make_envp ( maybe_env : Option < BTreeMap < EnvKey , OsString > > ) -> io:: Result < ( * mut c_void , Vec < u16 > ) > {
0 commit comments