about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-10-10 04:31:24 -0700
committerbors <bors@rust-lang.org>2013-10-10 04:31:24 -0700
commit0ede2ea4e2e9384ac5bd614012d85ed213873dab (patch)
tree1c1273aa2aabe17e0557c01b41d4d438c5dd130e /src
parent34d123db4eb03c1b2378b6248ebea5f0f40f2a4f (diff)
parent413747176c9ce52a87775175e096b3eca88e6b64 (diff)
downloadrust-0ede2ea4e2e9384ac5bd614012d85ed213873dab.tar.gz
rust-0ede2ea4e2e9384ac5bd614012d85ed213873dab.zip
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?).
Diffstat (limited to 'src')
-rw-r--r--src/compiletest/errors.rs15
-rw-r--r--src/compiletest/header.rs18
-rw-r--r--src/compiletest/procsrv.rs2
-rw-r--r--src/libstd/cleanup.rs17
-rw-r--r--src/libstd/io.rs37
-rw-r--r--src/libstd/macros.rs2
-rw-r--r--src/libstd/prelude.rs2
-rw-r--r--src/libstd/rt/borrowck.rs56
-rw-r--r--src/libstd/rt/io/buffered.rs72
-rw-r--r--src/libstd/rt/io/file.rs2
-rw-r--r--src/libstd/rt/io/mod.rs27
-rw-r--r--src/libstd/rt/io/native/file.rs256
-rw-r--r--src/libstd/rt/io/native/process.rs745
-rw-r--r--src/libstd/rt/io/native/stdio.rs67
-rw-r--r--src/libstd/rt/io/process.rs7
-rw-r--r--src/libstd/rt/io/stdio.rs105
-rw-r--r--src/libstd/rt/logging.rs49
-rw-r--r--src/libstd/rt/util.rs10
-rw-r--r--src/libstd/run.rs859
-rw-r--r--src/test/bench/core-std.rs7
-rw-r--r--src/test/bench/shootout-k-nucleotide-pipes.rs31
21 files changed, 1382 insertions, 1004 deletions
diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs
index 02195e684e3..e49a9701460 100644
--- a/src/compiletest/errors.rs
+++ b/src/compiletest/errors.rs
@@ -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) {
diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs
index 36780abc7ee..730df66af23 100644
--- a/src/compiletest/header.rs
+++ b/src/compiletest/header.rs
@@ -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;
 }
diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs
index ef8049aa717..829916117d2 100644
--- a/src/compiletest/procsrv.rs
+++ b/src/compiletest/procsrv.rs
@@ -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();
 
diff --git a/src/libstd/cleanup.rs b/src/libstd/cleanup.rs
index 6b982ec75da..1c9944664ee 100644
--- a/src/libstd/cleanup.rs
+++ b/src/libstd/cleanup.rs
@@ -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);
     }
 }
diff --git a/src/libstd/io.rs b/src/libstd/io.rs
index 2dfd41a4435..791616d330e 100644
--- a/src/libstd/io.rs
+++ b/src/libstd/io.rs
@@ -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?
@@ -2087,16 +2060,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();
         wr.write([0u8, 1u8, 2u8, 3u8]);
diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs
index 2ef25548535..17dc03d0098 100644
--- a/src/libstd/macros.rs
+++ b/src/libstd/macros.rs
@@ -13,7 +13,7 @@
 
 macro_rules! rterrln (
     ($($arg:tt)*) => ( {
-        ::rt::util::dumb_println(format!($($arg)*));
+        format_args!(::rt::util::dumb_println, $($arg)*)
     } )
 )
 
diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs
index 273a01c1811..3da337add94 100644
--- a/src/libstd/prelude.rs
+++ b/src/libstd/prelude.rs
@@ -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;
 
diff --git a/src/libstd/rt/borrowck.rs b/src/libstd/rt/borrowck.rs
index d703272420c..6be23a983ab 100644
--- a/src/libstd/rt/borrowck.rs
+++ b/src/libstd/rt/borrowck.rs
@@ -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);
     }
 }
 
diff --git a/src/libstd/rt/io/buffered.rs b/src/libstd/rt/io/buffered.rs
index 2269469ee23..9dcb35c806f 100644
--- a/src/libstd/rt/io/buffered.rs
+++ b/src/libstd/rt/io/buffered.rs
@@ -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);
+    }
 }
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs
index a18eec8773e..3258c350cd0 100644
--- a/src/libstd/rt/io/file.rs
+++ b/src/libstd/rt/io/file.rs
@@ -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
diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs
index f14f8f28d12..f9542cbf5f9 100644
--- a/src/libstd/rt/io/mod.rs
+++ b/src/libstd/rt/io/mod.rs
@@ -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 {}
diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs
index 47ae89ccf9f..dc8d34d1b11 100644
--- a/src/libstd/rt/io/native/file.rs
+++ b/src/libstd/rt/io/native/file.rs
@@ -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)
+            }
+        }
+    }
 }
diff --git a/src/libstd/rt/io/native/process.rs b/src/libstd/rt/io/native/process.rs
new file mode 100644
index 00000000000..d338192c664
--- /dev/null
+++ b/src/libstd/rt/io/native/process.rs
@@ -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
+}
diff --git a/src/libstd/rt/io/native/stdio.rs b/src/libstd/rt/io/native/stdio.rs
new file mode 100644
index 00000000000..5661725d77b
--- /dev/null
+++ b/src/libstd/rt/io/native/stdio.rs
@@ -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() }
+}
diff --git a/src/libstd/rt/io/process.rs b/src/libstd/rt/io/process.rs
index c190547889d..5f2453852ee 100644
--- a/src/libstd/rt/io/process.rs
+++ b/src/libstd/rt/io/process.rs
@@ -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>],
diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs
index 734a40429a6..e3ca148862f 100644
--- a/src/libstd/rt/io/stdio.rs
+++ b/src/libstd/rt/io/stdio.rs
@@ -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!() }
+/// 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 fn print(_s: &str) { fail2!() }
+/// 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 }
+}
 
-pub fn println(_s: &str) { 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 }
+}
 
-pub enum StdStream {
-    StdIn,
-    StdOut,
-    StdErr
+/// 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());
 }
 
-pub struct StdReader;
+/// 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]);
+}
 
-impl StdReader {
-    pub fn new(_stream: StdStream) -> StdReader { fail2!() }
+/// 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)
+        }
+    }
 }
diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs
index b08e76921d8..660d1cd4359 100644
--- a/src/libstd/rt/logging.rs
+++ b/src/libstd/rt/logging.rs
@@ -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 => {}
             }
diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs
index 68996a3a2a5..727bdb782d2 100644
--- a/src/libstd/rt/util.rs
+++ b/src/libstd/rt/util.rs
@@ -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) -> ! {
diff --git a/src/libstd/run.rs b/src/libstd/run.rs
index 074c232e149..8712d01aae9 100644
--- a/src/libstd/run.rs
+++ b/src/libstd/run.rs
@@ -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 {
diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs
index 7323dcf4ecb..5222c4f59b7 100644
--- a/src/test/bench/core-std.rs
+++ b/src/test/bench/core-std.rs
@@ -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();
         }
diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs
index ff7e7192b9f..445b28b693c 100644
--- a/src/test/bench/shootout-k-nucleotide-pipes.rs
+++ b/src/test/bench/shootout-k-nucleotide-pipes.rs
@@ -156,17 +156,21 @@ fn make_sequence_processor(sz: uint,
 
 // given a FASTA file on stdin, process sequence THREE
 fn main() {
-    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()
-   };
-
+    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"));
+        ~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; }