zippo 0.2.0 → 0.2.1
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 +6 -14
- data/.rubocop.yml +35 -0
- data/.travis.yml +3 -0
- data/README.md +4 -0
- data/Rakefile +8 -2
- data/lib/zippo.rb +1 -1
- data/lib/zippo/binary_structure/base.rb +10 -8
- data/lib/zippo/binary_structure/binary_packer.rb +2 -2
- data/lib/zippo/binary_structure/meta.rb +9 -11
- data/lib/zippo/binary_structure/structure.rb +6 -4
- data/lib/zippo/binary_structure/structure_member.rb +1 -1
- data/lib/zippo/cd_file_header.rb +14 -14
- data/lib/zippo/central_directory_entries_unpacker.rb +3 -2
- data/lib/zippo/central_directory_reader.rb +4 -3
- data/lib/zippo/end_cd_record.rb +6 -6
- data/lib/zippo/filter/base.rb +3 -2
- data/lib/zippo/filter/compressor.rb +4 -4
- data/lib/zippo/filter/compressor/deflate.rb +1 -0
- data/lib/zippo/filter/null_filters.rb +1 -0
- data/lib/zippo/filter/uncompressor.rb +3 -5
- data/lib/zippo/filter/uncompressor/deflate.rb +1 -0
- data/lib/zippo/io_zip_member.rb +1 -1
- data/lib/zippo/local_file_header.rb +8 -8
- data/lib/zippo/version.rb +1 -1
- data/lib/zippo/zip_directory.rb +15 -12
- data/lib/zippo/zip_file.rb +8 -6
- data/lib/zippo/zip_file_writer.rb +4 -4
- data/lib/zippo/zip_member.rb +4 -3
- data/spec/binary_structure_spec.rb +26 -28
- data/spec/central_directory_entries_unpacker_spec.rb +2 -3
- data/spec/central_directory_parser_spec.rb +4 -4
- data/spec/central_directory_unpacker_spec.rb +0 -1
- data/spec/deflate_compressor_spec.rb +4 -4
- data/spec/deflate_uncompressor_spec.rb +5 -6
- data/spec/integration/compressors_spec.rb +1 -1
- data/spec/integration/zippo_spec.rb +4 -3
- data/spec/spec_helper.rb +9 -0
- data/spec/store_compressor_spec.rb +3 -3
- data/spec/store_uncompressor_spec.rb +1 -1
- data/spec/zip_directory_spec.rb +15 -15
- data/spec/zip_file_spec.rb +1 -1
- data/spec/zip_file_writer_spec.rb +0 -1
- data/spec/zip_member_spec.rb +4 -6
- data/yard_extensions.rb +1 -1
- data/zippo.gemspec +7 -6
- metadata +59 -19
@@ -27,7 +27,7 @@ module Zippo::Filter
|
|
27
27
|
@remaining = @compressed_size
|
28
28
|
end
|
29
29
|
|
30
|
-
def read
|
30
|
+
def read(n, buf = nil)
|
31
31
|
if @remaining >= n
|
32
32
|
@remaining -= n
|
33
33
|
elsif (n = @remaining) > 0
|
@@ -43,11 +43,9 @@ module Zippo::Filter
|
|
43
43
|
# Uncompresses the data to the specified IO
|
44
44
|
#
|
45
45
|
# @param [IO] io the object to uncompress to, must respond to #<<
|
46
|
-
def uncompress_to
|
46
|
+
def uncompress_to(io)
|
47
47
|
buf = ""
|
48
|
-
while
|
49
|
-
io << buf
|
50
|
-
end
|
48
|
+
io << buf while read BLOCK_SIZE, buf
|
51
49
|
io << tail_filter
|
52
50
|
end
|
53
51
|
|
data/lib/zippo/io_zip_member.rb
CHANGED
@@ -17,7 +17,7 @@ module Zippo
|
|
17
17
|
@source.rewind
|
18
18
|
end
|
19
19
|
|
20
|
-
def write_to
|
20
|
+
def write_to(out, preferred_compression_method = Filter::DeflateCompressor::METHOD, _recompress = nil)
|
21
21
|
Filter::Compressor.for(preferred_compression_method).new(@source).compress_to(out)
|
22
22
|
end
|
23
23
|
end
|
@@ -8,21 +8,21 @@ module Zippo
|
|
8
8
|
binary_structure do
|
9
9
|
# @!macro [attach] bs.field
|
10
10
|
# @!attribute [rw] $1
|
11
|
-
field :signature, 'L', :
|
12
|
-
field :version_extractable_by, 'S', :
|
13
|
-
field :bit_flags, 'S', :
|
11
|
+
field :signature, 'L', signature: SIGNATURE
|
12
|
+
field :version_extractable_by, 'S', default: 20
|
13
|
+
field :bit_flags, 'S', default: 0
|
14
14
|
field :compression_method, 'S'
|
15
|
-
field :last_modified_time, 'S', :
|
16
|
-
field :last_modified_date, 'S', :
|
15
|
+
field :last_modified_time, 'S', default: 0 # XXX
|
16
|
+
field :last_modified_date, 'S', default: 0 # XXX
|
17
17
|
field :crc32, 'L'
|
18
18
|
field :compressed_size, 'L'
|
19
19
|
field :uncompressed_size, 'L'
|
20
20
|
# set when name is set
|
21
21
|
field :file_name_length, 'S'
|
22
22
|
# set when extra_field is set
|
23
|
-
field :extra_field_length, 'S', :
|
24
|
-
field :name, 'a*', :
|
25
|
-
field :extra_field, 'a*', :
|
23
|
+
field :extra_field_length, 'S', default: 0
|
24
|
+
field :name, 'a*', size: :file_name_length
|
25
|
+
field :extra_field, 'a*', default: '', size: :extra_field_length
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/zippo/version.rb
CHANGED
data/lib/zippo/zip_directory.rb
CHANGED
@@ -13,7 +13,7 @@ module Zippo
|
|
13
13
|
def_delegators :entries_hash, :empty?
|
14
14
|
def_delegators :entries, :each, :map
|
15
15
|
|
16
|
-
def initialize
|
16
|
+
def initialize(io = nil)
|
17
17
|
@io = io
|
18
18
|
end
|
19
19
|
|
@@ -48,23 +48,25 @@ module Zippo
|
|
48
48
|
# - another ZipMember (allowing direct stream copy)
|
49
49
|
def insert(name, source)
|
50
50
|
set name,
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
case source
|
52
|
+
when ZipMember then source.with_name name
|
53
|
+
when String then IOZipMember.new name, File.open(source, 'r:BINARY')
|
54
|
+
else IOZipMember.new name, source
|
55
|
+
end
|
56
56
|
end
|
57
57
|
|
58
58
|
# @return [Hash] the hash of ZipMembers, the hash key is the
|
59
59
|
# member's name
|
60
60
|
def entries_hash
|
61
|
-
@entries_hash ||=
|
62
|
-
|
63
|
-
|
61
|
+
@entries_hash ||=
|
62
|
+
if @io
|
63
|
+
CentralDirectoryReader.new(@io)
|
64
|
+
.cd_file_headers.each_with_object({}) do |header, hash|
|
65
|
+
hash[header.name] = ZipMember.new @io, header
|
66
|
+
end
|
67
|
+
else
|
68
|
+
{}
|
64
69
|
end
|
65
|
-
else
|
66
|
-
{}
|
67
|
-
end
|
68
70
|
end
|
69
71
|
|
70
72
|
# @return [Array] the members of the ZipFile
|
@@ -73,6 +75,7 @@ module Zippo
|
|
73
75
|
end
|
74
76
|
|
75
77
|
private
|
78
|
+
|
76
79
|
def set(name, member)
|
77
80
|
entries_hash[name] = member
|
78
81
|
end
|
data/lib/zippo/zip_file.rb
CHANGED
@@ -87,14 +87,16 @@ module Zippo
|
|
87
87
|
|
88
88
|
# @return [ZipDirectory] the ZipDirectory
|
89
89
|
def directory
|
90
|
-
@directory ||=
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
@directory ||=
|
91
|
+
if read?
|
92
|
+
ZipDirectory.new io
|
93
|
+
else
|
94
|
+
ZipDirectory.new
|
95
|
+
end
|
95
96
|
end
|
96
97
|
|
97
98
|
private
|
99
|
+
|
98
100
|
def read?
|
99
101
|
File.exist? @filename and @mode.include? 'r'
|
100
102
|
end
|
@@ -104,7 +106,7 @@ module Zippo
|
|
104
106
|
end
|
105
107
|
|
106
108
|
def update?
|
107
|
-
read?
|
109
|
+
read? && write?
|
108
110
|
end
|
109
111
|
|
110
112
|
def io
|
@@ -10,10 +10,10 @@ module Zippo
|
|
10
10
|
|
11
11
|
# Writes the directory to the file.
|
12
12
|
def write
|
13
|
-
File.open(@filename,'wb:ASCII-8BIT') do |io|
|
13
|
+
File.open(@filename, 'wb:ASCII-8BIT') do |io|
|
14
14
|
packer = LocalFileHeader::Packer.new io
|
15
15
|
headers = []
|
16
|
-
|
16
|
+
@directory.each do |member|
|
17
17
|
header = CdFileHeader.default
|
18
18
|
header.compression_method = 8 # XXX configurable
|
19
19
|
# XXX hack fix for maintaining method in zipped data copies
|
@@ -36,7 +36,7 @@ module Zippo
|
|
36
36
|
|
37
37
|
# write the completed header, returning to the current position
|
38
38
|
io.seek header.local_file_header_offset
|
39
|
-
#packer.pack LocalFileHeader.from header.convert_to LocalHileHeader
|
39
|
+
# packer.pack LocalFileHeader.from header.convert_to LocalHileHeader
|
40
40
|
packer.pack header.convert_to LocalFileHeader
|
41
41
|
io.seek header.compressed_size, IO::SEEK_CUR
|
42
42
|
headers << header
|
@@ -45,7 +45,7 @@ module Zippo
|
|
45
45
|
eocdr = EndCdRecord.default
|
46
46
|
eocdr.cd_offset = io.pos
|
47
47
|
packer = CdFileHeader::Packer.new io
|
48
|
-
|
48
|
+
headers.each do |header|
|
49
49
|
packer.pack header
|
50
50
|
end
|
51
51
|
eocdr.cd_size = io.pos - eocdr.cd_offset
|
data/lib/zippo/zip_member.rb
CHANGED
@@ -7,7 +7,7 @@ require 'forwardable'
|
|
7
7
|
module Zippo
|
8
8
|
# A member of a Zip archive file.
|
9
9
|
class ZipMember
|
10
|
-
def initialize
|
10
|
+
def initialize(io, header)
|
11
11
|
@io = io
|
12
12
|
@header = header
|
13
13
|
end
|
@@ -38,7 +38,7 @@ module Zippo
|
|
38
38
|
#
|
39
39
|
# @param [String] name the name to use
|
40
40
|
# @return [ZipMember] the new ZipMember
|
41
|
-
def with_name
|
41
|
+
def with_name(name)
|
42
42
|
dup.tap do |obj|
|
43
43
|
obj.instance_variable_set :@name, name
|
44
44
|
end
|
@@ -53,7 +53,7 @@ module Zippo
|
|
53
53
|
#
|
54
54
|
# @return [Integer, Integer, Integer] the amount written, the
|
55
55
|
# original size of the data, the crc32 of the data
|
56
|
-
def write_to
|
56
|
+
def write_to(out, preferred_method = Filter::DeflateCompressor::METHOD, recompress = false)
|
57
57
|
seek_to_compressed_data
|
58
58
|
if recompress
|
59
59
|
Filter::Compressor.for(preferred_method).new(uncompressor).compress_to(out)
|
@@ -64,6 +64,7 @@ module Zippo
|
|
64
64
|
end
|
65
65
|
|
66
66
|
private
|
67
|
+
|
67
68
|
def local_file_header
|
68
69
|
@io.seek @header.local_file_header_offset
|
69
70
|
LocalFileHeader::Unpacker.new(@io).unpack
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
# TODO - spec that unpackers can take strings
|
3
|
+
# TODO: - spec that unpackers can take strings
|
4
4
|
|
5
5
|
require 'zippo/binary_structure'
|
6
6
|
|
@@ -17,14 +17,14 @@ module Zippo
|
|
17
17
|
klass.class_eval do
|
18
18
|
binary_structure do
|
19
19
|
field :foo, 'L'
|
20
|
-
field :yay, 'a4', :
|
20
|
+
field :yay, 'a4', signature: "baz"
|
21
21
|
field :bar, 'S'
|
22
|
-
field :quux, 'a*', :
|
22
|
+
field :quux, 'a*', size: :foo
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
it "should store field information in the class" do
|
27
|
-
klass.structure.should
|
27
|
+
klass.structure.fields.size.should eq 4
|
28
28
|
klass.structure.fields[0].name.should eq :foo
|
29
29
|
klass.structure.fields[3].options[:size].should eq :foo
|
30
30
|
klass.structure.fields[1].should be_signature
|
@@ -42,7 +42,7 @@ module Zippo
|
|
42
42
|
obj.bar.should eq 10
|
43
43
|
end
|
44
44
|
it "should have an unpacker" do
|
45
|
-
array = [10,"baz", 42,"foobar baz"]
|
45
|
+
array = [10, "baz", 42, "foobar baz"]
|
46
46
|
packed = array.pack 'La4Sa*'
|
47
47
|
io = StringIO.new packed
|
48
48
|
obj = klass::Unpacker.new(io).unpack
|
@@ -51,14 +51,14 @@ module Zippo
|
|
51
51
|
obj.quux.should eq "foobar baz"
|
52
52
|
end
|
53
53
|
it "should unpack oversized strings correctly" do
|
54
|
-
array = [3,"baz", 42,"foobar baz"]
|
54
|
+
array = [3, "baz", 42, "foobar baz"]
|
55
55
|
packed = array.pack 'La4Sa*'
|
56
56
|
io = StringIO.new packed
|
57
57
|
obj = klass::Unpacker.new(io).unpack
|
58
58
|
obj.quux.should eq "foo"
|
59
59
|
end
|
60
60
|
it "should have a packer" do
|
61
|
-
array = [10,"baz", 42,"foobar baz"]
|
61
|
+
array = [10, "baz", 42, "foobar baz"]
|
62
62
|
packed = array.pack 'La4Sa*'
|
63
63
|
io = StringIO.new
|
64
64
|
obj.bar = 42
|
@@ -79,7 +79,7 @@ module Zippo
|
|
79
79
|
other_klass.class_eval do
|
80
80
|
binary_structure do
|
81
81
|
field :bar, 'S'
|
82
|
-
field :yay, 'a4', :
|
82
|
+
field :yay, 'a4', signature: "quux"
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -108,25 +108,23 @@ module Zippo
|
|
108
108
|
# need to test a few fixed fields, followed by a
|
109
109
|
# variable length field, then a few more fixed
|
110
110
|
# fields
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
=end
|
111
|
+
# it "should complain if the order is bad" do
|
112
|
+
# pending
|
113
|
+
# klass = Class.new BinaryStructure
|
114
|
+
# lambda { klass.class_eval do
|
115
|
+
# field :foo, 'S'
|
116
|
+
# field :bar, 'a*', :size => :baz
|
117
|
+
# field :baz, 'S'
|
118
|
+
# end }.should raise_error "size not found"
|
119
|
+
# end
|
120
|
+
# it "should remain silent if the order is ok" do
|
121
|
+
# pending
|
122
|
+
# klass = Class.new BinaryStructure
|
123
|
+
# lambda { klass.class_eval do
|
124
|
+
# field :foo, 'S'
|
125
|
+
# field :baz, 'S'
|
126
|
+
# field :bar, 'a*', :size => :baz
|
127
|
+
# end }.should_not raise_error
|
128
|
+
# end
|
131
129
|
end
|
132
130
|
end
|
@@ -11,7 +11,7 @@ module Zippo
|
|
11
11
|
let(:offset) { 112 }
|
12
12
|
let(:size) { 79 }
|
13
13
|
|
14
|
-
specify { subject.should
|
14
|
+
specify { subject.size.should eq 1 }
|
15
15
|
specify { subject.first.name.should eq "test.file" }
|
16
16
|
end
|
17
17
|
|
@@ -20,10 +20,9 @@ module Zippo
|
|
20
20
|
let(:offset) { 242 }
|
21
21
|
let(:size) { 158 }
|
22
22
|
|
23
|
-
specify { subject.should
|
23
|
+
specify { subject.size.should eq 2 }
|
24
24
|
specify { subject[0].name.should eq "test.file" }
|
25
25
|
specify { subject[1].name.should eq "other.test" }
|
26
26
|
end
|
27
|
-
|
28
27
|
end
|
29
28
|
end
|
@@ -6,7 +6,7 @@ require "zippo/zip_file"
|
|
6
6
|
module Zippo
|
7
7
|
describe CentralDirectoryReader do
|
8
8
|
let(:io) { File.open(file, "rb:ASCII-8BIT") }
|
9
|
-
|
9
|
+
let(:file) { test_file "test.zip" }
|
10
10
|
let(:parser) { CentralDirectoryReader.new(io) }
|
11
11
|
after(:each) { io.close }
|
12
12
|
|
@@ -17,7 +17,7 @@ module Zippo
|
|
17
17
|
end
|
18
18
|
|
19
19
|
specify { parser.end_of_cd_record_position.should eq 0xbf }
|
20
|
-
specify { parser.should
|
20
|
+
specify { parser.cd_file_headers.size.should eq 1 }
|
21
21
|
end
|
22
22
|
|
23
23
|
context "when parsing a file with a comment" do
|
@@ -33,7 +33,7 @@ module Zippo
|
|
33
33
|
|
34
34
|
context "when parsing a file that is not a zip" do
|
35
35
|
let(:file) { test_file "not_a.zip" }
|
36
|
-
specify { -> {parser.end_of_cd_record_position}.should raise_error(/not found/) }
|
36
|
+
specify { -> { parser.end_of_cd_record_position }.should raise_error(/not found/) }
|
37
37
|
end
|
38
38
|
|
39
39
|
context "when parsing a zip file larger than the maximum comment size" do
|
@@ -42,7 +42,7 @@ module Zippo
|
|
42
42
|
Zippo::ZipFile.open("large.zip", "w") do |v|
|
43
43
|
v["test.file"] = "a" * 65535
|
44
44
|
end
|
45
|
-
Zippo::ZipFile.open("large.zip") {|v| v["test.file"].read }.should eq "a" * 65535
|
45
|
+
Zippo::ZipFile.open("large.zip") { |v| v["test.file"].read }.should eq "a" * 65535
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -5,10 +5,10 @@ require "zippo/filter/compressor/deflate"
|
|
5
5
|
|
6
6
|
module Zippo::Filter
|
7
7
|
describe DeflateCompressor do
|
8
|
-
let
|
9
|
-
let
|
10
|
-
let
|
11
|
-
let
|
8
|
+
let(:data) { "a" * 20 }
|
9
|
+
let(:zdata) { "KL\xC4\u0004\u0000" }
|
10
|
+
let(:io) { StringIO.new data }
|
11
|
+
let(:out) { StringIO.new "" }
|
12
12
|
it "should write the data" do
|
13
13
|
compressor = DeflateCompressor.new(io)
|
14
14
|
csize, size, crc32 = compressor.compress_to out
|
@@ -4,12 +4,11 @@ require "zippo/filter/uncompressor/deflate"
|
|
4
4
|
|
5
5
|
module Zippo::Filter
|
6
6
|
describe DeflateUncompressor do
|
7
|
-
|
8
|
-
let
|
9
|
-
let
|
10
|
-
let
|
11
|
-
let
|
12
|
-
let (:io) { StringIO.new string }
|
7
|
+
let(:zstring) { Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS).deflate("a" * 20, Zlib::FINISH) }
|
8
|
+
let(:size) { zstring.size }
|
9
|
+
let(:string) { "aaa" + zstring + "bbb" }
|
10
|
+
let(:out) { StringIO.new }
|
11
|
+
let(:io) { StringIO.new string }
|
13
12
|
it "should deflate only the compressed string" do
|
14
13
|
io.seek 3
|
15
14
|
DeflateUncompressor.new(io, size).uncompress.should eq "a" * 20
|
@@ -7,7 +7,7 @@ require 'zippo/filter/compressors'
|
|
7
7
|
|
8
8
|
module Zippo::Filter
|
9
9
|
describe Uncompressor do
|
10
|
-
let
|
10
|
+
let(:zstring) { Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS).deflate("a" * 20, Zlib::FINISH) }
|
11
11
|
it "should allow uncompressors to work with compressors" do
|
12
12
|
stream = StringIO.new zstring
|
13
13
|
deflater = DeflateUncompressor.new(stream, zstring.size)
|
@@ -24,7 +24,8 @@ module Zippo
|
|
24
24
|
|
25
25
|
it "should work like File.open" do
|
26
26
|
io = File.open(file)
|
27
|
-
File.
|
27
|
+
expect(File).to receive(:open)
|
28
|
+
.with(file, 'r:ASCII-8BIT').and_return(io)
|
28
29
|
s = Zippo.open(file) { |v| v['test.file'].read }
|
29
30
|
s.should eq member_data
|
30
31
|
io.should be_closed
|
@@ -33,9 +34,9 @@ module Zippo
|
|
33
34
|
it "should create zip files" do
|
34
35
|
in_working_directory do
|
35
36
|
File.write "xyzzy.txt", "plugh"
|
36
|
-
Zippo.open("new.zip", "w") {|v| v['xyzzy.txt'] = "plugh" }
|
37
|
+
Zippo.open("new.zip", "w") { |v| v['xyzzy.txt'] = "plugh" }
|
37
38
|
Pathname.new("new.zip").should exist
|
38
|
-
Zippo.open("new.zip") {|v| v['xyzzy.txt'].read }.should eq "plugh"
|
39
|
+
Zippo.open("new.zip") { |v| v['xyzzy.txt'].read }.should eq "plugh"
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|