rust/src/libstd/path/mod.rs

1095 lines
37 KiB
Rust
Raw Normal View History

// 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.
//! Cross-platform file path handling (re-write)
use container::Container;
use c_str::CString;
use clone::Clone;
use fmt;
2013-09-30 19:13:13 -05:00
use iter::Iterator;
use option::{Option, None, Some};
use str;
use str::{OwnedStr, Str, StrSlice};
use vec;
use vec::{CopyableVector, OwnedCopyableVector, OwnedVector, Vector};
use vec::{ImmutableEqVector, ImmutableVector};
/// Typedef for POSIX file paths.
/// See `posix::Path` for more info.
pub use PosixPath = self::posix::Path;
2013-08-27 02:51:08 -05:00
/// Typedef for Windows file paths.
/// See `windows::Path` for more info.
pub use WindowsPath = self::windows::Path;
/// Typedef for the platform-native path type
#[cfg(unix)]
pub use Path = self::posix::Path;
2013-08-27 02:51:08 -05:00
/// Typedef for the platform-native path type
#[cfg(windows)]
pub use Path = self::windows::Path;
/// Typedef for the platform-native component iterator
#[cfg(unix)]
pub use ComponentIter = self::posix::ComponentIter;
/// Typedef for the platform-native reverse component iterator
#[cfg(unix)]
pub use RevComponentIter = self::posix::RevComponentIter;
/// Typedef for the platform-native component iterator
#[cfg(windows)]
pub use ComponentIter = self::windows::ComponentIter;
/// Typedef for the platform-native reverse component iterator
#[cfg(windows)]
pub use RevComponentIter = self::windows::RevComponentIter;
/// Typedef for the platform-native str component iterator
#[cfg(unix)]
pub use StrComponentIter = self::posix::StrComponentIter;
/// Typedef for the platform-native reverse str component iterator
#[cfg(unix)]
pub use RevStrComponentIter = self::posix::RevStrComponentIter;
/// Typedef for the platform-native str component iterator
#[cfg(windows)]
pub use StrComponentIter = self::windows::StrComponentIter;
/// Typedef for the platform-native reverse str component iterator
#[cfg(windows)]
pub use RevStrComponentIter = self::windows::RevStrComponentIter;
pub mod posix;
pub mod windows;
// Condition that is raised when a NUL is found in a byte vector given to a Path function
condition! {
// this should be a &[u8] but there's a lifetime issue
null_byte: ~[u8] -> ~[u8];
}
/// A trait that represents the generic operations available on paths
pub trait GenericPath: Clone + GenericPathUnsafe {
/// Creates a new Path from a byte vector.
/// The resulting Path will always be normalized.
///
/// # Failure
///
/// Raises the `null_byte` condition if the path contains a NUL.
2013-08-27 02:51:08 -05:00
///
/// See individual Path impls for additional restrictions.
#[inline]
fn from_vec(path: &[u8]) -> Self {
if contains_nul(path) {
let path = self::null_byte::cond.raise(path.to_owned());
assert!(!contains_nul(path));
unsafe { GenericPathUnsafe::from_vec_unchecked(path) }
} else {
unsafe { GenericPathUnsafe::from_vec_unchecked(path) }
}
}
/// Creates a new Path from a byte vector, if possible.
/// The resulting Path will always be normalized.
#[inline]
fn from_vec_opt(path: &[u8]) -> Option<Self> {
if contains_nul(path) {
None
} else {
Some(unsafe { GenericPathUnsafe::from_vec_unchecked(path) })
}
}
/// Creates a new Path from a string.
/// The resulting Path will always be normalized.
///
/// # Failure
///
/// Raises the `null_byte` condition if the path contains a NUL.
#[inline]
fn from_str(path: &str) -> Self {
2013-08-27 02:51:08 -05:00
let v = path.as_bytes();
if contains_nul(v) {
GenericPath::from_vec(path.as_bytes()) // let from_vec handle the condition
} else {
unsafe { GenericPathUnsafe::from_str_unchecked(path) }
}
}
/// Creates a new Path from a string, if possible.
/// The resulting Path will always be normalized.
#[inline]
fn from_str_opt(path: &str) -> Option<Self> {
let v = path.as_bytes();
if contains_nul(v) {
None
} else {
Some(unsafe { GenericPathUnsafe::from_str_unchecked(path) })
}
}
/// Creates a new Path from a CString.
/// The resulting Path will always be normalized.
2013-08-27 02:51:08 -05:00
///
/// See individual Path impls for potential restrictions.
#[inline]
fn from_c_str(path: CString) -> Self {
// CStrings can't contain NULs
let v = path.as_bytes();
// v is NUL-terminated. Strip it off
let v = v.slice_to(v.len()-1);
unsafe { GenericPathUnsafe::from_vec_unchecked(v) }
}
/// Returns the path as a string, if possible.
/// If the path is not representable in utf-8, this returns None.
#[inline]
fn as_str<'a>(&'a self) -> Option<&'a str> {
2013-09-30 19:13:13 -05:00
str::from_utf8_slice_opt(self.as_vec())
}
/// Returns the path as a byte vector
fn as_vec<'a>(&'a self) -> &'a [u8];
/// Provides the path as a string
///
/// If the path is not UTF-8, invalid sequences will be replaced with the unicode
/// replacement char. This involves allocation.
#[inline]
fn with_display_str<T>(&self, f: &fn(&str) -> T) -> T {
match self.as_str() {
Some(s) => f(s),
None => {
let s = self.to_display_str();
f(s.as_slice())
}
}
}
/// Returns the path as a string
///
/// If the path is not UTF-8, invalid sequences will be replaced with the unicode
/// replacement char. This involves allocation.
///
/// This is similar to `with_display_str()` except it will always allocate a new ~str.
fn to_display_str(&self) -> ~str {
from_utf8_with_replacement(self.as_vec())
}
/// Provides the filename as a string
///
/// If the filename is not UTF-8, invalid sequences will be replaced with the unicode
/// replacement char. This involves allocation.
#[inline]
fn with_filename_display_str<T>(&self, f: &fn(Option<&str>) -> T) -> T {
match self.filename_str() {
s@Some(_) => f(s),
None => {
let o = self.to_filename_display_str();
f(o.map(|s|s.as_slice()))
}
}
}
/// Returns the filename as a string
///
/// If the filename is not UTF-8, invalid sequences will be replaced with the unicode
/// replacement char. This involves allocation.
///
/// This is similar to `to_filename_display_str` except it will always allocate a new ~str.
fn to_filename_display_str(&self) -> Option<~str> {
match self.filename() {
None => None,
Some(v) => Some(from_utf8_with_replacement(v))
}
}
/// Returns an object that implements `fmt::Default` for printing paths
///
/// This will print the equivalent of `to_display_str()` when used with a {} format parameter.
fn display<'a>(&'a self) -> Display<'a, Self> {
Display{ path: self }
}
/// Returns an object that implements `fmt::Default` for printing filenames
///
/// This will print the equivalent of `to_filename_display_str()` when used with a {}
/// format parameter. If there is no filename, nothing will be printed.
fn filename_display<'a>(&'a self) -> FilenameDisplay<'a, Self> {
FilenameDisplay{ path: self }
}
/// Returns the directory component of `self`, as a byte vector (with no trailing separator).
/// If `self` has no directory component, returns ['.'].
fn dirname<'a>(&'a self) -> &'a [u8];
/// Returns the directory component of `self`, as a string, if possible.
/// See `dirname` for details.
#[inline]
fn dirname_str<'a>(&'a self) -> Option<&'a str> {
2013-09-30 19:13:13 -05:00
str::from_utf8_slice_opt(self.dirname())
}
/// Returns the file component of `self`, as a byte vector.
/// If `self` represents the root of the file hierarchy, returns None.
/// If `self` is "." or "..", returns None.
fn filename<'a>(&'a self) -> Option<&'a [u8]>;
/// Returns the file component of `self`, as a string, if possible.
/// See `filename` for details.
#[inline]
fn filename_str<'a>(&'a self) -> Option<&'a str> {
self.filename().and_then(str::from_utf8_slice_opt)
}
/// Returns the stem of the filename of `self`, as a byte vector.
/// The stem is the portion of the filename just before the last '.'.
/// If there is no '.', the entire filename is returned.
fn filestem<'a>(&'a self) -> Option<&'a [u8]> {
match self.filename() {
None => None,
Some(name) => Some({
let dot = '.' as u8;
match name.rposition_elem(&dot) {
None | Some(0) => name,
Some(1) if name == bytes!("..") => name,
Some(pos) => name.slice_to(pos)
}
})
}
}
/// Returns the stem of the filename of `self`, as a string, if possible.
/// See `filestem` for details.
#[inline]
fn filestem_str<'a>(&'a self) -> Option<&'a str> {
self.filestem().and_then(str::from_utf8_slice_opt)
}
/// Returns the extension of the filename of `self`, as an optional byte vector.
/// The extension is the portion of the filename just after the last '.'.
/// If there is no extension, None is returned.
/// If the filename ends in '.', the empty vector is returned.
fn extension<'a>(&'a self) -> Option<&'a [u8]> {
match self.filename() {
None => None,
Some(name) => {
let dot = '.' as u8;
match name.rposition_elem(&dot) {
None | Some(0) => None,
Some(1) if name == bytes!("..") => None,
Some(pos) => Some(name.slice_from(pos+1))
}
}
}
}
/// Returns the extension of the filename of `self`, as a string, if possible.
/// See `extension` for details.
#[inline]
fn extension_str<'a>(&'a self) -> Option<&'a str> {
self.extension().and_then(str::from_utf8_slice_opt)
}
/// Replaces the directory portion of the path with the given byte vector.
/// If `self` represents the root of the filesystem hierarchy, the last path component
/// of the given byte vector becomes the filename.
///
/// # Failure
///
/// Raises the `null_byte` condition if the dirname contains a NUL.
#[inline]
fn set_dirname(&mut self, dirname: &[u8]) {
if contains_nul(dirname) {
let dirname = self::null_byte::cond.raise(dirname.to_owned());
assert!(!contains_nul(dirname));
unsafe { self.set_dirname_unchecked(dirname) }
} else {
unsafe { self.set_dirname_unchecked(dirname) }
}
}
/// Replaces the directory portion of the path with the given string.
/// See `set_dirname` for details.
#[inline]
fn set_dirname_str(&mut self, dirname: &str) {
2013-08-27 02:51:08 -05:00
if contains_nul(dirname.as_bytes()) {
self.set_dirname(dirname.as_bytes()) // triggers null_byte condition
} else {
unsafe { self.set_dirname_str_unchecked(dirname) }
}
}
/// Replaces the filename portion of the path with the given byte vector.
/// If the replacement name is [], this is equivalent to popping the path.
///
/// # Failure
///
/// Raises the `null_byte` condition if the filename contains a NUL.
#[inline]
fn set_filename(&mut self, filename: &[u8]) {
if contains_nul(filename) {
let filename = self::null_byte::cond.raise(filename.to_owned());
assert!(!contains_nul(filename));
unsafe { self.set_filename_unchecked(filename) }
} else {
unsafe { self.set_filename_unchecked(filename) }
}
}
/// Replaces the filename portion of the path with the given string.
/// See `set_filename` for details.
#[inline]
fn set_filename_str(&mut self, filename: &str) {
2013-08-27 02:51:08 -05:00
if contains_nul(filename.as_bytes()) {
self.set_filename(filename.as_bytes()) // triggers null_byte condition
} else {
unsafe { self.set_filename_str_unchecked(filename) }
}
}
/// Replaces the filestem with the given byte vector.
/// If there is no extension in `self` (or `self` has no filename), this is equivalent
/// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including
/// the preceding '.') becomes the new filename.
///
/// # Failure
///
/// Raises the `null_byte` condition if the filestem contains a NUL.
fn set_filestem(&mut self, filestem: &[u8]) {
// borrowck is being a pain here
let val = {
match self.filename() {
None => None,
Some(name) => {
let dot = '.' as u8;
match name.rposition_elem(&dot) {
None | Some(0) => None,
Some(idx) => {
let mut v;
if contains_nul(filestem) {
let filestem = self::null_byte::cond.raise(filestem.to_owned());
assert!(!contains_nul(filestem));
v = filestem;
let n = v.len();
v.reserve(n + name.len() - idx);
} else {
v = vec::with_capacity(filestem.len() + name.len() - idx);
v.push_all(filestem);
}
v.push_all(name.slice_from(idx));
Some(v)
}
}
}
}
};
match val {
None => self.set_filename(filestem),
Some(v) => unsafe { self.set_filename_unchecked(v) }
}
}
/// Replaces the filestem with the given string.
/// See `set_filestem` for details.
#[inline]
fn set_filestem_str(&mut self, filestem: &str) {
self.set_filestem(filestem.as_bytes())
}
/// Replaces the extension with the given byte vector.
/// If there is no extension in `self`, this adds one.
/// If the given byte vector is [], this removes the extension.
/// If `self` has no filename, this is a no-op.
///
/// # Failure
///
/// Raises the `null_byte` condition if the extension contains a NUL.
fn set_extension(&mut self, extension: &[u8]) {
// borrowck causes problems here too
let val = {
match self.filename() {
None => None,
Some(name) => {
let dot = '.' as u8;
match name.rposition_elem(&dot) {
None | Some(0) => {
if extension.is_empty() {
None
} else {
let mut v;
if contains_nul(extension) {
let ext = extension.to_owned();
let extension = self::null_byte::cond.raise(ext);
assert!(!contains_nul(extension));
v = vec::with_capacity(name.len() + extension.len() + 1);
v.push_all(name);
v.push(dot);
v.push_all(extension);
} else {
v = vec::with_capacity(name.len() + extension.len() + 1);
v.push_all(name);
v.push(dot);
v.push_all(extension);
}
Some(v)
}
}
Some(idx) => {
if extension.is_empty() {
Some(name.slice_to(idx).to_owned())
} else {
let mut v;
if contains_nul(extension) {
let ext = extension.to_owned();
let extension = self::null_byte::cond.raise(ext);
assert!(!contains_nul(extension));
v = vec::with_capacity(idx + extension.len() + 1);
v.push_all(name.slice_to(idx+1));
v.push_all(extension);
} else {
v = vec::with_capacity(idx + extension.len() + 1);
v.push_all(name.slice_to(idx+1));
v.push_all(extension);
}
Some(v)
}
}
}
}
}
};
match val {
None => (),
Some(v) => unsafe { self.set_filename_unchecked(v) }
}
}
/// Replaces the extension with the given string.
/// See `set_extension` for details.
#[inline]
fn set_extension_str(&mut self, extension: &str) {
self.set_extension(extension.as_bytes())
}
/// Adds the given extension (as a byte vector) to the file.
/// This does not remove any existing extension.
/// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`.
/// If `self` has no filename, this is a no-op.
/// If the given byte vector is [], this is a no-op.
///
/// # Failure
///
/// Raises the `null_byte` condition if the extension contains a NUL.
fn add_extension(&mut self, extension: &[u8]) {
if extension.is_empty() { return; }
// appease borrowck
let val = {
match self.filename() {
None => None,
Some(name) => {
let mut v;
if contains_nul(extension) {
let ext = extension.to_owned();
let extension = self::null_byte::cond.raise(ext);
assert!(!contains_nul(extension));
v = vec::with_capacity(name.len() + 1 + extension.len());
v.push_all(name);
v.push('.' as u8);
v.push_all(extension);
} else {
v = vec::with_capacity(name.len() + 1 + extension.len());
v.push_all(name);
v.push('.' as u8);
v.push_all(extension);
}
Some(v)
}
}
};
match val {
None => (),
Some(v) => unsafe { self.set_filename_unchecked(v) }
}
}
/// Adds the given extension (as a string) to the file.
/// See `add_extension` for details.
#[inline]
fn add_extension_str(&mut self, extension: &str) {
self.add_extension(extension.as_bytes())
}
/// Returns a new Path constructed by replacing the dirname with the given byte vector.
/// See `set_dirname` for details.
///
/// # Failure
///
/// Raises the `null_byte` condition if the dirname contains a NUL.
#[inline]
fn with_dirname(&self, dirname: &[u8]) -> Self {
let mut p = self.clone();
p.set_dirname(dirname);
p
}
/// Returns a new Path constructed by replacing the dirname with the given string.
/// See `set_dirname` for details.
#[inline]
fn with_dirname_str(&self, dirname: &str) -> Self {
2013-08-27 02:51:08 -05:00
let mut p = self.clone();
p.set_dirname_str(dirname);
p
}
/// Returns a new Path constructed by replacing the filename with the given byte vector.
/// See `set_filename` for details.
///
/// # Failure
///
/// Raises the `null_byte` condition if the filename contains a NUL.
#[inline]
fn with_filename(&self, filename: &[u8]) -> Self {
let mut p = self.clone();
p.set_filename(filename);
p
}
/// Returns a new Path constructed by replacing the filename with the given string.
/// See `set_filename` for details.
#[inline]
fn with_filename_str(&self, filename: &str) -> Self {
2013-08-27 02:51:08 -05:00
let mut p = self.clone();
p.set_filename_str(filename);
p
}
/// Returns a new Path constructed by setting the filestem to the given byte vector.
/// See `set_filestem` for details.
///
/// # Failure
///
/// Raises the `null_byte` condition if the filestem contains a NUL.
#[inline]
fn with_filestem(&self, filestem: &[u8]) -> Self {
let mut p = self.clone();
p.set_filestem(filestem);
p
}
/// Returns a new Path constructed by setting the filestem to the given string.
/// See `set_filestem` for details.
#[inline]
fn with_filestem_str(&self, filestem: &str) -> Self {
2013-08-27 02:51:08 -05:00
let mut p = self.clone();
p.set_filestem_str(filestem);
p
}
/// Returns a new Path constructed by setting the extension to the given byte vector.
/// See `set_extension` for details.
///
/// # Failure
///
/// Raises the `null_byte` condition if the extension contains a NUL.
#[inline]
fn with_extension(&self, extension: &[u8]) -> Self {
let mut p = self.clone();
p.set_extension(extension);
p
}
/// Returns a new Path constructed by setting the extension to the given string.
/// See `set_extension` for details.
#[inline]
fn with_extension_str(&self, extension: &str) -> Self {
2013-08-27 02:51:08 -05:00
let mut p = self.clone();
p.set_extension_str(extension);
p
}
/// Returns the directory component of `self`, as a Path.
/// If `self` represents the root of the filesystem hierarchy, returns `self`.
fn dir_path(&self) -> Self {
2013-08-27 02:51:08 -05:00
// self.dirname() returns a NUL-free vector
unsafe { GenericPathUnsafe::from_vec_unchecked(self.dirname()) }
}
/// Returns the file component of `self`, as a relative Path.
/// If `self` represents the root of the filesystem hierarchy, returns None.
fn file_path(&self) -> Option<Self> {
2013-08-27 02:51:08 -05:00
// self.filename() returns a NUL-free vector
self.filename().map_move(|v| unsafe { GenericPathUnsafe::from_vec_unchecked(v) })
}
/// Returns a Path that represents the filesystem root that `self` is rooted in.
///
/// If `self` is not absolute, or vol-relative in the case of Windows, this returns None.
fn root_path(&self) -> Option<Self>;
/// Pushes a path (as a byte vector) onto `self`.
/// If the argument represents an absolute path, it replaces `self`.
///
/// # Failure
///
/// Raises the `null_byte` condition if the path contains a NUL.
#[inline]
fn push(&mut self, path: &[u8]) {
if contains_nul(path) {
let path = self::null_byte::cond.raise(path.to_owned());
assert!(!contains_nul(path));
unsafe { self.push_unchecked(path) }
} else {
unsafe { self.push_unchecked(path) }
}
}
/// Pushes a path (as a string) onto `self.
/// See `push` for details.
#[inline]
fn push_str(&mut self, path: &str) {
2013-08-27 02:51:08 -05:00
if contains_nul(path.as_bytes()) {
self.push(path.as_bytes()) // triggers null_byte condition
} else {
unsafe { self.push_str_unchecked(path) }
}
}
/// Pushes a Path onto `self`.
/// If the argument represents an absolute path, it replaces `self`.
#[inline]
fn push_path(&mut self, path: &Self) {
self.push(path.as_vec())
}
/// Pushes multiple paths (as byte vectors) onto `self`.
/// See `push` for details.
#[inline]
fn push_many<V: Vector<u8>>(&mut self, paths: &[V]) {
for p in paths.iter() {
self.push(p.as_slice());
}
}
/// Pushes multiple paths (as strings) onto `self`.
#[inline]
fn push_many_str<S: Str>(&mut self, paths: &[S]) {
for p in paths.iter() {
self.push_str(p.as_slice());
}
}
/// Pops the last path component off of `self` and returns it.
/// If `self` represents the root of the file hierarchy, None is returned.
fn pop(&mut self) -> Option<~[u8]>;
/// Pops the last path component off of `self` and returns it as a string, if possible.
/// `self` will still be modified even if None is returned.
/// See `pop` for details.
#[inline]
fn pop_str(&mut self) -> Option<~str> {
self.pop().and_then(|v| str::from_utf8_owned_opt(v))
}
/// Returns a new Path constructed by joining `self` with the given path (as a byte vector).
/// If the given path is absolute, the new Path will represent just that.
///
/// # Failure
///
/// Raises the `null_byte` condition if the path contains a NUL.
#[inline]
fn join(&self, path: &[u8]) -> Self {
let mut p = self.clone();
p.push(path);
p
}
/// Returns a new Path constructed by joining `self` with the given path (as a string).
/// See `join` for details.
#[inline]
fn join_str(&self, path: &str) -> Self {
2013-08-27 02:51:08 -05:00
let mut p = self.clone();
p.push_str(path);
p
}
/// Returns a new Path constructed by joining `self` with the given path.
/// If the given path is absolute, the new Path will represent just that.
#[inline]
fn join_path(&self, path: &Self) -> Self {
let mut p = self.clone();
p.push_path(path);
p
}
/// Returns a new Path constructed by joining `self` with the given paths (as byte vectors).
/// See `join` for details.
#[inline]
fn join_many<V: Vector<u8>>(&self, paths: &[V]) -> Self {
let mut p = self.clone();
p.push_many(paths);
p
}
/// Returns a new Path constructed by joining `self` with the given paths (as strings).
/// See `join` for details.
#[inline]
fn join_many_str<S: Str>(&self, paths: &[S]) -> Self {
let mut p = self.clone();
p.push_many_str(paths);
p
}
/// Returns whether `self` represents an absolute path.
2013-08-27 02:51:08 -05:00
/// An absolute path is defined as one that, when joined to another path, will
/// yield back the same absolute path.
fn is_absolute(&self) -> bool;
/// Returns whether `self` represents a relative path.
/// Typically this is the inverse of `is_absolute`.
/// But for Windows paths, it also means the path is not volume-relative or
/// relative to the current working directory.
fn is_relative(&self) -> bool {
!self.is_absolute()
}
/// Returns whether `self` is equal to, or is an ancestor of, the given path.
/// If both paths are relative, they are compared as though they are relative
/// to the same parent path.
fn is_ancestor_of(&self, other: &Self) -> bool;
/// Returns the Path that, were it joined to `base`, would yield `self`.
/// If no such path exists, None is returned.
/// If `self` is absolute and `base` is relative, or on Windows if both
/// paths refer to separate drives, an absolute path is returned.
fn path_relative_from(&self, base: &Self) -> Option<Self>;
/// Executes a callback with the receiver and every parent
fn each_parent(&self, f: &fn(&Self) -> bool) -> bool {
let mut p = self.clone();
loop {
if !f(&p) {
return false;
}
let f = p.pop();
if f.is_none() || bytes!("..") == f.unwrap() {
break;
}
}
true
}
}
/// A trait that represents the unsafe operations on GenericPaths
pub trait GenericPathUnsafe {
/// Creates a new Path from a byte vector without checking for null bytes.
/// The resulting Path will always be normalized.
unsafe fn from_vec_unchecked(path: &[u8]) -> Self;
2013-08-27 02:51:08 -05:00
/// Creates a new Path from a str without checking for null bytes.
/// The resulting Path will always be normalized.
#[inline]
unsafe fn from_str_unchecked(path: &str) -> Self {
GenericPathUnsafe::from_vec_unchecked(path.as_bytes())
}
/// Replaces the directory portion of the path with the given byte vector without
/// checking for null bytes.
/// See `set_dirname` for details.
unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]);
2013-08-27 02:51:08 -05:00
/// Replaces the directory portion of the path with the given str without
/// checking for null bytes.
/// See `set_dirname_str` for details.
#[inline]
unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) {
self.set_dirname_unchecked(dirname.as_bytes())
}
/// Replaces the filename portion of the path with the given byte vector without
/// checking for null bytes.
/// See `set_filename` for details.
unsafe fn set_filename_unchecked(&mut self, filename: &[u8]);
2013-08-27 02:51:08 -05:00
/// Replaces the filename portion of the path with the given str without
/// checking for null bytes.
/// See `set_filename_str` for details.
#[inline]
unsafe fn set_filename_str_unchecked(&mut self, filename: &str) {
self.set_filename_unchecked(filename.as_bytes())
}
/// Pushes a byte vector onto `self` without checking for null bytes.
/// See `push` for details.
unsafe fn push_unchecked(&mut self, path: &[u8]);
2013-08-27 02:51:08 -05:00
/// Pushes a str onto `self` without checking for null bytes.
/// See `push_str` for details.
#[inline]
unsafe fn push_str_unchecked(&mut self, path: &str) {
self.push_unchecked(path.as_bytes())
}
}
/// Helper struct for printing paths with format!()
pub struct Display<'self, P> {
priv path: &'self P
}
/// Helper struct for printing filenames with format!()
pub struct FilenameDisplay<'self, P> {
priv path: &'self P
}
impl<'self, P: GenericPath> fmt::Default for Display<'self, P> {
fn fmt(d: &Display<P>, f: &mut fmt::Formatter) {
do d.path.with_display_str |s| {
f.pad(s)
}
}
}
impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> {
fn fmt(d: &FilenameDisplay<P>, f: &mut fmt::Formatter) {
do d.path.with_filename_display_str |s| {
f.pad(s.unwrap_or(""))
}
}
}
#[inline(always)]
fn contains_nul(v: &[u8]) -> bool {
v.iter().any(|&x| x == 0)
}
2013-09-26 16:24:06 -05:00
#[inline(always)]
fn from_utf8_with_replacement(mut v: &[u8]) -> ~str {
// FIXME (#9516): Don't decode utf-8 manually here once we have a good way to do it in str
// This is a truly horrifically bad implementation, done as a functionality stopgap until
// we have a proper utf-8 decoder. I don't really want to write one here.
static REPLACEMENT_CHAR: char = '\uFFFD';
let mut s = str::with_capacity(v.len());
while !v.is_empty() {
let w = str::utf8_char_width(v[0]);
if w == 0u {
s.push_char(REPLACEMENT_CHAR);
v = v.slice_from(1);
} else if v.len() < w || !str::is_utf8(v.slice_to(w)) {
s.push_char(REPLACEMENT_CHAR);
v = v.slice_from(1);
} else {
s.push_str(unsafe { ::cast::transmute(v.slice_to(w)) });
v = v.slice_from(w);
}
}
s
}
2013-09-26 16:24:06 -05:00
// FIXME (#9537): libc::stat should derive Default
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
mod stat {
#[allow(missing_doc)];
#[cfg(target_arch = "x86")]
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
__pad1: 0,
st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
__pad2: 0,
st_size: 0,
st_blksize: 0,
st_blocks: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
__unused4: 0,
__unused5: 0,
}
}
}
#[cfg(target_arch = "arm")]
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
__pad0: [0, ..4],
__st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
__pad3: [0, ..4],
st_size: 0,
st_blksize: 0,
st_blocks: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
st_ino: 0
}
}
}
#[cfg(target_arch = "mips")]
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
st_pad1: [0, ..3],
st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_pad2: [0, ..2],
st_size: 0,
st_pad3: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
st_blksize: 0,
st_blocks: 0,
st_pad5: [0, ..14],
}
}
}
#[cfg(target_arch = "x86_64")]
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
st_ino: 0,
st_nlink: 0,
st_mode: 0,
st_uid: 0,
st_gid: 0,
__pad0: 0,
st_rdev: 0,
st_size: 0,
st_blksize: 0,
st_blocks: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
__unused: [0, 0, 0],
}
}
}
}
#[cfg(target_os = "freebsd")]
mod stat {
#[allow(missing_doc)];
#[cfg(target_arch = "x86_64")]
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
st_size: 0,
st_blocks: 0,
st_blksize: 0,
st_flags: 0,
st_gen: 0,
st_lspare: 0,
st_birthtime: 0,
st_birthtime_nsec: 0,
__unused: [0, 0],
}
}
}
}
#[cfg(target_os = "macos")]
mod stat {
#[allow(missing_doc)];
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
st_mode: 0,
st_nlink: 0,
st_ino: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_atime: 0,
st_atime_nsec: 0,
st_mtime: 0,
st_mtime_nsec: 0,
st_ctime: 0,
st_ctime_nsec: 0,
st_birthtime: 0,
st_birthtime_nsec: 0,
st_size: 0,
st_blocks: 0,
st_blksize: 0,
st_flags: 0,
st_gen: 0,
st_lspare: 0,
st_qspare: [0, 0],
}
}
}
}
#[cfg(target_os = "win32")]
mod stat {
#[allow(missing_doc)];
pub mod arch {
use libc;
pub fn default_stat() -> libc::stat {
libc::stat {
st_dev: 0,
st_ino: 0,
st_mode: 0,
st_nlink: 0,
st_uid: 0,
st_gid: 0,
st_rdev: 0,
st_size: 0,
st_atime: 0,
st_mtime: 0,
st_ctime: 0,
}
}
}
}
#[cfg(test)]
mod tests {
use super::{GenericPath, PosixPath, WindowsPath};
use c_str::ToCStr;
#[test]
fn test_from_c_str() {
let input = "/foo/bar/baz";
let path: PosixPath = GenericPath::from_c_str(input.to_c_str());
assert_eq!(path.as_vec(), input.as_bytes());
let input = "\\foo\\bar\\baz";
let path: WindowsPath = GenericPath::from_c_str(input.to_c_str());
assert_eq!(path.as_str().unwrap(), input.as_slice());
}
}