diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-11-10 22:46:32 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-11-11 20:44:07 -0800 |
| commit | 49ee49296b65f3d807142f3326bee71dd7e13290 (patch) | |
| tree | b3380df09c8a10473820969a62f5775832255fda /src/libstd/rt/io | |
| parent | 8b4683d79d4b74f53808470cd2f98b23a0af9b93 (diff) | |
| download | rust-49ee49296b65f3d807142f3326bee71dd7e13290.tar.gz rust-49ee49296b65f3d807142f3326bee71dd7e13290.zip | |
Move std::rt::io to std::io
Diffstat (limited to 'src/libstd/rt/io')
| -rw-r--r-- | src/libstd/rt/io/buffered.rs | 472 | ||||
| -rw-r--r-- | src/libstd/rt/io/comm_adapters.rs | 57 | ||||
| -rw-r--r-- | src/libstd/rt/io/extensions.rs | 490 | ||||
| -rw-r--r-- | src/libstd/rt/io/flate.rs | 123 | ||||
| -rw-r--r-- | src/libstd/rt/io/fs.rs | 1288 | ||||
| -rw-r--r-- | src/libstd/rt/io/mem.rs | 308 | ||||
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 1224 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/file.rs | 761 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/process.rs | 734 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/stdio.rs | 63 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/addrinfo.rs | 127 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/ip.rs | 449 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/mod.rs | 18 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/tcp.rs | 725 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/udp.rs | 321 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/unix.rs | 295 | ||||
| -rw-r--r-- | src/libstd/rt/io/option.rs | 181 | ||||
| -rw-r--r-- | src/libstd/rt/io/pipe.rs | 89 | ||||
| -rw-r--r-- | src/libstd/rt/io/process.rs | 177 | ||||
| -rw-r--r-- | src/libstd/rt/io/signal.rs | 223 | ||||
| -rw-r--r-- | src/libstd/rt/io/stdio.rs | 321 | ||||
| -rw-r--r-- | src/libstd/rt/io/timer.rs | 186 |
22 files changed, 0 insertions, 8632 deletions
diff --git a/src/libstd/rt/io/buffered.rs b/src/libstd/rt/io/buffered.rs deleted file mode 100644 index 8afb7183d77..00000000000 --- a/src/libstd/rt/io/buffered.rs +++ /dev/null @@ -1,472 +0,0 @@ -// 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. - -//! Buffering wrappers for I/O traits -//! -//! It can be excessively inefficient to work directly with a `Reader` or -//! `Writer`. Every call to `read` or `write` on `TcpStream` results in a -//! system call, for example. This module provides structures that wrap -//! `Readers`, `Writers`, and `Streams` and buffer input and output to them. -//! -//! # Examples -//! -//! ``` -//! let tcp_stream = TcpStream::connect(addr); -//! let reader = BufferedReader::new(tcp_stream); -//! -//! let mut buf: ~[u8] = vec::from_elem(100, 0u8); -//! match reader.read(buf.as_slice()) { -//! Some(nread) => println!("Read {} bytes", nread), -//! None => println!("At the end of the stream!") -//! } -//! ``` -//! -//! ``` -//! let tcp_stream = TcpStream::connect(addr); -//! let writer = BufferedWriter::new(tcp_stream); -//! -//! writer.write("hello, world".as_bytes()); -//! writer.flush(); -//! ``` -//! -//! ``` -//! let tcp_stream = TcpStream::connect(addr); -//! let stream = BufferedStream::new(tcp_stream); -//! -//! stream.write("hello, world".as_bytes()); -//! stream.flush(); -//! -//! let mut buf = vec::from_elem(100, 0u8); -//! match stream.read(buf.as_slice()) { -//! Some(nread) => println!("Read {} bytes", nread), -//! None => println!("At the end of the stream!") -//! } -//! ``` -//! - -use prelude::*; - -use num; -use vec; -use str; -use super::{Reader, Writer, Stream, Decorator}; - -// libuv recommends 64k buffers to maximize throughput -// https://groups.google.com/forum/#!topic/libuv/oQO1HJAIDdA -static DEFAULT_CAPACITY: uint = 64 * 1024; - -/// Wraps a Reader and buffers input from it -pub struct BufferedReader<R> { - priv inner: R, - priv buf: ~[u8], - priv pos: uint, - priv cap: uint -} - -impl<R: Reader> BufferedReader<R> { - /// Creates a new `BufferedReader` with with the specified buffer capacity - pub fn with_capacity(cap: uint, inner: R) -> BufferedReader<R> { - // 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 BufferedReader instances - // to be very cheap (large mallocs are not nearly as expensive as large - // callocs). - let mut buf = vec::with_capacity(cap); - unsafe { vec::raw::set_len(&mut buf, cap); } - BufferedReader { - inner: inner, - buf: buf, - pos: 0, - cap: 0 - } - } - - /// Creates a new `BufferedReader` with a default buffer capacity - pub fn new(inner: R) -> BufferedReader<R> { - BufferedReader::with_capacity(DEFAULT_CAPACITY, inner) - } - - /// Reads the next line of input, interpreted as a sequence of utf-8 - /// encoded unicode codepoints. If a newline is encountered, then the - /// newline is contained in the returned string. - pub fn read_line(&mut self) -> Option<~str> { - self.read_until('\n' as u8).map(str::from_utf8_owned) - } - - /// Reads a sequence of bytes leading up to a specified delimeter. Once the - /// specified byte is encountered, reading ceases and the bytes up to and - /// including the delimiter are returned. - pub fn read_until(&mut self, byte: u8) -> Option<~[u8]> { - let mut res = ~[]; - let mut used; - loop { - { - let available = self.fill_buffer(); - match available.iter().position(|&b| b == byte) { - Some(i) => { - res.push_all(available.slice_to(i + 1)); - used = i + 1; - break - } - None => { - res.push_all(available); - used = available.len(); - } - } - } - if used == 0 { - break - } - self.pos += used; - } - self.pos += used; - return if res.len() == 0 {None} else {Some(res)}; - } - - fn fill_buffer<'a>(&'a mut self) -> &'a [u8] { - if self.pos == self.cap { - match self.inner.read(self.buf) { - Some(cap) => { - self.pos = 0; - self.cap = cap; - } - None => {} - } - } - return self.buf.slice(self.pos, self.cap); - } -} - -impl<R: Reader> Reader for BufferedReader<R> { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - let nread = { - let available = self.fill_buffer(); - if available.len() == 0 { - return None; - } - let nread = num::min(available.len(), buf.len()); - vec::bytes::copy_memory(buf, available, nread); - nread - }; - self.pos += nread; - Some(nread) - } - - fn eof(&mut self) -> bool { - self.pos == self.cap && self.inner.eof() - } -} - -impl<R: Reader> Decorator<R> for BufferedReader<R> { - fn inner(self) -> R { - self.inner - } - - fn inner_ref<'a>(&'a self) -> &'a R { - &self.inner - } - - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { - &mut self.inner - } -} - -/// Wraps a Writer and buffers output to it -/// -/// Note that `BufferedWriter` will NOT flush its buffer when dropped. -pub struct BufferedWriter<W> { - priv inner: W, - priv buf: ~[u8], - priv pos: uint -} - -impl<W: Writer> BufferedWriter<W> { - /// Creates a new `BufferedWriter` with with the specified buffer capacity - pub fn with_capacity(cap: uint, inner: W) -> BufferedWriter<W> { - // See comments in BufferedReader for why this uses unsafe code. - let mut buf = vec::with_capacity(cap); - unsafe { vec::raw::set_len(&mut buf, cap); } - BufferedWriter { - inner: 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_CAPACITY, inner) - } -} - -impl<W: Writer> Writer for BufferedWriter<W> { - fn write(&mut self, buf: &[u8]) { - if self.pos + buf.len() > self.buf.len() { - self.flush(); - } - - if buf.len() > self.buf.len() { - self.inner.write(buf); - } else { - let dst = self.buf.mut_slice_from(self.pos); - vec::bytes::copy_memory(dst, buf, buf.len()); - self.pos += buf.len(); - } - } - - fn flush(&mut self) { - if self.pos != 0 { - self.inner.write(self.buf.slice_to(self.pos)); - self.pos = 0; - } - self.inner.flush(); - } -} - -impl<W: Writer> Decorator<W> for BufferedWriter<W> { - fn inner(self) -> W { self.inner } - fn inner_ref<'a>(&'a self) -> &'a W { &self.inner } - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { &mut self.inner } -} - -/// Wraps a Writer and buffers output to it, flushing whenever a newline (0xa, -/// '\n') is detected. -/// -/// Note that this structure does NOT flush the output when dropped. -pub struct LineBufferedWriter<W> { - priv inner: BufferedWriter<W>, -} - -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) - } - } -} - -impl<W: Writer> Writer for LineBufferedWriter<W> { - fn write(&mut self, buf: &[u8]) { - match buf.iter().position(|&b| b == '\n' as u8) { - Some(i) => { - self.inner.write(buf.slice_to(i + 1)); - self.inner.flush(); - self.inner.write(buf.slice_from(i + 1)); - } - None => self.inner.write(buf), - } - } - - fn flush(&mut self) { self.inner.flush() } -} - -impl<W: Writer> Decorator<W> for LineBufferedWriter<W> { - fn inner(self) -> W { self.inner.inner() } - fn inner_ref<'a>(&'a self) -> &'a W { self.inner.inner_ref() } - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { self.inner.inner_mut_ref() } -} - -struct InternalBufferedWriter<W>(BufferedWriter<W>); - -impl<W: Reader> Reader for InternalBufferedWriter<W> { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - self.inner.read(buf) - } - - fn eof(&mut self) -> bool { - self.inner.eof() - } -} - -/// Wraps a Stream and buffers input and output to and from it -/// -/// Note that `BufferedStream` will NOT flush its output buffer when dropped. -pub struct BufferedStream<S> { - priv inner: BufferedReader<InternalBufferedWriter<S>> -} - -impl<S: Stream> BufferedStream<S> { - 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 } - } - - pub fn new(inner: S) -> BufferedStream<S> { - BufferedStream::with_capacities(DEFAULT_CAPACITY, DEFAULT_CAPACITY, - inner) - } -} - -impl<S: Stream> Reader for BufferedStream<S> { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - self.inner.read(buf) - } - - fn eof(&mut self) -> bool { - self.inner.eof() - } -} - -impl<S: Stream> Writer for BufferedStream<S> { - fn write(&mut self, buf: &[u8]) { - self.inner.inner.write(buf) - } - - fn flush(&mut self) { - self.inner.inner.flush() - } -} - -impl<S: Stream> Decorator<S> for BufferedStream<S> { - fn inner(self) -> S { - self.inner.inner.inner() - } - - fn inner_ref<'a>(&'a self) -> &'a S { - self.inner.inner.inner_ref() - } - - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut S { - self.inner.inner.inner_mut_ref() - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use super::super::mem::{MemReader, MemWriter}; - - #[test] - fn test_buffered_reader() { - let inner = MemReader::new(~[0, 1, 2, 3, 4]); - let mut reader = BufferedReader::with_capacity(2, inner); - - let mut buf = [0, 0, 0]; - let nread = reader.read(buf); - assert_eq!(Some(2), nread); - assert_eq!([0, 1, 0], buf); - assert!(!reader.eof()); - - let mut buf = [0]; - let nread = reader.read(buf); - assert_eq!(Some(1), nread); - assert_eq!([2], buf); - assert!(!reader.eof()); - - let mut buf = [0, 0, 0]; - let nread = reader.read(buf); - assert_eq!(Some(1), nread); - assert_eq!([3, 0, 0], buf); - assert!(!reader.eof()); - - let nread = reader.read(buf); - assert_eq!(Some(1), nread); - assert_eq!([4, 0, 0], buf); - assert!(reader.eof()); - - assert_eq!(None, reader.read(buf)); - } - - #[test] - fn test_buffered_writer() { - let inner = MemWriter::new(); - let mut writer = BufferedWriter::with_capacity(2, inner); - - writer.write([0, 1]); - assert_eq!([], writer.inner_ref().inner_ref().as_slice()); - - writer.write([2]); - assert_eq!([0, 1], writer.inner_ref().inner_ref().as_slice()); - - writer.write([3]); - assert_eq!([0, 1], writer.inner_ref().inner_ref().as_slice()); - - writer.flush(); - assert_eq!([0, 1, 2, 3], writer.inner_ref().inner_ref().as_slice()); - - writer.write([4]); - writer.write([5]); - assert_eq!([0, 1, 2, 3], writer.inner_ref().inner_ref().as_slice()); - - writer.write([6]); - assert_eq!([0, 1, 2, 3, 4, 5], - writer.inner_ref().inner_ref().as_slice()); - - writer.write([7, 8]); - assert_eq!([0, 1, 2, 3, 4, 5, 6], - writer.inner_ref().inner_ref().as_slice()); - - writer.write([9, 10, 11]); - assert_eq!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], - writer.inner_ref().inner_ref().as_slice()); - - writer.flush(); - assert_eq!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], - writer.inner_ref().inner_ref().as_slice()); - } - - // This is just here to make sure that we don't infinite loop in the - // newtype struct autoderef weirdness - #[test] - fn test_buffered_stream() { - use rt; - struct S; - - impl rt::io::Writer for S { - fn write(&mut self, _: &[u8]) {} - } - - impl rt::io::Reader for S { - fn read(&mut self, _: &mut [u8]) -> Option<uint> { None } - fn eof(&mut self) -> bool { true } - } - - let mut stream = BufferedStream::new(S); - let mut buf = []; - stream.read(buf); - stream.eof(); - stream.write(buf); - stream.flush(); - } - - #[test] - fn test_read_until() { - let inner = MemReader::new(~[0, 1, 2, 1, 0]); - let mut reader = BufferedReader::with_capacity(2, inner); - assert_eq!(reader.read_until(0), Some(~[0])); - assert_eq!(reader.read_until(2), Some(~[1, 2])); - assert_eq!(reader.read_until(1), Some(~[1])); - assert_eq!(reader.read_until(8), Some(~[0])); - assert_eq!(reader.read_until(9), None); - } - - #[test] - fn test_line_buffer() { - let mut writer = LineBufferedWriter::new(MemWriter::new()); - writer.write([0]); - assert_eq!(*writer.inner_ref().inner_ref(), ~[]); - writer.write([1]); - assert_eq!(*writer.inner_ref().inner_ref(), ~[]); - writer.flush(); - assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1]); - writer.write([0, '\n' as u8, 1]); - assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8]); - writer.flush(); - assert_eq!(*writer.inner_ref().inner_ref(), ~[0, 1, 0, '\n' as u8, 1]); - } -} diff --git a/src/libstd/rt/io/comm_adapters.rs b/src/libstd/rt/io/comm_adapters.rs deleted file mode 100644 index 98dbec27fb9..00000000000 --- a/src/libstd/rt/io/comm_adapters.rs +++ /dev/null @@ -1,57 +0,0 @@ -// 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 option::Option; -use comm::{GenericPort, GenericChan}; -use super::{Reader, Writer}; - -struct PortReader<P>; - -impl<P: GenericPort<~[u8]>> PortReader<P> { - pub fn new(_port: P) -> PortReader<P> { fail!() } -} - -impl<P: GenericPort<~[u8]>> Reader for PortReader<P> { - fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } - - fn eof(&mut self) -> bool { fail!() } -} - -struct ChanWriter<C>; - -impl<C: GenericChan<~[u8]>> ChanWriter<C> { - pub fn new(_chan: C) -> ChanWriter<C> { fail!() } -} - -impl<C: GenericChan<~[u8]>> Writer for ChanWriter<C> { - fn write(&mut self, _buf: &[u8]) { fail!() } -} - -struct ReaderPort<R>; - -impl<R: Reader> ReaderPort<R> { - pub fn new(_reader: R) -> ReaderPort<R> { fail!() } -} - -impl<R: Reader> GenericPort<~[u8]> for ReaderPort<R> { - fn recv(&self) -> ~[u8] { fail!() } - - fn try_recv(&self) -> Option<~[u8]> { fail!() } -} - -struct WriterChan<W>; - -impl<W: Writer> WriterChan<W> { - pub fn new(_writer: W) -> WriterChan<W> { fail!() } -} - -impl<W: Writer> GenericChan<~[u8]> for WriterChan<W> { - fn send(&self, _x: ~[u8]) { fail!() } -} diff --git a/src/libstd/rt/io/extensions.rs b/src/libstd/rt/io/extensions.rs deleted file mode 100644 index 261792977b3..00000000000 --- a/src/libstd/rt/io/extensions.rs +++ /dev/null @@ -1,490 +0,0 @@ -// 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 mixins that apply to all Readers and Writers - -// XXX: Not sure how this should be structured -// XXX: Iteration should probably be considered separately - -use iter::Iterator; -use option::Option; -use rt::io::{Reader, Decorator}; - -/// An iterator that reads a single byte on each iteration, -/// until `.read_byte()` returns `None`. -/// -/// # Notes about the Iteration Protocol -/// -/// The `ByteIterator` may yield `None` and thus terminate -/// an iteration, but continue to yield elements if iteration -/// is attempted again. -/// -/// # Failure -/// -/// Raises the same conditions as the `read` method, for -/// each call to its `.next()` method. -/// Yields `None` if the condition is handled. -pub struct ByteIterator<T> { - priv reader: T, -} - -impl<R: Reader> ByteIterator<R> { - pub fn new(r: R) -> ByteIterator<R> { - ByteIterator { reader: r } - } -} - -impl<R> Decorator<R> for ByteIterator<R> { - fn inner(self) -> R { self.reader } - fn inner_ref<'a>(&'a self) -> &'a R { &self.reader } - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { &mut self.reader } -} - -impl<'self, R: Reader> Iterator<u8> for ByteIterator<R> { - #[inline] - fn next(&mut self) -> Option<u8> { - self.reader.read_byte() - } -} - -pub fn u64_to_le_bytes<T>(n: u64, size: uint, - f: &fn(v: &[u8]) -> T) -> T { - assert!(size <= 8u); - match size { - 1u => f(&[n as u8]), - 2u => f(&[n as u8, - (n >> 8) as u8]), - 4u => f(&[n as u8, - (n >> 8) as u8, - (n >> 16) as u8, - (n >> 24) as u8]), - 8u => f(&[n as u8, - (n >> 8) as u8, - (n >> 16) as u8, - (n >> 24) as u8, - (n >> 32) as u8, - (n >> 40) as u8, - (n >> 48) as u8, - (n >> 56) as u8]), - _ => { - - let mut bytes: ~[u8] = ~[]; - let mut i = size; - let mut n = n; - while i > 0u { - bytes.push((n & 255_u64) as u8); - n >>= 8_u64; - i -= 1u; - } - f(bytes) - } - } -} - -pub fn u64_to_be_bytes<T>(n: u64, size: uint, - f: &fn(v: &[u8]) -> T) -> T { - assert!(size <= 8u); - match size { - 1u => f(&[n as u8]), - 2u => f(&[(n >> 8) as u8, - n as u8]), - 4u => f(&[(n >> 24) as u8, - (n >> 16) as u8, - (n >> 8) as u8, - n as u8]), - 8u => f(&[(n >> 56) as u8, - (n >> 48) as u8, - (n >> 40) as u8, - (n >> 32) as u8, - (n >> 24) as u8, - (n >> 16) as u8, - (n >> 8) as u8, - n as u8]), - _ => { - let mut bytes: ~[u8] = ~[]; - let mut i = size; - while i > 0u { - let shift = ((i - 1u) * 8u) as u64; - bytes.push((n >> shift) as u8); - i -= 1u; - } - f(bytes) - } - } -} - -pub fn u64_from_be_bytes(data: &[u8], - start: uint, - size: uint) - -> u64 { - let mut sz = size; - assert!((sz <= 8u)); - let mut val = 0_u64; - let mut pos = start; - while sz > 0u { - sz -= 1u; - val += (data[pos] as u64) << ((sz * 8u) as u64); - pos += 1u; - } - return val; -} - -#[cfg(test)] -mod test { - use option::{None, Option, Some}; - use rt::io::mem::{MemReader, MemWriter}; - use rt::io::{Reader, io_error, placeholder_error}; - - struct InitialZeroByteReader { - count: int, - } - - impl Reader for InitialZeroByteReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - if self.count == 0 { - self.count = 1; - Some(0) - } else { - buf[0] = 10; - Some(1) - } - } - fn eof(&mut self) -> bool { - false - } - } - - struct EofReader; - - impl Reader for EofReader { - fn read(&mut self, _: &mut [u8]) -> Option<uint> { - None - } - fn eof(&mut self) -> bool { - false - } - } - - struct ErroringReader; - - impl Reader for ErroringReader { - fn read(&mut self, _: &mut [u8]) -> Option<uint> { - io_error::cond.raise(placeholder_error()); - None - } - fn eof(&mut self) -> bool { - false - } - } - - struct PartialReader { - count: int, - } - - impl Reader for PartialReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - buf[1] = 11; - Some(2) - } else { - buf[0] = 12; - buf[1] = 13; - Some(2) - } - } - fn eof(&mut self) -> bool { - false - } - } - - struct ErroringLaterReader { - count: int, - } - - impl Reader for ErroringLaterReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - Some(1) - } else { - io_error::cond.raise(placeholder_error()); - None - } - } - fn eof(&mut self) -> bool { - false - } - } - - struct ThreeChunkReader { - count: int, - } - - impl Reader for ThreeChunkReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - if self.count == 0 { - self.count = 1; - buf[0] = 10; - buf[1] = 11; - Some(2) - } else if self.count == 1 { - self.count = 2; - buf[0] = 12; - buf[1] = 13; - Some(2) - } else { - None - } - } - fn eof(&mut self) -> bool { - false - } - } - - #[test] - fn read_byte() { - let mut reader = MemReader::new(~[10]); - let byte = reader.read_byte(); - assert!(byte == Some(10)); - } - - #[test] - fn read_byte_0_bytes() { - let mut reader = InitialZeroByteReader { - count: 0, - }; - let byte = reader.read_byte(); - assert!(byte == Some(10)); - } - - #[test] - fn read_byte_eof() { - let mut reader = EofReader; - let byte = reader.read_byte(); - assert!(byte == None); - } - - #[test] - fn read_byte_error() { - let mut reader = ErroringReader; - do io_error::cond.trap(|_| { - }).inside { - let byte = reader.read_byte(); - assert!(byte == None); - } - } - - #[test] - fn bytes_0_bytes() { - let reader = InitialZeroByteReader { - count: 0, - }; - let byte = reader.bytes().next(); - assert!(byte == Some(10)); - } - - #[test] - fn bytes_eof() { - let reader = EofReader; - let byte = reader.bytes().next(); - assert!(byte == None); - } - - #[test] - fn bytes_error() { - let reader = ErroringReader; - let mut it = reader.bytes(); - do io_error::cond.trap(|_| ()).inside { - let byte = it.next(); - assert!(byte == None); - } - } - - #[test] - fn read_bytes() { - let mut reader = MemReader::new(~[10, 11, 12, 13]); - let bytes = reader.read_bytes(4); - assert!(bytes == ~[10, 11, 12, 13]); - } - - #[test] - fn read_bytes_partial() { - let mut reader = PartialReader { - count: 0, - }; - let bytes = reader.read_bytes(4); - assert!(bytes == ~[10, 11, 12, 13]); - } - - #[test] - fn read_bytes_eof() { - let mut reader = MemReader::new(~[10, 11]); - do io_error::cond.trap(|_| { - }).inside { - assert!(reader.read_bytes(4) == ~[10, 11]); - } - } - - #[test] - fn push_bytes() { - let mut reader = MemReader::new(~[10, 11, 12, 13]); - let mut buf = ~[8, 9]; - reader.push_bytes(&mut buf, 4); - assert!(buf == ~[8, 9, 10, 11, 12, 13]); - } - - #[test] - fn push_bytes_partial() { - let mut reader = PartialReader { - count: 0, - }; - let mut buf = ~[8, 9]; - reader.push_bytes(&mut buf, 4); - assert!(buf == ~[8, 9, 10, 11, 12, 13]); - } - - #[test] - fn push_bytes_eof() { - let mut reader = MemReader::new(~[10, 11]); - let mut buf = ~[8, 9]; - do io_error::cond.trap(|_| { - }).inside { - reader.push_bytes(&mut buf, 4); - assert!(buf == ~[8, 9, 10, 11]); - } - } - - #[test] - fn push_bytes_error() { - let mut reader = ErroringLaterReader { - count: 0, - }; - let mut buf = ~[8, 9]; - do io_error::cond.trap(|_| { } ).inside { - reader.push_bytes(&mut buf, 4); - } - assert!(buf == ~[8, 9, 10]); - } - - #[test] - #[should_fail] - fn push_bytes_fail_reset_len() { - // push_bytes unsafely sets the vector length. This is testing that - // upon failure the length is reset correctly. - let mut reader = ErroringLaterReader { - count: 0, - }; - let buf = @mut ~[8, 9]; - do (|| { - reader.push_bytes(&mut *buf, 4); - }).finally { - // NB: Using rtassert here to trigger abort on failure since this is a should_fail test - // FIXME: #7049 This fails because buf is still borrowed - //rtassert!(*buf == ~[8, 9, 10]); - } - } - - #[test] - fn read_to_end() { - let mut reader = ThreeChunkReader { - count: 0, - }; - let buf = reader.read_to_end(); - assert!(buf == ~[10, 11, 12, 13]); - } - - #[test] - #[should_fail] - fn read_to_end_error() { - let mut reader = ThreeChunkReader { - count: 0, - }; - let buf = reader.read_to_end(); - assert!(buf == ~[10, 11]); - } - - #[test] - fn test_read_write_le_mem() { - let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::max_value]; - - let mut writer = MemWriter::new(); - for i in uints.iter() { - writer.write_le_u64(*i); - } - - let mut reader = MemReader::new(writer.inner()); - for i in uints.iter() { - assert!(reader.read_le_u64() == *i); - } - } - - - #[test] - fn test_read_write_be() { - let uints = [0, 1, 2, 42, 10_123, 100_123_456, ::u64::max_value]; - - let mut writer = MemWriter::new(); - for i in uints.iter() { - writer.write_be_u64(*i); - } - - let mut reader = MemReader::new(writer.inner()); - for i in uints.iter() { - assert!(reader.read_be_u64() == *i); - } - } - - #[test] - fn test_read_be_int_n() { - let ints = [::i32::min_value, -123456, -42, -5, 0, 1, ::i32::max_value]; - - let mut writer = MemWriter::new(); - for i in ints.iter() { - writer.write_be_i32(*i); - } - - let mut reader = MemReader::new(writer.inner()); - 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) == *i as i64); - } - } - - #[test] - fn test_read_f32() { - //big-endian floating-point 8.1250 - let buf = ~[0x41, 0x02, 0x00, 0x00]; - - let mut writer = MemWriter::new(); - writer.write(buf); - - let mut reader = MemReader::new(writer.inner()); - let f = reader.read_be_f32(); - assert!(f == 8.1250); - } - - #[test] - fn test_read_write_f32() { - let f:f32 = 8.1250; - - let mut writer = MemWriter::new(); - writer.write_be_f32(f); - writer.write_le_f32(f); - - let mut reader = MemReader::new(writer.inner()); - assert!(reader.read_be_f32() == 8.1250); - assert!(reader.read_le_f32() == 8.1250); - } - -} diff --git a/src/libstd/rt/io/flate.rs b/src/libstd/rt/io/flate.rs deleted file mode 100644 index 8a5aa171eb8..00000000000 --- a/src/libstd/rt/io/flate.rs +++ /dev/null @@ -1,123 +0,0 @@ -// 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. - -//! Some various other I/O types - -// FIXME(#3660): should move to libextra - -use prelude::*; -use super::*; - -/// A Writer decorator that compresses using the 'deflate' scheme -pub struct DeflateWriter<W> { - priv inner_writer: W -} - -impl<W: Writer> DeflateWriter<W> { - pub fn new(inner_writer: W) -> DeflateWriter<W> { - DeflateWriter { - inner_writer: inner_writer - } - } -} - -impl<W: Writer> Writer for DeflateWriter<W> { - fn write(&mut self, _buf: &[u8]) { fail!() } - - fn flush(&mut self) { fail!() } -} - -impl<W: Writer> Decorator<W> for DeflateWriter<W> { - fn inner(self) -> W { - match self { - DeflateWriter { inner_writer: w } => w - } - } - - fn inner_ref<'a>(&'a self) -> &'a W { - match *self { - DeflateWriter { inner_writer: ref w } => w - } - } - - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut W { - match *self { - DeflateWriter { inner_writer: ref mut w } => w - } - } -} - -/// A Reader decorator that decompresses using the 'deflate' scheme -pub struct InflateReader<R> { - priv inner_reader: R -} - -impl<R: Reader> InflateReader<R> { - pub fn new(inner_reader: R) -> InflateReader<R> { - InflateReader { - inner_reader: inner_reader - } - } -} - -impl<R: Reader> Reader for InflateReader<R> { - fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() } - - fn eof(&mut self) -> bool { fail!() } -} - -impl<R: Reader> Decorator<R> for InflateReader<R> { - fn inner(self) -> R { - match self { - InflateReader { inner_reader: r } => r - } - } - - fn inner_ref<'a>(&'a self) -> &'a R { - match *self { - InflateReader { inner_reader: ref r } => r - } - } - - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { - match *self { - InflateReader { inner_reader: ref mut r } => r - } - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use super::super::mem::*; - use super::super::Decorator; - - use str; - - #[test] - #[ignore] - fn smoke_test() { - let mem_writer = MemWriter::new(); - let mut deflate_writer = DeflateWriter::new(mem_writer); - let in_msg = "test"; - let in_bytes = in_msg.as_bytes(); - deflate_writer.write(in_bytes); - deflate_writer.flush(); - let buf = deflate_writer.inner().inner(); - let mem_reader = MemReader::new(buf); - let mut inflate_reader = InflateReader::new(mem_reader); - let mut out_bytes = [0, .. 100]; - let bytes_read = inflate_reader.read(out_bytes).unwrap(); - assert_eq!(bytes_read, in_bytes.len()); - let out_msg = str::from_utf8(out_bytes); - assert!(in_msg == out_msg); - } -} diff --git a/src/libstd/rt/io/fs.rs b/src/libstd/rt/io/fs.rs deleted file mode 100644 index 06c07308cf6..00000000000 --- a/src/libstd/rt/io/fs.rs +++ /dev/null @@ -1,1288 +0,0 @@ -// 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 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 a `Path` object. - -All operations in this module, including those as part of `File` et al -block the task during execution. Most will raise `std::rt::io::io_error` -conditions in the event of failure. - -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 - - use std::rt::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(bytes!("foobar")); - - // open the file in read-only mode - let mut file = File::open(&path); - file.read_to_end(); - - println!("{}", path.stat().size); - fs::symlink(&path, &Path::new("bar.txt")); - fs::unlink(&path); - -*/ - -use c_str::ToCStr; -use iter::Iterator; -use super::{Reader, Writer, Seek}; -use super::{SeekStyle, Read, Write, Open, IoError, Truncate, - FileMode, FileAccess, FileStat, io_error, FilePermission}; -use rt::rtio::{RtioFileStream, IoFactory, with_local_io}; -use rt::io; -use option::{Some, None, Option}; -use result::{Ok, Err, Result}; -use path; -use path::{Path, GenericPath}; -use vec::OwnedVector; - -/// Unconstrained file access type that exposes read and write operations -/// -/// Can be constructed via `File::open()`, `File::create()`, and -/// `File::open_mode()`. -/// -/// # Errors -/// -/// This type will raise an io_error condition 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 { - priv fd: ~RtioFileStream, - priv path: Path, - priv last_nread: int, -} - -fn io_raise<T>(f: &fn(io: &mut IoFactory) -> Result<T, IoError>) -> Option<T> { - do with_local_io |io| { - match f(io) { - Ok(t) => Some(t), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -impl File { - /// Open a file at `path` in the mode specified by the `mode` and `access` - /// arguments - /// - /// # Example - /// - /// use std::rt::io::{File, io_error, Open, ReadWrite}; - /// - /// let p = Path::new("/some/file/path.txt"); - /// - /// do io_error::cond.trap(|_| { - /// // hoo-boy... - /// }).inside { - /// let file = match File::open_mode(&p, Open, ReadWrite) { - /// Some(s) => s, - /// None => fail!("whoops! I'm sure this raised, anyways..") - /// }; - /// // 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::rt::io`'s docs. If a file is opened with `Write` - /// or `ReadWrite` access, then it will be created it 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 raise an `io_error` condition at runtime). - /// - /// # Errors - /// - /// This function will raise an `io_error` condition 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) -> Option<File> { - do with_local_io |io| { - match io.fs_open(&path.to_c_str(), mode, access) { - Ok(fd) => Some(File { - path: path.clone(), - fd: fd, - last_nread: -1 - }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } - - /// 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 - /// - /// use std::rt::io::File; - /// - /// let contents = File::open(&Path::new("foo.txt")).read_to_end(); - pub fn open(path: &Path) -> Option<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 - /// - /// use std::rt::io::File; - /// - /// let mut f = File::create(&Path::new("foo.txt")); - /// f.write(bytes!("This is a sample file")); - pub fn create(path: &Path) -> Option<File> { - File::open_mode(path, Truncate, Write) - } - - /// Returns the original path which 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. - /// - /// # Errors - /// - /// This function will raise on the `io_error` condition on failure. - pub fn fsync(&mut self) { - self.fd.fsync(); - } - - /// This function is similar to `fsync`, except that it may not synchronize - /// file metadata to the filesystem. This is intended for use case which - /// must synchronize content, but don't need the metadata on disk. The goal - /// of this method is to reduce disk operations. - /// - /// # Errors - /// - /// This function will raise on the `io_error` condition on failure. - pub fn datasync(&mut self) { - self.fd.datasync(); - } - - /// Either truncates or extends the underlying file, as extended from the - /// file's current position. This is equivalent to the unix `truncate` - /// function. - /// - /// The offset given is added to the file's current position and the result - /// is the new size of the file. If the new size is less than the current - /// size, then the file is truncated. If the new size is greater than the - /// current size, then the file is expanded to be filled with 0s. - /// - /// # Errors - /// - /// On error, this function will raise on the `io_error` condition. - pub fn truncate(&mut self, offset: i64) { - self.fd.truncate(offset); - } -} - -/// Unlink a file from the underlying filesystem. -/// -/// # Example -/// -/// use std::rt::io::fs; -/// -/// let p = Path::new("/some/file/path.txt"); -/// fs::unlink(&p); -/// // if we made it here without failing, then the -/// // unlink operation was successful -/// -/// 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) -/// -/// # Errors -/// -/// This function will raise an `io_error` condition if the path points to a -/// directory, the user lacks permissions to remove the file, or if some -/// other filesystem-level error occurs. -pub fn unlink(path: &Path) { - do io_raise |io| { io.fs_unlink(&path.to_c_str()) }; -} - -/// 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. -/// -/// Returns a fully-filled out stat structure on succes, and on failure it -/// will return a dummy stat structure (it is expected that the condition -/// raised is handled as well). -/// -/// # Example -/// -/// use std::rt::io; -/// use std::rt::io::fs; -/// -/// let p = Path::new("/some/file/path.txt"); -/// match io::result(|| fs::stat(&p)) { -/// Ok(stat) => { /* ... */ } -/// Err(e) => { /* handle error */ } -/// } -/// -/// # Errors -/// -/// This call will raise an `io_error` condition 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) -> FileStat { - do io_raise |io| { - io.fs_stat(&path.to_c_str()) - }.unwrap_or_else(dummystat) -} - -fn dummystat() -> FileStat { - FileStat { - path: Path::new(""), - size: 0, - kind: io::TypeFile, - perm: 0, - created: 0, - modified: 0, - accessed: 0, - unstable: io::UnstableFileStat { - device: 0, - inode: 0, - rdev: 0, - nlink: 0, - uid: 0, - gid: 0, - blksize: 0, - blocks: 0, - flags: 0, - gen: 0, - } - } -} - -/// 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. -/// -/// # Errors -/// -/// See `stat` -pub fn lstat(path: &Path) -> FileStat { - do io_raise |io| { - io.fs_lstat(&path.to_c_str()) - }.unwrap_or_else(dummystat) -} - -/// Rename a file or directory to a new name. -/// -/// # Example -/// -/// use std::rt::io::fs; -/// -/// fs::rename(&Path::new("foo"), &Path::new("bar")); -/// // Oh boy, nothing was raised! -/// -/// # Errors -/// -/// Will raise an `io_error` condition if the provided `path` doesn't exist, -/// the process lacks permissions to view the contents, or if some other -/// intermittent I/O error occurs. -pub fn rename(from: &Path, to: &Path) { - do io_raise |io| { - io.fs_rename(&from.to_c_str(), &to.to_c_str()) - }; -} - -/// 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 -/// -/// use std::rt::io::fs; -/// -/// fs::copy(&Path::new("foo.txt"), &Path::new("bar.txt")); -/// // Oh boy, nothing was raised! -/// -/// # Errors -/// -/// Will raise an `io_error` condition is the following situtations, 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) { - if !from.is_file() { - return io_error::cond.raise(IoError { - kind: io::MismatchedFileTypeForOperation, - desc: "the source path is not an existing file", - detail: None, - }); - } - - let mut reader = match File::open(from) { Some(f) => f, None => return }; - let mut writer = match File::create(to) { Some(f) => f, None => return }; - let mut buf = [0, ..io::DEFAULT_BUF_SIZE]; - - loop { - match reader.read(buf) { - Some(amt) => writer.write(buf.slice_to(amt)), - None => break - } - } - - chmod(to, from.stat().perm) -} - -/// Changes the permission mode bits found on a file or a directory. This -/// function takes a mask from the `io` module -/// -/// # Example -/// -/// use std::rt::io; -/// use std::rt::io::fs; -/// -/// fs::chmod(&Path::new("file.txt"), io::UserFile); -/// fs::chmod(&Path::new("file.txt"), io::UserRead | io::UserWrite); -/// fs::chmod(&Path::new("dir"), io::UserDir); -/// fs::chmod(&Path::new("file.exe"), io::UserExec); -/// -/// # Errors -/// -/// If this funciton encounters an I/O error, it will raise on the `io_error` -/// condition. Some possible error situations are not having the permission to -/// change the attributes of a file or the file not existing. -pub fn chmod(path: &Path, mode: io::FilePermission) { - do io_raise |io| { - io.fs_chmod(&path.to_c_str(), mode) - }; -} - -/// Change the user and group owners of a file at the specified path. -/// -/// # Errors -/// -/// This funtion will raise on the `io_error` condition on failure. -pub fn chown(path: &Path, uid: int, gid: int) { - do io_raise |io| { io.fs_chown(&path.to_c_str(), 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. -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition on failure. -pub fn link(src: &Path, dst: &Path) { - do io_raise |io| { io.fs_link(&src.to_c_str(), &dst.to_c_str()) }; -} - -/// Creates a new symbolic link on the filesystem. The `dst` path will be a -/// symlink pointing to the `src` path. -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition on failure. -pub fn symlink(src: &Path, dst: &Path) { - do io_raise |io| { io.fs_symlink(&src.to_c_str(), &dst.to_c_str()) }; -} - -/// Reads a symlink, returning the file that the symlink points to. -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition on failure. Failure -/// conditions include reading a file that does not exist or reading a file -/// which is not a symlink. -pub fn readlink(path: &Path) -> Option<Path> { - do io_raise |io| { io.fs_readlink(&path.to_c_str()) } -} - -/// Create a new, empty directory at the provided path -/// -/// # Example -/// -/// use std::libc::S_IRWXU; -/// use std::rt::io::fs; -/// -/// let p = Path::new("/some/dir"); -/// fs::mkdir(&p, S_IRWXU as int); -/// // If we got here, our directory exists! Horray! -/// -/// # Errors -/// -/// This call will raise an `io_error` condition 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) { - do io_raise |io| { - io.fs_mkdir(&path.to_c_str(), mode) - }; -} - -/// Remove an existing, empty directory -/// -/// # Example -/// -/// use std::rt::io::fs; -/// -/// let p = Path::new("/some/dir"); -/// fs::rmdir(&p); -/// // good riddance, you mean ol' directory -/// -/// # Errors -/// -/// This call will raise an `io_error` condition 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) { - do io_raise |io| { - io.fs_rmdir(&path.to_c_str()) - }; -} - -/// Retrieve a vector containing all entries within a provided directory -/// -/// # Example -/// -/// use std::rt::io::fs; -/// -/// // one possible implementation of fs::walk_dir only visiting files -/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { -/// if dir.is_dir() { -/// let contents = fs::readdir(dir).unwrap(); -/// for entry in contents.iter() { -/// if entry.is_dir() { visit_dirs(entry, cb); } -/// else { cb(entry); } -/// } -/// } -/// else { fail!("nope"); } -/// } -/// -/// # Errors -/// -/// Will raise an `io_error` condition if the provided `from` doesn't exist, -/// the process lacks permissions to view the contents or if the `path` points -/// at a non-directory file -pub fn readdir(path: &Path) -> ~[Path] { - do io_raise |io| { - io.fs_readdir(&path.to_c_str(), 0) - }.unwrap_or_else(|| ~[]) -} - -/// Returns an iterator which will recursively walk the directory structure -/// rooted at `path`. The path given will not be iterated over, and this will -/// perform iteration in a top-down order. -pub fn walk_dir(path: &Path) -> WalkIterator { - WalkIterator { stack: readdir(path) } -} - -/// An iterator which walks over a directory -pub struct WalkIterator { - priv stack: ~[Path], -} - -impl Iterator<Path> for WalkIterator { - fn next(&mut self) -> Option<Path> { - match self.stack.shift_opt() { - Some(path) => { - if path.is_dir() { - self.stack.push_all_move(readdir(&path)); - } - Some(path) - } - None => None - } - } -} - -/// Recursively create a directory and all of its parent components if they -/// are missing. -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition if an error -/// happens, see `fs::mkdir` for more information about error conditions -/// and performance. -pub fn mkdir_recursive(path: &Path, mode: FilePermission) { - // tjc: if directory exists but with different permissions, - // should we return false? - if path.is_dir() { - return - } - if path.filename().is_some() { - mkdir_recursive(&path.dir_path(), mode); - } - mkdir(path, mode) -} - -/// Removes a directory at this path, after removing all its contents. Use -/// carefully! -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition if an error -/// happens. See `file::unlink` and `fs::readdir` for possible error -/// conditions. -pub fn rmdir_recursive(path: &Path) { - let children = readdir(path); - for child in children.iter() { - if child.is_dir() { - rmdir_recursive(child); - } else { - unlink(child); - } - } - // Directory should now be empty - rmdir(path); -} - -/// 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. -/// -/// # Errors -/// -/// This function will raise on the `io_error` condition if an error -/// happens. -// FIXME(#10301) these arguments should not be u64 -pub fn change_file_times(path: &Path, atime: u64, mtime: u64) { - do io_raise |io| { - io.fs_utime(&path.to_c_str(), atime, mtime) - }; -} - -impl Reader for File { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - match self.fd.read(buf) { - Ok(read) => { - self.last_nread = read; - match read { - 0 => None, - _ => Some(read as uint) - } - }, - Err(ioerr) => { - // EOF is indicated by returning None - if ioerr.kind != io::EndOfFile { - io_error::cond.raise(ioerr); - } - return None; - } - } - } - - fn eof(&mut self) -> bool { self.last_nread == 0 } -} - -impl Writer for File { - fn write(&mut self, buf: &[u8]) { - match self.fd.write(buf) { - Ok(()) => (), - Err(ioerr) => { - io_error::cond.raise(ioerr); - } - } - } -} - -impl Seek for File { - fn tell(&self) -> u64 { - let res = self.fd.tell(); - match res { - Ok(cursor) => cursor, - Err(ioerr) => { - io_error::cond.raise(ioerr); - return -1; - } - } - } - - fn seek(&mut self, pos: i64, style: SeekStyle) { - match self.fd.seek(pos, style) { - Ok(_) => { - // successful seek resets EOF indicator - self.last_nread = -1; - () - }, - Err(ioerr) => { - io_error::cond.raise(ioerr); - } - } - } -} - -impl path::Path { - /// Get information on the file, directory, etc at this path. - /// - /// Consult the `file::stat` documentation for more info. - /// - /// This call preserves identical runtime/error semantics with `file::stat`. - pub fn stat(&self) -> FileStat { stat(self) } - - /// Boolean value indicator whether the underlying file exists on the local - /// filesystem. This will return true if the path points to either a - /// directory or a file. - /// - /// # Errors - /// - /// Will not raise a condition - pub fn exists(&self) -> bool { - io::result(|| self.stat()).is_ok() - } - - /// Whether the underlying implemention (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). - /// - /// # Errors - /// - /// Will not raise a condition - pub fn is_file(&self) -> bool { - match io::result(|| self.stat()) { - Ok(s) => s.kind == io::TypeFile, - Err(*) => false - } - } - - /// Whether the underlying implemention (be it a file path, - /// or something else) is pointing at a directory in the underlying FS. - /// Will return false for paths to non-existent locations or if the item is - /// not a directory (eg files, named pipes, links, etc) - /// - /// # Errors - /// - /// Will not raise a condition - pub fn is_dir(&self) -> bool { - match io::result(|| self.stat()) { - Ok(s) => s.kind == io::TypeDirectory, - Err(*) => false - } - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use rt::io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, ReadWrite}; - use rt::io; - use str; - use super::{File, rmdir, mkdir, readdir, rmdir_recursive, mkdir_recursive, - copy, unlink, stat, symlink, link, readlink, chmod, - lstat, change_file_times}; - - fn tmpdir() -> Path { - use os; - use rand; - let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>())); - mkdir(&ret, io::UserRWX); - ret - } - - fn free<T>(_: T) {} - - #[test] - fn file_test_io_smoke_test() { - let message = "it's alright. have a good time"; - let filename = &Path::new("./tmp/file_rt_io_file_test.txt"); - { - let mut write_stream = File::open_mode(filename, Open, ReadWrite); - 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 read_stream.read(read_buf).unwrap() { - -1|0 => fail!("shouldn't happen"), - n => str::from_utf8(read_buf.slice_to(n)) - }; - assert!(read_str == message.to_owned()); - } - unlink(filename); - } - - #[test] - fn file_test_io_invalid_path_opened_without_create_should_raise_condition() { - let filename = &Path::new("./tmp/file_that_does_not_exist.txt"); - let mut called = false; - do io_error::cond.trap(|_| { - called = true; - }).inside { - let result = File::open_mode(filename, Open, Read); - assert!(result.is_none()); - } - assert!(called); - } - - #[test] - fn file_test_iounlinking_invalid_path_should_raise_condition() { - let filename = &Path::new("./tmp/file_another_file_that_does_not_exist.txt"); - let mut called = false; - do io_error::cond.trap(|_| { - called = true; - }).inside { - unlink(filename); - } - assert!(called); - } - - #[test] - fn file_test_io_non_positional_read() { - let message = "ten-four"; - let mut read_mem = [0, .. 8]; - let filename = &Path::new("./tmp/file_rt_io_file_test_positional.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(message.as_bytes()); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - { - let read_buf = read_mem.mut_slice(0, 4); - read_stream.read(read_buf); - } - { - let read_buf = read_mem.mut_slice(4, 8); - read_stream.read(read_buf); - } - } - unlink(filename); - let read_str = str::from_utf8(read_mem); - assert!(read_str == message.to_owned()); - } - - #[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 filename = &Path::new("./tmp/file_rt_io_file_test_seeking.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(message.as_bytes()); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.seek(set_cursor as i64, SeekSet); - tell_pos_pre_read = read_stream.tell(); - read_stream.read(read_mem); - tell_pos_post_read = read_stream.tell(); - } - unlink(filename); - let read_str = str::from_utf8(read_mem); - assert!(read_str == message.slice(4, 8).to_owned()); - assert!(tell_pos_pre_read == set_cursor); - assert!(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 = 3; - let mut read_mem = [0, .. 13]; - let filename = &Path::new("./tmp/file_rt_io_file_test_seek_and_write.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(initial_msg.as_bytes()); - rw_stream.seek(seek_idx as i64, SeekSet); - rw_stream.write(overwrite_msg.as_bytes()); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.read(read_mem); - } - unlink(filename); - let read_str = str::from_utf8(read_mem); - assert!(read_str == final_msg.to_owned()); - } - - #[test] - fn file_test_io_seek_shakedown() { - use std::str; // 01234567890123 - let initial_msg = "qwer-asdf-zxcv"; - let chunk_one = "qwer"; - let chunk_two = "asdf"; - let chunk_three = "zxcv"; - let mut read_mem = [0, .. 4]; - let filename = &Path::new("./tmp/file_rt_io_file_test_seek_shakedown.txt"); - { - let mut rw_stream = File::open_mode(filename, Open, ReadWrite); - rw_stream.write(initial_msg.as_bytes()); - } - { - let mut read_stream = File::open_mode(filename, Open, Read); - - read_stream.seek(-4, SeekEnd); - read_stream.read(read_mem); - let read_str = str::from_utf8(read_mem); - assert!(read_str == chunk_three.to_owned()); - - read_stream.seek(-9, SeekCur); - read_stream.read(read_mem); - let read_str = str::from_utf8(read_mem); - assert!(read_str == chunk_two.to_owned()); - - read_stream.seek(0, SeekSet); - read_stream.read(read_mem); - let read_str = str::from_utf8(read_mem); - assert!(read_str == chunk_one.to_owned()); - } - unlink(filename); - } - - #[test] - fn file_test_stat_is_correct_on_is_file() { - let filename = &Path::new("./tmp/file_stat_correct_on_is_file.txt"); - { - let mut fs = File::open_mode(filename, Open, ReadWrite); - let msg = "hw"; - fs.write(msg.as_bytes()); - } - let stat_res = stat(filename); - assert_eq!(stat_res.kind, io::TypeFile); - unlink(filename); - } - - #[test] - fn file_test_stat_is_correct_on_is_dir() { - let filename = &Path::new("./tmp/file_stat_correct_on_is_dir"); - mkdir(filename, io::UserRWX); - let stat_res = filename.stat(); - assert!(stat_res.kind == io::TypeDirectory); - rmdir(filename); - } - - #[test] - fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - let dir = &Path::new("./tmp/fileinfo_false_on_dir"); - mkdir(dir, io::UserRWX); - assert!(dir.is_file() == false); - rmdir(dir); - } - - #[test] - fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - let file = &Path::new("./tmp/fileinfo_check_exists_b_and_a.txt"); - File::create(file).write(bytes!("foo")); - assert!(file.exists()); - unlink(file); - assert!(!file.exists()); - } - - #[test] - fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { - let dir = &Path::new("./tmp/before_and_after_dir"); - assert!(!dir.exists()); - mkdir(dir, io::UserRWX); - assert!(dir.exists()); - assert!(dir.is_dir()); - rmdir(dir); - assert!(!dir.exists()); - } - - #[test] - fn file_test_directoryinfo_readdir() { - use std::str; - let dir = &Path::new("./tmp/di_readdir"); - mkdir(dir, io::UserRWX); - let prefix = "foo"; - for n in range(0,3) { - let f = dir.join(format!("{}.txt", n)); - let mut w = File::create(&f); - let msg_str = (prefix + n.to_str().to_owned()).to_owned(); - let msg = msg_str.as_bytes(); - w.write(msg); - } - let files = readdir(dir); - let mut mem = [0u8, .. 4]; - for f in files.iter() { - { - let n = f.filestem_str(); - File::open(f).read(mem); - let read_str = str::from_utf8(mem); - let expected = match n { - None|Some("") => fail!("really shouldn't happen.."), - Some(n) => prefix+n - }; - assert!(expected == read_str); - } - unlink(f); - } - rmdir(dir); - } - - #[test] - fn recursive_mkdir_slash() { - mkdir_recursive(&Path::new("/"), io::UserRWX); - } - - #[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.clone(); - dirpath.push(format!("test-가一ー你好")); - mkdir(&dirpath, io::UserRWX); - assert!(dirpath.is_dir()); - - let mut filepath = dirpath; - filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); - File::create(&filepath); // ignore return; touch only - assert!(!filepath.is_dir()); - assert!(filepath.exists()); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn unicode_path_exists() { - assert!(Path::new(".").exists()); - assert!(!Path::new("test/nonexistent-bogus-path").exists()); - - let tmpdir = tmpdir(); - let unicode = tmpdir.clone(); - let unicode = unicode.join(format!("test-각丁ー再见")); - mkdir(&unicode, io::UserRWX); - assert!(unicode.exists()); - assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_does_not_exist() { - let from = Path::new("test/nonexistent-bogus-path"); - let to = Path::new("test/other-bogus-path"); - match io::result(|| copy(&from, &to)) { - Ok(*) => fail!(), - 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"); - - File::create(&input).write(bytes!("hello")); - copy(&input, &out); - let contents = File::open(&out).read_to_end(); - assert_eq!(contents.as_slice(), bytes!("hello")); - - assert_eq!(input.stat().perm, out.stat().perm); - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_dst_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - File::create(&out); - match io::result(|| copy(&out, &tmpdir)) { - Ok(*) => fail!(), Err(*) => {} - } - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_dst_exists() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in"); - let output = tmpdir.join("out"); - - File::create(&input).write("foo".as_bytes()); - File::create(&output).write("bar".as_bytes()); - copy(&input, &output); - - assert_eq!(File::open(&output).read_to_end(), - (bytes!("foo")).to_owned()); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_src_dir() { - let tmpdir = tmpdir(); - let out = tmpdir.join("out"); - - match io::result(|| copy(&tmpdir, &out)) { - Ok(*) => fail!(), Err(*) => {} - } - assert!(!out.exists()); - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_preserves_perm_bits() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - File::create(&input); - chmod(&input, io::UserRead); - copy(&input, &out); - assert!(out.stat().perm & io::UserWrite == 0); - - chmod(&input, io::UserFile); - chmod(&out, io::UserFile); - rmdir_recursive(&tmpdir); - } - - #[test] - #[ignore(cfg(windows))] // FIXME(#10264) operation not permitted? - fn symlinks_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - File::create(&input).write("foobar".as_bytes()); - symlink(&input, &out); - assert_eq!(lstat(&out).kind, io::TypeSymlink); - assert_eq!(stat(&out).size, stat(&input).size); - assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned()); - - rmdir_recursive(&tmpdir); - } - - #[test] - #[ignore(cfg(windows))] // apparently windows doesn't like symlinks - fn symlink_noexist() { - let tmpdir = tmpdir(); - // symlinks can point to things that don't exist - symlink(&tmpdir.join("foo"), &tmpdir.join("bar")); - assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); - rmdir_recursive(&tmpdir); - } - - #[test] - fn readlink_not_symlink() { - let tmpdir = tmpdir(); - match io::result(|| readlink(&tmpdir)) { - Ok(*) => fail!("wanted a failure"), - Err(*) => {} - } - rmdir_recursive(&tmpdir); - } - - #[test] - fn links_work() { - let tmpdir = tmpdir(); - let input = tmpdir.join("in.txt"); - let out = tmpdir.join("out.txt"); - - File::create(&input).write("foobar".as_bytes()); - link(&input, &out); - assert_eq!(lstat(&out).kind, io::TypeFile); - assert_eq!(stat(&out).size, stat(&input).size); - assert_eq!(stat(&out).unstable.nlink, 2); - assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned()); - - // can't link to yourself - match io::result(|| link(&input, &input)) { - Ok(*) => fail!("wanted a failure"), - Err(*) => {} - } - // can't link to something that doesn't exist - match io::result(|| link(&tmpdir.join("foo"), &tmpdir.join("bar"))) { - Ok(*) => fail!("wanted a failure"), - Err(*) => {} - } - - rmdir_recursive(&tmpdir); - } - - #[test] - fn chmod_works() { - let tmpdir = tmpdir(); - let file = tmpdir.join("in.txt"); - - File::create(&file); - assert!(stat(&file).perm & io::UserWrite == io::UserWrite); - chmod(&file, io::UserRead); - assert!(stat(&file).perm & io::UserWrite == 0); - - match io::result(|| chmod(&tmpdir.join("foo"), io::UserRWX)) { - Ok(*) => fail!("wanted a failure"), - Err(*) => {} - } - - chmod(&file, io::UserFile); - rmdir_recursive(&tmpdir); - } - - #[test] - fn sync_doesnt_kill_anything() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); - file.fsync(); - file.datasync(); - file.write(bytes!("foo")); - file.fsync(); - file.datasync(); - free(file); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn truncate_works() { - let tmpdir = tmpdir(); - let path = tmpdir.join("in.txt"); - - let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); - file.write(bytes!("foo")); - - // Do some simple things with truncation - assert_eq!(stat(&path).size, 3); - file.truncate(10); - assert_eq!(stat(&path).size, 10); - file.write(bytes!("bar")); - assert_eq!(stat(&path).size, 10); - assert_eq!(File::open(&path).read_to_end(), - (bytes!("foobar", 0, 0, 0, 0)).to_owned()); - - // 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). - file.truncate(2); - assert_eq!(stat(&path).size, 2); - file.write(bytes!("wut")); - assert_eq!(stat(&path).size, 9); - assert_eq!(File::open(&path).read_to_end(), - (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned()); - free(file); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn open_flavors() { - let tmpdir = tmpdir(); - - match io::result(|| File::open_mode(&tmpdir.join("a"), io::Open, - io::Read)) { - Ok(*) => fail!(), Err(*) => {} - } - File::open_mode(&tmpdir.join("b"), io::Open, io::Write).unwrap(); - File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("d"), io::Append, io::Write).unwrap(); - File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write).unwrap(); - File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite).unwrap(); - - File::create(&tmpdir.join("h")).write("foo".as_bytes()); - File::open_mode(&tmpdir.join("h"), io::Open, io::Read).unwrap(); - { - let mut f = File::open_mode(&tmpdir.join("h"), io::Open, - io::Read).unwrap(); - match io::result(|| f.write("wut".as_bytes())) { - Ok(*) => fail!(), Err(*) => {} - } - } - assert_eq!(stat(&tmpdir.join("h")).size, 3); - { - let mut f = File::open_mode(&tmpdir.join("h"), io::Append, - io::Write).unwrap(); - f.write("bar".as_bytes()); - } - assert_eq!(stat(&tmpdir.join("h")).size, 6); - { - let mut f = File::open_mode(&tmpdir.join("h"), io::Truncate, - io::Write).unwrap(); - f.write("bar".as_bytes()); - } - assert_eq!(stat(&tmpdir.join("h")).size, 3); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn utime() { - let tmpdir = tmpdir(); - let path = tmpdir.join("a"); - File::create(&path); - - change_file_times(&path, 1000, 2000); - assert_eq!(path.stat().accessed, 1000); - assert_eq!(path.stat().modified, 2000); - - rmdir_recursive(&tmpdir); - } - - #[test] - fn utime_noexist() { - let tmpdir = tmpdir(); - - match io::result(|| change_file_times(&tmpdir.join("a"), 100, 200)) { - Ok(*) => fail!(), - Err(*) => {} - } - - rmdir_recursive(&tmpdir); - } -} diff --git a/src/libstd/rt/io/mem.rs b/src/libstd/rt/io/mem.rs deleted file mode 100644 index 6803637d7cb..00000000000 --- a/src/libstd/rt/io/mem.rs +++ /dev/null @@ -1,308 +0,0 @@ -// 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. - -//! Readers and Writers for in-memory buffers -//! -//! # XXX -//! -//! * Should probably have something like this for strings. -//! * Should they implement Closable? Would take extra state. - -use cmp::min; -use prelude::*; -use super::*; -use vec; - -/// Writes to an owned, growable byte vector -pub struct MemWriter { - priv buf: ~[u8], - priv pos: uint, -} - -impl MemWriter { - pub fn new() -> MemWriter { - MemWriter { buf: vec::with_capacity(128), pos: 0 } - } -} - -impl Writer for MemWriter { - fn write(&mut self, buf: &[u8]) { - // Make sure the internal buffer is as least as big as where we - // currently are - let difference = self.pos as i64 - self.buf.len() as i64; - if difference > 0 { - self.buf.grow(difference as uint, &0); - } - - // Figure out what bytes will be used to overwrite what's currently - // there (left), and what will be appended on the end (right) - let cap = self.buf.len() - self.pos; - let (left, right) = if cap <= buf.len() { - (buf.slice_to(cap), buf.slice_from(cap)) - } else { - (buf, &[]) - }; - - // Do the necessary writes - if left.len() > 0 { - vec::bytes::copy_memory(self.buf.mut_slice_from(self.pos), - left, left.len()); - } - if right.len() > 0 { - self.buf.push_all(right); - } - - // Bump us forward - self.pos += buf.len(); - } -} - -impl Seek for MemWriter { - fn tell(&self) -> u64 { self.pos as u64 } - - fn seek(&mut self, pos: i64, style: SeekStyle) { - match style { - SeekSet => { self.pos = pos as uint; } - SeekEnd => { self.pos = self.buf.len() + pos as uint; } - SeekCur => { self.pos += pos as uint; } - } - } -} - -impl Decorator<~[u8]> for MemWriter { - fn inner(self) -> ~[u8] { self.buf } - fn inner_ref<'a>(&'a self) -> &'a ~[u8] { &self.buf } - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { &mut self.buf } -} - -/// Reads from an owned byte vector -pub struct MemReader { - priv buf: ~[u8], - priv pos: uint -} - -impl MemReader { - pub fn new(buf: ~[u8]) -> MemReader { - MemReader { - buf: buf, - pos: 0 - } - } -} - -impl Reader for MemReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - { if self.eof() { return None; } } - - let write_len = min(buf.len(), self.buf.len() - self.pos); - { - let input = self.buf.slice(self.pos, self.pos + write_len); - let output = buf.mut_slice(0, write_len); - assert_eq!(input.len(), output.len()); - vec::bytes::copy_memory(output, input, write_len); - } - self.pos += write_len; - assert!(self.pos <= self.buf.len()); - - return Some(write_len); - } - - fn eof(&mut self) -> bool { self.pos == self.buf.len() } -} - -impl Seek for MemReader { - fn tell(&self) -> u64 { self.pos as u64 } - - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } -} - -impl Decorator<~[u8]> for MemReader { - - fn inner(self) -> ~[u8] { - match self { - MemReader { buf: buf, _ } => buf - } - } - - fn inner_ref<'a>(&'a self) -> &'a ~[u8] { - match *self { - MemReader { buf: ref buf, _ } => buf - } - } - - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut ~[u8] { - match *self { - MemReader { buf: ref mut buf, _ } => buf - } - } -} - - -/// Writes to a fixed-size byte slice -pub struct BufWriter<'self> { - priv buf: &'self mut [u8], - priv pos: uint -} - -impl<'self> BufWriter<'self> { - pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { - BufWriter { - buf: buf, - pos: 0 - } - } -} - -impl<'self> Writer for BufWriter<'self> { - fn write(&mut self, _buf: &[u8]) { fail!() } - - fn flush(&mut self) { fail!() } -} - -impl<'self> Seek for BufWriter<'self> { - fn tell(&self) -> u64 { fail!() } - - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } -} - - -/// Reads from a fixed-size byte slice -pub struct BufReader<'self> { - priv buf: &'self [u8], - priv pos: uint -} - -impl<'self> BufReader<'self> { - pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { - BufReader { - buf: buf, - pos: 0 - } - } -} - -impl<'self> Reader for BufReader<'self> { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - { if self.eof() { return None; } } - - let write_len = min(buf.len(), self.buf.len() - self.pos); - { - let input = self.buf.slice(self.pos, self.pos + write_len); - let output = buf.mut_slice(0, write_len); - assert_eq!(input.len(), output.len()); - vec::bytes::copy_memory(output, input, write_len); - } - self.pos += write_len; - assert!(self.pos <= self.buf.len()); - - return Some(write_len); - } - - fn eof(&mut self) -> bool { self.pos == self.buf.len() } -} - -impl<'self> Seek for BufReader<'self> { - fn tell(&self) -> u64 { self.pos as u64 } - - fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() } -} - -///Calls a function with a MemWriter and returns -///the writer's stored vector. -pub fn with_mem_writer(writeFn:&fn(&mut MemWriter)) -> ~[u8] { - let mut writer = MemWriter::new(); - writeFn(&mut writer); - writer.inner() -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use rt::io::*; - - #[test] - fn test_mem_writer() { - let mut writer = MemWriter::new(); - assert_eq!(writer.tell(), 0); - writer.write([0]); - assert_eq!(writer.tell(), 1); - writer.write([1, 2, 3]); - writer.write([4, 5, 6, 7]); - assert_eq!(writer.tell(), 8); - assert_eq!(*writer.inner_ref(), ~[0, 1, 2, 3, 4, 5, 6, 7]); - - writer.seek(0, SeekSet); - assert_eq!(writer.tell(), 0); - writer.write([3, 4]); - assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 3, 4, 5, 6, 7]); - - writer.seek(1, SeekCur); - writer.write([0, 1]); - assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 7]); - - writer.seek(-1, SeekEnd); - writer.write([1, 2]); - assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 1, 2]); - - writer.seek(1, SeekEnd); - writer.write([1]); - assert_eq!(*writer.inner_ref(), ~[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]); - } - - #[test] - fn test_mem_reader() { - let mut reader = MemReader::new(~[0, 1, 2, 3, 4, 5, 6, 7]); - let mut buf = []; - assert_eq!(reader.read(buf), Some(0)); - assert_eq!(reader.tell(), 0); - let mut buf = [0]; - assert_eq!(reader.read(buf), Some(1)); - assert_eq!(reader.tell(), 1); - assert_eq!(buf, [0]); - let mut buf = [0, ..4]; - assert_eq!(reader.read(buf), Some(4)); - assert_eq!(reader.tell(), 5); - assert_eq!(buf, [1, 2, 3, 4]); - assert_eq!(reader.read(buf), Some(3)); - assert_eq!(buf.slice(0, 3), [5, 6, 7]); - assert!(reader.eof()); - assert_eq!(reader.read(buf), None); - assert!(reader.eof()); - } - - #[test] - fn test_buf_reader() { - let in_buf = ~[0, 1, 2, 3, 4, 5, 6, 7]; - let mut reader = BufReader::new(in_buf); - let mut buf = []; - assert_eq!(reader.read(buf), Some(0)); - assert_eq!(reader.tell(), 0); - let mut buf = [0]; - assert_eq!(reader.read(buf), Some(1)); - assert_eq!(reader.tell(), 1); - assert_eq!(buf, [0]); - let mut buf = [0, ..4]; - assert_eq!(reader.read(buf), Some(4)); - assert_eq!(reader.tell(), 5); - assert_eq!(buf, [1, 2, 3, 4]); - assert_eq!(reader.read(buf), Some(3)); - assert_eq!(buf.slice(0, 3), [5, 6, 7]); - assert!(reader.eof()); - assert_eq!(reader.read(buf), None); - assert!(reader.eof()); - } - - #[test] - fn test_with_mem_writer() { - let buf = with_mem_writer(|wr| wr.write([1,2,3,4,5,6,7])); - assert_eq!(buf, ~[1,2,3,4,5,6,7]); - } -} diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs deleted file mode 100644 index ce9504a5b43..00000000000 --- a/src/libstd/rt/io/mod.rs +++ /dev/null @@ -1,1224 +0,0 @@ -// 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 I/O - -This module defines the Rust interface for synchronous I/O. -It models byte-oriented input and output with the Reader and Writer traits. -Types that implement both `Reader` and `Writer` are called 'streams', -and automatically implement the `Stream` trait. -Implementations are provided for common I/O streams like -file, TCP, UDP, Unix domain sockets. -Readers and Writers may be composed to add capabilities like string -parsing, encoding, and compression. - -This will likely live in std::io, not std::rt::io. - -# Examples - -Some examples of obvious things you might want to do - -* Read lines from stdin - - for stdin().each_line |line| { - println(line) - } - -* Read a complete file to a string, (converting newlines?) - - let contents = File::open("message.txt").read_to_str(); // read_to_str?? - -* Write a line to a file - - let file = File::open("message.txt", Create, Write); - file.write_line("hello, file!"); - -* Iterate over the lines of a file - - do File::open("message.txt").each_line |line| { - println(line) - } - -* Pull the lines of a file into a vector of strings - - let lines = File::open("message.txt").line_iter().to_vec(); - -* Make an simple HTTP request - - let socket = TcpStream::open("localhost:8080"); - socket.write_line("GET / HTTP/1.0"); - socket.write_line(""); - let response = socket.read_to_end(); - -* Connect based on URL? Requires thinking about where the URL type lives - and how to make protocol handlers extensible, e.g. the "tcp" protocol - yields a `TcpStream`. - - connect("tcp://localhost:8080"); - -# Terms - -* Reader - An I/O source, reads bytes into a buffer -* Writer - An I/O sink, writes bytes from a buffer -* Stream - Typical I/O sources like files and sockets are both Readers and Writers, - and are collectively referred to a `streams`. -* Decorator - A Reader or Writer that composes with others to add additional capabilities - such as encoding or decoding - -# Blocking and synchrony - -When discussing I/O you often hear the terms 'synchronous' and -'asynchronous', along with 'blocking' and 'non-blocking' compared and -contrasted. A synchronous I/O interface performs each I/O operation to -completion before proceeding to the next. Synchronous interfaces are -usually used in imperative style as a sequence of commands. An -asynchronous interface allows multiple I/O requests to be issued -simultaneously, without waiting for each to complete before proceeding -to the next. - -Asynchronous interfaces are used to achieve 'non-blocking' I/O. In -traditional single-threaded systems, performing a synchronous I/O -operation means that the program stops all activity (it 'blocks') -until the I/O is complete. Blocking is bad for performance when -there are other computations that could be done. - -Asynchronous interfaces are most often associated with the callback -(continuation-passing) style popularised by node.js. Such systems rely -on all computations being run inside an event loop which maintains a -list of all pending I/O events; when one completes the registered -callback is run and the code that made the I/O request continues. -Such interfaces achieve non-blocking at the expense of being more -difficult to reason about. - -Rust's I/O interface is synchronous - easy to read - and non-blocking by default. - -Remember that Rust tasks are 'green threads', lightweight threads that -are multiplexed onto a single operating system thread. If that system -thread blocks then no other task may proceed. Rust tasks are -relatively cheap to create, so as long as other tasks are free to -execute then non-blocking code may be written by simply creating a new -task. - -When discussing blocking in regards to Rust's I/O model, we are -concerned with whether performing I/O blocks other Rust tasks from -proceeding. In other words, when a task calls `read`, it must then -wait (or 'sleep', or 'block') until the call to `read` is complete. -During this time, other tasks may or may not be executed, depending on -how `read` is implemented. - - -Rust's default I/O implementation is non-blocking; by cooperating -directly with the task scheduler it arranges to never block progress -of *other* tasks. Under the hood, Rust uses asynchronous I/O via a -per-scheduler (and hence per-thread) event loop. Synchronous I/O -requests are implemented by descheduling the running task and -performing an asynchronous request; the task is only resumed once the -asynchronous request completes. - -For blocking (but possibly more efficient) implementations, look -in the `io::native` module. - -# Error Handling - -I/O is an area where nearly every operation can result in unexpected -errors. It should allow errors to be handled efficiently. -It needs to be convenient to use I/O when you don't care -about dealing with specific errors. - -Rust's I/O employs a combination of techniques to reduce boilerplate -while still providing feedback about errors. The basic strategy: - -* Errors are fatal by default, resulting in task failure -* Errors raise the `io_error` condition which provides an opportunity to inspect - an IoError object containing details. -* Return values must have a sensible null or zero value which is returned - if a condition is handled successfully. This may be an `Option`, an empty - vector, or other designated error value. -* Common traits are implemented for `Option`, e.g. `impl<R: Reader> Reader for Option<R>`, - so that nullable values do not have to be 'unwrapped' before use. - -These features combine in the API to allow for expressions like -`File::new("diary.txt").write_line("met a girl")` 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 the task will fail. - -If you wanted to handle the error though you might write - - let mut error = None; - do io_error::cond(|e: IoError| { - error = Some(e); - }).in { - File::new("diary.txt").write_line("met a girl"); - } - - if error.is_some() { - println("failed to write my diary"); - } - -XXX: Need better condition handling syntax - -In this case the condition handler will have the opportunity to -inspect the IoError raised by either the call to `new` or the call to -`write_line`, but then execution will continue. - -So what actually happens if `new` encounters an error? To understand -that it's important to know that what `new` returns is not a `File` -but an `Option<File>`. If the file does not open, and the condition -is handled, then `new` will simply return `None`. 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 `Option<File>` and we simply call `write_line` on it. If `new` -returned a `None` then the followup call to `write_line` will also -raise an error. - -## Concerns about this strategy - -This structure will encourage a programming style that is prone -to errors similar to null pointer dereferences. -In particular code written to ignore errors and expect conditions to be unhandled -will start passing around null or zero objects when wrapped in a condition handler. - -* XXX: How should we use condition handlers that return values? -* XXX: Should EOF raise default conditions when EOF is not an error? - -# Issues with i/o scheduler affinity, work stealing, task pinning - -# Resource management - -* `close` vs. RAII - -# Paths, URLs and overloaded constructors - - - -# Scope - -In scope for core - -* Url? - -Some I/O things don't belong in core - - - url - - net - `fn connect` - - http - - flate - -Out of scope - -* Async I/O. We'll probably want it eventually - - -# XXX Questions and issues - -* Should default constructors take `Path` or `&str`? `Path` makes simple cases verbose. - Overloading would be nice. -* Add overloading for Path and &str and Url &str -* stdin/err/out -* print, println, etc. -* fsync -* relationship with filesystem querying, Directory, File types etc. -* Rename Reader/Writer to ByteReader/Writer, make Reader/Writer generic? -* Can Port and Chan be implementations of a generic Reader<T>/Writer<T>? -* Trait for things that are both readers and writers, Stream? -* How to handle newline conversion -* String conversion -* open vs. connect for generic stream opening -* Do we need `close` at all? dtors might be good enough -* How does I/O relate to the Iterator trait? -* std::base64 filters -* Using conditions is a big unknown since we don't have much experience with them -* Too many uses of OtherIoError - -*/ - -use cast; -use int; -use path::Path; -use str::{StrSlice, OwnedStr}; -use option::{Option, Some, None}; -use result::{Ok, Err, Result}; -use iter::Iterator; -use to_str::ToStr; -use uint; -use unstable::finally::Finally; -use 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::net::udp::UdpStream; -pub use self::pipe::PipeStream; -pub use self::process::Process; - -/// Synchronous, non-blocking filesystem operations. -pub mod fs; - -/// Synchronous, in-memory I/O. -pub mod pipe; - -/// Child process management. -pub mod process; - -/// Synchronous, non-blocking network I/O. -pub mod net; - -/// Readers and Writers for memory buffers and strings. -pub mod mem; - -/// Non-blocking access to stdin, stdout, stderr -pub mod stdio; - -/// Implementations for Option -mod option; - -/// Basic stream compression. XXX: Belongs with other flate code -pub mod flate; - -/// Interop between byte streams and pipes. Not sure where it belongs -pub mod comm_adapters; - -/// Extension traits -pub mod extensions; - -/// Basic Timer -pub mod timer; - -/// Buffered I/O wrappers -pub mod buffered; - -/// Thread-blocking implementations -pub mod native { - /// Posix file I/O - pub mod file; - /// Process spawning and child management - pub mod process; - /// Posix stdio - pub mod stdio; - - /// Sockets - /// # XXX - implement this - pub mod net { - pub mod tcp { } - pub mod udp { } - #[cfg(unix)] - pub mod unix { } - } -} - -/// Signal handling -pub mod signal; - -/// The default buffer size for various I/O operations -static DEFAULT_BUF_SIZE: uint = 1024 * 64; - -/// The type passed to I/O condition handlers to indicate error -/// -/// # XXX -/// -/// Is something like this sufficient? It's kind of archaic -pub struct IoError { - kind: IoErrorKind, - desc: &'static str, - detail: Option<~str> -} - -// FIXME: #8242 implementing manually because deriving doesn't work for some reason -impl ToStr for IoError { - fn to_str(&self) -> ~str { - let mut s = ~"IoError { kind: "; - s.push_str(self.kind.to_str()); - s.push_str(", desc: "); - s.push_str(self.desc); - s.push_str(", detail: "); - s.push_str(self.detail.to_str()); - s.push_str(" }"); - s - } -} - -#[deriving(Eq)] -pub enum IoErrorKind { - PreviousIoError, - OtherIoError, - EndOfFile, - FileNotFound, - PermissionDenied, - ConnectionFailed, - Closed, - ConnectionRefused, - ConnectionReset, - ConnectionAborted, - NotConnected, - BrokenPipe, - PathAlreadyExists, - PathDoesntExist, - MismatchedFileTypeForOperation, - ResourceUnavailable, - IoUnavailable, -} - -// FIXME: #8242 implementing manually because deriving doesn't work for some reason -impl ToStr for IoErrorKind { - fn to_str(&self) -> ~str { - match *self { - PreviousIoError => ~"PreviousIoError", - OtherIoError => ~"OtherIoError", - EndOfFile => ~"EndOfFile", - FileNotFound => ~"FileNotFound", - PermissionDenied => ~"PermissionDenied", - ConnectionFailed => ~"ConnectionFailed", - Closed => ~"Closed", - ConnectionRefused => ~"ConnectionRefused", - ConnectionReset => ~"ConnectionReset", - NotConnected => ~"NotConnected", - BrokenPipe => ~"BrokenPipe", - PathAlreadyExists => ~"PathAlreadyExists", - PathDoesntExist => ~"PathDoesntExist", - MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation", - IoUnavailable => ~"IoUnavailable", - ResourceUnavailable => ~"ResourceUnavailable", - ConnectionAborted => ~"ConnectionAborted", - } - } -} - -// XXX: Can't put doc comments on macros -// Raised by `I/O` operations on error. -condition! { - pub io_error: IoError -> (); -} - -/// Helper for wrapper calls where you want to -/// ignore any io_errors that might be raised -pub fn ignore_io_error<T>(cb: &fn() -> T) -> T { - do io_error::cond.trap(|_| { - // just swallow the error.. downstream users - // who can make a decision based on a None result - // won't care - }).inside { - cb() - } -} - -/// Helper for catching an I/O error and wrapping it in a Result object. The -/// return result will be the last I/O error that happened or the result of the -/// closure if no error occurred. -pub fn result<T>(cb: &fn() -> T) -> Result<T, IoError> { - let mut err = None; - let ret = io_error::cond.trap(|e| { - if err.is_none() { - err = Some(e); - } - }).inside(cb); - match err { - Some(e) => Err(e), - None => Ok(ret), - } -} - -pub trait Reader { - - // Only two methods 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 my - /// be less than the number requested, even 0. Returns `None` on EOF. - /// - /// # Failure - /// - /// Raises the `io_error` condition on error. If the condition - /// is handled then no guarantee is made about the number of bytes - /// read and the contents of `buf`. If the condition is handled - /// returns `None` (XXX see below). - /// - /// # XXX - /// - /// * Should raise_default error on eof? - /// * If the condition is handled it should still return the bytes read, - /// in which case there's no need to return Option - but then you *have* - /// to install a handler to detect eof. - /// - /// This doesn't take a `len` argument like the old `read`. - /// Will people often need to slice their vectors to call this - /// and will that be annoying? - /// Is it actually possible for 0 bytes to be read successfully? - fn read(&mut self, buf: &mut [u8]) -> Option<uint>; - - /// Return whether the Reader has reached the end of the stream. - /// - /// # Example - /// - /// let reader = File::open(&Path::new("foo.txt")) - /// while !reader.eof() { - /// println(reader.read_line()); - /// } - /// - /// # Failure - /// - /// Returns `true` on failure. - fn eof(&mut self) -> bool; - - // Convenient helper methods based on the above methods - - /// Reads a single byte. Returns `None` on EOF. - /// - /// # Failure - /// - /// Raises the same conditions as the `read` method. Returns - /// `None` if the condition is handled. - fn read_byte(&mut self) -> Option<u8> { - let mut buf = [0]; - match self.read(buf) { - Some(0) => { - debug!("read 0 bytes. trying again"); - self.read_byte() - } - Some(1) => Some(buf[0]), - Some(_) => unreachable!(), - None => None - } - } - - /// Reads `len` bytes and appends them to a vector. - /// - /// May push fewer than the requested number of bytes on error - /// or EOF. Returns true on success, false on EOF or error. - /// - /// # Failure - /// - /// Raises the same conditions as `read`. Additionally raises `io_error` - /// on EOF. If `io_error` is handled then `push_bytes` may push less - /// than the requested number of bytes. - fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) { - unsafe { - let start_len = buf.len(); - let mut total_read = 0; - - buf.reserve_additional(len); - vec::raw::set_len(buf, start_len + len); - - do (|| { - while total_read < len { - let len = buf.len(); - let slice = buf.mut_slice(start_len + total_read, len); - match self.read(slice) { - Some(nread) => { - total_read += nread; - } - None => { - io_error::cond.raise(standard_error(EndOfFile)); - break; - } - } - } - }).finally { - vec::raw::set_len(buf, start_len + total_read); - } - } - } - - /// Reads `len` bytes and gives you back a new vector of length `len` - /// - /// # Failure - /// - /// Raises the same conditions as `read`. Additionally raises `io_error` - /// on EOF. If `io_error` is handled then the returned vector may - /// contain less than the requested number of bytes. - fn read_bytes(&mut self, len: uint) -> ~[u8] { - let mut buf = vec::with_capacity(len); - self.push_bytes(&mut buf, len); - return buf; - } - - /// Reads all remaining bytes from the stream. - /// - /// # Failure - /// - /// Raises the same conditions as the `read` method. - fn read_to_end(&mut self) -> ~[u8] { - let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE); - let mut keep_reading = true; - do io_error::cond.trap(|e| { - if e.kind == EndOfFile { - keep_reading = false; - } else { - io_error::cond.raise(e) - } - }).inside { - while keep_reading { - self.push_bytes(&mut buf, DEFAULT_BUF_SIZE) - } - } - return buf; - } - - /// Create an iterator that reads a single byte on - /// each iteration, until EOF. - /// - /// # Failure - /// - /// Raises the same conditions as the `read` method, for - /// each call to its `.next()` method. - /// Ends the iteration if the condition is handled. - fn bytes(self) -> extensions::ByteIterator<Self> { - extensions::ByteIterator::new(self) - } - - // 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) -> u64 { - assert!(nbytes > 0 && nbytes <= 8); - - let mut val = 0u64; - let mut pos = 0; - let mut i = nbytes; - while i > 0 { - val += (self.read_u8() as u64) << pos; - pos += 8; - i -= 1; - } - 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) -> i64 { - extend_sign(self.read_le_uint_n(nbytes), 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) -> u64 { - assert!(nbytes > 0 && nbytes <= 8); - - let mut val = 0u64; - let mut i = nbytes; - while i > 0 { - i -= 1; - val += (self.read_u8() as u64) << i * 8; - } - 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) -> i64 { - extend_sign(self.read_be_uint_n(nbytes), nbytes) - } - - /// Reads a little-endian unsigned integer. - /// - /// The number of bytes returned is system-dependant. - fn read_le_uint(&mut self) -> uint { - self.read_le_uint_n(uint::bytes) as uint - } - - /// Reads a little-endian integer. - /// - /// The number of bytes returned is system-dependant. - fn read_le_int(&mut self) -> int { - self.read_le_int_n(int::bytes) as int - } - - /// Reads a big-endian unsigned integer. - /// - /// The number of bytes returned is system-dependant. - fn read_be_uint(&mut self) -> uint { - self.read_be_uint_n(uint::bytes) as uint - } - - /// Reads a big-endian integer. - /// - /// The number of bytes returned is system-dependant. - fn read_be_int(&mut self) -> int { - self.read_be_int_n(int::bytes) as int - } - - /// Reads a big-endian `u64`. - /// - /// `u64`s are 8 bytes long. - fn read_be_u64(&mut self) -> u64 { - self.read_be_uint_n(8) as u64 - } - - /// Reads a big-endian `u32`. - /// - /// `u32`s are 4 bytes long. - fn read_be_u32(&mut self) -> u32 { - self.read_be_uint_n(4) as u32 - } - - /// Reads a big-endian `u16`. - /// - /// `u16`s are 2 bytes long. - fn read_be_u16(&mut self) -> u16 { - self.read_be_uint_n(2) as u16 - } - - /// Reads a big-endian `i64`. - /// - /// `i64`s are 8 bytes long. - fn read_be_i64(&mut self) -> i64 { - self.read_be_int_n(8) as i64 - } - - /// Reads a big-endian `i32`. - /// - /// `i32`s are 4 bytes long. - fn read_be_i32(&mut self) -> i32 { - self.read_be_int_n(4) as i32 - } - - /// Reads a big-endian `i16`. - /// - /// `i16`s are 2 bytes long. - fn read_be_i16(&mut self) -> i16 { - self.read_be_int_n(2) as i16 - } - - /// Reads a big-endian `f64`. - /// - /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - fn read_be_f64(&mut self) -> f64 { - unsafe { - cast::transmute::<u64, f64>(self.read_be_u64()) - } - } - - /// Reads a big-endian `f32`. - /// - /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - fn read_be_f32(&mut self) -> f32 { - unsafe { - cast::transmute::<u32, f32>(self.read_be_u32()) - } - } - - /// Reads a little-endian `u64`. - /// - /// `u64`s are 8 bytes long. - fn read_le_u64(&mut self) -> u64 { - self.read_le_uint_n(8) as u64 - } - - /// Reads a little-endian `u32`. - /// - /// `u32`s are 4 bytes long. - fn read_le_u32(&mut self) -> u32 { - self.read_le_uint_n(4) as u32 - } - - /// Reads a little-endian `u16`. - /// - /// `u16`s are 2 bytes long. - fn read_le_u16(&mut self) -> u16 { - self.read_le_uint_n(2) as u16 - } - - /// Reads a little-endian `i64`. - /// - /// `i64`s are 8 bytes long. - fn read_le_i64(&mut self) -> i64 { - self.read_le_int_n(8) as i64 - } - - /// Reads a little-endian `i32`. - /// - /// `i32`s are 4 bytes long. - fn read_le_i32(&mut self) -> i32 { - self.read_le_int_n(4) as i32 - } - - /// Reads a little-endian `i16`. - /// - /// `i16`s are 2 bytes long. - fn read_le_i16(&mut self) -> i16 { - self.read_le_int_n(2) as i16 - } - - /// Reads a little-endian `f64`. - /// - /// `f64`s are 8 byte, IEEE754 double-precision floating point numbers. - fn read_le_f64(&mut self) -> f64 { - unsafe { - cast::transmute::<u64, f64>(self.read_le_u64()) - } - } - - /// Reads a little-endian `f32`. - /// - /// `f32`s are 4 byte, IEEE754 single-precision floating point numbers. - fn read_le_f32(&mut self) -> f32 { - unsafe { - cast::transmute::<u32, f32>(self.read_le_u32()) - } - } - - /// Read a u8. - /// - /// `u8`s are 1 byte. - fn read_u8(&mut self) -> u8 { - match self.read_byte() { - Some(b) => b as u8, - None => 0 - } - } - - /// Read an i8. - /// - /// `i8`s are 1 byte. - fn read_i8(&mut self) -> i8 { - match self.read_byte() { - Some(b) => b as i8, - None => 0 - } - } - -} - -impl Reader for ~Reader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) } - fn eof(&mut self) -> bool { self.eof() } -} - -impl<'self> Reader for &'self mut Reader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.read(buf) } - fn eof(&mut self) -> bool { self.eof() } -} - -fn extend_sign(val: u64, nbytes: uint) -> i64 { - let shift = (8 - nbytes) * 8; - (val << shift) as i64 >> shift -} - -pub trait Writer { - /// Write the given buffer - /// - /// # Failure - /// - /// Raises the `io_error` condition on error - fn write(&mut self, buf: &[u8]); - - /// Flush this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// This is by default a no-op and implementors of the `Writer` trait should - /// decide whether their stream needs to be buffered or not. - fn flush(&mut self) {} - - /// Write the result of passing n through `int::to_str_bytes`. - fn write_int(&mut self, n: int) { - int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) - } - - /// Write the result of passing n through `uint::to_str_bytes`. - fn write_uint(&mut self, n: uint) { - uint::to_str_bytes(n, 10u, |bytes| self.write(bytes)) - } - - /// Write a little-endian uint (number of bytes depends on system). - fn write_le_uint(&mut self, n: uint) { - extensions::u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) - } - - /// Write a little-endian int (number of bytes depends on system). - fn write_le_int(&mut self, n: int) { - extensions::u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v)) - } - - /// Write a big-endian uint (number of bytes depends on system). - fn write_be_uint(&mut self, n: uint) { - extensions::u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v)) - } - - /// Write a big-endian int (number of bytes depends on system). - fn write_be_int(&mut self, n: int) { - extensions::u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v)) - } - - /// Write a big-endian u64 (8 bytes). - fn write_be_u64(&mut self, n: u64) { - extensions::u64_to_be_bytes(n, 8u, |v| self.write(v)) - } - - /// Write a big-endian u32 (4 bytes). - fn write_be_u32(&mut self, n: u32) { - extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a big-endian u16 (2 bytes). - fn write_be_u16(&mut self, n: u16) { - extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a big-endian i64 (8 bytes). - fn write_be_i64(&mut self, n: i64) { - extensions::u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) - } - - /// Write a big-endian i32 (4 bytes). - fn write_be_i32(&mut self, n: i32) { - extensions::u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a big-endian i16 (2 bytes). - fn write_be_i16(&mut self, n: i16) { - extensions::u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a big-endian IEEE754 double-precision floating-point (8 bytes). - fn write_be_f64(&mut self, f: f64) { - unsafe { - self.write_be_u64(cast::transmute(f)) - } - } - - /// Write a big-endian IEEE754 single-precision floating-point (4 bytes). - fn write_be_f32(&mut self, f: f32) { - unsafe { - self.write_be_u32(cast::transmute(f)) - } - } - - /// Write a little-endian u64 (8 bytes). - fn write_le_u64(&mut self, n: u64) { - extensions::u64_to_le_bytes(n, 8u, |v| self.write(v)) - } - - /// Write a little-endian u32 (4 bytes). - fn write_le_u32(&mut self, n: u32) { - extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a little-endian u16 (2 bytes). - fn write_le_u16(&mut self, n: u16) { - extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a little-endian i64 (8 bytes). - fn write_le_i64(&mut self, n: i64) { - extensions::u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) - } - - /// Write a little-endian i32 (4 bytes). - fn write_le_i32(&mut self, n: i32) { - extensions::u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) - } - - /// Write a little-endian i16 (2 bytes). - fn write_le_i16(&mut self, n: i16) { - extensions::u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) - } - - /// Write a little-endian IEEE754 double-precision floating-point - /// (8 bytes). - fn write_le_f64(&mut self, f: f64) { - unsafe { - self.write_le_u64(cast::transmute(f)) - } - } - - /// Write a little-endian IEEE754 single-precision floating-point - /// (4 bytes). - fn write_le_f32(&mut self, f: f32) { - unsafe { - self.write_le_u32(cast::transmute(f)) - } - } - - /// Write a u8 (1 byte). - fn write_u8(&mut self, n: u8) { - self.write([n]) - } - - /// Write a i8 (1 byte). - fn write_i8(&mut self, n: i8) { - self.write([n as u8]) - } -} - -impl Writer for ~Writer { - fn write(&mut self, buf: &[u8]) { self.write(buf) } - fn flush(&mut self) { self.flush() } -} - -impl<'self> Writer for &'self mut Writer { - fn write(&mut self, buf: &[u8]) { self.write(buf) } - fn flush(&mut self) { self.flush() } -} - -pub trait Stream: Reader + Writer { } - -impl<T: Reader + Writer> Stream for T {} - -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, -} - -/// # XXX -/// * Are `u64` and `i64` the right choices? -pub trait Seek { - /// Return position of file cursor in the stream - fn tell(&self) -> u64; - - /// Seek to an offset in a stream - /// - /// A successful seek clears the EOF indicator. - /// - /// # XXX - /// - /// * What is the behavior when seeking past the end of a stream? - fn seek(&mut self, pos: i64, style: SeekStyle); -} - -/// 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 queueing incoming connections - /// - /// # Failure - /// - /// Raises `io_error` condition. If the condition is handled, - /// then `listen` returns `None`. - fn listen(self) -> Option<A>; -} - -/// An acceptor is a value that presents incoming connections -pub trait Acceptor<T> { - /// Wait for and accept an incoming connection - /// - /// # Failure - /// Raise `io_error` condition. If the condition is handled, - /// then `accept` returns `None`. - fn accept(&mut self) -> Option<T>; - - /// Create an iterator over incoming connection attempts - fn incoming<'r>(&'r mut self) -> IncomingIterator<'r, Self> { - IncomingIterator { 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 another Option representing whether the connection attempt was succesful. -/// A successful connection will be wrapped in Some. -/// A failed connection is represented as a None and raises a condition. -struct IncomingIterator<'self, A> { - priv inc: &'self mut A, -} - -impl<'self, T, A: Acceptor<T>> Iterator<Option<T>> for IncomingIterator<'self, A> { - fn next(&mut self) -> Option<Option<T>> { - Some(self.inc.accept()) - } -} - -/// Common trait for decorator types. -/// -/// Provides accessors to get the inner, 'decorated' values. The I/O library -/// uses decorators to add functionality like compression and encryption to I/O -/// streams. -/// -/// # XXX -/// -/// Is this worth having a trait for? May be overkill -pub trait Decorator<T> { - /// Destroy the decorator and extract the decorated value - /// - /// # XXX - /// - /// Because this takes `self' one could never 'undecorate' a Reader/Writer - /// that has been boxed. Is that ok? This feature is mostly useful for - /// extracting the buffer from MemWriter - fn inner(self) -> T; - - /// Take an immutable reference to the decorated value - fn inner_ref<'a>(&'a self) -> &'a T; - - /// Take a mutable reference to the decorated value - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T; -} - -pub fn standard_error(kind: IoErrorKind) -> IoError { - match kind { - PreviousIoError => { - IoError { - kind: PreviousIoError, - desc: "Failing due to a previous I/O error", - detail: None - } - } - EndOfFile => { - IoError { - kind: EndOfFile, - desc: "End of file", - detail: None - } - } - IoUnavailable => { - IoError { - kind: IoUnavailable, - desc: "I/O is unavailable", - detail: None - } - } - _ => fail!() - } -} - -pub fn placeholder_error() -> IoError { - IoError { - kind: OtherIoError, - desc: "Placeholder error. You shouldn't be seeing this", - 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. -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 raise an `io_error` condition if written to. -pub enum FileAccess { - Read, - Write, - ReadWrite, -} - -/// Different kinds of files which can be identified by a call to stat -#[deriving(Eq)] -pub enum FileType { - TypeFile, - TypeDirectory, - TypeNamedPipe, - TypeBlockSpecial, - TypeSymlink, - TypeUnknown, -} - -pub struct FileStat { - /// The path that this stat structure is describing - path: Path, - /// The size of the file, in bytes - size: u64, - /// The kind of file this path points to (directory, file, pipe, etc.) - kind: FileType, - /// The file permissions currently on the file - 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 - created: u64, - /// The time that this file was last modified, in platform-dependent - /// milliseconds - modified: u64, - /// The time that this file was last accessed, in platform-dependent - /// milliseconds - 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] - 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] -pub struct UnstableFileStat { - device: u64, - inode: u64, - rdev: u64, - nlink: u64, - uid: u64, - gid: u64, - blksize: u64, - blocks: u64, - flags: u64, - gen: u64, -} - -/// A set of permissions for a file or directory is represented by a set of -/// flags which are or'd together. -pub type FilePermission = u32; - -// Each permission bit -pub static UserRead: FilePermission = 0x100; -pub static UserWrite: FilePermission = 0x080; -pub static UserExecute: FilePermission = 0x040; -pub static GroupRead: FilePermission = 0x020; -pub static GroupWrite: FilePermission = 0x010; -pub static GroupExecute: FilePermission = 0x008; -pub static OtherRead: FilePermission = 0x004; -pub static OtherWrite: FilePermission = 0x002; -pub static OtherExecute: FilePermission = 0x001; - -// Common combinations of these bits -pub static UserRWX: FilePermission = UserRead | UserWrite | UserExecute; -pub static GroupRWX: FilePermission = GroupRead | GroupWrite | GroupExecute; -pub static OtherRWX: FilePermission = OtherRead | OtherWrite | OtherExecute; - -/// A set of permissions for user owned files, this is equivalent to 0644 on -/// unix-like systems. -pub static UserFile: FilePermission = UserRead | UserWrite | GroupRead | OtherRead; -/// A set of permissions for user owned directories, this is equivalent to 0755 -/// on unix-like systems. -pub static UserDir: FilePermission = UserRWX | GroupRead | GroupExecute | - OtherRead | OtherExecute; -/// A set of permissions for user owned executables, this is equivalent to 0755 -/// on unix-like systems. -pub static UserExec: FilePermission = UserDir; - -/// A mask for all possible permission bits -pub static AllPermissions: FilePermission = 0x1ff; diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs deleted file mode 100644 index 69d1159bf91..00000000000 --- a/src/libstd/rt/io/native/file.rs +++ /dev/null @@ -1,761 +0,0 @@ -// 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. - -//! Blocking posix-based file I/O - -#[allow(non_camel_case_types)]; - -use libc; -use os; -use prelude::*; -use super::super::*; - -#[cfg(windows)] -fn get_err(errno: i32) -> (IoErrorKind, &'static str) { - match errno { - libc::EOF => (EndOfFile, "end of file"), - _ => (OtherIoError, "unknown error"), - } -} - -#[cfg(not(windows))] -fn get_err(errno: i32) -> (IoErrorKind, &'static str) { - // XXX: this should probably be a bit more descriptive... - match errno { - libc::EOF => (EndOfFile, "end of file"), - - // These two constants can have the same value on some systems, but - // different values on others, so we can't use a match clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => - (ResourceUnavailable, "resource temporarily unavailable"), - - _ => (OtherIoError, "unknown error"), - } -} - -fn raise_error() { - let (kind, desc) = get_err(os::errno() as i32); - io_error::cond.raise(IoError { - kind: kind, - desc: desc, - detail: Some(os::last_os_error()) - }); -} - -fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 { - #[cfg(windows)] static eintr: int = 0; // doesn't matter - #[cfg(not(windows))] static eintr: int = libc::EINTR as int; - - let (data, origamt) = do data.as_imm_buf |data, amt| { (data, amt) }; - let mut data = data; - let mut amt = origamt; - while amt > 0 { - let mut ret; - loop { - ret = f(data, amt); - if cfg!(not(windows)) { break } // windows has no eintr - // if we get an eintr, then try again - if ret != -1 || os::errno() as int != eintr { break } - } - if ret == 0 { - break - } else if ret != -1 { - amt -= ret as uint; - data = unsafe { data.offset(ret as int) }; - } else { - return ret; - } - } - return (origamt - amt) as i64; -} - -pub type fd_t = libc::c_int; - -pub struct FileDesc { - priv fd: fd_t, - priv close_on_drop: bool, -} - -impl FileDesc { - /// Create a `FileDesc` from an open C file descriptor. - /// - /// The `FileDesc` will take ownership of the specified file descriptor and - /// close it upon destruction if the `close_on_drop` flag is true, otherwise - /// it will not close the file descriptor when this `FileDesc` is dropped. - /// - /// Note that all I/O operations done on this object will be *blocking*, but - /// they do not require the runtime to be active. - pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { - FileDesc { fd: fd, close_on_drop: close_on_drop } - } -} - -impl Reader for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - #[cfg(windows)] type rlen = libc::c_uint; - #[cfg(not(windows))] type rlen = libc::size_t; - let ret = do keep_going(buf) |buf, len| { - unsafe { - libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64 - } - }; - if ret == 0 { - None - } else if ret < 0 { - raise_error(); - None - } else { - Some(ret as uint) - } - } - - fn eof(&mut self) -> bool { false } -} - -impl Writer for FileDesc { - fn write(&mut self, buf: &[u8]) { - #[cfg(windows)] type wlen = libc::c_uint; - #[cfg(not(windows))] type wlen = libc::size_t; - let ret = do keep_going(buf) |buf, len| { - unsafe { - libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64 - } - }; - if ret < 0 { - raise_error(); - } - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - if self.close_on_drop { - unsafe { libc::close(self.fd); } - } - } -} - -pub struct CFile { - priv file: *libc::FILE -} - -impl CFile { - /// Create a `CFile` from an open `FILE` pointer. - /// - /// The `CFile` takes ownership of the `FILE` pointer and will close it upon - /// destruction. - pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } } -} - -impl Reader for CFile { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - let ret = do keep_going(buf) |buf, len| { - unsafe { - libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }; - if ret == 0 { - None - } else if ret < 0 { - raise_error(); - None - } else { - Some(ret as uint) - } - } - - fn eof(&mut self) -> bool { - unsafe { libc::feof(self.file) != 0 } - } -} - -impl Writer for CFile { - fn write(&mut self, buf: &[u8]) { - let ret = do keep_going(buf) |buf, len| { - unsafe { - libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }; - if ret < 0 { - raise_error(); - } - } - - fn flush(&mut self) { - if unsafe { libc::fflush(self.file) } < 0 { - raise_error(); - } - } -} - -impl Seek for CFile { - fn tell(&self) -> u64 { - let ret = unsafe { libc::ftell(self.file) }; - if ret < 0 { - raise_error(); - } - return ret as u64; - } - - fn seek(&mut self, pos: i64, style: SeekStyle) { - let whence = match style { - SeekSet => libc::SEEK_SET, - SeekEnd => libc::SEEK_END, - SeekCur => libc::SEEK_CUR, - }; - if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 { - raise_error(); - } - } -} - -impl Drop for CFile { - fn drop(&mut self) { - unsafe { libc::fclose(self.file); } - } -} - -#[cfg(test)] -mod tests { - use libc; - use os; - use prelude::*; - use rt::io::{io_error, SeekSet}; - use super::*; - - #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer - fn test_file_desc() { - // Run this test with some pipes so we don't have to mess around with - // opening or closing files. - unsafe { - let os::Pipe { input, out } = os::pipe(); - let mut reader = FileDesc::new(input, true); - let mut writer = FileDesc::new(out, true); - - writer.write(bytes!("test")); - let mut buf = [0u8, ..4]; - match reader.read(buf) { - Some(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => fail!("invalid read: {:?}", r) - } - - let mut raised = false; - do io_error::cond.trap(|_| { raised = true; }).inside { - writer.read(buf); - } - assert!(raised); - - raised = false; - do io_error::cond.trap(|_| { raised = true; }).inside { - reader.write(buf); - } - assert!(raised); - } - } - - #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile - fn test_cfile() { - unsafe { - let f = libc::tmpfile(); - assert!(!f.is_null()); - let mut file = CFile::new(f); - - file.write(bytes!("test")); - let mut buf = [0u8, ..4]; - file.seek(0, SeekSet); - match file.read(buf) { - Some(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => fail!("invalid read: {:?}", r) - } - } - } -} - -// n.b. these functions were all part of the old `std::os` module. There's lots -// of fun little nuances that were taken care of by these functions, but -// they are all thread-blocking versions that are no longer desired (we now -// use a non-blocking event loop implementation backed by libuv). -// -// In theory we will have a thread-blocking version of the event loop (if -// desired), so these functions may just need to get adapted to work in -// those situtations. For now, I'm leaving the code around so it doesn't -// get bitrotted instantaneously. -mod old_os { - use prelude::*; - use libc::{size_t, c_void, c_int}; - use libc; - use vec; - - #[cfg(not(windows))] use c_str::CString; - #[cfg(not(windows))] use libc::fclose; - #[cfg(test)] #[cfg(windows)] use os; - #[cfg(test)] use rand; - #[cfg(windows)] use str; - #[cfg(windows)] use ptr; - - // On Windows, wide character version of function must be used to support - // unicode, so functions should be split into at least two versions, - // which are for Windows and for non-Windows, if necessary. - // See https://github.com/mozilla/rust/issues/9822 for more information. - - mod rustrt { - use libc::{c_char, c_int}; - use libc; - - extern { - pub fn rust_path_is_dir(path: *libc::c_char) -> c_int; - pub fn rust_path_exists(path: *libc::c_char) -> c_int; - } - - // Uses _wstat instead of stat. - #[cfg(windows)] - extern { - pub fn rust_path_is_dir_u16(path: *u16) -> c_int; - pub fn rust_path_exists_u16(path: *u16) -> c_int; - } - } - - /// Recursively walk a directory structure - pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { - let r = list_dir(p); - r.iter().advance(|q| { - let path = &p.join(q); - f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p))) - }) - } - - #[cfg(unix)] - /// Indicates whether a path represents a directory - pub fn path_is_dir(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - rustrt::rust_path_is_dir(buf) != 0 as c_int - } - } - } - - - #[cfg(windows)] - pub fn path_is_dir(p: &Path) -> bool { - unsafe { - do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { - rustrt::rust_path_is_dir_u16(buf) != 0 as c_int - } - } - } - - #[cfg(unix)] - /// Indicates whether a path exists - pub fn path_exists(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - rustrt::rust_path_exists(buf) != 0 as c_int - } - } - } - - #[cfg(windows)] - pub fn path_exists(p: &Path) -> bool { - unsafe { - do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { - rustrt::rust_path_exists_u16(buf) != 0 as c_int - } - } - } - - /// Creates a directory at the specified path - pub fn make_dir(p: &Path, mode: c_int) -> bool { - return mkdir(p, mode); - - #[cfg(windows)] - fn mkdir(p: &Path, _mode: c_int) -> bool { - unsafe { - use os::win32::as_utf16_p; - // FIXME: turn mode into something useful? #2623 - do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::CreateDirectoryW(buf, ptr::mut_null()) - != (0 as libc::BOOL) - } - } - } - - #[cfg(unix)] - fn mkdir(p: &Path, mode: c_int) -> bool { - do p.with_c_str |buf| { - unsafe { - libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int) - } - } - } - } - - /// Creates a directory with a given mode. - /// Returns true iff creation - /// succeeded. Also creates all intermediate subdirectories - /// if they don't already exist, giving all of them the same mode. - - // tjc: if directory exists but with different permissions, - // should we return false? - pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { - if path_is_dir(p) { - return true; - } - if p.filename().is_some() { - let mut p_ = p.clone(); - p_.pop(); - if !mkdir_recursive(&p_, mode) { - return false; - } - } - return make_dir(p, mode); - } - - /// Lists the contents of a directory - /// - /// Each resulting Path is a relative path with no directory component. - pub fn list_dir(p: &Path) -> ~[Path] { - unsafe { - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - #[cfg(target_os = "freebsd")] - #[cfg(target_os = "macos")] - unsafe fn get_list(p: &Path) -> ~[Path] { - use libc::{dirent_t}; - use libc::{opendir, readdir, closedir}; - extern { - fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; - } - let mut paths = ~[]; - debug!("os::list_dir -- BEFORE OPENDIR"); - - let dir_ptr = do p.with_c_str |buf| { - opendir(buf) - }; - - if (dir_ptr as uint != 0) { - debug!("os::list_dir -- opendir() SUCCESS"); - let mut entry_ptr = readdir(dir_ptr); - while (entry_ptr as uint != 0) { - let cstr = CString::new(rust_list_dir_val(entry_ptr), false); - paths.push(Path::new(cstr)); - entry_ptr = readdir(dir_ptr); - } - closedir(dir_ptr); - } - else { - debug!("os::list_dir -- opendir() FAILURE"); - } - debug!("os::list_dir -- AFTER -- \\#: {}", paths.len()); - paths - } - #[cfg(windows)] - unsafe fn get_list(p: &Path) -> ~[Path] { - use libc::consts::os::extra::INVALID_HANDLE_VALUE; - use libc::{wcslen, free}; - use libc::funcs::extra::kernel32::{ - FindFirstFileW, - FindNextFileW, - FindClose, - }; - use libc::types::os::arch::extra::HANDLE; - use os::win32::{ - as_utf16_p - }; - use rt::global_heap::malloc_raw; - - #[nolink] - extern { - fn rust_list_dir_wfd_size() -> libc::size_t; - fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; - } - let star = p.join("*"); - do as_utf16_p(star.as_str().unwrap()) |path_ptr| { - let mut paths = ~[]; - let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); - let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); - if find_handle as libc::c_int != INVALID_HANDLE_VALUE { - let mut more_files = 1 as libc::c_int; - while more_files != 0 { - let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); - if fp_buf as uint == 0 { - fail!("os::list_dir() failure: got null ptr from wfd"); - } - else { - let fp_vec = vec::from_buf( - fp_buf, wcslen(fp_buf) as uint); - let fp_str = str::from_utf16(fp_vec); - paths.push(Path::new(fp_str)); - } - more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); - } - FindClose(find_handle); - free(wfd_ptr) - } - paths - } - } - do get_list(p).move_iter().filter |path| { - path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") - }.collect() - } - } - - /// Removes a directory at the specified path, after removing - /// all its contents. Use carefully! - pub fn remove_dir_recursive(p: &Path) -> bool { - let mut error_happened = false; - do walk_dir(p) |inner| { - if !error_happened { - if path_is_dir(inner) { - if !remove_dir_recursive(inner) { - error_happened = true; - } - } - else { - if !remove_file(inner) { - error_happened = true; - } - } - } - true - }; - // Directory should now be empty - !error_happened && remove_dir(p) - } - - /// Removes a directory at the specified path - pub fn remove_dir(p: &Path) -> bool { - return rmdir(p); - - #[cfg(windows)] - fn rmdir(p: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::RemoveDirectoryW(buf) != (0 as libc::BOOL) - }; - } - } - - #[cfg(unix)] - fn rmdir(p: &Path) -> bool { - do p.with_c_str |buf| { - unsafe { - libc::rmdir(buf) == (0 as c_int) - } - } - } - } - - /// Deletes an existing file - pub fn remove_file(p: &Path) -> bool { - return unlink(p); - - #[cfg(windows)] - fn unlink(p: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::DeleteFileW(buf) != (0 as libc::BOOL) - }; - } - } - - #[cfg(unix)] - fn unlink(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - libc::unlink(buf) == (0 as c_int) - } - } - } - } - - /// Renames an existing file or directory - pub fn rename_file(old: &Path, new: &Path) -> bool { - unsafe { - do old.with_c_str |old_buf| { - do new.with_c_str |new_buf| { - libc::rename(old_buf, new_buf) == (0 as c_int) - } - } - } - } - - /// Copies a file from one location to another - pub fn copy_file(from: &Path, to: &Path) -> bool { - return do_copy_file(from, to); - - #[cfg(windows)] - fn do_copy_file(from: &Path, to: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(from.as_str().unwrap()) |fromp| { - do as_utf16_p(to.as_str().unwrap()) |top| { - libc::CopyFileW(fromp, top, (0 as libc::BOOL)) != - (0 as libc::BOOL) - } - } - } - } - - #[cfg(unix)] - fn do_copy_file(from: &Path, to: &Path) -> bool { - unsafe { - let istream = do from.with_c_str |fromp| { - do "rb".with_c_str |modebuf| { - libc::fopen(fromp, modebuf) - } - }; - if istream as uint == 0u { - return false; - } - // Preserve permissions - let from_mode = from.stat().perm; - - let ostream = do to.with_c_str |top| { - do "w+b".with_c_str |modebuf| { - libc::fopen(top, modebuf) - } - }; - if ostream as uint == 0u { - fclose(istream); - return false; - } - let bufsize = 8192u; - let mut buf = vec::with_capacity::<u8>(bufsize); - let mut done = false; - let mut ok = true; - while !done { - do buf.as_mut_buf |b, _sz| { - let nread = libc::fread(b as *mut c_void, 1u as size_t, - bufsize as size_t, - istream); - if nread > 0 as size_t { - if libc::fwrite(b as *c_void, 1u as size_t, nread, - ostream) != nread { - ok = false; - done = true; - } - } else { - done = true; - } - } - } - fclose(istream); - fclose(ostream); - - // Give the new file the old file's permissions - if do to.with_c_str |to_buf| { - libc::chmod(to_buf, from_mode as libc::mode_t) - } != 0 { - return false; // should be a condition... - } - return ok; - } - } - } - - #[test] - fn tmpdir() { - let p = os::tmpdir(); - let s = p.as_str(); - assert!(s.is_some() && s.unwrap() != "."); - } - - // Issue #712 - #[test] - fn test_list_dir_no_invalid_memory_access() { - list_dir(&Path::new(".")); - } - - #[test] - fn test_list_dir() { - let dirs = list_dir(&Path::new(".")); - // Just assuming that we've got some contents in the current directory - assert!(dirs.len() > 0u); - - for dir in dirs.iter() { - debug!("{:?}", (*dir).clone()); - } - } - - #[test] - #[cfg(not(windows))] - fn test_list_dir_root() { - let dirs = list_dir(&Path::new("/")); - assert!(dirs.len() > 1); - } - #[test] - #[cfg(windows)] - fn test_list_dir_root() { - let dirs = list_dir(&Path::new("C:\\")); - assert!(dirs.len() > 1); - } - - #[test] - fn test_path_is_dir() { - use rt::io::fs::{mkdir_recursive}; - use rt::io::{File, UserRWX}; - - assert!((path_is_dir(&Path::new(".")))); - assert!((!path_is_dir(&Path::new("test/stdtest/fs.rs")))); - - let mut dirpath = os::tmpdir(); - dirpath.push(format!("rust-test-{}/test-\uac00\u4e00\u30fc\u4f60\u597d", - rand::random::<u32>())); // 가一ー你好 - debug!("path_is_dir dirpath: {}", dirpath.display()); - - mkdir_recursive(&dirpath, UserRWX); - - assert!((path_is_dir(&dirpath))); - - let mut filepath = dirpath; - filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); - debug!("path_is_dir filepath: {}", filepath.display()); - - File::create(&filepath); // ignore return; touch only - assert!((!path_is_dir(&filepath))); - - assert!((!path_is_dir(&Path::new( - "test/unicode-bogus-dir-\uac00\u4e00\u30fc\u4f60\u597d")))); - } - - #[test] - fn test_path_exists() { - use rt::io::fs::mkdir_recursive; - use rt::io::UserRWX; - - assert!((path_exists(&Path::new(".")))); - assert!((!path_exists(&Path::new( - "test/nonexistent-bogus-path")))); - - let mut dirpath = os::tmpdir(); - dirpath.push(format!("rust-test-{}/test-\uac01\u4e01\u30fc\u518d\u89c1", - rand::random::<u32>())); // 각丁ー再见 - - mkdir_recursive(&dirpath, UserRWX); - assert!((path_exists(&dirpath))); - assert!((!path_exists(&Path::new( - "test/unicode-bogus-path-\uac01\u4e01\u30fc\u518d\u89c1")))); - } -} diff --git a/src/libstd/rt/io/native/process.rs b/src/libstd/rt/io/native/process.rs deleted file mode 100644 index 9bf0ed63e8c..00000000000 --- a/src/libstd/rt/io/native/process.rs +++ /dev/null @@ -1,734 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use cast; -use libc::{pid_t, c_void, c_int}; -use libc; -use os; -use prelude::*; -use ptr; -use rt::io; -use super::file; - -/** - * A value representing a child process. - * - * The lifetime of this value is linked to the lifetime of the actual - * process - the Process destructor calls self.finish() which waits - * for the process to terminate. - */ -pub struct Process { - /// The unique id of the process (this should never be negative). - priv pid: pid_t, - - /// A handle to the process - on unix this will always be NULL, but on - /// windows it will be a HANDLE to the process, which will prevent the - /// pid being re-used until the handle is closed. - priv handle: *(), - - /// Currently known stdin of the child, if any - priv input: Option<file::FileDesc>, - /// Currently known stdout of the child, if any - priv output: Option<file::FileDesc>, - /// Currently known stderr of the child, if any - priv error: Option<file::FileDesc>, - - /// None until finish() is called. - priv exit_code: Option<int>, -} - -impl Process { - /// Creates a new process using native process-spawning abilities provided - /// by the OS. Operations on this process will be blocking instead of using - /// the runtime for sleeping just this current task. - /// - /// # Arguments - /// - /// * prog - the program to run - /// * args - the arguments to pass to the program, not including the program - /// itself - /// * env - an optional envrionment to specify for the child process. If - /// this value is `None`, then the child will inherit the parent's - /// environment - /// * cwd - an optionally specified current working directory of the child, - /// defaulting to the parent's current working directory - /// * stdin, stdout, stderr - These optionally specified file descriptors - /// dictate where the stdin/out/err of the child process will go. If - /// these are `None`, then this module will bind the input/output to an - /// os pipe instead. This process takes ownership of these file - /// descriptors, closing them upon destruction of the process. - pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>, - cwd: Option<&Path>, - stdin: Option<file::fd_t>, - stdout: Option<file::fd_t>, - stderr: Option<file::fd_t>) -> Process { - let (in_pipe, in_fd) = match stdin { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.input) - }, - Some(fd) => (None, fd) - }; - let (out_pipe, out_fd) = match stdout { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.out) - }, - Some(fd) => (None, fd) - }; - let (err_pipe, err_fd) = match stderr { - None => { - let pipe = os::pipe(); - (Some(pipe), pipe.out) - }, - Some(fd) => (None, fd) - }; - - let res = spawn_process_os(prog, args, env, cwd, - in_fd, out_fd, err_fd); - - unsafe { - for pipe in in_pipe.iter() { libc::close(pipe.input); } - for pipe in out_pipe.iter() { libc::close(pipe.out); } - for pipe in err_pipe.iter() { libc::close(pipe.out); } - } - - Process { - pid: res.pid, - handle: res.handle, - input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out, true)), - output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)), - error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)), - exit_code: None, - } - } - - /// Returns the unique id of the process - pub fn id(&self) -> pid_t { self.pid } - - /** - * Returns an io::Writer that can be used to write to this Process's stdin. - * - * Fails if there is no stdinavailable (it's already been removed by - * take_input) - */ - pub fn input<'a>(&'a mut self) -> &'a mut io::Writer { - match self.input { - Some(ref mut fd) => fd as &mut io::Writer, - None => fail!("This process has no stdin") - } - } - - /** - * Returns an io::Reader that can be used to read from this Process's - * stdout. - * - * Fails if there is no stdin available (it's already been removed by - * take_output) - */ - pub fn output<'a>(&'a mut self) -> &'a mut io::Reader { - match self.input { - Some(ref mut fd) => fd as &mut io::Reader, - None => fail!("This process has no stdout") - } - } - - /** - * Returns an io::Reader that can be used to read from this Process's - * stderr. - * - * Fails if there is no stdin available (it's already been removed by - * take_error) - */ - pub fn error<'a>(&'a mut self) -> &'a mut io::Reader { - match self.error { - Some(ref mut fd) => fd as &mut io::Reader, - None => fail!("This process has no stderr") - } - } - - /** - * Takes the stdin of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_input(&mut self) -> Option<~io::Writer> { - self.input.take().map(|fd| ~fd as ~io::Writer) - } - - /** - * Takes the stdout of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_output(&mut self) -> Option<~io::Reader> { - self.output.take().map(|fd| ~fd as ~io::Reader) - } - - /** - * Takes the stderr of this process, transferring ownership to the caller. - * Note that when the return value is destroyed, the handle will be closed - * for the child process. - */ - pub fn take_error(&mut self) -> Option<~io::Reader> { - self.error.take().map(|fd| ~fd as ~io::Reader) - } - - pub fn wait(&mut self) -> int { - for &code in self.exit_code.iter() { - return code; - } - let code = waitpid(self.pid); - self.exit_code = Some(code); - return code; - } - - pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> { - // if the process has finished, and therefore had waitpid called, - // and we kill it, then on unix we might ending up killing a - // newer process that happens to have the same (re-used) id - match self.exit_code { - Some(*) => return Err(io::IoError { - kind: io::OtherIoError, - desc: "can't kill an exited process", - detail: None, - }), - None => {} - } - return unsafe { killpid(self.pid, signum) }; - - #[cfg(windows)] - unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { - match signal { - io::process::PleaseExitSignal | - io::process::MustDieSignal => { - libc::funcs::extra::kernel32::TerminateProcess( - cast::transmute(pid), 1); - Ok(()) - } - _ => Err(io::IoError { - kind: io::OtherIoError, - desc: "unsupported signal on windows", - detail: None, - }) - } - } - - #[cfg(not(windows))] - unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { - libc::funcs::posix88::signal::kill(pid, signal as c_int); - Ok(()) - } - } -} - -impl Drop for Process { - fn drop(&mut self) { - // close all these handles - self.take_input(); - self.take_output(); - self.take_error(); - self.wait(); - free_handle(self.handle); - } -} - -struct SpawnProcessResult { - pid: pid_t, - handle: *(), -} - -#[cfg(windows)] -fn spawn_process_os(prog: &str, args: &[~str], - env: Option<~[(~str, ~str)]>, - dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { - use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; - use libc::consts::os::extra::{ - TRUE, FALSE, - STARTF_USESTDHANDLES, - INVALID_HANDLE_VALUE, - DUPLICATE_SAME_ACCESS - }; - use libc::funcs::extra::kernel32::{ - GetCurrentProcess, - DuplicateHandle, - CloseHandle, - CreateProcessA - }; - use libc::funcs::extra::msvcrt::get_osfhandle; - - use mem; - - unsafe { - - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::<STARTUPINFO>() as DWORD; - si.dwFlags = STARTF_USESTDHANDLES; - - let cur_proc = GetCurrentProcess(); - - let orig_std_in = get_osfhandle(in_fd) as HANDLE; - if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let orig_std_out = get_osfhandle(out_fd) as HANDLE; - if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let orig_std_err = get_osfhandle(err_fd) as HANDLE; - if orig_std_err == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let cmd = make_command_line(prog, args); - let mut pi = zeroed_process_information(); - let mut create_err = None; - - do with_envp(env) |envp| { - do with_dirp(dir) |dirp| { - do cmd.with_c_str |cmdp| { - let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), - ptr::mut_null(), ptr::mut_null(), TRUE, - 0, envp, dirp, &mut si, &mut pi); - if created == FALSE { - create_err = Some(os::last_os_error()); - } - } - } - } - - CloseHandle(si.hStdInput); - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdError); - - for msg in create_err.iter() { - fail!("failure in CreateProcess: {}", *msg); - } - - // We close the thread handle because we don't care about keeping the - // thread id valid, and we aren't keeping the thread handle around to be - // able to close it later. We don't close the process handle however - // because we want the process id to stay valid at least until the - // calling code closes the process handle. - CloseHandle(pi.hThread); - - SpawnProcessResult { - pid: pi.dwProcessId as pid_t, - handle: pi.hProcess as *() - } - } -} - -#[cfg(windows)] -fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { - libc::types::os::arch::extra::STARTUPINFO { - cb: 0, - lpReserved: ptr::mut_null(), - lpDesktop: ptr::mut_null(), - lpTitle: ptr::mut_null(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountCharts: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::mut_null(), - hStdInput: ptr::mut_null(), - hStdOutput: ptr::mut_null(), - hStdError: ptr::mut_null() - } -} - -#[cfg(windows)] -fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { - libc::types::os::arch::extra::PROCESS_INFORMATION { - hProcess: ptr::mut_null(), - hThread: ptr::mut_null(), - dwProcessId: 0, - dwThreadId: 0 - } -} - -// FIXME: this is only pub so it can be tested (see issue #4536) -#[cfg(windows)] -pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { - let mut cmd = ~""; - append_arg(&mut cmd, prog); - for arg in args.iter() { - cmd.push_char(' '); - append_arg(&mut cmd, *arg); - } - return cmd; - - fn append_arg(cmd: &mut ~str, arg: &str) { - let quote = arg.iter().any(|c| c == ' ' || c == '\t'); - if quote { - cmd.push_char('"'); - } - for i in range(0u, arg.len()) { - append_char_at(cmd, arg, i); - } - if quote { - cmd.push_char('"'); - } - } - - fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { - match arg[i] as char { - '"' => { - // Escape quotes. - cmd.push_str("\\\""); - } - '\\' => { - if backslash_run_ends_in_quote(arg, i) { - // Double all backslashes that are in runs before quotes. - cmd.push_str("\\\\"); - } else { - // Pass other backslashes through unescaped. - cmd.push_char('\\'); - } - } - c => { - cmd.push_char(c); - } - } - } - - fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { - while i < s.len() && s[i] as char == '\\' { - i += 1; - } - return i < s.len() && s[i] as char == '"'; - } -} - -#[cfg(unix)] -fn spawn_process_os(prog: &str, args: &[~str], - env: Option<~[(~str, ~str)]>, - dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { - use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; - use libc::funcs::bsd44::getdtablesize; - - mod rustrt { - #[abi = "cdecl"] - extern { - pub fn rust_unset_sigprocmask(); - } - } - - #[cfg(windows)] - unsafe fn set_environ(_envp: *c_void) {} - #[cfg(target_os = "macos")] - unsafe fn set_environ(envp: *c_void) { - extern { fn _NSGetEnviron() -> *mut *c_void; } - - *_NSGetEnviron() = envp; - } - #[cfg(not(target_os = "macos"), not(windows))] - unsafe fn set_environ(envp: *c_void) { - extern { - static mut environ: *c_void; - } - environ = envp; - } - - unsafe { - - let pid = fork(); - if pid < 0 { - fail!("failure in fork: {}", os::last_os_error()); - } else if pid > 0 { - return SpawnProcessResult {pid: pid, handle: ptr::null()}; - } - - rustrt::rust_unset_sigprocmask(); - - if dup2(in_fd, 0) == -1 { - fail!("failure in dup2(in_fd, 0): {}", os::last_os_error()); - } - if dup2(out_fd, 1) == -1 { - fail!("failure in dup2(out_fd, 1): {}", os::last_os_error()); - } - if dup2(err_fd, 2) == -1 { - fail!("failure in dup3(err_fd, 2): {}", os::last_os_error()); - } - // close all other fds - for fd in range(3, getdtablesize()).invert() { - close(fd as c_int); - } - - do with_dirp(dir) |dirp| { - if !dirp.is_null() && chdir(dirp) == -1 { - fail!("failure in chdir: {}", os::last_os_error()); - } - } - - do with_envp(env) |envp| { - if !envp.is_null() { - set_environ(envp); - } - do with_argv(prog, args) |argv| { - execvp(*argv, argv); - // execvp only returns if an error occurred - fail!("failure in execvp: {}", os::last_os_error()); - } - } - } -} - -#[cfg(unix)] -fn with_argv<T>(prog: &str, args: &[~str], cb: &fn(**libc::c_char) -> T) -> T { - use vec; - - // We can't directly convert `str`s into `*char`s, as someone needs to hold - // a reference to the intermediary byte buffers. So first build an array to - // hold all the ~[u8] byte strings. - let mut tmps = vec::with_capacity(args.len() + 1); - - tmps.push(prog.to_c_str()); - - for arg in args.iter() { - tmps.push(arg.to_c_str()); - } - - // Next, convert each of the byte strings into a pointer. This is - // technically unsafe as the caller could leak these pointers out of our - // scope. - let mut ptrs = do tmps.map |tmp| { - tmp.with_ref(|buf| buf) - }; - - // Finally, make sure we add a null pointer. - ptrs.push(ptr::null()); - - ptrs.as_imm_buf(|buf, _| cb(buf)) -} - -#[cfg(unix)] -fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { - use vec; - - // On posixy systems we can pass a char** for envp, which is a - // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to - // have a temporary buffer to hold the intermediary `~[u8]` byte strings. - match env { - Some(env) => { - let mut tmps = vec::with_capacity(env.len()); - - for pair in env.iter() { - let kv = format!("{}={}", pair.first(), pair.second()); - tmps.push(kv.to_c_str()); - } - - // Once again, this is unsafe. - let mut ptrs = do tmps.map |tmp| { - tmp.with_ref(|buf| buf) - }; - ptrs.push(ptr::null()); - - do ptrs.as_imm_buf |buf, _| { - unsafe { cb(cast::transmute(buf)) } - } - } - _ => cb(ptr::null()) - } -} - -#[cfg(windows)] -fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T { - // On win32 we pass an "environment block" which is not a char**, but - // rather a concatenation of null-terminated k=v\0 sequences, with a final - // \0 to terminate. - match env { - Some(env) => { - let mut blk = ~[]; - - for pair in env.iter() { - let kv = format!("{}={}", pair.first(), pair.second()); - blk.push_all(kv.as_bytes()); - blk.push(0); - } - - blk.push(0); - - do blk.as_imm_buf |p, _len| { - unsafe { cb(cast::transmute(p)) } - } - } - _ => cb(ptr::mut_null()) - } -} - -fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T { - match d { - Some(dir) => dir.with_c_str(|buf| cb(buf)), - None => cb(ptr::null()) - } -} - -#[cfg(windows)] -fn free_handle(handle: *()) { - unsafe { - libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle)); - } -} - -#[cfg(unix)] -fn free_handle(_handle: *()) { - // unix has no process handle object, just a pid -} - -/** - * Waits for a process to exit and returns the exit code, failing - * if there is no process with the specified id. - * - * Note that this is private to avoid race conditions on unix where if - * a user calls waitpid(some_process.get_id()) then some_process.finish() - * and some_process.destroy() and some_process.finalize() will then either - * operate on a none-existent process or, even worse, on a newer process - * with the same id. - */ -fn waitpid(pid: pid_t) -> int { - return waitpid_os(pid); - - #[cfg(windows)] - fn waitpid_os(pid: pid_t) -> int { - use libc::types::os::arch::extra::DWORD; - use libc::consts::os::extra::{ - SYNCHRONIZE, - PROCESS_QUERY_INFORMATION, - FALSE, - STILL_ACTIVE, - INFINITE, - WAIT_FAILED - }; - use libc::funcs::extra::kernel32::{ - OpenProcess, - GetExitCodeProcess, - CloseHandle, - WaitForSingleObject - }; - - unsafe { - - let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, - FALSE, - pid as DWORD); - if process.is_null() { - fail!("failure in OpenProcess: {}", os::last_os_error()); - } - - loop { - let mut status = 0; - if GetExitCodeProcess(process, &mut status) == FALSE { - CloseHandle(process); - fail!("failure in GetExitCodeProcess: {}", os::last_os_error()); - } - if status != STILL_ACTIVE { - CloseHandle(process); - return status as int; - } - if WaitForSingleObject(process, INFINITE) == WAIT_FAILED { - CloseHandle(process); - fail!("failure in WaitForSingleObject: {}", os::last_os_error()); - } - } - } - } - - #[cfg(unix)] - fn waitpid_os(pid: pid_t) -> int { - use libc::funcs::posix01::wait::*; - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - fn WIFEXITED(status: i32) -> bool { - (status & 0xffi32) == 0i32 - } - - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - fn WIFEXITED(status: i32) -> bool { - (status & 0x7fi32) == 0i32 - } - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - fn WEXITSTATUS(status: i32) -> i32 { - (status >> 8i32) & 0xffi32 - } - - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - fn WEXITSTATUS(status: i32) -> i32 { - status >> 8i32 - } - - let mut status = 0 as c_int; - if unsafe { waitpid(pid, &mut status, 0) } == -1 { - fail!("failure in waitpid: {}", os::last_os_error()); - } - - return if WIFEXITED(status) { - WEXITSTATUS(status) as int - } else { - 1 - }; - } -} - -#[cfg(test)] -mod tests { - - #[test] #[cfg(windows)] - fn test_make_command_line() { - use super::make_command_line; - assert_eq!( - make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), - ~"prog aaa bbb ccc" - ); - assert_eq!( - make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), - ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" - ); - assert_eq!( - make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), - ~"\"C:\\Program Files\\test\" aa\\\"bb" - ); - assert_eq!( - make_command_line("echo", [~"a b c"]), - ~"echo \"a b c\"" - ); - } - - // Currently most of the tests of this functionality live inside std::run, - // but they may move here eventually as a non-blocking backend is added to - // std::run -} diff --git a/src/libstd/rt/io/native/stdio.rs b/src/libstd/rt/io/native/stdio.rs deleted file mode 100644 index ddfbb9a8f8c..00000000000 --- a/src/libstd/rt/io/native/stdio.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use libc; -use option::Option; -use rt::io::{Reader, Writer}; -use super::file; - -/// Creates a new handle to the stdin of this process -pub fn stdin() -> StdIn { StdIn::new() } -/// Creates a new handle to the stdout of this process -pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) } -/// Creates a new handle to the stderr of this process -pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) } - -pub fn print(s: &str) { - stdout().write(s.as_bytes()) -} - -pub fn println(s: &str) { - let mut out = stdout(); - out.write(s.as_bytes()); - out.write(['\n' as u8]); -} - -pub struct StdIn { - priv fd: file::FileDesc -} - -impl StdIn { - /// Duplicates the stdin file descriptor, returning an io::Reader - pub fn new() -> StdIn { - StdIn { fd: file::FileDesc::new(libc::STDIN_FILENO, false) } - } -} - -impl Reader for StdIn { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.fd.read(buf) } - fn eof(&mut self) -> bool { self.fd.eof() } -} - -pub struct StdOut { - priv fd: file::FileDesc -} - -impl StdOut { - /// Duplicates the specified file descriptor, returning an io::Writer - pub fn new(fd: file::fd_t) -> StdOut { - StdOut { fd: file::FileDesc::new(fd, false) } - } -} - -impl Writer for StdOut { - fn write(&mut self, buf: &[u8]) { self.fd.write(buf) } - fn flush(&mut self) { self.fd.flush() } -} diff --git a/src/libstd/rt/io/net/addrinfo.rs b/src/libstd/rt/io/net/addrinfo.rs deleted file mode 100644 index 684a6429775..00000000000 --- a/src/libstd/rt/io/net/addrinfo.rs +++ /dev/null @@ -1,127 +0,0 @@ -// 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 in a style related to -getaddrinfo() - -*/ - -use option::{Option, Some, None}; -use result::{Ok, Err}; -use rt::io::{io_error}; -use rt::io::net::ip::{SocketAddr, IpAddr}; -use rt::rtio::{IoFactory, with_local_io}; - -/// Hints to the types of sockets that are desired when looking up hosts -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` -pub enum Flag { - AddrConfig, - All, - CanonName, - NumericHost, - NumericServ, - Passive, - V4Mapped, -} - -/// A transport protocol associated with either a hint or a return value of -/// `lookup` -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` -pub struct Hint { - family: uint, - socktype: Option<SocketType>, - protocol: Option<Protocol>, - flags: uint, -} - -pub struct Info { - address: SocketAddr, - family: uint, - socktype: Option<SocketType>, - protocol: Option<Protocol>, - flags: uint, -} - -/// Easy name resolution. Given a hostname, returns the list of IP addresses for -/// that hostname. -/// -/// # Failure -/// -/// On failure, this will raise on the `io_error` condition. -pub fn get_host_addresses(host: &str) -> Option<~[IpAddr]> { - lookup(Some(host), None, None).map(|a| a.map(|i| i.address.ip)) -} - -/// Full-fleged 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 -/// -/// # Failure -/// -/// On failure, this will raise on the `io_error` condition. -/// -/// XXX: this is not public because the `Hint` structure is not ready for public -/// consumption just yet. -fn lookup(hostname: Option<&str>, servname: Option<&str>, - hint: Option<Hint>) -> Option<~[Info]> { - do with_local_io |io| { - match io.get_host_addresses(hostname, servname, hint) { - Ok(i) => Some(i), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -#[cfg(test)] -mod test { - use option::Some; - use rt::io::net::ip::Ipv4Addr; - use super::*; - - #[test] - #[ignore(cfg(target_os="android"))] // cannot give tcp/ip permission without help of apk - 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); - } -} diff --git a/src/libstd/rt/io/net/ip.rs b/src/libstd/rt/io/net/ip.rs deleted file mode 100644 index f72d2e1f19b..00000000000 --- a/src/libstd/rt/io/net/ip.rs +++ /dev/null @@ -1,449 +0,0 @@ -// 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 vec::MutableCloneableVector; -use to_str::ToStr; -use from_str::FromStr; -use option::{Option, None, Some}; - - -pub type Port = u16; - -#[deriving(Eq, TotalEq, Clone)] -pub enum IpAddr { - Ipv4Addr(u8, u8, u8, u8), - Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16) -} - -impl ToStr for IpAddr { - fn to_str(&self) -> ~str { - match *self { - Ipv4Addr(a, b, c, d) => - format!("{}.{}.{}.{}", a, b, c, d), - - // Ipv4 Compatible address - Ipv6Addr(0, 0, 0, 0, 0, 0, g, h) => { - format!("::{}.{}.{}.{}", (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) => { - format!("::FFFF:{}.{}.{}.{}", (g >> 8) as u8, g as u8, - (h >> 8) as u8, h as u8) - } - - Ipv6Addr(a, b, c, d, e, f, g, h) => - format!("{}:{}:{}:{}:{}:{}:{}:{}", a, b, c, d, e, f, g, h) - } - } -} - -#[deriving(Eq, TotalEq, Clone)] -pub struct SocketAddr { - ip: IpAddr, - port: Port, -} - - -impl ToStr for SocketAddr { - fn to_str(&self) -> ~str { - match self.ip { - Ipv4Addr(*) => format!("{}:{}", self.ip.to_str(), self.port), - Ipv6Addr(*) => format!("[{}]:{}", self.ip.to_str(), self.port), - } - } -} - -struct Parser<'self> { - // parsing as ASCII, so can use byte array - s: &'self [u8], - pos: uint, -} - -impl<'self> Parser<'self> { - fn new(s: &'self str) -> Parser<'self> { - 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>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> 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>(&mut self, cb: &fn(&mut Parser) -> Option<T>) -> Option<T> { - do self.read_atomically |p| { - cb(p).filtered(|_| p.is_eof()) - } - } - - // Return result of first successful parser - fn read_or<T>(&mut self, parsers: &[&fn(&mut Parser) -> Option<T>]) -> Option<T> { - for pf in parsers.iter() { - match self.read_atomically(|p: &mut Parser| (*pf)(p)) { - Some(r) => return Some(r), - None => {} - } - } - None - } - - // Apply 3 parsers sequentially - fn read_seq_3<A, B, C>(&mut self, - pa: &fn(&mut Parser) -> Option<A>, - pb: &fn(&mut Parser) -> Option<B>, - pc: &fn(&mut Parser) -> Option<C> - ) -> Option<(A, B, C)> - { - do self.read_atomically |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> { - do self.read_atomically |p| { - p.read_char().filtered(|&next| next == c) - } - } - - // 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 >= '0' as u8 && c <= '9' as u8 { - Some((c - '0' as u8) as u8) - } else if radix > 10 && c >= 'a' as u8 && c < 'a' as u8 + (radix - 10) { - Some((c - 'a' as u8 + 10) as u8) - } else if radix > 10 && c >= 'A' as u8 && c < 'A' as u8 + (radix - 10) { - Some((c - 'A' as u8 + 10) as u8) - } else { - None - } - } - - do 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> { - do 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> { - do 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.copy_from(head); - gs.mut_slice(8 - tail.len(), 8).copy_from(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 = do 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 = do 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.slice(0, head_size), tail.slice(0, tail_size))) - } - - fn read_ipv6_addr(&mut self) -> Option<IpAddr> { - do self.read_atomically |p| { - p.read_ipv6_addr_impl() - } - } - - fn read_ip_addr(&mut self) -> Option<IpAddr> { - let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr(); - let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr(); - self.read_or([ipv4_addr, ipv6_addr]) - } - - fn read_socket_addr(&mut self) -> Option<SocketAddr> { - let ip_addr = |p: &mut Parser| { - let ipv4_p = |p: &mut Parser| p.read_ip_addr(); - let ipv6_p = |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([ipv4_p, 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> { - do Parser::new(s).read_till_eof |p| { - p.read_ip_addr() - } - } -} - -impl FromStr for SocketAddr { - fn from_str(s: &str) -> Option<SocketAddr> { - do Parser::new(s).read_till_eof |p| { - p.read_socket_addr() - } - } -} - - -#[cfg(test)] -mod test { - use super::*; - use from_str::FromStr; - use option::{Option, Some, None}; - - #[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 enought 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_str() { - let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert!(a1.to_str() == ~"::ffff:192.0.2.128" || a1.to_str() == ~"::FFFF:192.0.2.128"); - } - -} diff --git a/src/libstd/rt/io/net/mod.rs b/src/libstd/rt/io/net/mod.rs deleted file mode 100644 index cf109167089..00000000000 --- a/src/libstd/rt/io/net/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -pub use self::addrinfo::get_host_addresses; - -pub mod addrinfo; -pub mod tcp; -pub mod udp; -pub mod ip; -#[cfg(unix)] -pub mod unix; diff --git a/src/libstd/rt/io/net/tcp.rs b/src/libstd/rt/io/net/tcp.rs deleted file mode 100644 index 493ed6aba87..00000000000 --- a/src/libstd/rt/io/net/tcp.rs +++ /dev/null @@ -1,725 +0,0 @@ -// 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 option::{Option, Some, None}; -use result::{Ok, Err}; -use rt::io::net::ip::SocketAddr; -use rt::io::{Reader, Writer, Listener, Acceptor}; -use rt::io::{io_error, EndOfFile}; -use rt::rtio::{IoFactory, with_local_io, - RtioSocket, RtioTcpListener, RtioTcpAcceptor, RtioTcpStream}; - -pub struct TcpStream { - priv obj: ~RtioTcpStream -} - -impl TcpStream { - fn new(s: ~RtioTcpStream) -> TcpStream { - TcpStream { obj: s } - } - - pub fn connect(addr: SocketAddr) -> Option<TcpStream> { - do with_local_io |io| { - match io.tcp_connect(addr) { - Ok(s) => Some(TcpStream::new(s)), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } - - pub fn peer_name(&mut self) -> Option<SocketAddr> { - match self.obj.peer_name() { - Ok(pn) => Some(pn), - Err(ioerr) => { - rtdebug!("failed to get peer name: {:?}", ioerr); - io_error::cond.raise(ioerr); - None - } - } - } - - pub fn socket_name(&mut self) -> Option<SocketAddr> { - match self.obj.socket_name() { - Ok(sn) => Some(sn), - Err(ioerr) => { - rtdebug!("failed to get socket name: {:?}", ioerr); - io_error::cond.raise(ioerr); - None - } - } - } -} - -impl Reader for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - match self.obj.read(buf) { - Ok(read) => Some(read), - Err(ioerr) => { - // EOF is indicated by returning None - if ioerr.kind != EndOfFile { - io_error::cond.raise(ioerr); - } - return None; - } - } - } - - fn eof(&mut self) -> bool { fail!() } -} - -impl Writer for TcpStream { - fn write(&mut self, buf: &[u8]) { - match self.obj.write(buf) { - Ok(_) => (), - Err(ioerr) => io_error::cond.raise(ioerr), - } - } -} - -pub struct TcpListener { - priv obj: ~RtioTcpListener -} - -impl TcpListener { - pub fn bind(addr: SocketAddr) -> Option<TcpListener> { - do with_local_io |io| { - match io.tcp_bind(addr) { - Ok(l) => Some(TcpListener { obj: l }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } - - pub fn socket_name(&mut self) -> Option<SocketAddr> { - match self.obj.socket_name() { - Ok(sn) => Some(sn), - Err(ioerr) => { - rtdebug!("failed to get socket name: {:?}", ioerr); - io_error::cond.raise(ioerr); - None - } - } - } -} - -impl Listener<TcpStream, TcpAcceptor> for TcpListener { - fn listen(self) -> Option<TcpAcceptor> { - match self.obj.listen() { - Ok(acceptor) => Some(TcpAcceptor { obj: acceptor }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -pub struct TcpAcceptor { - priv obj: ~RtioTcpAcceptor -} - -impl Acceptor<TcpStream> for TcpAcceptor { - fn accept(&mut self) -> Option<TcpStream> { - match self.obj.accept() { - Ok(s) => Some(TcpStream::new(s)), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use cell::Cell; - use rt::test::*; - use rt::io::net::ip::{Ipv4Addr, SocketAddr}; - use rt::io::*; - use prelude::*; - use rt::comm::oneshot; - - #[test] #[ignore] - fn bind_error() { - do run_in_mt_newsched_task { - let mut called = false; - do io_error::cond.trap(|e| { - assert!(e.kind == PermissionDenied); - called = true; - }).inside { - let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; - let listener = TcpListener::bind(addr); - assert!(listener.is_none()); - } - assert!(called); - } - } - - #[test] - fn connect_error() { - do run_in_mt_newsched_task { - let mut called = false; - do io_error::cond.trap(|e| { - let expected_error = if cfg!(unix) { - ConnectionRefused - } else { - // On Win32, opening port 1 gives WSAEADDRNOTAVAIL error. - OtherIoError - }; - assert_eq!(e.kind, expected_error); - called = true; - }).inside { - let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; - let stream = TcpStream::connect(addr); - assert!(stream.is_none()); - } - assert!(called); - } - } - - #[test] - fn smoke_test_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == 99); - } - - do spawntask { - port.take().recv(); - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } - } - } - - #[test] - fn smoke_test_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == 99); - } - - do spawntask { - port.take().recv(); - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } - } - } - - #[test] - fn read_eof_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(buf); - assert!(nread.is_none()); - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn read_eof_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(buf); - assert!(nread.is_none()); - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn read_eof_twice_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(buf); - assert!(nread.is_none()); - do io_error::cond.trap(|e| { - if cfg!(windows) { - assert_eq!(e.kind, NotConnected); - } else { - fail!(); - } - }).inside { - let nread = stream.read(buf); - assert!(nread.is_none()); - } - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn read_eof_twice_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let mut buf = [0]; - let nread = stream.read(buf); - assert!(nread.is_none()); - do io_error::cond.trap(|e| { - if cfg!(windows) { - assert_eq!(e.kind, NotConnected); - } else { - fail!(); - } - }).inside { - let nread = stream.read(buf); - assert!(nread.is_none()); - } - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn write_close_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let buf = [0]; - loop { - let mut stop = false; - do io_error::cond.trap(|e| { - // NB: ECONNRESET on linux, EPIPE on mac, ECONNABORTED - // on windows - assert!(e.kind == ConnectionReset || - e.kind == BrokenPipe || - e.kind == ConnectionAborted, - "unknown error: {:?}", e); - stop = true; - }).inside { - stream.write(buf); - } - if stop { break } - } - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn write_close_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - let mut stream = acceptor.accept(); - let buf = [0]; - loop { - let mut stop = false; - do io_error::cond.trap(|e| { - // NB: ECONNRESET on linux, EPIPE on mac, ECONNABORTED - // on windows - assert!(e.kind == ConnectionReset || - e.kind == BrokenPipe || - e.kind == ConnectionAborted, - "unknown error: {:?}", e); - stop = true; - }).inside { - stream.write(buf); - } - if stop { break } - } - } - - do spawntask { - port.take().recv(); - let _stream = TcpStream::connect(addr); - // Close - } - } - } - - #[test] - fn multiple_connect_serial_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - let max = 10; - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for ref mut stream in acceptor.incoming().take(max) { - let mut buf = [0]; - stream.read(buf); - assert_eq!(buf[0], 99); - } - } - - do spawntask { - port.take().recv(); - do max.times { - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } - } - } - } - - #[test] - fn multiple_connect_serial_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - let max = 10; - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for ref mut stream in acceptor.incoming().take(max) { - let mut buf = [0]; - stream.read(buf); - assert_eq!(buf[0], 99); - } - } - - do spawntask { - port.take().recv(); - do max.times { - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } - } - } - } - - #[test] - fn multiple_connect_interleaved_greedy_schedule_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - static MAX: int = 10; - let (port, chan) = oneshot(); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); - // Start another task to handle the connection - do spawntask { - let mut stream = stream.take(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == i as u8); - rtdebug!("read"); - } - } - } - - port.recv(); - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - do spawntask { - rtdebug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - rtdebug!("writing"); - stream.write([i as u8]); - } - } - } - } - - #[test] - fn multiple_connect_interleaved_greedy_schedule_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - static MAX: int = 10; - let (port, chan) = oneshot(); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); - // Start another task to handle the connection - do spawntask { - let mut stream = stream.take(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == i as u8); - rtdebug!("read"); - } - } - } - - port.recv(); - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - do spawntask { - rtdebug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - rtdebug!("writing"); - stream.write([i as u8]); - } - } - } - } - - #[test] - fn multiple_connect_interleaved_lazy_schedule_ip4() { - do run_in_mt_newsched_task { - let addr = next_test_ip4(); - static MAX: int = 10; - let (port, chan) = oneshot(); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); - // Start another task to handle the connection - do spawntask_later { - let mut stream = stream.take(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == 99); - rtdebug!("read"); - } - } - } - - port.recv(); - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - do spawntask_later { - rtdebug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - rtdebug!("writing"); - stream.write([99]); - } - } - } - } - #[test] - fn multiple_connect_interleaved_lazy_schedule_ip6() { - do run_in_mt_newsched_task { - let addr = next_test_ip6(); - static MAX: int = 10; - let (port, chan) = oneshot(); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); - // Start another task to handle the connection - do spawntask_later { - let mut stream = stream.take(); - let mut buf = [0]; - stream.read(buf); - assert!(buf[0] == 99); - rtdebug!("read"); - } - } - } - - port.recv(); - connect(0, addr); - - fn connect(i: int, addr: SocketAddr) { - if i == MAX { return } - - do spawntask_later { - rtdebug!("connecting"); - let mut stream = TcpStream::connect(addr); - // Connect again before writing - connect(i + 1, addr); - rtdebug!("writing"); - stream.write([99]); - } - } - } - } - - #[cfg(test)] - fn socket_name(addr: SocketAddr) { - do run_in_mt_newsched_task { - do spawntask { - 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_some()); - assert_eq!(addr, so_name.unwrap()); - - } - } - } - - #[cfg(test)] - fn peer_name(addr: SocketAddr) { - do run_in_mt_newsched_task { - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = TcpListener::bind(addr).listen(); - chan.take().send(()); - - acceptor.accept(); - } - - do spawntask { - port.take().recv(); - let stream = TcpStream::connect(addr); - - assert!(stream.is_some()); - 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_some()); - 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() { - // XXX: peer name is not consistent - //peer_name(next_test_ip6()); - socket_name(next_test_ip6()); - } - -} diff --git a/src/libstd/rt/io/net/udp.rs b/src/libstd/rt/io/net/udp.rs deleted file mode 100644 index f9cf8f5f9ae..00000000000 --- a/src/libstd/rt/io/net/udp.rs +++ /dev/null @@ -1,321 +0,0 @@ -// 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 option::{Option, Some, None}; -use result::{Ok, Err}; -use rt::io::net::ip::SocketAddr; -use rt::io::{Reader, Writer}; -use rt::io::{io_error, EndOfFile}; -use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory, with_local_io}; - -pub struct UdpSocket { - priv obj: ~RtioUdpSocket -} - -impl UdpSocket { - pub fn bind(addr: SocketAddr) -> Option<UdpSocket> { - do with_local_io |io| { - match io.udp_bind(addr) { - Ok(s) => Some(UdpSocket { obj: s }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } - - pub fn recvfrom(&mut self, buf: &mut [u8]) -> Option<(uint, SocketAddr)> { - match self.obj.recvfrom(buf) { - Ok((nread, src)) => Some((nread, src)), - Err(ioerr) => { - // EOF is indicated by returning None - if ioerr.kind != EndOfFile { - io_error::cond.raise(ioerr); - } - None - } - } - } - - pub fn sendto(&mut self, buf: &[u8], dst: SocketAddr) { - match self.obj.sendto(buf, dst) { - Ok(_) => (), - Err(ioerr) => io_error::cond.raise(ioerr), - } - } - - pub fn connect(self, other: SocketAddr) -> UdpStream { - UdpStream { socket: self, connectedTo: other } - } - - pub fn socket_name(&mut self) -> Option<SocketAddr> { - match self.obj.socket_name() { - Ok(sn) => Some(sn), - Err(ioerr) => { - rtdebug!("failed to get socket name: {:?}", ioerr); - io_error::cond.raise(ioerr); - None - } - } - } -} - -pub struct UdpStream { - priv socket: UdpSocket, - priv connectedTo: SocketAddr -} - -impl UdpStream { - pub fn as_socket<T>(&mut self, f: &fn(&mut UdpSocket) -> T) -> T { f(&mut self.socket) } - - pub fn disconnect(self) -> UdpSocket { self.socket } -} - -impl Reader for UdpStream { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - let peer = self.connectedTo; - do self.as_socket |sock| { - match sock.recvfrom(buf) { - Some((_nread, src)) if src != peer => Some(0), - Some((nread, _src)) => Some(nread), - None => None, - } - } - } - - fn eof(&mut self) -> bool { fail!() } -} - -impl Writer for UdpStream { - fn write(&mut self, buf: &[u8]) { - do self.as_socket |sock| { - sock.sendto(buf, self.connectedTo); - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use rt::test::*; - use rt::io::net::ip::{Ipv4Addr, SocketAddr}; - use rt::io::*; - use option::{Some, None}; - use rt::comm::oneshot; - use cell::Cell; - - #[test] #[ignore] - fn bind_error() { - do run_in_mt_newsched_task { - let mut called = false; - do io_error::cond.trap(|e| { - assert!(e.kind == PermissionDenied); - called = true; - }).inside { - let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; - let socket = UdpSocket::bind(addr); - assert!(socket.is_none()); - } - assert!(called); - } - } - - #[test] - fn socket_smoke_test_ip4() { - do run_in_mt_newsched_task { - let server_ip = next_test_ip4(); - let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - match UdpSocket::bind(server_ip) { - Some(ref mut server) => { - chan.take().send(()); - let mut buf = [0]; - match server.recvfrom(buf) { - Some((nread, src)) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); - } - None => fail!() - } - } - None => fail!() - } - } - - do spawntask { - match UdpSocket::bind(client_ip) { - Some(ref mut client) => { - port.take().recv(); - client.sendto([99], server_ip) - } - None => fail!() - } - } - } - } - - #[test] - fn socket_smoke_test_ip6() { - do run_in_mt_newsched_task { - let server_ip = next_test_ip6(); - let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - match UdpSocket::bind(server_ip) { - Some(ref mut server) => { - chan.take().send(()); - let mut buf = [0]; - match server.recvfrom(buf) { - Some((nread, src)) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - assert_eq!(src, client_ip); - } - None => fail!() - } - } - None => fail!() - } - } - - do spawntask { - match UdpSocket::bind(client_ip) { - Some(ref mut client) => { - port.take().recv(); - client.sendto([99], server_ip) - } - None => fail!() - } - } - } - } - - #[test] - fn stream_smoke_test_ip4() { - do run_in_mt_newsched_task { - let server_ip = next_test_ip4(); - let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - match UdpSocket::bind(server_ip) { - Some(server) => { - let server = ~server; - let mut stream = server.connect(client_ip); - chan.take().send(()); - let mut buf = [0]; - match stream.read(buf) { - Some(nread) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - } - None => fail!() - } - } - None => fail!() - } - } - - do spawntask { - match UdpSocket::bind(client_ip) { - Some(client) => { - let client = ~client; - let mut stream = client.connect(server_ip); - port.take().recv(); - stream.write([99]); - } - None => fail!() - } - } - } - } - - #[test] - fn stream_smoke_test_ip6() { - do run_in_mt_newsched_task { - let server_ip = next_test_ip6(); - let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - match UdpSocket::bind(server_ip) { - Some(server) => { - let server = ~server; - let mut stream = server.connect(client_ip); - chan.take().send(()); - let mut buf = [0]; - match stream.read(buf) { - Some(nread) => { - assert_eq!(nread, 1); - assert_eq!(buf[0], 99); - } - None => fail!() - } - } - None => fail!() - } - } - - do spawntask { - match UdpSocket::bind(client_ip) { - Some(client) => { - let client = ~client; - let mut stream = client.connect(server_ip); - port.take().recv(); - stream.write([99]); - } - None => fail!() - } - } - } - } - - #[cfg(test)] - fn socket_name(addr: SocketAddr) { - do run_in_mt_newsched_task { - do spawntask { - let server = UdpSocket::bind(addr); - - assert!(server.is_some()); - 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_some()); - 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()); - } -} diff --git a/src/libstd/rt/io/net/unix.rs b/src/libstd/rt/io/net/unix.rs deleted file mode 100644 index dd8a999c6de..00000000000 --- a/src/libstd/rt/io/net/unix.rs +++ /dev/null @@ -1,295 +0,0 @@ -// 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. - -*/ - -use prelude::*; - -use c_str::ToCStr; -use rt::rtio::{IoFactory, RtioUnixListener, with_local_io}; -use rt::rtio::{RtioUnixAcceptor, RtioPipe}; -use rt::io::pipe::PipeStream; -use rt::io::{io_error, Listener, Acceptor, Reader, Writer}; - -/// A stream which communicates over a named pipe. -pub struct UnixStream { - priv obj: PipeStream, -} - -impl UnixStream { - fn new(obj: ~RtioPipe) -> UnixStream { - UnixStream { obj: PipeStream::new(obj) } - } - - /// 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. - /// - /// # Failure - /// - /// This function will raise on the `io_error` condition if the connection - /// could not be made. - /// - /// # Example - /// - /// use std::rt::io::net::unix::UnixStream; - /// - /// let server = Path("path/to/my/socket"); - /// let mut stream = UnixStream::connect(&server); - /// stream.write([1, 2, 3]); - /// - pub fn connect<P: ToCStr>(path: &P) -> Option<UnixStream> { - do with_local_io |io| { - match io.unix_connect(&path.to_c_str()) { - Ok(s) => Some(UnixStream::new(s)), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } -} - -impl Reader for UnixStream { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.obj.read(buf) } - fn eof(&mut self) -> bool { self.obj.eof() } -} - -impl Writer for UnixStream { - fn write(&mut self, buf: &[u8]) { self.obj.write(buf) } -} - -pub struct UnixListener { - priv obj: ~RtioUnixListener, -} - -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. - /// - /// # Failure - /// - /// This function will raise on the `io_error` condition if the specified - /// path could not be bound. - /// - /// # Example - /// - /// use std::rt::io::net::unix::UnixListener; - /// - /// let server = Path("path/to/my/socket"); - /// let mut stream = UnixListener::bind(&server); - /// for client in stream.incoming() { - /// let mut client = client; - /// client.write([1, 2, 3, 4]); - /// } - /// - pub fn bind<P: ToCStr>(path: &P) -> Option<UnixListener> { - do with_local_io |io| { - match io.unix_bind(&path.to_c_str()) { - Ok(s) => Some(UnixListener{ obj: s }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } -} - -impl Listener<UnixStream, UnixAcceptor> for UnixListener { - fn listen(self) -> Option<UnixAcceptor> { - match self.obj.listen() { - Ok(acceptor) => Some(UnixAcceptor { obj: acceptor }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -pub struct UnixAcceptor { - priv obj: ~RtioUnixAcceptor, -} - -impl Acceptor<UnixStream> for UnixAcceptor { - fn accept(&mut self) -> Option<UnixStream> { - match self.obj.accept() { - Ok(s) => Some(UnixStream::new(s)), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } -} - -#[cfg(test)] -mod tests { - use prelude::*; - use super::*; - use cell::Cell; - use rt::test::*; - use rt::io::*; - use rt::comm::oneshot; - - fn smalltest(server: ~fn(UnixStream), client: ~fn(UnixStream)) { - let server = Cell::new(server); - let client = Cell::new(client); - do run_in_mt_newsched_task { - let server = Cell::new(server.take()); - let client = Cell::new(client.take()); - let path1 = next_test_unix(); - let path2 = path1.clone(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = UnixListener::bind(&path1).listen(); - chan.take().send(()); - server.take()(acceptor.accept().unwrap()); - } - - do spawntask { - port.take().recv(); - client.take()(UnixStream::connect(&path2).unwrap()); - } - } - } - - #[test] - fn bind_error() { - do run_in_mt_newsched_task { - let mut called = false; - do io_error::cond.trap(|e| { - assert!(e.kind == PermissionDenied); - called = true; - }).inside { - let listener = UnixListener::bind(&("path/to/nowhere")); - assert!(listener.is_none()); - } - assert!(called); - } - } - - #[test] - fn connect_error() { - do run_in_mt_newsched_task { - let mut called = false; - do io_error::cond.trap(|e| { - assert_eq!(e.kind, OtherIoError); - called = true; - }).inside { - let stream = UnixStream::connect(&("path/to/nowhere")); - assert!(stream.is_none()); - } - assert!(called); - } - } - - #[test] - fn smoke() { - smalltest(|mut server| { - let mut buf = [0]; - server.read(buf); - assert!(buf[0] == 99); - }, |mut client| { - client.write([99]); - }) - } - - #[test] - fn read_eof() { - smalltest(|mut server| { - let mut buf = [0]; - assert!(server.read(buf).is_none()); - assert!(server.read(buf).is_none()); - }, |_client| { - // drop the client - }) - } - - #[test] - fn write_begone() { - smalltest(|mut server| { - let buf = [0]; - let mut stop = false; - while !stop{ - do io_error::cond.trap(|e| { - assert!(e.kind == BrokenPipe || e.kind == NotConnected, - "unknown error {:?}", e); - stop = true; - }).inside { - server.write(buf); - } - } - }, |_client| { - // drop the client - }) - } - - #[test] - fn accept_lots() { - do run_in_mt_newsched_task { - let times = 10; - let path1 = next_test_unix(); - let path2 = path1.clone(); - let (port, chan) = oneshot(); - let port = Cell::new(port); - let chan = Cell::new(chan); - - do spawntask { - let mut acceptor = UnixListener::bind(&path1).listen(); - chan.take().send(()); - do times.times { - let mut client = acceptor.accept(); - let mut buf = [0]; - client.read(buf); - assert_eq!(buf[0], 100); - } - } - - do spawntask { - port.take().recv(); - do times.times { - let mut stream = UnixStream::connect(&path2); - stream.write([100]); - } - } - } - } - - #[test] - fn path_exists() { - do run_in_mt_newsched_task { - let path = next_test_unix(); - let _acceptor = UnixListener::bind(&path).listen(); - assert!(path.exists()); - } - } -} diff --git a/src/libstd/rt/io/option.rs b/src/libstd/rt/io/option.rs deleted file mode 100644 index 5938252571f..00000000000 --- a/src/libstd/rt/io/option.rs +++ /dev/null @@ -1,181 +0,0 @@ -// 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 Option type -//! -//! I/O constructors return option types to allow errors to be handled. -//! These implementations allow e.g. `Option<File>` to be used -//! as a `Reader` without unwrapping the option first. - -use option::*; -use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle}; -use super::{standard_error, PreviousIoError, io_error, IoError}; - -fn prev_io_error() -> IoError { - standard_error(PreviousIoError) -} - -impl<W: Writer> Writer for Option<W> { - fn write(&mut self, buf: &[u8]) { - match *self { - Some(ref mut writer) => writer.write(buf), - None => io_error::cond.raise(prev_io_error()) - } - } - - fn flush(&mut self) { - match *self { - Some(ref mut writer) => writer.flush(), - None => io_error::cond.raise(prev_io_error()) - } - } -} - -impl<R: Reader> Reader for Option<R> { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - match *self { - Some(ref mut reader) => reader.read(buf), - None => { - io_error::cond.raise(prev_io_error()); - None - } - } - } - - fn eof(&mut self) -> bool { - match *self { - Some(ref mut reader) => reader.eof(), - None => { - io_error::cond.raise(prev_io_error()); - true - } - } - } -} - -impl<S: Seek> Seek for Option<S> { - fn tell(&self) -> u64 { - match *self { - Some(ref seeker) => seeker.tell(), - None => { - io_error::cond.raise(prev_io_error()); - 0 - } - } - } - fn seek(&mut self, pos: i64, style: SeekStyle) { - match *self { - Some(ref mut seeker) => seeker.seek(pos, style), - None => io_error::cond.raise(prev_io_error()) - } - } -} - -impl<T, A: Acceptor<T>, L: Listener<T, A>> Listener<T, A> for Option<L> { - fn listen(self) -> Option<A> { - match self { - Some(listener) => listener.listen(), - None => { - io_error::cond.raise(prev_io_error()); - None - } - } - } -} - -impl<T, A: Acceptor<T>> Acceptor<T> for Option<A> { - fn accept(&mut self) -> Option<T> { - match *self { - Some(ref mut acceptor) => acceptor.accept(), - None => { - io_error::cond.raise(prev_io_error()); - None - } - } - } -} - -#[cfg(test)] -mod test { - use option::*; - use super::super::mem::*; - use rt::test::*; - use super::super::{PreviousIoError, io_error}; - - #[test] - fn test_option_writer() { - do run_in_mt_newsched_task { - let mut writer: Option<MemWriter> = Some(MemWriter::new()); - writer.write([0, 1, 2]); - writer.flush(); - assert_eq!(writer.unwrap().inner(), ~[0, 1, 2]); - } - } - - #[test] - fn test_option_writer_error() { - do run_in_mt_newsched_task { - let mut writer: Option<MemWriter> = None; - - let mut called = false; - do io_error::cond.trap(|err| { - assert_eq!(err.kind, PreviousIoError); - called = true; - }).inside { - writer.write([0, 0, 0]); - } - assert!(called); - - let mut called = false; - do io_error::cond.trap(|err| { - assert_eq!(err.kind, PreviousIoError); - called = true; - }).inside { - writer.flush(); - } - assert!(called); - } - } - - #[test] - fn test_option_reader() { - do run_in_mt_newsched_task { - let mut reader: Option<MemReader> = Some(MemReader::new(~[0, 1, 2, 3])); - let mut buf = [0, 0]; - reader.read(buf); - assert_eq!(buf, [0, 1]); - assert!(!reader.eof()); - } - } - - #[test] - fn test_option_reader_error() { - let mut reader: Option<MemReader> = None; - let mut buf = []; - - let mut called = false; - do io_error::cond.trap(|err| { - assert_eq!(err.kind, PreviousIoError); - called = true; - }).inside { - reader.read(buf); - } - assert!(called); - - let mut called = false; - do io_error::cond.trap(|err| { - assert_eq!(err.kind, PreviousIoError); - called = true; - }).inside { - assert!(reader.eof()); - } - assert!(called); - } -} diff --git a/src/libstd/rt/io/pipe.rs b/src/libstd/rt/io/pipe.rs deleted file mode 100644 index fbbd5a83561..00000000000 --- a/src/libstd/rt/io/pipe.rs +++ /dev/null @@ -1,89 +0,0 @@ -// 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. - -use prelude::*; -use super::{Reader, Writer}; -use rt::io::{io_error, EndOfFile}; -use rt::io::native::file; -use rt::rtio::{RtioPipe, with_local_io}; - -pub struct PipeStream { - priv obj: ~RtioPipe, -} - -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 - /// - /// use std::libc; - /// use std::rt::io::pipe; - /// - /// let mut pipe = PipeStream::open(libc::STDERR_FILENO); - /// pipe.write(bytes!("Hello, stderr!")); - /// - /// # Failure - /// - /// If the pipe cannot be created, an error will be raised on the - /// `io_error` condition. - pub fn open(fd: file::fd_t) -> Option<PipeStream> { - do with_local_io |io| { - match io.pipe_open(fd) { - Ok(obj) => Some(PipeStream { obj: obj }), - Err(e) => { - io_error::cond.raise(e); - None - } - } - } - } - - pub fn new(inner: ~RtioPipe) -> PipeStream { - PipeStream { obj: inner } - } -} - -impl Reader for PipeStream { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - match self.obj.read(buf) { - Ok(read) => Some(read), - Err(ioerr) => { - // EOF is indicated by returning None - if ioerr.kind != EndOfFile { - io_error::cond.raise(ioerr); - } - return None; - } - } - } - - fn eof(&mut self) -> bool { false } -} - -impl Writer for PipeStream { - fn write(&mut self, buf: &[u8]) { - match self.obj.write(buf) { - Ok(_) => (), - Err(ioerr) => { - io_error::cond.raise(ioerr); - } - } - } -} diff --git a/src/libstd/rt/io/process.rs b/src/libstd/rt/io/process.rs deleted file mode 100644 index 6b21cde2488..00000000000 --- a/src/libstd/rt/io/process.rs +++ /dev/null @@ -1,177 +0,0 @@ -// 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 - -use prelude::*; -use cell::Cell; - -use libc; -use rt::io; -use rt::io::io_error; -use rt::rtio::{RtioProcess, IoFactory, with_local_io}; - -use fmt; - -// windows values don't matter as long as they're at least one of unix's -// TERM/KILL/INT signals -#[cfg(windows)] pub static PleaseExitSignal: int = 15; -#[cfg(windows)] pub static MustDieSignal: int = 9; -#[cfg(not(windows))] pub static PleaseExitSignal: int = libc::SIGTERM as int; -#[cfg(not(windows))] pub static MustDieSignal: int = libc::SIGKILL as int; - -pub struct Process { - priv handle: ~RtioProcess, - io: ~[Option<io::PipeStream>], -} - -/// This configuration describes how a new process should be spawned. This is -/// translated to libuv's own configuration -pub struct ProcessConfig<'self> { - /// Path to the program to run - program: &'self str, - - /// Arguments to pass to the program (doesn't include the program itself) - args: &'self [~str], - - /// Optional environment to specify for the program. If this is None, then - /// it will inherit the current process's environment. - env: Option<&'self [(~str, ~str)]>, - - /// Optional working directory for the new process. If this is None, then - /// the current directory of the running process is inherited. - cwd: Option<&'self str>, - - /// Any number of streams/file descriptors/pipes may be attached to this - /// process. This list enumerates the file descriptors and such for the - /// process to be spawned, and the file descriptors inherited will start at - /// 0 and go to the length of this array. - /// - /// Standard file descriptors are: - /// - /// 0 - stdin - /// 1 - stdout - /// 2 - stderr - io: &'self [StdioContainer] -} - -/// Describes what to do with a standard io stream for a child process. -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. - 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. -#[deriving(Eq)] -pub enum ProcessExit { - /// Normal termination with an exit status. - ExitStatus(int), - - /// Termination by signal, with the signal number. - ExitSignal(int), -} - -impl fmt::Default for ProcessExit { - /// Format a ProcessExit enum, to nicely present the information. - fn fmt(obj: &ProcessExit, f: &mut fmt::Formatter) { - match *obj { - ExitStatus(code) => write!(f.buf, "exit code: {}", code), - ExitSignal(code) => write!(f.buf, "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 { - /// Creates a new pipe initialized, but not bound to any particular - /// source/destination - pub fn new(config: ProcessConfig) -> Option<Process> { - let config = Cell::new(config); - do with_local_io |io| { - match io.spawn(config.take()) { - Ok((p, io)) => Some(Process{ - handle: p, - io: io.move_iter().map(|p| - p.map(|p| io::PipeStream::new(p)) - ).collect() - }), - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - } - } - - /// 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 this is purely a wrapper around libuv's `uv_process_kill` - /// function. - /// - /// If the signal delivery fails, then the `io_error` condition is raised on - pub fn signal(&mut self, signal: int) { - match self.handle.kill(signal) { - Ok(()) => {} - Err(err) => { - io_error::cond.raise(err) - } - } - } - - /// 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. - pub fn wait(&mut self) -> ProcessExit { self.handle.wait() } -} - -impl Drop for Process { - fn drop(&mut self) { - // Close all I/O before exiting to ensure that the child doesn't wait - // forever to print some text or something similar. - for _ in range(0, self.io.len()) { - self.io.pop(); - } - - self.wait(); - } -} - -// Tests for this module can be found in the rtio-processes run-pass test, along -// with the justification for why it's not located here. diff --git a/src/libstd/rt/io/signal.rs b/src/libstd/rt/io/signal.rs deleted file mode 100644 index 3f013d5cac9..00000000000 --- a/src/libstd/rt/io/signal.rs +++ /dev/null @@ -1,223 +0,0 @@ -// 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. - -/*! - -Signal handling - -This modules provides bindings to receive signals safely, built on top of the -local I/O factory. There are a number of defined signals which can be caught, -but not all signals will work across all platforms (windows doesn't have -definitions for a number of signals. - -*/ - -use container::{Map, MutableMap}; -use comm::{Port, SharedChan, stream}; -use hashmap; -use option::{Some, None}; -use result::{Err, Ok}; -use rt::io::io_error; -use rt::rtio::{IoFactory, RtioSignal, with_local_io}; - -#[repr(int)] -#[deriving(Eq, IterBytes)] -pub enum Signum { - /// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break. - Break = 21i, - /// Equivalent to SIGHUP, delivered when the user closes the terminal - /// window. On delivery of HangUp, the program is given approximately - /// 10 seconds to perfom any cleanup. After that, Windows will - /// unconditionally terminate it. - HangUp = 1i, - /// Equivalent to SIGINT, delivered when the user presses Ctrl-c. - Interrupt = 2i, - /// Equivalent to SIGQUIT, delivered when the user presses Ctrl-\. - Quit = 3i, - /// Equivalent to SIGTSTP, delivered when the user presses Ctrl-z. - StopTemporarily = 20i, - /// Equivalent to SIGUSR1. - User1 = 10i, - /// Equivalent to SIGUSR2. - User2 = 12i, - /// Equivalent to SIGWINCH, delivered when the console has been resized. - /// WindowSizeChange may not be delivered in a timely manner; size change - /// will only be detected when the cursor is being moved. - WindowSizeChange = 28i, -} - -/// Listener provides a port to listen for registered signals. -/// -/// Listener automatically unregisters its handles once it is out of scope. -/// However, clients can still unregister signums manually. -/// -/// # Example -/// -/// ```rust -/// use std::rt::io::signal::{Listener, Interrupt}; -/// -/// let mut listener = Listener::new(); -/// listener.register(signal::Interrupt); -/// -/// do spawn { -/// loop { -/// match listener.port.recv() { -/// Interrupt => println("Got Interrupt'ed"), -/// _ => (), -/// } -/// } -/// } -/// -/// ``` -pub struct Listener { - /// A map from signums to handles to keep the handles in memory - priv handles: hashmap::HashMap<Signum, ~RtioSignal>, - /// chan is where all the handles send signums, which are received by - /// the clients from port. - priv chan: SharedChan<Signum>, - - /// Clients of Listener can `recv()` from this port. This is exposed to - /// allow selection over this port as well as manipulation of the port - /// directly. - port: Port<Signum>, -} - -impl Listener { - /// Creates a new listener for signals. Once created, signals are bound via - /// the `register` method (otherwise nothing will ever be received) - pub fn new() -> Listener { - let (port, chan) = stream(); - Listener { - chan: SharedChan::new(chan), - port: port, - handles: hashmap::HashMap::new(), - } - } - - /// Listen for a signal, returning true when successfully registered for - /// signum. Signals can be received using `recv()`. - /// - /// Once a signal is registered, this listener will continue to receive - /// notifications of signals until it is unregistered. This occurs - /// regardless of the number of other listeners registered in other tasks - /// (or on this task). - /// - /// Signals are still received if there is no task actively waiting for - /// a signal, and a later call to `recv` will return the signal that was - /// received while no task was waiting on it. - /// - /// # Failure - /// - /// If this function fails to register a signal handler, then an error will - /// be raised on the `io_error` condition and the function will return - /// false. - pub fn register(&mut self, signum: Signum) -> bool { - if self.handles.contains_key(&signum) { - return true; // self is already listening to signum, so succeed - } - do with_local_io |io| { - match io.signal(signum, self.chan.clone()) { - Ok(w) => { - self.handles.insert(signum, w); - Some(()) - }, - Err(ioerr) => { - io_error::cond.raise(ioerr); - None - } - } - }.is_some() - } - - /// Unregisters a signal. If this listener currently had a handler - /// registered for the signal, then it will stop receiving any more - /// notification about the signal. If the signal has already been received, - /// it may still be returned by `recv`. - pub fn unregister(&mut self, signum: Signum) { - self.handles.pop(&signum); - } -} - -#[cfg(test)] -mod test { - use libc; - use rt::io::timer; - use super::{Listener, Interrupt}; - use comm::{GenericPort, Peekable}; - - // kill is only available on Unixes - #[cfg(unix)] - fn sigint() { - unsafe { - libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT); - } - } - - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) - fn test_io_signal_smoketest() { - let mut signal = Listener::new(); - signal.register(Interrupt); - sigint(); - timer::sleep(10); - match signal.port.recv() { - Interrupt => (), - s => fail!("Expected Interrupt, got {:?}", s), - } - } - - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) - fn test_io_signal_two_signal_one_signum() { - let mut s1 = Listener::new(); - let mut s2 = Listener::new(); - s1.register(Interrupt); - s2.register(Interrupt); - sigint(); - timer::sleep(10); - match s1.port.recv() { - Interrupt => (), - s => fail!("Expected Interrupt, got {:?}", s), - } - match s2.port.recv() { - Interrupt => (), - s => fail!("Expected Interrupt, got {:?}", s), - } - } - - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) - fn test_io_signal_unregister() { - let mut s1 = Listener::new(); - let mut s2 = Listener::new(); - s1.register(Interrupt); - s2.register(Interrupt); - s2.unregister(Interrupt); - sigint(); - timer::sleep(10); - if s2.port.peek() { - fail!("Unexpected {:?}", s2.port.recv()); - } - } - - #[cfg(windows)] - #[test] - fn test_io_signal_invalid_signum() { - use rt::io; - use super::User1; - let mut s = Listener::new(); - let mut called = false; - do io::io_error::cond.trap(|_| { - called = true; - }).inside { - if s.register(User1) { - fail!("Unexpected successful registry of signum {:?}", User1); - } - } - assert!(called); - } -} diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs deleted file mode 100644 index e829c77cec1..00000000000 --- a/src/libstd/rt/io/stdio.rs +++ /dev/null @@ -1,321 +0,0 @@ -// 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. - -/*! - -This modules provides bindings to the local event loop's TTY interface, using it -to have synchronous, but non-blocking versions of stdio. These handles can be -inspected for information about terminal dimensions or related information -about the stream or terminal that it is attached to. - -# Example - -```rust -use std::rt::io; - -let mut out = io::stdout(); -out.write(bytes!("Hello, world!")); -``` - -*/ - -use fmt; -use libc; -use option::{Option, Some, None}; -use result::{Ok, Err}; -use rt::io::buffered::LineBufferedWriter; -use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io, - CloseAsynchronously}; -use super::{Reader, Writer, io_error, IoError, OtherIoError, - standard_error, EndOfFile}; - -// 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(~RtioTTY), - File(~RtioFileStream), -} - -fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T { - do with_local_io |io| { - let fd = unsafe { libc::dup(fd) }; - match io.tty_open(fd, readable) { - Ok(tty) => Some(f(TTY(tty))), - Err(_) => { - // It's not really that desirable if these handles are closed - // synchronously, and because they're squirreled away in a task - // structure the destructors will be run when the task is - // attempted to get destroyed. This means that if we run a - // synchronous destructor we'll attempt to do some scheduling - // operations which will just result in sadness. - Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously)))) - } - } - }.unwrap() -} - -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// See `stdout()` for notes about this function. -pub fn stdin() -> StdReader { - do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } } -} - -/// Creates a new non-blocking handle to the stdout of the current process. -/// -/// Note that this is a fairly expensive operation in that at least one memory -/// allocation is performed. Additionally, this must be called from a runtime -/// task context because the stream returned will be a non-blocking object using -/// the local scheduler to perform the I/O. -pub fn stdout() -> StdWriter { - do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } } -} - -/// Creates a new non-blocking handle to the stderr of the current process. -/// -/// See `stdout()` for notes about this function. -pub fn stderr() -> StdWriter { - do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } } -} - -// 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: -// -// do with_task_stdout |io1| { -// do with_task_stdout |io2| { -// // io1 aliases io2 -// } -// } -fn with_task_stdout(f: &fn(&mut Writer)) { - use rt::local::Local; - use rt::task::Task; - - unsafe { - // Logging may require scheduling operations, so we can't remove the - // task from TLS right now, hence the unsafe borrow. Sad. - let task: *mut Task = Local::unsafe_borrow(); - - match (*task).stdout_handle { - Some(ref mut handle) => f(*handle), - None => { - let handle = stdout(); - let mut handle = ~LineBufferedWriter::new(handle) as ~Writer; - f(handle); - (*task).stdout_handle = Some(handle); - } - } - } -} - -/// 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() { - do 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) { - do with_task_stdout |io| { - io.write(s.as_bytes()); - } -} - -/// Prints a string as a line. to the stdout of the current process. A literal -/// `\n` character is printed to the console after the string. -pub fn println(s: &str) { - do with_task_stdout |io| { - io.write(s.as_bytes()); - io.write(['\n' as u8]); - } -} - -/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible -/// with the `format_args!` macro. -pub fn print_args(fmt: &fmt::Arguments) { - do with_task_stdout |io| { - fmt::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) { - do with_task_stdout |io| { - fmt::writeln(io, fmt); - } -} - -/// Representation of a reader of a standard input stream -pub struct StdReader { - priv inner: StdSource -} - -impl Reader for StdReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - let ret = match self.inner { - TTY(ref mut tty) => tty.read(buf), - 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) => { - io_error::cond.raise(standard_error(EndOfFile)); - None - } - Ok(amt) => Some(amt as uint), - Err(e) => { - io_error::cond.raise(e); - None - } - } - } - - fn eof(&mut self) -> bool { false } -} - -/// Representation of a writer to a standard output stream -pub struct StdWriter { - priv inner: StdSource -} - -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 Some((width, height)). - /// - /// # Failure - /// - /// This function will raise on the `io_error` condition if an error - /// happens. - pub fn winsize(&mut self) -> Option<(int, int)> { - match self.inner { - TTY(ref mut tty) => { - match tty.get_winsize() { - Ok(p) => Some(p), - Err(e) => { - io_error::cond.raise(e); - None - } - } - } - File(*) => { - io_error::cond.raise(IoError { - kind: OtherIoError, - desc: "stream is not a tty", - detail: None, - }); - None - } - } - } - - /// Controls whether this output stream is a "raw stream" or simply a normal - /// stream. - /// - /// # Failure - /// - /// This function will raise on the `io_error` condition if an error - /// happens. - pub fn set_raw(&mut self, raw: bool) { - match self.inner { - TTY(ref mut tty) => { - match tty.set_raw(raw) { - Ok(()) => {}, - Err(e) => io_error::cond.raise(e), - } - } - File(*) => { - io_error::cond.raise(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(&mut self, buf: &[u8]) { - let ret = match self.inner { - TTY(ref mut tty) => tty.write(buf), - File(ref mut file) => file.write(buf), - }; - match ret { - Ok(()) => {} - Err(e) => io_error::cond.raise(e) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn smoke() { - // Just make sure we can acquire handles - stdin(); - stdout(); - stderr(); - } -} diff --git a/src/libstd/rt/io/timer.rs b/src/libstd/rt/io/timer.rs deleted file mode 100644 index 48e0182354a..00000000000 --- a/src/libstd/rt/io/timer.rs +++ /dev/null @@ -1,186 +0,0 @@ -// 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 ports which will receive notifications after a period of time. - -# Example - -```rust - -use std::rt::io::Timer; - -let mut timer = Timer::new().unwrap(); -timer.sleep(10); // block the task for awhile - -let timeout = timer.oneshot(10); -// do some work -timeout.recv(); // wait for the timeout to expire - -let periodic = timer.periodic(10); -loop { - periodic.recv(); - // this loop is only executed once every 10ms -} - -``` - -*/ - -use comm::{Port, PortOne}; -use option::{Option, Some, None}; -use result::{Ok, Err}; -use rt::io::io_error; -use rt::rtio::{IoFactory, RtioTimer, with_local_io}; - -pub struct Timer { - priv obj: ~RtioTimer -} - -/// Sleep the current task for `msecs` milliseconds. -pub fn sleep(msecs: u64) { - let mut timer = Timer::new().expect("timer::sleep: could not create a Timer"); - - timer.sleep(msecs) -} - -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() -> Option<Timer> { - do with_local_io |io| { - match io.timer_init() { - Ok(t) => Some(Timer { obj: t }), - Err(ioerr) => { - rtdebug!("Timer::init: failed to init: {:?}", ioerr); - io_error::cond.raise(ioerr); - None - } - } - - } - } - - /// Blocks the current task for `msecs` milliseconds. - /// - /// Note that this function will cause any other ports for this timer to be - /// invalidated (the other end will be closed). - pub fn sleep(&mut self, msecs: u64) { - self.obj.sleep(msecs); - } - - /// Creates a oneshot port which will have a notification sent when `msecs` - /// milliseconds has elapsed. This does *not* block the current task, but - /// instead returns immediately. - /// - /// Note that this invalidates any previous port which has been created by - /// this timer, and that the returned port will be invalidated once the - /// timer is destroyed (when it falls out of scope). - pub fn oneshot(&mut self, msecs: u64) -> PortOne<()> { - self.obj.oneshot(msecs) - } - - /// Creates a port which will have a continuous stream of notifications - /// being sent every `msecs` milliseconds. This does *not* block the - /// current task, but instead returns immediately. The first notification - /// will not be received immediately, but rather after `msec` milliseconds - /// have passed. - /// - /// Note that this invalidates any previous port which has been created by - /// this timer, and that the returned port will be invalidated once the - /// timer is destroyed (when it falls out of scope). - pub fn periodic(&mut self, msecs: u64) -> Port<()> { - self.obj.period(msecs) - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use rt::test::*; - - #[test] - fn test_io_timer_sleep_simple() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - timer.sleep(1); - } - } - - #[test] - fn test_io_timer_sleep_oneshot() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - timer.oneshot(1).recv(); - } - } - - #[test] - fn test_io_timer_sleep_oneshot_forget() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - timer.oneshot(100000000000); - } - } - - #[test] - fn oneshot_twice() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - let port1 = timer.oneshot(10000); - let port = timer.oneshot(1); - port.recv(); - assert_eq!(port1.try_recv(), None); - } - } - - #[test] - fn test_io_timer_oneshot_then_sleep() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - let port = timer.oneshot(100000000000); - timer.sleep(1); // this should invalidate the port - - assert_eq!(port.try_recv(), None); - } - } - - #[test] - fn test_io_timer_sleep_periodic() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - let port = timer.periodic(1); - port.recv(); - port.recv(); - port.recv(); - } - } - - #[test] - fn test_io_timer_sleep_periodic_forget() { - do run_in_mt_newsched_task { - let mut timer = Timer::new().unwrap(); - timer.periodic(100000000000); - } - } - - #[test] - fn test_io_timer_sleep_standalone() { - do run_in_mt_newsched_task { - sleep(1) - } - } -} |
