| ... | ... | @@ -413,15 +413,7 @@ pub const Iterator = struct { |
| 413 | 413 | uncompressed_size: u64, |
| 414 | 414 | file_offset: u64, |
| 415 | 415 | |
| 416 | | pub fn extract( |
| 417 | | self: Entry, |
| 418 | | stream: *File.Reader, |
| 419 | | options: ExtractOptions, |
| 420 | | filename_buf: []u8, |
| 421 | | dest: Io.Dir, |
| 422 | | ) !void { |
| 423 | | const io = stream.io; |
| 424 | | |
| 416 | pub fn getFilename(self: Entry, stream: *File.Reader, filename_buf: []u8, options: ExtractOptions) ![]u8 { |
| 425 | 417 | if (filename_buf.len < self.filename_len) |
| 426 | 418 | return error.ZipInsufficientBuffer; |
| 427 | 419 | switch (self.compression_method) { |
| ... | ... | @@ -434,6 +426,25 @@ pub const Iterator = struct { |
| 434 | 426 | try stream.interface.readSliceAll(filename); |
| 435 | 427 | } |
| 436 | 428 | |
| 429 | if (options.allow_backslashes) { |
| 430 | std.mem.replaceScalar(u8, filename, '\\', '/'); |
| 431 | } else { |
| 432 | if (std.mem.findScalar(u8, filename, '\\')) |_| |
| 433 | return error.ZipFilenameHasBackslash; |
| 434 | } |
| 435 | |
| 436 | if (isBadFilename(filename)) |
| 437 | return error.ZipBadFilename; |
| 438 | |
| 439 | return filename; |
| 440 | } |
| 441 | |
| 442 | pub fn extractTo(self: Entry, stream: *File.Reader, w: *Writer) !void { |
| 443 | switch (self.compression_method) { |
| 444 | .store, .deflate => {}, |
| 445 | else => return error.UnsupportedCompressionMethod, |
| 446 | } |
| 447 | |
| 437 | 448 | const local_data_header_offset: u64 = local_data_header_offset: { |
| 438 | 449 | const local_header = blk: { |
| 439 | 450 | try stream.seekTo(self.file_offset); |
| ... | ... | @@ -496,15 +507,45 @@ pub const Iterator = struct { |
| 496 | 507 | @as(u64, local_header.extra_len); |
| 497 | 508 | }; |
| 498 | 509 | |
| 499 | | if (options.allow_backslashes) { |
| 500 | | std.mem.replaceScalar(u8, filename, '\\', '/'); |
| 501 | | } else { |
| 502 | | if (std.mem.findScalar(u8, filename, '\\')) |_| |
| 503 | | return error.ZipFilenameHasBackslash; |
| 510 | const local_data_file_offset: u64 = |
| 511 | @as(u64, self.file_offset) + |
| 512 | @as(u64, @sizeOf(LocalFileHeader)) + |
| 513 | local_data_header_offset; |
| 514 | try stream.seekTo(local_data_file_offset); |
| 515 | |
| 516 | // TODO limit based on self.compressed_size |
| 517 | |
| 518 | switch (self.compression_method) { |
| 519 | .store => { |
| 520 | stream.interface.streamExact64(w, self.uncompressed_size) catch |err| switch (err) { |
| 521 | error.ReadFailed => |e| return stream.err orelse e, |
| 522 | error.WriteFailed => |e| return e, |
| 523 | error.EndOfStream => return error.ZipDecompressTruncated, |
| 524 | }; |
| 525 | }, |
| 526 | .deflate => { |
| 527 | var flate_buffer: [flate.max_window_len]u8 = undefined; |
| 528 | var decompress: flate.Decompress = .init(&stream.interface, .raw, &flate_buffer); |
| 529 | decompress.reader.streamExact64(w, self.uncompressed_size) catch |err| switch (err) { |
| 530 | error.ReadFailed => |e| return decompress.err orelse (stream.err orelse e), |
| 531 | error.WriteFailed => |e| return e, |
| 532 | error.EndOfStream => return error.ZipDecompressTruncated, |
| 533 | }; |
| 534 | }, |
| 535 | else => return error.UnsupportedCompressionMethod, |
| 504 | 536 | } |
| 537 | } |
| 505 | 538 | |
| 506 | | if (isBadFilename(filename)) |
| 507 | | return error.ZipBadFilename; |
| 539 | pub fn extract( |
| 540 | self: Entry, |
| 541 | stream: *File.Reader, |
| 542 | options: ExtractOptions, |
| 543 | filename_buf: []u8, |
| 544 | dest: Io.Dir, |
| 545 | ) !void { |
| 546 | const io = stream.io; |
| 547 | |
| 548 | const filename = try self.getFilename(stream, filename_buf, options); |
| 508 | 549 | |
| 509 | 550 | // All entries that end in '/' are directories |
| 510 | 551 | if (filename[filename.len - 1] == '/') { |
| ... | ... | @@ -527,33 +568,10 @@ pub const Iterator = struct { |
| 527 | 568 | defer out_file.close(io); |
| 528 | 569 | var out_file_buffer: [1024]u8 = undefined; |
| 529 | 570 | var file_writer = out_file.writer(io, &out_file_buffer); |
| 530 | | const local_data_file_offset: u64 = |
| 531 | | @as(u64, self.file_offset) + |
| 532 | | @as(u64, @sizeOf(LocalFileHeader)) + |
| 533 | | local_data_header_offset; |
| 534 | | try stream.seekTo(local_data_file_offset); |
| 535 | | |
| 536 | | // TODO limit based on self.compressed_size |
| 537 | | |
| 538 | | switch (self.compression_method) { |
| 539 | | .store => { |
| 540 | | stream.interface.streamExact64(&file_writer.interface, self.uncompressed_size) catch |err| switch (err) { |
| 541 | | error.ReadFailed => return stream.err.?, |
| 542 | | error.WriteFailed => return file_writer.err.?, |
| 543 | | error.EndOfStream => return error.ZipDecompressTruncated, |
| 544 | | }; |
| 545 | | }, |
| 546 | | .deflate => { |
| 547 | | var flate_buffer: [flate.max_window_len]u8 = undefined; |
| 548 | | var decompress: flate.Decompress = .init(&stream.interface, .raw, &flate_buffer); |
| 549 | | decompress.reader.streamExact64(&file_writer.interface, self.uncompressed_size) catch |err| switch (err) { |
| 550 | | error.ReadFailed => return stream.err.?, |
| 551 | | error.WriteFailed => return file_writer.err orelse decompress.err.?, |
| 552 | | error.EndOfStream => return error.ZipDecompressTruncated, |
| 553 | | }; |
| 554 | | }, |
| 555 | | else => return error.UnsupportedCompressionMethod, |
| 556 | | } |
| 571 | self.extractTo(stream, &file_writer.interface) catch |err| switch (err) { |
| 572 | error.WriteFailed => |e| return file_writer.err orelse e, |
| 573 | else => return err, |
| 574 | }; |
| 557 | 575 | try file_writer.end(); |
| 558 | 576 | } |
| 559 | 577 | }; |