rubyzip 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- checksums.yaml +6 -14
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/lib/zip.rb +7 -2
- data/lib/zip/central_directory.rb +41 -13
- data/lib/zip/constants.rb +2 -0
- data/lib/zip/entry.rb +72 -20
- data/lib/zip/entry_set.rb +10 -9
- data/lib/zip/extra_field.rb +10 -2
- data/lib/zip/extra_field/zip64.rb +42 -5
- data/lib/zip/extra_field/zip64_placeholder.rb +16 -0
- data/lib/zip/file.rb +62 -56
- data/lib/zip/filesystem.rb +8 -8
- data/lib/zip/inflater.rb +3 -3
- data/lib/zip/input_stream.rb +77 -66
- data/lib/zip/ioextras/abstract_input_stream.rb +6 -3
- data/lib/zip/null_decompressor.rb +2 -2
- data/lib/zip/null_input_stream.rb +2 -1
- data/lib/zip/output_stream.rb +9 -9
- data/lib/zip/pass_thru_decompressor.rb +2 -2
- data/lib/zip/version.rb +1 -1
- metadata +6 -6
- data/NEWS +0 -182
@@ -2,10 +2,13 @@ module Zip
|
|
2
2
|
# Info-ZIP Extra for Zip64 size
|
3
3
|
class ExtraField::Zip64 < ExtraField::Generic
|
4
4
|
attr_accessor :original_size, :compressed_size, :relative_header_offset, :disk_start_number
|
5
|
-
HEADER_ID =
|
5
|
+
HEADER_ID = ['0100'].pack('H*')
|
6
6
|
register_map
|
7
7
|
|
8
8
|
def initialize(binstr = nil)
|
9
|
+
@content = nil # unparsed binary; we don't actually know what this contains
|
10
|
+
# without looking for FFs in the associated file header
|
11
|
+
# call parse after initializing with a binary string
|
9
12
|
@original_size = nil
|
10
13
|
@compressed_size = nil
|
11
14
|
@relative_header_offset = nil
|
@@ -13,18 +16,52 @@ module Zip
|
|
13
16
|
binstr and merge(binstr)
|
14
17
|
end
|
15
18
|
|
19
|
+
def ==(other)
|
20
|
+
other.original_size == @original_size &&
|
21
|
+
other.compressed_size == @compressed_size &&
|
22
|
+
other.relative_header_offset == @relative_header_offset &&
|
23
|
+
other.disk_start_number == @disk_start_number
|
24
|
+
end
|
25
|
+
|
16
26
|
def merge(binstr)
|
17
27
|
return if binstr.empty?
|
18
|
-
|
28
|
+
_, @content = initial_parse(binstr)
|
29
|
+
end
|
30
|
+
|
31
|
+
# pass the values from the base entry (if applicable)
|
32
|
+
# wider values are only present in the extra field for base values set to all FFs
|
33
|
+
# returns the final values for the four attributes (from the base or zip64 extra record)
|
34
|
+
def parse(original_size, compressed_size, relative_header_offset = nil, disk_start_number = nil)
|
35
|
+
@original_size = extract(8, 'Q<') if original_size == 0xFFFFFFFF
|
36
|
+
@compressed_size = extract(8, 'Q<') if compressed_size == 0xFFFFFFFF
|
37
|
+
@relative_header_offset = extract(8, 'Q<') if relative_header_offset && relative_header_offset == 0xFFFFFFFF
|
38
|
+
@disk_start_number = extract(4, 'V') if disk_start_number && disk_start_number == 0xFFFF
|
39
|
+
@content = nil
|
40
|
+
[@original_size || original_size,
|
41
|
+
@compressed_size || compressed_size,
|
42
|
+
@relative_header_offset || relative_header_offset,
|
43
|
+
@disk_start_number || disk_start_number]
|
44
|
+
end
|
45
|
+
|
46
|
+
def extract(size, format)
|
47
|
+
@content.slice!(0, size).unpack(format)[0]
|
19
48
|
end
|
49
|
+
private :extract
|
20
50
|
|
21
51
|
def pack_for_local
|
22
|
-
|
23
|
-
|
52
|
+
# local header entries must contain original size and compressed size; other fields do not apply
|
53
|
+
return '' unless @original_size && @compressed_size
|
54
|
+
[@original_size, @compressed_size].pack("Q<Q<")
|
24
55
|
end
|
25
56
|
|
26
57
|
def pack_for_c_dir
|
27
|
-
|
58
|
+
# central directory entries contain only fields that didn't fit in the main entry part
|
59
|
+
packed = ''.force_encoding('BINARY')
|
60
|
+
packed << [@original_size].pack("Q<") if @original_size
|
61
|
+
packed << [@compressed_size].pack("Q<") if @compressed_size
|
62
|
+
packed << [@relative_header_offset].pack("Q<") if @relative_header_offset
|
63
|
+
packed << [@disk_start_number].pack("V") if @disk_start_number
|
64
|
+
packed
|
28
65
|
end
|
29
66
|
end
|
30
67
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Zip
|
2
|
+
# placeholder to reserve space for a Zip64 extra information record, for the
|
3
|
+
# local file header only, that we won't know if we'll need until after
|
4
|
+
# we write the file data
|
5
|
+
class ExtraField::Zip64Placeholder < ExtraField::Generic
|
6
|
+
HEADER_ID = ['9999'].pack('H*') # this ID is used by other libraries such as .NET's Ionic.zip
|
7
|
+
register_map
|
8
|
+
|
9
|
+
def initialize(binstr = nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def pack_for_local
|
13
|
+
"\x00" * 16
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/zip/file.rb
CHANGED
@@ -44,12 +44,12 @@ module Zip
|
|
44
44
|
|
45
45
|
class File < CentralDirectory
|
46
46
|
|
47
|
-
CREATE
|
48
|
-
SPLIT_SIGNATURE
|
47
|
+
CREATE = 1
|
48
|
+
SPLIT_SIGNATURE = 0x08074b50
|
49
49
|
ZIP64_EOCD_SIGNATURE = 0x06064b50
|
50
|
-
MAX_SEGMENT_SIZE
|
51
|
-
MIN_SEGMENT_SIZE
|
52
|
-
DATA_BUFFER_SIZE
|
50
|
+
MAX_SEGMENT_SIZE = 3221225472
|
51
|
+
MIN_SEGMENT_SIZE = 65536
|
52
|
+
DATA_BUFFER_SIZE = 8192
|
53
53
|
|
54
54
|
attr_reader :name
|
55
55
|
|
@@ -64,35 +64,36 @@ module Zip
|
|
64
64
|
|
65
65
|
# Opens a zip archive. Pass true as the second parameter to create
|
66
66
|
# a new archive if it doesn't exist already.
|
67
|
-
def initialize(
|
67
|
+
def initialize(file_name, create = nil, buffer = false, options = {})
|
68
68
|
super()
|
69
|
-
@name =
|
69
|
+
@name = file_name
|
70
70
|
@comment = ''
|
71
|
-
@create
|
71
|
+
@create = create
|
72
72
|
case
|
73
|
-
when ::File.exists?(
|
73
|
+
when ::File.exists?(file_name) && !buffer
|
74
74
|
@create = nil
|
75
|
+
@exist_file_perms = ::File.stat(file_name).mode
|
75
76
|
::File.open(name, 'rb') do |f|
|
76
77
|
read_from_stream(f)
|
77
78
|
end
|
78
79
|
when create
|
79
80
|
@entry_set = EntrySet.new
|
80
81
|
else
|
81
|
-
raise ZipError, "File #{
|
82
|
+
raise ZipError, "File #{file_name} not found"
|
82
83
|
end
|
83
|
-
@
|
84
|
-
@
|
85
|
-
@restore_ownership = false
|
86
|
-
@restore_permissions =
|
87
|
-
@restore_times = true
|
84
|
+
@stored_entries = @entry_set.dup
|
85
|
+
@stored_comment = @comment
|
86
|
+
@restore_ownership = options[:restore_ownership] || false
|
87
|
+
@restore_permissions = options[:restore_permissions] || true
|
88
|
+
@restore_times = options[:restore_times] || true
|
88
89
|
end
|
89
90
|
|
90
91
|
class << self
|
91
92
|
# Same as #new. If a block is passed the ZipFile object is passed
|
92
93
|
# to the block and is automatically closed afterwards just as with
|
93
94
|
# ruby's builtin File.open method.
|
94
|
-
def open(
|
95
|
-
zf = ::Zip::File.new(
|
95
|
+
def open(file_name, create = nil)
|
96
|
+
zf = ::Zip::File.new(file_name, create)
|
96
97
|
if block_given?
|
97
98
|
begin
|
98
99
|
yield zf
|
@@ -115,12 +116,12 @@ module Zip
|
|
115
116
|
# stream, and outputs data to a buffer.
|
116
117
|
# (This can be used to extract data from a
|
117
118
|
# downloaded zip archive without first saving it to disk.)
|
118
|
-
def open_buffer(io)
|
119
|
-
unless io.is_a?(IO)
|
120
|
-
raise "Zip::
|
119
|
+
def open_buffer(io, options = {})
|
120
|
+
unless io.is_a?(IO) || io.is_a?(String)
|
121
|
+
raise "Zip::File.open_buffer expects an argument of class String or IO. Found: #{io.class}"
|
121
122
|
end
|
122
|
-
zf = ::Zip::File.new('', true, true)
|
123
|
-
if io.is_a(::String)
|
123
|
+
zf = ::Zip::File.new('', true, true, options)
|
124
|
+
if io.is_a?(::String)
|
124
125
|
require 'stringio'
|
125
126
|
io = ::StringIO.new(io)
|
126
127
|
end
|
@@ -173,8 +174,8 @@ module Zip
|
|
173
174
|
# TODO: Make the code more understandable
|
174
175
|
#
|
175
176
|
def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
|
176
|
-
ssegment_size
|
177
|
-
ssegment_size
|
177
|
+
ssegment_size = zip_file_size - zip_file.pos
|
178
|
+
ssegment_size = segment_size if ssegment_size > segment_size
|
178
179
|
szip_file_name = "#{partial_zip_file_name}.#{'%03d'%(szip_file_index)}"
|
179
180
|
::File.open(szip_file_name, 'wb') do |szip_file|
|
180
181
|
if szip_file_index == 1
|
@@ -224,11 +225,18 @@ module Zip
|
|
224
225
|
get_entry(entry).get_input_stream(&aProc)
|
225
226
|
end
|
226
227
|
|
227
|
-
# Returns an output stream to the specified entry. If
|
228
|
-
#
|
229
|
-
#
|
230
|
-
|
231
|
-
|
228
|
+
# Returns an output stream to the specified entry. If entry is not an instance
|
229
|
+
# of Zip::Entry, a new Zip::Entry will be initialized using the arguments
|
230
|
+
# specified. If a block is passed the stream object is passed to the block and
|
231
|
+
# the stream is automatically closed afterwards just as with ruby's builtin
|
232
|
+
# File.open method.
|
233
|
+
def get_output_stream(entry, permissionInt = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
|
234
|
+
newEntry =
|
235
|
+
if entry.kind_of?(Entry)
|
236
|
+
entry
|
237
|
+
else
|
238
|
+
Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
|
239
|
+
end
|
232
240
|
if newEntry.directory?
|
233
241
|
raise ArgumentError,
|
234
242
|
"cannot open stream to directory entry - '#{newEntry}'"
|
@@ -291,30 +299,25 @@ module Zip
|
|
291
299
|
# the zip archive.
|
292
300
|
def commit
|
293
301
|
return if !commit_required?
|
294
|
-
on_success_replace
|
295
|
-
|
296
|
-
|
297
|
-
|zos|
|
298
|
-
|
299
|
-
@entry_set.each {
|
300
|
-
|e|
|
302
|
+
on_success_replace do |tmpFile|
|
303
|
+
::Zip::OutputStream.open(tmpFile) do |zos|
|
304
|
+
@entry_set.each do |e|
|
301
305
|
e.write_to_zip_output_stream(zos)
|
302
306
|
e.dirty = false
|
303
|
-
|
307
|
+
end
|
304
308
|
zos.comment = comment
|
305
|
-
|
309
|
+
end
|
306
310
|
true
|
307
|
-
|
311
|
+
end
|
308
312
|
initialize(name)
|
309
313
|
end
|
310
314
|
|
311
315
|
# Write buffer write changes to buffer and return
|
312
316
|
def write_buffer
|
313
|
-
|
317
|
+
OutputStream.write_buffer do |zos|
|
314
318
|
@entry_set.each { |e| e.write_to_zip_output_stream(zos) }
|
315
319
|
zos.comment = comment
|
316
320
|
end
|
317
|
-
return buffer
|
318
321
|
end
|
319
322
|
|
320
323
|
# Closes the zip file committing any changes that has been made.
|
@@ -328,7 +331,7 @@ module Zip
|
|
328
331
|
@entry_set.each do |e|
|
329
332
|
return true if e.dirty
|
330
333
|
end
|
331
|
-
@comment != @
|
334
|
+
@comment != @stored_comment || @entry_set != @stored_entries || @create == File::CREATE
|
332
335
|
end
|
333
336
|
|
334
337
|
# Searches for entry with the specified name. Returns nil if
|
@@ -345,14 +348,14 @@ module Zip
|
|
345
348
|
# Searches for an entry just as find_entry, but throws Errno::ENOENT
|
346
349
|
# if no entry is found.
|
347
350
|
def get_entry(entry)
|
348
|
-
|
349
|
-
unless
|
351
|
+
selected_entry = find_entry(entry)
|
352
|
+
unless selected_entry
|
350
353
|
raise Errno::ENOENT, entry
|
351
354
|
end
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
355
|
+
selected_entry.restore_ownership = @restore_ownership
|
356
|
+
selected_entry.restore_permissions = @restore_permissions
|
357
|
+
selected_entry.restore_times = @restore_times
|
358
|
+
selected_entry
|
356
359
|
end
|
357
360
|
|
358
361
|
# Creates a directory
|
@@ -362,7 +365,7 @@ module Zip
|
|
362
365
|
end
|
363
366
|
entryName = entryName.dup.to_s
|
364
367
|
entryName << '/' unless entryName.end_with?('/')
|
365
|
-
@entry_set << StreamableDirectory.new(@name, entryName, nil, permissionInt)
|
368
|
+
@entry_set << ::Zip::StreamableDirectory.new(@name, entryName, nil, permissionInt)
|
366
369
|
end
|
367
370
|
|
368
371
|
private
|
@@ -397,19 +400,22 @@ module Zip
|
|
397
400
|
end
|
398
401
|
end
|
399
402
|
|
400
|
-
def on_success_replace
|
403
|
+
def on_success_replace
|
401
404
|
tmpfile = get_tempfile
|
402
|
-
|
405
|
+
tmp_filename = tmpfile.path
|
403
406
|
tmpfile.close
|
404
|
-
if yield
|
405
|
-
::File.rename(
|
407
|
+
if yield tmp_filename
|
408
|
+
::File.rename(tmp_filename, self.name)
|
409
|
+
if defined?(@exist_file_perms)
|
410
|
+
::File.chmod(@exist_file_perms, self.name)
|
411
|
+
end
|
406
412
|
end
|
407
413
|
end
|
408
414
|
|
409
415
|
def get_tempfile
|
410
|
-
|
411
|
-
|
412
|
-
|
416
|
+
temp_file = Tempfile.new(::File.basename(name), ::File.dirname(name))
|
417
|
+
temp_file.binmode
|
418
|
+
temp_file
|
413
419
|
end
|
414
420
|
|
415
421
|
end
|
data/lib/zip/filesystem.rb
CHANGED
@@ -6,8 +6,8 @@ module Zip
|
|
6
6
|
# a zip archive that is similar to ruby's builtin File and Dir
|
7
7
|
# classes.
|
8
8
|
#
|
9
|
-
# Requiring 'zip/
|
10
|
-
# making the methods in this module available on
|
9
|
+
# Requiring 'zip/filesystem' includes this module in Zip::File
|
10
|
+
# making the methods in this module available on Zip::File objects.
|
11
11
|
#
|
12
12
|
# Using this API the following example creates a new zip file
|
13
13
|
# <code>my.zip</code> containing a normal entry with the name
|
@@ -16,7 +16,7 @@ module Zip
|
|
16
16
|
#
|
17
17
|
# require 'zip/filesystem'
|
18
18
|
#
|
19
|
-
# Zip::File.open("my.zip", Zip::
|
19
|
+
# Zip::File.open("my.zip", Zip::File::CREATE) {
|
20
20
|
# |zipfile|
|
21
21
|
# zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" }
|
22
22
|
# zipfile.dir.mkdir("mydir")
|
@@ -45,22 +45,22 @@ module Zip
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Returns a ZipFsDir which is much like ruby's builtin Dir (class)
|
48
|
-
# object, except it works on the
|
48
|
+
# object, except it works on the Zip::File on which this method is
|
49
49
|
# invoked
|
50
50
|
def dir
|
51
51
|
@zipFsDir
|
52
52
|
end
|
53
53
|
|
54
54
|
# Returns a ZipFsFile which is much like ruby's builtin File (class)
|
55
|
-
# object, except it works on the
|
55
|
+
# object, except it works on the Zip::File on which this method is
|
56
56
|
# invoked
|
57
57
|
def file
|
58
58
|
@zipFsFile
|
59
59
|
end
|
60
60
|
|
61
61
|
# Instances of this class are normally accessed via the accessor
|
62
|
-
#
|
63
|
-
# builtin File (class) object, except it works on
|
62
|
+
# Zip::File::file. An instance of ZipFsFile behaves like ruby's
|
63
|
+
# builtin File (class) object, except it works on Zip::File entries.
|
64
64
|
#
|
65
65
|
# The individual methods are not documented due to their
|
66
66
|
# similarity with the methods in File
|
@@ -543,7 +543,7 @@ module Zip
|
|
543
543
|
end
|
544
544
|
end
|
545
545
|
|
546
|
-
# All access to
|
546
|
+
# All access to Zip::File from ZipFsFile and ZipFsDir goes through a
|
547
547
|
# ZipFileNameMapper, which has one responsibility: ensure
|
548
548
|
class ZipFileNameMapper # :nodoc:all
|
549
549
|
include Enumerable
|
data/lib/zip/inflater.rb
CHANGED
@@ -7,9 +7,9 @@ module Zip
|
|
7
7
|
@has_returned_empty_string = false
|
8
8
|
end
|
9
9
|
|
10
|
-
def sysread(number_of_bytes = nil, buf =
|
10
|
+
def sysread(number_of_bytes = nil, buf = '')
|
11
11
|
readEverything = number_of_bytes.nil?
|
12
|
-
while
|
12
|
+
while readEverything || @output_buffer.bytesize < number_of_bytes
|
13
13
|
break if internal_input_finished?
|
14
14
|
@output_buffer << internal_produce_input(buf)
|
15
15
|
end
|
@@ -37,7 +37,7 @@ module Zip
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
def internal_produce_input(buf =
|
40
|
+
def internal_produce_input(buf = '')
|
41
41
|
retried = 0
|
42
42
|
begin
|
43
43
|
@zlib_inflater.inflate(@input_stream.read(Decompressor::CHUNK_SIZE, buf))
|
data/lib/zip/input_stream.rb
CHANGED
@@ -1,41 +1,39 @@
|
|
1
1
|
module Zip
|
2
|
-
#
|
3
|
-
# zip file. It is possible to create a
|
2
|
+
# InputStream is the basic class for reading zip entries in a
|
3
|
+
# zip file. It is possible to create a InputStream object directly,
|
4
4
|
# passing the zip file name to the constructor, but more often than not
|
5
|
-
# the
|
5
|
+
# the InputStream will be obtained from a File (perhaps using the
|
6
6
|
# ZipFileSystem interface) object for a particular entry in the zip
|
7
7
|
# archive.
|
8
8
|
#
|
9
|
-
# A
|
9
|
+
# A InputStream inherits IOExtras::AbstractInputStream in order
|
10
10
|
# to provide an IO-like interface for reading from a single zip
|
11
11
|
# entry. Beyond methods for mimicking an IO-object it contains
|
12
12
|
# the method get_next_entry for iterating through the entries of
|
13
|
-
# an archive. get_next_entry returns a
|
14
|
-
# the zip entry the
|
13
|
+
# an archive. get_next_entry returns a Entry object that describes
|
14
|
+
# the zip entry the InputStream is currently reading from.
|
15
15
|
#
|
16
16
|
# Example that creates a zip archive with ZipOutputStream and reads it
|
17
|
-
# back again with a
|
17
|
+
# back again with a InputStream.
|
18
18
|
#
|
19
|
-
# require 'zip
|
19
|
+
# require 'zip'
|
20
20
|
#
|
21
|
-
# Zip::
|
22
|
-
# |io|
|
21
|
+
# Zip::OutputStream.open("my.zip") do |io|
|
23
22
|
#
|
24
23
|
# io.put_next_entry("first_entry.txt")
|
25
24
|
# io.write "Hello world!"
|
26
25
|
#
|
27
26
|
# io.put_next_entry("adir/first_entry.txt")
|
28
27
|
# io.write "Hello again!"
|
29
|
-
#
|
28
|
+
# end
|
30
29
|
#
|
31
30
|
#
|
32
|
-
# Zip::
|
33
|
-
# |io|
|
31
|
+
# Zip::InputStream.open("my.zip") do |io|
|
34
32
|
#
|
35
33
|
# while (entry = io.get_next_entry)
|
36
34
|
# puts "Contents of #{entry.name}: '#{io.read}'"
|
37
35
|
# end
|
38
|
-
#
|
36
|
+
# end
|
39
37
|
#
|
40
38
|
# java.util.zip.ZipInputStream is the original inspiration for this
|
41
39
|
# class.
|
@@ -46,88 +44,101 @@ module Zip
|
|
46
44
|
# Opens the indicated zip file. An exception is thrown
|
47
45
|
# if the specified offset in the specified filename is
|
48
46
|
# not a local zip entry header.
|
49
|
-
|
47
|
+
#
|
48
|
+
# @param context [String||IO||StringIO] file path or IO/StringIO object
|
49
|
+
# @param offset [Integer] offset in the IO/StringIO
|
50
|
+
def initialize(context, offset = 0)
|
50
51
|
super()
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
@archiveIO = io
|
56
|
-
end
|
57
|
-
@decompressor = NullDecompressor.instance
|
58
|
-
@currentEntry = nil
|
59
|
-
end
|
60
|
-
|
61
|
-
def close
|
62
|
-
@archiveIO.close
|
63
|
-
end
|
64
|
-
|
65
|
-
# Same as #initialize but if a block is passed the opened
|
66
|
-
# stream is passed to the block and closed when the block
|
67
|
-
# returns.
|
68
|
-
def InputStream.open(filename)
|
69
|
-
return new(filename) unless block_given?
|
70
|
-
|
71
|
-
zio = new(filename)
|
72
|
-
yield zio
|
73
|
-
ensure
|
74
|
-
zio.close if zio
|
52
|
+
@archive_io = get_io(context, offset)
|
53
|
+
@decompressor = ::Zip::NullDecompressor
|
54
|
+
@current_entry = nil
|
75
55
|
end
|
76
56
|
|
77
|
-
def
|
78
|
-
|
79
|
-
zio = new('',0,io)
|
80
|
-
yield zio
|
81
|
-
ensure
|
82
|
-
zio.close if zio
|
57
|
+
def close
|
58
|
+
@archive_io.close
|
83
59
|
end
|
84
60
|
|
85
|
-
# Returns a
|
86
|
-
# method on a newly created
|
61
|
+
# Returns a Entry object. It is necessary to call this
|
62
|
+
# method on a newly created InputStream before reading from
|
87
63
|
# the first entry in the archive. Returns nil when there are
|
88
64
|
# no more entries.
|
89
|
-
|
90
65
|
def get_next_entry
|
91
|
-
@
|
66
|
+
@archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry
|
92
67
|
open_entry
|
93
68
|
end
|
94
69
|
|
95
70
|
# Rewinds the stream to the beginning of the current entry
|
96
71
|
def rewind
|
97
|
-
return if @
|
72
|
+
return if @current_entry.nil?
|
98
73
|
@lineno = 0
|
99
|
-
@pos
|
100
|
-
@
|
101
|
-
IO::SEEK_SET)
|
74
|
+
@pos = 0
|
75
|
+
@archive_io.seek(@current_entry.local_header_offset, IO::SEEK_SET)
|
102
76
|
open_entry
|
103
77
|
end
|
104
78
|
|
105
79
|
# Modeled after IO.sysread
|
106
|
-
def sysread(
|
107
|
-
@decompressor.sysread(
|
80
|
+
def sysread(number_of_bytes = nil, buf = nil)
|
81
|
+
@decompressor.sysread(number_of_bytes, buf)
|
108
82
|
end
|
109
83
|
|
110
84
|
def eof
|
111
85
|
@output_buffer.empty? && @decompressor.eof
|
112
86
|
end
|
87
|
+
|
113
88
|
alias :eof? :eof
|
114
89
|
|
90
|
+
class << self
|
91
|
+
# Same as #initialize but if a block is passed the opened
|
92
|
+
# stream is passed to the block and closed when the block
|
93
|
+
# returns.
|
94
|
+
def open(filename_or_io, offset = 0)
|
95
|
+
zio = self.new(filename_or_io, offset)
|
96
|
+
return zio unless block_given?
|
97
|
+
begin
|
98
|
+
yield zio
|
99
|
+
ensure
|
100
|
+
zio.close if zio
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def open_buffer(filename_or_io, offset = 0)
|
105
|
+
puts "open_buffer is deprecated!!! Use open instead!"
|
106
|
+
self.open(filename_or_io, offset)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
115
110
|
protected
|
116
111
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
elsif @currentEntry.compression_method == Entry::STORED
|
122
|
-
@decompressor = PassThruDecompressor.new(@archiveIO, @currentEntry.size)
|
123
|
-
elsif @currentEntry.compression_method == Entry::DEFLATED
|
124
|
-
@decompressor = Inflater.new(@archiveIO)
|
112
|
+
def get_io(io_or_file, offset = 0)
|
113
|
+
case io_or_file
|
114
|
+
when IO, StringIO
|
115
|
+
io_or_file
|
125
116
|
else
|
126
|
-
|
127
|
-
|
117
|
+
file = ::File.open(io_or_file, 'rb')
|
118
|
+
file.seek(offset, ::IO::SEEK_SET)
|
119
|
+
file
|
128
120
|
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def open_entry
|
124
|
+
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
125
|
+
@decompressor = get_decompressor
|
129
126
|
flush
|
130
|
-
|
127
|
+
@current_entry
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_decompressor
|
131
|
+
case
|
132
|
+
when @current_entry.nil?
|
133
|
+
::Zip::NullDecompressor
|
134
|
+
when @current_entry.compression_method == ::Zip::Entry::STORED
|
135
|
+
::Zip::PassThruDecompressor.new(@archive_io, @current_entry.size)
|
136
|
+
when @current_entry.compression_method == ::Zip::Entry::DEFLATED
|
137
|
+
::Zip::Inflater.new(@archive_io)
|
138
|
+
else
|
139
|
+
raise ZipCompressionMethodError,
|
140
|
+
"Unsupported compression method #{@current_entry.compression_method}"
|
141
|
+
end
|
131
142
|
end
|
132
143
|
|
133
144
|
def produce_input
|