rubyzip 1.0.0 → 2.4.1

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 (52) hide show
  1. checksums.yaml +6 -14
  2. data/README.md +231 -46
  3. data/Rakefile +13 -5
  4. data/TODO +0 -1
  5. data/lib/zip/central_directory.rb +64 -29
  6. data/lib/zip/compressor.rb +1 -2
  7. data/lib/zip/constants.rb +59 -5
  8. data/lib/zip/crypto/decrypted_io.rb +40 -0
  9. data/lib/zip/crypto/encryption.rb +11 -0
  10. data/lib/zip/crypto/null_encryption.rb +43 -0
  11. data/lib/zip/crypto/traditional_encryption.rb +99 -0
  12. data/lib/zip/decompressor.rb +22 -4
  13. data/lib/zip/deflater.rb +11 -6
  14. data/lib/zip/dos_time.rb +27 -16
  15. data/lib/zip/entry.rb +299 -163
  16. data/lib/zip/entry_set.rb +22 -20
  17. data/lib/zip/errors.rb +17 -6
  18. data/lib/zip/extra_field/generic.rb +15 -14
  19. data/lib/zip/extra_field/ntfs.rb +94 -0
  20. data/lib/zip/extra_field/old_unix.rb +46 -0
  21. data/lib/zip/extra_field/universal_time.rb +46 -16
  22. data/lib/zip/extra_field/unix.rb +10 -9
  23. data/lib/zip/extra_field/zip64.rb +46 -6
  24. data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
  25. data/lib/zip/extra_field.rb +32 -18
  26. data/lib/zip/file.rb +260 -160
  27. data/lib/zip/filesystem.rb +297 -276
  28. data/lib/zip/inflater.rb +23 -34
  29. data/lib/zip/input_stream.rb +130 -82
  30. data/lib/zip/ioextras/abstract_input_stream.rb +36 -22
  31. data/lib/zip/ioextras/abstract_output_stream.rb +4 -6
  32. data/lib/zip/ioextras.rb +4 -6
  33. data/lib/zip/null_compressor.rb +2 -2
  34. data/lib/zip/null_decompressor.rb +4 -12
  35. data/lib/zip/null_input_stream.rb +2 -1
  36. data/lib/zip/output_stream.rb +75 -45
  37. data/lib/zip/pass_thru_compressor.rb +6 -6
  38. data/lib/zip/pass_thru_decompressor.rb +14 -24
  39. data/lib/zip/streamable_directory.rb +3 -3
  40. data/lib/zip/streamable_stream.rb +21 -16
  41. data/lib/zip/version.rb +1 -1
  42. data/lib/zip.rb +42 -3
  43. data/samples/example.rb +30 -40
  44. data/samples/example_filesystem.rb +16 -18
  45. data/samples/example_recursive.rb +33 -28
  46. data/samples/gtk_ruby_zip.rb +84 -0
  47. data/samples/qtzip.rb +25 -34
  48. data/samples/write_simple.rb +10 -13
  49. data/samples/zipfind.rb +38 -45
  50. metadata +129 -28
  51. data/NEWS +0 -182
  52. data/samples/gtkRubyzip.rb +0 -86
data/lib/zip/file.rb CHANGED
@@ -43,90 +43,133 @@ module Zip
43
43
  # interface for accessing the filesystem, ie. the File and Dir classes.
44
44
 
45
45
  class File < CentralDirectory
46
-
47
- CREATE = 1
48
- SPLIT_SIGNATURE = 0x08074b50
46
+ CREATE = true
47
+ SPLIT_SIGNATURE = 0x08074b50
49
48
  ZIP64_EOCD_SIGNATURE = 0x06064b50
50
- MAX_SEGMENT_SIZE = 3221225472
51
- MIN_SEGMENT_SIZE = 65536
52
- DATA_BUFFER_SIZE = 8192
49
+ MAX_SEGMENT_SIZE = 3_221_225_472
50
+ MIN_SEGMENT_SIZE = 65_536
51
+ DATA_BUFFER_SIZE = 8192
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(fileName, create = nil, buffer = false)
76
+ def initialize(path_or_io, dep_create = false, dep_buffer = false,
77
+ create: false, buffer: false, **options)
68
78
  super()
