#![crate_type = "bin"]
#![crate_name = "ln"]
#![feature(path_ext,slice_splits)]
static UTIL: Prog = Prog { name: "ln", vers: "0.1.0", yr: "2015" };
extern crate pgetopts;
extern crate rpf;
use pgetopts::{Options};
use rpf::{AsPath,Status,Color,Styled,PathMod,Prog};
use std::env;
use std::fs;
#[cfg(target_family = "unix")]
use std::os;
use std::path::{PathBuf};
use std::fs::{PathExt};
#[cfg(target_family = "windows")]
use std::os::windows;
struct Opts {
verbose: bool,
force: bool,
inter: bool,
}
#[cfg(target_family = "unix")]
fn create_sym_link(from: &PathBuf, to: &PathBuf, options: &Opts) {
match os::unix::fs::symlink(from, to) {
Ok(_) => {
if options.verbose {
println!("{} {} {}", from.as_str().bold(),
"->".paint(Color::Green), to.as_str().bold());
}
},
Err(e) => {
Status::Error.err(UTIL.name, e.to_string());
}
};
}
#[cfg(target_family = "windows")]
fn create_sym_link(from: &PathBuf, to: &PathBuf, options: &Opts) {
if from.is_file() {
match windows::fs::symlink_file(from, to) {
Ok(_) => {
if options.verbose {
println!("{} {} {}", from.as_str().bold(),
"->".paint(Color::Green), to.as_str().bold());
}
},
Err(e) => {
Status::Error.err(UTIL.name, e.to_string());
}
};
} else if from.is_dir() {
match windows::fs::symlink_dir(from, to) {
Ok(_) => {
if options.verbose {
println!("{} {} {}", from.as_str().bold(),
"->".paint(Color::Green), to.as_str().bold());
}
},
Err(e) => {
Status::Error.err(UTIL.name, e.to_string());
}
};
}
}
fn sym_link_files(files: Vec<String>, to: &PathBuf, options: &Opts) {
for item in files.iter() {
if !to.is_dir() {
Status::Error.err(UTIL.name, String::from(String::from(
to.as_str()) + " is not a directory"));
} else {
soft_link(&item.as_pathbuf(), &to.join(item.as_pathbuf()), options);
}
}
}
fn hard_link_files(files: Vec<String>, to: &PathBuf, options: &Opts) {
for item in files.iter() {
if !to.is_dir() {
Status::Error.err(UTIL.name, String::from(String::from(
to.as_str()) + " is not a directory"));
} else {
let to_path = item.as_pathbuf();
hard_link(&to_path, &to.join(&to_path), options);
}
}
}
fn soft_link(from: &PathBuf, to: &PathBuf, options: &Opts) {
if !from.exists() {
Status::Error.err(UTIL.name, String::from("\'".to_string() +
from.as_str() +
"\' does not exist"));
}
if !options.force {
create_sym_link(from, to, options);
} else {
fs::remove_file(to).ok();
create_sym_link(from, to, options);
}
}
fn hard_link(from: &PathBuf, to: &PathBuf, options: &Opts) {
if !from.exists() {
Status::Error.err(UTIL.name, String::from("\'".to_string() +
from.as_str() +
"\' does not exist"));
}
if !options.force {
match fs::hard_link(from, to) {
Ok(_) => {
if options.verbose {
println!("{} {} {}", from.as_str().bold(),
"->".paint(Color::Green), to.as_str().bold());
}
},
Err(e) => {
Status::Error.err(UTIL.name, e.to_string());
}
};
} else {
if to.as_path().exists() {
fs::remove_file(to).ok();
match fs::hard_link(from, to) {
Ok(_) => { },
Err(e) => {
Status::Error.err(UTIL.name, e.to_string());
}
};
}
}
}
fn print_usage(opts: Options) {
print!("{}: {} {} {} {}\n\
Create links between files", "Usage".bold(), UTIL.name.bold(),
"[OPTION]".underline(), "TARGET".underline(), "LINK_NAME".underline());
println!("{}", opts.options());
}
fn main() {
let args: Vec<String> = env::args().collect();
let mut opts = Options::new();
opts.optflag("f", "force", "Force operation. Removes existsing target files");
opts.optflag("h", "help", "Print help information");
opts.optflag("p", "physical", "Create a hard link");
opts.optflag("s", "soft", "Create a symbolic link");
opts.optflag("v", "verbose", "Verbose print the operation");
opts.optflag("", "version", "Print version information");
let matches = match opts.parse(&args[1..]) {
Ok(m) => { m },
Err(f) => {
Status::OptError.err(UTIL.name, f.to_string());
panic!(f.to_string());
}
};
if matches.opt_present("h") {
print_usage(opts);
} else if matches.opt_present("version") {
UTIL.copyright("Copyright (C) 2015 core-utils developers\n\
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
This is free software: you are free to change and redistribute it.\n\
There is NO WARRANTY, to the extent permitted by law.\n\n",
vec!["Alberto Corona"]);
} else {
let options = &Opts {
verbose: matches.opt_present("v"),
force: matches.opt_present("f"),
inter: false,
};
let src = match matches.free.first() {
Some(s) => { s.as_pathbuf() },
None => {
UTIL.prog_try();
Status::OptError.exit();
panic!();
},
};
let dest = matches.free.last().unwrap().as_pathbuf();
if matches.free.len() == 1 {
println!("{}: No link name for {}", UTIL.name, src.display());
Status::ArgError.exit();
}
let files = matches.free.split_last().unwrap().1.to_vec();
if matches.opt_present("s") {
if matches.free.len() > 2 {
sym_link_files(files, &dest, options);
} else {
soft_link(&src, &matches.free[1].as_pathbuf(), options);
}
} else if matches.opt_present("p") {
if matches.free.len() > 2 {
hard_link_files(files, &dest, options);
} else {
hard_link(&src, &matches.free[1].as_pathbuf(), options);
}
}
}
}