mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 20:47:46 +00:00
Switch over to internal canonicalize().
The method, fs::canonicalize(), is unstable and can't be used for stable builds. We already have our own implementation of canonicalize(), which supports more options than the Rust library implementation.
This commit is contained in:
parent
4a331897cb
commit
b089831ea0
6 changed files with 150 additions and 134 deletions
|
@ -12,15 +12,16 @@
|
||||||
// be backported to stable (<= 1.1). This will likely be dropped
|
// be backported to stable (<= 1.1). This will likely be dropped
|
||||||
// when the path trait stabilizes.
|
// when the path trait stabilizes.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io::{Error, ErrorKind, Result};
|
||||||
use std::path::Path;
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
pub trait UUPathExt {
|
pub trait UUPathExt {
|
||||||
fn uu_exists(&self) -> bool;
|
fn uu_exists(&self) -> bool;
|
||||||
fn uu_is_file(&self) -> bool;
|
fn uu_is_file(&self) -> bool;
|
||||||
fn uu_is_dir(&self) -> bool;
|
fn uu_is_dir(&self) -> bool;
|
||||||
fn uu_metadata(&self) -> io::Result<fs::Metadata>;
|
fn uu_metadata(&self) -> Result<fs::Metadata>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UUPathExt for Path {
|
impl UUPathExt for Path {
|
||||||
|
@ -36,7 +37,111 @@ impl UUPathExt for Path {
|
||||||
fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
|
fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uu_metadata(&self) -> io::Result<fs::Metadata> {
|
fn uu_metadata(&self) -> Result<fs::Metadata> {
|
||||||
fs::metadata(self)
|
fs::metadata(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum CanonicalizeMode {
|
||||||
|
None,
|
||||||
|
Normal,
|
||||||
|
Existing,
|
||||||
|
Missing,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#![crate_name = "cp"]
|
#![crate_name = "cp"]
|
||||||
#![feature(fs_canonicalize)]
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
|
@ -24,7 +23,7 @@ mod util;
|
||||||
#[path = "../common/filesystem.rs"]
|
#[path = "../common/filesystem.rs"]
|
||||||
mod filesystem;
|
mod filesystem;
|
||||||
|
|
||||||
use filesystem::UUPathExt;
|
use filesystem::{canonicalize, CanonicalizeMode, UUPathExt};
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
|
@ -155,8 +154,8 @@ fn copy(matches: getopts::Matches) {
|
||||||
|
|
||||||
pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> Result<bool> {
|
pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> Result<bool> {
|
||||||
// We have to take symlinks and relative paths into account.
|
// We have to take symlinks and relative paths into account.
|
||||||
let pathbuf1 = try!(fs::canonicalize(p1));
|
let pathbuf1 = try!(canonicalize(p1, CanonicalizeMode::Normal));
|
||||||
let pathbuf2 = try!(fs::canonicalize(p2));
|
let pathbuf2 = try!(canonicalize(p2, CanonicalizeMode::Normal));
|
||||||
|
|
||||||
Ok(pathbuf1 == pathbuf2)
|
Ok(pathbuf1 == pathbuf2)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,120 +11,22 @@
|
||||||
|
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
|
||||||
use std::env;
|
use std::fs;
|
||||||
use std::fs::{metadata, read_link};
|
use std::io::Write;
|
||||||
use std::io::{Error, ErrorKind, Result, Write};
|
use std::path::PathBuf;
|
||||||
use std::path::{Component, PathBuf};
|
|
||||||
|
|
||||||
use CanonicalizeMode::{None, Normal, Existing, Missing};
|
|
||||||
|
|
||||||
#[path = "../common/util.rs"]
|
#[path = "../common/util.rs"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
#[path = "../common/filesystem.rs"]
|
||||||
|
mod filesystem;
|
||||||
|
|
||||||
|
use filesystem::{canonicalize, CanonicalizeMode};
|
||||||
|
|
||||||
const NAME: &'static str = "readlink";
|
const NAME: &'static str = "readlink";
|
||||||
const VERSION: &'static str = "0.0.1";
|
const VERSION: &'static str = "0.0.1";
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum CanonicalizeMode {
|
|
||||||
None,
|
|
||||||
Normal,
|
|
||||||
Existing,
|
|
||||||
Missing,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve(original: &PathBuf) -> Result<PathBuf> {
|
|
||||||
const MAX_LINKS_FOLLOWED: u32 = 255;
|
|
||||||
let mut followed = 0;
|
|
||||||
let mut result = original.clone();
|
|
||||||
loop {
|
|
||||||
if followed == MAX_LINKS_FOLLOWED {
|
|
||||||
return Err(Error::new(ErrorKind::InvalidInput, "maximum links followed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
match metadata(&result) {
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
Ok(ref m) if !m.file_type().is_symlink() => break,
|
|
||||||
Ok(..) => {
|
|
||||||
followed += 1;
|
|
||||||
match read_link(&result) {
|
|
||||||
Ok(path) => {
|
|
||||||
result.pop();
|
|
||||||
result.push(path);
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn canonicalize(original: &PathBuf, can_mode: &CanonicalizeMode) -> Result<PathBuf> {
|
|
||||||
// Create an absolute path
|
|
||||||
let original = if original.as_path().is_absolute() {
|
|
||||||
original.clone()
|
|
||||||
} 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 == None {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match resolve(&result) {
|
|
||||||
Err(e) => match *can_mode {
|
|
||||||
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 == Existing { return Err(e); } },
|
|
||||||
Ok(path) => {
|
|
||||||
result.pop();
|
|
||||||
result.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uumain(args: Vec<String>) -> i32 {
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let mut opts = getopts::Options::new();
|
let mut opts = getopts::Options::new();
|
||||||
|
|
||||||
|
@ -164,17 +66,17 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let silent = matches.opt_present("silent") || matches.opt_present("quiet");
|
let silent = matches.opt_present("silent") || matches.opt_present("quiet");
|
||||||
let verbose = matches.opt_present("verbose");
|
let verbose = matches.opt_present("verbose");
|
||||||
|
|
||||||
let mut can_mode = None;
|
let mut can_mode = CanonicalizeMode::None;
|
||||||
if matches.opt_present("canonicalize") {
|
if matches.opt_present("canonicalize") {
|
||||||
can_mode = Normal;
|
can_mode = CanonicalizeMode::Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.opt_present("canonicalize-existing") {
|
if matches.opt_present("canonicalize-existing") {
|
||||||
can_mode = Existing;
|
can_mode = CanonicalizeMode::Existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.opt_present("canonicalize-missing") {
|
if matches.opt_present("canonicalize-missing") {
|
||||||
can_mode = Missing;
|
can_mode = CanonicalizeMode::Missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
let files = matches.free;
|
let files = matches.free;
|
||||||
|
@ -191,8 +93,8 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
|
||||||
for f in files.iter() {
|
for f in files.iter() {
|
||||||
let p = PathBuf::from(f);
|
let p = PathBuf::from(f);
|
||||||
if can_mode == None {
|
if can_mode == CanonicalizeMode::None {
|
||||||
match read_link(&p) {
|
match fs::read_link(&p) {
|
||||||
Ok(path) => show(&path, no_newline, use_zero),
|
Ok(path) => show(&path, no_newline, use_zero),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if verbose {
|
if verbose {
|
||||||
|
@ -202,7 +104,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match canonicalize(&p, &can_mode) {
|
match canonicalize(&p, can_mode) {
|
||||||
Ok(path) => show(&path, no_newline, use_zero),
|
Ok(path) => show(&path, no_newline, use_zero),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if verbose {
|
if verbose {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#![crate_name= "realpath"]
|
#![crate_name= "realpath"]
|
||||||
#![feature(fs_canonicalize)]
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
|
@ -17,7 +16,14 @@ use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
#[path = "../common/util.rs"] #[macro_use] mod util;
|
#[path = "../common/util.rs"]
|
||||||
|
#[macro_use]
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
#[path = "../common/filesystem.rs"]
|
||||||
|
mod filesystem;
|
||||||
|
|
||||||
|
use filesystem::{canonicalize, CanonicalizeMode};
|
||||||
|
|
||||||
static NAME: &'static str = "realpath";
|
static NAME: &'static str = "realpath";
|
||||||
static VERSION: &'static str = "1.0.0";
|
static VERSION: &'static str = "1.0.0";
|
||||||
|
@ -63,7 +69,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
|
||||||
fn resolve_path(path: &str, strip: bool, zero: bool, quiet: bool) -> bool {
|
fn resolve_path(path: &str, strip: bool, zero: bool, quiet: bool) -> bool {
|
||||||
let p = Path::new(path).to_path_buf();
|
let p = Path::new(path).to_path_buf();
|
||||||
let abs = fs::canonicalize(p).unwrap();
|
let abs = canonicalize(p, CanonicalizeMode::Normal).unwrap();
|
||||||
|
|
||||||
if strip {
|
if strip {
|
||||||
if zero {
|
if zero {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#![crate_name = "relpath"]
|
#![crate_name = "relpath"]
|
||||||
#![feature(fs_canonicalize)]
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
|
@ -14,11 +13,17 @@ extern crate getopts;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
#[path = "../common/util.rs"] #[macro_use] mod util;
|
#[path = "../common/util.rs"]
|
||||||
|
#[macro_use]
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
#[path = "../common/filesystem.rs"]
|
||||||
|
mod filesystem;
|
||||||
|
|
||||||
|
use filesystem::{canonicalize, CanonicalizeMode};
|
||||||
|
|
||||||
static NAME: &'static str = "relpath";
|
static NAME: &'static str = "relpath";
|
||||||
static VERSION: &'static str = "1.0.0";
|
static VERSION: &'static str = "1.0.0";
|
||||||
|
@ -54,12 +59,12 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
} else {
|
} else {
|
||||||
env::current_dir().unwrap()
|
env::current_dir().unwrap()
|
||||||
};
|
};
|
||||||
let absto = fs::canonicalize(to).unwrap();
|
let absto = canonicalize(to, CanonicalizeMode::Normal).unwrap();
|
||||||
let absfrom = fs::canonicalize(from).unwrap();
|
let absfrom = canonicalize(from, CanonicalizeMode::Normal).unwrap();
|
||||||
|
|
||||||
if matches.opt_present("d") {
|
if matches.opt_present("d") {
|
||||||
let base = Path::new(&matches.opt_str("d").unwrap()).to_path_buf();
|
let base = Path::new(&matches.opt_str("d").unwrap()).to_path_buf();
|
||||||
let absbase = fs::canonicalize(base).unwrap();
|
let absbase = canonicalize(base, CanonicalizeMode::Normal).unwrap();
|
||||||
if !absto.as_path().starts_with(absbase.as_path()) || !absfrom.as_path().starts_with(absbase.as_path()) {
|
if !absto.as_path().starts_with(absbase.as_path()) || !absfrom.as_path().starts_with(absbase.as_path()) {
|
||||||
println!("{}", absto.display());
|
println!("{}", absto.display());
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#![crate_name = "stdbuf"]
|
#![crate_name = "stdbuf"]
|
||||||
#![feature(fs_canonicalize, negate_unsigned)]
|
#![feature(negate_unsigned)]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
|
@ -15,7 +15,6 @@ extern crate libc;
|
||||||
|
|
||||||
use getopts::{Matches, Options};
|
use getopts::{Matches, Options};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::os::unix::process::ExitStatusExt;
|
use std::os::unix::process::ExitStatusExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -28,7 +27,7 @@ mod util;
|
||||||
#[path = "../common/filesystem.rs"]
|
#[path = "../common/filesystem.rs"]
|
||||||
mod filesystem;
|
mod filesystem;
|
||||||
|
|
||||||
use filesystem::UUPathExt;
|
use filesystem::{canonicalize, CanonicalizeMode, UUPathExt};
|
||||||
|
|
||||||
static NAME: &'static str = "stdbuf";
|
static NAME: &'static str = "stdbuf";
|
||||||
static VERSION: &'static str = "1.0.0";
|
static VERSION: &'static str = "1.0.0";
|
||||||
|
@ -195,7 +194,7 @@ fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: Buffer
|
||||||
|
|
||||||
fn exe_path() -> io::Result<PathBuf> {
|
fn exe_path() -> io::Result<PathBuf> {
|
||||||
let exe_path = try!(env::current_exe());
|
let exe_path = try!(env::current_exe());
|
||||||
let absolute_path = try!(fs::canonicalize(exe_path.as_path()));
|
let absolute_path = try!(canonicalize(exe_path, CanonicalizeMode::Normal));
|
||||||
Ok(match absolute_path.parent() {
|
Ok(match absolute_path.parent() {
|
||||||
Some(p) => p.to_path_buf(),
|
Some(p) => p.to_path_buf(),
|
||||||
None => absolute_path.clone()
|
None => absolute_path.clone()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue