2015-02-06 09:42:57 -08:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use prelude::v1::*;
|
|
|
|
|
|
|
|
use ascii::*;
|
|
|
|
use collections::HashMap;
|
|
|
|
use collections;
|
2015-04-27 13:44:20 -07:00
|
|
|
use env::split_paths;
|
2015-02-06 09:42:57 -08:00
|
|
|
use env;
|
|
|
|
use ffi::{OsString, OsStr};
|
|
|
|
use fmt;
|
2015-02-23 10:59:17 -08:00
|
|
|
use fs;
|
2015-02-06 09:42:57 -08:00
|
|
|
use io::{self, Error};
|
2015-11-02 16:23:22 -08:00
|
|
|
use libc::c_void;
|
2015-04-27 13:44:20 -07:00
|
|
|
use mem;
|
std: Stabilize portions of `std::os::$platform`
This commit starts to organize the `std::os::$platform` modules and in the
process stabilizes some of the functionality contained within. The organization
of these modules will reflect the organization of the standard library itself
with extension traits for primitives in the same corresponding module.
The OS-specific modules will grow more functionality over time including
concrete types that are not extending functionality of other structures, and
these will either go into the closest module in `std::os::$platform` or they
will grow a new module in the hierarchy.
The following items are now stable:
* `os::{unix, windows}`
* `unix::ffi`
* `unix::ffi::OsStrExt`
* `unix::ffi::OsStrExt::{from_bytes, as_bytes, to_cstring}`
* `unix::ffi::OsString`
* `unix::ffi::OsStringExt::{from_vec, into_vec}`
* `unix::process`
* `unix::process::CommandExt`
* `unix::process::CommandExt::{uid, gid}`
* `unix::process::ExitStatusExt`
* `unix::process::ExitStatusExt::signal`
* `unix::prelude`
* `windows::ffi`
* `windows::ffi::OsStringExt`
* `windows::ffi::OsStringExt::from_wide`
* `windows::ffi::OsStrExt`
* `windows::ffi::OsStrExt::encode_wide`
* `windows::prelude`
The following items remain unstable:
* `unix::io`
* `unix::io::{Fd, AsRawFd}`
* `unix::fs::{PermissionsExt, OpenOptionsExt}`
* `windows::io`
* `windows::io::{Handle, AsRawHandle}`
* `windows::io::{Socket, AsRawSocket}`
* `windows::fs`
* `windows::fs::OpenOptionsExt`
Due to the reorgnization of the platform extension modules, this commit is a
breaking change. Most imports can be fixed by adding the relevant libstd module
in the `use` path (such as `ffi` or `fs`).
[breaking-change]
2015-03-13 17:12:38 -07:00
|
|
|
use os::windows::ffi::OsStrExt;
|
2015-04-27 13:44:20 -07:00
|
|
|
use path::Path;
|
2015-02-06 09:42:57 -08:00
|
|
|
use ptr;
|
2015-05-27 11:18:36 +03:00
|
|
|
use sync::StaticMutex;
|
2015-04-27 13:44:20 -07:00
|
|
|
use sys::c;
|
2015-11-02 16:23:22 -08:00
|
|
|
|
2015-05-05 16:35:15 -07:00
|
|
|
use sys::fs::{OpenOptions, File};
|
2015-05-12 11:03:49 -07:00
|
|
|
use sys::handle::{Handle, RawHandle};
|
2015-04-27 13:44:20 -07:00
|
|
|
use sys::stdio;
|
2015-02-06 09:42:57 -08:00
|
|
|
use sys::{self, cvt};
|
|
|
|
use sys_common::{AsInner, FromInner};
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Command
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
fn mk_key(s: &OsStr) -> OsString {
|
|
|
|
FromInner::from_inner(sys::os_str::Buf {
|
|
|
|
inner: s.as_inner().inner.to_ascii_uppercase()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Command {
|
|
|
|
pub program: OsString,
|
|
|
|
pub args: Vec<OsString>,
|
|
|
|
pub env: Option<HashMap<OsString, OsString>>,
|
|
|
|
pub cwd: Option<OsString>,
|
|
|
|
pub detach: bool, // not currently exposed in std::process
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
pub fn new(program: &OsStr) -> Command {
|
|
|
|
Command {
|
|
|
|
program: program.to_os_string(),
|
|
|
|
args: Vec::new(),
|
|
|
|
env: None,
|
|
|
|
cwd: None,
|
|
|
|
detach: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arg(&mut self, arg: &OsStr) {
|
|
|
|
self.args.push(arg.to_os_string())
|
|
|
|
}
|
|
|
|
pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) {
|
|
|
|
self.args.extend(args.map(OsStr::to_os_string))
|
|
|
|
}
|
|
|
|
fn init_env_map(&mut self){
|
|
|
|
if self.env.is_none() {
|
|
|
|
self.env = Some(env::vars_os().map(|(key, val)| {
|
|
|
|
(mk_key(&key), val)
|
|
|
|
}).collect());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn env(&mut self, key: &OsStr, val: &OsStr) {
|
|
|
|
self.init_env_map();
|
|
|
|
self.env.as_mut().unwrap().insert(mk_key(key), val.to_os_string());
|
|
|
|
}
|
|
|
|
pub fn env_remove(&mut self, key: &OsStr) {
|
|
|
|
self.init_env_map();
|
|
|
|
self.env.as_mut().unwrap().remove(&mk_key(key));
|
|
|
|
}
|
|
|
|
pub fn env_clear(&mut self) {
|
|
|
|
self.env = Some(HashMap::new())
|
|
|
|
}
|
|
|
|
pub fn cwd(&mut self, dir: &OsStr) {
|
|
|
|
self.cwd = Some(dir.to_os_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Processes
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/// A value representing a child process.
|
|
|
|
///
|
|
|
|
/// The lifetime of this value is linked to the lifetime of the actual
|
|
|
|
/// process - the Process destructor calls self.finish() which waits
|
|
|
|
/// for the process to terminate.
|
|
|
|
pub struct Process {
|
|
|
|
handle: Handle,
|
|
|
|
}
|
|
|
|
|
2015-04-03 15:44:14 -07:00
|
|
|
pub enum Stdio {
|
|
|
|
Inherit,
|
|
|
|
None,
|
2015-11-02 16:23:22 -08:00
|
|
|
Raw(c::HANDLE),
|
2015-04-03 15:44:14 -07:00
|
|
|
}
|
|
|
|
|
2015-06-09 16:41:14 -07:00
|
|
|
pub type RawStdio = Handle;
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
impl Process {
|
|
|
|
pub fn spawn(cfg: &Command,
|
2015-04-27 13:44:20 -07:00
|
|
|
in_handle: Stdio,
|
|
|
|
out_handle: Stdio,
|
|
|
|
err_handle: Stdio) -> io::Result<Process>
|
2015-02-06 09:42:57 -08:00
|
|
|
{
|
2015-04-27 13:44:20 -07:00
|
|
|
// To have the spawning semantics of unix/windows stay the same, we need
|
|
|
|
// to read the *child's* PATH if one is provided. See #15149 for more
|
|
|
|
// details.
|
2015-02-06 09:42:57 -08:00
|
|
|
let program = cfg.env.as_ref().and_then(|env| {
|
|
|
|
for (key, v) in env {
|
2015-04-17 23:45:55 -07:00
|
|
|
if OsStr::new("PATH") != &**key { continue }
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
// Split the value and test each path to see if the
|
|
|
|
// program exists.
|
|
|
|
for path in split_paths(&v) {
|
|
|
|
let path = path.join(cfg.program.to_str().unwrap())
|
|
|
|
.with_extension(env::consts::EXE_EXTENSION);
|
2015-02-23 10:59:17 -08:00
|
|
|
if fs::metadata(&path).is_ok() {
|
|
|
|
return Some(path.into_os_string())
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
None
|
|
|
|
});
|
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
let mut si = zeroed_startupinfo();
|
2015-11-02 16:23:22 -08:00
|
|
|
si.cb = mem::size_of::<c::STARTUPINFO>() as c::DWORD;
|
|
|
|
si.dwFlags = c::STARTF_USESTDHANDLES;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE));
|
|
|
|
let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE));
|
|
|
|
let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE));
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
si.hStdInput = stdin.raw();
|
|
|
|
si.hStdOutput = stdout.raw();
|
|
|
|
si.hStdError = stderr.raw();
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
let program = program.as_ref().unwrap_or(&cfg.program);
|
|
|
|
let mut cmd_str = make_command_line(program, &cfg.args);
|
|
|
|
cmd_str.push(0); // add null terminator
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
// stolen from the libuv code.
|
2015-11-02 16:23:22 -08:00
|
|
|
let mut flags = c::CREATE_UNICODE_ENVIRONMENT;
|
2015-04-27 13:44:20 -07:00
|
|
|
if cfg.detach {
|
2015-11-02 16:23:22 -08:00
|
|
|
flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP;
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
let (envp, _data) = make_envp(cfg.env.as_ref());
|
|
|
|
let (dirp, _data) = make_dirp(cfg.cwd.as_ref());
|
|
|
|
let mut pi = zeroed_process_information();
|
|
|
|
try!(unsafe {
|
|
|
|
// `CreateProcess` is racy!
|
|
|
|
// http://support.microsoft.com/kb/315939
|
2015-05-27 11:18:36 +03:00
|
|
|
static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new();
|
2015-04-27 13:44:20 -07:00
|
|
|
let _lock = CREATE_PROCESS_LOCK.lock();
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
cvt(c::CreateProcessW(ptr::null(),
|
|
|
|
cmd_str.as_mut_ptr(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
ptr::null_mut(),
|
|
|
|
c::TRUE, flags, envp, dirp,
|
|
|
|
&mut si, &mut pi))
|
2015-04-27 13:44:20 -07:00
|
|
|
});
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
// We close the thread handle because we don't care about keeping
|
|
|
|
// the thread id valid, and we aren't keeping the thread handle
|
|
|
|
// around to be able to close it later.
|
|
|
|
drop(Handle::new(pi.hThread));
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
Ok(Process { handle: Handle::new(pi.hProcess) })
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub unsafe fn kill(&self) -> io::Result<()> {
|
2015-11-02 16:23:22 -08:00
|
|
|
try!(cvt(c::TerminateProcess(self.handle.raw(), 1)));
|
2015-02-06 09:42:57 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-04-16 09:44:05 -07:00
|
|
|
pub fn id(&self) -> u32 {
|
|
|
|
unsafe {
|
|
|
|
c::GetProcessId(self.handle.raw()) as u32
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
pub fn wait(&self) -> io::Result<ExitStatus> {
|
|
|
|
unsafe {
|
2015-11-02 16:23:22 -08:00
|
|
|
let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
|
|
|
|
if res != c::WAIT_OBJECT_0 {
|
2015-10-29 13:45:56 -07:00
|
|
|
return Err(Error::last_os_error())
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-29 13:45:56 -07:00
|
|
|
let mut status = 0;
|
2015-11-02 16:23:22 -08:00
|
|
|
try!(cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status)));
|
2015-10-29 13:45:56 -07:00
|
|
|
Ok(ExitStatus(status))
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
2015-05-12 11:03:49 -07:00
|
|
|
|
|
|
|
pub fn handle(&self) -> &Handle { &self.handle }
|
2015-07-15 23:31:24 -07:00
|
|
|
|
|
|
|
pub fn into_handle(self) -> Handle { self.handle }
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
2015-11-02 16:23:22 -08:00
|
|
|
pub struct ExitStatus(c::DWORD);
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
impl ExitStatus {
|
|
|
|
pub fn success(&self) -> bool {
|
|
|
|
self.0 == 0
|
|
|
|
}
|
|
|
|
pub fn code(&self) -> Option<i32> {
|
2015-10-29 13:45:56 -07:00
|
|
|
Some(self.0 as i32)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ExitStatus {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "exit code: {}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
fn zeroed_startupinfo() -> c::STARTUPINFO {
|
|
|
|
c::STARTUPINFO {
|
2015-02-06 09:42:57 -08:00
|
|
|
cb: 0,
|
|
|
|
lpReserved: ptr::null_mut(),
|
|
|
|
lpDesktop: ptr::null_mut(),
|
|
|
|
lpTitle: ptr::null_mut(),
|
|
|
|
dwX: 0,
|
|
|
|
dwY: 0,
|
|
|
|
dwXSize: 0,
|
|
|
|
dwYSize: 0,
|
|
|
|
dwXCountChars: 0,
|
|
|
|
dwYCountCharts: 0,
|
|
|
|
dwFillAttribute: 0,
|
|
|
|
dwFlags: 0,
|
|
|
|
wShowWindow: 0,
|
|
|
|
cbReserved2: 0,
|
|
|
|
lpReserved2: ptr::null_mut(),
|
2015-11-02 16:23:22 -08:00
|
|
|
hStdInput: c::INVALID_HANDLE_VALUE,
|
|
|
|
hStdOutput: c::INVALID_HANDLE_VALUE,
|
|
|
|
hStdError: c::INVALID_HANDLE_VALUE,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
fn zeroed_process_information() -> c::PROCESS_INFORMATION {
|
|
|
|
c::PROCESS_INFORMATION {
|
2015-02-06 09:42:57 -08:00
|
|
|
hProcess: ptr::null_mut(),
|
|
|
|
hThread: ptr::null_mut(),
|
|
|
|
dwProcessId: 0,
|
|
|
|
dwThreadId: 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Produces a wide string *without terminating null*
|
|
|
|
fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec<u16> {
|
2015-04-25 15:35:22 -04:00
|
|
|
// Encode the command and arguments in a command line string such
|
|
|
|
// that the spawned process may recover them using CommandLineToArgvW.
|
2015-02-06 09:42:57 -08:00
|
|
|
let mut cmd: Vec<u16> = Vec::new();
|
|
|
|
append_arg(&mut cmd, prog);
|
|
|
|
for arg in args {
|
|
|
|
cmd.push(' ' as u16);
|
|
|
|
append_arg(&mut cmd, arg);
|
|
|
|
}
|
|
|
|
return cmd;
|
|
|
|
|
|
|
|
fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr) {
|
|
|
|
// If an argument has 0 characters then we need to quote it to ensure
|
|
|
|
// that it actually gets passed through on the command line or otherwise
|
|
|
|
// it will be dropped entirely when parsed on the other end.
|
|
|
|
let arg_bytes = &arg.as_inner().inner.as_inner();
|
|
|
|
let quote = arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t')
|
2015-03-24 16:53:34 -07:00
|
|
|
|| arg_bytes.is_empty();
|
2015-02-06 09:42:57 -08:00
|
|
|
if quote {
|
|
|
|
cmd.push('"' as u16);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut iter = arg.encode_wide();
|
2015-04-25 15:35:22 -04:00
|
|
|
let mut backslashes: usize = 0;
|
2015-02-06 09:42:57 -08:00
|
|
|
while let Some(x) = iter.next() {
|
2015-04-25 15:35:22 -04:00
|
|
|
if x == '\\' as u16 {
|
|
|
|
backslashes += 1;
|
2015-02-06 09:42:57 -08:00
|
|
|
} else {
|
2015-04-25 15:35:22 -04:00
|
|
|
if x == '"' as u16 {
|
|
|
|
// Add n+1 backslashes to total 2n+1 before internal '"'.
|
|
|
|
for _ in 0..(backslashes+1) {
|
|
|
|
cmd.push('\\' as u16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
backslashes = 0;
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-04-25 15:35:22 -04:00
|
|
|
cmd.push(x);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if quote {
|
2015-04-25 15:35:22 -04:00
|
|
|
// Add n backslashes to total 2n before ending '"'.
|
|
|
|
for _ in 0..backslashes {
|
|
|
|
cmd.push('\\' as u16);
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
cmd.push('"' as u16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
fn make_envp(env: Option<&collections::HashMap<OsString, OsString>>)
|
|
|
|
-> (*mut c_void, Vec<u16>) {
|
2015-02-06 09:42:57 -08:00
|
|
|
// On Windows we pass an "environment block" which is not a char**, but
|
|
|
|
// rather a concatenation of null-terminated k=v\0 sequences, with a final
|
|
|
|
// \0 to terminate.
|
|
|
|
match env {
|
|
|
|
Some(env) => {
|
|
|
|
let mut blk = Vec::new();
|
|
|
|
|
|
|
|
for pair in env {
|
|
|
|
blk.extend(pair.0.encode_wide());
|
|
|
|
blk.push('=' as u16);
|
|
|
|
blk.extend(pair.1.encode_wide());
|
|
|
|
blk.push(0);
|
|
|
|
}
|
|
|
|
blk.push(0);
|
2015-04-27 13:44:20 -07:00
|
|
|
(blk.as_mut_ptr() as *mut c_void, blk)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-04-27 13:44:20 -07:00
|
|
|
_ => (ptr::null_mut(), Vec::new())
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-27 13:44:20 -07:00
|
|
|
fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec<u16>) {
|
2015-02-06 09:42:57 -08:00
|
|
|
match d {
|
2015-04-27 13:44:20 -07:00
|
|
|
Some(dir) => {
|
|
|
|
let mut dir_str: Vec<u16> = dir.encode_wide().collect();
|
|
|
|
dir_str.push(0);
|
|
|
|
(dir_str.as_ptr(), dir_str)
|
|
|
|
},
|
|
|
|
None => (ptr::null(), Vec::new())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Stdio {
|
2015-11-02 16:23:22 -08:00
|
|
|
fn to_handle(&self, stdio_id: c::DWORD) -> io::Result<Handle> {
|
2015-04-27 13:44:20 -07:00
|
|
|
match *self {
|
2016-01-24 21:14:56 -08:00
|
|
|
// If no stdio handle is available, then inherit means that it
|
|
|
|
// should still be unavailable so propagate the
|
|
|
|
// INVALID_HANDLE_VALUE.
|
2015-04-27 13:44:20 -07:00
|
|
|
Stdio::Inherit => {
|
2016-01-24 21:14:56 -08:00
|
|
|
match stdio::get(stdio_id) {
|
|
|
|
Ok(io) => io.handle().duplicate(0, true,
|
|
|
|
c::DUPLICATE_SAME_ACCESS),
|
|
|
|
Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
|
|
|
|
}
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
2015-06-09 16:41:14 -07:00
|
|
|
Stdio::Raw(handle) => {
|
2015-11-02 16:23:22 -08:00
|
|
|
RawHandle::new(handle).duplicate(0, true, c::DUPLICATE_SAME_ACCESS)
|
2015-04-27 13:44:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Similarly to unix, we don't actually leave holes for the
|
|
|
|
// stdio file descriptors, but rather open up /dev/null
|
|
|
|
// equivalents. These equivalents are drawn from libuv's
|
|
|
|
// windows process spawning.
|
|
|
|
Stdio::None => {
|
2015-11-02 16:23:22 -08:00
|
|
|
let size = mem::size_of::<c::SECURITY_ATTRIBUTES>();
|
|
|
|
let mut sa = c::SECURITY_ATTRIBUTES {
|
|
|
|
nLength: size as c::DWORD,
|
2015-04-27 13:44:20 -07:00
|
|
|
lpSecurityDescriptor: ptr::null_mut(),
|
|
|
|
bInheritHandle: 1,
|
|
|
|
};
|
|
|
|
let mut opts = OpenOptions::new();
|
|
|
|
opts.read(stdio_id == c::STD_INPUT_HANDLE);
|
|
|
|
opts.write(stdio_id != c::STD_INPUT_HANDLE);
|
|
|
|
opts.security_attributes(&mut sa);
|
|
|
|
File::open(Path::new("NUL"), &opts).map(|file| {
|
|
|
|
file.into_handle()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use prelude::v1::*;
|
|
|
|
use ffi::{OsStr, OsString};
|
|
|
|
use super::make_command_line;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_make_command_line() {
|
|
|
|
fn test_wrapper(prog: &str, args: &[&str]) -> String {
|
|
|
|
String::from_utf16(
|
2015-04-17 23:45:55 -07:00
|
|
|
&make_command_line(OsStr::new(prog),
|
2015-03-31 18:59:36 -07:00
|
|
|
&args.iter()
|
|
|
|
.map(|a| OsString::from(a))
|
|
|
|
.collect::<Vec<OsString>>())).unwrap()
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("prog", &["aaa", "bbb", "ccc"]),
|
|
|
|
"prog aaa bbb ccc"
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
|
|
|
|
"\"C:\\Program Files\\blah\\blah.exe\" aaa"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
|
|
|
|
"\"C:\\Program Files\\test\" aa\\\"bb"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("echo", &["a b c"]),
|
|
|
|
"echo \"a b c\""
|
|
|
|
);
|
2015-04-25 15:35:22 -04:00
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("echo", &["\" \\\" \\", "\\"]),
|
|
|
|
"echo \"\\\" \\\\\\\" \\\\\" \\"
|
|
|
|
);
|
2015-02-06 09:42:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
|
|
|
|
"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|