69
- @name = fileName
79
+
80
+ Zip.warn_about_v3_api('File#new') if dep_create || dep_buffer
81
+
82
+ options = DEFAULT_OPTIONS.merge(options)
83
+ @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
70
84
  @comment = ''
71
- @create = create
72
- case
73
- when ::File.exists?(fileName) && !buffer
74
- @create = nil
75
- ::File.open(name, 'rb') do |f|
76
- read_from_stream(f)
85
+ @create = create || dep_create ? true : false # allow any truthy value to mean true
86
+ buffer ||= dep_buffer
87
+
88
+ if ::File.size?(@name.to_s)
89
+ # There is a file, which exists, that is associated with this zip.
90
+ @create = false
91
+ @file_permissions = ::File.stat(@name).mode
92
+
93
+ if buffer
94
+ read_from_stream(path_or_io)
95
+ else
96
+ ::File.open(@name, 'rb') do |f|
97
+ read_from_stream(f)
98
+ end
77
99
  end
78
- when create
100
+ elsif buffer && path_or_io.size > 0
101
+ # This zip is probably a non-empty StringIO.
102
+ @create = false
103
+ read_from_stream(path_or_io)
104
+ elsif @create
105
+ # This zip is completely new/empty and is to be created.
79
106
  @entry_set = EntrySet.new
107
+ elsif ::File.zero?(@name)
108
+ # A file exists, but it is empty.
109
+ raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?"
80
110
  else
81
- raise ZipError, "File #{fileName} not found"
111
+ # Everything is wrong.
112
+ raise Error, "File #{@name} not found"
82
113
  end
83
- @storedEntries = @entry_set.dup
84
- @storedComment = @comment
85
- @restore_ownership = false
86
- @restore_permissions = false
87
- @restore_times = true
114
+
115
+ @stored_entries = @entry_set.dup
116
+ @stored_comment = @comment
117
+ @restore_ownership = options[:restore_ownership]
118
+ @restore_permissions = options[:restore_permissions]
119
+ @restore_times = options[:restore_times]
88
120
  end
89
121
 
90
122
  class << self
91
- # Same as #new. If a block is passed the ZipFile object is passed
92
- # to the block and is automatically closed afterwards just as with
93
- # ruby's builtin File.open method.
94
- def open(fileName, create = nil)
95
- zf = ::Zip::File.new(fileName, create)
96
- if block_given?
97
- begin
98
- yield zf
99
- ensure
100
- zf.close
101
- end
102
- else
103
- zf
123
+ # Similar to ::new. If a block is passed the Zip::File object is passed
124
+ # to the block and is automatically closed afterwards, just as with
125
+ # ruby's builtin File::open method.
126
+ def open(file_name, dep_create = false, create: false, **options)
127
+ Zip.warn_about_v3_api('Zip::File.open') if dep_create
128
+
129
+ zf = ::Zip::File.new(file_name, create: (dep_create || create), buffer: false, **options)
130
+ return zf unless block_given?
131
+
132
+ begin
133
+ yield zf
134
+ ensure
135
+ zf.close
104
136
  end
105
137
  end
106
138
 
107
139
  # Same as #open. But outputs data to a buffer instead of a file
108
140
  def add_buffer
109
- zf = ::Zip::File.new('', true, true)
141
+ Zip.warn_about_v3_api('Zip::File.add_buffer')
142
+
143
+ io = ::StringIO.new
144
+ zf = ::Zip::File.new(io, true, true)
110
145
  yield zf
111
- zf.write_buffer
146
+ zf.write_buffer(io)
112
147
  end
113
148
 
114
149
  # Like #open, but reads zip archive contents from a String or open IO
115
150
  # stream, and outputs data to a buffer.
116
- # (This can be used to extract data from a
151
+ # (This can be used to extract data from a
117
152
  # downloaded zip archive without first saving it to disk.)
118
- def open_buffer(io)
119
- unless io.is_a?(IO) && io.is_a?(String)
120
- raise "Zip::ZipFile.open_buffer expects an argument of class String or IO. Found: #{io.class}"
121
- end
122
- zf = ::Zip::File.new('', true, true)
123
- if io.is_a(::String)
124
- require 'stringio'
125
- io = ::StringIO.new(io)
153
+ def open_buffer(io, **options)
154
+ unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String)
155
+ raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
126
156
  end
127
- zf.read_from_stream(io)
157
+
158
+ io = ::StringIO.new(io) if io.kind_of?(::String)
159
+
160
+ # https://github.com/rubyzip/rubyzip/issues/119
161
+ io.binmode if io.respond_to?(:binmode)
162
+
163
+ zf = ::Zip::File.new(io, create: true, buffer: true, **options)
164
+ return zf unless block_given?
165
+
128
166
  yield zf
129
- zf.write_buffer
167
+
168
+ begin
169
+ zf.write_buffer(io)
170
+ rescue IOError => e
171
+ raise unless e.message == 'not opened for writing'
172
+ end
130
173
  end
131
174
 
132
175
  # Iterates over the contents of the ZipFile. This is more efficient
@@ -135,17 +178,16 @@ module Zip
135
178
  # whereas ZipInputStream jumps through the entire archive accessing the
136
179
  # local entry headers (which contain the same information as the
137
180
  # central directory).
138
- def foreach(aZipFileName, &block)
139
- open(aZipFileName) do |zipFile|
140
- zipFile.each(&block)
181
+ def foreach(zip_file_name, &block)
182
+ ::Zip::File.open(zip_file_name) do |zip_file|
183
+ zip_file.each(&block)
141
184
  end
142
185
  end
143
186
 
144
187
  def get_segment_size_for_split(segment_size)
145
- case
146
- when MIN_SEGMENT_SIZE > segment_size
188
+ if MIN_SEGMENT_SIZE > segment_size
147
189
  MIN_SEGMENT_SIZE
148
- when MAX_SEGMENT_SIZE < segment_size
190
+ elsif MAX_SEGMENT_SIZE < segment_size
149
191
  MAX_SEGMENT_SIZE
150
192
  else
151
193
  segment_size
@@ -153,8 +195,10 @@ module Zip
153
195
  end
154
196
 
155
197
  def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
156
- partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
157
- partial_zip_file_name + ::File.extname(zip_file_name)) unless partial_zip_file_name.nil?
198
+ unless partial_zip_file_name.nil?
199
+ partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
200
+ partial_zip_file_name + ::File.extname(zip_file_name))
201
+ end
158
202
  partial_zip_file_name ||= zip_file_name
159
203
  partial_zip_file_name
160
204
  end
@@ -173,9 +217,9 @@ module Zip
173
217
  # TODO: Make the code more understandable
174
218
  #
175
219
  def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
176
- ssegment_size = zip_file_size - zip_file.pos
177
- ssegment_size = segment_size if ssegment_size > segment_size
178
- szip_file_name = "#{partial_zip_file_name}.#{'%03d'%(szip_file_index)}"
220
+ ssegment_size = zip_file_size - zip_file.pos
221
+ ssegment_size = segment_size if ssegment_size > segment_size
222
+ szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}"
179
223
  ::File.open(szip_file_name, 'wb') do |szip_file|
180
224
  if szip_file_index == 1
181
225
  ssegment_size = put_split_signature(szip_file, segment_size)
@@ -185,7 +229,7 @@ module Zip
185
229
  segment_bytes_left = ssegment_size - chunk_bytes
186
230
  buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
187
231
  chunk = zip_file.read(buffer_size)
188
- chunk_bytes += buffer_size
232
+ chunk_bytes += buffer_size
189
233
  szip_file << chunk
190
234
  # Info for track splitting
191
235
  yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
@@ -194,16 +238,24 @@ module Zip
194
238
  end
195
239
 
196
240
  # Splits an archive into parts with segment size
197
- def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
198
- raise ZipError, "File #{zip_file_name} not found" unless ::File.exists?(zip_file_name)
241
+ def split(zip_file_name,
242
+ dep_segment_size = MAX_SEGMENT_SIZE, dep_delete_zip_file = true, dep_partial_zip_file_name = nil,
243
+ segment_size: MAX_SEGMENT_SIZE, delete_zip_file: nil, partial_zip_file_name: nil)
244
+ raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
199
245
  raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
246
+
247
+ if dep_segment_size != MAX_SEGMENT_SIZE || !dep_delete_zip_file || dep_partial_zip_file_name
248
+ Zip.warn_about_v3_api('Zip::File.split')
249
+ end
250
+
200
251
  zip_file_size = ::File.size(zip_file_name)
201
- segment_size = get_segment_size_for_split(segment_size)
252
+ segment_size = get_segment_size_for_split(segment_size || dep_segment_size)
202
253
  return if zip_file_size <= segment_size
254
+
203
255
  segment_count = get_segment_count_for_split(zip_file_size, segment_size)
204
256
  # Checking for correct zip structure
205
- self.open(zip_file_name) {}
206
- partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
257
+ ::Zip::File.open(zip_file_name) {}
258
+ partial_zip_file_name = get_partial_zip_file_name(zip_file_name, (partial_zip_file_name || dep_partial_zip_file_name))
207
259
  szip_file_index = 0
208
260
  ::File.open(zip_file_name, 'rb') do |zip_file|
209
261
  until zip_file.eof?
@@ -211,33 +263,63 @@ module Zip
211
263
  save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
212
264
  end
213
265
  end
266
+ delete_zip_file = delete_zip_file.nil? ? dep_delete_zip_file : delete_zip_file
214
267
  ::File.delete(zip_file_name) if delete_zip_file
215
268
  szip_file_index
216
269
  end
217
270
  end
218
271
 
219
-
220
272
  # Returns an input stream to the specified entry. If a block is passed
221
273
  # the stream object is passed to the block and the stream is automatically
222
274
  # closed afterwards just as with ruby's builtin File.open method.
223
- def get_input_stream(entry, &aProc)
224
- get_entry(entry).get_input_stream(&aProc)
275
+ def get_input_stream(entry, &a_proc)
276
+ get_entry(entry).get_input_stream(&a_proc)
225
277
  end
226
278
 
227
- # Returns an output stream to the specified entry. If a block is passed
228
- # the stream object is passed to the block and the stream is automatically
229
- # closed afterwards just as with ruby's builtin File.open method.
230
- def get_output_stream(entry, permissionInt = nil, &aProc)
231
- newEntry = entry.kind_of?(Entry) ? entry : Entry.new(@name, entry.to_s)
232
- if newEntry.directory?
279
+ # Returns an output stream to the specified entry. If entry is not an instance
280
+ # of Zip::Entry, a new Zip::Entry will be initialized using the arguments
281
+ # specified. If a block is passed the stream object is passed to the block and
282
+ # the stream is automatically closed afterwards just as with ruby's builtin
283
+ # File.open method.
284
+ # rubocop:disable Metrics/ParameterLists, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
285
+ def get_output_stream(entry,
286
+ dep_permission_int = nil, dep_comment = nil,
287
+ dep_extra = nil, dep_compressed_size = nil, dep_crc = nil,
288
+ dep_compression_method = nil, dep_size = nil, dep_time = nil,
289
+ permission_int: nil, comment: nil,
290
+ extra: nil, compressed_size: nil, crc: nil,
291
+ compression_method: nil, size: nil, time: nil,
292
+ &a_proc)
293
+
294
+ unless dep_permission_int.nil? && dep_comment.nil? && dep_extra.nil? &&
295
+ dep_compressed_size.nil? && dep_crc.nil? && dep_compression_method.nil? &&
296
+ dep_size.nil? && dep_time.nil?
297
+ Zip.warn_about_v3_api('Zip::File#get_output_stream')
298
+ end
299
+
300
+ new_entry =
301
+ if entry.kind_of?(Entry)
302
+ entry
303
+ else
304
+ Entry.new(@name, entry.to_s,
305
+ comment: (comment || dep_comment),
306
+ extra: (extra || dep_extra),
307
+ compressed_size: (compressed_size || dep_compressed_size),
308
+ crc: (crc || dep_crc),
309
+ compression_method: (compression_method || dep_compression_method),
310
+ size: (size || dep_size),
311
+ time: (time || dep_time))
312
+ end
313
+ if new_entry.directory?
233
314
  raise ArgumentError,
