diff --git a/Cargo.toml b/Cargo.toml index 5ebd9bf35..fc003d5a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,6 +135,10 @@ path = "pwd/pwd.rs" name = "realpath" path = "realpath/realpath.rs" +[[bin]] +name = "relpath" +path = "relpath/relpath.rs" + [[bin]] name = "rm" path = "rm/rm.rs" diff --git a/Makefile b/Makefile index 95d89a65e..a5186626b 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ PROGS := \ printenv \ pwd \ realpath \ + relpath \ rm \ rmdir \ sleep \ diff --git a/README.md b/README.md index 2caf82425..1568ee924 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,6 @@ To do - printf - ptx - readlink -- relpath - remove - runcon - setuidgid diff --git a/relpath/relpath.rs b/relpath/relpath.rs new file mode 100644 index 000000000..7ae9acd90 --- /dev/null +++ b/relpath/relpath.rs @@ -0,0 +1,100 @@ +#![crate_id = "relpath#1.0.0"] + +/* + * This file is part of the uutils coreutils package. + * + * (c) 2014 Vsevolod Velichko + * + * 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) -> 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) + ); +} diff --git a/uutils/uutils.rs b/uutils/uutils.rs index 58cdfc1ac..1c710a6aa 100644 --- a/uutils/uutils.rs +++ b/uutils/uutils.rs @@ -43,6 +43,7 @@ extern crate paste; extern crate printenv; extern crate pwd; extern crate realpath; +extern crate relpath; extern crate rm; extern crate rmdir; extern crate seq; @@ -113,6 +114,7 @@ fn util_map() -> HashMap<&str, fn(Vec) -> int> { 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);