summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibArchive
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2021-03-22 21:53:12 +0200
committerAndreas Kling <kling@serenityos.org>2021-03-23 16:09:36 +0100
commit550ae23e8018bbde91b00af59071a3aee71cd78b (patch)
treeaf4cb789275359a4070302e676b5d08e4f65351b /Userland/Libraries/LibArchive
parent8eceef0b1bd2019c5cc0d09cf63b1a31b055f5a3 (diff)
downloadserenity-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.cpp75
-rw-r--r--Userland/Libraries/LibArchive/Zip.h14
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 };
+};
+
}