auto merge of #9749 : alexcrichton/rust/less-io, r=brson
This implements a number of the baby steps needed to start eliminating everything inside of `std::io`. It turns out that there are a *lot* of users of that module, so I'm going to try to tackle them separately instead of bringing down the whole system all at once. This pull implements a large amount of unimplemented functionality inside of `std::rt::io` including: * Native file I/O (file descriptors, *FILE) * Native stdio (through the native file descriptors) * Native processes (extracted from `std::run`) I also found that there are a number of users of `std::io` which desire to read an input line-by-line, so I added an implementation of `read_until` and `read_line` to `BufferedReader`. With all of these changes in place, I started to axe various usages of `std::io`. There's a lot of one-off uses here-and-there, but the major use-case remaining that doesn't have a fantastic solution is `extra::json`. I ran into a few compiler bugs when attempting to remove that, so I figured I'd come back to it later instead. There is one fairly major change in this pull, and it's moving from native stdio to uv stdio via `print` and `println`. Unfortunately logging still goes through native I/O (via `dumb_println`). This is going to need some thinking, because I still want the goal of logging/printing to be 0 allocations, and this is not possible if `io::stdio::stderr()` is called on each log message. Instead I think that this may need to be cached as the `logger` field inside the `Task` struct, but that will require a little more workings to get right (this is also a similar problem for print/println, do we cache `stdout()` to not have to re-create it every time?).
This commit is contained in:
commit
0ede2ea4e2
21 changed files with 1385 additions and 1007 deletions
|
|
@ -8,17 +8,21 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::io;
|
||||
|
||||
pub struct ExpectedError { line: uint, kind: ~str, msg: ~str }
|
||||
|
||||
// Load any test directives embedded in the file
|
||||
pub fn load_errors(testfile: &Path) -> ~[ExpectedError] {
|
||||
use std::rt::io::Open;
|
||||
use std::rt::io::file::FileInfo;
|
||||
use std::rt::io::buffered::BufferedReader;
|
||||
|
||||
let mut error_patterns = ~[];
|
||||
let rdr = io::file_reader(testfile).unwrap();
|
||||
let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap());
|
||||
let mut line_num = 1u;
|
||||
while !rdr.eof() {
|
||||
let ln = rdr.read_line();
|
||||
loop {
|
||||
let ln = match rdr.read_line() {
|
||||
Some(ln) => ln, None => break,
|
||||
};
|
||||
error_patterns.push_all_move(parse_expected(line_num, ln));
|
||||
line_num += 1u;
|
||||
}
|
||||
|
|
@ -26,6 +30,7 @@ pub fn load_errors(testfile: &Path) -> ~[ExpectedError] {
|
|||
}
|
||||
|
||||
fn parse_expected(line_num: uint, line: ~str) -> ~[ExpectedError] {
|
||||
let line = line.trim();
|
||||
let error_tag = ~"//~";
|
||||
let mut idx;
|
||||
match line.find_str(error_tag) {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ use common::config;
|
|||
use common;
|
||||
use util;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub struct TestProps {
|
||||
// Lines that should be expected, in order, on standard out
|
||||
error_patterns: ~[~str],
|
||||
|
|
@ -104,17 +102,23 @@ pub fn is_test_ignored(config: &config, testfile: &Path) -> bool {
|
|||
!val
|
||||
}
|
||||
|
||||
fn iter_header(testfile: &Path, it: &fn(~str) -> bool) -> bool {
|
||||
let rdr = io::file_reader(testfile).unwrap();
|
||||
while !rdr.eof() {
|
||||
let ln = rdr.read_line();
|
||||
fn iter_header(testfile: &Path, it: &fn(&str) -> bool) -> bool {
|
||||
use std::rt::io::Open;
|
||||
use std::rt::io::file::FileInfo;
|
||||
use std::rt::io::buffered::BufferedReader;
|
||||
|
||||
let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap());
|
||||
loop {
|
||||
let ln = match rdr.read_line() {
|
||||
Some(ln) => ln, None => break
|
||||
};
|
||||
|
||||
// Assume that any directives will be found before the first
|
||||
// module or function. This doesn't seem to be an optimization
|
||||
// with a warm page cache. Maybe with a cold one.
|
||||
if ln.starts_with("fn") || ln.starts_with("mod") {
|
||||
return true;
|
||||
} else { if !(it(ln)) { return false; } }
|
||||
} else { if !(it(ln.trim())) { return false; } }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ pub fn run(lib_path: &str,
|
|||
});
|
||||
|
||||
for input in input.iter() {
|
||||
proc.input().write_str(*input);
|
||||
proc.input().write(input.as_bytes());
|
||||
}
|
||||
let output = proc.finish_with_output();
|
||||
|
||||
|
|
|
|||
|
|
@ -68,9 +68,6 @@ fn debug_mem() -> bool {
|
|||
/// Destroys all managed memory (i.e. @ boxes) held by the current task.
|
||||
pub unsafe fn annihilate() {
|
||||
use rt::local_heap::local_free;
|
||||
use io::WriterUtil;
|
||||
use io;
|
||||
use libc;
|
||||
use sys;
|
||||
use managed;
|
||||
|
||||
|
|
@ -126,14 +123,10 @@ pub unsafe fn annihilate() {
|
|||
|
||||
if debug_mem() {
|
||||
// We do logging here w/o allocation.
|
||||
let dbg = libc::STDERR_FILENO as io::fd_t;
|
||||
dbg.write_str("annihilator stats:");
|
||||
dbg.write_str("\n total_boxes: ");
|
||||
dbg.write_uint(stats.n_total_boxes);
|
||||
dbg.write_str("\n unique_boxes: ");
|
||||
dbg.write_uint(stats.n_unique_boxes);
|
||||
dbg.write_str("\n bytes_freed: ");
|
||||
dbg.write_uint(stats.n_bytes_freed);
|
||||
dbg.write_str("\n");
|
||||
rterrln!("annihilator stats:\n \
|
||||
total boxes: {}\n \
|
||||
unique boxes: {}\n \
|
||||
bytes freed: {}",
|
||||
stats.n_total_boxes, stats.n_unique_boxes, stats.n_bytes_freed);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1233,14 +1233,6 @@ impl Writer for *libc::FILE {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer {
|
||||
if cleanup {
|
||||
@Wrapper { base: f, cleanup: FILERes::new(f) } as @Writer
|
||||
} else {
|
||||
@f as @Writer
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for fd_t {
|
||||
fn write(&self, v: &[u8]) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
|
@ -1618,25 +1610,6 @@ pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result<@Writer, ~str> {
|
|||
mk_file_writer(path, flags).and_then(|w| Ok(w))
|
||||
}
|
||||
|
||||
|
||||
// FIXME: fileflags // #2004
|
||||
pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
unsafe {
|
||||
let f = do path.with_c_str |pathbuf| {
|
||||
do "w".with_c_str |modebuf| {
|
||||
libc::fopen(pathbuf, modebuf)
|
||||
}
|
||||
};
|
||||
return if f as uint == 0u {
|
||||
Err(~"error opening " + path.to_str())
|
||||
} else {
|
||||
Ok(FILE_writer(f, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME (#2004) it would be great if this could be a const
|
||||
// FIXME (#2004) why are these different from the way stdin() is
|
||||
// implemented?
|
||||
|
|
@ -2086,16 +2059,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buffered_file_writer_bad_name() {
|
||||
match io::buffered_file_writer(&Path("?/?")) {
|
||||
Err(e) => {
|
||||
assert!(e.starts_with("error opening"));
|
||||
}
|
||||
Ok(_) => fail2!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_buffer_overwrite() {
|
||||
let wr = BytesWriter::new();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
macro_rules! rterrln (
|
||||
($($arg:tt)*) => ( {
|
||||
::rt::util::dumb_println(format!($($arg)*));
|
||||
format_args!(::rt::util::dumb_println, $($arg)*)
|
||||
} )
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub use option::{Option, Some, None};
|
|||
pub use result::{Result, Ok, Err};
|
||||
|
||||
// Reexported functions
|
||||
pub use io::{print, println};
|
||||
pub use rt::io::stdio::{print, println};
|
||||
pub use iter::range;
|
||||
pub use from_str::from_str;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,8 @@
|
|||
// except according to those terms.
|
||||
|
||||
use cell::Cell;
|
||||
use c_str::ToCStr;
|
||||
use cast::transmute;
|
||||
use io::{Writer, WriterUtil};
|
||||
use io;
|
||||
use libc::{c_char, size_t, STDERR_FILENO};
|
||||
use c_str::{ToCStr, CString};
|
||||
use libc::{c_char, size_t};
|
||||
use option::{Option, None, Some};
|
||||
use ptr::RawPtr;
|
||||
use rt::env;
|
||||
|
|
@ -113,51 +110,10 @@ unsafe fn debug_borrow<T,P:RawPtr<T>>(tag: &'static str,
|
|||
new_bits: uint,
|
||||
filename: *c_char,
|
||||
line: size_t) {
|
||||
let dbg = STDERR_FILENO as io::fd_t;
|
||||
dbg.write_str(tag);
|
||||
dbg.write_hex(p.to_uint());
|
||||
dbg.write_str(" ");
|
||||
dbg.write_hex(old_bits);
|
||||
dbg.write_str(" ");
|
||||
dbg.write_hex(new_bits);
|
||||
dbg.write_str(" ");
|
||||
dbg.write_cstr(filename);
|
||||
dbg.write_str(":");
|
||||
dbg.write_hex(line as uint);
|
||||
dbg.write_str("\n");
|
||||
}
|
||||
}
|
||||
|
||||
trait DebugPrints {
|
||||
fn write_hex(&self, val: uint);
|
||||
unsafe fn write_cstr(&self, str: *c_char);
|
||||
}
|
||||
|
||||
impl DebugPrints for io::fd_t {
|
||||
fn write_hex(&self, mut i: uint) {
|
||||
let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||
'9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
static UINT_NIBBLES: uint = ::uint::bytes << 1;
|
||||
let mut buffer = [0_u8, ..UINT_NIBBLES+1];
|
||||
let mut c = UINT_NIBBLES;
|
||||
while c > 0 {
|
||||
c -= 1;
|
||||
buffer[c] = letters[i & 0xF] as u8;
|
||||
i >>= 4;
|
||||
}
|
||||
self.write(buffer.slice(0, UINT_NIBBLES));
|
||||
}
|
||||
|
||||
unsafe fn write_cstr(&self, p: *c_char) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
use libc::strlen;
|
||||
use vec;
|
||||
|
||||
let len = strlen(p);
|
||||
let p: *u8 = transmute(p);
|
||||
do vec::raw::buf_as_slice(p, len as uint) |s| {
|
||||
self.write(s);
|
||||
}
|
||||
let filename = CString::new(filename, false);
|
||||
rterrln!("{}{:#x} {:x} {:x} {}:{}",
|
||||
tag, p.to_uint(), old_bits, new_bits,
|
||||
filename.as_str().unwrap(), line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ use prelude::*;
|
|||
|
||||
use num;
|
||||
use vec;
|
||||
use str;
|
||||
use super::{Reader, Writer, Stream, Decorator};
|
||||
|
||||
// libuv recommends 64k buffers to maximize throughput
|
||||
|
|
@ -84,23 +85,69 @@ impl<R: Reader> BufferedReader<R> {
|
|||
pub fn new(inner: R) -> BufferedReader<R> {
|
||||
BufferedReader::with_capacity(DEFAULT_CAPACITY, inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Reader> Reader for BufferedReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
/// Reads the next line of input, interpreted as a sequence of utf-8
|
||||
/// encoded unicode codepoints. If a newline is encountered, then the
|
||||
/// newline is contained in the returned string.
|
||||
pub fn read_line(&mut self) -> Option<~str> {
|
||||
self.read_until('\n' as u8).map(str::from_utf8_owned)
|
||||
}
|
||||
|
||||
/// Reads a sequence of bytes leading up to a specified delimeter. Once the
|
||||
/// specified byte is encountered, reading ceases and the bytes up to and
|
||||
/// including the delimiter are returned.
|
||||
pub fn read_until(&mut self, byte: u8) -> Option<~[u8]> {
|
||||
let mut res = ~[];
|
||||
let mut used;
|
||||
loop {
|
||||
{
|
||||
let available = self.fill_buffer();
|
||||
match available.iter().position(|&b| b == byte) {
|
||||
Some(i) => {
|
||||
res.push_all(available.slice_to(i + 1));
|
||||
used = i + 1;
|
||||
break
|
||||
}
|
||||
None => {
|
||||
res.push_all(available);
|
||||
used = available.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
if used == 0 {
|
||||
break
|
||||
}
|
||||
self.pos += used;
|
||||
}
|
||||
self.pos += used;
|
||||
return if res.len() == 0 {None} else {Some(res)};
|
||||
}
|
||||
|
||||
fn fill_buffer<'a>(&'a mut self) -> &'a [u8] {
|
||||
if self.pos == self.cap {
|
||||
match self.inner.read(self.buf) {
|
||||
Some(cap) => {
|
||||
self.pos = 0;
|
||||
self.cap = cap;
|
||||
}
|
||||
None => return None
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
return self.buf.slice(self.pos, self.cap);
|
||||
}
|
||||
}
|
||||
|
||||
let src = self.buf.slice(self.pos, self.cap);
|
||||
let nread = num::min(src.len(), buf.len());
|
||||
vec::bytes::copy_memory(buf, src, nread);
|
||||
impl<R: Reader> Reader for BufferedReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
let nread = {
|
||||
let available = self.fill_buffer();
|
||||
if available.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
let nread = num::min(available.len(), buf.len());
|
||||
vec::bytes::copy_memory(buf, available, nread);
|
||||
nread
|
||||
};
|
||||
self.pos += nread;
|
||||
Some(nread)
|
||||
}
|
||||
|
|
@ -355,4 +402,15 @@ mod test {
|
|||
stream.write(buf);
|
||||
stream.flush();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_until() {
|
||||
let inner = MemReader::new(~[0, 1, 2, 1, 0]);
|
||||
let mut reader = BufferedReader::with_capacity(2, inner);
|
||||
assert_eq!(reader.read_until(0), Some(~[0]));
|
||||
assert_eq!(reader.read_until(2), Some(~[1, 2]));
|
||||
assert_eq!(reader.read_until(1), Some(~[1]));
|
||||
assert_eq!(reader.read_until(8), Some(~[0]));
|
||||
assert_eq!(reader.read_until(9), None);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ impl FileInfo for Path { }
|
|||
/// else { fail2!("nope"); }
|
||||
/// }
|
||||
/// ```
|
||||
trait DirectoryInfo : FileSystemInfo {
|
||||
pub trait DirectoryInfo : FileSystemInfo {
|
||||
/// Whether the underlying implemention (be it a file path,
|
||||
/// or something else) is pointing at a directory in the underlying FS.
|
||||
/// Will return false for paths to non-existent locations or if the item is
|
||||
|
|
|
|||
|
|
@ -313,8 +313,11 @@ pub mod buffered;
|
|||
pub mod native {
|
||||
/// Posix file I/O
|
||||
pub mod file;
|
||||
/// # XXX - implement this
|
||||
pub mod stdio { }
|
||||
/// Process spawning and child management
|
||||
pub mod process;
|
||||
/// Posix stdio
|
||||
pub mod stdio;
|
||||
|
||||
/// Sockets
|
||||
/// # XXX - implement this
|
||||
pub mod net {
|
||||
|
|
@ -459,6 +462,16 @@ pub trait Reader {
|
|||
fn eof(&mut self) -> bool;
|
||||
}
|
||||
|
||||
impl Reader for ~Reader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) }
|
||||
fn eof(&mut self) -> bool { self.eof() }
|
||||
}
|
||||
|
||||
impl<'self> Reader for &'self mut Reader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) }
|
||||
fn eof(&mut self) -> bool { self.eof() }
|
||||
}
|
||||
|
||||
pub trait Writer {
|
||||
/// Write the given buffer
|
||||
///
|
||||
|
|
@ -471,6 +484,16 @@ pub trait Writer {
|
|||
fn flush(&mut self);
|
||||
}
|
||||
|
||||
impl Writer for ~Writer {
|
||||
fn write(&mut self, buf: &[u8]) { self.write(buf) }
|
||||
fn flush(&mut self) { self.flush() }
|
||||
}
|
||||
|
||||
impl<'self> Writer for &'self mut Writer {
|
||||
fn write(&mut self, buf: &[u8]) { self.write(buf) }
|
||||
fn flush(&mut self) { self.flush() }
|
||||
}
|
||||
|
||||
pub trait Stream: Reader + Writer { }
|
||||
|
||||
impl<T: Reader + Writer> Stream for T {}
|
||||
|
|
|
|||
|
|
@ -10,68 +10,274 @@
|
|||
|
||||
//! Blocking posix-based file I/O
|
||||
|
||||
#[allow(non_camel_case_types)];
|
||||
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
use super::super::*;
|
||||
use libc::{c_int, FILE};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type fd_t = c_int;
|
||||
fn raise_error() {
|
||||
// XXX: this should probably be a bit more descriptive...
|
||||
let (kind, desc) = match os::errno() as i32 {
|
||||
libc::EOF => (EndOfFile, "end of file"),
|
||||
_ => (OtherIoError, "unknown error"),
|
||||
};
|
||||
|
||||
io_error::cond.raise(IoError {
|
||||
kind: kind,
|
||||
desc: desc,
|
||||
detail: Some(os::last_os_error())
|
||||
});
|
||||
}
|
||||
|
||||
fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
|
||||
#[cfg(windows)] static eintr: int = 0; // doesn't matter
|
||||
#[cfg(not(windows))] static eintr: int = libc::EINTR as int;
|
||||
|
||||
let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) };
|
||||
let mut data = data;
|
||||
let mut amt = origamt;
|
||||
while amt > 0 {
|
||||
let mut ret;
|
||||
loop {
|
||||
ret = f(data, amt);
|
||||
if cfg!(not(windows)) { break } // windows has no eintr
|
||||
// if we get an eintr, then try again
|
||||
if ret != -1 || os::errno() as int != eintr { break }
|
||||
}
|
||||
if ret == 0 {
|
||||
break
|
||||
} else if ret != -1 {
|
||||
amt -= ret as uint;
|
||||
data = unsafe { data.offset(ret as int) };
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return (origamt - amt) as i64;
|
||||
}
|
||||
|
||||
pub type fd_t = libc::c_int;
|
||||
|
||||
pub struct FileDesc {
|
||||
priv fd: fd_t
|
||||
priv fd: fd_t,
|
||||
}
|
||||
|
||||
impl FileDesc {
|
||||
/// Create a `FileDesc` from an open C file descriptor.
|
||||
///
|
||||
/// The `FileDesc` takes ownership of the file descriptor
|
||||
/// and will close it upon destruction.
|
||||
pub fn new(_fd: fd_t) -> FileDesc { fail2!() }
|
||||
/// The `FileDesc` will take ownership of the specified file descriptor and
|
||||
/// close it upon destruction.
|
||||
///
|
||||
/// Note that all I/O operations done on this object will be *blocking*, but
|
||||
/// they do not require the runtime to be active.
|
||||
pub fn new(fd: fd_t) -> FileDesc {
|
||||
FileDesc { fd: fd }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for FileDesc {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
#[cfg(windows)] type rlen = libc::c_uint;
|
||||
#[cfg(not(windows))] type rlen = libc::size_t;
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64
|
||||
}
|
||||
};
|
||||
if ret == 0 {
|
||||
None
|
||||
} else if ret < 0 {
|
||||
raise_error();
|
||||
None
|
||||
} else {
|
||||
Some(ret as uint)
|
||||
}
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool { fail2!() }
|
||||
fn eof(&mut self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Writer for FileDesc {
|
||||
fn write(&mut self, _buf: &[u8]) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
#[cfg(windows)] type wlen = libc::c_uint;
|
||||
#[cfg(not(windows))] type wlen = libc::size_t;
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64
|
||||
}
|
||||
};
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) { fail2!() }
|
||||
fn flush(&mut self) {}
|
||||
}
|
||||
|
||||
impl Seek for FileDesc {
|
||||
fn tell(&self) -> u64 { fail2!() }
|
||||
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
|
||||
impl Drop for FileDesc {
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.fd); }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CFile {
|
||||
priv file: *FILE
|
||||
priv file: *libc::FILE
|
||||
}
|
||||
|
||||
impl CFile {
|
||||
/// Create a `CFile` from an open `FILE` pointer.
|
||||
///
|
||||
/// The `CFile` takes ownership of the file descriptor
|
||||
/// and will close it upon destruction.
|
||||
pub fn new(_file: *FILE) -> CFile { fail2!() }
|
||||
/// The `CFile` takes ownership of the `FILE` pointer and will close it upon
|
||||
/// destruction.
|
||||
pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
|
||||
}
|
||||
|
||||
impl Reader for CFile {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
};
|
||||
if ret == 0 {
|
||||
None
|
||||
} else if ret < 0 {
|
||||
raise_error();
|
||||
None
|
||||
} else {
|
||||
Some(ret as uint)
|
||||
}
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn eof(&mut self) -> bool {
|
||||
unsafe { libc::feof(self.file) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for CFile {
|
||||
fn write(&mut self, _buf: &[u8]) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
let ret = do keep_going(buf) |buf, len| {
|
||||
unsafe {
|
||||
libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
|
||||
self.file) as i64
|
||||
}
|
||||
};
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn flush(&mut self) {
|
||||
if unsafe { libc::fflush(self.file) } < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for CFile {
|
||||
fn tell(&self) -> u64 { fail2!() }
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail2!() }
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn tell(&self) -> u64 {
|
||||
let ret = unsafe { libc::ftell(self.file) };
|
||||
if ret < 0 {
|
||||
raise_error();
|
||||
}
|
||||
return ret as u64;
|
||||
}
|
||||
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) {
|
||||
let whence = match style {
|
||||
SeekSet => libc::SEEK_SET,
|
||||
SeekEnd => libc::SEEK_END,
|
||||
SeekCur => libc::SEEK_CUR,
|
||||
};
|
||||
if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
|
||||
raise_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CFile {
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::fclose(self.file); }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
use rt::io::{io_error, SeekSet};
|
||||
use super::*;
|
||||
|
||||
#[test] #[fixed_stack_segment]
|
||||
#[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer
|
||||
fn test_file_desc() {
|
||||
// Run this test with some pipes so we don't have to mess around with
|
||||
// opening or closing files.
|
||||
unsafe {
|
||||
let os::Pipe { input, out } = os::pipe();
|
||||
let mut reader = FileDesc::new(input);
|
||||
let mut writer = FileDesc::new(out);
|
||||
|
||||
writer.write(bytes!("test"));
|
||||
let mut buf = [0u8, ..4];
|
||||
match reader.read(buf) {
|
||||
Some(4) => {
|
||||
assert_eq!(buf[0], 't' as u8);
|
||||
assert_eq!(buf[1], 'e' as u8);
|
||||
assert_eq!(buf[2], 's' as u8);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => fail2!("invalid read: {:?}", r)
|
||||
}
|
||||
|
||||
let mut raised = false;
|
||||
do io_error::cond.trap(|_| { raised = true; }).inside {
|
||||
writer.read(buf);
|
||||
}
|
||||
assert!(raised);
|
||||
|
||||
raised = false;
|
||||
do io_error::cond.trap(|_| { raised = true; }).inside {
|
||||
reader.write(buf);
|
||||
}
|
||||
assert!(raised);
|
||||
}
|
||||
}
|
||||
|
||||
#[test] #[fixed_stack_segment]
|
||||
#[ignore(cfg(windows))] // apparently windows doesn't like tmpfile
|
||||
fn test_cfile() {
|
||||
unsafe {
|
||||
let f = libc::tmpfile();
|
||||
assert!(!f.is_null());
|
||||
let mut file = CFile::new(f);
|
||||
|
||||
file.write(bytes!("test"));
|
||||
let mut buf = [0u8, ..4];
|
||||
file.seek(0, SeekSet);
|
||||
match file.read(buf) {
|
||||
Some(4) => {
|
||||
assert_eq!(buf[0], 't' as u8);
|
||||
assert_eq!(buf[1], 'e' as u8);
|
||||
assert_eq!(buf[2], 's' as u8);
|
||||
assert_eq!(buf[3], 't' as u8);
|
||||
}
|
||||
r => fail2!("invalid read: {:?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
745
src/libstd/rt/io/native/process.rs
Normal file
745
src/libstd/rt/io/native/process.rs
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
// Copyright 2012-2013 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 cast;
|
||||
use libc::{pid_t, c_void, c_int};
|
||||
use libc;
|
||||
use os;
|
||||
use prelude::*;
|
||||
use ptr;
|
||||
use rt::io;
|
||||
use super::file;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/// The unique id of the process (this should never be negative).
|
||||
priv pid: pid_t,
|
||||
|
||||
/// A handle to the process - on unix this will always be NULL, but on
|
||||
/// windows it will be a HANDLE to the process, which will prevent the
|
||||
/// pid being re-used until the handle is closed.
|
||||
priv handle: *(),
|
||||
|
||||
/// Currently known stdin of the child, if any
|
||||
priv input: Option<file::FileDesc>,
|
||||
/// Currently known stdout of the child, if any
|
||||
priv output: Option<file::FileDesc>,
|
||||
/// Currently known stderr of the child, if any
|
||||
priv error: Option<file::FileDesc>,
|
||||
|
||||
/// None until finish() is called.
|
||||
priv exit_code: Option<int>,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
/// Creates a new process using native process-spawning abilities provided
|
||||
/// by the OS. Operations on this process will be blocking instead of using
|
||||
/// the runtime for sleeping just this current task.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * prog - the program to run
|
||||
/// * args - the arguments to pass to the program, not including the program
|
||||
/// itself
|
||||
/// * env - an optional envrionment to specify for the child process. If
|
||||
/// this value is `None`, then the child will inherit the parent's
|
||||
/// environment
|
||||
/// * cwd - an optionally specified current working directory of the child,
|
||||
/// defaulting to the parent's current working directory
|
||||
/// * stdin, stdout, stderr - These optionally specified file descriptors
|
||||
/// dictate where the stdin/out/err of the child process will go. If
|
||||
/// these are `None`, then this module will bind the input/output to an
|
||||
/// os pipe instead. This process takes ownership of these file
|
||||
/// descriptors, closing them upon destruction of the process.
|
||||
pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>,
|
||||
cwd: Option<&Path>,
|
||||
stdin: Option<file::fd_t>,
|
||||
stdout: Option<file::fd_t>,
|
||||
stderr: Option<file::fd_t>) -> Process {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
let (in_pipe, in_fd) = match stdin {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.input)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
let (out_pipe, out_fd) = match stdout {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.out)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
let (err_pipe, err_fd) = match stderr {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.out)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
|
||||
let res = spawn_process_os(prog, args, env, cwd,
|
||||
in_fd, out_fd, err_fd);
|
||||
|
||||
unsafe {
|
||||
for pipe in in_pipe.iter() { libc::close(pipe.input); }
|
||||
for pipe in out_pipe.iter() { libc::close(pipe.out); }
|
||||
for pipe in err_pipe.iter() { libc::close(pipe.out); }
|
||||
}
|
||||
|
||||
Process {
|
||||
pid: res.pid,
|
||||
handle: res.handle,
|
||||
input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out)),
|
||||
output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input)),
|
||||
error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input)),
|
||||
exit_code: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the unique id of the process
|
||||
pub fn id(&self) -> pid_t { self.pid }
|
||||
|
||||
/**
|
||||
* Returns an io::Writer that can be used to write to this Process's stdin.
|
||||
*
|
||||
* Fails if there is no stdinavailable (it's already been removed by
|
||||
* take_input)
|
||||
*/
|
||||
pub fn input<'a>(&'a mut self) -> &'a mut io::Writer {
|
||||
match self.input {
|
||||
Some(ref mut fd) => fd as &mut io::Writer,
|
||||
None => fail2!("This process has no stdin")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an io::Reader that can be used to read from this Process's
|
||||
* stdout.
|
||||
*
|
||||
* Fails if there is no stdin available (it's already been removed by
|
||||
* take_output)
|
||||
*/
|
||||
pub fn output<'a>(&'a mut self) -> &'a mut io::Reader {
|
||||
match self.input {
|
||||
Some(ref mut fd) => fd as &mut io::Reader,
|
||||
None => fail2!("This process has no stdout")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an io::Reader that can be used to read from this Process's
|
||||
* stderr.
|
||||
*
|
||||
* Fails if there is no stdin available (it's already been removed by
|
||||
* take_error)
|
||||
*/
|
||||
pub fn error<'a>(&'a mut self) -> &'a mut io::Reader {
|
||||
match self.error {
|
||||
Some(ref mut fd) => fd as &mut io::Reader,
|
||||
None => fail2!("This process has no stderr")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the stdin of this process, transferring ownership to the caller.
|
||||
* Note that when the return value is destroyed, the handle will be closed
|
||||
* for the child process.
|
||||
*/
|
||||
pub fn take_input(&mut self) -> Option<~io::Writer> {
|
||||
self.input.take().map(|fd| ~fd as ~io::Writer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the stdout of this process, transferring ownership to the caller.
|
||||
* Note that when the return value is destroyed, the handle will be closed
|
||||
* for the child process.
|
||||
*/
|
||||
pub fn take_output(&mut self) -> Option<~io::Reader> {
|
||||
self.output.take().map(|fd| ~fd as ~io::Reader)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the stderr of this process, transferring ownership to the caller.
|
||||
* Note that when the return value is destroyed, the handle will be closed
|
||||
* for the child process.
|
||||
*/
|
||||
pub fn take_error(&mut self) -> Option<~io::Reader> {
|
||||
self.error.take().map(|fd| ~fd as ~io::Reader)
|
||||
}
|
||||
|
||||
pub fn wait(&mut self) -> int {
|
||||
for &code in self.exit_code.iter() {
|
||||
return code;
|
||||
}
|
||||
let code = waitpid(self.pid);
|
||||
self.exit_code = Some(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> {
|
||||
// if the process has finished, and therefore had waitpid called,
|
||||
// and we kill it, then on unix we might ending up killing a
|
||||
// newer process that happens to have the same (re-used) id
|
||||
match self.exit_code {
|
||||
Some(*) => return Err(io::IoError {
|
||||
kind: io::OtherIoError,
|
||||
desc: "can't kill an exited process",
|
||||
detail: None,
|
||||
}),
|
||||
None => {}
|
||||
}
|
||||
return unsafe { killpid(self.pid, signum) };
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
match signal {
|
||||
io::process::PleaseExitSignal |
|
||||
io::process::MustDieSignal => {
|
||||
libc::funcs::extra::kernel32::TerminateProcess(
|
||||
cast::transmute(pid), 1);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(io::IoError {
|
||||
kind: io::OtherIoError,
|
||||
desc: "unsupported signal on windows",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
libc::funcs::posix88::signal::kill(pid, signal as c_int);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
// close all these handles
|
||||
self.take_input();
|
||||
self.take_output();
|
||||
self.take_error();
|
||||
self.wait();
|
||||
free_handle(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnProcessResult {
|
||||
pid: pid_t,
|
||||
handle: *(),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn spawn_process_os(prog: &str, args: &[~str],
|
||||
env: Option<~[(~str, ~str)]>,
|
||||
dir: Option<&Path>,
|
||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
||||
use libc::consts::os::extra::{
|
||||
TRUE, FALSE,
|
||||
STARTF_USESTDHANDLES,
|
||||
INVALID_HANDLE_VALUE,
|
||||
DUPLICATE_SAME_ACCESS
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
GetCurrentProcess,
|
||||
DuplicateHandle,
|
||||
CloseHandle,
|
||||
CreateProcessA
|
||||
};
|
||||
use libc::funcs::extra::msvcrt::get_osfhandle;
|
||||
|
||||
use sys;
|
||||
|
||||
unsafe {
|
||||
|
||||
let mut si = zeroed_startupinfo();
|
||||
si.cb = sys::size_of::<STARTUPINFO>() as DWORD;
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
let cur_proc = GetCurrentProcess();
|
||||
|
||||
let orig_std_in = get_osfhandle(in_fd) as HANDLE;
|
||||
if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let orig_std_out = get_osfhandle(out_fd) as HANDLE;
|
||||
if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let orig_std_err = get_osfhandle(err_fd) as HANDLE;
|
||||
if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let cmd = make_command_line(prog, args);
|
||||
let mut pi = zeroed_process_information();
|
||||
let mut create_err = None;
|
||||
|
||||
do with_envp(env) |envp| {
|
||||
do with_dirp(dir) |dirp| {
|
||||
do cmd.with_c_str |cmdp| {
|
||||
let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
|
||||
ptr::mut_null(), ptr::mut_null(), TRUE,
|
||||
0, envp, dirp, &mut si, &mut pi);
|
||||
if created == FALSE {
|
||||
create_err = Some(os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(si.hStdInput);
|
||||
CloseHandle(si.hStdOutput);
|
||||
CloseHandle(si.hStdError);
|
||||
|
||||
for msg in create_err.iter() {
|
||||
fail2!("failure in CreateProcess: {}", *msg);
|
||||
}
|
||||
|
||||
// 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. We don't close the process handle however
|
||||
// because we want the process id to stay valid at least until the
|
||||
// calling code closes the process handle.
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
SpawnProcessResult {
|
||||
pid: pi.dwProcessId as pid_t,
|
||||
handle: pi.hProcess as *()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
|
||||
libc::types::os::arch::extra::STARTUPINFO {
|
||||
cb: 0,
|
||||
lpReserved: ptr::mut_null(),
|
||||
lpDesktop: ptr::mut_null(),
|
||||
lpTitle: ptr::mut_null(),
|
||||
dwX: 0,
|
||||
dwY: 0,
|
||||
dwXSize: 0,
|
||||
dwYSize: 0,
|
||||
dwXCountChars: 0,
|
||||
dwYCountCharts: 0,
|
||||
dwFillAttribute: 0,
|
||||
dwFlags: 0,
|
||||
wShowWindow: 0,
|
||||
cbReserved2: 0,
|
||||
lpReserved2: ptr::mut_null(),
|
||||
hStdInput: ptr::mut_null(),
|
||||
hStdOutput: ptr::mut_null(),
|
||||
hStdError: ptr::mut_null()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
hProcess: ptr::mut_null(),
|
||||
hThread: ptr::mut_null(),
|
||||
dwProcessId: 0,
|
||||
dwThreadId: 0
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is only pub so it can be tested (see issue #4536)
|
||||
#[cfg(windows)]
|
||||
pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
|
||||
let mut cmd = ~"";
|
||||
append_arg(&mut cmd, prog);
|
||||
for arg in args.iter() {
|
||||
cmd.push_char(' ');
|
||||
append_arg(&mut cmd, *arg);
|
||||
}
|
||||
return cmd;
|
||||
|
||||
fn append_arg(cmd: &mut ~str, arg: &str) {
|
||||
let quote = arg.iter().any(|c| c == ' ' || c == '\t');
|
||||
if quote {
|
||||
cmd.push_char('"');
|
||||
}
|
||||
for i in range(0u, arg.len()) {
|
||||
append_char_at(cmd, arg, i);
|
||||
}
|
||||
if quote {
|
||||
cmd.push_char('"');
|
||||
}
|
||||
}
|
||||
|
||||
fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
|
||||
match arg[i] as char {
|
||||
'"' => {
|
||||
// Escape quotes.
|
||||
cmd.push_str("\\\"");
|
||||
}
|
||||
'\\' => {
|
||||
if backslash_run_ends_in_quote(arg, i) {
|
||||
// Double all backslashes that are in runs before quotes.
|
||||
cmd.push_str("\\\\");
|
||||
} else {
|
||||
// Pass other backslashes through unescaped.
|
||||
cmd.push_char('\\');
|
||||
}
|
||||
}
|
||||
c => {
|
||||
cmd.push_char(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
|
||||
while i < s.len() && s[i] as char == '\\' {
|
||||
i += 1;
|
||||
}
|
||||
return i < s.len() && s[i] as char == '"';
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn spawn_process_os(prog: &str, args: &[~str],
|
||||
env: Option<~[(~str, ~str)]>,
|
||||
dir: Option<&Path>,
|
||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
||||
use libc::funcs::bsd44::getdtablesize;
|
||||
|
||||
mod rustrt {
|
||||
#[abi = "cdecl"]
|
||||
extern {
|
||||
pub fn rust_unset_sigprocmask();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn set_environ(_envp: *c_void) {}
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn set_environ(envp: *c_void) {
|
||||
externfn!(fn _NSGetEnviron() -> *mut *c_void);
|
||||
|
||||
*_NSGetEnviron() = envp;
|
||||
}
|
||||
#[cfg(not(target_os = "macos"), not(windows))]
|
||||
unsafe fn set_environ(envp: *c_void) {
|
||||
extern {
|
||||
static mut environ: *c_void;
|
||||
}
|
||||
environ = envp;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
||||
let pid = fork();
|
||||
if pid < 0 {
|
||||
fail2!("failure in fork: {}", os::last_os_error());
|
||||
} else if pid > 0 {
|
||||
return SpawnProcessResult {pid: pid, handle: ptr::null()};
|
||||
}
|
||||
|
||||
rustrt::rust_unset_sigprocmask();
|
||||
|
||||
if dup2(in_fd, 0) == -1 {
|
||||
fail2!("failure in dup2(in_fd, 0): {}", os::last_os_error());
|
||||
}
|
||||
if dup2(out_fd, 1) == -1 {
|
||||
fail2!("failure in dup2(out_fd, 1): {}", os::last_os_error());
|
||||
}
|
||||
if dup2(err_fd, 2) == -1 {
|
||||
fail2!("failure in dup3(err_fd, 2): {}", os::last_os_error());
|
||||
}
|
||||
// close all other fds
|
||||
for fd in range(3, getdtablesize()).invert() {
|
||||
close(fd as c_int);
|
||||
}
|
||||
|
||||
do with_dirp(dir) |dirp| {
|
||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
||||
fail2!("failure in chdir: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
do with_envp(env) |envp| {
|
||||
if !envp.is_null() {
|
||||
set_environ(envp);
|
||||
}
|
||||
do with_argv(prog, args) |argv| {
|
||||
execvp(*argv, argv);
|
||||
// execvp only returns if an error occurred
|
||||
fail2!("failure in execvp: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn with_argv<T>(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T {
|
||||
use vec;
|
||||
|
||||
// We can't directly convert `str`s into `*char`s, as someone needs to hold
|
||||
// a reference to the intermediary byte buffers. So first build an array to
|
||||
// hold all the ~[u8] byte strings.
|
||||
let mut tmps = vec::with_capacity(args.len() + 1);
|
||||
|
||||
tmps.push(prog.to_c_str());
|
||||
|
||||
for arg in args.iter() {
|
||||
tmps.push(arg.to_c_str());
|
||||
}
|
||||
|
||||
// Next, convert each of the byte strings into a pointer. This is
|
||||
// technically unsafe as the caller could leak these pointers out of our
|
||||
// scope.
|
||||
let mut ptrs = do tmps.map |tmp| {
|
||||
tmp.with_ref(|buf| buf)
|
||||
};
|
||||
|
||||
// Finally, make sure we add a null pointer.
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
ptrs.as_imm_buf(|buf, _| cb(buf))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T {
|
||||
use vec;
|
||||
|
||||
// On posixy systems we can pass a char** for envp, which is a
|
||||
// null-terminated array of "k=v\n" strings. Like `with_argv`, we have to
|
||||
// have a temporary buffer to hold the intermediary `~[u8]` byte strings.
|
||||
match env {
|
||||
Some(env) => {
|
||||
let mut tmps = vec::with_capacity(env.len());
|
||||
|
||||
for pair in env.iter() {
|
||||
let kv = format!("{}={}", pair.first(), pair.second());
|
||||
tmps.push(kv.to_c_str());
|
||||
}
|
||||
|
||||
// Once again, this is unsafe.
|
||||
let mut ptrs = do tmps.map |tmp| {
|
||||
tmp.with_ref(|buf| buf)
|
||||
};
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
do ptrs.as_imm_buf |buf, _| {
|
||||
unsafe { cb(cast::transmute(buf)) }
|
||||
}
|
||||
}
|
||||
_ => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T {
|
||||
// On win32 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 = ~[];
|
||||
|
||||
for pair in env.iter() {
|
||||
let kv = format!("{}={}", pair.first(), pair.second());
|
||||
blk.push_all(kv.as_bytes());
|
||||
blk.push(0);
|
||||
}
|
||||
|
||||
blk.push(0);
|
||||
|
||||
do blk.as_imm_buf |p, _len| {
|
||||
unsafe { cb(cast::transmute(p)) }
|
||||
}
|
||||
}
|
||||
_ => cb(ptr::mut_null())
|
||||
}
|
||||
}
|
||||
|
||||
fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T {
|
||||
match d {
|
||||
Some(dir) => dir.with_c_str(|buf| cb(buf)),
|
||||
None => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn free_handle(handle: *()) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
unsafe {
|
||||
libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn free_handle(_handle: *()) {
|
||||
// unix has no process handle object, just a pid
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a process to exit and returns the exit code, failing
|
||||
* if there is no process with the specified id.
|
||||
*
|
||||
* Note that this is private to avoid race conditions on unix where if
|
||||
* a user calls waitpid(some_process.get_id()) then some_process.finish()
|
||||
* and some_process.destroy() and some_process.finalize() will then either
|
||||
* operate on a none-existent process or, even worse, on a newer process
|
||||
* with the same id.
|
||||
*/
|
||||
fn waitpid(pid: pid_t) -> int {
|
||||
return waitpid_os(pid);
|
||||
|
||||
#[cfg(windows)]
|
||||
fn waitpid_os(pid: pid_t) -> int {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
use libc::consts::os::extra::{
|
||||
SYNCHRONIZE,
|
||||
PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
STILL_ACTIVE,
|
||||
INFINITE,
|
||||
WAIT_FAILED
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
OpenProcess,
|
||||
GetExitCodeProcess,
|
||||
CloseHandle,
|
||||
WaitForSingleObject
|
||||
};
|
||||
|
||||
unsafe {
|
||||
|
||||
let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
|
||||
if proc.is_null() {
|
||||
fail2!("failure in OpenProcess: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
if GetExitCodeProcess(proc, &mut status) == FALSE {
|
||||
CloseHandle(proc);
|
||||
fail2!("failure in GetExitCodeProcess: {}", os::last_os_error());
|
||||
}
|
||||
if status != STILL_ACTIVE {
|
||||
CloseHandle(proc);
|
||||
return status as int;
|
||||
}
|
||||
if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
|
||||
CloseHandle(proc);
|
||||
fail2!("failure in WaitForSingleObject: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn waitpid_os(pid: pid_t) -> int {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::funcs::posix01::wait::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
fn WIFEXITED(status: i32) -> bool {
|
||||
(status & 0xffi32) == 0i32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn WIFEXITED(status: i32) -> bool {
|
||||
(status & 0x7fi32) == 0i32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
fn WEXITSTATUS(status: i32) -> i32 {
|
||||
(status >> 8i32) & 0xffi32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn WEXITSTATUS(status: i32) -> i32 {
|
||||
status >> 8i32
|
||||
}
|
||||
|
||||
let mut status = 0 as c_int;
|
||||
if unsafe { waitpid(pid, &mut status, 0) } == -1 {
|
||||
fail2!("failure in waitpid: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
return if WIFEXITED(status) {
|
||||
WEXITSTATUS(status) as int
|
||||
} else {
|
||||
1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test] #[cfg(windows)]
|
||||
fn test_make_command_line() {
|
||||
use super::make_command_line;
|
||||
assert_eq!(
|
||||
make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
|
||||
~"prog aaa bbb ccc"
|
||||
);
|
||||
assert_eq!(
|
||||
make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
|
||||
~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
|
||||
);
|
||||
assert_eq!(
|
||||
make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
|
||||
~"\"C:\\Program Files\\test\" aa\\\"bb"
|
||||
);
|
||||
assert_eq!(
|
||||
make_command_line("echo", [~"a b c"]),
|
||||
~"echo \"a b c\""
|
||||
);
|
||||
}
|
||||
|
||||
// Currently most of the tests of this functionality live inside std::run,
|
||||
// but they may move here eventually as a non-blocking backend is added to
|
||||
// std::run
|
||||
}
|
||||
67
src/libstd/rt/io/native/stdio.rs
Normal file
67
src/libstd/rt/io/native/stdio.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2013 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 libc;
|
||||
use option::Option;
|
||||
use rt::io::{Reader, Writer};
|
||||
use super::file;
|
||||
|
||||
/// Creates a new handle to the stdin of this process
|
||||
pub fn stdin() -> StdIn { StdIn::new() }
|
||||
/// Creates a new handle to the stdout of this process
|
||||
pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) }
|
||||
/// Creates a new handle to the stderr of this process
|
||||
pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) }
|
||||
|
||||
pub fn print(s: &str) {
|
||||
stdout().write(s.as_bytes())
|
||||
}
|
||||
|
||||
pub fn println(s: &str) {
|
||||
let mut out = stdout();
|
||||
out.write(s.as_bytes());
|
||||
out.write(['\n' as u8]);
|
||||
}
|
||||
|
||||
pub struct StdIn {
|
||||
priv fd: file::FileDesc
|
||||
}
|
||||
|
||||
impl StdIn {
|
||||
/// Duplicates the stdin file descriptor, returning an io::Reader
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
pub fn new() -> StdIn {
|
||||
let fd = unsafe { libc::dup(libc::STDIN_FILENO) };
|
||||
StdIn { fd: file::FileDesc::new(fd) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for StdIn {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.fd.read(buf) }
|
||||
fn eof(&mut self) -> bool { self.fd.eof() }
|
||||
}
|
||||
|
||||
pub struct StdOut {
|
||||
priv fd: file::FileDesc
|
||||
}
|
||||
|
||||
impl StdOut {
|
||||
/// Duplicates the specified file descriptor, returning an io::Writer
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
pub fn new(fd: file::fd_t) -> StdOut {
|
||||
let fd = unsafe { libc::dup(fd) };
|
||||
StdOut { fd: file::FileDesc::new(fd) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer for StdOut {
|
||||
fn write(&mut self, buf: &[u8]) { self.fd.write(buf) }
|
||||
fn flush(&mut self) { self.fd.flush() }
|
||||
}
|
||||
|
|
@ -18,6 +18,13 @@ use rt::io::io_error;
|
|||
use rt::local::Local;
|
||||
use rt::rtio::{RtioProcess, RtioProcessObject, IoFactoryObject, IoFactory};
|
||||
|
||||
// windows values don't matter as long as they're at least one of unix's
|
||||
// TERM/KILL/INT signals
|
||||
#[cfg(windows)] pub static PleaseExitSignal: int = 15;
|
||||
#[cfg(windows)] pub static MustDieSignal: int = 9;
|
||||
#[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int;
|
||||
#[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int;
|
||||
|
||||
pub struct Process {
|
||||
priv handle: ~RtioProcessObject,
|
||||
io: ~[Option<io::PipeStream>],
|
||||
|
|
|
|||
|
|
@ -8,45 +8,102 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use prelude::*;
|
||||
use super::{Reader, Writer};
|
||||
use libc;
|
||||
use option::{Option, Some, None};
|
||||
use result::{Ok, Err};
|
||||
use rt::local::Local;
|
||||
use rt::rtio::{RtioFileStream, IoFactoryObject, IoFactory};
|
||||
use super::{Reader, Writer, io_error};
|
||||
|
||||
pub fn stdin() -> StdReader { fail2!() }
|
||||
|
||||
pub fn stdout() -> StdWriter { fail2!() }
|
||||
|
||||
pub fn stderr() -> StdReader { fail2!() }
|
||||
|
||||
pub fn print(_s: &str) { fail2!() }
|
||||
|
||||
pub fn println(_s: &str) { fail2!() }
|
||||
|
||||
pub enum StdStream {
|
||||
StdIn,
|
||||
StdOut,
|
||||
StdErr
|
||||
/// Creates a new non-blocking handle to the stdin of the current process.
|
||||
///
|
||||
/// See `stdout()` for notes about this function.
|
||||
pub fn stdin() -> StdReader {
|
||||
let stream = unsafe {
|
||||
let io: *mut IoFactoryObject = Local::unsafe_borrow();
|
||||
(*io).fs_from_raw_fd(libc::STDIN_FILENO, false)
|
||||
};
|
||||
StdReader { inner: stream }
|
||||
}
|
||||
|
||||
pub struct StdReader;
|
||||
/// Creates a new non-blocking handle to the stdout of the current process.
|
||||
///
|
||||
/// Note that this is a fairly expensive operation in that at least one memory
|
||||
/// allocation is performed. Additionally, this must be called from a runtime
|
||||
/// task context because the stream returned will be a non-blocking object using
|
||||
/// the local scheduler to perform the I/O.
|
||||
pub fn stdout() -> StdWriter {
|
||||
let stream = unsafe {
|
||||
let io: *mut IoFactoryObject = Local::unsafe_borrow();
|
||||
(*io).fs_from_raw_fd(libc::STDOUT_FILENO, false)
|
||||
};
|
||||
StdWriter { inner: stream }
|
||||
}
|
||||
|
||||
impl StdReader {
|
||||
pub fn new(_stream: StdStream) -> StdReader { fail2!() }
|
||||
/// Creates a new non-blocking handle to the stderr of the current process.
|
||||
///
|
||||
/// See `stdout()` for notes about this function.
|
||||
pub fn stderr() -> StdWriter {
|
||||
let stream = unsafe {
|
||||
let io: *mut IoFactoryObject = Local::unsafe_borrow();
|
||||
(*io).fs_from_raw_fd(libc::STDERR_FILENO, false)
|
||||
};
|
||||
StdWriter { inner: stream }
|
||||
}
|
||||
|
||||
/// Prints a string to the stdout of the current process. No newline is emitted
|
||||
/// after the string is printed.
|
||||
pub fn print(s: &str) {
|
||||
// XXX: need to see if not caching stdin() is the cause of performance
|
||||
// issues, it should be possible to cache a stdout handle in each Task
|
||||
// and then re-use that across calls to print/println
|
||||
stdout().write(s.as_bytes());
|
||||
}
|
||||
|
||||
/// Prints a string as a line. to the stdout of the current process. A literal
|
||||
/// `\n` character is printed to the console after the string.
|
||||
pub fn println(s: &str) {
|
||||
let mut out = stdout();
|
||||
out.write(s.as_bytes());
|
||||
out.write(['\n' as u8]);
|
||||
}
|
||||
|
||||
/// Representation of a reader of a standard input stream
|
||||
pub struct StdReader {
|
||||
priv inner: ~RtioFileStream
|
||||
}
|
||||
|
||||
impl Reader for StdReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail2!() }
|
||||
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
|
||||
match self.inner.read(buf) {
|
||||
Ok(amt) => Some(amt as uint),
|
||||
Err(e) => {
|
||||
io_error::cond.raise(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> bool { fail2!() }
|
||||
fn eof(&mut self) -> bool { false }
|
||||
}
|
||||
|
||||
pub struct StdWriter;
|
||||
|
||||
impl StdWriter {
|
||||
pub fn new(_stream: StdStream) -> StdWriter { fail2!() }
|
||||
/// Representation of a writer to a standard output stream
|
||||
pub struct StdWriter {
|
||||
priv inner: ~RtioFileStream
|
||||
}
|
||||
|
||||
impl Writer for StdWriter {
|
||||
fn write(&mut self, _buf: &[u8]) { fail2!() }
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
match self.inner.write(buf) {
|
||||
Ok(()) => {}
|
||||
Err(e) => io_error::cond.raise(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) { fail2!() }
|
||||
fn flush(&mut self) {
|
||||
match self.inner.flush() {
|
||||
Ok(()) => {}
|
||||
Err(e) => io_error::cond.raise(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ use fmt;
|
|||
use from_str::from_str;
|
||||
use libc::exit;
|
||||
use option::{Some, None, Option};
|
||||
use rt;
|
||||
use rt::util::dumb_println;
|
||||
use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map};
|
||||
use str::StrSlice;
|
||||
use u32;
|
||||
|
|
@ -88,16 +86,16 @@ fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
|
|||
log_level = num;
|
||||
},
|
||||
_ => {
|
||||
dumb_println(format!("warning: invalid logging spec \
|
||||
'{}', ignoring it", parts[1]));
|
||||
continue;
|
||||
rterrln!("warning: invalid logging spec '{}', \
|
||||
ignoring it", parts[1]);
|
||||
continue
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
dumb_println(format!("warning: invalid logging spec '{}',\
|
||||
ignoring it", s));
|
||||
continue;
|
||||
rterrln!("warning: invalid logging spec '{}', \
|
||||
ignoring it", s);
|
||||
continue
|
||||
}
|
||||
}
|
||||
let dir = LogDirective {name: name, level: log_level};
|
||||
|
|
@ -141,9 +139,9 @@ fn update_log_settings(crate_map: &CrateMap, settings: ~str) {
|
|||
let mut dirs = ~[];
|
||||
if settings.len() > 0 {
|
||||
if settings == ~"::help" || settings == ~"?" {
|
||||
dumb_println("\nCrate log map:\n");
|
||||
rterrln!("\nCrate log map:\n");
|
||||
do iter_crate_map(crate_map) |entry| {
|
||||
dumb_println(" "+entry.name);
|
||||
rterrln!(" {}", entry.name);
|
||||
}
|
||||
unsafe { exit(1); }
|
||||
}
|
||||
|
|
@ -157,12 +155,10 @@ fn update_log_settings(crate_map: &CrateMap, settings: ~str) {
|
|||
}
|
||||
|
||||
if n_matches < (dirs.len() as u32) {
|
||||
dumb_println(format!("warning: got {} RUST_LOG specs but only matched\n\
|
||||
{} of them. You may have mistyped a RUST_LOG \
|
||||
spec. \n\
|
||||
Use RUST_LOG=::help to see the list of crates \
|
||||
and modules.\n",
|
||||
dirs.len(), n_matches));
|
||||
rterrln!("warning: got {} RUST_LOG specs but only matched\n\
|
||||
{} of them. You may have mistyped a RUST_LOG spec. \n\
|
||||
Use RUST_LOG=::help to see the list of crates and modules.\n",
|
||||
dirs.len(), n_matches);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,24 +170,13 @@ pub struct StdErrLogger;
|
|||
|
||||
impl Logger for StdErrLogger {
|
||||
fn log(&mut self, args: &fmt::Arguments) {
|
||||
fmt::writeln(self as &mut rt::io::Writer, args);
|
||||
// FIXME(#6846): this should not call the blocking version of println,
|
||||
// or at least the default loggers for tasks shouldn't do
|
||||
// that
|
||||
::rt::util::dumb_println(args);
|
||||
}
|
||||
}
|
||||
|
||||
impl rt::io::Writer for StdErrLogger {
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
// Nothing like swapping between I/O implementations! In theory this
|
||||
// could use the libuv bindings for writing to file descriptors, but
|
||||
// that may not necessarily be desirable because logging should work
|
||||
// outside of the uv loop. (modify with caution)
|
||||
use io::Writer;
|
||||
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
|
||||
dbg.write(buf);
|
||||
}
|
||||
|
||||
fn flush(&mut self) {}
|
||||
}
|
||||
|
||||
/// Configure logging by traversing the crate map and setting the
|
||||
/// per-module global logging flags based on the logging spec
|
||||
pub fn init() {
|
||||
|
|
@ -212,7 +197,7 @@ pub fn init() {
|
|||
_ => {
|
||||
match log_spec {
|
||||
Some(_) => {
|
||||
dumb_println("warning: RUST_LOG set, but no crate map found.");
|
||||
rterrln!("warning: RUST_LOG set, but no crate map found.");
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
use container::Container;
|
||||
use fmt;
|
||||
use from_str::FromStr;
|
||||
use libc;
|
||||
use option::{Some, None, Option};
|
||||
|
|
@ -74,10 +75,11 @@ pub fn default_sched_threads() -> uint {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn dumb_println(s: &str) {
|
||||
use io::WriterUtil;
|
||||
let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
|
||||
dbg.write_str(s + "\n");
|
||||
pub fn dumb_println(args: &fmt::Arguments) {
|
||||
use rt::io::native::stdio::stderr;
|
||||
use rt::io::Writer;
|
||||
let mut out = stderr();
|
||||
fmt::writeln(&mut out as &mut Writer, args);
|
||||
}
|
||||
|
||||
pub fn abort(msg: &str) -> ! {
|
||||
|
|
|
|||
|
|
@ -12,19 +12,14 @@
|
|||
|
||||
#[allow(missing_doc)];
|
||||
|
||||
use c_str::ToCStr;
|
||||
use cast;
|
||||
use clone::Clone;
|
||||
use comm::{stream, SharedChan, GenericChan, GenericPort};
|
||||
use io;
|
||||
use libc::{pid_t, c_void, c_int};
|
||||
use cell::Cell;
|
||||
use comm::{stream, SharedChan};
|
||||
use libc::{pid_t, c_int};
|
||||
use libc;
|
||||
use option::{Some, None};
|
||||
use os;
|
||||
use prelude::*;
|
||||
use ptr;
|
||||
use rt::io::native::process;
|
||||
use rt::io;
|
||||
use task;
|
||||
use vec::ImmutableVector;
|
||||
|
||||
/**
|
||||
* A value representing a child process.
|
||||
|
|
@ -34,28 +29,7 @@ use vec::ImmutableVector;
|
|||
* for the process to terminate.
|
||||
*/
|
||||
pub struct Process {
|
||||
|
||||
/// The unique id of the process (this should never be negative).
|
||||
priv pid: pid_t,
|
||||
|
||||
/**
|
||||
* A handle to the process - on unix this will always be NULL, but on
|
||||
* windows it will be a HANDLE to the process, which will prevent the
|
||||
* pid being re-used until the handle is closed.
|
||||
*/
|
||||
priv handle: *(),
|
||||
|
||||
/// Some(fd), or None when stdin is being redirected from a fd not created by Process::new.
|
||||
priv input: Option<c_int>,
|
||||
|
||||
/// Some(file), or None when stdout is being redirected to a fd not created by Process::new.
|
||||
priv output: Option<*libc::FILE>,
|
||||
|
||||
/// Some(file), or None when stderr is being redirected to a fd not created by Process::new.
|
||||
priv error: Option<*libc::FILE>,
|
||||
|
||||
/// None until finish() is called.
|
||||
priv exit_code: Option<int>,
|
||||
priv inner: process::Process,
|
||||
}
|
||||
|
||||
/// Options that can be given when starting a Process.
|
||||
|
|
@ -147,178 +121,50 @@ impl Process {
|
|||
* * options - Options to configure the environment of the process,
|
||||
* the working directory and the standard IO streams.
|
||||
*/
|
||||
pub fn new(prog: &str, args: &[~str],
|
||||
options: ProcessOptions)
|
||||
-> Process {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
let (in_pipe, in_fd) = match options.in_fd {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.input)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
let (out_pipe, out_fd) = match options.out_fd {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.out)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
let (err_pipe, err_fd) = match options.err_fd {
|
||||
None => {
|
||||
let pipe = os::pipe();
|
||||
(Some(pipe), pipe.out)
|
||||
},
|
||||
Some(fd) => (None, fd)
|
||||
};
|
||||
|
||||
let res = spawn_process_os(prog, args, options.env.clone(), options.dir,
|
||||
in_fd, out_fd, err_fd);
|
||||
|
||||
unsafe {
|
||||
for pipe in in_pipe.iter() { libc::close(pipe.input); }
|
||||
for pipe in out_pipe.iter() { libc::close(pipe.out); }
|
||||
for pipe in err_pipe.iter() { libc::close(pipe.out); }
|
||||
}
|
||||
|
||||
Process {
|
||||
pid: res.pid,
|
||||
handle: res.handle,
|
||||
input: in_pipe.map(|pipe| pipe.out),
|
||||
output: out_pipe.map(|pipe| os::fdopen(pipe.input)),
|
||||
error: err_pipe.map(|pipe| os::fdopen(pipe.input)),
|
||||
exit_code: None,
|
||||
}
|
||||
pub fn new(prog: &str, args: &[~str], options: ProcessOptions) -> Process {
|
||||
let ProcessOptions { env, dir, in_fd, out_fd, err_fd } = options;
|
||||
let inner = process::Process::new(prog, args, env, dir,
|
||||
in_fd, out_fd, err_fd);
|
||||
Process { inner: inner }
|
||||
}
|
||||
|
||||
/// Returns the unique id of the process
|
||||
pub fn get_id(&self) -> pid_t { self.pid }
|
||||
|
||||
fn input_fd(&mut self) -> c_int {
|
||||
match self.input {
|
||||
Some(fd) => fd,
|
||||
None => fail2!("This Process's stdin was redirected to an \
|
||||
existing file descriptor.")
|
||||
}
|
||||
}
|
||||
|
||||
fn output_file(&mut self) -> *libc::FILE {
|
||||
match self.output {
|
||||
Some(file) => file,
|
||||
None => fail2!("This Process's stdout was redirected to an \
|
||||
existing file descriptor.")
|
||||
}
|
||||
}
|
||||
|
||||
fn error_file(&mut self) -> *libc::FILE {
|
||||
match self.error {
|
||||
Some(file) => file,
|
||||
None => fail2!("This Process's stderr was redirected to an \
|
||||
existing file descriptor.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this process is reading its stdin from an existing file
|
||||
* descriptor rather than a pipe that was created specifically for this
|
||||
* process.
|
||||
*
|
||||
* If this method returns true then self.input() will fail.
|
||||
*/
|
||||
pub fn input_redirected(&self) -> bool {
|
||||
self.input.is_none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this process is writing its stdout to an existing file
|
||||
* descriptor rather than a pipe that was created specifically for this
|
||||
* process.
|
||||
*
|
||||
* If this method returns true then self.output() will fail.
|
||||
*/
|
||||
pub fn output_redirected(&self) -> bool {
|
||||
self.output.is_none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this process is writing its stderr to an existing file
|
||||
* descriptor rather than a pipe that was created specifically for this
|
||||
* process.
|
||||
*
|
||||
* If this method returns true then self.error() will fail.
|
||||
*/
|
||||
pub fn error_redirected(&self) -> bool {
|
||||
self.error.is_none()
|
||||
}
|
||||
pub fn get_id(&self) -> pid_t { self.inner.id() }
|
||||
|
||||
/**
|
||||
* Returns an io::Writer that can be used to write to this Process's stdin.
|
||||
*
|
||||
* Fails if this Process's stdin was redirected to an existing file descriptor.
|
||||
* Fails if there is no stdin available (it's already been removed by
|
||||
* take_input)
|
||||
*/
|
||||
pub fn input(&mut self) -> @io::Writer {
|
||||
// FIXME: the Writer can still be used after self is destroyed: #2625
|
||||
io::fd_writer(self.input_fd(), false)
|
||||
}
|
||||
pub fn input<'a>(&'a mut self) -> &'a mut io::Writer { self.inner.input() }
|
||||
|
||||
/**
|
||||
* Returns an io::Reader that can be used to read from this Process's stdout.
|
||||
*
|
||||
* Fails if this Process's stdout was redirected to an existing file descriptor.
|
||||
* Fails if there is no stdout available (it's already been removed by
|
||||
* take_output)
|
||||
*/
|
||||
pub fn output(&mut self) -> @io::Reader {
|
||||
// FIXME: the Reader can still be used after self is destroyed: #2625
|
||||
io::FILE_reader(self.output_file(), false)
|
||||
}
|
||||
pub fn output<'a>(&'a mut self) -> &'a mut io::Reader { self.inner.output() }
|
||||
|
||||
/**
|
||||
* Returns an io::Reader that can be used to read from this Process's stderr.
|
||||
*
|
||||
* Fails if this Process's stderr was redirected to an existing file descriptor.
|
||||
* Fails if there is no stderr available (it's already been removed by
|
||||
* take_error)
|
||||
*/
|
||||
pub fn error(&mut self) -> @io::Reader {
|
||||
// FIXME: the Reader can still be used after self is destroyed: #2625
|
||||
io::FILE_reader(self.error_file(), false)
|
||||
}
|
||||
pub fn error<'a>(&'a mut self) -> &'a mut io::Reader { self.inner.error() }
|
||||
|
||||
/**
|
||||
* Closes the handle to the child process's stdin.
|
||||
*
|
||||
* If this process is reading its stdin from an existing file descriptor, then this
|
||||
* method does nothing.
|
||||
*/
|
||||
pub fn close_input(&mut self) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
match self.input {
|
||||
Some(-1) | None => (),
|
||||
Some(fd) => {
|
||||
unsafe {
|
||||
libc::close(fd);
|
||||
}
|
||||
self.input = Some(-1);
|
||||
}
|
||||
}
|
||||
self.inner.take_input();
|
||||
}
|
||||
|
||||
fn close_outputs(&mut self) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
fclose_and_null(&mut self.output);
|
||||
fclose_and_null(&mut self.error);
|
||||
|
||||
fn fclose_and_null(f_opt: &mut Option<*libc::FILE>) {
|
||||
#[allow(cstack)]; // fixed_stack_segment declared on enclosing fn
|
||||
match *f_opt {
|
||||
Some(f) if !f.is_null() => {
|
||||
unsafe {
|
||||
libc::fclose(f);
|
||||
*f_opt = Some(0 as *libc::FILE);
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
self.inner.take_output();
|
||||
self.inner.take_error();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -327,29 +173,35 @@ impl Process {
|
|||
*
|
||||
* If the child has already been finished then the exit code is returned.
|
||||
*/
|
||||
pub fn finish(&mut self) -> int {
|
||||
for &code in self.exit_code.iter() {
|
||||
return code;
|
||||
}
|
||||
self.close_input();
|
||||
let code = waitpid(self.pid);
|
||||
self.exit_code = Some(code);
|
||||
return code;
|
||||
}
|
||||
pub fn finish(&mut self) -> int { self.inner.wait() }
|
||||
|
||||
/**
|
||||
* Closes the handle to stdin, waits for the child process to terminate, and reads
|
||||
* and returns all remaining output of stdout and stderr, along with the exit code.
|
||||
* Closes the handle to stdin, waits for the child process to terminate, and
|
||||
* reads and returns all remaining output of stdout and stderr, along with
|
||||
* the exit code.
|
||||
*
|
||||
* If the child has already been finished then the exit code and any remaining
|
||||
* unread output of stdout and stderr will be returned.
|
||||
* If the child has already been finished then the exit code and any
|
||||
* remaining unread output of stdout and stderr will be returned.
|
||||
*
|
||||
* This method will fail if the child process's stdout or stderr streams were
|
||||
* redirected to existing file descriptors.
|
||||
* This method will fail if the child process's stdout or stderr streams
|
||||
* were redirected to existing file descriptors.
|
||||
*/
|
||||
pub fn finish_with_output(&mut self) -> ProcessOutput {
|
||||
let output_file = self.output_file();
|
||||
let error_file = self.error_file();
|
||||
self.inner.take_input(); // close stdin
|
||||
let output = Cell::new(self.inner.take_output());
|
||||
let error = Cell::new(self.inner.take_error());
|
||||
|
||||
fn read_everything(r: &mut io::Reader) -> ~[u8] {
|
||||
let mut ret = ~[];
|
||||
let mut buf = [0, ..1024];
|
||||
loop {
|
||||
match r.read(buf) {
|
||||
Some(n) => { ret.push_all(buf.slice_to(n)); }
|
||||
None => break
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Spawn two entire schedulers to read both stdout and sterr
|
||||
// in parallel so we don't deadlock while blocking on one
|
||||
|
|
@ -359,12 +211,16 @@ impl Process {
|
|||
let ch = SharedChan::new(ch);
|
||||
let ch_clone = ch.clone();
|
||||
do task::spawn_sched(task::SingleThreaded) {
|
||||
let errput = io::FILE_reader(error_file, false);
|
||||
ch.send((2, errput.read_whole_stream()));
|
||||
match error.take() {
|
||||
Some(ref mut e) => ch.send((2, read_everything(*e))),
|
||||
None => ch.send((2, ~[]))
|
||||
}
|
||||
}
|
||||
do task::spawn_sched(task::SingleThreaded) {
|
||||
let output = io::FILE_reader(output_file, false);
|
||||
ch_clone.send((1, output.read_whole_stream()));
|
||||
match output.take() {
|
||||
Some(ref mut e) => ch_clone.send((1, read_everything(*e))),
|
||||
None => ch_clone.send((1, ~[]))
|
||||
}
|
||||
}
|
||||
|
||||
let status = self.finish();
|
||||
|
|
@ -382,40 +238,6 @@ impl Process {
|
|||
error: errs};
|
||||
}
|
||||
|
||||
fn destroy_internal(&mut self, force: bool) {
|
||||
// if the process has finished, and therefore had waitpid called,
|
||||
// and we kill it, then on unix we might ending up killing a
|
||||
// newer process that happens to have the same (re-used) id
|
||||
if self.exit_code.is_none() {
|
||||
killpid(self.pid, force);
|
||||
self.finish();
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn killpid(pid: pid_t, _force: bool) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
unsafe {
|
||||
libc::funcs::extra::kernel32::TerminateProcess(
|
||||
cast::transmute(pid), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn killpid(pid: pid_t, force: bool) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
let signal = if force {
|
||||
libc::consts::os::posix88::SIGKILL
|
||||
} else {
|
||||
libc::consts::os::posix88::SIGTERM
|
||||
};
|
||||
|
||||
unsafe {
|
||||
libc::funcs::posix88::signal::kill(pid, signal as c_int);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the process, giving it a chance to clean itself up if
|
||||
* this is supported by the operating system.
|
||||
|
|
@ -423,7 +245,10 @@ impl Process {
|
|||
* On Posix OSs SIGTERM will be sent to the process. On Win32
|
||||
* TerminateProcess(..) will be called.
|
||||
*/
|
||||
pub fn destroy(&mut self) { self.destroy_internal(false); }
|
||||
pub fn destroy(&mut self) {
|
||||
self.inner.signal(io::process::PleaseExitSignal);
|
||||
self.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the process as soon as possible without giving it a
|
||||
|
|
@ -432,386 +257,12 @@ impl Process {
|
|||
* On Posix OSs SIGKILL will be sent to the process. On Win32
|
||||
* TerminateProcess(..) will be called.
|
||||
*/
|
||||
pub fn force_destroy(&mut self) { self.destroy_internal(true); }
|
||||
}
|
||||
|
||||
impl Drop for Process {
|
||||
fn drop(&mut self) {
|
||||
pub fn force_destroy(&mut self) {
|
||||
self.inner.signal(io::process::MustDieSignal);
|
||||
self.finish();
|
||||
self.close_outputs();
|
||||
free_handle(self.handle);
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnProcessResult {
|
||||
pid: pid_t,
|
||||
handle: *(),
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn spawn_process_os(prog: &str, args: &[~str],
|
||||
env: Option<~[(~str, ~str)]>,
|
||||
dir: Option<&Path>,
|
||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
||||
use libc::consts::os::extra::{
|
||||
TRUE, FALSE,
|
||||
STARTF_USESTDHANDLES,
|
||||
INVALID_HANDLE_VALUE,
|
||||
DUPLICATE_SAME_ACCESS
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
GetCurrentProcess,
|
||||
DuplicateHandle,
|
||||
CloseHandle,
|
||||
CreateProcessA
|
||||
};
|
||||
use libc::funcs::extra::msvcrt::get_osfhandle;
|
||||
|
||||
use sys;
|
||||
|
||||
unsafe {
|
||||
|
||||
let mut si = zeroed_startupinfo();
|
||||
si.cb = sys::size_of::<STARTUPINFO>() as DWORD;
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
let cur_proc = GetCurrentProcess();
|
||||
|
||||
let orig_std_in = get_osfhandle(in_fd) as HANDLE;
|
||||
if orig_std_in == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let orig_std_out = get_osfhandle(out_fd) as HANDLE;
|
||||
if orig_std_out == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let orig_std_err = get_osfhandle(err_fd) as HANDLE;
|
||||
if orig_std_err == INVALID_HANDLE_VALUE as HANDLE {
|
||||
fail2!("failure in get_osfhandle: {}", os::last_os_error());
|
||||
}
|
||||
if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError,
|
||||
0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
|
||||
fail2!("failure in DuplicateHandle: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
let cmd = make_command_line(prog, args);
|
||||
let mut pi = zeroed_process_information();
|
||||
let mut create_err = None;
|
||||
|
||||
do with_envp(env) |envp| {
|
||||
do with_dirp(dir) |dirp| {
|
||||
do cmd.with_c_str |cmdp| {
|
||||
let created = CreateProcessA(ptr::null(), cast::transmute(cmdp),
|
||||
ptr::mut_null(), ptr::mut_null(), TRUE,
|
||||
0, envp, dirp, &mut si, &mut pi);
|
||||
if created == FALSE {
|
||||
create_err = Some(os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(si.hStdInput);
|
||||
CloseHandle(si.hStdOutput);
|
||||
CloseHandle(si.hStdError);
|
||||
|
||||
for msg in create_err.iter() {
|
||||
fail2!("failure in CreateProcess: {}", *msg);
|
||||
}
|
||||
|
||||
// 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. We don't
|
||||
// close the process handle however because we want the process id to stay valid at least
|
||||
// until the calling code closes the process handle.
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
SpawnProcessResult {
|
||||
pid: pi.dwProcessId as pid_t,
|
||||
handle: pi.hProcess as *()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
|
||||
libc::types::os::arch::extra::STARTUPINFO {
|
||||
cb: 0,
|
||||
lpReserved: ptr::mut_null(),
|
||||
lpDesktop: ptr::mut_null(),
|
||||
lpTitle: ptr::mut_null(),
|
||||
dwX: 0,
|
||||
dwY: 0,
|
||||
dwXSize: 0,
|
||||
dwYSize: 0,
|
||||
dwXCountChars: 0,
|
||||
dwYCountCharts: 0,
|
||||
dwFillAttribute: 0,
|
||||
dwFlags: 0,
|
||||
wShowWindow: 0,
|
||||
cbReserved2: 0,
|
||||
lpReserved2: ptr::mut_null(),
|
||||
hStdInput: ptr::mut_null(),
|
||||
hStdOutput: ptr::mut_null(),
|
||||
hStdError: ptr::mut_null()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
libc::types::os::arch::extra::PROCESS_INFORMATION {
|
||||
hProcess: ptr::mut_null(),
|
||||
hThread: ptr::mut_null(),
|
||||
dwProcessId: 0,
|
||||
dwThreadId: 0
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: this is only pub so it can be tested (see issue #4536)
|
||||
#[cfg(windows)]
|
||||
pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
|
||||
let mut cmd = ~"";
|
||||
append_arg(&mut cmd, prog);
|
||||
for arg in args.iter() {
|
||||
cmd.push_char(' ');
|
||||
append_arg(&mut cmd, *arg);
|
||||
}
|
||||
return cmd;
|
||||
|
||||
fn append_arg(cmd: &mut ~str, arg: &str) {
|
||||
let quote = arg.iter().any(|c| c == ' ' || c == '\t');
|
||||
if quote {
|
||||
cmd.push_char('"');
|
||||
}
|
||||
for i in range(0u, arg.len()) {
|
||||
append_char_at(cmd, arg, i);
|
||||
}
|
||||
if quote {
|
||||
cmd.push_char('"');
|
||||
}
|
||||
}
|
||||
|
||||
fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) {
|
||||
match arg[i] as char {
|
||||
'"' => {
|
||||
// Escape quotes.
|
||||
cmd.push_str("\\\"");
|
||||
}
|
||||
'\\' => {
|
||||
if backslash_run_ends_in_quote(arg, i) {
|
||||
// Double all backslashes that are in runs before quotes.
|
||||
cmd.push_str("\\\\");
|
||||
} else {
|
||||
// Pass other backslashes through unescaped.
|
||||
cmd.push_char('\\');
|
||||
}
|
||||
}
|
||||
c => {
|
||||
cmd.push_char(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool {
|
||||
while i < s.len() && s[i] as char == '\\' {
|
||||
i += 1;
|
||||
}
|
||||
return i < s.len() && s[i] as char == '"';
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn spawn_process_os(prog: &str, args: &[~str],
|
||||
env: Option<~[(~str, ~str)]>,
|
||||
dir: Option<&Path>,
|
||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
||||
use libc::funcs::bsd44::getdtablesize;
|
||||
|
||||
mod rustrt {
|
||||
#[abi = "cdecl"]
|
||||
extern {
|
||||
pub fn rust_unset_sigprocmask();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn set_environ(_envp: *c_void) {}
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn set_environ(envp: *c_void) {
|
||||
externfn!(fn _NSGetEnviron() -> *mut *c_void);
|
||||
|
||||
*_NSGetEnviron() = envp;
|
||||
}
|
||||
#[cfg(not(target_os = "macos"), not(windows))]
|
||||
unsafe fn set_environ(envp: *c_void) {
|
||||
extern {
|
||||
static mut environ: *c_void;
|
||||
}
|
||||
environ = envp;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
||||
let pid = fork();
|
||||
if pid < 0 {
|
||||
fail2!("failure in fork: {}", os::last_os_error());
|
||||
} else if pid > 0 {
|
||||
return SpawnProcessResult {pid: pid, handle: ptr::null()};
|
||||
}
|
||||
|
||||
rustrt::rust_unset_sigprocmask();
|
||||
|
||||
if dup2(in_fd, 0) == -1 {
|
||||
fail2!("failure in dup2(in_fd, 0): {}", os::last_os_error());
|
||||
}
|
||||
if dup2(out_fd, 1) == -1 {
|
||||
fail2!("failure in dup2(out_fd, 1): {}", os::last_os_error());
|
||||
}
|
||||
if dup2(err_fd, 2) == -1 {
|
||||
fail2!("failure in dup3(err_fd, 2): {}", os::last_os_error());
|
||||
}
|
||||
// close all other fds
|
||||
for fd in range(3, getdtablesize()).invert() {
|
||||
close(fd as c_int);
|
||||
}
|
||||
|
||||
do with_dirp(dir) |dirp| {
|
||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
||||
fail2!("failure in chdir: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
do with_envp(env) |envp| {
|
||||
if !envp.is_null() {
|
||||
set_environ(envp);
|
||||
}
|
||||
do with_argv(prog, args) |argv| {
|
||||
execvp(*argv, argv);
|
||||
// execvp only returns if an error occurred
|
||||
fail2!("failure in execvp: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn with_argv<T>(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T {
|
||||
use vec;
|
||||
|
||||
// We can't directly convert `str`s into `*char`s, as someone needs to hold
|
||||
// a reference to the intermediary byte buffers. So first build an array to
|
||||
// hold all the ~[u8] byte strings.
|
||||
let mut tmps = vec::with_capacity(args.len() + 1);
|
||||
|
||||
tmps.push(prog.to_c_str());
|
||||
|
||||
for arg in args.iter() {
|
||||
tmps.push(arg.to_c_str());
|
||||
}
|
||||
|
||||
// Next, convert each of the byte strings into a pointer. This is
|
||||
// technically unsafe as the caller could leak these pointers out of our
|
||||
// scope.
|
||||
let mut ptrs = do tmps.map |tmp| {
|
||||
tmp.with_ref(|buf| buf)
|
||||
};
|
||||
|
||||
// Finally, make sure we add a null pointer.
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
ptrs.as_imm_buf(|buf, _| cb(buf))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T {
|
||||
use vec;
|
||||
|
||||
// On posixy systems we can pass a char** for envp, which is a
|
||||
// null-terminated array of "k=v\n" strings. Like `with_argv`, we have to
|
||||
// have a temporary buffer to hold the intermediary `~[u8]` byte strings.
|
||||
match env {
|
||||
Some(env) => {
|
||||
let mut tmps = vec::with_capacity(env.len());
|
||||
|
||||
for pair in env.iter() {
|
||||
let kv = format!("{}={}", pair.first(), pair.second());
|
||||
tmps.push(kv.to_c_str());
|
||||
}
|
||||
|
||||
// Once again, this is unsafe.
|
||||
let mut ptrs = do tmps.map |tmp| {
|
||||
tmp.with_ref(|buf| buf)
|
||||
};
|
||||
ptrs.push(ptr::null());
|
||||
|
||||
do ptrs.as_imm_buf |buf, _| {
|
||||
unsafe { cb(cast::transmute(buf)) }
|
||||
}
|
||||
}
|
||||
_ => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T {
|
||||
// On win32 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 = ~[];
|
||||
|
||||
for pair in env.iter() {
|
||||
let kv = format!("{}={}", pair.first(), pair.second());
|
||||
blk.push_all(kv.as_bytes());
|
||||
blk.push(0);
|
||||
}
|
||||
|
||||
blk.push(0);
|
||||
|
||||
do blk.as_imm_buf |p, _len| {
|
||||
unsafe { cb(cast::transmute(p)) }
|
||||
}
|
||||
}
|
||||
_ => cb(ptr::mut_null())
|
||||
}
|
||||
}
|
||||
|
||||
fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T {
|
||||
match d {
|
||||
Some(dir) => dir.with_c_str(|buf| cb(buf)),
|
||||
None => cb(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn free_handle(handle: *()) {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
unsafe {
|
||||
libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn free_handle(_handle: *()) {
|
||||
// unix has no process handle object, just a pid
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a process and waits for it to terminate. The process will
|
||||
* inherit the current stdin/stdout/stderr file descriptors.
|
||||
|
|
@ -825,13 +276,14 @@ fn free_handle(_handle: *()) {
|
|||
*
|
||||
* The process's exit code
|
||||
*/
|
||||
#[fixed_stack_segment] #[inline(never)]
|
||||
pub fn process_status(prog: &str, args: &[~str]) -> int {
|
||||
let mut prog = Process::new(prog, args, ProcessOptions {
|
||||
env: None,
|
||||
dir: None,
|
||||
in_fd: Some(0),
|
||||
out_fd: Some(1),
|
||||
err_fd: Some(2)
|
||||
in_fd: Some(unsafe { libc::dup(libc::STDIN_FILENO) }),
|
||||
out_fd: Some(unsafe { libc::dup(libc::STDOUT_FILENO) }),
|
||||
err_fd: Some(unsafe { libc::dup(libc::STDERR_FILENO) })
|
||||
});
|
||||
prog.finish()
|
||||
}
|
||||
|
|
@ -853,110 +305,8 @@ pub fn process_output(prog: &str, args: &[~str]) -> ProcessOutput {
|
|||
prog.finish_with_output()
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a process to exit and returns the exit code, failing
|
||||
* if there is no process with the specified id.
|
||||
*
|
||||
* Note that this is private to avoid race conditions on unix where if
|
||||
* a user calls waitpid(some_process.get_id()) then some_process.finish()
|
||||
* and some_process.destroy() and some_process.finalize() will then either
|
||||
* operate on a none-existent process or, even worse, on a newer process
|
||||
* with the same id.
|
||||
*/
|
||||
fn waitpid(pid: pid_t) -> int {
|
||||
return waitpid_os(pid);
|
||||
|
||||
#[cfg(windows)]
|
||||
fn waitpid_os(pid: pid_t) -> int {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::types::os::arch::extra::DWORD;
|
||||
use libc::consts::os::extra::{
|
||||
SYNCHRONIZE,
|
||||
PROCESS_QUERY_INFORMATION,
|
||||
FALSE,
|
||||
STILL_ACTIVE,
|
||||
INFINITE,
|
||||
WAIT_FAILED
|
||||
};
|
||||
use libc::funcs::extra::kernel32::{
|
||||
OpenProcess,
|
||||
GetExitCodeProcess,
|
||||
CloseHandle,
|
||||
WaitForSingleObject
|
||||
};
|
||||
|
||||
unsafe {
|
||||
|
||||
let proc = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid as DWORD);
|
||||
if proc.is_null() {
|
||||
fail2!("failure in OpenProcess: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
if GetExitCodeProcess(proc, &mut status) == FALSE {
|
||||
CloseHandle(proc);
|
||||
fail2!("failure in GetExitCodeProcess: {}", os::last_os_error());
|
||||
}
|
||||
if status != STILL_ACTIVE {
|
||||
CloseHandle(proc);
|
||||
return status as int;
|
||||
}
|
||||
if WaitForSingleObject(proc, INFINITE) == WAIT_FAILED {
|
||||
CloseHandle(proc);
|
||||
fail2!("failure in WaitForSingleObject: {}", os::last_os_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn waitpid_os(pid: pid_t) -> int {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
use libc::funcs::posix01::wait::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
fn WIFEXITED(status: i32) -> bool {
|
||||
(status & 0xffi32) == 0i32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn WIFEXITED(status: i32) -> bool {
|
||||
(status & 0x7fi32) == 0i32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
fn WEXITSTATUS(status: i32) -> i32 {
|
||||
(status >> 8i32) & 0xffi32
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
fn WEXITSTATUS(status: i32) -> i32 {
|
||||
status >> 8i32
|
||||
}
|
||||
|
||||
let mut status = 0 as c_int;
|
||||
if unsafe { waitpid(pid, &mut status, 0) } == -1 {
|
||||
fail2!("failure in waitpid: {}", os::last_os_error());
|
||||
}
|
||||
|
||||
return if WIFEXITED(status) {
|
||||
WEXITSTATUS(status) as int
|
||||
} else {
|
||||
1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use io;
|
||||
use libc::c_int;
|
||||
use option::{Option, None, Some};
|
||||
use os;
|
||||
|
|
@ -964,27 +314,8 @@ mod tests {
|
|||
use run;
|
||||
use str;
|
||||
use unstable::running_on_valgrind;
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_make_command_line() {
|
||||
assert_eq!(
|
||||
run::make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]),
|
||||
~"prog aaa bbb ccc"
|
||||
);
|
||||
assert_eq!(
|
||||
run::make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]),
|
||||
~"\"C:\\Program Files\\blah\\blah.exe\" aaa"
|
||||
);
|
||||
assert_eq!(
|
||||
run::make_command_line("C:\\Program Files\\test", [~"aa\"bb"]),
|
||||
~"\"C:\\Program Files\\test\" aa\\\"bb"
|
||||
);
|
||||
assert_eq!(
|
||||
run::make_command_line("echo", [~"a b c"]),
|
||||
~"echo \"a b c\""
|
||||
);
|
||||
}
|
||||
use rt::io::native::file;
|
||||
use rt::io::{Writer, Reader};
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os="android"))]
|
||||
|
|
@ -1068,10 +399,6 @@ mod tests {
|
|||
err_fd: Some(pipe_err.out)
|
||||
});
|
||||
|
||||
assert!(proc.input_redirected());
|
||||
assert!(proc.output_redirected());
|
||||
assert!(proc.error_redirected());
|
||||
|
||||
os::close(pipe_in.input);
|
||||
os::close(pipe_out.out);
|
||||
os::close(pipe_err.out);
|
||||
|
|
@ -1086,21 +413,21 @@ mod tests {
|
|||
}
|
||||
|
||||
fn writeclose(fd: c_int, s: &str) {
|
||||
let writer = io::fd_writer(fd, false);
|
||||
writer.write_str(s);
|
||||
os::close(fd);
|
||||
let mut writer = file::FileDesc::new(fd);
|
||||
writer.write(s.as_bytes());
|
||||
}
|
||||
|
||||
fn readclose(fd: c_int) -> ~str {
|
||||
#[fixed_stack_segment]; #[inline(never)];
|
||||
|
||||
unsafe {
|
||||
let file = os::fdopen(fd);
|
||||
let reader = io::FILE_reader(file, false);
|
||||
let buf = reader.read_whole_stream();
|
||||
os::fclose(file);
|
||||
str::from_utf8(buf)
|
||||
let mut res = ~[];
|
||||
let mut reader = file::FileDesc::new(fd);
|
||||
let mut buf = [0, ..1024];
|
||||
loop {
|
||||
match reader.read(buf) {
|
||||
Some(n) => { res.push_all(buf.slice_to(n)); }
|
||||
None => break
|
||||
}
|
||||
}
|
||||
str::from_utf8_owned(res)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1223,36 +550,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
#[cfg(not(windows),not(target_os="android"))]
|
||||
fn test_finish_with_output_redirected() {
|
||||
let mut prog = run::Process::new("echo", [~"hello"], run::ProcessOptions {
|
||||
env: None,
|
||||
dir: None,
|
||||
in_fd: Some(0),
|
||||
out_fd: Some(1),
|
||||
err_fd: Some(2)
|
||||
});
|
||||
// this should fail because it is not valid to read the output when it was redirected
|
||||
prog.finish_with_output();
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
#[cfg(not(windows),target_os="android")]
|
||||
fn test_finish_with_output_redirected() {
|
||||
let mut prog = run::Process::new("/system/bin/sh", [~"-c",~"echo hello"],
|
||||
run::ProcessOptions {
|
||||
env: None,
|
||||
dir: None,
|
||||
in_fd: Some(0),
|
||||
out_fd: Some(1),
|
||||
err_fd: Some(2)
|
||||
});
|
||||
// this should fail because it is not valid to read the output when it was redirected
|
||||
prog.finish_with_output();
|
||||
}
|
||||
|
||||
#[cfg(unix,not(target_os="android"))]
|
||||
fn run_pwd(dir: Option<&Path>) -> run::Process {
|
||||
run::Process::new("pwd", [], run::ProcessOptions {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
extern mod extra;
|
||||
|
||||
use extra::time::precise_time_s;
|
||||
use std::io;
|
||||
use std::os;
|
||||
use std::rand::Rng;
|
||||
use std::rand;
|
||||
|
|
@ -70,11 +69,15 @@ fn shift_push() {
|
|||
}
|
||||
|
||||
fn read_line() {
|
||||
use std::rt::io::{Reader, Open};
|
||||
use std::rt::io::file::FileInfo;
|
||||
use std::rt::io::buffered::BufferedReader;
|
||||
|
||||
let path = Path(env!("CFG_SRC_DIR"))
|
||||
.push_rel(&Path("src/test/bench/shootout-k-nucleotide.data"));
|
||||
|
||||
for _ in range(0, 3) {
|
||||
let reader = io::file_reader(&path).unwrap();
|
||||
let mut reader = BufferedReader::new(path.open_reader(Open).unwrap());
|
||||
while !reader.eof() {
|
||||
reader.read_line();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,17 +156,21 @@ fn make_sequence_processor(sz: uint,
|
|||
|
||||
// given a FASTA file on stdin, process sequence THREE
|
||||
fn main() {
|
||||
use std::rt::io::{Reader, Open};
|
||||
use std::rt::io::file::FileInfo;
|
||||
use std::rt::io::native::stdio;
|
||||
use std::rt::io::buffered::BufferedReader;
|
||||
|
||||
let rdr = if os::getenv("RUST_BENCH").is_some() {
|
||||
// FIXME: Using this compile-time env variable is a crummy way to
|
||||
// get to this massive data set, but include_bin! chokes on it (#2598)
|
||||
let path = Path(env!("CFG_SRC_DIR"))
|
||||
.push_rel(&Path("src/test/bench/shootout-k-nucleotide.data"));
|
||||
io::file_reader(&path).unwrap()
|
||||
} else {
|
||||
io::stdin()
|
||||
};
|
||||
|
||||
|
||||
// FIXME: Using this compile-time env variable is a crummy way to
|
||||
// get to this massive data set, but include_bin! chokes on it (#2598)
|
||||
let path = Path(env!("CFG_SRC_DIR"))
|
||||
.push_rel(&Path("src/test/bench/shootout-k-nucleotide.data"));
|
||||
~path.open_reader(Open).unwrap() as ~Reader
|
||||
} else {
|
||||
~stdio::stdin() as ~Reader
|
||||
};
|
||||
let mut rdr = BufferedReader::new(rdr);
|
||||
|
||||
// initialize each sequence sorter
|
||||
let sizes = ~[1u,2,3,4,6,12,18];
|
||||
|
|
@ -193,8 +197,11 @@ fn main() {
|
|||
// reading the sequence of interest
|
||||
let mut proc_mode = false;
|
||||
|
||||
while !rdr.eof() {
|
||||
let line: ~str = rdr.read_line();
|
||||
loop {
|
||||
let line = match rdr.read_line() {
|
||||
Some(ln) => ln, None => break,
|
||||
};
|
||||
let line = line.trim().to_owned();
|
||||
|
||||
if line.len() == 0u { continue; }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue