rubyzip 0.9.1 → 2.3.2

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 (103) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +354 -0
  3. data/Rakefile +15 -104
  4. data/TODO +0 -1
  5. data/lib/zip/central_directory.rb +212 -0
  6. data/lib/zip/compressor.rb +9 -0
  7. data/lib/zip/constants.rb +115 -0
  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 +31 -0
  13. data/lib/zip/deflater.rb +34 -0
  14. data/lib/zip/dos_time.rb +53 -0
  15. data/lib/zip/entry.rb +719 -0
  16. data/lib/zip/entry_set.rb +88 -0
  17. data/lib/zip/errors.rb +19 -0
  18. data/lib/zip/extra_field/generic.rb +44 -0
  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 +77 -0
  22. data/lib/zip/extra_field/unix.rb +39 -0
  23. data/lib/zip/extra_field/zip64.rb +70 -0
  24. data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
  25. data/lib/zip/extra_field.rb +103 -0
  26. data/lib/zip/file.rb +468 -0
  27. data/lib/zip/filesystem.rb +643 -0
  28. data/lib/zip/inflater.rb +54 -0
  29. data/lib/zip/input_stream.rb +180 -0
  30. data/lib/zip/ioextras/abstract_input_stream.rb +122 -0
  31. data/lib/zip/ioextras/abstract_output_stream.rb +43 -0
  32. data/lib/zip/ioextras.rb +21 -140
  33. data/lib/zip/null_compressor.rb +15 -0
  34. data/lib/zip/null_decompressor.rb +19 -0
  35. data/lib/zip/null_input_stream.rb +10 -0
  36. data/lib/zip/output_stream.rb +198 -0
  37. data/lib/zip/pass_thru_compressor.rb +23 -0
  38. data/lib/zip/pass_thru_decompressor.rb +31 -0
  39. data/lib/zip/streamable_directory.rb +15 -0
  40. data/lib/zip/streamable_stream.rb +52 -0
  41. data/lib/zip/version.rb +3 -0
  42. data/lib/zip.rb +72 -0
  43. data/samples/example.rb +44 -32
  44. data/samples/example_filesystem.rb +16 -19
  45. data/samples/example_recursive.rb +54 -0
  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 +182 -91
  51. data/ChangeLog +0 -1504
  52. data/NEWS +0 -144
  53. data/README +0 -72
  54. data/install.rb +0 -22
  55. data/lib/download_quizzes.rb +0 -119
  56. data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +0 -205
  57. data/lib/quiz1/t/solutions/Carlos/solitaire.rb +0 -111
  58. data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +0 -111
  59. data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +0 -301
  60. data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +0 -268
  61. data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +0 -132
  62. data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +0 -13
  63. data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +0 -230
  64. data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +0 -24
  65. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +0 -30
  66. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +0 -19
  67. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +0 -31
  68. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +0 -66
  69. data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +0 -17
  70. data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +0 -2
  71. data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +0 -204
  72. data/lib/quiz1/t/solutions/Jim Menard/test.rb +0 -47
  73. data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +0 -97
  74. data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +0 -140
  75. data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +0 -14
  76. data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +0 -68
  77. data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +0 -146
  78. data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +0 -38
  79. data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +0 -5
  80. data/lib/quiz1/t/solutions/Moses Hohman/util.rb +0 -27
  81. data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +0 -151
  82. data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +0 -198
  83. data/lib/zip/stdrubyext.rb +0 -111
  84. data/lib/zip/tempfile_bugfixed.rb +0 -195
  85. data/lib/zip/zip.rb +0 -1847
  86. data/lib/zip/zipfilesystem.rb +0 -609
  87. data/lib/zip/ziprequire.rb +0 -90
  88. data/samples/gtkRubyzip.rb +0 -86
  89. data/test/alltests.rb +0 -9
  90. data/test/data/file1.txt +0 -46
  91. data/test/data/file1.txt.deflatedData +0 -0
  92. data/test/data/file2.txt +0 -1504
  93. data/test/data/notzippedruby.rb +0 -7
  94. data/test/data/rubycode.zip +0 -0
  95. data/test/data/rubycode2.zip +0 -0
  96. data/test/data/testDirectory.bin +0 -0
  97. data/test/data/zipWithDirs.zip +0 -0
  98. data/test/gentestfiles.rb +0 -157
  99. data/test/ioextrastest.rb +0 -208
  100. data/test/stdrubyexttest.rb +0 -52
  101. data/test/zipfilesystemtest.rb +0 -831
  102. data/test/ziprequiretest.rb +0 -43
  103. data/test/ziptest.rb +0 -1599
