summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2023-09-17 20:59:41 +0100
committerMartin Ashby <martin@ashbysoft.com>2023-09-17 20:59:41 +0100
commitd29b334ac905087a9938b273953578aad88eda3c (patch)
tree53e0c160e7cd328d385feb9af7d0428c803f9c87
parentebe6444f36671ff326d40d620beb220f983e4767 (diff)
downloadzip-zig-d29b334ac905087a9938b273953578aad88eda3c.tar.gz
zip-zig-d29b334ac905087a9938b273953578aad88eda3c.tar.bz2
zip-zig-d29b334ac905087a9938b273953578aad88eda3c.tar.xz
zip-zig-d29b334ac905087a9938b273953578aad88eda3c.zip
Switch slice -> ArrayList for central_directory_headers
Will help support writing new zip files or modifying existing ones. Add initial write methods for structures, TODO make it generic
-rw-r--r--src/main.zig153
1 files changed, 126 insertions, 27 deletions
diff --git a/src/main.zig b/src/main.zig
index d154c8c..cfcac3e 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -56,7 +56,15 @@ const CompressionMethod = enum(u16) {
// [end of central directory record]
allocator: std.mem.Allocator,
end_of_central_directory_record: EndOfCentralDirectoryRecord,
-central_directory_headers: []CentralDirectoryHeader,
+central_directory_headers: std.ArrayList(CentralDirectoryHeader),
+
+pub fn empty(allocator: std.mem.Allocator) !@This() {
+ return .{
+ .allocator = allocator,
+ .end_of_central_directory_record = EndOfCentralDirectoryRecord.empty(),
+ .central_directory_headers = std.ArrayList(CentralDirectoryHeader).init(allocator),
+ };
+}
pub fn from(allocator: std.mem.Allocator, file_or_stream: anytype) !@This() {
// Find the EndOfCentralDirectoryRecord. It must be in the last 64k of the file
@@ -74,11 +82,22 @@ pub fn from(allocator: std.mem.Allocator, file_or_stream: anytype) !@This() {
if (eocdr.disk_number_this != 0 or eocdr.disk_number_central_dir_start != 0) return error.SpansNotSupported;
if (eocdr.total_central_dir_entries != eocdr.total_central_dir_entries_on_this_disk) return error.SpansNotSupported;
- var central_directory_headers = try allocator.alloc(CentralDirectoryHeader, eocdr.total_central_dir_entries);
- errdefer allocator.free(central_directory_headers);
try file_or_stream.seekTo(eocdr.central_dir_offset);
- for (0..eocdr.total_central_dir_entries) |i| {
- central_directory_headers[i] = try CentralDirectoryHeader.read(allocator, reader);
+
+ var central_directory_headers = try std.ArrayList(CentralDirectoryHeader).initCapacity(allocator, eocdr.total_central_dir_entries);
+ errdefer {
+ // while (central_directory_headers.popOrNull()) |cdh| {
+ // cdh.deinit(allocator);
+ // }
+ var cdh: ?CentralDirectoryHeader = central_directory_headers.popOrNull();
+ while (cdh != null) {
+ cdh.?.deinit(allocator);
+ cdh = central_directory_headers.popOrNull();
+ }
+ central_directory_headers.deinit();
+ }
+ for (0..eocdr.total_central_dir_entries) |_| {
+ central_directory_headers.appendAssumeCapacity(try CentralDirectoryHeader.read(allocator, reader));
}
return .{
@@ -88,7 +107,18 @@ pub fn from(allocator: std.mem.Allocator, file_or_stream: anytype) !@This() {
};
}
-/// returns how much to seekBy after the signature is found (becuase we'll now have read over it.)
+pub fn deinit(self: *@This()) void {
+ self.end_of_central_directory_record.deinit(self.allocator);
+ var cdh: ?CentralDirectoryHeader = self.central_directory_headers.popOrNull();
+ while (cdh != null) {
+ cdh.?.deinit(self.allocator);
+ cdh = self.central_directory_headers.popOrNull();
+ }
+ self.central_directory_headers.deinit();
+}
+
+
+/// returns how much to seekBy after the signature is found (becuase we'll now have read past it.)
fn read_to_sig(reader: anytype, sig: u32) !i32 {
const needle = @byteSwap(sig);
var window: u32 = try reader.readIntLittle(u32);
@@ -104,28 +134,20 @@ fn read_to_sig(reader: anytype, sig: u32) !i32 {
}
}
-pub fn deinit(self: *@This()) void {
- self.end_of_central_directory_record.deinit(self.allocator);
- for (0..self.central_directory_headers.len) |i| {
- self.central_directory_headers[i].deinit(self.allocator);
- }
- self.allocator.free(self.central_directory_headers);
-}
-
pub fn count_files(self: @This()) u16 {
return self.end_of_central_directory_record.total_central_dir_entries;
}
pub fn file_name(self: @This(), index: usize) []const u8 {
- return self.central_directory_headers[index].file_name;
+ return self.central_directory_headers.items[index].file_name;
}
pub fn file_comment(self: @This(), index: usize) []const u8 {
- return self.central_directory_headers[index].file_comment;
+ return self.central_directory_headers.items[index].file_comment;
}
pub fn is_dir(self: *@This(), index: usize) bool {
- return std.mem.endsWith(u8, self.central_directory_headers[index].file_name, "/"); // This is what the java stdlib does
+ return std.mem.endsWith(u8, self.central_directory_headers.items[index].file_name, "/"); // This is what the java stdlib does
}
pub fn extract(self: @This(), index: usize, stream_or_file_in: anytype, writer: anytype) !void {
- const cdh = self.central_directory_headers[index];
+ const cdh = self.central_directory_headers.items[index];
try stream_or_file_in.seekTo(cdh.relative_offset_of_local_header);
var reader = stream_or_file_in.reader();
var lfh = try LocalFileHeader.read(self.allocator, reader);
@@ -180,6 +202,34 @@ const CentralDirectoryHeader = struct {
});
}
+ fn write(self: *CentralDirectoryHeader, writer: anytype) !void {
+ // TODO generics
+ self.file_name_length = @intCast(self.file_name.len);
+ self.extra_field_length = @intCast(self.extra_field.len);
+ self.file_comment_length = @intCast(self.file_comment);
+
+ try writer.writeIntLittle(u32, SIG);
+ try writer.writeIntLittle(u16, self.version_made_by);
+ try writer.writeIntLittle(u16, self.version_needed_to_extract);
+ try writer.writeIntLittle(u16, self.general_purpose_bit_flag);
+ try writer.writeIntLittle(u16, @intFromEnum(self.compression_method));
+ try writer.writeIntLittle(u16, self.last_mod_file_time);
+ try writer.writeIntLittle(u16, self.last_mod_file_date);
+ try writer.writeIntLittle(u32, self.crc32);
+ try writer.writeIntLittle(u32, self.compressed_size);
+ try writer.writeIntLittle(u32, self.uncompressed_size);
+ try writer.writeIntLittle(u16, self.file_name_length);
+ try writer.writeIntLittle(u16, self.extra_field_length);
+ try writer.writeIntLittle(u16, self.file_comment_length);
+ try writer.writeIntLittle(u16, self.disk_number_start);
+ try writer.writeIntLittle(u16, self.internal_file_attributes);
+ try writer.writeIntLittle(u32, self.external_file_attributes);
+ try writer.writeIntLittle(u32, self.relative_offset_of_local_header);
+ try writer.writeAll(self.file_name);
+ try writer.writeAll(self.extra_field);
+ try writer.writeAll(self.file_comment);
+ }
+
fn deinit(self: *CentralDirectoryHeader, allocator: std.mem.Allocator) void {
allocator.free(self.file_name);
allocator.free(self.extra_field);
@@ -204,6 +254,32 @@ const EndOfCentralDirectoryRecord = struct {
});
}
+ fn empty() !EndOfCentralDirectoryRecord {
+ return .{
+ .disk_number_this = 0,
+ .disk_number_central_dir_start = 0,
+ .total_central_dir_entries_on_this_disk = 0,
+ .total_central_dir_entries = 0,
+ .size_of_central_dir = 0,
+ .central_dir_offset = 0,
+ .comment_length = 0,
+ .comment = &[_]u8{},
+ };
+ }
+
+ fn write(self: *EndOfCentralDirectoryRecord, writer: anytype) !void {
+ self.comment_length = @intCast(self.comment.len);
+ try writer.writeIntLittle(u32, SIG);
+ try writer.writeIntLittle(u16, self.disk_number_this);
+ try writer.writeIntLittle(u16, self.disk_number_central_dir_start);
+ try writer.writeIntLittle(u16, self.total_central_dir_entries_on_this_disk);
+ try writer.writeIntLittle(u16, self.total_central_dir_entries);
+ try writer.writeIntLittle(u32, self.size_of_central_dir);
+ try writer.writeIntLittle(u32, self.central_dir_offset);
+ try writer.writeIntLittle(u16, self.comment_length);
+ try writer.writeAll(self.comment);
+ }
+
fn deinit(self: *EndOfCentralDirectoryRecord, allocator: std.mem.Allocator) void {
allocator.free(self.comment);
}
@@ -231,6 +307,24 @@ const LocalFileHeader = struct {
});
}
+ fn write(self: *LocalFileHeader, writer: anytype) !void {
+ self.file_name_length = @intCast(self.file_name.len);
+ self.extra_field_length = @intCast(self.extra_field.len);
+ try writer.writeIntLittle(u32, SIG);
+ try writer.writeIntLittle(u16, self.version_needed_to_extract);
+ try writer.writeIntLittle(u16, self.general_purpose_bit_flag);
+ try writer.writeIntLittle(u16, self.compression_method);
+ try writer.writeIntLittle(u16, self.last_mod_file_time);
+ try writer.writeIntLittle(u16, self.last_mod_file_date);
+ try writer.writeIntLittle(u32, self.crc32);
+ try writer.writeIntLittle(u32, self.compressed_size);
+ try writer.writeIntLittle(u32, self.uncompressed_size);
+ try writer.writeIntLittle(u16, self.file_name_length);
+ try writer.writeIntLittle(u16, self.extra_field_length);
+ try writer.writeAll(self.file_name);
+ try writer.writeAll(self.extra_field);
+ }
+
fn deinit(self: *LocalFileHeader, allocator: std.mem.Allocator) void {
allocator.free(self.file_name);
allocator.free(self.extra_field);
@@ -341,7 +435,8 @@ fn read2(
pub const Options = struct {
allocator: std.mem.Allocator,
};
-// tar.zig compatibility, ish. It manages a forwards-only read of the filesystem anyway
+
+/// tar.zig compatibility, ish. It does a forwards only pass of a zipfile
pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
const allocator = options.allocator;
// var peek_stream = std.io.peekStream(4, reader);
@@ -363,16 +458,20 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
var writer = f.writer();
try lfh.extract(allocator, reader, writer);
}
+ // TODO skip data descriptor
i+=1;
- // try reader.skipBytes(12, .{}); // Data descriptor signature.
- // _ = read_to_sig(peek_reader, LocalFileHeader.SIG) catch |e| switch (e) {
- // error.EndOfStream => return,
- // else => return e,
- // };
- // try peek_stream.putBack(&@as([4]u8, @bitCast(LocalFileHeader.SIG)));
}
}
+pub const WriteOptions = struct {};
+
+/// Write a whole zip file. file_iterator
+pub fn write_zip(file_iterator: anytype, writer: anytype) !void {
+ _ = writer;
+ _ = file_iterator;
+
+}
+
test "open stream" {
const test_zip = @embedFile("hello.zip");
var fbs = std.io.fixedBufferStream(test_zip);
@@ -416,7 +515,7 @@ test "extract deflate" {
var fbs_out = std.io.fixedBufferStream(&out);
var writer = fbs_out.writer();
try std.testing.expectEqualStrings("Here is a comment :)", zf.file_comment(0));
- try std.testing.expectEqual(@This().CompressionMethod.deflate, zf.central_directory_headers[0].compression_method);
+ try std.testing.expectEqual(@This().CompressionMethod.deflate, zf.central_directory_headers.items[0].compression_method);
try zf.extract(0, &fbs, writer);
try std.testing.expectEqualStrings(@embedFile("foo.txt"), fbs_out.getWritten());
}
@@ -429,7 +528,7 @@ test "subdir" {
try std.testing.expectEqual(false, zf.is_dir(1));
}
-test "filesystem" {
+test "pipe to filesystem" {
var f = try std.fs.cwd().openFile("src/subfolder.zip", .{});
defer f.close();
try pipeToFileSystem(try std.fs.cwd().makeOpenPath("test", .{}), f.reader(), .{ .allocator = std.testing.allocator });