234
- "cannot open stream to directory entry - '#{newEntry}'"
315
+ "cannot open stream to directory entry - '#{new_entry}'"
235
316
  end
236
- newEntry.unix_perms = permissionInt
237
- zipStreamableEntry = StreamableStream.new(newEntry)
238
- @entry_set << zipStreamableEntry
239
- zipStreamableEntry.get_output_stream(&aProc)
317
+ new_entry.unix_perms = (permission_int || dep_permission_int)
318
+ zip_streamable_entry = StreamableStream.new(new_entry)
319
+ @entry_set << zip_streamable_entry
320
+ zip_streamable_entry.get_output_stream(&a_proc)
240
321
  end
322
+ # rubocop:enable Metrics/ParameterLists, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
241
323
 
242
324
  # Returns the name of the zip archive
243
325
  def to_s
@@ -246,16 +328,24 @@ module Zip
246
328
 
247
329
  # Returns a string containing the contents of the specified entry
248
330
  def read(entry)
249
- get_input_stream(entry) { |is| is.read }
331
+ get_input_stream(entry, &:read)
250
332
  end
251
333
 
252
334
  # Convenience method for adding the contents of a file to the archive
253
- def add(entry, srcPath, &continue_on_exists_proc)
254
- continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
255
- check_entry_exists(entry, continue_on_exists_proc, "add")
256
- newEntry = entry.kind_of?(Entry) ? entry : Entry.new(@name, entry.to_s)
257
- newEntry.gather_fileinfo_from_srcpath(srcPath)
258
- @entry_set << newEntry
335
+ def add(entry, src_path, &continue_on_exists_proc)
336
+ continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
337
+ check_entry_exists(entry, continue_on_exists_proc, 'add')
338
+ new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
339
+ new_entry.gather_fileinfo_from_srcpath(src_path)
340
+ new_entry.dirty = true
341
+ @entry_set << new_entry
342
+ end
343
+
344
+ # Convenience method for adding the contents of a file to the archive
345
+ # in Stored format (uncompressed)
346
+ def add_stored(entry, src_path, &continue_on_exists_proc)
347
+ entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED)
348
+ add(entry, src_path, &continue_on_exists_proc)
259
349
  end
260
350
 
261
351
  # Removes the specified entry.
@@ -265,56 +355,69 @@ module Zip
265
355
 
266
356
  # Renames the specified entry.
267
357
  def rename(entry, new_name, &continue_on_exists_proc)
268
- foundEntry = get_entry(entry)
358
+ found_entry = get_entry(entry)
269
359
  check_entry_exists(new_name, continue_on_exists_proc, 'rename')
270
- @entry_set.delete(foundEntry)
271
- foundEntry.name = new_name
272
- @entry_set << foundEntry
360
+ @entry_set.delete(found_entry)
361
+ found_entry.name = new_name
362
+ @entry_set << found_entry
273
363
  end
274
364
 
275
- # Replaces the specified entry with the contents of srcPath (from
365
+ # Replaces the specified entry with the contents of src_path (from
276
366
  # the file system).
277
- def replace(entry, srcPath)
278
- check_file(srcPath)
367
+ def replace(entry, src_path)
368
+ check_file(src_path)
279
369
  remove(entry)
280
- add(entry, srcPath)
370
+ add(entry, src_path)
281
371
  end
