zippo 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|