rubyzip 1.2.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +64 -23
- data/Rakefile +3 -0
- data/lib/zip.rb +6 -3
- 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 +69 -37
- data/lib/zip/entry_set.rb +2 -0
- data/lib/zip/errors.rb +2 -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 +115 -70
- 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 +32 -167
- data/test/basic_zip_file_test.rb +0 -60
- data/test/case_sensitivity_test.rb +0 -69
- data/test/central_directory_entry_test.rb +0 -69
- data/test/central_directory_test.rb +0 -100
- data/test/crypto/null_encryption_test.rb +0 -57
- data/test/crypto/traditional_encryption_test.rb +0 -80
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/globTest.zip +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/gpbit3stored.zip +0 -0
- data/test/data/mimetype +0 -1
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/ntfs.zip +0 -0
- data/test/data/oddExtraField.zip +0 -0
- data/test/data/path_traversal/Makefile +0 -10
- data/test/data/path_traversal/jwilk/README.md +0 -5
- data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
- data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
- data/test/data/path_traversal/jwilk/relative0.zip +0 -0
- data/test/data/path_traversal/jwilk/relative2.zip +0 -0
- data/test/data/path_traversal/jwilk/symlink.zip +0 -0
- data/test/data/path_traversal/relative1.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
- data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/test.xls +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zip64-sample.zip +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +0 -65
- data/test/encryption_test.rb +0 -42
- data/test/entry_set_test.rb +0 -163
- data/test/entry_test.rb +0 -154
- data/test/errors_test.rb +0 -35
- data/test/extra_field_test.rb +0 -76
- data/test/file_extract_directory_test.rb +0 -54
- data/test/file_extract_test.rb +0 -83
- data/test/file_permissions_test.rb +0 -65
- data/test/file_split_test.rb +0 -57
- data/test/file_test.rb +0 -601
- data/test/filesystem/dir_iterator_test.rb +0 -58
- data/test/filesystem/directory_test.rb +0 -139
- data/test/filesystem/file_mutating_test.rb +0 -87
- data/test/filesystem/file_nonmutating_test.rb +0 -508
- data/test/filesystem/file_stat_test.rb +0 -64
- data/test/gentestfiles.rb +0 -126
- data/test/inflater_test.rb +0 -14
- data/test/input_stream_test.rb +0 -182
- data/test/ioextras/abstract_input_stream_test.rb +0 -102
- data/test/ioextras/abstract_output_stream_test.rb +0 -106
- data/test/ioextras/fake_io_test.rb +0 -18
- data/test/local_entry_test.rb +0 -154
- data/test/output_stream_test.rb +0 -128
- data/test/pass_thru_compressor_test.rb +0 -30
- data/test/pass_thru_decompressor_test.rb +0 -14
- data/test/path_traversal_test.rb +0 -134
- data/test/samples/example_recursive_test.rb +0 -37
- data/test/settings_test.rb +0 -95
- data/test/test_helper.rb +0 -234
- data/test/unicode_file_names_and_comments_test.rb +0 -62
- data/test/zip64_full_test.rb +0 -51
- data/test/zip64_support_test.rb +0 -14
@@ -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,53 +49,78 @@ 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
|
|
65
74
|
# Opens a zip archive. Pass true as the second parameter to create
|
66
75
|
# a new archive if it doesn't exist already.
|
67
|
-
def initialize(
|
76
|
+
def initialize(path_or_io, create = false, buffer = false, options = {})
|
68
77
|
super()
|
69
|
-
|
78
|
+
options = DEFAULT_OPTIONS.merge(options)
|
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
|
72
|
-
|
82
|
+
|
83
|
+
if ::File.size?(@name.to_s)
|
84
|
+
# There is a file, which exists, that is associated with this zip.
|
73
85
|
@create = false
|
74
|
-
@file_permissions = ::File.stat(
|
75
|
-
|
76
|
-
|
86
|
+
@file_permissions = ::File.stat(@name).mode
|
87
|
+
|
88
|
+
if buffer
|
89
|
+
read_from_stream(path_or_io)
|
90
|
+
else
|
91
|
+
::File.open(@name, 'rb') do |f|
|
92
|
+
read_from_stream(f)
|
93
|
+
end
|
77
94
|
end
|
95
|
+
elsif buffer && path_or_io.size > 0
|
96
|
+
# This zip is probably a non-empty StringIO.
|
97
|
+
read_from_stream(path_or_io)
|
78
98
|
elsif @create
|
99
|
+
# This zip is completely new/empty and is to be created.
|
79
100
|
@entry_set = EntrySet.new
|
80
|
-
elsif ::File.zero?(
|
81
|
-
|
101
|
+
elsif ::File.zero?(@name)
|
102
|
+
# A file exists, but it is empty.
|
103
|
+
raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?"
|
82
104
|
else
|
83
|
-
|
105
|
+
# Everything is wrong.
|
106
|
+
raise Error, "File #{@name} not found"
|
84
107
|
end
|
108
|
+
|
85
109
|
@stored_entries = @entry_set.dup
|
86
110
|
@stored_comment = @comment
|
87
|
-
@restore_ownership = options[:restore_ownership]
|
88
|
-
@restore_permissions = options[:restore_permissions]
|
89
|
-
@restore_times = options[:restore_times]
|
111
|
+
@restore_ownership = options[:restore_ownership]
|
112
|
+
@restore_permissions = options[:restore_permissions]
|
113
|
+
@restore_times = options[:restore_times]
|
90
114
|
end
|
91
115
|
|
92
116
|
class << self
|
93
|
-
#
|
94
|
-
# to the block and is automatically closed afterwards just as with
|
95
|
-
# ruby's builtin File
|
96
|
-
def open(file_name, create = false)
|
97
|
-
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)
|
98
122
|
return zf unless block_given?
|
123
|
+
|
99
124
|
begin
|
100
125
|
yield zf
|
101
126
|
ensure
|
@@ -116,20 +141,20 @@ module Zip
|
|
116
141
|
# (This can be used to extract data from a
|
117
142
|
# downloaded zip archive without first saving it to disk.)
|
118
143
|
def open_buffer(io, options = {})
|
119
|
-
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)
|
120
145
|
raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
|
121
146
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
147
|
+
|
148
|
+
io = ::StringIO.new(io) if io.kind_of?(::String)
|
149
|
+
|
150
|
+
# https://github.com/rubyzip/rubyzip/issues/119
|
151
|
+
io.binmode if io.respond_to?(:binmode)
|
152
|
+
|
129
153
|
zf = ::Zip::File.new(io, true, true, options)
|
130
|
-
zf.read_from_stream(io)
|
131
154
|
return zf unless block_given?
|
155
|
+
|
132
156
|
yield zf
|
157
|
+
|
133
158
|
begin
|
134
159
|
zf.write_buffer(io)
|
135
160
|
rescue IOError => e
|
@@ -143,9 +168,9 @@ module Zip
|
|
143
168
|
# whereas ZipInputStream jumps through the entire archive accessing the
|
144
169
|
# local entry headers (which contain the same information as the
|
145
170
|
# central directory).
|
146
|
-
def foreach(
|
147
|
-
open(
|
148
|
-
|
171
|
+
def foreach(zip_file_name, &block)
|
172
|
+
::Zip::File.open(zip_file_name) do |zip_file|
|
173
|
+
zip_file.each(&block)
|
149
174
|
end
|
150
175
|
end
|
151
176
|
|
@@ -206,12 +231,14 @@ module Zip
|
|
206
231
|
def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
|
207
232
|
raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
|
208
233
|
raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
|
234
|
+
|
209
235
|
zip_file_size = ::File.size(zip_file_name)
|
210
236
|
segment_size = get_segment_size_for_split(segment_size)
|
211
237
|
return if zip_file_size <= segment_size
|
238
|
+
|
212
239
|
segment_count = get_segment_count_for_split(zip_file_size, segment_size)
|
213
240
|
# Checking for correct zip structure
|
214
|
-
open(zip_file_name) {}
|
241
|
+
::Zip::File.open(zip_file_name) {}
|
215
242
|
partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
|
216
243
|
szip_file_index = 0
|
217
244
|
::File.open(zip_file_name, 'rb') do |zip_file|
|
@@ -228,8 +255,8 @@ module Zip
|
|
228
255
|
# Returns an input stream to the specified entry. If a block is passed
|
229
256
|
# the stream object is passed to the block and the stream is automatically
|
230
257
|
# closed afterwards just as with ruby's builtin File.open method.
|
231
|
-
def get_input_stream(entry, &
|
232
|
-
get_entry(entry).get_input_stream(&
|
258
|
+
def get_input_stream(entry, &a_proc)
|
259
|
+
get_entry(entry).get_input_stream(&a_proc)
|
233
260
|
end
|
234
261
|
|
235
262
|
# Returns an output stream to the specified entry. If entry is not an instance
|
@@ -237,7 +264,11 @@ module Zip
|
|
237
264
|
# specified. If a block is passed the stream object is passed to the block and
|
238
265
|
# the stream is automatically closed afterwards just as with ruby's builtin
|
239
266
|
# File.open method.
|
240
|
-
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
|
+
|
241
272
|
new_entry =
|
242
273
|
if entry.kind_of?(Entry)
|
243
274
|
entry
|
@@ -251,7 +282,7 @@ module Zip
|
|
251
282
|
new_entry.unix_perms = permission_int
|
252
283
|
zip_streamable_entry = StreamableStream.new(new_entry)
|
253
284
|
@entry_set << zip_streamable_entry
|
254
|
-
zip_streamable_entry.get_output_stream(&
|
285
|
+
zip_streamable_entry.get_output_stream(&a_proc)
|
255
286
|
end
|
256
287
|
|
257
288
|
# Returns the name of the zip archive
|
@@ -261,7 +292,7 @@ module Zip
|
|
261
292
|
|
262
293
|
# Returns a string containing the contents of the specified entry
|
263
294
|
def read(entry)
|
264
|
-
get_input_stream(entry
|
295
|
+
get_input_stream(entry, &:read)
|
265
296
|
end
|
266
297
|
|
267
298
|
# Convenience method for adding the contents of a file to the archive
|
@@ -274,6 +305,13 @@ module Zip
|
|
274
305
|
@entry_set << new_entry
|
275
306
|
end
|
276
307
|
|
308
|
+
# Convenience method for adding the contents of a file to the archive
|
309
|
+
# in Stored format (uncompressed)
|
310
|
+
def add_stored(entry, src_path, &continue_on_exists_proc)
|
311
|
+
entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED)
|
312
|
+
add(entry, src_path, &continue_on_exists_proc)
|
313
|
+
end
|
314
|
+
|
277
315
|
# Removes the specified entry.
|
278
316
|
def remove(entry)
|
279
317
|
@entry_set.delete(get_entry(entry))
|
@@ -281,19 +319,19 @@ module Zip
|
|
281
319
|
|
282
320
|
# Renames the specified entry.
|
283
321
|
def rename(entry, new_name, &continue_on_exists_proc)
|
284
|
-
|
322
|
+
found_entry = get_entry(entry)
|
285
323
|
check_entry_exists(new_name, continue_on_exists_proc, 'rename')
|
286
|
-
@entry_set.delete(
|
287
|
-
|
288
|
-
@entry_set <<
|
324
|
+
@entry_set.delete(found_entry)
|
325
|
+
found_entry.name = new_name
|
326
|
+
@entry_set << found_entry
|
289
327
|
end
|
290
328
|
|
291
|
-
# Replaces the specified entry with the contents of
|
329
|
+
# Replaces the specified entry with the contents of src_path (from
|
292
330
|
# the file system).
|
293
|
-
def replace(entry,
|
294
|
-
check_file(
|
331
|
+
def replace(entry, src_path)
|
332
|
+
check_file(src_path)
|
295
333
|
remove(entry)
|
296
|
-
add(entry,
|
334
|
+
add(entry, src_path)
|
297
335
|
end
|
298
336
|
|
299
337
|
# Extracts entry to file dest_path.
|
@@ -306,7 +344,8 @@ module Zip
|
|
306
344
|
# Commits changes that has been made since the previous commit to
|
307
345
|
# the zip archive.
|
308
346
|
def commit
|
309
|
-
return if name.
|
347
|
+
return if name.kind_of?(StringIO) || !commit_required?
|
348
|
+
|
310
349
|
on_success_replace do |tmp_file|
|
311
350
|
::Zip::OutputStream.open(tmp_file) do |zos|
|
312
351
|
@entry_set.each do |e|
|
@@ -346,7 +385,13 @@ module Zip
|
|
346
385
|
# Searches for entry with the specified name. Returns nil if
|
347
386
|
# no entry is found. See also get_entry
|
348
387
|
def find_entry(entry_name)
|
349
|
-
@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
|
350
395
|
end
|
351
396
|
|
352
397
|
# Searches for entries given a glob
|
@@ -358,43 +403,43 @@ module Zip
|
|
358
403
|
# if no entry is found.
|
359
404
|
def get_entry(entry)
|
360
405
|
selected_entry = find_entry(entry)
|
361
|
-
raise Errno::ENOENT, entry
|
362
|
-
|
363
|
-
selected_entry.restore_permissions = @restore_permissions
|
364
|
-
selected_entry.restore_times = @restore_times
|
406
|
+
raise Errno::ENOENT, entry if selected_entry.nil?
|
407
|
+
|
365
408
|
selected_entry
|
366
409
|
end
|
367
410
|
|
368
411
|
# Creates a directory
|
369
|
-
def mkdir(
|
370
|
-
raise Errno::EEXIST, "File exists - #{
|
371
|
-
|
372
|
-
|
373
|
-
|
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)
|
374
418
|
end
|
375
419
|
|
376
420
|
private
|
377
421
|
|
378
|
-
def directory?(
|
379
|
-
|
380
|
-
if
|
422
|
+
def directory?(new_entry, src_path)
|
423
|
+
path_is_directory = ::File.directory?(src_path)
|
424
|
+
if new_entry.directory? && !path_is_directory
|
381
425
|
raise ArgumentError,
|
382
|
-
"entry name '#{
|
383
|
-
"'#{
|
384
|
-
elsif !
|
385
|
-
|
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 += '/'
|
386
430
|
end
|
387
|
-
|
431
|
+
new_entry.directory? && path_is_directory
|
388
432
|
end
|
389
433
|
|
390
|
-
def check_entry_exists(
|
434
|
+
def check_entry_exists(entry_name, continue_on_exists_proc, proc_name)
|
391
435
|
continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
|
392
|
-
return unless @entry_set.include?(
|
436
|
+
return unless @entry_set.include?(entry_name)
|
437
|
+
|
393
438
|
if continue_on_exists_proc.call
|
394
|
-
remove get_entry(
|
439
|
+
remove get_entry(entry_name)
|
395
440
|
else
|
396
441
|
raise ::Zip::EntryExistsError,
|
397
|
-
|
442
|
+
proc_name + " failed. Entry #{entry_name} already exists"
|
398
443
|
end
|
399
444
|
end
|
400
445
|
|