install: allow to install a file to a file

This commit is contained in:
Sunrin SHIMURA (keen) 2017-12-27 17:31:19 +09:00
parent c386d5bfaf
commit ee34206520
3 changed files with 203 additions and 113 deletions

227
Cargo.lock generated
View file

@ -1,112 +1,3 @@
[root]
name = "uutils"
version = "0.0.1"
dependencies = [
"arch 0.0.1",
"base32 0.0.1",
"base64 0.0.1",
"basename 0.0.1",
"cat 0.0.1",
"chgrp 0.0.1",
"chmod 0.0.1",
"chown 0.0.1",
"chroot 0.0.1",
"cksum 0.0.1",
"comm 0.0.1",
"cp 0.0.1",
"cut 0.0.1",
"date 0.0.1",
"dircolors 0.0.1",
"dirname 0.0.1",
"du 0.0.1",
"echo 0.0.1",
"env 0.0.1",
"expand 0.0.1",
"expr 0.0.1",
"factor 0.0.1",
"false 0.0.1",
"filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"fmt 0.0.1",
"fold 0.0.1",
"groups 0.0.1",
"hashsum 0.0.1",
"head 0.0.1",
"hostid 0.0.1",
"hostname 0.0.1",
"id 0.0.1",
"install 0.0.1",
"join 0.0.1",
"kill 0.0.1",
"lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"link 0.0.1",
"ln 0.0.1",
"logname 0.0.1",
"ls 0.0.1",
"mkdir 0.0.1",
"mkfifo 0.0.1",
"mknod 0.0.1",
"mktemp 0.0.1",
"more 0.0.1",
"mv 0.0.1",
"nice 0.0.1",
"nl 0.0.1",
"nohup 0.0.1",
"nproc 0.0.1",
"numfmt 0.0.1",
"od 0.0.1",
"paste 0.0.1",
"pathchk 0.0.1",
"pinky 0.0.1",
"printenv 0.0.1",
"printf 0.0.1",
"ptx 0.0.1",
"pwd 0.0.1",
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"readlink 0.0.1",
"realpath 0.0.1",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"relpath 0.0.1",
"rm 0.0.1",
"rmdir 0.0.1",
"seq 0.0.1",
"shred 0.0.1",
"shuf 0.0.1",
"sleep 0.0.1",
"sort 0.0.1",
"split 0.0.1",
"stat 0.0.1",
"stdbuf 0.0.1",
"sum 0.0.1",
"sync 0.0.1",
"tac 0.0.1",
"tail 0.0.1",
"tee 0.0.1",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"test 0.0.1",
"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",
"true 0.0.1",
"truncate 0.0.1",
"tsort 0.0.1",
"tty 0.0.1",
"uname 0.0.1",
"unexpand 0.0.1",
"unindent 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uniq 0.0.1",
"unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unlink 0.0.1",
"uptime 0.0.1",
"users 0.0.1",
"uucore 0.0.1",
"wc 0.0.1",
"who 0.0.1",
"whoami 0.0.1",
"yes 0.0.1",
]
[[package]]
name = "advapi32-sys"
version = "0.2.0"
@ -435,6 +326,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "false"
version = "0.0.1"
dependencies = [
"uucore 0.0.1",
]
[[package]]
name = "filetime"
@ -593,7 +487,7 @@ dependencies = [
name = "join"
version = "0.0.1"
dependencies = [
"getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.1",
]
@ -1170,6 +1064,7 @@ version = "0.0.1"
dependencies = [
"cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"libstdbuf 0.0.1",
"uucore 0.0.1",
]
@ -1345,6 +1240,9 @@ dependencies = [
[[package]]
name = "true"
version = "0.0.1"
dependencies = [
"uucore 0.0.1",
]
[[package]]
name = "truncate"
@ -1467,6 +1365,115 @@ dependencies = [
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "uutils"
version = "0.0.1"
dependencies = [
"arch 0.0.1",
"base32 0.0.1",
"base64 0.0.1",
"basename 0.0.1",
"cat 0.0.1",
"chgrp 0.0.1",
"chmod 0.0.1",
"chown 0.0.1",
"chroot 0.0.1",
"cksum 0.0.1",
"comm 0.0.1",
"cp 0.0.1",
"cut 0.0.1",
"date 0.0.1",
"dircolors 0.0.1",
"dirname 0.0.1",
"du 0.0.1",
"echo 0.0.1",
"env 0.0.1",
"expand 0.0.1",
"expr 0.0.1",
"factor 0.0.1",
"false 0.0.1",
"filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"fmt 0.0.1",
"fold 0.0.1",
"groups 0.0.1",
"hashsum 0.0.1",
"head 0.0.1",
"hostid 0.0.1",
"hostname 0.0.1",
"id 0.0.1",
"install 0.0.1",
"join 0.0.1",
"kill 0.0.1",
"lazy_static 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"link 0.0.1",
"ln 0.0.1",
"logname 0.0.1",
"ls 0.0.1",
"mkdir 0.0.1",
"mkfifo 0.0.1",
"mknod 0.0.1",
"mktemp 0.0.1",
"more 0.0.1",
"mv 0.0.1",
"nice 0.0.1",
"nl 0.0.1",
"nohup 0.0.1",
"nproc 0.0.1",
"numfmt 0.0.1",
"od 0.0.1",
"paste 0.0.1",
"pathchk 0.0.1",
"pinky 0.0.1",
"printenv 0.0.1",
"printf 0.0.1",
"ptx 0.0.1",
"pwd 0.0.1",
"rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
"readlink 0.0.1",
"realpath 0.0.1",
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"relpath 0.0.1",
"rm 0.0.1",
"rmdir 0.0.1",
"seq 0.0.1",
"shred 0.0.1",
"shuf 0.0.1",
"sleep 0.0.1",
"sort 0.0.1",
"split 0.0.1",
"stat 0.0.1",
"stdbuf 0.0.1",
"sum 0.0.1",
"sync 0.0.1",
"tac 0.0.1",
"tail 0.0.1",
"tee 0.0.1",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"test 0.0.1",
"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",
"true 0.0.1",
"truncate 0.0.1",
"tsort 0.0.1",
"tty 0.0.1",
"uname 0.0.1",
"unexpand 0.0.1",
"unindent 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uniq 0.0.1",
"unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unlink 0.0.1",
"uptime 0.0.1",
"users 0.0.1",
"uucore 0.0.1",
"wc 0.0.1",
"who 0.0.1",
"whoami 0.0.1",
"yes 0.0.1",
]
[[package]]
name = "vec_map"
version = "0.8.0"

View file

@ -109,7 +109,7 @@ fn parse_opts(args: Vec<String>) -> getopts::Matches {
// TODO implement flag
.optflag("C", "compare", "(unimplemented) compare each pair of source and destination\n \
files, and in some cases, do not modify the destination at all")
.optflag("d", "directory", "treat all arguments as directory names\n \
.optflag("d", "directory", "treat all arguments as directory names.\n \
create all components of the specified directories")
// TODO implement flag
.optflag("D", "", "(unimplemented) create all leading components of DEST except the\n \
@ -286,6 +286,13 @@ fn directory(paths: &[PathBuf], b: Behaviour) -> i32 {
}
}
/// Test if the path is a a new file path that can be
/// created immediately
fn is_new_file_path(path: &Path) -> bool {
path.is_file() ||
! path.exists() && path.parent().map(|p| p.is_dir()).unwrap_or(true)
}
/// Perform an install, given a list of paths and behaviour.
///
/// Returns an integer intended as a program return code.
@ -296,9 +303,13 @@ fn standard(paths: &[PathBuf], b: Behaviour) -> i32 {
1
} else {
let sources = &paths[0..paths.len() - 1];
let target_directory = &paths[paths.len() - 1];
let target = &paths[paths.len() - 1];
copy_files_into_dir(sources, target_directory, &b)
if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
copy_file_to_file(&sources[0], target, &b)
} else {
copy_files_into_dir(sources, target, &b)
}
}
}
@ -338,6 +349,20 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &PathBuf, b: &Behaviour) -
if all_successful { 0 } else { 1 }
}
/// Copy a file to another file.
///
/// Prints verbose information and error messages.
/// Returns an integer intended as a program return code.
///
/// # Parameters
///
/// _file_ must exist as a non-directory.
/// _target_ must be a non-directory
///
fn copy_file_to_file(file: &PathBuf, target: &PathBuf, b: &Behaviour) -> i32 {
if copy(file, &target, b).is_err() { 1 } else { 0 }
}
/// Copy one file to a new location, changing metadata.
///
/// # Parameters

