For example, it re-exports `println` which is defined in `std::io::println`:
~~~
-use puts = std::rt::io::stdio::println;
+use puts = std::io::stdio::println;
fn main() {
println("println is imported per default.");
puts("Doesn't hinder you from importing it under an different name yourself.");
- ::std::rt::io::stdio::println("Or from not using the automatic import.");
+ ::std::io::stdio::println("Or from not using the automatic import.");
}
~~~
use std::os;
use std::rt;
-use std::rt::io::fs;
+use std::io::fs;
use extra::getopts;
use extra::getopts::groups::{optopt, optflag, reqopt};
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rt::io::buffered::BufferedReader;
-use std::rt::io::File;
+use std::io::buffered::BufferedReader;
+use std::io::File;
pub struct ExpectedError { line: uint, kind: ~str, msg: ~str }
}
fn iter_header(testfile: &Path, it: &fn(&str) -> bool) -> bool {
- use std::rt::io::buffered::BufferedReader;
- use std::rt::io::File;
+ use std::io::buffered::BufferedReader;
+ use std::io::File;
let mut rdr = BufferedReader::new(File::open(testfile).unwrap());
loop {
use std::os;
use std::run;
use std::str;
-use std::rt::io::process::ProcessExit;
+use std::io::process::ProcessExit;
#[cfg(target_os = "win32")]
fn target_env(lib_path: &str, prog: &str) -> ~[(~str,~str)] {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use common::mode_run_pass;
-use common::mode_run_fail;
+use common::config;
use common::mode_compile_fail;
use common::mode_pretty;
-use common::config;
+use common::mode_run_fail;
+use common::mode_run_pass;
use errors;
-use header::load_props;
use header::TestProps;
+use header::load_props;
use procsrv;
-use util;
use util::logv;
+use util;
-use std::rt::io;
-use std::rt::io::fs;
-use std::rt::io::File;
-use std::rt::io::process;
-use std::rt::io::process::ProcessExit;
+use std::io::File;
+use std::io::fs;
+use std::io::net::ip::{Ipv4Addr, SocketAddr};
+use std::io::net::tcp;
+use std::io::process::ProcessExit;
+use std::io::process;
+use std::io::timer;
+use std::io;
use std::os;
use std::str;
-use std::vec;
-use std::rt::io::net::tcp;
-use std::rt::io::net::ip::{Ipv4Addr, SocketAddr};
use std::task;
-use std::rt::io::timer;
+use std::vec;
use extra::test::MetricMap;
d.write("extern mod extra;\n")
d.write("extern mod run_pass_stage2;\n")
d.write("use run_pass_stage2::*;\n")
-d.write("use std::rt::io;\n");
-d.write("use std::rt::io::Writer;\n");
+d.write("use std::io;\n");
+d.write("use std::io::Writer;\n");
d.write("fn main() {\n");
d.write(" let mut out = io::stdout();\n");
i = 0
use std::cast::transmute;
use std::int;
use std::option::{None, Option, Some};
- use std::rt::io::extensions::u64_from_be_bytes;
+ use std::io::extensions::u64_from_be_bytes;
// ebml reading
use std::cast;
use std::clone::Clone;
- use std::rt::io;
- use std::rt::io::{Writer, Seek};
- use std::rt::io::mem::MemWriter;
- use std::rt::io::extensions::u64_to_be_bytes;
+ use std::io;
+ use std::io::{Writer, Seek};
+ use std::io::mem::MemWriter;
+ use std::io::extensions::u64_to_be_bytes;
// ebml writing
pub struct Encoder {
use serialize::Encodable;
use serialize;
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
+ use std::io::Decorator;
+ use std::io::mem::MemWriter;
use std::option::{None, Option, Some};
#[test]
*/
use std::{os, path};
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
use std::path::is_sep;
use sort;
use std::cast::transmute;
use std::f64;
use std::hashmap::HashMap;
-use std::rt::io;
-use std::rt::io::Decorator;
-use std::rt::io::mem::MemWriter;
+use std::io;
+use std::io::Decorator;
+use std::io::mem::MemWriter;
use std::num;
use std::str;
use std::to_str;
use super::*;
- use std::rt::io;
+ use std::io;
use serialize::Decodable;
use treemap::TreeMap;
}
fn with_str_writer(f: &fn(@mut io::Writer)) -> ~str {
- use std::rt::io::mem::MemWriter;
- use std::rt::io::Decorator;
+ use std::io::mem::MemWriter;
+ use std::io::Decorator;
use std::str;
let m = @mut MemWriter::new();
use sort;
use std::cmp;
use std::hashmap;
-use std::rt::io;
+use std::io;
use std::num;
// NB: this can probably be rewritten in terms of num::Num
use stats::Summary;
use stats::write_5_number_summary;
use stats::write_boxplot;
- use std::rt::io;
+ use std::io;
use std::str;
fn check(samples: &[f64], summ: &Summary) {
#[test]
fn test_boxplot_nonpositive() {
fn t(s: &Summary, expected: ~str) {
- use std::rt::io::mem::MemWriter;
- use std::rt::io::Decorator;
+ use std::io::mem::MemWriter;
+ use std::io::Decorator;
let mut m = MemWriter::new();
write_boxplot(&mut m as &mut io::Writer, s, 30);
let out = str::from_utf8_owned(m.inner());
use std::os;
use std::rand::Rng;
use std::rand;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
/// A wrapper for a path to temporary directory implementing automatic
/// scope-pased deletion.
#[allow(missing_doc)];
-use std::rt::io;
+use std::io;
#[cfg(not(target_os = "win32"))] use std::os;
#[cfg(not(target_os = "win32"))] use terminfo::*;
use std::{vec, str};
use std::hashmap::HashMap;
-use std::rt::io;
+use std::io;
use super::super::TermInfo;
// These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
use std::{os, str};
use std::os::getenv;
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<~Path> {
use std::clone::Clone;
use std::comm::{stream, SharedChan, GenericPort, GenericChan};
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
use std::task;
use std::to_str::ToStr;
use std::f64;
#[test]
fn should_sort_failures_before_printing_them() {
- use std::rt::io;
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
+ use std::io;
+ use std::io::Decorator;
+ use std::io::mem::MemWriter;
use std::str;
fn dummy() {}
#[allow(missing_doc)];
-use std::rt::io::Reader;
-use std::rt::io::mem::BufReader;
+use std::io::Reader;
+use std::io::mem::BufReader;
use std::num;
use std::str;
#[allow(missing_doc)];
-use std::rt::io::{Reader, Seek};
-use std::rt::io::mem::BufReader;
+use std::io::{Reader, Seek};
+use std::io::mem::BufReader;
use std::cmp::Eq;
use std::hashmap::HashMap;
use std::to_bytes;
use std::str;
use std::rand;
use std::num::Zero;
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
+ use std::io::Decorator;
+ use std::io::mem::MemWriter;
#[test]
fn test_new_nil() {
use std::cell::Cell;
use std::comm::{PortOne, oneshot};
use std::{str, task};
-use std::rt::io;
-use std::rt::io::{File, Decorator};
-use std::rt::io::mem::MemWriter;
+use std::io;
+use std::io::{File, Decorator};
+use std::io::mem::MemWriter;
/**
*
#[test]
fn test() {
use std::{os, run};
- use std::rt::io::fs;
+ use std::io::fs;
use std::str::from_utf8_owned;
// Create a path to a new file 'filename' in the directory in which
use std::run;
use std::str;
use std::vec;
-use std::rt::io::fs;
+use std::io::fs;
use syntax::abi;
use syntax::ast;
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
}
fn is_writeable(p: &Path) -> bool {
- use std::rt::io;
+ use std::io;
!p.exists() ||
(match io::result(|| p.stat()) {
use util::ppaux;
use std::hashmap::{HashMap,HashSet};
-use std::rt::io;
-use std::rt::io::fs;
-use std::rt::io::mem::MemReader;
+use std::io;
+use std::io::fs;
+use std::io::mem::MemReader;
use std::os;
use std::vec;
use extra::getopts::groups::{optopt, optmulti, optflag, optflagopt};
use middle::lint;
use std::comm;
-use std::rt::io;
-use std::rt::io::Reader;
+use std::io;
+use std::io::Reader;
use std::num;
use std::os;
use std::result;
use std::at_vec;
use std::u64;
-use std::rt::io;
-use std::rt::io::extensions::u64_from_be_bytes;
+use std::io;
+use std::io::extensions::u64_from_be_bytes;
use std::option;
use std::str;
use std::vec;
use middle;
use std::hashmap::{HashMap, HashSet};
-use std::rt::io::{Writer, Seek, Decorator};
-use std::rt::io::mem::MemWriter;
+use std::io::{Writer, Seek, Decorator};
+use std::io::mem::MemWriter;
use std::str;
use std::vec;
use std::option;
use std::os;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
use std::hashmap::HashSet;
pub enum FileMatch { FileMatches, FileDoesntMatch }
use std::c_str::ToCStr;
use std::cast;
-use std::rt::io;
+use std::io;
use std::num;
use std::option;
use std::os::consts::{macos, freebsd, linux, android, win32};
// Type encoding
use std::hashmap::HashMap;
-use std::rt::io;
-use std::rt::io::{Decorator, Writer, Seek};
-use std::rt::io::mem::MemWriter;
+use std::io;
+use std::io::{Decorator, Writer, Seek};
+use std::io::mem::MemWriter;
use std::str;
use std::fmt;
use std::at_vec;
use std::libc;
use std::cast;
-use std::rt::io::Seek;
+use std::io::Seek;
use extra::ebml::reader;
use extra::ebml;
#[cfg(test)]
fn roundtrip(in_item: Option<@ast::item>) {
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
+ use std::io::Decorator;
+ use std::io::mem::MemWriter;
let in_item = in_item.unwrap();
let wr = @mut MemWriter::new();
use std::cast;
-use std::rt::io;
+use std::io;
use std::uint;
use std::vec;
use std::hashmap::HashMap;
use std::cast::transmute;
use std::hashmap::HashMap;
-use std::rt::io;
+use std::io;
use std::str;
use std::to_str;
use std::uint;
use std::fmt;
use std::local_data;
-use std::rt::io;
+use std::io;
use syntax::ast;
use syntax::ast_util;
// except according to those terms.
use std::fmt;
-use std::rt::io;
+use std::io;
#[deriving(Clone)]
pub struct Layout {
use std::fmt;
use std::libc;
-use std::rt::io;
+use std::io;
use std::vec;
/// A unit struct which has the `fmt::Default` trait implemented. When
use std::fmt;
use std::hashmap::{HashMap, HashSet};
use std::local_data;
-use std::rt::io::buffered::BufferedWriter;
-use std::rt::io;
-use std::rt::io::fs;
-use std::rt::io::File;
+use std::io::buffered::BufferedWriter;
+use std::io;
+use std::io::fs;
+use std::io::File;
use std::os;
use std::str;
use std::task;
use std::cell::Cell;
use std::local_data;
-use std::rt::io;
-use std::rt::io::File;
-use std::rt::io::mem::MemWriter;
-use std::rt::io::Decorator;
+use std::io;
+use std::io::File;
+use std::io::mem::MemWriter;
+use std::io::Decorator;
use std::str;
use extra::getopts;
use extra::getopts::groups;
// Useful conditions
-pub use std::path::Path;
pub use package_id::PkgId;
-pub use std::rt::io::FileStat;
-pub use std::rt::io::process::ProcessExit;
+pub use std::io::FileStat;
+pub use std::io::process::ProcessExit;
+pub use std::path::Path;
condition! {
pub bad_path: (Path, ~str) -> Path;
use rustc::metadata::filesearch::rust_path;
use path_util::*;
use std::os;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool {
let workspaces = rust_path();
extern mod syntax;
use std::{os, result, run, str, task};
-use std::rt::io::process;
+use std::io::process;
use std::hashmap::HashSet;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
pub use std::path::Path;
use extra::workcache;
// except according to those terms.
use extra::term;
-use std::rt::io;
+use std::io;
pub fn note(msg: &str) {
pretty_message(msg, "note: ", term::color::GREEN,
use target::*;
use package_id::PkgId;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
use std::os;
use context::*;
use crate::Crate;
use std::libc;
use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use std::os;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
use messages::*;
pub fn default_workspace() -> Path {
use std::{run, str};
use std::run::{ProcessOutput, ProcessOptions, Process};
-use std::rt::io::fs;
+use std::io::fs;
use extra::tempfile::TempDir;
use version::*;
use path_util::chmod_read_only;
use context::{BuildContext, Context, RustcFlags};
use std::{os, run, str, task};
-use std::rt::io;
-use std::rt::io::fs;
-use std::rt::io::File;
-use std::rt::io::process;
-use std::rt::io::process::ProcessExit;
+use std::io;
+use std::io::fs;
+use std::io::File;
+use std::io::process;
+use std::io::process::ProcessExit;
use extra::arc::Arc;
use extra::arc::RWArc;
use extra::tempfile::TempDir;
extern mod rustc;
use std::os;
-use std::rt::io::File;
+use std::io::File;
use rustpkg::api;
use rustpkg::version::NoVersion;
use std::libc;
use std::os;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
use extra::workcache;
use rustc::driver::{driver, session};
use extra::getopts::groups::getopts;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
use extra::workcache;
use sha1::{Digest, Sha1};
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use ai = std::rt::io::net::addrinfo;
+use ai = std::io::net::addrinfo;
use std::libc::c_int;
use std::ptr::null;
use std::rt::BlockedTask;
#[cfg(test)]
mod test {
- use std::rt::io::net::ip::{SocketAddr, Ipv4Addr};
+ use std::io::net::ip::{SocketAddr, Ipv4Addr};
use super::*;
use super::super::local_loop;
use std::libc::{c_int, c_char, c_void, size_t};
use std::libc;
use std::rt::BlockedTask;
-use std::rt::io::{FileStat, IoError};
-use std::rt::io;
+use std::io::{FileStat, IoError};
+use std::io;
use std::rt::local::Local;
use std::rt::rtio;
use std::rt::sched::{Scheduler, SchedHandle};
mod test {
use std::libc::c_int;
use std::libc::{O_CREAT, O_RDWR, O_RDONLY, S_IWUSR, S_IRUSR};
- use std::rt::io;
+ use std::io;
use std::str;
use std::vec;
use super::*;
use std::unstable::finally::Finally;
use std::vec;
-use std::rt::io::IoError;
+use std::io::IoError;
pub use self::async::AsyncWatcher;
pub use self::file::{FsRequest, FileWatcher};
unsafe {
// Importing error constants
use uvll::*;
- use std::rt::io::*;
+ use std::io::*;
// uv error descriptions are static
let c_desc = uvll::uv_strerror(*uverr);
)
pub fn dumb_println(args: &fmt::Arguments) {
- use std::rt::io::native::stdio::stderr;
- use std::rt::io::Writer;
+ use std::io::native::stdio::stderr;
+ use std::io::Writer;
let mut out = stderr();
fmt::writeln(&mut out as &mut Writer, args);
use std::libc::{size_t, ssize_t, c_int, c_void, c_uint, c_char};
use std::ptr;
use std::rt::BlockedTask;
-use std::rt::io::IoError;
-use std::rt::io::net::ip::{Ipv4Addr, Ipv6Addr, SocketAddr, IpAddr};
+use std::io::IoError;
+use std::io::net::ip::{Ipv4Addr, Ipv6Addr, SocketAddr, IpAddr};
use std::rt::local::Local;
use std::rt::rtio;
use std::rt::sched::{Scheduler, SchedHandle};
use std::c_str::CString;
use std::libc;
use std::rt::BlockedTask;
-use std::rt::io::IoError;
+use std::io::IoError;
use std::rt::local::Local;
use std::rt::rtio::{RtioPipe, RtioUnixListener, RtioUnixAcceptor};
use std::rt::sched::{Scheduler, SchedHandle};
use std::libc;
use std::ptr;
use std::rt::BlockedTask;
-use std::rt::io::IoError;
-use std::rt::io::process::*;
+use std::io::IoError;
+use std::io::process::*;
use std::rt::local::Local;
use std::rt::rtio::RtioProcess;
use std::rt::sched::{Scheduler, SchedHandle};
// except according to those terms.
use std::libc::c_int;
-use std::rt::io::signal::Signum;
+use std::io::signal::Signum;
use std::rt::sched::{SchedHandle, Scheduler};
use std::comm::{SharedChan, SendDeferred};
use std::rt::local::Local;
use super::*;
use std::cell::Cell;
use super::super::local_loop;
- use std::rt::io::signal;
+ use std::io::signal;
use std::comm::{SharedChan, stream};
#[test]
// except according to those terms.
use std::libc;
-use std::rt::io::IoError;
+use std::io::IoError;
use std::rt::local::Local;
use std::rt::rtio::RtioTTY;
use std::rt::sched::{Scheduler, SchedHandle};
use std::libc::c_int;
use std::libc;
use std::path::Path;
-use std::rt::io::IoError;
-use std::rt::io::net::ip::SocketAddr;
-use std::rt::io::process::ProcessConfig;
-use std::rt::io;
+use std::io::IoError;
+use std::io::net::ip::SocketAddr;
+use std::io::process::ProcessConfig;
+use std::io;
use std::rt::local::Local;
use std::rt::rtio::*;
use std::rt::sched::{Scheduler, SchedHandle};
use std::rt::task::Task;
use std::libc::{O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY,
S_IRUSR, S_IWUSR};
-use std::rt::io::{FileMode, FileAccess, Open, Append, Truncate, Read, Write,
+use std::io::{FileMode, FileAccess, Open, Append, Truncate, Read, Write,
ReadWrite, FileStat};
-use std::rt::io::signal::Signum;
+use std::io::signal::Signum;
use std::util;
-use ai = std::rt::io::net::addrinfo;
+use ai = std::io::net::addrinfo;
#[cfg(test)] use std::unstable::run_in_bare_thread;
```rust
format! // described above
-write! // first argument is a &mut rt::io::Writer, the destination
+write! // first argument is a &mut io::Writer, the destination
writeln! // same as write but appends a newline
print! // the format string is printed to the standard output
println! // same as print but appends a newline
actually invoking the `write` function defined in this module. Example usage is:
```rust
-use std::rt::io;
+use std::io;
let mut w = io::mem::MemWriter::new();
write!(&mut w as &mut io::Writer, "Hello {}!", "world");
use cast;
use char::Char;
-use rt::io::Decorator;
-use rt::io::mem::MemWriter;
-use rt::io;
+use io::Decorator;
+use io::mem::MemWriter;
+use io;
use str;
use repr;
use util;
use container::Container;
use iter::Iterator;
use option::{Some, None};
-use rt::io::Writer;
+use io::Writer;
use str::OwnedStr;
use to_bytes::IterBytes;
use vec::ImmutableVector;
--- /dev/null
+// 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 io;
+ 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 io::Writer for S {
+ fn write(&mut self, _: &[u8]) {}
+ }
+
+ impl 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]);
+ }
+}
--- /dev/null
+// 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!() }
+}
--- /dev/null
+// 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 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 io::mem::{MemReader, MemWriter};
+ use io::{Reader, io_error, placeholder_error};
+ use vec::ImmutableVector;
+
+ 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);
+ }
+
+}
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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::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::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 clone::Clone;
+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 io;
+use option::{Some, None, Option};
+use result::{Ok, Err, Result};
+use path;
+use path::{Path, GenericPath};
+use vec::{OwnedVector, ImmutableVector};
+
+/// 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::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::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::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::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::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::io;
+/// use std::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::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::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::io;
+/// use std::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::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::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::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 io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, ReadWrite};
+ use 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);
+ }
+}
--- /dev/null
+// 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 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]);
+ }
+}
--- /dev/null
+// 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.
+
+# 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
+
+*/
+
+#[allow(missing_doc)];
+
+use cast;
+use container::Container;
+use int;
+use iter::Iterator;
+use option::{Option, Some, None};
+use path::Path;
+use result::{Ok, Err, Result};
+use str::{StrSlice, OwnedStr};
+use to_str::ToStr;
+use uint;
+use unstable::finally::Finally;
+use vec::{OwnedVector, MutableVector};
+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;
--- /dev/null
+// 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 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 io::fs::{mkdir_recursive};
+ use 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 io::fs::mkdir_recursive;
+ use 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"))));
+ }
+}
--- /dev/null
+// 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 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
+}
--- /dev/null
+// 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 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() }
+}
--- /dev/null
+// 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 io::{io_error};
+use io::net::ip::{SocketAddr, IpAddr};
+use rt::rtio::{IoFactory, with_local_io};
+use vec::ImmutableVector;
+
+/// 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 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);
+ }
+}
--- /dev/null
+// 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 container::Container;
+use from_str::FromStr;
+use option::{Option, None, Some};
+use to_str::ToStr;
+use vec::{MutableCloneableVector, ImmutableVector};
+
+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");
+ }
+
+}
--- /dev/null
+// 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;
--- /dev/null
+// 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 io::net::ip::SocketAddr;
+use io::{Reader, Writer, Listener, Acceptor};
+use 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) => {
+ debug!("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) => {
+ debug!("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) => {
+ debug!("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 io::net::ip::{Ipv4Addr, SocketAddr};
+ use 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);
+ debug!("read");
+ }
+ }
+ }
+
+ port.recv();
+ connect(0, addr);
+
+ fn connect(i: int, addr: SocketAddr) {
+ if i == MAX { return }
+
+ do spawntask {
+ debug!("connecting");
+ let mut stream = TcpStream::connect(addr);
+ // Connect again before writing
+ connect(i + 1, addr);
+ debug!("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);
+ debug!("read");
+ }
+ }
+ }
+
+ port.recv();
+ connect(0, addr);
+
+ fn connect(i: int, addr: SocketAddr) {
+ if i == MAX { return }
+
+ do spawntask {
+ debug!("connecting");
+ let mut stream = TcpStream::connect(addr);
+ // Connect again before writing
+ connect(i + 1, addr);
+ debug!("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);
+ debug!("read");
+ }
+ }
+ }
+
+ port.recv();
+ connect(0, addr);
+
+ fn connect(i: int, addr: SocketAddr) {
+ if i == MAX { return }
+
+ do spawntask_later {
+ debug!("connecting");
+ let mut stream = TcpStream::connect(addr);
+ // Connect again before writing
+ connect(i + 1, addr);
+ debug!("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);
+ debug!("read");
+ }
+ }
+ }
+
+ port.recv();
+ connect(0, addr);
+
+ fn connect(i: int, addr: SocketAddr) {
+ if i == MAX { return }
+
+ do spawntask_later {
+ debug!("connecting");
+ let mut stream = TcpStream::connect(addr);
+ // Connect again before writing
+ connect(i + 1, addr);
+ debug!("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());
+ }
+
+}
--- /dev/null
+// 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 io::net::ip::SocketAddr;
+use io::{Reader, Writer};
+use 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) => {
+ debug!("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 io::net::ip::{Ipv4Addr, SocketAddr};
+ use 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());
+ }
+}
--- /dev/null
+// 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 io::pipe::PipeStream;
+use 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::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::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 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());
+ }
+ }
+}
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+// 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 io::{io_error, EndOfFile};
+use 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::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);
+ }
+ }
+ }
+}
--- /dev/null
+// 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 io;
+use 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.
--- /dev/null
+// 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 clone::Clone;
+use comm::{Port, SharedChan, stream};
+use container::{Map, MutableMap};
+use hashmap;
+use io::io_error;
+use option::{Some, None};
+use result::{Err, Ok};
+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::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 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 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);
+ }
+}
--- /dev/null
+// 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::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 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();
+ }
+}
--- /dev/null
+// 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::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 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) => {
+ debug!("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)
+ }
+ }
+}
//! boxes (`owned`), and unsafe and borrowed pointers (`ptr`, `borrowed`).
//! Additionally, `std` provides pervasive types (`option` and `result`),
//! task creation and communication primitives (`task`, `comm`), platform
-//! abstractions (`os` and `path`), basic I/O abstractions (`rt::io`), common
+//! abstractions (`os` and `path`), basic I/O abstractions (`io`), common
//! traits (`kinds`, `ops`, `cmp`, `num`, `to_str`), and complete bindings
//! to the C standard library (`libc`).
//!
#[allow(cstack)]; // NOTE: remove after the next snapshot.
// When testing libstd, bring in libuv as the I/O backend so tests can print
-// things and all of the std::rt::io tests have an I/O interface to run on top
+// things and all of the std::io tests have an I/O interface to run on top
// of
#[cfg(test)] extern mod rustuv(vers = "0.9-pre");
pub mod libc;
pub mod c_str;
pub mod os;
+pub mod io;
pub mod path;
pub mod rand;
pub mod run;
pub use logging;
pub use option;
pub use os;
+ pub use io;
pub use rt;
pub use str;
pub use to_bytes;
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
fn load_self() -> Option<~[u8]> {
- use std::rt::io;
+ use std::io;
match io::result(|| io::fs::readlink(&Path::new("/proc/self/exe"))) {
Ok(Some(path)) => Some(path.as_vec().to_owned()),
use result::{Ok, Err};
use os::*;
use libc::*;
- use rt::io;
- use rt::io::fs;
+ use io;
+ use io::fs;
#[cfg(unix)]
fn lseek_(fd: c_int, size: uint) {
// Reexported functions
pub use from_str::from_str;
pub use iter::range;
-pub use rt::io::stdio::{print, println};
+pub use io::stdio::{print, println};
// Reexported types and traits
pub use num::{Primitive, Int, Float, ToStrRadix, ToPrimitive, FromPrimitive};
pub use path::{GenericPath, Path, PosixPath, WindowsPath};
pub use ptr::RawPtr;
-pub use rt::io::{Writer, Reader, Seek};
+pub use io::{Writer, Reader, Seek};
pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr};
pub use str::{Str, StrVector, StrSlice, OwnedStr};
pub use to_bytes::IterBytes;
#[cfg(unix)]
use rand::reader::ReaderRng;
#[cfg(unix)]
-use rt::io::File;
+use io::File;
#[cfg(windows)]
use cast;
//! A wrapper around any Reader to treat it as an RNG.
use option::{Some, None};
-use rt::io::Reader;
+use io::Reader;
use rand::Rng;
///
/// ```rust
/// use std::rand::{reader, Rng};
-/// use std::rt::io::mem;
+/// use std::io::mem;
///
/// fn main() {
/// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
#[cfg(test)]
mod test {
use super::*;
- use rt::io::mem::MemReader;
+ use io::mem::MemReader;
use cast;
#[test]
use cast::transmute;
use char;
use container::Container;
-use rt::io;
+use io;
use iter::Iterator;
use libc::c_void;
use option::{Some, None};
pub fn repr_to_str<T>(t: &T) -> ~str {
use str;
- use rt::io;
- use rt::io::Decorator;
+ use io;
+ use io::Decorator;
let mut result = io::mem::MemWriter::new();
write_repr(&mut result as &mut io::Writer, t);
use prelude::*;
use str;
use str::Str;
- use rt::io::Decorator;
+ use io::Decorator;
use util::swap;
use char::is_alphabetic;
+++ /dev/null
-// 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]);
- }
-}
+++ /dev/null
-// 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!() }
-}
+++ /dev/null
-// 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);
- }
-
-}
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-// 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]);
- }
-}
+++ /dev/null
-// 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;
+++ /dev/null
-// 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"))));
- }
-}
+++ /dev/null
-// 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
-}
+++ /dev/null
-// 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() }
-}
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-// 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");
- }
-
-}
+++ /dev/null
-// 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;
+++ /dev/null
-// 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());
- }
-
-}
+++ /dev/null
-// 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());
- }
-}
+++ /dev/null
-// 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());
- }
- }
-}
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-// 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);
- }
- }
- }
-}
+++ /dev/null
-// 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.
+++ /dev/null
-// 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);
- }
-}
+++ /dev/null
-// 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();
- }
-}
+++ /dev/null
-// 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)
- }
- }
-}
use from_str::from_str;
use libc::exit;
use option::{Some, None, Option};
-use rt::io;
-use rt::io::stdio::StdWriter;
-use rt::io::buffered::LineBufferedWriter;
+use io;
+use io::stdio::StdWriter;
+use io::buffered::LineBufferedWriter;
use rt::crate_map::{ModEntry, CrateMap, iter_crate_map, get_crate_map};
use str::StrSlice;
use u32;
/// The coroutine task scheduler, built on the `io` event loop.
pub mod sched;
-/// Synchronous I/O.
-pub mod io;
+#[cfg(stage0)]
+pub mod io {
+ pub use io::stdio;
+}
/// The EventLoop and internal synchronous I/O interface.
pub mod rtio;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use c_str::CString;
+use comm::{SharedChan, PortOne, Port};
+use libc::c_int;
use libc;
use option::*;
+use path::Path;
use result::*;
-use comm::{SharedChan, PortOne, Port};
-use libc::c_int;
-use c_str::CString;
-use ai = rt::io::net::addrinfo;
-use rt::io::IoError;
-use rt::io::signal::Signum;
-use super::io::process::{ProcessConfig, ProcessExit};
-use super::io::net::ip::{IpAddr, SocketAddr};
-use path::Path;
-use super::io::{SeekStyle};
-use super::io::{FileMode, FileAccess, FileStat, FilePermission};
+use ai = io::net::addrinfo;
+use io::IoError;
+use io::net::ip::{IpAddr, SocketAddr};
+use io::process::{ProcessConfig, ProcessExit};
+use io::signal::Signum;
+use io::{FileMode, FileAccess, FileStat, FilePermission};
+use io::{SeekStyle};
pub trait Callback {
fn call(&mut self);
pub fn with_local_io<T>(f: &fn(&mut IoFactory) -> Option<T>) -> Option<T> {
use rt::sched::Scheduler;
use rt::local::Local;
- use rt::io::{io_error, standard_error, IoUnavailable};
+ use io::{io_error, standard_error, IoUnavailable};
unsafe {
let sched: *mut Scheduler = Local::unsafe_borrow();
#[test]
fn test_io_callback() {
- use rt::io::timer;
+ use io::timer;
// This is a regression test that when there are no schedulable tasks
// in the work queue, but we are performing I/O, that once we do put
use rt::context::Context;
use rt::context;
use rt::env;
-use rt::io::Writer;
+use io::Writer;
use rt::kill::Death;
use rt::local::Local;
use rt::logging::StdErrLogger;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
+use io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use cell::Cell;
use clone::Clone;
}
pub fn dumb_println(args: &fmt::Arguments) {
- use rt::io::native::stdio::stderr;
- use rt::io::{Writer, io_error, ResourceUnavailable};
+ use io::native::stdio::stderr;
+ use io::{Writer, io_error, ResourceUnavailable};
use rt::task::Task;
use rt::local::Local;
use cell::Cell;
use comm::{stream, SharedChan};
+use io::Reader;
+use io::process::ProcessExit;
+use io::process;
+use io;
use libc::{pid_t, c_int};
use libc;
use prelude::*;
-use rt::io::process;
-use rt::io::process::ProcessExit;
-use rt::io;
-use rt::io::Reader;
use task;
/**
use str;
use task::spawn;
use unstable::running_on_valgrind;
- use rt::io::native::file;
- use rt::io::{Writer, Reader};
+ use io::native::file;
+ use io::{Writer, Reader};
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)
impl<A:IterBytes> ToBytes for A {
fn to_bytes(&self, lsb0: bool) -> ~[u8] {
- use rt::io::mem;
- use rt::io::Writer;
+ use io::mem;
+ use io::Writer;
do mem::with_mem_writer |wr| {
do self.iter_bytes(lsb0) |bytes| {
use codemap::{Pos, Span};
use codemap;
-use std::rt::io;
+use std::io;
use std::local_data;
use extra::term;
format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
))
macro_rules! print (
- ($($arg:tt)*) => (format_args!(::std::rt::io::stdio::print_args, $($arg)*))
+ ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*))
)
macro_rules! println (
- ($($arg:tt)*) => (format_args!(::std::rt::io::stdio::println_args, $($arg)*))
+ ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*))
)
macro_rules! local_data_key (
}
fn fake_print_crate(crate: &ast::Crate) {
- let out = @mut std::rt::io::stderr() as @mut std::rt::io::Writer;
+ let out = @mut std::io::stderr() as @mut std::io::Writer;
let s = pprust::rust_printer(out, get_ident_interner());
pprust::print_crate_(s, crate);
}
use parse::token::{get_ident_interner};
use print::pprust;
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
use std::str;
// These macros all relate to the file system; they either return
use parse::token;
use parse::token::{get_ident_interner};
-use std::rt::io;
+use std::io;
use std::str;
use std::uint;
use parse::lexer::reader;
use parse::parser::Parser;
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
use std::str;
pub mod lexer;
use super::*;
use extra::serialize::Encodable;
use extra;
- use std::rt::io;
- use std::rt::io::Decorator;
- use std::rt::io::mem::MemWriter;
+ use std::io;
+ use std::io::Decorator;
+ use std::io::mem::MemWriter;
use std::str;
use codemap::{Span, BytePos, Spanned};
use opt_vec;
* avoid combining it with other lines and making matters even worse.
*/
-use std::rt::io;
+use std::io;
use std::vec;
#[deriving(Clone, Eq)]
use std::char;
use std::str;
-use std::rt::io;
-use std::rt::io::Decorator;
-use std::rt::io::mem::MemWriter;
+use std::io;
+use std::io::Decorator;
+use std::io::mem::MemWriter;
// The @ps is stored here to prevent recursive type.
pub enum ann_node<'self> {
use std::str;
use std::util;
use std::vec;
-use std::rt::io::File;
+use std::io::File;
macro_rules! bench (
($argv:expr, $id:ident) => (maybe_run_test($argv, stringify!($id).to_owned(), $id))
}
fn read_line() {
- use std::rt::io::buffered::BufferedReader;
+ use std::io::buffered::BufferedReader;
let mut path = Path::new(env!("CFG_SRC_DIR"));
path.push("src/test/bench/shootout-k-nucleotide.data");
extern mod extra;
use std::int;
-use std::rt::io;
-use std::rt::io::File;
+use std::io;
+use std::io::File;
use std::os;
use std::rand::Rng;
use std::rand;
use std::hashmap::HashMap;
use std::option;
use std::os;
-use std::rt::io;
+use std::io;
use std::str;
use std::task;
use std::util;
// given a FASTA file on stdin, process sequence THREE
fn main() {
- use std::rt::io::Reader;
- use std::rt::io::native::stdio;
- use std::rt::io::mem::MemReader;
- use std::rt::io::buffered::BufferedReader;
+ use std::io::Reader;
+ use std::io::native::stdio;
+ use std::io::mem::MemReader;
+ use std::io::buffered::BufferedReader;
let rdr = if os::getenv("RUST_BENCH").is_some() {
let foo = include_bin!("shootout-k-nucleotide.data");
extern mod extra;
-use std::rt::io;
-use std::rt::io::stdio::StdReader;
-use std::rt::io::buffered::BufferedReader;
+use std::io;
+use std::io::stdio::StdReader;
+use std::io::buffered::BufferedReader;
use std::os;
use std::uint;
use std::unstable::intrinsics::cttz16;
use std::libc;
use std::run;
use std::str;
-use std::rt::io;
+use std::io;
#[test]
fn test_destroy_once() {
extern mod extra;
-use std::rt::io::mem::MemWriter;
-use std::rt::io::Decorator;
+use std::io::mem::MemWriter;
+use std::io::Decorator;
use std::rand::{random, Rand};
use extra::serialize::{Encodable, Decodable};
use extra::ebml;
use extra::tempfile::TempDir;
use std::unstable::finally::Finally;
use std::{os, unstable};
-use std::rt::io;
+use std::io;
pub fn main() {
fn mk_file(path: &str, directory: bool) {
#[deny(warnings)];
use std::fmt;
-use std::rt::io::Decorator;
-use std::rt::io::mem::MemWriter;
-use std::rt::io;
-use std::rt::io::Writer;
+use std::io::Decorator;
+use std::io::mem::MemWriter;
+use std::io;
+use std::io::Writer;
use std::str;
struct A;
extern mod extra;
-use std::rt::io;
+use std::io;
use std::to_str;
enum square {
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rt::io;
+use std::io;
pub fn main() {
let stdout = &mut io::stdout() as &mut io::Writer;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rt::io;
+use std::io;
fn foo(a: &mut io::Writer) {
a.write([])
use extra::tempfile::TempDir;
use std::os;
use std::libc;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
fn rename_directory() {
unsafe {
//
// See #9341
-use std::rt::io;
-use std::rt::io::process;
-use std::rt::io::process::{Process, ProcessConfig, CreatePipe, Ignored};
+use std::io;
+use std::io::process;
+use std::io::process::{Process, ProcessConfig, CreatePipe, Ignored};
use std::str;
#[test]
// xfail-fast
use std::{os, run};
-use std::rt::io::process;
+use std::io::process;
fn main() {
let args = os::args();
extern mod extra;
use extra::tempfile;
-use std::rt::io::File;
+use std::io::File;
pub fn main() {
let dir = tempfile::TempDir::new_in(&Path::new("."), "").unwrap();
use std::os;
use std::task;
use std::cell::Cell;
-use std::rt::io;
-use std::rt::io::fs;
+use std::io;
+use std::io::fs;
fn test_tempdir() {
let path = {