summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Malpas <40313754+malpas@users.noreply.github.com>2019-09-07 13:02:29 +1000
committerAndreas Kling <awesomekling@gmail.com>2019-09-07 16:54:49 +0200
commitfe1ab7989f48eb450d5d65445e4f3517f8165df6 (patch)
tree541849ed5130f89d4f7207974dd3ecf94fc4ab05
parent86415acd6a925fbf89aecade1e4d322a2ee00429 (diff)
downloadserenity-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.cpp62
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