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