--- /dev/null
+# i686-unknown-freebsd configuration
+CC_i686-unknown-freebsd=$(CC)
+CXX_i686-unknown-freebsd=$(CXX)
+CPP_i686-unknown-freebsd=$(CPP)
+AR_i686-unknown-freebsd=$(AR)
+CFG_LIB_NAME_i686-unknown-freebsd=lib$(1).so
+CFG_STATIC_LIB_NAME_i686-unknown-freebsd=lib$(1).a
+CFG_LIB_GLOB_i686-unknown-freebsd=lib$(1)-*.so
+CFG_LIB_DSYM_GLOB_i686-unknown-freebsd=$(1)-*.dylib.dSYM
+CFG_JEMALLOC_CFLAGS_i686-unknown-freebsd := -m32 -arch i386 -I/usr/local/include $(CFLAGS)
+CFG_GCCISH_CFLAGS_i686-unknown-freebsd := -Wall -Werror -g -fPIC -m32 -arch i386 -I/usr/local/include $(CFLAGS)
+CFG_GCCISH_LINK_FLAGS_i686-unknown-freebsd := -m32 -shared -fPIC -g -pthread -lrt
+CFG_GCCISH_DEF_FLAG_i686-unknown-freebsd := -Wl,--export-dynamic,--dynamic-list=
+CFG_LLC_FLAGS_i686-unknown-freebsd :=
+CFG_INSTALL_NAME_i686-unknown-freebsd =
+CFG_EXE_SUFFIX_i686-unknown-freebsd :=
+CFG_WINDOWSY_i686-unknown-freebsd :=
+CFG_UNIXY_i686-unknown-freebsd := 1
+CFG_LDPATH_i686-unknown-freebsd :=
+CFG_RUN_i686-unknown-freebsd=$(2)
+CFG_RUN_TARG_i686-unknown-freebsd=$(call CFG_RUN_i686-unknown-freebsd,,$(2))
+CFG_GNU_TRIPLE_i686-unknown-freebsd := i686-unknown-freebsd
/// The following two examples are equivalent:
///
/// ```
-/// # #![feature(box_heap)]
+/// #![feature(box_heap)]
/// #![feature(box_syntax)]
/// use std::boxed::HEAP;
///
///
/// # Examples
/// ```
-/// # #![feature(box_raw)]
+/// #![feature(box_raw)]
/// use std::boxed;
///
/// let seventeen = Box::new(17u32);
use core::str as core_str;
use core::str::pattern::Pattern;
use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher};
+use core::mem;
use rustc_unicode::str::{UnicodeStr, Utf16Encoder};
use vec_deque::VecDeque;
use rustc_unicode;
use vec::Vec;
use slice::SliceConcatExt;
+use boxed::Box;
pub use core::str::{FromStr, Utf8Error};
pub use core::str::{Lines, LinesAny, CharRange};
pub use rustc_unicode::str::{SplitWhitespace, Words, Graphemes, GraphemeIndices};
pub use core::str::pattern;
-/*
-Section: Creating a string
-*/
-
impl<S: Borrow<str>> SliceConcatExt<str> for [S] {
type Output = String;
}
}
-/*
-Section: Iterators
-*/
-
// Helper functions used for Unicode normalization
fn canonical_sort(comb: &mut [(char, u8)]) {
let len = comb.len();
fn size_hint(&self) -> (usize, Option<usize>) { self.encoder.size_hint() }
}
-/*
-Section: Misc
-*/
-
// Return the initial codepoint accumulator for the first byte.
// The first byte is special, only want bottom 5 bits for width 2, 4 bits
// for width 3, and 3 bits for width 4
}
}
-/*
-Section: CowString
-*/
-
-/*
-Section: Trait implementations
-*/
-
-
/// Any string that can be represented as a slice.
#[lang = "str"]
#[cfg(not(test))]
pub fn escape_unicode(&self) -> String {
self.chars().flat_map(|c| c.escape_unicode()).collect()
}
+
+ /// Converts the `Box<str>` into a `String` without copying or allocating.
+ #[unstable(feature = "box_str",
+ reason = "recently added, matches RFC")]
+ pub fn into_string(self: Box<str>) -> String {
+ unsafe {
+ let slice = mem::transmute::<Box<str>, Box<[u8]>>(self);
+ String::from_utf8_unchecked(slice.into_vec())
+ }
+ }
}
use range::RangeArgument;
use str::{self, FromStr, Utf8Error, Chars};
use vec::{DerefVec, Vec, as_vec};
+use boxed::Box;
/// A growable string stored as a UTF-8 encoded buffer.
#[derive(Clone, PartialOrd, Eq, Ord)]
string: self_ptr,
}
}
+
+ /// Converts the string into `Box<str>`.
+ ///
+ /// Note that this will drop any excess capacity.
+ #[unstable(feature = "box_str",
+ reason = "recently added, matches RFC")]
+ pub fn into_boxed_slice(self) -> Box<str> {
+ let slice = self.vec.into_boxed_slice();
+ unsafe { mem::transmute::<Box<[u8]>, Box<str>>(slice) }
+ }
}
impl FromUtf8Error {
ptr::write(self.ptr.offset(off as isize), t);
}
- /// Returns true iff the buffer is at capacity
+ /// Returns true if and only if the buffer is at capacity
#[inline]
fn is_full(&self) -> bool { self.cap - self.len() == 1 }
#![feature(str_escape)]
#![feature(str_match_indices)]
#![feature(str_utf16)]
+#![feature(box_str)]
#![feature(subslice_offset)]
#![feature(test)]
#![feature(unboxed_closures)]
assert_eq!("aéDžßfiᾀ".to_uppercase(), "AÉDŽSSFIἈΙ");
}
+#[test]
+fn test_into_string() {
+ // The only way to acquire a Box<str> in the first place is through a String, so just
+ // test that we can round-trip between Box<str> and String.
+ let string = String::from("Some text goes here");
+ assert_eq!(string.clone().into_boxed_slice().into_string(), string);
+}
+
mod pattern {
use std::str::pattern::Pattern;
use std::str::pattern::{Searcher, ReverseSearcher};
assert_eq!(&a, "foobar");
}
+#[test]
+fn test_into_boxed_slice() {
+ let xs = String::from("hello my name is bob");
+ let ys = xs.into_boxed_slice();
+ assert_eq!(&*ys, "hello my name is bob");
+}
+
#[bench]
fn bench_with_capacity(b: &mut Bencher) {
b.iter(|| {
fn is_pretty(&self) -> bool {
self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0
}
+
+ /// Returns the wrapped `Formatter`.
+ #[unstable(feature = "debug_builder_formatter", reason = "recently added")]
+ pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
+ &mut self.fmt
+ }
}
struct DebugInner<'a, 'b: 'a> {
impl<$($name:Debug),*> Debug for ($($name,)*) {
#[allow(non_snake_case, unused_assignments)]
fn fmt(&self, f: &mut Formatter) -> Result {
- try!(write!(f, "("));
+ let mut builder = f.debug_tuple("");
let ($(ref $name,)*) = *self;
let mut n = 0;
$(
- if n > 0 {
- try!(write!(f, ", "));
- }
- try!(write!(f, "{:?}", *$name));
+ builder.field($name);
n += 1;
)*
+
if n == 1 {
- try!(write!(f, ","));
+ try!(write!(builder.formatter(), ","));
}
- write!(f, ")")
+
+ builder.finish()
}
}
peel! { $($name,)* }
acc
}
- /// Returns `true` iff `self == 2^k` for some `k`.
+ /// Returns `true` if and only if `self == 2^k` for some `k`.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn is_power_of_two(self) -> bool {
//! // The division was valid
//! Some(x) => println!("Result: {}", x),
//! // The division was invalid
-//! None => println!("Cannot divide by 0")
+//! None => println!("Cannot divide by 0"),
//! }
//! ```
//!
//! fn check_optional(optional: &Option<Box<i32>>) {
//! match *optional {
//! Some(ref p) => println!("have value {}", p),
-//! None => println!("have no value")
+//! None => println!("have no value"),
//! }
//! }
//! ```
//! // Take a reference to the contained string
//! match msg {
//! Some(ref m) => println!("{}", *m),
-//! None => ()
+//! None => (),
//! }
//!
//! // Remove the contained string, destroying the Option
//! let unwrapped_msg = match msg {
//! Some(m) => m,
-//! None => "default message"
+//! None => "default message",
//! };
//! ```
//!
//!
//! match name_of_biggest_animal {
//! Some(name) => println!("the biggest animal is {}", name),
-//! None => println!("there are no animals :(")
+//! None => println!("there are no animals :("),
//! }
//! ```
pub fn is_some(&self) -> bool {
match *self {
Some(_) => true,
- None => false
+ None => false,
}
}
pub fn as_ref<'r>(&'r self) -> Option<&'r T> {
match *self {
Some(ref x) => Some(x),
- None => None
+ None => None,
}
}
pub fn as_mut<'r>(&'r mut self) -> Option<&'r mut T> {
match *self {
Some(ref mut x) => Some(x),
- None => None
+ None => None,
}
}
pub fn unwrap_or(self, def: T) -> T {
match self {
Some(x) => x,
- None => def
+ None => def,
}
}
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Some(x) => x,
- None => f()
+ None => f(),
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> {
match self {
Some(x) => Some(f(x)),
- None => None
+ None => None,
}
}
pub fn map_or_else<U, D: FnOnce() -> U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U {
match self {
Some(t) => f(t),
- None => default()
+ None => default(),
}
}
pub fn or(self, optb: Option<T>) -> Option<T> {
match self {
Some(_) => self,
- None => optb
+ None => optb,
}
}
pub fn or_else<F: FnOnce() -> Option<T>>(self, f: F) -> Option<T> {
match self {
Some(_) => self,
- None => f()
+ None => f(),
}
}
pub fn unwrap_or_default(self) -> T {
match self {
Some(x) => x,
- None => Default::default()
+ None => Default::default(),
}
}
}
}
}
+ #[cfg(target_arch = "x86")]
+ pub mod arch {
+ pub mod c95 {
+ pub type c_char = i8;
+ pub type c_schar = i8;
+ pub type c_uchar = u8;
+ pub type c_short = i16;
+ pub type c_ushort = u16;
+ pub type c_int = i32;
+ pub type c_uint = u32;
+ pub type c_long = i32;
+ pub type c_ulong = u32;
+ pub type c_float = f32;
+ pub type c_double = f64;
+ pub type size_t = u32;
+ pub type ptrdiff_t = i32;
+ pub type clock_t = i32;
+ pub type time_t = i32;
+ pub type suseconds_t = i32;
+ pub type wchar_t = i32;
+ }
+ pub mod c99 {
+ pub type c_longlong = i64;
+ pub type c_ulonglong = u64;
+ pub type intptr_t = i32;
+ pub type uintptr_t = u32;
+ pub type intmax_t = i64;
+ pub type uintmax_t = u64;
+ }
+ pub mod posix88 {
+ pub type off_t = i64;
+ pub type dev_t = u32;
+ pub type ino_t = u32;
+ pub type pid_t = i32;
+ pub type uid_t = u32;
+ pub type gid_t = u32;
+ pub type useconds_t = u32;
+ pub type mode_t = u16;
+ pub type ssize_t = i32;
+ }
+ pub mod posix01 {
+ use types::common::c95::{c_void};
+ use types::common::c99::{uint8_t, uint32_t, int32_t};
+ use types::os::arch::c95::{c_long, time_t};
+ use types::os::arch::posix88::{dev_t, gid_t, ino_t};
+ use types::os::arch::posix88::{mode_t, off_t};
+ use types::os::arch::posix88::{uid_t};
+
+ pub type nlink_t = u16;
+ pub type blksize_t = i32;
+ pub type blkcnt_t = i64;
+ pub type fflags_t = u32;
+ #[repr(C)]
+ #[derive(Copy, Clone)] pub struct stat {
+ pub st_dev: dev_t,
+ pub st_ino: ino_t,
+ pub st_mode: mode_t,
+ pub st_nlink: nlink_t,
+ pub st_uid: uid_t,
+ pub st_gid: gid_t,
+ pub st_rdev: dev_t,
+ pub st_atime: time_t,
+ pub st_atime_nsec: c_long,
+ pub st_mtime: time_t,
+ pub st_mtime_nsec: c_long,
+ pub st_ctime: time_t,
+ pub st_ctime_nsec: c_long,
+ pub st_size: off_t,
+ pub st_blocks: blkcnt_t,
+ pub st_blksize: blksize_t,
+ pub st_flags: fflags_t,
+ pub st_gen: uint32_t,
+ pub st_lspare: int32_t,
+ pub st_birthtime: time_t,
+ pub st_birthtime_nsec: c_long,
+ pub __unused: [uint8_t; 2],
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)] pub struct utimbuf {
+ pub actime: time_t,
+ pub modtime: time_t,
+ }
+
+ pub type pthread_attr_t = *mut c_void;
+ }
+ pub mod posix08 {
+ }
+ pub mod bsd44 {
+ }
+ pub mod extra {
+ }
+ }
+
#[cfg(target_arch = "x86_64")]
pub mod arch {
pub mod c95 {
pub const F_GETFL : c_int = 3;
pub const F_SETFL : c_int = 4;
+ pub const O_ACCMODE : c_int = 3;
+
pub const SIGTRAP : c_int = 5;
pub const SIG_IGN: size_t = 1;
pub const O_DSYNC : c_int = 4194304;
pub const O_SYNC : c_int = 128;
pub const O_NONBLOCK : c_int = 4;
+ pub const F_GETPATH : c_int = 50;
pub const F_FULLFSYNC : c_int = 51;
pub const MAP_COPY : c_int = 0x0002;
pub const SO_DONTTRUNC: c_int = 0x2000;
pub const SO_WANTMORE: c_int = 0x4000;
pub const SO_WANTOOBFLAG: c_int = 0x8000;
+
+ pub const PATH_MAX: c_int = 1024;
}
pub mod sysconf {
use types::os::arch::c95::c_int;
fn bar(&self);
}
-// we now declare a function which takes an object with Foo trait implemented
-// as parameter
+// we now declare a function which takes an object implementing the Foo trait
fn some_func<T: Foo>(foo: T) {
foo.bar();
}
E0308: r##"
This error occurs when the compiler was unable to infer the concrete type of a
-variable. This error can occur for several cases, the most common of which is a
+variable. It can occur for several cases, the most common of which is a
mismatch in the expected type that the compiler inferred for a variable's
initializing expression, and the actual type explicitly assigned to the
variable.
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use target::Target;
+
+pub fn target() -> Target {
+ let mut base = super::freebsd_base::opts();
+ base.cpu = "pentium4".to_string();
+ base.pre_link_args.push("-m32".to_string());
+ base.morestack = false;
+
+ Target {
+ data_layout: "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string(),
+ llvm_target: "i686-unknown-freebsd".to_string(),
+ target_endian: "little".to_string(),
+ target_pointer_width: "32".to_string(),
+ arch: "x86".to_string(),
+ target_os: "freebsd".to_string(),
+ target_env: "".to_string(),
+ options: base,
+ }
+}
arm_linux_androideabi,
aarch64_linux_android,
+ i686_unknown_freebsd,
x86_64_unknown_freebsd,
i686_unknown_dragonfly,
<div class="search-container">
<input class="search-input" name="search"
autocomplete="off"
- placeholder="Click or press 'S' to search, '?' for more options..."
+ placeholder="Click or press ‘S’ to search, ‘?’ for more options…"
type="search">
</div>
</form>
<div id="help" class="hidden">
<div class="shortcuts">
- <h1>Keyboard shortcuts</h1>
+ <h1>Keyboard Shortcuts</h1>
<dl>
<dt>?</dt>
<dd>Show this help dialog</dd>
</dl>
</div>
<div class="infos">
- <h1>Search tricks</h1>
+ <h1>Search Tricks</h1>
<p>
Prefix searches with a type followed by a colon (e.g.
<code>fn:</code>) to restrict the search to a given type.
#help {
background: #e9e9e9;
- border-radius: 4px;
box-shadow: 0 0 6px rgba(0,0,0,.2);
position: absolute;
top: 300px;
#help dt {
float: left;
- border-radius: 3px;
+ border-radius: 4px;
border: 1px solid #bfbfbf;
background: #fff;
width: 23px;
highlightSourceLines(null);
$(window).on('hashchange', highlightSourceLines);
- // Helper function for Keyboard events,
- // Get's the char from the keypress event
+ // Gets the human-readable string for the virtual-key code of the
+ // given KeyboardEvent, ev.
//
- // This method is used because e.wich === x is not
- // compatible with non-english keyboard layouts
+ // This function is meant as a polyfill for KeyboardEvent#key,
+ // since it is not supported in Trident. We also test for
+ // KeyboardEvent#keyCode because the handleShortcut handler is
+ // also registered for the keydown event, because Blink doesn't fire
+ // keypress on hitting the Escape key.
//
- // Note: event.type must be keypress !
- function getChar(event) {
- if (event.which == null) {
- return String.fromCharCode(event.keyCode) // IE
- } else if (event.which!=0 && event.charCode!=0) {
- return String.fromCharCode(event.which) // the rest
- } else {
- return null // special key
- }
+ // So I guess you could say things are getting pretty interoperable.
+ function getVirtualKey(ev) {
+ if ("key" in ev && typeof ev.key != "undefined")
+ return ev.key;
+
+ var c = ev.charCode || ev.keyCode;
+ if (c == 27)
+ return "Escape";
+ return String.fromCharCode(c);
}
- $(document).on('keypress', function handleKeyboardShortcut(e) {
- if (document.activeElement.tagName === 'INPUT') {
+ function handleShortcut(ev) {
+ if (document.activeElement.tagName == "INPUT")
return;
- }
- if (getChar(e) === '?') {
- if (e.shiftKey && $('#help').hasClass('hidden')) {
- e.preventDefault();
- $('#help').removeClass('hidden');
+ switch (getVirtualKey(ev)) {
+ case "Escape":
+ if (!$("#help").hasClass("hidden")) {
+ ev.preventDefault();
+ $("#help").addClass("hidden");
+ } else if (!$("#search").hasClass("hidden")) {
+ ev.preventDefault();
+ $("#search").addClass("hidden");
+ $("#main").removeClass("hidden");
}
- } else if (getChar(e) === 's' || getChar(e) === 'S') {
- e.preventDefault();
- $('.search-input').focus();
- }
- }).on('keydown', function(e) {
- // The escape key event has to be captured with the keydown event.
- // Because keypressed has no keycode for the escape key
- // (and other special keys in general)...
- if (document.activeElement.tagName === 'INPUT') {
- return;
- }
-
- if (e.keyCode === 27) { // escape key
- if (!$('#help').hasClass('hidden')) {
- e.preventDefault();
- $('#help').addClass('hidden');
- } else if (!$('#search').hasClass('hidden')) {
- e.preventDefault();
- $('#search').addClass('hidden');
- $('#main').removeClass('hidden');
+ break;
+
+ case "s":
+ case "S":
+ ev.preventDefault();
+ $(".search-input").focus();
+ break;
+
+ case "?":
+ if (ev.shiftKey && $("#help").hasClass("hidden")) {
+ ev.preventDefault();
+ $("#help").removeClass("hidden");
}
+ break;
}
- }).on('click', function(e) {
- if (!$(e.target).closest('#help').length) {
- $('#help').addClass('hidden');
+ }
+
+ $(document).on("keypress", handleShortcut);
+ $(document).on("keydown", handleShortcut);
+ $(document).on("click", function(ev) {
+ if (!$(ev.target).closest("#help").length) {
+ $("#help").addClass("hidden");
}
});
-
$('.version-selector').on('change', function() {
var i, match,
url = document.location.href,
document.location.href = url;
});
+
/**
* A function to compute the Levenshtein distance between two strings
* Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
return;
}
- var elements = document.querySelectorAll('pre.rust');
+ var featureRegexp = new RegExp('^\s*#!\\[feature\\(\.*?\\)\\]');
+ var elements = document.querySelectorAll('pre.rust-example-rendered');
Array.prototype.forEach.call(elements, function(el) {
el.onmouseover = function(e) {
a.setAttribute('class', 'test-arrow');
var code = el.previousElementSibling.textContent;
+
+ var channel = '';
+ if (featureRegexp.test(code)) {
+ channel = '&version=nightly';
+ }
+
a.setAttribute('href', window.playgroundUrl + '?code=' +
- encodeURIComponent(code));
+ encodeURIComponent(code) + channel);
a.setAttribute('target', '_blank');
el.appendChild(a);
// except according to those terms.
//! A fixed-size array is denoted `[T; N]` for the element type `T` and
-//! the compile time constant size `N`. The size should be zero or positive.
+//! the compile time constant size `N`. The size must be zero or positive.
//!
//! Arrays values are created either with an explicit expression that lists
//! each element: `[x, y, z]` or a repeat expression: `[x; N]`. The repeat
//!
//! [slice]: primitive.slice.html
//!
-//! ## Examples
+//! Rust does not currently support generics over the size of an array type.
+//!
+//! # Examples
//!
//! ```
//! let mut array: [i32; 3] = [0; 3];
//!
//! ```
//!
-//! Rust does not currently support generics over the size of an array type.
-//!
#![doc(primitive = "array")]
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use ascii;
use borrow::{Cow, ToOwned, Borrow};
use boxed::Box;
use clone::Clone;
use convert::{Into, From};
use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
use error::Error;
-use fmt;
+use fmt::{self, Write};
use io;
use iter::Iterator;
use libc;
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for CString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&String::from_utf8_lossy(self.as_bytes()), f)
+ fmt::Debug::fmt(&**self, f)
+ }
+}
+
+#[stable(feature = "cstr_debug", since = "1.3.0")]
+impl fmt::Debug for CStr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "\""));
+ for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) {
+ try!(f.write_char(byte as char));
+ }
+ write!(f, "\"")
}
}
#[test]
fn formatted() {
- let s = CString::new(&b"12"[..]).unwrap();
- assert_eq!(format!("{:?}", s), "\"12\"");
+ let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap();
+ assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
}
#[test]
/// will be extended to `size` and have all of the intermediate data filled
/// in with 0s.
///
+ /// # Errors
+ ///
+ /// This function will return an error if the file is not opened for writing.
+ ///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// # fn foo() -> std::io::Result<()> {
- /// let mut f = try!(File::open("foo.txt"));
- /// try!(f.set_len(0));
+ /// let mut f = try!(File::create("foo.txt"));
+ /// try!(f.set_len(10));
/// # Ok(())
/// # }
/// ```
use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
use ptr;
-/// Wraps a `Read` and buffers input from it.
+/// The `BufReader` struct adds buffering to any reader.
///
/// It can be excessively inefficient to work directly with a `Read` instance.
/// For example, every call to `read` on `TcpStream` results in a system call.
///
/// # Examples
///
-/// ```no_run
+/// ```
/// use std::io::prelude::*;
/// use std::io::BufReader;
/// use std::fs::File;
impl<R: Read> BufReader<R> {
/// Creates a new `BufReader` with a default buffer capacity.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f);
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: R) -> BufReader<R> {
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
}
/// Creates a new `BufReader` with the specified buffer capacity.
+ ///
+ /// # Examples
+ ///
+ /// Creating a buffer with ten bytes of capacity:
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::with_capacity(10, f);
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
BufReader {
}
/// Gets a reference to the underlying reader.
+ ///
+ /// It is inadvisable to directly read from the underlying reader.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.get_ref();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_ref(&self) -> &R { &self.inner }
/// Gets a mutable reference to the underlying reader.
///
- /// # Warning
- ///
/// It is inadvisable to directly read from the underlying reader.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.get_mut();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
/// Unwraps this `BufReader`, returning the underlying reader.
///
/// Note that any leftover data in the internal buffer is lost.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.into_inner();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn into_inner(self) -> R { self.inner }
}
use result;
use sys;
-/// A type for results generated by I/O related functions where the `Err` type
-/// is hard-wired to `io::Error`.
+/// A specialized [`Result`][result] type for I/O operations.
+///
+/// [result]: ../result/enum.Result.html
+///
+/// This type is broadly used across `std::io` for any operation which may
+/// produce an error.
///
/// This typedef is generally used to avoid writing out `io::Error` directly and
-/// is otherwise a direct mapping to `std::result::Result`.
+/// is otherwise a direct mapping to `Result`.
+///
+/// While usual Rust style is to import types directly, aliases of `Result`
+/// often are not, to make it easier to distinguish between them. `Result` is
+/// generally assumed to be `std::result::Result`, and so users of this alias
+/// will generally use `io::Result` instead of shadowing the prelude's import
+/// of `std::result::Result`.
+///
+/// # Examples
+///
+/// A convenience function that bubbles an `io::Result` to its caller:
+///
+/// ```
+/// use std::io;
+///
+/// fn get_string() -> io::Result<String> {
+/// let mut buffer = String::new();
+///
+/// try!(io::stdin().read_line(&mut buffer));
+///
+/// Ok(buffer)
+/// }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub type Result<T> = result::Result<T, Error>;
//! # #![allow(unused_imports)]
//! use std::io::prelude::*;
//! ```
-//!
-//! This module contains reexports of many core I/O traits such as `Read`,
-//! `Write` and `BufRead`. Structures and functions are not
-//! contained in this module.
#![stable(feature = "rust1", since = "1.0.0")]
inner: MutexGuard<'a, BufReader<Maybe<StdinRaw>>>,
}
-/// Creates a new handle to the global standard input stream of this process.
+/// Constructs a new handle to the standard input of the current process.
///
-/// The handle returned refers to a globally shared buffer between all threads.
-/// Access is synchronized and can be explicitly controlled with the `lock()`
-/// method.
+/// Each handle returned is a reference to a shared global buffer whose access
+/// is synchronized via a mutex. If you need more explicit control over
+/// locking, see the [lock() method][lock].
+///
+/// [lock]: struct.Stdin.html#method.lock
+///
+/// # Examples
+///
+/// Using implicit synchronization:
+///
+/// ```
+/// use std::io::{self, Read};
+///
+/// # fn foo() -> io::Result<String> {
+/// let mut buffer = String::new();
+/// try!(io::stdin().read_to_string(&mut buffer));
+/// # Ok(buffer)
+/// # }
+/// ```
+///
+/// Using explicit synchronization:
///
-/// The `Read` trait is implemented for the returned value but the `BufRead`
-/// trait is not due to the global nature of the standard input stream. The
-/// locked version, `StdinLock`, implements both `Read` and `BufRead`, however.
+/// ```
+/// use std::io::{self, Read};
+///
+/// # fn foo() -> io::Result<String> {
+/// let mut buffer = String::new();
+/// let stdin = io::stdin();
+/// let mut handle = stdin.lock();
+///
+/// try!(handle.read_to_string(&mut buffer));
+/// # Ok(buffer)
+/// # }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdin() -> Stdin {
static INSTANCE: Lazy<Mutex<BufReader<Maybe<StdinRaw>>>> = Lazy::new(stdin_init);
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<Maybe<StdoutRaw>>>>,
}
-/// Constructs a new reference to the standard output of the current process.
+/// Constructs a new handle to the standard output of the current process.
///
/// Each handle returned is a reference to a shared global buffer whose access
-/// is synchronized via a mutex. Explicit control over synchronization is
-/// provided via the `lock` method.
+/// is synchronized via a mutex. If you need more explicit control over
+/// locking, see the [lock() method][lock].
+///
+/// [lock]: struct.Stdout.html#method.lock
+///
+/// # Examples
+///
+/// Using implicit synchronization:
+///
+/// ```
+/// use std::io::{self, Write};
+///
+/// # fn foo() -> io::Result<()> {
+/// try!(io::stdout().write(b"hello world"));
///
-/// The returned handle implements the `Write` trait.
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Using explicit synchronization:
+///
+/// ```
+/// use std::io::{self, Write};
+///
+/// # fn foo() -> io::Result<()> {
+/// let stdout = io::stdout();
+/// let mut handle = stdout.lock();
+///
+/// try!(handle.write(b"hello world"));
+///
+/// # Ok(())
+/// # }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>
inner: ReentrantMutexGuard<'a, RefCell<Maybe<StderrRaw>>>,
}
-/// Constructs a new reference to the standard error stream of a process.
+/// Constructs a new handle to the standard error of the current process.
+///
+/// This handle is not buffered.
+///
+/// # Examples
+///
+/// Using implicit synchronization:
+///
+/// ```
+/// use std::io::{self, Write};
+///
+/// # fn foo() -> io::Result<()> {
+/// try!(io::stderr().write(b"hello world"));
+///
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Using explicit synchronization:
+///
+/// ```
+/// use std::io::{self, Write};
+///
+/// # fn foo() -> io::Result<()> {
+/// let stderr = io::stderr();
+/// let mut handle = stderr.lock();
///
-/// Each returned handle is synchronized amongst all other handles created from
-/// this function. No handles are buffered, however.
+/// try!(handle.write(b"hello world"));
///
-/// The returned handle implements the `Write` trait.
+/// # Ok(())
+/// # }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stderr() -> Stderr {
static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new(stderr_init);
/// This function will return an error immediately if any call to `read` or
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
/// handled by this function and the underlying operation is retried.
+///
+/// # Examples
+///
+/// ```
+/// use std::io;
+///
+/// # fn foo() -> io::Result<()> {
+/// let mut reader: &[u8] = b"hello";
+/// let mut writer: Vec<u8> = vec![];
+///
+/// try!(io::copy(&mut reader, &mut writer));
+///
+/// assert_eq!(reader, &writer[..]);
+/// # Ok(())
+/// # }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<R: Read, W: Write>(reader: &mut R, writer: &mut W) -> io::Result<u64> {
let mut buf = [0; super::DEFAULT_BUF_SIZE];
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Empty { _priv: () }
-/// Creates an instance of an empty reader.
+/// Constructs a new handle to an empty reader.
///
/// All reads from the returned reader will return `Ok(0)`.
+///
+/// # Examples
+///
+/// A slightly sad example of not reading anything into a buffer:
+///
+/// ```
+/// use std::io;
+/// use std::io::Read;
+///
+/// # fn foo() -> io::Result<String> {
+/// let mut buffer = String::new();
+/// try!(io::empty().read_to_string(&mut buffer));
+/// # Ok(buffer)
+/// # }
+/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn empty() -> Empty { Empty { _priv: () } }
asm!("movl $$0x48+90*4, %eax
movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
}
- #[cfg(all(target_arch = "x86",
- any(target_os = "linux", target_os = "freebsd")))]
+ #[cfg(all(target_arch = "x86", target_os = "linux"))]
#[inline(always)]
unsafe fn target_record_sp_limit(limit: usize) {
asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
// aarch64 - FIXME(AARCH64): missing...
// powerpc - FIXME(POWERPC): missing...
// arm-ios - iOS segmented stack is disabled for now, see related notes
- // openbsd - segmented stack is disabled
+ // openbsd/bitrig/netbsd - no segmented stacks.
+ // x86-freebsd - no segmented stacks.
#[cfg(any(target_arch = "aarch64",
target_arch = "powerpc",
all(target_arch = "arm", target_os = "ios"),
+ all(target_arch = "x86", target_os = "freebsd"),
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd"))]
movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
return limit;
}
- #[cfg(all(target_arch = "x86",
- any(target_os = "linux", target_os = "freebsd")))]
+ #[cfg(all(target_arch = "x86", target_os = "linux"))]
#[inline(always)]
unsafe fn target_get_sp_limit() -> usize {
let limit;
// aarch64 - FIXME(AARCH64): missing...
// powerpc - FIXME(POWERPC): missing...
- // arm-ios - iOS doesn't support segmented stacks yet.
- // openbsd - OpenBSD doesn't support segmented stacks.
+ // arm-ios - no segmented stacks.
+ // openbsd/bitrig/netbsd - no segmented stacks.
+ // x86-freebsd - no segmented stacks..
//
// This function might be called by runtime though
// so it is unsafe to unreachable, let's return a fixed constant.
#[cfg(any(target_arch = "aarch64",
target_arch = "powerpc",
all(target_arch = "arm", target_os = "ios"),
+ all(target_arch = "x86", target_os = "freebsd"),
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd"))]
readlink(&p).ok()
}
- #[cfg(not(target_os = "linux"))]
+ #[cfg(target_os = "macos")]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ let mut buf = vec![0;libc::PATH_MAX as usize];
+ let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
+ if n == -1 {
+ return None;
+ }
+ let l = buf.iter().position(|&c| c == 0).unwrap();
+ buf.truncate(l as usize);
+ Some(PathBuf::from(OsString::from_vec(buf)))
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#24570): implement this for other Unix platforms
None
}
- #[cfg(target_os = "linux")]
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if mode == -1 {
}
}
- #[cfg(not(target_os = "linux"))]
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
// FIXME(#24570): implement this for other Unix platforms
None
pub const ERROR_NO_MORE_FILES: libc::DWORD = 18;
pub const TOKEN_READ: libc::DWORD = 0x20008;
pub const FILE_FLAG_OPEN_REPARSE_POINT: libc::DWORD = 0x00200000;
+pub const FILE_FLAG_BACKUP_SEMANTICS: libc::DWORD = 0x02000000;
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
pub const FSCTL_GET_REPARSE_POINT: libc::DWORD = 0x900a8;
pub const IO_REPARSE_TAG_SYMLINK: libc::DWORD = 0xa000000c;
+pub const IO_REPARSE_TAG_MOUNT_POINT: libc::DWORD = 0xa0000003;
+pub const FSCTL_SET_REPARSE_POINT: libc::DWORD = 0x900a4;
+pub const FSCTL_DELETE_REPARSE_POINT: libc::DWORD = 0x900ac;
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: libc::DWORD = 0x1;
pub const PROGRESS_STOP: libc::DWORD = 2;
pub const PROGRESS_QUIET: libc::DWORD = 3;
+pub const TOKEN_ADJUST_PRIVILEGES: libc::DWORD = 0x0020;
+pub const SE_PRIVILEGE_ENABLED: libc::DWORD = 2;
+
#[repr(C)]
#[cfg(target_arch = "x86")]
pub struct WSADATA {
};
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
+#[repr(C)]
+pub struct LUID {
+ pub LowPart: libc::DWORD,
+ pub HighPart: libc::c_long,
+}
+
+pub type PLUID = *mut LUID;
+
+#[repr(C)]
+pub struct TOKEN_PRIVILEGES {
+ pub PrivilegeCount: libc::DWORD,
+ pub Privileges: [LUID_AND_ATTRIBUTES; 1],
+}
+
+pub type PTOKEN_PRIVILEGES = *mut TOKEN_PRIVILEGES;
+
+#[repr(C)]
+pub struct LUID_AND_ATTRIBUTES {
+ pub Luid: LUID,
+ pub Attributes: libc::DWORD,
+}
+
+#[repr(C)]
+pub struct REPARSE_MOUNTPOINT_DATA_BUFFER {
+ pub ReparseTag: libc::DWORD,
+ pub ReparseDataLength: libc::DWORD,
+ pub Reserved: libc::WORD,
+ pub ReparseTargetLength: libc::WORD,
+ pub ReparseTargetMaximumLength: libc::WORD,
+ pub Reserved1: libc::WORD,
+ pub ReparseTarget: libc::WCHAR,
+}
+
+
#[link(name = "ws2_32")]
#[link(name = "userenv")]
extern "system" {
lpData: libc::LPVOID,
pbCancel: LPBOOL,
dwCopyFlags: libc::DWORD) -> libc::BOOL;
+ pub fn LookupPrivilegeValueW(lpSystemName: libc::LPCWSTR,
+ lpName: libc::LPCWSTR,
+ lpLuid: PLUID) -> libc::BOOL;
+ pub fn AdjustTokenPrivileges(TokenHandle: libc::HANDLE,
+ DisableAllPrivileges: libc::BOOL,
+ NewState: PTOKEN_PRIVILEGES,
+ BufferLength: libc::DWORD,
+ PreviousState: PTOKEN_PRIVILEGES,
+ ReturnLength: *mut libc::DWORD) -> libc::BOOL;
}
// Functions that aren't available on Windows XP, but we still use them and just
pub struct FileAttr {
data: c::WIN32_FILE_ATTRIBUTE_DATA,
- is_symlink: bool,
+ reparse_tag: libc::DWORD,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum FileType {
- Dir, File, Symlink, ReparsePoint
+ Dir, File, Symlink, ReparsePoint, MountPoint,
}
pub struct ReadDir {
pub fn file_type(&self) -> io::Result<FileType> {
Ok(FileType::new(self.data.dwFileAttributes,
- self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK))
+ /* reparse_tag = */ self.data.dwReserved0))
}
pub fn metadata(&self) -> io::Result<FileAttr> {
nFileSizeHigh: self.data.nFileSizeHigh,
nFileSizeLow: self.data.nFileSizeLow,
},
- is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK,
+ reparse_tag: self.data.dwReserved0,
})
}
}
}
impl File {
- fn open_reparse_point(path: &Path) -> io::Result<File> {
+ fn open_reparse_point(path: &Path, write: bool) -> io::Result<File> {
let mut opts = OpenOptions::new();
- opts.read(true);
- opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT);
+ opts.read(!write);
+ opts.write(write);
+ opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT |
+ c::FILE_FLAG_BACKUP_SEMANTICS);
File::open(path, &opts)
}
nFileSizeHigh: info.nFileSizeHigh,
nFileSizeLow: info.nFileSizeLow,
},
- is_symlink: false,
+ reparse_tag: 0,
};
if attr.is_reparse_point() {
- attr.is_symlink = self.is_symlink();
+ let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ if let Ok((_, buf)) = self.reparse_point(&mut b) {
+ attr.reparse_tag = buf.ReparseTag;
+ }
}
Ok(attr)
}
pub fn handle(&self) -> &Handle { &self.handle }
- fn is_symlink(&self) -> bool {
- self.readlink().is_ok()
- }
-
- fn readlink(&self) -> io::Result<PathBuf> {
- let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- let mut bytes = 0;
-
+ fn reparse_point<'a>(&self,
+ space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE])
+ -> io::Result<(libc::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
unsafe {
+ let mut bytes = 0;
try!(cvt({
c::DeviceIoControl(self.handle.raw(),
c::FSCTL_GET_REPARSE_POINT,
&mut bytes,
0 as *mut _)
}));
- let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _;
- if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK {
- return Err(io::Error::new(io::ErrorKind::Other, "not a symlink"))
- }
+ Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+ }
+ }
+
+ fn readlink(&self) -> io::Result<PathBuf> {
+ let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let (_bytes, buf) = try!(self.reparse_point(&mut space));
+ if buf.ReparseTag != c::IO_REPARSE_TAG_SYMLINK {
+ return Err(io::Error::new(io::ErrorKind::Other, "not a symlink"))
+ }
+
+ unsafe {
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
- &(*buf).rest as *const _ as *const _;
+ &buf.rest as *const _ as *const _;
let path_buffer = &(*info).PathBuffer as *const _ as *const u16;
let subst_off = (*info).SubstituteNameOffset / 2;
let subst_ptr = path_buffer.offset(subst_off as isize);
pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 }
pub fn file_type(&self) -> FileType {
- FileType::new(self.data.dwFileAttributes, self.is_symlink)
+ FileType::new(self.data.dwFileAttributes, self.reparse_tag)
}
pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) }
}
impl FileType {
- fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType {
+ fn new(attrs: libc::DWORD, reparse_tag: libc::DWORD) -> FileType {
if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- if is_symlink {
- FileType::Symlink
- } else {
- FileType::ReparsePoint
+ match reparse_tag {
+ c::IO_REPARSE_TAG_SYMLINK => FileType::Symlink,
+ c::IO_REPARSE_TAG_MOUNT_POINT => FileType::MountPoint,
+ _ => FileType::ReparsePoint,
}
} else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
FileType::Dir
pub fn is_dir(&self) -> bool { *self == FileType::Dir }
pub fn is_file(&self) -> bool { *self == FileType::File }
- pub fn is_symlink(&self) -> bool { *self == FileType::Symlink }
+ pub fn is_symlink(&self) -> bool {
+ *self == FileType::Symlink || *self == FileType::MountPoint
+ }
}
impl DirBuilder {
}
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
- let file = try!(File::open_reparse_point(p));
+ let file = try!(File::open_reparse_point(p, false));
file.readlink()
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
let attr = try!(lstat(p));
- if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- let opts = OpenOptions::new();
+
+ // If this is a reparse point, then we need to reopen the file to get the
+ // actual destination. We also pass the FILE_FLAG_BACKUP_SEMANTICS flag to
+ // ensure that we can open directories (this path may be a directory
+ // junction). Once the file is opened we ask the opened handle what its
+ // metadata information is.
+ if attr.is_reparse_point() {
+ let mut opts = OpenOptions::new();
+ opts.flags_and_attributes(c::FILE_FLAG_BACKUP_SEMANTICS);
let file = try!(File::open(p, &opts));
file.file_attr()
} else {
c::GetFileExInfoStandard,
&mut attr.data as *mut _ as *mut _)));
if attr.is_reparse_point() {
- attr.is_symlink = File::open_reparse_point(p).map(|f| {
- f.is_symlink()
- }).unwrap_or(false);
+ attr.reparse_tag = File::open_reparse_point(p, false).and_then(|f| {
+ let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ f.reparse_point(&mut b).map(|(_, b)| b.ReparseTag)
+ }).unwrap_or(0);
}
Ok(attr)
}
}));
Ok(size as u64)
}
+
+#[test]
+fn directory_junctions_are_directories() {
+ use ffi::OsStr;
+ use env;
+ use rand::{self, StdRng, Rng};
+
+ macro_rules! t {
+ ($e:expr) => (match $e {
+ Ok(e) => e,
+ Err(e) => panic!("{} failed with: {}", stringify!($e), e),
+ })
+ }
+
+ let d = DirBuilder::new();
+ let p = env::temp_dir();
+ let mut r = rand::thread_rng();
+ let ret = p.join(&format!("rust-{}", r.next_u32()));
+ let foo = ret.join("foo");
+ let bar = ret.join("bar");
+ t!(d.mkdir(&ret));
+ t!(d.mkdir(&foo));
+ t!(d.mkdir(&bar));
+
+ t!(create_junction(&bar, &foo));
+ let metadata = stat(&bar);
+ t!(delete_junction(&bar));
+
+ t!(rmdir(&foo));
+ t!(rmdir(&bar));
+ t!(rmdir(&ret));
+
+ let metadata = t!(metadata);
+ assert!(metadata.file_type().is_dir());
+
+ // Creating a directory junction on windows involves dealing with reparse
+ // points and the DeviceIoControl function, and this code is a skeleton of
+ // what can be found here:
+ //
+ // http://www.flexhex.com/docs/articles/hard-links.phtml
+ fn create_junction(src: &Path, dst: &Path) -> io::Result<()> {
+ let f = try!(opendir(src, true));
+ let h = f.handle().raw();
+
+ unsafe {
+ let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut db = data.as_mut_ptr()
+ as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
+ let mut buf = &mut (*db).ReparseTarget as *mut _;
+ let mut i = 0;
+ let v = br"\??\";
+ let v = v.iter().map(|x| *x as u16);
+ for c in v.chain(dst.as_os_str().encode_wide()) {
+ *buf.offset(i) = c;
+ i += 1;
+ }
+ *buf.offset(i) = 0;
+ i += 1;
+ (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
+ (*db).ReparseTargetMaximumLength = (i * 2) as libc::WORD;
+ (*db).ReparseTargetLength = ((i - 1) * 2) as libc::WORD;
+ (*db).ReparseDataLength =
+ (*db).ReparseTargetLength as libc::DWORD + 12;
+
+ let mut ret = 0;
+ cvt(c::DeviceIoControl(h as *mut _,
+ c::FSCTL_SET_REPARSE_POINT,
+ data.as_ptr() as *mut _,
+ (*db).ReparseDataLength + 8,
+ 0 as *mut _, 0,
+ &mut ret,
+ 0 as *mut _)).map(|_| ())
+ }
+ }
+
+ fn opendir(p: &Path, write: bool) -> io::Result<File> {
+ unsafe {
+ let mut token = 0 as *mut _;
+ let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed();
+ try!(cvt(c::OpenProcessToken(c::GetCurrentProcess(),
+ c::TOKEN_ADJUST_PRIVILEGES,
+ &mut token)));
+ let name: &OsStr = if write {
+ "SeRestorePrivilege".as_ref()
+ } else {
+ "SeBackupPrivilege".as_ref()
+ };
+ let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
+ try!(cvt(c::LookupPrivilegeValueW(0 as *const _,
+ name.as_ptr(),
+ &mut tp.Privileges[0].Luid)));
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED;
+ let size = mem::size_of::<c::TOKEN_PRIVILEGES>() as libc::DWORD;
+ try!(cvt(c::AdjustTokenPrivileges(token, libc::FALSE, &mut tp, size,
+ 0 as *mut _, 0 as *mut _)));
+ try!(cvt(libc::CloseHandle(token)));
+
+ File::open_reparse_point(p, write)
+ }
+ }
+
+ fn delete_junction(p: &Path) -> io::Result<()> {
+ unsafe {
+ let f = try!(opendir(p, true));
+ let h = f.handle().raw();
+ let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut db = data.as_mut_ptr()
+ as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
+ (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
+ let mut bytes = 0;
+ cvt(c::DeviceIoControl(h as *mut _,
+ c::FSCTL_DELETE_REPARSE_POINT,
+ data.as_ptr() as *mut _,
+ (*db).ReparseDataLength + 8,
+ 0 as *mut _, 0,
+ &mut bytes,
+ 0 as *mut _)).map(|_| ())
+ }
+ }
+}
-Subproject commit 0da191a30ba385215c5eb1dc97c2b5f076f93b07
+Subproject commit c37d3747da75c280237dc2d6b925078e69555499