rubyzip 0.9.5 → 1.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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +334 -45
  3. data/Rakefile +11 -6
  4. data/TODO +0 -1
  5. data/lib/zip/central_directory.rb +208 -0
  6. data/lib/zip/compressor.rb +1 -2
  7. data/lib/zip/constants.rb +61 -8
  8. data/lib/zip/crypto/encryption.rb +11 -0
  9. data/lib/zip/crypto/null_encryption.rb +43 -0
  10. data/lib/zip/crypto/traditional_encryption.rb +99 -0
  11. data/lib/zip/decompressor.rb +4 -4
  12. data/lib/zip/deflater.rb +18 -14
  13. data/lib/zip/dos_time.rb +48 -0
  14. data/lib/zip/entry.rb +700 -0
  15. data/lib/zip/entry_set.rb +86 -0
  16. data/lib/zip/errors.rb +18 -0
  17. data/lib/zip/extra_field/generic.rb +43 -0
  18. data/lib/zip/extra_field/ntfs.rb +90 -0
  19. data/lib/zip/extra_field/old_unix.rb +44 -0
  20. data/lib/zip/extra_field/universal_time.rb +47 -0
  21. data/lib/zip/extra_field/unix.rb +37 -0
  22. data/lib/zip/extra_field/zip64.rb +68 -0
  23. data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
  24. data/lib/zip/extra_field.rb +101 -0
  25. data/lib/zip/file.rb +443 -0
  26. data/lib/zip/{zipfilesystem.rb → filesystem.rb} +179 -164
  27. data/lib/zip/inflater.rb +29 -28
  28. data/lib/zip/input_stream.rb +173 -0
  29. data/lib/zip/ioextras/abstract_input_stream.rb +111 -0
  30. data/lib/zip/ioextras/abstract_output_stream.rb +43 -0
  31. data/lib/zip/ioextras.rb +21 -149
  32. data/lib/zip/null_compressor.rb +2 -2
  33. data/lib/zip/null_decompressor.rb +8 -6
  34. data/lib/zip/null_input_stream.rb +3 -2
  35. data/lib/zip/output_stream.rb +189 -0
  36. data/lib/zip/pass_thru_compressor.rb +7 -7
  37. data/lib/zip/pass_thru_decompressor.rb +19 -19
  38. data/lib/zip/{zip_streamable_directory.rb → streamable_directory.rb} +3 -3
  39. data/lib/zip/streamable_stream.rb +56 -0
  40. data/lib/zip/version.rb +3 -0
  41. data/lib/zip.rb +71 -0
  42. data/samples/example.rb +44 -32
  43. data/samples/example_filesystem.rb +16 -18
  44. data/samples/example_recursive.rb +33 -28
  45. data/samples/{gtkRubyzip.rb → gtk_ruby_zip.rb} +26 -28
  46. data/samples/qtzip.rb +22 -31
  47. data/samples/write_simple.rb +12 -13
  48. data/samples/zipfind.rb +31 -39
  49. data/test/basic_zip_file_test.rb +60 -0
  50. data/test/case_sensitivity_test.rb +69 -0
  51. data/test/central_directory_entry_test.rb +69 -0
  52. data/test/central_directory_test.rb +100 -0
  53. data/test/crypto/null_encryption_test.rb +57 -0
  54. data/test/crypto/traditional_encryption_test.rb +80 -0
  55. data/test/data/WarnInvalidDate.zip +0 -0
  56. data/test/data/file1.txt +46 -0
  57. data/test/data/file1.txt.deflatedData +0 -0
  58. data/test/data/file2.txt +1504 -0
  59. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  60. data/test/data/globTest/foo.txt +0 -0
  61. data/test/data/globTest/food.txt +0 -0
  62. data/test/data/globTest.zip +0 -0
  63. data/test/data/gpbit3stored.zip +0 -0
  64. data/test/data/mimetype +1 -0
  65. data/test/data/notzippedruby.rb +7 -0
  66. data/test/data/ntfs.zip +0 -0
  67. data/test/data/oddExtraField.zip +0 -0
  68. data/test/data/path_traversal/Makefile +10 -0
  69. data/test/data/path_traversal/jwilk/README.md +5 -0
  70. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  71. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  72. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  73. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  74. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  75. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  76. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  77. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  78. data/test/data/path_traversal/relative1.zip +0 -0
  79. data/test/data/path_traversal/tilde.zip +0 -0
  80. data/test/data/path_traversal/tuzovakaoff/README.md +3 -0
  81. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  82. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  83. data/test/data/rubycode.zip +0 -0
  84. data/test/data/rubycode2.zip +0 -0
  85. data/test/data/test.xls +0 -0
  86. data/test/data/testDirectory.bin +0 -0
  87. data/test/data/zip64-sample.zip +0 -0
  88. data/test/data/zipWithDirs.zip +0 -0
  89. data/test/data/zipWithEncryption.zip +0 -0
  90. data/test/deflater_test.rb +65 -0
  91. data/test/encryption_test.rb +42 -0
  92. data/test/entry_set_test.rb +163 -0
  93. data/test/entry_test.rb +154 -0
  94. data/test/errors_test.rb +35 -0
  95. data/test/extra_field_test.rb +76 -0
  96. data/test/file_extract_directory_test.rb +54 -0
  97. data/test/file_extract_test.rb +145 -0
  98. data/test/file_permissions_test.rb +65 -0
  99. data/test/file_split_test.rb +57 -0
  100. data/test/file_test.rb +666 -0
  101. data/test/filesystem/dir_iterator_test.rb +58 -0
  102. data/test/filesystem/directory_test.rb +139 -0
  103. data/test/filesystem/file_mutating_test.rb +87 -0
  104. data/test/filesystem/file_nonmutating_test.rb +508 -0
  105. data/test/filesystem/file_stat_test.rb +64 -0
  106. data/test/gentestfiles.rb +126 -0
  107. data/test/inflater_test.rb +14 -0
  108. data/test/input_stream_test.rb +182 -0
  109. data/test/ioextras/abstract_input_stream_test.rb +102 -0
  110. data/test/ioextras/abstract_output_stream_test.rb +106 -0
  111. data/test/ioextras/fake_io_test.rb +18 -0
  112. data/test/local_entry_test.rb +154 -0
  113. data/test/output_stream_test.rb +128 -0
  114. data/test/pass_thru_compressor_test.rb +30 -0
  115. data/test/pass_thru_decompressor_test.rb +14 -0
  116. data/test/path_traversal_test.rb +141 -0
  117. data/test/samples/example_recursive_test.rb +37 -0
  118. data/test/settings_test.rb +95 -0
  119. data/test/test_helper.rb +234 -0
  120. data/test/unicode_file_names_and_comments_test.rb +62 -0
  121. data/test/zip64_full_test.rb +51 -0
  122. data/test/zip64_support_test.rb +14 -0
  123. metadata +275 -41
  124. data/NEWS +0 -162
  125. data/lib/zip/stdrubyext.rb +0 -77
  126. data/lib/zip/tempfile_bugfixed.rb +0 -195
  127. data/lib/zip/zip.rb +0 -54
  128. data/lib/zip/zip_central_directory.rb +0 -139
  129. data/lib/zip/zip_entry.rb +0 -639
  130. data/lib/zip/zip_entry_set.rb +0 -66
  131. data/lib/zip/zip_extra_field.rb +0 -213
  132. data/lib/zip/zip_file.rb +0 -318
  133. data/lib/zip/zip_input_stream.rb +0 -134
  134. data/lib/zip/zip_output_stream.rb +0 -172
  135. data/lib/zip/zip_streamable_stream.rb +0 -47
