rubyzip 1.1.0 → 1.1.1
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 +4 -4
- data/README.md +41 -29
- data/Rakefile +9 -9
- data/TODO +0 -1
- data/lib/zip.rb +3 -1
- data/lib/zip/central_directory.rb +16 -13
- data/lib/zip/deflater.rb +1 -1
- data/lib/zip/entry.rb +39 -27
- data/lib/zip/entry_set.rb +1 -1
- data/lib/zip/errors.rb +6 -6
- data/lib/zip/extra_field.rb +7 -4
- data/lib/zip/extra_field/generic.rb +4 -4
- data/lib/zip/extra_field/old_unix.rb +45 -0
- data/lib/zip/file.rb +41 -42
- data/lib/zip/inflater.rb +44 -15
- data/lib/zip/input_stream.rb +17 -15
- data/lib/zip/output_stream.rb +42 -32
- data/lib/zip/streamable_stream.rb +17 -12
- data/lib/zip/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7eca6764ebd3b2104ce779b422472ed1df3f034
|
4
|
+
data.tar.gz: 2f90a3751e1533d46ff864f177f09cd2433b90e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebd0ca646df6f8d652fc91f5d5306b101fe56bb8577d6a401b3b46f6a1fc6c9281df3cb88a555bed175c53ae01d437fd92b01487983f1eaf50d03fc94586bd1a
|
7
|
+
data.tar.gz: eff9c8f931440447bf5656720f32748523b792e5189e7bed1d000c7686f09a19cd4a019629d81f058dffd6d770aa1928e28e86cdb93561c801dbc3c6c21f7c56
|
data/README.md
CHANGED
@@ -9,11 +9,11 @@ rubyzip is a ruby library for reading and writing zip files.
|
|
9
9
|
|
10
10
|
Rubyzip interface changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed.
|
11
11
|
|
12
|
-
If you have issues with any third-party gems what required rubyzip you can use next
|
12
|
+
If you have issues with any third-party gems what required old version of rubyzip you can use next workaround:
|
13
13
|
|
14
14
|
```ruby
|
15
|
-
|
16
|
-
gem '
|
15
|
+
gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version
|
16
|
+
gem 'zip-zip' # will load compatibility for old rubyzip API.
|
17
17
|
```
|
18
18
|
|
19
19
|
## Requirements
|
@@ -53,6 +53,7 @@ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
|
|
53
53
|
# - The original file, including the path to find it
|
54
54
|
zipfile.add(filename, folder + '/' + filename)
|
55
55
|
end
|
56
|
+
zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
|
56
57
|
end
|
57
58
|
```
|
58
59
|
|
@@ -90,6 +91,26 @@ fruit/orange
|
|
90
91
|
|
91
92
|
After this entries in zip archive will be saved in ordered state.
|
92
93
|
|
94
|
+
### Reading a Zip file
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
Zip::File.open('foo.zip') do |zip_file|
|
98
|
+
# Handle entries one by one
|
99
|
+
zip_file.each do |entry|
|
100
|
+
# Extract to file/directory/symlink
|
101
|
+
puts "Extracting #{entry.name}"
|
102
|
+
entry.extract(dest_file)
|
103
|
+
|
104
|
+
# Read into memory
|
105
|
+
content = entry.get_input_stream.read
|
106
|
+
end
|
107
|
+
|
108
|
+
# Find specific entry
|
109
|
+
entry = zip_file.glob('*.csv').first
|
110
|
+
puts entry.get_input_stream.read
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
93
114
|
## Known issues
|
94
115
|
|
95
116
|
### Modify docx file with rubyzip
|
@@ -115,32 +136,6 @@ end
|
|
115
136
|
File.open(new_path, "w") {|f| f.write(buffer.string) }
|
116
137
|
```
|
117
138
|
|
118
|
-
## Further Documentation
|
119
|
-
|
120
|
-
There is more than one way to access or create a zip archive with
|
121
|
-
rubyzip. The basic API is modeled after the classes in
|
122
|
-
java.util.zip from the Java SDK. This means there are classes such
|
123
|
-
as Zip::InputStream, Zip::OutputStream and
|
124
|
-
Zip::File. Zip::InputStream provides a basic interface for
|
125
|
-
iterating through the entries in a zip archive and reading from the
|
126
|
-
entries in the same way as from a regular File or IO
|
127
|
-
object. OutputStream is the corresponding basic output
|
128
|
-
facility. Zip::File provides a mean for accessing the archives
|
129
|
-
central directory and provides means for accessing any entry without
|
130
|
-
having to iterate through the archive. Unlike Java's
|
131
|
-
java.util.zip.ZipFile rubyzip's Zip::File is mutable, which means
|
132
|
-
it can be used to change zip files as well.
|
133
|
-
|
134
|
-
Another way to access a zip archive with rubyzip is to use rubyzip's
|
135
|
-
Zip::FileSystem API. Using this API files can be read from and
|
136
|
-
written to the archive in much the same manner as ruby's builtin
|
137
|
-
classes allows files to be read from and written to the file system.
|
138
|
-
|
139
|
-
For details about the specific behaviour of classes and methods refer
|
140
|
-
to the test suite. Finally you can generate the rdoc documentation or
|
141
|
-
visit http://rubyzip.sourceforge.net.
|
142
|
-
|
143
|
-
|
144
139
|
## Configuration
|
145
140
|
|
146
141
|
By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:
|
@@ -163,6 +158,14 @@ If you want to store non english names and want to open properly file on Windows
|
|
163
158
|
Zip.unicode_names = true
|
164
159
|
```
|
165
160
|
|
161
|
+
You can set the default compression level like so:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
Zip.default_compression = Zlib::DEFAULT_COMPRESSION
|
165
|
+
```
|
166
|
+
|
167
|
+
It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
|
168
|
+
|
166
169
|
All settings in same time
|
167
170
|
|
168
171
|
```ruby
|
@@ -170,9 +173,18 @@ All settings in same time
|
|
170
173
|
c.on_exists_proc = true
|
171
174
|
c.continue_on_exists_proc = true
|
172
175
|
c.unicode_names = true
|
176
|
+
c.default_compression = Zlib::BEST_COMPRESSION
|
173
177
|
end
|
174
178
|
```
|
175
179
|
|
180
|
+
By default Zip64 support is disabled for writing. To enable it do next:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
Zip.write_zip64_support = true
|
184
|
+
```
|
185
|
+
|
186
|
+
_NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
|
187
|
+
|
176
188
|
## Developing
|
177
189
|
|
178
190
|
To run tests you need run next commands:
|
data/Rakefile
CHANGED
@@ -4,16 +4,16 @@ require 'rake/testtask'
|
|
4
4
|
task :default => :test
|
5
5
|
|
6
6
|
Rake::TestTask.new(:test) do |test|
|
7
|
-
test.libs <<
|
8
|
-
test.libs <<
|
9
|
-
test.pattern =
|
7
|
+
test.libs << 'lib'
|
8
|
+
test.libs << 'test'
|
9
|
+
test.pattern = 'test/**/*_test.rb'
|
10
10
|
test.verbose = true
|
11
11
|
end
|
12
12
|
|
13
|
-
Rake::TestTask.new(:zip64_full_test) do |test|
|
14
|
-
test.libs << File.join(File.dirname(__FILE__), 'lib')
|
15
|
-
test.libs << File.join(File.dirname(__FILE__), 'test')
|
16
|
-
test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb')
|
17
|
-
test.verbose = true
|
18
|
-
end
|
13
|
+
#Rake::TestTask.new(:zip64_full_test) do |test|
|
14
|
+
# test.libs << File.join(File.dirname(__FILE__), 'lib')
|
15
|
+
# test.libs << File.join(File.dirname(__FILE__), 'test')
|
16
|
+
# test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb')
|
17
|
+
# test.verbose = true
|
18
|
+
#end
|
19
19
|
|
data/TODO
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
* Suggestion: Add ZipFile/ZipInputStream example that demonstrates extracting all entries.
|
5
5
|
* Suggestion: ZipFile#extract destination should default to "."
|
6
6
|
* Suggestion: ZipEntry should have extract(), get_input_stream() methods etc
|
7
|
-
* Suggestion: ZipInputStream/ZipOutputStream should accept an IO object in addition to a filename.
|
8
7
|
* (is buffering used anywhere with write?)
|
9
8
|
* Inflater.sysread should pass the buffer to produce_input.
|
10
9
|
* Implement ZipFsDir.glob
|
data/lib/zip.rb
CHANGED
@@ -34,7 +34,7 @@ end
|
|
34
34
|
|
35
35
|
module Zip
|
36
36
|
extend self
|
37
|
-
attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries
|
37
|
+
attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support
|
38
38
|
|
39
39
|
def reset!
|
40
40
|
@_ran_once = false
|
@@ -42,6 +42,8 @@ module Zip
|
|
42
42
|
@on_exists_proc = false
|
43
43
|
@continue_on_exists_proc = false
|
44
44
|
@sort_entries = false
|
45
|
+
@default_compression = ::Zlib::DEFAULT_COMPRESSION
|
46
|
+
@write_zip64_support = false
|
45
47
|
end
|
46
48
|
|
47
49
|
def setup
|
@@ -26,10 +26,13 @@ module Zip
|
|
26
26
|
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
|
27
27
|
eocd_offset = io.tell
|
28
28
|
cdir_size = eocd_offset - cdir_offset
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
if ::Zip.write_zip64_support
|
30
|
+
need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF
|
31
|
+
need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] }
|
32
|
+
if need_zip64_eocd
|
33
|
+
write_64_e_o_c_d(io, cdir_offset, cdir_size)
|
34
|
+
write_64_eocd_locator(io, eocd_offset)
|
35
|
+
end
|
33
36
|
end
|
34
37
|
write_e_o_c_d(io, cdir_offset, cdir_size)
|
35
38
|
end
|
@@ -93,7 +96,7 @@ module Zip
|
|
93
96
|
@size_in_bytes = Entry.read_zip_64_long(buf)
|
94
97
|
@cdir_offset = Entry.read_zip_64_long(buf)
|
95
98
|
@zip_64_extensible = buf.slice!(0, buf.bytesize)
|
96
|
-
raise
|
99
|
+
raise Error, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
97
100
|
end
|
98
101
|
|
99
102
|
def read_e_o_c_d(buf) #:nodoc:
|
@@ -105,19 +108,19 @@ module Zip
|
|
105
108
|
@size_in_bytes = Entry.read_zip_long(buf)
|
106
109
|
@cdir_offset = Entry.read_zip_long(buf)
|
107
110
|
comment_length = Entry.read_zip_short(buf)
|
108
|
-
@comment = if comment_length <= 0
|
111
|
+
@comment = if comment_length.to_i <= 0
|
109
112
|
buf.slice!(0, buf.size)
|
110
113
|
else
|
111
114
|
buf.read(comment_length)
|
112
115
|
end
|
113
|
-
raise
|
116
|
+
raise Error, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
114
117
|
end
|
115
118
|
|
116
119
|
def read_central_directory_entries(io) #:nodoc:
|
117
120
|
begin
|
118
121
|
io.seek(@cdir_offset, IO::SEEK_SET)
|
119
122
|
rescue Errno::EINVAL
|
120
|
-
raise
|
123
|
+
raise Error, "Zip consistency problem while reading central directory entry"
|
121
124
|
end
|
122
125
|
@entry_set = EntrySet.new
|
123
126
|
@size.times do
|
@@ -137,7 +140,7 @@ module Zip
|
|
137
140
|
|
138
141
|
def get_e_o_c_d(buf) #:nodoc:
|
139
142
|
sig_index = buf.rindex([END_OF_CDS].pack('V'))
|
140
|
-
raise
|
143
|
+
raise Error, "Zip end of central directory signature not found" unless sig_index
|
141
144
|
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
142
145
|
|
143
146
|
def buf.read(count)
|
@@ -162,9 +165,9 @@ module Zip
|
|
162
165
|
|
163
166
|
def get_64_e_o_c_d(buf) #:nodoc:
|
164
167
|
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
165
|
-
raise
|
168
|
+
raise Error, "Zip64 end of central directory signature not found" unless zip_64_start
|
166
169
|
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
167
|
-
raise
|
170
|
+
raise Error, "Zip64 end of central directory signature locator not found" unless zip_64_locator
|
168
171
|
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
169
172
|
|
170
173
|
def buf.read(count)
|
@@ -179,7 +182,7 @@ module Zip
|
|
179
182
|
@entry_set.each(&proc)
|
180
183
|
end
|
181
184
|
|
182
|
-
# Returns the number of entries in the central directory (and
|
185
|
+
# Returns the number of entries in the central directory (and
|
183
186
|
# consequently in the zip archive).
|
184
187
|
def size
|
185
188
|
@entry_set.size
|
@@ -189,7 +192,7 @@ module Zip
|
|
189
192
|
cdir = new
|
190
193
|
cdir.read_from_stream(io)
|
191
194
|
return cdir
|
192
|
-
rescue
|
195
|
+
rescue Error
|
193
196
|
return nil
|
194
197
|
end
|
195
198
|
|
data/lib/zip/deflater.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Zip
|
2
2
|
class Deflater < Compressor #:nodoc:all
|
3
3
|
|
4
|
-
def initialize(output_stream, level =
|
4
|
+
def initialize(output_stream, level = Zip.default_compression)
|
5
5
|
super()
|
6
6
|
@output_stream = output_stream
|
7
7
|
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
|
data/lib/zip/entry.rb
CHANGED
@@ -15,7 +15,7 @@ module Zip
|
|
15
15
|
|
16
16
|
def set_default_vars_values
|
17
17
|
@local_header_offset = 0
|
18
|
-
@local_header_size =
|
18
|
+
@local_header_size = nil # not known until local entry is created or read
|
19
19
|
@internal_file_attributes = 1
|
20
20
|
@external_file_attributes = 0
|
21
21
|
@header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
@@ -46,7 +46,7 @@ module Zip
|
|
46
46
|
|
47
47
|
def check_name(name)
|
48
48
|
if name.start_with?('/')
|
49
|
-
raise ::Zip::
|
49
|
+
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -92,7 +92,7 @@ module Zip
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def file_type_is?(type)
|
95
|
-
raise
|
95
|
+
raise InternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
|
96
96
|
@ftype == type
|
97
97
|
end
|
98
98
|
|
@@ -130,9 +130,9 @@ module Zip
|
|
130
130
|
# check before rewriting an entry (after file sizes are known)
|
131
131
|
# that we didn't change the header size (and thus clobber file data or something)
|
132
132
|
def verify_local_header_size!
|
133
|
-
return if @local_header_size
|
133
|
+
return if @local_header_size.nil?
|
134
134
|
new_size = calculate_local_header_size
|
135
|
-
raise
|
135
|
+
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
|
136
136
|
end
|
137
137
|
|
138
138
|
def cdir_header_size #:nodoc:all
|
@@ -177,18 +177,23 @@ module Zip
|
|
177
177
|
end
|
178
178
|
|
179
179
|
def read_c_dir_entry(io) #:nodoc:all
|
180
|
-
|
180
|
+
path = if io.is_a?(::IO)
|
181
|
+
io.path
|
182
|
+
else
|
183
|
+
io
|
184
|
+
end
|
185
|
+
entry = new(path)
|
181
186
|
entry.read_c_dir_entry(io)
|
182
187
|
entry
|
183
|
-
rescue
|
188
|
+
rescue Error
|
184
189
|
nil
|
185
190
|
end
|
186
191
|
|
187
192
|
def read_local_entry(io)
|
188
|
-
entry = self.new
|
193
|
+
entry = self.new(io)
|
189
194
|
entry.read_local_entry(io)
|
190
195
|
entry
|
191
|
-
rescue
|
196
|
+
rescue Error
|
192
197
|
nil
|
193
198
|
end
|
194
199
|
|
@@ -217,13 +222,13 @@ module Zip
|
|
217
222
|
static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
218
223
|
|
219
224
|
unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
|
220
|
-
raise
|
225
|
+
raise Error, "Premature end of file. Not enough data for zip entry local header"
|
221
226
|
end
|
222
227
|
|
223
228
|
unpack_local_entry(static_sized_fields_buf)
|
224
229
|
|
225
230
|
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
226
|
-
raise ::Zip::
|
231
|
+
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
227
232
|
end
|
228
233
|
set_time(@last_mod_date, @last_mod_time)
|
229
234
|
|
@@ -233,7 +238,7 @@ module Zip
|
|
233
238
|
@name.gsub!('\\', '/')
|
234
239
|
|
235
240
|
if extra && extra.bytesize != @extra_length
|
236
|
-
raise ::Zip::
|
241
|
+
raise ::Zip::Error, "Truncated local zip entry header"
|
237
242
|
else
|
238
243
|
if ::Zip::ExtraField === @extra
|
239
244
|
@extra.merge(extra)
|
@@ -327,19 +332,19 @@ module Zip
|
|
327
332
|
|
328
333
|
def check_c_dir_entry_static_header_length(buf)
|
329
334
|
unless buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
330
|
-
raise
|
335
|
+
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
|
331
336
|
end
|
332
337
|
end
|
333
338
|
|
334
339
|
def check_c_dir_entry_signature
|
335
340
|
unless header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
336
|
-
raise
|
341
|
+
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
337
342
|
end
|
338
343
|
end
|
339
344
|
|
340
345
|
def check_c_dir_entry_comment_size
|
341
346
|
unless @comment && @comment.bytesize == @comment_length
|
342
|
-
raise ::Zip::
|
347
|
+
raise ::Zip::Error, "Truncated cdir zip entry header"
|
343
348
|
end
|
344
349
|
end
|
345
350
|
|
@@ -363,7 +368,6 @@ module Zip
|
|
363
368
|
check_c_dir_entry_comment_size
|
364
369
|
set_ftype_from_c_dir_entry
|
365
370
|
parse_zip64_extra(false)
|
366
|
-
@local_header_size = calculate_local_header_size
|
367
371
|
end
|
368
372
|
|
369
373
|
def file_stat(path) # :nodoc:
|
@@ -422,7 +426,10 @@ module Zip
|
|
422
426
|
(zip64 && zip64.disk_start_number) ? 0xFFFF : 0, # disk number start
|
423
427
|
@internal_file_attributes, # file type (binary=0, text=1)
|
424
428
|
@external_file_attributes, # native filesystem attributes
|
425
|
-
(zip64 && zip64.relative_header_offset) ? 0xFFFFFFFF : @local_header_offset
|
429
|
+
(zip64 && zip64.relative_header_offset) ? 0xFFFFFFFF : @local_header_offset,
|
430
|
+
@name,
|
431
|
+
@extra,
|
432
|
+
@comment
|
426
433
|
].pack('VCCvvvvvVVVvvvvvVV')
|
427
434
|
end
|
428
435
|
|
@@ -511,7 +518,7 @@ module Zip
|
|
511
518
|
end
|
512
519
|
:file
|
513
520
|
when 'directory'
|
514
|
-
@name +=
|
521
|
+
@name += '/' unless name_is_directory?
|
515
522
|
:directory
|
516
523
|
when 'link'
|
517
524
|
if name_is_directory?
|
@@ -530,9 +537,9 @@ module Zip
|
|
530
537
|
|
531
538
|
def write_to_zip_output_stream(zip_output_stream) #:nodoc:all
|
532
539
|
if @ftype == :directory
|
533
|
-
zip_output_stream.put_next_entry(self)
|
540
|
+
zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED)
|
534
541
|
elsif @filepath
|
535
|
-
zip_output_stream.put_next_entry(self, nil, nil,
|
542
|
+
zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::DEFLATED)
|
536
543
|
get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) }
|
537
544
|
else
|
538
545
|
zip_output_stream.copy_raw_entry(self)
|
@@ -546,7 +553,11 @@ module Zip
|
|
546
553
|
end
|
547
554
|
|
548
555
|
def get_raw_input_stream(&block)
|
549
|
-
|
556
|
+
if @zipfile.is_a?(::IO) || @zipfile.is_a?(::StringIO)
|
557
|
+
yield @zipfile
|
558
|
+
else
|
559
|
+
::File.open(@zipfile, "rb", &block)
|
560
|
+
end
|
550
561
|
end
|
551
562
|
|
552
563
|
private
|
@@ -558,8 +569,8 @@ module Zip
|
|
558
569
|
end
|
559
570
|
|
560
571
|
def create_file(dest_path, continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
561
|
-
if ::File.
|
562
|
-
raise ::Zip::
|
572
|
+
if ::File.exist?(dest_path) && !yield(self, dest_path)
|
573
|
+
raise ::Zip::DestinationFileExistsError,
|
563
574
|
"Destination '#{dest_path}' already exists"
|
564
575
|
end
|
565
576
|
::File.open(dest_path, "wb") do |os|
|
@@ -576,11 +587,11 @@ module Zip
|
|
576
587
|
|
577
588
|
def create_directory(dest_path)
|
578
589
|
return if ::File.directory?(dest_path)
|
579
|
-
if ::File.
|
590
|
+
if ::File.exist?(dest_path)
|
580
591
|
if block_given? && yield(self, dest_path)
|
581
592
|
::FileUtils::rm_f dest_path
|
582
593
|
else
|
583
|
-
raise ::Zip::
|
594
|
+
raise ::Zip::DestinationFileExistsError,
|
584
595
|
"Cannot create directory '#{dest_path}'. "+
|
585
596
|
"A file already exists with that name"
|
586
597
|
end
|
@@ -605,12 +616,12 @@ module Zip
|
|
605
616
|
if ::File.readlink(dest_path) == linkto
|
606
617
|
return
|
607
618
|
else
|
608
|
-
raise
|
619
|
+
raise ::Zip::DestinationFileExistsError,
|
609
620
|
"Cannot create symlink '#{dest_path}'. "+
|
610
621
|
"A symlink already exists with that name"
|
611
622
|
end
|
612
623
|
else
|
613
|
-
raise
|
624
|
+
raise ::Zip::DestinationFileExistsError,
|
614
625
|
"Cannot create symlink '#{dest_path}'. "+
|
615
626
|
"A file already exists with that name"
|
616
627
|
end
|
@@ -633,6 +644,7 @@ module Zip
|
|
633
644
|
|
634
645
|
# create a zip64 extra information field if we need one
|
635
646
|
def prep_zip64_extra(for_local_header) #:nodoc:all
|
647
|
+
return unless ::Zip.write_zip64_support
|
636
648
|
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
637
649
|
unless for_local_header
|
638
650
|
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF
|
data/lib/zip/entry_set.rb
CHANGED
data/lib/zip/errors.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Zip
|
2
|
-
class
|
3
|
-
class
|
4
|
-
class
|
5
|
-
class
|
6
|
-
class
|
7
|
-
class
|
2
|
+
class Error < StandardError; end
|
3
|
+
class EntryExistsError < Error; end
|
4
|
+
class DestinationFileExistsError < Error; end
|
5
|
+
class CompressionMethodError < Error; end
|
6
|
+
class EntryNameError < Error; end
|
7
|
+
class InternalError < Error; end
|
8
8
|
end
|
data/lib/zip/extra_field.rb
CHANGED
@@ -52,7 +52,7 @@ module Zip
|
|
52
52
|
|
53
53
|
def create(name)
|
54
54
|
unless field_class = ID_MAP.values.find { |k| k.name == name }
|
55
|
-
raise
|
55
|
+
raise Error, "Unknown extra field '#{name}'"
|
56
56
|
end
|
57
57
|
self[name] = field_class.new
|
58
58
|
end
|
@@ -60,17 +60,19 @@ module Zip
|
|
60
60
|
# place Unknown last, so "extra" data that is missing the proper signature/size
|
61
61
|
# does not prevent known fields from being read back in
|
62
62
|
def ordered_values
|
63
|
-
|
63
|
+
result = []
|
64
|
+
self.each { |k,v| k == 'Unknown' ? result.push(v) : result.unshift(v) }
|
65
|
+
result
|
64
66
|
end
|
65
67
|
|
66
68
|
def to_local_bin
|
67
|
-
ordered_values.map { |v| v.to_local_bin.force_encoding('BINARY') }.join
|
69
|
+
ordered_values.map! { |v| v.to_local_bin.force_encoding('BINARY') }.join
|
68
70
|
end
|
69
71
|
|
70
72
|
alias :to_s :to_local_bin
|
71
73
|
|
72
74
|
def to_c_dir_bin
|
73
|
-
ordered_values.map { |v| v.to_c_dir_bin.force_encoding('BINARY') }.join
|
75
|
+
ordered_values.map! { |v| v.to_c_dir_bin.force_encoding('BINARY') }.join
|
74
76
|
end
|
75
77
|
|
76
78
|
def c_dir_size
|
@@ -88,6 +90,7 @@ end
|
|
88
90
|
|
89
91
|
require 'zip/extra_field/generic'
|
90
92
|
require 'zip/extra_field/universal_time'
|
93
|
+
require 'zip/extra_field/old_unix'
|
91
94
|
require 'zip/extra_field/unix'
|
92
95
|
require 'zip/extra_field/zip64'
|
93
96
|
require 'zip/extra_field/zip64_placeholder'
|
@@ -7,7 +7,7 @@ module Zip
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.name
|
10
|
-
self.to_s.split("::")[-1]
|
10
|
+
@name ||= self.to_s.split("::")[-1]
|
11
11
|
end
|
12
12
|
|
13
13
|
# return field [size, content] or false
|
@@ -32,12 +32,12 @@ module Zip
|
|
32
32
|
|
33
33
|
def to_local_bin
|
34
34
|
s = pack_for_local
|
35
|
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v")
|
35
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") << s
|
36
36
|
end
|
37
37
|
|
38
38
|
def to_c_dir_bin
|
39
39
|
s = pack_for_c_dir
|
40
|
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v")
|
40
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") << s
|
41
41
|
end
|
42
42
|
end
|
43
|
-
end
|
43
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Zip
|
2
|
+
# Olf Info-ZIP Extra for UNIX uid/gid and file timestampes
|
3
|
+
class ExtraField::OldUnix < ExtraField::Generic
|
4
|
+
HEADER_ID = "UX"
|
5
|
+
register_map
|
6
|
+
|
7
|
+
def initialize(binstr = nil)
|
8
|
+
@uid = 0
|
9
|
+
@gid = 0
|
10
|
+
@atime = nil
|
11
|
+
@mtime = nil
|
12
|
+
binstr and merge(binstr)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :uid, :gid, :atime, :mtime
|
16
|
+
|
17
|
+
def merge(binstr)
|
18
|
+
return if binstr.empty?
|
19
|
+
size, content = initial_parse(binstr)
|
20
|
+
# size: 0 for central directory. 4 for local header
|
21
|
+
return if (!size || size == 0)
|
22
|
+
atime, mtime, uid, gid = content.unpack("VVvv")
|
23
|
+
@uid ||= uid
|
24
|
+
@gid ||= gid
|
25
|
+
@atime ||= atime
|
26
|
+
@mtime ||= mtime
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
@uid == other.uid &&
|
31
|
+
@gid == other.gid &&
|
32
|
+
@atime == other.atime &&
|
33
|
+
@mtime == other.mtime
|
34
|
+
end
|
35
|
+
|
36
|
+
def pack_for_local
|
37
|
+
[@atime, @mtime, @uid, @gid].pack("VVvv")
|
38
|
+
end
|
39
|
+
|
40
|
+
def pack_for_c_dir
|
41
|
+
[@atime, @mtime].pack("VV")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/zip/file.rb
CHANGED
@@ -70,7 +70,7 @@ module Zip
|
|
70
70
|
@comment = ''
|
71
71
|
@create = create
|
72
72
|
case
|
73
|
-
when ::File.
|
73
|
+
when !buffer && ::File.exist?(file_name)
|
74
74
|
@create = nil
|
75
75
|
@exist_file_perms = ::File.stat(file_name).mode
|
76
76
|
::File.open(name, 'rb') do |f|
|
@@ -79,7 +79,7 @@ module Zip
|
|
79
79
|
when create
|
80
80
|
@entry_set = EntrySet.new
|
81
81
|
else
|
82
|
-
raise
|
82
|
+
raise Error, "File #{file_name} not found"
|
83
83
|
end
|
84
84
|
@stored_entries = @entry_set.dup
|
85
85
|
@stored_comment = @comment
|
@@ -94,40 +94,38 @@ module Zip
|
|
94
94
|
# ruby's builtin File.open method.
|
95
95
|
def open(file_name, create = nil)
|
96
96
|
zf = ::Zip::File.new(file_name, create)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
else
|
104
|
-
zf
|
97
|
+
return zf unless block_given?
|
98
|
+
begin
|
99
|
+
yield zf
|
100
|
+
ensure
|
101
|
+
zf.close
|
105
102
|
end
|
106
103
|
end
|
107
104
|
|
108
105
|
# Same as #open. But outputs data to a buffer instead of a file
|
109
106
|
def add_buffer
|
110
|
-
|
107
|
+
io = ::StringIO.new('')
|
108
|
+
zf = ::Zip::File.new(io, true, true)
|
111
109
|
yield zf
|
112
|
-
zf.write_buffer
|
110
|
+
zf.write_buffer(io)
|
113
111
|
end
|
114
112
|
|
115
113
|
# Like #open, but reads zip archive contents from a String or open IO
|
116
114
|
# stream, and outputs data to a buffer.
|
117
|
-
# (This can be used to extract data from a
|
115
|
+
# (This can be used to extract data from a
|
118
116
|
# downloaded zip archive without first saving it to disk.)
|
119
117
|
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
|
118
|
+
unless io.is_a?(IO) || io.is_a?(String) || io.is_a?(Tempfile)
|
119
|
+
raise "Zip::File.open_buffer expects an argument of class String, IO, or Tempfile. Found: #{io.class}"
|
122
120
|
end
|
123
|
-
zf = ::Zip::File.new('', true, true, options)
|
124
121
|
if io.is_a?(::String)
|
125
122
|
require 'stringio'
|
126
123
|
io = ::StringIO.new(io)
|
127
124
|
end
|
125
|
+
zf = ::Zip::File.new(io, true, true, options)
|
128
126
|
zf.read_from_stream(io)
|
129
127
|
yield zf
|
130
|
-
zf.write_buffer
|
128
|
+
zf.write_buffer(io)
|
131
129
|
end
|
132
130
|
|
133
131
|
# Iterates over the contents of the ZipFile. This is more efficient
|
@@ -196,7 +194,7 @@ module Zip
|
|
196
194
|
|
197
195
|
# Splits an archive into parts with segment size
|
198
196
|
def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
|
199
|
-
raise
|
197
|
+
raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
|
200
198
|
raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
|
201
199
|
zip_file_size = ::File.size(zip_file_name)
|
202
200
|
segment_size = get_segment_size_for_split(segment_size)
|
@@ -227,24 +225,24 @@ module Zip
|
|
227
225
|
|
228
226
|
# Returns an output stream to the specified entry. If entry is not an instance
|
229
227
|
# 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
|
228
|
+
# specified. If a block is passed the stream object is passed to the block and
|
229
|
+
# the stream is automatically closed afterwards just as with ruby's builtin
|
232
230
|
# File.open method.
|
233
|
-
def get_output_stream(entry,
|
234
|
-
|
231
|
+
def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
|
232
|
+
new_entry =
|
235
233
|
if entry.kind_of?(Entry)
|
236
234
|
entry
|
237
235
|
else
|
238
236
|
Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
|
239
237
|
end
|
240
|
-
if
|
238
|
+
if new_entry.directory?
|
241
239
|
raise ArgumentError,
|
242
|
-
"cannot open stream to directory entry - '#{
|
240
|
+
"cannot open stream to directory entry - '#{new_entry}'"
|
243
241
|
end
|
244
|
-
|
245
|
-
|
246
|
-
@entry_set <<
|
247
|
-
|
242
|
+
new_entry.unix_perms = permission_int
|
243
|
+
zip_streamable_entry = StreamableStream.new(new_entry)
|
244
|
+
@entry_set << zip_streamable_entry
|
245
|
+
zip_streamable_entry.get_output_stream(&aProc)
|
248
246
|
end
|
249
247
|
|
250
248
|
# Returns the name of the zip archive
|
@@ -258,12 +256,13 @@ module Zip
|
|
258
256
|
end
|
259
257
|
|
260
258
|
# Convenience method for adding the contents of a file to the archive
|
261
|
-
def add(entry,
|
262
|
-
continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
|
259
|
+
def add(entry, src_path, &continue_on_exists_proc)
|
260
|
+
continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
|
263
261
|
check_entry_exists(entry, continue_on_exists_proc, "add")
|
264
|
-
|
265
|
-
|
266
|
-
|
262
|
+
new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
|
263
|
+
new_entry.gather_fileinfo_from_srcpath(src_path)
|
264
|
+
new_entry.dirty = true
|
265
|
+
@entry_set << new_entry
|
267
266
|
end
|
268
267
|
|
269
268
|
# Removes the specified entry.
|
@@ -290,7 +289,7 @@ module Zip
|
|
290
289
|
|
291
290
|
# Extracts entry to file dest_path.
|
292
291
|
def extract(entry, dest_path, &block)
|
293
|
-
block
|
292
|
+
block ||= proc { ::Zip.on_exists_proc }
|
294
293
|
found_entry = get_entry(entry)
|
295
294
|
found_entry.extract(dest_path, &block)
|
296
295
|
end
|
@@ -298,9 +297,9 @@ module Zip
|
|
298
297
|
# Commits changes that has been made since the previous commit to
|
299
298
|
# the zip archive.
|
300
299
|
def commit
|
301
|
-
return
|
302
|
-
on_success_replace do |
|
303
|
-
::Zip::OutputStream.open(
|
300
|
+
return unless commit_required?
|
301
|
+
on_success_replace do |tmp_file|
|
302
|
+
::Zip::OutputStream.open(tmp_file) do |zos|
|
304
303
|
@entry_set.each do |e|
|
305
304
|
e.write_to_zip_output_stream(zos)
|
306
305
|
e.dirty = false
|
@@ -313,8 +312,8 @@ module Zip
|
|
313
312
|
end
|
314
313
|
|
315
314
|
# Write buffer write changes to buffer and return
|
316
|
-
def write_buffer
|
317
|
-
OutputStream.write_buffer do |zos|
|
315
|
+
def write_buffer(io)
|
316
|
+
::Zip::OutputStream.write_buffer(io) do |zos|
|
318
317
|
@entry_set.each { |e| e.write_to_zip_output_stream(zos) }
|
319
318
|
zos.comment = comment
|
320
319
|
end
|
@@ -331,7 +330,7 @@ module Zip
|
|
331
330
|
@entry_set.each do |e|
|
332
331
|
return true if e.dirty
|
333
332
|
end
|
334
|
-
@comment != @stored_comment || @entry_set != @stored_entries || @create == File::CREATE
|
333
|
+
@comment != @stored_comment || @entry_set != @stored_entries || @create == ::Zip::File::CREATE
|
335
334
|
end
|
336
335
|
|
337
336
|
# Searches for entry with the specified name. Returns nil if
|
@@ -388,7 +387,7 @@ module Zip
|
|
388
387
|
if continue_on_exists_proc.call
|
389
388
|
remove get_entry(entryName)
|
390
389
|
else
|
391
|
-
raise
|
390
|
+
raise ::Zip::EntryExistsError,
|
392
391
|
procedureName + " failed. Entry #{entryName} already exists"
|
393
392
|
end
|
394
393
|
end
|
@@ -401,7 +400,7 @@ module Zip
|
|
401
400
|
end
|
402
401
|
|
403
402
|
def on_success_replace
|
404
|
-
tmpfile
|
403
|
+
tmpfile = get_tempfile
|
405
404
|
tmp_filename = tmpfile.path
|
406
405
|
tmpfile.close
|
407
406
|
if yield tmp_filename
|
data/lib/zip/inflater.rb
CHANGED
@@ -4,26 +4,42 @@ module Zip
|
|
4
4
|
super
|
5
5
|
@zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
6
6
|
@output_buffer = ''
|
7
|
+
@output_buffer_pos = 0
|
7
8
|
@has_returned_empty_string = false
|
8
9
|
end
|
9
10
|
|
10
11
|
def sysread(number_of_bytes = nil, buf = '')
|
12
|
+
buf ||= ''
|
13
|
+
buf.clear
|
11
14
|
readEverything = number_of_bytes.nil?
|
12
|
-
|
15
|
+
if readEverything
|
16
|
+
buf << @output_buffer[@output_buffer_pos...@output_buffer.bytesize]
|
17
|
+
|
18
|
+
move_output_buffer_pos(buf.bytesize)
|
19
|
+
else
|
20
|
+
buf << @output_buffer[@output_buffer_pos, number_of_bytes]
|
21
|
+
|
22
|
+
move_output_buffer_pos(buf.bytesize)
|
23
|
+
|
24
|
+
if buf.bytesize == number_of_bytes
|
25
|
+
return buf
|
26
|
+
end
|
27
|
+
end
|
28
|
+
while readEverything || buf.bytesize + @output_buffer.bytesize < number_of_bytes
|
13
29
|
break if internal_input_finished?
|
14
|
-
@output_buffer << internal_produce_input
|
30
|
+
@output_buffer << internal_produce_input
|
15
31
|
end
|
16
|
-
return value_when_finished if @output_buffer.bytesize == 0 && input_finished?
|
17
|
-
end_index = number_of_bytes.nil? ? @output_buffer.bytesize : number_of_bytes
|
18
|
-
@output_buffer
|
32
|
+
return value_when_finished(number_of_bytes, buf) if @output_buffer.bytesize == 0 && input_finished?
|
33
|
+
end_index = (number_of_bytes.nil? ? @output_buffer.bytesize : number_of_bytes) - buf.bytesize
|
34
|
+
data = @output_buffer[0...end_index]
|
35
|
+
|
36
|
+
move_output_buffer_pos(data.bytesize)
|
37
|
+
|
38
|
+
buf << data
|
19
39
|
end
|
20
40
|
|
21
41
|
def produce_input
|
22
|
-
|
23
|
-
internal_produce_input
|
24
|
-
else
|
25
|
-
@output_buffer.slice!(0...(@output_buffer.length))
|
26
|
-
end
|
42
|
+
sysread()
|
27
43
|
end
|
28
44
|
|
29
45
|
# to be used with produce_input, not read (as read may still have more data cached)
|
@@ -37,7 +53,16 @@ module Zip
|
|
37
53
|
|
38
54
|
private
|
39
55
|
|
40
|
-
def
|
56
|
+
def move_output_buffer_pos(inc)
|
57
|
+
@output_buffer_pos += inc
|
58
|
+
if @output_buffer_pos == @output_buffer.bytesize
|
59
|
+
@output_buffer.clear
|
60
|
+
@output_buffer_pos = 0
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def internal_produce_input
|
65
|
+
buf = ''
|
41
66
|
retried = 0
|
42
67
|
begin
|
43
68
|
@zlib_inflater.inflate(@input_stream.read(Decompressor::CHUNK_SIZE, buf))
|
@@ -52,10 +77,14 @@ module Zip
|
|
52
77
|
@zlib_inflater.finished?
|
53
78
|
end
|
54
79
|
|
55
|
-
def value_when_finished # mimic behaviour of ruby File object.
|
56
|
-
|
57
|
-
|
58
|
-
|
80
|
+
def value_when_finished(number_of_bytes, buf) # mimic behaviour of ruby File object.
|
81
|
+
if number_of_bytes.nil?
|
82
|
+
buf
|
83
|
+
elsif buf.bytesize == 0
|
84
|
+
nil
|
85
|
+
else
|
86
|
+
buf
|
87
|
+
end
|
59
88
|
end
|
60
89
|
end
|
61
90
|
end
|
data/lib/zip/input_stream.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
module Zip
|
2
2
|
# InputStream is the basic class for reading zip entries in a
|
3
3
|
# zip file. It is possible to create a InputStream object directly,
|
4
|
-
# passing the zip file name to the constructor, but more often than not
|
4
|
+
# passing the zip file name to the constructor, but more often than not
|
5
5
|
# the InputStream will be obtained from a File (perhaps using the
|
6
|
-
# ZipFileSystem interface) object for a particular entry in the zip
|
6
|
+
# ZipFileSystem interface) object for a particular entry in the zip
|
7
7
|
# archive.
|
8
8
|
#
|
9
9
|
# A InputStream inherits IOExtras::AbstractInputStream in order
|
10
|
-
# to provide an IO-like interface for reading from a single zip
|
11
|
-
# entry. Beyond methods for mimicking an IO-object it contains
|
12
|
-
# the method get_next_entry for iterating through the entries of
|
10
|
+
# to provide an IO-like interface for reading from a single zip
|
11
|
+
# entry. Beyond methods for mimicking an IO-object it contains
|
12
|
+
# the method get_next_entry for iterating through the entries of
|
13
13
|
# an archive. get_next_entry returns a Entry object that describes
|
14
14
|
# the zip entry the InputStream is currently reading from.
|
15
15
|
#
|
16
|
-
# Example that creates a zip archive with ZipOutputStream and reads it
|
16
|
+
# Example that creates a zip archive with ZipOutputStream and reads it
|
17
17
|
# back again with a InputStream.
|
18
18
|
#
|
19
19
|
# require 'zip'
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# Zip::OutputStream.open("my.zip") do |io|
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# io.put_next_entry("first_entry.txt")
|
24
24
|
# io.write "Hello world!"
|
25
|
-
#
|
25
|
+
#
|
26
26
|
# io.put_next_entry("adir/first_entry.txt")
|
27
27
|
# io.write "Hello again!"
|
28
28
|
# end
|
29
29
|
#
|
30
|
-
#
|
30
|
+
#
|
31
31
|
# Zip::InputStream.open("my.zip") do |io|
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# while (entry = io.get_next_entry)
|
34
34
|
# puts "Contents of #{entry.name}: '#{io.read}'"
|
35
35
|
# end
|
36
36
|
# end
|
37
37
|
#
|
38
|
-
# java.util.zip.ZipInputStream is the original inspiration for this
|
38
|
+
# java.util.zip.ZipInputStream is the original inspiration for this
|
39
39
|
# class.
|
40
40
|
|
41
41
|
class InputStream
|
@@ -60,7 +60,7 @@ module Zip
|
|
60
60
|
|
61
61
|
# Returns a Entry object. It is necessary to call this
|
62
62
|
# method on a newly created InputStream before reading from
|
63
|
-
# the first entry in the archive. Returns nil when there are
|
63
|
+
# the first entry in the archive. Returns nil when there are
|
64
64
|
# no more entries.
|
65
65
|
def get_next_entry
|
66
66
|
@archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry
|
@@ -112,7 +112,9 @@ module Zip
|
|
112
112
|
def get_io(io_or_file, offset = 0)
|
113
113
|
case io_or_file
|
114
114
|
when IO, StringIO
|
115
|
-
io_or_file
|
115
|
+
io = io_or_file.dup
|
116
|
+
io.seek(offset, ::IO::SEEK_SET)
|
117
|
+
io
|
116
118
|
else
|
117
119
|
file = ::File.open(io_or_file, 'rb')
|
118
120
|
file.seek(offset, ::IO::SEEK_SET)
|
@@ -136,7 +138,7 @@ module Zip
|
|
136
138
|
when @current_entry.compression_method == ::Zip::Entry::DEFLATED
|
137
139
|
::Zip::Inflater.new(@archive_io)
|
138
140
|
else
|
139
|
-
raise
|
141
|
+
raise ::Zip::CompressionMethodError,
|
140
142
|
"Unsupported compression method #{@current_entry.compression_method}"
|
141
143
|
end
|
142
144
|
end
|
data/lib/zip/output_stream.rb
CHANGED
@@ -24,14 +24,17 @@ module Zip
|
|
24
24
|
|
25
25
|
# Opens the indicated zip file. If a file with that name already
|
26
26
|
# exists it will be overwritten.
|
27
|
-
def initialize(
|
27
|
+
def initialize(file_name, stream=false)
|
28
28
|
super()
|
29
|
-
@
|
30
|
-
if stream
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
@file_name = file_name
|
30
|
+
@output_stream = if stream
|
31
|
+
iostream = @file_name.dup
|
32
|
+
iostream.reopen
|
33
|
+
iostream.rewind
|
34
|
+
iostream
|
35
|
+
else
|
36
|
+
::File.new(@file_name, "wb")
|
37
|
+
end
|
35
38
|
@entry_set = ::Zip::EntrySet.new
|
36
39
|
@compressor = ::Zip::NullCompressor.instance
|
37
40
|
@closed = false
|
@@ -43,19 +46,19 @@ module Zip
|
|
43
46
|
# stream is passed to the block and closed when the block
|
44
47
|
# returns.
|
45
48
|
class << self
|
46
|
-
def open(
|
47
|
-
return new(
|
48
|
-
zos = new(
|
49
|
+
def open(file_name)
|
50
|
+
return new(file_name) unless block_given?
|
51
|
+
zos = new(file_name)
|
49
52
|
yield zos
|
50
53
|
ensure
|
51
54
|
zos.close if zos
|
52
55
|
end
|
53
56
|
|
54
|
-
|
55
|
-
def write_buffer
|
56
|
-
zos = new(
|
57
|
+
# Same as #open but writes to a filestream instead
|
58
|
+
def write_buffer(io)
|
59
|
+
zos = new(io, true)
|
57
60
|
yield zos
|
58
|
-
|
61
|
+
zos.close_buffer
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
@@ -79,35 +82,36 @@ module Zip
|
|
79
82
|
@output_stream
|
80
83
|
end
|
81
84
|
|
82
|
-
|
85
|
+
# Closes the current entry and opens a new for writing.
|
83
86
|
# +entry+ can be a ZipEntry object or a string.
|
84
|
-
def put_next_entry(
|
85
|
-
raise
|
86
|
-
if
|
87
|
-
new_entry =
|
87
|
+
def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression)
|
88
|
+
raise Error, "zip stream is closed" if @closed
|
89
|
+
if entry_name.kind_of?(Entry)
|
90
|
+
new_entry = entry_name
|
88
91
|
else
|
89
|
-
new_entry = Entry.new(@
|
92
|
+
new_entry = Entry.new(@file_name, entry_name.to_s)
|
90
93
|
end
|
91
|
-
new_entry.comment = comment
|
92
|
-
|
94
|
+
new_entry.comment = comment unless comment.nil?
|
95
|
+
unless extra.nil?
|
93
96
|
new_entry.extra = ExtraField === extra ? extra : ExtraField.new(extra.to_s)
|
94
97
|
end
|
95
|
-
new_entry.compression_method = compression_method
|
98
|
+
new_entry.compression_method = compression_method unless compression_method.nil?
|
96
99
|
init_next_entry(new_entry, level)
|
97
100
|
@current_entry = new_entry
|
98
101
|
end
|
99
102
|
|
100
103
|
def copy_raw_entry(entry)
|
101
104
|
entry = entry.dup
|
102
|
-
raise
|
103
|
-
raise
|
105
|
+
raise Error, "zip stream is closed" if @closed
|
106
|
+
raise Error, "entry is not a ZipEntry" unless entry.is_a?(Entry)
|
104
107
|
finalize_current_entry
|
105
108
|
@entry_set << entry
|
106
|
-
src_pos = entry.
|
109
|
+
src_pos = entry.local_header_offset
|
107
110
|
entry.write_local_entry(@output_stream)
|
108
111
|
@compressor = NullCompressor.instance
|
109
112
|
entry.get_raw_input_stream do |is|
|
110
113
|
is.seek(src_pos, IO::SEEK_SET)
|
114
|
+
::Zip::Entry.read_local_entry(is)
|
111
115
|
IOExtras.copy_stream_n(@output_stream, is, entry.compressed_size)
|
112
116
|
end
|
113
117
|
@compressor = NullCompressor.instance
|
@@ -123,10 +127,10 @@ module Zip
|
|
123
127
|
@current_entry.size = @compressor.size
|
124
128
|
@current_entry.crc = @compressor.crc
|
125
129
|
@current_entry = nil
|
126
|
-
@compressor = NullCompressor.instance
|
130
|
+
@compressor = ::Zip::NullCompressor.instance
|
127
131
|
end
|
128
132
|
|
129
|
-
def init_next_entry(entry, level =
|
133
|
+
def init_next_entry(entry, level = Zip.default_compression)
|
130
134
|
finalize_current_entry
|
131
135
|
@entry_set << entry
|
132
136
|
entry.write_local_entry(@output_stream)
|
@@ -135,10 +139,13 @@ module Zip
|
|
135
139
|
|
136
140
|
def get_compressor(entry, level)
|
137
141
|
case entry.compression_method
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
+
when Entry::DEFLATED then
|
143
|
+
::Zip::Deflater.new(@output_stream, level)
|
144
|
+
when Entry::STORED then
|
145
|
+
::Zip::PassThruCompressor.new(@output_stream)
|
146
|
+
else
|
147
|
+
raise ::Zip::CompressionMethodError,
|
148
|
+
"Invalid compression method: '#{entry.compression_method}'"
|
142
149
|
end
|
143
150
|
end
|
144
151
|
|
@@ -163,10 +170,13 @@ module Zip
|
|
163
170
|
end
|
164
171
|
|
165
172
|
public
|
173
|
+
|
166
174
|
# Modeled after IO.<<
|
167
175
|
def << (data)
|
168
176
|
@compressor << data
|
177
|
+
self
|
169
178
|
end
|
179
|
+
|
170
180
|
end
|
171
181
|
end
|
172
182
|
|
@@ -2,39 +2,44 @@ module Zip
|
|
2
2
|
class StreamableStream < DelegateClass(Entry) #nodoc:all
|
3
3
|
def initialize(entry)
|
4
4
|
super(entry)
|
5
|
-
|
6
|
-
|
5
|
+
dirname = if zipfile.is_a?(::String)
|
6
|
+
::File.dirname(zipfile)
|
7
|
+
else
|
8
|
+
'.'
|
9
|
+
end
|
10
|
+
@temp_file = Tempfile.new(::File.basename(name), dirname)
|
11
|
+
@temp_file.binmode
|
7
12
|
end
|
8
13
|
|
9
14
|
def get_output_stream
|
10
15
|
if block_given?
|
11
16
|
begin
|
12
|
-
yield(@
|
17
|
+
yield(@temp_file)
|
13
18
|
ensure
|
14
|
-
@
|
19
|
+
@temp_file.close
|
15
20
|
end
|
16
21
|
else
|
17
|
-
@
|
22
|
+
@temp_file
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
26
|
def get_input_stream
|
22
|
-
if
|
27
|
+
if !@temp_file.closed?
|
23
28
|
raise StandardError, "cannot open entry for reading while its open for writing - #{name}"
|
24
29
|
end
|
25
|
-
@
|
26
|
-
@
|
30
|
+
@temp_file.open # reopens tempfile from top
|
31
|
+
@temp_file.binmode
|
27
32
|
if block_given?
|
28
33
|
begin
|
29
|
-
yield(@
|
34
|
+
yield(@temp_file)
|
30
35
|
ensure
|
31
|
-
@
|
36
|
+
@temp_file.close
|
32
37
|
end
|
33
38
|
else
|
34
|
-
@
|
39
|
+
@temp_file
|
35
40
|
end
|
36
41
|
end
|
37
|
-
|
42
|
+
|
38
43
|
def write_to_zip_output_stream(aZipOutputStream)
|
39
44
|
aZipOutputStream.put_next_entry(self)
|
40
45
|
get_input_stream { |is| ::Zip::IOExtras.copy_stream(aZipOutputStream, is) }
|
data/lib/zip/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyzip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Simonov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- lib/zip/entry_set.rb
|
35
35
|
- lib/zip/errors.rb
|
36
36
|
- lib/zip/extra_field/generic.rb
|
37
|
+
- lib/zip/extra_field/old_unix.rb
|
37
38
|
- lib/zip/extra_field/universal_time.rb
|
38
39
|
- lib/zip/extra_field/unix.rb
|
39
40
|
- lib/zip/extra_field/zip64.rb
|