View file

@ -28,6 +28,20 @@ fn test_install_basic() {
assert!(at.file_exists(&format!("{}/{}", dir, file2)));
}
#[test]
fn test_install_failing_not_dir() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_dir_file_a1";
let file2 = "test_install_target_dir_file_a2";
let file3 = "test_install_target_dir_file_a3";
at.touch(file1);
at.touch(file2);
at.touch(file3);
assert!(ucmd.arg(file1).arg(file2).arg(file3)
.fails().stderr.contains("not a directory"));
}
#[test]
fn test_install_unimplemented_arg() {
let (at, mut ucmd) = at_and_ucmd!();
@ -136,3 +150,47 @@ fn test_install_mode_directories() {
let permissions = at.metadata(component).permissions();
assert_eq!(0o040333 as u32, PermissionsExt::mode(&permissions));
}
#[test]
fn test_install_target_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_file_file_i1";
let file2 = "test_install_target_file_file_i2";
at.touch(file1);
at.touch(file2);
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
assert!(at.file_exists(file1));
assert!(at.file_exists(file2));
}
#[test]
fn test_install_target_new_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "test_install_target_new_filer_file_j";
let dir = "test_install_target_new_file_dir_j";
at.touch(file);
at.mkdir(dir);
ucmd.arg(file).arg(format!("{}/{}", dir, file)).succeeds().no_stderr();
assert!(at.file_exists(file));
assert!(at.file_exists(&format!("{}/{}", dir, file)));
}
#[test]
fn test_install_target_new_file_failing_nonexistent_parent() {
let (at, mut ucmd) = at_and_ucmd!();
let file1 = "test_install_target_new_file_failing_file_k1";
let file2 = "test_install_target_new_file_failing_file_k2";
let dir = "test_install_target_new_file_failing_dir_k";
at.touch(file1);
let err = ucmd.arg(file1).arg(format!("{}/{}", dir, file2))
.fails().stderr;
assert!(err.contains("not a directory"))
}