diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2021-03-22 21:53:12 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-03-23 16:09:36 +0100 |
commit | 550ae23e8018bbde91b00af59071a3aee71cd78b (patch) | |
tree | af4cb789275359a4070302e676b5d08e4f65351b /Userland/Libraries/LibArchive | |
parent | 8eceef0b1bd2019c5cc0d09cf63b1a31b055f5a3 (diff) | |
download | serenity-550ae23e8018bbde91b00af59071a3aee71cd78b.zip |
LibArchive: Implement ZipOutputStream for zip archive creation
This output stream can be used to create zip archives, and will
be used in the implementation of the zip utility.
Diffstat (limited to 'Userland/Libraries/LibArchive')
-rw-r--r-- | Userland/Libraries/LibArchive/Zip.cpp | 75 | ||||
-rw-r--r-- | Userland/Libraries/LibArchive/Zip.h | 14 |
2 files changed, 89 insertions, 0 deletions
diff --git a/Userland/Libraries/LibArchive/Zip.cpp b/Userland/Libraries/LibArchive/Zip.cpp index 4c54622085..69a70169f5 100644 --- a/Userland/Libraries/LibArchive/Zip.cpp +++ b/Userland/Libraries/LibArchive/Zip.cpp @@ -117,4 +117,79 @@ bool Zip::for_each_member(Function<IterationDecision(const ZipMember&)> callback return true; } +ZipOutputStream::ZipOutputStream(OutputStream& stream) + : m_stream(stream) +{ +} + +void ZipOutputStream::add_member(const ZipMember& member) +{ + VERIFY(!m_finished); + VERIFY(member.name.length() <= UINT16_MAX); + VERIFY(member.compressed_data.size() <= UINT32_MAX); + m_members.append(member); + + LocalFileHeader local_file_header {}; + local_file_header.minimum_version = member.compression_method == ZipCompressionMethod::Deflate ? 20 : 10; // Deflate was added in PKZip 2.0 + local_file_header.general_purpose_flags = 0; + local_file_header.compression_method = static_cast<u16>(member.compression_method); + local_file_header.modification_time = 0; // TODO: support modification time + local_file_header.modification_date = 0; + local_file_header.crc32 = member.crc32; + local_file_header.compressed_size = member.compressed_data.size(); + local_file_header.uncompressed_size = member.uncompressed_size; + local_file_header.name_length = member.name.length(); + local_file_header.extra_data_length = 0; + local_file_header.name = (const u8*)(member.name.characters()); + local_file_header.extra_data = nullptr; + local_file_header.compressed_data = member.compressed_data.data(); + local_file_header.write(m_stream); +} + +void ZipOutputStream::finish() +{ + VERIFY(!m_finished); + m_finished = true; + + auto file_header_offset = 0; + auto central_directory_size = 0; + for (const ZipMember& member : m_members) { + CentralDirectoryRecord central_directory_record {}; + auto zip_version = member.compression_method == ZipCompressionMethod::Deflate ? 20 : 10; // Deflate was added in PKZip 2.0 + central_directory_record.made_by_version = zip_version; + central_directory_record.minimum_version = zip_version; + central_directory_record.general_purpose_flags = 0; + central_directory_record.compression_method = static_cast<u16>(member.compression_method); + central_directory_record.modification_time = 0; // TODO: support modification time + central_directory_record.modification_date = 0; + central_directory_record.crc32 = member.crc32; + central_directory_record.compressed_size = member.compressed_data.size(); + central_directory_record.uncompressed_size = member.uncompressed_size; + central_directory_record.name_length = member.name.length(); + central_directory_record.extra_data_length = 0; + central_directory_record.comment_length = 0; + central_directory_record.start_disk = 0; + central_directory_record.internal_attributes = 0; + central_directory_record.external_attributes = member.is_directory ? zip_directory_external_attribute : 0; + central_directory_record.local_file_header_offset = file_header_offset; // FIXME: we assume the wrapped output stream was never written to before us + file_header_offset += sizeof(local_file_header_signature) + (sizeof(LocalFileHeader) - (sizeof(u8*) * 3)) + member.name.length() + member.compressed_data.size(); + central_directory_record.name = (const u8*)(member.name.characters()); + central_directory_record.extra_data = nullptr; + central_directory_record.comment = nullptr; + central_directory_record.write(m_stream); + central_directory_size += central_directory_record.size(); + } + + EndOfCentralDirectory end_of_central_directory {}; + end_of_central_directory.disk_number = 0; + end_of_central_directory.central_directory_start_disk = 0; + end_of_central_directory.disk_records_count = m_members.size(); + end_of_central_directory.total_records_count = m_members.size(); + end_of_central_directory.central_directory_size = central_directory_size; + end_of_central_directory.central_directory_offset = file_header_offset; + end_of_central_directory.comment_length = 0; + end_of_central_directory.comment = nullptr; + end_of_central_directory.write(m_stream); +} + } diff --git a/Userland/Libraries/LibArchive/Zip.h b/Userland/Libraries/LibArchive/Zip.h index dd8c632ec2..ac835b8bfa 100644 --- a/Userland/Libraries/LibArchive/Zip.h +++ b/Userland/Libraries/LibArchive/Zip.h @@ -31,6 +31,7 @@ #include <AK/Span.h> #include <AK/Stream.h> #include <AK/String.h> +#include <AK/Vector.h> #include <string.h> namespace Archive { @@ -231,4 +232,17 @@ private: ReadonlyBytes m_input_data; }; +class ZipOutputStream { +public: + ZipOutputStream(OutputStream&); + void add_member(const ZipMember&); + void finish(); + +private: + OutputStream& m_stream; + Vector<ZipMember> m_members; + + bool m_finished { false }; +}; + } |