282
372
 
283
373
  # Extracts entry to file dest_path.
284
374
  def extract(entry, dest_path, &block)
285
- block ||= proc { ::Zip.on_exists_proc }
375
+ Zip.warn_about_v3_api('Zip::File#extract')
376
+
377
+ block ||= proc { ::Zip.on_exists_proc }
286
378
  found_entry = get_entry(entry)
287
379
  found_entry.extract(dest_path, &block)
288
380
  end
289
381
 
382
+ # Extracts `entry` to a file at `entry_path`, with `destination_directory`
383
+ # as the base location in the filesystem.
384
+ #
385
+ # NB: The caller is responsible for making sure `destination_directory` is
386
+ # safe, if it is passed.
387
+ def extract_v3(entry, entry_path = nil, destination_directory: '.', &block)
388
+ block ||= proc { ::Zip.on_exists_proc }
389
+ found_entry = get_entry(entry)
390
+ entry_path ||= found_entry.name
391
+ found_entry.extract_v3(entry_path, destination_directory: destination_directory, &block)
392
+ end
393
+
290
394
  # Commits changes that has been made since the previous commit to
291
395
  # the zip archive.
292
396
  def commit
293
- return if !commit_required?
294
- on_success_replace(name) {
295
- |tmpFile|
296
- OutputStream.open(tmpFile) {
297
- |zos|
298
-
299
- @entry_set.each {
300
- |e|
397
+ return if name.kind_of?(StringIO) || !commit_required?
398
+
399
+ on_success_replace do |tmp_file|
400
+ ::Zip::OutputStream.open(tmp_file) do |zos|
401
+ @entry_set.each do |e|
301
402
  e.write_to_zip_output_stream(zos)
302
403
  e.dirty = false
303
- }
404
+ e.clean_up
405
+ end
304
406
  zos.comment = comment
305
- }
407
+ end
306
408
  true
307
- }
409
+ end
308
410
  initialize(name)
309
411
  end
310
412
 
311
413
  # Write buffer write changes to buffer and return
312
- def write_buffer
313
- buffer = OutputStream.write_buffer do |zos|
414
+ def write_buffer(io = ::StringIO.new)
415
+ return io unless commit_required?
416
+
417
+ ::Zip::OutputStream.write_buffer(io) do |zos|
314
418
  @entry_set.each { |e| e.write_to_zip_output_stream(zos) }
315
419
  zos.comment = comment
316
420
  end
317
- return buffer
318
421
  end
319
422
 
320
423
  # Closes the zip file committing any changes that has been made.
@@ -328,13 +431,19 @@ module Zip
328
431
  @entry_set.each do |e|
329
432
  return true if e.dirty
330
433
  end
331
- @comment != @storedComment || @entry_set != @storedEntries || @create == File::CREATE
434
+ @comment != @stored_comment || @entry_set != @stored_entries || @create
332
435
  end
333
436
 
334
437
  # Searches for entry with the specified name. Returns nil if
335
438
  # no entry is found. See also get_entry
336
439
  def find_entry(entry_name)
337
- @entry_set.find_entry(entry_name)
440
+ selected_entry = @entry_set.find_entry(entry_name)
441
+ return if selected_entry.nil?
442
+
443
+ selected_entry.restore_ownership = @restore_ownership
444
+ selected_entry.restore_permissions = @restore_permissions
445
+ selected_entry.restore_times = @restore_times
446
+ selected_entry
338
447
  end
339
448
 
340
449
  # Searches for entries given a glob
@@ -345,73 +454,64 @@ module Zip
345
454
  # Searches for an entry just as find_entry, but throws Errno::ENOENT
346
455
  # if no entry is found.
347
456
  def get_entry(entry)
348
- selectedEntry = find_entry(entry)
349
- unless selectedEntry
350
- raise Errno::ENOENT, entry
351
- end
352
- selectedEntry.restore_ownership = @restore_ownership
353
- selectedEntry.restore_permissions = @restore_permissions
354
- selectedEntry.restore_times = @restore_times
355
- selectedEntry
457
+ selected_entry = find_entry(entry)
458
+ raise Errno::ENOENT, entry if selected_entry.nil?
459
+
460
+ selected_entry
356
461
  end
