about summary refs log tree commit diff
path: root/src/libstd/old_io
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2015-01-27 09:38:30 -0800
committerBrian Anderson <banderson@mozilla.com>2015-01-27 15:05:04 -0800
commit71223050538939ed758fcd3b9114f71abff20bb2 (patch)
tree43ddd18223904fa86601f1a0e16ebcbaddead270 /src/libstd/old_io
parent3c172392cf0c86ffd1d7b39d3f44de98f77afc44 (diff)
parent777435990e0e91df6b72ce80c9b6fa485eeb5daa (diff)
downloadrust-71223050538939ed758fcd3b9114f71abff20bb2.tar.gz
rust-71223050538939ed758fcd3b9114f71abff20bb2.zip
Merge remote-tracking branch 'rust-lang/master'
Conflicts:
	src/libcore/cell.rs
	src/librustc_driver/test.rs
	src/libstd/old_io/net/tcp.rs
	src/libstd/old_io/process.rs
Diffstat (limited to 'src/libstd/old_io')
-rw-r--r--src/libstd/old_io/buffered.rs697
-rw-r--r--src/libstd/old_io/comm_adapters.rs248
-rw-r--r--src/libstd/old_io/extensions.rs562
-rw-r--r--src/libstd/old_io/fs.rs1572
-rw-r--r--src/libstd/old_io/mem.rs759
-rw-r--r--src/libstd/old_io/mod.rs1961
-rw-r--r--src/libstd/old_io/net/addrinfo.rs137
-rw-r--r--src/libstd/old_io/net/ip.rs700
-rw-r--r--src/libstd/old_io/net/mod.rs46
-rw-r--r--src/libstd/old_io/net/pipe.rs869
-rw-r--r--src/libstd/old_io/net/tcp.rs1477
-rw-r--r--src/libstd/old_io/net/udp.rs459
-rw-r--r--src/libstd/old_io/pipe.rs139
-rw-r--r--src/libstd/old_io/process.rs1230
-rw-r--r--src/libstd/old_io/result.rs129
-rw-r--r--src/libstd/old_io/stdio.rs566
-rw-r--r--src/libstd/old_io/tempfile.rs182
-rw-r--r--src/libstd/old_io/test.rs175
-rw-r--r--src/libstd/old_io/timer.rs481
-rw-r--r--src/libstd/old_io/util.rs444
20 files changed, 12833 insertions, 0 deletions
diff --git a/src/libstd/old_io/buffered.rs b/src/libstd/old_io/buffered.rs
new file mode 100644
index 00000000000..1590598c0b8
--- /dev/null
+++ b/src/libstd/old_io/buffered.rs
@@ -0,0 +1,697 @@
+// 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.
+//
+// ignore-lexer-test FIXME #15883
+
+//! Buffering wrappers for I/O traits
+
+use cmp;
+use fmt;
+use old_io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult};
+use iter::{IteratorExt, ExactSizeIterator, repeat};
+use ops::Drop;
+use option::Option;
+use option::Option::{Some, None};
+use result::Result::Ok;
+use slice::{SliceExt};
+use slice;
+use vec::Vec;
+
+/// Wraps a Reader and buffers input from it
+///
+/// It can be excessively inefficient to work directly with a `Reader`. For
+/// example, every call to `read` on `TcpStream` results in a system call. A
+/// `BufferedReader` performs large, infrequent reads on the underlying
+/// `Reader` and maintains an in-memory buffer of the results.
+///
+/// # Example
+///
+/// ```rust
+/// use std::old_io::{BufferedReader, File};
+///
+/// let file = File::open(&Path::new("message.txt"));
+/// let mut reader = BufferedReader::new(file);
+///
+/// let mut buf = [0; 100];
+/// match reader.read(&mut buf) {
+///     Ok(nread) => println!("Read {} bytes", nread),
+///     Err(e) => println!("error reading: {}", e)
+/// }
+/// ```
+pub struct BufferedReader<R> {
+    inner: R,
+    buf: Vec<u8>,
+    pos: uint,
+    cap: uint,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<R> fmt::Debug for BufferedReader<R> where R: fmt::Debug {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "BufferedReader {{ reader: {:?}, buffer: {}/{} }}",
+               self.inner, self.cap - self.pos, self.buf.len())
+    }
+}
+
+impl<R: Reader> BufferedReader<R> {
+    /// Creates a new `BufferedReader` with the specified buffer capacity
+    pub fn with_capacity(cap: uint, inner: R) -> BufferedReader<R> {
+        BufferedReader {
+            inner: inner,
+            // We can't use the same trick here as we do for BufferedWriter,
+            // since this memory is visible to the inner Reader.
+            buf: repeat(0).take(cap).collect(),
+            pos: 0,
+            cap: 0,
+        }
+    }
+
+    /// Creates a new `BufferedReader` with a default buffer capacity
+    pub fn new(inner: R) -> BufferedReader<R> {
+        BufferedReader::with_capacity(DEFAULT_BUF_SIZE, inner)
+    }
+
+    /// Gets a reference to the underlying reader.
+    pub fn get_ref<'a>(&self) -> &R { &self.inner }
+
+    /// Gets a mutable reference to the underlying reader.
+    ///
+    /// # Warning
+    ///
+    /// It is inadvisable to directly read from the underlying reader.
+    pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
+
+    /// Unwraps this `BufferedReader`, returning the underlying reader.
+    ///
+    /// Note that any leftover data in the internal buffer is lost.
+    pub fn into_inner(self) -> R { self.inner }
+}
+
+impl<R: Reader> Buffer for BufferedReader<R> {
+    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
+        if self.pos == self.cap {
+            self.cap = try!(self.inner.read(self.buf.as_mut_slice()));
+            self.pos = 0;
+        }
+        Ok(&self.buf[self.pos..self.cap])
+    }
+
+    fn consume(&mut self, amt: uint) {
+        self.pos += amt;
+        assert!(self.pos <= self.cap);
+    }
+}
+
+impl<R: Reader> Reader for BufferedReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        if self.pos == self.cap && buf.len() >= self.buf.capacity() {
+            return self.inner.read(buf);
+        }
+        let nread = {
+            let available = try!(self.fill_buf());
+            let nread = cmp::min(available.len(), buf.len());
+            slice::bytes::copy_memory(buf, &available[..nread]);
+            nread
+        };
+        self.pos += nread;
+        Ok(nread)
+    }
+}
+
+/// Wraps a Writer and buffers output to it
+///
+/// It can be excessively inefficient to work directly with a `Writer`. For
+/// example, every call to `write` on `TcpStream` results in a system call. A
+/// `BufferedWriter` keeps an in memory buffer of data and writes it to the
+/// underlying `Writer` in large, infrequent batches.
+///
+/// This writer will be flushed when it is dropped.
+///
+/// # Example
+///
+/// ```rust
+/// use std::old_io::{BufferedWriter, File};
+///
+/// let file = File::create(&Path::new("message.txt")).unwrap();
+/// let mut writer = BufferedWriter::new(file);
+///
+/// writer.write_str("hello, world").unwrap();
+/// writer.flush().unwrap();
+/// ```
+pub struct BufferedWriter<W> {
+    inner: Option<W>,
+    buf: Vec<u8>,
+    pos: uint
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<W> fmt::Debug for BufferedWriter<W> where W: fmt::Debug {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "BufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
+               self.inner.as_ref().unwrap(), self.pos, self.buf.len())
+    }
+}
+
+impl<W: Writer> BufferedWriter<W> {
+    /// Creates a new `BufferedWriter` with the specified buffer capacity
+    pub fn with_capacity(cap: uint, inner: W) -> BufferedWriter<W> {
+        // It's *much* faster to create an uninitialized buffer than it is to
+        // fill everything in with 0. This buffer is entirely an implementation
+        // detail and is never exposed, so we're safe to not initialize
+        // everything up-front. This allows creation of BufferedWriter instances
+        // to be very cheap (large mallocs are not nearly as expensive as large
+        // callocs).
+        let mut buf = Vec::with_capacity(cap);
+        unsafe { buf.set_len(cap); }
+        BufferedWriter {
+            inner: Some(inner),
+            buf: buf,
+            pos: 0
+        }
+    }
+
+    /// Creates a new `BufferedWriter` with a default buffer capacity
+    pub fn new(inner: W) -> BufferedWriter<W> {
+        BufferedWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
+    }
+
+    fn flush_buf(&mut self) -> IoResult<()> {
+        if self.pos != 0 {
+            let ret = self.inner.as_mut().unwrap().write_all(&self.buf[..self.pos]);
+            self.pos = 0;
+            ret
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Gets a reference to the underlying writer.
+    pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() }
+
+    /// Gets a mutable reference to the underlying write.
+    ///
+    /// # Warning
+    ///
+    /// It is inadvisable to directly read from the underlying writer.
+    pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() }
+
+    /// Unwraps this `BufferedWriter`, returning the underlying writer.
+    ///
+    /// The buffer is flushed before returning the writer.
+    pub fn into_inner(mut self) -> W {
+        // FIXME(#12628): is panicking the right thing to do if flushing panicks?
+        self.flush_buf().unwrap();
+        self.inner.take().unwrap()
+    }
+}
+
+impl<W: Writer> Writer for BufferedWriter<W> {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        if self.pos + buf.len() > self.buf.len() {
+            try!(self.flush_buf());
+        }
+
+        if buf.len() > self.buf.len() {
+            self.inner.as_mut().unwrap().write_all(buf)
+        } else {
+            let dst = &mut self.buf[self.pos..];
+            slice::bytes::copy_memory(dst, buf);
+            self.pos += buf.len();
+            Ok(())
+        }
+    }
+
+    fn flush(&mut self) -> IoResult<()> {
+        self.flush_buf().and_then(|()| self.inner.as_mut().unwrap().flush())
+    }
+}
+
+#[unsafe_destructor]
+impl<W: Writer> Drop for BufferedWriter<W> {
+    fn drop(&mut self) {
+        if self.inner.is_some() {
+            // dtors should not panic, so we ignore a panicked flush
+            let _ = self.flush_buf();
+        }
+    }
+}
+
+/// Wraps a Writer and buffers output to it, flushing whenever a newline (`0x0a`,
+/// `'\n'`) is detected.
+///
+/// This writer will be flushed when it is dropped.
+pub struct LineBufferedWriter<W> {
+    inner: BufferedWriter<W>,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<W> fmt::Debug for LineBufferedWriter<W> where W: fmt::Debug {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "LineBufferedWriter {{ writer: {:?}, buffer: {}/{} }}",
+               self.inner.inner, self.inner.pos, self.inner.buf.len())
+    }
+}
+
+impl<W: Writer> LineBufferedWriter<W> {
+    /// Creates a new `LineBufferedWriter`
+    pub fn new(inner: W) -> LineBufferedWriter<W> {
+        // Lines typically aren't that long, don't use a giant buffer
+        LineBufferedWriter {
+            inner: BufferedWriter::with_capacity(1024, inner)
+        }
+    }
+
+    /// Gets a reference to the underlying writer.
+    ///
+    /// This type does not expose the ability to get a mutable reference to the
+    /// underlying reader because that could possibly corrupt the buffer.
+    pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() }
+
+    /// Unwraps this `LineBufferedWriter`, returning the underlying writer.
+    ///
+    /// The internal buffer is flushed before returning the writer.
+    pub fn into_inner(self) -> W { self.inner.into_inner() }
+}
+
+impl<W: Writer> Writer for LineBufferedWriter<W> {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        match buf.iter().rposition(|&b| b == b'\n') {
+            Some(i) => {
+                try!(self.inner.write_all(&buf[..i + 1]));
+                try!(self.inner.flush());
+                try!(self.inner.write_all(&buf[i + 1..]));
+                Ok(())
+            }
+            None => self.inner.write_all(buf),
+        }
+    }
+
+    fn flush(&mut self) -> IoResult<()> { self.inner.flush() }
+}
+
+struct InternalBufferedWriter<W>(BufferedWriter<W>);
+
+impl<W> InternalBufferedWriter<W> {
+    fn get_mut<'a>(&'a mut self) -> &'a mut BufferedWriter<W> {
+        let InternalBufferedWriter(ref mut w) = *self;
+        return w;
+    }
+}
+
+impl<W: Reader> Reader for InternalBufferedWriter<W> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.get_mut().inner.as_mut().unwrap().read(buf)
+    }
+}
+
+/// Wraps a Stream and buffers input and output to and from it.
+///
+/// It can be excessively inefficient to work directly with a `Stream`. For
+/// example, every call to `read` or `write` on `TcpStream` results in a system
+/// call. A `BufferedStream` keeps in memory buffers of data, making large,
+/// infrequent calls to `read` and `write` on the underlying `Stream`.
+///
+/// The output half will be flushed when this stream is dropped.
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::{BufferedStream, File};
+///
+/// let file = File::open(&Path::new("message.txt"));
+/// let mut stream = BufferedStream::new(file);
+///
+/// stream.write_all("hello, world".as_bytes());
+/// stream.flush();
+///
+/// let mut buf = [0; 100];
+/// match stream.read(&mut buf) {
+///     Ok(nread) => println!("Read {} bytes", nread),
+///     Err(e) => println!("error reading: {}", e)
+/// }
+/// ```
+pub struct BufferedStream<S> {
+    inner: BufferedReader<InternalBufferedWriter<S>>
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<S> fmt::Debug for BufferedStream<S> where S: fmt::Debug {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        let reader = &self.inner;
+        let writer = &self.inner.inner.0;
+        write!(fmt, "BufferedStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}",
+               writer.inner,
+               writer.pos, writer.buf.len(),
+               reader.cap - reader.pos, reader.buf.len())
+    }
+}
+
+impl<S: Stream> BufferedStream<S> {
+    /// Creates a new buffered stream with explicitly listed capacities for the
+    /// reader/writer buffer.
+    pub fn with_capacities(reader_cap: uint, writer_cap: uint, inner: S)
+                           -> BufferedStream<S> {
+        let writer = BufferedWriter::with_capacity(writer_cap, inner);
+        let internal_writer = InternalBufferedWriter(writer);
+        let reader = BufferedReader::with_capacity(reader_cap,
+                                                   internal_writer);
+        BufferedStream { inner: reader }
+    }
+
+    /// Creates a new buffered stream with the default reader/writer buffer
+    /// capacities.
+    pub fn new(inner: S) -> BufferedStream<S> {
+        BufferedStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE,
+                                        inner)
+    }
+
+    /// Gets a reference to the underlying stream.
+    pub fn get_ref(&self) -> &S {
+        let InternalBufferedWriter(ref w) = self.inner.inner;
+        w.get_ref()
+    }
+
+    /// Gets a mutable reference to the underlying stream.
+    ///
+    /// # Warning
+    ///
+    /// It is inadvisable to read directly from or write directly to the
+    /// underlying stream.
+    pub fn get_mut(&mut self) -> &mut S {
+        let InternalBufferedWriter(ref mut w) = self.inner.inner;
+        w.get_mut()
+    }
+
+    /// Unwraps this `BufferedStream`, returning the underlying stream.
+    ///
+    /// The internal buffer is flushed before returning the stream. Any leftover
+    /// data in the read buffer is lost.
+    pub fn into_inner(self) -> S {
+        let InternalBufferedWriter(w) = self.inner.inner;
+        w.into_inner()
+    }
+}
+
+impl<S: Stream> Buffer for BufferedStream<S> {
+    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { self.inner.fill_buf() }
+    fn consume(&mut self, amt: uint) { self.inner.consume(amt) }
+}
+
+impl<S: Stream> Reader for BufferedStream<S> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.read(buf)
+    }
+}
+
+impl<S: Stream> Writer for BufferedStream<S> {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner.inner.get_mut().write_all(buf)
+    }
+    fn flush(&mut self) -> IoResult<()> {
+        self.inner.inner.get_mut().flush()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    extern crate test;
+    use old_io;
+    use prelude::v1::*;
+    use super::*;
+    use super::super::{IoResult, EndOfFile};
+    use super::super::mem::MemReader;
+    use self::test::Bencher;
+
+    /// A type, free to create, primarily intended for benchmarking creation of
+    /// wrappers that, just for construction, don't need a Reader/Writer that
+    /// does anything useful. Is equivalent to `/dev/null` in semantics.
+    #[derive(Clone,PartialEq,PartialOrd)]
+    pub struct NullStream;
+
+    impl Reader for NullStream {
+        fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        }
+    }
+
+    impl Writer for NullStream {
+        fn write_all(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) }
+    }
+
+    /// A dummy reader intended at testing short-reads propagation.
+    pub struct ShortReader {
+        lengths: Vec<uint>,
+    }
+
+    impl Reader for ShortReader {
+        fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> {
+            if self.lengths.is_empty() {
+                Err(old_io::standard_error(old_io::EndOfFile))
+            } else {
+                Ok(self.lengths.remove(0))
+            }
+        }
+    }
+
+    #[test]
+    fn test_buffered_reader() {
+        let inner = MemReader::new(vec!(5, 6, 7, 0, 1, 2, 3, 4));
+        let mut reader = BufferedReader::with_capacity(2, inner);
+
+        let mut buf = [0, 0, 0];
+        let nread = reader.read(&mut buf);
+        assert_eq!(Ok(3), nread);
+        let b: &[_] = &[5, 6, 7];
+        assert_eq!(buf, b);
+
+        let mut buf = [0, 0];
+        let nread = reader.read(&mut buf);
+        assert_eq!(Ok(2), nread);
+        let b: &[_] = &[0, 1];
+        assert_eq!(buf, b);
+
+        let mut buf = [0];
+        let nread = reader.read(&mut buf);
+        assert_eq!(Ok(1), nread);
+        let b: &[_] = &[2];
+        assert_eq!(buf, b);
+
+        let mut buf = [0, 0, 0];
+        let nread = reader.read(&mut buf);
+        assert_eq!(Ok(1), nread);
+        let b: &[_] = &[3, 0, 0];
+        assert_eq!(buf, b);
+
+        let nread = reader.read(&mut buf);
+        assert_eq!(Ok(1), nread);
+        let b: &[_] = &[4, 0, 0];
+        assert_eq!(buf, b);
+
+        assert!(reader.read(&mut buf).is_err());
+    }
+
+    #[test]
+    fn test_buffered_writer() {
+        let inner = Vec::new();
+        let mut writer = BufferedWriter::with_capacity(2, inner);
+
+        writer.write_all(&[0, 1]).unwrap();
+        let b: &[_] = &[];
+        assert_eq!(&writer.get_ref()[], b);
+
+        writer.write_all(&[2]).unwrap();
+        let b: &[_] = &[0, 1];
+        assert_eq!(&writer.get_ref()[], b);
+
+        writer.write_all(&[3]).unwrap();
+        assert_eq!(&writer.get_ref()[], b);
+
+        writer.flush().unwrap();
+        let a: &[_] = &[0, 1, 2, 3];
+        assert_eq!(a, &writer.get_ref()[]);
+
+        writer.write_all(&[4]).unwrap();
+        writer.write_all(&[5]).unwrap();
+        assert_eq!(a, &writer.get_ref()[]);
+
+        writer.write_all(&[6]).unwrap();
+        let a: &[_] = &[0, 1, 2, 3, 4, 5];
+        assert_eq!(a, &writer.get_ref()[]);
+
+        writer.write_all(&[7, 8]).unwrap();
+        let a: &[_] = &[0, 1, 2, 3, 4, 5, 6];
+        assert_eq!(a, &writer.get_ref()[]);
+
+        writer.write_all(&[9, 10, 11]).unwrap();
+        let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
+        assert_eq!(a, &writer.get_ref()[]);
+
+        writer.flush().unwrap();
+        assert_eq!(a, &writer.get_ref()[]);
+    }
+
+    #[test]
+    fn test_buffered_writer_inner_flushes() {
+        let mut w = BufferedWriter::with_capacity(3, Vec::new());
+        w.write_all(&[0, 1]).unwrap();
+        let a: &[_] = &[];
+        assert_eq!(a, &w.get_ref()[]);
+        let w = w.into_inner();
+        let a: &[_] = &[0, 1];
+        assert_eq!(a, &w[]);
+    }
+
+    // This is just here to make sure that we don't infinite loop in the
+    // newtype struct autoderef weirdness
+    #[test]
+    fn test_buffered_stream() {
+        struct S;
+
+        impl old_io::Writer for S {
+            fn write_all(&mut self, _: &[u8]) -> old_io::IoResult<()> { Ok(()) }
+        }
+
+        impl old_io::Reader for S {
+            fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> {
+                Err(old_io::standard_error(old_io::EndOfFile))
+            }
+        }
+
+        let mut stream = BufferedStream::new(S);
+        let mut buf = [];
+        assert!(stream.read(&mut buf).is_err());
+        stream.write_all(&buf).unwrap();
+        stream.flush().unwrap();
+    }
+
+    #[test]
+    fn test_read_until() {
+        let inner = MemReader::new(vec!(0, 1, 2, 1, 0));
+        let mut reader = BufferedReader::with_capacity(2, inner);
+        assert_eq!(reader.read_until(0), Ok(vec!(0)));
+        assert_eq!(reader.read_until(2), Ok(vec!(1, 2)));
+        assert_eq!(reader.read_until(1), Ok(vec!(1)));
+        assert_eq!(reader.read_until(8), Ok(vec!(0)));
+        assert!(reader.read_until(9).is_err());
+    }
+
+    #[test]
+    fn test_line_buffer() {
+        let mut writer = LineBufferedWriter::new(Vec::new());
+        writer.write_all(&[0]).unwrap();
+        let b: &[_] = &[];
+        assert_eq!(&writer.get_ref()[], b);
+        writer.write_all(&[1]).unwrap();
+        assert_eq!(&writer.get_ref()[], b);
+        writer.flush().unwrap();
+        let b: &[_] = &[0, 1];
+        assert_eq!(&writer.get_ref()[], b);
+        writer.write_all(&[0, b'\n', 1, b'\n', 2]).unwrap();
+        let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n'];
+        assert_eq!(&writer.get_ref()[], b);
+        writer.flush().unwrap();
+        let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2];
+        assert_eq!(&writer.get_ref()[], b);
+        writer.write_all(&[3, b'\n']).unwrap();
+        let b: &[_] = &[0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n'];
+        assert_eq!(&writer.get_ref()[], b);
+    }
+
+    #[test]
+    fn test_read_line() {
+        let in_buf = MemReader::new(b"a\nb\nc".to_vec());
+        let mut reader = BufferedReader::with_capacity(2, in_buf);
+        assert_eq!(reader.read_line(), Ok("a\n".to_string()));
+        assert_eq!(reader.read_line(), Ok("b\n".to_string()));
+        assert_eq!(reader.read_line(), Ok("c".to_string()));
+        assert!(reader.read_line().is_err());
+    }
+
+    #[test]
+    fn test_lines() {
+        let in_buf = MemReader::new(b"a\nb\nc".to_vec());
+        let mut reader = BufferedReader::with_capacity(2, in_buf);
+        let mut it = reader.lines();
+        assert_eq!(it.next(), Some(Ok("a\n".to_string())));
+        assert_eq!(it.next(), Some(Ok("b\n".to_string())));
+        assert_eq!(it.next(), Some(Ok("c".to_string())));
+        assert_eq!(it.next(), None);
+    }
+
+    #[test]
+    fn test_short_reads() {
+        let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]};
+        let mut reader = BufferedReader::new(inner);
+        let mut buf = [0, 0];
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        assert_eq!(reader.read(&mut buf), Ok(1));
+        assert_eq!(reader.read(&mut buf), Ok(2));
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        assert_eq!(reader.read(&mut buf), Ok(1));
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        assert!(reader.read(&mut buf).is_err());
+    }
+
+    #[test]
+    fn read_char_buffered() {
+        let buf = [195u8, 159u8];
+        let mut reader = BufferedReader::with_capacity(1, &buf[]);
+        assert_eq!(reader.read_char(), Ok('ß'));
+    }
+
+    #[test]
+    fn test_chars() {
+        let buf = [195u8, 159u8, b'a'];
+        let mut reader = BufferedReader::with_capacity(1, &buf[]);
+        let mut it = reader.chars();
+        assert_eq!(it.next(), Some(Ok('ß')));
+        assert_eq!(it.next(), Some(Ok('a')));
+        assert_eq!(it.next(), None);
+    }
+
+    #[test]
+    #[should_fail]
+    fn dont_panic_in_drop_on_panicked_flush() {
+        struct FailFlushWriter;
+
+        impl Writer for FailFlushWriter {
+            fn write_all(&mut self, _buf: &[u8]) -> IoResult<()> { Ok(()) }
+            fn flush(&mut self) -> IoResult<()> { Err(old_io::standard_error(EndOfFile)) }
+        }
+
+        let writer = FailFlushWriter;
+        let _writer = BufferedWriter::new(writer);
+
+        // If writer panics *again* due to the flush error then the process will abort.
+        panic!();
+    }
+
+    #[bench]
+    fn bench_buffered_reader(b: &mut Bencher) {
+        b.iter(|| {
+            BufferedReader::new(NullStream)
+        });
+    }
+
+    #[bench]
+    fn bench_buffered_writer(b: &mut Bencher) {
+        b.iter(|| {
+            BufferedWriter::new(NullStream)
+        });
+    }
+
+    #[bench]
+    fn bench_buffered_stream(b: &mut Bencher) {
+        b.iter(|| {
+            BufferedStream::new(NullStream);
+        });
+    }
+}
diff --git a/src/libstd/old_io/comm_adapters.rs b/src/libstd/old_io/comm_adapters.rs
new file mode 100644
index 00000000000..feb2ef6f4f3
--- /dev/null
+++ b/src/libstd/old_io/comm_adapters.rs
@@ -0,0 +1,248 @@
+// 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 clone::Clone;
+use cmp;
+use sync::mpsc::{Sender, Receiver};
+use old_io;
+use option::Option::{None, Some};
+use result::Result::{Ok, Err};
+use slice::{bytes, SliceExt};
+use super::{Buffer, Reader, Writer, IoResult};
+use vec::Vec;
+
+/// Allows reading from a rx.
+///
+/// # Example
+///
+/// ```
+/// use std::sync::mpsc::channel;
+/// use std::old_io::ChanReader;
+///
+/// let (tx, rx) = channel();
+/// # drop(tx);
+/// let mut reader = ChanReader::new(rx);
+///
+/// let mut buf = [0u8; 100];
+/// match reader.read(&mut buf) {
+///     Ok(nread) => println!("Read {} bytes", nread),
+///     Err(e) => println!("read error: {}", e),
+/// }
+/// ```
+pub struct ChanReader {
+    buf: Vec<u8>,          // A buffer of bytes received but not consumed.
+    pos: uint,             // How many of the buffered bytes have already be consumed.
+    rx: Receiver<Vec<u8>>, // The Receiver to pull data from.
+    closed: bool,          // Whether the channel this Receiver connects to has been closed.
+}
+
+impl ChanReader {
+    /// Wraps a `Port` in a `ChanReader` structure
+    pub fn new(rx: Receiver<Vec<u8>>) -> ChanReader {
+        ChanReader {
+            buf: Vec::new(),
+            pos: 0,
+            rx: rx,
+            closed: false,
+        }
+    }
+}
+
+impl Buffer for ChanReader {
+    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
+        if self.pos >= self.buf.len() {
+            self.pos = 0;
+            match self.rx.recv() {
+                Ok(bytes) => {
+                    self.buf = bytes;
+                },
+                Err(..) => {
+                    self.closed = true;
+                    self.buf = Vec::new();
+                }
+            }
+        }
+        if self.closed {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        } else {
+            Ok(&self.buf[self.pos..])
+        }
+    }
+
+    fn consume(&mut self, amt: uint) {
+        self.pos += amt;
+        assert!(self.pos <= self.buf.len());
+    }
+}
+
+impl Reader for ChanReader {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        let mut num_read = 0;
+        loop {
+            let count = match self.fill_buf().ok() {
+                Some(src) => {
+                    let dst = &mut buf[num_read..];
+                    let count = cmp::min(src.len(), dst.len());
+                    bytes::copy_memory(dst, &src[..count]);
+                    count
+                },
+                None => 0,
+            };
+            self.consume(count);
+            num_read += count;
+            if num_read == buf.len() || self.closed {
+                break;
+            }
+        }
+        if self.closed && num_read == 0 {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        } else {
+            Ok(num_read)
+        }
+    }
+}
+
+/// Allows writing to a tx.
+///
+/// # Example
+///
+/// ```
+/// # #![allow(unused_must_use)]
+/// use std::sync::mpsc::channel;
+/// use std::old_io::ChanWriter;
+///
+/// let (tx, rx) = channel();
+/// # drop(rx);
+/// let mut writer = ChanWriter::new(tx);
+/// writer.write("hello, world".as_bytes());
+/// ```
+pub struct ChanWriter {
+    tx: Sender<Vec<u8>>,
+}
+
+impl ChanWriter {
+    /// Wraps a channel in a `ChanWriter` structure
+    pub fn new(tx: Sender<Vec<u8>>) -> ChanWriter {
+        ChanWriter { tx: tx }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Clone for ChanWriter {
+    fn clone(&self) -> ChanWriter {
+        ChanWriter { tx: self.tx.clone() }
+    }
+}
+
+impl Writer for ChanWriter {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.tx.send(buf.to_vec()).map_err(|_| {
+            old_io::IoError {
+                kind: old_io::BrokenPipe,
+                desc: "Pipe closed",
+                detail: None
+            }
+        })
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+
+    use sync::mpsc::channel;
+    use super::*;
+    use old_io;
+    use thread::Thread;
+
+    #[test]
+    fn test_rx_reader() {
+        let (tx, rx) = channel();
+        Thread::spawn(move|| {
+          tx.send(vec![1u8, 2u8]).unwrap();
+          tx.send(vec![]).unwrap();
+          tx.send(vec![3u8, 4u8]).unwrap();
+          tx.send(vec![5u8, 6u8]).unwrap();
+          tx.send(vec![7u8, 8u8]).unwrap();
+        });
+
+        let mut reader = ChanReader::new(rx);
+        let mut buf = [0u8; 3];
+
+        assert_eq!(Ok(0), reader.read(&mut []));
+
+        assert_eq!(Ok(3), reader.read(&mut buf));
+        let a: &[u8] = &[1,2,3];
+        assert_eq!(a, buf);
+
+        assert_eq!(Ok(3), reader.read(&mut buf));
+        let a: &[u8] = &[4,5,6];
+        assert_eq!(a, buf);
+
+        assert_eq!(Ok(2), reader.read(&mut buf));
+        let a: &[u8] = &[7,8,6];
+        assert_eq!(a, buf);
+
+        match reader.read(buf.as_mut_slice()) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+        assert_eq!(a, buf);
+
+        // Ensure it continues to panic in the same way.
+        match reader.read(buf.as_mut_slice()) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+        assert_eq!(a, buf);
+    }
+
+    #[test]
+    fn test_rx_buffer() {
+        let (tx, rx) = channel();
+        Thread::spawn(move|| {
+          tx.send(b"he".to_vec()).unwrap();
+          tx.send(b"llo wo".to_vec()).unwrap();
+          tx.send(b"".to_vec()).unwrap();
+          tx.send(b"rld\nhow ".to_vec()).unwrap();
+          tx.send(b"are you?".to_vec()).unwrap();
+          tx.send(b"".to_vec()).unwrap();
+        });
+
+        let mut reader = ChanReader::new(rx);
+
+        assert_eq!(Ok("hello world\n".to_string()), reader.read_line());
+        assert_eq!(Ok("how are you?".to_string()), reader.read_line());
+        match reader.read_line() {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+    }
+
+    #[test]
+    fn test_chan_writer() {
+        let (tx, rx) = channel();
+        let mut writer = ChanWriter::new(tx);
+        writer.write_be_u32(42).unwrap();
+
+        let wanted = vec![0u8, 0u8, 0u8, 42u8];
+        let got = match Thread::scoped(move|| { rx.recv().unwrap() }).join() {
+            Ok(got) => got,
+            Err(_) => panic!(),
+        };
+        assert_eq!(wanted, got);
+
+        match writer.write_u8(1) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::BrokenPipe),
+        }
+    }
+}
diff --git a/src/libstd/old_io/extensions.rs b/src/libstd/old_io/extensions.rs
new file mode 100644
index 00000000000..826271a9f83
--- /dev/null
+++ b/src/libstd/old_io/extensions.rs
@@ -0,0 +1,562 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utility mixins that apply to all Readers and Writers
+
+#![allow(missing_docs)]
+
+// FIXME: Not sure how this should be structured
+// FIXME: Iteration should probably be considered separately
+
+use old_io::{IoError, IoResult, Reader};
+use old_io;
+use iter::Iterator;
+use num::Int;
+use ops::FnOnce;
+use option::Option;
+use option::Option::{Some, None};
+use ptr::PtrExt;
+use result::Result::{Ok, Err};
+use slice::{SliceExt, AsSlice};
+
+/// An iterator that reads a single byte on each iteration,
+/// until `.read_byte()` returns `EndOfFile`.
+///
+/// # Notes about the Iteration Protocol
+///
+/// The `Bytes` may yield `None` and thus terminate
+/// an iteration, but continue to yield elements if iteration
+/// is attempted again.
+///
+/// # Error
+///
+/// Any error other than `EndOfFile` that is produced by the underlying Reader
+/// is returned by the iterator and should be handled by the caller.
+pub struct Bytes<'r, T:'r> {
+    reader: &'r mut T,
+}
+
+impl<'r, R: Reader> Bytes<'r, R> {
+    /// Constructs a new byte iterator from the given Reader instance.
+    pub fn new(r: &'r mut R) -> Bytes<'r, R> {
+        Bytes {
+            reader: r,
+        }
+    }
+}
+
+impl<'r, R: Reader> Iterator for Bytes<'r, R> {
+    type Item = IoResult<u8>;
+
+    #[inline]
+    fn next(&mut self) -> Option<IoResult<u8>> {
+        match self.reader.read_byte() {
+            Ok(x) => Some(Ok(x)),
+            Err(IoError { kind: old_io::EndOfFile, .. }) => None,
+            Err(e) => Some(Err(e))
+        }
+    }
+}
+
+/// Converts an 8-bit to 64-bit unsigned value to a little-endian byte
+/// representation of the given size. If the size is not big enough to
+/// represent the value, then the high-order bytes are truncated.
+///
+/// Arguments:
+///
+/// * `n`: The value to convert.
+/// * `size`: The size of the value, in bytes. This must be 8 or less, or task
+///           panic occurs. If this is less than 8, then a value of that
+///           many bytes is produced. For example, if `size` is 4, then a
+///           32-bit byte representation is produced.
+/// * `f`: A callback that receives the value.
+///
+/// This function returns the value returned by the callback, for convenience.
+pub fn u64_to_le_bytes<T, F>(n: u64, size: uint, f: F) -> T where
+    F: FnOnce(&[u8]) -> T,
+{
+    use mem::transmute;
+
+    // LLVM fails to properly optimize this when using shifts instead of the to_le* intrinsics
+    assert!(size <= 8u);
+    match size {
+      1u => f(&[n as u8]),
+      2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_le()) }),
+      4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_le()) }),
+      8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_le()) }),
+      _ => {
+
+        let mut bytes = vec!();
+        let mut i = size;
+        let mut n = n;
+        while i > 0u {
+            bytes.push((n & 255_u64) as u8);
+            n >>= 8;
+            i -= 1u;
+        }
+        f(bytes.as_slice())
+      }
+    }
+}
+
+/// Converts an 8-bit to 64-bit unsigned value to a big-endian byte
+/// representation of the given size. If the size is not big enough to
+/// represent the value, then the high-order bytes are truncated.
+///
+/// Arguments:
+///
+/// * `n`: The value to convert.
+/// * `size`: The size of the value, in bytes. This must be 8 or less, or task
+///           panic occurs. If this is less than 8, then a value of that
+///           many bytes is produced. For example, if `size` is 4, then a
+///           32-bit byte representation is produced.
+/// * `f`: A callback that receives the value.
+///
+/// This function returns the value returned by the callback, for convenience.
+pub fn u64_to_be_bytes<T, F>(n: u64, size: uint, f: F) -> T where
+    F: FnOnce(&[u8]) -> T,
+{
+    use mem::transmute;
+
+    // LLVM fails to properly optimize this when using shifts instead of the to_be* intrinsics
+    assert!(size <= 8u);
+    match size {
+      1u => f(&[n as u8]),
+      2u => f(unsafe { & transmute::<_, [u8; 2]>((n as u16).to_be()) }),
+      4u => f(unsafe { & transmute::<_, [u8; 4]>((n as u32).to_be()) }),
+      8u => f(unsafe { & transmute::<_, [u8; 8]>(n.to_be()) }),
+      _ => {
+        let mut bytes = vec!();
+        let mut i = size;
+        while i > 0u {
+            let shift = (i - 1u) * 8u;
+            bytes.push((n >> shift) as u8);
+            i -= 1u;
+        }
+        f(bytes.as_slice())
+      }
+    }
+}
+
+/// Extracts an 8-bit to 64-bit unsigned big-endian value from the given byte
+/// buffer and returns it as a 64-bit value.
+///
+/// Arguments:
+///
+/// * `data`: The buffer in which to extract the value.
+/// * `start`: The offset at which to extract the value.
+/// * `size`: The size of the value in bytes to extract. This must be 8 or
+///           less, or task panic occurs. If this is less than 8, then only
+///           that many bytes are parsed. For example, if `size` is 4, then a
+///           32-bit value is parsed.
+pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 {
+    use ptr::{copy_nonoverlapping_memory};
+    use slice::SliceExt;
+
+    assert!(size <= 8u);
+
+    if data.len() - start < size {
+        panic!("index out of bounds");
+    }
+
+    let mut buf = [0u8; 8];
+    unsafe {
+        let ptr = data.as_ptr().offset(start as int);
+        let out = buf.as_mut_ptr();
+        copy_nonoverlapping_memory(out.offset((8 - size) as int), ptr, size);
+        (*(out as *const u64)).to_be()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+    use old_io;
+    use old_io::{MemReader, BytesReader};
+
+    struct InitialZeroByteReader {
+        count: int,
+    }
+
+    impl Reader for InitialZeroByteReader {
+        fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                Ok(0)
+            } else {
+                buf[0] = 10;
+                Ok(1)
+            }
+        }
+    }
+
+    struct EofReader;
+
+    impl Reader for EofReader {
+        fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        }
+    }
+
+    struct ErroringReader;
+
+    impl Reader for ErroringReader {
+        fn read(&mut self, _: &mut [u8]) -> old_io::IoResult<uint> {
+            Err(old_io::standard_error(old_io::InvalidInput))
+        }
+    }
+
+    struct PartialReader {
+        count: int,
+    }
+
+    impl Reader for PartialReader {
+        fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                buf[1] = 11;
+                Ok(2)
+            } else {
+                buf[0] = 12;
+                buf[1] = 13;
+                Ok(2)
+            }
+        }
+    }
+
+    struct ErroringLaterReader {
+        count: int,
+    }
+
+    impl Reader for ErroringLaterReader {
+        fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                Ok(1)
+            } else {
+                Err(old_io::standard_error(old_io::InvalidInput))
+            }
+        }
+    }
+
+    struct ThreeChunkReader {
+        count: int,
+    }
+
+    impl Reader for ThreeChunkReader {
+        fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+            if self.count == 0 {
+                self.count = 1;
+                buf[0] = 10;
+                buf[1] = 11;
+                Ok(2)
+            } else if self.count == 1 {
+                self.count = 2;
+                buf[0] = 12;
+                buf[1] = 13;
+                Ok(2)
+            } else {
+                Err(old_io::standard_error(old_io::EndOfFile))
+            }
+        }
+    }
+
+    #[test]
+    fn read_byte() {
+        let mut reader = MemReader::new(vec!(10));
+        let byte = reader.read_byte();
+        assert!(byte == Ok(10));
+    }
+
+    #[test]
+    fn read_byte_0_bytes() {
+        let mut reader = InitialZeroByteReader {
+            count: 0,
+        };
+        let byte = reader.read_byte();
+        assert!(byte == Ok(10));
+    }
+
+    #[test]
+    fn read_byte_eof() {
+        let mut reader = EofReader;
+        let byte = reader.read_byte();
+        assert!(byte.is_err());
+    }
+
+    #[test]
+    fn read_byte_error() {
+        let mut reader = ErroringReader;
+        let byte = reader.read_byte();
+        assert!(byte.is_err());
+    }
+
+    #[test]
+    fn bytes_0_bytes() {
+        let mut reader = InitialZeroByteReader {
+            count: 0,
+        };
+        let byte = reader.bytes().next();
+        assert!(byte == Some(Ok(10)));
+    }
+
+    #[test]
+    fn bytes_eof() {
+        let mut reader = EofReader;
+        let byte = reader.bytes().next();
+        assert!(byte.is_none());
+    }
+
+    #[test]
+    fn bytes_error() {
+        let mut reader = ErroringReader;
+        let mut it = reader.bytes();
+        let byte = it.next();
+        assert!(byte.unwrap().is_err());
+    }
+
+    #[test]
+    fn read_bytes() {
+        let mut reader = MemReader::new(vec!(10, 11, 12, 13));
+        let bytes = reader.read_exact(4).unwrap();
+        assert!(bytes == vec!(10, 11, 12, 13));
+    }
+
+    #[test]
+    fn read_bytes_partial() {
+        let mut reader = PartialReader {
+            count: 0,
+        };
+        let bytes = reader.read_exact(4).unwrap();
+        assert!(bytes == vec!(10, 11, 12, 13));
+    }
+
+    #[test]
+    fn read_bytes_eof() {
+        let mut reader = MemReader::new(vec!(10, 11));
+        assert!(reader.read_exact(4).is_err());
+    }
+
+    #[test]
+    fn push_at_least() {
+        let mut reader = MemReader::new(vec![10, 11, 12, 13]);
+        let mut buf = vec![8, 9];
+        assert!(reader.push_at_least(4, 4, &mut buf).is_ok());
+        assert!(buf == vec![8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_at_least_partial() {
+        let mut reader = PartialReader {
+            count: 0,
+        };
+        let mut buf = vec![8, 9];
+        assert!(reader.push_at_least(4, 4, &mut buf).is_ok());
+        assert!(buf == vec![8, 9, 10, 11, 12, 13]);
+    }
+
+    #[test]
+    fn push_at_least_eof() {
+        let mut reader = MemReader::new(vec![10, 11]);
+        let mut buf = vec![8, 9];
+        assert!(reader.push_at_least(4, 4, &mut buf).is_err());
+        assert!(buf == vec![8, 9, 10, 11]);
+    }
+
+    #[test]
+    fn push_at_least_error() {
+        let mut reader = ErroringLaterReader {
+            count: 0,
+        };
+        let mut buf = vec![8, 9];
+        assert!(reader.push_at_least(4, 4, &mut buf).is_err());
+        assert!(buf == vec![8, 9, 10]);
+    }
+
+    #[test]
+    fn read_to_end() {
+        let mut reader = ThreeChunkReader {
+            count: 0,
+        };
+        let buf = reader.read_to_end().unwrap();
+        assert!(buf == vec!(10, 11, 12, 13));
+    }
+
+    #[test]
+    #[should_fail]
+    fn read_to_end_error() {
+        let mut reader = ThreeChunkReader {
+            count: 0,
+        };
+        let buf = reader.read_to_end().unwrap();
+        assert!(buf == vec!(10, 11));
+    }
+
+    #[test]
+    fn test_read_write_le_mem() {
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX];
+
+        let mut writer = Vec::new();
+        for i in uints.iter() {
+            writer.write_le_u64(*i).unwrap();
+        }
+
+        let mut reader = MemReader::new(writer);
+        for i in uints.iter() {
+            assert!(reader.read_le_u64().unwrap() == *i);
+        }
+    }
+
+
+    #[test]
+    fn test_read_write_be() {
+        let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::MAX];
+
+        let mut writer = Vec::new();
+        for i in uints.iter() {
+            writer.write_be_u64(*i).unwrap();
+        }
+
+        let mut reader = MemReader::new(writer);
+        for i in uints.iter() {
+            assert!(reader.read_be_u64().unwrap() == *i);
+        }
+    }
+
+    #[test]
+    fn test_read_be_int_n() {
+        let ints = [::i32::MIN, -123456, -42, -5, 0, 1, ::i32::MAX];
+
+        let mut writer = Vec::new();
+        for i in ints.iter() {
+            writer.write_be_i32(*i).unwrap();
+        }
+
+        let mut reader = MemReader::new(writer);
+        for i in ints.iter() {
+            // this tests that the sign extension is working
+            // (comparing the values as i32 would not test this)
+            assert!(reader.read_be_int_n(4).unwrap() == *i as i64);
+        }
+    }
+
+    #[test]
+    fn test_read_f32() {
+        //big-endian floating-point 8.1250
+        let buf = vec![0x41, 0x02, 0x00, 0x00];
+
+        let mut writer = Vec::new();
+        writer.write(buf.as_slice()).unwrap();
+
+        let mut reader = MemReader::new(writer);
+        let f = reader.read_be_f32().unwrap();
+        assert!(f == 8.1250);
+    }
+
+    #[test]
+    fn test_read_write_f32() {
+        let f:f32 = 8.1250;
+
+        let mut writer = Vec::new();
+        writer.write_be_f32(f).unwrap();
+        writer.write_le_f32(f).unwrap();
+
+        let mut reader = MemReader::new(writer);
+        assert!(reader.read_be_f32().unwrap() == 8.1250);
+        assert!(reader.read_le_f32().unwrap() == 8.1250);
+    }
+
+    #[test]
+    fn test_u64_from_be_bytes() {
+        use super::u64_from_be_bytes;
+
+        let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
+
+        // Aligned access
+        assert_eq!(u64_from_be_bytes(&buf, 0, 0), 0);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 1), 0x01);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 2), 0x0102);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 3), 0x010203);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 4), 0x01020304);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 5), 0x0102030405);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 6), 0x010203040506);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 7), 0x01020304050607);
+        assert_eq!(u64_from_be_bytes(&buf, 0, 8), 0x0102030405060708);
+
+        // Unaligned access
+        assert_eq!(u64_from_be_bytes(&buf, 1, 0), 0);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 1), 0x02);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 2), 0x0203);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 3), 0x020304);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 4), 0x02030405);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 5), 0x0203040506);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 6), 0x020304050607);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 7), 0x02030405060708);
+        assert_eq!(u64_from_be_bytes(&buf, 1, 8), 0x0203040506070809);
+    }
+}
+
+#[cfg(test)]
+mod bench {
+    extern crate test;
+
+    use prelude::v1::*;
+    use self::test::Bencher;
+
+    // why is this a macro? wouldn't an inlined function work just as well?
+    macro_rules! u64_from_be_bytes_bench_impl {
+        ($b:expr, $size:expr, $stride:expr, $start_index:expr) =>
+        ({
+            use super::u64_from_be_bytes;
+
+            let data = range(0u8, $stride*100+$start_index).collect::<Vec<_>>();
+            let mut sum = 0u64;
+            $b.iter(|| {
+                let mut i = $start_index;
+                while i < data.len() {
+                    sum += u64_from_be_bytes(data.as_slice(), i, $size);
+                    i += $stride;
+                }
+            });
+        })
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_4_aligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 4, 4, 0);
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 4, 4, 1);
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_7_aligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 7, 8, 0);
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 7, 8, 1);
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_8_aligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 8, 8, 0);
+    }
+
+    #[bench]
+    fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) {
+        u64_from_be_bytes_bench_impl!(b, 8, 8, 1);
+    }
+}
diff --git a/src/libstd/old_io/fs.rs b/src/libstd/old_io/fs.rs
new file mode 100644
index 00000000000..a1ac5d2eab0
--- /dev/null
+++ b/src/libstd/old_io/fs.rs
@@ -0,0 +1,1572 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// ignore-lexer-test FIXME #15679
+
+//! Synchronous File I/O
+//!
+//! This module provides a set of functions and traits for working
+//! with regular files & directories on a filesystem.
+//!
+//! At the top-level of the module are a set of freestanding functions, associated
+//! with various filesystem operations. They all operate on `Path` objects.
+//!
+//! All operations in this module, including those as part of `File` et al block
+//! the task during execution. In the event of failure, all functions/methods
+//! will return an `IoResult` type with an `Err` value.
+//!
+//! Also included in this module is an implementation block on the `Path` object
+//! defined in `std::path::Path`. The impl adds useful methods about inspecting
+//! the metadata of a file. This includes getting the `stat` information,
+//! reading off particular bits of it, etc.
+//!
+//! # Example
+//!
+//! ```rust
+//! # #![allow(unused_must_use)]
+//! use std::old_io::fs::PathExtensions;
+//! use std::old_io::{File, fs};
+//!
+//! let path = Path::new("foo.txt");
+//!
+//! // create the file, whether it exists or not
+//! let mut file = File::create(&path);
+//! file.write(b"foobar");
+//! # drop(file);
+//!
+//! // open the file in read-only mode
+//! let mut file = File::open(&path);
+//! file.read_to_end();
+//!
+//! println!("{}", path.stat().unwrap().size);
+//! # drop(file);
+//! fs::unlink(&path);
+//! ```
+
+use clone::Clone;
+use old_io::standard_error;
+use old_io::{FilePermission, Write, Open, FileAccess, FileMode, FileType};
+use old_io::{IoResult, IoError, InvalidInput};
+use old_io::{FileStat, SeekStyle, Seek, Writer, Reader};
+use old_io::{Read, Truncate, ReadWrite, Append};
+use old_io::UpdateIoError;
+use old_io;
+use iter::{Iterator, Extend};
+use option::Option;
+use option::Option::{Some, None};
+use path::{Path, GenericPath};
+use path;
+use result::Result::{Err, Ok};
+use slice::SliceExt;
+use string::String;
+use vec::Vec;
+
+use sys::fs as fs_imp;
+use sys_common;
+
+/// Unconstrained file access type that exposes read and write operations
+///
+/// Can be constructed via `File::open()`, `File::create()`, and
+/// `File::open_mode()`.
+///
+/// # Error
+///
+/// This type will return errors as an `IoResult<T>` if operations are
+/// attempted against it for which its underlying file descriptor was not
+/// configured at creation time, via the `FileAccess` parameter to
+/// `File::open_mode()`.
+pub struct File {
+    fd: fs_imp::FileDesc,
+    path: Path,
+    last_nread: int,
+}
+
+impl sys_common::AsInner<fs_imp::FileDesc> for File {
+    fn as_inner(&self) -> &fs_imp::FileDesc {
+        &self.fd
+    }
+}
+
+impl File {
+    /// Open a file at `path` in the mode specified by the `mode` and `access`
+    /// arguments
+    ///
+    /// # Example
+    ///
+    /// ```rust,should_fail
+    /// use std::old_io::{File, Open, ReadWrite};
+    ///
+    /// let p = Path::new("/some/file/path.txt");
+    ///
+    /// let file = match File::open_mode(&p, Open, ReadWrite) {
+    ///     Ok(f) => f,
+    ///     Err(e) => panic!("file error: {}", e),
+    /// };
+    /// // do some stuff with that file
+    ///
+    /// // the file will be closed at the end of this block
+    /// ```
+    ///
+    /// `FileMode` and `FileAccess` provide information about the permissions
+    /// context in which a given stream is created. More information about them
+    /// can be found in `std::io`'s docs. If a file is opened with `Write`
+    /// or `ReadWrite` access, then it will be created if it does not already
+    /// exist.
+    ///
+    /// Note that, with this function, a `File` is returned regardless of the
+    /// access-limitations indicated by `FileAccess` (e.g. calling `write` on a
+    /// `File` opened as `Read` will return an error at runtime).
+    ///
+    /// # Error
+    ///
+    /// This function will return an error under a number of different
+    /// circumstances, to include but not limited to:
+    ///
+    /// * Opening a file that does not exist with `Read` access.
+    /// * Attempting to open a file with a `FileAccess` that the user lacks
+    ///   permissions for
+    /// * Filesystem-level errors (full disk, etc)
+    pub fn open_mode(path: &Path,
+                     mode: FileMode,
+                     access: FileAccess) -> IoResult<File> {
+        fs_imp::open(path, mode, access).and_then(|fd| {
+            // On *BSD systems, we can open a directory as a file and read from it:
+            // fd=open("/tmp", O_RDONLY); read(fd, buf, N);
+            // due to an old tradition before the introduction of opendir(3).
+            // We explicitly reject it because there are few use cases.
+            if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) &&
+               try!(fd.fstat()).kind == FileType::Directory {
+                Err(IoError {
+                    kind: InvalidInput,
+                    desc: "is a directory",
+                    detail: None
+                })
+            } else {
+                Ok(File {
+                    path: path.clone(),
+                    fd: fd,
+                    last_nread: -1
+                })
+            }
+        }).update_err("couldn't open path as file", |e| {
+            format!("{}; path={}; mode={}; access={}", e, path.display(),
+                mode_string(mode), access_string(access))
+        })
+    }
+
+    /// Attempts to open a file in read-only mode. This function is equivalent to
+    /// `File::open_mode(path, Open, Read)`, and will raise all of the same
+    /// errors that `File::open_mode` does.
+    ///
+    /// For more information, see the `File::open_mode` function.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use std::old_io::File;
+    ///
+    /// let contents = File::open(&Path::new("foo.txt")).read_to_end();
+    /// ```
+    pub fn open(path: &Path) -> IoResult<File> {
+        File::open_mode(path, Open, Read)
+    }
+
+    /// Attempts to create a file in write-only mode. This function is
+    /// equivalent to `File::open_mode(path, Truncate, Write)`, and will
+    /// raise all of the same errors that `File::open_mode` does.
+    ///
+    /// For more information, see the `File::open_mode` function.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # #![allow(unused_must_use)]
+    /// use std::old_io::File;
+    ///
+    /// let mut f = File::create(&Path::new("foo.txt"));
+    /// f.write(b"This is a sample file");
+    /// # drop(f);
+    /// # ::std::old_io::fs::unlink(&Path::new("foo.txt"));
+    /// ```
+    pub fn create(path: &Path) -> IoResult<File> {
+        File::open_mode(path, Truncate, Write)
+             .update_desc("couldn't create file")
+    }
+
+    /// Returns the original path that was used to open this file.
+    pub fn path<'a>(&'a self) -> &'a Path {
+        &self.path
+    }
+
+    /// Synchronizes all modifications to this file to its permanent storage
+    /// device. This will flush any internal buffers necessary to perform this
+    /// operation.
+    pub fn fsync(&mut self) -> IoResult<()> {
+        self.fd.fsync()
+            .update_err("couldn't fsync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
+    }
+
+    /// This function is similar to `fsync`, except that it may not synchronize
+    /// file metadata to the filesystem. This is intended for use cases that
+    /// must synchronize content, but don't need the metadata on disk. The goal
+    /// of this method is to reduce disk operations.
+    pub fn datasync(&mut self) -> IoResult<()> {
+        self.fd.datasync()
+            .update_err("couldn't datasync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
+    }
+
+    /// Either truncates or extends the underlying file, updating the size of
+    /// this file to become `size`. This is equivalent to unix's `truncate`
+    /// function.
+    ///
+    /// If the `size` is less than the current file's size, then the file will
+    /// be shrunk. If it is greater than the current file's size, then the file
+    /// will be extended to `size` and have all of the intermediate data filled
+    /// in with 0s.
+    pub fn truncate(&mut self, size: i64) -> IoResult<()> {
+        self.fd.truncate(size)
+            .update_err("couldn't truncate file", |e|
+                format!("{}; path={}; size={}", e, self.path.display(), size))
+    }
+
+    /// Returns true if the stream has reached the end of the file.
+    ///
+    /// If true, then this file will no longer continue to return data via
+    /// `read`.
+    ///
+    /// Note that the operating system will not return an `EOF` indicator
+    /// until you have attempted to read past the end of the file, so if
+    /// you've read _exactly_ the number of bytes in the file, this will
+    /// return `false`, not `true`.
+    pub fn eof(&self) -> bool {
+        self.last_nread == 0
+    }
+
+    /// Queries information about the underlying file.
+    pub fn stat(&self) -> IoResult<FileStat> {
+        self.fd.fstat()
+            .update_err("couldn't fstat file", |e|
+                format!("{}; path={}", e, self.path.display()))
+    }
+}
+
+/// Unlink a file from the underlying filesystem.
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::fs;
+///
+/// let p = Path::new("/some/file/path.txt");
+/// fs::unlink(&p);
+/// ```
+///
+/// Note that, just because an unlink call was successful, it is not
+/// guaranteed that a file is immediately deleted (e.g. depending on
+/// platform, other open file descriptors may prevent immediate removal)
+///
+/// # Error
+///
+/// This function will return an error if `path` points to a directory, if the
+/// user lacks permissions to remove the file, or if some other filesystem-level
+/// error occurs.
+pub fn unlink(path: &Path) -> IoResult<()> {
+    fs_imp::unlink(path)
+           .update_err("couldn't unlink path", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+/// Given a path, query the file system to get information about a file,
+/// directory, etc. This function will traverse symlinks to query
+/// information about the destination file.
+///
+/// # Example
+///
+/// ```rust
+/// use std::old_io::fs;
+///
+/// let p = Path::new("/some/file/path.txt");
+/// match fs::stat(&p) {
+///     Ok(stat) => { /* ... */ }
+///     Err(e) => { /* handle error */ }
+/// }
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the user lacks the requisite permissions
+/// to perform a `stat` call on the given `path` or if there is no entry in the
+/// filesystem at the provided path.
+pub fn stat(path: &Path) -> IoResult<FileStat> {
+    fs_imp::stat(path)
+           .update_err("couldn't stat path", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+/// Perform the same operation as the `stat` function, except that this
+/// function does not traverse through symlinks. This will return
+/// information about the symlink file instead of the file that it points
+/// to.
+///
+/// # Error
+///
+/// See `stat`
+pub fn lstat(path: &Path) -> IoResult<FileStat> {
+    fs_imp::lstat(path)
+           .update_err("couldn't lstat path", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+/// Rename a file or directory to a new name.
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::fs;
+///
+/// fs::rename(&Path::new("foo"), &Path::new("bar"));
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the provided `from` doesn't exist, if
+/// the process lacks permissions to view the contents, or if some other
+/// intermittent I/O error occurs.
+pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
+    fs_imp::rename(from, to)
+           .update_err("couldn't rename path", |e|
+               format!("{}; from={:?}; to={:?}", e, from.display(), to.display()))
+}
+
+/// Copies the contents of one file to another. This function will also
+/// copy the permission bits of the original file to the destination file.
+///
+/// Note that if `from` and `to` both point to the same file, then the file
+/// will likely get truncated by this operation.
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::fs;
+///
+/// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt"));
+/// ```
+///
+/// # Error
+///
+/// This function will return an error in the following situations, but is not
+/// limited to just these cases:
+///
+/// * The `from` path is not a file
+/// * The `from` file does not exist
+/// * The current process does not have the permission rights to access
+///   `from` or write `to`
+///
+/// Note that this copy is not atomic in that once the destination is
+/// ensured to not exist, there is nothing preventing the destination from
+/// being created and then destroyed by this operation.
+pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
+    fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
+        result.update_err("couldn't copy path", |e| {
+            format!("{}; from={:?}; to={:?}", e, from.display(), to.display())
+        })
+    }
+
+    if !from.is_file() {
+        return update_err(Err(IoError {
+            kind: old_io::MismatchedFileTypeForOperation,
+            desc: "the source path is not an existing file",
+            detail: None
+        }), from, to)
+    }
+
+    let mut reader = try!(File::open(from));
+    let mut writer = try!(File::create(to));
+
+    try!(update_err(super::util::copy(&mut reader, &mut writer), from, to));
+
+    chmod(to, try!(update_err(from.stat(), from, to)).perm)
+}
+
+/// Changes the permission mode bits found on a file or a directory. This
+/// function takes a mask from the `io` module
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io;
+/// use std::old_io::fs;
+///
+/// fs::chmod(&Path::new("file.txt"), old_io::USER_FILE);
+/// fs::chmod(&Path::new("file.txt"), old_io::USER_READ | old_io::USER_WRITE);
+/// fs::chmod(&Path::new("dir"),      old_io::USER_DIR);
+/// fs::chmod(&Path::new("file.exe"), old_io::USER_EXEC);
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the provided `path` doesn't exist, if
+/// the process lacks permissions to change the attributes of the file, or if
+/// some other I/O error is encountered.
+pub fn chmod(path: &Path, mode: old_io::FilePermission) -> IoResult<()> {
+    fs_imp::chmod(path, mode.bits() as uint)
+           .update_err("couldn't chmod path", |e|
+               format!("{}; path={}; mode={:?}", e, path.display(), mode))
+}
+
+/// Change the user and group owners of a file at the specified path.
+pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
+    fs_imp::chown(path, uid, gid)
+           .update_err("couldn't chown path", |e|
+               format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid))
+}
+
+/// Creates a new hard link on the filesystem. The `dst` path will be a
+/// link pointing to the `src` path. Note that systems often require these
+/// two paths to both be located on the same filesystem.
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
+    fs_imp::link(src, dst)
+           .update_err("couldn't link path", |e|
+               format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display()))
+}
+
+/// Creates a new symbolic link on the filesystem. The `dst` path will be a
+/// symlink pointing to the `src` path.
+pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
+    fs_imp::symlink(src, dst)
+           .update_err("couldn't symlink path", |e|
+               format!("{}; src={:?}; dest={:?}", e, src.display(), dst.display()))
+}
+
+/// Reads a symlink, returning the file that the symlink points to.
+///
+/// # Error
+///
+/// This function will return an error on failure. Failure conditions include
+/// reading a file that does not exist or reading a file that is not a symlink.
+pub fn readlink(path: &Path) -> IoResult<Path> {
+    fs_imp::readlink(path)
+           .update_err("couldn't resolve symlink for path", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+/// Create a new, empty directory at the provided path
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io;
+/// use std::old_io::fs;
+///
+/// let p = Path::new("/some/dir");
+/// fs::mkdir(&p, old_io::USER_RWX);
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the user lacks permissions to make a
+/// new directory at the provided `path`, or if the directory already exists.
+pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
+    fs_imp::mkdir(path, mode.bits() as uint)
+           .update_err("couldn't create directory", |e|
+               format!("{}; path={}; mode={}", e, path.display(), mode))
+}
+
+/// Remove an existing, empty directory
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::fs;
+///
+/// let p = Path::new("/some/dir");
+/// fs::rmdir(&p);
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the user lacks permissions to remove
+/// the directory at the provided `path`, or if the directory isn't empty.
+pub fn rmdir(path: &Path) -> IoResult<()> {
+    fs_imp::rmdir(path)
+           .update_err("couldn't remove directory", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+/// Retrieve a vector containing all entries within a provided directory
+///
+/// # Example
+///
+/// ```rust
+/// use std::old_io::fs::PathExtensions;
+/// use std::old_io::fs;
+/// use std::old_io;
+///
+/// // one possible implementation of fs::walk_dir only visiting files
+/// fn visit_dirs<F>(dir: &Path, cb: &mut F) -> old_io::IoResult<()> where
+///     F: FnMut(&Path),
+/// {
+///     if dir.is_dir() {
+///         let contents = try!(fs::readdir(dir));
+///         for entry in contents.iter() {
+///             if entry.is_dir() {
+///                 try!(visit_dirs(entry, cb));
+///             } else {
+///                 (*cb)(entry);
+///             }
+///         }
+///         Ok(())
+///     } else {
+///         Err(old_io::standard_error(old_io::InvalidInput))
+///     }
+/// }
+/// ```
+///
+/// # Error
+///
+/// This function will return an error if the provided `path` doesn't exist, if
+/// the process lacks permissions to view the contents or if the `path` points
+/// at a non-directory file
+pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
+    fs_imp::readdir(path)
+           .update_err("couldn't read directory",
+                       |e| format!("{}; path={}", e, path.display()))
+}
+
+/// Returns an iterator that will recursively walk the directory structure
+/// rooted at `path`. The path given will not be iterated over, and this will
+/// perform iteration in some top-down order.  The contents of unreadable
+/// subdirectories are ignored.
+pub fn walk_dir(path: &Path) -> IoResult<Directories> {
+    Ok(Directories {
+        stack: try!(readdir(path).update_err("couldn't walk directory",
+                                             |e| format!("{}; path={}", e, path.display())))
+    })
+}
+
+/// An iterator that walks over a directory
+#[derive(Clone)]
+pub struct Directories {
+    stack: Vec<Path>,
+}
+
+impl Iterator for Directories {
+    type Item = Path;
+
+    fn next(&mut self) -> Option<Path> {
+        match self.stack.pop() {
+            Some(path) => {
+                if path.is_dir() {
+                    match readdir(&path) {
+                        Ok(dirs) => { self.stack.extend(dirs.into_iter()); }
+                        Err(..) => {}
+                    }
+                }
+                Some(path)
+            }
+            None => None
+        }
+    }
+}
+
+/// Recursively create a directory and all of its parent components if they
+/// are missing.
+///
+/// # Error
+///
+/// See `fs::mkdir`.
+pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> {
+    // tjc: if directory exists but with different permissions,
+    // should we return false?
+    if path.is_dir() {
+        return Ok(())
+    }
+
+    let mut comps = path.components();
+    let mut curpath = path.root_path().unwrap_or(Path::new("."));
+
+    for c in comps {
+        curpath.push(c);
+
+        let result = mkdir(&curpath, mode)
+            .update_err("couldn't recursively mkdir",
+                        |e| format!("{}; path={}", e, path.display()));
+
+        match result {
+            Err(mkdir_err) => {
+                // already exists ?
+                if try!(stat(&curpath)).kind != FileType::Directory {
+                    return Err(mkdir_err);
+                }
+            }
+            Ok(()) => ()
+        }
+    }
+
+    Ok(())
+}
+
+/// Removes a directory at this path, after removing all its contents. Use
+/// carefully!
+///
+/// # Error
+///
+/// See `file::unlink` and `fs::readdir`
+pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
+    let mut rm_stack = Vec::new();
+    rm_stack.push(path.clone());
+
+    fn rmdir_failed(err: &IoError, path: &Path) -> String {
+        format!("rmdir_recursive failed; path={}; cause={}",
+                path.display(), err)
+    }
+
+    fn update_err<T>(err: IoResult<T>, path: &Path) -> IoResult<T> {
+        err.update_err("couldn't recursively rmdir",
+                       |e| rmdir_failed(e, path))
+    }
+
+    while !rm_stack.is_empty() {
+        let children = try!(readdir(rm_stack.last().unwrap())
+            .update_detail(|e| rmdir_failed(e, path)));
+
+        let mut has_child_dir = false;
+
+        // delete all regular files in the way and push subdirs
+        // on the stack
+        for child in children.into_iter() {
+            // FIXME(#12795) we should use lstat in all cases
+            let child_type = match cfg!(windows) {
+                true => try!(update_err(stat(&child), path)),
+                false => try!(update_err(lstat(&child), path))
+            };
+
+            if child_type.kind == FileType::Directory {
+                rm_stack.push(child);
+                has_child_dir = true;
+            } else {
+                // we can carry on safely if the file is already gone
+                // (eg: deleted by someone else since readdir)
+                match update_err(unlink(&child), path) {
+                    Ok(()) => (),
+                    Err(ref e) if e.kind == old_io::FileNotFound => (),
+                    Err(e) => return Err(e)
+                }
+            }
+        }
+
+        // if no subdir was found, let's pop and delete
+        if !has_child_dir {
+            let result = update_err(rmdir(&rm_stack.pop().unwrap()), path);
+            match result {
+                Ok(()) => (),
+                Err(ref e) if e.kind == old_io::FileNotFound => (),
+                Err(e) => return Err(e)
+            }
+        }
+    }
+
+    Ok(())
+}
+
+/// Changes the timestamps for a file's last modification and access time.
+/// The file at the path specified will have its last access time set to
+/// `atime` and its modification time set to `mtime`. The times specified should
+/// be in milliseconds.
+// FIXME(#10301) these arguments should not be u64
+pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
+    fs_imp::utime(path, atime, mtime)
+           .update_err("couldn't change_file_times", |e|
+               format!("{}; path={}", e, path.display()))
+}
+
+impl Reader for File {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        fn update_err<T>(result: IoResult<T>, file: &File) -> IoResult<T> {
+            result.update_err("couldn't read file",
+                              |e| format!("{}; path={}",
+                                          e, file.path.display()))
+        }
+
+        let result = update_err(self.fd.read(buf), self);
+
+        match result {
+            Ok(read) => {
+                self.last_nread = read as int;
+                match read {
+                    0 => update_err(Err(standard_error(old_io::EndOfFile)), self),
+                    _ => Ok(read as uint)
+                }
+            },
+            Err(e) => Err(e)
+        }
+    }
+}
+
+impl Writer for File {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.fd.write(buf)
+            .update_err("couldn't write to file",
+                        |e| format!("{}; path={}", e, self.path.display()))
+    }
+}
+
+impl Seek for File {
+    fn tell(&self) -> IoResult<u64> {
+        self.fd.tell()
+            .update_err("couldn't retrieve file cursor (`tell`)",
+                        |e| format!("{}; path={}", e, self.path.display()))
+    }
+
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
+        let err = match self.fd.seek(pos, style) {
+            Ok(_) => {
+                // successful seek resets EOF indicator
+                self.last_nread = -1;
+                Ok(())
+            }
+            Err(e) => Err(e),
+        };
+        err.update_err("couldn't seek in file",
+                       |e| format!("{}; path={}", e, self.path.display()))
+    }
+}
+
+/// Utility methods for paths.
+pub trait PathExtensions {
+    /// Get information on the file, directory, etc at this path.
+    ///
+    /// Consult the `fs::stat` documentation for more info.
+    ///
+    /// This call preserves identical runtime/error semantics with `file::stat`.
+    fn stat(&self) -> IoResult<FileStat>;
+
+    /// Get information on the file, directory, etc at this path, not following
+    /// symlinks.
+    ///
+    /// Consult the `fs::lstat` documentation for more info.
+    ///
+    /// This call preserves identical runtime/error semantics with `file::lstat`.
+    fn lstat(&self) -> IoResult<FileStat>;
+
+    /// Boolean value indicator whether the underlying file exists on the local
+    /// filesystem. Returns false in exactly the cases where `fs::stat` fails.
+    fn exists(&self) -> bool;
+
+    /// Whether the underlying implementation (be it a file path, or something
+    /// else) points at a "regular file" on the FS. Will return false for paths
+    /// to non-existent locations or directories or other non-regular files
+    /// (named pipes, etc). Follows links when making this determination.
+    fn is_file(&self) -> bool;
+
+    /// Whether the underlying implementation (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 not a
+    /// directory (eg files, named pipes, etc). Follows links when making this
+    /// determination.
+    fn is_dir(&self) -> bool;
+}
+
+impl PathExtensions for path::Path {
+    fn stat(&self) -> IoResult<FileStat> { stat(self) }
+    fn lstat(&self) -> IoResult<FileStat> { lstat(self) }
+    fn exists(&self) -> bool {
+        self.stat().is_ok()
+    }
+    fn is_file(&self) -> bool {
+        match self.stat() {
+            Ok(s) => s.kind == FileType::RegularFile,
+            Err(..) => false
+        }
+    }
+    fn is_dir(&self) -> bool {
+        match self.stat() {
+            Ok(s) => s.kind == FileType::Directory,
+            Err(..) => false
+        }
+    }
+}
+
+fn mode_string(mode: FileMode) -> &'static str {
+    match mode {
+        super::Open => "open",
+        super::Append => "append",
+        super::Truncate => "truncate"
+    }
+}
+
+fn access_string(access: FileAccess) -> &'static str {
+    match access {
+        super::Read => "read",
+        super::Write => "write",
+        super::ReadWrite => "readwrite"
+    }
+}
+
+#[cfg(test)]
+#[allow(unused_imports)]
+#[allow(unused_variables)]
+#[allow(unused_mut)]
+mod test {
+    use prelude::v1::*;
+    use old_io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType};
+    use old_io;
+    use str;
+    use old_io::fs::*;
+
+    macro_rules! check { ($e:expr) => (
+        match $e {
+            Ok(t) => t,
+            Err(e) => panic!("{} failed with: {:?}", stringify!($e), e),
+        }
+    ) }
+
+    macro_rules! error { ($e:expr, $s:expr) => (
+        match $e {
+            Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
+            Err(ref err) => assert!(err.to_string().contains($s.as_slice()),
+                                    format!("`{}` did not contain `{}`", err, $s))
+        }
+    ) }
+
+    pub struct TempDir(Path);
+
+    impl TempDir {
+        fn join(&self, path: &str) -> Path {
+            let TempDir(ref p) = *self;
+            p.join(path)
+        }
+
+        fn path<'a>(&'a self) -> &'a Path {
+            let TempDir(ref p) = *self;
+            p
+        }
+    }
+
+    impl Drop for TempDir {
+        fn drop(&mut self) {
+            // Gee, seeing how we're testing the fs module I sure hope that we
+            // at least implement this correctly!
+            let TempDir(ref p) = *self;
+            check!(old_io::fs::rmdir_recursive(p));
+        }
+    }
+
+    pub fn tmpdir() -> TempDir {
+        use os;
+        use rand;
+        let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>()));
+        check!(old_io::fs::mkdir(&ret, old_io::USER_RWX));
+        TempDir(ret)
+    }
+
+    #[test]
+    fn file_test_io_smoke_test() {
+        let message = "it's alright. have a good time";
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_rt_io_file_test.txt");
+        {
+            let mut write_stream = File::open_mode(filename, Open, ReadWrite);
+            check!(write_stream.write(message.as_bytes()));
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            let mut read_buf = [0; 1028];
+            let read_str = match check!(read_stream.read(&mut read_buf)) {
+                -1|0 => panic!("shouldn't happen"),
+                n => str::from_utf8(&read_buf[..n]).unwrap().to_string()
+            };
+            assert_eq!(read_str.as_slice(), message);
+        }
+        check!(unlink(filename));
+    }
+
+    #[test]
+    fn invalid_path_raises() {
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_that_does_not_exist.txt");
+        let result = File::open_mode(filename, Open, Read);
+
+        error!(result, "couldn't open path as file");
+        if cfg!(unix) {
+            error!(result, "no such file or directory");
+        }
+        error!(result, format!("path={}; mode=open; access=read", filename.display()));
+    }
+
+    #[test]
+    fn file_test_iounlinking_invalid_path_should_raise_condition() {
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
+
+        let result = unlink(filename);
+
+        error!(result, "couldn't unlink path");
+        if cfg!(unix) {
+            error!(result, "no such file or directory");
+        }
+        error!(result, format!("path={}", filename.display()));
+    }
+
+    #[test]
+    fn file_test_io_non_positional_read() {
+        let message: &str = "ten-four";
+        let mut read_mem = [0; 8];
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            check!(rw_stream.write(message.as_bytes()));
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            {
+                let read_buf = read_mem.slice_mut(0, 4);
+                check!(read_stream.read(read_buf));
+            }
+            {
+                let read_buf = read_mem.slice_mut(4, 8);
+                check!(read_stream.read(read_buf));
+            }
+        }
+        check!(unlink(filename));
+        let read_str = str::from_utf8(&read_mem).unwrap();
+        assert_eq!(read_str, message);
+    }
+
+    #[test]
+    fn file_test_io_seek_and_tell_smoke_test() {
+        let message = "ten-four";
+        let mut read_mem = [0; 4];
+        let set_cursor = 4 as u64;
+        let mut tell_pos_pre_read;
+        let mut tell_pos_post_read;
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            check!(rw_stream.write(message.as_bytes()));
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            check!(read_stream.seek(set_cursor as i64, SeekSet));
+            tell_pos_pre_read = check!(read_stream.tell());
+            check!(read_stream.read(&mut read_mem));
+            tell_pos_post_read = check!(read_stream.tell());
+        }
+        check!(unlink(filename));
+        let read_str = str::from_utf8(&read_mem).unwrap();
+        assert_eq!(read_str, message.slice(4, 8));
+        assert_eq!(tell_pos_pre_read, set_cursor);
+        assert_eq!(tell_pos_post_read, message.len() as u64);
+    }
+
+    #[test]
+    fn file_test_io_seek_and_write() {
+        let initial_msg =   "food-is-yummy";
+        let overwrite_msg =    "-the-bar!!";
+        let final_msg =     "foo-the-bar!!";
+        let seek_idx = 3i;
+        let mut read_mem = [0; 13];
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            check!(rw_stream.write(initial_msg.as_bytes()));
+            check!(rw_stream.seek(seek_idx as i64, SeekSet));
+            check!(rw_stream.write(overwrite_msg.as_bytes()));
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+            check!(read_stream.read(&mut read_mem));
+        }
+        check!(unlink(filename));
+        let read_str = str::from_utf8(&read_mem).unwrap();
+        assert!(read_str == final_msg);
+    }
+
+    #[test]
+    fn file_test_io_seek_shakedown() {
+        use str;          // 01234567890123
+        let initial_msg =   "qwer-asdf-zxcv";
+        let chunk_one: &str = "qwer";
+        let chunk_two: &str = "asdf";
+        let chunk_three: &str = "zxcv";
+        let mut read_mem = [0; 4];
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
+        {
+            let mut rw_stream = File::open_mode(filename, Open, ReadWrite);
+            check!(rw_stream.write(initial_msg.as_bytes()));
+        }
+        {
+            let mut read_stream = File::open_mode(filename, Open, Read);
+
+            check!(read_stream.seek(-4, SeekEnd));
+            check!(read_stream.read(&mut read_mem));
+            assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
+
+            check!(read_stream.seek(-9, SeekCur));
+            check!(read_stream.read(&mut read_mem));
+            assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
+
+            check!(read_stream.seek(0, SeekSet));
+            check!(read_stream.read(&mut read_mem));
+            assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
+        }
+        check!(unlink(filename));
+    }
+
+    #[test]
+    fn file_test_stat_is_correct_on_is_file() {
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
+        {
+            let mut fs = check!(File::open_mode(filename, Open, ReadWrite));
+            let msg = "hw";
+            fs.write(msg.as_bytes()).unwrap();
+
+            let fstat_res = check!(fs.stat());
+            assert_eq!(fstat_res.kind, FileType::RegularFile);
+        }
+        let stat_res_fn = check!(stat(filename));
+        assert_eq!(stat_res_fn.kind, FileType::RegularFile);
+        let stat_res_meth = check!(filename.stat());
+        assert_eq!(stat_res_meth.kind, FileType::RegularFile);
+        check!(unlink(filename));
+    }
+
+    #[test]
+    fn file_test_stat_is_correct_on_is_dir() {
+        let tmpdir = tmpdir();
+        let filename = &tmpdir.join("file_stat_correct_on_is_dir");
+        check!(mkdir(filename, old_io::USER_RWX));
+        let stat_res_fn = check!(stat(filename));
+        assert!(stat_res_fn.kind == FileType::Directory);
+        let stat_res_meth = check!(filename.stat());
+        assert!(stat_res_meth.kind == FileType::Directory);
+        check!(rmdir(filename));
+    }
+
+    #[test]
+    fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
+        let tmpdir = tmpdir();
+        let dir = &tmpdir.join("fileinfo_false_on_dir");
+        check!(mkdir(dir, old_io::USER_RWX));
+        assert!(dir.is_file() == false);
+        check!(rmdir(dir));
+    }
+
+    #[test]
+    fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
+        let tmpdir = tmpdir();
+        let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
+        check!(File::create(file).write(b"foo"));
+        assert!(file.exists());
+        check!(unlink(file));
+        assert!(!file.exists());
+    }
+
+    #[test]
+    fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
+        let tmpdir = tmpdir();
+        let dir = &tmpdir.join("before_and_after_dir");
+        assert!(!dir.exists());
+        check!(mkdir(dir, old_io::USER_RWX));
+        assert!(dir.exists());
+        assert!(dir.is_dir());
+        check!(rmdir(dir));
+        assert!(!dir.exists());
+    }
+
+    #[test]
+    fn file_test_directoryinfo_readdir() {
+        use str;
+        let tmpdir = tmpdir();
+        let dir = &tmpdir.join("di_readdir");
+        check!(mkdir(dir, old_io::USER_RWX));
+        let prefix = "foo";
+        for n in range(0i,3) {
+            let f = dir.join(format!("{}.txt", n));
+            let mut w = check!(File::create(&f));
+            let msg_str = format!("{}{}", prefix, n.to_string());
+            let msg = msg_str.as_bytes();
+            check!(w.write(msg));
+        }
+        let files = check!(readdir(dir));
+        let mut mem = [0u8; 4];
+        for f in files.iter() {
+            {
+                let n = f.filestem_str();
+                check!(File::open(f).read(&mut mem));
+                let read_str = str::from_utf8(&mem).unwrap();
+                let expected = match n {
+                    None|Some("") => panic!("really shouldn't happen.."),
+                    Some(n) => format!("{}{}", prefix, n),
+                };
+                assert_eq!(expected.as_slice(), read_str);
+            }
+            check!(unlink(f));
+        }
+        check!(rmdir(dir));
+    }
+
+    #[test]
+    fn file_test_walk_dir() {
+        let tmpdir = tmpdir();
+        let dir = &tmpdir.join("walk_dir");
+        check!(mkdir(dir, old_io::USER_RWX));
+
+        let dir1 = &dir.join("01/02/03");
+        check!(mkdir_recursive(dir1, old_io::USER_RWX));
+        check!(File::create(&dir1.join("04")));
+
+        let dir2 = &dir.join("11/12/13");
+        check!(mkdir_recursive(dir2, old_io::USER_RWX));
+        check!(File::create(&dir2.join("14")));
+
+        let mut files = check!(walk_dir(dir));
+        let mut cur = [0u8; 2];
+        for f in files {
+            let stem = f.filestem_str().unwrap();
+            let root = stem.as_bytes()[0] - b'0';
+            let name = stem.as_bytes()[1] - b'0';
+            assert!(cur[root as uint] < name);
+            cur[root as uint] = name;
+        }
+
+        check!(rmdir_recursive(dir));
+    }
+
+    #[test]
+    fn mkdir_path_already_exists_error() {
+        use old_io::{IoError, PathAlreadyExists};
+
+        let tmpdir = tmpdir();
+        let dir = &tmpdir.join("mkdir_error_twice");
+        check!(mkdir(dir, old_io::USER_RWX));
+        match mkdir(dir, old_io::USER_RWX) {
+            Err(IoError{kind:PathAlreadyExists,..}) => (),
+            _ => assert!(false)
+        };
+    }
+
+    #[test]
+    fn recursive_mkdir() {
+        let tmpdir = tmpdir();
+        let dir = tmpdir.join("d1/d2");
+        check!(mkdir_recursive(&dir, old_io::USER_RWX));
+        assert!(dir.is_dir())
+    }
+
+    #[test]
+    fn recursive_mkdir_failure() {
+        let tmpdir = tmpdir();
+        let dir = tmpdir.join("d1");
+        let file = dir.join("f1");
+
+        check!(mkdir_recursive(&dir, old_io::USER_RWX));
+        check!(File::create(&file));
+
+        let result = mkdir_recursive(&file, old_io::USER_RWX);
+
+        error!(result, "couldn't recursively mkdir");
+        error!(result, "couldn't create directory");
+        error!(result, "mode=0700");
+        error!(result, format!("path={}", file.display()));
+    }
+
+    #[test]
+    fn recursive_mkdir_slash() {
+        check!(mkdir_recursive(&Path::new("/"), old_io::USER_RWX));
+    }
+
+    // FIXME(#12795) depends on lstat to work on windows
+    #[cfg(not(windows))]
+    #[test]
+    fn recursive_rmdir() {
+        let tmpdir = tmpdir();
+        let d1 = tmpdir.join("d1");
+        let dt = d1.join("t");
+        let dtt = dt.join("t");
+        let d2 = tmpdir.join("d2");
+        let canary = d2.join("do_not_delete");
+        check!(mkdir_recursive(&dtt, old_io::USER_RWX));
+        check!(mkdir_recursive(&d2, old_io::USER_RWX));
+        check!(File::create(&canary).write(b"foo"));
+        check!(symlink(&d2, &dt.join("d2")));
+        check!(rmdir_recursive(&d1));
+
+        assert!(!d1.is_dir());
+        assert!(canary.exists());
+    }
+
+    #[test]
+    fn unicode_path_is_dir() {
+        assert!(Path::new(".").is_dir());
+        assert!(!Path::new("test/stdtest/fs.rs").is_dir());
+
+        let tmpdir = tmpdir();
+
+        let mut dirpath = tmpdir.path().clone();
+        dirpath.push(format!("test-가一ー你好"));
+        check!(mkdir(&dirpath, old_io::USER_RWX));
+        assert!(dirpath.is_dir());
+
+        let mut filepath = dirpath;
+        filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
+        check!(File::create(&filepath)); // ignore return; touch only
+        assert!(!filepath.is_dir());
+        assert!(filepath.exists());
+    }
+
+    #[test]
+    fn unicode_path_exists() {
+        assert!(Path::new(".").exists());
+        assert!(!Path::new("test/nonexistent-bogus-path").exists());
+
+        let tmpdir = tmpdir();
+        let unicode = tmpdir.path();
+        let unicode = unicode.join(format!("test-각丁ー再见"));
+        check!(mkdir(&unicode, old_io::USER_RWX));
+        assert!(unicode.exists());
+        assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
+    }
+
+    #[test]
+    fn copy_file_does_not_exist() {
+        let from = Path::new("test/nonexistent-bogus-path");
+        let to = Path::new("test/other-bogus-path");
+
+        error!(copy(&from, &to),
+            format!("couldn't copy path (the source path is not an \
+                    existing file; from={:?}; to={:?})",
+                    from.display(), to.display()));
+
+        match copy(&from, &to) {
+            Ok(..) => panic!(),
+            Err(..) => {
+                assert!(!from.exists());
+                assert!(!to.exists());
+            }
+        }
+    }
+
+    #[test]
+    fn copy_file_ok() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        check!(File::create(&input).write(b"hello"));
+        check!(copy(&input, &out));
+        let contents = check!(File::open(&out).read_to_end());
+        assert_eq!(contents.as_slice(), b"hello");
+
+        assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm);
+    }
+
+    #[test]
+    fn copy_file_dst_dir() {
+        let tmpdir = tmpdir();
+        let out = tmpdir.join("out");
+
+        check!(File::create(&out));
+        match copy(&out, tmpdir.path()) {
+            Ok(..) => panic!(), Err(..) => {}
+        }
+    }
+
+    #[test]
+    fn copy_file_dst_exists() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in");
+        let output = tmpdir.join("out");
+
+        check!(File::create(&input).write("foo".as_bytes()));
+        check!(File::create(&output).write("bar".as_bytes()));
+        check!(copy(&input, &output));
+
+        assert_eq!(check!(File::open(&output).read_to_end()),
+                   b"foo".to_vec());
+    }
+
+    #[test]
+    fn copy_file_src_dir() {
+        let tmpdir = tmpdir();
+        let out = tmpdir.join("out");
+
+        match copy(tmpdir.path(), &out) {
+            Ok(..) => panic!(), Err(..) => {}
+        }
+        assert!(!out.exists());
+    }
+
+    #[test]
+    fn copy_file_preserves_perm_bits() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        check!(File::create(&input));
+        check!(chmod(&input, old_io::USER_READ));
+        check!(copy(&input, &out));
+        assert!(!check!(out.stat()).perm.intersects(old_io::USER_WRITE));
+
+        check!(chmod(&input, old_io::USER_FILE));
+        check!(chmod(&out, old_io::USER_FILE));
+    }
+
+    #[cfg(not(windows))] // FIXME(#10264) operation not permitted?
+    #[test]
+    fn symlinks_work() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        check!(File::create(&input).write("foobar".as_bytes()));
+        check!(symlink(&input, &out));
+        if cfg!(not(windows)) {
+            assert_eq!(check!(lstat(&out)).kind, FileType::Symlink);
+            assert_eq!(check!(out.lstat()).kind, FileType::Symlink);
+        }
+        assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
+        assert_eq!(check!(File::open(&out).read_to_end()),
+                   b"foobar".to_vec());
+    }
+
+    #[cfg(not(windows))] // apparently windows doesn't like symlinks
+    #[test]
+    fn symlink_noexist() {
+        let tmpdir = tmpdir();
+        // symlinks can point to things that don't exist
+        check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar")));
+        assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo"));
+    }
+
+    #[test]
+    fn readlink_not_symlink() {
+        let tmpdir = tmpdir();
+        match readlink(tmpdir.path()) {
+            Ok(..) => panic!("wanted a failure"),
+            Err(..) => {}
+        }
+    }
+
+    #[test]
+    fn links_work() {
+        let tmpdir = tmpdir();
+        let input = tmpdir.join("in.txt");
+        let out = tmpdir.join("out.txt");
+
+        check!(File::create(&input).write("foobar".as_bytes()));
+        check!(link(&input, &out));
+        if cfg!(not(windows)) {
+            assert_eq!(check!(lstat(&out)).kind, FileType::RegularFile);
+            assert_eq!(check!(out.lstat()).kind, FileType::RegularFile);
+            assert_eq!(check!(stat(&out)).unstable.nlink, 2);
+            assert_eq!(check!(out.stat()).unstable.nlink, 2);
+        }
+        assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size);
+        assert_eq!(check!(stat(&out)).size, check!(input.stat()).size);
+        assert_eq!(check!(File::open(&out).read_to_end()),
+                   b"foobar".to_vec());
+
+        // can't link to yourself
+        match link(&input, &input) {
+            Ok(..) => panic!("wanted a failure"),
+            Err(..) => {}
+        }
+        // can't link to something that doesn't exist
+        match link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
+            Ok(..) => panic!("wanted a failure"),
+            Err(..) => {}
+        }
+    }
+
+    #[test]
+    fn chmod_works() {
+        let tmpdir = tmpdir();
+        let file = tmpdir.join("in.txt");
+
+        check!(File::create(&file));
+        assert!(check!(stat(&file)).perm.contains(old_io::USER_WRITE));
+        check!(chmod(&file, old_io::USER_READ));
+        assert!(!check!(stat(&file)).perm.contains(old_io::USER_WRITE));
+
+        match chmod(&tmpdir.join("foo"), old_io::USER_RWX) {
+            Ok(..) => panic!("wanted a panic"),
+            Err(..) => {}
+        }
+
+        check!(chmod(&file, old_io::USER_FILE));
+    }
+
+    #[test]
+    fn sync_doesnt_kill_anything() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("in.txt");
+
+        let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite));
+        check!(file.fsync());
+        check!(file.datasync());
+        check!(file.write(b"foo"));
+        check!(file.fsync());
+        check!(file.datasync());
+        drop(file);
+    }
+
+    #[test]
+    fn truncate_works() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("in.txt");
+
+        let mut file = check!(File::open_mode(&path, old_io::Open, old_io::ReadWrite));
+        check!(file.write(b"foo"));
+        check!(file.fsync());
+
+        // Do some simple things with truncation
+        assert_eq!(check!(file.stat()).size, 3);
+        check!(file.truncate(10));
+        assert_eq!(check!(file.stat()).size, 10);
+        check!(file.write(b"bar"));
+        check!(file.fsync());
+        assert_eq!(check!(file.stat()).size, 10);
+        assert_eq!(check!(File::open(&path).read_to_end()),
+                   b"foobar\0\0\0\0".to_vec());
+
+        // Truncate to a smaller length, don't seek, and then write something.
+        // Ensure that the intermediate zeroes are all filled in (we're seeked
+        // past the end of the file).
+        check!(file.truncate(2));
+        assert_eq!(check!(file.stat()).size, 2);
+        check!(file.write(b"wut"));
+        check!(file.fsync());
+        assert_eq!(check!(file.stat()).size, 9);
+        assert_eq!(check!(File::open(&path).read_to_end()),
+                   b"fo\0\0\0\0wut".to_vec());
+        drop(file);
+    }
+
+    #[test]
+    fn open_flavors() {
+        let tmpdir = tmpdir();
+
+        match File::open_mode(&tmpdir.join("a"), old_io::Open, old_io::Read) {
+            Ok(..) => panic!(), Err(..) => {}
+        }
+
+        // Perform each one twice to make sure that it succeeds the second time
+        // (where the file exists)
+        check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write));
+        assert!(tmpdir.join("b").exists());
+        check!(File::open_mode(&tmpdir.join("b"), old_io::Open, old_io::Write));
+
+        check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite));
+        assert!(tmpdir.join("c").exists());
+        check!(File::open_mode(&tmpdir.join("c"), old_io::Open, old_io::ReadWrite));
+
+        check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write));
+        assert!(tmpdir.join("d").exists());
+        check!(File::open_mode(&tmpdir.join("d"), old_io::Append, old_io::Write));
+
+        check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite));
+        assert!(tmpdir.join("e").exists());
+        check!(File::open_mode(&tmpdir.join("e"), old_io::Append, old_io::ReadWrite));
+
+        check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write));
+        assert!(tmpdir.join("f").exists());
+        check!(File::open_mode(&tmpdir.join("f"), old_io::Truncate, old_io::Write));
+
+        check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite));
+        assert!(tmpdir.join("g").exists());
+        check!(File::open_mode(&tmpdir.join("g"), old_io::Truncate, old_io::ReadWrite));
+
+        check!(File::create(&tmpdir.join("h")).write("foo".as_bytes()));
+        check!(File::open_mode(&tmpdir.join("h"), old_io::Open, old_io::Read));
+        {
+            let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Open,
+                                               old_io::Read));
+            match f.write("wut".as_bytes()) {
+                Ok(..) => panic!(), Err(..) => {}
+            }
+        }
+        assert!(check!(stat(&tmpdir.join("h"))).size == 3,
+                "write/stat failed");
+        {
+            let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Append,
+                                               old_io::Write));
+            check!(f.write("bar".as_bytes()));
+        }
+        assert!(check!(stat(&tmpdir.join("h"))).size == 6,
+                "append didn't append");
+        {
+            let mut f = check!(File::open_mode(&tmpdir.join("h"), old_io::Truncate,
+                                               old_io::Write));
+            check!(f.write("bar".as_bytes()));
+        }
+        assert!(check!(stat(&tmpdir.join("h"))).size == 3,
+                "truncate didn't truncate");
+    }
+
+    #[test]
+    fn utime() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("a");
+        check!(File::create(&path));
+        // These numbers have to be bigger than the time in the day to account
+        // for timezones Windows in particular will fail in certain timezones
+        // with small enough values
+        check!(change_file_times(&path, 100000, 200000));
+        assert_eq!(check!(path.stat()).accessed, 100000);
+        assert_eq!(check!(path.stat()).modified, 200000);
+    }
+
+    #[test]
+    fn utime_noexist() {
+        let tmpdir = tmpdir();
+
+        match change_file_times(&tmpdir.join("a"), 100, 200) {
+            Ok(..) => panic!(),
+            Err(..) => {}
+        }
+    }
+
+    #[test]
+    fn binary_file() {
+        use rand::{StdRng, Rng};
+
+        let mut bytes = [0; 1024];
+        StdRng::new().ok().unwrap().fill_bytes(&mut bytes);
+
+        let tmpdir = tmpdir();
+
+        check!(File::create(&tmpdir.join("test")).write(&bytes));
+        let actual = check!(File::open(&tmpdir.join("test")).read_to_end());
+        assert!(actual == bytes.as_slice());
+    }
+
+    #[test]
+    fn unlink_readonly() {
+        let tmpdir = tmpdir();
+        let path = tmpdir.join("file");
+        check!(File::create(&path));
+        check!(chmod(&path, old_io::USER_READ));
+        check!(unlink(&path));
+    }
+}
diff --git a/src/libstd/old_io/mem.rs b/src/libstd/old_io/mem.rs
new file mode 100644
index 00000000000..254daf3202a
--- /dev/null
+++ b/src/libstd/old_io/mem.rs
@@ -0,0 +1,759 @@
+// 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.
+//
+// ignore-lexer-test FIXME #15679
+
+//! Readers and Writers for in-memory buffers
+
+use cmp::min;
+use option::Option::None;
+use result::Result::{Err, Ok};
+use old_io;
+use old_io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult};
+use slice::{self, AsSlice, SliceExt};
+use vec::Vec;
+
+const BUF_CAPACITY: uint = 128;
+
+fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult<u64> {
+    // compute offset as signed and clamp to prevent overflow
+    let pos = match seek {
+        old_io::SeekSet => 0,
+        old_io::SeekEnd => end,
+        old_io::SeekCur => cur,
+    } as i64;
+
+    if offset + pos < 0 {
+        Err(IoError {
+            kind: old_io::InvalidInput,
+            desc: "invalid seek to a negative offset",
+            detail: None
+        })
+    } else {
+        Ok((offset + pos) as u64)
+    }
+}
+
+impl Writer for Vec<u8> {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.push_all(buf);
+        Ok(())
+    }
+}
+
+/// Writes to an owned, growable byte vector
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::MemWriter;
+///
+/// let mut w = MemWriter::new();
+/// w.write(&[0, 1, 2]);
+///
+/// assert_eq!(w.into_inner(), vec!(0, 1, 2));
+/// ```
+#[unstable(feature = "io")]
+#[deprecated(since = "1.0.0",
+             reason = "use the Vec<u8> Writer implementation directly")]
+#[derive(Clone)]
+pub struct MemWriter {
+    buf: Vec<u8>,
+}
+
+#[allow(deprecated)]
+impl MemWriter {
+    /// Create a new `MemWriter`.
+    #[inline]
+    pub fn new() -> MemWriter {
+        MemWriter::with_capacity(BUF_CAPACITY)
+    }
+    /// Create a new `MemWriter`, allocating at least `n` bytes for
+    /// the internal buffer.
+    #[inline]
+    pub fn with_capacity(n: uint) -> MemWriter {
+        MemWriter::from_vec(Vec::with_capacity(n))
+    }
+    /// Create a new `MemWriter` that will append to an existing `Vec`.
+    #[inline]
+    pub fn from_vec(buf: Vec<u8>) -> MemWriter {
+        MemWriter { buf: buf }
+    }
+
+    /// Acquires an immutable reference to the underlying buffer of this
+    /// `MemWriter`.
+    #[inline]
+    pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() }
+
+    /// Unwraps this `MemWriter`, returning the underlying buffer
+    #[inline]
+    pub fn into_inner(self) -> Vec<u8> { self.buf }
+}
+
+impl Writer for MemWriter {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.buf.push_all(buf);
+        Ok(())
+    }
+}
+
+/// Reads from an owned byte vector
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::MemReader;
+///
+/// let mut r = MemReader::new(vec!(0, 1, 2));
+///
+/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2));
+/// ```
+pub struct MemReader {
+    buf: Vec<u8>,
+    pos: uint
+}
+
+impl MemReader {
+    /// Creates a new `MemReader` which will read the buffer given. The buffer
+    /// can be re-acquired through `unwrap`
+    #[inline]
+    pub fn new(buf: Vec<u8>) -> MemReader {
+        MemReader {
+            buf: buf,
+            pos: 0
+        }
+    }
+
+    /// Tests whether this reader has read all bytes in its buffer.
+    ///
+    /// If `true`, then this will no longer return bytes from `read`.
+    #[inline]
+    pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
+
+    /// Acquires an immutable reference to the underlying buffer of this
+    /// `MemReader`.
+    ///
+    /// No method is exposed for acquiring a mutable reference to the buffer
+    /// because it could corrupt the state of this `MemReader`.
+    #[inline]
+    pub fn get_ref<'a>(&'a self) -> &'a [u8] { self.buf.as_slice() }
+
+    /// Unwraps this `MemReader`, returning the underlying buffer
+    #[inline]
+    pub fn into_inner(self) -> Vec<u8> { self.buf }
+}
+
+impl Reader for MemReader {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) }
+
+        let write_len = min(buf.len(), self.buf.len() - self.pos);
+        {
+            let input = &self.buf[self.pos.. self.pos + write_len];
+            let output = &mut buf[..write_len];
+            assert_eq!(input.len(), output.len());
+            slice::bytes::copy_memory(output, input);
+        }
+        self.pos += write_len;
+        assert!(self.pos <= self.buf.len());
+
+        return Ok(write_len);
+    }
+}
+
+impl Seek for MemReader {
+    #[inline]
+    fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
+
+    #[inline]
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
+        let new = try!(combine(style, self.pos, self.buf.len(), pos));
+        self.pos = new as uint;
+        Ok(())
+    }
+}
+
+impl Buffer for MemReader {
+    #[inline]
+    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
+        if self.pos < self.buf.len() {
+            Ok(&self.buf[self.pos..])
+        } else {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        }
+    }
+
+    #[inline]
+    fn consume(&mut self, amt: uint) { self.pos += amt; }
+}
+
+impl<'a> Reader for &'a [u8] {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        if self.is_empty() { return Err(old_io::standard_error(old_io::EndOfFile)); }
+
+        let write_len = min(buf.len(), self.len());
+        {
+            let input = &self[..write_len];
+            let output = &mut buf[.. write_len];
+            slice::bytes::copy_memory(output, input);
+        }
+
+        *self = &self[write_len..];
+
+        Ok(write_len)
+    }
+}
+
+impl<'a> Buffer for &'a [u8] {
+    #[inline]
+    fn fill_buf(&mut self) -> IoResult<&[u8]> {
+        if self.is_empty() {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        } else {
+            Ok(*self)
+        }
+    }
+
+    #[inline]
+    fn consume(&mut self, amt: uint) {
+        *self = &self[amt..];
+    }
+}
+
+
+/// Writes to a fixed-size byte slice
+///
+/// If a write will not fit in the buffer, it returns an error and does not
+/// write any data.
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::BufWriter;
+///
+/// let mut buf = [0; 4];
+/// {
+///     let mut w = BufWriter::new(&mut buf);
+///     w.write(&[0, 1, 2]);
+/// }
+/// assert!(buf == [0, 1, 2, 0]);
+/// ```
+pub struct BufWriter<'a> {
+    buf: &'a mut [u8],
+    pos: uint
+}
+
+impl<'a> BufWriter<'a> {
+    /// Creates a new `BufWriter` which will wrap the specified buffer. The
+    /// writer initially starts at position 0.
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> BufWriter<'a> {
+        BufWriter {
+            buf: buf,
+            pos: 0
+        }
+    }
+}
+
+impl<'a> Writer for BufWriter<'a> {
+    #[inline]
+    fn write_all(&mut self, src: &[u8]) -> IoResult<()> {
+        let dst = &mut self.buf[self.pos..];
+        let dst_len = dst.len();
+
+        if dst_len == 0 {
+            return Err(old_io::standard_error(old_io::EndOfFile));
+        }
+
+        let src_len = src.len();
+
+        if dst_len >= src_len {
+            slice::bytes::copy_memory(dst, src);
+
+            self.pos += src_len;
+
+            Ok(())
+        } else {
+            slice::bytes::copy_memory(dst, &src[..dst_len]);
+
+            self.pos += dst_len;
+
+            Err(old_io::standard_error(old_io::ShortWrite(dst_len)))
+        }
+    }
+}
+
+impl<'a> Seek for BufWriter<'a> {
+    #[inline]
+    fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
+
+    #[inline]
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
+        let new = try!(combine(style, self.pos, self.buf.len(), pos));
+        self.pos = min(new as uint, self.buf.len());
+        Ok(())
+    }
+}
+
+/// Reads from a fixed-size byte slice
+///
+/// # Example
+///
+/// ```rust
+/// # #![allow(unused_must_use)]
+/// use std::old_io::BufReader;
+///
+/// let buf = [0, 1, 2, 3];
+/// let mut r = BufReader::new(&buf);
+///
+/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]);
+/// ```
+pub struct BufReader<'a> {
+    buf: &'a [u8],
+    pos: uint
+}
+
+impl<'a> BufReader<'a> {
+    /// Creates a new buffered reader which will read the specified buffer
+    #[inline]
+    pub fn new(buf: &'a [u8]) -> BufReader<'a> {
+        BufReader {
+            buf: buf,
+            pos: 0
+        }
+    }
+
+    /// Tests whether this reader has read all bytes in its buffer.
+    ///
+    /// If `true`, then this will no longer return bytes from `read`.
+    #[inline]
+    pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
+}
+
+impl<'a> Reader for BufReader<'a> {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        if self.eof() { return Err(old_io::standard_error(old_io::EndOfFile)) }
+
+        let write_len = min(buf.len(), self.buf.len() - self.pos);
+        {
+            let input = &self.buf[self.pos.. self.pos + write_len];
+            let output = &mut buf[..write_len];
+            assert_eq!(input.len(), output.len());
+            slice::bytes::copy_memory(output, input);
+        }
+        self.pos += write_len;
+        assert!(self.pos <= self.buf.len());
+
+        return Ok(write_len);
+     }
+}
+
+impl<'a> Seek for BufReader<'a> {
+    #[inline]
+    fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
+
+    #[inline]
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
+        let new = try!(combine(style, self.pos, self.buf.len(), pos));
+        self.pos = new as uint;
+        Ok(())
+    }
+}
+
+impl<'a> Buffer for BufReader<'a> {
+    #[inline]
+    fn fill_buf(&mut self) -> IoResult<&[u8]> {
+        if self.pos < self.buf.len() {
+            Ok(&self.buf[self.pos..])
+        } else {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        }
+    }
+
+    #[inline]
+    fn consume(&mut self, amt: uint) { self.pos += amt; }
+}
+
+#[cfg(test)]
+mod test {
+    extern crate "test" as test_crate;
+    use old_io::{SeekSet, SeekCur, SeekEnd, Reader, Writer, Seek};
+    use prelude::v1::{Ok, Err, range,  Vec, Buffer,  AsSlice, SliceExt};
+    use prelude::v1::IteratorExt;
+    use old_io;
+    use iter::repeat;
+    use self::test_crate::Bencher;
+    use super::*;
+
+    #[test]
+    fn test_vec_writer() {
+        let mut writer = Vec::new();
+        writer.write(&[0]).unwrap();
+        writer.write(&[1, 2, 3]).unwrap();
+        writer.write(&[4, 5, 6, 7]).unwrap();
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+        assert_eq!(writer.as_slice(), b);
+    }
+
+    #[test]
+    fn test_mem_writer() {
+        let mut writer = MemWriter::new();
+        writer.write(&[0]).unwrap();
+        writer.write(&[1, 2, 3]).unwrap();
+        writer.write(&[4, 5, 6, 7]).unwrap();
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
+        assert_eq!(writer.get_ref(), b);
+    }
+
+    #[test]
+    fn test_buf_writer() {
+        let mut buf = [0 as u8; 9];
+        {
+            let mut writer = BufWriter::new(&mut buf);
+            assert_eq!(writer.tell(), Ok(0));
+            writer.write(&[0]).unwrap();
+            assert_eq!(writer.tell(), Ok(1));
+            writer.write(&[1, 2, 3]).unwrap();
+            writer.write(&[4, 5, 6, 7]).unwrap();
+            assert_eq!(writer.tell(), Ok(8));
+            writer.write(&[]).unwrap();
+            assert_eq!(writer.tell(), Ok(8));
+
+            assert_eq!(writer.write(&[8, 9]).err().unwrap().kind, old_io::ShortWrite(1));
+            assert_eq!(writer.write(&[10]).err().unwrap().kind, old_io::EndOfFile);
+        }
+        let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+        assert_eq!(buf, b);
+    }
+
+    #[test]
+    fn test_buf_writer_seek() {
+        let mut buf = [0 as u8; 8];
+        {
+            let mut writer = BufWriter::new(&mut buf);
+            assert_eq!(writer.tell(), Ok(0));
+            writer.write(&[1]).unwrap();
+            assert_eq!(writer.tell(), Ok(1));
+
+            writer.seek(2, SeekSet).unwrap();
+            assert_eq!(writer.tell(), Ok(2));
+            writer.write(&[2]).unwrap();
+            assert_eq!(writer.tell(), Ok(3));
+
+            writer.seek(-2, SeekCur).unwrap();
+            assert_eq!(writer.tell(), Ok(1));
+            writer.write(&[3]).unwrap();
+            assert_eq!(writer.tell(), Ok(2));
+
+            writer.seek(-1, SeekEnd).unwrap();
+            assert_eq!(writer.tell(), Ok(7));
+            writer.write(&[4]).unwrap();
+            assert_eq!(writer.tell(), Ok(8));
+
+        }
+        let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
+        assert_eq!(buf, b);
+    }
+
+    #[test]
+    fn test_buf_writer_error() {
+        let mut buf = [0 as u8; 2];
+        let mut writer = BufWriter::new(&mut buf);
+        writer.write(&[0]).unwrap();
+
+        match writer.write(&[0, 0]) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::ShortWrite(1)),
+        }
+    }
+
+    #[test]
+    fn test_mem_reader() {
+        let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
+        let mut buf = [];
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        assert_eq!(reader.tell(), Ok(0));
+        let mut buf = [0];
+        assert_eq!(reader.read(&mut buf), Ok(1));
+        assert_eq!(reader.tell(), Ok(1));
+        let b: &[_] = &[0];
+        assert_eq!(buf, b);
+        let mut buf = [0; 4];
+        assert_eq!(reader.read(&mut buf), Ok(4));
+        assert_eq!(reader.tell(), Ok(5));
+        let b: &[_] = &[1, 2, 3, 4];
+        assert_eq!(buf, b);
+        assert_eq!(reader.read(&mut buf), Ok(3));
+        let b: &[_] = &[5, 6, 7];
+        assert_eq!(&buf[..3], b);
+        assert!(reader.read(&mut buf).is_err());
+        let mut reader = MemReader::new(vec!(0, 1, 2, 3, 4, 5, 6, 7));
+        assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3));
+        assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7));
+        assert!(reader.read(&mut buf).is_err());
+    }
+
+    #[test]
+    fn test_slice_reader() {
+        let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+        let mut reader = &mut in_buf.as_slice();
+        let mut buf = [];
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        let mut buf = [0];
+        assert_eq!(reader.read(&mut buf), Ok(1));
+        assert_eq!(reader.len(), 7);
+        let b: &[_] = &[0];
+        assert_eq!(buf.as_slice(), b);
+        let mut buf = [0; 4];
+        assert_eq!(reader.read(&mut buf), Ok(4));
+        assert_eq!(reader.len(), 3);
+        let b: &[_] = &[1, 2, 3, 4];
+        assert_eq!(buf.as_slice(), b);
+        assert_eq!(reader.read(&mut buf), Ok(3));
+        let b: &[_] = &[5, 6, 7];
+        assert_eq!(&buf[..3], b);
+        assert!(reader.read(&mut buf).is_err());
+        let mut reader = &mut in_buf.as_slice();
+        assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3));
+        assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7));
+        assert!(reader.read(&mut buf).is_err());
+    }
+
+    #[test]
+    fn test_buf_reader() {
+        let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
+        let mut reader = BufReader::new(in_buf.as_slice());
+        let mut buf = [];
+        assert_eq!(reader.read(&mut buf), Ok(0));
+        assert_eq!(reader.tell(), Ok(0));
+        let mut buf = [0];
+        assert_eq!(reader.read(&mut buf), Ok(1));
+        assert_eq!(reader.tell(), Ok(1));
+        let b: &[_] = &[0];
+        assert_eq!(buf, b);
+        let mut buf = [0; 4];
+        assert_eq!(reader.read(&mut buf), Ok(4));
+        assert_eq!(reader.tell(), Ok(5));
+        let b: &[_] = &[1, 2, 3, 4];
+        assert_eq!(buf, b);
+        assert_eq!(reader.read(&mut buf), Ok(3));
+        let b: &[_] = &[5, 6, 7];
+        assert_eq!(&buf[..3], b);
+        assert!(reader.read(&mut buf).is_err());
+        let mut reader = BufReader::new(in_buf.as_slice());
+        assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3));
+        assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7));
+        assert!(reader.read(&mut buf).is_err());
+    }
+
+    #[test]
+    fn test_read_char() {
+        let b = b"Vi\xE1\xBB\x87t";
+        let mut r = BufReader::new(b);
+        assert_eq!(r.read_char(), Ok('V'));
+        assert_eq!(r.read_char(), Ok('i'));
+        assert_eq!(r.read_char(), Ok('ệ'));
+        assert_eq!(r.read_char(), Ok('t'));
+        assert!(r.read_char().is_err());
+    }
+
+    #[test]
+    fn test_read_bad_char() {
+        let b = b"\x80";
+        let mut r = BufReader::new(b);
+        assert!(r.read_char().is_err());
+    }
+
+    #[test]
+    fn test_write_strings() {
+        let mut writer = MemWriter::new();
+        writer.write_str("testing").unwrap();
+        writer.write_line("testing").unwrap();
+        writer.write_str("testing").unwrap();
+        let mut r = BufReader::new(writer.get_ref());
+        assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting");
+    }
+
+    #[test]
+    fn test_write_char() {
+        let mut writer = MemWriter::new();
+        writer.write_char('a').unwrap();
+        writer.write_char('\n').unwrap();
+        writer.write_char('ệ').unwrap();
+        let mut r = BufReader::new(writer.get_ref());
+        assert_eq!(r.read_to_string().unwrap(), "a\nệ");
+    }
+
+    #[test]
+    fn test_read_whole_string_bad() {
+        let buf = [0xff];
+        let mut r = BufReader::new(&buf);
+        match r.read_to_string() {
+            Ok(..) => panic!(),
+            Err(..) => {}
+        }
+    }
+
+    #[test]
+    fn seek_past_end() {
+        let buf = [0xff];
+        let mut r = BufReader::new(&buf);
+        r.seek(10, SeekSet).unwrap();
+        assert!(r.read(&mut []).is_err());
+
+        let mut r = MemReader::new(vec!(10));
+        r.seek(10, SeekSet).unwrap();
+        assert!(r.read(&mut []).is_err());
+
+        let mut buf = [0];
+        let mut r = BufWriter::new(&mut buf);
+        r.seek(10, SeekSet).unwrap();
+        assert!(r.write(&[3]).is_err());
+    }
+
+    #[test]
+    fn seek_before_0() {
+        let buf = [0xff];
+        let mut r = BufReader::new(&buf);
+        assert!(r.seek(-1, SeekSet).is_err());
+
+        let mut r = MemReader::new(vec!(10));
+        assert!(r.seek(-1, SeekSet).is_err());
+
+        let mut buf = [0];
+        let mut r = BufWriter::new(&mut buf);
+        assert!(r.seek(-1, SeekSet).is_err());
+    }
+
+    #[test]
+    fn io_read_at_least() {
+        let mut r = MemReader::new(vec![1, 2, 3, 4, 5, 6, 7, 8]);
+        let mut buf = [0; 3];
+        assert!(r.read_at_least(buf.len(), &mut buf).is_ok());
+        let b: &[_] = &[1, 2, 3];
+        assert_eq!(buf, b);
+        assert!(r.read_at_least(0, buf.slice_to_mut(0)).is_ok());
+        assert_eq!(buf, b);
+        assert!(r.read_at_least(buf.len(), &mut buf).is_ok());
+        let b: &[_] = &[4, 5, 6];
+        assert_eq!(buf, b);
+        assert!(r.read_at_least(buf.len(), &mut buf).is_err());
+        let b: &[_] = &[7, 8, 6];
+        assert_eq!(buf, b);
+    }
+
+    fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) {
+        let src: Vec<u8> = repeat(5).take(len).collect();
+
+        b.bytes = (times * len) as u64;
+        b.iter(|| {
+            let mut wr = MemWriter::new();
+            for _ in range(0, times) {
+                wr.write(src.as_slice()).unwrap();
+            }
+
+            let v = wr.into_inner();
+            assert_eq!(v.len(), times * len);
+            assert!(v.iter().all(|x| *x == 5));
+        });
+    }
+
+    #[bench]
+    fn bench_mem_writer_001_0000(b: &mut Bencher) {
+        do_bench_mem_writer(b, 1, 0)
+    }
+
+    #[bench]
+    fn bench_mem_writer_001_0010(b: &mut Bencher) {
+        do_bench_mem_writer(b, 1, 10)
+    }
+
+    #[bench]
+    fn bench_mem_writer_001_0100(b: &mut Bencher) {
+        do_bench_mem_writer(b, 1, 100)
+    }
+
+    #[bench]
+    fn bench_mem_writer_001_1000(b: &mut Bencher) {
+        do_bench_mem_writer(b, 1, 1000)
+    }
+
+    #[bench]
+    fn bench_mem_writer_100_0000(b: &mut Bencher) {
+        do_bench_mem_writer(b, 100, 0)
+    }
+
+    #[bench]
+    fn bench_mem_writer_100_0010(b: &mut Bencher) {
+        do_bench_mem_writer(b, 100, 10)
+    }
+
+    #[bench]
+    fn bench_mem_writer_100_0100(b: &mut Bencher) {
+        do_bench_mem_writer(b, 100, 100)
+    }
+
+    #[bench]
+    fn bench_mem_writer_100_1000(b: &mut Bencher) {
+        do_bench_mem_writer(b, 100, 1000)
+    }
+
+    #[bench]
+    fn bench_mem_reader(b: &mut Bencher) {
+        b.iter(|| {
+            let buf = [5 as u8; 100].to_vec();
+            {
+                let mut rdr = MemReader::new(buf);
+                for _i in range(0u, 10) {
+                    let mut buf = [0 as u8; 10];
+                    rdr.read(&mut buf).unwrap();
+                    assert_eq!(buf.as_slice(), [5; 10].as_slice());
+                }
+            }
+        });
+    }
+
+    #[bench]
+    fn bench_buf_writer(b: &mut Bencher) {
+        b.iter(|| {
+            let mut buf = [0 as u8; 100];
+            {
+                let mut wr = BufWriter::new(&mut buf);
+                for _i in range(0u, 10) {
+                    wr.write(&[5; 10]).unwrap();
+                }
+            }
+            assert_eq!(buf.as_slice(), [5; 100].as_slice());
+        });
+    }
+
+    #[bench]
+    fn bench_buf_reader(b: &mut Bencher) {
+        b.iter(|| {
+            let buf = [5 as u8; 100];
+            {
+                let mut rdr = BufReader::new(&buf);
+                for _i in range(0u, 10) {
+                    let mut buf = [0 as u8; 10];
+                    rdr.read(&mut buf).unwrap();
+                    assert_eq!(buf, [5; 10]);
+                }
+            }
+        });
+    }
+}
diff --git a/src/libstd/old_io/mod.rs b/src/libstd/old_io/mod.rs
new file mode 100644
index 00000000000..6c5ce129a33
--- /dev/null
+++ b/src/libstd/old_io/mod.rs
@@ -0,0 +1,1961 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// ignore-lexer-test FIXME #15883
+
+// FIXME: cover these topics:
+//        path, reader, writer, stream, raii (close not needed),
+//        stdio, print!, println!, file access, process spawning,
+//        error handling
+
+
+//! I/O, including files, networking, timers, and processes
+//!
+//! > **Warning**: This module is currently called `old_io` for a reason! The
+//! > module is currently being redesigned in a number of RFCs. For more details
+//! > follow the RFC repository in connection with [RFC 517][base] or follow
+//! > some of these sub-RFCs
+//! >
+//! > * [String handling][osstr]
+//! > * [Core I/O support][core]
+//! > * [Deadlines][deadlines]
+//! > * [std::env][env]
+//! > * [std::process][process]
+//!
+//! [base]: https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md
+//! [osstr]: https://github.com/rust-lang/rfcs/pull/575
+//! [core]: https://github.com/rust-lang/rfcs/pull/576
+//! [deadlines]: https://github.com/rust-lang/rfcs/pull/577
+//! [env]: https://github.com/rust-lang/rfcs/pull/578
+//! [process]: https://github.com/rust-lang/rfcs/pull/579
+//!
+//! `std::io` provides Rust's basic I/O types,
+//! for reading and writing to files, TCP, UDP,
+//! and other types of sockets and pipes,
+//! manipulating the file system, spawning processes.
+//!
+//! # Examples
+//!
+//! Some examples of obvious things you might want to do
+//!
+//! * Read lines from stdin
+//!
+//!     ```rust
+//!     use std::old_io as io;
+//!
+//!     for line in io::stdin().lock().lines() {
+//!         print!("{}", line.unwrap());
+//!     }
+//!     ```
+//!
+//! * Read a complete file
+//!
+//!     ```rust
+//!     use std::old_io::File;
+//!
+//!     let contents = File::open(&Path::new("message.txt")).read_to_end();
+//!     ```
+//!
+//! * Write a line to a file
+//!
+//!     ```rust
+//!     # #![allow(unused_must_use)]
+//!     use std::old_io::File;
+//!
+//!     let mut file = File::create(&Path::new("message.txt"));
+//!     file.write_all(b"hello, file!\n");
+//!     # drop(file);
+//!     # ::std::old_io::fs::unlink(&Path::new("message.txt"));
+//!     ```
+//!
+//! * Iterate over the lines of a file
+//!
+//!     ```rust,no_run
+//!     use std::old_io::BufferedReader;
+//!     use std::old_io::File;
+//!
+//!     let path = Path::new("message.txt");
+//!     let mut file = BufferedReader::new(File::open(&path));
+//!     for line in file.lines() {
+//!         print!("{}", line.unwrap());
+//!     }
+//!     ```
+//!
+//! * Pull the lines of a file into a vector of strings
+//!
+//!     ```rust,no_run
+//!     use std::old_io::BufferedReader;
+//!     use std::old_io::File;
+//!
+//!     let path = Path::new("message.txt");
+//!     let mut file = BufferedReader::new(File::open(&path));
+//!     let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect();
+//!     ```
+//!
+//! * Make a simple TCP client connection and request
+//!
+//!     ```rust
+//!     # #![allow(unused_must_use)]
+//!     use std::old_io::TcpStream;
+//!
+//!     # // connection doesn't fail if a server is running on 8080
+//!     # // locally, we still want to be type checking this code, so lets
+//!     # // just stop it running (#11576)
+//!     # if false {
+//!     let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap();
+//!     socket.write_all(b"GET / HTTP/1.0\n\n");
+//!     let response = socket.read_to_end();
+//!     # }
+//!     ```
+//!
+//! * Make a simple TCP server
+//!
+//!     ```rust
+//!     # fn main() { }
+//!     # fn foo() {
+//!     # #![allow(dead_code)]
+//!     use std::old_io::{TcpListener, TcpStream};
+//!     use std::old_io::{Acceptor, Listener};
+//!     use std::thread::Thread;
+//!
+//!     let listener = TcpListener::bind("127.0.0.1:80");
+//!
+//!     // bind the listener to the specified address
+//!     let mut acceptor = listener.listen();
+//!
+//!     fn handle_client(mut stream: TcpStream) {
+//!         // ...
+//!     # &mut stream; // silence unused mutability/variable warning
+//!     }
+//!     // accept connections and process them, spawning a new tasks for each one
+//!     for stream in acceptor.incoming() {
+//!         match stream {
+//!             Err(e) => { /* connection failed */ }
+//!             Ok(stream) => {
+//!                 Thread::spawn(move|| {
+//!                     // connection succeeded
+//!                     handle_client(stream)
+//!                 });
+//!             }
+//!         }
+//!     }
+//!
+//!     // close the socket server
+//!     drop(acceptor);
+//!     # }
+//!     ```
+//!
+//!
+//! # Error Handling
+//!
+//! I/O is an area where nearly every operation can result in unexpected
+//! errors. Errors should be painfully visible when they happen, and handling them
+//! should be easy to work with. It should be convenient to handle specific I/O
+//! errors, and it should also be convenient to not deal with I/O errors.
+//!
+//! Rust's I/O employs a combination of techniques to reduce boilerplate
+//! while still providing feedback about errors. The basic strategy:
+//!
+//! * All I/O operations return `IoResult<T>` which is equivalent to
+//!   `Result<T, IoError>`. The `Result` type is defined in the `std::result`
+//!   module.
+//! * If the `Result` type goes unused, then the compiler will by default emit a
+//!   warning about the unused result. This is because `Result` has the
+//!   `#[must_use]` attribute.
+//! * Common traits are implemented for `IoResult`, e.g.
+//!   `impl<R: Reader> Reader for IoResult<R>`, so that error values do not have
+//!   to be 'unwrapped' before use.
+//!
+//! These features combine in the API to allow for expressions like
+//! `File::create(&Path::new("diary.txt")).write_all(b"Met a girl.\n")`
+//! without having to worry about whether "diary.txt" exists or whether
+//! the write succeeds. As written, if either `new` or `write_line`
+//! encounters an error then the result of the entire expression will
+//! be an error.
+//!
+//! If you wanted to handle the error though you might write:
+//!
+//! ```rust
+//! # #![allow(unused_must_use)]
+//! use std::old_io::File;
+//!
+//! match File::create(&Path::new("diary.txt")).write_all(b"Met a girl.\n") {
+//!     Ok(()) => (), // succeeded
+//!     Err(e) => println!("failed to write to my diary: {}", e),
+//! }
+//!
+//! # ::std::old_io::fs::unlink(&Path::new("diary.txt"));
+//! ```
+//!
+//! So what actually happens if `create` encounters an error?
+//! It's important to know that what `new` returns is not a `File`
+//! but an `IoResult<File>`.  If the file does not open, then `new` will simply
+//! return `Err(..)`. Because there is an implementation of `Writer` (the trait
+//! required ultimately required for types to implement `write_line`) there is no
+//! need to inspect or unwrap the `IoResult<File>` and we simply call `write_line`
+//! on it. If `new` returned an `Err(..)` then the followup call to `write_line`
+//! will also return an error.
+//!
+//! ## `try!`
+//!
+//! Explicit pattern matching on `IoResult`s can get quite verbose, especially
+//! when performing many I/O operations. Some examples (like those above) are
+//! alleviated with extra methods implemented on `IoResult`, but others have more
+//! complex interdependencies among each I/O operation.
+//!
+//! The `try!` macro from `std::macros` is provided as a method of early-return
+//! inside `Result`-returning functions. It expands to an early-return on `Err`
+//! and otherwise unwraps the contained `Ok` value.
+//!
+//! If you wanted to read several `u32`s from a file and return their product:
+//!
+//! ```rust
+//! use std::old_io::{File, IoResult};
+//!
+//! fn file_product(p: &Path) -> IoResult<u32> {
+//!     let mut f = File::open(p);
+//!     let x1 = try!(f.read_le_u32());
+//!     let x2 = try!(f.read_le_u32());
+//!
+//!     Ok(x1 * x2)
+//! }
+//!
+//! match file_product(&Path::new("numbers.bin")) {
+//!     Ok(x) => println!("{}", x),
+//!     Err(e) => println!("Failed to read numbers!")
+//! }
+//! ```
+//!
+//! With `try!` in `file_product`, each `read_le_u32` need not be directly
+//! concerned with error handling; instead its caller is responsible for
+//! responding to errors that may occur while attempting to read the numbers.
+
+#![unstable(feature = "io")]
+#![deny(unused_must_use)]
+
+pub use self::SeekStyle::*;
+pub use self::FileMode::*;
+pub use self::FileAccess::*;
+pub use self::IoErrorKind::*;
+
+use char::CharExt;
+use default::Default;
+use error::Error;
+use fmt;
+use int;
+use iter::{Iterator, IteratorExt};
+use marker::Sized;
+use mem::transmute;
+use ops::FnOnce;
+use option::Option;
+use option::Option::{Some, None};
+use os;
+use boxed::Box;
+use result::Result;
+use result::Result::{Ok, Err};
+use sys;
+use slice::SliceExt;
+use str::StrExt;
+use str;
+use string::String;
+use uint;
+use unicode;
+use vec::Vec;
+
+// Reexports
+pub use self::stdio::stdin;
+pub use self::stdio::stdout;
+pub use self::stdio::stderr;
+pub use self::stdio::print;
+pub use self::stdio::println;
+
+pub use self::fs::File;
+pub use self::timer::Timer;
+pub use self::net::ip::IpAddr;
+pub use self::net::tcp::TcpListener;
+pub use self::net::tcp::TcpStream;
+pub use self::pipe::PipeStream;
+pub use self::process::{Process, Command};
+pub use self::tempfile::TempDir;
+
+pub use self::mem::{MemReader, BufReader, MemWriter, BufWriter};
+pub use self::buffered::{BufferedReader, BufferedWriter, BufferedStream,
+                         LineBufferedWriter};
+pub use self::comm_adapters::{ChanReader, ChanWriter};
+
+mod buffered;
+mod comm_adapters;
+mod mem;
+mod result;
+mod tempfile;
+pub mod extensions;
+pub mod fs;
+pub mod net;
+pub mod pipe;
+pub mod process;
+pub mod stdio;
+pub mod timer;
+pub mod util;
+
+#[macro_use]
+pub mod test;
+
+/// The default buffer size for various I/O operations
+// libuv recommends 64k buffers to maximize throughput
+// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA
+const DEFAULT_BUF_SIZE: uint = 1024 * 64;
+
+/// A convenient typedef of the return value of any I/O action.
+pub type IoResult<T> = Result<T, IoError>;
+
+/// The type passed to I/O condition handlers to indicate error
+///
+/// # FIXME
+///
+/// Is something like this sufficient? It's kind of archaic
+#[derive(PartialEq, Eq, Clone, Show)]
+pub struct IoError {
+    /// An enumeration which can be matched against for determining the flavor
+    /// of error.
+    pub kind: IoErrorKind,
+    /// A human-readable description about the error
+    pub desc: &'static str,
+    /// Detailed information about this error, not always available
+    pub detail: Option<String>
+}
+
+impl IoError {
+    /// Convert an `errno` value into an `IoError`.
+    ///
+    /// If `detail` is `true`, the `detail` field of the `IoError`
+    /// struct is filled with an allocated string describing the error
+    /// in more detail, retrieved from the operating system.
+    pub fn from_errno(errno: uint, detail: bool) -> IoError {
+        let mut err = sys::decode_error(errno as i32);
+        if detail && err.kind == OtherIoError {
+            err.detail = Some(os::error_string(errno).chars()
+                                 .map(|c| c.to_lowercase()).collect())
+        }
+        err
+    }
+
+    /// Retrieve the last error to occur as a (detailed) IoError.
+    ///
+    /// This uses the OS `errno`, and so there should not be any task
+    /// descheduling or migration (other than that performed by the
+    /// operating system) between the call(s) for which errors are
+    /// being checked and the call of this function.
+    pub fn last_error() -> IoError {
+        IoError::from_errno(os::errno() as uint, true)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for IoError {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } =>
+                write!(fmt, "{}", detail),
+            IoError { detail: None, desc, .. } =>
+                write!(fmt, "{}", desc),
+            IoError { detail: Some(ref detail), desc, .. } =>
+                write!(fmt, "{} ({})", desc, detail)
+        }
+    }
+}
+
+impl Error for IoError {
+    fn description(&self) -> &str { self.desc }
+}
+
+/// A list specifying general categories of I/O error.
+#[derive(Copy, PartialEq, Eq, Clone, Show)]
+pub enum IoErrorKind {
+    /// Any I/O error not part of this list.
+    OtherIoError,
+    /// The operation could not complete because end of file was reached.
+    EndOfFile,
+    /// The file was not found.
+    FileNotFound,
+    /// The file permissions disallowed access to this file.
+    PermissionDenied,
+    /// A network connection failed for some reason not specified in this list.
+    ConnectionFailed,
+    /// The network operation failed because the network connection was closed.
+    Closed,
+    /// The connection was refused by the remote server.
+    ConnectionRefused,
+    /// The connection was reset by the remote server.
+    ConnectionReset,
+    /// The connection was aborted (terminated) by the remote server.
+    ConnectionAborted,
+    /// The network operation failed because it was not connected yet.
+    NotConnected,
+    /// The operation failed because a pipe was closed.
+    BrokenPipe,
+    /// A file already existed with that name.
+    PathAlreadyExists,
+    /// No file exists at that location.
+    PathDoesntExist,
+    /// The path did not specify the type of file that this operation required. For example,
+    /// attempting to copy a directory with the `fs::copy()` operation will fail with this error.
+    MismatchedFileTypeForOperation,
+    /// The operation temporarily failed (for example, because a signal was received), and retrying
+    /// may succeed.
+    ResourceUnavailable,
+    /// No I/O functionality is available for this task.
+    IoUnavailable,
+    /// A parameter was incorrect in a way that caused an I/O error not part of this list.
+    InvalidInput,
+    /// The I/O operation's timeout expired, causing it to be canceled.
+    TimedOut,
+    /// This write operation failed to write all of its data.
+    ///
+    /// Normally the write() method on a Writer guarantees that all of its data
+    /// has been written, but some operations may be terminated after only
+    /// partially writing some data. An example of this is a timed out write
+    /// which successfully wrote a known number of bytes, but bailed out after
+    /// doing so.
+    ///
+    /// The payload contained as part of this variant is the number of bytes
+    /// which are known to have been successfully written.
+    ShortWrite(uint),
+    /// The Reader returned 0 bytes from `read()` too many times.
+    NoProgress,
+}
+
+/// A trait that lets you add a `detail` to an IoError easily
+trait UpdateIoError<T> {
+    /// Returns an IoError with updated description and detail
+    fn update_err<D>(self, desc: &'static str, detail: D) -> Self where
+        D: FnOnce(&IoError) -> String;
+
+    /// Returns an IoError with updated detail
+    fn update_detail<D>(self, detail: D) -> Self where
+        D: FnOnce(&IoError) -> String;
+
+    /// Returns an IoError with update description
+    fn update_desc(self, desc: &'static str) -> Self;
+}
+
+impl<T> UpdateIoError<T> for IoResult<T> {
+    fn update_err<D>(self, desc: &'static str, detail: D) -> IoResult<T> where
+        D: FnOnce(&IoError) -> String,
+    {
+        self.map_err(move |mut e| {
+            let detail = detail(&e);
+            e.desc = desc;
+            e.detail = Some(detail);
+            e
+        })
+    }
+
+    fn update_detail<D>(self, detail: D) -> IoResult<T> where
+        D: FnOnce(&IoError) -> String,
+    {
+        self.map_err(move |mut e| { e.detail = Some(detail(&e)); e })
+    }
+
+    fn update_desc(self, desc: &'static str) -> IoResult<T> {
+        self.map_err(|mut e| { e.desc = desc; e })
+    }
+}
+
+static NO_PROGRESS_LIMIT: uint = 1000;
+
+/// A trait for objects which are byte-oriented streams. Readers are defined by
+/// one method, `read`. This function will block until data is available,
+/// filling in the provided buffer with any data read.
+///
+/// Readers are intended to be composable with one another. Many objects
+/// throughout the I/O and related libraries take and provide types which
+/// implement the `Reader` trait.
+pub trait Reader {
+
+    // Only method which need to get implemented for this trait
+
+    /// Read bytes, up to the length of `buf` and place them in `buf`.
+    /// Returns the number of bytes read. The number of bytes read may
+    /// be less than the number requested, even 0. Returns `Err` on EOF.
+    ///
+    /// # Error
+    ///
+    /// If an error occurs during this I/O operation, then it is returned as
+    /// `Err(IoError)`. Note that end-of-file is considered an error, and can be
+    /// inspected for in the error's `kind` field. Also note that reading 0
+    /// bytes is not considered an error in all circumstances
+    ///
+    /// # Implementation Note
+    ///
+    /// When implementing this method on a new Reader, you are strongly encouraged
+    /// not to return 0 if you can avoid it.
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
+
+    // Convenient helper methods based on the above methods
+
+    /// Reads at least `min` bytes and places them in `buf`.
+    /// Returns the number of bytes read.
+    ///
+    /// This will continue to call `read` until at least `min` bytes have been
+    /// read. If `read` returns 0 too many times, `NoProgress` will be
+    /// returned.
+    ///
+    /// # Error
+    ///
+    /// If an error occurs at any point, that error is returned, and no further
+    /// bytes are read.
+    fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
+        if min > buf.len() {
+            return Err(IoError {
+                detail: Some(String::from_str("the buffer is too short")),
+                ..standard_error(InvalidInput)
+            });
+        }
+        let mut read = 0;
+        while read < min {
+            let mut zeroes = 0;
+            loop {
+                match self.read(&mut buf[read..]) {
+                    Ok(0) => {
+                        zeroes += 1;
+                        if zeroes >= NO_PROGRESS_LIMIT {
+                            return Err(standard_error(NoProgress));
+                        }
+                    }
+                    Ok(n) => {
+                        read += n;
+                        break;
+                    }
+                    err@Err(_) => return err
+                }
+            }
+        }
+        Ok(read)
+    }
+
+    /// Reads a single byte. Returns `Err` on EOF.
+    fn read_byte(&mut self) -> IoResult<u8> {
+        let mut buf = [0];
+        try!(self.read_at_least(1, &mut buf));
+        Ok(buf[0])
+    }
+
+    /// Reads up to `len` bytes and appends them to a vector.
+    /// Returns the number of bytes read. The number of bytes read may be
+    /// less than the number requested, even 0. Returns Err on EOF.
+    ///
+    /// # Error
+    ///
+    /// If an error occurs during this I/O operation, then it is returned
+    /// as `Err(IoError)`. See `read()` for more details.
+    fn push(&mut self, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
+        let start_len = buf.len();
+        buf.reserve(len);
+
+        let n = {
+            let s = unsafe { slice_vec_capacity(buf, start_len, start_len + len) };
+            try!(self.read(s))
+        };
+        unsafe { buf.set_len(start_len + n) };
+        Ok(n)
+    }
+
+    /// Reads at least `min` bytes, but no more than `len`, and appends them to
+    /// a vector.
+    /// Returns the number of bytes read.
+    ///
+    /// This will continue to call `read` until at least `min` bytes have been
+    /// read. If `read` returns 0 too many times, `NoProgress` will be
+    /// returned.
+    ///
+    /// # Error
+    ///
+    /// If an error occurs at any point, that error is returned, and no further
+    /// bytes are read.
+    fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
+        if min > len {
+            return Err(IoError {
+                detail: Some(String::from_str("the buffer is too short")),
+                ..standard_error(InvalidInput)
+            });
+        }
+
+        let start_len = buf.len();
+        buf.reserve(len);
+
+        // we can't just use self.read_at_least(min, slice) because we need to push
+        // successful reads onto the vector before any returned errors.
+
+        let mut read = 0;
+        while read < min {
+            read += {
+                let s = unsafe { slice_vec_capacity(buf, start_len + read, start_len + len) };
+                try!(self.read_at_least(1, s))
+            };
+            unsafe { buf.set_len(start_len + read) };
+        }
+        Ok(read)
+    }
+
+    /// Reads exactly `len` bytes and gives you back a new vector of length
+    /// `len`
+    ///
+    /// # Error
+    ///
+    /// Fails with the same conditions as `read`. Additionally returns error
+    /// on EOF. Note that if an error is returned, then some number of bytes may
+    /// have already been consumed from the underlying reader, and they are lost
+    /// (not returned as part of the error). If this is unacceptable, then it is
+    /// recommended to use the `push_at_least` or `read` methods.
+    fn read_exact(&mut self, len: uint) -> IoResult<Vec<u8>> {
+        let mut buf = Vec::with_capacity(len);
+        match self.push_at_least(len, len, &mut buf) {
+            Ok(_) => Ok(buf),
+            Err(e) => Err(e),
+        }
+    }
+
+    /// Reads all remaining bytes from the stream.
+    ///
+    /// # Error
+    ///
+    /// Returns any non-EOF error immediately. Previously read bytes are
+    /// discarded when an error is returned.
+    ///
+    /// When EOF is encountered, all bytes read up to that point are returned.
+    fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
+        let mut buf = Vec::with_capacity(DEFAULT_BUF_SIZE);
+        loop {
+            match self.push_at_least(1, DEFAULT_BUF_SIZE, &mut buf) {
+                Ok(_) => {}
+                Err(ref e) if e.kind == EndOfFile => break,
+                Err(e) => return Err(e)
+            }
+        }
+        return Ok(buf);
+    }
+
+    /// Reads all of the remaining bytes of this stream, interpreting them as a
+    /// UTF-8 encoded stream. The corresponding string is returned.
+    ///
+    /// # Error
+    ///
+    /// This function returns all of the same errors as `read_to_end` with an
+    /// additional error if the reader's contents are not a valid sequence of
+    /// UTF-8 bytes.
+    fn read_to_string(&mut self) -> IoResult<String> {
+        self.read_to_end().and_then(|s| {
+            match String::from_utf8(s) {
+                Ok(s)  => Ok(s),
+                Err(_) => Err(standard_error(InvalidInput)),
+            }
+        })
+    }
+
+    // Byte conversion helpers
+
+    /// Reads `n` little-endian unsigned integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        assert!(nbytes > 0 && nbytes <= 8);
+
+        let mut val = 0u64;
+        let mut pos = 0;
+        let mut i = nbytes;
+        while i > 0 {
+            val += (try!(self.read_u8()) as u64) << pos;
+            pos += 8;
+            i -= 1;
+        }
+        Ok(val)
+    }
+
+    /// Reads `n` little-endian signed integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_le_int_n(&mut self, nbytes: uint) -> IoResult<i64> {
+        self.read_le_uint_n(nbytes).map(|i| extend_sign(i, nbytes))
+    }
+
+    /// Reads `n` big-endian unsigned integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        assert!(nbytes > 0 && nbytes <= 8);
+
+        let mut val = 0u64;
+        let mut i = nbytes;
+        while i > 0 {
+            i -= 1;
+            val += (try!(self.read_u8()) as u64) << i * 8;
+        }
+        Ok(val)
+    }
+
+    /// Reads `n` big-endian signed integer bytes.
+    ///
+    /// `n` must be between 1 and 8, inclusive.
+    fn read_be_int_n(&mut self, nbytes: uint) -> IoResult<i64> {
+        self.read_be_uint_n(nbytes).map(|i| extend_sign(i, nbytes))
+    }
+
+    /// Reads a little-endian unsigned integer.
+    ///
+    /// The number of bytes returned is system-dependent.
+    fn read_le_uint(&mut self) -> IoResult<uint> {
+        self.read_le_uint_n(uint::BYTES).map(|i| i as uint)
+    }
+
+    /// Reads a little-endian integer.
+    ///
+    /// The number of bytes returned is system-dependent.
+    fn read_le_int(&mut self) -> IoResult<int> {
+        self.read_le_int_n(int::BYTES).map(|i| i as int)
+    }
+
+    /// Reads a big-endian unsigned integer.
+    ///
+    /// The number of bytes returned is system-dependent.
+    fn read_be_uint(&mut self) -> IoResult<uint> {
+        self.read_be_uint_n(uint::BYTES).map(|i| i as uint)
+    }
+
+    /// Reads a big-endian integer.
+    ///
+    /// The number of bytes returned is system-dependent.
+    fn read_be_int(&mut self) -> IoResult<int> {
+        self.read_be_int_n(int::BYTES).map(|i| i as int)
+    }
+
+    /// Reads a big-endian `u64`.
+    ///
+    /// `u64`s are 8 bytes long.
+    fn read_be_u64(&mut self) -> IoResult<u64> {
+        self.read_be_uint_n(8)
+    }
+
+    /// Reads a big-endian `u32`.
+    ///
+    /// `u32`s are 4 bytes long.
+    fn read_be_u32(&mut self) -> IoResult<u32> {
+        self.read_be_uint_n(4).map(|i| i as u32)
+    }
+
+    /// Reads a big-endian `u16`.
+    ///
+    /// `u16`s are 2 bytes long.
+    fn read_be_u16(&mut self) -> IoResult<u16> {
+        self.read_be_uint_n(2).map(|i| i as u16)
+    }
+
+    /// Reads a big-endian `i64`.
+    ///
+    /// `i64`s are 8 bytes long.
+    fn read_be_i64(&mut self) -> IoResult<i64> {
+        self.read_be_int_n(8)
+    }
+
+    /// Reads a big-endian `i32`.
+    ///
+    /// `i32`s are 4 bytes long.
+    fn read_be_i32(&mut self) -> IoResult<i32> {
+        self.read_be_int_n(4).map(|i| i as i32)
+    }
+
+    /// Reads a big-endian `i16`.
+    ///
+    /// `i16`s are 2 bytes long.
+    fn read_be_i16(&mut self) -> IoResult<i16> {
+        self.read_be_int_n(2).map(|i| i as i16)
+    }
+
+    /// Reads a big-endian `f64`.
+    ///
+    /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
+    fn read_be_f64(&mut self) -> IoResult<f64> {
+        self.read_be_u64().map(|i| unsafe {
+            transmute::<u64, f64>(i)
+        })
+    }
+
+    /// Reads a big-endian `f32`.
+    ///
+    /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
+    fn read_be_f32(&mut self) -> IoResult<f32> {
+        self.read_be_u32().map(|i| unsafe {
+            transmute::<u32, f32>(i)
+        })
+    }
+
+    /// Reads a little-endian `u64`.
+    ///
+    /// `u64`s are 8 bytes long.
+    fn read_le_u64(&mut self) -> IoResult<u64> {
+        self.read_le_uint_n(8)
+    }
+
+    /// Reads a little-endian `u32`.
+    ///
+    /// `u32`s are 4 bytes long.
+    fn read_le_u32(&mut self) -> IoResult<u32> {
+        self.read_le_uint_n(4).map(|i| i as u32)
+    }
+
+    /// Reads a little-endian `u16`.
+    ///
+    /// `u16`s are 2 bytes long.
+    fn read_le_u16(&mut self) -> IoResult<u16> {
+        self.read_le_uint_n(2).map(|i| i as u16)
+    }
+
+    /// Reads a little-endian `i64`.
+    ///
+    /// `i64`s are 8 bytes long.
+    fn read_le_i64(&mut self) -> IoResult<i64> {
+        self.read_le_int_n(8)
+    }
+
+    /// Reads a little-endian `i32`.
+    ///
+    /// `i32`s are 4 bytes long.
+    fn read_le_i32(&mut self) -> IoResult<i32> {
+        self.read_le_int_n(4).map(|i| i as i32)
+    }
+
+    /// Reads a little-endian `i16`.
+    ///
+    /// `i16`s are 2 bytes long.
+    fn read_le_i16(&mut self) -> IoResult<i16> {
+        self.read_le_int_n(2).map(|i| i as i16)
+    }
+
+    /// Reads a little-endian `f64`.
+    ///
+    /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
+    fn read_le_f64(&mut self) -> IoResult<f64> {
+        self.read_le_u64().map(|i| unsafe {
+            transmute::<u64, f64>(i)
+        })
+    }
+
+    /// Reads a little-endian `f32`.
+    ///
+    /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
+    fn read_le_f32(&mut self) -> IoResult<f32> {
+        self.read_le_u32().map(|i| unsafe {
+            transmute::<u32, f32>(i)
+        })
+    }
+
+    /// Read a u8.
+    ///
+    /// `u8`s are 1 byte.
+    fn read_u8(&mut self) -> IoResult<u8> {
+        self.read_byte()
+    }
+
+    /// Read an i8.
+    ///
+    /// `i8`s are 1 byte.
+    fn read_i8(&mut self) -> IoResult<i8> {
+        self.read_byte().map(|i| i as i8)
+    }
+}
+
+/// A reader which can be converted to a RefReader.
+pub trait ByRefReader {
+    /// Creates a wrapper around a mutable reference to the reader.
+    ///
+    /// This is useful to allow applying adaptors while still
+    /// retaining ownership of the original value.
+    fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self>;
+}
+
+impl<T: Reader> ByRefReader for T {
+    fn by_ref<'a>(&'a mut self) -> RefReader<'a, T> {
+        RefReader { inner: self }
+    }
+}
+
+/// A reader which can be converted to bytes.
+pub trait BytesReader {
+    /// Create an iterator that reads a single byte on
+    /// each iteration, until EOF.
+    ///
+    /// # Error
+    ///
+    /// Any error other than `EndOfFile` that is produced by the underlying Reader
+    /// is returned by the iterator and should be handled by the caller.
+    fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, Self>;
+}
+
+impl<T: Reader> BytesReader for T {
+    fn bytes<'r>(&'r mut self) -> extensions::Bytes<'r, T> {
+        extensions::Bytes::new(self)
+    }
+}
+
+impl<'a> Reader for Box<Reader+'a> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        let reader: &mut Reader = &mut **self;
+        reader.read(buf)
+    }
+}
+
+impl<'a> Reader for &'a mut (Reader+'a) {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { (*self).read(buf) }
+}
+
+/// Returns a slice of `v` between `start` and `end`.
+///
+/// Similar to `slice()` except this function only bounds the slice on the
+/// capacity of `v`, not the length.
+///
+/// # Panics
+///
+/// Panics when `start` or `end` point outside the capacity of `v`, or when
+/// `start` > `end`.
+// Private function here because we aren't sure if we want to expose this as
+// API yet. If so, it should be a method on Vec.
+unsafe fn slice_vec_capacity<'a, T>(v: &'a mut Vec<T>, start: uint, end: uint) -> &'a mut [T] {
+    use raw::Slice;
+    use ptr::PtrExt;
+
+    assert!(start <= end);
+    assert!(end <= v.capacity());
+    transmute(Slice {
+        data: v.as_ptr().offset(start as int),
+        len: end - start
+    })
+}
+
+/// A `RefReader` is a struct implementing `Reader` which contains a reference
+/// to another reader. This is often useful when composing streams.
+///
+/// # Examples
+///
+/// ```
+/// use std::old_io as io;
+/// use std::old_io::ByRefReader;
+/// use std::old_io::util::LimitReader;
+///
+/// fn process_input<R: Reader>(r: R) {}
+///
+/// let mut stream = io::stdin();
+///
+/// // Only allow the function to process at most one kilobyte of input
+/// {
+///     let stream = LimitReader::new(stream.by_ref(), 1024);
+///     process_input(stream);
+/// }
+///
+/// // 'stream' is still available for use here
+/// ```
+pub struct RefReader<'a, R:'a> {
+    /// The underlying reader which this is referencing
+    inner: &'a mut R
+}
+
+impl<'a, R: Reader> Reader for RefReader<'a, R> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { self.inner.read(buf) }
+}
+
+impl<'a, R: Buffer> Buffer for RefReader<'a, R> {
+    fn fill_buf(&mut self) -> IoResult<&[u8]> { self.inner.fill_buf() }
+    fn consume(&mut self, amt: uint) { self.inner.consume(amt) }
+}
+
+fn extend_sign(val: u64, nbytes: uint) -> i64 {
+    let shift = (8 - nbytes) * 8;
+    (val << shift) as i64 >> shift
+}
+
+/// A trait for objects which are byte-oriented streams. Writers are defined by
+/// one method, `write`. This function will block until the provided buffer of
+/// bytes has been entirely written, and it will return any failures which occur.
+///
+/// Another commonly overridden method is the `flush` method for writers such as
+/// buffered writers.
+///
+/// Writers are intended to be composable with one another. Many objects
+/// throughout the I/O and related libraries take and provide types which
+/// implement the `Writer` trait.
+pub trait Writer {
+    /// Write the entirety of a given buffer
+    ///
+    /// # Errors
+    ///
+    /// If an error happens during the I/O operation, the error is returned as
+    /// `Err`. Note that it is considered an error if the entire buffer could
+    /// not be written, and if an error is returned then it is unknown how much
+    /// data (if any) was actually written.
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()>;
+
+    /// Deprecated, this method was renamed to `write_all`
+    #[unstable(feature = "io")]
+    #[deprecated(since = "1.0.0", reason = "renamed to `write_all`")]
+    fn write(&mut self, buf: &[u8]) -> IoResult<()> { self.write_all(buf) }
+
+    /// Flush this output stream, ensuring that all intermediately buffered
+    /// contents reach their destination.
+    ///
+    /// This is by default a no-op and implementers of the `Writer` trait should
+    /// decide whether their stream needs to be buffered or not.
+    fn flush(&mut self) -> IoResult<()> { Ok(()) }
+
+    /// Writes a formatted string into this writer, returning any error
+    /// encountered.
+    ///
+    /// This method is primarily used to interface with the `format_args!`
+    /// macro, but it is rare that this should explicitly be called. The
+    /// `write!` macro should be favored to invoke this method instead.
+    ///
+    /// # Errors
+    ///
+    /// This function will return any I/O error reported while formatting.
+    fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
+        // Create a shim which translates a Writer to a fmt::Writer and saves
+        // off I/O errors. instead of discarding them
+        struct Adaptor<'a, T: ?Sized +'a> {
+            inner: &'a mut T,
+            error: IoResult<()>,
+        }
+
+        impl<'a, T: ?Sized + Writer> fmt::Writer for Adaptor<'a, T> {
+            fn write_str(&mut self, s: &str) -> fmt::Result {
+                match self.inner.write_all(s.as_bytes()) {
+                    Ok(()) => Ok(()),
+                    Err(e) => {
+                        self.error = Err(e);
+                        Err(fmt::Error)
+                    }
+                }
+            }
+        }
+
+        let mut output = Adaptor { inner: self, error: Ok(()) };
+        match fmt::write(&mut output, fmt) {
+            Ok(()) => Ok(()),
+            Err(..) => output.error
+        }
+    }
+
+
+    /// Write a rust string into this sink.
+    ///
+    /// The bytes written will be the UTF-8 encoded version of the input string.
+    /// If other encodings are desired, it is recommended to compose this stream
+    /// with another performing the conversion, or to use `write` with a
+    /// converted byte-array instead.
+    #[inline]
+    fn write_str(&mut self, s: &str) -> IoResult<()> {
+        self.write_all(s.as_bytes())
+    }
+
+    /// Writes a string into this sink, and then writes a literal newline (`\n`)
+    /// byte afterwards. Note that the writing of the newline is *not* atomic in
+    /// the sense that the call to `write` is invoked twice (once with the
+    /// string and once with a newline character).
+    ///
+    /// If other encodings or line ending flavors are desired, it is recommended
+    /// that the `write` method is used specifically instead.
+    #[inline]
+    fn write_line(&mut self, s: &str) -> IoResult<()> {
+        self.write_str(s).and_then(|()| self.write_all(&[b'\n']))
+    }
+
+    /// Write a single char, encoded as UTF-8.
+    #[inline]
+    fn write_char(&mut self, c: char) -> IoResult<()> {
+        let mut buf = [0u8; 4];
+        let n = c.encode_utf8(buf.as_mut_slice()).unwrap_or(0);
+        self.write_all(&buf[..n])
+    }
+
+    /// Write the result of passing n through `int::to_str_bytes`.
+    #[inline]
+    fn write_int(&mut self, n: int) -> IoResult<()> {
+        write!(self, "{}", n)
+    }
+
+    /// Write the result of passing n through `uint::to_str_bytes`.
+    #[inline]
+    fn write_uint(&mut self, n: uint) -> IoResult<()> {
+        write!(self, "{}", n)
+    }
+
+    /// Write a little-endian uint (number of bytes depends on system).
+    #[inline]
+    fn write_le_uint(&mut self, n: uint) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, uint::BYTES, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian int (number of bytes depends on system).
+    #[inline]
+    fn write_le_int(&mut self, n: int) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, int::BYTES, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian uint (number of bytes depends on system).
+    #[inline]
+    fn write_be_uint(&mut self, n: uint) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, uint::BYTES, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian int (number of bytes depends on system).
+    #[inline]
+    fn write_be_int(&mut self, n: int) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, int::BYTES, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian u64 (8 bytes).
+    #[inline]
+    fn write_be_u64(&mut self, n: u64) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n, 8u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian u32 (4 bytes).
+    #[inline]
+    fn write_be_u32(&mut self, n: u32) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian u16 (2 bytes).
+    #[inline]
+    fn write_be_u16(&mut self, n: u16) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian i64 (8 bytes).
+    #[inline]
+    fn write_be_i64(&mut self, n: i64) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian i32 (4 bytes).
+    #[inline]
+    fn write_be_i32(&mut self, n: i32) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian i16 (2 bytes).
+    #[inline]
+    fn write_be_i16(&mut self, n: i16) -> IoResult<()> {
+        extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write_all(v))
+    }
+
+    /// Write a big-endian IEEE754 double-precision floating-point (8 bytes).
+    #[inline]
+    fn write_be_f64(&mut self, f: f64) -> IoResult<()> {
+        unsafe {
+            self.write_be_u64(transmute(f))
+        }
+    }
+
+    /// Write a big-endian IEEE754 single-precision floating-point (4 bytes).
+    #[inline]
+    fn write_be_f32(&mut self, f: f32) -> IoResult<()> {
+        unsafe {
+            self.write_be_u32(transmute(f))
+        }
+    }
+
+    /// Write a little-endian u64 (8 bytes).
+    #[inline]
+    fn write_le_u64(&mut self, n: u64) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n, 8u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian u32 (4 bytes).
+    #[inline]
+    fn write_le_u32(&mut self, n: u32) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian u16 (2 bytes).
+    #[inline]
+    fn write_le_u16(&mut self, n: u16) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian i64 (8 bytes).
+    #[inline]
+    fn write_le_i64(&mut self, n: i64) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian i32 (4 bytes).
+    #[inline]
+    fn write_le_i32(&mut self, n: i32) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian i16 (2 bytes).
+    #[inline]
+    fn write_le_i16(&mut self, n: i16) -> IoResult<()> {
+        extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write_all(v))
+    }
+
+    /// Write a little-endian IEEE754 double-precision floating-point
+    /// (8 bytes).
+    #[inline]
+    fn write_le_f64(&mut self, f: f64) -> IoResult<()> {
+        unsafe {
+            self.write_le_u64(transmute(f))
+        }
+    }
+
+    /// Write a little-endian IEEE754 single-precision floating-point
+    /// (4 bytes).
+    #[inline]
+    fn write_le_f32(&mut self, f: f32) -> IoResult<()> {
+        unsafe {
+            self.write_le_u32(transmute(f))
+        }
+    }
+
+    /// Write a u8 (1 byte).
+    #[inline]
+    fn write_u8(&mut self, n: u8) -> IoResult<()> {
+        self.write_all(&[n])
+    }
+
+    /// Write an i8 (1 byte).
+    #[inline]
+    fn write_i8(&mut self, n: i8) -> IoResult<()> {
+        self.write_all(&[n as u8])
+    }
+}
+
+/// A writer which can be converted to a RefWriter.
+pub trait ByRefWriter {
+    /// Creates a wrapper around a mutable reference to the writer.
+    ///
+    /// This is useful to allow applying wrappers while still
+    /// retaining ownership of the original value.
+    #[inline]
+    fn by_ref<'a>(&'a mut self) -> RefWriter<'a, Self>;
+}
+
+impl<T: Writer> ByRefWriter for T {
+    fn by_ref<'a>(&'a mut self) -> RefWriter<'a, T> {
+        RefWriter { inner: self }
+    }
+}
+
+impl<'a> Writer for Box<Writer+'a> {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        (&mut **self).write_all(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> IoResult<()> {
+        (&mut **self).flush()
+    }
+}
+
+impl<'a> Writer for &'a mut (Writer+'a) {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { (**self).write_all(buf) }
+
+    #[inline]
+    fn flush(&mut self) -> IoResult<()> { (**self).flush() }
+}
+
+/// A `RefWriter` is a struct implementing `Writer` which contains a reference
+/// to another writer. This is often useful when composing streams.
+///
+/// # Example
+///
+/// ```
+/// use std::old_io::util::TeeReader;
+/// use std::old_io::{stdin, ByRefWriter};
+///
+/// fn process_input<R: Reader>(r: R) {}
+///
+/// let mut output = Vec::new();
+///
+/// {
+///     // Don't give ownership of 'output' to the 'tee'. Instead we keep a
+///     // handle to it in the outer scope
+///     let mut tee = TeeReader::new(stdin(), output.by_ref());
+///     process_input(tee);
+/// }
+///
+/// println!("input processed: {:?}", output);
+/// ```
+pub struct RefWriter<'a, W:'a> {
+    /// The underlying writer which this is referencing
+    inner: &'a mut W
+}
+
+impl<'a, W: Writer> Writer for RefWriter<'a, W> {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { self.inner.write_all(buf) }
+
+    #[inline]
+    fn flush(&mut self) -> IoResult<()> { self.inner.flush() }
+}
+
+
+/// A Stream is a readable and a writable object. Data written is typically
+/// received by the object which reads receive data from.
+pub trait Stream: Reader + Writer { }
+
+impl<T: Reader + Writer> Stream for T {}
+
+/// An iterator that reads a line on each iteration,
+/// until `.read_line()` encounters `EndOfFile`.
+///
+/// # Notes about the Iteration Protocol
+///
+/// The `Lines` may yield `None` and thus terminate
+/// an iteration, but continue to yield elements if iteration
+/// is attempted again.
+///
+/// # Error
+///
+/// Any error other than `EndOfFile` that is produced by the underlying Reader
+/// is returned by the iterator and should be handled by the caller.
+pub struct Lines<'r, T:'r> {
+    buffer: &'r mut T,
+}
+
+impl<'r, T: Buffer> Iterator for Lines<'r, T> {
+    type Item = IoResult<String>;
+
+    fn next(&mut self) -> Option<IoResult<String>> {
+        match self.buffer.read_line() {
+            Ok(x) => Some(Ok(x)),
+            Err(IoError { kind: EndOfFile, ..}) => None,
+            Err(y) => Some(Err(y))
+        }
+    }
+}
+
+/// An iterator that reads a utf8-encoded character on each iteration,
+/// until `.read_char()` encounters `EndOfFile`.
+///
+/// # Notes about the Iteration Protocol
+///
+/// The `Chars` may yield `None` and thus terminate
+/// an iteration, but continue to yield elements if iteration
+/// is attempted again.
+///
+/// # Error
+///
+/// Any error other than `EndOfFile` that is produced by the underlying Reader
+/// is returned by the iterator and should be handled by the caller.
+pub struct Chars<'r, T:'r> {
+    buffer: &'r mut T
+}
+
+impl<'r, T: Buffer> Iterator for Chars<'r, T> {
+    type Item = IoResult<char>;
+
+    fn next(&mut self) -> Option<IoResult<char>> {
+        match self.buffer.read_char() {
+            Ok(x) => Some(Ok(x)),
+            Err(IoError { kind: EndOfFile, ..}) => None,
+            Err(y) => Some(Err(y))
+        }
+    }
+}
+
+/// A Buffer is a type of reader which has some form of internal buffering to
+/// allow certain kinds of reading operations to be more optimized than others.
+/// This type extends the `Reader` trait with a few methods that are not
+/// possible to reasonably implement with purely a read interface.
+pub trait Buffer: Reader {
+    /// Fills the internal buffer of this object, returning the buffer contents.
+    /// Note that none of the contents will be "read" in the sense that later
+    /// calling `read` may return the same contents.
+    ///
+    /// The `consume` function must be called with the number of bytes that are
+    /// consumed from this buffer returned to ensure that the bytes are never
+    /// returned twice.
+    ///
+    /// # Error
+    ///
+    /// This function will return an I/O error if the underlying reader was
+    /// read, but returned an error. Note that it is not an error to return a
+    /// 0-length buffer.
+    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]>;
+
+    /// Tells this buffer that `amt` bytes have been consumed from the buffer,
+    /// so they should no longer be returned in calls to `read`.
+    fn consume(&mut self, amt: 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.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use std::old_io::BufReader;
+    ///
+    /// let mut reader = BufReader::new(b"hello\nworld");
+    /// assert_eq!("hello\n", &*reader.read_line().unwrap());
+    /// ```
+    ///
+    /// # Error
+    ///
+    /// This function has the same error semantics as `read_until`:
+    ///
+    /// * All non-EOF errors will be returned immediately
+    /// * If an error is returned previously consumed bytes are lost
+    /// * EOF is only returned if no bytes have been read
+    /// * Reach EOF may mean that the delimiter is not present in the return
+    ///   value
+    ///
+    /// Additionally, this function can fail if the line of input read is not a
+    /// valid UTF-8 sequence of bytes.
+    fn read_line(&mut self) -> IoResult<String> {
+        self.read_until(b'\n').and_then(|line|
+            match String::from_utf8(line) {
+                Ok(s)  => Ok(s),
+                Err(_) => Err(standard_error(InvalidInput)),
+            }
+        )
+    }
+
+    /// Reads a sequence of bytes leading up to a specified delimiter. Once the
+    /// specified byte is encountered, reading ceases and the bytes up to and
+    /// including the delimiter are returned.
+    ///
+    /// # Error
+    ///
+    /// If any I/O error is encountered other than EOF, the error is immediately
+    /// returned. Note that this may discard bytes which have already been read,
+    /// and those bytes will *not* be returned. It is recommended to use other
+    /// methods if this case is worrying.
+    ///
+    /// If EOF is encountered, then this function will return EOF if 0 bytes
+    /// have been read, otherwise the pending byte buffer is returned. This
+    /// is the reason that the byte buffer returned may not always contain the
+    /// delimiter.
+    fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
+        let mut res = Vec::new();
+
+        loop {
+            let (done, used) = {
+                let available = match self.fill_buf() {
+                    Ok(n) => n,
+                    Err(ref e) if res.len() > 0 && e.kind == EndOfFile => {
+                        return Ok(res);
+                    }
+                    Err(e) => return Err(e)
+                };
+                match available.iter().position(|&b| b == byte) {
+                    Some(i) => {
+                        res.push_all(&available[..i + 1]);
+                        (true, i + 1)
+                    }
+                    None => {
+                        res.push_all(available);
+                        (false, available.len())
+                    }
+                }
+            };
+            self.consume(used);
+            if done {
+                return Ok(res);
+            }
+        }
+    }
+
+    /// Reads the next utf8-encoded character from the underlying stream.
+    ///
+    /// # Error
+    ///
+    /// If an I/O error occurs, or EOF, then this function will return `Err`.
+    /// This function will also return error if the stream does not contain a
+    /// valid utf-8 encoded codepoint as the next few bytes in the stream.
+    fn read_char(&mut self) -> IoResult<char> {
+        let first_byte = try!(self.read_byte());
+        let width = unicode::str::utf8_char_width(first_byte);
+        if width == 1 { return Ok(first_byte as char) }
+        if width == 0 { return Err(standard_error(InvalidInput)) } // not utf8
+        let mut buf = [first_byte, 0, 0, 0];
+        {
+            let mut start = 1;
+            while start < width {
+                match try!(self.read(&mut buf[start .. width])) {
+                    n if n == width - start => break,
+                    n if n < width - start => { start += n; }
+                    _ => return Err(standard_error(InvalidInput)),
+                }
+            }
+        }
+        match str::from_utf8(&buf[..width]).ok() {
+            Some(s) => Ok(s.char_at(0)),
+            None => Err(standard_error(InvalidInput))
+        }
+    }
+}
+
+/// Extension methods for the Buffer trait which are included in the prelude.
+pub trait BufferPrelude {
+    /// Create an iterator that reads a utf8-encoded character on each iteration
+    /// until EOF.
+    ///
+    /// # Error
+    ///
+    /// Any error other than `EndOfFile` that is produced by the underlying Reader
+    /// is returned by the iterator and should be handled by the caller.
+    fn chars<'r>(&'r mut self) -> Chars<'r, Self>;
+
+    /// Create an iterator that reads a line on each iteration until EOF.
+    ///
+    /// # Error
+    ///
+    /// Any error other than `EndOfFile` that is produced by the underlying Reader
+    /// is returned by the iterator and should be handled by the caller.
+    fn lines<'r>(&'r mut self) -> Lines<'r, Self>;
+}
+
+impl<T: Buffer> BufferPrelude for T {
+    fn chars<'r>(&'r mut self) -> Chars<'r, T> {
+        Chars { buffer: self }
+    }
+
+    fn lines<'r>(&'r mut self) -> Lines<'r, T> {
+        Lines { buffer: self }
+    }
+}
+
+/// When seeking, the resulting cursor is offset from a base by the offset given
+/// to the `seek` function. The base used is specified by this enumeration.
+#[derive(Copy)]
+pub enum SeekStyle {
+    /// Seek from the beginning of the stream
+    SeekSet,
+    /// Seek from the end of the stream
+    SeekEnd,
+    /// Seek from the current position
+    SeekCur,
+}
+
+/// An object implementing `Seek` internally has some form of cursor which can
+/// be moved within a stream of bytes. The stream typically has a fixed size,
+/// allowing seeking relative to either end.
+pub trait Seek {
+    /// Return position of file cursor in the stream
+    fn tell(&self) -> IoResult<u64>;
+
+    /// Seek to an offset in a stream
+    ///
+    /// A successful seek clears the EOF indicator. Seeking beyond EOF is
+    /// allowed, but seeking before position 0 is not allowed.
+    ///
+    /// # Errors
+    ///
+    /// * Seeking to a negative offset is considered an error
+    /// * Seeking past the end of the stream does not modify the underlying
+    ///   stream, but the next write may cause the previous data to be filled in
+    ///   with a bit pattern.
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>;
+}
+
+/// A listener is a value that can consume itself to start listening for
+/// connections.
+///
+/// Doing so produces some sort of Acceptor.
+pub trait Listener<T, A: Acceptor<T>> {
+    /// Spin up the listener and start queuing incoming connections
+    ///
+    /// # Error
+    ///
+    /// Returns `Err` if this listener could not be bound to listen for
+    /// connections. In all cases, this listener is consumed.
+    fn listen(self) -> IoResult<A>;
+}
+
+/// An acceptor is a value that presents incoming connections
+pub trait Acceptor<T> {
+    /// Wait for and accept an incoming connection
+    ///
+    /// # Error
+    ///
+    /// Returns `Err` if an I/O error is encountered.
+    fn accept(&mut self) -> IoResult<T>;
+
+    /// Create an iterator over incoming connection attempts.
+    ///
+    /// Note that I/O errors will be yielded by the iterator itself.
+    fn incoming<'r>(&'r mut self) -> IncomingConnections<'r, Self> {
+        IncomingConnections { inc: self }
+    }
+}
+
+/// An infinite iterator over incoming connection attempts.
+/// Calling `next` will block the task until a connection is attempted.
+///
+/// Since connection attempts can continue forever, this iterator always returns
+/// `Some`. The `Some` contains the `IoResult` representing whether the
+/// connection attempt was successful.  A successful connection will be wrapped
+/// in `Ok`. A failed connection is represented as an `Err`.
+pub struct IncomingConnections<'a, A: ?Sized +'a> {
+    inc: &'a mut A,
+}
+
+#[old_impl_check]
+impl<'a, T, A: ?Sized + Acceptor<T>> Iterator for IncomingConnections<'a, A> {
+    type Item = IoResult<T>;
+
+    fn next(&mut self) -> Option<IoResult<T>> {
+        Some(self.inc.accept())
+    }
+}
+
+/// Creates a standard error for a commonly used flavor of error. The `detail`
+/// field of the returned error will always be `None`.
+///
+/// # Example
+///
+/// ```
+/// use std::old_io as io;
+///
+/// let eof = io::standard_error(io::EndOfFile);
+/// let einval = io::standard_error(io::InvalidInput);
+/// ```
+pub fn standard_error(kind: IoErrorKind) -> IoError {
+    let desc = match kind {
+        EndOfFile => "end of file",
+        IoUnavailable => "I/O is unavailable",
+        InvalidInput => "invalid input",
+        OtherIoError => "unknown I/O error",
+        FileNotFound => "file not found",
+        PermissionDenied => "permission denied",
+        ConnectionFailed => "connection failed",
+        Closed => "stream is closed",
+        ConnectionRefused => "connection refused",
+        ConnectionReset => "connection reset",
+        ConnectionAborted => "connection aborted",
+        NotConnected => "not connected",
+        BrokenPipe => "broken pipe",
+        PathAlreadyExists => "file already exists",
+        PathDoesntExist => "no such file",
+        MismatchedFileTypeForOperation => "mismatched file type",
+        ResourceUnavailable => "resource unavailable",
+        TimedOut => "operation timed out",
+        ShortWrite(..) => "short write",
+        NoProgress => "no progress",
+    };
+    IoError {
+        kind: kind,
+        desc: desc,
+        detail: None,
+    }
+}
+
+/// A mode specifies how a file should be opened or created. These modes are
+/// passed to `File::open_mode` and are used to control where the file is
+/// positioned when it is initially opened.
+#[derive(Copy, Clone, PartialEq, Eq, Show)]
+pub enum FileMode {
+    /// Opens a file positioned at the beginning.
+    Open,
+    /// Opens a file positioned at EOF.
+    Append,
+    /// Opens a file, truncating it if it already exists.
+    Truncate,
+}
+
+/// Access permissions with which the file should be opened. `File`s
+/// opened with `Read` will return an error if written to.
+#[derive(Copy, Clone, PartialEq, Eq, Show)]
+pub enum FileAccess {
+    /// Read-only access, requests to write will result in an error
+    Read,
+    /// Write-only access, requests to read will result in an error
+    Write,
+    /// Read-write access, no requests are denied by default
+    ReadWrite,
+}
+
+/// Different kinds of files which can be identified by a call to stat
+#[derive(Copy, PartialEq, Show, Hash, Clone)]
+pub enum FileType {
+    /// This is a normal file, corresponding to `S_IFREG`
+    RegularFile,
+
+    /// This file is a directory, corresponding to `S_IFDIR`
+    Directory,
+
+    /// This file is a named pipe, corresponding to `S_IFIFO`
+    NamedPipe,
+
+    /// This file is a block device, corresponding to `S_IFBLK`
+    BlockSpecial,
+
+    /// This file is a symbolic link to another file, corresponding to `S_IFLNK`
+    Symlink,
+
+    /// The type of this file is not recognized as one of the other categories
+    Unknown,
+}
+
+/// A structure used to describe metadata information about a file. This
+/// structure is created through the `stat` method on a `Path`.
+///
+/// # Examples
+///
+/// ```no_run
+///
+/// use std::old_io::fs::PathExtensions;
+///
+/// let info = match Path::new("foo.txt").stat() {
+///     Ok(stat) => stat,
+///     Err(e) => panic!("couldn't read foo.txt: {}", e),
+/// };
+///
+/// println!("byte size: {}", info.size);
+/// ```
+#[derive(Copy, Hash)]
+pub struct FileStat {
+    /// The size of the file, in bytes
+    pub size: u64,
+    /// The kind of file this path points to (directory, file, pipe, etc.)
+    pub kind: FileType,
+    /// The file permissions currently on the file
+    pub perm: FilePermission,
+
+    // FIXME(#10301): These time fields are pretty useless without an actual
+    //                time representation, what are the milliseconds relative
+    //                to?
+
+    /// The time that the file was created at, in platform-dependent
+    /// milliseconds
+    pub created: u64,
+    /// The time that this file was last modified, in platform-dependent
+    /// milliseconds
+    pub modified: u64,
+    /// The time that this file was last accessed, in platform-dependent
+    /// milliseconds
+    pub accessed: u64,
+
+    /// Information returned by stat() which is not guaranteed to be
+    /// platform-independent. This information may be useful on some platforms,
+    /// but it may have different meanings or no meaning at all on other
+    /// platforms.
+    ///
+    /// Usage of this field is discouraged, but if access is desired then the
+    /// fields are located here.
+    #[unstable(feature = "io")]
+    pub unstable: UnstableFileStat,
+}
+
+/// This structure represents all of the possible information which can be
+/// returned from a `stat` syscall which is not contained in the `FileStat`
+/// structure. This information is not necessarily platform independent, and may
+/// have different meanings or no meaning at all on some platforms.
+#[unstable(feature = "io")]
+#[derive(Copy, Hash)]
+pub struct UnstableFileStat {
+    /// The ID of the device containing the file.
+    pub device: u64,
+    /// The file serial number.
+    pub inode: u64,
+    /// The device ID.
+    pub rdev: u64,
+    /// The number of hard links to this file.
+    pub nlink: u64,
+    /// The user ID of the file.
+    pub uid: u64,
+    /// The group ID of the file.
+    pub gid: u64,
+    /// The optimal block size for I/O.
+    pub blksize: u64,
+    /// The blocks allocated for this file.
+    pub blocks: u64,
+    /// User-defined flags for the file.
+    pub flags: u64,
+    /// The file generation number.
+    pub gen: u64,
+}
+
+
+bitflags! {
+    /// A set of permissions for a file or directory is represented by a set of
+    /// flags which are or'd together.
+    #[derive(Show)]
+    flags FilePermission: u32 {
+        const USER_READ     = 0o400,
+        const USER_WRITE    = 0o200,
+        const USER_EXECUTE  = 0o100,
+        const GROUP_READ    = 0o040,
+        const GROUP_WRITE   = 0o020,
+        const GROUP_EXECUTE = 0o010,
+        const OTHER_READ    = 0o004,
+        const OTHER_WRITE   = 0o002,
+        const OTHER_EXECUTE = 0o001,
+
+        const USER_RWX  = USER_READ.bits | USER_WRITE.bits | USER_EXECUTE.bits,
+        const GROUP_RWX = GROUP_READ.bits | GROUP_WRITE.bits | GROUP_EXECUTE.bits,
+        const OTHER_RWX = OTHER_READ.bits | OTHER_WRITE.bits | OTHER_EXECUTE.bits,
+
+        /// Permissions for user owned files, equivalent to 0644 on unix-like
+        /// systems.
+        const USER_FILE = USER_READ.bits | USER_WRITE.bits | GROUP_READ.bits | OTHER_READ.bits,
+
+        /// Permissions for user owned directories, equivalent to 0755 on
+        /// unix-like systems.
+        const USER_DIR  = USER_RWX.bits | GROUP_READ.bits | GROUP_EXECUTE.bits |
+                   OTHER_READ.bits | OTHER_EXECUTE.bits,
+
+        /// Permissions for user owned executables, equivalent to 0755
+        /// on unix-like systems.
+        const USER_EXEC = USER_DIR.bits,
+
+        /// All possible permissions enabled.
+        const ALL_PERMISSIONS = USER_RWX.bits | GROUP_RWX.bits | OTHER_RWX.bits,
+    }
+}
+
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl Default for FilePermission {
+    #[stable(feature = "rust1", since = "1.0.0")]
+    #[inline]
+    fn default() -> FilePermission { FilePermission::empty() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for FilePermission {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:04o}", self.bits)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use self::BadReaderBehavior::*;
+    use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput, Writer};
+    use prelude::v1::{Ok, Vec, Buffer, SliceExt};
+    use uint;
+
+    #[derive(Clone, PartialEq, Show)]
+    enum BadReaderBehavior {
+        GoodBehavior(uint),
+        BadBehavior(uint)
+    }
+
+    struct BadReader<T> {
+        r: T,
+        behavior: Vec<BadReaderBehavior>,
+    }
+
+    impl<T: Reader> BadReader<T> {
+        fn new(r: T, behavior: Vec<BadReaderBehavior>) -> BadReader<T> {
+            BadReader { behavior: behavior, r: r }
+        }
+    }
+
+    impl<T: Reader> Reader for BadReader<T> {
+        fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+            let BadReader { ref mut behavior, ref mut r } = *self;
+            loop {
+                if behavior.is_empty() {
+                    // fall back on good
+                    return r.read(buf);
+                }
+                match behavior.as_mut_slice()[0] {
+                    GoodBehavior(0) => (),
+                    GoodBehavior(ref mut x) => {
+                        *x -= 1;
+                        return r.read(buf);
+                    }
+                    BadBehavior(0) => (),
+                    BadBehavior(ref mut x) => {
+                        *x -= 1;
+                        return Ok(0);
+                    }
+                };
+                behavior.remove(0);
+            }
+        }
+    }
+
+    #[test]
+    fn test_read_at_least() {
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![GoodBehavior(uint::MAX)]);
+        let buf = &mut [0u8; 5];
+        assert!(r.read_at_least(1, buf).unwrap() >= 1);
+        assert!(r.read_exact(5).unwrap().len() == 5); // read_exact uses read_at_least
+        assert!(r.read_at_least(0, buf).is_ok());
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(50), GoodBehavior(uint::MAX)]);
+        assert!(r.read_at_least(1, buf).unwrap() >= 1);
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(1), GoodBehavior(1),
+                                        BadBehavior(50), GoodBehavior(uint::MAX)]);
+        assert!(r.read_at_least(1, buf).unwrap() >= 1);
+        assert!(r.read_at_least(1, buf).unwrap() >= 1);
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(uint::MAX)]);
+        assert_eq!(r.read_at_least(1, buf).unwrap_err().kind, NoProgress);
+
+        let mut r = MemReader::new(b"hello, world!".to_vec());
+        assert_eq!(r.read_at_least(5, buf).unwrap(), 5);
+        assert_eq!(r.read_at_least(6, buf).unwrap_err().kind, InvalidInput);
+    }
+
+    #[test]
+    fn test_push_at_least() {
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![GoodBehavior(uint::MAX)]);
+        let mut buf = Vec::new();
+        assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1);
+        assert!(r.push_at_least(0, 5, &mut buf).is_ok());
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(50), GoodBehavior(uint::MAX)]);
+        assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1);
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(1), GoodBehavior(1),
+                                        BadBehavior(50), GoodBehavior(uint::MAX)]);
+        assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1);
+        assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1);
+
+        let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()),
+                                   vec![BadBehavior(uint::MAX)]);
+        assert_eq!(r.push_at_least(1, 5, &mut buf).unwrap_err().kind, NoProgress);
+
+        let mut r = MemReader::new(b"hello, world!".to_vec());
+        assert_eq!(r.push_at_least(5, 1, &mut buf).unwrap_err().kind, InvalidInput);
+    }
+
+    #[test]
+    fn test_show() {
+        use super::*;
+
+        assert_eq!(format!("{}", USER_READ), "0400");
+        assert_eq!(format!("{}", USER_FILE), "0644");
+        assert_eq!(format!("{}", USER_EXEC), "0755");
+        assert_eq!(format!("{}", USER_RWX),  "0700");
+        assert_eq!(format!("{}", GROUP_RWX), "0070");
+        assert_eq!(format!("{}", OTHER_RWX), "0007");
+        assert_eq!(format!("{}", ALL_PERMISSIONS), "0777");
+        assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602");
+    }
+
+    fn _ensure_buffer_is_object_safe<T: Buffer>(x: &T) -> &Buffer {
+        x as &Buffer
+    }
+}
diff --git a/src/libstd/old_io/net/addrinfo.rs b/src/libstd/old_io/net/addrinfo.rs
new file mode 100644
index 00000000000..9800cc6829e
--- /dev/null
+++ b/src/libstd/old_io/net/addrinfo.rs
@@ -0,0 +1,137 @@
+// 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.
+
+//! Synchronous DNS Resolution
+//!
+//! Contains the functionality to perform DNS resolution or reverse lookup,
+//! in a style related to `getaddrinfo()` and `getnameinfo()`, respectively.
+
+#![allow(missing_docs)]
+
+pub use self::SocketType::*;
+pub use self::Flag::*;
+pub use self::Protocol::*;
+
+use iter::IteratorExt;
+use old_io::{IoResult};
+use old_io::net::ip::{SocketAddr, IpAddr};
+use option::Option;
+use option::Option::{Some, None};
+use string::String;
+use sys;
+use vec::Vec;
+
+/// Hints to the types of sockets that are desired when looking up hosts
+#[derive(Copy, Show)]
+pub enum SocketType {
+    Stream, Datagram, Raw
+}
+
+/// Flags which can be or'd into the `flags` field of a `Hint`. These are used
+/// to manipulate how a query is performed.
+///
+/// The meaning of each of these flags can be found with `man -s 3 getaddrinfo`
+#[derive(Copy, Show)]
+pub enum Flag {
+    AddrConfig,
+    All,
+    CanonName,
+    NumericHost,
+    NumericServ,
+    Passive,
+    V4Mapped,
+}
+
+/// A transport protocol associated with either a hint or a return value of
+/// `lookup`
+#[derive(Copy, Show)]
+pub enum Protocol {
+    TCP, UDP
+}
+
+/// This structure is used to provide hints when fetching addresses for a
+/// remote host to control how the lookup is performed.
+///
+/// For details on these fields, see their corresponding definitions via
+/// `man -s 3 getaddrinfo`
+#[derive(Copy, Show)]
+pub struct Hint {
+    pub family: uint,
+    pub socktype: Option<SocketType>,
+    pub protocol: Option<Protocol>,
+    pub flags: uint,
+}
+
+#[derive(Copy, Show)]
+pub struct Info {
+    pub address: SocketAddr,
+    pub family: uint,
+    pub socktype: Option<SocketType>,
+    pub protocol: Option<Protocol>,
+    pub flags: uint,
+}
+
+/// Easy name resolution. Given a hostname, returns the list of IP addresses for
+/// that hostname.
+pub fn get_host_addresses(host: &str) -> IoResult<Vec<IpAddr>> {
+    lookup(Some(host), None, None).map(|a| a.into_iter().map(|i| i.address.ip).collect())
+}
+
+/// Reverse name resolution. Given an address, returns the corresponding
+/// hostname.
+pub fn get_address_name(addr: IpAddr) -> IoResult<String> {
+    sys::addrinfo::get_address_name(addr)
+}
+
+/// Full-fledged resolution. This function will perform a synchronous call to
+/// getaddrinfo, controlled by the parameters
+///
+/// # Arguments
+///
+/// * hostname - an optional hostname to lookup against
+/// * servname - an optional service name, listed in the system services
+/// * hint - see the hint structure, and "man -s 3 getaddrinfo", for how this
+///          controls lookup
+///
+/// FIXME: this is not public because the `Hint` structure is not ready for public
+///      consumption just yet.
+#[allow(unused_variables)]
+fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option<Hint>)
+          -> IoResult<Vec<Info>> {
+    sys::addrinfo::get_host_addresses(hostname, servname, hint)
+}
+
+// Ignored on android since we cannot give tcp/ip
+// permission without help of apk
+#[cfg(all(test, not(target_os = "android")))]
+mod test {
+    use prelude::v1::*;
+    use super::*;
+    use old_io::net::ip::*;
+
+    #[test]
+    fn dns_smoke_test() {
+        let ipaddrs = get_host_addresses("localhost").unwrap();
+        let mut found_local = false;
+        let local_addr = &Ipv4Addr(127, 0, 0, 1);
+        for addr in ipaddrs.iter() {
+            found_local = found_local || addr == local_addr;
+        }
+        assert!(found_local);
+    }
+
+    #[ignore]
+    #[test]
+    fn issue_10663() {
+        // Something should happen here, but this certainly shouldn't cause
+        // everything to die. The actual outcome we don't care too much about.
+        get_host_addresses("example.com").unwrap();
+    }
+}
diff --git a/src/libstd/old_io/net/ip.rs b/src/libstd/old_io/net/ip.rs
new file mode 100644
index 00000000000..e60b455aecd
--- /dev/null
+++ b/src/libstd/old_io/net/ip.rs
@@ -0,0 +1,700 @@
+// 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.
+
+//! Internet Protocol (IP) addresses.
+//!
+//! This module contains functions useful for parsing, formatting, and
+//! manipulating IP addresses.
+
+#![allow(missing_docs)]
+
+pub use self::IpAddr::*;
+
+use boxed::Box;
+use fmt;
+use old_io::{self, IoResult, IoError};
+use old_io::net;
+use iter::{Iterator, IteratorExt};
+use ops::{FnOnce, FnMut};
+use option::Option;
+use option::Option::{None, Some};
+use result::Result::{Ok, Err};
+use slice::SliceExt;
+use str::{FromStr, StrExt};
+use vec::Vec;
+
+pub type Port = u16;
+
+#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)]
+pub enum IpAddr {
+    Ipv4Addr(u8, u8, u8, u8),
+    Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16)
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for IpAddr {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            Ipv4Addr(a, b, c, d) =>
+                write!(fmt, "{}.{}.{}.{}", a, b, c, d),
+
+            // Ipv4 Compatible address
+            Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => {
+                write!(fmt, "::{}.{}.{}.{}", (g >> 8) as u8, g as u8,
+                       (h >> 8) as u8, h as u8)
+            }
+
+            // Ipv4-Mapped address
+            Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, g, h) => {
+                write!(fmt, "::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8,
+                       (h >> 8) as u8, h as u8)
+            }
+
+            Ipv6Addr(a, b, c, d, e, f, g, h) =>
+                write!(fmt, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
+                       a, b, c, d, e, f, g, h)
+        }
+    }
+}
+
+#[derive(Copy, PartialEq, Eq, Clone, Hash, Show)]
+pub struct SocketAddr {
+    pub ip: IpAddr,
+    pub port: Port,
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for SocketAddr {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.ip {
+            Ipv4Addr(..) => write!(f, "{}:{}", self.ip, self.port),
+            Ipv6Addr(..) => write!(f, "[{}]:{}", self.ip, self.port),
+        }
+    }
+}
+
+struct Parser<'a> {
+    // parsing as ASCII, so can use byte array
+    s: &'a [u8],
+    pos: uint,
+}
+
+impl<'a> Parser<'a> {
+    fn new(s: &'a str) -> Parser<'a> {
+        Parser {
+            s: s.as_bytes(),
+            pos: 0,
+        }
+    }
+
+    fn is_eof(&self) -> bool {
+        self.pos == self.s.len()
+    }
+
+    // Commit only if parser returns Some
+    fn read_atomically<T, F>(&mut self, cb: F) -> Option<T> where
+        F: FnOnce(&mut Parser) -> Option<T>,
+    {
+        let pos = self.pos;
+        let r = cb(self);
+        if r.is_none() {
+            self.pos = pos;
+        }
+        r
+    }
+
+    // Commit only if parser read till EOF
+    fn read_till_eof<T, F>(&mut self, cb: F) -> Option<T> where
+        F: FnOnce(&mut Parser) -> Option<T>,
+    {
+        self.read_atomically(move |p| {
+            match cb(p) {
+                Some(x) => if p.is_eof() {Some(x)} else {None},
+                None => None,
+            }
+        })
+    }
+
+    // Return result of first successful parser
+    fn read_or<T>(&mut self, parsers: &mut [Box<FnMut(&mut Parser) -> Option<T>>])
+               -> Option<T> {
+        for pf in parsers.iter_mut() {
+            match self.read_atomically(|p: &mut Parser| pf.call_mut((p,))) {
+                Some(r) => return Some(r),
+                None => {}
+            }
+        }
+        None
+    }
+
+    // Apply 3 parsers sequentially
+    fn read_seq_3<A, B, C, PA, PB, PC>(&mut self,
+                                       pa: PA,
+                                       pb: PB,
+                                       pc: PC)
+                                       -> Option<(A, B, C)> where
+        PA: FnOnce(&mut Parser) -> Option<A>,
+        PB: FnOnce(&mut Parser) -> Option<B>,
+        PC: FnOnce(&mut Parser) -> Option<C>,
+    {
+        self.read_atomically(move |p| {
+            let a = pa(p);
+            let b = if a.is_some() { pb(p) } else { None };
+            let c = if b.is_some() { pc(p) } else { None };
+            match (a, b, c) {
+                (Some(a), Some(b), Some(c)) => Some((a, b, c)),
+                _ => None
+            }
+        })
+    }
+
+    // Read next char
+    fn read_char(&mut self) -> Option<char> {
+        if self.is_eof() {
+            None
+        } else {
+            let r = self.s[self.pos] as char;
+            self.pos += 1;
+            Some(r)
+        }
+    }
+
+    // Return char and advance iff next char is equal to requested
+    fn read_given_char(&mut self, c: char) -> Option<char> {
+        self.read_atomically(|p| {
+            match p.read_char() {
+                Some(next) if next == c => Some(next),
+                _ => None,
+            }
+        })
+    }
+
+    // Read digit
+    fn read_digit(&mut self, radix: u8) -> Option<u8> {
+        fn parse_digit(c: char, radix: u8) -> Option<u8> {
+            let c = c as u8;
+            // assuming radix is either 10 or 16
+            if c >= b'0' && c <= b'9' {
+                Some(c - b'0')
+            } else if radix > 10 && c >= b'a' && c < b'a' + (radix - 10) {
+                Some(c - b'a' + 10)
+            } else if radix > 10 && c >= b'A' && c < b'A' + (radix - 10) {
+                Some(c - b'A' + 10)
+            } else {
+                None
+            }
+        }
+
+        self.read_atomically(|p| {
+            p.read_char().and_then(|c| parse_digit(c, radix))
+        })
+    }
+
+    fn read_number_impl(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        let mut r = 0u32;
+        let mut digit_count = 0;
+        loop {
+            match self.read_digit(radix) {
+                Some(d) => {
+                    r = r * (radix as u32) + (d as u32);
+                    digit_count += 1;
+                    if digit_count > max_digits || r >= upto {
+                        return None
+                    }
+                }
+                None => {
+                    if digit_count == 0 {
+                        return None
+                    } else {
+                        return Some(r)
+                    }
+                }
+            };
+        }
+    }
+
+    // Read number, failing if max_digits of number value exceeded
+    fn read_number(&mut self, radix: u8, max_digits: u32, upto: u32) -> Option<u32> {
+        self.read_atomically(|p| p.read_number_impl(radix, max_digits, upto))
+    }
+
+    fn read_ipv4_addr_impl(&mut self) -> Option<IpAddr> {
+        let mut bs = [0u8; 4];
+        let mut i = 0;
+        while i < 4 {
+            if i != 0 && self.read_given_char('.').is_none() {
+                return None;
+            }
+
+            let octet = self.read_number(10, 3, 0x100).map(|n| n as u8);
+            match octet {
+                Some(d) => bs[i] = d,
+                None => return None,
+            };
+            i += 1;
+        }
+        Some(Ipv4Addr(bs[0], bs[1], bs[2], bs[3]))
+    }
+
+    // Read IPv4 address
+    fn read_ipv4_addr(&mut self) -> Option<IpAddr> {
+        self.read_atomically(|p| p.read_ipv4_addr_impl())
+    }
+
+    fn read_ipv6_addr_impl(&mut self) -> Option<IpAddr> {
+        fn ipv6_addr_from_head_tail(head: &[u16], tail: &[u16]) -> IpAddr {
+            assert!(head.len() + tail.len() <= 8);
+            let mut gs = [0u16; 8];
+            gs.clone_from_slice(head);
+            gs[(8 - tail.len()) .. 8].clone_from_slice(tail);
+            Ipv6Addr(gs[0], gs[1], gs[2], gs[3], gs[4], gs[5], gs[6], gs[7])
+        }
+
+        fn read_groups(p: &mut Parser, groups: &mut [u16; 8], limit: uint) -> (uint, bool) {
+            let mut i = 0;
+            while i < limit {
+                if i < limit - 1 {
+                    let ipv4 = p.read_atomically(|p| {
+                        if i == 0 || p.read_given_char(':').is_some() {
+                            p.read_ipv4_addr()
+                        } else {
+                            None
+                        }
+                    });
+                    match ipv4 {
+                        Some(Ipv4Addr(a, b, c, d)) => {
+                            groups[i + 0] = ((a as u16) << 8) | (b as u16);
+                            groups[i + 1] = ((c as u16) << 8) | (d as u16);
+                            return (i + 2, true);
+                        }
+                        _ => {}
+                    }
+                }
+
+                let group = p.read_atomically(|p| {
+                    if i == 0 || p.read_given_char(':').is_some() {
+                        p.read_number(16, 4, 0x10000).map(|n| n as u16)
+                    } else {
+                        None
+                    }
+                });
+                match group {
+                    Some(g) => groups[i] = g,
+                    None => return (i, false)
+                }
+                i += 1;
+            }
+            (i, false)
+        }
+
+        let mut head = [0u16; 8];
+        let (head_size, head_ipv4) = read_groups(self, &mut head, 8);
+
+        if head_size == 8 {
+            return Some(Ipv6Addr(
+                head[0], head[1], head[2], head[3],
+                head[4], head[5], head[6], head[7]))
+        }
+
+        // IPv4 part is not allowed before `::`
+        if head_ipv4 {
+            return None
+        }
+
+        // read `::` if previous code parsed less than 8 groups
+        if !self.read_given_char(':').is_some() || !self.read_given_char(':').is_some() {
+            return None;
+        }
+
+        let mut tail = [0u16; 8];
+        let (tail_size, _) = read_groups(self, &mut tail, 8 - head_size);
+        Some(ipv6_addr_from_head_tail(&head[..head_size], &tail[..tail_size]))
+    }
+
+    fn read_ipv6_addr(&mut self) -> Option<IpAddr> {
+        self.read_atomically(|p| p.read_ipv6_addr_impl())
+    }
+
+    fn read_ip_addr(&mut self) -> Option<IpAddr> {
+        let ipv4_addr = |&mut: p: &mut Parser| p.read_ipv4_addr();
+        let ipv6_addr = |&mut: p: &mut Parser| p.read_ipv6_addr();
+        self.read_or(&mut [box ipv4_addr, box ipv6_addr])
+    }
+
+    fn read_socket_addr(&mut self) -> Option<SocketAddr> {
+        let ip_addr = |&: p: &mut Parser| {
+            let ipv4_p = |&mut: p: &mut Parser| p.read_ip_addr();
+            let ipv6_p = |&mut: p: &mut Parser| {
+                let open_br = |&: p: &mut Parser| p.read_given_char('[');
+                let ip_addr = |&: p: &mut Parser| p.read_ipv6_addr();
+                let clos_br = |&: p: &mut Parser| p.read_given_char(']');
+                p.read_seq_3::<char, IpAddr, char, _, _, _>(open_br, ip_addr, clos_br)
+                        .map(|t| match t { (_, ip, _) => ip })
+            };
+            p.read_or(&mut [box ipv4_p, box ipv6_p])
+        };
+        let colon = |&: p: &mut Parser| p.read_given_char(':');
+        let port  = |&: p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16);
+
+        // host, colon, port
+        self.read_seq_3::<IpAddr, char, u16, _, _, _>(ip_addr, colon, port)
+                .map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } })
+    }
+}
+
+impl FromStr for IpAddr {
+    fn from_str(s: &str) -> Option<IpAddr> {
+        Parser::new(s).read_till_eof(|p| p.read_ip_addr())
+    }
+}
+
+impl FromStr for SocketAddr {
+    fn from_str(s: &str) -> Option<SocketAddr> {
+        Parser::new(s).read_till_eof(|p| p.read_socket_addr())
+    }
+}
+
+/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values.
+///
+/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all`
+/// method, and its trivial counterpart will be available automatically.
+///
+/// This trait is used for generic address resolution when constructing network objects.
+/// By default it is implemented for the following types:
+///
+///  * `SocketAddr` - `to_socket_addr` is identity function.
+///
+///  * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially.
+///
+///  * `(&str, u16)` - the string should be either a string representation of an IP address
+///    expected by `FromStr` implementation for `IpAddr` or a host name.
+///
+///    For the former, `to_socket_addr_all` returns a vector with a single element corresponding
+///    to that IP address joined with the given port.
+///
+///    For the latter, it tries to resolve the host name and returns a vector of all IP addresses
+///    for the host name, each joined with the given port.
+///
+///  * `&str` - the string should be either a string representation of a `SocketAddr` as
+///    expected by its `FromStr` implementation or a string like `<host_name>:<port>` pair
+///    where `<port>` is a `u16` value.
+///
+///    For the former, `to_socket_addr_all` returns a vector with a single element corresponding
+///    to that socket address.
+///
+///    For the latter, it tries to resolve the host name and returns a vector of all IP addresses
+///    for the host name, each joined with the port.
+///
+///
+/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with
+/// values of various types for the bind/connection address. It is needed because sometimes
+/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"`
+/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes
+/// `SocketAddr` value is *the* main source of the address, and converting it to some other type
+/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods
+/// is pointless.
+///
+/// Some examples:
+///
+/// ```rust,no_run
+/// # #![allow(unused_must_use)]
+///
+/// use std::old_io::{TcpStream, TcpListener};
+/// use std::old_io::net::udp::UdpSocket;
+/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr};
+///
+/// fn main() {
+///     // The following lines are equivalent modulo possible "localhost" name resolution
+///     // differences
+///     let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 });
+///     let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345u16));
+///     let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16));
+///     let tcp_s = TcpStream::connect(("localhost", 12345u16));
+///     let tcp_s = TcpStream::connect("127.0.0.1:12345");
+///     let tcp_s = TcpStream::connect("localhost:12345");
+///
+///     // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly
+///     let tcp_l = TcpListener::bind("localhost:12345");
+///
+///     let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)).unwrap();
+///     udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16));
+/// }
+/// ```
+pub trait ToSocketAddr {
+    /// Converts this object to single socket address value.
+    ///
+    /// If more than one value is available, this method returns the first one. If no
+    /// values are available, this method returns an `IoError`.
+    ///
+    /// By default this method delegates to `to_socket_addr_all` method, taking the first
+    /// item from its result.
+    fn to_socket_addr(&self) -> IoResult<SocketAddr> {
+        self.to_socket_addr_all()
+            .and_then(|v| v.into_iter().next().ok_or_else(|| IoError {
+                kind: old_io::InvalidInput,
+                desc: "no address available",
+                detail: None
+            }))
+    }
+
+    /// Converts this object to all available socket address values.
+    ///
+    /// Some values like host name string naturally correspond to multiple IP addresses.
+    /// This method tries to return all available addresses corresponding to this object.
+    ///
+    /// By default this method delegates to `to_socket_addr` method, creating a singleton
+    /// vector from its result.
+    #[inline]
+    fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
+        self.to_socket_addr().map(|a| vec![a])
+    }
+}
+
+impl ToSocketAddr for SocketAddr {
+    #[inline]
+    fn to_socket_addr(&self) -> IoResult<SocketAddr> { Ok(*self) }
+}
+
+impl ToSocketAddr for (IpAddr, u16) {
+    #[inline]
+    fn to_socket_addr(&self) -> IoResult<SocketAddr> {
+        let (ip, port) = *self;
+        Ok(SocketAddr { ip: ip, port: port })
+    }
+}
+
+fn resolve_socket_addr(s: &str, p: u16) -> IoResult<Vec<SocketAddr>> {
+    net::get_host_addresses(s)
+        .map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect())
+}
+
+fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> {
+    macro_rules! try_opt {
+        ($e:expr, $msg:expr) => (
+            match $e {
+                Some(r) => r,
+                None => return Err(IoError {
+                    kind: old_io::InvalidInput,
+                    desc: $msg,
+                    detail: None
+                })
+            }
+        )
+    }
+
+    // split the string by ':' and convert the second part to u16
+    let mut parts_iter = s.rsplitn(2, ':');
+    let port_str = try_opt!(parts_iter.next(), "invalid socket address");
+    let host = try_opt!(parts_iter.next(), "invalid socket address");
+    let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value");
+    resolve_socket_addr(host, port)
+}
+
+impl<'a> ToSocketAddr for (&'a str, u16) {
+    fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
+        let (host, port) = *self;
+
+        // try to parse the host as a regular IpAddr first
+        match FromStr::from_str(host) {
+            Some(addr) => return Ok(vec![SocketAddr {
+                ip: addr,
+                port: port
+            }]),
+            None => {}
+        }
+
+        resolve_socket_addr(host, port)
+    }
+}
+
+// accepts strings like 'localhost:12345'
+impl<'a> ToSocketAddr for &'a str {
+    fn to_socket_addr(&self) -> IoResult<SocketAddr> {
+        // try to parse as a regular SocketAddr first
+        match FromStr::from_str(*self) {
+            Some(addr) => return Ok(addr),
+            None => {}
+        }
+
+        parse_and_resolve_socket_addr(*self)
+            .and_then(|v| v.into_iter().next()
+                .ok_or_else(|| IoError {
+                    kind: old_io::InvalidInput,
+                    desc: "no address available",
+                    detail: None
+                })
+            )
+    }
+
+    fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> {
+        // try to parse as a regular SocketAddr first
+        match FromStr::from_str(*self) {
+            Some(addr) => return Ok(vec![addr]),
+            None => {}
+        }
+
+        parse_and_resolve_socket_addr(*self)
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+    use super::*;
+    use str::FromStr;
+
+    #[test]
+    fn test_from_str_ipv4() {
+        assert_eq!(Some(Ipv4Addr(127, 0, 0, 1)), FromStr::from_str("127.0.0.1"));
+        assert_eq!(Some(Ipv4Addr(255, 255, 255, 255)), FromStr::from_str("255.255.255.255"));
+        assert_eq!(Some(Ipv4Addr(0, 0, 0, 0)), FromStr::from_str("0.0.0.0"));
+
+        // out of range
+        let none: Option<IpAddr> = FromStr::from_str("256.0.0.1");
+        assert_eq!(None, none);
+        // too short
+        let none: Option<IpAddr> = FromStr::from_str("255.0.0");
+        assert_eq!(None, none);
+        // too long
+        let none: Option<IpAddr> = FromStr::from_str("255.0.0.1.2");
+        assert_eq!(None, none);
+        // no number between dots
+        let none: Option<IpAddr> = FromStr::from_str("255.0..1");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("0:0:0:0:0:0:0:0"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("0:0:0:0:0:0:0:1"));
+
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1)), FromStr::from_str("::1"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 0)), FromStr::from_str("::"));
+
+        assert_eq!(Some(Ipv6Addr(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)),
+                FromStr::from_str("2a02:6b8::11:11"));
+
+        // too long group
+        let none: Option<IpAddr> = FromStr::from_str("::00000");
+        assert_eq!(None, none);
+        // too short
+        let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7");
+        assert_eq!(None, none);
+        // too long
+        let none: Option<IpAddr> = FromStr::from_str("1:2:3:4:5:6:7:8:9");
+        assert_eq!(None, none);
+        // triple colon
+        let none: Option<IpAddr> = FromStr::from_str("1:2:::6:7:8");
+        assert_eq!(None, none);
+        // two double colons
+        let none: Option<IpAddr> = FromStr::from_str("1:2::6::8");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_ipv4_in_ipv6() {
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)),
+                FromStr::from_str("::FFFF:192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)),
+                FromStr::from_str("64:ff9b::192.0.2.33"));
+        assert_eq!(Some(Ipv6Addr(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)),
+                FromStr::from_str("2001:db8:122:c000:2:2100:192.0.2.33"));
+
+        // colon after v4
+        let none: Option<IpAddr> = FromStr::from_str("::127.0.0.1:");
+        assert_eq!(None, none);
+        // not enough groups
+        let none: Option<IpAddr> = FromStr::from_str("1.2.3.4.5:127.0.0.1");
+        assert_eq!(None, none);
+        // too many groups
+        let none: Option<IpAddr> =
+            FromStr::from_str("1.2.3.4.5:6:7:127.0.0.1");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn test_from_str_socket_addr() {
+        assert_eq!(Some(SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 80 }),
+                FromStr::from_str("77.88.21.11:80"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }),
+                FromStr::from_str("[2a02:6b8:0:1::1]:53"));
+        assert_eq!(Some(SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0x7F00, 1), port: 22 }),
+                FromStr::from_str("[::127.0.0.1]:22"));
+
+        // without port
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1");
+        assert_eq!(None, none);
+        // without port
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:");
+        assert_eq!(None, none);
+        // wrong brackets around v4
+        let none: Option<SocketAddr> = FromStr::from_str("[127.0.0.1]:22");
+        assert_eq!(None, none);
+        // port out of range
+        let none: Option<SocketAddr> = FromStr::from_str("127.0.0.1:123456");
+        assert_eq!(None, none);
+    }
+
+    #[test]
+    fn ipv6_addr_to_string() {
+        let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280);
+        assert!(a1.to_string() == "::ffff:192.0.2.128" ||
+                a1.to_string() == "::FFFF:192.0.2.128");
+        assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(),
+                   "8:9:a:b:c:d:e:f");
+    }
+
+    #[test]
+    fn to_socket_addr_socketaddr() {
+        let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 };
+        assert_eq!(Ok(a), a.to_socket_addr());
+        assert_eq!(Ok(vec![a]), a.to_socket_addr_all());
+    }
+
+    #[test]
+    fn to_socket_addr_ipaddr_u16() {
+        let a = Ipv4Addr(77, 88, 21, 11);
+        let p = 12345u16;
+        let e = SocketAddr { ip: a, port: p };
+        assert_eq!(Ok(e), (a, p).to_socket_addr());
+        assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all());
+    }
+
+    #[test]
+    fn to_socket_addr_str_u16() {
+        let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
+        assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr());
+        assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all());
+
+        let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
+        assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr());
+        assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all());
+
+        let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
+        assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a));
+    }
+
+    #[test]
+    fn to_socket_addr_str() {
+        let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 };
+        assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr());
+        assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all());
+
+        let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 };
+        assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr());
+        assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all());
+
+        let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 };
+        assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a));
+    }
+}
diff --git a/src/libstd/old_io/net/mod.rs b/src/libstd/old_io/net/mod.rs
new file mode 100644
index 00000000000..d8394aa8b6a
--- /dev/null
+++ b/src/libstd/old_io/net/mod.rs
@@ -0,0 +1,46 @@
+// 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.
+
+//! Networking I/O
+
+use old_io::{IoError, IoResult, InvalidInput};
+use ops::FnMut;
+use option::Option::None;
+use result::Result::{Ok, Err};
+use self::ip::{SocketAddr, ToSocketAddr};
+
+pub use self::addrinfo::get_host_addresses;
+
+pub mod addrinfo;
+pub mod tcp;
+pub mod udp;
+pub mod ip;
+pub mod pipe;
+
+fn with_addresses<A, T, F>(addr: A, mut action: F) -> IoResult<T> where
+    A: ToSocketAddr,
+    F: FnMut(SocketAddr) -> IoResult<T>,
+{
+    const DEFAULT_ERROR: IoError = IoError {
+        kind: InvalidInput,
+        desc: "no addresses found for hostname",
+        detail: None
+    };
+
+    let addresses = try!(addr.to_socket_addr_all());
+    let mut err = DEFAULT_ERROR;
+    for addr in addresses.into_iter() {
+        match action(addr) {
+            Ok(r) => return Ok(r),
+            Err(e) => err = e
+        }
+    }
+    Err(err)
+}
diff --git a/src/libstd/old_io/net/pipe.rs b/src/libstd/old_io/net/pipe.rs
new file mode 100644
index 00000000000..71b77adcd96
--- /dev/null
+++ b/src/libstd/old_io/net/pipe.rs
@@ -0,0 +1,869 @@
+// 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.
+
+//! Named pipes
+//!
+//! This module contains the ability to communicate over named pipes with
+//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe,
+//! while on Unix it corresponds to UNIX domain sockets.
+//!
+//! These pipes are similar to TCP in the sense that you can have both a stream to a
+//! server and a server itself. The server provided accepts other `UnixStream`
+//! instances as clients.
+
+#![allow(missing_docs)]
+
+use prelude::v1::*;
+
+use ffi::CString;
+use path::BytesContainer;
+use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
+use sys::pipe::UnixAcceptor as UnixAcceptorImp;
+use sys::pipe::UnixListener as UnixListenerImp;
+use sys::pipe::UnixStream as UnixStreamImp;
+use time::Duration;
+
+use sys_common;
+
+/// A stream which communicates over a named pipe.
+pub struct UnixStream {
+    inner: UnixStreamImp,
+}
+
+impl UnixStream {
+
+    /// Connect to a pipe named by `path`. This will attempt to open a
+    /// connection to the underlying socket.
+    ///
+    /// The returned stream will be closed when the object falls out of scope.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// # #![allow(unused_must_use)]
+    /// use std::old_io::net::pipe::UnixStream;
+    ///
+    /// let server = Path::new("path/to/my/socket");
+    /// let mut stream = UnixStream::connect(&server);
+    /// stream.write(&[1, 2, 3]);
+    /// ```
+    pub fn connect<P: BytesContainer>(path: P) -> IoResult<UnixStream> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, None)
+            .map(|inner| UnixStream { inner: inner })
+    }
+
+    /// Connect to a pipe named by `path`, timing out if the specified number of
+    /// milliseconds.
+    ///
+    /// This function is similar to `connect`, except that if `timeout`
+    /// elapses the function will return an error of kind `TimedOut`.
+    ///
+    /// If a `timeout` with zero or negative duration is specified then
+    /// the function returns `Err`, with the error kind set to `TimedOut`.
+    #[unstable(feature = "io",
+               reason = "the timeout argument is likely to change types")]
+    pub fn connect_timeout<P>(path: P, timeout: Duration)
+                              -> IoResult<UnixStream>
+                              where P: BytesContainer {
+        if timeout <= Duration::milliseconds(0) {
+            return Err(standard_error(TimedOut));
+        }
+
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixStreamImp::connect(&path, Some(timeout.num_milliseconds() as u64))
+            .map(|inner| UnixStream { inner: inner })
+    }
+
+
+    /// Closes the reading half of this connection.
+    ///
+    /// This method will close the reading portion of this connection, causing
+    /// all pending and future reads to immediately return with an error.
+    ///
+    /// Note that this method affects all cloned handles associated with this
+    /// stream, not just this one handle.
+    pub fn close_read(&mut self) -> IoResult<()> {
+        self.inner.close_read()
+    }
+
+    /// Closes the writing half of this connection.
+    ///
+    /// This method will close the writing portion of this connection, causing
+    /// all pending and future writes to immediately return with an error.
+    ///
+    /// Note that this method affects all cloned handles associated with this
+    /// stream, not just this one handle.
+    pub fn close_write(&mut self) -> IoResult<()> {
+        self.inner.close_write()
+    }
+
+    /// Sets the read/write timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_timeout(timeout_ms)
+    }
+
+    /// Sets the read timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_read_timeout(timeout_ms)
+    }
+
+    /// Sets the write timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_write_timeout(timeout_ms)
+    }
+}
+
+impl Clone for UnixStream {
+    fn clone(&self) -> UnixStream {
+        UnixStream { inner: self.inner.clone() }
+    }
+}
+
+impl Reader for UnixStream {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.read(buf)
+    }
+}
+
+impl Writer for UnixStream {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner.write(buf)
+    }
+}
+
+impl sys_common::AsInner<UnixStreamImp> for UnixStream {
+    fn as_inner(&self) -> &UnixStreamImp {
+        &self.inner
+    }
+}
+
+/// A value that can listen for incoming named pipe connection requests.
+pub struct UnixListener {
+    /// The internal, opaque runtime Unix listener.
+    inner: UnixListenerImp,
+}
+
+impl UnixListener {
+    /// Creates a new listener, ready to receive incoming connections on the
+    /// specified socket. The server will be named by `path`.
+    ///
+    /// This listener will be closed when it falls out of scope.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # fn foo() {
+    /// use std::old_io::net::pipe::UnixListener;
+    /// use std::old_io::{Listener, Acceptor};
+    ///
+    /// let server = Path::new("/path/to/my/socket");
+    /// let stream = UnixListener::bind(&server);
+    /// for mut client in stream.listen().incoming() {
+    ///     client.write(&[1, 2, 3, 4]);
+    /// }
+    /// # }
+    /// ```
+    pub fn bind<P: BytesContainer>(path: P) -> IoResult<UnixListener> {
+        let path = CString::from_slice(path.container_as_bytes());
+        UnixListenerImp::bind(&path)
+            .map(|inner| UnixListener { inner: inner })
+    }
+}
+
+impl Listener<UnixStream, UnixAcceptor> for UnixListener {
+    fn listen(self) -> IoResult<UnixAcceptor> {
+        self.inner.listen()
+            .map(|inner| UnixAcceptor { inner: inner })
+    }
+}
+
+impl sys_common::AsInner<UnixListenerImp> for UnixListener {
+    fn as_inner(&self) -> &UnixListenerImp {
+        &self.inner
+    }
+}
+
+/// A value that can accept named pipe connections, returned from `listen()`.
+pub struct UnixAcceptor {
+    /// The internal, opaque runtime Unix acceptor.
+    inner: UnixAcceptorImp
+}
+
+impl UnixAcceptor {
+    /// Sets a timeout for this acceptor, after which accept() will no longer
+    /// block indefinitely.
+    ///
+    /// The argument specified is the amount of time, in milliseconds, into the
+    /// future after which all invocations of accept() will not block (and any
+    /// pending invocation will return). A value of `None` will clear any
+    /// existing timeout.
+    ///
+    /// When using this method, it is likely necessary to reset the timeout as
+    /// appropriate, the timeout specified is specific to this object, not
+    /// specific to the next request.
+    #[unstable(feature = "io",
+               reason = "the name and arguments to this function are likely \
+                         to change")]
+    pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_timeout(timeout_ms)
+    }
+
+    /// Closes the accepting capabilities of this acceptor.
+    ///
+    /// This function has the same semantics as `TcpAcceptor::close_accept`, and
+    /// more information can be found in that documentation.
+    #[unstable(feature = "io")]
+    pub fn close_accept(&mut self) -> IoResult<()> {
+        self.inner.close_accept()
+    }
+}
+
+impl Acceptor<UnixStream> for UnixAcceptor {
+    fn accept(&mut self) -> IoResult<UnixStream> {
+        self.inner.accept().map(|s| {
+            UnixStream { inner: s }
+        })
+    }
+}
+
+impl Clone for UnixAcceptor {
+    /// Creates a new handle to this unix acceptor, allowing for simultaneous
+    /// accepts.
+    ///
+    /// The underlying unix acceptor will not be closed until all handles to the
+    /// acceptor have been deallocated. Incoming connections will be received on
+    /// at most once acceptor, the same connection will not be accepted twice.
+    ///
+    /// The `close_accept` method will shut down *all* acceptors cloned from the
+    /// same original acceptor, whereas the `set_timeout` method only affects
+    /// the selector that it is called on.
+    ///
+    /// This function is useful for creating a handle to invoke `close_accept`
+    /// on to wake up any other task blocked in `accept`.
+    fn clone(&self) -> UnixAcceptor {
+        UnixAcceptor { inner: self.inner.clone() }
+    }
+}
+
+impl sys_common::AsInner<UnixAcceptorImp> for UnixAcceptor {
+    fn as_inner(&self) -> &UnixAcceptorImp {
+        &self.inner
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::v1::*;
+
+    use old_io::fs::PathExtensions;
+    use old_io::{EndOfFile, TimedOut, ShortWrite, IoError, ConnectionReset};
+    use old_io::{NotConnected, BrokenPipe, FileNotFound, InvalidInput, OtherIoError};
+    use old_io::{PermissionDenied, Acceptor, Listener};
+    use old_io::test::*;
+    use super::*;
+    use sync::mpsc::channel;
+    use thread::Thread;
+    use time::Duration;
+
+    pub fn smalltest<F,G>(server: F, client: G)
+        where F : FnOnce(UnixStream), F : Send,
+              G : FnOnce(UnixStream), G : Send
+    {
+        let path1 = next_test_unix();
+        let path2 = path1.clone();
+
+        let mut acceptor = UnixListener::bind(&path1).listen();
+
+        let _t = Thread::spawn(move|| {
+            match UnixStream::connect(&path2) {
+                Ok(c) => client(c),
+                Err(e) => panic!("failed connect: {}", e),
+            }
+        });
+
+        match acceptor.accept() {
+            Ok(c) => server(c),
+            Err(e) => panic!("failed accept: {}", e),
+        }
+    }
+
+    #[test]
+    fn bind_error() {
+        let path = "path/to/nowhere";
+        match UnixListener::bind(&path) {
+            Ok(..) => panic!(),
+            Err(e) => {
+                assert!(e.kind == PermissionDenied || e.kind == FileNotFound ||
+                        e.kind == InvalidInput);
+            }
+        }
+    }
+
+    #[test]
+    fn connect_error() {
+        let path = if cfg!(windows) {
+            r"\\.\pipe\this_should_not_exist_ever"
+        } else {
+            "path/to/nowhere"
+        };
+        match UnixStream::connect(&path) {
+            Ok(..) => panic!(),
+            Err(e) => {
+                assert!(e.kind == FileNotFound || e.kind == OtherIoError);
+            }
+        }
+    }
+
+    #[test]
+    fn smoke() {
+        smalltest(move |mut server| {
+            let mut buf = [0];
+            server.read(&mut buf).unwrap();
+            assert!(buf[0] == 99);
+        }, move|mut client| {
+            client.write(&[99]).unwrap();
+        })
+    }
+
+    #[cfg_attr(windows, ignore)] // FIXME(#12516)
+    #[test]
+    fn read_eof() {
+        smalltest(move|mut server| {
+            let mut buf = [0];
+            assert!(server.read(&mut buf).is_err());
+            assert!(server.read(&mut buf).is_err());
+        }, move|_client| {
+            // drop the client
+        })
+    }
+
+    #[test]
+    fn write_begone() {
+        smalltest(move|mut server| {
+            let buf = [0];
+            loop {
+                match server.write(&buf) {
+                    Ok(..) => {}
+                    Err(e) => {
+                        assert!(e.kind == BrokenPipe ||
+                                e.kind == NotConnected ||
+                                e.kind == ConnectionReset,
+                                "unknown error {}", e);
+                        break;
+                    }
+                }
+            }
+        }, move|_client| {
+            // drop the client
+        })
+    }
+
+    #[test]
+    fn accept_lots() {
+        let times = 10;
+        let path1 = next_test_unix();
+        let path2 = path1.clone();
+
+        let mut acceptor = match UnixListener::bind(&path1).listen() {
+            Ok(a) => a,
+            Err(e) => panic!("failed listen: {}", e),
+        };
+
+        let _t = Thread::spawn(move|| {
+            for _ in range(0u, times) {
+                let mut stream = UnixStream::connect(&path2);
+                match stream.write(&[100]) {
+                    Ok(..) => {}
+                    Err(e) => panic!("failed write: {}", e)
+                }
+            }
+        });
+
+        for _ in range(0, times) {
+            let mut client = acceptor.accept();
+            let mut buf = [0];
+            match client.read(&mut buf) {
+                Ok(..) => {}
+                Err(e) => panic!("failed read/accept: {}", e),
+            }
+            assert_eq!(buf[0], 100);
+        }
+    }
+
+    #[cfg(unix)]
+    #[test]
+    fn path_exists() {
+        let path = next_test_unix();
+        let _acceptor = UnixListener::bind(&path).listen();
+        assert!(path.exists());
+    }
+
+    #[test]
+    fn unix_clone_smoke() {
+        let addr = next_test_unix();
+        let mut acceptor = UnixListener::bind(&addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr);
+            let mut buf = [0, 0];
+            debug!("client reading");
+            assert_eq!(s.read(&mut buf), Ok(1));
+            assert_eq!(buf[0], 1);
+            debug!("client writing");
+            s.write(&[2]).unwrap();
+            debug!("client dropping");
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (tx1, rx1) = channel();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            rx1.recv().unwrap();
+            debug!("writer writing");
+            s2.write(&[1]).unwrap();
+            debug!("writer done");
+            tx2.send(()).unwrap();
+        });
+        tx1.send(()).unwrap();
+        let mut buf = [0, 0];
+        debug!("reader reading");
+        assert_eq!(s1.read(&mut buf), Ok(1));
+        debug!("reader done");
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn unix_clone_two_read() {
+        let addr = next_test_unix();
+        let mut acceptor = UnixListener::bind(&addr).listen();
+        let (tx1, rx) = channel();
+        let tx2 = tx1.clone();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr);
+            s.write(&[1]).unwrap();
+            rx.recv().unwrap();
+            s.write(&[2]).unwrap();
+            rx.recv().unwrap();
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (done, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            let mut buf = [0, 0];
+            s2.read(&mut buf).unwrap();
+            tx2.send(()).unwrap();
+            done.send(()).unwrap();
+        });
+        let mut buf = [0, 0];
+        s1.read(&mut buf).unwrap();
+        tx1.send(()).unwrap();
+
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn unix_clone_two_write() {
+        let addr = next_test_unix();
+        let mut acceptor = UnixListener::bind(&addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr);
+            let buf = &mut [0, 1];
+            s.read(buf).unwrap();
+            s.read(buf).unwrap();
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            s2.write(&[1]).unwrap();
+            tx.send(()).unwrap();
+        });
+        s1.write(&[2]).unwrap();
+
+        rx.recv().unwrap();
+    }
+
+    #[cfg(not(windows))]
+    #[test]
+    fn drop_removes_listener_path() {
+        let path = next_test_unix();
+        let l = UnixListener::bind(&path).unwrap();
+        assert!(path.exists());
+        drop(l);
+        assert!(!path.exists());
+    }
+
+    #[cfg(not(windows))]
+    #[test]
+    fn drop_removes_acceptor_path() {
+        let path = next_test_unix();
+        let l = UnixListener::bind(&path).unwrap();
+        assert!(path.exists());
+        drop(l.listen().unwrap());
+        assert!(!path.exists());
+    }
+
+    #[test]
+    fn accept_timeout() {
+        let addr = next_test_unix();
+        let mut a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+
+        a.set_timeout(Some(10));
+
+        // Make sure we time out once and future invocations also time out
+        let err = a.accept().err().unwrap();
+        assert_eq!(err.kind, TimedOut);
+        let err = a.accept().err().unwrap();
+        assert_eq!(err.kind, TimedOut);
+
+        // Also make sure that even though the timeout is expired that we will
+        // continue to receive any pending connections.
+        let (tx, rx) = channel();
+        let addr2 = addr.clone();
+        let _t = Thread::spawn(move|| {
+            tx.send(UnixStream::connect(&addr2).unwrap()).unwrap();
+        });
+        let l = rx.recv().unwrap();
+        for i in range(0u, 1001) {
+            match a.accept() {
+                Ok(..) => break,
+                Err(ref e) if e.kind == TimedOut => {}
+                Err(e) => panic!("error: {}", e),
+            }
+            ::thread::Thread::yield_now();
+            if i == 1000 { panic!("should have a pending connection") }
+        }
+        drop(l);
+
+        // Unset the timeout and make sure that this always blocks.
+        a.set_timeout(None);
+        let addr2 = addr.clone();
+        let _t = Thread::spawn(move|| {
+            drop(UnixStream::connect(&addr2).unwrap());
+        });
+        a.accept().unwrap();
+    }
+
+    #[test]
+    fn connect_timeout_error() {
+        let addr = next_test_unix();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_err());
+    }
+
+    #[test]
+    fn connect_timeout_success() {
+        let addr = next_test_unix();
+        let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(100)).is_ok());
+    }
+
+    #[test]
+    fn connect_timeout_zero() {
+        let addr = next_test_unix();
+        let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(0)).is_err());
+    }
+
+    #[test]
+    fn connect_timeout_negative() {
+        let addr = next_test_unix();
+        let _a = UnixListener::bind(&addr).unwrap().listen().unwrap();
+        assert!(UnixStream::connect_timeout(&addr, Duration::milliseconds(-1)).is_err());
+    }
+
+    #[test]
+    fn close_readwrite_smoke() {
+        let addr = next_test_unix();
+        let a = UnixListener::bind(&addr).listen().unwrap();
+        let (_tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut a = a;
+            let _s = a.accept().unwrap();
+            let _ = rx.recv();
+        });
+
+        let mut b = [0];
+        let mut s = UnixStream::connect(&addr).unwrap();
+        let mut s2 = s.clone();
+
+        // closing should prevent reads/writes
+        s.close_write().unwrap();
+        assert!(s.write(&[0]).is_err());
+        s.close_read().unwrap();
+        assert!(s.read(&mut b).is_err());
+
+        // closing should affect previous handles
+        assert!(s2.write(&[0]).is_err());
+        assert!(s2.read(&mut b).is_err());
+
+        // closing should affect new handles
+        let mut s3 = s.clone();
+        assert!(s3.write(&[0]).is_err());
+        assert!(s3.read(&mut b).is_err());
+
+        // make sure these don't die
+        let _ = s2.close_read();
+        let _ = s2.close_write();
+        let _ = s3.close_read();
+        let _ = s3.close_write();
+    }
+
+    #[test]
+    fn close_read_wakes_up() {
+        let addr = next_test_unix();
+        let a = UnixListener::bind(&addr).listen().unwrap();
+        let (_tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut a = a;
+            let _s = a.accept().unwrap();
+            let _ = rx.recv();
+        });
+
+        let mut s = UnixStream::connect(&addr).unwrap();
+        let s2 = s.clone();
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            assert!(s2.read(&mut [0]).is_err());
+            tx.send(()).unwrap();
+        });
+        // this should wake up the child task
+        s.close_read().unwrap();
+
+        // this test will never finish if the child doesn't wake up
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn readwrite_timeouts() {
+        let addr = next_test_unix();
+        let mut a = UnixListener::bind(&addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr).unwrap();
+            rx.recv().unwrap();
+            assert!(s.write(&[0]).is_ok());
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+
+        s.set_timeout(Some(20));
+        for i in range(0u, 1001) {
+            match s.write(&[0; 128 * 1024]) {
+                Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
+                Err(IoError { kind: TimedOut, .. }) => break,
+                Err(e) => panic!("{}", e),
+           }
+           if i == 1000 { panic!("should have filled up?!"); }
+        }
+
+        // I'm not sure as to why, but apparently the write on windows always
+        // succeeds after the previous timeout. Who knows?
+        if !cfg!(windows) {
+            assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut);
+        }
+
+        tx.send(()).unwrap();
+        s.set_timeout(None);
+        assert_eq!(s.read(&mut [0, 0]), Ok(1));
+    }
+
+    #[test]
+    fn read_timeouts() {
+        let addr = next_test_unix();
+        let mut a = UnixListener::bind(&addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr).unwrap();
+            rx.recv().unwrap();
+            let mut amt = 0;
+            while amt < 100 * 128 * 1024 {
+                match s.read(&mut [0;128 * 1024]) {
+                    Ok(n) => { amt += n; }
+                    Err(e) => panic!("{}", e),
+                }
+            }
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_read_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+
+        tx.send(()).unwrap();
+        for _ in range(0u, 100) {
+            assert!(s.write(&[0;128 * 1024]).is_ok());
+        }
+    }
+
+    #[test]
+    fn write_timeouts() {
+        let addr = next_test_unix();
+        let mut a = UnixListener::bind(&addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr).unwrap();
+            rx.recv().unwrap();
+            assert!(s.write(&[0]).is_ok());
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_write_timeout(Some(20));
+        for i in range(0u, 1001) {
+            match s.write(&[0; 128 * 1024]) {
+                Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
+                Err(IoError { kind: TimedOut, .. }) => break,
+                Err(e) => panic!("{}", e),
+           }
+           if i == 1000 { panic!("should have filled up?!"); }
+        }
+
+        tx.send(()).unwrap();
+        assert!(s.read(&mut [0]).is_ok());
+    }
+
+    #[test]
+    fn timeout_concurrent_read() {
+        let addr = next_test_unix();
+        let mut a = UnixListener::bind(&addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = UnixStream::connect(&addr).unwrap();
+            rx.recv().unwrap();
+            assert!(s.write(&[0]).is_ok());
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        let s2 = s.clone();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            assert!(s2.read(&mut [0]).is_ok());
+            tx2.send(()).unwrap();
+        });
+
+        s.set_read_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        tx.send(()).unwrap();
+
+        rx2.recv().unwrap();
+    }
+
+    #[cfg(not(windows))]
+    #[test]
+    fn clone_accept_smoke() {
+        let addr = next_test_unix();
+        let l = UnixListener::bind(&addr);
+        let mut a = l.listen().unwrap();
+        let mut a2 = a.clone();
+
+        let addr2 = addr.clone();
+        let _t = Thread::spawn(move|| {
+            let _ = UnixStream::connect(&addr2);
+        });
+        let _t = Thread::spawn(move|| {
+            let _ = UnixStream::connect(&addr);
+        });
+
+        assert!(a.accept().is_ok());
+        drop(a);
+        assert!(a2.accept().is_ok());
+    }
+
+    #[cfg(not(windows))] // FIXME #17553
+    #[test]
+    fn clone_accept_concurrent() {
+        let addr = next_test_unix();
+        let l = UnixListener::bind(&addr);
+        let a = l.listen().unwrap();
+        let a2 = a.clone();
+
+        let (tx, rx) = channel();
+        let tx2 = tx.clone();
+
+        let _t = Thread::spawn(move|| {
+            let mut a = a;
+            tx.send(a.accept()).unwrap()
+        });
+        let _t = Thread::spawn(move|| {
+            let mut a = a2;
+            tx2.send(a.accept()).unwrap()
+        });
+
+        let addr2 = addr.clone();
+        let _t = Thread::spawn(move|| {
+            let _ = UnixStream::connect(&addr2);
+        });
+        let _t = Thread::spawn(move|| {
+            let _ = UnixStream::connect(&addr);
+        });
+
+        assert!(rx.recv().unwrap().is_ok());
+        assert!(rx.recv().unwrap().is_ok());
+    }
+
+    #[test]
+    fn close_accept_smoke() {
+        let addr = next_test_unix();
+        let l = UnixListener::bind(&addr);
+        let mut a = l.listen().unwrap();
+
+        a.close_accept().unwrap();
+        assert_eq!(a.accept().err().unwrap().kind, EndOfFile);
+    }
+
+    #[test]
+    fn close_accept_concurrent() {
+        let addr = next_test_unix();
+        let l = UnixListener::bind(&addr);
+        let a = l.listen().unwrap();
+        let mut a2 = a.clone();
+
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut a = a;
+            tx.send(a.accept()).unwrap();
+        });
+        a2.close_accept().unwrap();
+
+        assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile);
+    }
+}
diff --git a/src/libstd/old_io/net/tcp.rs b/src/libstd/old_io/net/tcp.rs
new file mode 100644
index 00000000000..1e76bb3ab0d
--- /dev/null
+++ b/src/libstd/old_io/net/tcp.rs
@@ -0,0 +1,1477 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! TCP network connections
+//!
+//! This module contains the ability to open a TCP stream to a socket address,
+//! as well as creating a socket server to accept incoming connections. The
+//! destination and binding addresses can either be an IPv4 or IPv6 address.
+//!
+//! A TCP connection implements the `Reader` and `Writer` traits, while the TCP
+//! listener (socket server) implements the `Listener` and `Acceptor` traits.
+
+use clone::Clone;
+use old_io::IoResult;
+use result::Result::Err;
+use old_io::net::ip::{SocketAddr, ToSocketAddr};
+use old_io::{Reader, Writer, Listener, Acceptor};
+use old_io::{standard_error, TimedOut};
+use option::Option;
+use option::Option::{None, Some};
+use time::Duration;
+
+use sys::tcp::TcpStream as TcpStreamImp;
+use sys::tcp::TcpListener as TcpListenerImp;
+use sys::tcp::TcpAcceptor as TcpAcceptorImp;
+
+use sys_common;
+
+/// A structure which represents a TCP stream between a local socket and a
+/// remote socket.
+///
+/// The socket will be closed when the value is dropped.
+///
+/// # Example
+///
+/// ```no_run
+/// use std::old_io::TcpStream;
+///
+/// {
+///     let mut stream = TcpStream::connect("127.0.0.1:34254");
+///
+///     // ignore the Result
+///     let _ = stream.write(&[1]);
+///
+///     let mut buf = [0];
+///     let _ = stream.read(&mut buf); // ignore here too
+/// } // the stream is closed here
+/// ```
+pub struct TcpStream {
+    inner: TcpStreamImp,
+}
+
+impl TcpStream {
+    fn new(s: TcpStreamImp) -> TcpStream {
+        TcpStream { inner: s }
+    }
+
+    /// Open a TCP connection to a remote host.
+    ///
+    /// `addr` is an address of the remote host. Anything which implements `ToSocketAddr`
+    /// trait can be supplied for the address; see this trait documentation for
+    /// concrete examples.
+    pub fn connect<A: ToSocketAddr>(addr: A) -> IoResult<TcpStream> {
+        super::with_addresses(addr, |addr| {
+            TcpStreamImp::connect(addr, None).map(TcpStream::new)
+        })
+    }
+
+    /// Creates a TCP connection to a remote socket address, timing out after
+    /// the specified duration.
+    ///
+    /// This is the same as the `connect` method, except that if the timeout
+    /// specified elapses before a connection is made an error will be
+    /// returned. The error's kind will be `TimedOut`.
+    ///
+    /// Same as the `connect` method, `addr` argument type can be anything which
+    /// implements `ToSocketAddr` trait.
+    ///
+    /// If a `timeout` with zero or negative duration is specified then
+    /// the function returns `Err`, with the error kind set to `TimedOut`.
+    #[unstable(feature = "io",
+               reason = "the timeout argument may eventually change types")]
+    pub fn connect_timeout<A: ToSocketAddr>(addr: A,
+                                            timeout: Duration) -> IoResult<TcpStream> {
+        if timeout <= Duration::milliseconds(0) {
+            return Err(standard_error(TimedOut));
+        }
+
+        super::with_addresses(addr, |addr| {
+            TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64))
+                .map(TcpStream::new)
+        })
+    }
+
+    /// Returns the socket address of the remote peer of this TCP connection.
+    pub fn peer_name(&mut self) -> IoResult<SocketAddr> {
+        self.inner.peer_name()
+    }
+
+    /// Returns the socket address of the local half of this TCP connection.
+    pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
+        self.inner.socket_name()
+    }
+
+    /// Sets the nodelay flag on this connection to the boolean specified
+    #[unstable(feature = "io")]
+    pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
+        self.inner.set_nodelay(nodelay)
+    }
+
+    /// Sets the keepalive timeout to the timeout specified.
+    ///
+    /// If the value specified is `None`, then the keepalive flag is cleared on
+    /// this connection. Otherwise, the keepalive timeout will be set to the
+    /// specified time, in seconds.
+    #[unstable(feature = "io")]
+    pub fn set_keepalive(&mut self, delay_in_seconds: Option<uint>) -> IoResult<()> {
+        self.inner.set_keepalive(delay_in_seconds)
+    }
+
+    /// Closes the reading half of this connection.
+    ///
+    /// This method will close the reading portion of this connection, causing
+    /// all pending and future reads to immediately return with an error.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// # #![allow(unused_must_use)]
+    /// use std::old_io::timer;
+    /// use std::old_io::TcpStream;
+    /// use std::time::Duration;
+    /// use std::thread::Thread;
+    ///
+    /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();
+    /// let stream2 = stream.clone();
+    ///
+    /// let _t = Thread::spawn(move|| {
+    ///     // close this stream after one second
+    ///     timer::sleep(Duration::seconds(1));
+    ///     let mut stream = stream2;
+    ///     stream.close_read();
+    /// });
+    ///
+    /// // wait for some data, will get canceled after one second
+    /// let mut buf = [0];
+    /// stream.read(&mut buf);
+    /// ```
+    ///
+    /// Note that this method affects all cloned handles associated with this
+    /// stream, not just this one handle.
+    pub fn close_read(&mut self) -> IoResult<()> {
+        self.inner.close_read()
+    }
+
+    /// Closes the writing half of this connection.
+    ///
+    /// This method will close the writing portion of this connection, causing
+    /// all future writes to immediately return with an error.
+    ///
+    /// Note that this method affects all cloned handles associated with this
+    /// stream, not just this one handle.
+    pub fn close_write(&mut self) -> IoResult<()> {
+        self.inner.close_write()
+    }
+
+    /// Sets a timeout, in milliseconds, for blocking operations on this stream.
+    ///
+    /// This function will set a timeout for all blocking operations (including
+    /// reads and writes) on this stream. The timeout specified is a relative
+    /// time, in milliseconds, into the future after which point operations will
+    /// time out. This means that the timeout must be reset periodically to keep
+    /// it from expiring. Specifying a value of `None` will clear the timeout
+    /// for this stream.
+    ///
+    /// The timeout on this stream is local to this stream only. Setting a
+    /// timeout does not affect any other cloned instances of this stream, nor
+    /// does the timeout propagated to cloned handles of this stream. Setting
+    /// this timeout will override any specific read or write timeouts
+    /// previously set for this stream.
+    ///
+    /// For clarification on the semantics of interrupting a read and a write,
+    /// take a look at `set_read_timeout` and `set_write_timeout`.
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_timeout(timeout_ms)
+    }
+
+    /// Sets the timeout for read operations on this stream.
+    ///
+    /// See documentation in `set_timeout` for the semantics of this read time.
+    /// This will overwrite any previous read timeout set through either this
+    /// function or `set_timeout`.
+    ///
+    /// # Errors
+    ///
+    /// When this timeout expires, if there is no pending read operation, no
+    /// action is taken. Otherwise, the read operation will be scheduled to
+    /// promptly return. If a timeout error is returned, then no data was read
+    /// during the timeout period.
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_read_timeout(timeout_ms)
+    }
+
+    /// Sets the timeout for write operations on this stream.
+    ///
+    /// See documentation in `set_timeout` for the semantics of this write time.
+    /// This will overwrite any previous write timeout set through either this
+    /// function or `set_timeout`.
+    ///
+    /// # Errors
+    ///
+    /// When this timeout expires, if there is no pending write operation, no
+    /// action is taken. Otherwise, the pending write operation will be
+    /// scheduled to promptly return. The actual state of the underlying stream
+    /// is not specified.
+    ///
+    /// The write operation may return an error of type `ShortWrite` which
+    /// indicates that the object is known to have written an exact number of
+    /// bytes successfully during the timeout period, and the remaining bytes
+    /// were never written.
+    ///
+    /// If the write operation returns `TimedOut`, then it the timeout primitive
+    /// does not know how many bytes were written as part of the timeout
+    /// operation. It may be the case that bytes continue to be written in an
+    /// asynchronous fashion after the call to write returns.
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_write_timeout(timeout_ms)
+    }
+}
+
+impl Clone for TcpStream {
+    /// Creates a new handle to this TCP stream, allowing for simultaneous reads
+    /// and writes of this connection.
+    ///
+    /// The underlying TCP stream will not be closed until all handles to the
+    /// stream have been deallocated. All handles will also follow the same
+    /// stream, but two concurrent reads will not receive the same data.
+    /// Instead, the first read will receive the first packet received, and the
+    /// second read will receive the second packet.
+    fn clone(&self) -> TcpStream {
+        TcpStream { inner: self.inner.clone() }
+    }
+}
+
+impl Reader for TcpStream {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.read(buf)
+    }
+}
+
+impl Writer for TcpStream {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner.write(buf)
+    }
+}
+
+impl sys_common::AsInner<TcpStreamImp> for TcpStream {
+    fn as_inner(&self) -> &TcpStreamImp {
+        &self.inner
+    }
+}
+
+/// A structure representing a socket server. This listener is used to create a
+/// `TcpAcceptor` which can be used to accept sockets on a local port.
+///
+/// # Examples
+///
+/// ```
+/// # fn foo() {
+/// use std::old_io::{TcpListener, TcpStream};
+/// use std::old_io::{Acceptor, Listener};
+/// use std::thread::Thread;
+///
+/// let listener = TcpListener::bind("127.0.0.1:80").unwrap();
+///
+/// // bind the listener to the specified address
+/// let mut acceptor = listener.listen().unwrap();
+///
+/// fn handle_client(mut stream: TcpStream) {
+///     // ...
+/// # &mut stream; // silence unused mutability/variable warning
+/// }
+/// // accept connections and process them, spawning a new tasks for each one
+/// for stream in acceptor.incoming() {
+///     match stream {
+///         Err(e) => { /* connection failed */ }
+///         Ok(stream) => {
+///             Thread::spawn(move|| {
+///                 // connection succeeded
+///                 handle_client(stream)
+///             });
+///         }
+///     }
+/// }
+///
+/// // close the socket server
+/// drop(acceptor);
+/// # }
+/// ```
+pub struct TcpListener {
+    inner: TcpListenerImp,
+}
+
+impl TcpListener {
+    /// Creates a new `TcpListener` which will be bound to the specified address.
+    /// This listener is not ready for accepting connections, `listen` must be called
+    /// on it before that's possible.
+    ///
+    /// Binding with a port number of 0 will request that the OS assigns a port
+    /// to this listener. The port allocated can be queried via the
+    /// `socket_name` function.
+    ///
+    /// The address type can be any implementer of `ToSocketAddr` trait. See its
+    /// documentation for concrete examples.
+    pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<TcpListener> {
+        super::with_addresses(addr, |addr| {
+            TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner })
+        })
+    }
+
+    /// Returns the local socket address of this listener.
+    pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
+        self.inner.socket_name()
+    }
+}
+
+impl Listener<TcpStream, TcpAcceptor> for TcpListener {
+    fn listen(self) -> IoResult<TcpAcceptor> {
+        self.inner.listen(128).map(|a| TcpAcceptor { inner: a })
+    }
+}
+
+impl sys_common::AsInner<TcpListenerImp> for TcpListener {
+    fn as_inner(&self) -> &TcpListenerImp {
+        &self.inner
+    }
+}
+
+/// The accepting half of a TCP socket server. This structure is created through
+/// a `TcpListener`'s `listen` method, and this object can be used to accept new
+/// `TcpStream` instances.
+pub struct TcpAcceptor {
+    inner: TcpAcceptorImp,
+}
+
+impl TcpAcceptor {
+    /// Prevents blocking on all future accepts after `ms` milliseconds have
+    /// elapsed.
+    ///
+    /// This function is used to set a deadline after which this acceptor will
+    /// time out accepting any connections. The argument is the relative
+    /// distance, in milliseconds, to a point in the future after which all
+    /// accepts will fail.
+    ///
+    /// If the argument specified is `None`, then any previously registered
+    /// timeout is cleared.
+    ///
+    /// A timeout of `0` can be used to "poll" this acceptor to see if it has
+    /// any pending connections. All pending connections will be accepted,
+    /// regardless of whether the timeout has expired or not (the accept will
+    /// not block in this case).
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::old_io::TcpListener;
+    /// use std::old_io::{Listener, Acceptor, TimedOut};
+    ///
+    /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap();
+    ///
+    /// // After 100ms have passed, all accepts will fail
+    /// a.set_timeout(Some(100));
+    ///
+    /// match a.accept() {
+    ///     Ok(..) => println!("accepted a socket"),
+    ///     Err(ref e) if e.kind == TimedOut => { println!("timed out!"); }
+    ///     Err(e) => println!("err: {}", e),
+    /// }
+    ///
+    /// // Reset the timeout and try again
+    /// a.set_timeout(Some(100));
+    /// let socket = a.accept();
+    ///
+    /// // Clear the timeout and block indefinitely waiting for a connection
+    /// a.set_timeout(None);
+    /// let socket = a.accept();
+    /// ```
+    #[unstable(feature = "io",
+               reason = "the type of the argument and name of this function are \
+                         subject to change")]
+    pub fn set_timeout(&mut self, ms: Option<u64>) { self.inner.set_timeout(ms); }
+
+    /// Closes the accepting capabilities of this acceptor.
+    ///
+    /// This function is similar to `TcpStream`'s `close_{read,write}` methods
+    /// in that it will affect *all* cloned handles of this acceptor's original
+    /// handle.
+    ///
+    /// Once this function succeeds, all future calls to `accept` will return
+    /// immediately with an error, preventing all future calls to accept. The
+    /// underlying socket will not be relinquished back to the OS until all
+    /// acceptors have been deallocated.
+    ///
+    /// This is useful for waking up a thread in an accept loop to indicate that
+    /// it should exit.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use std::old_io::{TcpListener, Listener, Acceptor, EndOfFile};
+    /// use std::thread::Thread;
+    ///
+    /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap();
+    /// let a2 = a.clone();
+    ///
+    /// let _t = Thread::spawn(move|| {
+    ///     let mut a2 = a2;
+    ///     for socket in a2.incoming() {
+    ///         match socket {
+    ///             Ok(s) => { /* handle s */ }
+    ///             Err(ref e) if e.kind == EndOfFile => break, // closed
+    ///             Err(e) => panic!("unexpected error: {}", e),
+    ///         }
+    ///     }
+    /// });
+    ///
+    /// # fn wait_for_sigint() {}
+    /// // Now that our accept loop is running, wait for the program to be
+    /// // requested to exit.
+    /// wait_for_sigint();
+    ///
+    /// // Signal our accept loop to exit
+    /// assert!(a.close_accept().is_ok());
+    /// ```
+    #[unstable(feature = "io")]
+    pub fn close_accept(&mut self) -> IoResult<()> {
+        self.inner.close_accept()
+    }
+}
+
+impl Acceptor<TcpStream> for TcpAcceptor {
+    fn accept(&mut self) -> IoResult<TcpStream> {
+        self.inner.accept().map(TcpStream::new)
+    }
+}
+
+impl Clone for TcpAcceptor {
+    /// Creates a new handle to this TCP acceptor, allowing for simultaneous
+    /// accepts.
+    ///
+    /// The underlying TCP acceptor will not be closed until all handles to the
+    /// acceptor have been deallocated. Incoming connections will be received on
+    /// at most once acceptor, the same connection will not be accepted twice.
+    ///
+    /// The `close_accept` method will shut down *all* acceptors cloned from the
+    /// same original acceptor, whereas the `set_timeout` method only affects
+    /// the selector that it is called on.
+    ///
+    /// This function is useful for creating a handle to invoke `close_accept`
+    /// on to wake up any other task blocked in `accept`.
+    fn clone(&self) -> TcpAcceptor {
+        TcpAcceptor { inner: self.inner.clone() }
+    }
+}
+
+impl sys_common::AsInner<TcpAcceptorImp> for TcpAcceptor {
+    fn as_inner(&self) -> &TcpAcceptorImp {
+        &self.inner
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+
+    use sync::mpsc::channel;
+    use thread::Thread;
+    use old_io::net::tcp::*;
+    use old_io::net::ip::*;
+    use old_io::test::*;
+    use old_io::{EndOfFile, TimedOut, ShortWrite, IoError};
+    use old_io::{ConnectionRefused, BrokenPipe, ConnectionAborted};
+    use old_io::{ConnectionReset, NotConnected, PermissionDenied, OtherIoError};
+    use old_io::{Acceptor, Listener};
+
+    // FIXME #11530 this fails on android because tests are run as root
+    #[cfg_attr(any(windows, target_os = "android"), ignore)]
+    #[test]
+    fn bind_error() {
+        match TcpListener::bind("0.0.0.0:1") {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, PermissionDenied),
+        }
+    }
+
+    #[test]
+    fn connect_error() {
+        match TcpStream::connect("0.0.0.0:1") {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, ConnectionRefused),
+        }
+    }
+
+    #[test]
+    fn listen_ip4_localhost() {
+        let socket_addr = next_test_ip4();
+        let listener = TcpListener::bind(socket_addr);
+        let mut acceptor = listener.listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(("localhost", socket_addr.port));
+            stream.write(&[144]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 144);
+    }
+
+    #[test]
+    fn connect_localhost() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(("localhost", addr.port));
+            stream.write(&[64]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 64);
+    }
+
+    #[test]
+    fn connect_ip4_loopback() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(("127.0.0.1", addr.port));
+            stream.write(&[44]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 44);
+    }
+
+    #[test]
+    fn connect_ip6_loopback() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(("::1", addr.port));
+            stream.write(&[66]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 66);
+    }
+
+    #[test]
+    fn smoke_test_ip4() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(addr);
+            stream.write(&[99]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 99);
+    }
+
+    #[test]
+    fn smoke_test_ip6() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut stream = TcpStream::connect(addr);
+            stream.write(&[99]).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        stream.read(&mut buf).unwrap();
+        assert!(buf[0] == 99);
+    }
+
+    #[test]
+    fn read_eof_ip4() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let _stream = TcpStream::connect(addr);
+            // Close
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        let nread = stream.read(&mut buf);
+        assert!(nread.is_err());
+    }
+
+    #[test]
+    fn read_eof_ip6() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let _stream = TcpStream::connect(addr);
+            // Close
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        let nread = stream.read(&mut buf);
+        assert!(nread.is_err());
+    }
+
+    #[test]
+    fn read_eof_twice_ip4() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let _stream = TcpStream::connect(addr);
+            // Close
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        let nread = stream.read(&mut buf);
+        assert!(nread.is_err());
+
+        match stream.read(&mut buf) {
+            Ok(..) => panic!(),
+            Err(ref e) => {
+                assert!(e.kind == NotConnected || e.kind == EndOfFile,
+                        "unknown kind: {:?}", e.kind);
+            }
+        }
+    }
+
+    #[test]
+    fn read_eof_twice_ip6() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let _stream = TcpStream::connect(addr);
+            // Close
+        });
+
+        let mut stream = acceptor.accept();
+        let mut buf = [0];
+        let nread = stream.read(&mut buf);
+        assert!(nread.is_err());
+
+        match stream.read(&mut buf) {
+            Ok(..) => panic!(),
+            Err(ref e) => {
+                assert!(e.kind == NotConnected || e.kind == EndOfFile,
+                        "unknown kind: {:?}", e.kind);
+            }
+        }
+    }
+
+    #[test]
+    fn write_close_ip4() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            drop(TcpStream::connect(addr));
+            tx.send(()).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        rx.recv().unwrap();
+        let buf = [0];
+        match stream.write(&buf) {
+            Ok(..) => {}
+            Err(e) => {
+                assert!(e.kind == ConnectionReset ||
+                        e.kind == BrokenPipe ||
+                        e.kind == ConnectionAborted,
+                        "unknown error: {}", e);
+            }
+        }
+    }
+
+    #[test]
+    fn write_close_ip6() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            drop(TcpStream::connect(addr));
+            tx.send(()).unwrap();
+        });
+
+        let mut stream = acceptor.accept();
+        rx.recv().unwrap();
+        let buf = [0];
+        match stream.write(&buf) {
+            Ok(..) => {}
+            Err(e) => {
+                assert!(e.kind == ConnectionReset ||
+                        e.kind == BrokenPipe ||
+                        e.kind == ConnectionAborted,
+                        "unknown error: {}", e);
+            }
+        }
+    }
+
+    #[test]
+    fn multiple_connect_serial_ip4() {
+        let addr = next_test_ip4();
+        let max = 10u;
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            for _ in range(0, max) {
+                let mut stream = TcpStream::connect(addr);
+                stream.write(&[99]).unwrap();
+            }
+        });
+
+        for ref mut stream in acceptor.incoming().take(max) {
+            let mut buf = [0];
+            stream.read(&mut buf).unwrap();
+            assert_eq!(buf[0], 99);
+        }
+    }
+
+    #[test]
+    fn multiple_connect_serial_ip6() {
+        let addr = next_test_ip6();
+        let max = 10u;
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            for _ in range(0, max) {
+                let mut stream = TcpStream::connect(addr);
+                stream.write(&[99]).unwrap();
+            }
+        });
+
+        for ref mut stream in acceptor.incoming().take(max) {
+            let mut buf = [0];
+            stream.read(&mut buf).unwrap();
+            assert_eq!(buf[0], 99);
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_greedy_schedule_ip4() {
+        let addr = next_test_ip4();
+        static MAX: int = 10;
+        let acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut acceptor = acceptor;
+            for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) {
+                // Start another task to handle the connection
+                let _t = Thread::spawn(move|| {
+                    let mut stream = stream;
+                    let mut buf = [0];
+                    stream.read(&mut buf).unwrap();
+                    assert!(buf[0] == i as u8);
+                    debug!("read");
+                });
+            }
+        });
+
+        connect(0, addr);
+
+        fn connect(i: int, addr: SocketAddr) {
+            if i == MAX { return }
+
+            let _t = Thread::spawn(move|| {
+                debug!("connecting");
+                let mut stream = TcpStream::connect(addr);
+                // Connect again before writing
+                connect(i + 1, addr);
+                debug!("writing");
+                stream.write(&[i as u8]).unwrap();
+            });
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_greedy_schedule_ip6() {
+        let addr = next_test_ip6();
+        static MAX: int = 10;
+        let acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut acceptor = acceptor;
+            for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) {
+                // Start another task to handle the connection
+                let _t = Thread::spawn(move|| {
+                    let mut stream = stream;
+                    let mut buf = [0];
+                    stream.read(&mut buf).unwrap();
+                    assert!(buf[0] == i as u8);
+                    debug!("read");
+                });
+            }
+        });
+
+        connect(0, addr);
+
+        fn connect(i: int, addr: SocketAddr) {
+            if i == MAX { return }
+
+            let _t = Thread::spawn(move|| {
+                debug!("connecting");
+                let mut stream = TcpStream::connect(addr);
+                // Connect again before writing
+                connect(i + 1, addr);
+                debug!("writing");
+                stream.write(&[i as u8]).unwrap();
+            });
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_lazy_schedule_ip4() {
+        static MAX: int = 10;
+        let addr = next_test_ip4();
+        let acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut acceptor = acceptor;
+            for stream in acceptor.incoming().take(MAX as uint) {
+                // Start another task to handle the connection
+                let _t = Thread::spawn(move|| {
+                    let mut stream = stream;
+                    let mut buf = [0];
+                    stream.read(&mut buf).unwrap();
+                    assert!(buf[0] == 99);
+                    debug!("read");
+                });
+            }
+        });
+
+        connect(0, addr);
+
+        fn connect(i: int, addr: SocketAddr) {
+            if i == MAX { return }
+
+            let _t = Thread::spawn(move|| {
+                debug!("connecting");
+                let mut stream = TcpStream::connect(addr);
+                // Connect again before writing
+                connect(i + 1, addr);
+                debug!("writing");
+                stream.write(&[99]).unwrap();
+            });
+        }
+    }
+
+    #[test]
+    fn multiple_connect_interleaved_lazy_schedule_ip6() {
+        static MAX: int = 10;
+        let addr = next_test_ip6();
+        let acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut acceptor = acceptor;
+            for stream in acceptor.incoming().take(MAX as uint) {
+                // Start another task to handle the connection
+                let _t = Thread::spawn(move|| {
+                    let mut stream = stream;
+                    let mut buf = [0];
+                    stream.read(&mut buf).unwrap();
+                    assert!(buf[0] == 99);
+                    debug!("read");
+                });
+            }
+        });
+
+        connect(0, addr);
+
+        fn connect(i: int, addr: SocketAddr) {
+            if i == MAX { return }
+
+            let _t = Thread::spawn(move|| {
+                debug!("connecting");
+                let mut stream = TcpStream::connect(addr);
+                // Connect again before writing
+                connect(i + 1, addr);
+                debug!("writing");
+                stream.write(&[99]).unwrap();
+            });
+        }
+    }
+
+    pub fn socket_name(addr: SocketAddr) {
+        let mut listener = TcpListener::bind(addr).unwrap();
+
+        // Make sure socket_name gives
+        // us the socket we binded to.
+        let so_name = listener.socket_name();
+        assert!(so_name.is_ok());
+        assert_eq!(addr, so_name.unwrap());
+    }
+
+    pub fn peer_name(addr: SocketAddr) {
+        let acceptor = TcpListener::bind(addr).listen();
+        let _t = Thread::spawn(move|| {
+            let mut acceptor = acceptor;
+            acceptor.accept().unwrap();
+        });
+
+        let stream = TcpStream::connect(addr);
+
+        assert!(stream.is_ok());
+        let mut stream = stream.unwrap();
+
+        // Make sure peer_name gives us the
+        // address/port of the peer we've
+        // connected to.
+        let peer_name = stream.peer_name();
+        assert!(peer_name.is_ok());
+        assert_eq!(addr, peer_name.unwrap());
+    }
+
+    #[test]
+    fn socket_and_peer_name_ip4() {
+        peer_name(next_test_ip4());
+        socket_name(next_test_ip4());
+    }
+
+    #[test]
+    fn socket_and_peer_name_ip6() {
+        // FIXME: peer name is not consistent
+        //peer_name(next_test_ip6());
+        socket_name(next_test_ip6());
+    }
+
+    #[test]
+    fn partial_read() {
+        let addr = next_test_ip4();
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut srv = TcpListener::bind(addr).listen().unwrap();
+            tx.send(()).unwrap();
+            let mut cl = srv.accept().unwrap();
+            cl.write(&[10]).unwrap();
+            let mut b = [0];
+            cl.read(&mut b).unwrap();
+            tx.send(()).unwrap();
+        });
+
+        rx.recv().unwrap();
+        let mut c = TcpStream::connect(addr).unwrap();
+        let mut b = [0; 10];
+        assert_eq!(c.read(&mut b), Ok(1));
+        c.write(&[1]).unwrap();
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn double_bind() {
+        let addr = next_test_ip4();
+        let listener = TcpListener::bind(addr).unwrap().listen();
+        assert!(listener.is_ok());
+        match TcpListener::bind(addr).listen() {
+            Ok(..) => panic!(),
+            Err(e) => {
+                assert!(e.kind == ConnectionRefused || e.kind == OtherIoError,
+                        "unknown error: {} {:?}", e, e.kind);
+            }
+        }
+    }
+
+    #[test]
+    fn fast_rebind() {
+        let addr = next_test_ip4();
+        let (tx, rx) = channel();
+
+        let _t = Thread::spawn(move|| {
+            rx.recv().unwrap();
+            let _stream = TcpStream::connect(addr).unwrap();
+            // Close
+            rx.recv().unwrap();
+        });
+
+        {
+            let mut acceptor = TcpListener::bind(addr).listen();
+            tx.send(()).unwrap();
+            {
+                let _stream = acceptor.accept().unwrap();
+                // Close client
+                tx.send(()).unwrap();
+            }
+            // Close listener
+        }
+        let _listener = TcpListener::bind(addr);
+    }
+
+    #[test]
+    fn tcp_clone_smoke() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr);
+            let mut buf = [0, 0];
+            assert_eq!(s.read(&mut buf), Ok(1));
+            assert_eq!(buf[0], 1);
+            s.write(&[2]).unwrap();
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (tx1, rx1) = channel();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            rx1.recv().unwrap();
+            s2.write(&[1]).unwrap();
+            tx2.send(()).unwrap();
+        });
+        tx1.send(()).unwrap();
+        let mut buf = [0, 0];
+        assert_eq!(s1.read(&mut buf), Ok(1));
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn tcp_clone_two_read() {
+        let addr = next_test_ip6();
+        let mut acceptor = TcpListener::bind(addr).listen();
+        let (tx1, rx) = channel();
+        let tx2 = tx1.clone();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr);
+            s.write(&[1]).unwrap();
+            rx.recv().unwrap();
+            s.write(&[2]).unwrap();
+            rx.recv().unwrap();
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (done, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            let mut buf = [0, 0];
+            s2.read(&mut buf).unwrap();
+            tx2.send(()).unwrap();
+            done.send(()).unwrap();
+        });
+        let mut buf = [0, 0];
+        s1.read(&mut buf).unwrap();
+        tx1.send(()).unwrap();
+
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn tcp_clone_two_write() {
+        let addr = next_test_ip4();
+        let mut acceptor = TcpListener::bind(addr).listen();
+
+        let _t = Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr);
+            let mut buf = [0, 1];
+            s.read(&mut buf).unwrap();
+            s.read(&mut buf).unwrap();
+        });
+
+        let mut s1 = acceptor.accept().unwrap();
+        let s2 = s1.clone();
+
+        let (done, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            s2.write(&[1]).unwrap();
+            done.send(()).unwrap();
+        });
+        s1.write(&[2]).unwrap();
+
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn shutdown_smoke() {
+        let addr = next_test_ip4();
+        let a = TcpListener::bind(addr).unwrap().listen();
+        let _t = Thread::spawn(move|| {
+            let mut a = a;
+            let mut c = a.accept().unwrap();
+            assert_eq!(c.read_to_end(), Ok(vec!()));
+            c.write(&[1]).unwrap();
+        });
+
+        let mut s = TcpStream::connect(addr).unwrap();
+        assert!(s.inner.close_write().is_ok());
+        assert!(s.write(&[1]).is_err());
+        assert_eq!(s.read_to_end(), Ok(vec!(1)));
+    }
+
+    #[test]
+    fn accept_timeout() {
+        let addr = next_test_ip4();
+        let mut a = TcpListener::bind(addr).unwrap().listen().unwrap();
+
+        a.set_timeout(Some(10));
+
+        // Make sure we time out once and future invocations also time out
+        let err = a.accept().err().unwrap();
+        assert_eq!(err.kind, TimedOut);
+        let err = a.accept().err().unwrap();
+        assert_eq!(err.kind, TimedOut);
+
+        // Also make sure that even though the timeout is expired that we will
+        // continue to receive any pending connections.
+        //
+        // FIXME: freebsd apparently never sees the pending connection, but
+        //        testing manually always works. Need to investigate this
+        //        flakiness.
+        if !cfg!(target_os = "freebsd") {
+            let (tx, rx) = channel();
+            let _t = Thread::spawn(move|| {
+                tx.send(TcpStream::connect(addr).unwrap()).unwrap();
+            });
+            let _l = rx.recv().unwrap();
+            for i in range(0i, 1001) {
+                match a.accept() {
+                    Ok(..) => break,
+                    Err(ref e) if e.kind == TimedOut => {}
+                    Err(e) => panic!("error: {}", e),
+                }
+                ::thread::Thread::yield_now();
+                if i == 1000 { panic!("should have a pending connection") }
+            }
+        }
+
+        // Unset the timeout and make sure that this always blocks.
+        a.set_timeout(None);
+        let _t = Thread::spawn(move|| {
+            drop(TcpStream::connect(addr).unwrap());
+        });
+        a.accept().unwrap();
+    }
+
+    #[test]
+    fn close_readwrite_smoke() {
+        let addr = next_test_ip4();
+        let a = TcpListener::bind(addr).listen().unwrap();
+        let (_tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut a = a;
+            let _s = a.accept().unwrap();
+            let _ = rx.recv().unwrap();
+        });
+
+        let mut b = [0];
+        let mut s = TcpStream::connect(addr).unwrap();
+        let mut s2 = s.clone();
+
+        // closing should prevent reads/writes
+        s.close_write().unwrap();
+        assert!(s.write(&[0]).is_err());
+        s.close_read().unwrap();
+        assert!(s.read(&mut b).is_err());
+
+        // closing should affect previous handles
+        assert!(s2.write(&[0]).is_err());
+        assert!(s2.read(&mut b).is_err());
+
+        // closing should affect new handles
+        let mut s3 = s.clone();
+        assert!(s3.write(&[0]).is_err());
+        assert!(s3.read(&mut b).is_err());
+
+        // make sure these don't die
+        let _ = s2.close_read();
+        let _ = s2.close_write();
+        let _ = s3.close_read();
+        let _ = s3.close_write();
+    }
+
+    #[test]
+    fn close_read_wakes_up() {
+        let addr = next_test_ip4();
+        let a = TcpListener::bind(addr).listen().unwrap();
+        let (_tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut a = a;
+            let _s = a.accept().unwrap();
+            let _ = rx.recv().unwrap();
+        });
+
+        let mut s = TcpStream::connect(addr).unwrap();
+        let s2 = s.clone();
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            assert!(s2.read(&mut [0]).is_err());
+            tx.send(()).unwrap();
+        });
+        // this should wake up the child task
+        s.close_read().unwrap();
+
+        // this test will never finish if the child doesn't wake up
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn readwrite_timeouts() {
+        let addr = next_test_ip6();
+        let mut a = TcpListener::bind(addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr).unwrap();
+            rx.recv().unwrap();
+            assert!(s.write(&[0]).is_ok());
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+
+        s.set_timeout(Some(20));
+        for i in range(0i, 1001) {
+            match s.write(&[0; 128 * 1024]) {
+                Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
+                Err(IoError { kind: TimedOut, .. }) => break,
+                Err(e) => panic!("{}", e),
+           }
+           if i == 1000 { panic!("should have filled up?!"); }
+        }
+        assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut);
+
+        tx.send(()).unwrap();
+        s.set_timeout(None);
+        assert_eq!(s.read(&mut [0, 0]), Ok(1));
+    }
+
+    #[test]
+    fn read_timeouts() {
+        let addr = next_test_ip6();
+        let mut a = TcpListener::bind(addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr).unwrap();
+            rx.recv().unwrap();
+            let mut amt = 0;
+            while amt < 100 * 128 * 1024 {
+                match s.read(&mut [0;128 * 1024]) {
+                    Ok(n) => { amt += n; }
+                    Err(e) => panic!("{}", e),
+                }
+            }
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_read_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+
+        tx.send(()).unwrap();
+        for _ in range(0i, 100) {
+            assert!(s.write(&[0;128 * 1024]).is_ok());
+        }
+    }
+
+    #[test]
+    fn write_timeouts() {
+        let addr = next_test_ip6();
+        let mut a = TcpListener::bind(addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr).unwrap();
+            rx.recv().unwrap();
+            assert!(s.write(&[0]).is_ok());
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        s.set_write_timeout(Some(20));
+        for i in range(0i, 1001) {
+            match s.write(&[0; 128 * 1024]) {
+                Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
+                Err(IoError { kind: TimedOut, .. }) => break,
+                Err(e) => panic!("{}", e),
+           }
+           if i == 1000 { panic!("should have filled up?!"); }
+        }
+        assert_eq!(s.write(&[0]).err().unwrap().kind, TimedOut);
+
+        tx.send(()).unwrap();
+        assert!(s.read(&mut [0]).is_ok());
+    }
+
+    #[test]
+    fn timeout_concurrent_read() {
+        let addr = next_test_ip6();
+        let mut a = TcpListener::bind(addr).listen().unwrap();
+        let (tx, rx) = channel::<()>();
+        Thread::spawn(move|| {
+            let mut s = TcpStream::connect(addr).unwrap();
+            rx.recv().unwrap();
+            assert_eq!(s.write(&[0]), Ok(()));
+            let _ = rx.recv();
+        });
+
+        let mut s = a.accept().unwrap();
+        let s2 = s.clone();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut s2 = s2;
+            assert_eq!(s2.read(&mut [0]), Ok(1));
+            tx2.send(()).unwrap();
+        });
+
+        s.set_read_timeout(Some(20));
+        assert_eq!(s.read(&mut [0]).err().unwrap().kind, TimedOut);
+        tx.send(()).unwrap();
+
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn clone_while_reading() {
+        let addr = next_test_ip6();
+        let listen = TcpListener::bind(addr);
+        let mut accept = listen.listen().unwrap();
+
+        // Enqueue a task to write to a socket
+        let (tx, rx) = channel();
+        let (txdone, rxdone) = channel();
+        let txdone2 = txdone.clone();
+        let _t = Thread::spawn(move|| {
+            let mut tcp = TcpStream::connect(addr).unwrap();
+            rx.recv().unwrap();
+            tcp.write_u8(0).unwrap();
+            txdone2.send(()).unwrap();
+        });
+
+        // Spawn off a reading clone
+        let tcp = accept.accept().unwrap();
+        let tcp2 = tcp.clone();
+        let txdone3 = txdone.clone();
+        let _t = Thread::spawn(move|| {
+            let mut tcp2 = tcp2;
+            tcp2.read_u8().unwrap();
+            txdone3.send(()).unwrap();
+        });
+
+        // Try to ensure that the reading clone is indeed reading
+        for _ in range(0i, 50) {
+            ::thread::Thread::yield_now();
+        }
+
+        // clone the handle again while it's reading, then let it finish the
+        // read.
+        let _ = tcp.clone();
+        tx.send(()).unwrap();
+        rxdone.recv().unwrap();
+        rxdone.recv().unwrap();
+    }
+
+    #[test]
+    fn clone_accept_smoke() {
+        let addr = next_test_ip4();
+        let l = TcpListener::bind(addr);
+        let mut a = l.listen().unwrap();
+        let mut a2 = a.clone();
+
+        let _t = Thread::spawn(move|| {
+            let _ = TcpStream::connect(addr);
+        });
+        let _t = Thread::spawn(move|| {
+            let _ = TcpStream::connect(addr);
+        });
+
+        assert!(a.accept().is_ok());
+        assert!(a2.accept().is_ok());
+    }
+
+    #[test]
+    fn clone_accept_concurrent() {
+        let addr = next_test_ip4();
+        let l = TcpListener::bind(addr);
+        let a = l.listen().unwrap();
+        let a2 = a.clone();
+
+        let (tx, rx) = channel();
+        let tx2 = tx.clone();
+
+        let _t = Thread::spawn(move|| {
+            let mut a = a;
+            tx.send(a.accept()).unwrap();
+        });
+        let _t = Thread::spawn(move|| {
+            let mut a = a2;
+            tx2.send(a.accept()).unwrap();
+        });
+
+        let _t = Thread::spawn(move|| {
+            let _ = TcpStream::connect(addr);
+        });
+        let _t = Thread::spawn(move|| {
+            let _ = TcpStream::connect(addr);
+        });
+
+        assert!(rx.recv().unwrap().is_ok());
+        assert!(rx.recv().unwrap().is_ok());
+    }
+
+    #[test]
+    fn close_accept_smoke() {
+        let addr = next_test_ip4();
+        let l = TcpListener::bind(addr);
+        let mut a = l.listen().unwrap();
+
+        a.close_accept().unwrap();
+        assert_eq!(a.accept().err().unwrap().kind, EndOfFile);
+    }
+
+    #[test]
+    fn close_accept_concurrent() {
+        let addr = next_test_ip4();
+        let l = TcpListener::bind(addr);
+        let a = l.listen().unwrap();
+        let mut a2 = a.clone();
+
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut a = a;
+            tx.send(a.accept()).unwrap();
+        });
+        a2.close_accept().unwrap();
+
+        assert_eq!(rx.recv().unwrap().err().unwrap().kind, EndOfFile);
+    }
+}
diff --git a/src/libstd/old_io/net/udp.rs b/src/libstd/old_io/net/udp.rs
new file mode 100644
index 00000000000..9055a089eec
--- /dev/null
+++ b/src/libstd/old_io/net/udp.rs
@@ -0,0 +1,459 @@
+// 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.
+
+//! UDP (User Datagram Protocol) network connections.
+//!
+//! This module contains the ability to open a UDP stream to a socket address.
+//! The destination and binding addresses can either be an IPv4 or IPv6
+//! address. There is no corresponding notion of a server because UDP is a
+//! datagram protocol.
+
+use clone::Clone;
+use old_io::net::ip::{SocketAddr, IpAddr, ToSocketAddr};
+use old_io::IoResult;
+use option::Option;
+use sys::udp::UdpSocket as UdpSocketImp;
+use sys_common;
+
+/// A User Datagram Protocol socket.
+///
+/// This is an implementation of a bound UDP socket. This supports both IPv4 and
+/// IPv6 addresses, and there is no corresponding notion of a server because UDP
+/// is a datagram protocol.
+///
+/// # Example
+///
+/// ```rust,no_run
+/// # #![allow(unused_must_use)]
+/// #![feature(slicing_syntax)]
+///
+/// use std::old_io::net::udp::UdpSocket;
+/// use std::old_io::net::ip::{Ipv4Addr, SocketAddr};
+/// fn main() {
+///     let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 34254 };
+///     let mut socket = match UdpSocket::bind(addr) {
+///         Ok(s) => s,
+///         Err(e) => panic!("couldn't bind socket: {}", e),
+///     };
+///
+///     let mut buf = [0; 10];
+///     match socket.recv_from(&mut buf) {
+///         Ok((amt, src)) => {
+///             // Send a reply to the socket we received data from
+///             let buf = buf.slice_to_mut(amt);
+///             buf.reverse();
+///             socket.send_to(buf, src);
+///         }
+///         Err(e) => println!("couldn't receive a datagram: {}", e)
+///     }
+///     drop(socket); // close the socket
+/// }
+/// ```
+pub struct UdpSocket {
+    inner: UdpSocketImp,
+}
+
+impl UdpSocket {
+    /// Creates a UDP socket from the given address.
+    ///
+    /// Address type can be any implementor of `ToSocketAddr` trait. See its
+    /// documentation for concrete examples.
+    pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<UdpSocket> {
+        super::with_addresses(addr, |addr| {
+            UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s })
+        })
+    }
+
+    /// Receives data from the socket. On success, returns the number of bytes
+    /// read and the address from whence the data came.
+    pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> {
+        self.inner.recv_from(buf)
+    }
+
+    /// Sends data on the socket to the given address. Returns nothing on
+    /// success.
+    ///
+    /// Address type can be any implementer of `ToSocketAddr` trait. See its
+    /// documentation for concrete examples.
+    pub fn send_to<A: ToSocketAddr>(&mut self, buf: &[u8], addr: A) -> IoResult<()> {
+        super::with_addresses(addr, |addr| self.inner.send_to(buf, addr))
+    }
+
+    /// Returns the socket address that this socket was created from.
+    pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
+        self.inner.socket_name()
+    }
+
+    /// Joins a multicast IP address (becomes a member of it)
+    #[unstable(feature = "io")]
+    pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
+        self.inner.join_multicast(multi)
+    }
+
+    /// Leaves a multicast IP address (drops membership from it)
+    #[unstable(feature = "io")]
+    pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
+        self.inner.leave_multicast(multi)
+    }
+
+    /// Set the multicast loop flag to the specified value
+    ///
+    /// This lets multicast packets loop back to local sockets (if enabled)
+    #[unstable(feature = "io")]
+    pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> {
+        self.inner.set_multicast_loop(on)
+    }
+
+    /// Sets the multicast TTL
+    #[unstable(feature = "io")]
+    pub fn set_multicast_ttl(&mut self, ttl: int) -> IoResult<()> {
+        self.inner.multicast_time_to_live(ttl)
+    }
+
+    /// Sets this socket's TTL
+    #[unstable(feature = "io")]
+    pub fn set_ttl(&mut self, ttl: int) -> IoResult<()> {
+        self.inner.time_to_live(ttl)
+    }
+
+    /// Sets the broadcast flag on or off
+    #[unstable(feature = "io")]
+    pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> {
+        self.inner.set_broadcast(broadcast)
+    }
+
+    /// Sets the read/write timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_timeout(timeout_ms)
+    }
+
+    /// Sets the read timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_read_timeout(timeout_ms)
+    }
+
+    /// Sets the write timeout for this socket.
+    ///
+    /// For more information, see `TcpStream::set_timeout`
+    #[unstable(feature = "io",
+               reason = "the timeout argument may change in type and value")]
+    pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.inner.set_write_timeout(timeout_ms)
+    }
+}
+
+impl Clone for UdpSocket {
+    /// Creates a new handle to this UDP socket, allowing for simultaneous
+    /// reads and writes of the socket.
+    ///
+    /// The underlying UDP socket will not be closed until all handles to the
+    /// socket have been deallocated. Two concurrent reads will not receive
+    /// the same data. Instead, the first read will receive the first packet
+    /// received, and the second read will receive the second packet.
+    fn clone(&self) -> UdpSocket {
+        UdpSocket {
+            inner: self.inner.clone(),
+        }
+    }
+}
+
+impl sys_common::AsInner<UdpSocketImp> for UdpSocket {
+    fn as_inner(&self) -> &UdpSocketImp {
+        &self.inner
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+
+    use sync::mpsc::channel;
+    use old_io::net::ip::*;
+    use old_io::test::*;
+    use old_io::{IoError, TimedOut, PermissionDenied, ShortWrite};
+    use super::*;
+    use thread::Thread;
+
+    // FIXME #11530 this fails on android because tests are run as root
+    #[cfg_attr(any(windows, target_os = "android"), ignore)]
+    #[test]
+    fn bind_error() {
+        let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
+        match UdpSocket::bind(addr) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, PermissionDenied),
+        }
+    }
+
+    #[test]
+    fn socket_smoke_test_ip4() {
+        let server_ip = next_test_ip4();
+        let client_ip = next_test_ip4();
+        let (tx1, rx1) = channel();
+        let (tx2, rx2) = channel();
+
+        let _t = Thread::spawn(move|| {
+            match UdpSocket::bind(client_ip) {
+                Ok(ref mut client) => {
+                    rx1.recv().unwrap();
+                    client.send_to(&[99], server_ip).unwrap()
+                }
+                Err(..) => panic!()
+            }
+            tx2.send(()).unwrap();
+        });
+
+        match UdpSocket::bind(server_ip) {
+            Ok(ref mut server) => {
+                tx1.send(()).unwrap();
+                let mut buf = [0];
+                match server.recv_from(&mut buf) {
+                    Ok((nread, src)) => {
+                        assert_eq!(nread, 1);
+                        assert_eq!(buf[0], 99);
+                        assert_eq!(src, client_ip);
+                    }
+                    Err(..) => panic!()
+                }
+            }
+            Err(..) => panic!()
+        }
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn socket_smoke_test_ip6() {
+        let server_ip = next_test_ip6();
+        let client_ip = next_test_ip6();
+        let (tx, rx) = channel::<()>();
+
+        let _t = Thread::spawn(move|| {
+            match UdpSocket::bind(client_ip) {
+                Ok(ref mut client) => {
+                    rx.recv().unwrap();
+                    client.send_to(&[99], server_ip).unwrap()
+                }
+                Err(..) => panic!()
+            }
+        });
+
+        match UdpSocket::bind(server_ip) {
+            Ok(ref mut server) => {
+                tx.send(()).unwrap();
+                let mut buf = [0];
+                match server.recv_from(&mut buf) {
+                    Ok((nread, src)) => {
+                        assert_eq!(nread, 1);
+                        assert_eq!(buf[0], 99);
+                        assert_eq!(src, client_ip);
+                    }
+                    Err(..) => panic!()
+                }
+            }
+            Err(..) => panic!()
+        }
+    }
+
+    pub fn socket_name(addr: SocketAddr) {
+        let server = UdpSocket::bind(addr);
+
+        assert!(server.is_ok());
+        let mut server = server.unwrap();
+
+        // Make sure socket_name gives
+        // us the socket we binded to.
+        let so_name = server.socket_name();
+        assert!(so_name.is_ok());
+        assert_eq!(addr, so_name.unwrap());
+    }
+
+    #[test]
+    fn socket_name_ip4() {
+        socket_name(next_test_ip4());
+    }
+
+    #[test]
+    fn socket_name_ip6() {
+        socket_name(next_test_ip6());
+    }
+
+    #[test]
+    fn udp_clone_smoke() {
+        let addr1 = next_test_ip4();
+        let addr2 = next_test_ip4();
+        let mut sock1 = UdpSocket::bind(addr1).unwrap();
+        let sock2 = UdpSocket::bind(addr2).unwrap();
+
+        let _t = Thread::spawn(move|| {
+            let mut sock2 = sock2;
+            let mut buf = [0, 0];
+            assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1)));
+            assert_eq!(buf[0], 1);
+            sock2.send_to(&[2], addr1).unwrap();
+        });
+
+        let sock3 = sock1.clone();
+
+        let (tx1, rx1) = channel();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut sock3 = sock3;
+            rx1.recv().unwrap();
+            sock3.send_to(&[1], addr2).unwrap();
+            tx2.send(()).unwrap();
+        });
+        tx1.send(()).unwrap();
+        let mut buf = [0, 0];
+        assert_eq!(sock1.recv_from(&mut buf), Ok((1, addr2)));
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn udp_clone_two_read() {
+        let addr1 = next_test_ip4();
+        let addr2 = next_test_ip4();
+        let mut sock1 = UdpSocket::bind(addr1).unwrap();
+        let sock2 = UdpSocket::bind(addr2).unwrap();
+        let (tx1, rx) = channel();
+        let tx2 = tx1.clone();
+
+        let _t = Thread::spawn(move|| {
+            let mut sock2 = sock2;
+            sock2.send_to(&[1], addr1).unwrap();
+            rx.recv().unwrap();
+            sock2.send_to(&[2], addr1).unwrap();
+            rx.recv().unwrap();
+        });
+
+        let sock3 = sock1.clone();
+
+        let (done, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut sock3 = sock3;
+            let mut buf = [0, 0];
+            sock3.recv_from(&mut buf).unwrap();
+            tx2.send(()).unwrap();
+            done.send(()).unwrap();
+        });
+        let mut buf = [0, 0];
+        sock1.recv_from(&mut buf).unwrap();
+        tx1.send(()).unwrap();
+
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn udp_clone_two_write() {
+        let addr1 = next_test_ip4();
+        let addr2 = next_test_ip4();
+        let mut sock1 = UdpSocket::bind(addr1).unwrap();
+        let sock2 = UdpSocket::bind(addr2).unwrap();
+
+        let (tx, rx) = channel();
+        let (serv_tx, serv_rx) = channel();
+
+        let _t = Thread::spawn(move|| {
+            let mut sock2 = sock2;
+            let mut buf = [0, 1];
+
+            rx.recv().unwrap();
+            match sock2.recv_from(&mut buf) {
+                Ok(..) => {}
+                Err(e) => panic!("failed receive: {}", e),
+            }
+            serv_tx.send(()).unwrap();
+        });
+
+        let sock3 = sock1.clone();
+
+        let (done, rx) = channel();
+        let tx2 = tx.clone();
+        let _t = Thread::spawn(move|| {
+            let mut sock3 = sock3;
+            match sock3.send_to(&[1], addr2) {
+                Ok(..) => { let _ = tx2.send(()); }
+                Err(..) => {}
+            }
+            done.send(()).unwrap();
+        });
+        match sock1.send_to(&[2], addr2) {
+            Ok(..) => { let _ = tx.send(()); }
+            Err(..) => {}
+        }
+        drop(tx);
+
+        rx.recv().unwrap();
+        serv_rx.recv().unwrap();
+    }
+
+    #[cfg(not(windows))] // FIXME #17553
+    #[test]
+    fn recv_from_timeout() {
+        let addr1 = next_test_ip4();
+        let addr2 = next_test_ip4();
+        let mut a = UdpSocket::bind(addr1).unwrap();
+        let a2 = UdpSocket::bind(addr2).unwrap();
+
+        let (tx, rx) = channel();
+        let (tx2, rx2) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut a = a2;
+            assert_eq!(a.recv_from(&mut [0]), Ok((1, addr1)));
+            assert_eq!(a.send_to(&[0], addr1), Ok(()));
+            rx.recv().unwrap();
+            assert_eq!(a.send_to(&[0], addr1), Ok(()));
+
+            tx2.send(()).unwrap();
+        });
+
+        // Make sure that reads time out, but writes can continue
+        a.set_read_timeout(Some(20));
+        assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(a.recv_from(&mut [0]).err().unwrap().kind, TimedOut);
+        assert_eq!(a.send_to(&[0], addr2), Ok(()));
+
+        // Cloned handles should be able to block
+        let mut a2 = a.clone();
+        assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2)));
+
+        // Clearing the timeout should allow for receiving
+        a.set_timeout(None);
+        tx.send(()).unwrap();
+        assert_eq!(a2.recv_from(&mut [0]), Ok((1, addr2)));
+
+        // Make sure the child didn't die
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn send_to_timeout() {
+        let addr1 = next_test_ip4();
+        let addr2 = next_test_ip4();
+        let mut a = UdpSocket::bind(addr1).unwrap();
+        let _b = UdpSocket::bind(addr2).unwrap();
+
+        a.set_write_timeout(Some(1000));
+        for _ in range(0u, 100) {
+            match a.send_to(&[0;4*1024], addr2) {
+                Ok(()) | Err(IoError { kind: ShortWrite(..), .. }) => {},
+                Err(IoError { kind: TimedOut, .. }) => break,
+                Err(e) => panic!("other error: {}", e),
+            }
+        }
+    }
+}
diff --git a/src/libstd/old_io/pipe.rs b/src/libstd/old_io/pipe.rs
new file mode 100644
index 00000000000..5843b1ba1b1
--- /dev/null
+++ b/src/libstd/old_io/pipe.rs
@@ -0,0 +1,139 @@
+// 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.
+
+//! Synchronous, in-memory pipes.
+//!
+//! Currently these aren't particularly useful, there only exists bindings
+//! enough so that pipes can be created to child processes.
+
+#![allow(missing_docs)]
+
+use prelude::v1::*;
+
+use old_io::IoResult;
+use libc;
+use sync::Arc;
+
+use sys_common;
+use sys;
+use sys::fs::FileDesc as FileDesc;
+
+/// A synchronous, in-memory pipe.
+pub struct PipeStream {
+    inner: Arc<FileDesc>
+}
+
+pub struct PipePair {
+    pub reader: PipeStream,
+    pub writer: PipeStream,
+}
+
+impl PipeStream {
+    /// Consumes a file descriptor to return a pipe stream that will have
+    /// synchronous, but non-blocking reads/writes. This is useful if the file
+    /// descriptor is acquired via means other than the standard methods.
+    ///
+    /// This operation consumes ownership of the file descriptor and it will be
+    /// closed once the object is deallocated.
+    ///
+    /// # Example
+    ///
+    /// ```{rust,no_run}
+    /// # #![allow(unused_must_use)]
+    /// extern crate libc;
+    ///
+    /// use std::old_io::pipe::PipeStream;
+    ///
+    /// fn main() {
+    ///     let mut pipe = PipeStream::open(libc::STDERR_FILENO);
+    ///     pipe.write(b"Hello, stderr!");
+    /// }
+    /// ```
+    pub fn open(fd: libc::c_int) -> IoResult<PipeStream> {
+        Ok(PipeStream::from_filedesc(FileDesc::new(fd, true)))
+    }
+
+    // FIXME: expose this some other way
+    /// Wrap a FileDesc directly, taking ownership.
+    #[doc(hidden)]
+    pub fn from_filedesc(fd: FileDesc) -> PipeStream {
+        PipeStream { inner: Arc::new(fd) }
+    }
+
+    /// Creates a pair of in-memory OS pipes for a unidirectional communication
+    /// stream.
+    ///
+    /// The structure returned contains a reader and writer I/O object. Data
+    /// written to the writer can be read from the reader.
+    ///
+    /// # Errors
+    ///
+    /// This function can fail to succeed if the underlying OS has run out of
+    /// available resources to allocate a new pipe.
+    pub fn pair() -> IoResult<PipePair> {
+        let (reader, writer) = try!(unsafe { sys::os::pipe() });
+        Ok(PipePair {
+            reader: PipeStream::from_filedesc(reader),
+            writer: PipeStream::from_filedesc(writer),
+        })
+    }
+}
+
+impl sys_common::AsInner<sys::fs::FileDesc> for PipeStream {
+    fn as_inner(&self) -> &sys::fs::FileDesc {
+        &*self.inner
+    }
+}
+
+impl Clone for PipeStream {
+    fn clone(&self) -> PipeStream {
+        PipeStream { inner: self.inner.clone() }
+    }
+}
+
+impl Reader for PipeStream {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.read(buf)
+    }
+}
+
+impl Writer for PipeStream {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner.write(buf)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+
+    use sync::mpsc::channel;
+    use thread::Thread;
+
+    #[test]
+    fn partial_read() {
+        use os;
+        use old_io::pipe::PipeStream;
+
+        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
+        let out = PipeStream::open(writer);
+        let mut input = PipeStream::open(reader);
+        let (tx, rx) = channel();
+        let _t = Thread::spawn(move|| {
+            let mut out = out;
+            out.write(&[10]).unwrap();
+            rx.recv().unwrap(); // don't close the pipe until the other read has finished
+        });
+
+        let mut buf = [0; 10];
+        input.read(&mut buf).unwrap();
+        tx.send(()).unwrap();
+    }
+}
diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs
new file mode 100644
index 00000000000..78910882467
--- /dev/null
+++ b/src/libstd/old_io/process.rs
@@ -0,0 +1,1230 @@
+// 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.
+
+//! Bindings for executing child processes
+
+#![allow(non_upper_case_globals)]
+
+pub use self::StdioContainer::*;
+pub use self::ProcessExit::*;
+
+use prelude::v1::*;
+
+use collections::HashMap;
+use ffi::CString;
+use fmt;
+use old_io::pipe::{PipeStream, PipePair};
+use old_io::{IoResult, IoError};
+use old_io;
+use libc;
+use os;
+use path::BytesContainer;
+use sync::mpsc::{channel, Receiver};
+use sys::fs::FileDesc;
+use sys::process::Process as ProcessImp;
+use sys;
+use thread::Thread;
+
+#[cfg(windows)] use hash;
+#[cfg(windows)] use str;
+
+/// Signal a process to exit, without forcibly killing it. Corresponds to
+/// SIGTERM on unix platforms.
+#[cfg(windows)] pub const PleaseExitSignal: int = 15;
+/// Signal a process to exit immediately, forcibly killing it. Corresponds to
+/// SIGKILL on unix platforms.
+#[cfg(windows)] pub const MustDieSignal: int = 9;
+/// Signal a process to exit, without forcibly killing it. Corresponds to
+/// SIGTERM on unix platforms.
+#[cfg(not(windows))] pub const PleaseExitSignal: int = libc::SIGTERM as int;
+/// Signal a process to exit immediately, forcibly killing it. Corresponds to
+/// SIGKILL on unix platforms.
+#[cfg(not(windows))] pub const MustDieSignal: int = libc::SIGKILL as int;
+
+/// Representation of a running or exited child process.
+///
+/// This structure is used to represent and manage child processes. A child
+/// process is created via the `Command` struct, which configures the spawning
+/// process and can itself be constructed using a builder-style interface.
+///
+/// # Example
+///
+/// ```should_fail
+/// use std::old_io::Command;
+///
+/// let mut child = match Command::new("/bin/cat").arg("file.txt").spawn() {
+///     Ok(child) => child,
+///     Err(e) => panic!("failed to execute child: {}", e),
+/// };
+///
+/// let contents = child.stdout.as_mut().unwrap().read_to_end();
+/// assert!(child.wait().unwrap().success());
+/// ```
+pub struct Process {
+    handle: ProcessImp,
+    forget: bool,
+
+    /// None until wait() is called.
+    exit_code: Option<ProcessExit>,
+
+    /// Manually delivered signal
+    exit_signal: Option<int>,
+
+    /// Deadline after which wait() will return
+    deadline: u64,
+
+    /// Handle to the child's stdin, if the `stdin` field of this process's
+    /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
+    pub stdin: Option<PipeStream>,
+
+    /// Handle to the child's stdout, if the `stdout` field of this process's
+    /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
+    pub stdout: Option<PipeStream>,
+
+    /// Handle to the child's stderr, if the `stderr` field of this process's
+    /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
+    pub stderr: Option<PipeStream>,
+}
+
+/// A representation of environment variable name
+/// It compares case-insensitive on Windows and case-sensitive everywhere else.
+#[cfg(not(windows))]
+#[derive(Hash, PartialEq, Eq, Clone, Show)]
+struct EnvKey(CString);
+
+#[doc(hidden)]
+#[cfg(windows)]
+#[derive(Eq, Clone, Show)]
+struct EnvKey(CString);
+
+#[cfg(windows)]
+impl<H: hash::Writer + hash::Hasher> hash::Hash<H> for EnvKey {
+    fn hash(&self, state: &mut H) {
+        let &EnvKey(ref x) = self;
+        match str::from_utf8(x.as_bytes()) {
+            Ok(s) => for ch in s.chars() {
+                (ch as u8 as char).to_lowercase().hash(state);
+            },
+            Err(..) => x.hash(state)
+        }
+    }
+}
+
+#[cfg(windows)]
+impl PartialEq for EnvKey {
+    fn eq(&self, other: &EnvKey) -> bool {
+        let &EnvKey(ref x) = self;
+        let &EnvKey(ref y) = other;
+        match (str::from_utf8(x.as_bytes()), str::from_utf8(y.as_bytes())) {
+            (Ok(xs), Ok(ys)) => {
+                if xs.len() != ys.len() {
+                    return false
+                } else {
+                    for (xch, ych) in xs.chars().zip(ys.chars()) {
+                        if xch.to_lowercase() != ych.to_lowercase() {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+            },
+            // If either is not a valid utf8 string, just compare them byte-wise
+            _ => return x.eq(y)
+        }
+    }
+}
+
+impl BytesContainer for EnvKey {
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        let &EnvKey(ref k) = self;
+        k.container_as_bytes()
+    }
+}
+
+/// A HashMap representation of environment variables.
+pub type EnvMap = HashMap<EnvKey, CString>;
+
+/// The `Command` type acts as a process builder, providing fine-grained control
+/// over how a new process should be spawned. A default configuration can be
+/// generated using `Command::new(program)`, where `program` gives a path to the
+/// program to be executed. Additional builder methods allow the configuration
+/// to be changed (for example, by adding arguments) prior to spawning:
+///
+/// ```
+/// use std::old_io::Command;
+///
+/// let mut process = match Command::new("sh").arg("-c").arg("echo hello").spawn() {
+///   Ok(p) => p,
+///   Err(e) => panic!("failed to execute process: {}", e),
+/// };
+///
+/// let output = process.stdout.as_mut().unwrap().read_to_end();
+/// ```
+#[derive(Clone)]
+pub struct Command {
+    // The internal data for the builder. Documented by the builder
+    // methods below, and serialized into rt::rtio::ProcessConfig.
+    program: CString,
+    args: Vec<CString>,
+    env: Option<EnvMap>,
+    cwd: Option<CString>,
+    stdin: StdioContainer,
+    stdout: StdioContainer,
+    stderr: StdioContainer,
+    uid: Option<uint>,
+    gid: Option<uint>,
+    detach: bool,
+}
+
+// FIXME (#12938): Until DST lands, we cannot decompose &str into & and str, so
+// we cannot usefully take BytesContainer arguments by reference (without forcing an
+// additional & around &str). So we are instead temporarily adding an instance
+// for &Path, so that we can take BytesContainer as owned. When DST lands, the &Path
+// instance should be removed, and arguments bound by BytesContainer should be passed by
+// reference. (Here: {new, arg, args, env}.)
+
+impl Command {
+    /// Constructs a new `Command` for launching the program at
+    /// path `program`, with the following default configuration:
+    ///
+    /// * No arguments to the program
+    /// * Inherit the current process's environment
+    /// * Inherit the current process's working directory
+    /// * A readable pipe for stdin (file descriptor 0)
+    /// * A writeable pipe for stdout and stderr (file descriptors 1 and 2)
+    ///
+    /// Builder methods are provided to change these defaults and
+    /// otherwise configure the process.
+    pub fn new<T: BytesContainer>(program: T) -> Command {
+        Command {
+            program: CString::from_slice(program.container_as_bytes()),
+            args: Vec::new(),
+            env: None,
+            cwd: None,
+            stdin: CreatePipe(true, false),
+            stdout: CreatePipe(false, true),
+            stderr: CreatePipe(false, true),
+            uid: None,
+            gid: None,
+            detach: false,
+        }
+    }
+
+    /// Add an argument to pass to the program.
+    pub fn arg<'a, T: BytesContainer>(&'a mut self, arg: T) -> &'a mut Command {
+        self.args.push(CString::from_slice(arg.container_as_bytes()));
+        self
+    }
+
+    /// Add multiple arguments to pass to the program.
+    pub fn args<'a, T: BytesContainer>(&'a mut self, args: &[T]) -> &'a mut Command {
+        self.args.extend(args.iter().map(|arg| {
+            CString::from_slice(arg.container_as_bytes())
+        }));
+        self
+    }
+    // Get a mutable borrow of the environment variable map for this `Command`.
+    fn get_env_map<'a>(&'a mut self) -> &'a mut EnvMap {
+        match self.env {
+            Some(ref mut map) => map,
+            None => {
+                // if the env is currently just inheriting from the parent's,
+                // materialize the parent's env into a hashtable.
+                self.env = Some(os::env_as_bytes().into_iter().map(|(k, v)| {
+                    (EnvKey(CString::from_slice(k.as_slice())),
+                     CString::from_slice(v.as_slice()))
+                }).collect());
+                self.env.as_mut().unwrap()
+            }
+        }
+    }
+
+    /// Inserts or updates an environment variable mapping.
+    ///
+    /// Note that environment variable names are case-insensitive (but case-preserving) on Windows,
+    /// and case-sensitive on all other platforms.
+    pub fn env<'a, T, U>(&'a mut self, key: T, val: U)
+                         -> &'a mut Command
+                         where T: BytesContainer, U: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        let val = CString::from_slice(val.container_as_bytes());
+        self.get_env_map().insert(key, val);
+        self
+    }
+
+    /// Removes an environment variable mapping.
+    pub fn env_remove<'a, T>(&'a mut self, key: T) -> &'a mut Command
+                             where T: BytesContainer {
+        let key = EnvKey(CString::from_slice(key.container_as_bytes()));
+        self.get_env_map().remove(&key);
+        self
+    }
+
+    /// Sets the entire environment map for the child process.
+    ///
+    /// If the given slice contains multiple instances of an environment
+    /// variable, the *rightmost* instance will determine the value.
+    pub fn env_set_all<'a, T, U>(&'a mut self, env: &[(T,U)])
+                                 -> &'a mut Command
+                                 where T: BytesContainer, U: BytesContainer {
+        self.env = Some(env.iter().map(|&(ref k, ref v)| {
+            (EnvKey(CString::from_slice(k.container_as_bytes())),
+             CString::from_slice(v.container_as_bytes()))
+        }).collect());
+        self
+    }
+
+    /// Set the working directory for the child process.
+    pub fn cwd<'a>(&'a mut self, dir: &Path) -> &'a mut Command {
+        self.cwd = Some(CString::from_slice(dir.as_vec()));
+        self
+    }
+
+    /// Configuration for the child process's stdin handle (file descriptor 0).
+    /// Defaults to `CreatePipe(true, false)` so the input can be written to.
+    pub fn stdin<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stdin = cfg;
+        self
+    }
+
+    /// Configuration for the child process's stdout handle (file descriptor 1).
+    /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+    pub fn stdout<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stdout = cfg;
+        self
+    }
+
+    /// Configuration for the child process's stderr handle (file descriptor 2).
+    /// Defaults to `CreatePipe(false, true)` so the output can be collected.
+    pub fn stderr<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
+        self.stderr = cfg;
+        self
+    }
+
+    /// Sets the child process's user id. This translates to a `setuid` call in
+    /// the child process. Setting this value on windows will cause the spawn to
+    /// fail. Failure in the `setuid` call on unix will also cause the spawn to
+    /// fail.
+    pub fn uid<'a>(&'a mut self, id: uint) -> &'a mut Command {
+        self.uid = Some(id);
+        self
+    }
+
+    /// Similar to `uid`, but sets the group id of the child process. This has
+    /// the same semantics as the `uid` field.
+    pub fn gid<'a>(&'a mut self, id: uint) -> &'a mut Command {
+        self.gid = Some(id);
+        self
+    }
+
+    /// Sets the child process to be spawned in a detached state. On unix, this
+    /// means that the child is the leader of a new process group.
+    pub fn detached<'a>(&'a mut self) -> &'a mut Command {
+        self.detach = true;
+        self
+    }
+
+    /// Executes the command as a child process, which is returned.
+    pub fn spawn(&self) -> IoResult<Process> {
+        let (their_stdin, our_stdin) = try!(setup_io(self.stdin));
+        let (their_stdout, our_stdout) = try!(setup_io(self.stdout));
+        let (their_stderr, our_stderr) = try!(setup_io(self.stderr));
+
+        match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) {
+            Err(e) => Err(e),
+            Ok(handle) => Ok(Process {
+                handle: handle,
+                forget: false,
+                exit_code: None,
+                exit_signal: None,
+                deadline: 0,
+                stdin: our_stdin,
+                stdout: our_stdout,
+                stderr: our_stderr,
+            })
+        }
+    }
+
+    /// Executes the command as a child process, waiting for it to finish and
+    /// collecting all of its output.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use std::old_io::Command;
+    ///
+    /// let output = match Command::new("cat").arg("foot.txt").output() {
+    ///     Ok(output) => output,
+    ///     Err(e) => panic!("failed to execute process: {}", e),
+    /// };
+    ///
+    /// println!("status: {}", output.status);
+    /// println!("stdout: {}", String::from_utf8_lossy(output.output.as_slice()));
+    /// println!("stderr: {}", String::from_utf8_lossy(output.error.as_slice()));
+    /// ```
+    pub fn output(&self) -> IoResult<ProcessOutput> {
+        self.spawn().and_then(|p| p.wait_with_output())
+    }
+
+    /// Executes a command as a child process, waiting for it to finish and
+    /// collecting its exit status.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use std::old_io::Command;
+    ///
+    /// let status = match Command::new("ls").status() {
+    ///     Ok(status) => status,
+    ///     Err(e) => panic!("failed to execute process: {}", e),
+    /// };
+    ///
+    /// println!("process exited with: {}", status);
+    /// ```
+    pub fn status(&self) -> IoResult<ProcessExit> {
+        self.spawn().and_then(|mut p| p.wait())
+    }
+}
+
+impl fmt::Debug for Command {
+    /// Format the program and arguments of a Command for display. Any
+    /// non-utf8 data is lossily converted using the utf8 replacement
+    /// character.
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        try!(write!(f, "{}", String::from_utf8_lossy(self.program.as_bytes())));
+        for arg in self.args.iter() {
+            try!(write!(f, " '{}'", String::from_utf8_lossy(arg.as_bytes())));
+        }
+        Ok(())
+    }
+}
+
+fn setup_io(io: StdioContainer) -> IoResult<(Option<PipeStream>, Option<PipeStream>)> {
+    let ours;
+    let theirs;
+    match io {
+        Ignored => {
+            theirs = None;
+            ours = None;
+        }
+        InheritFd(fd) => {
+            theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false)));
+            ours = None;
+        }
+        CreatePipe(readable, _writable) => {
+            let PipePair { reader, writer } = try!(PipeStream::pair());
+            if readable {
+                theirs = Some(reader);
+                ours = Some(writer);
+            } else {
+                theirs = Some(writer);
+                ours = Some(reader);
+            }
+        }
+    }
+    Ok((theirs, ours))
+}
+
+// Allow the sys module to get access to the Command state
+impl sys::process::ProcessConfig<EnvKey, CString> for Command {
+    fn program(&self) -> &CString {
+        &self.program
+    }
+    fn args(&self) -> &[CString] {
+        self.args.as_slice()
+    }
+    fn env(&self) -> Option<&EnvMap> {
+        self.env.as_ref()
+    }
+    fn cwd(&self) -> Option<&CString> {
+        self.cwd.as_ref()
+    }
+    fn uid(&self) -> Option<uint> {
+        self.uid.clone()
+    }
+    fn gid(&self) -> Option<uint> {
+        self.gid.clone()
+    }
+    fn detach(&self) -> bool {
+        self.detach
+    }
+
+}
+
+/// The output of a finished process.
+#[derive(PartialEq, Eq, Clone)]
+pub struct ProcessOutput {
+    /// The status (exit code) of the process.
+    pub status: ProcessExit,
+    /// The data that the process wrote to stdout.
+    pub output: Vec<u8>,
+    /// The data that the process wrote to stderr.
+    pub error: Vec<u8>,
+}
+
+/// Describes what to do with a standard io stream for a child process.
+#[derive(Clone, Copy)]
+pub enum StdioContainer {
+    /// This stream will be ignored. This is the equivalent of attaching the
+    /// stream to `/dev/null`
+    Ignored,
+
+    /// The specified file descriptor is inherited for the stream which it is
+    /// specified for. Ownership of the file descriptor is *not* taken, so the
+    /// caller must clean it up.
+    InheritFd(libc::c_int),
+
+    /// Creates a pipe for the specified file descriptor which will be created
+    /// when the process is spawned.
+    ///
+    /// The first boolean argument is whether the pipe is readable, and the
+    /// second is whether it is writable. These properties are from the view of
+    /// the *child* process, not the parent process.
+    CreatePipe(bool /* readable */, bool /* writable */),
+}
+
+/// Describes the result of a process after it has terminated.
+/// Note that Windows have no signals, so the result is usually ExitStatus.
+#[derive(PartialEq, Eq, Clone, Copy, Show)]
+pub enum ProcessExit {
+    /// Normal termination with an exit status.
+    ExitStatus(int),
+
+    /// Termination by signal, with the signal number.
+    ExitSignal(int),
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl fmt::Display for ProcessExit {
+    /// Format a ProcessExit enum, to nicely present the information.
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            ExitStatus(code) =>  write!(f, "exit code: {}", code),
+            ExitSignal(code) =>  write!(f, "signal: {}", code),
+        }
+    }
+}
+
+impl ProcessExit {
+    /// Was termination successful? Signal termination not considered a success,
+    /// and success is defined as a zero exit status.
+    pub fn success(&self) -> bool {
+        return self.matches_exit_status(0);
+    }
+
+    /// Checks whether this ProcessExit matches the given exit status.
+    /// Termination by signal will never match an exit code.
+    pub fn matches_exit_status(&self, wanted: int) -> bool {
+        *self == ExitStatus(wanted)
+    }
+}
+
+impl Process {
+    /// Sends `signal` to another process in the system identified by `id`.
+    ///
+    /// Note that windows doesn't quite have the same model as unix, so some
+    /// unix signals are mapped to windows signals. Notably, unix termination
+    /// signals (SIGTERM/SIGKILL/SIGINT) are translated to `TerminateProcess`.
+    ///
+    /// Additionally, a signal number of 0 can check for existence of the target
+    /// process. Note, though, that on some platforms signals will continue to
+    /// be successfully delivered if the child has exited, but not yet been
+    /// reaped.
+    pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
+        unsafe { ProcessImp::killpid(id, signal) }
+    }
+
+    /// Returns the process id of this child process
+    pub fn id(&self) -> libc::pid_t { self.handle.id() }
+
+    /// Sends the specified signal to the child process, returning whether the
+    /// signal could be delivered or not.
+    ///
+    /// Note that signal 0 is interpreted as a poll to check whether the child
+    /// process is still alive or not. If an error is returned, then the child
+    /// process has exited.
+    ///
+    /// On some unix platforms signals will continue to be received after a
+    /// child has exited but not yet been reaped. In order to report the status
+    /// of signal delivery correctly, unix implementations may invoke
+    /// `waitpid()` with `WNOHANG` in order to reap the child as necessary.
+    ///
+    /// # Errors
+    ///
+    /// If the signal delivery fails, the corresponding error is returned.
+    pub fn signal(&mut self, signal: int) -> IoResult<()> {
+        #[cfg(unix)] fn collect_status(p: &mut Process) {
+            // On Linux (and possibly other unices), a process that has exited will
+            // continue to accept signals because it is "defunct". The delivery of
+            // signals will only fail once the child has been reaped. For this
+            // reason, if the process hasn't exited yet, then we attempt to collect
+            // their status with WNOHANG.
+            if p.exit_code.is_none() {
+                match p.handle.try_wait() {
+                    Some(code) => { p.exit_code = Some(code); }
+                    None => {}
+                }
+            }
+        }
+        #[cfg(windows)] fn collect_status(_p: &mut Process) {}
+
+        collect_status(self);
+
+        // 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_some() {
+            return Err(IoError {
+                kind: old_io::InvalidInput,
+                desc: "invalid argument: can't kill an exited process",
+                detail: None,
+            })
+        }
+
+        // A successfully delivered signal that isn't 0 (just a poll for being
+        // alive) is recorded for windows (see wait())
+        match unsafe { self.handle.kill(signal) } {
+            Ok(()) if signal == 0 => Ok(()),
+            Ok(()) => { self.exit_signal = Some(signal); Ok(()) }
+            Err(e) => Err(e),
+        }
+
+    }
+
+    /// Sends a signal to this child requesting that it exits. This is
+    /// equivalent to sending a SIGTERM on unix platforms.
+    pub fn signal_exit(&mut self) -> IoResult<()> {
+        self.signal(PleaseExitSignal)
+    }
+
+    /// Sends a signal to this child forcing it to exit. This is equivalent to
+    /// sending a SIGKILL on unix platforms.
+    pub fn signal_kill(&mut self) -> IoResult<()> {
+        self.signal(MustDieSignal)
+    }
+
+    /// Wait for the child to exit completely, returning the status that it
+    /// exited with. This function will continue to have the same return value
+    /// after it has been called at least once.
+    ///
+    /// The stdin handle to the child process will be closed before waiting.
+    ///
+    /// # Errors
+    ///
+    /// This function can fail if a timeout was previously specified via
+    /// `set_timeout` and the timeout expires before the child exits.
+    pub fn wait(&mut self) -> IoResult<ProcessExit> {
+        drop(self.stdin.take());
+        match self.exit_code {
+            Some(code) => Ok(code),
+            None => {
+                let code = try!(self.handle.wait(self.deadline));
+                // On windows, waitpid will never return a signal. If a signal
+                // was successfully delivered to the process, however, we can
+                // consider it as having died via a signal.
+                let code = match self.exit_signal {
+                    None => code,
+                    Some(signal) if cfg!(windows) => ExitSignal(signal),
+                    Some(..) => code,
+                };
+                self.exit_code = Some(code);
+                Ok(code)
+            }
+        }
+    }
+
+    /// Sets a timeout, in milliseconds, for future calls to wait().
+    ///
+    /// The argument specified is a relative distance into the future, in
+    /// milliseconds, after which any call to wait() will return immediately
+    /// with a timeout error, and all future calls to wait() will not block.
+    ///
+    /// A value of `None` will clear any previous timeout, and a value of `Some`
+    /// will override any previously set timeout.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::old_io::{Command, IoResult};
+    /// use std::old_io::process::ProcessExit;
+    ///
+    /// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> {
+    ///     let mut p = try!(Command::new("long-running-process").spawn());
+    ///
+    ///     // give the process 10 seconds to finish completely
+    ///     p.set_timeout(Some(10_000));
+    ///     match p.wait() {
+    ///         Ok(status) => return Ok(status),
+    ///         Err(..) => {}
+    ///     }
+    ///
+    ///     // Attempt to exit gracefully, but don't wait for it too long
+    ///     try!(p.signal_exit());
+    ///     p.set_timeout(Some(1_000));
+    ///     match p.wait() {
+    ///         Ok(status) => return Ok(status),
+    ///         Err(..) => {}
+    ///     }
+    ///
+    ///     // Well, we did our best, forcefully kill the process
+    ///     try!(p.signal_kill());
+    ///     p.set_timeout(None);
+    ///     p.wait()
+    /// }
+    /// ```
+    #[unstable(feature = "io",
+               reason = "the type of the timeout is likely to change")]
+    pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
+        self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0);
+    }
+
+    /// Simultaneously wait for the child to exit and collect all remaining
+    /// output on the stdout/stderr handles, returning a `ProcessOutput`
+    /// instance.
+    ///
+    /// The stdin handle to the child is closed before waiting.
+    ///
+    /// # Errors
+    ///
+    /// This function can fail for any of the same reasons that `wait()` can
+    /// fail.
+    pub fn wait_with_output(mut self) -> IoResult<ProcessOutput> {
+        drop(self.stdin.take());
+        fn read(stream: Option<old_io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
+            let (tx, rx) = channel();
+            match stream {
+                Some(stream) => {
+                    Thread::spawn(move |:| {
+                        let mut stream = stream;
+                        tx.send(stream.read_to_end()).unwrap();
+                    });
+                }
+                None => tx.send(Ok(Vec::new())).unwrap()
+            }
+            rx
+        }
+        let stdout = read(self.stdout.take());
+        let stderr = read(self.stderr.take());
+
+        let status = try!(self.wait());
+
+        Ok(ProcessOutput {
+            status: status,
+            output: stdout.recv().unwrap().unwrap_or(Vec::new()),
+            error:  stderr.recv().unwrap().unwrap_or(Vec::new()),
+        })
+    }
+
+    /// Forgets this process, allowing it to outlive the parent
+    ///
+    /// This function will forcefully prevent calling `wait()` on the child
+    /// process in the destructor, allowing the child to outlive the
+    /// parent. Note that this operation can easily lead to leaking the
+    /// resources of the child process, so care must be taken when
+    /// invoking this method.
+    pub fn forget(mut self) {
+        self.forget = true;
+    }
+}
+
+impl Drop for Process {
+    fn drop(&mut self) {
+        if self.forget { return }
+
+        // Close all I/O before exiting to ensure that the child doesn't wait
+        // forever to print some text or something similar.
+        drop(self.stdin.take());
+        drop(self.stdout.take());
+        drop(self.stderr.take());
+
+        self.set_timeout(None);
+        let _ = self.wait().unwrap();
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use old_io::{Truncate, Write, TimedOut, timer, process, FileNotFound};
+    use prelude::v1::{Ok, Err, range, drop, Some, None, Vec};
+    use prelude::v1::{Path, String, Reader, Writer, Clone};
+    use prelude::v1::{SliceExt, Str, StrExt, AsSlice, ToString, GenericPath};
+    use old_io::fs::PathExtensions;
+    use old_io::timer::*;
+    use rt::running_on_valgrind;
+    use str;
+    use super::{CreatePipe};
+    use super::{InheritFd, Process, PleaseExitSignal, Command, ProcessOutput};
+    use sync::mpsc::channel;
+    use thread::Thread;
+    use time::Duration;
+
+    // FIXME(#10380) these tests should not all be ignored on android.
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn smoke() {
+        let p = Command::new("true").spawn();
+        assert!(p.is_ok());
+        let mut p = p.unwrap();
+        assert!(p.wait().unwrap().success());
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn smoke_failure() {
+        match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() {
+            Ok(..) => panic!(),
+            Err(..) => {}
+        }
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn exit_reported_right() {
+        let p = Command::new("false").spawn();
+        assert!(p.is_ok());
+        let mut p = p.unwrap();
+        assert!(p.wait().unwrap().matches_exit_status(1));
+        drop(p.wait().clone());
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    #[test]
+    fn signal_reported_right() {
+        let p = Command::new("/bin/sh").arg("-c").arg("kill -1 $$").spawn();
+        assert!(p.is_ok());
+        let mut p = p.unwrap();
+        match p.wait().unwrap() {
+            process::ExitSignal(1) => {},
+            result => panic!("not terminated by signal 1 (instead, {})", result),
+        }
+    }
+
+    pub fn read_all(input: &mut Reader) -> String {
+        input.read_to_string().unwrap()
+    }
+
+    pub fn run_output(cmd: Command) -> String {
+        let p = cmd.spawn();
+        assert!(p.is_ok());
+        let mut p = p.unwrap();
+        assert!(p.stdout.is_some());
+        let ret = read_all(p.stdout.as_mut().unwrap() as &mut Reader);
+        assert!(p.wait().unwrap().success());
+        return ret;
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn stdout_works() {
+        let mut cmd = Command::new("echo");
+        cmd.arg("foobar").stdout(CreatePipe(false, true));
+        assert_eq!(run_output(cmd), "foobar\n");
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    #[test]
+    fn set_cwd_works() {
+        let mut cmd = Command::new("/bin/sh");
+        cmd.arg("-c").arg("pwd")
+           .cwd(&Path::new("/"))
+           .stdout(CreatePipe(false, true));
+        assert_eq!(run_output(cmd), "/\n");
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    #[test]
+    fn stdin_works() {
+        let mut p = Command::new("/bin/sh")
+                            .arg("-c").arg("read line; echo $line")
+                            .stdin(CreatePipe(true, false))
+                            .stdout(CreatePipe(false, true))
+                            .spawn().unwrap();
+        p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap();
+        drop(p.stdin.take());
+        let out = read_all(p.stdout.as_mut().unwrap() as &mut Reader);
+        assert!(p.wait().unwrap().success());
+        assert_eq!(out, "foobar\n");
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn detach_works() {
+        let mut p = Command::new("true").detached().spawn().unwrap();
+        assert!(p.wait().unwrap().success());
+    }
+
+    #[cfg(windows)]
+    #[test]
+    fn uid_fails_on_windows() {
+        assert!(Command::new("test").uid(10).spawn().is_err());
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    #[test]
+    fn uid_works() {
+        use libc;
+        let mut p = Command::new("/bin/sh")
+                            .arg("-c").arg("true")
+                            .uid(unsafe { libc::getuid() as uint })
+                            .gid(unsafe { libc::getgid() as uint })
+                            .spawn().unwrap();
+        assert!(p.wait().unwrap().success());
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    #[test]
+    fn uid_to_root_fails() {
+        use libc;
+
+        // if we're already root, this isn't a valid test. Most of the bots run
+        // as non-root though (android is an exception).
+        if unsafe { libc::getuid() == 0 } { return }
+        assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err());
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_process_status() {
+        let mut status = Command::new("false").status().unwrap();
+        assert!(status.matches_exit_status(1));
+
+        status = Command::new("true").status().unwrap();
+        assert!(status.success());
+    }
+
+    #[test]
+    fn test_process_output_fail_to_start() {
+        match Command::new("/no-binary-by-this-name-should-exist").output() {
+            Err(e) => assert_eq!(e.kind, FileNotFound),
+            Ok(..) => panic!()
+        }
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_process_output_output() {
+        let ProcessOutput {status, output, error}
+             = Command::new("echo").arg("hello").output().unwrap();
+        let output_str = str::from_utf8(output.as_slice()).unwrap();
+
+        assert!(status.success());
+        assert_eq!(output_str.trim().to_string(), "hello");
+        // FIXME #7224
+        if !running_on_valgrind() {
+            assert_eq!(error, Vec::new());
+        }
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_process_output_error() {
+        let ProcessOutput {status, output, error}
+             = Command::new("mkdir").arg(".").output().unwrap();
+
+        assert!(status.matches_exit_status(1));
+        assert_eq!(output, Vec::new());
+        assert!(!error.is_empty());
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_finish_once() {
+        let mut prog = Command::new("false").spawn().unwrap();
+        assert!(prog.wait().unwrap().matches_exit_status(1));
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_finish_twice() {
+        let mut prog = Command::new("false").spawn().unwrap();
+        assert!(prog.wait().unwrap().matches_exit_status(1));
+        assert!(prog.wait().unwrap().matches_exit_status(1));
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_wait_with_output_once() {
+        let prog = Command::new("echo").arg("hello").spawn().unwrap();
+        let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap();
+        let output_str = str::from_utf8(output.as_slice()).unwrap();
+
+        assert!(status.success());
+        assert_eq!(output_str.trim().to_string(), "hello");
+        // FIXME #7224
+        if !running_on_valgrind() {
+            assert_eq!(error, Vec::new());
+        }
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    pub fn pwd_cmd() -> Command {
+        Command::new("pwd")
+    }
+    #[cfg(target_os="android")]
+    pub fn pwd_cmd() -> Command {
+        let mut cmd = Command::new("/system/bin/sh");
+        cmd.arg("-c").arg("pwd");
+        cmd
+    }
+
+    #[cfg(windows)]
+    pub fn pwd_cmd() -> Command {
+        let mut cmd = Command::new("cmd");
+        cmd.arg("/c").arg("cd");
+        cmd
+    }
+
+    #[test]
+    fn test_keep_current_working_dir() {
+        use os;
+        let prog = pwd_cmd().spawn().unwrap();
+
+        let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
+        let parent_dir = os::getcwd().unwrap();
+        let child_dir = Path::new(output.trim());
+
+        let parent_stat = parent_dir.stat().unwrap();
+        let child_stat = child_dir.stat().unwrap();
+
+        assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
+        assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
+    }
+
+    #[test]
+    fn test_change_working_directory() {
+        use os;
+        // test changing to the parent of os::getcwd() because we know
+        // the path exists (and os::getcwd() is not expected to be root)
+        let parent_dir = os::getcwd().unwrap().dir_path();
+        let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap();
+
+        let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
+        let child_dir = Path::new(output.trim());
+
+        let parent_stat = parent_dir.stat().unwrap();
+        let child_stat = child_dir.stat().unwrap();
+
+        assert_eq!(parent_stat.unstable.device, child_stat.unstable.device);
+        assert_eq!(parent_stat.unstable.inode, child_stat.unstable.inode);
+    }
+
+    #[cfg(all(unix, not(target_os="android")))]
+    pub fn env_cmd() -> Command {
+        Command::new("env")
+    }
+    #[cfg(target_os="android")]
+    pub fn env_cmd() -> Command {
+        let mut cmd = Command::new("/system/bin/sh");
+        cmd.arg("-c").arg("set");
+        cmd
+    }
+
+    #[cfg(windows)]
+    pub fn env_cmd() -> Command {
+        let mut cmd = Command::new("cmd");
+        cmd.arg("/c").arg("set");
+        cmd
+    }
+
+    #[cfg(not(target_os="android"))]
+    #[test]
+    fn test_inherit_env() {
+        use os;
+        if running_on_valgrind() { return; }
+
+        let prog = env_cmd().spawn().unwrap();
+        let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
+
+        let r = os::env();
+        for &(ref k, ref v) in r.iter() {
+            // don't check windows magical empty-named variables
+            assert!(k.is_empty() ||
+                    output.contains(format!("{}={}", *k, *v).as_slice()),
+                    "output doesn't contain `{}={}`\n{}",
+                    k, v, output);
+        }
+    }
+    #[cfg(target_os="android")]
+    #[test]
+    fn test_inherit_env() {
+        use os;
+        if running_on_valgrind() { return; }
+
+        let mut prog = env_cmd().spawn().unwrap();
+        let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap();
+
+        let r = os::env();
+        for &(ref k, ref v) in r.iter() {
+            // don't check android RANDOM variables
+            if *k != "RANDOM".to_string() {
+                assert!(output.contains(format!("{}={}",
+                                                *k,
+                                                *v).as_slice()) ||
+                        output.contains(format!("{}=\'{}\'",
+                                                *k,
+                                                *v).as_slice()));
+            }
+        }
+    }
+
+    #[test]
+    fn test_override_env() {
+        use os;
+
+        // In some build environments (such as chrooted Nix builds), `env` can
+        // only be found in the explicitly-provided PATH env variable, not in
+        // default places such as /bin or /usr/bin. So we need to pass through
+        // PATH to our sub-process.
+        let path_val: String;
+        let mut new_env = vec![("RUN_TEST_NEW_ENV", "123")];
+        match os::getenv("PATH") {
+            None => {}
+            Some(val) => {
+                path_val = val;
+                new_env.push(("PATH", path_val.as_slice()))
+            }
+        }
+
+        let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap();
+        let result = prog.wait_with_output().unwrap();
+        let output = String::from_utf8_lossy(result.output.as_slice()).to_string();
+
+        assert!(output.contains("RUN_TEST_NEW_ENV=123"),
+                "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+    }
+
+    #[test]
+    fn test_add_to_env() {
+        let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap();
+        let result = prog.wait_with_output().unwrap();
+        let output = String::from_utf8_lossy(result.output.as_slice()).to_string();
+
+        assert!(output.contains("RUN_TEST_NEW_ENV=123"),
+                "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+    }
+
+    #[cfg(unix)]
+    pub fn sleeper() -> Process {
+        Command::new("sleep").arg("1000").spawn().unwrap()
+    }
+    #[cfg(windows)]
+    pub fn sleeper() -> Process {
+        // There's a `timeout` command on windows, but it doesn't like having
+        // its output piped, so instead just ping ourselves a few times with
+        // gaps in between so we're sure this process is alive for awhile
+        Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn().unwrap()
+    }
+
+    #[test]
+    fn test_kill() {
+        let mut p = sleeper();
+        Process::kill(p.id(), PleaseExitSignal).unwrap();
+        assert!(!p.wait().unwrap().success());
+    }
+
+    #[test]
+    fn test_exists() {
+        let mut p = sleeper();
+        assert!(Process::kill(p.id(), 0).is_ok());
+        p.signal_kill().unwrap();
+        assert!(!p.wait().unwrap().success());
+    }
+
+    #[test]
+    fn test_zero() {
+        let mut p = sleeper();
+        p.signal_kill().unwrap();
+        for _ in range(0i, 20) {
+            if p.signal(0).is_err() {
+                assert!(!p.wait().unwrap().success());
+                return
+            }
+            timer::sleep(Duration::milliseconds(100));
+        }
+        panic!("never saw the child go away");
+    }
+
+    #[test]
+    fn wait_timeout() {
+        let mut p = sleeper();
+        p.set_timeout(Some(10));
+        assert_eq!(p.wait().err().unwrap().kind, TimedOut);
+        assert_eq!(p.wait().err().unwrap().kind, TimedOut);
+        p.signal_kill().unwrap();
+        p.set_timeout(None);
+        assert!(p.wait().is_ok());
+    }
+
+    #[test]
+    fn wait_timeout2() {
+        let (tx, rx) = channel();
+        let tx2 = tx.clone();
+        let _t = Thread::spawn(move|| {
+            let mut p = sleeper();
+            p.set_timeout(Some(10));
+            assert_eq!(p.wait().err().unwrap().kind, TimedOut);
+            p.signal_kill().unwrap();
+            tx.send(()).unwrap();
+        });
+        let _t = Thread::spawn(move|| {
+            let mut p = sleeper();
+            p.set_timeout(Some(10));
+            assert_eq!(p.wait().err().unwrap().kind, TimedOut);
+            p.signal_kill().unwrap();
+            tx2.send(()).unwrap();
+        });
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn forget() {
+        let p = sleeper();
+        let id = p.id();
+        p.forget();
+        assert!(Process::kill(id, 0).is_ok());
+        assert!(Process::kill(id, PleaseExitSignal).is_ok());
+    }
+
+    #[test]
+    fn dont_close_fd_on_command_spawn() {
+        use sys::fs;
+
+        let path = if cfg!(windows) {
+            Path::new("NUL")
+        } else {
+            Path::new("/dev/null")
+        };
+
+        let fdes = match fs::open(&path, Truncate, Write) {
+            Ok(f) => f,
+            Err(_) => panic!("failed to open file descriptor"),
+        };
+
+        let mut cmd = pwd_cmd();
+        let _ = cmd.stdout(InheritFd(fdes.fd()));
+        assert!(cmd.status().unwrap().success());
+        assert!(fdes.write("extra write\n".as_bytes()).is_ok());
+    }
+
+    #[test]
+    #[cfg(windows)]
+    fn env_map_keys_ci() {
+        use ffi::CString;
+        use super::EnvKey;
+        let mut cmd = Command::new("");
+        cmd.env("path", "foo");
+        cmd.env("Path", "bar");
+        let env = &cmd.env.unwrap();
+        let val = env.get(&EnvKey(CString::from_slice(b"PATH")));
+        assert!(val.unwrap() == &CString::from_slice(b"bar"));
+    }
+}
diff --git a/src/libstd/old_io/result.rs b/src/libstd/old_io/result.rs
new file mode 100644
index 00000000000..96b979860ae
--- /dev/null
+++ b/src/libstd/old_io/result.rs
@@ -0,0 +1,129 @@
+// 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.
+
+//! Implementations of I/O traits for the IoResult type
+//!
+//! I/O constructors return option types to allow errors to be handled.
+//! These implementations allow e.g. `IoResult<File>` to be used
+//! as a `Reader` without unwrapping the result first.
+
+use clone::Clone;
+use result::Result::{Ok, Err};
+use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle, IoResult};
+
+impl<W: Writer> Writer for IoResult<W> {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        match *self {
+            Ok(ref mut writer) => writer.write_all(buf),
+            Err(ref e) => Err((*e).clone())
+        }
+    }
+
+    fn flush(&mut self) -> IoResult<()> {
+        match *self {
+            Ok(ref mut writer) => writer.flush(),
+            Err(ref e) => Err(e.clone()),
+        }
+    }
+}
+
+impl<R: Reader> Reader for IoResult<R> {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        match *self {
+            Ok(ref mut reader) => reader.read(buf),
+            Err(ref e) => Err(e.clone()),
+        }
+    }
+}
+
+impl<S: Seek> Seek for IoResult<S> {
+    fn tell(&self) -> IoResult<u64> {
+        match *self {
+            Ok(ref seeker) => seeker.tell(),
+            Err(ref e) => Err(e.clone()),
+        }
+    }
+    fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
+        match *self {
+            Ok(ref mut seeker) => seeker.seek(pos, style),
+            Err(ref e) => Err(e.clone())
+        }
+    }
+}
+
+impl<T, A: Acceptor<T>, L: Listener<T, A>> Listener<T, A> for IoResult<L> {
+    fn listen(self) -> IoResult<A> {
+        match self {
+            Ok(listener) => listener.listen(),
+            Err(e) => Err(e),
+        }
+    }
+}
+
+impl<T, A: Acceptor<T>> Acceptor<T> for IoResult<A> {
+    fn accept(&mut self) -> IoResult<T> {
+        match *self {
+            Ok(ref mut acceptor) => acceptor.accept(),
+            Err(ref e) => Err(e.clone()),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+    use super::super::mem::*;
+    use old_io;
+
+    #[test]
+    fn test_option_writer() {
+        let mut writer: old_io::IoResult<Vec<u8>> = Ok(Vec::new());
+        writer.write_all(&[0, 1, 2]).unwrap();
+        writer.flush().unwrap();
+        assert_eq!(writer.unwrap(), vec!(0, 1, 2));
+    }
+
+    #[test]
+    fn test_option_writer_error() {
+        let mut writer: old_io::IoResult<Vec<u8>> =
+            Err(old_io::standard_error(old_io::EndOfFile));
+
+        match writer.write_all(&[0, 0, 0]) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+        match writer.flush() {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+    }
+
+    #[test]
+    fn test_option_reader() {
+        let mut reader: old_io::IoResult<MemReader> =
+            Ok(MemReader::new(vec!(0, 1, 2, 3)));
+        let mut buf = [0, 0];
+        reader.read(&mut buf).unwrap();
+        let b: &[_] = &[0, 1];
+        assert_eq!(buf.as_slice(), b);
+    }
+
+    #[test]
+    fn test_option_reader_error() {
+        let mut reader: old_io::IoResult<MemReader> =
+            Err(old_io::standard_error(old_io::EndOfFile));
+        let mut buf = [];
+
+        match reader.read(&mut buf) {
+            Ok(..) => panic!(),
+            Err(e) => assert_eq!(e.kind, old_io::EndOfFile),
+        }
+    }
+}
diff --git a/src/libstd/old_io/stdio.rs b/src/libstd/old_io/stdio.rs
new file mode 100644
index 00000000000..70400619bea
--- /dev/null
+++ b/src/libstd/old_io/stdio.rs
@@ -0,0 +1,566 @@
+// 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.
+
+//! Non-blocking access to stdin, stdout, and stderr.
+//!
+//! This module provides bindings to the local event loop's TTY interface, using it
+//! to offer synchronous but non-blocking versions of stdio. These handles can be
+//! inspected for information about terminal dimensions or for related information
+//! about the stream or terminal to which it is attached.
+//!
+//! # Example
+//!
+//! ```rust
+//! # #![allow(unused_must_use)]
+//! use std::old_io;
+//!
+//! let mut out = old_io::stdout();
+//! out.write_all(b"Hello, world!");
+//! ```
+
+use self::StdSource::*;
+
+use boxed::Box;
+use cell::RefCell;
+use clone::Clone;
+use failure::LOCAL_STDERR;
+use fmt;
+use old_io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
+         standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
+use marker::{Sync, Send};
+use libc;
+use mem;
+use option::Option;
+use option::Option::{Some, None};
+use ops::{Deref, DerefMut, FnOnce};
+use ptr;
+use result::Result::{Ok, Err};
+use rt;
+use slice::SliceExt;
+use str::StrExt;
+use string::String;
+use sys::{fs, tty};
+use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
+use uint;
+use vec::Vec;
+
+// And so begins the tale of acquiring a uv handle to a stdio stream on all
+// platforms in all situations. Our story begins by splitting the world into two
+// categories, windows and unix. Then one day the creators of unix said let
+// there be redirection! And henceforth there was redirection away from the
+// console for standard I/O streams.
+//
+// After this day, the world split into four factions:
+//
+// 1. Unix with stdout on a terminal.
+// 2. Unix with stdout redirected.
+// 3. Windows with stdout on a terminal.
+// 4. Windows with stdout redirected.
+//
+// Many years passed, and then one day the nation of libuv decided to unify this
+// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
+// These three ideas propagated throughout the lands and the four great factions
+// decided to settle among them.
+//
+// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
+// doing so, they even enhanced themselves further then their Pipe/File
+// brethren, becoming the dominant powers.
+//
+// The group of 4, however, decided to work independently. They abandoned the
+// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
+// The members of the 4th faction decided to only align themselves with File.
+//
+// tl;dr; TTY works on everything but when windows stdout is redirected, in that
+//        case pipe also doesn't work, but magically file does!
+enum StdSource {
+    TTY(tty::TTY),
+    File(fs::FileDesc),
+}
+
+fn src<T, F>(fd: libc::c_int, _readable: bool, f: F) -> T where
+    F: FnOnce(StdSource) -> T,
+{
+    match tty::TTY::new(fd) {
+        Ok(tty) => f(TTY(tty)),
+        Err(_) => f(File(fs::FileDesc::new(fd, false))),
+    }
+}
+
+thread_local! {
+    static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
+        RefCell::new(None)
+    }
+}
+
+struct RaceBox(BufferedReader<StdReader>);
+
+unsafe impl Send for RaceBox {}
+unsafe impl Sync for RaceBox {}
+
+/// A synchronized wrapper around a buffered reader from stdin
+#[derive(Clone)]
+pub struct StdinReader {
+    inner: Arc<Mutex<RaceBox>>,
+}
+
+unsafe impl Send for StdinReader {}
+unsafe impl Sync for StdinReader {}
+
+/// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`.
+pub struct StdinReaderGuard<'a> {
+    inner: MutexGuard<'a, RaceBox>,
+}
+
+impl<'a> Deref for StdinReaderGuard<'a> {
+    type Target = BufferedReader<StdReader>;
+
+    fn deref(&self) -> &BufferedReader<StdReader> {
+        &self.inner.0
+    }
+}
+
+impl<'a> DerefMut for StdinReaderGuard<'a> {
+    fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> {
+        &mut self.inner.0
+    }
+}
+
+impl StdinReader {
+    /// Locks the `StdinReader`, granting the calling thread exclusive access
+    /// to the underlying `BufferedReader`.
+    ///
+    /// This provides access to methods like `chars` and `lines`.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use std::old_io;
+    ///
+    /// for line in old_io::stdin().lock().lines() {
+    ///     println!("{}", line.unwrap());
+    /// }
+    /// ```
+    pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
+        StdinReaderGuard {
+            inner: self.inner.lock().unwrap()
+        }
+    }
+
+    /// Like `Buffer::read_line`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_line(&mut self) -> IoResult<String> {
+        self.inner.lock().unwrap().0.read_line()
+    }
+
+    /// Like `Buffer::read_until`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
+        self.inner.lock().unwrap().0.read_until(byte)
+    }
+
+    /// Like `Buffer::read_char`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_char(&mut self) -> IoResult<char> {
+        self.inner.lock().unwrap().0.read_char()
+    }
+}
+
+impl Reader for StdinReader {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.lock().unwrap().0.read(buf)
+    }
+
+    // We have to manually delegate all of these because the default impls call
+    // read more than once and we don't want those calls to interleave (or
+    // incur the costs of repeated locking).
+
+    fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.lock().unwrap().0.read_at_least(min, buf)
+    }
+
+    fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
+        self.inner.lock().unwrap().0.push_at_least(min, len, buf)
+    }
+
+    fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
+        self.inner.lock().unwrap().0.read_to_end()
+    }
+
+    fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        self.inner.lock().unwrap().0.read_le_uint_n(nbytes)
+    }
+
+    fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        self.inner.lock().unwrap().0.read_be_uint_n(nbytes)
+    }
+}
+
+/// Creates a new handle to the stdin of the current process.
+///
+/// The returned handle is a wrapper around a global `BufferedReader` shared
+/// by all threads. If buffered access is not desired, the `stdin_raw` function
+/// is provided to provided unbuffered access to stdin.
+///
+/// See `stdout()` for more notes about this function.
+pub fn stdin() -> StdinReader {
+    // We're following the same strategy as kimundi's lazy_static library
+    static mut STDIN: *const StdinReader = 0 as *const StdinReader;
+    static ONCE: Once = ONCE_INIT;
+
+    unsafe {
+        ONCE.call_once(|| {
+            // The default buffer capacity is 64k, but apparently windows doesn't like
+            // 64k reads on stdin. See #13304 for details, but the idea is that on
+            // windows we use a slightly smaller buffer that's been seen to be
+            // acceptable.
+            let stdin = if cfg!(windows) {
+                BufferedReader::with_capacity(8 * 1024, stdin_raw())
+            } else {
+                BufferedReader::new(stdin_raw())
+            };
+            let stdin = StdinReader {
+                inner: Arc::new(Mutex::new(RaceBox(stdin)))
+            };
+            STDIN = mem::transmute(box stdin);
+
+            // Make sure to free it at exit
+            rt::at_exit(|| {
+                mem::transmute::<_, Box<StdinReader>>(STDIN);
+                STDIN = ptr::null();
+            });
+        });
+
+        (*STDIN).clone()
+    }
+}
+
+/// Creates a new non-blocking handle to the stdin of the current process.
+///
+/// Unlike `stdin()`, the returned reader is *not* a buffered reader.
+///
+/// See `stdout()` for more notes about this function.
+pub fn stdin_raw() -> StdReader {
+    src(libc::STDIN_FILENO, true, |src| StdReader { inner: src })
+}
+
+/// Creates a line-buffered 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.
+///
+/// Care should be taken when creating multiple handles to an output stream for
+/// a single process. While usage is still safe, the output may be surprising if
+/// no synchronization is performed to ensure a sane output.
+pub fn stdout() -> LineBufferedWriter<StdWriter> {
+    LineBufferedWriter::new(stdout_raw())
+}
+
+/// Creates an unbuffered handle to the stdout of the current process
+///
+/// See notes in `stdout()` for more information.
+pub fn stdout_raw() -> StdWriter {
+    src(libc::STDOUT_FILENO, false, |src| StdWriter { inner: src })
+}
+
+/// Creates a line-buffered handle to the stderr of the current process.
+///
+/// See `stdout()` for notes about this function.
+pub fn stderr() -> LineBufferedWriter<StdWriter> {
+    LineBufferedWriter::new(stderr_raw())
+}
+
+/// Creates an unbuffered handle to the stderr of the current process
+///
+/// See notes in `stdout()` for more information.
+pub fn stderr_raw() -> StdWriter {
+    src(libc::STDERR_FILENO, false, |src| StdWriter { inner: src })
+}
+
+/// Resets the task-local stdout handle to the specified writer
+///
+/// This will replace the current task's stdout handle, returning the old
+/// handle. All future calls to `print` and friends will emit their output to
+/// this specified handle.
+///
+/// Note that this does not need to be called for all new tasks; the default
+/// output handle is to the process's stdout stream.
+pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
+    let mut new = Some(stdout);
+    LOCAL_STDOUT.with(|slot| {
+        mem::replace(&mut *slot.borrow_mut(), new.take())
+    }).and_then(|mut s| {
+        let _ = s.flush();
+        Some(s)
+    })
+}
+
+/// Resets the task-local stderr handle to the specified writer
+///
+/// This will replace the current task's stderr handle, returning the old
+/// handle. Currently, the stderr handle is used for printing panic messages
+/// during task panic.
+///
+/// Note that this does not need to be called for all new tasks; the default
+/// output handle is to the process's stderr stream.
+pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> {
+    let mut new = Some(stderr);
+    LOCAL_STDERR.with(|slot| {
+        mem::replace(&mut *slot.borrow_mut(), new.take())
+    }).and_then(|mut s| {
+        let _ = s.flush();
+        Some(s)
+    })
+}
+
+// Helper to access the local task's stdout handle
+//
+// Note that this is not a safe function to expose because you can create an
+// aliased pointer very easily:
+//
+//  with_task_stdout(|io1| {
+//      with_task_stdout(|io2| {
+//          // io1 aliases io2
+//      })
+//  })
+fn with_task_stdout<F>(f: F) where F: FnOnce(&mut Writer) -> IoResult<()> {
+    let mut my_stdout = LOCAL_STDOUT.with(|slot| {
+        slot.borrow_mut().take()
+    }).unwrap_or_else(|| {
+        box stdout() as Box<Writer + Send>
+    });
+    let result = f(&mut *my_stdout);
+    let mut var = Some(my_stdout);
+    LOCAL_STDOUT.with(|slot| {
+        *slot.borrow_mut() = var.take();
+    });
+    match result {
+        Ok(()) => {}
+        Err(e) => panic!("failed printing to stdout: {:?}", e),
+    }
+}
+
+/// Flushes the local task's stdout handle.
+///
+/// By default, this stream is a line-buffering stream, so flushing may be
+/// necessary to ensure that all output is printed to the screen (if there are
+/// no newlines printed).
+///
+/// Note that logging macros do not use this stream. Using the logging macros
+/// will emit output to stderr, and while they are line buffered the log
+/// messages are always terminated in a newline (no need to flush).
+pub fn flush() {
+    with_task_stdout(|io| io.flush())
+}
+
+/// Prints a string to the stdout of the current process. No newline is emitted
+/// after the string is printed.
+pub fn print(s: &str) {
+    with_task_stdout(|io| io.write_all(s.as_bytes()))
+}
+
+/// Prints a string to the stdout of the current process. A literal
+/// `\n` character is printed to the console after the string.
+pub fn println(s: &str) {
+    with_task_stdout(|io| {
+        io.write_all(s.as_bytes()).and_then(|()| io.write_all(&[b'\n']))
+    })
+}
+
+/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
+/// with the `format_args!` macro.
+pub fn print_args(fmt: fmt::Arguments) {
+    with_task_stdout(|io| write!(io, "{}", fmt))
+}
+
+/// Similar to `println`, but takes a `fmt::Arguments` structure to be
+/// compatible with the `format_args!` macro.
+pub fn println_args(fmt: fmt::Arguments) {
+    with_task_stdout(|io| writeln!(io, "{}", fmt))
+}
+
+/// Representation of a reader of a standard input stream
+pub struct StdReader {
+    inner: StdSource
+}
+
+impl StdReader {
+    /// Returns whether this stream is attached to a TTY instance or not.
+    pub fn isatty(&self) -> bool {
+        match self.inner {
+            TTY(..) => true,
+            File(..) => false,
+        }
+    }
+}
+
+impl Reader for StdReader {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        let ret = match self.inner {
+            TTY(ref mut tty) => {
+                // Flush the task-local stdout so that weird issues like a
+                // print!'d prompt not being shown until after the user hits
+                // enter.
+                flush();
+                tty.read(buf).map(|i| i as uint)
+            },
+            File(ref mut file) => file.read(buf).map(|i| i as uint),
+        };
+        match ret {
+            // When reading a piped stdin, libuv will return 0-length reads when
+            // stdin reaches EOF. For pretty much all other streams it will
+            // return an actual EOF error, but apparently for stdin it's a
+            // little different. Hence, here we convert a 0 length read to an
+            // end-of-file indicator so the caller knows to stop reading.
+            Ok(0) => { Err(standard_error(EndOfFile)) }
+            ret @ Ok(..) | ret @ Err(..) => ret,
+        }
+    }
+}
+
+/// Representation of a writer to a standard output stream
+pub struct StdWriter {
+    inner: StdSource
+}
+
+unsafe impl Send for StdWriter {}
+unsafe impl Sync for StdWriter {}
+
+impl StdWriter {
+    /// Gets the size of this output window, if possible. This is typically used
+    /// when the writer is attached to something like a terminal, this is used
+    /// to fetch the dimensions of the terminal.
+    ///
+    /// If successful, returns `Ok((width, height))`.
+    ///
+    /// # Error
+    ///
+    /// This function will return an error if the output stream is not actually
+    /// connected to a TTY instance, or if querying the TTY instance fails.
+    pub fn winsize(&mut self) -> IoResult<(int, int)> {
+        match self.inner {
+            TTY(ref mut tty) => {
+                tty.get_winsize()
+            }
+            File(..) => {
+                Err(IoError {
+                    kind: OtherIoError,
+                    desc: "stream is not a tty",
+                    detail: None,
+                })
+            }
+        }
+    }
+
+    /// Controls whether this output stream is a "raw stream" or simply a normal
+    /// stream.
+    ///
+    /// # Error
+    ///
+    /// This function will return an error if the output stream is not actually
+    /// connected to a TTY instance, or if querying the TTY instance fails.
+    pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
+        match self.inner {
+            TTY(ref mut tty) => {
+                tty.set_raw(raw)
+            }
+            File(..) => {
+                Err(IoError {
+                    kind: OtherIoError,
+                    desc: "stream is not a tty",
+                    detail: None,
+                })
+            }
+        }
+    }
+
+    /// Returns whether this stream is attached to a TTY instance or not.
+    pub fn isatty(&self) -> bool {
+        match self.inner {
+            TTY(..) => true,
+            File(..) => false,
+        }
+    }
+}
+
+impl Writer for StdWriter {
+    fn write_all(&mut self, buf: &[u8]) -> IoResult<()> {
+        // As with stdin on windows, stdout often can't handle writes of large
+        // sizes. For an example, see #14940. For this reason, chunk the output
+        // buffer on windows, but on unix we can just write the whole buffer all
+        // at once.
+        //
+        // For some other references, it appears that this problem has been
+        // encountered by others [1] [2]. We choose the number 8KB just because
+        // libuv does the same.
+        //
+        // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
+        // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
+        let max_size = if cfg!(windows) {8192} else {uint::MAX};
+        for chunk in buf.chunks(max_size) {
+            try!(match self.inner {
+                TTY(ref mut tty) => tty.write(chunk),
+                File(ref mut file) => file.write(chunk),
+            })
+        }
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::v1::*;
+
+    use super::*;
+    use sync::mpsc::channel;
+    use thread::Thread;
+
+    #[test]
+    fn smoke() {
+        // Just make sure we can acquire handles
+        stdin();
+        stdout();
+        stderr();
+    }
+
+    #[test]
+    fn capture_stdout() {
+        use old_io::{ChanReader, ChanWriter};
+
+        let (tx, rx) = channel();
+        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
+        let _t = Thread::spawn(move|| {
+            set_stdout(box w);
+            println!("hello!");
+        });
+        assert_eq!(r.read_to_string().unwrap(), "hello!\n");
+    }
+
+    #[test]
+    fn capture_stderr() {
+        use old_io::{ChanReader, ChanWriter, Reader};
+
+        let (tx, rx) = channel();
+        let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx));
+        let _t = Thread::spawn(move || -> () {
+            set_stderr(box w);
+            panic!("my special message");
+        });
+        let s = r.read_to_string().unwrap();
+        assert!(s.contains("my special message"));
+    }
+}
diff --git a/src/libstd/old_io/tempfile.rs b/src/libstd/old_io/tempfile.rs
new file mode 100644
index 00000000000..029fef7c197
--- /dev/null
+++ b/src/libstd/old_io/tempfile.rs
@@ -0,0 +1,182 @@
+// 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.
+
+//! Temporary files and directories
+
+use old_io::{fs, IoError, IoErrorKind, IoResult};
+use old_io;
+use iter::{IteratorExt, range};
+use ops::Drop;
+use option::Option;
+use option::Option::{None, Some};
+use os;
+use path::{Path, GenericPath};
+use rand::{Rng, thread_rng};
+use result::Result::{Ok, Err};
+use str::StrExt;
+use string::String;
+
+/// A wrapper for a path to temporary directory implementing automatic
+/// scope-based deletion.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::old_io::TempDir;
+///
+/// {
+///     // create a temporary directory
+///     let tmpdir = match TempDir::new("myprefix") {
+///         Ok(dir) => dir,
+///         Err(e) => panic!("couldn't create temporary directory: {}", e)
+///     };
+///
+///     // get the path of the temporary directory without affecting the wrapper
+///     let tmppath = tmpdir.path();
+///
+///     println!("The path of temporary directory is {}", tmppath.display());
+///
+///     // the temporary directory is automatically removed when tmpdir goes
+///     // out of scope at the end of the block
+/// }
+/// {
+///     // create a temporary directory, this time using a custom path
+///     let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "myprefix") {
+///         Ok(dir) => dir,
+///         Err(e) => panic!("couldn't create temporary directory: {}", e)
+///     };
+///
+///     // get the path of the temporary directory and disable automatic deletion in the wrapper
+///     let tmppath = tmpdir.into_inner();
+///
+///     println!("The path of the not-so-temporary directory is {}", tmppath.display());
+///
+///     // the temporary directory is not removed here
+///     // because the directory is detached from the wrapper
+/// }
+/// {
+///     // create a temporary directory
+///     let tmpdir = match TempDir::new("myprefix") {
+///         Ok(dir) => dir,
+///         Err(e) => panic!("couldn't create temporary directory: {}", e)
+///     };
+///
+///     // close the temporary directory manually and check the result
+///     match tmpdir.close() {
+///         Ok(_) => println!("success!"),
+///         Err(e) => panic!("couldn't remove temporary directory: {}", e)
+///     };
+/// }
+/// ```
+pub struct TempDir {
+    path: Option<Path>,
+    disarmed: bool
+}
+
+// How many times should we (re)try finding an unused random name? It should be
+// enough that an attacker will run out of luck before we run out of patience.
+const NUM_RETRIES: u32 = 1 << 31;
+// How many characters should we include in a random file name? It needs to
+// be enough to dissuade an attacker from trying to preemptively create names
+// of that length, but not so huge that we unnecessarily drain the random number
+// generator of entropy.
+const NUM_RAND_CHARS: uint = 12;
+
+impl TempDir {
+    /// Attempts to make a temporary directory inside of `tmpdir` whose name
+    /// will have the prefix `prefix`. The directory will be automatically
+    /// deleted once the returned wrapper is destroyed.
+    ///
+    /// If no directory can be created, `Err` is returned.
+    pub fn new_in(tmpdir: &Path, prefix: &str) -> IoResult<TempDir> {
+        if !tmpdir.is_absolute() {
+            let abs_tmpdir = try!(os::make_absolute(tmpdir));
+            return TempDir::new_in(&abs_tmpdir, prefix);
+        }
+
+        let mut rng = thread_rng();
+        for _ in range(0, NUM_RETRIES) {
+            let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect();
+            let leaf = if prefix.len() > 0 {
+                format!("{}.{}", prefix, suffix)
+            } else {
+                // If we're given an empty string for a prefix, then creating a
+                // directory starting with "." would lead to it being
+                // semi-invisible on some systems.
+                suffix
+            };
+            let path = tmpdir.join(leaf);
+            match fs::mkdir(&path, old_io::USER_RWX) {
+                Ok(_) => return Ok(TempDir { path: Some(path), disarmed: false }),
+                Err(IoError{kind:IoErrorKind::PathAlreadyExists,..}) => (),
+                Err(e) => return Err(e)
+            }
+        }
+
+        return Err(IoError{
+                       kind: IoErrorKind::PathAlreadyExists,
+                       desc:"Exhausted",
+                       detail: None});
+    }
+
+    /// Attempts to make a temporary directory inside of `os::tmpdir()` whose
+    /// name will have the prefix `prefix`. The directory will be automatically
+    /// deleted once the returned wrapper is destroyed.
+    ///
+    /// If no directory can be created, `Err` is returned.
+    pub fn new(prefix: &str) -> IoResult<TempDir> {
+        TempDir::new_in(&os::tmpdir(), prefix)
+    }
+
+    /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
+    /// This discards the wrapper so that the automatic deletion of the
+    /// temporary directory is prevented.
+    pub fn into_inner(self) -> Path {
+        let mut tmpdir = self;
+        tmpdir.path.take().unwrap()
+    }
+
+    /// Access the wrapped `std::path::Path` to the temporary directory.
+    pub fn path<'a>(&'a self) -> &'a Path {
+        self.path.as_ref().unwrap()
+    }
+
+    /// Close and remove the temporary directory
+    ///
+    /// Although `TempDir` removes the directory on drop, in the destructor
+    /// any errors are ignored. To detect errors cleaning up the temporary
+    /// directory, call `close` instead.
+    pub fn close(mut self) -> IoResult<()> {
+        self.cleanup_dir()
+    }
+
+    fn cleanup_dir(&mut self) -> IoResult<()> {
+        assert!(!self.disarmed);
+        self.disarmed = true;
+        match self.path {
+            Some(ref p) => {
+                fs::rmdir_recursive(p)
+            }
+            None => Ok(())
+        }
+    }
+}
+
+impl Drop for TempDir {
+    fn drop(&mut self) {
+        if !self.disarmed {
+            let _ = self.cleanup_dir();
+        }
+    }
+}
+
+// the tests for this module need to change the path using change_dir,
+// and this doesn't play nicely with other tests so these unit tests are located
+// in src/test/run-pass/tempfile.rs
diff --git a/src/libstd/old_io/test.rs b/src/libstd/old_io/test.rs
new file mode 100644
index 00000000000..f49e2397d42
--- /dev/null
+++ b/src/libstd/old_io/test.rs
@@ -0,0 +1,175 @@
+// 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.
+
+//! Various utility functions useful for writing I/O tests
+
+use prelude::v1::*;
+
+use libc;
+use os;
+use std::old_io::net::ip::*;
+use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
+
+/// Get a port number, starting at 9600, for use in tests
+pub fn next_test_port() -> u16 {
+    static NEXT_OFFSET: AtomicUsize = ATOMIC_USIZE_INIT;
+    base_port() + NEXT_OFFSET.fetch_add(1, Ordering::Relaxed) as u16
+}
+
+// iOS has a pretty long tmpdir path which causes pipe creation
+// to like: invalid argument: path must be smaller than SUN_LEN
+fn next_test_unix_socket() -> String {
+    static COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
+    // base port and pid are an attempt to be unique between multiple
+    // test-runners of different configurations running on one
+    // buildbot, the count is to be unique within this executable.
+    format!("rust-test-unix-path-{}-{}-{}",
+            base_port(),
+            unsafe {libc::getpid()},
+            COUNT.fetch_add(1, Ordering::Relaxed))
+}
+
+/// Get a temporary path which could be the location of a unix socket
+#[cfg(not(target_os = "ios"))]
+pub fn next_test_unix() -> Path {
+    let string = next_test_unix_socket();
+    if cfg!(unix) {
+        os::tmpdir().join(string)
+    } else {
+        Path::new(format!("{}{}", r"\\.\pipe\", string))
+    }
+}
+
+/// Get a temporary path which could be the location of a unix socket
+#[cfg(target_os = "ios")]
+pub fn next_test_unix() -> Path {
+    Path::new(format!("/var/tmp/{}", next_test_unix_socket()))
+}
+
+/// Get a unique IPv4 localhost:port pair starting at 9600
+pub fn next_test_ip4() -> SocketAddr {
+    SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() }
+}
+
+/// Get a unique IPv6 localhost:port pair starting at 9600
+pub fn next_test_ip6() -> SocketAddr {
+    SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() }
+}
+
+/*
+XXX: Welcome to MegaHack City.
+
+The bots run multiple builds at the same time, and these builds
+all want to use ports. This function figures out which workspace
+it is running in and assigns a port range based on it.
+*/
+fn base_port() -> u16 {
+
+    let base = 9600u16;
+    let range = 1000u16;
+
+    let bases = [
+        ("32-opt", base + range * 1),
+        ("32-nopt", base + range * 2),
+        ("64-opt", base + range * 3),
+        ("64-nopt", base + range * 4),
+        ("64-opt-vg", base + range * 5),
+        ("all-opt", base + range * 6),
+        ("snap3", base + range * 7),
+        ("dist", base + range * 8)
+    ];
+
+    // FIXME (#9639): This needs to handle non-utf8 paths
+    let path = os::getcwd().unwrap();
+    let path_s = path.as_str().unwrap();
+
+    let mut final_base = base;
+
+    for &(dir, base) in bases.iter() {
+        if path_s.contains(dir) {
+            final_base = base;
+            break;
+        }
+    }
+
+    return final_base;
+}
+
+/// Raises the file descriptor limit when running tests if necessary
+pub fn raise_fd_limit() {
+    unsafe { darwin_fd_limit::raise_fd_limit() }
+}
+
+/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the rlimit
+/// maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low for our
+/// multithreaded scheduler testing, depending on the number of cores available.
+///
+/// This fixes issue #7772.
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[allow(non_camel_case_types)]
+mod darwin_fd_limit {
+    use libc;
+    type rlim_t = libc::uint64_t;
+    #[repr(C)]
+    struct rlimit {
+        rlim_cur: rlim_t,
+        rlim_max: rlim_t
+    }
+    extern {
+        // name probably doesn't need to be mut, but the C function doesn't specify const
+        fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint,
+                  oldp: *mut libc::c_void, oldlenp: *mut libc::size_t,
+                  newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int;
+        fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int;
+        fn setrlimit(resource: libc::c_int, rlp: *const rlimit) -> libc::c_int;
+    }
+    static CTL_KERN: libc::c_int = 1;
+    static KERN_MAXFILESPERPROC: libc::c_int = 29;
+    static RLIMIT_NOFILE: libc::c_int = 8;
+
+    pub unsafe fn raise_fd_limit() {
+        // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc
+        // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value.
+        use ptr::null_mut;
+        use mem::size_of_val;
+        use os::last_os_error;
+
+        // Fetch the kern.maxfilesperproc value
+        let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC];
+        let mut maxfiles: libc::c_int = 0;
+        let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
+        if sysctl(&mut mib[0], 2, &mut maxfiles as *mut libc::c_int as *mut libc::c_void, &mut size,
+                  null_mut(), 0) != 0 {
+            let err = last_os_error();
+            panic!("raise_fd_limit: error calling sysctl: {}", err);
+        }
+
+        // Fetch the current resource limits
+        let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0};
+        if getrlimit(RLIMIT_NOFILE, &mut rlim) != 0 {
+            let err = last_os_error();
+            panic!("raise_fd_limit: error calling getrlimit: {}", err);
+        }
+
+        // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit
+        rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max);
+
+        // Set our newly-increased resource limit
+        if setrlimit(RLIMIT_NOFILE, &rlim) != 0 {
+            let err = last_os_error();
+            panic!("raise_fd_limit: error calling setrlimit: {}", err);
+        }
+    }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+mod darwin_fd_limit {
+    pub unsafe fn raise_fd_limit() {}
+}
diff --git a/src/libstd/old_io/timer.rs b/src/libstd/old_io/timer.rs
new file mode 100644
index 00000000000..7e15c9ad7fc
--- /dev/null
+++ b/src/libstd/old_io/timer.rs
@@ -0,0 +1,481 @@
+// 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.
+
+//! Synchronous Timers
+//!
+//! This module exposes the functionality to create timers, block the current task,
+//! and create receivers which will receive notifications after a period of time.
+
+// FIXME: These functions take Durations but only pass ms to the backend impls.
+
+use sync::mpsc::{Receiver, Sender, channel};
+use time::Duration;
+use old_io::IoResult;
+use sys::timer::Callback;
+use sys::timer::Timer as TimerImp;
+
+/// A synchronous timer object
+///
+/// Values of this type can be used to put the current task to sleep for a
+/// period of time. Handles to this timer can also be created in the form of
+/// receivers which will receive notifications over time.
+///
+/// # Examples
+///
+/// ```
+/// # fn foo() {
+/// use std::old_io::Timer;
+/// use std::time::Duration;
+///
+/// let mut timer = Timer::new().unwrap();
+/// timer.sleep(Duration::milliseconds(10)); // block the task for awhile
+///
+/// let timeout = timer.oneshot(Duration::milliseconds(10));
+/// // do some work
+/// timeout.recv().unwrap(); // wait for the timeout to expire
+///
+/// let periodic = timer.periodic(Duration::milliseconds(10));
+/// loop {
+///     periodic.recv().unwrap();
+///     // this loop is only executed once every 10ms
+/// }
+/// # }
+/// ```
+///
+/// If only sleeping is necessary, then a convenience API is provided through
+/// the `old_io::timer` module.
+///
+/// ```
+/// # fn foo() {
+/// use std::old_io::timer;
+/// use std::time::Duration;
+///
+/// // Put this task to sleep for 5 seconds
+/// timer::sleep(Duration::seconds(5));
+/// # }
+/// ```
+pub struct Timer {
+    inner: TimerImp,
+}
+
+struct TimerCallback { tx: Sender<()> }
+
+/// Sleep the current task for the specified duration.
+///
+/// When provided a zero or negative `duration`, the function will
+/// return immediately.
+pub fn sleep(duration: Duration) {
+    let timer = Timer::new();
+    let mut timer = timer.ok().expect("timer::sleep: could not create a Timer");
+
+    timer.sleep(duration)
+}
+
+impl Timer {
+    /// Creates a new timer which can be used to put the current task to sleep
+    /// for a number of milliseconds, or to possibly create channels which will
+    /// get notified after an amount of time has passed.
+    pub fn new() -> IoResult<Timer> {
+        TimerImp::new().map(|t| Timer { inner: t })
+    }
+
+    /// Blocks the current task for the specified duration.
+    ///
+    /// Note that this function will cause any other receivers for this timer to
+    /// be invalidated (the other end will be closed).
+    ///
+    /// When provided a zero or negative `duration`, the function will
+    /// return immediately.
+    pub fn sleep(&mut self, duration: Duration) {
+        // Short-circuit the timer backend for 0 duration
+        let ms = in_ms_u64(duration);
+        if ms == 0 { return }
+        self.inner.sleep(ms);
+    }
+
+    /// Creates a oneshot receiver which will have a notification sent when
+    /// the specified duration has elapsed.
+    ///
+    /// This does *not* block the current task, but instead returns immediately.
+    ///
+    /// Note that this invalidates any previous receiver which has been created
+    /// by this timer, and that the returned receiver will be invalidated once
+    /// the timer is destroyed (when it falls out of scope). In particular, if
+    /// this is called in method-chaining style, the receiver will be
+    /// invalidated at the end of that statement, and all `recv` calls will
+    /// fail.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use std::old_io::Timer;
+    /// use std::time::Duration;
+    ///
+    /// let mut timer = Timer::new().unwrap();
+    /// let ten_milliseconds = timer.oneshot(Duration::milliseconds(10));
+    ///
+    /// for _ in range(0u, 100) { /* do work */ }
+    ///
+    /// // blocks until 10 ms after the `oneshot` call
+    /// ten_milliseconds.recv().unwrap();
+    /// ```
+    ///
+    /// ```rust
+    /// use std::old_io::Timer;
+    /// use std::time::Duration;
+    ///
+    /// // Incorrect, method chaining-style:
+    /// let mut five_ms = Timer::new().unwrap().oneshot(Duration::milliseconds(5));
+    /// // The timer object was destroyed, so this will always fail:
+    /// // five_ms.recv().unwrap()
+    /// ```
+    ///
+    /// When provided a zero or negative `duration`, the message will
+    /// be sent immediately.
+    pub fn oneshot(&mut self, duration: Duration) -> Receiver<()> {
+        let (tx, rx) = channel();
+        // Short-circuit the timer backend for 0 duration
+        if in_ms_u64(duration) != 0 {
+            self.inner.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
+        } else {
+            tx.send(()).unwrap();
+        }
+        return rx
+    }
+
+    /// Creates a receiver which will have a continuous stream of notifications
+    /// being sent each time the specified duration has elapsed.
+    ///
+    /// This does *not* block the current task, but instead returns
+    /// immediately. The first notification will not be received immediately,
+    /// but rather after the first duration.
+    ///
+    /// Note that this invalidates any previous receiver which has been created
+    /// by this timer, and that the returned receiver will be invalidated once
+    /// the timer is destroyed (when it falls out of scope). In particular, if
+    /// this is called in method-chaining style, the receiver will be
+    /// invalidated at the end of that statement, and all `recv` calls will
+    /// fail.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use std::old_io::Timer;
+    /// use std::time::Duration;
+    ///
+    /// let mut timer = Timer::new().unwrap();
+    /// let ten_milliseconds = timer.periodic(Duration::milliseconds(10));
+    ///
+    /// for _ in range(0u, 100) { /* do work */ }
+    ///
+    /// // blocks until 10 ms after the `periodic` call
+    /// ten_milliseconds.recv().unwrap();
+    ///
+    /// for _ in range(0u, 100) { /* do work */ }
+    ///
+    /// // blocks until 20 ms after the `periodic` call (*not* 10ms after the
+    /// // previous `recv`)
+    /// ten_milliseconds.recv().unwrap();
+    /// ```
+    ///
+    /// ```rust
+    /// use std::old_io::Timer;
+    /// use std::time::Duration;
+    ///
+    /// // Incorrect, method chaining-style.
+    /// let mut five_ms = Timer::new().unwrap().periodic(Duration::milliseconds(5));
+    /// // The timer object was destroyed, so this will always fail:
+    /// // five_ms.recv().unwrap()
+    /// ```
+    ///
+    /// When provided a zero or negative `duration`, the messages will
+    /// be sent without delay.
+    pub fn periodic(&mut self, duration: Duration) -> Receiver<()> {
+        let ms = in_ms_u64(duration);
+        // FIXME: The backend implementations don't ever send a message
+        // if given a 0 ms duration. Temporarily using 1ms. It's
+        // not clear what use a 0ms period is anyway...
+        let ms = if ms == 0 { 1 } else { ms };
+        let (tx, rx) = channel();
+        self.inner.period(ms, box TimerCallback { tx: tx });
+        return rx
+    }
+}
+
+impl Callback for TimerCallback {
+    fn call(&mut self) {
+        let _ = self.tx.send(());
+    }
+}
+
+fn in_ms_u64(d: Duration) -> u64 {
+    let ms = d.num_milliseconds();
+    if ms < 0 { return 0 };
+    return ms as u64;
+}
+
+#[cfg(test)]
+mod test {
+    use super::Timer;
+    use thread::Thread;
+    use time::Duration;
+
+    #[test]
+    fn test_timer_send() {
+        let mut timer = Timer::new().unwrap();
+        Thread::spawn(move || timer.sleep(Duration::milliseconds(1)));
+    }
+
+    #[test]
+    fn test_io_timer_sleep_simple() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(1));
+    }
+
+    #[test]
+    fn test_io_timer_sleep_oneshot() {
+        let mut timer = Timer::new().unwrap();
+        timer.oneshot(Duration::milliseconds(1)).recv().unwrap();
+    }
+
+    #[test]
+    fn test_io_timer_sleep_oneshot_forget() {
+        let mut timer = Timer::new().unwrap();
+        timer.oneshot(Duration::milliseconds(100000000));
+    }
+
+    #[test]
+    fn oneshot_twice() {
+        let mut timer = Timer::new().unwrap();
+        let rx1 = timer.oneshot(Duration::milliseconds(10000));
+        let rx = timer.oneshot(Duration::milliseconds(1));
+        rx.recv().unwrap();
+        assert!(rx1.recv().is_err());
+    }
+
+    #[test]
+    fn test_io_timer_oneshot_then_sleep() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.oneshot(Duration::milliseconds(100000000));
+        timer.sleep(Duration::milliseconds(1)); // this should invalidate rx
+
+        assert!(rx.recv().is_err());
+    }
+
+    #[test]
+    fn test_io_timer_sleep_periodic() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(1));
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn test_io_timer_sleep_periodic_forget() {
+        let mut timer = Timer::new().unwrap();
+        timer.periodic(Duration::milliseconds(100000000));
+    }
+
+    #[test]
+    fn test_io_timer_sleep_standalone() {
+        super::sleep(Duration::milliseconds(1))
+    }
+
+    #[test]
+    fn oneshot() {
+        let mut timer = Timer::new().unwrap();
+
+        let rx = timer.oneshot(Duration::milliseconds(1));
+        rx.recv().unwrap();
+        assert!(rx.recv().is_err());
+
+        let rx = timer.oneshot(Duration::milliseconds(1));
+        rx.recv().unwrap();
+        assert!(rx.recv().is_err());
+    }
+
+    #[test]
+    fn test_override() {
+        let mut timer = Timer::new().unwrap();
+        let orx = timer.oneshot(Duration::milliseconds(100));
+        let prx = timer.periodic(Duration::milliseconds(100));
+        timer.sleep(Duration::milliseconds(1));
+        assert!(orx.recv().is_err());
+        assert!(prx.recv().is_err());
+        timer.oneshot(Duration::milliseconds(1)).recv().unwrap();
+    }
+
+    #[test]
+    fn period() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(1));
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        let rx2 = timer.periodic(Duration::milliseconds(1));
+        rx2.recv().unwrap();
+        rx2.recv().unwrap();
+    }
+
+    #[test]
+    fn sleep() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(1));
+        timer.sleep(Duration::milliseconds(1));
+    }
+
+    #[test]
+    #[should_fail]
+    fn oneshot_fail() {
+        let mut timer = Timer::new().unwrap();
+        let _rx = timer.oneshot(Duration::milliseconds(1));
+        panic!();
+    }
+
+    #[test]
+    #[should_fail]
+    fn period_fail() {
+        let mut timer = Timer::new().unwrap();
+        let _rx = timer.periodic(Duration::milliseconds(1));
+        panic!();
+    }
+
+    #[test]
+    #[should_fail]
+    fn normal_fail() {
+        let _timer = Timer::new().unwrap();
+        panic!();
+    }
+
+    #[test]
+    fn closing_channel_during_drop_doesnt_kill_everything() {
+        // see issue #10375
+        let mut timer = Timer::new().unwrap();
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
+
+        Thread::spawn(move|| {
+            let _ = timer_rx.recv();
+        });
+
+        // when we drop the TimerWatcher we're going to destroy the channel,
+        // which must wake up the task on the other end
+    }
+
+    #[test]
+    fn reset_doesnt_switch_tasks() {
+        // similar test to the one above.
+        let mut timer = Timer::new().unwrap();
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
+
+        Thread::spawn(move|| {
+            let _ = timer_rx.recv();
+        });
+
+        timer.oneshot(Duration::milliseconds(1));
+    }
+
+    #[test]
+    fn reset_doesnt_switch_tasks2() {
+        // similar test to the one above.
+        let mut timer = Timer::new().unwrap();
+        let timer_rx = timer.periodic(Duration::milliseconds(1000));
+
+        Thread::spawn(move|| {
+            let _ = timer_rx.recv();
+        });
+
+        timer.sleep(Duration::milliseconds(1));
+    }
+
+    #[test]
+    fn sender_goes_away_oneshot() {
+        let rx = {
+            let mut timer = Timer::new().unwrap();
+            timer.oneshot(Duration::milliseconds(1000))
+        };
+        assert!(rx.recv().is_err());
+    }
+
+    #[test]
+    fn sender_goes_away_period() {
+        let rx = {
+            let mut timer = Timer::new().unwrap();
+            timer.periodic(Duration::milliseconds(1000))
+        };
+        assert!(rx.recv().is_err());
+    }
+
+    #[test]
+    fn receiver_goes_away_oneshot() {
+        let mut timer1 = Timer::new().unwrap();
+        timer1.oneshot(Duration::milliseconds(1));
+        let mut timer2 = Timer::new().unwrap();
+        // while sleeping, the previous timer should fire and not have its
+        // callback do something terrible.
+        timer2.sleep(Duration::milliseconds(2));
+    }
+
+    #[test]
+    fn receiver_goes_away_period() {
+        let mut timer1 = Timer::new().unwrap();
+        timer1.periodic(Duration::milliseconds(1));
+        let mut timer2 = Timer::new().unwrap();
+        // while sleeping, the previous timer should fire and not have its
+        // callback do something terrible.
+        timer2.sleep(Duration::milliseconds(2));
+    }
+
+    #[test]
+    fn sleep_zero() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(0));
+    }
+
+    #[test]
+    fn sleep_negative() {
+        let mut timer = Timer::new().unwrap();
+        timer.sleep(Duration::milliseconds(-1000000));
+    }
+
+    #[test]
+    fn oneshot_zero() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.oneshot(Duration::milliseconds(0));
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn oneshot_negative() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.oneshot(Duration::milliseconds(-1000000));
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn periodic_zero() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(0));
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+    }
+
+    #[test]
+    fn periodic_negative() {
+        let mut timer = Timer::new().unwrap();
+        let rx = timer.periodic(Duration::milliseconds(-1000000));
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+        rx.recv().unwrap();
+    }
+
+}
diff --git a/src/libstd/old_io/util.rs b/src/libstd/old_io/util.rs
new file mode 100644
index 00000000000..4b6d9b08141
--- /dev/null
+++ b/src/libstd/old_io/util.rs
@@ -0,0 +1,444 @@
+// 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.
+
+//! Utility implementations of Reader and Writer
+
+use prelude::v1::*;
+use cmp;
+use old_io;
+use slice::bytes::MutableByteVector;
+
+/// Wraps a `Reader`, limiting the number of bytes that can be read from it.
+#[derive(Show)]
+pub struct LimitReader<R> {
+    limit: uint,
+    inner: R
+}
+
+impl<R: Reader> LimitReader<R> {
+    /// Creates a new `LimitReader`
+    pub fn new(r: R, limit: uint) -> LimitReader<R> {
+        LimitReader { limit: limit, inner: r }
+    }
+
+    /// Consumes the `LimitReader`, returning the underlying `Reader`.
+    pub fn into_inner(self) -> R { self.inner }
+
+    /// Returns the number of bytes that can be read before the `LimitReader`
+    /// will return EOF.
+    ///
+    /// # Note
+    ///
+    /// The reader may reach EOF after reading fewer bytes than indicated by
+    /// this method if the underlying reader reaches EOF.
+    pub fn limit(&self) -> uint { self.limit }
+}
+
+impl<R: Reader> Reader for LimitReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+        if self.limit == 0 {
+            return Err(old_io::standard_error(old_io::EndOfFile));
+        }
+
+        let len = cmp::min(self.limit, buf.len());
+        let res = self.inner.read(&mut buf[..len]);
+        match res {
+            Ok(len) => self.limit -= len,
+            _ => {}
+        }
+        res
+    }
+}
+
+impl<R: Buffer> Buffer for LimitReader<R> {
+    fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
+        let amt = try!(self.inner.fill_buf());
+        let buf = &amt[..cmp::min(amt.len(), self.limit)];
+        if buf.len() == 0 {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        } else {
+            Ok(buf)
+        }
+    }
+
+    fn consume(&mut self, amt: uint) {
+        // Don't let callers reset the limit by passing an overlarge value
+        let amt = cmp::min(amt, self.limit);
+        self.limit -= amt;
+        self.inner.consume(amt);
+    }
+
+}
+
+/// A `Writer` which ignores bytes written to it, like /dev/null.
+#[derive(Copy, Show)]
+pub struct NullWriter;
+
+impl Writer for NullWriter {
+    #[inline]
+    fn write_all(&mut self, _buf: &[u8]) -> old_io::IoResult<()> { Ok(()) }
+}
+
+/// A `Reader` which returns an infinite stream of 0 bytes, like /dev/zero.
+#[derive(Copy, Show)]
+pub struct ZeroReader;
+
+impl Reader for ZeroReader {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+        buf.set_memory(0);
+        Ok(buf.len())
+    }
+}
+
+impl Buffer for ZeroReader {
+    fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
+        static DATA: [u8; 64] = [0; 64];
+        Ok(DATA.as_slice())
+    }
+
+    fn consume(&mut self, _amt: uint) {}
+}
+
+/// A `Reader` which is always at EOF, like /dev/null.
+#[derive(Copy, Show)]
+pub struct NullReader;
+
+impl Reader for NullReader {
+    #[inline]
+    fn read(&mut self, _buf: &mut [u8]) -> old_io::IoResult<uint> {
+        Err(old_io::standard_error(old_io::EndOfFile))
+    }
+}
+
+impl Buffer for NullReader {
+    fn fill_buf<'a>(&'a mut self) -> old_io::IoResult<&'a [u8]> {
+        Err(old_io::standard_error(old_io::EndOfFile))
+    }
+    fn consume(&mut self, _amt: uint) {}
+}
+
+/// A `Writer` which multiplexes writes to a set of `Writer`s.
+///
+/// The `Writer`s are delegated to in order. If any `Writer` returns an error,
+/// that error is returned immediately and remaining `Writer`s are not called.
+#[derive(Show)]
+pub struct MultiWriter<W> {
+    writers: Vec<W>
+}
+
+impl<W> MultiWriter<W> where W: Writer {
+    /// Creates a new `MultiWriter`
+    pub fn new(writers: Vec<W>) -> MultiWriter<W> {
+        MultiWriter { writers: writers }
+    }
+}
+
+impl<W> Writer for MultiWriter<W> where W: Writer {
+    #[inline]
+    fn write_all(&mut self, buf: &[u8]) -> old_io::IoResult<()> {
+        for writer in self.writers.iter_mut() {
+            try!(writer.write_all(buf));
+        }
+        Ok(())
+    }
+
+    #[inline]
+    fn flush(&mut self) -> old_io::IoResult<()> {
+        for writer in self.writers.iter_mut() {
+            try!(writer.flush());
+        }
+        Ok(())
+    }
+}
+
+/// A `Reader` which chains input from multiple `Reader`s, reading each to
+/// completion before moving onto the next.
+#[derive(Clone, Show)]
+pub struct ChainedReader<I, R> {
+    readers: I,
+    cur_reader: Option<R>,
+}
+
+impl<R: Reader, I: Iterator<Item=R>> ChainedReader<I, R> {
+    /// Creates a new `ChainedReader`
+    pub fn new(mut readers: I) -> ChainedReader<I, R> {
+        let r = readers.next();
+        ChainedReader { readers: readers, cur_reader: r }
+    }
+}
+
+impl<R: Reader, I: Iterator<Item=R>> Reader for ChainedReader<I, R> {
+    fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+        loop {
+            let err = match self.cur_reader {
+                Some(ref mut r) => {
+                    match r.read(buf) {
+                        Ok(len) => return Ok(len),
+                        Err(ref e) if e.kind == old_io::EndOfFile => None,
+                        Err(e) => Some(e),
+                    }
+                }
+                None => break
+            };
+            self.cur_reader = self.readers.next();
+            match err {
+                Some(e) => return Err(e),
+                None => {}
+            }
+        }
+        Err(old_io::standard_error(old_io::EndOfFile))
+    }
+}
+
+/// A `Reader` which forwards input from another `Reader`, passing it along to
+/// a `Writer` as well. Similar to the `tee(1)` command.
+#[derive(Show)]
+pub struct TeeReader<R, W> {
+    reader: R,
+    writer: W,
+}
+
+impl<R: Reader, W: Writer> TeeReader<R, W> {
+    /// Creates a new `TeeReader`
+    pub fn new(r: R, w: W) -> TeeReader<R, W> {
+        TeeReader { reader: r, writer: w }
+    }
+
+    /// Consumes the `TeeReader`, returning the underlying `Reader` and
+    /// `Writer`.
+    pub fn into_inner(self) -> (R, W) {
+        let TeeReader { reader, writer } = self;
+        (reader, writer)
+    }
+}
+
+impl<R: Reader, W: Writer> Reader for TeeReader<R, W> {
+    fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+        self.reader.read(buf).and_then(|len| {
+            self.writer.write_all(&mut buf[..len]).map(|()| len)
+        })
+    }
+}
+
+/// Copies all data from a `Reader` to a `Writer`.
+pub fn copy<R: Reader, W: Writer>(r: &mut R, w: &mut W) -> old_io::IoResult<()> {
+    let mut buf = [0; super::DEFAULT_BUF_SIZE];
+    loop {
+        let len = match r.read(&mut buf) {
+            Ok(len) => len,
+            Err(ref e) if e.kind == old_io::EndOfFile => return Ok(()),
+            Err(e) => return Err(e),
+        };
+        try!(w.write_all(&buf[..len]));
+    }
+}
+
+/// An adaptor converting an `Iterator<u8>` to a `Reader`.
+#[derive(Clone, Show)]
+pub struct IterReader<T> {
+    iter: T,
+}
+
+impl<T: Iterator<Item=u8>> IterReader<T> {
+    /// Creates a new `IterReader` which will read from the specified
+    /// `Iterator`.
+    pub fn new(iter: T) -> IterReader<T> {
+        IterReader { iter: iter }
+    }
+}
+
+impl<T: Iterator<Item=u8>> Reader for IterReader<T> {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<uint> {
+        let mut len = 0;
+        for (slot, elt) in buf.iter_mut().zip(self.iter.by_ref()) {
+            *slot = elt;
+            len += 1;
+        }
+        if len == 0 && buf.len() != 0 {
+            Err(old_io::standard_error(old_io::EndOfFile))
+        } else {
+            Ok(len)
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::v1::*;
+
+    use old_io::{MemReader, ByRefReader};
+    use old_io;
+    use super::*;
+
+    #[test]
+    fn test_limit_reader_unlimited() {
+        let mut r = MemReader::new(vec!(0, 1, 2));
+        {
+            let mut r = LimitReader::new(r.by_ref(), 4);
+            assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap());
+        }
+    }
+
+    #[test]
+    fn test_limit_reader_limited() {
+        let mut r = MemReader::new(vec!(0, 1, 2));
+        {
+            let mut r = LimitReader::new(r.by_ref(), 2);
+            assert_eq!(vec!(0, 1), r.read_to_end().unwrap());
+        }
+        assert_eq!(vec!(2), r.read_to_end().unwrap());
+    }
+
+    #[test]
+    fn test_limit_reader_limit() {
+        let r = MemReader::new(vec!(0, 1, 2));
+        let mut r = LimitReader::new(r, 3);
+        assert_eq!(3, r.limit());
+        assert_eq!(0, r.read_byte().unwrap());
+        assert_eq!(2, r.limit());
+        assert_eq!(vec!(1, 2), r.read_to_end().unwrap());
+        assert_eq!(0, r.limit());
+    }
+
+    #[test]
+    fn test_limit_reader_overlong_consume() {
+        let mut r = MemReader::new(vec![0, 1, 2, 3, 4, 5]);
+        let mut r = LimitReader::new(r.by_ref(), 1);
+        r.consume(2);
+        assert_eq!(vec![], r.read_to_end().unwrap());
+    }
+
+    #[test]
+    fn test_null_writer() {
+        let mut s = NullWriter;
+        let buf = vec![0, 0, 0];
+        s.write_all(buf.as_slice()).unwrap();
+        s.flush().unwrap();
+    }
+
+    #[test]
+    fn test_zero_reader() {
+        let mut s = ZeroReader;
+        let mut buf = vec![1, 2, 3];
+        assert_eq!(s.read(buf.as_mut_slice()), Ok(3));
+        assert_eq!(vec![0, 0, 0], buf);
+    }
+
+    #[test]
+    fn test_null_reader() {
+        let mut r = NullReader;
+        let mut buf = vec![0];
+        assert!(r.read(buf.as_mut_slice()).is_err());
+    }
+
+    #[test]
+    fn test_multi_writer() {
+        static mut writes: uint = 0;
+        static mut flushes: uint = 0;
+
+        struct TestWriter;
+        impl Writer for TestWriter {
+            fn write_all(&mut self, _buf: &[u8]) -> old_io::IoResult<()> {
+                unsafe { writes += 1 }
+                Ok(())
+            }
+
+            fn flush(&mut self) -> old_io::IoResult<()> {
+                unsafe { flushes += 1 }
+                Ok(())
+            }
+        }
+
+        let mut multi = MultiWriter::new(vec!(box TestWriter as Box<Writer>,
+                                              box TestWriter as Box<Writer>));
+        multi.write_all(&[1, 2, 3]).unwrap();
+        assert_eq!(2, unsafe { writes });
+        assert_eq!(0, unsafe { flushes });
+        multi.flush().unwrap();
+        assert_eq!(2, unsafe { writes });
+        assert_eq!(2, unsafe { flushes });
+    }
+
+    #[test]
+    fn test_chained_reader() {
+        let rs = vec!(MemReader::new(vec!(0, 1)), MemReader::new(vec!()),
+                      MemReader::new(vec!(2, 3)));
+        let mut r = ChainedReader::new(rs.into_iter());
+        assert_eq!(vec!(0, 1, 2, 3), r.read_to_end().unwrap());
+    }
+
+    #[test]
+    fn test_tee_reader() {
+        let mut r = TeeReader::new(MemReader::new(vec!(0, 1, 2)),
+                                   Vec::new());
+        assert_eq!(vec!(0, 1, 2), r.read_to_end().unwrap());
+        let (_, w) = r.into_inner();
+        assert_eq!(vec!(0, 1, 2), w);
+    }
+
+    #[test]
+    fn test_copy() {
+        let mut r = MemReader::new(vec!(0, 1, 2, 3, 4));
+        let mut w = Vec::new();
+        copy(&mut r, &mut w).unwrap();
+        assert_eq!(vec!(0, 1, 2, 3, 4), w);
+    }
+
+    #[test]
+    fn limit_reader_buffer() {
+        let r = &mut b"0123456789\n0123456789\n";
+        {
+            let mut r = LimitReader::new(r.by_ref(), 3);
+            assert_eq!(r.read_line(), Ok("012".to_string()));
+            assert_eq!(r.limit(), 0);
+            assert_eq!(r.read_line().err().unwrap().kind, old_io::EndOfFile);
+        }
+        {
+            let mut r = LimitReader::new(r.by_ref(), 9);
+            assert_eq!(r.read_line(), Ok("3456789\n".to_string()));
+            assert_eq!(r.limit(), 1);
+            assert_eq!(r.read_line(), Ok("0".to_string()));
+        }
+        {
+            let mut r = LimitReader::new(r.by_ref(), 100);
+            assert_eq!(r.read_char(), Ok('1'));
+            assert_eq!(r.limit(), 99);
+            assert_eq!(r.read_line(), Ok("23456789\n".to_string()));
+        }
+    }
+
+    #[test]
+    fn test_iter_reader() {
+        let mut r = IterReader::new(range(0u8, 8));
+        let mut buf = [0, 0, 0];
+        let len = r.read(&mut buf).unwrap();
+        assert_eq!(len, 3);
+        assert!(buf == [0, 1, 2]);
+
+        let len = r.read(&mut buf).unwrap();
+        assert_eq!(len, 3);
+        assert!(buf == [3, 4, 5]);
+
+        let len = r.read(&mut buf).unwrap();
+        assert_eq!(len, 2);
+        assert!(buf == [6, 7, 5]);
+
+        assert_eq!(r.read(&mut buf).unwrap_err().kind, old_io::EndOfFile);
+    }
+
+    #[test]
+    fn iter_reader_zero_length() {
+        let mut r = IterReader::new(range(0u8, 8));
+        let mut buf = [];
+        assert_eq!(Ok(0), r.read(&mut buf));
+    }
+}