diff --git a/CMakeLists.txt b/CMakeLists.txt index 8042fee..10efa13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,5 +11,12 @@ add_library(ReArchive SHARED ${SOURCES}) set_target_properties(ReArchive PROPERTIES LINKER_LANGUAGE CXX) target_include_directories(ReArchive PUBLIC ${PROJECT_SOURCE_DIR}/include) -add_executable(ReArchive_Demo main.cpp) -target_link_libraries(ReArchive_Demo PUBLIC ReArchive) \ No newline at end of file +add_executable(rsarchive main.cpp) +target_link_libraries(rsarchive PUBLIC ReArchive) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the build type" FORCE) +endif() + +install(TARGETS ReArchive DESTINATION /usr/lib) +install(TARGETS rsarchive DESTINATION /usr/bin) \ No newline at end of file diff --git a/include/ReArchive/types/FileTable.h b/include/ReArchive/types/FileTable.h index 4af386b..2a93669 100644 --- a/include/ReArchive/types/FileTable.h +++ b/include/ReArchive/types/FileTable.h @@ -15,7 +15,7 @@ protected: public: void Append(const FileEntry& file_entry); void Remove(const FileEntry& file_entry); - [[nodiscard]] bool Contains(std::filesystem::path& entry ) const { return entries.contains(entry); } + [[nodiscard]] bool Contains(const std::filesystem::path& entry ) const { return entries.contains(entry); } [[nodiscard]] std::unordered_map GetEntries() const { return entries; } [[nodiscard]] int64_t Count() const { return entries.size(); } public: diff --git a/main.cpp b/main.cpp index c486f9e..08b4da4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,34 +1,230 @@ #include #include +#include -int main() { - ReArchive::FileTable running_tally; +bool GetConfirmation(const std::string& message) { + std::string user_input; + while (true) { + std::cout << message << "(Y/N)"<< std::endl; + std::cin >> user_input; - if(std::filesystem::exists("test.rsa")) - std::filesystem::remove("test.rsa"); - - if (!ReArchive::CreateArchive("test.rsa")) - return -1; - std::string some_string = "some other string0."; - if (!ReArchive::WriteFile("test.rsa", "assets/test0.png", reinterpret_cast(some_string.data()), some_string.size(), &running_tally)) - return -1; + if (user_input == "y" || user_input == "Y") + return true; - some_string = "some other string1."; - if (!ReArchive::WriteFile("test.rsa", "assets/test1.png", reinterpret_cast(some_string.data()), some_string.size(), &running_tally)) - return -1; + 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()); - auto retrieved = ReArchive::ReadFile("test.rsa", "assets/test0.png"); - std::cout << std::string( retrieved.begin(), retrieved.end()) << std::endl; + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); - if (!ReArchive::EraseFile("test.rsa", "assets/test0.png", &running_tally)) - return -1; + std::vector buffer(size); - auto retrieved2 = ReArchive::ReadFile("test.rsa", "assets/test1.png"); - std::cout << std::string( retrieved2.begin(), retrieved2.end()) << std::endl; + if (!file.read(reinterpret_cast(buffer.data()), size)) + throw std::runtime_error("Error reading file: " + file_to_read.string()); - for (auto& e : running_tally.GetEntries()) - std::cout << e.second.Path() << std::endl; + return buffer; +} - ReArchive::FileTable copy = running_tally; +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 << "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." << std::endl; +} + +void DisplayHelp() { + std::cout << "Redacted Software Archive version 1.0" << std::endl; + std::cout << "-v version: show the version string -h help: shows this listing" << std::endl; + std::cout << "-L license: show software license -l list: show all files in-to an archive" << std::endl; + std::cout << "-x extract: retrieve files from an archive -a add: put a file in-to an archive" << std::endl; + std::cout << "-c create: make a new, empty archive -r create an archive from a directory" << std::endl; + std::cout << "-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; +} + +void DisplayInvalidParameters() { + std::cerr << "Invalid parameters received. Use -h or 'man rsarchive' for a complete guide." << std::endl; +} + +void AddFileToArchive(const std::filesystem::path& file_to_add, const std::filesystem::path& archive) { + 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_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 file_table = file_table_result.second; + if (file_table.Contains(file_to_add)) { + std::cerr << "The specified path for the file to be added already exists within the archive." << std::endl; + return; + } + + auto file_data = ReadFileFromDisk(file_to_add); + auto result = ReArchive::WriteFile(archive, file_to_add, file_data.data(), file_data.size()); + + if (!result) + std::cerr << "The specified path is inaccessible or not a valid 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; + } + + 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); + } + } +} + +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; + } + + 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; + + if (!WriteFileToDisk(ReArchive::ReadFile(archive, entry.first),std::filesystem::current_path() / entry.first)) + std::cerr << "The path for writing is inaccessible." << std::endl; + } +} + +int main(int argc, char* argv[]) { + if (argc == 1) + DisplayInvalidParameters(); + + if (argc == 2) { + if (std::string(argv[1]) == "-v") + std::cout << "Redacted Software Archive version 1.0" << 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(); + } } \ No newline at end of file diff --git a/src/ReArchive.cpp b/src/ReArchive.cpp index 0873ed4..8815ecc 100644 --- a/src/ReArchive.cpp +++ b/src/ReArchive.cpp @@ -125,6 +125,9 @@ bool ReArchive::WriteFile(const std::filesystem::path& archive, const std::files if (!std::filesystem::exists(archive)) return false; + if (std::filesystem::is_directory(archive)) + return false; + // Busy-wait. while (locked.contains(archive)) {} locked.insert(archive);