#include #include #include #include #include #include using os::File; Result copy_file(StringView source, StringView destination, bool verbose, bool interactive) { SharedPtr source_file = TRY(os::File::open(source, File::ReadOnly)); SharedPtr 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 luna_main(int argc, char** argv) { Vector 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; }