diff --git a/src/BUILD.bazel b/src/BUILD.bazel index b447bf4..f4d0c69 100644 --- a/src/BUILD.bazel +++ b/src/BUILD.bazel @@ -6,3 +6,19 @@ cc_library( name = "decompress", hdrs = ["decompress.hpp"], ) + +cc_library( + name = "gunzip", + srcs = ["gunzip.cpp"], + hdrs = ["gunzip.hpp"], + deps = ["//huffman"], +) + +cc_binary( + name = "gunzip_main", + srcs = ["gunzip_main.cpp"], + deps = [ + ":decompress", + ":gunzip", + ], +) diff --git a/src/gunzip.cpp b/src/gunzip.cpp new file mode 100644 index 0000000..f08c4d6 --- /dev/null +++ b/src/gunzip.cpp @@ -0,0 +1,9 @@ +#include "gunzip.hpp" + +namespace starflate { +auto gunzip(std::istream& in, std::ostream& out) -> GunzipError +{ + // TODO: implement + return GunzipError::NoError; +} +} // namespace starflate diff --git a/src/gunzip.hpp b/src/gunzip.hpp new file mode 100644 index 0000000..6442fb5 --- /dev/null +++ b/src/gunzip.hpp @@ -0,0 +1,12 @@ +#include +#include +#include + +namespace starflate { +enum class GunzipError : std::uint8_t +{ + NoError, + Error, +}; +auto gunzip(std::istream& in, std::ostream& out) -> GunzipError; +} // namespace starflate diff --git a/src/gunzip_main.cpp b/src/gunzip_main.cpp new file mode 100644 index 0000000..f77c90a --- /dev/null +++ b/src/gunzip_main.cpp @@ -0,0 +1,76 @@ +#include "gunzip.hpp" + +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + +auto usage(const char* program) -> int +{ + std::cerr << "Usage: " << program << "[OPTIONS] " << std::endl; + std::cerr << "OPTIONS:" << std::endl; + std::cerr + << "\n\t-c --stdout --to-stdout\n\t\tWrite to standard output" + << std::endl; + return EXIT_FAILURE; +} + +auto decompressed_path(const std::filesystem::path& in_path) + -> std::filesystem::path +{ + std::filesystem::path res{in_path}; + if (in_path.extension() == ".gz") { + res.replace_extension(); + } else { + res += ".decompressed"; + } + return res; +} + +auto main(const int argc, const char* argv[]) -> int +{ + if (argc < 2 || argc > 3) { + return usage(argv[0]); + } + + std::filesystem::path in_path; + bool to_stdout{}; + std::ofstream out_file; + if (argc == 3) { + const auto option = argv[1]; + // NOLINTBEGIN(readability-magic-numbers) + if (strncmp(option, "-c", 2) != 0 && strncmp(option, "--stdout", 8) != 0 && + strncmp(option, "--to-stdout", 11) != 0) { + // NOLINTEND(readability-magic-numbers) + return usage(argv[0]); + } + in_path = argv[2]; + to_stdout = true; + } else { + in_path = argv[1]; + std::filesystem::path out_path{decompressed_path(in_path)}; + out_file.open(out_path, std::ios::binary); + if (!out_file.is_open()) { + std::cerr << "Failed to open " << out_path << " for writing" << std::endl; + return EXIT_FAILURE; + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto& out = to_stdout ? std::cout : out_file; + + std::ifstream in{in_path, std::ios::binary}; + if (!in.is_open()) { + std::cerr << "Failed to open " << in_path << " for reading" << std::endl; + return EXIT_FAILURE; + } + + const auto err = starflate::gunzip(in, out); + if (err != starflate::GunzipError::NoError) { + std::cerr << "Error: " << static_cast(err) << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +}