#include #include #include #include bool GetConfirmation(const std::string& message) { std::string user_input; while (true) { std::cout << message << "(Y/N)"<< std::endl; std::cin >> user_input; if (user_input == "y" || user_input == "Y") return true; if (user_input == "n" || user_input == "N") return false; } } std::vector ReadFileFromDisk(const std::filesystem::path& file_to_read) { std::ifstream file(file_to_read, std::ios::binary | std::ios::ate); if (!file) throw std::runtime_error("Failed to open file: " + file_to_read.string()); std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(size); if (!file.read(reinterpret_cast(buffer.data()), size)) throw std::runtime_error("Error reading file: " + file_to_read.string()); return buffer; } std::string PrettyVersionString() { return std::format("{}Redacted Software Archive Project v{} (rsarchive v{}) (RSA Format v{}){}", Colors::Browns::GoldenRod.ToEscapeCode(), ARCHIVE_PROJECT_VERSION, ARCHIVE_APP_VERSION, ARCHIVE_FORMAT_VERSION, mcolor::AnsiEscapeCodes::ResetAll); } bool WriteFileToDisk(const std::vector& file_data, const std::filesystem::path& destination) { std::ofstream file(destination, std::ios::binary); if (!file) return false; file.write(reinterpret_cast(file_data.data()), file_data.size()); return file.good(); } void DisplayLicense() { std::cout << Colors::Oranges::Gold.ToEscapeCode(); std::cout << "This is free and unencumbered software released into the public domain." << std::endl; std::cout << std::endl; std::cout << "Anyone is free to copy, modify, publish, use, compile, sell, or" << std::endl; std::cout << "distribute this software, either in source code form or as a compiled" << std::endl; std::cout << "binary, for any purpose, commercial or non-commercial, and by any" << std::endl; std::cout << "means." << std::endl; std::cout << std::endl; std::cout << "In jurisdictions that recognize copyright laws, the author or authors" << std::endl; std::cout << "of this software dedicate any and all copyright interest in the" << std::endl; std::cout << "software to the public domain. We make this dedication for the benefit" << std::endl; std::cout << "of the public at large and to the detriment of our heirs and" << std::endl; std::cout << "successors. We intend this dedication to be an overt act of" << std::endl; std::cout << "relinquishment in perpetuity of all present and future rights to this" << std::endl; std::cout << "software under copyright law." << std::endl; std::cout << std::endl; std::cout << "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND," << std::endl; std::cout << "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF" << std::endl; std::cout << "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT." << std::endl; std::cout << "IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR" << std::endl; std::cout << "OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE," << std::endl; std::cout << "ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR" << std::endl; std::cout << "OTHER DEALINGS IN THE SOFTWARE." << mcolor::AnsiEscapeCodes::ResetAll << std::endl; } void DisplayHelp() { std::string sep = Colors::DarkGray.ToEscapeCode() + mcolor::AnsiEscapeCodes::Bold + ">>> " + mcolor::AnsiEscapeCodes::ResetAll; std::cout << PrettyVersionString() << std::endl; int col = 45; std::cout.width(col); std::cout << std::left << "-v version: show the version string " << sep << std::left << "-h help: shows this listing" << std::endl; std::cout.width(col); std::cout << std::left << "-L license: show software license " << sep << std::right << "-l list: show all files in-to an archive" << std::endl; std::cout.width(col); std::cout << std::left <<"-x extract: retrieve files from an archive " << sep << std::right << "-a add: put a file in-to an archive" << std::endl; std::cout.width(col); std::cout << std::left << "-c create: make a new, empty archive " << sep << std::right << "-r create an archive from a directory" << std::endl; std::cout.width(col); std::cout << std::left << "-ar add recursive: put all files in a directory in-to an archive" << std::endl; } void DisplayArchiveContents(const std::filesystem::path& archive) { auto result = ReArchive::ReadFileTable(archive); if (!result.first) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } auto file_table = result.second; std::cout << "path | size (bytes)" << std::endl; for (const auto& e : *file_table.GetEntries()) std::cout << e.second.Path() << " " << e.second.Size() << std::endl; std::cout << file_table.Count() << " files" << std::endl; } void DisplayInvalidParameters() { //std::cerr << "Invalid parameters received. Use -h or 'man rsarchive' for a complete guide." << std::endl; std::cout << Colors::Reds::LightCoral.ToEscapeCode() << "Invalid parameters received." << Colors::White.ToEscapeCode() << " Use -h or 'man rsarchive' for a complete guide." << mcolor::AnsiEscapeCodes::ResetAll << std::endl; } void AddFileToArchive(const std::filesystem::path& file_to_add, const std::filesystem::path& archive, ReArchive::FileTable* file_table = nullptr) { if (!std::filesystem::exists(file_to_add)) { std::cerr << "The specified path for the file to be added is inaccessible." << std::endl; return; } if (std::filesystem::is_directory(file_to_add)) { std::cerr << "The specified path for the file(s) to be added is a directory." << std::endl; return; } auto file_data = ReadFileFromDisk(file_to_add); auto result = ReArchive::WriteFile(archive, file_to_add, file_data.data(), file_data.size(), file_table); if (!result) std::cerr << "The specified path for the file to be added already exists within the archive." << std::endl; } void AddDirectoryToArchive(const std::filesystem::path& directory_to_add, const std::filesystem::path& archive) { if (!std::filesystem::exists(directory_to_add)) { std::cerr << "The specified path for the file(s) to add is inaccessible or does not exist." << std::endl; return; } if (!std::filesystem::is_directory(directory_to_add)) { std::cerr << "The specified path for the file(s) to add is not a directory." << std::endl; return; } if (!std::filesystem::exists(archive)) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } auto result = ReArchive::ReadFileTable(archive); if (!result.first) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } for (const auto& entry : std::filesystem::recursive_directory_iterator(directory_to_add)) { if (std::filesystem::is_regular_file(entry) && !std::filesystem::is_directory(entry)) { auto entry_relative_path = std::filesystem::relative(entry.path(), std::filesystem::current_path()); AddFileToArchive(entry_relative_path, archive, &result.second); } } } void NewArchiveFromDirectory(const std::filesystem::path& directory_to_add, const std::filesystem::path& archive) { if (std::filesystem::exists(archive)) { std::cerr << "The specified path for the new archive already exists." << std::endl; return; } auto result = ReArchive::CreateArchive(archive, false); if (!result) std::cerr << "The specified path for the new archive already exists." << std::endl; AddDirectoryToArchive(directory_to_add, archive); } void ExtractArchive(const std::filesystem::path& archive) { if (!std::filesystem::exists(archive)) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } auto file_table_result = ReArchive::ReadFileTable(archive); if (!file_table_result.first) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } auto result = ReArchive::ReadFileTable(archive); if (!result.first) { std::cerr << "The specified path is inaccessible or not a valid archive." << std::endl; return; } for (const auto& entry : *file_table_result.second.GetEntries()) { if (std::filesystem::exists(entry.first)) if (!GetConfirmation("File " + entry.first.string() + " already exists, overwrite?")) continue; std::filesystem::create_directories(std::filesystem::current_path() / entry.first.parent_path()); if (!WriteFileToDisk(ReArchive::ReadFile(archive, entry.first, &result.second),std::filesystem::current_path() / entry.first)) std::cerr << "The path for writing is inaccessible." << std::endl; } } int main(int argc, char* argv[]) { mcolor::windowsSaneify(); if (argc == 1) DisplayInvalidParameters(); if (argc == 2) { if (std::string(argv[1]) == "-v") std::cout << PrettyVersionString() << std::endl; else if (std::string(argv[1]) == "-h") DisplayHelp(); else if (std::string(argv[1]) == "-L") DisplayLicense(); else DisplayInvalidParameters(); } else if (argc == 3) { if (std::string(argv[1]) == "-l") DisplayArchiveContents(argv[2]); else if (std::string(argv[1]) == "-c") { if(!ReArchive::CreateArchive(argv[2], false)) std::cerr << "The specified path for the new archive is inaccessible or already exists." << std::endl; } else if (std::string(argv[1]) == "-x") { ExtractArchive(argv[2]); } else DisplayInvalidParameters(); } else if (argc == 4) { if (std::string(argv[1]) == "-a") AddFileToArchive(argv[2], argv[3]); else if (std::string(argv[1]) == "-ar") AddDirectoryToArchive(argv[2], argv[3]); else if (std::string(argv[1]) == "-r") NewArchiveFromDirectory(argv[2], argv[3]); else DisplayInvalidParameters(); } }