cp: Implement directory copying

Recursive copying is only allowed if cp is called with the -r switch, ala POSIX.
This commit is contained in:
Aaron Malpas 2019-09-07 13:02:29 +10:00 committed by Andreas Kling
parent 86415acd6a
commit fe1ab7989f
Notes: sideshowbarker 2024-07-19 12:14:13 +09:00

View file

@ -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;
}