357
462
 
358
463
  # Creates a directory
359
- def mkdir(entryName, permissionInt = 0755)
360
- if find_entry(entryName)
361
- raise Errno::EEXIST, "File exists - #{entryName}"
362
- end
363
- entryName = entryName.dup.to_s
364
- entryName << '/' unless entryName.end_with?('/')
365
- @entry_set << StreamableDirectory.new(@name, entryName, nil, permissionInt)
464
+ def mkdir(entry_name, permission = 0o755)
465
+ raise Errno::EEXIST, "File exists - #{entry_name}" if find_entry(entry_name)
466
+
467
+ entry_name = entry_name.dup.to_s
468
+ entry_name << '/' unless entry_name.end_with?('/')
469
+ @entry_set << ::Zip::StreamableDirectory.new(@name, entry_name, nil, permission)
366
470
  end
367
471
 
368
472
  private
369
473
 
370
- def is_directory(newEntry, srcPath)
371
- srcPathIsDirectory = ::File.directory?(srcPath)
372
- if newEntry.is_directory && !srcPathIsDirectory
474
+ def directory?(new_entry, src_path)
475
+ path_is_directory = ::File.directory?(src_path)
476
+ if new_entry.directory? && !path_is_directory
373
477
  raise ArgumentError,
374
- "entry name '#{newEntry}' indicates directory entry, but "+
375
- "'#{srcPath}' is not a directory"
376
- elsif !newEntry.is_directory && srcPathIsDirectory
377
- newEntry.name += "/"
478
+ "entry name '#{new_entry}' indicates directory entry, but " \
479
+ "'#{src_path}' is not a directory"
480
+ elsif !new_entry.directory? && path_is_directory
481
+ new_entry.name += '/'
378
482
  end
379
- newEntry.is_directory && srcPathIsDirectory
483
+ new_entry.directory? && path_is_directory
380
484
  end
381
485
 
382
- def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
486
+ def check_entry_exists(entry_name, continue_on_exists_proc, proc_name)
383
487
  continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
384
- if @entry_set.include?(entryName)
385
- if continue_on_exists_proc.call
386
- remove get_entry(entryName)
387
- else
388
- raise ZipEntryExistsError,
389
- procedureName + " failed. Entry #{entryName} already exists"
390
- end
488
+ return unless @entry_set.include?(entry_name)
489
+
490
+ if continue_on_exists_proc.call
491
+ remove get_entry(entry_name)
492
+ else
493
+ raise ::Zip::EntryExistsError,
494
+ proc_name + " failed. Entry #{entry_name} already exists"
391
495
  end
392
496
  end
393
497
 
394
498
  def check_file(path)
395
- unless ::File.readable?(path)
396
- raise Errno::ENOENT, path
397
- end
499
+ raise Errno::ENOENT, path unless ::File.readable?(path)
398
500
  end
399
501
 
400
- def on_success_replace(aFilename)
401
- tmpfile = get_tempfile
402
- tmpFilename = tmpfile.path
403
- tmpfile.close
404
- if yield tmpFilename
405
- ::File.rename(tmpFilename, name)
502
+ def on_success_replace
503
+ dirname, basename = ::File.split(name)
504
+ ::Dir::Tmpname.create(basename, dirname) do |tmp_filename|
505
+ begin
506
+ if yield tmp_filename
507
+ ::File.rename(tmp_filename, name)
508
+ ::File.chmod(@file_permissions, name) unless @create
509
+ end
510
+ ensure
511
+ ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename)
512
+ end
406
513
  end
407
514
  end
408
-
409
- def get_tempfile
410
- tempFile = Tempfile.new(::File.basename(name), ::File.dirname(name))
411
- tempFile.binmode
412
- tempFile
413
- end
414
-
415
515
  end
416
516
  end
417
517