Merge pull request #319 from torkve/master

realpath and relpath implementation
This commit is contained in:
Arcterus 2014-06-29 13:03:52 -07:00
commit 48727e4ee8
7 changed files with 262 additions and 2 deletions

View file

@ -131,6 +131,14 @@ path = "printenv/printenv.rs"
name = "pwd"
path = "pwd/pwd.rs"
[[bin]]
name = "realpath"
path = "realpath/realpath.rs"
[[bin]]
name = "relpath"
path = "relpath/relpath.rs"
[[bin]]
name = "rm"
path = "rm/rm.rs"

View file

@ -29,6 +29,8 @@ PROGS := \
paste \
printenv \
pwd \
realpath \
relpath \
rm \
rmdir \
sleep \

View file

@ -139,8 +139,6 @@ To do
- printf
- ptx
- readlink
- realpath
- relpath
- remove
- runcon
- setuidgid

View file

@ -19,6 +19,16 @@ macro_rules! show_error(
})
)
#[macro_export]
macro_rules! eprint(
($($args:expr),+) => (safe_write!(&mut ::std::io::stderr(), $($args),+))
)
#[macro_export]
macro_rules! eprintln(
($($args:expr),+) => (safe_writeln!(&mut ::std::io::stderr(), $($args),+))
)
#[macro_export]
macro_rules! show_warning(
($($args:expr),+) => ({

138
realpath/realpath.rs Normal file
View file

@ -0,0 +1,138 @@
#![crate_id = "realpath#1.0.0"]
/*
* This file is part of the uutils coreutils package.
*
* (c) 2014 Vsevolod Velichko <torkvemada@sorokdva.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#![feature(macro_rules)]
extern crate getopts;
extern crate libc;
use getopts::{optflag, getopts, usage};
#[path = "../common/util.rs"] mod util;
static NAME: &'static str = "realpath";
static VERSION: &'static str = "1.0.0";
pub fn uumain(args: Vec<String>) -> int {
let program = args.get(0);
let options = [
optflag("h", "help", "Show help and exit"),
optflag("V", "version", "Show version and exit"),
optflag("s", "strip", "Only strip '.' and '..' components, but don't resolve symbolic links"),
optflag("z", "zero", "Separate output filenames with \\0 rather than newline"),
optflag("q", "quiet", "Do not print warnings for invalid paths"),
];
let opts = match getopts(args.tail(), options) {
Ok(m) => m,
Err(f) => {
show_error!("{}", f);
show_usage(program.as_slice(), options);
return 1
}
};
if opts.opt_present("V") { version(); return 0 }
if opts.opt_present("h") { show_usage(program.as_slice(), options); return 0 }
if opts.free.len() == 0 {
show_error!("Missing operand: FILENAME, at least one is required");
println!("Try `{:s} --help` for more information.", program.as_slice());
return 1
}
let strip = opts.opt_present("s");
let zero = opts.opt_present("z");
let quiet = opts.opt_present("q");
let mut retcode = 0;
opts.free.iter().map(|x|
if !resolve_path(x.as_slice(), strip, zero, quiet) {
retcode = 1
}
).last();
retcode
}
fn resolve_path(path: &str, strip: bool, zero: bool, quiet: bool) -> bool {
let p = Path::new(path);
let abs = std::os::make_absolute(&p);
if strip {
if zero {
print!("{}\0", abs.display());
} else {
println!("{}", abs.display())
}
return true
}
let mut result = match abs.root_path() {
None => crash!(2, "Broken path parse! Report to developers: {}", path),
Some(x) => x,
};
let mut links_left = 256;
for part in abs.components() {
result.push(part);
loop {
if links_left == 0 {
if !quiet { show_error!("Too many symbolic links: {}", path) };
return false
}
match std::io::fs::lstat(&result) {
Err(_) => break,
Ok(ref s) if s.kind != std::io::TypeSymlink => break,
Ok(_) => {
links_left -= 1;
match std::io::fs::readlink(&result) {
Ok(x) => {
result.pop();
result.push(x);
},
_ => {
if !quiet {
show_error!("Invalid path: {}", path)
};
return false
},
}
}
}
}
}
if zero {
print!("{}\0", result.display());
} else {
println!("{}", result.display());
}
true
}
fn version() {
println!("{} v{}", NAME, VERSION)
}
fn show_usage(program: &str, options: &[getopts::OptGroup]) {
version();
println!("Usage:");
println!(" {:s} [-s|--strip] [-z|--zero] FILENAME…", program);
println!(" {:s} -V|--version", program);
println!(" {:s} -h|--help", program);
println!("");
print!("{:s}", usage(
"Convert each FILENAME to the absolute path.\n\
All the symbolic links will be resolved, resulting path will contain no special components like '.' or '..'.\n\
Each path component must exist or resolution will fail and non-zero exit status returned.\n\
Each resolved FILENAME will be written to the standard output, one per line.", options)
);
}

100
relpath/relpath.rs Normal file
View file

@ -0,0 +1,100 @@
#![crate_id = "relpath#1.0.0"]
/*
* This file is part of the uutils coreutils package.
*
* (c) 2014 Vsevolod Velichko <torkvemada@sorokdva.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#![feature(macro_rules)]
extern crate getopts;
extern crate libc;
use getopts::{optflag, optopt, getopts, usage};
#[path = "../common/util.rs"] mod util;
static NAME: &'static str = "relpath";
static VERSION: &'static str = "1.0.0";
pub fn uumain(args: Vec<String>) -> int {
let program = args.get(0);
let options = [
optflag("h", "help", "Show help and exit"),
optflag("V", "version", "Show version and exit"),
optopt("d", "", "If any of FROM and TO is not subpath of DIR, output absolute path instead of relative", "DIR"),
];
let opts = match getopts(args.tail(), options) {
Ok(m) => m,
Err(f) => {
show_error!("{}", f);
show_usage(program.as_slice(), options);
return 1
}
};
if opts.opt_present("V") { version(); return 0 }
if opts.opt_present("h") { show_usage(program.as_slice(), options); return 0 }
if opts.free.len() == 0 {
show_error!("Missing operand: TO");
println!("Try `{:s} --help` for more information.", program.as_slice());
return 1
}
let to = Path::new(opts.free.get(0).as_slice());
let from = if opts.free.len() > 1 {
Path::new(opts.free.get(1).as_slice())
} else {
std::os::getcwd()
};
let absto = std::os::make_absolute(&to);
let absfrom = std::os::make_absolute(&from);
if opts.opt_present("d") {
let base = Path::new(opts.opt_str("d").unwrap());
let absbase = std::os::make_absolute(&base);
if !absbase.is_ancestor_of(&absto) || !absbase.is_ancestor_of(&absfrom) {
println!("{}", absto.display());
return 0
}
}
let mut suffixPos = 0;
absfrom.components()
.zip(absto.components())
.take_while(
|&(f, t)| if f == t {
suffixPos += 1; true
} else {
false
}).last();
let mut result = Path::new("");
absfrom.components().skip(suffixPos).map(|_| result.push("..")).last();
absto.components().skip(suffixPos).map(|x| result.push(x)).last();
println!("{}", result.display());
0
}
fn version() {
println!("{} v{}", NAME, VERSION)
}
fn show_usage(program: &str, options: &[getopts::OptGroup]) {
version();
println!("Usage:");
println!(" {:s} [-d DIR] TO [FROM]", program);
println!(" {:s} -V|--version", program);
println!(" {:s} -h|--help", program);
println!("");
print!("{:s}", usage(
"Convert TO destination to the relative path from the FROM dir.\n\
If FROM path is omitted, current working dir will be used.", options)
);
}

View file

@ -42,6 +42,8 @@ extern crate nohup;
extern crate paste;
extern crate printenv;
extern crate pwd;
extern crate realpath;
extern crate relpath;
extern crate rm;
extern crate rmdir;
extern crate seq;
@ -111,6 +113,8 @@ fn util_map() -> HashMap<&str, fn(Vec<String>) -> int> {
map.insert("paste", paste::uumain);
map.insert("printenv", printenv::uumain);
map.insert("pwd", pwd::uumain);
map.insert("realpath", realpath::uumain);
map.insert("relpath", relpath::uumain);
map.insert("rm", rm::uumain);
map.insert("rmdir", rmdir::uumain);
map.insert("seq", seq::uumain);