From f4ea1a9cc519ad4fd3b6418a746081214d6636b6 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 2 Jun 2017 13:19:33 +0200 Subject: [PATCH 01/31] cp: added -r/--recursive flag --- src/cp/Cargo.toml | 1 + src/cp/cp.rs | 149 +++++++++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 42 deletions(-) diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index 2e7758e49..ba10e498e 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -11,6 +11,7 @@ path = "cp.rs" getopts = "*" libc = "*" uucore = { path="../uucore" } +walkdir = "*" [[bin]] name = "cp" diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 102f9eda3..4b190307d 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -14,6 +14,10 @@ extern crate getopts; #[macro_use] extern crate uucore; +extern crate walkdir; + +use walkdir::WalkDir; + use getopts::Options; use std::fs; use std::io::{ErrorKind, Result, Write}; @@ -34,9 +38,15 @@ pub fn uumain(args: Vec) -> i32 { let mut opts = Options::new(); opts.optflag("h", "help", "display this help and exit"); + opts.optflag("r", "recursive", "copy directories recursively"); opts.optflag("", "version", "output version information and exit"); - opts.optopt("t", "target-directory", "copy all SOURCE arguments into DIRECTORY", "DEST"); - opts.optflag("T", "no-target-directory", "Treat DEST as a regular file and not a directory"); + opts.optopt("t", + "target-directory", + "copy all SOURCE arguments into DIRECTORY", + "DEST"); + opts.optflag("T", + "no-target-directory", + "Treat DEST as a regular file and not a directory"); opts.optflag("v", "verbose", "explicitly state what is being done"); let matches = match opts.parse(&args[1..]) { @@ -44,7 +54,7 @@ pub fn uumain(args: Vec) -> i32 { Err(e) => { show_error!("{}", e); panic!() - }, + } }; let usage = opts.usage("Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."); let mode = if matches.opt_present("version") { @@ -56,8 +66,8 @@ pub fn uumain(args: Vec) -> i32 { }; match mode { - Mode::Copy => copy(matches), - Mode::Help => help(&usage), + Mode::Copy => copy(matches), + Mode::Help => help(&usage), Mode::Version => version(), } @@ -74,7 +84,10 @@ fn help(usage: &str) { or: {0} SOURCE... DIRECTORY\n \ or: {0} -t DIRECTORY SOURCE...\n\ \n\ - {2}", NAME, VERSION, usage); + {2}", + NAME, + VERSION, + usage); println!("{}", msg); } @@ -84,12 +97,19 @@ fn copy(matches: getopts::Matches) { show_error!("Missing SOURCE or DEST argument. Try --help."); panic!() } else if !matches.opt_present("target-directory") { - matches.free[..matches.free.len() - 1].iter().cloned().collect() + matches.free[..matches.free.len() - 1] + .iter() + .cloned() + .collect() } else { matches.free.iter().cloned().collect() }; + let recursive: bool = matches.opt_present("recursive"); + let dest_str = if matches.opt_present("target-directory") { - matches.opt_str("target-directory").expect("Option -t/--target-directory requires an argument") + matches + .opt_str("target-directory") + .expect("Option -t/--target-directory requires an argument") } else { matches.free[matches.free.len() - 1].clone() }; @@ -100,7 +120,8 @@ fn copy(matches: getopts::Matches) { //the argument to the -t/--target-directory= options let path = Path::new(&dest_str); if !path.is_dir() && matches.opt_present("target-directory") { - show_error!("Target {} is not a directory", matches.opt_str("target-directory").unwrap()); + show_error!("Target {} is not a directory", + matches.opt_str("target-directory").unwrap()); panic!() } else { path @@ -110,38 +131,59 @@ fn copy(matches: getopts::Matches) { assert!(sources.len() >= 1); if matches.opt_present("no-target-directory") && dest.is_dir() { - show_error!("Can't overwrite directory {} with non-directory", dest.display()); + show_error!("Can't overwrite directory {} with non-directory", + dest.display()); panic!() } if sources.len() == 1 { let source = Path::new(&sources[0]); - let same_file = paths_refer_to_same_file(source, dest).unwrap_or_else(|err| { - match err.kind() { - ErrorKind::NotFound => false, - _ => { - show_error!("{}", err); - panic!() - } + let same_file = + paths_refer_to_same_file(source, dest).unwrap_or_else(|err| match err.kind() { + ErrorKind::NotFound => false, + _ => { + show_error!("{}", err); + panic!() } - }); + }); if same_file { show_error!("\"{}\" and \"{}\" are the same file", - source.display(), - dest.display()); + source.display(), + dest.display()); panic!(); } let mut full_dest = dest.to_path_buf(); - if dest.is_dir() { - full_dest.push(source.file_name().unwrap()); //the destination path is the destination - } // directory + the file name we're copying - if verbose { - println!("{} -> {}", source.display(), full_dest.display()); - } - if let Err(err) = fs::copy(source, full_dest) { - show_error!("{}", err); - panic!(); + if recursive { + for entry in WalkDir::new(source) { + let entry = entry.unwrap(); + if entry.path().is_dir() { + let mut dst_path = full_dest.clone(); + dst_path.push(entry.path()); + if let Err(err) = fs::create_dir(dst_path) { + show_error!("{}", err); + panic!(); + } + } else { + let mut dst_path = full_dest.clone(); + dst_path.push(entry.path()); + if let Err(err) = fs::copy(entry.path(), dst_path) { + show_error!("{}", err); + panic!(); + } + } + } + } else { + if dest.is_dir() { + full_dest.push(source.file_name().unwrap()); //the destination path is the destination + } // directory + the file name we're copying + if verbose { + println!("{} -> {}", source.display(), full_dest.display()); + } + if let Err(err) = fs::copy(source, full_dest) { + show_error!("{}", err); + panic!(); + } } } else { if !dest.is_dir() { @@ -151,24 +193,47 @@ fn copy(matches: getopts::Matches) { for src in &sources { let source = Path::new(&src); - if !source.is_file() { - show_error!("\"{}\" is not a file", source.display()); - continue; - } + if !recursive { + if !source.is_file() { + show_error!("\"{}\" is not a file", source.display()); + continue; + } - let mut full_dest = dest.to_path_buf(); + let mut full_dest = dest.to_path_buf(); - full_dest.push(source.file_name().unwrap()); + full_dest.push(source.file_name().unwrap()); - if verbose { - println!("{} -> {}", source.display(), full_dest.display()); - } + if verbose { + println!("{} -> {}", source.display(), full_dest.display()); + } - let io_result = fs::copy(source, full_dest); + let io_result = fs::copy(source, full_dest); - if let Err(err) = io_result { - show_error!("{}", err); - panic!() + if let Err(err) = io_result { + show_error!("{}", err); + panic!() + } + } else { + for entry in WalkDir::new(source) { + let entry = entry.unwrap(); + let full_dest = dest.to_path_buf(); + + if entry.path().is_dir() { + let mut dst_path = full_dest.clone(); + dst_path.push(entry.path()); + if let Err(err) = fs::create_dir(dst_path) { + show_error!("{}", err); + panic!(); + } + } else { + let mut dst_path = full_dest.clone(); + dst_path.push(entry.path()); + if let Err(err) = fs::copy(entry.path(), dst_path) { + show_error!("{}", err); + panic!(); + } + } + } } } } From 1121146de2842298b41ccfbc43bc575ef6bacd2e Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 2 Jun 2017 13:37:31 +0200 Subject: [PATCH 02/31] cp: add test for -r/--recursive --- tests/test_cp.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_cp.rs b/tests/test_cp.rs index 4429686f6..7aa01b5c3 100644 --- a/tests/test_cp.rs +++ b/tests/test_cp.rs @@ -4,6 +4,7 @@ static TEST_HELLO_WORLD_SOURCE: &'static str = "hello_world.txt"; static TEST_HELLO_WORLD_DEST: &'static str = "copy_of_hello_world.txt"; static TEST_COPY_TO_FOLDER: &'static str = "hello_dir/"; static TEST_COPY_TO_FOLDER_FILE: &'static str = "hello_dir/hello_world.txt"; +static TEST_COPY_FROM_FOLDER: &'static str = "hello_dir_with_file/"; static TEST_COPY_FROM_FOLDER_FILE: &'static str = "hello_dir_with_file/hello_world.txt"; #[test] @@ -22,6 +23,31 @@ fn test_cp_cp() { assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); } +#[test] +fn test_cp_recurse() { + //let (at, mut ucmd) = at_and_ucmd!(); + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + // Invoke our binary to make the copy. + let result_to_dir = scene.ucmd() + .arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_COPY_TO_FOLDER) + .run(); + assert!(result_to_dir.success); + assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); + + let result = scene.ucmd() + .arg("-r") + .arg(TEST_COPY_FROM_FOLDER) + .arg(TEST_COPY_TO_FOLDER) + .run(); + + assert!(result.success); + // Check the content of the destination file that was copied. + assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); +} + #[test] fn test_cp_with_dirs_t() { let (at, mut ucmd) = at_and_ucmd!(); From e8073d2596a7927d02600b517b1aa911197fc284 Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Sat, 15 Jul 2017 14:03:43 -0500 Subject: [PATCH 03/31] pin dependencies --- Cargo.lock | 445 ++++++++++++++++++++++++--------------- Cargo.toml | 18 +- src/base32/Cargo.toml | 2 +- src/chgrp/Cargo.toml | 2 +- src/chmod/Cargo.toml | 4 +- src/chown/Cargo.toml | 4 +- src/chroot/Cargo.toml | 2 +- src/cksum/Cargo.toml | 2 +- src/comm/Cargo.toml | 4 +- src/cp/Cargo.toml | 6 +- src/date/Cargo.toml | 4 +- src/date/main.rs | 2 +- src/dircolors/Cargo.toml | 2 +- src/dirname/Cargo.toml | 2 +- src/du/Cargo.toml | 2 +- src/env/Cargo.toml | 2 +- src/expand/Cargo.toml | 4 +- src/expr/Cargo.toml | 2 +- src/factor/Cargo.toml | 2 +- src/fmt/Cargo.toml | 4 +- src/hashsum/Cargo.toml | 12 +- src/head/Cargo.toml | 2 +- src/hostid/Cargo.toml | 2 +- src/hostname/Cargo.toml | 2 +- src/install/Cargo.toml | 4 +- src/kill/Cargo.toml | 2 +- src/link/Cargo.toml | 2 +- src/ln/Cargo.toml | 2 +- src/logname/Cargo.toml | 2 +- src/ls/Cargo.toml | 14 +- src/mkdir/Cargo.toml | 4 +- src/mkfifo/Cargo.toml | 4 +- src/mknod/Cargo.toml | 2 +- src/mktemp/Cargo.toml | 4 +- src/more/Cargo.toml | 4 +- src/mv/Cargo.toml | 2 +- src/nice/Cargo.toml | 4 +- src/nl/Cargo.toml | 12 +- src/nohup/Cargo.toml | 4 +- src/nproc/Cargo.toml | 4 +- src/od/Cargo.toml | 8 +- src/paste/Cargo.toml | 2 +- src/pathchk/Cargo.toml | 4 +- src/printenv/Cargo.toml | 2 +- src/printf/Cargo.toml | 2 +- src/printf/printf.rs | 67 +++--- src/ptx/Cargo.toml | 12 +- src/pwd/Cargo.toml | 2 +- src/readlink/Cargo.toml | 4 +- src/realpath/Cargo.toml | 2 +- src/relpath/Cargo.toml | 2 +- src/rm/Cargo.toml | 2 +- src/rmdir/Cargo.toml | 2 +- src/seq/Cargo.toml | 2 +- src/shred/Cargo.toml | 8 +- src/shuf/Cargo.toml | 4 +- src/sleep/Cargo.toml | 2 +- src/sort/Cargo.toml | 6 +- src/sort/sort.rs | 1 - src/split/Cargo.toml | 2 +- src/stat/Cargo.toml | 4 +- src/stdbuf/Cargo.toml | 2 +- src/sum/Cargo.toml | 2 +- src/sync/Cargo.toml | 8 +- src/sync/sync.rs | 4 + src/tac/Cargo.toml | 2 +- src/tail/Cargo.toml | 8 +- src/tee/Cargo.toml | 4 +- src/test/Cargo.toml | 2 +- src/timeout/Cargo.toml | 6 +- src/touch/Cargo.toml | 6 +- src/tr/Cargo.toml | 6 +- src/truncate/Cargo.toml | 2 +- src/tsort/Cargo.toml | 2 +- src/tty/Cargo.toml | 4 +- src/unexpand/Cargo.toml | 4 +- src/uniq/Cargo.toml | 2 +- src/unlink/Cargo.toml | 4 +- src/uptime/Cargo.toml | 2 +- src/users/Cargo.toml | 2 +- src/users/users.rs | 3 +- src/uucore/Cargo.toml | 4 +- src/wc/Cargo.toml | 2 +- src/who/Cargo.toml | 2 +- src/whoami/Cargo.toml | 6 +- src/yes/Cargo.toml | 2 +- 86 files changed, 466 insertions(+), 368 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a5898451..0e3a113b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,8 +36,8 @@ dependencies = [ "id 0.0.1", "install 0.0.1", "kill 0.0.1", - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "link 0.0.1", "ln 0.0.1", "logname 0.0.1", @@ -82,7 +82,7 @@ dependencies = [ "tee 0.0.1", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "test 0.0.1", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "timeout 0.0.1", "touch 0.0.1", "tr 0.0.1", @@ -122,6 +122,14 @@ dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "aho-corasick" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ansi_term" version = "0.9.0" @@ -140,7 +148,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -170,17 +188,12 @@ name = "bit-set" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bit-vec" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -188,23 +201,28 @@ name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" -version = "0.5.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cat" version = "0.0.1" dependencies = [ - "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "cfg-if" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -212,14 +230,14 @@ name = "chgrp" version = "0.0.1" dependencies = [ "uucore 0.0.1", - "walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "chmod" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", "walker 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -238,8 +256,8 @@ name = "chrono" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -254,23 +272,24 @@ dependencies = [ name = "cksum" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "clap" -version = "2.20.0" +version = "2.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -278,7 +297,7 @@ name = "comm" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -287,8 +306,9 @@ name = "cp" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", + "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -300,7 +320,7 @@ dependencies = [ [[package]] name = "data-encoding" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -308,7 +328,7 @@ name = "date" version = "0.0.1" dependencies = [ "chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -324,7 +344,7 @@ dependencies = [ name = "dirname" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -332,7 +352,7 @@ dependencies = [ name = "du" version = "0.0.1" dependencies = [ - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -345,14 +365,14 @@ dependencies = [ [[package]] name = "either" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "env" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -369,7 +389,7 @@ dependencies = [ name = "expr" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -390,18 +410,23 @@ name = "filetime" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fmt" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] +[[package]] +name = "fnv" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fold" version = "0.0.1" @@ -411,7 +436,7 @@ dependencies = [ [[package]] name = "gcc" -version = "0.3.40" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -433,7 +458,7 @@ dependencies = [ [[package]] name = "half" -version = "0.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -441,11 +466,11 @@ name = "hashsum" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -453,7 +478,7 @@ dependencies = [ name = "head" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -461,7 +486,7 @@ dependencies = [ name = "hostid" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -469,7 +494,7 @@ dependencies = [ name = "hostname" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -485,16 +510,16 @@ name = "install" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "itertools" -version = "0.5.7" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -510,30 +535,30 @@ dependencies = [ name = "kill" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "lazy_static" -version = "0.2.2" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.18" -source = "git+https://github.com/rust-lang/libc.git#fb8587d327ca0a14edbaf7c1656c580efee156d2" +version = "0.2.26" +source = "git+https://github.com/rust-lang/libc.git#c43c0770db6b85ade0b70009464603ff9be4b37d" [[package]] name = "libc" -version = "0.2.18" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "link" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -541,7 +566,7 @@ dependencies = [ name = "ln" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -549,7 +574,7 @@ dependencies = [ name = "logname" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -558,11 +583,11 @@ name = "ls" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "pretty-bytes 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "term_grid 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "termsize 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -572,7 +597,15 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memchr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -580,7 +613,7 @@ name = "mkdir" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -589,7 +622,7 @@ name = "mkfifo" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -598,7 +631,7 @@ name = "mknod" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -608,7 +641,7 @@ version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -617,7 +650,7 @@ name = "more" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -634,20 +667,18 @@ name = "nice" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "nix" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -655,12 +686,12 @@ dependencies = [ name = "nl" version = "0.0.1" dependencies = [ - "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -669,7 +700,7 @@ name = "nohup" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -678,59 +709,59 @@ name = "nproc" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] [[package]] name = "num" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-integer" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-iter" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.5.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "od" version = "0.0.1" dependencies = [ - "byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "half 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -747,7 +778,7 @@ name = "pathchk" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -764,7 +795,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -779,7 +810,7 @@ dependencies = [ name = "printf" version = "0.0.1" dependencies = [ - "itertools 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -787,12 +818,12 @@ dependencies = [ name = "ptx" version = "0.0.1" dependencies = [ - "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -806,7 +837,7 @@ dependencies = [ [[package]] name = "quick-error" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -814,7 +845,7 @@ name = "rand" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -822,7 +853,7 @@ name = "readlink" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -836,7 +867,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.17" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -851,11 +882,28 @@ dependencies = [ "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex-syntax" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "regex-syntax" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "relpath" version = "0.0.1" @@ -885,16 +933,16 @@ name = "rust-crypto" version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-serialize" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -905,6 +953,15 @@ dependencies = [ "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "same-file" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "semver" version = "0.1.20" @@ -912,20 +969,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "semver" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver-parser" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "seq" @@ -941,9 +994,9 @@ version = "0.0.1" dependencies = [ "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -969,8 +1022,8 @@ name = "sort" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", - "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -987,7 +1040,7 @@ name = "stat" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1018,7 +1071,7 @@ version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1037,7 +1090,7 @@ version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1047,7 +1100,7 @@ name = "tee" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1061,11 +1114,11 @@ dependencies = [ [[package]] name = "tempfile" -version = "2.1.4" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1081,11 +1134,11 @@ dependencies = [ [[package]] name = "term_size" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1096,7 +1149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1104,17 +1157,26 @@ dependencies = [ name = "test" version = "0.0.1" dependencies = [ - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] +[[package]] +name = "textwrap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread-id" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1125,14 +1187,23 @@ dependencies = [ "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "time" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1141,8 +1212,8 @@ name = "timeout" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1152,7 +1223,7 @@ version = "0.0.1" dependencies = [ "filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1161,6 +1232,7 @@ name = "tr" version = "0.0.1" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1190,7 +1262,7 @@ name = "tty" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -1238,8 +1310,8 @@ name = "unix_socket" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1247,10 +1319,18 @@ name = "unlink" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "uptime" version = "0.0.1" @@ -1272,19 +1352,24 @@ name = "utf8-ranges" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "utf8-ranges" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "uucore" version = "0.0.1" dependencies = [ - "data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.18 (git+https://github.com/rust-lang/libc.git)", - "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (git+https://github.com/rust-lang/libc.git)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "vec_map" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1303,10 +1388,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1361,65 +1447,76 @@ dependencies = [ [metadata] "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" +"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum atty 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0fd4c0631f06448cc45a6bbb3b710ebb7ff8ccb96a0800c994afe23a70d5df2" +"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" -"checksum bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5b97c2c8e8bbb4251754f559df8af22fb264853c7d009084a576cdf12565089d" -"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" +"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" -"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158b0bd7d75cbb6bf9c25967a48a2e9f77da95876b858eadfabaa99cd069de6e" -"checksum clap 2.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1cb22651881e6379f4492d0d572ecb8022faef8c8aaae285bb18cb307bfa30" -"checksum data-encoding 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f13f03d68d1906eb3222536f5756953e30de4dff4417e5d428414fb8edb26723" -"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91" +"checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771" +"checksum data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867ddbf09de0b73e09ec798972fb7f870495a0893f6f736c1855448c5a56789" +"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922" -"checksum gcc 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "872db9e59486ef2b14f8e8c10e9ef02de2bccef6363d7f34835dedb386b3d950" +"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" +"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum half 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4d07978d54bfc4102f0656a19b2a6b8af375dd6a09db587920d03ea35195d844" -"checksum itertools 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8e8d6f7e00ae9fd6ad3075412717c706d823cebc4a2f127ae43c1744452a087a" +"checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f" +"checksum itertools 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "772a0928a97246167d59a2a4729df5871f1327ab8b36fd24c4224b229cb47b99" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" -"checksum libc 0.2.18 (git+https://github.com/rust-lang/libc.git)" = "" -"checksum libc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "a51822fc847e7a8101514d1d44e354ba2ffa7d4c194dcab48870740e327cac70" +"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" +"checksum libc 0.2.26 (git+https://github.com/rust-lang/libc.git)" = "" +"checksum libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "30885bcb161cf67054244d10d4a7f4835ffd58773bc72e07d35fecf472295503" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" -"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b" -"checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" -"checksum num-integer 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "21e4df1098d1d797d27ef0c69c178c3fab64941559b290fcae198e0825c9c8b5" -"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e" -"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99" -"checksum num_cpus 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6e850c7f35c3de263e6094e819f6b4b9c09190ff4438fc6dec1aef1568547bc" +"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4" +"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" +"checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525" +"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" +"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" +"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" +"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" "checksum pretty-bytes 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3095b93999fae14b4e0bb661c53875a441d9058b7b1a7ba2dfebc104d3776349" -"checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c" +"checksum quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c36987d4978eb1be2e422b1e0423a557923a5c3e7e6f31d5699e9aafaefa469" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" -"checksum redox_syscall 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "29dbdfd4b9df8ab31dec47c6087b7b13cbf4a776f335e4de8efba8288dda075b" +"checksum redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "9df6a71a1e67be2104410736b2389fb8e383c1d7e9e792d629ff13c02867147a" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" +"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" +"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" -"checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -"checksum semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae2ff60ecdb19c255841c066cbfa5f8c2a4ada1eb3ae47c77ab6667128da71f5" -"checksum semver-parser 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e88e43a5a74dd2a11707f9c21dfd4a423c66bd871df813227bb0a3e78f3a1ae9" +"checksum semver 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd61b85a0fa777f7fb7c454b9189b2941b110d1385ce84d7f76efdf1606a85" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" -"checksum tempfile 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9270837a93bad1b1dac18fe67e786b3c960513af86231f6f4f57fddd594ff0c8" +"checksum tempfile 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3213fd2b7ed87e39306737ccfac04b1233b57a33ca64cfbf52f2ffaa2b765e2f" "checksum term_grid 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ccc202875496cf72a683a1ecd66f0742a830e73c202bdbd21867d73dfaac8343" -"checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0" +"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" "checksum termsize 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3a225cb94c3630aabd2e289cad545679dd38b5f4891524e92da1be10aae6e4e8" +"checksum textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f86300c3e7416ee233abd7cda890c492007a3980f941f79185c753a701257167" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" -"checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" +"checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" +"checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" "checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unindent 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3508be0ce1bacc38d579b69bffb4b8d469f5af0c388ff4890b2b294e61671ffe" "checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" -"checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" +"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" -"checksum walkdir 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7c16466ecc507c7cb5988db03e6eab4aaeab89a5c37a29251fcfd3ac9b7afe" +"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" "checksum walker 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0488b82b198ac3588ba688f5e56cbd53e0b6dad64f075e67c15647e54aac610" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 73849ec11..2c8043944 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -238,17 +238,17 @@ whoami = { optional=true, path="src/whoami" } yes = { optional=true, path="src/yes" } [dev-dependencies] -time = "*" -filetime = "*" -libc = "*" -regex="*" -rand="*" -tempdir="*" -unindent="*" -lazy_static = "*" +time = "0.1.38" +filetime = "0.1.10" +libc = "0.2.26" +regex = "0.1.80" +rand = "0.3.15" +tempdir = "0.3.5" +unindent = "0.1.0" +lazy_static = "0.2.2" [target.'cfg(unix)'.dev-dependencies] -unix_socket = "*" +unix_socket = "0.5.0" [[bin]] name = "uutils" diff --git a/src/base32/Cargo.toml b/src/base32/Cargo.toml index 1401f6650..9a561397a 100644 --- a/src/base32/Cargo.toml +++ b/src/base32/Cargo.toml @@ -11,7 +11,7 @@ path = "base32.rs" uucore = { path="../uucore" } [dependencies.clippy] -version = "*" +version = "0.0.143" optional = true [[bin]] diff --git a/src/chgrp/Cargo.toml b/src/chgrp/Cargo.toml index cfcfbd4e3..44adbfa5f 100644 --- a/src/chgrp/Cargo.toml +++ b/src/chgrp/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_chgrp" path = "chgrp.rs" [dependencies] -walkdir = "*" +walkdir = "1.0.7" [dependencies.uucore] path = "../uucore" diff --git a/src/chmod/Cargo.toml b/src/chmod/Cargo.toml index d17902d0b..7e4f9b494 100644 --- a/src/chmod/Cargo.toml +++ b/src/chmod/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_chmod" path = "chmod.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } -walker = "*" +walker = "1.0.0" [[bin]] name = "chmod" diff --git a/src/chown/Cargo.toml b/src/chown/Cargo.toml index b0da5635e..8c9fd1d7f 100644 --- a/src/chown/Cargo.toml +++ b/src/chown/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_chown" path = "chown.rs" [dependencies] -glob = "*" +glob = "0.2.11" walkdir = "0.1" [dependencies.uucore] @@ -17,7 +17,7 @@ default-features = false features = ["entries", "fs"] [dependencies.clippy] -version = "*" +version = "0.0.143" optional = true [[bin]] diff --git a/src/chroot/Cargo.toml b/src/chroot/Cargo.toml index 6761366a8..8212631c8 100644 --- a/src/chroot/Cargo.toml +++ b/src/chroot/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_chroot" path = "chroot.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" [dependencies.uucore] path = "../uucore" diff --git a/src/cksum/Cargo.toml b/src/cksum/Cargo.toml index 9f7bc96e8..e72405ae6 100644 --- a/src/cksum/Cargo.toml +++ b/src/cksum/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_cksum" path = "cksum.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/comm/Cargo.toml b/src/comm/Cargo.toml index 545b9b7d9..7ce4ba005 100644 --- a/src/comm/Cargo.toml +++ b/src/comm/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_comm" path = "comm.rs" [dependencies] -libc = "*" -getopts = "*" +libc = "0.2.26" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index ba10e498e..58fdb831f 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -8,10 +8,10 @@ name = "uu_cp" path = "cp.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } -walkdir = "*" +walkdir = "1.0.7" [[bin]] name = "cp" diff --git a/src/date/Cargo.toml b/src/date/Cargo.toml index 43584dc6c..7a0e6c1bd 100644 --- a/src/date/Cargo.toml +++ b/src/date/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_date" path = "date.rs" [dependencies] -chrono = "*" -clap = "*" +chrono = "0.3.0" +clap = "2.24.1" uucore = { path="../uucore" } [[bin]] diff --git a/src/date/main.rs b/src/date/main.rs index 3b0db953e..8cf62b61b 100644 --- a/src/date/main.rs +++ b/src/date/main.rs @@ -1,4 +1,4 @@ -extern crate uu_echo; +extern crate uu_date; fn main() { std::process::exit(uu_date::uumain(std::env::args().collect())); diff --git a/src/dircolors/Cargo.toml b/src/dircolors/Cargo.toml index 61cac633a..b4f1869e7 100644 --- a/src/dircolors/Cargo.toml +++ b/src/dircolors/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_dircolors" path = "dircolors.rs" [dependencies] -glob = "*" +glob = "0.2.11" uucore = { path="../uucore" } [[bin]] diff --git a/src/dirname/Cargo.toml b/src/dirname/Cargo.toml index e1b6c447c..c2327e3e3 100644 --- a/src/dirname/Cargo.toml +++ b/src/dirname/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_dirname" path = "dirname.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/du/Cargo.toml b/src/du/Cargo.toml index 9ccc48274..98ee97066 100644 --- a/src/du/Cargo.toml +++ b/src/du/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_du" path = "du.rs" [dependencies] -time = "*" +time = "0.1.38" uucore = { path="../uucore" } [[bin]] diff --git a/src/env/Cargo.toml b/src/env/Cargo.toml index 9822ec6b9..b9ba01a2d 100644 --- a/src/env/Cargo.toml +++ b/src/env/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_env" path = "env.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/expand/Cargo.toml b/src/expand/Cargo.toml index cab048be0..832e63735 100644 --- a/src/expand/Cargo.toml +++ b/src/expand/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_expand" path = "expand.rs" [dependencies] -unicode-width = "*" -getopts = "*" +unicode-width = "0.1.4" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/expr/Cargo.toml b/src/expr/Cargo.toml index 37ee12bf6..bcb004801 100644 --- a/src/expr/Cargo.toml +++ b/src/expr/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_expr" path = "expr.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/factor/Cargo.toml b/src/factor/Cargo.toml index c13b64065..1077e4ab4 100644 --- a/src/factor/Cargo.toml +++ b/src/factor/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_factor" path = "factor.rs" [dependencies] -rand = "*" +rand = "0.3.15" uucore = { path="../uucore" } [[bin]] diff --git a/src/fmt/Cargo.toml b/src/fmt/Cargo.toml index 8af1f9d17..0fe85b4b1 100644 --- a/src/fmt/Cargo.toml +++ b/src/fmt/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_fmt" path = "fmt.rs" [dependencies] -libc = "*" -unicode-width = "*" +libc = "0.2.26" +unicode-width = "0.1.4" uucore = { path="../uucore" } [[bin]] diff --git a/src/hashsum/Cargo.toml b/src/hashsum/Cargo.toml index a326dd8d3..83f57a388 100644 --- a/src/hashsum/Cargo.toml +++ b/src/hashsum/Cargo.toml @@ -8,12 +8,12 @@ name = "uu_hashsum" path = "hashsum.rs" [dependencies] -getopts = "*" -libc = "*" -regex = "*" -regex-syntax = "*" -rust-crypto = "*" -rustc-serialize = "*" +getopts = "0.2.14" +libc = "0.2.26" +regex = "0.1.80" +regex-syntax = "0.4.1" +rust-crypto = "0.2.36" +rustc-serialize = "0.3.24" uucore = { path="../uucore" } [[bin]] diff --git a/src/head/Cargo.toml b/src/head/Cargo.toml index ee9f934dd..3b9841ce9 100644 --- a/src/head/Cargo.toml +++ b/src/head/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_head" path = "head.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/hostid/Cargo.toml b/src/hostid/Cargo.toml index cab7329c8..e7f14ecef 100644 --- a/src/hostid/Cargo.toml +++ b/src/hostid/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_hostid" path = "hostid.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/hostname/Cargo.toml b/src/hostname/Cargo.toml index dd8825f8f..820bec64b 100644 --- a/src/hostname/Cargo.toml +++ b/src/hostname/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_hostname" path = "hostname.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/install/Cargo.toml b/src/install/Cargo.toml index d2059c88e..03356137b 100644 --- a/src/install/Cargo.toml +++ b/src/install/Cargo.toml @@ -8,12 +8,12 @@ name = "uu_install" path = "install.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" libc = ">= 0.2" uucore = { path="../uucore" } [dev-dependencies] -time = "*" +time = "0.1.38" [[bin]] name = "install" diff --git a/src/kill/Cargo.toml b/src/kill/Cargo.toml index f29aed1d5..b42f671fd 100644 --- a/src/kill/Cargo.toml +++ b/src/kill/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_kill" path = "kill.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/link/Cargo.toml b/src/link/Cargo.toml index f57088ad2..d1128a892 100644 --- a/src/link/Cargo.toml +++ b/src/link/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_link" path = "link.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/ln/Cargo.toml b/src/ln/Cargo.toml index ccce5bb02..1fabd8f76 100644 --- a/src/ln/Cargo.toml +++ b/src/ln/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_ln" path = "ln.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/logname/Cargo.toml b/src/logname/Cargo.toml index ead9c73ce..4865fc3fa 100644 --- a/src/logname/Cargo.toml +++ b/src/logname/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_logname" path = "logname.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/ls/Cargo.toml b/src/ls/Cargo.toml index f02c1bd99..6582fec19 100644 --- a/src/ls/Cargo.toml +++ b/src/ls/Cargo.toml @@ -8,13 +8,13 @@ name = "uu_ls" path = "ls.rs" [dependencies] -getopts = "*" -pretty-bytes = "*" -term_grid = "*" -termsize = "*" -time = "*" -lazy_static = "*" -unicode-width = "*" +getopts = "0.2.14" +pretty-bytes = "0.2.1" +term_grid = "0.1.5" +termsize = "0.1.4" +time = "0.1.38" +lazy_static = "0.2.8" +unicode-width = "0.1.4" [dependencies.uucore] path = "../uucore" diff --git a/src/mkdir/Cargo.toml b/src/mkdir/Cargo.toml index 662aeef54..ebaf31029 100644 --- a/src/mkdir/Cargo.toml +++ b/src/mkdir/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_mkdir" path = "mkdir.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/mkfifo/Cargo.toml b/src/mkfifo/Cargo.toml index 29b840c0e..08f6cc85d 100644 --- a/src/mkfifo/Cargo.toml +++ b/src/mkfifo/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_mkfifo" path = "mkfifo.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/mknod/Cargo.toml b/src/mknod/Cargo.toml index e6e4036cc..de407e5b3 100644 --- a/src/mknod/Cargo.toml +++ b/src/mknod/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_mknod" path = "mknod.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" libc = "^0.2.4" uucore = { path="../uucore" } diff --git a/src/mktemp/Cargo.toml b/src/mktemp/Cargo.toml index 14e618cec..3802f1b06 100644 --- a/src/mktemp/Cargo.toml +++ b/src/mktemp/Cargo.toml @@ -9,9 +9,9 @@ path = "mktemp.rs" [dependencies] uucore = { path="../uucore" } -getopts = "*" +getopts = "0.2.14" rand = "0.3" -tempfile = "*" +tempfile = "2.1.5" [[bin]] name = "mktemp" diff --git a/src/more/Cargo.toml b/src/more/Cargo.toml index 09e53e429..fe82c5059 100644 --- a/src/more/Cargo.toml +++ b/src/more/Cargo.toml @@ -8,11 +8,11 @@ name = "uu_more" path = "more.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [target.'cfg(all(unix, not(target_os = "fuchsia")))'.dependencies] -nix = "*" +nix = "0.8.1" [[bin]] name = "more" diff --git a/src/mv/Cargo.toml b/src/mv/Cargo.toml index 87644c62b..92418c219 100644 --- a/src/mv/Cargo.toml +++ b/src/mv/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_mv" path = "mv.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/nice/Cargo.toml b/src/nice/Cargo.toml index db73abdf6..f5f4a8500 100644 --- a/src/nice/Cargo.toml +++ b/src/nice/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_nice" path = "nice.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/nl/Cargo.toml b/src/nl/Cargo.toml index 31a160807..ca04dffb1 100644 --- a/src/nl/Cargo.toml +++ b/src/nl/Cargo.toml @@ -8,12 +8,12 @@ name = "uu_nl" path = "nl.rs" [dependencies] -getopts = "*" -libc = "*" -aho-corasick = "*" -memchr = "*" -regex = "*" -regex-syntax = "*" +getopts = "0.2.14" +libc = "0.2.26" +aho-corasick = "0.6.3" +memchr = "1.0.1" +regex = "0.2.2" +regex-syntax = "0.4.1" uucore = { path="../uucore" } [[bin]] diff --git a/src/nohup/Cargo.toml b/src/nohup/Cargo.toml index adc53805d..00b1614b8 100644 --- a/src/nohup/Cargo.toml +++ b/src/nohup/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_nohup" path = "nohup.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/nproc/Cargo.toml b/src/nproc/Cargo.toml index 85fe9bf4e..12bc1ea79 100644 --- a/src/nproc/Cargo.toml +++ b/src/nproc/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_nproc" path = "nproc.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" num_cpus = "1.5" uucore = { path="../uucore" } diff --git a/src/od/Cargo.toml b/src/od/Cargo.toml index a500caecc..88286685b 100644 --- a/src/od/Cargo.toml +++ b/src/od/Cargo.toml @@ -8,10 +8,10 @@ name = "uu_od" path = "od.rs" [dependencies] -getopts = "*" -libc = "*" -byteorder = "*" -half = "*" +getopts = "0.2.14" +libc = "0.2.26" +byteorder = "1.1.0" +half = "1.0.0" uucore = { path="../uucore" } [[bin]] diff --git a/src/paste/Cargo.toml b/src/paste/Cargo.toml index e1d6cad06..e4341390a 100644 --- a/src/paste/Cargo.toml +++ b/src/paste/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_paste" path = "paste.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/pathchk/Cargo.toml b/src/pathchk/Cargo.toml index 7afe4d511..e07ba3285 100644 --- a/src/pathchk/Cargo.toml +++ b/src/pathchk/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_pathchk" path = "pathchk.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/printenv/Cargo.toml b/src/printenv/Cargo.toml index 87282aa8f..fc501aeed 100644 --- a/src/printenv/Cargo.toml +++ b/src/printenv/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_printenv" path = "printenv.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/printf/Cargo.toml b/src/printf/Cargo.toml index a2e1dfdad..b2bac7d64 100644 --- a/src/printf/Cargo.toml +++ b/src/printf/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_printf" path = "printf.rs" [dependencies] -"itertools" = "*" +"itertools" = "0.6.0" uucore = { path="../uucore" } [[bin]] diff --git a/src/printf/printf.rs b/src/printf/printf.rs index f7a732df6..c599729b7 100644 --- a/src/printf/printf.rs +++ b/src/printf/printf.rs @@ -3,13 +3,12 @@ #![allow(dead_code)] extern crate itertools; +extern crate uucore; mod cli; mod memo; mod tokenize; -#[macro_use] -extern crate uucore; static NAME: &'static str = "printf"; static VERSION: &'static str = "0.0.1"; @@ -39,12 +38,12 @@ static LONGHELP_BODY: &'static str = " ESCAPE SEQUENCES The following escape sequences, organized here in alphabetical order, - will print the corresponding character literal: + will print the corresponding character literal: \" double quote \\\\ backslash - + \\a alert (BEL) \\b backspace @@ -64,7 +63,7 @@ static LONGHELP_BODY: &'static str = " \\v vertical tab \\NNN byte with value expressed in octal value NNN (1 to 3 digits) - values greater than 256 will be treated + values greater than 256 will be treated \\xHH byte with value expressed in hexadecimal value NN (1 to 2 digits) @@ -80,14 +79,14 @@ static LONGHELP_BODY: &'static str = " Fields - %s - string - %b - string parsed for literals + %s - string + %b - string parsed for literals second parameter is max length %c - char no second parameter - %i or %d - 64-bit integer + %i or %d - 64-bit integer %u - 64 bit unsigned integer %x or %X - 64-bit unsigned integer as hex %o - 64-bit unsigned integer as octal @@ -97,7 +96,7 @@ static LONGHELP_BODY: &'static str = " %f or %F - decimal floating point value %e or %E - scientific notation floating point value %g or %G - shorter of specially interpreted decimal or SciNote floating point value. - second parameter is + second parameter is -max places after decimal point for floating point output -max number of significant digits for scientific notation output @@ -108,7 +107,7 @@ static LONGHELP_BODY: &'static str = " printf '%4.3i' 7 has a first parameter of 4 and a second parameter of 3 - will result in ' 007' + will result in ' 007' printf '%.1s' abcde has no first parameter @@ -121,7 +120,7 @@ static LONGHELP_BODY: &'static str = " will result in ' q' The first parameter of a field is the minimum width to pad the output to - if the output is less than this absolute value of this width, + if the output is less than this absolute value of this width, it will be padded with leading spaces, or, if the argument is negative, with trailing spaces. the default is zero. @@ -132,7 +131,7 @@ static LONGHELP_BODY: &'static str = " 0 (e.g. 010) - interpret argument as octal (integer output fields only) 0x (e.g. 0xABC) - interpret argument as hex (numeric output fields only) \' (e.g. \'a) - interpret argument as a character constant - + HOW TO USE SUBSTITUTIONS Substitutions are used to pass additional argument(s) into the FORMAT string, to be formatted a @@ -140,14 +139,14 @@ static LONGHELP_BODY: &'static str = " printf 'the letter %X comes before the letter %X' 10 11 - will print - - 'the letter A comes before the letter B' + will print + + 'the letter A comes before the letter B' because the substitution field %X means 'take an integer argument and write it as a hexadecimal number' - Passing more arguments than are in the format string will cause the format string to be + Passing more arguments than are in the format string will cause the format string to be repeated for the remaining substitutions printf 'it is %i F in %s \n' 22 Portland 25 Boston 27 New York @@ -160,18 +159,18 @@ static LONGHELP_BODY: &'static str = " ' If a format string is printed but there are less arguments remaining than there are substitution fields, substitution fields without - an argument will default to empty strings, or for numeric fields + an argument will default to empty strings, or for numeric fields the value 0 AVAILABLE SUBSTITUTIONS - This program, like GNU coreutils printf, - interprets a modified subset of the POSIX C printf spec, - a quick reference to substitutions is below. + This program, like GNU coreutils printf, + interprets a modified subset of the POSIX C printf spec, + a quick reference to substitutions is below. STRING SUBSTITUTIONS - All string fields have a 'max width' parameter - %.3s means 'print no more than three characters of the original input' + All string fields have a 'max width' parameter + %.3s means 'print no more than three characters of the original input' %s - string @@ -193,7 +192,7 @@ static LONGHELP_BODY: &'static str = " %.4i means an integer which if it is less than 4 digits in length, is padded with leading zeros until it is 4 digits in length. - %d or %i - 64-bit integer + %d or %i - 64-bit integer %u - 64 bit unsigned integer @@ -202,7 +201,7 @@ static LONGHELP_BODY: &'static str = " %o - 64 bit unsigned integer printed in octal (base 8) - FLOATING POINT SUBSTITUTIONS + FLOATING POINT SUBSTITUTIONS All floating point fields have a 'max decimal places / max significant digits' parameter %.10f means a decimal floating point with 7 decimal places past 0 @@ -212,7 +211,7 @@ static LONGHELP_BODY: &'static str = " Like with GNU coreutils, the value after the decimal point is these outputs is parsed as a double first before being rendered to text. For both implementations do not expect meaningful precision past the 18th decimal place. When using a number of decimal places that is 18 or higher, you can expect variation in output between GNU coreutils printf and this printf at the 18th decimal place of +/- 1 %f - floating point value presented in decimal, truncated and displayed to 6 decimal places by default. - There is not past-double behavior parity with Coreutils printf, values are not estimated or adjusted beyond input values. + There is not past-double behavior parity with Coreutils printf, values are not estimated or adjusted beyond input values. %e or %E - floating point value presented in scientific notation 7 significant digits by default @@ -225,9 +224,9 @@ static LONGHELP_BODY: &'static str = " Sci Note has 6 significant digits by default Trailing zeroes are removed Instead of being truncated, digit after last is rounded - - Like other behavior in this utility, the design choices of floating point - behavior in this utility is selected to reproduce in exact + + Like other behavior in this utility, the design choices of floating point + behavior in this utility is selected to reproduce in exact the behavior of GNU coreutils' printf from an inputs and outputs standpoint. USING PARAMETERS @@ -239,8 +238,8 @@ static LONGHELP_BODY: &'static str = " leading spaces The 2nd parameter is proceeded by a dot. You do not have to use parameters - - SPECIAL FORMS OF INPUT + + SPECIAL FORMS OF INPUT For numeric input, the following additional forms of input are accepted besides decimal: Octal (only with integer): if the argument begins with a 0 the proceeding characters @@ -255,13 +254,13 @@ static LONGHELP_BODY: &'static str = " of the next character will be interpreted as an 8-bit unsigned integer. If there are additional bytes, they will throw an error (unless the environment variable POSIXLY_CORRECt is set) -WRITTEN BY : +WRITTEN BY : Nathan E. Ross, et al. for the uutils project -MORE INFO : +MORE INFO : https://github.com/uutils/coreutils -COPYRIGHT : +COPYRIGHT : Copyright 2015 uutils project. Licensed under the MIT License, please see LICENSE file for details @@ -279,7 +278,7 @@ pub fn uumain(args: Vec) -> i32 { if formatstr == "--help" { print!("{} {}", LONGHELP_LEAD, LONGHELP_BODY); } else if formatstr == "--version" { - println!("{} {}", NAME, VERSION); + println!("{} {}", NAME, VERSION); } else { let printf_args = &args[2..]; memo::Memo::run_all(formatstr, printf_args); diff --git a/src/ptx/Cargo.toml b/src/ptx/Cargo.toml index d26d3fa4c..57fd9ebb4 100644 --- a/src/ptx/Cargo.toml +++ b/src/ptx/Cargo.toml @@ -8,12 +8,12 @@ name = "uu_ptx" path = "ptx.rs" [dependencies] -getopts = "*" -libc = "*" -aho-corasick = "*" -memchr = "*" -regex-syntax = "*" -regex = "*" +getopts = "0.2.14" +libc = "0.2.26" +aho-corasick = "0.6.3" +memchr = "1.0.1" +regex-syntax = "0.4.1" +regex = "0.1.80" uucore = { path="../uucore" } [[bin]] diff --git a/src/pwd/Cargo.toml b/src/pwd/Cargo.toml index d927c3fd7..02c1e6a5e 100644 --- a/src/pwd/Cargo.toml +++ b/src/pwd/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_pwd" path = "pwd.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/readlink/Cargo.toml b/src/readlink/Cargo.toml index c0b238180..d1bfb4764 100644 --- a/src/readlink/Cargo.toml +++ b/src/readlink/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_readlink" path = "readlink.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/realpath/Cargo.toml b/src/realpath/Cargo.toml index 1b7e62df9..1dacda910 100644 --- a/src/realpath/Cargo.toml +++ b/src/realpath/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_realpath" path = "realpath.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/relpath/Cargo.toml b/src/relpath/Cargo.toml index fd993334a..faa7e263d 100644 --- a/src/relpath/Cargo.toml +++ b/src/relpath/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_relpath" path = "relpath.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/rm/Cargo.toml b/src/rm/Cargo.toml index 25017bee2..0e97a4188 100644 --- a/src/rm/Cargo.toml +++ b/src/rm/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_rm" path = "rm.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/rmdir/Cargo.toml b/src/rmdir/Cargo.toml index 8e2d8f561..6a2ac3220 100644 --- a/src/rmdir/Cargo.toml +++ b/src/rmdir/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_rmdir" path = "rmdir.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/seq/Cargo.toml b/src/seq/Cargo.toml index 4b3e1e873..cebc0080b 100644 --- a/src/seq/Cargo.toml +++ b/src/seq/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_seq" path = "seq.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/shred/Cargo.toml b/src/shred/Cargo.toml index b89438cbf..cad2ce136 100644 --- a/src/shred/Cargo.toml +++ b/src/shred/Cargo.toml @@ -9,10 +9,10 @@ path = "shred.rs" [dependencies] rand = "0.3" -filetime = "*" -getopts = "*" -libc = "*" -time = "*" +filetime = "0.1.10" +getopts = "0.2.14" +libc = "0.2.26" +time = "0.1.38" uucore = { path="../uucore" } [[bin]] diff --git a/src/shuf/Cargo.toml b/src/shuf/Cargo.toml index aa79d3e80..fe1cff7d1 100644 --- a/src/shuf/Cargo.toml +++ b/src/shuf/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_shuf" path = "shuf.rs" [dependencies] -getopts = "*" -rand = "*" +getopts = "0.2.14" +rand = "0.3.15" uucore = { path="../uucore" } [[bin]] diff --git a/src/sleep/Cargo.toml b/src/sleep/Cargo.toml index eb7cd1e6f..caa9f54e4 100644 --- a/src/sleep/Cargo.toml +++ b/src/sleep/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_sleep" path = "sleep.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/sort/Cargo.toml b/src/sort/Cargo.toml index bfff3658a..52387d014 100644 --- a/src/sort/Cargo.toml +++ b/src/sort/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_sort" path = "sort.rs" [dependencies] -getopts = "*" -semver = "*" -itertools = "*" +getopts = "0.2.14" +semver = "0.7.0" +itertools = "0.6.0" uucore = { path="../uucore" } [[bin]] diff --git a/src/sort/sort.rs b/src/sort/sort.rs index f6ef46792..39245ae34 100644 --- a/src/sort/sort.rs +++ b/src/sort/sort.rs @@ -16,7 +16,6 @@ extern crate semver; #[macro_use] extern crate uucore; -#[macro_use] extern crate itertools; use std::cmp::Ordering; diff --git a/src/split/Cargo.toml b/src/split/Cargo.toml index e34c2aec3..da08ecef8 100644 --- a/src/split/Cargo.toml +++ b/src/split/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_split" path = "split.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/stat/Cargo.toml b/src/stat/Cargo.toml index 51630441c..a25b67293 100644 --- a/src/stat/Cargo.toml +++ b/src/stat/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_stat" path = "stat.rs" [dependencies] -getopts = "*" -time = "*" +getopts = "0.2.14" +time = "0.1.38" [dependencies.uucore] path = "../uucore" diff --git a/src/stdbuf/Cargo.toml b/src/stdbuf/Cargo.toml index 7acb6b969..6ffdc8c3d 100644 --- a/src/stdbuf/Cargo.toml +++ b/src/stdbuf/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_stdbuf" path = "stdbuf.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/sum/Cargo.toml b/src/sum/Cargo.toml index aeda3133a..13e9c1f8e 100644 --- a/src/sum/Cargo.toml +++ b/src/sum/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_sum" path = "sum.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/sync/Cargo.toml b/src/sync/Cargo.toml index 6b7c749c9..15a111bb6 100644 --- a/src/sync/Cargo.toml +++ b/src/sync/Cargo.toml @@ -8,10 +8,10 @@ name = "uu_sync" path = "sync.rs" [dependencies] -getopts = "*" -libc = "*" -winapi = "*" -kernel32-sys = "*" +getopts = "0.2.14" +libc = "0.2.26" +winapi = "0.2.8" +kernel32-sys = "0.2.2" uucore = { path="../uucore" } [[bin]] diff --git a/src/sync/sync.rs b/src/sync/sync.rs index 580ae9b20..24b9d2c36 100644 --- a/src/sync/sync.rs +++ b/src/sync/sync.rs @@ -14,9 +14,13 @@ extern crate getopts; extern crate libc; +#[cfg(windows)] #[macro_use] extern crate uucore; +#[cfg(not(windows))] +extern crate uucore; + static NAME: &'static str = "sync"; static VERSION: &'static str = env!("CARGO_PKG_VERSION"); diff --git a/src/tac/Cargo.toml b/src/tac/Cargo.toml index 3e59a4b48..a3558e9df 100644 --- a/src/tac/Cargo.toml +++ b/src/tac/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_tac" path = "tac.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/tail/Cargo.toml b/src/tail/Cargo.toml index a5af8e7bf..b4ff5bb70 100644 --- a/src/tail/Cargo.toml +++ b/src/tail/Cargo.toml @@ -8,10 +8,10 @@ name = "uu_tail" path = "tail.rs" [dependencies] -getopts = "*" -kernel32-sys = "*" -libc = "*" -winapi = "*" +getopts = "0.2.14" +kernel32-sys = "0.2.2" +libc = "0.2.26" +winapi = "0.2.8" uucore = { path="../uucore" } [[bin]] diff --git a/src/tee/Cargo.toml b/src/tee/Cargo.toml index 65c86dc97..502f5f6de 100644 --- a/src/tee/Cargo.toml +++ b/src/tee/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_tee" path = "tee.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/test/Cargo.toml b/src/test/Cargo.toml index 924e201e8..7141275b6 100644 --- a/src/test/Cargo.toml +++ b/src/test/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_test" path = "test.rs" [dependencies] -libc = "*" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/timeout/Cargo.toml b/src/timeout/Cargo.toml index 2e71af3d8..5e31f58ee 100644 --- a/src/timeout/Cargo.toml +++ b/src/timeout/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_timeout" path = "timeout.rs" [dependencies] -getopts = "*" -libc = "*" -time = "*" +getopts = "0.2.14" +libc = "0.2.26" +time = "0.1.38" uucore = { path="../uucore" } [[bin]] diff --git a/src/touch/Cargo.toml b/src/touch/Cargo.toml index efe94b554..86a82f335 100644 --- a/src/touch/Cargo.toml +++ b/src/touch/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_touch" path = "touch.rs" [dependencies] -filetime = "*" -getopts = "*" -time = "*" +filetime = "0.1.10" +getopts = "0.2.14" +time = "0.1.38" [dependencies.uucore] path = "../uucore" diff --git a/src/tr/Cargo.toml b/src/tr/Cargo.toml index 49f3f891b..f1e38f0b6 100644 --- a/src/tr/Cargo.toml +++ b/src/tr/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_tr" path = "tr.rs" [dependencies] -getopts = "*" -bit-set = "*" -fnv = "*" +getopts = "0.2.14" +bit-set = "0.4.0" +fnv = "1.0.5" [dependencies.uucore] path = "../uucore" diff --git a/src/truncate/Cargo.toml b/src/truncate/Cargo.toml index 02d2ad1c4..da394341b 100644 --- a/src/truncate/Cargo.toml +++ b/src/truncate/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_truncate" path = "truncate.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/tsort/Cargo.toml b/src/tsort/Cargo.toml index bbc20fbd1..f19fc15fb 100644 --- a/src/tsort/Cargo.toml +++ b/src/tsort/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_tsort" path = "tsort.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/tty/Cargo.toml b/src/tty/Cargo.toml index 692484290..cce2c9276 100644 --- a/src/tty/Cargo.toml +++ b/src/tty/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_tty" path = "tty.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/unexpand/Cargo.toml b/src/unexpand/Cargo.toml index 377f90aa9..f088504ad 100644 --- a/src/unexpand/Cargo.toml +++ b/src/unexpand/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_unexpand" path = "unexpand.rs" [dependencies] -getopts = "*" -unicode-width = "*" +getopts = "0.2.14" +unicode-width = "0.1.4" uucore = { path="../uucore" } [[bin]] diff --git a/src/uniq/Cargo.toml b/src/uniq/Cargo.toml index b3c415e54..162a24070 100644 --- a/src/uniq/Cargo.toml +++ b/src/uniq/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_uniq" path = "uniq.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" [dependencies.uucore] path="../uucore" diff --git a/src/unlink/Cargo.toml b/src/unlink/Cargo.toml index b2f682b6d..578218c0b 100644 --- a/src/unlink/Cargo.toml +++ b/src/unlink/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_unlink" path = "unlink.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" uucore = { path="../uucore" } [[bin]] diff --git a/src/uptime/Cargo.toml b/src/uptime/Cargo.toml index 4ee431eb8..552604e62 100644 --- a/src/uptime/Cargo.toml +++ b/src/uptime/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_uptime" path = "uptime.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" [dependencies.uucore] path = "../uucore" diff --git a/src/users/Cargo.toml b/src/users/Cargo.toml index 88fe009ea..16cbf36db 100644 --- a/src/users/Cargo.toml +++ b/src/users/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_users" path = "users.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" [dependencies.uucore] default-features = false diff --git a/src/users/users.rs b/src/users/users.rs index d0b6cfd92..28da6fe49 100644 --- a/src/users/users.rs +++ b/src/users/users.rs @@ -16,9 +16,8 @@ #![allow(dead_code)] extern crate getopts; - -#[macro_use] extern crate uucore; + use uucore::utmpx::*; use getopts::Options; diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 9d53cae62..eba667645 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -4,8 +4,8 @@ version = "0.0.1" authors = [] [dependencies] -getopts = "*" -time = { version = "*", optional = true } +getopts = "0.2.14" +time = { version = "0.1.38", optional = true } data-encoding = { version = "^1.1", optional = true } [dependencies.libc] diff --git a/src/wc/Cargo.toml b/src/wc/Cargo.toml index 166a79749..d38d86ca2 100644 --- a/src/wc/Cargo.toml +++ b/src/wc/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_wc" path = "wc.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] diff --git a/src/who/Cargo.toml b/src/who/Cargo.toml index aa769a348..55bca68fa 100644 --- a/src/who/Cargo.toml +++ b/src/who/Cargo.toml @@ -13,7 +13,7 @@ default-features = false features = ["utmpx"] [dependencies.clippy] -version = "*" +version = "0.0.143" optional = true [[bin]] diff --git a/src/whoami/Cargo.toml b/src/whoami/Cargo.toml index a84dddfa9..afb67bbba 100644 --- a/src/whoami/Cargo.toml +++ b/src/whoami/Cargo.toml @@ -8,9 +8,9 @@ name = "uu_whoami" path = "whoami.rs" [dependencies] -getopts = "*" -winapi = "*" -advapi32-sys = "*" +getopts = "0.2.14" +winapi = "0.2.8" +advapi32-sys = "0.2.0" [dependencies.uucore] path = "../uucore" diff --git a/src/yes/Cargo.toml b/src/yes/Cargo.toml index 0a34042d8..e48223ebb 100644 --- a/src/yes/Cargo.toml +++ b/src/yes/Cargo.toml @@ -8,7 +8,7 @@ name = "uu_yes" path = "yes.rs" [dependencies] -getopts = "*" +getopts = "0.2.14" uucore = { path="../uucore" } [[bin]] From 7dafb649d5e37b5656e8b0c92bc838c2681efe8b Mon Sep 17 00:00:00 2001 From: Joshua Miller Date: Wed, 28 Dec 2016 02:08:43 +0100 Subject: [PATCH 04/31] implement many copy flags - Refactored towards extensibility --- Cargo.lock | 2 + src/cp/Cargo.toml | 9 +- src/cp/README.md | 38 + src/cp/cp.rs | 1039 +++++++++++++++++++++------ tests/fixtures/cp/existing_file.txt | 1 + tests/fixtures/cp/how_are_you.txt | 1 + tests/test_cp.rs | 221 +++++- 7 files changed, 1103 insertions(+), 208 deletions(-) create mode 100644 src/cp/README.md create mode 100644 tests/fixtures/cp/existing_file.txt create mode 100644 tests/fixtures/cp/how_are_you.txt diff --git a/Cargo.lock b/Cargo.lock index 0e3a113b3..c80206ba9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,8 +305,10 @@ dependencies = [ name = "cp" version = "0.0.1" dependencies = [ + "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index 58fdb831f..bdfde5a69 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "cp" version = "0.0.1" -authors = [] +authors = [ + "Jordy Dickinson ", + "Joshua S. Miller ", +] [lib] name = "uu_cp" @@ -10,8 +13,10 @@ path = "cp.rs" [dependencies] getopts = "0.2.14" libc = "0.2.26" -uucore = { path="../uucore" } walkdir = "1.0.7" +clap = "2.20.0" +quick-error = "1.1.0" +uucore = { path="../uucore" } [[bin]] name = "cp" diff --git a/src/cp/README.md b/src/cp/README.md new file mode 100644 index 000000000..d536e5c47 --- /dev/null +++ b/src/cp/README.md @@ -0,0 +1,38 @@ +## Feature list + +### To Do + +- [ ] archive +- [ ] attributes-only +- [ ] copy-contents +- [ ] no-dereference-preserve-linkgs +- [ ] dereference +- [ ] no-dereference +- [ ] preserve-default-attributes +- [ ] preserve +- [ ] no-preserve +- [ ] parents +- [ ] reflink +- [ ] sparse +- [ ] strip-trailing-slashes +- [ ] update +- [ ] one-file-system +- [ ] context +- [ ] cli-symbolic-links + +### Completed + +- [x] backup +- [x] force (Not implemented on Windows) +- [x] interactive +- [x] link +- [x] no-clobber +- [x] no-target-directory +- [x] paths +- [x] recursive +- [x] remove-destination (On Windows, current only works for writeable files) +- [x] suffix +- [x] symbolic-link +- [x] target-directory +- [x] verbose +- [x] version diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 4b190307d..354a9cf0f 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -4,245 +4,884 @@ * This file is part of the uutils coreutils package. * * (c) Jordy Dickinson + * (c) Joshua S. Miller * * For the full copyright and license information, please view the LICENSE file * that was distributed with this source code. */ -extern crate getopts; - -#[macro_use] -extern crate uucore; - +extern crate clap; extern crate walkdir; +#[macro_use] extern crate uucore; +#[macro_use] extern crate quick_error; +use clap::{Arg, App, ArgMatches}; +use quick_error::ResultExt; +use std::collections::HashSet; +use std::fs; +use std::io::{BufReader, BufRead, stdin, Write}; +use std::io; +use std::path::{Path, PathBuf, StripPrefixError}; +use std::str::FromStr; +use uucore::fs::{canonicalize, CanonicalizeMode}; use walkdir::WalkDir; -use getopts::Options; -use std::fs; -use std::io::{ErrorKind, Result, Write}; -use std::path::Path; -use uucore::fs::{canonicalize, CanonicalizeMode}; +#[cfg(unix)] use std::os::unix::fs::PermissionsExt; + +quick_error! { + #[derive(Debug)] + pub enum Error { + /// Simple io::Error wrapper + IoErr(err: io::Error) { from() cause(err) display("{}", err) } + + /// Wrapper for io::Error with path context + IoErrContext(err: io::Error, path: String) { + display("{}: {}", path, err) + context(path: &'a str, err: io::Error) -> (err, path.to_owned()) + cause(err) + } + + /// General copy error + Error(err: String) { + display("{}", err) + from(err: String) -> (err) + from(err: &'static str) -> (err.to_string()) + } + + /// Represents the state when a non-fatal error has occured + /// and not all files were copied. + NotAllFilesCopied {} + + /// Simple walkdir::Error wrapper + WalkDirErr(err: walkdir::Error) { from() display("{}", err) cause(err) } + + /// Simple std::path::StripPrefixError wrapper + StripPrefixError(err: StripPrefixError) { from() } + + /// Result of a skipped file + Skipped(reason: String) { display("{}", reason) } + + /// Result of a skipped file + InvalidArgument(description: String) { display("{}", description) } + + /// All standard options are included as an an implementation + /// path, but those that are not implemented yet should return + /// a NotImplemented error. + NotImplemented(opt: String) { display("Option '{}' not yet implemented.", opt) } + } +} + + +/// Continue next iteration of loop if result of expression is error +macro_rules! or_continue( + ($expr:expr) => (match $expr { + Ok(temp) => temp, + Err(error) => { + show_error!("{}", error); + continue + }, + }) +); + + +/// Prompts the user yes/no and returns `true` they if successfully +/// answered yes. +macro_rules! prompt_yes( + ($($args:tt)+) => ({ + pipe_write!(&mut ::std::io::stdout(), $($args)+); + pipe_write!(&mut ::std::io::stdout(), " [y/N]: "); + pipe_flush!(); + let mut s = String::new(); + match BufReader::new(stdin()).read_line(&mut s) { + Ok(_) => match s.char_indices().nth(0) { + Some((_, x)) => x == 'y' || x == 'Y', + _ => false + }, + _ => false + } + }) +); + +pub type CopyResult = Result; +pub type Source = PathBuf; +pub type Target = PathBuf; + +/// Specifies whether when overwrite files +#[derive (Clone, Eq, PartialEq)] +pub enum ClobberMode { + Force, + RemoveDestination, + Standard, +} + +/// Specifies whether when overwrite files +#[derive (Clone, Eq, PartialEq)] +pub enum OverwriteMode { + /// [Default] Always overwrite existing files + Clobber(ClobberMode), + /// Prompt before overwriting a file + Interactive(ClobberMode), + /// Never overwrite a file + NoClobber, +} + +/// Specifies the expected file type of copy target +pub enum TargetType { + Directory, + File, +} #[derive(Clone, Eq, PartialEq)] -pub enum Mode { - Copy, - Help, - Version, +pub enum BackupMode { + ExistingBackup, + NoBackup, + NumberedBackup, + SimpleBackup, } -static NAME: &'static str = "cp"; -static VERSION: &'static str = env!("CARGO_PKG_VERSION"); +pub enum CopyMode { + Link, + SymLink, + Sparse, + Copy, +} + +#[derive(Clone)] +pub enum Attribute { + #[cfg(unix)] Mode, + Ownership, + Timestamps, + Context, + Links, + Xattr, + All, +} + +/// Re-usable, extensible copy options +#[allow(dead_code)] +pub struct Options { + attributes_only: bool, + backup: bool, + copy_contents: bool, + copy_mode: CopyMode, + dereference: bool, + no_target_dir: bool, + one_file_system: bool, + overwrite: OverwriteMode, + parents: bool, + preserve_attributes: Vec, + recursive: bool, + backup_suffix: String, + target_dir: Option, + update: bool, + verbose: bool, +} + +static VERSION: &str = env!("CARGO_PKG_VERSION"); +static USAGE: &str = "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."; +static EXIT_OK: i32 = 0; +static EXIT_ERR: i32 = 1; + +/// Prints the version +fn print_version() { + println!("{} {}", executable!(), VERSION); +} + +/// Prints usage/help +fn get_about(usage: &str) -> String { + format!("Usage: {0} [OPTION]... [-T] SOURCE DEST + or: {0} [OPTION]... SOURCE... DIRECTORY + or: {0} [OPTION]... -t DIRECTORY SOURCE... +{1}", executable!(), usage) +} + + +// Argument constants +static OPT_ARCHIVE: &str = "archive"; +static OPT_ATTRIBUTES_ONLY: &str = "attributes-only"; +static OPT_BACKUP: &str = "backup"; +static OPT_CLI_SYMBOLIC_LINKS: &str = "cli-symbolic-links"; +static OPT_CONTEXT: &str = "context"; +static OPT_COPY_CONTENTS: &str = "copy-contents"; +static OPT_DEREFERENCE: &str = "dereference"; +static OPT_FORCE: &str = "force"; +static OPT_INTERACTIVE: &str = "interactive"; +static OPT_LINK: &str = "link"; +static OPT_NO_CLOBBER: &str = "no-clobber"; +static OPT_NO_DEREFERENCE: &str = "no-dereference"; +static OPT_NO_DEREFERENCE_PRESERVE_LINKS: &str = "no-dereference-preserve-linkgs"; +static OPT_NO_PRESERVE: &str = "no-preserve"; +static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory"; +static OPT_ONE_FILE_SYSTEM: &str = "one-file-system"; +static OPT_PARENTS: &str = "parents"; +static OPT_PATHS: &str = "paths"; +static OPT_PRESERVE: &str = "preserve"; +static OPT_PRESERVE_DEFUALT_ATTRIBUTES: &str = "preserve-default-attributes"; +static OPT_RECURSIVE: &str = "recursive"; +static OPT_RECURSIVE_ALIAS: &str = "recursive_alias"; +static OPT_REFLINK: &str = "reflink"; +static OPT_REMOVE_DESTINATION: &str = "remove-destination"; +static OPT_SPARSE: &str = "sparse"; +static OPT_STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes"; +static OPT_SUFFIX: &str = "suffix"; +static OPT_SYMBOLIC_LINK: &str = "symbolic-link"; +static OPT_TARGET_DIRECTORY: &str = "target-directory"; +static OPT_UPDATE: &str = "update"; +static OPT_VERBOSE: &str = "verbose"; +static OPT_VERSION: &str = "version"; + +#[cfg(unix)] +static PRESERVABLE_ATTRIBUTES: &[&str] = &["mode", "ownership", "timestamps", "context", "links", "xattr", "all"]; + +#[cfg(not(unix))] +static PRESERVABLE_ATTRIBUTES: &[&str] = &["ownership", "timestamps", "context", "links", "xattr", "all"]; + +static DEFAULT_ATTRIBUTES: &[Attribute] = &[ + #[cfg(unix)] Attribute::Mode, + Attribute::Ownership, + Attribute::Timestamps, +]; + pub fn uumain(args: Vec) -> i32 { - let mut opts = Options::new(); + let about = get_about(USAGE); + let matches = App::new(executable!()) + .version(VERSION) + .about(&about[..]) + .arg(Arg::with_name(OPT_TARGET_DIRECTORY) + .short("t") + .conflicts_with(OPT_NO_TARGET_DIRECTORY) + .long(OPT_TARGET_DIRECTORY) + .value_name(OPT_TARGET_DIRECTORY) + .takes_value(true) + .help("copy all SOURCE arguments into target-directory")) + .arg(Arg::with_name(OPT_NO_TARGET_DIRECTORY) + .short("T") + .long(OPT_NO_TARGET_DIRECTORY) + .conflicts_with(OPT_TARGET_DIRECTORY) + .help("Treat DEST as a regular file and not a directory")) + .arg(Arg::with_name(OPT_VERSION) + .short("V") + .long(OPT_VERSION) + .help("output version information and exit")) + .arg(Arg::with_name(OPT_INTERACTIVE) + .short("i") + .long(OPT_INTERACTIVE) + .conflicts_with(OPT_NO_CLOBBER) + .help("ask before overwriting files")) + .arg(Arg::with_name(OPT_LINK) + .short("l") + .long(OPT_LINK) + .help("hard-link files instead of copying")) + .arg(Arg::with_name(OPT_NO_CLOBBER) + .short("n") + .long(OPT_NO_CLOBBER) + .conflicts_with(OPT_INTERACTIVE) + .help("don't overwrite a file that already exists")) + .arg(Arg::with_name(OPT_RECURSIVE) + .short("r") + .long(OPT_RECURSIVE) + .help("copy directories recursively")) + .arg(Arg::with_name(OPT_RECURSIVE_ALIAS) + .short("R") + .help("same as -r")) + .arg(Arg::with_name(OPT_VERBOSE) + .short("v") + .long(OPT_VERBOSE) + .help("explicitly state what is being done")) + .arg(Arg::with_name(OPT_SYMBOLIC_LINK) + .short("s") + .long(OPT_SYMBOLIC_LINK) + .conflicts_with(OPT_LINK) + .help("make symbolic links instead of copying")) + .arg(Arg::with_name(OPT_FORCE) + .short("f") + .long(OPT_FORCE) + .help("if an existing destination file cannot be opened, remove it and \ + try again (this option is ignored when the -n option is also used). \ + Currently not implemented for Windows.")) + .arg(Arg::with_name(OPT_REMOVE_DESTINATION) + .long(OPT_REMOVE_DESTINATION) + .conflicts_with(OPT_FORCE) + .help("remove each existing destination file before attempting to open it \ + (contrast with --force). On Windows, current only works for writeable files.")) + .arg(Arg::with_name(OPT_BACKUP) + .short("b") + .long(OPT_BACKUP) + .help("make a backup of each existing destination file")) + .arg(Arg::with_name(OPT_SUFFIX) + .short("S") + .long(OPT_SUFFIX) + .takes_value(true) + .default_value("~") + .value_name("SUFFIX") + .help("override the usual backup suffix")) - opts.optflag("h", "help", "display this help and exit"); - opts.optflag("r", "recursive", "copy directories recursively"); - opts.optflag("", "version", "output version information and exit"); - opts.optopt("t", - "target-directory", - "copy all SOURCE arguments into DIRECTORY", - "DEST"); - opts.optflag("T", - "no-target-directory", - "Treat DEST as a regular file and not a directory"); - opts.optflag("v", "verbose", "explicitly state what is being done"); + // TODO: implement the following args + .arg(Arg::with_name(OPT_ARCHIVE) + .short("a") + .long(OPT_ARCHIVE) + .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_PRESERVE, OPT_NO_PRESERVE]) + .help("NotImplemented: same as -dR --preserve=all")) + .arg(Arg::with_name(OPT_ATTRIBUTES_ONLY) + .long(OPT_ATTRIBUTES_ONLY) + .conflicts_with(OPT_COPY_CONTENTS) + .help("NotImplemented: don't copy the file data, just the attributes")) + .arg(Arg::with_name(OPT_COPY_CONTENTS) + .long(OPT_COPY_CONTENTS) + .conflicts_with(OPT_ATTRIBUTES_ONLY) + .help("NotImplemented: copy contents of special files when recursive")) + .arg(Arg::with_name(OPT_NO_DEREFERENCE_PRESERVE_LINKS) + .short("d") + .help("NotImplemented: same as --no-dereference --preserve=links")) + .arg(Arg::with_name(OPT_DEREFERENCE) + .short("L") + .long(OPT_DEREFERENCE) + .conflicts_with(OPT_NO_DEREFERENCE) + .help("NotImplemented: always follow symbolic links in SOURCE")) + .arg(Arg::with_name(OPT_NO_DEREFERENCE) + .short("-P") + .long(OPT_NO_DEREFERENCE) + .conflicts_with(OPT_DEREFERENCE) + .help("NotImplemented: never follow symbolic links in SOURCE")) + .arg(Arg::with_name(OPT_PRESERVE_DEFUALT_ATTRIBUTES) + .short("-p") + .long(OPT_PRESERVE_DEFUALT_ATTRIBUTES) + .conflicts_with_all(&[OPT_PRESERVE, OPT_NO_PRESERVE, OPT_ARCHIVE]) + .help("NotImplemented: same as --preserve=mode(unix only),ownership,timestamps")) + .arg(Arg::with_name(OPT_PRESERVE) + .long(OPT_PRESERVE) + .takes_value(true) + .multiple(true) + .possible_values(PRESERVABLE_ATTRIBUTES) + .value_name("ATTR_LIST") + .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_NO_PRESERVE, OPT_ARCHIVE]) + .help("NotImplemented: preserve the specified attributes (default: mode(unix only),ownership,timestamps),\ + if possible additional attributes: context, links, xattr, all")) + .arg(Arg::with_name(OPT_NO_PRESERVE) + .long(OPT_NO_PRESERVE) + .takes_value(true) + .value_name("ATTR_LIST") + .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_PRESERVE, OPT_ARCHIVE]) + .help("NotImplemented: don't preserve the specified attributes")) + .arg(Arg::with_name(OPT_PARENTS) + .long(OPT_PARENTS) + .help("NotImplemented: use full source file name under DIRECTORY")) + .arg(Arg::with_name(OPT_REFLINK) + .long(OPT_REFLINK) + .takes_value(true) + .value_name("WHEN") + .help("NotImplemented: control clone/CoW copies. See below")) + .arg(Arg::with_name(OPT_SPARSE) + .long(OPT_SPARSE) + .takes_value(true) + .value_name("WHEN") + .help("NotImplemented: control creation of sparse files. See below")) + .arg(Arg::with_name(OPT_STRIP_TRAILING_SLASHES) + .long(OPT_STRIP_TRAILING_SLASHES) + .help("NotImplemented: remove any trailing slashes from each SOURCE argument")) + .arg(Arg::with_name(OPT_UPDATE) + .short("u") + .long(OPT_UPDATE) + .help("NotImplemented: copy only when the SOURCE file is newer than the destination file\ + or when the destination file is missing")) + .arg(Arg::with_name(OPT_ONE_FILE_SYSTEM) + .short("x") + .long(OPT_ONE_FILE_SYSTEM) + .help("NotImplemented: stay on this file system")) + .arg(Arg::with_name(OPT_CONTEXT) + .long(OPT_CONTEXT) + .takes_value(true) + .value_name("CTX") + .help("NotImplemented: set SELinux security context of destination file to default type")) + .arg(Arg::with_name(OPT_CLI_SYMBOLIC_LINKS) + .short("H") + .help("NotImplemented: follow command-line symbolic links in SOURCE")) + // END TODO - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(e) => { - show_error!("{}", e); - panic!() - } - }; - let usage = opts.usage("Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY."); - let mode = if matches.opt_present("version") { - Mode::Version - } else if matches.opt_present("help") { - Mode::Help - } else { - Mode::Copy - }; + .arg(Arg::with_name(OPT_PATHS) + .multiple(true)) + .get_matches_from(&args); - match mode { - Mode::Copy => copy(matches), - Mode::Help => help(&usage), - Mode::Version => version(), + if matches.is_present(OPT_VERSION) { + print_version(); + return EXIT_OK; } - 0 + let options = crash_if_err!(EXIT_ERR, Options::from_matches(&matches)); + let paths: Vec = matches.values_of("paths") + .map(|v| v.map(|p| p.to_string()).collect()) + .unwrap_or_default(); + + let (sources, target) = crash_if_err!(EXIT_ERR, parse_path_args(&paths, &options)); + + if let Err(error) = copy(&sources, &target, &options) { + match error { + // Error::NotAllFilesCopied is non-fatal, but the error + // code should still be EXIT_ERR as does GNU cp + Error::NotAllFilesCopied => {} + // Else we caught a fatal bubbled-up error, log it to stderr + _ => show_error!("{}", error), + }; + return EXIT_ERR; + } + + EXIT_OK } -fn version() { - println!("{} {}", NAME, VERSION); -} - -fn help(usage: &str) { - let msg = format!("{0} {1}\n\n\ - Usage: {0} SOURCE DEST\n \ - or: {0} SOURCE... DIRECTORY\n \ - or: {0} -t DIRECTORY SOURCE...\n\ - \n\ - {2}", - NAME, - VERSION, - usage); - println!("{}", msg); -} - -fn copy(matches: getopts::Matches) { - let verbose = matches.opt_present("verbose"); - let sources: Vec = if matches.free.is_empty() { - show_error!("Missing SOURCE or DEST argument. Try --help."); - panic!() - } else if !matches.opt_present("target-directory") { - matches.free[..matches.free.len() - 1] - .iter() - .cloned() - .collect() - } else { - matches.free.iter().cloned().collect() - }; - let recursive: bool = matches.opt_present("recursive"); - - let dest_str = if matches.opt_present("target-directory") { - matches - .opt_str("target-directory") - .expect("Option -t/--target-directory requires an argument") - } else { - matches.free[matches.free.len() - 1].clone() - }; - let dest = if matches.free.len() < 2 && !matches.opt_present("target-directory") { - show_error!("Missing DEST argument. Try --help."); - panic!() - } else { - //the argument to the -t/--target-directory= options - let path = Path::new(&dest_str); - if !path.is_dir() && matches.opt_present("target-directory") { - show_error!("Target {} is not a directory", - matches.opt_str("target-directory").unwrap()); - panic!() +impl ClobberMode { + fn from_matches(matches: &ArgMatches) -> ClobberMode { + if matches.is_present(OPT_FORCE) { + ClobberMode::Force + } else if matches.is_present(OPT_REMOVE_DESTINATION) { + ClobberMode::RemoveDestination } else { - path + ClobberMode::Standard + } + } +} + +impl OverwriteMode { + fn from_matches(matches: &ArgMatches) -> OverwriteMode { + if matches.is_present(OPT_INTERACTIVE) { + OverwriteMode::Interactive(ClobberMode::from_matches(matches)) + } else if matches.is_present(OPT_NO_CLOBBER) { + OverwriteMode::NoClobber + } else { + OverwriteMode::Clobber(ClobberMode::from_matches(matches)) + } + } +} + +impl CopyMode { + fn from_matches(matches: &ArgMatches) -> CopyMode { + if matches.is_present(OPT_LINK) { + CopyMode::Link + } else if matches.is_present(OPT_SYMBOLIC_LINK) { + CopyMode::SymLink + } else if matches.is_present(OPT_SPARSE) { + CopyMode::Sparse + } else { + CopyMode::Copy + } + } +} + +impl FromStr for Attribute { + type Err = Error; + + fn from_str(value: &str) -> CopyResult { + Ok(match &*value.to_lowercase() { + #[cfg(unix)] "mode" => Attribute::Mode, + "ownership" => Attribute::Ownership, + "timestamps" => Attribute::Timestamps, + "context" => Attribute::Context, + "links" => Attribute::Links, + "xattr" => Attribute::Xattr, + "all" => Attribute::All, + _ => return Err(Error::InvalidArgument(format!("invalid attribute '{}'", value))) + }) + } +} + +impl Options { + fn from_matches(matches: &ArgMatches) -> CopyResult { + let not_implemented_opts = vec![ + OPT_ARCHIVE, + OPT_ATTRIBUTES_ONLY, + OPT_COPY_CONTENTS, + OPT_NO_DEREFERENCE_PRESERVE_LINKS, + OPT_DEREFERENCE, + OPT_NO_DEREFERENCE, + OPT_PRESERVE_DEFUALT_ATTRIBUTES, + OPT_PRESERVE, + OPT_NO_PRESERVE, + OPT_PARENTS, + OPT_REFLINK, + OPT_SPARSE, + OPT_STRIP_TRAILING_SLASHES, + OPT_UPDATE, + OPT_ONE_FILE_SYSTEM, + OPT_CONTEXT, + #[cfg(windows)] OPT_FORCE, + ]; + + for not_implemented_opt in not_implemented_opts { + if matches.is_present(not_implemented_opt) { + return Err(Error::NotImplemented(not_implemented_opt.to_string())) + } } - }; + let recursive = matches.is_present(OPT_RECURSIVE) + || matches.is_present(OPT_RECURSIVE_ALIAS) + || matches.is_present(OPT_ARCHIVE); - assert!(sources.len() >= 1); - if matches.opt_present("no-target-directory") && dest.is_dir() { - show_error!("Can't overwrite directory {} with non-directory", - dest.display()); - panic!() + let backup = matches.is_present(OPT_BACKUP) + || matches.is_present(OPT_SUFFIX); + + // Parse target directory options + let no_target_dir = matches.is_present(OPT_NO_TARGET_DIRECTORY); + let target_dir = matches.value_of(OPT_TARGET_DIRECTORY).map(|v| v.to_string()); + + // Parse attributes to preserve + let preserve_attributes: Vec = if matches.is_present(OPT_PRESERVE) { + match matches.values_of(OPT_PRESERVE) { + None => DEFAULT_ATTRIBUTES.to_vec(), + Some(attribute_strs) => { + let mut attributes = Vec::new(); + for attribute_str in attribute_strs { + attributes.push(Attribute::from_str(attribute_str)?); + } + attributes + } + } + } else if matches.is_present(OPT_PRESERVE_DEFUALT_ATTRIBUTES) { + DEFAULT_ATTRIBUTES.to_vec() + } else { + vec![] + }; + + let options = Options { + attributes_only: matches.is_present(OPT_ATTRIBUTES_ONLY), + copy_contents: matches.is_present(OPT_COPY_CONTENTS), + copy_mode: CopyMode::from_matches(matches), + dereference: matches.is_present(OPT_DEREFERENCE), + one_file_system: matches.is_present(OPT_ONE_FILE_SYSTEM), + overwrite: OverwriteMode::from_matches(matches), + parents: matches.is_present(OPT_PARENTS), + backup_suffix: matches.value_of(OPT_SUFFIX).unwrap().to_string(), + update: matches.is_present(OPT_UPDATE), + verbose: matches.is_present(OPT_VERBOSE), + backup, + no_target_dir, + preserve_attributes, + recursive, + target_dir, + }; + + Ok(options) + } +} + + +impl TargetType { + /// Return TargetType required for `target`. + /// + /// Treat target as a dir if we have multiple sources or the target + /// exists and already is a directory + fn determine(sources: &[Source], target: &Target) -> TargetType { + if sources.len() > 1 || target.is_dir() { + TargetType::Directory + } else { + TargetType::File + } + } +} + + +/// Returns tuple of (Source paths, Target) +fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec, Target)> { + let mut paths = path_args.iter().map(PathBuf::from).collect::>(); + + if paths.len() < 1 { + // No files specified + return Err("missing file operand".into()); } - if sources.len() == 1 { - let source = Path::new(&sources[0]); - let same_file = - paths_refer_to_same_file(source, dest).unwrap_or_else(|err| match err.kind() { - ErrorKind::NotFound => false, - _ => { - show_error!("{}", err); - panic!() - } - }); + // Return an error if the user requested to copy more than one + // file source to a file target + if options.no_target_dir && !options.target_dir.is_some() && paths.len() > 2 { + return Err(format!("extra operand {:?}", paths[2]).into()); + } - if same_file { - show_error!("\"{}\" and \"{}\" are the same file", - source.display(), - dest.display()); - panic!(); + let (sources, target) = match options.target_dir { + Some(ref target) => { + // All path arges are sources, and the target dir was + // specified separately + (paths, PathBuf::from(target)) } - let mut full_dest = dest.to_path_buf(); - if recursive { - for entry in WalkDir::new(source) { - let entry = entry.unwrap(); - if entry.path().is_dir() { - let mut dst_path = full_dest.clone(); - dst_path.push(entry.path()); - if let Err(err) = fs::create_dir(dst_path) { - show_error!("{}", err); - panic!(); - } + None => { + // If there was no explicit target-dir, then use the last + // path_arg + let target = paths.pop().unwrap(); + (paths, target) + } + }; + + Ok((sources, target)) +} + + +/// Copy all `sources` to `target`. Returns an +/// `Err(Error::NotAllFilesCopied)` if at least one non-fatal error was +/// encountered. +/// +/// Behavior depends on `options`, see [`Options`] for details. +/// +/// [`Options`]: ./struct.Options.html +fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<()> { + let target_type = TargetType::determine(sources, target); + verify_target_type(target, &target_type)?; + + let mut non_fatal_errors = false; + let mut seen_sources = HashSet::with_capacity(sources.len()); + + for source in sources { + if seen_sources.contains(source) { + show_warning!("source '{}' specified more than once", source.display()); + + } else if let Err(error) = copy_source(source, target, &target_type, options) { + show_error!("{}", error); + match error { + Error::Skipped(_) => (), + _ => non_fatal_errors = true, + } + } + seen_sources.insert(source); + } + + if non_fatal_errors { + Err(Error::NotAllFilesCopied) + } else { + Ok(()) + } +} + + +fn construct_dest_path(source_path: &Path, target: &Target, target_type: &TargetType, options: &Options) + -> CopyResult +{ + if options.no_target_dir && target.is_dir() { + return Err(format!("cannot overwrite directory '{}' with non-directory", target.display()).into()) + } + + Ok(match *target_type { + TargetType::Directory => { + let root = source_path.parent().unwrap_or(source_path); + localize_to_target(root, source_path, target)? + }, + TargetType::File => target.to_path_buf(), + }) +} + +fn copy_source(source: &Source, target: &Target, target_type: &TargetType, options: &Options) + -> CopyResult<()> +{ + let source_path = Path::new(&source); + + if source_path.is_dir() { + // Copy as directory + copy_directory(source, target, options) + } else { + // Copy as file + let dest = construct_dest_path(source_path, target, target_type, options)?; + copy_file(source_path, dest.as_path(), options) + } +} + + +/// Read the contents of the directory `root` and recursively copy the +/// contents to `target`. +/// +/// Any errors encounted copying files in the tree will be logged but +/// will not cause a short-circuit. +fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult<()> { + if !options.recursive { + return Err(format!("omitting directory '{}'", root.display()).into()); + } + + let root_path = Path::new(&root).canonicalize()?; + let root_parent = root_path.parent(); + + for path in WalkDir::new(root) { + let path = or_continue!(or_continue!(path).path().canonicalize()); + let local_to_root_parent = match root_parent { + Some(parent) => or_continue!(path.strip_prefix(&parent)).to_path_buf(), + None => path.clone(), + }; + + let local_to_target = target.join(&local_to_root_parent); + + if path.is_dir() && !local_to_target.exists() { + or_continue!(fs::create_dir_all(local_to_target.clone())); + } else if !path.is_dir() { + copy_file(path.as_path(), local_to_target.as_path(), options)?; + } + } + + Ok(()) +} + + +impl OverwriteMode { + fn verify(&self, path: &Path) -> CopyResult<()> { + match *self { + OverwriteMode::NoClobber => { + Err(Error::Skipped(format!("Not overwriting {} because of option '{}'", path.display(), OPT_NO_CLOBBER))) + }, + OverwriteMode::Interactive(_) => { + if prompt_yes!("{}: overwrite {}? ", executable!(), path.display()) { + Ok(()) } else { - let mut dst_path = full_dest.clone(); - dst_path.push(entry.path()); - if let Err(err) = fs::copy(entry.path(), dst_path) { - show_error!("{}", err); - panic!(); - } + Err(Error::Skipped(format!("Not overwriting {} at user request", path.display()))) } - } - } else { - if dest.is_dir() { - full_dest.push(source.file_name().unwrap()); //the destination path is the destination - } // directory + the file name we're copying - if verbose { - println!("{} -> {}", source.display(), full_dest.display()); - } - if let Err(err) = fs::copy(source, full_dest) { - show_error!("{}", err); - panic!(); - } - } - } else { - if !dest.is_dir() { - show_error!("TARGET must be a directory"); - panic!(); - } - for src in &sources { - let source = Path::new(&src); - - if !recursive { - if !source.is_file() { - show_error!("\"{}\" is not a file", source.display()); - continue; - } - - let mut full_dest = dest.to_path_buf(); - - full_dest.push(source.file_name().unwrap()); - - if verbose { - println!("{} -> {}", source.display(), full_dest.display()); - } - - let io_result = fs::copy(source, full_dest); - - if let Err(err) = io_result { - show_error!("{}", err); - panic!() - } - } else { - for entry in WalkDir::new(source) { - let entry = entry.unwrap(); - let full_dest = dest.to_path_buf(); - - if entry.path().is_dir() { - let mut dst_path = full_dest.clone(); - dst_path.push(entry.path()); - if let Err(err) = fs::create_dir(dst_path) { - show_error!("{}", err); - panic!(); - } - } else { - let mut dst_path = full_dest.clone(); - dst_path.push(entry.path()); - if let Err(err) = fs::copy(entry.path(), dst_path) { - show_error!("{}", err); - panic!(); - } - } - } - } + }, + OverwriteMode::Clobber(_) => Ok(()), } } } -pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> Result { + +fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResult<()> { + let context = &*format!("'{}' -> '{}'", source.display().to_string(), dest.display()); + Ok(match *attribute { + #[cfg(unix)] + Attribute::Mode => { + let mode = fs::metadata(source).context(context)?.permissions().mode(); + let mut dest_metadata = fs::metadata(source).context(context)?.permissions(); + dest_metadata.set_mode(mode); + }, + Attribute::Ownership => { + let metadata = fs::metadata(source).context(context)?; + fs::set_permissions(dest, metadata.permissions()).context(context)?; + }, + Attribute::Timestamps => return Err(Error::NotImplemented("preserving timestamp not implemented".to_string())), + Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), + Attribute::Links => return Err(Error::NotImplemented("preserving links not implemented".to_string())), + Attribute::Xattr => return Err(Error::NotImplemented("preserving xattr not implemented".to_string())), + Attribute::All => return Err(Error::NotImplemented("preserving a not implemented".to_string())), + }) +} + +#[cfg(not(windows))] +fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { + Ok(std::os::unix::fs::symlink(source, dest).context(context)?) +} + +#[cfg(windows)] +fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { + Ok(std::os::windows::fs::symlink_file(source, dest).context(context)?) +} + +fn context_for(src: &Path, dest: &Path) -> String { + format!("'{}' -> '{}'", src.display(), dest.display()) +} + +/// Implements a relatively naive backup that is not as full featured +/// as GNU cp. No CONTROL version control method argument is taken +/// for backups. +/// TODO: Add version control methods +fn backup_file(path: &Path, suffix: &str) -> CopyResult { + let mut backup_path = path.to_path_buf().into_os_string(); + backup_path.push(suffix); + fs::copy(path, &backup_path)?; + Ok(backup_path.into()) +} + +fn handle_existing_dest(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { + if paths_refer_to_same_file(source, dest)? { + return Err(format!("{}: same file", context_for(source, dest)).into()); + } + + options.overwrite.verify(dest)?; + + if options.backup { + backup_file(dest, &options.backup_suffix)?; + } + + match options.overwrite { + OverwriteMode::Clobber(ClobberMode::Force) => { + if fs::metadata(dest)?.permissions().readonly() { + fs::remove_file(dest)?; + } + }, + OverwriteMode::Clobber(ClobberMode::RemoveDestination) => { + fs::remove_file(dest)?; + }, + _ => (), + }; + + Ok(()) +} + +/// Copy the a file from `source` to `dest`. No path manipulation is +/// done on either `source` or `dest`, the are used as provieded. +/// +/// Behavior when copying to existing files is contingent on the +/// `options.overwrite` mode. If a file is skipped, the return type +/// should be `Error:Skipped` +/// +/// The original permissions of `source` will be copied to `dest` +/// after a successful copy. +fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { + if dest.exists() { + handle_existing_dest(source, dest, options)?; + } + + if options.verbose { + println!("{}", context_for(source, dest)); + } + + match options.copy_mode { + CopyMode::Link => { fs::hard_link(source, dest).context(&*context_for(source, dest))?; }, + CopyMode::Copy => { fs::copy(source, dest).context(&*context_for(source, dest))?; }, + CopyMode::SymLink => { symlink_file(source, dest, &*context_for(source, dest))?; }, + CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())), + }; + + for attribute in &options.preserve_attributes { + copy_attribute(source, dest, attribute)?; + } + + Ok(()) +} + + +/// Generate an error message if `target` is not the correct `target_type` +pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> { + match (target_type, target.is_dir()) { + (&TargetType::Directory, false) => { + Err(format!("target: '{}' is not a directory", target.display()).into()) + } + (&TargetType::File, true) => { + Err(format!("cannot overwrite directory '{}' with non-directory", target.display()).into()) + } + _ => Ok(()), + } +} + + +/// Remove the `root` prefix from `source` and prefix it with `target` +/// to create a file that is local to `target` +/// # Examples +/// +/// ```ignore +/// assert!(uu_cp::localize_to_target( +/// &Path::new("a/source/"), +/// &Path::new("a/source/c.txt"), +/// &Path::new("target/"), +/// ).unwrap() == Path::new("target/c.txt")) +/// ``` +pub fn localize_to_target(root: &Path, source: &Path, target: &Path) -> CopyResult { + let local_to_root = source.strip_prefix(&root)?; + Ok(target.join(&local_to_root)) +} + + +pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> io::Result { // We have to take symlinks and relative paths into account. let pathbuf1 = try!(canonicalize(p1, CanonicalizeMode::Normal)); let pathbuf2 = try!(canonicalize(p2, CanonicalizeMode::Normal)); Ok(pathbuf1 == pathbuf2) } + + +#[test] +fn test_cp_localize_to_target() { + assert!(localize_to_target( + &Path::new("a/source/"), + &Path::new("a/source/c.txt"), + &Path::new("target/") + ).unwrap() == Path::new("target/c.txt")) +} diff --git a/tests/fixtures/cp/existing_file.txt b/tests/fixtures/cp/existing_file.txt new file mode 100644 index 000000000..651b4c7b0 --- /dev/null +++ b/tests/fixtures/cp/existing_file.txt @@ -0,0 +1 @@ +Cogito ergo sum. diff --git a/tests/fixtures/cp/how_are_you.txt b/tests/fixtures/cp/how_are_you.txt new file mode 100644 index 000000000..d18c6b11f --- /dev/null +++ b/tests/fixtures/cp/how_are_you.txt @@ -0,0 +1 @@ +How are you? diff --git a/tests/test_cp.rs b/tests/test_cp.rs index 7aa01b5c3..02f7c3b5a 100644 --- a/tests/test_cp.rs +++ b/tests/test_cp.rs @@ -1,11 +1,15 @@ use common::util::*; +use std::fs::set_permissions; -static TEST_HELLO_WORLD_SOURCE: &'static str = "hello_world.txt"; -static TEST_HELLO_WORLD_DEST: &'static str = "copy_of_hello_world.txt"; -static TEST_COPY_TO_FOLDER: &'static str = "hello_dir/"; -static TEST_COPY_TO_FOLDER_FILE: &'static str = "hello_dir/hello_world.txt"; -static TEST_COPY_FROM_FOLDER: &'static str = "hello_dir_with_file/"; -static TEST_COPY_FROM_FOLDER_FILE: &'static str = "hello_dir_with_file/hello_world.txt"; +static TEST_EXISTING_FILE: &str = "existing_file.txt"; +static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; +static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt"; +static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt"; +static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt"; +static TEST_COPY_TO_FOLDER: &str = "hello_dir/"; +static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt"; +static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/"; +static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt"; #[test] fn test_cp_cp() { @@ -23,6 +27,58 @@ fn test_cp_cp() { assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); } + +#[test] +fn test_cp_duplicate_files() { + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_COPY_TO_FOLDER) + .run(); + + assert!(result.success); + assert!(result.stderr.contains("specified more than once")); + assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); +} + + +#[test] +fn test_cp_multiple_files_target_is_file() { + let (_, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_EXISTING_FILE) + .run(); + + assert!(!result.success); + assert!(result.stderr.contains("not a directory")); +} + +#[test] +fn test_cp_directory_not_recursive() { + let (_, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_COPY_TO_FOLDER) + .arg(TEST_HELLO_WORLD_DEST) + .run(); + + assert!(!result.success); + assert!(result.stderr.contains("omitting directory")); +} + + +#[test] +fn test_cp_multiple_files() { + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg(TEST_COPY_TO_FOLDER) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); + assert_eq!(at.read(TEST_HOW_ARE_YOU_DEST), "How are you?\n"); +} + #[test] fn test_cp_recurse() { //let (at, mut ucmd) = at_and_ucmd!(); @@ -82,3 +138,156 @@ fn test_cp_with_dirs() { assert!(result_from_dir.success); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); } + +#[test] +fn test_cp_arg_target_directory() { + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("-t") + .arg(TEST_COPY_TO_FOLDER) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); +} + +#[test] +fn test_cp_arg_no_target_directory() { + let (_, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("-v") + .arg("-T") + .arg(TEST_COPY_TO_FOLDER) + .run(); + + assert!(!result.success); + assert!(result.stderr.contains("cannot overwrite directory")); +} + +#[test] +fn test_cp_arg_interactive() { + let (_, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg("-i") + .pipe_in("N\n") + .run(); + + assert!(result.success); + assert!(result.stderr.contains("Not overwriting")); +} + +#[test] +#[cfg(target_os="unix")] +fn test_cp_arg_link() { + use std::os::linux::fs::MetadataExt; + + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--link") + .arg(TEST_HELLO_WORLD_DEST) + .run(); + + assert!(result.success); + assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2); +} + +#[test] +fn test_cp_arg_symlink() { + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--symbolic-link") + .arg(TEST_HELLO_WORLD_DEST) + .run(); + + assert!(result.success); + assert!(at.is_symlink(TEST_HELLO_WORLD_DEST)); +} + + +#[test] +fn test_cp_arg_no_clobber() { + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--no-clobber") + .arg(TEST_HOW_ARE_YOU_SOURCE) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); + assert!(result.stderr.contains("Not overwriting")); +} + +#[test] +#[cfg(not(windows))] +fn test_cp_arg_force() { + let (at, mut ucmd) = at_and_ucmd!(); + + // create dest without write permissions + let mut permissions = at.make_file(TEST_HELLO_WORLD_DEST).metadata().unwrap().permissions(); + permissions.set_readonly(true); + set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap(); + + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--force") + .arg(TEST_HELLO_WORLD_DEST) + .run(); + + println!("{:?}", result.stderr); + println!("{:?}", result.stdout); + + assert!(result.success); + assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); +} + +/// TODO: write a better test that differentiates --remove-destination +/// from --force. Also this test currently doesn't work on +/// Windows. This test originally checked file timestamps, which +/// proved to be unreliable per target / CI platform +#[test] +#[cfg(not(windows))] +fn test_cp_arg_remove_destination() { + let (at, mut ucmd) = at_and_ucmd!(); + + // create dest without write permissions + let mut permissions = at.make_file(TEST_HELLO_WORLD_DEST).metadata().unwrap().permissions(); + permissions.set_readonly(true); + set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap(); + + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--remove-destination") + .arg(TEST_HELLO_WORLD_DEST) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); +} + +#[test] +fn test_cp_arg_backup() { + let (at, mut ucmd) = at_and_ucmd!(); + + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--backup") + .arg(TEST_HOW_ARE_YOU_SOURCE) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); + assert_eq!(at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)), "How are you?\n"); +} + +#[test] +fn test_cp_arg_suffix() { + let (at, mut ucmd) = at_and_ucmd!(); + + let result = ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg("--suffix") + .arg(".bak") + .arg(TEST_HOW_ARE_YOU_SOURCE) + .run(); + + assert!(result.success); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); + assert_eq!(at.read(&*format!("{}.bak", TEST_HOW_ARE_YOU_SOURCE)), "How are you?\n"); +} From 32bd2843d524eac066c92d4f89b7610fd2ba6219 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Thu, 20 Jul 2017 10:47:21 +0200 Subject: [PATCH 05/31] cp: add support for --update --- src/cp/cp.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 354a9cf0f..a6a977be3 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -144,6 +144,7 @@ pub enum CopyMode { SymLink, Sparse, Copy, + Update, } #[derive(Clone)] @@ -316,7 +317,11 @@ pub fn uumain(args: Vec) -> i32 { .default_value("~") .value_name("SUFFIX") .help("override the usual backup suffix")) - + .arg(Arg::with_name(OPT_UPDATE) + .short("u") + .long(OPT_UPDATE) + .help("copy only when the SOURCE file is newer than the destination file\ + or when the destination file is missing")) // TODO: implement the following args .arg(Arg::with_name(OPT_ARCHIVE) .short("a") @@ -380,11 +385,6 @@ pub fn uumain(args: Vec) -> i32 { .arg(Arg::with_name(OPT_STRIP_TRAILING_SLASHES) .long(OPT_STRIP_TRAILING_SLASHES) .help("NotImplemented: remove any trailing slashes from each SOURCE argument")) - .arg(Arg::with_name(OPT_UPDATE) - .short("u") - .long(OPT_UPDATE) - .help("NotImplemented: copy only when the SOURCE file is newer than the destination file\ - or when the destination file is missing")) .arg(Arg::with_name(OPT_ONE_FILE_SYSTEM) .short("x") .long(OPT_ONE_FILE_SYSTEM) @@ -461,6 +461,8 @@ impl CopyMode { CopyMode::SymLink } else if matches.is_present(OPT_SPARSE) { CopyMode::Sparse + } else if matches.is_present(OPT_UPDATE) { + CopyMode::Update } else { CopyMode::Copy } @@ -500,7 +502,6 @@ impl Options { OPT_REFLINK, OPT_SPARSE, OPT_STRIP_TRAILING_SLASHES, - OPT_UPDATE, OPT_ONE_FILE_SYSTEM, OPT_CONTEXT, #[cfg(windows)] OPT_FORCE, @@ -827,6 +828,22 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { CopyMode::Copy => { fs::copy(source, dest).context(&*context_for(source, dest))?; }, CopyMode::SymLink => { symlink_file(source, dest, &*context_for(source, dest))?; }, CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())), + CopyMode::Update => { + if dest.exists() { + let src_metadata = fs::metadata(source.clone())?; + let dest_metadata = fs::metadata(dest.clone())?; + + let src_time = src_metadata.modified()?; + let dest_time = dest_metadata.modified()?; + if src_time <= dest_time { + return Ok(()) + } else { + fs::copy(source, dest).context(&*context_for(source, dest))?; + } + } else { + fs::copy(source, dest).context(&*context_for(source, dest))?; + } + } }; for attribute in &options.preserve_attributes { From d8494974bcba8b758bae101205dcfe7b8294d8d8 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Sat, 22 Jul 2017 20:46:15 -0700 Subject: [PATCH 06/31] Make chmod use std where possible instead of libc; compile for Redox --- src/chmod/chmod.rs | 89 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/chmod/chmod.rs b/src/chmod/chmod.rs index cfb148bf6..b41686b24 100644 --- a/src/chmod/chmod.rs +++ b/src/chmod/chmod.rs @@ -9,6 +9,7 @@ * file that was distributed with this source code. */ +#[cfg(unix)] extern crate libc; extern crate walker; @@ -16,9 +17,9 @@ extern crate walker; extern crate uucore; use std::error::Error; -use std::ffi::CString; -use std::io::{self, Write}; -use std::mem; +use std::fs; +use std::io::Write; +use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::path::Path; use walker::Walker; @@ -74,16 +75,10 @@ pub fn uumain(mut args: Vec) -> i32 { let verbose = matches.opt_present("verbose"); let preserve_root = matches.opt_present("preserve-root"); let recursive = matches.opt_present("recursive"); - let fmode = matches.opt_str("reference").and_then(|fref| { - let s = CString::new(fref).unwrap_or_else( |_| { - crash!(1, "reference file name contains internal nul byte") - }); - let mut stat : libc::stat = unsafe { mem::uninitialized() }; - let statres = unsafe { libc::stat(s.as_ptr() as *const _, &mut stat as *mut libc::stat) }; - if statres == 0 { - Some(stat.st_mode) - } else { - crash!(1, "cannot stat attribues of '{}': {}", matches.opt_str("reference").unwrap(), io::Error::last_os_error()) + let fmode = matches.opt_str("reference").and_then(|ref fref| { + match fs::metadata(fref) { + Ok(meta) => Some(meta.mode()), + Err(err) => crash!(1, "cannot stat attribues of '{}': {}", fref, err) } }); let cmode = @@ -108,7 +103,7 @@ pub fn uumain(mut args: Vec) -> i32 { 0 } -fn chmod(files: Vec, changes: bool, quiet: bool, verbose: bool, preserve_root: bool, recursive: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { +fn chmod(files: Vec, changes: bool, quiet: bool, verbose: bool, preserve_root: bool, recursive: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { let mut r = Ok(()); for filename in &files { @@ -157,28 +152,25 @@ fn chmod(files: Vec, changes: bool, quiet: bool, verbose: bool, preserve } #[cfg(windows)] -fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { +fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { // chmod is useless on Windows // it doesn't set any permissions at all // instead it just sets the readonly attribute on the file Err(0) } -#[cfg(unix)] -fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { - let path = CString::new(name).unwrap_or_else(|e| panic!("{}", e)); - let mut stat: libc::stat = unsafe { mem::uninitialized() }; - let statres = unsafe { libc::stat(path.as_ptr(), &mut stat as *mut libc::stat) }; - let mut fperm = - if statres == 0 { - stat.st_mode & 0o7777 - } else { +#[cfg(any(unix, target_os = "redox"))] +fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool, fmode: Option, cmode: Option<&String>) -> Result<(), i32> { + let mut fperm = match fs::metadata(name) { + Ok(meta) => meta.mode() & 0o7777, + Err(err) => { if !quiet { - show_error!("{}", io::Error::last_os_error()); + show_error!("{}", err); } return Err(1); - }; + } + }; match fmode { - Some(mode) => try!(change_file(fperm, mode, file, &path, verbose, changes, quiet)), + Some(mode) => try!(change_file(fperm, mode, file, name, verbose, changes, quiet)), None => { for mode in cmode.unwrap().split(',') { // cmode is guaranteed to be Some in this case let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; @@ -190,7 +182,7 @@ fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool }; match result { Ok(mode) => { - try!(change_file(fperm, mode, file, &path, verbose, changes, quiet)); + try!(change_file(fperm, mode, file, name, verbose, changes, quiet)); fperm = mode; } Err(f) => { @@ -207,13 +199,13 @@ fn chmod_file(file: &Path, name: &str, changes: bool, quiet: bool, verbose: bool Ok(()) } -fn parse_numeric(fperm: libc::mode_t, mut mode: &str) -> Result { +fn parse_numeric(fperm: u32, mut mode: &str) -> Result { let (op, pos) = try!(parse_op(mode, Some('='))); mode = mode[pos..].trim_left_matches('0'); if mode.len() > 4 { Err(format!("mode is too large ({} > 7777)", mode)) } else { - match libc::mode_t::from_str_radix(mode, 8) { + match u32::from_str_radix(mode, 8) { Ok(change) => { Ok(match op { '+' => fperm | change, @@ -227,14 +219,23 @@ fn parse_numeric(fperm: libc::mode_t, mut mode: &str) -> Result Result { +fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result { + #[cfg(unix)] + use libc::umask; + + #[cfg(target_os = "redox")] + unsafe fn umask(_mask: u32) -> u32 { + // XXX + 0 + } + let (mask, pos) = parse_levels(mode); if pos == mode.len() { return Err(format!("invalid mode ({})", mode)); } let respect_umask = pos == 0; let last_umask = unsafe { - libc::umask(0) + umask(0) }; mode = &mode[pos..]; while mode.len() > 0 { @@ -253,12 +254,12 @@ fn parse_symbolic(mut fperm: libc::mode_t, mut mode: &str, file: &Path) -> Resul } } unsafe { - libc::umask(last_umask); + umask(last_umask); } Ok(fperm) } -fn parse_levels(mode: &str) -> (libc::mode_t, usize) { +fn parse_levels(mode: &str) -> (u32, usize) { let mut mask = 0; let mut pos = 0; for ch in mode.chars() { @@ -290,7 +291,7 @@ fn parse_op(mode: &str, default: Option) -> Result<(char, usize), String> } } -fn parse_change(mode: &str, fperm: libc::mode_t, file: &Path) -> (libc::mode_t, usize) { +fn parse_change(mode: &str, fperm: u32, file: &Path) -> (u32, usize) { let mut srwx = fperm & 0o7000; let mut pos = 0; for ch in mode.chars() { @@ -318,24 +319,24 @@ fn parse_change(mode: &str, fperm: libc::mode_t, file: &Path) -> (libc::mode_t, (srwx, pos) } -fn change_file(fperm: libc::mode_t, mode: libc::mode_t, file: &Path, path: &CString, verbose: bool, changes: bool, quiet: bool) -> Result<(), i32> { +fn change_file(fperm: u32, mode: u32, file: &Path, path: &str, verbose: bool, changes: bool, quiet: bool) -> Result<(), i32> { if fperm == mode { if verbose && !changes { show_info!("mode of '{}' retained as {:o}", file.display(), fperm); } Ok(()) - } else if unsafe { libc::chmod(path.as_ptr(), mode) } == 0 { - if verbose || changes { - show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode); - } - Ok(()) - } else { + } else if let Err(err) = fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode)) { if !quiet { - show_error!("{}", io::Error::last_os_error()); + show_error!("{}", err); } if verbose { show_info!("failed to change mode of file '{}' from {:o} to {:o}", file.display(), fperm, mode); } - return Err(1); + Err(1) + } else { + if verbose || changes { + show_info!("mode of '{}' changed from {:o} to {:o}", file.display(), fperm, mode); + } + Ok(()) } } From 99af79a7fab9f43a5ab393637c96de513daab5e3 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Sun, 23 Jul 2017 17:28:16 -0700 Subject: [PATCH 07/31] Cast umask result, and comment about Redox --- src/chmod/chmod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chmod/chmod.rs b/src/chmod/chmod.rs index b41686b24..7c3a5a352 100644 --- a/src/chmod/chmod.rs +++ b/src/chmod/chmod.rs @@ -225,7 +225,7 @@ fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result u32 { - // XXX + // XXX Redox does not currently have umask 0 } @@ -243,7 +243,7 @@ fn parse_symbolic(mut fperm: u32, mut mode: &str, file: &Path) -> Result Date: Tue, 25 Jul 2017 16:26:36 -0700 Subject: [PATCH 08/31] Make ls build on Redox --- src/ls/ls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ls/ls.rs b/src/ls/ls.rs index d22b524ad..9f0064aca 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -34,7 +34,7 @@ use std::io::Write; #[cfg(unix)] use std::collections::HashMap; -#[cfg(unix)] +#[cfg(any(unix, target_os = "redox"))] use std::os::unix::fs::MetadataExt; #[cfg(unix)] use std::os::unix::fs::FileTypeExt; @@ -183,7 +183,7 @@ fn list(options: getopts::Matches) { } } -#[cfg(unix)] +#[cfg(any(unix, target_os = "redox"))] fn sort_entries(entries: &mut Vec, options: &getopts::Matches) { let mut reverse = options.opt_present("r"); if options.opt_present("t") { From 3d34d06a1ef2f4f25b0dcd132547c4ad1a0526b7 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Wed, 26 Jul 2017 19:43:21 -0700 Subject: [PATCH 09/31] Add LOC counter to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cdb4b1173..6772af5ed 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ uutils coreutils [![License](http://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/coreutils/blob/master/LICENSE) [![Build Status](https://api.travis-ci.org/uutils/coreutils.svg?branch=master)](https://travis-ci.org/uutils/coreutils) [![Build status](https://ci.appveyor.com/api/projects/status/787ltcxgy86r20le?svg=true)](https://ci.appveyor.com/project/Arcterus/coreutils) +[![LOC](https://tokei.rs/b1/github/uutils/coreutils?category=code)](https://github.com/Aaronepower/tokei) uutils is an attempt at writing universal (as in cross-platform) CLI utils in [Rust](http://www.rust-lang.org). This repo is to aggregate the GNU From 3c3f5ab2a39bec862df1d72a4741826ee356fb40 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 25 Jul 2017 13:27:02 -0700 Subject: [PATCH 10/31] Fix ordering of ls -t, which was backwards --- src/ls/ls.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ls/ls.rs b/src/ls/ls.rs index d22b524ad..dfa55abc1 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -31,6 +31,7 @@ use std::fs; use std::fs::{DirEntry, FileType, Metadata}; use std::path::{Path, PathBuf}; use std::io::Write; +use std::cmp::Reverse; #[cfg(unix)] use std::collections::HashMap; @@ -191,9 +192,10 @@ fn sort_entries(entries: &mut Vec, options: &getopts::Matches) { entries.sort_by_key(|k| get_metadata(k, options).map(|md| md.ctime()).unwrap_or(0)); } else { entries.sort_by_key(|k| { - get_metadata(k, options) + // Newest first + Reverse(get_metadata(k, options) .and_then(|md| md.modified()) - .unwrap_or(std::time::UNIX_EPOCH) + .unwrap_or(std::time::UNIX_EPOCH)) }); } } else if options.opt_present("S") { @@ -213,9 +215,10 @@ fn sort_entries(entries: &mut Vec, options: &getopts::Matches) { let mut reverse = options.opt_present("r"); if options.opt_present("t") { entries.sort_by_key(|k| { - get_metadata(k, options) + // Newest first + Reverse(get_metadata(k, options) .and_then(|md| md.modified()) - .unwrap_or(std::time::UNIX_EPOCH) + .unwrap_or(std::time::UNIX_EPOCH)) }); } else if options.opt_present("S") { entries.sort_by_key(|k| get_metadata(k, options).map(|md| md.file_size()).unwrap_or(0)); From b39689ac37e7585e85f97804b251eff4844a1743 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Wed, 26 Jul 2017 23:21:23 -0700 Subject: [PATCH 11/31] rm: massive refactor --- Cargo.lock | 12 +++ src/rm/Cargo.toml | 2 + src/rm/rm.rs | 243 +++++++++++++++++++++++----------------------- 3 files changed, 137 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c80206ba9..f28152b96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -914,12 +914,23 @@ dependencies = [ "uucore 0.0.1", ] +[[package]] +name = "remove_dir_all" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rm" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", + "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1490,6 +1501,7 @@ dependencies = [ "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" +"checksum remove_dir_all 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0882bc41b0ba6131c7f0ce97233b62d8099e3f3abc60d4938185d3e35439c0cc" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" diff --git a/src/rm/Cargo.toml b/src/rm/Cargo.toml index 0e97a4188..51b305d16 100644 --- a/src/rm/Cargo.toml +++ b/src/rm/Cargo.toml @@ -9,6 +9,8 @@ path = "rm.rs" [dependencies] getopts = "0.2.14" +walkdir = "1.0.7" +remove_dir_all = "0.2" uucore = { path="../uucore" } [[bin]] diff --git a/src/rm/rm.rs b/src/rm/rm.rs index 3c099a4dd..969e6327e 100644 --- a/src/rm/rm.rs +++ b/src/rm/rm.rs @@ -10,6 +10,8 @@ */ extern crate getopts; +extern crate remove_dir_all; +extern crate walkdir; #[macro_use] extern crate uucore; @@ -18,7 +20,9 @@ use std::collections::VecDeque; use std::fs; use std::io::{stdin, stderr, BufRead, Write}; use std::ops::BitOr; -use std::path::{Path, PathBuf}; +use std::path::Path; +use remove_dir_all::remove_dir_all; +use walkdir::{DirEntry, WalkDir}; #[derive(Eq, PartialEq, Clone, Copy)] enum InteractiveMode { @@ -27,6 +31,17 @@ enum InteractiveMode { InteractiveAlways } +struct Options { + force: bool, + interactive: InteractiveMode, + #[allow(dead_code)] + one_fs: bool, + preserve_root: bool, + recursive: bool, + dir: bool, + verbose: bool +} + static NAME: &'static str = "rm"; static VERSION: &'static str = env!("CARGO_PKG_VERSION"); @@ -77,32 +92,36 @@ pub fn uumain(args: Vec) -> i32 { show_error!("for help, try '{0} --help'", NAME); return 1; } else { - let force = matches.opt_present("force"); - let interactive = - if matches.opt_present("i") { - InteractiveMode::InteractiveAlways - } else if matches.opt_present("I") { - InteractiveMode::InteractiveOnce - } else if matches.opt_present("interactive") { - match &matches.opt_str("interactive").unwrap()[..] { - "none" => InteractiveMode::InteractiveNone, - "once" => InteractiveMode::InteractiveOnce, - "always" => InteractiveMode::InteractiveAlways, - val => { - crash!(1, "Invalid argument to interactive ({})", val) + let options = Options { + force: matches.opt_present("force"), + interactive: { + if matches.opt_present("i") { + InteractiveMode::InteractiveAlways + } else if matches.opt_present("I") { + InteractiveMode::InteractiveOnce + } else if matches.opt_present("interactive") { + match &matches.opt_str("interactive").unwrap()[..] { + "none" => InteractiveMode::InteractiveNone, + "once" => InteractiveMode::InteractiveOnce, + "always" => InteractiveMode::InteractiveAlways, + val => { + crash!(1, "Invalid argument to interactive ({})", val) + } } + } else { + InteractiveMode::InteractiveNone } - } else { - InteractiveMode::InteractiveNone - }; - let one_fs = matches.opt_present("one-file-system"); - let preserve_root = !matches.opt_present("no-preserve-root"); - let recursive = matches.opt_present("recursive"); - let dir = matches.opt_present("dir"); - let verbose = matches.opt_present("verbose"); - if interactive == InteractiveMode::InteractiveOnce && (recursive || matches.free.len() > 3) { + }, + one_fs: matches.opt_present("one-file-system"), + preserve_root: !matches.opt_present("no-preserve-root"), + recursive: matches.opt_present("recursive"), + dir: matches.opt_present("dir"), + verbose: matches.opt_present("verbose") + }; + if options.interactive == InteractiveMode::InteractiveOnce + && (options.recursive || matches.free.len() > 3) { let msg = - if recursive { + if options.recursive { "Remove all arguments recursively? " } else { "Remove all arguments? " @@ -112,7 +131,7 @@ pub fn uumain(args: Vec) -> i32 { } } - if remove(matches.free, force, interactive, one_fs, preserve_root, recursive, dir, verbose) { + if remove(matches.free, options) { return 1; } } @@ -120,118 +139,100 @@ pub fn uumain(args: Vec) -> i32 { 0 } -// TODO: implement one-file-system -#[allow(unused_variables)] -fn remove(files: Vec, force: bool, interactive: InteractiveMode, one_fs: bool, preserve_root: bool, recursive: bool, dir: bool, verbose: bool) -> bool { +// TODO: implement one-file-system (this may get partially implemented in walkdir) +fn remove(files: Vec, options: Options) -> bool { let mut had_err = false; for filename in &files { - let filename = &filename[..]; let file = Path::new(filename); - match file.symlink_metadata() { - Ok(metadata) => if metadata.is_dir() { - if recursive && (filename != "/" || !preserve_root) { - if interactive != InteractiveMode::InteractiveAlways { - if let Err(e) = fs::remove_dir_all(file) { - had_err = true; - show_error!("could not remove '{}': {}", filename, e); - }; - } else { - let mut dirs: VecDeque = VecDeque::new(); - let mut files: Vec = Vec::new(); - let mut rmdirstack: Vec = Vec::new(); - dirs.push_back(file.to_path_buf()); - - while !dirs.is_empty() { - let dir = dirs.pop_front().unwrap(); - if !prompt(&(format!("rm: descend into directory '{}'? ", dir.display()))[..]) { - continue; - } - - // iterate over items in this directory, adding to either file or - // directory queue - match fs::read_dir(dir.as_path()) { - Ok(rdir) => { - for ent in rdir { - match ent { - Ok(ref f) => match f.file_type() { - Ok(t) => { - if t.is_dir() { - dirs.push_back(f.path()); - } else { - files.push(f.path()); - } - }, - Err(e) => { - had_err = true; - show_error!("reading '{}': {}", f.path().display(), e); - }, - }, - Err(ref e) => { - had_err = true; - show_error!("recursing into '{}': {}", filename, e); - }, - }; - } - }, - Err(e) => { - had_err = true; - show_error!("could not recurse into '{}': {}", dir.display(), e); - continue; - }, - }; - - for f in &files { - had_err = remove_file(f.as_path(), interactive, verbose).bitor(had_err); - } - - files.clear(); - rmdirstack.push(dir); - } - - for d in rmdirstack.iter().rev() { - had_err = remove_dir(d.as_path(), interactive, verbose).bitor(had_err); - } - } - } else if dir && (filename != "/" || !preserve_root) { - had_err = remove_dir(&file, interactive, verbose).bitor(had_err); + had_err = match file.symlink_metadata() { + Ok(metadata) => { + if metadata.is_dir() { + handle_dir(file, &options) } else { - if recursive { - show_error!("could not remove directory '{}'", filename); - had_err = true; - } else { - show_error!("could not remove directory '{}' (did you mean to pass '-r'?)", filename); - had_err = true; - } + remove_file(file, &options) } - } else { - had_err = remove_file(&file, interactive, verbose).bitor(had_err); - }, - Err(e) => { + } + Err(_e) => { + // TODO: actually print out the specific error // TODO: When the error is not about missing files // (e.g., permission), even rm -f should fail with // outputting the error, but there's no easy eay. - if !force { - had_err = true; + if !options.force { show_error!("no such file or directory '{}'", filename); + true + } else { + false } } + }.bitor(had_err); + } + + had_err +} + +fn handle_dir(path: &Path, options: &Options) -> bool { + let mut had_err = false; + + let is_root = path.has_root() && path.parent().is_none(); + if options.recursive && (!is_root || !options.preserve_root) { + if options.interactive != InteractiveMode::InteractiveAlways { + // we need the extra crate because apparently fs::remove_dir_all() does not function + // correctly on Windows + if let Err(e) = remove_dir_all(path) { + had_err = true; + show_error!("could not remove '{}': {}", path.display(), e); + } + } else { + let mut dirs: VecDeque = VecDeque::new(); + + for entry in WalkDir::new(path) { + match entry { + Ok(entry) => { + let file_type = entry.file_type(); + if file_type.is_dir() { + dirs.push_back(entry); + } else { + had_err = remove_file(entry.path(), options).bitor(had_err); + } + } + Err(e) => { + had_err = true; + show_error!("recursing in '{}': {}", path.display(), e); + } + } + } + + for dir in dirs.iter().rev() { + had_err = remove_dir(dir.path(), options).bitor(had_err); + } + } + } else if options.dir && (!is_root || !options.preserve_root) { + had_err = remove_dir(path, options).bitor(had_err); + } else { + if options.recursive { + show_error!("could not remove directory '{}'", path.display()); + had_err = true; + } else { + show_error!("could not remove directory '{}' (did you mean to pass '-r'?)", + path.display()); + had_err = true; } } had_err } -fn remove_dir(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool { +fn remove_dir(path: &Path, options: &Options) -> bool { let response = - if interactive == InteractiveMode::InteractiveAlways { + if options.interactive == InteractiveMode::InteractiveAlways { prompt_file(path, true) } else { true }; if response { match fs::remove_dir(path) { - Ok(_) => if verbose { println!("removed '{}'", path.display()); }, + Ok(_) => if options.verbose { println!("removed '{}'", path.display()); }, Err(e) => { show_error!("removing '{}': {}", path.display(), e); return true; @@ -242,16 +243,16 @@ fn remove_dir(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool false } -fn remove_file(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool { +fn remove_file(path: &Path, options: &Options) -> bool { let response = - if interactive == InteractiveMode::InteractiveAlways { + if options.interactive == InteractiveMode::InteractiveAlways { prompt_file(path, false) } else { true }; if response { match fs::remove_file(path) { - Ok(_) => if verbose { println!("removed '{}'", path.display()); }, + Ok(_) => if options.verbose { println!("removed '{}'", path.display()); }, Err(e) => { show_error!("removing '{}': {}", path.display(), e); return true; @@ -264,22 +265,24 @@ fn remove_file(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool fn prompt_file(path: &Path, is_dir: bool) -> bool { if is_dir { - prompt(&(format!("rm: remove directory '{}'? ", path.display()))[..]) + prompt(&(format!("rm: remove directory '{}'? ", path.display()))) } else { - prompt(&(format!("rm: remove file '{}'? ", path.display()))[..]) + prompt(&(format!("rm: remove file '{}'? ", path.display()))) } } fn prompt(msg: &str) -> bool { - stderr().write_all(msg.as_bytes()).unwrap_or(()); - stderr().flush().unwrap_or(()); + let _ = stderr().write_all(msg.as_bytes()); + let _ = stderr().flush(); + let mut buf = Vec::new(); let stdin = stdin(); let mut stdin = stdin.lock(); + match stdin.read_until('\n' as u8, &mut buf) { Ok(x) if x > 0 => { match buf[0] { - 0x59 | 0x79 => true, + b'y' | b'Y' => true, _ => false, } } From 86aa1eb47aac4cc482f8c445a3d695b2194c3ea6 Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Thu, 27 Jul 2017 00:09:42 -0700 Subject: [PATCH 12/31] ls: fix line that was forgotten in #1057 --- src/ls/ls.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ls/ls.rs b/src/ls/ls.rs index a9c8a6098..9ecdf0597 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -45,13 +45,13 @@ use unicode_width::UnicodeWidthStr; #[cfg(windows)] use std::os::windows::fs::MetadataExt; -static NAME: &'static str = "ls"; -static SUMMARY: &'static str = ""; +static NAME: &'static str = "ls"; +static SUMMARY: &'static str = ""; static LONG_HELP: &'static str = " - By default, ls will list the files and contents of any directories on - the command line, expect that it will ignore files and directories + By default, ls will list the files and contents of any directories on + the command line, expect that it will ignore files and directories whose names start with '.' -"; +"; #[cfg(unix)] static DEFAULT_COLORS: &'static str = "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"; @@ -189,7 +189,11 @@ fn sort_entries(entries: &mut Vec, options: &getopts::Matches) { let mut reverse = options.opt_present("r"); if options.opt_present("t") { if options.opt_present("c") { - entries.sort_by_key(|k| get_metadata(k, options).map(|md| md.ctime()).unwrap_or(0)); + entries.sort_by_key(|k| { + Reverse(get_metadata(k, options) + .map(|md| md.ctime()) + .unwrap_or(0)) + }); } else { entries.sort_by_key(|k| { // Newest first From 2c02fd3f0c4b1bfd984b9755108e0b95b1710c2c Mon Sep 17 00:00:00 2001 From: Alex Lyon Date: Thu, 27 Jul 2017 00:51:36 -0700 Subject: [PATCH 13/31] Format list of complete and incomplete utils as a table --- README.md | 192 +++++++++++++++++++++++++----------------------------- 1 file changed, 89 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 6772af5ed..fef10fa8b 100644 --- a/README.md +++ b/README.md @@ -145,110 +145,96 @@ Contribute To contribute to coreutils, please see [CONTRIBUTING](CONTRIBUTING.md). -To do ------ +Utilities +--------- -* [x] arch -* [x] base32 -* [x] base64 -* [x] basename -* [x] cat -* [ ] chcon -* [x] chgrp -* [x] chmod -* [x] chown -* [x] chroot -* [x] cksum -* [x] comm -* [ ] cp (not much done) -* [ ] csplit -* [x] cut -* [ ] date -* [ ] dd -* [ ] df -* [x] dircolors -* [x] dirname -* [x] du -* [x] echo -* [x] env -* [x] expand -* [ ] expr (almost done, no regular expressions) -* [x] factor -* [x] false -* [x] fmt -* [x] fold -* [x] groups -* [x] hashsum -* [x] head -* [x] hostid -* [x] hostname -* [x] id -* [ ] install (a couple of missing options) -* [ ] join -* [x] kill -* [x] link -* [x] ln -* [x] logname -* [ ] ls -* [x] ~~md5sum~~, ~~sha1sum~~, ~~sha224sum~~, ~~sha256sum~~, ~~sha384sum~~, ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) -* [x] mkdir -* [x] mkfifo -* [x] mknod -* [x] mktemp -* [x] mv -* [ ] more (in progress, needs lots of work) -* [x] nice -* [x] nl -* [x] nohup -* [x] nproc -* [ ] numfmt -* [ ] od (almost complete, `--strings` and 128-bit datatypes are missing) -* [x] paste -* [x] pathchk -* [x] pinky -* [ ] pr -* [x] printenv -* [ ] printf -* [x] ptx -* [x] pwd -* [x] readlink -* [x] realpath -* [x] relpath -* [x] rm -* [x] rmdir -* [ ] runcon -* [x] seq -* [x] shred -* [x] shuf -* [x] sleep -* [ ] sort (a couple of options implemented) -* [ ] split (a couple of missing options) -* [x] stat -* [x] stdbuf -* [ ] stty -* [x] sum -* [x] sync -* [x] tac -* [ ] tail (not all features implemented) -* [x] tee -* [ ] test (not all features implemented) -* [x] timeout -* [x] touch -* [x] tr -* [x] true -* [x] truncate -* [x] tsort -* [x] tty -* [x] uname -* [x] unexpand -* [x] uniq -* [x] unlink -* [x] uptime -* [x] users -* [x] wc -* [x] who -* [x] whoami -* [x] yes +| Done | Semi-Done | To Do | +|-----------|-----------|--------| +| arch | cp | chcon | +| base32 | expr (no regular expressions) | csplit | +| base64 | install | date | +| basename | ls | dd | +| cat | more | df | +| chgrp | od (`--strings` and 128-bit data types missing) | join | +| chmod | printf | numfmt | +| chown | sort | pr | +| chroot | split | runcon | +| cksum | tail | stty | +| comm | test | | +| cut | | | +| dircolors | | | +| dirname | | | +| du | | | +| echo | | | +| env | | | +| expand | | | +| factor | | | +| false | | | +| fmt | | | +| fold | | | +| groups | | | +| hashsum | | | +| head | | | +| hostid | | | +| hostname | | | +| id | | | +| kill | | | +| link | | | +| ln | | | +| logname | | | +| ~~md5sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| ~~sha1sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| ~~sha224sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| ~~sha256sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| ~~sha384sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| ~~sha512sum~~ (replaced by [hashsum](https://github.com/uutils/coreutils/blob/master/src/hashsum/hashsum.rs)) | | +| mkdir | | | +| mkfifo | | | +| mknod | | | +| mktemp | | | +| mv | | | +| nice | | | +| nl | | | +| nohup | | | +| nproc | | | +| paste | | | +| pathchk | | | +| pinky | | | +| printenv | | | +| ptx | | | +| pwd | | | +| readlink | | | +| realpath | | | +| relpath | | | +| rm | | | +| rmdir | | | +| seq | | | +| shred | | | +| shuf | | | +| sleep | | | +| stat | | | +| stdbuf | | | +| sum | | | +| sync | | | +| tac | | | +| tee | | | +| timeout | | | +| touch | | | +| tr | | | +| true | | | +| truncate | | | +| tsort | | | +| tty | | | +| uname | | | +| unexpand | | | +| uniq | | | +| unlink | | | +| uptime | | | +| users | | | +| wc | | | +| who | | | +| whoami | | | +| yes | | | License ------- From a73c74ecbcc9a2f2f2576cfb5c0b3cbf665fb745 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Sun, 23 Jul 2017 23:46:55 +0200 Subject: [PATCH 14/31] cp: add support for --reflink. --- src/cp/Cargo.toml | 3 ++ src/cp/cp.rs | 108 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index bdfde5a69..e12413b66 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -18,6 +18,9 @@ clap = "2.20.0" quick-error = "1.1.0" uucore = { path="../uucore" } +[target.'cfg(target_os = "linux")'.dependencies] +ioctl-sys = "0.5.2" + [[bin]] name = "cp" path = "main.rs" diff --git a/src/cp/cp.rs b/src/cp/cp.rs index a6a977be3..674e60acc 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -12,6 +12,8 @@ extern crate clap; extern crate walkdir; +#[cfg(target_os = "linux")] +#[macro_use] extern crate ioctl_sys; #[macro_use] extern crate uucore; #[macro_use] extern crate quick_error; @@ -25,9 +27,14 @@ use std::path::{Path, PathBuf, StripPrefixError}; use std::str::FromStr; use uucore::fs::{canonicalize, CanonicalizeMode}; use walkdir::WalkDir; +#[cfg(target_os = "linux")] use std::os::unix::io::IntoRawFd; +use std::fs::File; +use std::fs::OpenOptions; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; +#[cfg(target_os = "linux")] ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); + quick_error! { #[derive(Debug)] pub enum Error { @@ -125,6 +132,11 @@ pub enum OverwriteMode { NoClobber, } +#[derive (Clone, Eq, PartialEq)] +pub enum ReflinkMode { + Always, Auto, Never +} + /// Specifies the expected file type of copy target pub enum TargetType { Directory, @@ -170,6 +182,8 @@ pub struct Options { one_file_system: bool, overwrite: OverwriteMode, parents: bool, + reflink: bool, + reflink_mode: ReflinkMode, preserve_attributes: Vec, recursive: bool, backup_suffix: String, @@ -273,6 +287,7 @@ pub fn uumain(args: Vec) -> i32 { .arg(Arg::with_name(OPT_LINK) .short("l") .long(OPT_LINK) + .overrides_with(OPT_REFLINK) .help("hard-link files instead of copying")) .arg(Arg::with_name(OPT_NO_CLOBBER) .short("n") @@ -294,6 +309,7 @@ pub fn uumain(args: Vec) -> i32 { .short("s") .long(OPT_SYMBOLIC_LINK) .conflicts_with(OPT_LINK) + .overrides_with(OPT_REFLINK) .help("make symbolic links instead of copying")) .arg(Arg::with_name(OPT_FORCE) .short("f") @@ -322,6 +338,12 @@ pub fn uumain(args: Vec) -> i32 { .long(OPT_UPDATE) .help("copy only when the SOURCE file is newer than the destination file\ or when the destination file is missing")) + .arg(Arg::with_name(OPT_REFLINK) + .long(OPT_REFLINK) + .takes_value(true) + .value_name("WHEN") + .help("control clone/CoW copies. See below")) + // TODO: implement the following args .arg(Arg::with_name(OPT_ARCHIVE) .short("a") @@ -331,6 +353,7 @@ pub fn uumain(args: Vec) -> i32 { .arg(Arg::with_name(OPT_ATTRIBUTES_ONLY) .long(OPT_ATTRIBUTES_ONLY) .conflicts_with(OPT_COPY_CONTENTS) + .overrides_with(OPT_REFLINK) .help("NotImplemented: don't copy the file data, just the attributes")) .arg(Arg::with_name(OPT_COPY_CONTENTS) .long(OPT_COPY_CONTENTS) @@ -372,11 +395,6 @@ pub fn uumain(args: Vec) -> i32 { .arg(Arg::with_name(OPT_PARENTS) .long(OPT_PARENTS) .help("NotImplemented: use full source file name under DIRECTORY")) - .arg(Arg::with_name(OPT_REFLINK) - .long(OPT_REFLINK) - .takes_value(true) - .value_name("WHEN") - .help("NotImplemented: control clone/CoW copies. See below")) .arg(Arg::with_name(OPT_SPARSE) .long(OPT_SPARSE) .takes_value(true) @@ -499,7 +517,6 @@ impl Options { OPT_PRESERVE, OPT_NO_PRESERVE, OPT_PARENTS, - OPT_REFLINK, OPT_SPARSE, OPT_STRIP_TRAILING_SLASHES, OPT_ONE_FILE_SYSTEM, @@ -541,7 +558,6 @@ impl Options { } else { vec![] }; - let options = Options { attributes_only: matches.is_present(OPT_ATTRIBUTES_ONLY), copy_contents: matches.is_present(OPT_COPY_CONTENTS), @@ -553,6 +569,24 @@ impl Options { backup_suffix: matches.value_of(OPT_SUFFIX).unwrap().to_string(), update: matches.is_present(OPT_UPDATE), verbose: matches.is_present(OPT_VERBOSE), + reflink: matches.is_present(OPT_REFLINK), + reflink_mode: { + if let Some(reflink) = matches.value_of(OPT_REFLINK) { + match reflink { + "always" => { + ReflinkMode::Always + }, + "auto" => { + ReflinkMode::Auto + }, + value => { + return Err(Error::InvalidArgument(format!("invalid argument '{}' for \'reflink\'", value))) + } + } + } else { + ReflinkMode::Never + } + }, backup, no_target_dir, preserve_attributes, @@ -824,10 +858,16 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { } match options.copy_mode { - CopyMode::Link => { fs::hard_link(source, dest).context(&*context_for(source, dest))?; }, - CopyMode::Copy => { fs::copy(source, dest).context(&*context_for(source, dest))?; }, - CopyMode::SymLink => { symlink_file(source, dest, &*context_for(source, dest))?; }, - CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())), + CopyMode::Link => { + fs::hard_link(source, dest).context(&*context_for(source, dest))?; + } + CopyMode::Copy => { + copy_helper(source, dest, options)?; + } + CopyMode::SymLink => { + symlink_file(source, dest, &*context_for(source, dest))?; + } + CopyMode::Sparse => return Err(Error::NotImplemented(OPT_SPARSE.to_string())), CopyMode::Update => { if dest.exists() { let src_metadata = fs::metadata(source.clone())?; @@ -836,12 +876,12 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { let src_time = src_metadata.modified()?; let dest_time = dest_metadata.modified()?; if src_time <= dest_time { - return Ok(()) + return Ok(()); } else { - fs::copy(source, dest).context(&*context_for(source, dest))?; + copy_helper(source, dest, options)?; } } else { - fs::copy(source, dest).context(&*context_for(source, dest))?; + copy_helper(source, dest, options)?; } } }; @@ -853,6 +893,46 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { Ok(()) } +///Copy the file from `source` to `dest` either using the normal `fs::copy` or the +///`FICLONE` ioctl if --reflink is specified and the filesystem supports it. +fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { + if options.reflink { + #[cfg(not(target_os = "linux"))] + return Err(format!("--reflink is only supported on linux").into()); + + #[cfg(target_os = "linux")] + { + let src_file = File::open(source).unwrap().into_raw_fd(); + let dst_file = OpenOptions::new() + .write(true) + .truncate(false) + .create(true) + .open(dest) + .unwrap() + .into_raw_fd(); + match options.reflink_mode { + ReflinkMode::Always => unsafe { + let result = ficlone(dst_file, src_file as *const i32); + if result != 0 { + return Err(format!("failed to clone {:?} from {:?}: {}", source, dest, std::io::Error::last_os_error()).into()); + } else { + return Ok(()) + } + }, + ReflinkMode::Auto => unsafe { + let result = ficlone(dst_file, src_file as *const i32); + if result != 0 { + fs::copy(source, dest).context(&*context_for(source, dest))?; + } + }, + ReflinkMode::Never => {} + } + } + } else { + fs::copy(source, dest).context(&*context_for(source, dest))?; + } + Ok(()) +} /// Generate an error message if `target` is not the correct `target_type` pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> { From 7421f7c6aa41d6185cf623d826c9d7e21987c5ce Mon Sep 17 00:00:00 2001 From: penguwin Date: Sun, 6 Aug 2017 20:54:18 +0200 Subject: [PATCH 15/31] README: Moved 'date' into the 'Semi-Done' section. 'date' has been partially implemented into the coreutils. According to this I decided to move 'date' into the 'Semi-Done' section from the readme utilities chart. --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fef10fa8b..43ab6f8b1 100644 --- a/README.md +++ b/README.md @@ -152,16 +152,16 @@ Utilities |-----------|-----------|--------| | arch | cp | chcon | | base32 | expr (no regular expressions) | csplit | -| base64 | install | date | -| basename | ls | dd | -| cat | more | df | -| chgrp | od (`--strings` and 128-bit data types missing) | join | -| chmod | printf | numfmt | -| chown | sort | pr | -| chroot | split | runcon | -| cksum | tail | stty | +| base64 | install | dd | +| basename | ls | df | +| cat | more | join | +| chgrp | od (`--strings` and 128-bit data types missing) | numfmt | +| chmod | printf | pr | +| chown | sort | runcon | +| chroot | split | stty | +| cksum | tail | | | comm | test | | -| cut | | | +| cut | date | | | dircolors | | | | dirname | | | | du | | | From 13d97ee1cc51543a803ba66c15eaa983daad0032 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 9 Aug 2017 15:35:13 -0700 Subject: [PATCH 16/31] Correct behavior of cp -r with non-existent dest --- src/cp/cp.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 674e60acc..98f47fe51 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -726,7 +726,12 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult } let root_path = Path::new(&root).canonicalize()?; - let root_parent = root_path.parent(); + + let root_parent = if target.exists() { + root_path.parent() + } else { + Some(root_path.as_path()) + }; for path in WalkDir::new(root) { let path = or_continue!(or_continue!(path).path().canonicalize()); From b92c6edf142ba5f86f6cfc2236e7551797183f9f Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Thu, 10 Aug 2017 16:04:25 -0700 Subject: [PATCH 17/31] Fix cp -r test --- tests/test_cp.rs | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/tests/test_cp.rs b/tests/test_cp.rs index 02f7c3b5a..82651be57 100644 --- a/tests/test_cp.rs +++ b/tests/test_cp.rs @@ -1,15 +1,17 @@ use common::util::*; use std::fs::set_permissions; -static TEST_EXISTING_FILE: &str = "existing_file.txt"; -static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; -static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt"; -static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt"; -static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt"; -static TEST_COPY_TO_FOLDER: &str = "hello_dir/"; -static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt"; -static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/"; -static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt"; +static TEST_EXISTING_FILE: &str = "existing_file.txt"; +static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt"; +static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt"; +static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt"; +static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt"; +static TEST_COPY_TO_FOLDER: &str = "hello_dir/"; +static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt"; +static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/"; +static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt"; +static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new/"; +static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt"; #[test] fn test_cp_cp() { @@ -81,27 +83,17 @@ fn test_cp_multiple_files() { #[test] fn test_cp_recurse() { - //let (at, mut ucmd) = at_and_ucmd!(); - let scene = TestScenario::new(util_name!()); - let at = &scene.fixtures; + let (at, mut ucmd) = at_and_ucmd!(); - // Invoke our binary to make the copy. - let result_to_dir = scene.ucmd() - .arg(TEST_HELLO_WORLD_SOURCE) - .arg(TEST_COPY_TO_FOLDER) + let result = ucmd + .arg("-r") + .arg(TEST_COPY_FROM_FOLDER) + .arg(TEST_COPY_TO_FOLDER_NEW) .run(); - assert!(result_to_dir.success); - assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); - - let result = scene.ucmd() - .arg("-r") - .arg(TEST_COPY_FROM_FOLDER) - .arg(TEST_COPY_TO_FOLDER) - .run(); assert!(result.success); // Check the content of the destination file that was copied. - assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); + assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n"); } #[test] From de4bee232149ca55d940545d4cf5b1be6c842d68 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Sat, 12 Aug 2017 15:17:37 -0700 Subject: [PATCH 18/31] mktemp fix for Redox --- src/mktemp/tempdir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mktemp/tempdir.rs b/src/mktemp/tempdir.rs index 186bcd8f5..a2b3ec86c 100644 --- a/src/mktemp/tempdir.rs +++ b/src/mktemp/tempdir.rs @@ -11,7 +11,7 @@ use std::path::Path; // enough that an attacker will run out of luck before we run out of patience. const NUM_RETRIES: u32 = 1 << 31; -#[cfg(unix)] +#[cfg(any(unix, target_os = "redox"))] fn create_dir>(path: P) -> IOResult<()> { use std::fs::DirBuilder; use std::os::unix::fs::DirBuilderExt; From 79d5d14d115d6507d9720eb9a3c5fa3a880adc15 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Sat, 12 Aug 2017 15:20:50 -0700 Subject: [PATCH 19/31] Make install work on Redox, by using fs::set_permissions This is the same change I made earlier to chmod --- src/install/install.rs | 8 +++---- src/install/mode.rs | 51 +++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/install/install.rs b/src/install/install.rs index 53fe7b23a..1ce655fd0 100644 --- a/src/install/install.rs +++ b/src/install/install.rs @@ -27,12 +27,12 @@ static SUMMARY: &'static str = "Copy SOURCE to DEST or multiple SOURCE(s) to the DIRECTORY, while setting permission modes and owner/group"; static LONG_HELP: &'static str = ""; -const DEFAULT_MODE: libc::mode_t = 755; +const DEFAULT_MODE: u32 = 755; #[allow(dead_code)] pub struct Behaviour { main_function: MainFunction, - specified_mode: Option, + specified_mode: Option, suffix: String, verbose: bool } @@ -47,7 +47,7 @@ pub enum MainFunction { impl Behaviour { /// Determine the mode for chmod after copy. - pub fn mode(&self) -> libc::mode_t { + pub fn mode(&self) -> u32 { match self.specified_mode { Some(x) => x, None => DEFAULT_MODE @@ -209,7 +209,7 @@ fn behaviour(matches: &getopts::Matches) -> Result { let considering_dir: bool = MainFunction::Directory == main_function; - let specified_mode: Option = if matches.opt_present("mode") { + let specified_mode: Option = if matches.opt_present("mode") { match matches.opt_str("mode") { Some(x) => { match mode::parse(&x[..], considering_dir) { diff --git a/src/install/mode.rs b/src/install/mode.rs index fefb498fc..42aaa8478 100644 --- a/src/install/mode.rs +++ b/src/install/mode.rs @@ -2,9 +2,10 @@ extern crate libc; use std::io::Write; use std::path::Path; +use std::fs; /// Takes a user-supplied string and tries to parse to u16 mode bitmask. -pub fn parse(mode_string: &str, considering_dir: bool) -> Result { +pub fn parse(mode_string: &str, considering_dir: bool) -> Result { let numbers: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; // Passing 000 as the existing permissions seems to mirror GNU behaviour. @@ -19,21 +20,12 @@ pub fn parse(mode_string: &str, considering_dir: bool) -> Result Result<(), ()> { - use std::ffi::CString; - use std::io::Error; - - let file = CString::new(path.as_os_str().to_str().unwrap()). - unwrap_or_else(|e| crash!(1, "{}", e)); - let mode = mode as libc::mode_t; - - if unsafe { libc::chmod(file.as_ptr(), mode) } != 0 { - show_info!("{}: chmod failed with errno {}", path.display(), - Error::last_os_error().raw_os_error().unwrap()); - return Err(()); - } - Ok(()) +#[cfg(any(unix, target_os = "redox"))] +pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(path, fs::Permissions::from_mode(mode)).map_err(|err| { + show_info!("{}: chmod failed with error {}", path.display(), err); + }) } /// chmod a file or directory on Windows. @@ -41,7 +33,7 @@ pub fn chmod(path: &Path, mode: libc::mode_t) -> Result<(), ()> { /// Adapted from mkdir.rs. /// #[cfg(windows)] -pub fn chmod(path: &Path, mode: libc::mode_t) -> Result<(), ()> { +pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> { // chmod on Windows only sets the readonly flag, which isn't even honored on directories Ok(()) } @@ -53,13 +45,13 @@ pub fn chmod(path: &Path, mode: libc::mode_t) -> Result<(), ()> { mod chmod_rs { extern crate libc; - pub fn parse_numeric(fperm: libc::mode_t, mut mode: &str) -> Result { + pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result { let (op, pos) = try!(parse_op(mode, Some('='))); mode = mode[pos..].trim_left_matches('0'); if mode.len() > 4 { Err(format!("mode is too large ({} > 7777)", mode)) } else { - match libc::mode_t::from_str_radix(mode, 8) { + match u32::from_str_radix(mode, 8) { Ok(change) => { Ok(match op { '+' => fperm | change, @@ -73,14 +65,23 @@ mod chmod_rs { } } - pub fn parse_symbolic(mut fperm: libc::mode_t, mut mode: &str, considering_dir: bool) -> Result { + pub fn parse_symbolic(mut fperm: u32, mut mode: &str, considering_dir: bool) -> Result { + #[cfg(unix)] + use libc::umask; + + #[cfg(target_os = "redox")] + unsafe fn umask(_mask: u32) -> u32 { + // XXX Redox does not currently have umask + 0 + } + let (mask, pos) = parse_levels(mode); if pos == mode.len() { return Err(format!("invalid mode ({})", mode)); } let respect_umask = pos == 0; let last_umask = unsafe { - libc::umask(0) + umask(0) }; mode = &mode[pos..]; while mode.len() > 0 { @@ -88,7 +89,7 @@ mod chmod_rs { mode = &mode[pos..]; let (mut srwx, pos) = parse_change(mode, fperm, considering_dir); if respect_umask { - srwx &= !last_umask; + srwx &= !(last_umask as u32); } mode = &mode[pos..]; match op { @@ -99,12 +100,12 @@ mod chmod_rs { } } unsafe { - libc::umask(last_umask); + umask(last_umask); } Ok(fperm) } - fn parse_levels(mode: &str) -> (libc::mode_t, usize) { + fn parse_levels(mode: &str) -> (u32, usize) { let mut mask = 0; let mut pos = 0; for ch in mode.chars() { @@ -136,7 +137,7 @@ mod chmod_rs { } } - fn parse_change(mode: &str, fperm: libc::mode_t, considering_dir: bool) -> (libc::mode_t, usize) { + fn parse_change(mode: &str, fperm: u32, considering_dir: bool) -> (u32, usize) { let mut srwx = fperm & 0o7000; let mut pos = 0; for ch in mode.chars() { From 2853905ebc5ef02f8101257dc96aae708fef7154 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 14 Aug 2017 13:13:02 -0700 Subject: [PATCH 20/31] install: Fix verbose mode - Do not mark as unimplemented - Use ascii quotes - Print newline --- src/install/install.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/install/install.rs b/src/install/install.rs index 1ce655fd0..2aacda9bb 100644 --- a/src/install/install.rs +++ b/src/install/install.rs @@ -138,8 +138,7 @@ fn parse_opts(args: Vec) -> getopts::Matches { DIRECTORY", "DIRECTORY") // TODO implement flag .optflag("T", "no-target-directory", "(unimplemented) treat DEST as a normal file") - // TODO implement flag - .optflag("v", "verbose", "(unimplemented) explain what is being done") + .optflag("v", "verbose", "explain what is being done") // TODO implement flag .optflag("P", "preserve-context", "(unimplemented) preserve security context") // TODO implement flag @@ -181,8 +180,6 @@ fn check_unimplemented(matches: &getopts::Matches) -> Result<(), &str> { Err("--target-directory, -t") } else if matches.opt_present("no-target-directory") { Err("--no-target-directory, -T") - } else if matches.opt_present("verbose") { - Err("--verbose, -v") } else if matches.opt_present("preserve-context") { Err("--preserve-context, -P") } else if matches.opt_present("context") { @@ -367,7 +364,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behaviour) -> Result<(), ()> { } if b.verbose { - print!("‘{}’ -> ‘{}’", from.display(), to.display()); + show_info!("'{}' -> '{}'", from.display(), to.display()); } Ok(()) From 48ae9b64ba62c142c1edf22c38840a97aa65ecc9 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 4 Aug 2017 13:29:57 +0200 Subject: [PATCH 21/31] cp: add support for --attributes-only. --- src/cp/cp.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 98f47fe51..3ba76c935 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -157,6 +157,7 @@ pub enum CopyMode { Sparse, Copy, Update, + AttrOnly } #[derive(Clone)] @@ -343,6 +344,11 @@ pub fn uumain(args: Vec) -> i32 { .takes_value(true) .value_name("WHEN") .help("control clone/CoW copies. See below")) + .arg(Arg::with_name(OPT_ATTRIBUTES_ONLY) + .long(OPT_ATTRIBUTES_ONLY) + .conflicts_with(OPT_COPY_CONTENTS) + .overrides_with(OPT_REFLINK) + .help("Don't copy the file data, just the attributes")) // TODO: implement the following args .arg(Arg::with_name(OPT_ARCHIVE) @@ -350,11 +356,6 @@ pub fn uumain(args: Vec) -> i32 { .long(OPT_ARCHIVE) .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_PRESERVE, OPT_NO_PRESERVE]) .help("NotImplemented: same as -dR --preserve=all")) - .arg(Arg::with_name(OPT_ATTRIBUTES_ONLY) - .long(OPT_ATTRIBUTES_ONLY) - .conflicts_with(OPT_COPY_CONTENTS) - .overrides_with(OPT_REFLINK) - .help("NotImplemented: don't copy the file data, just the attributes")) .arg(Arg::with_name(OPT_COPY_CONTENTS) .long(OPT_COPY_CONTENTS) .conflicts_with(OPT_ATTRIBUTES_ONLY) @@ -481,6 +482,8 @@ impl CopyMode { CopyMode::Sparse } else if matches.is_present(OPT_UPDATE) { CopyMode::Update + } else if matches.is_present(OPT_ATTRIBUTES_ONLY) { + CopyMode::AttrOnly } else { CopyMode::Copy } @@ -508,7 +511,6 @@ impl Options { fn from_matches(matches: &ArgMatches) -> CopyResult { let not_implemented_opts = vec![ OPT_ARCHIVE, - OPT_ATTRIBUTES_ONLY, OPT_COPY_CONTENTS, OPT_NO_DEREFERENCE_PRESERVE_LINKS, OPT_DEREFERENCE, @@ -888,6 +890,14 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { } else { copy_helper(source, dest, options)?; } + }, + CopyMode::AttrOnly => { + let dst_file = OpenOptions::new() + .write(true) + .truncate(false) + .create(true) + .open(dest) + .unwrap(); } }; From 03432db9516bae53fb95551bc64e685da81613af Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 4 Aug 2017 14:08:16 +0200 Subject: [PATCH 22/31] Added support for preserving timestamps. --- src/cp/cp.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 3ba76c935..dfef452a2 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -10,6 +10,7 @@ * that was distributed with this source code. */ +extern crate libc; extern crate clap; extern crate walkdir; #[cfg(target_os = "linux")] @@ -17,6 +18,8 @@ extern crate walkdir; #[macro_use] extern crate uucore; #[macro_use] extern crate quick_error; +use std::mem; +use std::ffi::CString; use clap::{Arg, App, ArgMatches}; use quick_error::ResultExt; use std::collections::HashSet; @@ -516,7 +519,6 @@ impl Options { OPT_DEREFERENCE, OPT_NO_DEREFERENCE, OPT_PRESERVE_DEFUALT_ATTRIBUTES, - OPT_PRESERVE, OPT_NO_PRESERVE, OPT_PARENTS, OPT_SPARSE, @@ -787,7 +789,21 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu let metadata = fs::metadata(source).context(context)?; fs::set_permissions(dest, metadata.permissions()).context(context)?; }, - Attribute::Timestamps => return Err(Error::NotImplemented("preserving timestamp not implemented".to_string())), + Attribute::Timestamps => { + let meta = fs::metadata(source)?; + let modified = meta.modified()?; + let accessed = meta.accessed()?; + let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap(); + let dest_path = CString::new(dest.as_os_str().to_str().unwrap()).unwrap(); + unsafe { + let mut stat: libc::stat = mem::zeroed(); + libc::stat(src_path.as_ptr(), &mut stat); + libc::utime(dest_path.as_ptr(), &libc::utimbuf{ + actime: stat.st_atime, + modtime: stat.st_mtime + }); + } + }, Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), Attribute::Links => return Err(Error::NotImplemented("preserving links not implemented".to_string())), Attribute::Xattr => return Err(Error::NotImplemented("preserving xattr not implemented".to_string())), @@ -892,7 +908,7 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { } }, CopyMode::AttrOnly => { - let dst_file = OpenOptions::new() + OpenOptions::new() .write(true) .truncate(false) .create(true) From 38dd8c58366ad2c68e7f8e09b377031fa72c5b9a Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Tue, 15 Aug 2017 11:42:50 +0200 Subject: [PATCH 23/31] cp: use filetime to set timestamps. --- src/cp/Cargo.toml | 1 + src/cp/cp.rs | 17 ++++------------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index e12413b66..38ca8b40c 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -17,6 +17,7 @@ walkdir = "1.0.7" clap = "2.20.0" quick-error = "1.1.0" uucore = { path="../uucore" } +filetime = "0.1" [target.'cfg(target_os = "linux")'.dependencies] ioctl-sys = "0.5.2" diff --git a/src/cp/cp.rs b/src/cp/cp.rs index dfef452a2..9b188750a 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -13,6 +13,8 @@ extern crate libc; extern crate clap; extern crate walkdir; +extern crate filetime; +use filetime::FileTime; #[cfg(target_os = "linux")] #[macro_use] extern crate ioctl_sys; #[macro_use] extern crate uucore; @@ -790,19 +792,8 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu fs::set_permissions(dest, metadata.permissions()).context(context)?; }, Attribute::Timestamps => { - let meta = fs::metadata(source)?; - let modified = meta.modified()?; - let accessed = meta.accessed()?; - let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap(); - let dest_path = CString::new(dest.as_os_str().to_str().unwrap()).unwrap(); - unsafe { - let mut stat: libc::stat = mem::zeroed(); - libc::stat(src_path.as_ptr(), &mut stat); - libc::utime(dest_path.as_ptr(), &libc::utimbuf{ - actime: stat.st_atime, - modtime: stat.st_mtime - }); - } + let metadata = fs::metadata(source)?; + filetime::set_file_times(Path::new(dest), FileTime::from_last_access_time(&metadata), FileTime::from_last_modification_time(&metadata))?; }, Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), Attribute::Links => return Err(Error::NotImplemented("preserving links not implemented".to_string())), From 6d3e9eabe414c72da13ddb040a6fe2c8dd97ca1d Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 18 Aug 2017 10:05:41 +0200 Subject: [PATCH 24/31] cp: add support for --preserve. --- src/cp/cp.rs | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 9b188750a..b924b2604 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -237,7 +237,7 @@ static OPT_ONE_FILE_SYSTEM: &str = "one-file-system"; static OPT_PARENTS: &str = "parents"; static OPT_PATHS: &str = "paths"; static OPT_PRESERVE: &str = "preserve"; -static OPT_PRESERVE_DEFUALT_ATTRIBUTES: &str = "preserve-default-attributes"; +static OPT_PRESERVE_DEFAULT_ATTRIBUTES: &str = "preserve-default-attributes"; static OPT_RECURSIVE: &str = "recursive"; static OPT_RECURSIVE_ALIAS: &str = "recursive_alias"; static OPT_REFLINK: &str = "reflink"; @@ -354,12 +354,33 @@ pub fn uumain(args: Vec) -> i32 { .conflicts_with(OPT_COPY_CONTENTS) .overrides_with(OPT_REFLINK) .help("Don't copy the file data, just the attributes")) + .arg(Arg::with_name(OPT_PRESERVE) + .long(OPT_PRESERVE) + .takes_value(true) + .multiple(true) + .use_delimiter(true) + .possible_values(PRESERVABLE_ATTRIBUTES) + .value_name("ATTR_LIST") + .conflicts_with_all(&[OPT_PRESERVE_DEFAULT_ATTRIBUTES, OPT_NO_PRESERVE, OPT_ARCHIVE]) + .help("Preserve the specified attributes (default: mode(unix only),ownership,timestamps),\ + if possible additional attributes: context, links, xattr, all")) + .arg(Arg::with_name(OPT_PRESERVE_DEFAULT_ATTRIBUTES) + .short("-p") + .long(OPT_PRESERVE_DEFAULT_ATTRIBUTES) + .conflicts_with_all(&[OPT_PRESERVE, OPT_NO_PRESERVE, OPT_ARCHIVE]) + .help("same as --preserve=mode(unix only),ownership,timestamps")) + .arg(Arg::with_name(OPT_NO_PRESERVE) + .long(OPT_NO_PRESERVE) + .takes_value(true) + .value_name("ATTR_LIST") + .conflicts_with_all(&[OPT_PRESERVE_DEFAULT_ATTRIBUTES, OPT_PRESERVE, OPT_ARCHIVE]) + .help("don't preserve the specified attributes")) // TODO: implement the following args .arg(Arg::with_name(OPT_ARCHIVE) .short("a") .long(OPT_ARCHIVE) - .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_PRESERVE, OPT_NO_PRESERVE]) + .conflicts_with_all(&[OPT_PRESERVE_DEFAULT_ATTRIBUTES, OPT_PRESERVE, OPT_NO_PRESERVE]) .help("NotImplemented: same as -dR --preserve=all")) .arg(Arg::with_name(OPT_COPY_CONTENTS) .long(OPT_COPY_CONTENTS) @@ -378,26 +399,6 @@ pub fn uumain(args: Vec) -> i32 { .long(OPT_NO_DEREFERENCE) .conflicts_with(OPT_DEREFERENCE) .help("NotImplemented: never follow symbolic links in SOURCE")) - .arg(Arg::with_name(OPT_PRESERVE_DEFUALT_ATTRIBUTES) - .short("-p") - .long(OPT_PRESERVE_DEFUALT_ATTRIBUTES) - .conflicts_with_all(&[OPT_PRESERVE, OPT_NO_PRESERVE, OPT_ARCHIVE]) - .help("NotImplemented: same as --preserve=mode(unix only),ownership,timestamps")) - .arg(Arg::with_name(OPT_PRESERVE) - .long(OPT_PRESERVE) - .takes_value(true) - .multiple(true) - .possible_values(PRESERVABLE_ATTRIBUTES) - .value_name("ATTR_LIST") - .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_NO_PRESERVE, OPT_ARCHIVE]) - .help("NotImplemented: preserve the specified attributes (default: mode(unix only),ownership,timestamps),\ - if possible additional attributes: context, links, xattr, all")) - .arg(Arg::with_name(OPT_NO_PRESERVE) - .long(OPT_NO_PRESERVE) - .takes_value(true) - .value_name("ATTR_LIST") - .conflicts_with_all(&[OPT_PRESERVE_DEFUALT_ATTRIBUTES, OPT_PRESERVE, OPT_ARCHIVE]) - .help("NotImplemented: don't preserve the specified attributes")) .arg(Arg::with_name(OPT_PARENTS) .long(OPT_PARENTS) .help("NotImplemented: use full source file name under DIRECTORY")) @@ -520,8 +521,6 @@ impl Options { OPT_NO_DEREFERENCE_PRESERVE_LINKS, OPT_DEREFERENCE, OPT_NO_DEREFERENCE, - OPT_PRESERVE_DEFUALT_ATTRIBUTES, - OPT_NO_PRESERVE, OPT_PARENTS, OPT_SPARSE, OPT_STRIP_TRAILING_SLASHES, @@ -559,7 +558,7 @@ impl Options { attributes } } - } else if matches.is_present(OPT_PRESERVE_DEFUALT_ATTRIBUTES) { + } else if matches.is_present(OPT_PRESERVE_DEFAULT_ATTRIBUTES) { DEFAULT_ATTRIBUTES.to_vec() } else { vec![] From 4cb727f80b70936f52ad91a8158bad091578b630 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Fri, 18 Aug 2017 10:44:54 +0200 Subject: [PATCH 25/31] cp: Add support for preserving xattrs. --- src/cp/Cargo.toml | 3 +++ src/cp/cp.rs | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index 38ca8b40c..30ffda4b6 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -22,6 +22,9 @@ filetime = "0.1" [target.'cfg(target_os = "linux")'.dependencies] ioctl-sys = "0.5.2" +[target.'cfg(unix)'.dependencies] +xattr="0.2.1" + [[bin]] name = "cp" path = "main.rs" diff --git a/src/cp/cp.rs b/src/cp/cp.rs index b924b2604..6a8ce8b17 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -19,6 +19,8 @@ use filetime::FileTime; #[macro_use] extern crate ioctl_sys; #[macro_use] extern crate uucore; #[macro_use] extern crate quick_error; +#[cfg(unix)] +extern crate xattr; use std::mem; use std::ffi::CString; @@ -796,7 +798,21 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu }, Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), Attribute::Links => return Err(Error::NotImplemented("preserving links not implemented".to_string())), - Attribute::Xattr => return Err(Error::NotImplemented("preserving xattr not implemented".to_string())), + Attribute::Xattr => { + #[cfg(unix)] + { + let xattrs = xattr::list(source)?; + for attr in xattrs { + if let Some(attr_value) = xattr::get(source, attr.clone())? { + xattr::set(dest, attr, &attr_value[..]); + } + } + } + #[cfg(not(unix))] + { + return Err(format!("XAttrs are only supported on unix.").into()); + } + }, Attribute::All => return Err(Error::NotImplemented("preserving a not implemented".to_string())), }) } From f7072b7dfe55d93e8d7396abd5d8cf0c982a50a2 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Sat, 19 Aug 2017 13:35:53 +0200 Subject: [PATCH 26/31] cp: add support for preserving links on unix. --- src/cp/cp.rs | 109 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 17 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 6a8ce8b17..cef6812ff 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -167,7 +167,7 @@ pub enum CopyMode { AttrOnly } -#[derive(Clone)] +#[derive(Clone, Eq, PartialEq)] pub enum Attribute { #[cfg(unix)] Mode, Ownership, @@ -665,27 +665,66 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<() let target_type = TargetType::determine(sources, target); verify_target_type(target, &target_type)?; + let mut preserve_hard_links = false; + for attribute in &options.preserve_attributes { + if *attribute == Attribute::Links { + preserve_hard_links = true; + } + } + #[cfg(unix)] + let mut hard_links: Vec<(String, u64)> = vec![]; + let mut non_fatal_errors = false; let mut seen_sources = HashSet::with_capacity(sources.len()); - for source in sources { if seen_sources.contains(source) { show_warning!("source '{}' specified more than once", source.display()); - } else if let Err(error) = copy_source(source, target, &target_type, options) { - show_error!("{}", error); - match error { - Error::Skipped(_) => (), - _ => non_fatal_errors = true, - } - } - seen_sources.insert(source); - } + } else { + let mut found_hard_link = false; + if preserve_hard_links { + #[cfg(unix)] + unsafe { + let dest = construct_dest_path(source, target, &target_type, options)?; + let mut stat = mem::zeroed(); + let src_path = CString::new(Path::new(&source.clone()).as_os_str().to_str().unwrap()).unwrap(); + if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { + return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); + } + + let inode = stat.st_ino; + for hard_link in &hard_links { + if hard_link.1 == inode { + std::fs::hard_link(hard_link.0.clone(), dest.clone()); + found_hard_link = true; + } + } + if stat.st_nlink > 1 && !found_hard_link { + hard_links.push((dest.clone().to_str().unwrap().to_string(), inode)); + } + } + #[cfg(windows)] + { + + } + } + if !found_hard_link { + if let Err(error) = copy_source(source, target, &target_type, options) { + show_error!("{}", error); + match error { + Error::Skipped(_) => (), + _ => non_fatal_errors = true, + } + } + } + seen_sources.insert(source); + } + } if non_fatal_errors { - Err(Error::NotAllFilesCopied) + return Err(Error::NotAllFilesCopied) } else { - Ok(()) + return Ok(()) } } @@ -710,7 +749,6 @@ fn copy_source(source: &Source, target: &Target, target_type: &TargetType, optio -> CopyResult<()> { let source_path = Path::new(&source); - if source_path.is_dir() { // Copy as directory copy_directory(source, target, options) @@ -740,6 +778,14 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult Some(root_path.as_path()) }; + #[cfg(unix)] + let mut hard_links: Vec<(String, u64)> = vec![]; + let mut preserve_hard_links = false; + for attribute in &options.preserve_attributes { + if *attribute == Attribute::Links { + preserve_hard_links = true; + } + } for path in WalkDir::new(root) { let path = or_continue!(or_continue!(path).path().canonicalize()); let local_to_root_parent = match root_parent { @@ -752,14 +798,43 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult if path.is_dir() && !local_to_target.exists() { or_continue!(fs::create_dir_all(local_to_target.clone())); } else if !path.is_dir() { - copy_file(path.as_path(), local_to_target.as_path(), options)?; + if preserve_hard_links { + #[cfg(unix)] + unsafe { + let mut stat = mem::zeroed(); + let src_path = CString::new(path.as_os_str().to_str().unwrap()).unwrap(); + + if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { + return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); + } + + let inode = stat.st_ino; + let mut found_hard_link = false; + for hard_link in &hard_links { + if hard_link.1 == inode { + std::fs::hard_link(hard_link.0.clone(), local_to_target.as_path()); + found_hard_link = true; + } + } + if stat.st_nlink > 1 && !found_hard_link { + hard_links.push((local_to_target.as_path().to_str().unwrap().to_string(), inode)); + copy_file(path.as_path(), local_to_target.as_path(), options)?; + } + } + #[cfg(windows)] + { + + } + } else { + println!("copy"); + copy_file(path.as_path(), local_to_target.as_path(), options)?; + } } } Ok(()) } - impl OverwriteMode { fn verify(&self, path: &Path) -> CopyResult<()> { match *self { @@ -797,7 +872,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu filetime::set_file_times(Path::new(dest), FileTime::from_last_access_time(&metadata), FileTime::from_last_modification_time(&metadata))?; }, Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), - Attribute::Links => return Err(Error::NotImplemented("preserving links not implemented".to_string())), + Attribute::Links => {}, Attribute::Xattr => { #[cfg(unix)] { From 6476f6e616a3c206926ffc8c90a3b63e5c4ce23d Mon Sep 17 00:00:00 2001 From: Matteo Semenzato Date: Sun, 20 Aug 2017 16:55:36 +0200 Subject: [PATCH 27/31] cp: add support for preserving links on windows. --- src/cp/Cargo.toml | 4 +++ src/cp/cp.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/cp/Cargo.toml b/src/cp/Cargo.toml index 30ffda4b6..0057d9330 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -22,6 +22,10 @@ filetime = "0.1" [target.'cfg(target_os = "linux")'.dependencies] ioctl-sys = "0.5.2" +[target.'cfg(target_os = "windows")'.dependencies] +kernel32-sys = "*" +winapi = "*" + [target.'cfg(unix)'.dependencies] xattr="0.2.1" diff --git a/src/cp/cp.rs b/src/cp/cp.rs index cef6812ff..303b41019 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -22,6 +22,18 @@ use filetime::FileTime; #[cfg(unix)] extern crate xattr; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; + +#[cfg(windows)] +extern crate kernel32; +#[cfg(windows)] +use kernel32::GetFileInformationByHandle; +#[cfg(windows)] +use kernel32::CreateFile2; +#[cfg(windows)] +extern crate winapi; + use std::mem; use std::ffi::CString; use clap::{Arg, App, ArgMatches}; @@ -673,6 +685,9 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<() } #[cfg(unix)] let mut hard_links: Vec<(String, u64)> = vec![]; + + #[cfg(windows)] + let mut hard_links: Vec<(String, (u32, u32))> = vec![]; let mut non_fatal_errors = false; let mut seen_sources = HashSet::with_capacity(sources.len()); @@ -706,7 +721,32 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<() } #[cfg(windows)] { + unsafe { + if !source.is_dir() { + let dest = construct_dest_path(source, target, &target_type, options)?; + let handle = CreateFile2(CString::new(Path::new(&source.clone()).as_os_str().to_str().unwrap()).unwrap().as_ptr() as *const u16, + winapi::winnt::GENERIC_READ, + winapi::winnt::FILE_SHARE_READ, + 0, + std::ptr::null_mut()); + let file_info = std::mem::uninitialized(); + if GetFileInformationByHandle(handle, file_info) != 0 { + return Err(format!("cannot get file information {:?}: {}", source, std::io::Error::last_os_error()).into()); + } + let file_index = ((*file_info).nFileIndexHigh, (*file_info).nFileIndexLow); + for hard_link in &hard_links { + if (hard_link.1).0 == file_index.0 && (hard_link.1).1 == file_index.1 { + std::fs::hard_link(hard_link.0.clone(), dest.clone()).unwrap(); + found_hard_link = true; + } + } + if (((*file_info).nNumberOfLinks) > 1u32) && !found_hard_link { + println!("{}", (*file_info).nNumberOfLinks); + hard_links.push((dest.clone().to_str().unwrap().to_string(), file_index)); + } + } + } } } if !found_hard_link { @@ -786,6 +826,10 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult preserve_hard_links = true; } } + + #[cfg(windows)] + let mut hard_links: Vec<(String, (u32, u32))> = vec![]; + for path in WalkDir::new(root) { let path = or_continue!(or_continue!(path).path().canonicalize()); let local_to_root_parent = match root_parent { @@ -819,14 +863,39 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult if stat.st_nlink > 1 && !found_hard_link { hard_links.push((local_to_target.as_path().to_str().unwrap().to_string(), inode)); copy_file(path.as_path(), local_to_target.as_path(), options)?; + } else { + copy_file(path.as_path(), local_to_target.as_path(), options)?; } } #[cfg(windows)] { + unsafe { + let mut found_hard_link = false; + let src_path = CString::new(path.as_os_str().to_str().unwrap()).unwrap(); + let handle = File::open(path.clone()).unwrap().as_raw_handle(); + let file_info = std::mem::uninitialized(); + let handle = File::open(path.clone()).unwrap().as_raw_handle(); + if GetFileInformationByHandle(handle, file_info) != 0 { + return Err(format!("cannot get file information {:?}: {}", src_path, std::io::Error::last_os_error()).into()); + } + + let file_index = ((*file_info).nFileIndexHigh, (*file_info).nFileIndexLow); + for hard_link in &hard_links { + if (hard_link.1).0 == file_index.0 && (hard_link.1).1 == file_index.1 { + std::fs::hard_link(hard_link.0.clone(), local_to_target.as_path()).unwrap(); + found_hard_link = true; + } + } + if (*file_info).nNumberOfLinks > 1u32 && !found_hard_link { + hard_links.push((local_to_target.as_path().to_str().unwrap().to_string(), file_index)); + copy_file(path.as_path(), local_to_target.as_path(), options)?; + } else { + copy_file(path.as_path(), local_to_target.as_path(), options)?; + } + } } } else { - println!("copy"); copy_file(path.as_path(), local_to_target.as_path(), options)?; } } From e33de238ca64a2320b6fd02e23ab377609fc64c4 Mon Sep 17 00:00:00 2001 From: Matt8898 Date: Wed, 23 Aug 2017 16:01:32 +0200 Subject: [PATCH 28/31] cp: add support for --preserve=all. --- src/cp/cp.rs | 193 +++++++++++++++++++++------------------------------ 1 file changed, 79 insertions(+), 114 deletions(-) diff --git a/src/cp/cp.rs b/src/cp/cp.rs index 303b41019..1730ad43b 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -14,7 +14,6 @@ extern crate libc; extern crate clap; extern crate walkdir; extern crate filetime; -use filetime::FileTime; #[cfg(target_os = "linux")] #[macro_use] extern crate ioctl_sys; #[macro_use] extern crate uucore; @@ -49,6 +48,10 @@ use walkdir::WalkDir; #[cfg(target_os = "linux")] use std::os::unix::io::IntoRawFd; use std::fs::File; use std::fs::OpenOptions; +use filetime::FileTime; + +#[cfg(target_os = "linux")] +use libc::{c_int, c_char}; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; @@ -187,7 +190,6 @@ pub enum Attribute { Context, Links, Xattr, - All, } /// Re-usable, extensible copy options @@ -521,7 +523,6 @@ impl FromStr for Attribute { "context" => Attribute::Context, "links" => Attribute::Links, "xattr" => Attribute::Xattr, - "all" => Attribute::All, _ => return Err(Error::InvalidArgument(format!("invalid attribute '{}'", value))) }) } @@ -567,7 +568,18 @@ impl Options { Some(attribute_strs) => { let mut attributes = Vec::new(); for attribute_str in attribute_strs { - attributes.push(Attribute::from_str(attribute_str)?); + if attribute_str == "all" { + #[cfg(unix)] + attributes.push(Attribute::Mode); + attributes.push(Attribute::Ownership); + attributes.push(Attribute::Timestamps); + attributes.push(Attribute::Context); + attributes.push(Attribute::Xattr); + attributes.push(Attribute::Links); + break; + } else { + attributes.push(Attribute::from_str(attribute_str)?); + } } attributes } @@ -577,6 +589,7 @@ impl Options { } else { vec![] }; + let options = Options { attributes_only: matches.is_present(OPT_ATTRIBUTES_ONLY), copy_contents: matches.is_present(OPT_COPY_CONTENTS), @@ -617,7 +630,6 @@ impl Options { } } - impl TargetType { /// Return TargetType required for `target`. /// @@ -665,6 +677,49 @@ fn parse_path_args(path_args: &[String], options: &Options) -> CopyResult<(Vec, source: &std::path::PathBuf, dest: std::path::PathBuf, found_hard_link: &mut bool) -> CopyResult<()> { + if !source.is_dir() { + unsafe { + let src_path = CString::new(source.as_os_str().to_str().unwrap()).unwrap(); + let mut inode: u64 = 0; + let mut nlinks = 0; + #[cfg(unix)] + { + let mut stat = mem::zeroed(); + if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { + return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); + } + inode = stat.st_ino; + nlinks = stat.st_nlink; + } + #[cfg(windows)] + { + let mut stat = mem::uninitialized(); + let handle = CreateFile2(src_path.as_ptr() as *const u16, + winapi::winnt::GENERIC_READ, + winapi::winnt::FILE_SHARE_READ, + 0, + std::ptr::null_mut()); + if GetFileInformationByHandle(handle, stat) != 0 { + return Err(format!("cannot get file information {:?}: {}", source, std::io::Error::last_os_error()).into()); + } + inode = (((*stat).nFileIndexHigh as u64) << 32 | (*stat).nFileIndexLow as u64); + nlinks = (*stat).nNumberOfLinks; + } + + for hard_link in hard_links.iter() { + if hard_link.1 == inode { + std::fs::hard_link(hard_link.0.clone(), dest.clone()).unwrap(); + *found_hard_link = true; + } + } + if !(*found_hard_link) && nlinks > 1 { + hard_links.push((dest.clone().to_str().unwrap().to_string(), inode)); + } + } + } + Ok(()) +} /// Copy all `sources` to `target`. Returns an /// `Err(Error::NotAllFilesCopied)` if at least one non-fatal error was @@ -683,72 +738,21 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<() preserve_hard_links = true; } } - #[cfg(unix)] + let mut hard_links: Vec<(String, u64)> = vec![]; - - #[cfg(windows)] - let mut hard_links: Vec<(String, (u32, u32))> = vec![]; let mut non_fatal_errors = false; let mut seen_sources = HashSet::with_capacity(sources.len()); for source in sources { if seen_sources.contains(source) { show_warning!("source '{}' specified more than once", source.display()); - } else { let mut found_hard_link = false; if preserve_hard_links { - #[cfg(unix)] - unsafe { let dest = construct_dest_path(source, target, &target_type, options)?; - let mut stat = mem::zeroed(); let src_path = CString::new(Path::new(&source.clone()).as_os_str().to_str().unwrap()).unwrap(); - - if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { - return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); - } - - let inode = stat.st_ino; - for hard_link in &hard_links { - if hard_link.1 == inode { - std::fs::hard_link(hard_link.0.clone(), dest.clone()); - found_hard_link = true; - } - } - if stat.st_nlink > 1 && !found_hard_link { - hard_links.push((dest.clone().to_str().unwrap().to_string(), inode)); - } - } - #[cfg(windows)] - { - unsafe { - if !source.is_dir() { - let dest = construct_dest_path(source, target, &target_type, options)?; - let handle = CreateFile2(CString::new(Path::new(&source.clone()).as_os_str().to_str().unwrap()).unwrap().as_ptr() as *const u16, - winapi::winnt::GENERIC_READ, - winapi::winnt::FILE_SHARE_READ, - 0, - std::ptr::null_mut()); - let file_info = std::mem::uninitialized(); - if GetFileInformationByHandle(handle, file_info) != 0 { - return Err(format!("cannot get file information {:?}: {}", source, std::io::Error::last_os_error()).into()); - } - - let file_index = ((*file_info).nFileIndexHigh, (*file_info).nFileIndexLow); - for hard_link in &hard_links { - if (hard_link.1).0 == file_index.0 && (hard_link.1).1 == file_index.1 { - std::fs::hard_link(hard_link.0.clone(), dest.clone()).unwrap(); - found_hard_link = true; - } - } - if (((*file_info).nNumberOfLinks) > 1u32) && !found_hard_link { - println!("{}", (*file_info).nNumberOfLinks); - hard_links.push((dest.clone().to_str().unwrap().to_string(), file_index)); - } - } - } - } - } + preserve_hardlinks(&mut hard_links, source, dest, &mut found_hard_link).unwrap(); + } if !found_hard_link { if let Err(error) = copy_source(source, target, &target_type, options) { show_error!("{}", error); @@ -762,9 +766,9 @@ fn copy(sources: &[Source], target: &Target, options: &Options) -> CopyResult<() } } if non_fatal_errors { - return Err(Error::NotAllFilesCopied) + Err(Error::NotAllFilesCopied) } else { - return Ok(()) + Ok(()) } } @@ -828,7 +832,7 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult } #[cfg(windows)] - let mut hard_links: Vec<(String, (u32, u32))> = vec![]; + let mut hard_links: Vec<(String, u64)> = vec![]; for path in WalkDir::new(root) { let path = or_continue!(or_continue!(path).path().canonicalize()); @@ -843,57 +847,12 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult or_continue!(fs::create_dir_all(local_to_target.clone())); } else if !path.is_dir() { if preserve_hard_links { - #[cfg(unix)] - unsafe { - let mut stat = mem::zeroed(); - let src_path = CString::new(path.as_os_str().to_str().unwrap()).unwrap(); - - if libc::lstat(src_path.as_ptr(), &mut stat) < 0 { - return Err(format!("cannot stat {:?}: {}", src_path, std::io::Error::last_os_error()).into()); - } - - let inode = stat.st_ino; - let mut found_hard_link = false; - for hard_link in &hard_links { - if hard_link.1 == inode { - std::fs::hard_link(hard_link.0.clone(), local_to_target.as_path()); - found_hard_link = true; - } - } - if stat.st_nlink > 1 && !found_hard_link { - hard_links.push((local_to_target.as_path().to_str().unwrap().to_string(), inode)); + let mut found_hard_link = false; + let source = path.to_path_buf(); + let dest = local_to_target.as_path().to_path_buf(); + preserve_hardlinks(&mut hard_links, &source, dest, &mut found_hard_link).unwrap(); + if !found_hard_link { copy_file(path.as_path(), local_to_target.as_path(), options)?; - } else { - copy_file(path.as_path(), local_to_target.as_path(), options)?; - } - } - #[cfg(windows)] - { - unsafe { - let mut found_hard_link = false; - let src_path = CString::new(path.as_os_str().to_str().unwrap()).unwrap(); - let handle = File::open(path.clone()).unwrap().as_raw_handle(); - let file_info = std::mem::uninitialized(); - - let handle = File::open(path.clone()).unwrap().as_raw_handle(); - if GetFileInformationByHandle(handle, file_info) != 0 { - return Err(format!("cannot get file information {:?}: {}", src_path, std::io::Error::last_os_error()).into()); - } - - let file_index = ((*file_info).nFileIndexHigh, (*file_info).nFileIndexLow); - for hard_link in &hard_links { - if (hard_link.1).0 == file_index.0 && (hard_link.1).1 == file_index.1 { - std::fs::hard_link(hard_link.0.clone(), local_to_target.as_path()).unwrap(); - found_hard_link = true; - } - } - if (*file_info).nNumberOfLinks > 1u32 && !found_hard_link { - hard_links.push((local_to_target.as_path().to_str().unwrap().to_string(), file_index)); - copy_file(path.as_path(), local_to_target.as_path(), options)?; - } else { - copy_file(path.as_path(), local_to_target.as_path(), options)?; - } - } } } else { copy_file(path.as_path(), local_to_target.as_path(), options)?; @@ -940,7 +899,7 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu let metadata = fs::metadata(source)?; filetime::set_file_times(Path::new(dest), FileTime::from_last_access_time(&metadata), FileTime::from_last_modification_time(&metadata))?; }, - Attribute::Context => return Err(Error::NotImplemented("preserving context not implemented".to_string())), + Attribute::Context => {}, Attribute::Links => {}, Attribute::Xattr => { #[cfg(unix)] @@ -957,7 +916,6 @@ fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResu return Err(format!("XAttrs are only supported on unix.").into()); } }, - Attribute::All => return Err(Error::NotImplemented("preserving a not implemented".to_string())), }) } @@ -1030,6 +988,13 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { println!("{}", context_for(source, dest)); } + let mut preserve_context = false; + for attribute in &options.preserve_attributes { + if *attribute == Attribute::Context { + preserve_context = true; + } + } + match options.copy_mode { CopyMode::Link => { fs::hard_link(source, dest).context(&*context_for(source, dest))?; From c59e375c7a4dc5f926a7fb4194af69af48792521 Mon Sep 17 00:00:00 2001 From: Yury Krivopalov Date: Wed, 23 Aug 2017 22:12:03 +0300 Subject: [PATCH 29/31] tr: use as_bytes on whole output string On my environment on 1MiB file with this fix tr takes 30ms, instead of 44ms without fix. --- src/tr/tr.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tr/tr.rs b/src/tr/tr.rs index bc904ea57..bd1b35d31 100644 --- a/src/tr/tr.rs +++ b/src/tr/tr.rs @@ -71,7 +71,7 @@ fn tr<'a>(set1: ExpandSet<'a>, mut set2: ExpandSet<'a>) { let mut locked_stdin = stdin.lock(); let mut buffered_stdout = BufWriter::new(stdout()); let mut buf = String::with_capacity(BUFFER_LEN + 4); - let mut char_output_buffer: [u8; 4] = [0;4]; + let mut output_buf = String::with_capacity(BUFFER_LEN + 4); let mut s2_prev = '_'; for i in set1 { @@ -85,13 +85,12 @@ fn tr<'a>(set1: ExpandSet<'a>, mut set2: ExpandSet<'a>) { { // isolation to make borrow checker happy let output_stream = buf.chars().map(|c| *map.get(&(c as usize)).unwrap_or(&c)); - for c in output_stream { - let char_as_bytes = c.encode_utf8(&mut char_output_buffer); - buffered_stdout.write_all(char_as_bytes.as_bytes()).unwrap(); - } + output_buf.extend(output_stream); + buffered_stdout.write_all(output_buf.as_bytes()).unwrap(); } buf.clear(); + output_buf.clear(); } } From b4d8265a0767f6a971c6b24abd1741de8c2a9ad5 Mon Sep 17 00:00:00 2001 From: Yury Krivopalov Date: Thu, 24 Aug 2017 14:26:08 +0300 Subject: [PATCH 30/31] tr: generaze translation --- src/tr/tr.rs | 122 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 48 deletions(-) diff --git a/src/tr/tr.rs b/src/tr/tr.rs index bd1b35d31..308f36079 100644 --- a/src/tr/tr.rs +++ b/src/tr/tr.rs @@ -32,63 +32,80 @@ static NAME: &'static str = "tr"; static VERSION: &'static str = env!("CARGO_PKG_VERSION"); const BUFFER_LEN: usize = 1024; -fn delete(set: ExpandSet, complement: bool) { - let mut bset = BitSet::new(); - let stdin = stdin(); - let mut locked_stdin = stdin.lock(); - let mut buffered_stdout = BufWriter::new(stdout()); - let mut buf = String::with_capacity(BUFFER_LEN + 4); - let mut char_output_buffer: [u8; 4] = [0;4]; +trait SymbolTranslator { + fn translate(&self, c: &char, prev_c: &char) -> Option; +} - for c in set { - bset.insert(c as usize); - } +struct DeleteOperation { + bset: BitSet, + complement: bool, +} - let is_allowed = |c : char| { - if complement { - bset.contains(c as usize) - } else { - !bset.contains(c as usize) +impl DeleteOperation { + fn new(set: ExpandSet, complement: bool) -> DeleteOperation { + DeleteOperation { + bset: set.map(|c| c as usize).collect(), + complement: complement } - }; - - while let Ok(length) = locked_stdin.read_line(&mut buf) { - if length == 0 { break } - { // isolation to make borrow checker happy - let filtered = buf.chars().filter(|c| is_allowed(*c)); - for c in filtered { - let char_as_bytes = c.encode_utf8(&mut char_output_buffer); - buffered_stdout.write_all(char_as_bytes.as_bytes()).unwrap(); - } - } - buf.clear(); } } -fn tr<'a>(set1: ExpandSet<'a>, mut set2: ExpandSet<'a>) { - let mut map = FnvHashMap::default(); - let stdin = stdin(); - let mut locked_stdin = stdin.lock(); - let mut buffered_stdout = BufWriter::new(stdout()); +impl SymbolTranslator for DeleteOperation { + fn translate(&self, c: &char, _prev_c: &char) -> Option { + let uc = *c as usize; + if self.complement == self.bset.contains(uc) { + Some(*c) + } else { + None + } + } +} + +struct TranslateOperation { + translate_map: FnvHashMap, +} + +impl TranslateOperation { + fn new(set1: ExpandSet, set2: &mut ExpandSet) -> TranslateOperation { + let mut map = FnvHashMap::default(); + let mut s2_prev = '_'; + for i in set1 { + s2_prev = set2.next().unwrap_or(s2_prev); + + map.insert(i as usize, s2_prev); + } + TranslateOperation { + translate_map: map, + } + } +} + +impl SymbolTranslator for TranslateOperation { + fn translate(&self, c: &char, _prev_c: &char) -> Option { + Some(*self.translate_map.get(&(*c as usize)).unwrap_or(c)) + } +} + +fn translate_input(input: &mut BufRead, output: &mut Write, translator: T) { let mut buf = String::with_capacity(BUFFER_LEN + 4); let mut output_buf = String::with_capacity(BUFFER_LEN + 4); + // let mut char_output_buffer: [u8; 4] = [0;4]; - let mut s2_prev = '_'; - for i in set1 { - s2_prev = set2.next().unwrap_or(s2_prev); - - map.insert(i as usize, s2_prev); - } - - while let Ok(length) = locked_stdin.read_line(&mut buf) { + while let Ok(length) = input.read_line(&mut buf) { + let mut prev_c = 0 as char; if length == 0 { break } - { // isolation to make borrow checker happy - let output_stream = buf.chars().map(|c| *map.get(&(c as usize)).unwrap_or(&c)); - output_buf.extend(output_stream); - buffered_stdout.write_all(output_buf.as_bytes()).unwrap(); - } + let filtered = buf.chars().filter_map(|c| { + let res = translator.translate(&c, &prev_c); + if res.is_some() { + prev_c = c; + } + res + }); + output_buf.extend(filtered); + output.write_all(output_buf.as_bytes()).unwrap(); + } buf.clear(); output_buf.clear(); } @@ -110,6 +127,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("C", "", "same as -c"); opts.optflag("d", "delete", "delete characters in SET1"); opts.optflag("h", "help", "display this help and exit"); + opts.optflag("s", "squeeze", ""); opts.optflag("V", "version", "output version information and exit"); let matches = match opts.parse(&args[1..]) { @@ -144,13 +162,21 @@ pub fn uumain(args: Vec) -> i32 { return 1; } + let stdin = stdin(); + let mut locked_stdin = stdin.lock(); + let stdout = stdout(); + let locked_stdout = stdout.lock(); + let mut buffered_stdout = BufWriter::new(locked_stdout); + if dflag { let set1 = ExpandSet::new(sets[0].as_ref()); - delete(set1, cflag); + let delete_op = DeleteOperation::new(set1, cflag); + translate_input(&mut locked_stdin, &mut buffered_stdout, delete_op); } else { let set1 = ExpandSet::new(sets[0].as_ref()); - let set2 = ExpandSet::new(sets[1].as_ref()); - tr(set1, set2); + let mut set2 = ExpandSet::new(sets[1].as_ref()); + let op = TranslateOperation::new(set1, &mut set2); + translate_input(&mut locked_stdin, &mut buffered_stdout, op) } 0 From ac375d8b7d7655a8a2f1278ad8f971ad256482bf Mon Sep 17 00:00:00 2001 From: Yury Krivopalov Date: Sat, 26 Aug 2017 15:26:24 +0300 Subject: [PATCH 31/31] tr: add squeeze option --- src/tr/tr.rs | 74 ++++++++++++++++++++++++++++++++++++++++++------ tests/test_tr.rs | 25 ++++++++++++++++ 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/tr/tr.rs b/src/tr/tr.rs index 308f36079..4167234a5 100644 --- a/src/tr/tr.rs +++ b/src/tr/tr.rs @@ -61,6 +61,56 @@ impl SymbolTranslator for DeleteOperation { } } +struct SqueezeOperation { + squeeze_set: BitSet, + complement: bool, +} + +impl SqueezeOperation { + fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation { + SqueezeOperation { + squeeze_set: squeeze_set.map(|c| c as usize).collect(), + complement: complement + } + } +} + +impl SymbolTranslator for SqueezeOperation { + fn translate(&self, c: &char, prev_c: &char) -> Option { + if *prev_c == *c && self.complement != self.squeeze_set.contains(*c as usize) { + None + } else { + Some(*c) + } + } +} + +struct DeleteAndSqueezeOperation { + delete_set: BitSet, + squeeze_set: BitSet, + complement: bool, +} + +impl DeleteAndSqueezeOperation { + fn new(delete_set: ExpandSet, squeeze_set: ExpandSet, complement: bool) -> DeleteAndSqueezeOperation { + DeleteAndSqueezeOperation { + delete_set: delete_set.map(|c| c as usize).collect(), + squeeze_set: squeeze_set.map(|c| c as usize).collect(), + complement: complement + } + } +} + +impl SymbolTranslator for DeleteAndSqueezeOperation { + fn translate(&self, c: &char, prev_c: &char) -> Option { + if self.complement != self.delete_set.contains(*c as usize) || *prev_c == *c && self.squeeze_set.contains(*c as usize) { + None + } else { + Some(*c) + } + } +} + struct TranslateOperation { translate_map: FnvHashMap, } @@ -89,7 +139,6 @@ impl SymbolTranslator for TranslateOperation { fn translate_input(input: &mut BufRead, output: &mut Write, translator: T) { let mut buf = String::with_capacity(BUFFER_LEN + 4); let mut output_buf = String::with_capacity(BUFFER_LEN + 4); - // let mut char_output_buffer: [u8; 4] = [0;4]; while let Ok(length) = input.read_line(&mut buf) { let mut prev_c = 0 as char; @@ -127,7 +176,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("C", "", "same as -c"); opts.optflag("d", "delete", "delete characters in SET1"); opts.optflag("h", "help", "display this help and exit"); - opts.optflag("s", "squeeze", ""); + opts.optflag("s", "squeeze", "replace each sequence of a repeated character that is listed in the last specified SET, with a single occurrence of that character"); opts.optflag("V", "version", "output version information and exit"); let matches = match opts.parse(&args[1..]) { @@ -155,10 +204,11 @@ pub fn uumain(args: Vec) -> i32 { let dflag = matches.opt_present("d"); let cflag = matches.opts_present(&["c".to_owned(), "C".to_owned()]); + let sflag = matches.opt_present("s"); let sets = matches.free; - if cflag && !dflag { - show_error!("-c is only supported with -d"); + if cflag && !dflag && !sflag { + show_error!("-c is only supported with -d or -s"); return 1; } @@ -168,12 +218,20 @@ pub fn uumain(args: Vec) -> i32 { let locked_stdout = stdout.lock(); let mut buffered_stdout = BufWriter::new(locked_stdout); + let set1 = ExpandSet::new(sets[0].as_ref()); if dflag { - let set1 = ExpandSet::new(sets[0].as_ref()); - let delete_op = DeleteOperation::new(set1, cflag); - translate_input(&mut locked_stdin, &mut buffered_stdout, delete_op); + if sflag { + let set2 = ExpandSet::new(sets[1].as_ref()); + let op = DeleteAndSqueezeOperation::new(set1, set2, cflag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } else { + let op = DeleteOperation::new(set1, cflag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); + } + } else if sflag { + let op = SqueezeOperation::new(set1, cflag); + translate_input(&mut locked_stdin, &mut buffered_stdout, op); } else { - let set1 = ExpandSet::new(sets[0].as_ref()); let mut set2 = ExpandSet::new(sets[1].as_ref()); let op = TranslateOperation::new(set1, &mut set2); translate_input(&mut locked_stdin, &mut buffered_stdout, op) diff --git a/tests/test_tr.rs b/tests/test_tr.rs index 48a38d7a0..4e4c99bb1 100644 --- a/tests/test_tr.rs +++ b/tests/test_tr.rs @@ -32,3 +32,28 @@ fn test_delete_complement() { new_ucmd!() .args(&["-d", "-c", "a-z"]).pipe_in("aBcD").run().stdout_is("ac"); } + +#[test] +fn test_squeeze() { + new_ucmd!() + .args(&["-s", "a-z"]).pipe_in("aaBBcDcc").run().stdout_is("aBBcDc"); +} + + +#[test] +fn test_squeeze_complement() { + new_ucmd!() + .args(&["-sc", "a-z"]).pipe_in("aaBBcDcc").run().stdout_is("aaBcDcc"); +} + +#[test] +fn test_delete_and_squeeze() { + new_ucmd!() + .args(&["-ds", "a-z", "A-Z"]).pipe_in("abBcB").run().stdout_is("B"); +} + +#[test] +fn test_delete_and_squeeze_complement() { + new_ucmd!() + .args(&["-dsc", "a-z", "A-Z"]).pipe_in("abBcB").run().stdout_is("abc"); +}