| ... | @@ -413,15 +413,7 @@ pub const Iterator = struct { | ... | @@ -413,15 +413,7 @@ pub const Iterator = struct { |
| 413 | uncompressed_size: u64, | 413 | uncompressed_size: u64, |
| 414 | file_offset: u64, | 414 | file_offset: u64, |
| 415 | | 415 | |
| 416 | pub fn extract( | 416 | pub fn getFilename(self: Entry, stream: *File.Reader, filename_buf: []u8, options: ExtractOptions) ![]u8 { |
| 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 | | | |
| 425 | if (filename_buf.len < self.filename_len) | 417 | if (filename_buf.len < self.filename_len) |
| 426 | return error.ZipInsufficientBuffer; | 418 | return error.ZipInsufficientBuffer; |
| 427 | switch (self.compression_method) { | 419 | switch (self.compression_method) { |
| ... | @@ -434,6 +426,25 @@ pub const Iterator = struct { | ... | @@ -434,6 +426,25 @@ pub const Iterator = struct { |
| 434 | try stream.interface.readSliceAll(filename); | 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 | const local_data_header_offset: u64 = local_data_header_offset: { | 448 | const local_data_header_offset: u64 = local_data_header_offset: { |
| 438 | const local_header = blk: { | 449 | const local_header = blk: { |
| 439 | try stream.seekTo(self.file_offset); | 450 | try stream.seekTo(self.file_offset); |
| ... | @@ -496,15 +507,45 @@ pub const Iterator = struct { | ... | @@ -496,15 +507,45 @@ pub const Iterator = struct { |
| 496 | @as(u64, local_header.extra_len); | 507 | @as(u64, local_header.extra_len); |
| 497 | }; | 508 | }; |
| 498 | | 509 | |
| 499 | if (options.allow_backslashes) { | 510 | const local_data_file_offset: u64 = |
| 500 | std.mem.replaceScalar(u8, filename, '\\', '/'); | 511 | @as(u64, self.file_offset) + |
| 501 | } else { | 512 | @as(u64, @sizeOf(LocalFileHeader)) + |
| 502 | if (std.mem.findScalar(u8, filename, '\\')) |_| | 513 | local_data_header_offset; |
| 503 | return error.ZipFilenameHasBackslash; | 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)) | 539 | pub fn extract( |
| 507 | return error.ZipBadFilename; | 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 | // All entries that end in '/' are directories | 550 | // All entries that end in '/' are directories |
| 510 | if (filename[filename.len - 1] == '/') { | 551 | if (filename[filename.len - 1] == '/') { |
| ... | @@ -527,33 +568,10 @@ pub const Iterator = struct { | ... | @@ -527,33 +568,10 @@ pub const Iterator = struct { |
| 527 | defer out_file.close(io); | 568 | defer out_file.close(io); |
| 528 | var out_file_buffer: [1024]u8 = undefined; | 569 | var out_file_buffer: [1024]u8 = undefined; |
| 529 | var file_writer = out_file.writer(io, &out_file_buffer); | 570 | var file_writer = out_file.writer(io, &out_file_buffer); |
| 530 | const local_data_file_offset: u64 = | 571 | self.extractTo(stream, &file_writer.interface) catch |err| switch (err) { |
| 531 | @as(u64, self.file_offset) + | 572 | error.WriteFailed => |e| return file_writer.err orelse e, |
| 532 | @as(u64, @sizeOf(LocalFileHeader)) + | 573 | else => return err, |
| 533 | local_data_header_offset; | 574 | }; |
| 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 | } | | |
| 557 | try file_writer.end(); | 575 | try file_writer.end(); |
| 558 | } | 576 | } |
| 559 | }; | 577 | }; |