omnizip 0.3.20 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/TODO.refactor/00-overview.md +2 -2
- data/TODO.refactor/07-respond-to-replacement.md +225 -79
- data/lib/omnizip/algorithm.rb +2 -2
- data/lib/omnizip/algorithms/bzip2.rb +1 -0
- data/lib/omnizip/algorithms/deflate.rb +1 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +3 -3
- data/lib/omnizip/algorithms/lzma.rb +11 -5
- data/lib/omnizip/algorithms/lzma2/encoder.rb +2 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +2 -0
- data/lib/omnizip/algorithms/lzma2.rb +2 -0
- data/lib/omnizip/algorithms/ppmd7.rb +2 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +2 -0
- data/lib/omnizip/algorithms/registration.rb +1 -1
- data/lib/omnizip/algorithms/zstandard/encoder.rb +57 -18
- data/lib/omnizip/algorithms/zstandard/match_finder.rb +9 -5
- data/lib/omnizip/algorithms/zstandard.rb +1 -0
- data/lib/omnizip/chunked/writer.rb +3 -0
- data/lib/omnizip/commands/archive_extract_command.rb +3 -1
- data/lib/omnizip/commands/archive_list_command.rb +4 -3
- data/lib/omnizip/commands/profile_show_command.rb +2 -0
- data/lib/omnizip/convenience.rb +2 -0
- data/lib/omnizip/extraction/filter_chain.rb +4 -0
- data/lib/omnizip/extraction/selective_extractor.rb +7 -0
- data/lib/omnizip/file_type.rb +4 -0
- data/lib/omnizip/formats/ole.rb +2 -0
- data/lib/omnizip/formats/rar/decompressor.rb +1 -0
- data/lib/omnizip/formats/rar/license_validator.rb +2 -0
- data/lib/omnizip/formats/rar/reader.rb +1 -2
- data/lib/omnizip/formats/rpm.rb +1 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +8 -0
- data/lib/omnizip/formats/tar/reader.rb +2 -0
- data/lib/omnizip/formats/xar/reader.rb +2 -0
- data/lib/omnizip/formats/xar/writer.rb +1 -0
- data/lib/omnizip/formats/xz.rb +2 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +3 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +8 -0
- data/lib/omnizip/formats/zip/reader.rb +2 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +1 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +4 -9
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +1 -16
- data/lib/omnizip/io/buffered_input.rb +1 -0
- data/lib/omnizip/io/buffered_output.rb +1 -0
- data/lib/omnizip/io/source.rb +4 -0
- data/lib/omnizip/io/stream_manager.rb +3 -1
- data/lib/omnizip/link_handler.rb +2 -0
- data/lib/omnizip/metadata/metadata_validator.rb +2 -0
- data/lib/omnizip/models/filter_config.rb +2 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip/zip/file.rb +1 -1
- data/lib/omnizip/zip/input_stream.rb +2 -1
- metadata +1 -1
|
@@ -178,15 +178,18 @@ module Omnizip
|
|
|
178
178
|
limit = block_end - mm
|
|
179
179
|
|
|
180
180
|
while ip < limit
|
|
181
|
-
match = if ldm
|
|
181
|
+
match = if ldm || lazy.positive?
|
|
182
|
+
# Lazy levels share the LDM loop's rep0 fast-path
|
|
183
|
+
# (with backward extension): without it, levels
|
|
184
|
+
# 6+ measured worse than the greedy default level
|
|
185
|
+
# because every rep-offset match paid full offset
|
|
186
|
+
# coding.
|
|
182
187
|
find_match_ldm(src, ip, ms, mm, limit, anchor,
|
|
183
188
|
seq_store.rep_offsets[0], ldm,
|
|
184
189
|
max_distance)
|
|
185
|
-
|
|
190
|
+
else
|
|
186
191
|
find_greedy_match(src, ip, ms, mm, limit, anchor,
|
|
187
192
|
seq_store.rep_offsets[0])
|
|
188
|
-
else
|
|
189
|
-
find_best_match(src, ip, ms, mm, limit)
|
|
190
193
|
end
|
|
191
194
|
if match
|
|
192
195
|
dist, len = match
|
|
@@ -247,7 +250,8 @@ module Omnizip
|
|
|
247
250
|
ldm, max_distance)
|
|
248
251
|
return nil unless dist
|
|
249
252
|
|
|
250
|
-
|
|
253
|
+
back = backward_extension(src, ip, anchor, ip - dist)
|
|
254
|
+
[dist, len + back, ip - back]
|
|
251
255
|
end
|
|
252
256
|
|
|
253
257
|
# Greedy-path match search (port of the Rust
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
# See the COPYING file for the complete text of the license.
|
|
17
17
|
#
|
|
18
18
|
|
|
19
|
+
require "fileutils"
|
|
20
|
+
|
|
19
21
|
module Omnizip
|
|
20
22
|
module Commands
|
|
21
23
|
# Command to extract .7z archives to a directory.
|
|
@@ -222,7 +224,7 @@ module Omnizip
|
|
|
222
224
|
|
|
223
225
|
extracted.size
|
|
224
226
|
ensure
|
|
225
|
-
archive
|
|
227
|
+
archive.close if archive.is_a?(Omnizip::Zip::File)
|
|
226
228
|
end
|
|
227
229
|
|
|
228
230
|
def extract_gzip(archive_file, output_dir, verbose)
|
|
@@ -69,7 +69,7 @@ module Omnizip
|
|
|
69
69
|
entries = if patterns || excludes
|
|
70
70
|
filter_entries(archive, patterns, excludes)
|
|
71
71
|
else
|
|
72
|
-
archive.
|
|
72
|
+
archive.entries
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
if count_only
|
|
@@ -91,6 +91,8 @@ module Omnizip
|
|
|
91
91
|
rescue StandardError => e
|
|
92
92
|
raise Omnizip::CompressionError,
|
|
93
93
|
"Failed to list archive: #{e.message}"
|
|
94
|
+
ensure
|
|
95
|
+
archive.close if archive.is_a?(Omnizip::Zip::File)
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
def filter_entries(archive, patterns, excludes)
|
|
@@ -102,8 +104,7 @@ module Omnizip
|
|
|
102
104
|
# Add exclude patterns
|
|
103
105
|
excludes&.each { |pattern| filter.exclude_pattern(pattern) }
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
filter.filter(entries)
|
|
107
|
+
filter.filter(archive.entries)
|
|
107
108
|
end
|
|
108
109
|
|
|
109
110
|
def display_simple_listing_filtered(entries)
|
|
@@ -33,6 +33,8 @@ module Omnizip
|
|
|
33
33
|
puts "Solid: #{profile.solid}"
|
|
34
34
|
puts "Description: #{profile.description}"
|
|
35
35
|
|
|
36
|
+
# ProfileRegistry accepts any CompressionProfile subclass.
|
|
37
|
+
# allowed: a registered subclass may expose base_profile
|
|
36
38
|
return unless profile.respond_to?(:base_profile) && profile.base_profile
|
|
37
39
|
|
|
38
40
|
puts "Based on: #{profile.base_profile.name}"
|
data/lib/omnizip/convenience.rb
CHANGED
|
@@ -123,6 +123,7 @@ module Omnizip
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
handler = Omnizip::ArchiveHandler.for(format)
|
|
126
|
+
# allowed: handler is a registered duck; missing add_entry raises below
|
|
126
127
|
if handler.respond_to?(:add_entry)
|
|
127
128
|
handler.add_entry(archive_path, entry_name, source_path)
|
|
128
129
|
else
|
|
@@ -142,6 +143,7 @@ module Omnizip
|
|
|
142
143
|
def remove_from_archive(archive_path, entry_name, format: DEFAULT_FORMAT)
|
|
143
144
|
require_archive!(archive_path)
|
|
144
145
|
handler = Omnizip::ArchiveHandler.for(format)
|
|
146
|
+
# allowed: handler is a registered duck; missing remove_entry raises below
|
|
145
147
|
unless handler.respond_to?(:remove_entry)
|
|
146
148
|
raise Omnizip::UnsupportedFormatError,
|
|
147
149
|
"Format #{format.inspect} does not support removing entries"
|
|
@@ -159,10 +159,14 @@ module Omnizip
|
|
|
159
159
|
entry
|
|
160
160
|
else
|
|
161
161
|
# Try common filename methods
|
|
162
|
+
# Narrowing this changes which files a filter matches.
|
|
163
|
+
# allowed: entries may be foreign objects
|
|
162
164
|
if entry.respond_to?(:name)
|
|
163
165
|
entry.name
|
|
166
|
+
# allowed: entries may be foreign objects
|
|
164
167
|
elsif entry.respond_to?(:path)
|
|
165
168
|
entry.path
|
|
169
|
+
# allowed: entries may be foreign objects
|
|
166
170
|
elsif entry.respond_to?(:filename)
|
|
167
171
|
entry.filename
|
|
168
172
|
else
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
3
5
|
module Omnizip
|
|
4
6
|
module Extraction
|
|
5
7
|
# Coordinates selective extraction from archives
|
|
@@ -120,8 +122,10 @@ module Omnizip
|
|
|
120
122
|
#
|
|
121
123
|
# @return [Array] All entries
|
|
122
124
|
def list_all
|
|
125
|
+
# allowed: public API takes any archive object, including foreign ones
|
|
123
126
|
if @archive.respond_to?(:entries)
|
|
124
127
|
@archive.entries
|
|
128
|
+
# allowed: public API takes any Enumerable archive
|
|
125
129
|
elsif @archive.respond_to?(:each)
|
|
126
130
|
@archive.to_a
|
|
127
131
|
else
|
|
@@ -150,10 +154,13 @@ module Omnizip
|
|
|
150
154
|
# @param entry [Object] Entry to read
|
|
151
155
|
# @return [String] Entry content
|
|
152
156
|
def read_entry_content(entry)
|
|
157
|
+
# allowed: entries come from a caller-supplied archive
|
|
153
158
|
if entry.respond_to?(:read)
|
|
154
159
|
entry.read
|
|
160
|
+
# allowed: rubyzip-style entries expose get_input_stream, not read
|
|
155
161
|
elsif entry.respond_to?(:get_input_stream)
|
|
156
162
|
entry.get_input_stream.read
|
|
163
|
+
# allowed: some archives read entry content from the archive
|
|
157
164
|
elsif @archive.respond_to?(:read)
|
|
158
165
|
@archive.read(entry)
|
|
159
166
|
else
|
data/lib/omnizip/file_type.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "marcel"
|
|
4
|
+
require "stringio"
|
|
4
5
|
|
|
5
6
|
module Omnizip
|
|
6
7
|
# File type detection module using Marcel for MIME type detection
|
|
@@ -95,16 +96,19 @@ module Omnizip
|
|
|
95
96
|
return nil unless io
|
|
96
97
|
|
|
97
98
|
# Save current position
|
|
99
|
+
# allowed: caller-supplied IO; only saved when the stream tracks one
|
|
98
100
|
original_pos = io.pos if io.respond_to?(:pos)
|
|
99
101
|
|
|
100
102
|
mime_type = Marcel::MimeType.for(io, name: filename)
|
|
101
103
|
|
|
102
104
|
# Restore position
|
|
105
|
+
# allowed: caller-supplied IO capability probe
|
|
103
106
|
io.seek(original_pos) if original_pos && io.respond_to?(:seek)
|
|
104
107
|
|
|
105
108
|
mime_type
|
|
106
109
|
rescue StandardError
|
|
107
110
|
# Attempt to restore position even on error
|
|
111
|
+
# allowed: caller-supplied IO capability probe
|
|
108
112
|
io.seek(original_pos) if original_pos && io.respond_to?(:seek)
|
|
109
113
|
nil
|
|
110
114
|
end
|
data/lib/omnizip/formats/ole.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "stringio"
|
|
4
5
|
|
|
5
6
|
module Omnizip
|
|
6
7
|
module Formats
|
|
@@ -287,8 +288,6 @@ module Omnizip
|
|
|
287
288
|
# @param entry [Models::RarEntry] File entry
|
|
288
289
|
# @return [StringIO] Compressed data stream
|
|
289
290
|
def read_compressed_data(entry)
|
|
290
|
-
require "stringio"
|
|
291
|
-
|
|
292
291
|
# Find the entry's data offset in the archive
|
|
293
292
|
File.open(@file_path, "rb") do |io|
|
|
294
293
|
# Skip signature and headers
|
data/lib/omnizip/formats/rpm.rb
CHANGED
|
@@ -490,7 +490,15 @@ num_files)
|
|
|
490
490
|
|
|
491
491
|
def compress_with_lzma2(data)
|
|
492
492
|
# Use 7-Zip SDK LZMA2 encoder for 7-Zip format
|
|
493
|
+
#
|
|
494
|
+
# The encoder dictionary is sized to the data but never
|
|
495
|
+
# exceeds the size announced in the coder properties
|
|
496
|
+
# (options default 8MB): the decoder allocates its window
|
|
497
|
+
# from the announced size, so a larger encoder dictionary
|
|
498
|
+
# could emit matches reading outside the decoder window.
|
|
499
|
+
announced = @options[:dict_size] || (8 * 1024 * 1024)
|
|
493
500
|
dict_size = [4096, data.bytesize].max
|
|
501
|
+
dict_size = announced if announced < dict_size
|
|
494
502
|
|
|
495
503
|
encoder = Omnizip::Implementations::SevenZip::LZMA2::Encoder.new(
|
|
496
504
|
dict_size: dict_size,
|
data/lib/omnizip/formats/xz.rb
CHANGED
|
@@ -77,6 +77,7 @@ module Omnizip
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def set_encoding(enc)
|
|
80
|
+
# allowed: caller-supplied IO; String streams lack set_encoding
|
|
80
81
|
@stream.set_encoding(enc) if @stream.respond_to?(:set_encoding)
|
|
81
82
|
end
|
|
82
83
|
end
|
|
@@ -110,7 +111,9 @@ module Omnizip
|
|
|
110
111
|
|
|
111
112
|
if ENV["XZ_BLOCK_DEBUG"]
|
|
112
113
|
warn "DEBUG: decode - compressed_size=#{compressed_size.inspect}, check_type=#{@check_type}"
|
|
114
|
+
# allowed: debug output under XZ_BLOCK_DEBUG
|
|
113
115
|
warn "DEBUG: @input.class=#{@input.class}, @input.respond_to?(:pos)=#{@input.respond_to?(:pos)}"
|
|
116
|
+
# allowed: debug output under XZ_BLOCK_DEBUG
|
|
114
117
|
pos = @input.respond_to?(:pos) ? @input.pos : "N/A"
|
|
115
118
|
warn "DEBUG: @input.pos=#{pos}"
|
|
116
119
|
end
|
|
@@ -46,6 +46,7 @@ module Omnizip
|
|
|
46
46
|
|
|
47
47
|
# Store original input and file size for backward_size validation (if available)
|
|
48
48
|
original_input = input
|
|
49
|
+
# allowed: caller-supplied IO; only sized streams get validated
|
|
49
50
|
original_file_size = input.size if input.respond_to?(:size)
|
|
50
51
|
|
|
51
52
|
output, block_count, final_input, block_sizes = decode_blocks(input,
|
|
@@ -57,6 +58,7 @@ module Omnizip
|
|
|
57
58
|
# multiples of four bytes...If the stored value does not match the real size of
|
|
58
59
|
# the Index field, the decoder MUST indicate an error."
|
|
59
60
|
# Reference: /Users/mulgogi/src/external/xz/src/liblzma/common/stream_decoder.c
|
|
61
|
+
# allowed: caller-supplied IO; footer check needs seeking
|
|
60
62
|
if original_input.respond_to?(:seek) && original_file_size&.positive?
|
|
61
63
|
# Use original input and file size for validation
|
|
62
64
|
validate_backward_size_from_footer(original_input,
|
|
@@ -138,6 +140,7 @@ module Omnizip
|
|
|
138
140
|
# @param byte [Integer] Byte to restore
|
|
139
141
|
# @raise [RuntimeError] If IO doesn't support ungetbyte
|
|
140
142
|
def self.restore_byte(input, byte)
|
|
143
|
+
# allowed: caller-supplied IO; FormatError follows when unsupported
|
|
141
144
|
return input.ungetbyte(byte) if input.respond_to?(:ungetbyte)
|
|
142
145
|
|
|
143
146
|
raise FormatError,
|
|
@@ -227,6 +230,7 @@ module Omnizip
|
|
|
227
230
|
# @param check_type [Symbol] Expected checksum type
|
|
228
231
|
# @param index_size [Integer, nil] Actual index size for backward_size validation
|
|
229
232
|
def self.verify_footer_if_seekable(input, check_type, index_size = nil)
|
|
233
|
+
# allowed: caller-supplied IO; skipped on non-seekable streams
|
|
230
234
|
return unless input.respond_to?(:seek) && input.respond_to?(:size) && input.size
|
|
231
235
|
|
|
232
236
|
original_pos = input.pos
|
|
@@ -290,6 +294,7 @@ module Omnizip
|
|
|
290
294
|
# @param input [IO] Input stream
|
|
291
295
|
# @raise [FormatError] If there's invalid trailing data
|
|
292
296
|
def self.verify_no_trailing_data(input)
|
|
297
|
+
# allowed: caller-supplied IO; needs byte-level positioning
|
|
293
298
|
return unless input.respond_to?(:pos) && input.respond_to?(:getbyte)
|
|
294
299
|
|
|
295
300
|
# Skip stream padding (null bytes)
|
|
@@ -304,6 +309,7 @@ module Omnizip
|
|
|
304
309
|
else
|
|
305
310
|
# Non-zero byte found - this should be a new stream or it's invalid
|
|
306
311
|
# Restore the byte and check if it's a valid stream header
|
|
312
|
+
# allowed: caller-supplied IO capability probe
|
|
307
313
|
input.ungetbyte(byte) if input.respond_to?(:ungetbyte)
|
|
308
314
|
|
|
309
315
|
# Stream padding must be a multiple of 4 bytes
|
|
@@ -345,6 +351,7 @@ module Omnizip
|
|
|
345
351
|
end
|
|
346
352
|
|
|
347
353
|
# Restore the bytes we read
|
|
354
|
+
# allowed: caller-supplied IO capability probe
|
|
348
355
|
if input.respond_to?(:ungetbyte)
|
|
349
356
|
potential_header.reverse_each do |b|
|
|
350
357
|
input.ungetbyte(b)
|
|
@@ -381,6 +388,7 @@ module Omnizip
|
|
|
381
388
|
# @raise [FormatError] If backward_size points to invalid position
|
|
382
389
|
def self.validate_backward_size_from_footer(input, file_size,
|
|
383
390
|
_index_size)
|
|
391
|
+
# allowed: caller-supplied IO; skipped on non-seekable streams
|
|
384
392
|
return unless input.respond_to?(:seek)
|
|
385
393
|
return if file_size.nil? || file_size.zero?
|
|
386
394
|
|
|
@@ -121,6 +121,7 @@ module Omnizip
|
|
|
121
121
|
# For raw mode, return actual decode bytes (excluding flush padding)
|
|
122
122
|
if @raw_mode
|
|
123
123
|
[@output.string, @range_encoder.bytes_for_decode]
|
|
124
|
+
# allowed: caller-supplied output; any #string sink buffers
|
|
124
125
|
elsif @output.respond_to?(:string)
|
|
125
126
|
# For File output, just return bytes written (don't try to read back)
|
|
126
127
|
# For StringIO, return the string and its size
|
|
@@ -416,16 +416,11 @@ module Omnizip
|
|
|
416
416
|
|
|
417
417
|
if @lzma2_debug
|
|
418
418
|
warn "DEBUG: decompress_lzma_chunk - After set_input, checking range_decoder..."
|
|
419
|
-
|
|
420
|
-
if
|
|
421
|
-
range_decoder =
|
|
422
|
-
if range_decoder
|
|
423
|
-
warn " range_decoder exists: code=0x#{range_decoder.code.to_s(16)}, range=0x#{range_decoder.range.to_s(16)}, init_bytes_remaining=#{range_decoder.init_bytes_remaining}"
|
|
424
|
-
else
|
|
425
|
-
warn " range_decoder is nil"
|
|
426
|
-
end
|
|
419
|
+
range_decoder = @lzma_decoder.range_decoder
|
|
420
|
+
if range_decoder
|
|
421
|
+
warn " range_decoder exists: code=0x#{range_decoder.code.to_s(16)}, range=0x#{range_decoder.range.to_s(16)}, init_bytes_remaining=#{range_decoder.init_bytes_remaining}"
|
|
427
422
|
else
|
|
428
|
-
warn "
|
|
423
|
+
warn " range_decoder is nil"
|
|
429
424
|
end
|
|
430
425
|
end
|
|
431
426
|
else
|
|
@@ -33,19 +33,6 @@ module Omnizip
|
|
|
33
33
|
#
|
|
34
34
|
# Ported from XZ Utils liblzma/lzma2_encoder.c
|
|
35
35
|
#
|
|
36
|
-
# Compatibility helper for Ruby 3.0-3.1 where String#byteslice doesn't exist
|
|
37
|
-
module StringCompat
|
|
38
|
-
if "".respond_to?(:byteslice)
|
|
39
|
-
def self.byteslice(string, start, length)
|
|
40
|
-
string.byteslice(start, length)
|
|
41
|
-
end
|
|
42
|
-
else
|
|
43
|
-
def self.byteslice(string, start, length)
|
|
44
|
-
string.bytes[start, length]&.pack("C*") || ""
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
36
|
# Constants
|
|
50
37
|
UINT32_MAX = 0xFFFFFFFF
|
|
51
38
|
REPS = 4
|
|
@@ -364,11 +351,9 @@ module Omnizip
|
|
|
364
351
|
|
|
365
352
|
# Write to output stream
|
|
366
353
|
if out_pos.value.positive?
|
|
367
|
-
# Use StringCompat.byteslice for Ruby 3.0-3.1 compatibility
|
|
368
354
|
# Ruby's [] operator has a bug with null bytes that can return extra bytes
|
|
369
355
|
# See: https://bugs.ruby-lang.org/issues/15985
|
|
370
|
-
output.write(
|
|
371
|
-
out_pos.value))
|
|
356
|
+
output.write(temp_buffer.byteslice(0, out_pos.value))
|
|
372
357
|
end
|
|
373
358
|
|
|
374
359
|
# Return the number of bytes written
|
data/lib/omnizip/io/source.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Omnizip
|
|
|
31
31
|
when ::IO, ::StringIO, ::Tempfile then new(input)
|
|
32
32
|
when String then StringSource.new(input)
|
|
33
33
|
else
|
|
34
|
+
# allowed: boundary validation; duck-typing ends at this adapter
|
|
34
35
|
unless input.respond_to?(:read)
|
|
35
36
|
raise ArgumentError,
|
|
36
37
|
"Cannot adapt #{input.inspect} to Omnizip::IO::Source"
|
|
@@ -56,6 +57,7 @@ module Omnizip
|
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def close
|
|
60
|
+
# allowed: wrapped object may be a read-only duck with no close
|
|
59
61
|
@io.close if @io.respond_to?(:close)
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -98,6 +100,7 @@ module Omnizip
|
|
|
98
100
|
when ::IO, ::StringIO, ::Tempfile then new(output)
|
|
99
101
|
when String then PathSink.new(output)
|
|
100
102
|
else
|
|
103
|
+
# allowed: boundary validation; duck-typing ends at this adapter
|
|
101
104
|
unless output.respond_to?(:write)
|
|
102
105
|
raise ArgumentError,
|
|
103
106
|
"Cannot adapt #{output.inspect} to Omnizip::IO::Sink"
|
|
@@ -116,6 +119,7 @@ module Omnizip
|
|
|
116
119
|
end
|
|
117
120
|
|
|
118
121
|
def close
|
|
122
|
+
# allowed: wrapped object may be a write-only duck with no close
|
|
119
123
|
@io.close if @io.respond_to?(:close)
|
|
120
124
|
end
|
|
121
125
|
|
|
@@ -71,13 +71,14 @@ module Omnizip
|
|
|
71
71
|
#
|
|
72
72
|
# @return [void]
|
|
73
73
|
def close
|
|
74
|
-
@source.close if @owned
|
|
74
|
+
@source.close if @owned
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
# Check if source is at end.
|
|
78
78
|
#
|
|
79
79
|
# @return [Boolean] True if at EOF
|
|
80
80
|
def eof?
|
|
81
|
+
# allowed: write-only sources have no eof?
|
|
81
82
|
@source.eof? if @source.respond_to?(:eof?)
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -103,6 +104,7 @@ module Omnizip
|
|
|
103
104
|
end
|
|
104
105
|
|
|
105
106
|
def validate_io_interface(source)
|
|
107
|
+
# allowed: boundary validation of a caller-supplied IO-like object
|
|
106
108
|
if source.respond_to?(:read) || source.respond_to?(:write)
|
|
107
109
|
source
|
|
108
110
|
else
|
data/lib/omnizip/link_handler.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Omnizip
|
|
|
9
9
|
# @raise [ArgumentError] If metadata is invalid
|
|
10
10
|
def validate_entry(metadata)
|
|
11
11
|
validate_comment(metadata.comment) if metadata.comment
|
|
12
|
+
# validate_entry is public API.
|
|
13
|
+
# allowed: metadata need only expose what it validates
|
|
12
14
|
validate_time(metadata.mtime) if metadata.respond_to?(:mtime)
|
|
13
15
|
validate_permissions(metadata.unix_permissions) if metadata.unix_permissions
|
|
14
16
|
|
|
@@ -94,6 +94,8 @@ module Omnizip
|
|
|
94
94
|
# @raise [NotImplementedError] If filter doesn't support id_for_format
|
|
95
95
|
def id_for_format(format)
|
|
96
96
|
filter = filter_instance
|
|
97
|
+
# FilterRegistry accepts any class, with no inheritance check.
|
|
98
|
+
# allowed: keeps NotImplementedError for filters without the method
|
|
97
99
|
if filter.respond_to?(:id_for_format)
|
|
98
100
|
filter.id_for_format(format)
|
|
99
101
|
else
|
data/lib/omnizip/version.rb
CHANGED
data/lib/omnizip/zip/file.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "stringio"
|
|
4
5
|
|
|
5
6
|
module Omnizip
|
|
6
7
|
module Zip
|
|
@@ -102,7 +103,6 @@ module Omnizip
|
|
|
102
103
|
content = read_entry_data(entry)
|
|
103
104
|
|
|
104
105
|
if block
|
|
105
|
-
require "stringio"
|
|
106
106
|
StringIO.open(content, "rb", &block)
|
|
107
107
|
else
|
|
108
108
|
content
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
3
5
|
module Omnizip
|
|
4
6
|
module Zip
|
|
5
7
|
# Rubyzip-compatible InputStream class
|
|
@@ -88,7 +90,6 @@ module Omnizip
|
|
|
88
90
|
@current_entry.compression_method,
|
|
89
91
|
@current_entry.size,
|
|
90
92
|
)
|
|
91
|
-
require "stringio"
|
|
92
93
|
@current_entry_io = StringIO.new(decompressed, "rb")
|
|
93
94
|
end
|
|
94
95
|
|