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