Luna/apps/cp.cpp
apio 5c9503ac71
All checks were successful
continuous-integration/drone/push Build is passing
libos+cp: Add prompt functionality
2023-07-08 18:30:39 +02:00

75 lines
2.5 KiB
C++

#include <luna/PathParser.h>
#include <luna/String.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/FileSystem.h>
#include <os/Prompt.h>
using os::File;
Result<void> copy_file(StringView source, StringView destination, bool verbose, bool interactive)
{
SharedPtr<File> source_file = TRY(os::File::open(source, File::ReadOnly));
SharedPtr<File> destination_file;
struct stat st;
TRY(os::FileSystem::stat(source_file->fd(), st, true));
umask(0);
if (os::FileSystem::is_directory(destination, true))
{
auto basename = TRY(PathParser::basename(source));
auto path = TRY(PathParser::join_paths(destination, basename.view()));
if (interactive && os::FileSystem::exists(path.view(), false))
{
auto prompt = TRY(String::format("cp: Overwrite %s with %s?"_sv, path.chars(), source.chars()));
if (!os::conditional_prompt(prompt.view(), os::DefaultNo)) return {};
}
destination_file = TRY(File::open_or_create(path.view(), File::ReadWrite, st.st_mode & ~S_IFMT));
}
else
{
if (interactive && os::FileSystem::exists(destination, false))
{
auto prompt = TRY(String::format("cp: Overwrite %s with %s?"_sv, destination.chars(), source.chars()));
if (!os::conditional_prompt(prompt.view(), os::DefaultNo)) return {};
}
destination_file = TRY(File::open_or_create(destination, File::ReadWrite, st.st_mode & ~S_IFMT));
}
if (verbose) os::eprintln("copying %s to %s", source.chars(), destination.chars());
auto buf = TRY(Buffer::create_sized(4096));
while (1)
{
TRY(source_file->read(buf, 4096));
if (buf.is_empty()) break;
destination_file->write(buf);
}
return {};
}
Result<int> luna_main(int argc, char** argv)
{
Vector<StringView> files;
StringView destination;
bool verbose { false };
bool interactive { false };
os::ArgumentParser parser;
parser.add_description("Copy files or directories."_sv);
parser.add_system_program_info("cp"_sv);
parser.set_vector_argument(files, "files"_sv, true);
parser.add_positional_argument(destination, "destination"_sv, true);
parser.add_switch_argument(verbose, 'v', "verbose"_sv, "show more information"_sv);
parser.add_switch_argument(interactive, 'i', "interactive"_sv, "prompt before overwriting existing files"_sv);
TRY(parser.parse(argc, argv));
for (const auto& file : files) { TRY(copy_file(file, destination, verbose, interactive)); }
return 0;
}