rubyzip 1.1.7 → 2.3.2
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 +5 -5
- data/README.md +137 -54
- data/Rakefile +6 -4
- data/lib/zip/central_directory.rb +17 -13
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +57 -5
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/null_encryption.rb +4 -6
- data/lib/zip/crypto/traditional_encryption.rb +14 -14
- data/lib/zip/decompressor.rb +22 -4
- data/lib/zip/deflater.rb +8 -6
- data/lib/zip/dos_time.rb +17 -13
- data/lib/zip/entry.rb +171 -148
- data/lib/zip/entry_set.rb +16 -14
- data/lib/zip/errors.rb +3 -0
- data/lib/zip/extra_field/generic.rb +14 -13
- data/lib/zip/extra_field/ntfs.rb +18 -16
- data/lib/zip/extra_field/old_unix.rb +12 -11
- data/lib/zip/extra_field/universal_time.rb +46 -16
- data/lib/zip/extra_field/unix.rb +10 -9
- data/lib/zip/extra_field/zip64.rb +15 -12
- data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
- data/lib/zip/extra_field.rb +18 -16
- data/lib/zip/file.rb +147 -115
- data/lib/zip/filesystem.rb +289 -272
- data/lib/zip/inflater.rb +24 -36
- data/lib/zip/input_stream.rb +44 -28
- data/lib/zip/ioextras/abstract_input_stream.rb +24 -17
- data/lib/zip/ioextras/abstract_output_stream.rb +4 -6
- data/lib/zip/ioextras.rb +2 -4
- data/lib/zip/null_compressor.rb +2 -2
- data/lib/zip/null_decompressor.rb +3 -11
- data/lib/zip/null_input_stream.rb +0 -0
- data/lib/zip/output_stream.rb +25 -17
- data/lib/zip/pass_thru_compressor.rb +6 -6
- data/lib/zip/pass_thru_decompressor.rb +14 -24
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +7 -11
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +15 -6
- data/samples/example.rb +29 -39
- data/samples/example_filesystem.rb +16 -18
- data/samples/example_recursive.rb +31 -25
- data/samples/gtk_ruby_zip.rb +84 -0
- data/samples/qtzip.rb +23 -32
- data/samples/write_simple.rb +10 -13
- data/samples/zipfind.rb +33 -40
- metadata +50 -141
- data/samples/gtkRubyzip.rb +0 -86
- data/test/basic_zip_file_test.rb +0 -64
- data/test/central_directory_entry_test.rb +0 -73
- data/test/central_directory_test.rb +0 -104
- data/test/crypto/null_encryption_test.rb +0 -53
- data/test/crypto/traditional_encryption_test.rb +0 -80
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/globTest.zip +0 -0
- data/test/data/mimetype +0 -1
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/ntfs.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zip64-sample.zip +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +0 -67
- data/test/encryption_test.rb +0 -42
- data/test/entry_set_test.rb +0 -138
- data/test/entry_test.rb +0 -165
- data/test/errors_test.rb +0 -36
- data/test/extra_field_test.rb +0 -78
- data/test/file_extract_directory_test.rb +0 -56
- data/test/file_extract_test.rb +0 -90
- data/test/file_split_test.rb +0 -60
- data/test/file_test.rb +0 -559
- data/test/filesystem/dir_iterator_test.rb +0 -62
- data/test/filesystem/directory_test.rb +0 -131
- data/test/filesystem/file_mutating_test.rb +0 -100
- data/test/filesystem/file_nonmutating_test.rb +0 -514
- data/test/filesystem/file_stat_test.rb +0 -66
- data/test/gentestfiles.rb +0 -134
- data/test/inflater_test.rb +0 -14
- data/test/input_stream_test.rb +0 -170
- data/test/ioextras/abstract_input_stream_test.rb +0 -103
- data/test/ioextras/abstract_output_stream_test.rb +0 -106
- data/test/ioextras/fake_io_test.rb +0 -18
- data/test/local_entry_test.rb +0 -156
- data/test/output_stream_test.rb +0 -129
- data/test/pass_thru_compressor_test.rb +0 -31
- data/test/pass_thru_decompressor_test.rb +0 -15
- data/test/settings_test.rb +0 -92
- data/test/test_helper.rb +0 -228
- data/test/unicode_file_names_and_comments_test.rb +0 -52
- data/test/zip64_full_test.rb +0 -53
- data/test/zip64_support_test.rb +0 -15
data/lib/zip/entry_set.rb
CHANGED
@@ -5,7 +5,7 @@ module Zip
|
|
5
5
|
|
6
6
|
def initialize(an_enumerable = [])
|
7
7
|
super()
|
8
|
-
@entry_set
|
8
|
+
@entry_set = {}
|
9
9
|
an_enumerable.each { |o| push(o) }
|
10
10
|
end
|
11
11
|
|
@@ -18,28 +18,24 @@ module Zip
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def <<(entry)
|
21
|
-
@entry_set[to_key(entry)] = entry
|
21
|
+
@entry_set[to_key(entry)] = entry if entry
|
22
22
|
end
|
23
23
|
|
24
|
-
alias
|
24
|
+
alias push <<
|
25
25
|
|
26
26
|
def size
|
27
27
|
@entry_set.size
|
28
28
|
end
|
29
29
|
|
30
|
-
alias
|
30
|
+
alias length size
|
31
31
|
|
32
32
|
def delete(entry)
|
33
|
-
if @entry_set.delete(to_key(entry))
|
34
|
-
entry
|
35
|
-
else
|
36
|
-
nil
|
37
|
-
end
|
33
|
+
entry if @entry_set.delete(to_key(entry))
|
38
34
|
end
|
39
35
|
|
40
|
-
def each
|
36
|
+
def each
|
41
37
|
@entry_set = sorted_entries.dup.each do |_, value|
|
42
|
-
|
38
|
+
yield(value)
|
43
39
|
end
|
44
40
|
end
|
45
41
|
|
@@ -49,11 +45,12 @@ module Zip
|
|
49
45
|
|
50
46
|
# deep clone
|
51
47
|
def dup
|
52
|
-
EntrySet.new(@entry_set.map
|
48
|
+
EntrySet.new(@entry_set.values.map(&:dup))
|
53
49
|
end
|
54
50
|
|
55
51
|
def ==(other)
|
56
52
|
return false unless other.kind_of?(EntrySet)
|
53
|
+
|
57
54
|
@entry_set.values == other.entry_set.values
|
58
55
|
end
|
59
56
|
|
@@ -61,22 +58,27 @@ module Zip
|
|
61
58
|
@entry_set[to_key(entry.parent_as_string)]
|
62
59
|
end
|
63
60
|
|
64
|
-
def glob(pattern, flags = ::File::FNM_PATHNAME
|
61
|
+
def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB)
|
65
62
|
entries.map do |entry|
|
66
63
|
next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags)
|
64
|
+
|
67
65
|
yield(entry) if block_given?
|
68
66
|
entry
|
69
67
|
end.compact
|
70
68
|
end
|
71
69
|
|
72
70
|
protected
|
71
|
+
|
73
72
|
def sorted_entries
|
74
73
|
::Zip.sort_entries ? Hash[@entry_set.sort] : @entry_set
|
75
74
|
end
|
76
75
|
|
77
76
|
private
|
77
|
+
|
78
78
|
def to_key(entry)
|
79
|
-
entry.to_s.chomp('/')
|
79
|
+
k = entry.to_s.chomp('/')
|
80
|
+
k.downcase! if ::Zip.case_insensitive_match
|
81
|
+
k
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
data/lib/zip/errors.rb
CHANGED
@@ -4,7 +4,10 @@ module Zip
|
|
4
4
|
class DestinationFileExistsError < Error; end
|
5
5
|
class CompressionMethodError < Error; end
|
6
6
|
class EntryNameError < Error; end
|
7
|
+
class EntrySizeError < Error; end
|
7
8
|
class InternalError < Error; end
|
9
|
+
class GPFBit3Error < Error; end
|
10
|
+
class DecompressionError < Error; end
|
8
11
|
|
9
12
|
# Backwards compatibility with v1 (delete in v2)
|
10
13
|
ZipError = Error
|
@@ -1,43 +1,44 @@
|
|
1
1
|
module Zip
|
2
2
|
class ExtraField::Generic
|
3
3
|
def self.register_map
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
return unless const_defined?(:HEADER_ID)
|
5
|
+
|
6
|
+
::Zip::ExtraField::ID_MAP[const_get(:HEADER_ID)] = self
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.name
|
10
|
-
@name ||=
|
10
|
+
@name ||= to_s.split('::')[-1]
|
11
11
|
end
|
12
12
|
|
13
13
|
# return field [size, content] or false
|
14
14
|
def initial_parse(binstr)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
$stderr.puts "Warning: weired extra feild header ID. skip parsing"
|
15
|
+
return false unless binstr
|
16
|
+
|
17
|
+
if binstr[0, 2] != self.class.const_get(:HEADER_ID)
|
18
|
+
warn 'WARNING: weird extra field header ID. Skip parsing it.'
|
20
19
|
return false
|
21
20
|
end
|
22
|
-
|
21
|
+
|
22
|
+
[binstr[2, 2].unpack1('v'), binstr[4..-1]]
|
23
23
|
end
|
24
24
|
|
25
25
|
def ==(other)
|
26
26
|
return false if self.class != other.class
|
27
|
+
|
27
28
|
each do |k, v|
|
28
|
-
v != other[k]
|
29
|
+
return false if v != other[k]
|
29
30
|
end
|
30
31
|
true
|
31
32
|
end
|
32
33
|
|
33
34
|
def to_local_bin
|
34
35
|
s = pack_for_local
|
35
|
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack(
|
36
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s
|
36
37
|
end
|
37
38
|
|
38
39
|
def to_c_dir_bin
|
39
40
|
s = pack_for_c_dir
|
40
|
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack(
|
41
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
data/lib/zip/extra_field/ntfs.rb
CHANGED
@@ -5,33 +5,34 @@ module Zip
|
|
5
5
|
HEADER_ID = [0x000A].pack('v')
|
6
6
|
register_map
|
7
7
|
|
8
|
-
WINDOWS_TICK =
|
9
|
-
SEC_TO_UNIX_EPOCH =
|
8
|
+
WINDOWS_TICK = 10_000_000.0
|
9
|
+
SEC_TO_UNIX_EPOCH = 11_644_473_600
|
10
10
|
|
11
11
|
def initialize(binstr = nil)
|
12
12
|
@ctime = nil
|
13
13
|
@mtime = nil
|
14
14
|
@atime = nil
|
15
|
-
binstr
|
15
|
+
binstr && merge(binstr)
|
16
16
|
end
|
17
17
|
|
18
18
|
attr_accessor :atime, :ctime, :mtime
|
19
19
|
|
20
20
|
def merge(binstr)
|
21
21
|
return if binstr.empty?
|
22
|
+
|
22
23
|
size, content = initial_parse(binstr)
|
23
|
-
(size && content)
|
24
|
+
(size && content) || return
|
24
25
|
|
25
26
|
content = content[4..-1]
|
26
27
|
tags = parse_tags(content)
|
27
28
|
|
28
29
|
tag1 = tags[1]
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
return unless tag1
|
31
|
+
|
32
|
+
ntfs_mtime, ntfs_atime, ntfs_ctime = tag1.unpack('Q<Q<Q<')
|
33
|
+
ntfs_mtime && @mtime ||= from_ntfs_time(ntfs_mtime)
|
34
|
+
ntfs_atime && @atime ||= from_ntfs_time(ntfs_atime)
|
35
|
+
ntfs_ctime && @ctime ||= from_ntfs_time(ntfs_ctime)
|
35
36
|
end
|
36
37
|
|
37
38
|
def ==(other)
|
@@ -48,16 +49,14 @@ module Zip
|
|
48
49
|
# But 7-zip for Windows only stores at central dir
|
49
50
|
def pack_for_c_dir
|
50
51
|
# reserved 0 and tag 1
|
51
|
-
s = [0, 1].pack(
|
52
|
+
s = [0, 1].pack('Vv')
|
52
53
|
|
53
54
|
tag1 = ''.force_encoding(Encoding::BINARY)
|
54
55
|
if @mtime
|
55
56
|
tag1 << [to_ntfs_time(@mtime)].pack('Q<')
|
56
57
|
if @atime
|
57
58
|
tag1 << [to_ntfs_time(@atime)].pack('Q<')
|
58
|
-
if @ctime
|
59
|
-
tag1 << [to_ntfs_time(@ctime)].pack('Q<')
|
60
|
-
end
|
59
|
+
tag1 << [to_ntfs_time(@ctime)].pack('Q<') if @ctime
|
61
60
|
end
|
62
61
|
end
|
63
62
|
s << [tag1.bytesize].pack('v') << tag1
|
@@ -65,14 +64,17 @@ module Zip
|
|
65
64
|
end
|
66
65
|
|
67
66
|
private
|
67
|
+
|
68
68
|
def parse_tags(content)
|
69
69
|
return {} if content.nil?
|
70
|
+
|
70
71
|
tags = {}
|
71
72
|
i = 0
|
72
|
-
while i < content.bytesize
|
73
|
+
while i < content.bytesize
|
73
74
|
tag, size = content[i, 4].unpack('vv')
|
74
75
|
i += 4
|
75
76
|
break unless tag && size
|
77
|
+
|
76
78
|
value = content[i, size]
|
77
79
|
i += size
|
78
80
|
tags[tag] = value
|
@@ -89,4 +91,4 @@ module Zip
|
|
89
91
|
((time.to_f + SEC_TO_UNIX_EPOCH) * WINDOWS_TICK).to_i
|
90
92
|
end
|
91
93
|
end
|
92
|
-
end
|
94
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Zip
|
2
2
|
# Olf Info-ZIP Extra for UNIX uid/gid and file timestampes
|
3
3
|
class ExtraField::OldUnix < ExtraField::Generic
|
4
|
-
HEADER_ID =
|
4
|
+
HEADER_ID = 'UX'
|
5
5
|
register_map
|
6
6
|
|
7
7
|
def initialize(binstr = nil)
|
@@ -9,37 +9,38 @@ module Zip
|
|
9
9
|
@gid = 0
|
10
10
|
@atime = nil
|
11
11
|
@mtime = nil
|
12
|
-
binstr
|
12
|
+
binstr && merge(binstr)
|
13
13
|
end
|
14
14
|
|
15
15
|
attr_accessor :uid, :gid, :atime, :mtime
|
16
16
|
|
17
17
|
def merge(binstr)
|
18
18
|
return if binstr.empty?
|
19
|
+
|
19
20
|
size, content = initial_parse(binstr)
|
20
21
|
# size: 0 for central directory. 4 for local header
|
21
|
-
return if
|
22
|
-
|
22
|
+
return if !size || size == 0
|
23
|
+
|
24
|
+
atime, mtime, uid, gid = content.unpack('VVvv')
|
23
25
|
@uid ||= uid
|
24
26
|
@gid ||= gid
|
25
27
|
@atime ||= atime
|
26
|
-
@mtime ||= mtime
|
28
|
+
@mtime ||= mtime # rubocop:disable Naming/MemoizedInstanceVariableName
|
27
29
|
end
|
28
30
|
|
29
31
|
def ==(other)
|
30
32
|
@uid == other.uid &&
|
31
33
|
@gid == other.gid &&
|
32
|
-
|
33
|
-
|
34
|
+
@atime == other.atime &&
|
35
|
+
@mtime == other.mtime
|
34
36
|
end
|
35
37
|
|
36
38
|
def pack_for_local
|
37
|
-
[@atime, @mtime, @uid, @gid].pack(
|
39
|
+
[@atime, @mtime, @uid, @gid].pack('VVvv')
|
38
40
|
end
|
39
41
|
|
40
42
|
def pack_for_c_dir
|
41
|
-
[@atime, @mtime].pack(
|
43
|
+
[@atime, @mtime].pack('VV')
|
42
44
|
end
|
43
45
|
end
|
44
|
-
|
45
|
-
end
|
46
|
+
end
|
@@ -1,27 +1,57 @@
|
|
1
1
|
module Zip
|
2
2
|
# Info-ZIP Additional timestamp field
|
3
3
|
class ExtraField::UniversalTime < ExtraField::Generic
|
4
|
-
HEADER_ID =
|
4
|
+
HEADER_ID = 'UT'
|
5
5
|
register_map
|
6
6
|
|
7
|
+
ATIME_MASK = 0b010
|
8
|
+
CTIME_MASK = 0b100
|
9
|
+
MTIME_MASK = 0b001
|
10
|
+
|
7
11
|
def initialize(binstr = nil)
|
8
12
|
@ctime = nil
|
9
13
|
@mtime = nil
|
10
14
|
@atime = nil
|
11
|
-
@flag =
|
12
|
-
|
15
|
+
@flag = 0
|
16
|
+
|
17
|
+
merge(binstr) unless binstr.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :atime, :ctime, :mtime, :flag
|
21
|
+
|
22
|
+
def atime=(time)
|
23
|
+
@flag = time.nil? ? @flag & ~ATIME_MASK : @flag | ATIME_MASK
|
24
|
+
@atime = time
|
25
|
+
end
|
26
|
+
|
27
|
+
def ctime=(time)
|
28
|
+
@flag = time.nil? ? @flag & ~CTIME_MASK : @flag | CTIME_MASK
|
29
|
+
@ctime = time
|
13
30
|
end
|
14
31
|
|
15
|
-
|
32
|
+
def mtime=(time)
|
33
|
+
@flag = time.nil? ? @flag & ~MTIME_MASK : @flag | MTIME_MASK
|
34
|
+
@mtime = time
|
35
|
+
end
|
16
36
|
|
17
37
|
def merge(binstr)
|
18
38
|
return if binstr.empty?
|
39
|
+
|
19
40
|
size, content = initial_parse(binstr)
|
20
|
-
size
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
41
|
+
return if !size || size <= 0
|
42
|
+
|
43
|
+
@flag, *times = content.unpack('Cl<l<l<')
|
44
|
+
|
45
|
+
# Parse the timestamps, in order, based on which flags are set.
|
46
|
+
return if times[0].nil?
|
47
|
+
|
48
|
+
@mtime ||= ::Zip::DOSTime.at(times.shift) unless @flag & MTIME_MASK == 0
|
49
|
+
return if times[0].nil?
|
50
|
+
|
51
|
+
@atime ||= ::Zip::DOSTime.at(times.shift) unless @flag & ATIME_MASK == 0
|
52
|
+
return if times[0].nil?
|
53
|
+
|
54
|
+
@ctime ||= ::Zip::DOSTime.at(times.shift) unless @flag & CTIME_MASK == 0
|
25
55
|
end
|
26
56
|
|
27
57
|
def ==(other)
|
@@ -31,17 +61,17 @@ module Zip
|
|
31
61
|
end
|
32
62
|
|
33
63
|
def pack_for_local
|
34
|
-
s = [@flag].pack(
|
35
|
-
|
36
|
-
|
37
|
-
|
64
|
+
s = [@flag].pack('C')
|
65
|
+
s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
|
66
|
+
s << [@atime.to_i].pack('l<') unless @flag & ATIME_MASK == 0
|
67
|
+
s << [@ctime.to_i].pack('l<') unless @flag & CTIME_MASK == 0
|
38
68
|
s
|
39
69
|
end
|
40
70
|
|
41
71
|
def pack_for_c_dir
|
42
|
-
s = [@flag].pack(
|
43
|
-
|
72
|
+
s = [@flag].pack('C')
|
73
|
+
s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
|
44
74
|
s
|
45
75
|
end
|
46
76
|
end
|
47
|
-
end
|
77
|
+
end
|
data/lib/zip/extra_field/unix.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
1
|
module Zip
|
2
2
|
# Info-ZIP Extra for UNIX uid/gid
|
3
3
|
class ExtraField::IUnix < ExtraField::Generic
|
4
|
-
HEADER_ID =
|
4
|
+
HEADER_ID = 'Ux'
|
5
5
|
register_map
|
6
6
|
|
7
7
|
def initialize(binstr = nil)
|
8
8
|
@uid = 0
|
9
9
|
@gid = 0
|
10
|
-
binstr
|
10
|
+
binstr && merge(binstr)
|
11
11
|
end
|
12
12
|
|
13
13
|
attr_accessor :uid, :gid
|
14
14
|
|
15
15
|
def merge(binstr)
|
16
16
|
return if binstr.empty?
|
17
|
+
|
17
18
|
size, content = initial_parse(binstr)
|
18
19
|
# size: 0 for central directory. 4 for local header
|
19
|
-
return if
|
20
|
-
|
21
|
-
|
22
|
-
@
|
20
|
+
return if !size || size == 0
|
21
|
+
|
22
|
+
uid, gid = content.unpack('vv')
|
23
|
+
@uid ||= uid
|
24
|
+
@gid ||= gid # rubocop:disable Naming/MemoizedInstanceVariableName
|
23
25
|
end
|
24
26
|
|
25
27
|
def ==(other)
|
@@ -27,12 +29,11 @@ module Zip
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def pack_for_local
|
30
|
-
[@uid, @gid].pack(
|
32
|
+
[@uid, @gid].pack('vv')
|
31
33
|
end
|
32
34
|
|
33
35
|
def pack_for_c_dir
|
34
36
|
''
|
35
37
|
end
|
36
38
|
end
|
37
|
-
|
38
|
-
end
|
39
|
+
end
|
@@ -6,25 +6,27 @@ module Zip
|
|
6
6
|
register_map
|
7
7
|
|
8
8
|
def initialize(binstr = nil)
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# 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
|
12
|
+
@content = nil
|
12
13
|
@original_size = nil
|
13
14
|
@compressed_size = nil
|
14
15
|
@relative_header_offset = nil
|
15
16
|
@disk_start_number = nil
|
16
|
-
binstr
|
17
|
+
binstr && merge(binstr)
|
17
18
|
end
|
18
19
|
|
19
20
|
def ==(other)
|
20
21
|
other.original_size == @original_size &&
|
21
22
|
other.compressed_size == @compressed_size &&
|
22
|
-
|
23
|
-
|
23
|
+
other.relative_header_offset == @relative_header_offset &&
|
24
|
+
other.disk_start_number == @disk_start_number
|
24
25
|
end
|
25
26
|
|
26
27
|
def merge(binstr)
|
27
28
|
return if binstr.empty?
|
29
|
+
|
28
30
|
_, @content = initial_parse(binstr)
|
29
31
|
end
|
30
32
|
|
@@ -44,23 +46,24 @@ module Zip
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def extract(size, format)
|
47
|
-
@content.slice!(0, size).
|
49
|
+
@content.slice!(0, size).unpack1(format)
|
48
50
|
end
|
49
51
|
private :extract
|
50
52
|
|
51
53
|
def pack_for_local
|
52
54
|
# local header entries must contain original size and compressed size; other fields do not apply
|
53
55
|
return '' unless @original_size && @compressed_size
|
54
|
-
|
56
|
+
|
57
|
+
[@original_size, @compressed_size].pack('Q<Q<')
|
55
58
|
end
|
56
59
|
|
57
60
|
def pack_for_c_dir
|
58
61
|
# central directory entries contain only fields that didn't fit in the main entry part
|
59
62
|
packed = ''.force_encoding('BINARY')
|
60
|
-
packed << [@original_size].pack(
|
61
|
-
packed << [@compressed_size].pack(
|
62
|
-
packed << [@relative_header_offset].pack(
|
63
|
-
packed << [@disk_start_number].pack(
|
63
|
+
packed << [@original_size].pack('Q<') if @original_size
|
64
|
+
packed << [@compressed_size].pack('Q<') if @compressed_size
|
65
|
+
packed << [@relative_header_offset].pack('Q<') if @relative_header_offset
|
66
|
+
packed << [@disk_start_number].pack('V') if @disk_start_number
|
64
67
|
packed
|
65
68
|
end
|
66
69
|
end
|
data/lib/zip/extra_field.rb
CHANGED
@@ -3,30 +3,30 @@ module Zip
|
|
3
3
|
ID_MAP = {}
|
4
4
|
|
5
5
|
def initialize(binstr = nil)
|
6
|
-
binstr
|
6
|
+
merge(binstr) if binstr
|
7
7
|
end
|
8
8
|
|
9
|
-
def extra_field_type_exist(binstr, id, len,
|
9
|
+
def extra_field_type_exist(binstr, id, len, index)
|
10
10
|
field_name = ID_MAP[id].name
|
11
|
-
if
|
12
|
-
self[field_name].merge(binstr[
|
11
|
+
if member?(field_name)
|
12
|
+
self[field_name].merge(binstr[index, len + 4])
|
13
13
|
else
|
14
|
-
field_obj = ID_MAP[id].new(binstr[
|
14
|
+
field_obj = ID_MAP[id].new(binstr[index, len + 4])
|
15
15
|
self[field_name] = field_obj
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def extra_field_type_unknown(binstr, len,
|
19
|
+
def extra_field_type_unknown(binstr, len, index)
|
20
20
|
create_unknown_item unless self['Unknown']
|
21
|
-
if !len || len + 4 > binstr[
|
22
|
-
self['Unknown'] << binstr[
|
21
|
+
if !len || len + 4 > binstr[index..-1].bytesize
|
22
|
+
self['Unknown'] << binstr[index..-1]
|
23
23
|
return
|
24
24
|
end
|
25
|
-
self['Unknown'] << binstr[
|
25
|
+
self['Unknown'] << binstr[index, len + 4]
|
26
26
|
end
|
27
27
|
|
28
28
|
def create_unknown_item
|
29
|
-
s = ''
|
29
|
+
s = +''
|
30
30
|
class << s
|
31
31
|
alias_method :to_c_dir_bin, :to_s
|
32
32
|
alias_method :to_local_bin, :to_s
|
@@ -36,10 +36,11 @@ module Zip
|
|
36
36
|
|
37
37
|
def merge(binstr)
|
38
38
|
return if binstr.empty?
|
39
|
+
|
39
40
|
i = 0
|
40
41
|
while i < binstr.bytesize
|
41
42
|
id = binstr[i, 2]
|
42
|
-
len = binstr[i + 2, 2].to_s.
|
43
|
+
len = binstr[i + 2, 2].to_s.unpack1('v')
|
43
44
|
if id && ID_MAP.member?(id)
|
44
45
|
extra_field_type_exist(binstr, id, len, i)
|
45
46
|
elsif id
|
@@ -51,9 +52,10 @@ module Zip
|
|
51
52
|
end
|
52
53
|
|
53
54
|
def create(name)
|
54
|
-
unless field_class = ID_MAP.values.find { |k| k.name == name }
|
55
|
+
unless (field_class = ID_MAP.values.find { |k| k.name == name })
|
55
56
|
raise Error, "Unknown extra field '#{name}'"
|
56
57
|
end
|
58
|
+
|
57
59
|
self[name] = field_class.new
|
58
60
|
end
|
59
61
|
|
@@ -61,7 +63,7 @@ module Zip
|
|
61
63
|
# does not prevent known fields from being read back in
|
62
64
|
def ordered_values
|
63
65
|
result = []
|
64
|
-
|
66
|
+
each { |k, v| k == 'Unknown' ? result.push(v) : result.unshift(v) }
|
65
67
|
result
|
66
68
|
end
|
67
69
|
|
@@ -69,7 +71,7 @@ module Zip
|
|
69
71
|
ordered_values.map! { |v| v.to_local_bin.force_encoding('BINARY') }.join
|
70
72
|
end
|
71
73
|
|
72
|
-
alias
|
74
|
+
alias to_s to_local_bin
|
73
75
|
|
74
76
|
def to_c_dir_bin
|
75
77
|
ordered_values.map! { |v| v.to_c_dir_bin.force_encoding('BINARY') }.join
|
@@ -83,8 +85,8 @@ module Zip
|
|
83
85
|
to_local_bin.bytesize
|
84
86
|
end
|
85
87
|
|
86
|
-
alias
|
87
|
-
alias
|
88
|
+
alias length local_size
|
89
|
+
alias size local_size
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|