diff options
author | Aaron Malpas <40313754+malpas@users.noreply.github.com> | 2019-09-07 13:02:29 +1000 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-09-07 16:54:49 +0200 |
commit | fe1ab7989f48eb450d5d65445e4f3517f8165df6 (patch) | |
tree | 541849ed5130f89d4f7207974dd3ecf94fc4ab05 | |
parent | 86415acd6a925fbf89aecade1e4d322a2ee00429 (diff) | |
download | serenity-fe1ab7989f48eb450d5d65445e4f3517f8165df6.zip |
cp: Implement directory copying
Recursive copying is only allowed if cp is called with the -r switch, ala POSIX.
-rw-r--r-- | Userland/cp.cpp | 62 |
1 files changed, 55 insertions, 7 deletions
diff --git a/Userland/cp.cpp b/Userland/cp.cpp index a2ddb8bda4..1a4a5d554e 100644 --- a/Userland/cp.cpp +++ b/Userland/cp.cpp @@ -2,17 +2,21 @@ #include <AK/FileSystemPath.h> #include <AK/StringBuilder.h> #include <LibCore/CArgsParser.h> +#include <LibCore/CDirIterator.h> #include <assert.h> #include <fcntl.h> #include <stdio.h> #include <sys/stat.h> #include <unistd.h> -bool copy_file(String, String); +bool copy_file_or_directory(String, String, bool); +bool copy_file(String, String, struct stat, int); +bool copy_directory(String, String); int main(int argc, char** argv) { CArgsParser args_parser("cp"); + args_parser.add_arg("r", "copy directories recursively"); args_parser.add_required_single_value("source"); args_parser.add_required_single_value("destination"); @@ -22,16 +26,19 @@ int main(int argc, char** argv) args_parser.print_usage(); return 0; } + bool recursion_allowed = args.is_present("r"); String src_path = values[0]; String dst_path = values[1]; - return copy_file(src_path, dst_path) ? 0 : 1; + return copy_file_or_directory(src_path, dst_path, recursion_allowed) ? 0 : 1; } /** - * Copy a source file to a destination file. Returns true if successful, false + * Copy a file or directory to a new location. Returns true if successful, false * otherwise. If there is an error, its description is output to stderr. + * + * Directories should only be copied if recursion_allowed is set. */ -bool copy_file(String src_path, String dst_path) +bool copy_file_or_directory(String src_path, String dst_path, bool recursion_allowed) { int src_fd = open(src_path.characters(), O_RDONLY); if (src_fd < 0) { @@ -47,10 +54,23 @@ bool copy_file(String src_path, String dst_path) } if (S_ISDIR(src_stat.st_mode)) { - fprintf(stderr, "cp: FIXME: Copying directories is not yet supported\n"); - return false; + if (!recursion_allowed) { + fprintf(stderr, "cp: -r not specified; omitting directory '%s'\n", src_path.characters()); + return false; + } + return copy_directory(src_path, dst_path); } + return copy_file(src_path, dst_path, src_stat, src_fd); +} +/** + * Copy a source file to a destination file. Returns true if successful, false + * otherwise. If there is an error, its description is output to stderr. + * + * To avoid repeated work, the source file's stat and file descriptor are required. + */ +bool copy_file(String src_path, String dst_path, struct stat src_stat, int src_fd) +{ int dst_fd = creat(dst_path.characters(), 0666); if (dst_fd < 0) { if (errno != EISDIR) { @@ -94,7 +114,7 @@ bool copy_file(String src_path, String dst_path) auto my_umask = umask(0); umask(my_umask); - rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask); + int rc = fchmod(dst_fd, src_stat.st_mode & ~my_umask); if (rc < 0) { perror("fchmod dst"); return false; @@ -104,3 +124,31 @@ bool copy_file(String src_path, String dst_path) close(dst_fd); return true; } + +/** + * Copy the contents of a source directory into a destination directory. + */ +bool copy_directory(String src_path, String dst_path) +{ + int rc = mkdir(dst_path.characters(), 0755); + if (rc < 0) { + perror("cp: mkdir"); + return false; + } + CDirIterator di(src_path, CDirIterator::SkipDots); + if (di.has_error()) { + fprintf(stderr, "cp: CDirIterator: %s\n", di.error_string()); + return false; + } + while (di.has_next()) { + String filename = di.next_path(); + bool is_copied = copy_file_or_directory( + String::format("%s/%s", src_path.characters(), filename.characters()), + String::format("%s/%s", dst_path.characters(), filename.characters()), + true); + if (!is_copied) { + return false; + } + } + return true; +}
\ No newline at end of file |