data/lib/zip/file.rb ADDED
@@ -0,0 +1,468 @@
1
+ module Zip
2
+ # ZipFile is modeled after java.util.zip.ZipFile from the Java SDK.
3
+ # The most important methods are those inherited from
4
+ # ZipCentralDirectory for accessing information about the entries in
5
+ # the archive and methods such as get_input_stream and
6
+ # get_output_stream for reading from and writing entries to the
7
+ # archive. The class includes a few convenience methods such as
8
+ # #extract for extracting entries to the filesystem, and #remove,
9
+ # #replace, #rename and #mkdir for making simple modifications to
10
+ # the archive.
11
+ #
12
+ # Modifications to a zip archive are not committed until #commit or
13
+ # #close is called. The method #open accepts a block following
14
+ # the pattern from File.open offering a simple way to
15
+ # automatically close the archive when the block returns.
16
+ #
17
+ # The following example opens zip archive <code>my.zip</code>
18
+ # (creating it if it doesn't exist) and adds an entry
19
+ # <code>first.txt</code> and a directory entry <code>a_dir</code>
20
+ # to it.
21
+ #
22
+ # require 'zip'
23
+ #
24
+ # Zip::File.open("my.zip", Zip::File::CREATE) {
25
+ # |zipfile|
26
+ # zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" }
27
+ # zipfile.mkdir("a_dir")
28
+ # }
29
+ #
30
+ # The next example reopens <code>my.zip</code> writes the contents of
31
+ # <code>first.txt</code> to standard out and deletes the entry from
32
+ # the archive.
33
+ #
34
+ # require 'zip'
35
+ #
36
+ # Zip::File.open("my.zip", Zip::File::CREATE) {
37
+ # |zipfile|
38
+ # puts zipfile.read("first.txt")
39
+ # zipfile.remove("first.txt")
40
+ # }
41
+ #
42
+ # ZipFileSystem offers an alternative API that emulates ruby's
43
+ # interface for accessing the filesystem, ie. the File and Dir classes.
44
+
45
+ class File < CentralDirectory
46
+ CREATE = true
47
+ SPLIT_SIGNATURE = 0x08074b50
48
+ ZIP64_EOCD_SIGNATURE = 0x06064b50
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
59
+
60
+ attr_reader :name
61
+
62
+ # default -> false.
63
+ attr_accessor :restore_ownership
64
+
65
+ # default -> false, but will be set to true in a future version.
66
+ attr_accessor :restore_permissions
67
+
68
+ # default -> false, but will be set to true in a future version.
69
+ attr_accessor :restore_times
70
+
71
+ # Returns the zip files comment, if it has one
72
+ attr_accessor :comment
73
+
74
+ # Opens a zip archive. Pass true as the second parameter to create
75
+ # a new archive if it doesn't exist already.
76
+ def initialize(path_or_io, create = false, buffer = false, options = {})
77
+ super()
78
+ options = DEFAULT_OPTIONS.merge(options)
79
+ @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
80
+ @comment = ''
81
+ @create = create ? true : false # allow any truthy value to mean true
82
+
83
+ if ::File.size?(@name.to_s)
84
+ # There is a file, which exists, that is associated with this zip.
85
+ @create = false
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
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)
98
+ elsif @create
99
+ # This zip is completely new/empty and is to be created.
100
+ @entry_set = EntrySet.new
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?"
104
+ else
105
+ # Everything is wrong.
106
+ raise Error, "File #{@name} not found"
107
+ end
108
+
109
+ @stored_entries = @entry_set.dup
110
+ @stored_comment = @comment
111
+ @restore_ownership = options[:restore_ownership]
112
+ @restore_permissions = options[:restore_permissions]
113
+ @restore_times = options[:restore_times]
114
+ end
115
+
116
+ class << self
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)
122
+ return zf unless block_given?
123
+
124
+ begin
125
+ yield zf
126
+ ensure
127
+ zf.close
128
+ end
129
+ end
130
+
131
+ # Same as #open. But outputs data to a buffer instead of a file
132
+ def add_buffer
133
+ io = ::StringIO.new('')
134
+ zf = ::Zip::File.new(io, true, true)
135
+ yield zf
136
+ zf.write_buffer(io)
137
+ end
138
+
139
+ # Like #open, but reads zip archive contents from a String or open IO
140
+ # stream, and outputs data to a buffer.
141
+ # (This can be used to extract data from a
142
+ # downloaded zip archive without first saving it to disk.)
143
+ def open_buffer(io, options = {})
144
+ unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String)
145
+ raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
146
+ 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
+
153
+ zf = ::Zip::File.new(io, true, true, options)
154
+ return zf unless block_given?
155
+
156
+ yield zf
157
+
158
+ begin
159
+ zf.write_buffer(io)
160
+ rescue IOError => e
161
+ raise unless e.message == 'not opened for writing'
162
+ end
163
+ end
164
+
165
+ # Iterates over the contents of the ZipFile. This is more efficient
166
+ # than using a ZipInputStream since this methods simply iterates
167
+ # through the entries in the central directory structure in the archive
168
+ # whereas ZipInputStream jumps through the entire archive accessing the
169
+ # local entry headers (which contain the same information as the
170
+ # central directory).
171
+ def foreach(zip_file_name, &block)
172
+ ::Zip::File.open(zip_file_name) do |zip_file|
173
+ zip_file.each(&block)
174
+ end
175
+ end
176
+
177
+ def get_segment_size_for_split(segment_size)
178
+ if MIN_SEGMENT_SIZE > segment_size
179
+ MIN_SEGMENT_SIZE
180
+ elsif MAX_SEGMENT_SIZE < segment_size
181
+ MAX_SEGMENT_SIZE
182
+ else
183
+ segment_size
184
+ end
185
+ end
186
+
187
+ def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
188
+ unless partial_zip_file_name.nil?
189
+ partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
190
+ partial_zip_file_name + ::File.extname(zip_file_name))
191
+ end
192
+ partial_zip_file_name ||= zip_file_name
193
+ partial_zip_file_name
194
+ end
195
+
196
+ def get_segment_count_for_split(zip_file_size, segment_size)
197
+ (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1)
198
+ end
199
+
200
+ def put_split_signature(szip_file, segment_size)
201
+ signature_packed = [SPLIT_SIGNATURE].pack('V')
202
+ szip_file << signature_packed
203
+ segment_size - signature_packed.size
204
+ end
205
+
206
+ #
207
+ # TODO: Make the code more understandable
208
+ #
209
+ def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
210
+ ssegment_size = zip_file_size - zip_file.pos
211
+ ssegment_size = segment_size if ssegment_size > segment_size
212
+ szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}"
213
+ ::File.open(szip_file_name, 'wb') do |szip_file|
214
+ if szip_file_index == 1
215
+ ssegment_size = put_split_signature(szip_file, segment_size)
216
+ end
217
+ chunk_bytes = 0
218
+ until ssegment_size == chunk_bytes || zip_file.eof?
219
+ segment_bytes_left = ssegment_size - chunk_bytes
220
+ buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
221
+ chunk = zip_file.read(buffer_size)
222
+ chunk_bytes += buffer_size
223
+ szip_file << chunk
224
+ # Info for track splitting
225
+ yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
226
+ end
227
+ end
228
+ end
229
+
230
+ # Splits an archive into parts with segment size
231
+ def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
232
+ raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
233
+ raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
234
+
235
+ zip_file_size = ::File.size(zip_file_name)
236
+ segment_size = get_segment_size_for_split(segment_size)
237
+ return if zip_file_size <= segment_size
238
+
239
+ segment_count = get_segment_count_for_split(zip_file_size, segment_size)
240
+ # Checking for correct zip structure
241
+ ::Zip::File.open(zip_file_name) {}
242
+ partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
243
+ szip_file_index = 0
244
+ ::File.open(zip_file_name, 'rb') do |zip_file|
245
+ until zip_file.eof?
246
+ szip_file_index += 1
247
+ save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
248
+ end
249
+ end
250
+ ::File.delete(zip_file_name) if delete_zip_file
251
+ szip_file_index
252
+ end
253
+ end
254
+
255
+ # Returns an input stream to the specified entry. If a block is passed
256
+ # the stream object is passed to the block and the stream is automatically
257
+ # closed afterwards just as with ruby's builtin File.open method.
258
+ def get_input_stream(entry, &a_proc)
259
+ get_entry(entry).get_input_stream(&a_proc)
260
+ end
261
+
262
+ # Returns an output stream to the specified entry. If entry is not an instance
263
+ # of Zip::Entry, a new Zip::Entry will be initialized using the arguments
264
+ # specified. If a block is passed the stream object is passed to the block and
265
+ # the stream is automatically closed afterwards just as with ruby's builtin
266
+ # File.open method.
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
+
272
+ new_entry =
273
+ if entry.kind_of?(Entry)
274
+ entry
275
+ else
276
+ Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
277
+ end
278
+ if new_entry.directory?
279
+ raise ArgumentError,
280
+ "cannot open stream to directory entry - '#{new_entry}'"
281
+ end
282
+ new_entry.unix_perms = permission_int
283
+ zip_streamable_entry = StreamableStream.new(new_entry)
284
+ @entry_set << zip_streamable_entry
285
+ zip_streamable_entry.get_output_stream(&a_proc)
286
+ end
287
+
288
+ # Returns the name of the zip archive
289
+ def to_s
290
+ @name
291
+ end
292
+
293
+ # Returns a string containing the contents of the specified entry
294
+ def read(entry)
295
+ get_input_stream(entry, &:read)
296
+ end
297
+
298
+ # Convenience method for adding the contents of a file to the archive
299
+ def add(entry, src_path, &continue_on_exists_proc)
300
+ continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
301
+ check_entry_exists(entry, continue_on_exists_proc, 'add')
302
+ new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
303
+ new_entry.gather_fileinfo_from_srcpath(src_path)
304
+ new_entry.dirty = true
305
+ @entry_set << new_entry
306
+ end
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
+
315
+ # Removes the specified entry.
316
+ def remove(entry)
317
+ @entry_set.delete(get_entry(entry))
318
+ end
319
+
320
+ # Renames the specified entry.
321
+ def rename(entry, new_name, &continue_on_exists_proc)
322
+ found_entry = get_entry(entry)
323
+ check_entry_exists(new_name, continue_on_exists_proc, 'rename')
324
+ @entry_set.delete(found_entry)
325
+ found_entry.name = new_name
326
+ @entry_set << found_entry
327
+ end
328
+
329
+ # Replaces the specified entry with the contents of src_path (from
330
+ # the file system).
331
+ def replace(entry, src_path)
332
+ check_file(src_path)
333
+ remove(entry)
334
+ add(entry, src_path)
335
+ end
336
+
337
+ # Extracts entry to file dest_path.
338
+ def extract(entry, dest_path, &block)
339
+ block ||= proc { ::Zip.on_exists_proc }
340
+ found_entry = get_entry(entry)
341
+ found_entry.extract(dest_path, &block)
342
+ end
343
+
344
+ # Commits changes that has been made since the previous commit to
345
+ # the zip archive.
346
+ def commit
347
+ return if name.kind_of?(StringIO) || !commit_required?
348
+
349
+ on_success_replace do |tmp_file|
350
+ ::Zip::OutputStream.open(tmp_file) do |zos|
351
+ @entry_set.each do |e|
352
+ e.write_to_zip_output_stream(zos)
353
+ e.dirty = false
354
+ e.clean_up
355
+ end
356
+ zos.comment = comment
357
+ end
358
+ true
359
+ end
360
+ initialize(name)
361
+ end
362
+
363
+ # Write buffer write changes to buffer and return
364
+ def write_buffer(io = ::StringIO.new(''))
365
+ ::Zip::OutputStream.write_buffer(io) do |zos|
366
+ @entry_set.each { |e| e.write_to_zip_output_stream(zos) }
367
+ zos.comment = comment
368
+ end
369
+ end
370
+
371
+ # Closes the zip file committing any changes that has been made.
372
+ def close
373
+ commit
374
+ end
375
+
376
+ # Returns true if any changes has been made to this archive since
377
+ # the previous commit
378
+ def commit_required?
379
+ @entry_set.each do |e|
380
+ return true if e.dirty
381
+ end
382
+ @comment != @stored_comment || @entry_set != @stored_entries || @create
383
+ end
384
+
385
+ # Searches for entry with the specified name. Returns nil if
386
+ # no entry is found. See also get_entry
387
+ def 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
395
+ end
396
+
397
+ # Searches for entries given a glob
398
+ def glob(*args, &block)
399
+ @entry_set.glob(*args, &block)
400
+ end
401
+
402
+ # Searches for an entry just as find_entry, but throws Errno::ENOENT
403
+ # if no entry is found.
404
+ def get_entry(entry)
405
+ selected_entry = find_entry(entry)
406
+ raise Errno::ENOENT, entry if selected_entry.nil?
407
+
408
+ selected_entry
409
+ end
410
+
411
+ # Creates a directory
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)
418
+ end
419
+
420
+ private
421
+
422
+ def directory?(new_entry, src_path)
423
+ path_is_directory = ::File.directory?(src_path)
424
+ if new_entry.directory? && !path_is_directory
425
+ raise ArgumentError,
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 += '/'
430
+ end
431
+ new_entry.directory? && path_is_directory
432
+ end
433
+
434
+ def check_entry_exists(entry_name, continue_on_exists_proc, proc_name)
435
+ continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
436
+ return unless @entry_set.include?(entry_name)
437
+
438
+ if continue_on_exists_proc.call
439
+ remove get_entry(entry_name)
440
+ else
441
+ raise ::Zip::EntryExistsError,
442
+ proc_name + " failed. Entry #{entry_name} already exists"
443
+ end
444
+ end
445
+
446
+ def check_file(path)
447
+ raise Errno::ENOENT, path unless ::File.readable?(path)
448
+ end
449
+
450
+ def on_success_replace
451
+ dirname, basename = ::File.split(name)
452
+ ::Dir::Tmpname.create(basename, dirname) do |tmp_filename|
453
+ begin
454
+ if yield tmp_filename
455
+ ::File.rename(tmp_filename, name)
456
+ ::File.chmod(@file_permissions, name) unless @create
457
+ end
458
+ ensure
459
+ ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename)
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end
465
+
466
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
467
+ # rubyzip is free software; you can redistribute it and/or
468
+ # modify it under the terms of the ruby license.