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.
Files changed (114) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +64 -23
  3. data/Rakefile +3 -0
  4. data/lib/zip.rb +6 -3
  5. data/lib/zip/central_directory.rb +9 -5
  6. data/lib/zip/constants.rb +52 -0
  7. data/lib/zip/crypto/decrypted_io.rb +40 -0
  8. data/lib/zip/crypto/traditional_encryption.rb +9 -9
  9. data/lib/zip/decompressor.rb +19 -1
  10. data/lib/zip/dos_time.rb +12 -7
  11. data/lib/zip/entry.rb +69 -37
  12. data/lib/zip/entry_set.rb +2 -0
  13. data/lib/zip/errors.rb +2 -0
  14. data/lib/zip/extra_field.rb +11 -9
  15. data/lib/zip/extra_field/generic.rb +10 -9
  16. data/lib/zip/extra_field/ntfs.rb +4 -0
  17. data/lib/zip/extra_field/old_unix.rb +3 -1
  18. data/lib/zip/extra_field/universal_time.rb +42 -12
  19. data/lib/zip/extra_field/unix.rb +3 -1
  20. data/lib/zip/extra_field/zip64.rb +4 -2
  21. data/lib/zip/file.rb +115 -70
  22. data/lib/zip/filesystem.rb +193 -177
  23. data/lib/zip/inflater.rb +24 -36
  24. data/lib/zip/input_stream.rb +33 -26
  25. data/lib/zip/ioextras.rb +1 -1
  26. data/lib/zip/ioextras/abstract_input_stream.rb +19 -8
  27. data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
  28. data/lib/zip/null_decompressor.rb +1 -9
  29. data/lib/zip/output_stream.rb +14 -5
  30. data/lib/zip/pass_thru_compressor.rb +2 -2
  31. data/lib/zip/pass_thru_decompressor.rb +13 -22
  32. data/lib/zip/streamable_directory.rb +3 -3
  33. data/lib/zip/streamable_stream.rb +6 -10
  34. data/lib/zip/version.rb +1 -1
  35. data/samples/example.rb +2 -2
  36. data/samples/example_filesystem.rb +1 -1
  37. data/samples/gtk_ruby_zip.rb +19 -19
  38. data/samples/qtzip.rb +6 -6
  39. data/samples/write_simple.rb +2 -4
  40. data/samples/zipfind.rb +23 -22
  41. metadata +32 -167
  42. data/test/basic_zip_file_test.rb +0 -60
  43. data/test/case_sensitivity_test.rb +0 -69
  44. data/test/central_directory_entry_test.rb +0 -69
  45. data/test/central_directory_test.rb +0 -100
  46. data/test/crypto/null_encryption_test.rb +0 -57
  47. data/test/crypto/traditional_encryption_test.rb +0 -80
  48. data/test/data/WarnInvalidDate.zip +0 -0
  49. data/test/data/file1.txt +0 -46
  50. data/test/data/file1.txt.deflatedData +0 -0
  51. data/test/data/file2.txt +0 -1504
  52. data/test/data/globTest.zip +0 -0
  53. data/test/data/globTest/foo.txt +0 -0
  54. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  55. data/test/data/globTest/food.txt +0 -0
  56. data/test/data/gpbit3stored.zip +0 -0
  57. data/test/data/mimetype +0 -1
  58. data/test/data/notzippedruby.rb +0 -7
  59. data/test/data/ntfs.zip +0 -0
  60. data/test/data/oddExtraField.zip +0 -0
  61. data/test/data/path_traversal/Makefile +0 -10
  62. data/test/data/path_traversal/jwilk/README.md +0 -5
  63. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  64. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  65. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  66. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  67. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  68. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  69. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  70. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  71. data/test/data/path_traversal/relative1.zip +0 -0
  72. data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
  73. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  74. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  75. data/test/data/rubycode.zip +0 -0
  76. data/test/data/rubycode2.zip +0 -0
  77. data/test/data/test.xls +0 -0
  78. data/test/data/testDirectory.bin +0 -0
  79. data/test/data/zip64-sample.zip +0 -0
  80. data/test/data/zipWithDirs.zip +0 -0
  81. data/test/data/zipWithEncryption.zip +0 -0
  82. data/test/deflater_test.rb +0 -65
  83. data/test/encryption_test.rb +0 -42
  84. data/test/entry_set_test.rb +0 -163
  85. data/test/entry_test.rb +0 -154
  86. data/test/errors_test.rb +0 -35
  87. data/test/extra_field_test.rb +0 -76
  88. data/test/file_extract_directory_test.rb +0 -54
  89. data/test/file_extract_test.rb +0 -83
  90. data/test/file_permissions_test.rb +0 -65
  91. data/test/file_split_test.rb +0 -57
  92. data/test/file_test.rb +0 -601
  93. data/test/filesystem/dir_iterator_test.rb +0 -58
  94. data/test/filesystem/directory_test.rb +0 -139
  95. data/test/filesystem/file_mutating_test.rb +0 -87
  96. data/test/filesystem/file_nonmutating_test.rb +0 -508
  97. data/test/filesystem/file_stat_test.rb +0 -64
  98. data/test/gentestfiles.rb +0 -126
  99. data/test/inflater_test.rb +0 -14
  100. data/test/input_stream_test.rb +0 -182
  101. data/test/ioextras/abstract_input_stream_test.rb +0 -102
  102. data/test/ioextras/abstract_output_stream_test.rb +0 -106
  103. data/test/ioextras/fake_io_test.rb +0 -18
  104. data/test/local_entry_test.rb +0 -154
  105. data/test/output_stream_test.rb +0 -128
  106. data/test/pass_thru_compressor_test.rb +0 -30
  107. data/test/pass_thru_decompressor_test.rb +0 -14
  108. data/test/path_traversal_test.rb +0 -134
  109. data/test/samples/example_recursive_test.rb +0 -37
  110. data/test/settings_test.rb +0 -95
  111. data/test/test_helper.rb +0 -234
  112. data/test/unicode_file_names_and_comments_test.rb +0 -62
  113. data/test/zip64_full_test.rb +0 -51
  114. 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
