]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | #![unstable(feature = "process_internals", issue = "none")] |
ff7c6d11 | 2 | |
1b1a35ee XL |
3 | #[cfg(test)] |
4 | mod tests; | |
5 | ||
dfeec247 | 6 | use crate::borrow::Borrow; |
532ac7d7 | 7 | use crate::collections::BTreeMap; |
532ac7d7 | 8 | use crate::env; |
dfeec247 XL |
9 | use crate::env::split_paths; |
10 | use crate::ffi::{OsStr, OsString}; | |
532ac7d7 XL |
11 | use crate::fmt; |
12 | use crate::fs; | |
13 | use crate::io::{self, Error, ErrorKind}; | |
14 | use crate::mem; | |
15 | use crate::os::windows::ffi::OsStrExt; | |
16 | use crate::path::Path; | |
17 | use crate::ptr; | |
532ac7d7 | 18 | use crate::sys::c; |
dfeec247 XL |
19 | use crate::sys::cvt; |
20 | use crate::sys::fs::{File, OpenOptions}; | |
532ac7d7 | 21 | use crate::sys::handle::Handle; |
dfeec247 | 22 | use crate::sys::mutex::Mutex; |
532ac7d7 XL |
23 | use crate::sys::pipe::{self, AnonPipe}; |
24 | use crate::sys::stdio; | |
1b1a35ee | 25 | use crate::sys_common::process::{CommandEnv, CommandEnvs}; |
ba9703b0 | 26 | use crate::sys_common::AsInner; |
532ac7d7 | 27 | |
dfeec247 | 28 | use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; |
85aaf69f SL |
29 | |
30 | //////////////////////////////////////////////////////////////////////////////// | |
31 | // Command | |
32 | //////////////////////////////////////////////////////////////////////////////// | |
33 | ||
ff7c6d11 XL |
34 | #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] |
35 | #[doc(hidden)] | |
e1599b0c | 36 | pub struct EnvKey(OsString); |
ff7c6d11 | 37 | |
e1599b0c | 38 | impl From<OsString> for EnvKey { |
ba9703b0 XL |
39 | fn from(mut k: OsString) -> Self { |
40 | k.make_ascii_uppercase(); | |
41 | EnvKey(k) | |
ff7c6d11 XL |
42 | } |
43 | } | |
44 | ||
e1599b0c | 45 | impl From<EnvKey> for OsString { |
dfeec247 XL |
46 | fn from(k: EnvKey) -> Self { |
47 | k.0 | |
48 | } | |
85aaf69f SL |
49 | } |
50 | ||
e1599b0c | 51 | impl Borrow<OsStr> for EnvKey { |
dfeec247 XL |
52 | fn borrow(&self) -> &OsStr { |
53 | &self.0 | |
54 | } | |
ff7c6d11 XL |
55 | } |
56 | ||
e1599b0c | 57 | impl AsRef<OsStr> for EnvKey { |
dfeec247 XL |
58 | fn as_ref(&self) -> &OsStr { |
59 | &self.0 | |
60 | } | |
ff7c6d11 XL |
61 | } |
62 | ||
7453a54e SL |
63 | fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> { |
64 | if str.as_ref().encode_wide().any(|b| b == 0) { | |
65 | Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")) | |
66 | } else { | |
67 | Ok(str) | |
68 | } | |
69 | } | |
70 | ||
85aaf69f | 71 | pub struct Command { |
7453a54e SL |
72 | program: OsString, |
73 | args: Vec<OsString>, | |
e1599b0c | 74 | env: CommandEnv, |
7453a54e | 75 | cwd: Option<OsString>, |
476ff2be | 76 | flags: u32, |
7453a54e SL |
77 | detach: bool, // not currently exposed in std::process |
78 | stdin: Option<Stdio>, | |
79 | stdout: Option<Stdio>, | |
80 | stderr: Option<Stdio>, | |
6a06907d | 81 | force_quotes_enabled: bool, |
7453a54e SL |
82 | } |
83 | ||
84 | pub enum Stdio { | |
85 | Inherit, | |
86 | Null, | |
87 | MakePipe, | |
88 | Handle(Handle), | |
89 | } | |
90 | ||
91 | pub struct StdioPipes { | |
92 | pub stdin: Option<AnonPipe>, | |
93 | pub stdout: Option<AnonPipe>, | |
94 | pub stderr: Option<AnonPipe>, | |
85aaf69f SL |
95 | } |
96 | ||
a7813a04 XL |
97 | struct DropGuard<'a> { |
98 | lock: &'a Mutex, | |
99 | } | |
100 | ||
85aaf69f SL |
101 | impl Command { |
102 | pub fn new(program: &OsStr) -> Command { | |
103 | Command { | |
104 | program: program.to_os_string(), | |
105 | args: Vec::new(), | |
ff7c6d11 | 106 | env: Default::default(), |
85aaf69f | 107 | cwd: None, |
476ff2be | 108 | flags: 0, |
85aaf69f | 109 | detach: false, |
7453a54e SL |
110 | stdin: None, |
111 | stdout: None, | |
112 | stderr: None, | |
6a06907d | 113 | force_quotes_enabled: false, |
85aaf69f SL |
114 | } |
115 | } | |
116 | ||
117 | pub fn arg(&mut self, arg: &OsStr) { | |
118 | self.args.push(arg.to_os_string()) | |
119 | } | |
e1599b0c | 120 | pub fn env_mut(&mut self) -> &mut CommandEnv { |
ff7c6d11 | 121 | &mut self.env |
85aaf69f SL |
122 | } |
123 | pub fn cwd(&mut self, dir: &OsStr) { | |
124 | self.cwd = Some(dir.to_os_string()) | |
125 | } | |
7453a54e SL |
126 | pub fn stdin(&mut self, stdin: Stdio) { |
127 | self.stdin = Some(stdin); | |
128 | } | |
129 | pub fn stdout(&mut self, stdout: Stdio) { | |
130 | self.stdout = Some(stdout); | |
131 | } | |
132 | pub fn stderr(&mut self, stderr: Stdio) { | |
133 | self.stderr = Some(stderr); | |
134 | } | |
476ff2be SL |
135 | pub fn creation_flags(&mut self, flags: u32) { |
136 | self.flags = flags; | |
137 | } | |
62682a34 | 138 | |
6a06907d XL |
139 | pub fn force_quotes(&mut self, enabled: bool) { |
140 | self.force_quotes_enabled = enabled; | |
141 | } | |
142 | ||
1b1a35ee XL |
143 | pub fn get_program(&self) -> &OsStr { |
144 | &self.program | |
145 | } | |
146 | ||
147 | pub fn get_args(&self) -> CommandArgs<'_> { | |
148 | let iter = self.args.iter(); | |
149 | CommandArgs { iter } | |
150 | } | |
151 | ||
152 | pub fn get_envs(&self) -> CommandEnvs<'_> { | |
153 | self.env.iter() | |
154 | } | |
155 | ||
156 | pub fn get_current_dir(&self) -> Option<&Path> { | |
157 | self.cwd.as_ref().map(|cwd| Path::new(cwd)) | |
158 | } | |
159 | ||
dfeec247 XL |
160 | pub fn spawn( |
161 | &mut self, | |
162 | default: Stdio, | |
163 | needs_stdin: bool, | |
164 | ) -> io::Result<(Process, StdioPipes)> { | |
ff7c6d11 | 165 | let maybe_env = self.env.capture_if_changed(); |
bd371182 AL |
166 | // To have the spawning semantics of unix/windows stay the same, we need |
167 | // to read the *child's* PATH if one is provided. See #15149 for more | |
168 | // details. | |
ff7c6d11 XL |
169 | let program = maybe_env.as_ref().and_then(|env| { |
170 | if let Some(v) = env.get(OsStr::new("PATH")) { | |
85aaf69f SL |
171 | // Split the value and test each path to see if the |
172 | // program exists. | |
173 | for path in split_paths(&v) { | |
dfeec247 XL |
174 | let path = path |
175 | .join(self.program.to_str().unwrap()) | |
176 | .with_extension(env::consts::EXE_EXTENSION); | |
c34b1796 | 177 | if fs::metadata(&path).is_ok() { |
dfeec247 | 178 | return Some(path.into_os_string()); |
85aaf69f SL |
179 | } |
180 | } | |
85aaf69f SL |
181 | } |
182 | None | |
183 | }); | |
184 | ||
bd371182 | 185 | let mut si = zeroed_startupinfo(); |
92a42be0 SL |
186 | si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD; |
187 | si.dwFlags = c::STARTF_USESTDHANDLES; | |
85aaf69f | 188 | |
7453a54e | 189 | let program = program.as_ref().unwrap_or(&self.program); |
6a06907d | 190 | let mut cmd_str = make_command_line(program, &self.args, self.force_quotes_enabled)?; |
bd371182 | 191 | cmd_str.push(0); // add null terminator |
85aaf69f | 192 | |
bd371182 | 193 | // stolen from the libuv code. |
476ff2be | 194 | let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; |
7453a54e | 195 | if self.detach { |
92a42be0 | 196 | flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; |
bd371182 | 197 | } |
85aaf69f | 198 | |
ff7c6d11 | 199 | let (envp, _data) = make_envp(maybe_env)?; |
54a0048b | 200 | let (dirp, _data) = make_dirp(self.cwd.as_ref())?; |
bd371182 | 201 | let mut pi = zeroed_process_information(); |
bd371182 | 202 | |
7453a54e SL |
203 | // Prepare all stdio handles to be inherited by the child. This |
204 | // currently involves duplicating any existing ones with the ability to | |
205 | // be inherited by child processes. Note, however, that once an | |
206 | // inheritable handle is created, *any* spawned child will inherit that | |
207 | // handle. We only want our own child to inherit this handle, so we wrap | |
208 | // the remaining portion of this spawn in a mutex. | |
209 | // | |
210 | // For more information, msdn also has an article about this race: | |
211 | // http://support.microsoft.com/kb/315939 | |
a7813a04 XL |
212 | static CREATE_PROCESS_LOCK: Mutex = Mutex::new(); |
213 | let _guard = DropGuard::new(&CREATE_PROCESS_LOCK); | |
7453a54e | 214 | |
dfeec247 | 215 | let mut pipes = StdioPipes { stdin: None, stdout: None, stderr: None }; |
54a0048b | 216 | let null = Stdio::Null; |
dfeec247 | 217 | let default_stdin = if needs_stdin { &default } else { &null }; |
54a0048b | 218 | let stdin = self.stdin.as_ref().unwrap_or(default_stdin); |
7453a54e SL |
219 | let stdout = self.stdout.as_ref().unwrap_or(&default); |
220 | let stderr = self.stderr.as_ref().unwrap_or(&default); | |
54a0048b | 221 | let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; |
dfeec247 XL |
222 | let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; |
223 | let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; | |
7453a54e SL |
224 | si.hStdInput = stdin.raw(); |
225 | si.hStdOutput = stdout.raw(); | |
226 | si.hStdError = stderr.raw(); | |
227 | ||
54a0048b | 228 | unsafe { |
dfeec247 XL |
229 | cvt(c::CreateProcessW( |
230 | ptr::null(), | |
231 | cmd_str.as_mut_ptr(), | |
232 | ptr::null_mut(), | |
233 | ptr::null_mut(), | |
234 | c::TRUE, | |
235 | flags, | |
236 | envp, | |
237 | dirp, | |
238 | &mut si, | |
239 | &mut pi, | |
240 | )) | |
54a0048b | 241 | }?; |
85aaf69f | 242 | |
bd371182 AL |
243 | // We close the thread handle because we don't care about keeping |
244 | // the thread id valid, and we aren't keeping the thread handle | |
245 | // around to be able to close it later. | |
246 | drop(Handle::new(pi.hThread)); | |
85aaf69f | 247 | |
7453a54e | 248 | Ok((Process { handle: Handle::new(pi.hProcess) }, pipes)) |
85aaf69f | 249 | } |
7453a54e SL |
250 | } |
251 | ||
252 | impl fmt::Debug for Command { | |
532ac7d7 | 253 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
54a0048b | 254 | write!(f, "{:?}", self.program)?; |
7453a54e | 255 | for arg in &self.args { |
54a0048b | 256 | write!(f, " {:?}", arg)?; |
7453a54e SL |
257 | } |
258 | Ok(()) | |
259 | } | |
260 | } | |
261 | ||
a7813a04 XL |
262 | impl<'a> DropGuard<'a> { |
263 | fn new(lock: &'a Mutex) -> DropGuard<'a> { | |
264 | unsafe { | |
265 | lock.lock(); | |
a1dfa0c6 | 266 | DropGuard { lock } |
a7813a04 XL |
267 | } |
268 | } | |
269 | } | |
270 | ||
271 | impl<'a> Drop for DropGuard<'a> { | |
272 | fn drop(&mut self) { | |
273 | unsafe { | |
274 | self.lock.unlock(); | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
7453a54e | 279 | impl Stdio { |
dfeec247 | 280 | fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> { |
7453a54e SL |
281 | match *self { |
282 | // If no stdio handle is available, then inherit means that it | |
283 | // should still be unavailable so propagate the | |
284 | // INVALID_HANDLE_VALUE. | |
dfeec247 XL |
285 | Stdio::Inherit => match stdio::get_handle(stdio_id) { |
286 | Ok(io) => { | |
287 | let io = Handle::new(io); | |
288 | let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); | |
289 | io.into_raw(); | |
290 | ret | |
7453a54e | 291 | } |
dfeec247 XL |
292 | Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), |
293 | }, | |
7453a54e SL |
294 | |
295 | Stdio::MakePipe => { | |
476ff2be | 296 | let ours_readable = stdio_id != c::STD_INPUT_HANDLE; |
416331ca | 297 | let pipes = pipe::anon_pipe(ours_readable, true)?; |
476ff2be | 298 | *pipe = Some(pipes.ours); |
476ff2be | 299 | Ok(pipes.theirs.into_handle()) |
7453a54e SL |
300 | } |
301 | ||
dfeec247 | 302 | Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), |
7453a54e SL |
303 | |
304 | // Open up a reference to NUL with appropriate read/write | |
305 | // permissions as well as the ability to be inherited to child | |
306 | // processes (as this is about to be inherited). | |
307 | Stdio::Null => { | |
308 | let size = mem::size_of::<c::SECURITY_ATTRIBUTES>(); | |
309 | let mut sa = c::SECURITY_ATTRIBUTES { | |
310 | nLength: size as c::DWORD, | |
311 | lpSecurityDescriptor: ptr::null_mut(), | |
312 | bInheritHandle: 1, | |
313 | }; | |
314 | let mut opts = OpenOptions::new(); | |
315 | opts.read(stdio_id == c::STD_INPUT_HANDLE); | |
316 | opts.write(stdio_id != c::STD_INPUT_HANDLE); | |
317 | opts.security_attributes(&mut sa); | |
dfeec247 | 318 | File::open(Path::new("NUL"), &opts).map(|file| file.into_handle()) |
7453a54e SL |
319 | } |
320 | } | |
321 | } | |
322 | } | |
323 | ||
041b39d2 XL |
324 | impl From<AnonPipe> for Stdio { |
325 | fn from(pipe: AnonPipe) -> Stdio { | |
326 | Stdio::Handle(pipe.into_handle()) | |
327 | } | |
328 | } | |
329 | ||
330 | impl From<File> for Stdio { | |
331 | fn from(file: File) -> Stdio { | |
332 | Stdio::Handle(file.into_handle()) | |
333 | } | |
334 | } | |
335 | ||
7453a54e SL |
336 | //////////////////////////////////////////////////////////////////////////////// |
337 | // Processes | |
338 | //////////////////////////////////////////////////////////////////////////////// | |
339 | ||
340 | /// A value representing a child process. | |
341 | /// | |
342 | /// The lifetime of this value is linked to the lifetime of the actual | |
343 | /// process - the Process destructor calls self.finish() which waits | |
344 | /// for the process to terminate. | |
345 | pub struct Process { | |
346 | handle: Handle, | |
347 | } | |
348 | ||
349 | impl Process { | |
350 | pub fn kill(&mut self) -> io::Result<()> { | |
dfeec247 | 351 | cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?; |
85aaf69f SL |
352 | Ok(()) |
353 | } | |
354 | ||
62682a34 | 355 | pub fn id(&self) -> u32 { |
dfeec247 | 356 | unsafe { c::GetProcessId(self.handle.raw()) as u32 } |
62682a34 SL |
357 | } |
358 | ||
7453a54e | 359 | pub fn wait(&mut self) -> io::Result<ExitStatus> { |
85aaf69f | 360 | unsafe { |
92a42be0 SL |
361 | let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); |
362 | if res != c::WAIT_OBJECT_0 { | |
dfeec247 | 363 | return Err(Error::last_os_error()); |
85aaf69f | 364 | } |
92a42be0 | 365 | let mut status = 0; |
54a0048b | 366 | cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; |
92a42be0 | 367 | Ok(ExitStatus(status)) |
85aaf69f SL |
368 | } |
369 | } | |
62682a34 | 370 | |
8bb4bdeb | 371 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
32a655c1 SL |
372 | unsafe { |
373 | match c::WaitForSingleObject(self.handle.raw(), 0) { | |
374 | c::WAIT_OBJECT_0 => {} | |
375 | c::WAIT_TIMEOUT => { | |
8bb4bdeb | 376 | return Ok(None); |
32a655c1 SL |
377 | } |
378 | _ => return Err(io::Error::last_os_error()), | |
379 | } | |
380 | let mut status = 0; | |
381 | cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; | |
8bb4bdeb | 382 | Ok(Some(ExitStatus(status))) |
32a655c1 SL |
383 | } |
384 | } | |
385 | ||
dfeec247 XL |
386 | pub fn handle(&self) -> &Handle { |
387 | &self.handle | |
388 | } | |
c1a9b12d | 389 | |
dfeec247 XL |
390 | pub fn into_handle(self) -> Handle { |
391 | self.handle | |
392 | } | |
85aaf69f SL |
393 | } |
394 | ||
395 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
92a42be0 | 396 | pub struct ExitStatus(c::DWORD); |
85aaf69f SL |
397 | |
398 | impl ExitStatus { | |
399 | pub fn success(&self) -> bool { | |
400 | self.0 == 0 | |
401 | } | |
402 | pub fn code(&self) -> Option<i32> { | |
92a42be0 | 403 | Some(self.0 as i32) |
85aaf69f SL |
404 | } |
405 | } | |
406 | ||
3dfed10e | 407 | /// Converts a raw `c::DWORD` to a type-safe `ExitStatus` by wrapping it without copying. |
a7813a04 XL |
408 | impl From<c::DWORD> for ExitStatus { |
409 | fn from(u: c::DWORD) -> ExitStatus { | |
410 | ExitStatus(u) | |
411 | } | |
412 | } | |
413 | ||
85aaf69f | 414 | impl fmt::Display for ExitStatus { |
532ac7d7 | 415 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
0731742a XL |
416 | // Windows exit codes with the high bit set typically mean some form of |
417 | // unhandled exception or warning. In this scenario printing the exit | |
418 | // code in decimal doesn't always make sense because it's a very large | |
419 | // and somewhat gibberish number. The hex code is a bit more | |
420 | // recognizable and easier to search for, so print that. | |
421 | if self.0 & 0x80000000 != 0 { | |
422 | write!(f, "exit code: {:#x}", self.0) | |
423 | } else { | |
424 | write!(f, "exit code: {}", self.0) | |
425 | } | |
85aaf69f SL |
426 | } |
427 | } | |
428 | ||
0531ce1d XL |
429 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
430 | pub struct ExitCode(c::DWORD); | |
431 | ||
432 | impl ExitCode { | |
433 | pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); | |
434 | pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); | |
435 | ||
83c7162d | 436 | #[inline] |
0531ce1d XL |
437 | pub fn as_i32(&self) -> i32 { |
438 | self.0 as i32 | |
439 | } | |
440 | } | |
441 | ||
92a42be0 SL |
442 | fn zeroed_startupinfo() -> c::STARTUPINFO { |
443 | c::STARTUPINFO { | |
85aaf69f SL |
444 | cb: 0, |
445 | lpReserved: ptr::null_mut(), | |
446 | lpDesktop: ptr::null_mut(), | |
447 | lpTitle: ptr::null_mut(), | |
448 | dwX: 0, | |
449 | dwY: 0, | |
450 | dwXSize: 0, | |
451 | dwYSize: 0, | |
452 | dwXCountChars: 0, | |
453 | dwYCountCharts: 0, | |
454 | dwFillAttribute: 0, | |
455 | dwFlags: 0, | |
456 | wShowWindow: 0, | |
457 | cbReserved2: 0, | |
458 | lpReserved2: ptr::null_mut(), | |
92a42be0 SL |
459 | hStdInput: c::INVALID_HANDLE_VALUE, |
460 | hStdOutput: c::INVALID_HANDLE_VALUE, | |
461 | hStdError: c::INVALID_HANDLE_VALUE, | |
85aaf69f SL |
462 | } |
463 | } | |
464 | ||
92a42be0 SL |
465 | fn zeroed_process_information() -> c::PROCESS_INFORMATION { |
466 | c::PROCESS_INFORMATION { | |
85aaf69f SL |
467 | hProcess: ptr::null_mut(), |
468 | hThread: ptr::null_mut(), | |
469 | dwProcessId: 0, | |
dfeec247 | 470 | dwThreadId: 0, |
85aaf69f SL |
471 | } |
472 | } | |
473 | ||
7453a54e SL |
474 | // Produces a wide string *without terminating null*; returns an error if |
475 | // `prog` or any of the `args` contain a nul. | |
6a06907d | 476 | fn make_command_line(prog: &OsStr, args: &[OsString], force_quotes: bool) -> io::Result<Vec<u16>> { |
d9579d0f AL |
477 | // Encode the command and arguments in a command line string such |
478 | // that the spawned process may recover them using CommandLineToArgvW. | |
85aaf69f | 479 | let mut cmd: Vec<u16> = Vec::new(); |
041b39d2 XL |
480 | // Always quote the program name so CreateProcess doesn't interpret args as |
481 | // part of the name if the binary wasn't found first time. | |
482 | append_arg(&mut cmd, prog, true)?; | |
85aaf69f SL |
483 | for arg in args { |
484 | cmd.push(' ' as u16); | |
6a06907d | 485 | append_arg(&mut cmd, arg, force_quotes)?; |
85aaf69f | 486 | } |
7453a54e | 487 | return Ok(cmd); |
85aaf69f | 488 | |
041b39d2 | 489 | fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, force_quotes: bool) -> io::Result<()> { |
85aaf69f SL |
490 | // If an argument has 0 characters then we need to quote it to ensure |
491 | // that it actually gets passed through on the command line or otherwise | |
492 | // it will be dropped entirely when parsed on the other end. | |
54a0048b | 493 | ensure_no_nuls(arg)?; |
85aaf69f | 494 | let arg_bytes = &arg.as_inner().inner.as_inner(); |
dfeec247 XL |
495 | let quote = force_quotes |
496 | || arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') | |
9346a6ac | 497 | || arg_bytes.is_empty(); |
85aaf69f SL |
498 | if quote { |
499 | cmd.push('"' as u16); | |
500 | } | |
501 | ||
d9579d0f | 502 | let mut backslashes: usize = 0; |
e74abb32 | 503 | for x in arg.encode_wide() { |
d9579d0f AL |
504 | if x == '\\' as u16 { |
505 | backslashes += 1; | |
85aaf69f | 506 | } else { |
d9579d0f AL |
507 | if x == '"' as u16 { |
508 | // Add n+1 backslashes to total 2n+1 before internal '"'. | |
0731742a | 509 | cmd.extend((0..=backslashes).map(|_| '\\' as u16)); |
d9579d0f AL |
510 | } |
511 | backslashes = 0; | |
85aaf69f | 512 | } |
d9579d0f | 513 | cmd.push(x); |
85aaf69f SL |
514 | } |
515 | ||
516 | if quote { | |
d9579d0f | 517 | // Add n backslashes to total 2n before ending '"'. |
8faf50e0 | 518 | cmd.extend((0..backslashes).map(|_| '\\' as u16)); |
85aaf69f SL |
519 | cmd.push('"' as u16); |
520 | } | |
7453a54e | 521 | Ok(()) |
85aaf69f SL |
522 | } |
523 | } | |
524 | ||
dfeec247 | 525 | fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut c_void, Vec<u16>)> { |
85aaf69f SL |
526 | // On Windows we pass an "environment block" which is not a char**, but |
527 | // rather a concatenation of null-terminated k=v\0 sequences, with a final | |
528 | // \0 to terminate. | |
ff7c6d11 XL |
529 | if let Some(env) = maybe_env { |
530 | let mut blk = Vec::new(); | |
531 | ||
532 | for (k, v) in env { | |
533 | blk.extend(ensure_no_nuls(k.0)?.encode_wide()); | |
534 | blk.push('=' as u16); | |
535 | blk.extend(ensure_no_nuls(v)?.encode_wide()); | |
85aaf69f | 536 | blk.push(0); |
85aaf69f | 537 | } |
ff7c6d11 XL |
538 | blk.push(0); |
539 | Ok((blk.as_mut_ptr() as *mut c_void, blk)) | |
540 | } else { | |
541 | Ok((ptr::null_mut(), Vec::new())) | |
85aaf69f SL |
542 | } |
543 | } | |
544 | ||
7453a54e | 545 | fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> { |
85aaf69f | 546 | match d { |
bd371182 | 547 | Some(dir) => { |
54a0048b | 548 | let mut dir_str: Vec<u16> = ensure_no_nuls(dir)?.encode_wide().collect(); |
bd371182 | 549 | dir_str.push(0); |
7453a54e | 550 | Ok((dir_str.as_ptr(), dir_str)) |
dfeec247 XL |
551 | } |
552 | None => Ok((ptr::null(), Vec::new())), | |
85aaf69f SL |
553 | } |
554 | } | |
555 | ||
1b1a35ee XL |
556 | pub struct CommandArgs<'a> { |
557 | iter: crate::slice::Iter<'a, OsString>, | |
558 | } | |
559 | ||
560 | impl<'a> Iterator for CommandArgs<'a> { | |
561 | type Item = &'a OsStr; | |
562 | fn next(&mut self) -> Option<&'a OsStr> { | |
563 | self.iter.next().map(|s| s.as_ref()) | |
564 | } | |
565 | fn size_hint(&self) -> (usize, Option<usize>) { | |
566 | self.iter.size_hint() | |
567 | } | |
568 | } | |
85aaf69f | 569 | |
1b1a35ee XL |
570 | impl<'a> ExactSizeIterator for CommandArgs<'a> { |
571 | fn len(&self) -> usize { | |
572 | self.iter.len() | |
573 | } | |
574 | fn is_empty(&self) -> bool { | |
575 | self.iter.is_empty() | |
576 | } | |
577 | } | |
578 | ||
579 | impl<'a> fmt::Debug for CommandArgs<'a> { | |
580 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
581 | f.debug_list().entries(self.iter.clone()).finish() | |
85aaf69f SL |
582 | } |
583 | } |