2015-07-31 17:59:05 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the uutils coreutils package.
|
|
|
|
*
|
|
|
|
* (c) Joseph Crail <jbcrail@gmail.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Based on the pattern using by Cargo, I created a shim over the
|
|
|
|
// standard PathExt trait, so that the unstable path methods could
|
|
|
|
// be backported to stable (<= 1.1). This will likely be dropped
|
|
|
|
// when the path trait stabilizes.
|
|
|
|
|
2015-11-30 02:03:53 +00:00
|
|
|
use ::libc;
|
2015-07-31 22:37:50 +00:00
|
|
|
use std::env;
|
2015-07-31 17:59:05 +00:00
|
|
|
use std::fs;
|
2015-07-31 22:37:50 +00:00
|
|
|
use std::io::{Error, ErrorKind, Result};
|
|
|
|
use std::path::{Component, Path, PathBuf};
|
2015-07-31 17:59:05 +00:00
|
|
|
|
|
|
|
pub trait UUPathExt {
|
|
|
|
fn uu_exists(&self) -> bool;
|
|
|
|
fn uu_is_file(&self) -> bool;
|
|
|
|
fn uu_is_dir(&self) -> bool;
|
2015-07-31 22:37:50 +00:00
|
|
|
fn uu_metadata(&self) -> Result<fs::Metadata>;
|
2015-07-31 17:59:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UUPathExt for Path {
|
|
|
|
fn uu_exists(&self) -> bool {
|
|
|
|
fs::metadata(self).is_ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn uu_is_file(&self) -> bool {
|
|
|
|
fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn uu_is_dir(&self) -> bool {
|
|
|
|
fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
2015-07-31 22:37:50 +00:00
|
|
|
fn uu_metadata(&self) -> Result<fs::Metadata> {
|
2015-07-31 17:59:05 +00:00
|
|
|
fs::metadata(self)
|
|
|
|
}
|
|
|
|
}
|
2015-07-31 22:37:50 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
|
|
pub enum CanonicalizeMode {
|
|
|
|
None,
|
|
|
|
Normal,
|
|
|
|
Existing,
|
|
|
|
Missing,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve<P: AsRef<Path>>(original: P) -> Result<PathBuf> {
|
|
|
|
const MAX_LINKS_FOLLOWED: u32 = 255;
|
|
|
|
let mut followed = 0;
|
|
|
|
let mut result = original.as_ref().to_path_buf();
|
|
|
|
loop {
|
|
|
|
if followed == MAX_LINKS_FOLLOWED {
|
|
|
|
return Err(Error::new(ErrorKind::InvalidInput, "maximum links followed"));
|
|
|
|
}
|
|
|
|
|
|
|
|
match fs::metadata(&result) {
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
Ok(ref m) if !m.file_type().is_symlink() => break,
|
|
|
|
Ok(..) => {
|
|
|
|
followed += 1;
|
|
|
|
match fs::read_link(&result) {
|
|
|
|
Ok(path) => {
|
|
|
|
result.pop();
|
|
|
|
result.push(path);
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
return Err(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> Result<PathBuf> {
|
|
|
|
// Create an absolute path
|
|
|
|
let original = original.as_ref();
|
|
|
|
let original = if original.is_absolute() {
|
|
|
|
original.to_path_buf()
|
|
|
|
} else {
|
|
|
|
env::current_dir().unwrap().join(original)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut result = PathBuf::new();
|
|
|
|
let mut parts = vec!();
|
|
|
|
|
|
|
|
// Split path by directory separator; add prefix (Windows-only) and root
|
|
|
|
// directory to final path buffer; add remaining parts to temporary
|
|
|
|
// vector for canonicalization.
|
|
|
|
for part in original.components() {
|
|
|
|
match part {
|
|
|
|
Component::Prefix(_) | Component::RootDir => {
|
|
|
|
result.push(part.as_os_str());
|
|
|
|
},
|
|
|
|
Component::CurDir => {},
|
|
|
|
Component::ParentDir => {
|
|
|
|
parts.pop();
|
|
|
|
},
|
|
|
|
Component::Normal(_) => {
|
|
|
|
parts.push(part.as_os_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolve the symlinks where possible
|
|
|
|
if parts.len() > 0 {
|
|
|
|
for part in parts[..parts.len()-1].iter() {
|
|
|
|
result.push(part);
|
|
|
|
|
|
|
|
if can_mode == CanonicalizeMode::None {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match resolve(&result) {
|
|
|
|
Err(e) => match can_mode {
|
|
|
|
CanonicalizeMode::Missing => continue,
|
|
|
|
_ => return Err(e)
|
|
|
|
},
|
|
|
|
Ok(path) => {
|
|
|
|
result.pop();
|
|
|
|
result.push(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.push(parts.last().unwrap());
|
|
|
|
|
|
|
|
match resolve(&result) {
|
|
|
|
Err(e) => { if can_mode == CanonicalizeMode::Existing { return Err(e); } },
|
|
|
|
Ok(path) => {
|
|
|
|
result.pop();
|
|
|
|
result.push(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(result)
|
|
|
|
}
|
2015-11-30 02:03:53 +00:00
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
pub fn is_stdin_interactive() -> bool {
|
|
|
|
unsafe { libc::isatty(libc::STDIN_FILENO) == 1 }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn is_stdin_interactive() -> bool {
|
2015-12-01 06:27:08 +00:00
|
|
|
false
|
2015-11-30 02:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
pub fn is_stdout_interactive() -> bool {
|
|
|
|
unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn is_stdout_interactive() -> bool {
|
2015-12-01 06:27:08 +00:00
|
|
|
false
|
2015-11-30 02:03:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
pub fn is_stderr_interactive() -> bool {
|
|
|
|
unsafe { libc::isatty(libc::STDERR_FILENO) == 1 }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
pub fn is_stderr_interactive() -> bool {
|
2015-12-01 06:27:08 +00:00
|
|
|
false
|
2015-11-30 02:03:53 +00:00
|
|
|
}
|