- if const_defined?(:HEADER_ID)
5
- ::Zip::ExtraField::ID_MAP[const_get(:HEADER_ID)] = self
6
- end
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
- if !binstr
16
- # If nil, start with empty.
17
- return false
18
- elsif binstr[0, 2] != self.class.const_get(:HEADER_ID)
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
- [binstr[2, 2].unpack('v')[0], binstr[4..-1]]
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
@@ -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 = nil
12
- binstr && merge(binstr)
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
- attr_accessor :atime, :ctime, :mtime, :flag
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 || return
21
- @flag, mtime, atime, ctime = content.unpack('CVVV')
22
- mtime && @mtime ||= ::Zip::DOSTime.at(mtime)
23
- atime && @atime ||= ::Zip::DOSTime.at(atime)
24
- ctime && @ctime ||= ::Zip::DOSTime.at(ctime)
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
- @flag & 1 != 0 && s << [@mtime.to_i].pack('V')
36
- @flag & 2 != 0 && s << [@atime.to_i].pack('V')
37
- @flag & 4 != 0 && s << [@ctime.to_i].pack('V')
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
- @flag & 1 == 1 && s << [@mtime.to_i].pack('V')
73
+ s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
44
74
  s
45
75
  end
46
76
  end
@@ -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 = nil
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).unpack(format)[0]
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
 
@@ -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
- # default -> false
64
+
65
+ # default -> false, but will be set to true in a future version.
59
66
  attr_accessor :restore_permissions
60
- # default -> true
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(file_name, create = false, buffer = false, options = {})
76
+ def initialize(path_or_io, create = false, buffer = false, options = {})
68
77
  super()
69
- @name = file_name
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
- if !buffer && ::File.size?(file_name)
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(file_name).mode
75
- ::File.open(name, 'rb') do |f|
76
- read_from_stream(f)
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?(file_name)
81
- raise Error, "File #{file_name} has zero size. Did you mean to pass the create flag?"
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
- raise Error, "File #{file_name} not found"
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] || false
88
- @restore_permissions = options[:restore_permissions] || true
89
- @restore_times = options[:restore_times] || true
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
- # Same as #new. If a block is passed the ZipFile object is passed
94
- # to the block and is automatically closed afterwards just as with
95
- # ruby's builtin File.open method.
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.is_a?(String)
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
- if io.is_a?(::String)
123
- require 'stringio'
124
- io = ::StringIO.new(io)
125
- elsif io.respond_to?(:binmode)
126
- # https://github.com/rubyzip/rubyzip/issues/119
127
- io.binmode
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(aZipFileName, &block)
147
- open(aZipFileName) do |zipFile|
148
- zipFile.each(&block)
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, &aProc)
232
- get_entry(entry).get_input_stream(&aProc)
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, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
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(&aProc)
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) { |is| is.read }
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
- foundEntry = get_entry(entry)
322
+ found_entry = get_entry(entry)
285
323
  check_entry_exists(new_name, continue_on_exists_proc, 'rename')
286
- @entry_set.delete(foundEntry)
287
- foundEntry.name = new_name
288
- @entry_set << foundEntry
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 srcPath (from
329
+ # Replaces the specified entry with the contents of src_path (from
292
330
  # the file system).
293
- def replace(entry, srcPath)
294
- check_file(srcPath)
331
+ def replace(entry, src_path)
332
+ check_file(src_path)
295
333
  remove(entry)
296
- add(entry, srcPath)
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.is_a?(StringIO) || !commit_required?
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 unless selected_entry
362
- selected_entry.restore_ownership = @restore_ownership
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(entryName, permissionInt = 0o755)
370
- raise Errno::EEXIST, "File exists - #{entryName}" if find_entry(entryName)
371
- entryName = entryName.dup.to_s
372
- entryName << '/' unless entryName.end_with?('/')
373
- @entry_set << ::Zip::StreamableDirectory.new(@name, entryName, nil, permissionInt)
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?(newEntry, srcPath)
379
- srcPathIsDirectory = ::File.directory?(srcPath)
380
- if newEntry.directory? && !srcPathIsDirectory
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 '#{newEntry}' indicates directory entry, but " \
383
- "'#{srcPath}' is not a directory"
384
- elsif !newEntry.directory? && srcPathIsDirectory
385
- newEntry.name += '/'
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
- newEntry.directory? && srcPathIsDirectory
431
+ new_entry.directory? && path_is_directory
388
432
  end
389
433
 
390
- def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
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?(entryName)
436
+ return unless @entry_set.include?(entry_name)
437
+
393
438
  if continue_on_exists_proc.call
394
- remove get_entry(entryName)
439
+ remove get_entry(entry_name)
395
440
  else
396
441
  raise ::Zip::EntryExistsError,
397
- procedureName + " failed. Entry #{entryName} already exists"
442
+ proc_name + " failed. Entry #{entry_name} already exists"
398
443
  end
399
444
  end
400
445