diff --git a/Cargo.lock b/Cargo.lock index fa335d43f..cf8bd4348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ dependencies = [ "install 0.0.1", "kill 0.0.1", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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.37 (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", @@ -140,7 +140,7 @@ 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.24 (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)", ] @@ -150,7 +150,7 @@ 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.24 (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)", ] @@ -200,7 +200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -212,9 +212,14 @@ dependencies = [ "uucore 0.0.1", ] +[[package]] +name = "cc" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cfg-if" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -229,7 +234,7 @@ dependencies = [ name = "chmod" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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)", ] @@ -248,8 +253,8 @@ name = "chrono" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.37 (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]] @@ -264,7 +269,7 @@ dependencies = [ name = "cksum" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -286,10 +291,10 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -297,7 +302,7 @@ name = "comm" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -305,9 +310,17 @@ dependencies = [ name = "cp" version = "0.0.1" dependencies = [ + "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)", + "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.24 (registry+https://github.com/rust-lang/crates.io-index)", + "ioctl-sys 0.5.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.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)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "xattr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -343,7 +356,7 @@ dependencies = [ name = "dirname" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -351,7 +364,7 @@ dependencies = [ name = "du" version = "0.0.1" dependencies = [ - "time 0.1.37 (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", ] @@ -371,7 +384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "env" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -388,8 +401,8 @@ dependencies = [ name = "expr" version = "0.0.1" dependencies = [ - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", - "onig 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "onig 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.1", ] @@ -410,14 +423,14 @@ name = "filetime" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.24 (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.24 (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", ] @@ -466,7 +479,7 @@ name = "hashsum" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (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)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -478,7 +491,7 @@ dependencies = [ name = "head" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -486,7 +499,7 @@ dependencies = [ name = "hostid" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -494,7 +507,7 @@ dependencies = [ name = "hostname" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -510,10 +523,15 @@ name = "install" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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 = "ioctl-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "itertools" version = "0.6.0" @@ -535,7 +553,7 @@ dependencies = [ name = "kill" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -546,19 +564,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.24" -source = "git+https://github.com/rust-lang/libc.git#83f5543e761bad58c1f061447c28df4a0201c761" +version = "0.2.26" +source = "git+https://github.com/rust-lang/libc.git#c43c0770db6b85ade0b70009464603ff9be4b37d" [[package]] name = "libc" -version = "0.2.24" +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.24 (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", ] @@ -566,7 +584,7 @@ dependencies = [ name = "ln" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -574,7 +592,7 @@ dependencies = [ name = "logname" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -587,7 +605,7 @@ dependencies = [ "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.37 (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", ] @@ -597,7 +615,7 @@ name = "memchr" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -605,7 +623,7 @@ name = "mkdir" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -614,7 +632,7 @@ name = "mkfifo" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -623,7 +641,7 @@ name = "mknod" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -659,7 +677,7 @@ name = "nice" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -669,8 +687,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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)", ] @@ -680,7 +698,7 @@ version = "0.0.1" dependencies = [ "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.24 (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)", @@ -692,7 +710,7 @@ name = "nohup" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -701,41 +719,41 @@ name = "nproc" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.34 (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.39 (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.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.39 (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.34 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.39 (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.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -743,38 +761,38 @@ name = "num_cpus" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.24 (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 1.0.0 (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 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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 = "onig" -version = "1.3.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (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.24 (registry+https://github.com/rust-lang/crates.io-index)", - "onig_sys 63.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "onig_sys 65.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "onig_sys" -version = "63.0.2" +version = "65.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -791,7 +809,7 @@ name = "pathchk" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -813,7 +831,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.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -838,7 +856,7 @@ version = "0.0.1" dependencies = [ "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.24 (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)", @@ -863,7 +881,7 @@ name = "rand" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -871,7 +889,7 @@ name = "readlink" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -885,7 +903,7 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.19" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -913,12 +931,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]] @@ -935,10 +964,10 @@ version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -995,9 +1024,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.24 (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.37 (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", ] @@ -1041,7 +1070,7 @@ name = "stat" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.37 (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", ] @@ -1072,7 +1101,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.24 (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)", ] @@ -1091,7 +1120,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.24 (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)", ] @@ -1101,7 +1130,7 @@ name = "tee" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -1119,7 +1148,7 @@ 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.24 (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)", @@ -1139,7 +1168,7 @@ 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.24 (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)", ] @@ -1150,7 +1179,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.24 (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)", ] @@ -1158,7 +1187,7 @@ dependencies = [ name = "test" version = "0.0.1" dependencies = [ - "libc 0.2.24 (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", ] @@ -1182,12 +1211,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.37" +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.24 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.19 (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)", ] @@ -1196,8 +1225,8 @@ name = "timeout" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.37 (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", ] @@ -1207,7 +1236,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.37 (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", ] @@ -1246,7 +1275,7 @@ name = "tty" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -1294,8 +1323,8 @@ name = "unix_socket" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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]] @@ -1303,7 +1332,7 @@ name = "unlink" version = "0.0.1" dependencies = [ "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.24 (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", ] @@ -1342,8 +1371,8 @@ version = "0.0.1" dependencies = [ "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.24 (git+https://github.com/rust-lang/libc.git)", - "time 0.1.37 (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]] @@ -1415,6 +1444,14 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "xattr" +version = "0.2.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]] name = "yes" version = "0.0.1" @@ -1433,11 +1470,12 @@ dependencies = [ "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 bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" -"checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29" +"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum cc 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771" -"checksum cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ebbb35d3dc9cd09497168f33de1acb79b265d350ab0ac34133b98f8509af1f" +"checksum cmake 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "357c07e7a1fc95732793c1edb5901e1a1f305cfcf63a90eb12dbd22bdb6b789d" "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" @@ -1446,27 +1484,29 @@ dependencies = [ "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 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f" +"checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" "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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" -"checksum libc 0.2.24 (git+https://github.com/rust-lang/libc.git)" = "" -"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc" +"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 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.39 (registry+https://github.com/rust-lang/crates.io-index)" = "2c3a3dc9f30bf824141521b30c908a859ab190b76e20435fcd89f35eb6583887" -"checksum num-integer 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ef1a4bf6f9174aa5783a9b4cc892cacd11aebad6c69ad027a0b65c6ca5f8aa37" -"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e" -"checksum num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "1708c0628602a98b52fad936cf3edb9a107af06e52e49fdf0707e884456a6af6" +"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 onig 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee619da9cf707b167098e84fb00f10db61d5a662d1d29b59822bcac3a81553dd" -"checksum onig_sys 63.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "531682ab45a2cd40eff91f29340dae975f25336d2b61e624adabed39e61d7fb3" +"checksum onig 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1271a3f93197303deda8aedcd96ed2dbb908f61c0954ae70bf7a42f536dc35d7" +"checksum onig_sys 65.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fbdee58fb75f5b7749ebc8f601f570961eed595dfe4c2bb9a542e2f7ae20b946" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum pretty-bytes 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3095b93999fae14b4e0bb661c53875a441d9058b7b1a7ba2dfebc104d3776349" "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.19 (registry+https://github.com/rust-lang/crates.io-index)" = "e4a357d14a12e90a37d658725df0e6468504750b5948b9710f83f94a0c5818e8" +"checksum redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "9df6a71a1e67be2104410736b2389fb8e383c1d7e9e792d629ff13c02867147a" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "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" @@ -1482,7 +1522,7 @@ dependencies = [ "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_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" -"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3" +"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" @@ -1496,3 +1536,4 @@ dependencies = [ "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" +"checksum xattr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb373b92de38a4301d66bec009929b4fb83120ea1c4a401be89dbe0b9777443" diff --git a/Cargo.toml b/Cargo.toml index 73849ec11..e81ad29d8 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.2.2" +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/README.md b/README.md index f6ed97ee0..59c72ff24 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 @@ -144,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 -* [x] expr -* [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 | csplit | +| 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 | date | | +| 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 ------- 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/chmod/chmod.rs b/src/chmod/chmod.rs index cfb148bf6..7c3a5a352 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 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 { @@ -242,7 +243,7 @@ fn parse_symbolic(mut fperm: libc::mode_t, mut mode: &str, file: &Path) -> Resul mode = &mode[pos..]; let (mut srwx, pos) = parse_change(mode, fperm, file); if respect_umask { - srwx &= !last_umask; + srwx &= !(last_umask as u32); } mode = &mode[pos..]; match op { @@ -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(()) } } 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 2e7758e49..0057d9330 100644 --- a/src/cp/Cargo.toml +++ b/src/cp/Cargo.toml @@ -1,16 +1,33 @@ [package] name = "cp" version = "0.0.1" -authors = [] +authors = [ + "Jordy Dickinson ", + "Joshua S. Miller ", +] [lib] name = "uu_cp" path = "cp.rs" [dependencies] -getopts = "*" -libc = "*" +getopts = "0.2.14" +libc = "0.2.26" +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" + +[target.'cfg(target_os = "windows")'.dependencies] +kernel32-sys = "*" +winapi = "*" + +[target.'cfg(unix)'.dependencies] +xattr="0.2.1" [[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 102f9eda3..1730ad43b 100644 --- a/src/cp/cp.rs +++ b/src/cp/cp.rs @@ -4,180 +4,1127 @@ * 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; +extern crate libc; +extern crate clap; +extern crate walkdir; +extern crate filetime; +#[cfg(target_os = "linux")] +#[macro_use] extern crate ioctl_sys; +#[macro_use] extern crate uucore; +#[macro_use] extern crate quick_error; +#[cfg(unix)] +extern crate xattr; -#[macro_use] -extern crate uucore; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; -use getopts::Options; +#[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}; +use quick_error::ResultExt; +use std::collections::HashSet; use std::fs; -use std::io::{ErrorKind, Result, Write}; -use std::path::Path; +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; +#[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; + +#[cfg(target_os = "linux")] ioctl!(write ficlone with 0x94, 9; std::os::raw::c_int); + +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, +} + +#[derive (Clone, Eq, PartialEq)] +pub enum ReflinkMode { + Always, Auto, Never +} + +/// 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, + Update, + AttrOnly +} + +#[derive(Clone, Eq, PartialEq)] +pub enum Attribute { + #[cfg(unix)] Mode, + Ownership, + Timestamps, + Context, + Links, + Xattr, +} + +/// 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, + reflink: bool, + reflink_mode: ReflinkMode, + 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_DEFAULT_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) + .overrides_with(OPT_REFLINK) + .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) + .overrides_with(OPT_REFLINK) + .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")) + .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")) + .arg(Arg::with_name(OPT_REFLINK) + .long(OPT_REFLINK) + .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")) + .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")) - opts.optflag("h", "help", "display this help and exit"); - 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_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) + .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_PARENTS) + .long(OPT_PARENTS) + .help("NotImplemented: use full source file name under DIRECTORY")) + .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_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 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 if matches.is_present(OPT_UPDATE) { + CopyMode::Update + } else if matches.is_present(OPT_ATTRIBUTES_ONLY) { + CopyMode::AttrOnly + } 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, + _ => return Err(Error::InvalidArgument(format!("invalid attribute '{}'", value))) + }) + } +} + +impl Options { + fn from_matches(matches: &ArgMatches) -> CopyResult { + let not_implemented_opts = vec![ + OPT_ARCHIVE, + OPT_COPY_CONTENTS, + OPT_NO_DEREFERENCE_PRESERVE_LINKS, + OPT_DEREFERENCE, + OPT_NO_DEREFERENCE, + OPT_PARENTS, + OPT_SPARSE, + OPT_STRIP_TRAILING_SLASHES, + 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); - 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!() + // 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 { + 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 } } - }); + } else if matches.is_present(OPT_PRESERVE_DEFAULT_ATTRIBUTES) { + DEFAULT_ATTRIBUTES.to_vec() + } else { + vec![] + }; - if same_file { - show_error!("\"{}\" and \"{}\" are the same file", - 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!(); - } - } else { - if !dest.is_dir() { - show_error!("TARGET must be a directory"); - panic!(); - } - for src in &sources { - let source = Path::new(&src); + 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), + 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, + recursive, + target_dir, + }; - if !source.is_file() { - show_error!("\"{}\" is not a file", source.display()); - continue; - } + Ok(options) + } +} - 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!() - } +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 } } } -pub fn paths_refer_to_same_file(p1: &Path, p2: &Path) -> Result { + +/// 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()); + } + + // 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()); + } + + 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)) + } + None => { + // If there was no explicit target-dir, then use the last + // path_arg + let target = paths.pop().unwrap(); + (paths, target) + } + }; + + Ok((sources, target)) +} + +fn preserve_hardlinks(hard_links: &mut Vec<(String, u64)>, 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 +/// 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 preserve_hard_links = false; + for attribute in &options.preserve_attributes { + if *attribute == Attribute::Links { + preserve_hard_links = true; + } + } + + 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 { + let mut found_hard_link = false; + if preserve_hard_links { + let dest = construct_dest_path(source, target, &target_type, options)?; + let src_path = CString::new(Path::new(&source.clone()).as_os_str().to_str().unwrap()).unwrap(); + 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); + 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 = if target.exists() { + root_path.parent() + } else { + 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; + } + } + + #[cfg(windows)] + let mut hard_links: Vec<(String, u64)> = vec![]; + + 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() { + if preserve_hard_links { + 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)?; + } + } + } + + 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 { + Err(Error::Skipped(format!("Not overwriting {} at user request", path.display()))) + } + }, + OverwriteMode::Clobber(_) => Ok(()), + } + } +} + + +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 => { + 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 => {}, + Attribute::Links => {}, + 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()); + } + }, + }) +} + +#[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)); + } + + 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))?; + } + 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())?; + 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 { + copy_helper(source, dest, options)?; + } + } else { + copy_helper(source, dest, options)?; + } + }, + CopyMode::AttrOnly => { + OpenOptions::new() + .write(true) + .truncate(false) + .create(true) + .open(dest) + .unwrap(); + } + }; + + for attribute in &options.preserve_attributes { + copy_attribute(source, dest, attribute)?; + } + + 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<()> { + 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/src/date/Cargo.toml b/src/date/Cargo.toml index 3f1afad7d..613d82895 100644 --- a/src/date/Cargo.toml +++ b/src/date/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_date" path = "date.rs" [dependencies] -chrono = "0.4" -clap = "2.25" +chrono = "0.4.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 6e8176b3e..fea7f8ea4 100644 --- a/src/expr/Cargo.toml +++ b/src/expr/Cargo.toml @@ -8,8 +8,8 @@ name = "uu_expr" path = "expr.rs" [dependencies] -libc = "*" -onig = "1.3" +libc = "0.2.26" +onig = "1.3.0" 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..f4728dd84 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.2.2" +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/install/install.rs b/src/install/install.rs index 53fe7b23a..2aacda9bb 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 @@ -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") { @@ -209,7 +206,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) { @@ -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(()) 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() { 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/ls/ls.rs b/src/ls/ls.rs index d22b524ad..9ecdf0597 100644 --- a/src/ls/ls.rs +++ b/src/ls/ls.rs @@ -31,10 +31,11 @@ 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; -#[cfg(unix)] +#[cfg(any(unix, target_os = "redox"))] use std::os::unix::fs::MetadataExt; #[cfg(unix)] use std::os::unix::fs::FileTypeExt; @@ -44,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:"; @@ -183,17 +184,22 @@ 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") { 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| { - 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 +219,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)); 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/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; 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 7dac68a5e..29f7c32a4 100644 --- a/src/ptx/Cargo.toml +++ b/src/ptx/Cargo.toml @@ -8,12 +8,12 @@ name = "uu_ptx" path = "ptx.rs" [dependencies] -getopts = "0.2" -libc = "0.2" -aho-corasick = "0.6" -memchr = "1.0" -regex-syntax = "0.4" -regex = "0.2" +getopts = "0.2.14" +libc = "0.2.26" +aho-corasick = "0.6.3" +memchr = "1.0.1" +regex-syntax = "0.4.1" +regex = "0.2.2" 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..51b305d16 100644 --- a/src/rm/Cargo.toml +++ b/src/rm/Cargo.toml @@ -8,7 +8,9 @@ name = "uu_rm" path = "rm.rs" [dependencies] -getopts = "*" +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, } } 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/tr/tr.rs b/src/tr/tr.rs index bc904ea57..4167234a5 100644 --- a/src/tr/tr.rs +++ b/src/tr/tr.rs @@ -32,66 +32,131 @@ 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()); - let mut 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) { - 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)); - 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(); - } +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 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, +} + +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); + + 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 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(); } } @@ -111,6 +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", "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..]) { @@ -138,20 +204,37 @@ 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; } + 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); + + let set1 = ExpandSet::new(sets[0].as_ref()); if dflag { - let set1 = ExpandSet::new(sets[0].as_ref()); - delete(set1, cflag); + 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 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 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]] 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 4429686f6..82651be57 100644 --- a/tests/test_cp.rs +++ b/tests/test_cp.rs @@ -1,10 +1,17 @@ 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_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"; +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() { @@ -22,6 +29,73 @@ 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!(); + + let result = ucmd + .arg("-r") + .arg(TEST_COPY_FROM_FOLDER) + .arg(TEST_COPY_TO_FOLDER_NEW) + .run(); + + assert!(result.success); + // Check the content of the destination file that was copied. + assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n"); +} + #[test] fn test_cp_with_dirs_t() { let (at, mut ucmd) = at_and_ucmd!(); @@ -56,3 +130,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"); +} 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"); +}