data/lib/zip/file.rb ADDED
@@ -0,0 +1,443 @@
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, :close]
53
+
54
+ attr_reader :name
55
+
56
+ # default -> false
57
+ attr_accessor :restore_ownership
58
+ # default -> false
59
+ attr_accessor :restore_permissions
60
+ # default -> true
61
+ attr_accessor :restore_times
62
+ # Returns the zip files comment, if it has one
63
+ attr_accessor :comment
64
+
65
+ # Opens a zip archive. Pass true as the second parameter to create
66
+ # a new archive if it doesn't exist already.
67
+ def initialize(path_or_io, create = false, buffer = false, options = {})
68
+ super()
69
+ @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
70
+ @comment = ''
71
+ @create = create ? true : false # allow any truthy value to mean true
72
+
73
+ if ::File.size?(@name.to_s)
74
+ # There is a file, which exists, that is associated with this zip.
75
+ @create = false
76
+ @file_permissions = ::File.stat(@name).mode
77
+
78
+ if buffer
79
+ read_from_stream(path_or_io)
80
+ else
81
+ ::File.open(@name, 'rb') do |f|
82
+ read_from_stream(f)
83
+ end
84
+ end
85
+ elsif buffer && path_or_io.size > 0
86
+ # This zip is probably a non-empty StringIO.
87
+ read_from_stream(path_or_io)
88
+ elsif @create
89
+ # This zip is completely new/empty and is to be created.
90
+ @entry_set = EntrySet.new
91
+ elsif ::File.zero?(@name)
92
+ # A file exists, but it is empty.
93
+ raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?"
94
+ else
95
+ # Everything is wrong.
96
+ raise Error, "File #{@name} not found"
97
+ end
98
+
99
+ @stored_entries = @entry_set.dup
100
+ @stored_comment = @comment
101
+ @restore_ownership = options[:restore_ownership] || false
102
+ @restore_permissions = options[:restore_permissions] || true
103
+ @restore_times = options[:restore_times] || true
104
+ end
105
+
106
+ class << self
107
+ # Same as #new. If a block is passed the ZipFile object is passed
108
+ # to the block and is automatically closed afterwards just as with
109
+ # ruby's builtin File.open method.
110
+ def open(file_name, create = false)
111
+ zf = ::Zip::File.new(file_name, create)
112
+ return zf unless block_given?
113
+ begin
114
+ yield zf
115
+ ensure
116
+ zf.close
117
+ end
118
+ end
119
+
120
+ # Same as #open. But outputs data to a buffer instead of a file
121
+ def add_buffer
122
+ io = ::StringIO.new('')
123
+ zf = ::Zip::File.new(io, true, true)
124
+ yield zf
125
+ zf.write_buffer(io)
126
+ end
127
+
128
+ # Like #open, but reads zip archive contents from a String or open IO
129
+ # stream, and outputs data to a buffer.
130
+ # (This can be used to extract data from a
131
+ # downloaded zip archive without first saving it to disk.)
132
+ def open_buffer(io, options = {})
133
+ unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.is_a?(String)
134
+ raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
135
+ end
136
+
137
+ io = ::StringIO.new(io) if io.is_a?(::String)
138
+
139
+ # https://github.com/rubyzip/rubyzip/issues/119
140
+ io.binmode if io.respond_to?(:binmode)
141
+
142
+ zf = ::Zip::File.new(io, true, true, options)
143
+ return zf unless block_given?
144
+ yield zf
145
+
146
+ begin
147
+ zf.write_buffer(io)
148
+ rescue IOError => e
149
+ raise unless e.message == 'not opened for writing'
150
+ end
151
+ end
152
+
153
+ # Iterates over the contents of the ZipFile. This is more efficient
154
+ # than using a ZipInputStream since this methods simply iterates
155
+ # through the entries in the central directory structure in the archive
156
+ # whereas ZipInputStream jumps through the entire archive accessing the
157
+ # local entry headers (which contain the same information as the
158
+ # central directory).
159
+ def foreach(aZipFileName, &block)
160
+ open(aZipFileName) do |zipFile|
161
+ zipFile.each(&block)
162
+ end
163
+ end
164
+
165
+ def get_segment_size_for_split(segment_size)
166
+ if MIN_SEGMENT_SIZE > segment_size
167
+ MIN_SEGMENT_SIZE
168
+ elsif MAX_SEGMENT_SIZE < segment_size
169
+ MAX_SEGMENT_SIZE
170
+ else
171
+ segment_size
172
+ end
173
+ end
174
+
175
+ def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
176
+ unless partial_zip_file_name.nil?
177
+ partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
178
+ partial_zip_file_name + ::File.extname(zip_file_name))
179
+ end
180
+ partial_zip_file_name ||= zip_file_name
181
+ partial_zip_file_name
182
+ end
183
+
184
+ def get_segment_count_for_split(zip_file_size, segment_size)
185
+ (zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1)
186
+ end
187
+
188
+ def put_split_signature(szip_file, segment_size)
189
+ signature_packed = [SPLIT_SIGNATURE].pack('V')
190
+ szip_file << signature_packed
191
+ segment_size - signature_packed.size
192
+ end
193
+
194
+ #
195
+ # TODO: Make the code more understandable
196
+ #
197
+ def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
198
+ ssegment_size = zip_file_size - zip_file.pos
199
+ ssegment_size = segment_size if ssegment_size > segment_size
200
+ szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}"
201
+ ::File.open(szip_file_name, 'wb') do |szip_file|
202
+ if szip_file_index == 1
203
+ ssegment_size = put_split_signature(szip_file, segment_size)
204
+ end
205
+ chunk_bytes = 0
206
+ until ssegment_size == chunk_bytes || zip_file.eof?
207
+ segment_bytes_left = ssegment_size - chunk_bytes
208
+ buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
209
+ chunk = zip_file.read(buffer_size)
210
+ chunk_bytes += buffer_size
211
+ szip_file << chunk
212
+ # Info for track splitting
213
+ yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
214
+ end
215
+ end
216
+ end
217
+
218
+ # Splits an archive into parts with segment size
219
+ def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
220
+ raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
221
+ raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
222
+ zip_file_size = ::File.size(zip_file_name)
223
+ segment_size = get_segment_size_for_split(segment_size)
224
+ return if zip_file_size <= segment_size
225
+ segment_count = get_segment_count_for_split(zip_file_size, segment_size)
226
+ # Checking for correct zip structure
227
+ open(zip_file_name) {}
228
+ partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
229
+ szip_file_index = 0
230
+ ::File.open(zip_file_name, 'rb') do |zip_file|
231
+ until zip_file.eof?
232
+ szip_file_index += 1
233
+ save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
234
+ end
235
+ end
236
+ ::File.delete(zip_file_name) if delete_zip_file
237
+ szip_file_index
238
+ end
239
+ end
240
+
241
+ # Returns an input stream to the specified entry. If a block is passed
242
+ # the stream object is passed to the block and the stream is automatically
243
+ # closed afterwards just as with ruby's builtin File.open method.
244
+ def get_input_stream(entry, &aProc)
245
+ get_entry(entry).get_input_stream(&aProc)
246
+ end
247
+
248
+ # Returns an output stream to the specified entry. If entry is not an instance
249
+ # of Zip::Entry, a new Zip::Entry will be initialized using the arguments
250
+ # specified. If a block is passed the stream object is passed to the block and
251
+ # the stream is automatically closed afterwards just as with ruby's builtin
252
+ # File.open method.
253
+ 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)
254
+ new_entry =
255
+ if entry.kind_of?(Entry)
256
+ entry
257
+ else
258
+ Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
259
+ end
260
+ if new_entry.directory?
261
+ raise ArgumentError,
262
+ "cannot open stream to directory entry - '#{new_entry}'"
263
+ end
264
+ new_entry.unix_perms = permission_int
265
+ zip_streamable_entry = StreamableStream.new(new_entry)
266
+ @entry_set << zip_streamable_entry
267
+ zip_streamable_entry.get_output_stream(&aProc)
268
+ end
269
+
270
+ # Returns the name of the zip archive
271
+ def to_s
272
+ @name
273
+ end
274
+
275
+ # Returns a string containing the contents of the specified entry
276
+ def read(entry)
277
+ get_input_stream(entry) { |is| is.read }
278
+ end
279
+
280
+ # Convenience method for adding the contents of a file to the archive
281
+ def add(entry, src_path, &continue_on_exists_proc)
282
+ continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
283
+ check_entry_exists(entry, continue_on_exists_proc, 'add')
284
+ new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
285
+ new_entry.gather_fileinfo_from_srcpath(src_path)
286
+ new_entry.dirty = true
287
+ @entry_set << new_entry
288
+ end
289
+
290
+ # Convenience method for adding the contents of a file to the archive
291
+ # in Stored format (uncompressed)
292
+ def add_stored(entry, src_path, &continue_on_exists_proc)
293
+ entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED)
294
+ add(entry, src_path, &continue_on_exists_proc)
295
+ end
296
+
297
+ # Removes the specified entry.
298
+ def remove(entry)
299
+ @entry_set.delete(get_entry(entry))
300
+ end
301
+
302
+ # Renames the specified entry.
303
+ def rename(entry, new_name, &continue_on_exists_proc)
304
+ foundEntry = get_entry(entry)
305
+ check_entry_exists(new_name, continue_on_exists_proc, 'rename')
306
+ @entry_set.delete(foundEntry)
307
+ foundEntry.name = new_name
308
+ @entry_set << foundEntry
309
+ end
310
+
311
+ # Replaces the specified entry with the contents of srcPath (from
312
+ # the file system).
313
+ def replace(entry, srcPath)
314
+ check_file(srcPath)
315
+ remove(entry)
316
+ add(entry, srcPath)
317
+ end
318
+
319
+ # Extracts entry to file dest_path.
320
+ def extract(entry, dest_path, &block)
321
+ block ||= proc { ::Zip.on_exists_proc }
322
+ found_entry = get_entry(entry)
323
+ found_entry.extract(dest_path, &block)
324
+ end
325
+
326
+ # Commits changes that has been made since the previous commit to
327
+ # the zip archive.
328
+ def commit
329
+ return if name.is_a?(StringIO) || !commit_required?
330
+ on_success_replace do |tmp_file|
331
+ ::Zip::OutputStream.open(tmp_file) do |zos|
332
+ @entry_set.each do |e|
333
+ e.write_to_zip_output_stream(zos)
334
+ e.dirty = false
335
+ e.clean_up
336
+ end
337
+ zos.comment = comment
338
+ end
339
+ true
340
+ end
341
+ initialize(name)
342
+ end
343
+
344
+ # Write buffer write changes to buffer and return
345
+ def write_buffer(io = ::StringIO.new(''))
346
+ ::Zip::OutputStream.write_buffer(io) do |zos|
347
+ @entry_set.each { |e| e.write_to_zip_output_stream(zos) }
348
+ zos.comment = comment
349
+ end
350
+ end
351
+
352
+ # Closes the zip file committing any changes that has been made.
353
+ def close
354
+ commit
355
+ end
356
+
357
+ # Returns true if any changes has been made to this archive since
358
+ # the previous commit
359
+ def commit_required?
360
+ @entry_set.each do |e|
361
+ return true if e.dirty
362
+ end
363
+ @comment != @stored_comment || @entry_set != @stored_entries || @create
364
+ end
365
+
366
+ # Searches for entry with the specified name. Returns nil if
367
+ # no entry is found. See also get_entry
368
+ def find_entry(entry_name)
369
+ @entry_set.find_entry(entry_name)
370
+ end
371
+
372
+ # Searches for entries given a glob
373
+ def glob(*args, &block)
374
+ @entry_set.glob(*args, &block)
375
+ end
376
+
377
+ # Searches for an entry just as find_entry, but throws Errno::ENOENT
378
+ # if no entry is found.
379
+ def get_entry(entry)
380
+ selected_entry = find_entry(entry)
381
+ raise Errno::ENOENT, entry unless selected_entry
382
+ selected_entry.restore_ownership = @restore_ownership
383
+ selected_entry.restore_permissions = @restore_permissions
384
+ selected_entry.restore_times = @restore_times
385
+ selected_entry
386
+ end
387
+
388
+ # Creates a directory
389
+ def mkdir(entryName, permissionInt = 0o755)
390
+ raise Errno::EEXIST, "File exists - #{entryName}" if find_entry(entryName)
391
+ entryName = entryName.dup.to_s
392
+ entryName << '/' unless entryName.end_with?('/')
393
+ @entry_set << ::Zip::StreamableDirectory.new(@name, entryName, nil, permissionInt)
394
+ end
395
+
396
+ private
397
+
398
+ def directory?(newEntry, srcPath)
399
+ srcPathIsDirectory = ::File.directory?(srcPath)
400
+ if newEntry.directory? && !srcPathIsDirectory
401
+ raise ArgumentError,
402
+ "entry name '#{newEntry}' indicates directory entry, but " \
403
+ "'#{srcPath}' is not a directory"
404
+ elsif !newEntry.directory? && srcPathIsDirectory
405
+ newEntry.name += '/'
406
+ end
407
+ newEntry.directory? && srcPathIsDirectory
408
+ end
409
+
410
+ def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
411
+ continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
412
+ return unless @entry_set.include?(entryName)
413
+ if continue_on_exists_proc.call
414
+ remove get_entry(entryName)
415
+ else
416
+ raise ::Zip::EntryExistsError,
417
+ procedureName + " failed. Entry #{entryName} already exists"
418
+ end
419
+ end
420
+
421
+ def check_file(path)
422
+ raise Errno::ENOENT, path unless ::File.readable?(path)
423
+ end
424
+
425
+ def on_success_replace
426
+ dirname, basename = ::File.split(name)
427
+ ::Dir::Tmpname.create(basename, dirname) do |tmp_filename|
428
+ begin
429
+ if yield tmp_filename
430
+ ::File.rename(tmp_filename, name)
431
+ ::File.chmod(@file_permissions, name) unless @create
432
+ end
433
+ ensure
434
+ ::File.unlink(tmp_filename) if ::File.exist?(tmp_filename)
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
442
+ # rubyzip is free software; you can redistribute it and/or
443
+ # modify it under the terms of the ruby license.