rubyzip 2.0.0 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +3 -0
- data/lib/zip.rb +3 -2
- data/lib/zip/central_directory.rb +9 -5
- data/lib/zip/constants.rb +52 -0
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/traditional_encryption.rb +9 -9
- data/lib/zip/decompressor.rb +19 -1
- data/lib/zip/dos_time.rb +12 -7
- data/lib/zip/entry.rb +57 -38
- data/lib/zip/entry_set.rb +2 -0
- data/lib/zip/errors.rb +1 -0
- data/lib/zip/extra_field.rb +11 -9
- data/lib/zip/extra_field/generic.rb +10 -9
- data/lib/zip/extra_field/ntfs.rb +4 -0
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +42 -12
- data/lib/zip/extra_field/unix.rb +3 -1
- data/lib/zip/extra_field/zip64.rb +4 -2
- data/lib/zip/file.rb +79 -54
- data/lib/zip/filesystem.rb +193 -177
- data/lib/zip/inflater.rb +24 -36
- data/lib/zip/input_stream.rb +33 -26
- data/lib/zip/ioextras.rb +1 -1
- data/lib/zip/ioextras/abstract_input_stream.rb +19 -8
- data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
- data/lib/zip/null_decompressor.rb +1 -9
- data/lib/zip/output_stream.rb +14 -5
- data/lib/zip/pass_thru_compressor.rb +2 -2
- data/lib/zip/pass_thru_decompressor.rb +13 -22
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +6 -10
- data/lib/zip/version.rb +1 -1
- data/samples/example.rb +2 -2
- data/samples/example_filesystem.rb +1 -1
- data/samples/gtk_ruby_zip.rb +19 -19
- data/samples/qtzip.rb +6 -6
- data/samples/write_simple.rb +2 -4
- data/samples/zipfind.rb +23 -22
- metadata +44 -21
data/lib/zip/entry_set.rb
CHANGED
@@ -50,6 +50,7 @@ module Zip
|
|
50
50
|
|
51
51
|
def ==(other)
|
52
52
|
return false unless other.kind_of?(EntrySet)
|
53
|
+
|
53
54
|
@entry_set.values == other.entry_set.values
|
54
55
|
end
|
55
56
|
|
@@ -60,6 +61,7 @@ module Zip
|
|
60
61
|
def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB)
|
61
62
|
entries.map do |entry|
|
62
63
|
next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags)
|
64
|
+
|
63
65
|
yield(entry) if block_given?
|
64
66
|
entry
|
65
67
|
end.compact
|
data/lib/zip/errors.rb
CHANGED
data/lib/zip/extra_field.rb
CHANGED
@@ -6,27 +6,27 @@ module Zip
|
|
6
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
11
|
if member?(field_name)
|
12
|
-
self[field_name].merge(binstr[
|
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
|
@@ -54,6 +55,7 @@ module Zip
|
|
54
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
|
|
@@ -1,9 +1,9 @@
|
|
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
|
@@ -12,18 +12,19 @@ module Zip
|
|
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
29
|
return false if v != other[k]
|
29
30
|
end
|
data/lib/zip/extra_field/ntfs.rb
CHANGED
@@ -19,6 +19,7 @@ module Zip
|
|
19
19
|
|
20
20
|
def merge(binstr)
|
21
21
|
return if binstr.empty?
|
22
|
+
|
22
23
|
size, content = initial_parse(binstr)
|
23
24
|
(size && content) || return
|
24
25
|
|
@@ -27,6 +28,7 @@ module Zip
|
|
27
28
|
|
28
29
|
tag1 = tags[1]
|
29
30
|
return unless tag1
|
31
|
+
|
30
32
|
ntfs_mtime, ntfs_atime, ntfs_ctime = tag1.unpack('Q<Q<Q<')
|
31
33
|
ntfs_mtime && @mtime ||= from_ntfs_time(ntfs_mtime)
|
32
34
|
ntfs_atime && @atime ||= from_ntfs_time(ntfs_atime)
|
@@ -65,12 +67,14 @@ module Zip
|
|
65
67
|
|
66
68
|
def parse_tags(content)
|
67
69
|
return {} if content.nil?
|
70
|
+
|
68
71
|
tags = {}
|
69
72
|
i = 0
|
70
73
|
while i < content.bytesize
|
71
74
|
tag, size = content[i, 4].unpack('vv')
|
72
75
|
i += 4
|
73
76
|
break unless tag && size
|
77
|
+
|
74
78
|
value = content[i, size]
|
75
79
|
i += size
|
76
80
|
tags[tag] = value
|
@@ -16,14 +16,16 @@ module Zip
|
|
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
22
|
return if !size || size == 0
|
23
|
+
|
22
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)
|
@@ -4,24 +4,54 @@ module Zip
|
|
4
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)
|
@@ -32,15 +62,15 @@ module Zip
|
|
32
62
|
|
33
63
|
def pack_for_local
|
34
64
|
s = [@flag].pack('C')
|
35
|
-
|
36
|
-
|
37
|
-
|
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
72
|
s = [@flag].pack('C')
|
43
|
-
|
73
|
+
s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
|
44
74
|
s
|
45
75
|
end
|
46
76
|
end
|
data/lib/zip/extra_field/unix.rb
CHANGED
@@ -14,12 +14,14 @@ module Zip
|
|
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
20
|
return if !size || size == 0
|
21
|
+
|
20
22
|
uid, gid = content.unpack('vv')
|
21
23
|
@uid ||= uid
|
22
|
-
@gid ||= gid
|
24
|
+
@gid ||= gid # rubocop:disable Naming/MemoizedInstanceVariableName
|
23
25
|
end
|
24
26
|
|
25
27
|
def ==(other)
|
@@ -9,7 +9,7 @@ module Zip
|
|
9
9
|
# unparsed binary; we don't actually know what this contains
|
10
10
|
# without looking for FFs in the associated file header
|
11
11
|
# call parse after initializing with a binary string
|
12
|
-
@content
|
12
|
+
@content = nil
|
13
13
|
@original_size = nil
|
14
14
|
@compressed_size = nil
|
15
15
|
@relative_header_offset = nil
|
@@ -26,6 +26,7 @@ module Zip
|
|
26
26
|
|
27
27
|
def merge(binstr)
|
28
28
|
return if binstr.empty?
|
29
|
+
|
29
30
|
_, @content = initial_parse(binstr)
|
30
31
|
end
|
31
32
|
|
@@ -45,13 +46,14 @@ module Zip
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def extract(size, format)
|
48
|
-
@content.slice!(0, size).
|
49
|
+
@content.slice!(0, size).unpack1(format)
|
49
50
|
end
|
50
51
|
private :extract
|
51
52
|
|
52
53
|
def pack_for_local
|
53
54
|
# local header entries must contain original size and compressed size; other fields do not apply
|
54
55
|
return '' unless @original_size && @compressed_size
|
56
|
+
|
55
57
|
[@original_size, @compressed_size].pack('Q<Q<')
|
56
58
|
end
|
57
59
|
|
data/lib/zip/file.rb
CHANGED
@@ -49,16 +49,25 @@ module Zip
|
|
49
49
|
MAX_SEGMENT_SIZE = 3_221_225_472
|
50
50
|
MIN_SEGMENT_SIZE = 65_536
|
51
51
|
DATA_BUFFER_SIZE = 8192
|
52
|
-
IO_METHODS = [:tell, :seek, :read, :close]
|
52
|
+
IO_METHODS = [:tell, :seek, :read, :eof, :close]
|
53
|
+
|
54
|
+
DEFAULT_OPTIONS = {
|
55
|
+
restore_ownership: false,
|
56
|
+
restore_permissions: false,
|
57
|
+
restore_times: false
|
58
|
+
}.freeze
|
53
59
|
|
54
60
|
attr_reader :name
|
55
61
|
|
56
|
-
# default -> false
|
62
|
+
# default -> false.
|
57
63
|
attr_accessor :restore_ownership
|
58
|
-
|
64
|
+
|
65
|
+
# default -> false, but will be set to true in a future version.
|
59
66
|
attr_accessor :restore_permissions
|
60
|
-
|
67
|
+
|
68
|
+
# default -> false, but will be set to true in a future version.
|
61
69
|
attr_accessor :restore_times
|
70
|
+
|
62
71
|
# Returns the zip files comment, if it has one
|
63
72
|
attr_accessor :comment
|
64
73
|
|
@@ -66,6 +75,7 @@ module Zip
|
|
66
75
|
# a new archive if it doesn't exist already.
|
67
76
|
def initialize(path_or_io, create = false, buffer = false, options = {})
|
68
77
|
super()
|
78
|
+
options = DEFAULT_OPTIONS.merge(options)
|
69
79
|
@name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
|
70
80
|
@comment = ''
|
71
81
|
@create = create ? true : false # allow any truthy value to mean true
|
@@ -98,18 +108,19 @@ module Zip
|
|
98
108
|
|
99
109
|
@stored_entries = @entry_set.dup
|
100
110
|
@stored_comment = @comment
|
101
|
-
@restore_ownership = options[:restore_ownership]
|
102
|
-
@restore_permissions = options[:restore_permissions]
|
103
|
-
@restore_times = options[:restore_times]
|
111
|
+
@restore_ownership = options[:restore_ownership]
|
112
|
+
@restore_permissions = options[:restore_permissions]
|
113
|
+
@restore_times = options[:restore_times]
|
104
114
|
end
|
105
115
|
|
106
116
|
class << self
|
107
|
-
#
|
108
|
-
# to the block and is automatically closed afterwards just as with
|
109
|
-
# ruby's builtin File
|
110
|
-
def open(file_name, create = false)
|
111
|
-
zf = ::Zip::File.new(file_name, create)
|
117
|
+
# Similar to ::new. If a block is passed the Zip::File object is passed
|
118
|
+
# to the block and is automatically closed afterwards, just as with
|
119
|
+
# ruby's builtin File::open method.
|
120
|
+
def open(file_name, create = false, options = {})
|
121
|
+
zf = ::Zip::File.new(file_name, create, false, options)
|
112
122
|
return zf unless block_given?
|
123
|
+
|
113
124
|
begin
|
114
125
|
yield zf
|
115
126
|
ensure
|
@@ -130,17 +141,18 @@ module Zip
|
|
130
141
|
# (This can be used to extract data from a
|
131
142
|
# downloaded zip archive without first saving it to disk.)
|
132
143
|
def open_buffer(io, options = {})
|
133
|
-
unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.
|
144
|
+
unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String)
|
134
145
|
raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
|
135
146
|
end
|
136
147
|
|
137
|
-
io = ::StringIO.new(io) if io.
|
148
|
+
io = ::StringIO.new(io) if io.kind_of?(::String)
|
138
149
|
|
139
150
|
# https://github.com/rubyzip/rubyzip/issues/119
|
140
151
|
io.binmode if io.respond_to?(:binmode)
|
141
152
|
|
142
153
|
zf = ::Zip::File.new(io, true, true, options)
|
143
154
|
return zf unless block_given?
|
155
|
+
|
144
156
|
yield zf
|
145
157
|
|
146
158
|
begin
|
@@ -156,9 +168,9 @@ module Zip
|
|
156
168
|
# whereas ZipInputStream jumps through the entire archive accessing the
|
157
169
|
# local entry headers (which contain the same information as the
|
158
170
|
# central directory).
|
159
|
-
def foreach(
|
160
|
-
open(
|
161
|
-
|
171
|
+
def foreach(zip_file_name, &block)
|
172
|
+
::Zip::File.open(zip_file_name) do |zip_file|
|
173
|
+
zip_file.each(&block)
|
162
174
|
end
|
163
175
|
end
|
164
176
|
|
@@ -219,12 +231,14 @@ module Zip
|
|
219
231
|
def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
|
220
232
|
raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
|
221
233
|
raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
|
234
|
+
|
222
235
|
zip_file_size = ::File.size(zip_file_name)
|
223
236
|
segment_size = get_segment_size_for_split(segment_size)
|
224
237
|
return if zip_file_size <= segment_size
|
238
|
+
|
225
239
|
segment_count = get_segment_count_for_split(zip_file_size, segment_size)
|
226
240
|
# Checking for correct zip structure
|
227
|
-
open(zip_file_name) {}
|
241
|
+
::Zip::File.open(zip_file_name) {}
|
228
242
|
partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
|
229
243
|
szip_file_index = 0
|
230
244
|
::File.open(zip_file_name, 'rb') do |zip_file|
|
@@ -241,8 +255,8 @@ module Zip
|
|
241
255
|
# Returns an input stream to the specified entry. If a block is passed
|
242
256
|
# the stream object is passed to the block and the stream is automatically
|
243
257
|
# closed afterwards just as with ruby's builtin File.open method.
|
244
|
-
def get_input_stream(entry, &
|
245
|
-
get_entry(entry).get_input_stream(&
|
258
|
+
def get_input_stream(entry, &a_proc)
|
259
|
+
get_entry(entry).get_input_stream(&a_proc)
|
246
260
|
end
|
247
261
|
|
248
262
|
# Returns an output stream to the specified entry. If entry is not an instance
|
@@ -250,7 +264,11 @@ module Zip
|
|
250
264
|
# specified. If a block is passed the stream object is passed to the block and
|
251
265
|
# the stream is automatically closed afterwards just as with ruby's builtin
|
252
266
|
# File.open method.
|
253
|
-
def get_output_stream(entry, permission_int = nil, comment = nil,
|
267
|
+
def get_output_stream(entry, permission_int = nil, comment = nil,
|
268
|
+
extra = nil, compressed_size = nil, crc = nil,
|
269
|
+
compression_method = nil, size = nil, time = nil,
|
270
|
+
&a_proc)
|
271
|
+
|
254
272
|
new_entry =
|
255
273
|
if entry.kind_of?(Entry)
|
256
274
|
entry
|
@@ -264,7 +282,7 @@ module Zip
|
|
264
282
|
new_entry.unix_perms = permission_int
|
265
283
|
zip_streamable_entry = StreamableStream.new(new_entry)
|
266
284
|
@entry_set << zip_streamable_entry
|
267
|
-
zip_streamable_entry.get_output_stream(&
|
285
|
+
zip_streamable_entry.get_output_stream(&a_proc)
|
268
286
|
end
|
269
287
|
|
270
288
|
# Returns the name of the zip archive
|
@@ -274,7 +292,7 @@ module Zip
|
|
274
292
|
|
275
293
|
# Returns a string containing the contents of the specified entry
|
276
294
|
def read(entry)
|
277
|
-
get_input_stream(entry
|
295
|
+
get_input_stream(entry, &:read)
|
278
296
|
end
|
279
297
|
|
280
298
|
# Convenience method for adding the contents of a file to the archive
|
@@ -301,19 +319,19 @@ module Zip
|
|
301
319
|
|
302
320
|
# Renames the specified entry.
|
303
321
|
def rename(entry, new_name, &continue_on_exists_proc)
|
304
|
-
|
322
|
+
found_entry = get_entry(entry)
|
305
323
|
check_entry_exists(new_name, continue_on_exists_proc, 'rename')
|
306
|
-
@entry_set.delete(
|
307
|
-
|
308
|
-
@entry_set <<
|
324
|
+
@entry_set.delete(found_entry)
|
325
|
+
found_entry.name = new_name
|
326
|
+
@entry_set << found_entry
|
309
327
|
end
|
310
328
|
|
311
|
-
# Replaces the specified entry with the contents of
|
329
|
+
# Replaces the specified entry with the contents of src_path (from
|
312
330
|
# the file system).
|
313
|
-
def replace(entry,
|
314
|
-
check_file(
|
331
|
+
def replace(entry, src_path)
|
332
|
+
check_file(src_path)
|
315
333
|
remove(entry)
|
316
|
-
add(entry,
|
334
|
+
add(entry, src_path)
|
317
335
|
end
|
318
336
|
|
319
337
|
# Extracts entry to file dest_path.
|
@@ -326,7 +344,8 @@ module Zip
|
|
326
344
|
# Commits changes that has been made since the previous commit to
|
327
345
|
# the zip archive.
|
328
346
|
def commit
|
329
|
-
return if name.
|
347
|
+
return if name.kind_of?(StringIO) || !commit_required?
|
348
|
+
|
330
349
|
on_success_replace do |tmp_file|
|
331
350
|
::Zip::OutputStream.open(tmp_file) do |zos|
|
332
351
|
@entry_set.each do |e|
|
@@ -366,7 +385,13 @@ module Zip
|
|
366
385
|
# Searches for entry with the specified name. Returns nil if
|
367
386
|
# no entry is found. See also get_entry
|
368
387
|
def find_entry(entry_name)
|
369
|
-
@entry_set.find_entry(entry_name)
|
388
|
+
selected_entry = @entry_set.find_entry(entry_name)
|
389
|
+
return if selected_entry.nil?
|
390
|
+
|
391
|
+
selected_entry.restore_ownership = @restore_ownership
|
392
|
+
selected_entry.restore_permissions = @restore_permissions
|
393
|
+
selected_entry.restore_times = @restore_times
|
394
|
+
selected_entry
|
370
395
|
end
|
371
396
|
|
372
397
|
# Searches for entries given a glob
|
@@ -378,43 +403,43 @@ module Zip
|
|
378
403
|
# if no entry is found.
|
379
404
|
def get_entry(entry)
|
380
405
|
selected_entry = find_entry(entry)
|
381
|
-
raise Errno::ENOENT, entry
|
382
|
-
|
383
|
-
selected_entry.restore_permissions = @restore_permissions
|
384
|
-
selected_entry.restore_times = @restore_times
|
406
|
+
raise Errno::ENOENT, entry if selected_entry.nil?
|
407
|
+
|
385
408
|
selected_entry
|
386
409
|
end
|
387
410
|
|
388
411
|
# Creates a directory
|
389
|
-
def mkdir(
|
390
|
-
raise Errno::EEXIST, "File exists - #{
|
391
|
-
|
392
|
-
|
393
|
-
|
412
|
+
def mkdir(entry_name, permission = 0o755)
|
413
|
+
raise Errno::EEXIST, "File exists - #{entry_name}" if find_entry(entry_name)
|
414
|
+
|
415
|
+
entry_name = entry_name.dup.to_s
|
416
|
+
entry_name << '/' unless entry_name.end_with?('/')
|
417
|
+
@entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission)
|
394
418
|
end
|
395
419
|
|
396
420
|
private
|
397
421
|
|
398
|
-
def directory?(
|
399
|
-
|
400
|
-
if
|
422
|
+
def directory?(new_entry, src_path)
|
423
|
+
path_is_directory = ::File.directory?(src_path)
|
424
|
+
if new_entry.directory? && !path_is_directory
|
401
425
|
raise ArgumentError,
|
402
|
-
"entry name '#{
|
403
|
-
"'#{
|
404
|
-
elsif !
|
405
|
-
|
426
|
+
"entry name '#{new_entry}' indicates directory entry, but " \
|
427
|
+
"'#{src_path}' is not a directory"
|
428
|
+
elsif !new_entry.directory? && path_is_directory
|
429
|
+
new_entry.name += '/'
|
406
430
|
end
|
407
|
-
|
431
|
+
new_entry.directory? && path_is_directory
|
408
432
|
end
|
409
433
|
|
410
|
-
def check_entry_exists(
|
434
|
+
def check_entry_exists(entry_name, continue_on_exists_proc, proc_name)
|
411
435
|
continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
|
412
|
-
return unless @entry_set.include?(
|
436
|
+
return unless @entry_set.include?(entry_name)
|
437
|
+
|
413
438
|
if continue_on_exists_proc.call
|
414
|
-
remove get_entry(
|
439
|
+
remove get_entry(entry_name)
|
415
440
|
else
|
416
441
|
raise ::Zip::EntryExistsError,
|
417
|
-
|
442
|
+
proc_name + " failed. Entry #{entry_name} already exists"
|
418
443
|
end
|
419
444
|
end
|
420
445
|
|