rubyzip 1.1.7 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +143 -54
  3. data/Rakefile +3 -4
  4. data/lib/zip/central_directory.rb +8 -8
  5. data/lib/zip/compressor.rb +1 -2
  6. data/lib/zip/constants.rb +5 -5
  7. data/lib/zip/crypto/null_encryption.rb +4 -6
  8. data/lib/zip/crypto/traditional_encryption.rb +5 -5
  9. data/lib/zip/decompressor.rb +3 -3
  10. data/lib/zip/deflater.rb +8 -6
  11. data/lib/zip/dos_time.rb +5 -6
  12. data/lib/zip/entry.rb +132 -128
  13. data/lib/zip/entry_set.rb +14 -14
  14. data/lib/zip/errors.rb +2 -0
  15. data/lib/zip/extra_field/generic.rb +8 -8
  16. data/lib/zip/extra_field/ntfs.rb +14 -16
  17. data/lib/zip/extra_field/old_unix.rb +9 -10
  18. data/lib/zip/extra_field/universal_time.rb +14 -14
  19. data/lib/zip/extra_field/unix.rb +8 -9
  20. data/lib/zip/extra_field/zip64.rb +12 -11
  21. data/lib/zip/extra_field/zip64_placeholder.rb +1 -2
  22. data/lib/zip/extra_field.rb +8 -8
  23. data/lib/zip/file.rb +88 -81
  24. data/lib/zip/filesystem.rb +144 -143
  25. data/lib/zip/inflater.rb +5 -5
  26. data/lib/zip/input_stream.rb +22 -13
  27. data/lib/zip/ioextras/abstract_input_stream.rb +6 -10
  28. data/lib/zip/ioextras/abstract_output_stream.rb +3 -5
  29. data/lib/zip/ioextras.rb +1 -3
  30. data/lib/zip/null_compressor.rb +2 -2
  31. data/lib/zip/null_decompressor.rb +3 -3
  32. data/lib/zip/null_input_stream.rb +0 -0
  33. data/lib/zip/output_stream.rb +13 -14
  34. data/lib/zip/pass_thru_compressor.rb +4 -4
  35. data/lib/zip/pass_thru_decompressor.rb +3 -4
  36. data/lib/zip/streamable_directory.rb +2 -2
  37. data/lib/zip/streamable_stream.rb +3 -3
  38. data/lib/zip/version.rb +1 -1
  39. data/lib/zip.rb +13 -5
  40. data/samples/example.rb +29 -39
  41. data/samples/example_filesystem.rb +16 -18
  42. data/samples/example_recursive.rb +31 -25
  43. data/samples/{gtkRubyzip.rb → gtk_ruby_zip.rb} +23 -25
  44. data/samples/qtzip.rb +18 -27
  45. data/samples/write_simple.rb +12 -13
  46. data/samples/zipfind.rb +26 -34
  47. data/test/basic_zip_file_test.rb +11 -15
  48. data/test/case_sensitivity_test.rb +69 -0
  49. data/test/central_directory_entry_test.rb +32 -36
  50. data/test/central_directory_test.rb +46 -50
  51. data/test/crypto/null_encryption_test.rb +8 -4
  52. data/test/crypto/traditional_encryption_test.rb +5 -5
  53. data/test/data/gpbit3stored.zip +0 -0
  54. data/test/data/notzippedruby.rb +1 -1
  55. data/test/data/oddExtraField.zip +0 -0
  56. data/test/data/path_traversal/Makefile +10 -0
  57. data/test/data/path_traversal/jwilk/README.md +5 -0
  58. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  59. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  60. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  61. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  62. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  63. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  64. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  65. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  66. data/test/data/path_traversal/relative1.zip +0 -0
  67. data/test/data/path_traversal/tilde.zip +0 -0
  68. data/test/data/path_traversal/tuzovakaoff/README.md +3 -0
  69. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  70. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  71. data/test/data/rubycode.zip +0 -0
  72. data/test/data/test.xls +0 -0
  73. data/test/deflater_test.rb +10 -12
  74. data/test/encryption_test.rb +2 -2
  75. data/test/entry_set_test.rb +50 -25
  76. data/test/entry_test.rb +76 -87
  77. data/test/errors_test.rb +1 -2
  78. data/test/extra_field_test.rb +19 -21
  79. data/test/file_extract_directory_test.rb +12 -14
  80. data/test/file_extract_test.rb +94 -39
  81. data/test/file_permissions_test.rb +65 -0
  82. data/test/file_split_test.rb +24 -27
  83. data/test/file_test.rb +286 -179
  84. data/test/filesystem/dir_iterator_test.rb +13 -17
  85. data/test/filesystem/directory_test.rb +101 -93
  86. data/test/filesystem/file_mutating_test.rb +52 -65
  87. data/test/filesystem/file_nonmutating_test.rb +223 -229
  88. data/test/filesystem/file_stat_test.rb +17 -19
  89. data/test/gentestfiles.rb +54 -62
  90. data/test/inflater_test.rb +1 -1
  91. data/test/input_stream_test.rb +52 -40
  92. data/test/ioextras/abstract_input_stream_test.rb +22 -23
  93. data/test/ioextras/abstract_output_stream_test.rb +33 -33
  94. data/test/ioextras/fake_io_test.rb +1 -1
  95. data/test/local_entry_test.rb +36 -38
  96. data/test/output_stream_test.rb +20 -21
  97. data/test/pass_thru_compressor_test.rb +5 -6
  98. data/test/pass_thru_decompressor_test.rb +0 -1
  99. data/test/path_traversal_test.rb +141 -0
  100. data/test/samples/example_recursive_test.rb +37 -0
  101. data/test/settings_test.rb +18 -15
  102. data/test/test_helper.rb +52 -46
  103. data/test/unicode_file_names_and_comments_test.rb +17 -7
  104. data/test/zip64_full_test.rb +10 -12
  105. data/test/zip64_support_test.rb +0 -1
  106. metadata +100 -66
data/lib/zip/file.rb CHANGED
@@ -43,13 +43,13 @@ 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
46
+ CREATE = true
48
47
  SPLIT_SIGNATURE = 0x08074b50
49
48
  ZIP64_EOCD_SIGNATURE = 0x06064b50
50
- MAX_SEGMENT_SIZE = 3221225472
51
- MIN_SEGMENT_SIZE = 65536
49
+ MAX_SEGMENT_SIZE = 3_221_225_472
50
+ MIN_SEGMENT_SIZE = 65_536
52
51
  DATA_BUFFER_SIZE = 8192
52
+ IO_METHODS = [:tell, :seek, :read, :close]
53
53
 
54
54
  attr_reader :name
55
55
 
@@ -64,23 +64,38 @@ module Zip
64
64
 
65
65
  # Opens a zip archive. Pass true as the second parameter to create
66
66
  # a new archive if it doesn't exist already.
67
- def initialize(file_name, create = nil, buffer = false, options = {})
67
+ def initialize(path_or_io, create = false, buffer = false, options = {})
68
68
  super()
69
- @name = file_name
69
+ @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
70
70
  @comment = ''
71
- @create = create
72
- case
73
- when !buffer && ::File.size?(file_name)
74
- @create = nil
75
- @exist_file_perms = ::File.stat(file_name).mode
76
- ::File.open(name, 'rb') do |f|
77
- read_from_stream(f)
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
78
84
  end
79
- when create
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.
80
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?"
81
94
  else
82
- raise Error, "File #{file_name} not found"
95
+ # Everything is wrong.
96
+ raise Error, "File #{@name} not found"
83
97
  end
98
+
84
99
  @stored_entries = @entry_set.dup
85
100
  @stored_comment = @comment
86
101
  @restore_ownership = options[:restore_ownership] || false
@@ -92,7 +107,7 @@ module Zip
92
107
  # Same as #new. If a block is passed the ZipFile object is passed
93
108
  # to the block and is automatically closed afterwards just as with
94
109
  # ruby's builtin File.open method.
95
- def open(file_name, create = nil)
110
+ def open(file_name, create = false)
96
111
  zf = ::Zip::File.new(file_name, create)
97
112
  return zf unless block_given?
98
113
  begin
@@ -115,23 +130,23 @@ module Zip
115
130
  # (This can be used to extract data from a
116
131
  # downloaded zip archive without first saving it to disk.)
117
132
  def open_buffer(io, options = {})
118
- unless io.is_a?(IO) || io.is_a?(String) || io.is_a?(Tempfile)
119
- raise "Zip::File.open_buffer expects an argument of class String, IO, or Tempfile. Found: #{io.class}"
120
- end
121
- if io.is_a?(::String)
122
- require 'stringio'
123
- io = ::StringIO.new(io)
124
- elsif io.is_a?(IO)
125
- # https://github.com/rubyzip/rubyzip/issues/119
126
- io.binmode
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}"
127
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
+
128
142
  zf = ::Zip::File.new(io, true, true, options)
129
- zf.read_from_stream(io)
143
+ return zf unless block_given?
130
144
  yield zf
145
+
131
146
  begin
132
147
  zf.write_buffer(io)
133
148
  rescue IOError => e
134
- raise unless e.message == "not opened for writing"
149
+ raise unless e.message == 'not opened for writing'
135
150
  end
136
151
  end
137
152
 
@@ -148,10 +163,9 @@ module Zip
148
163
  end
149
164
 
150
165
  def get_segment_size_for_split(segment_size)
151
- case
152
- when MIN_SEGMENT_SIZE > segment_size
166
+ if MIN_SEGMENT_SIZE > segment_size
153
167
  MIN_SEGMENT_SIZE
154
- when MAX_SEGMENT_SIZE < segment_size
168
+ elsif MAX_SEGMENT_SIZE < segment_size
155
169
  MAX_SEGMENT_SIZE
156
170
  else
157
171
  segment_size
@@ -159,8 +173,10 @@ module Zip
159
173
  end
160
174
 
161
175
  def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
162
- partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
163
- partial_zip_file_name + ::File.extname(zip_file_name)) unless partial_zip_file_name.nil?
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
164
180
  partial_zip_file_name ||= zip_file_name
165
181
  partial_zip_file_name
166
182
  end
@@ -181,7 +197,7 @@ module Zip
181
197
  def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
182
198
  ssegment_size = zip_file_size - zip_file.pos
183
199
  ssegment_size = segment_size if ssegment_size > segment_size
184
- szip_file_name = "#{partial_zip_file_name}.#{'%03d'%(szip_file_index)}"
200
+ szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}"
185
201
  ::File.open(szip_file_name, 'wb') do |szip_file|
186
202
  if szip_file_index == 1
187
203
  ssegment_size = put_split_signature(szip_file, segment_size)
@@ -191,7 +207,7 @@ module Zip
191
207
  segment_bytes_left = ssegment_size - chunk_bytes
192
208
  buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
193
209
  chunk = zip_file.read(buffer_size)
194
- chunk_bytes += buffer_size
210
+ chunk_bytes += buffer_size
195
211
  szip_file << chunk
196
212
  # Info for track splitting
197
213
  yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
@@ -208,7 +224,7 @@ module Zip
208
224
  return if zip_file_size <= segment_size
209
225
  segment_count = get_segment_count_for_split(zip_file_size, segment_size)
210
226
  # Checking for correct zip structure
211
- self.open(zip_file_name) {}
227
+ open(zip_file_name) {}
212
228
  partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
213
229
  szip_file_index = 0
214
230
  ::File.open(zip_file_name, 'rb') do |zip_file|
@@ -222,7 +238,6 @@ module Zip
222
238
  end
223
239
  end
224
240
 
225
-
226
241
  # Returns an input stream to the specified entry. If a block is passed
227
242
  # the stream object is passed to the block and the stream is automatically
228
243
  # closed afterwards just as with ruby's builtin File.open method.
@@ -235,7 +250,7 @@ module Zip
235
250
  # specified. If a block is passed the stream object is passed to the block and
236
251
  # the stream is automatically closed afterwards just as with ruby's builtin
237
252
  # File.open method.
238
- 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)
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)
239
254
  new_entry =
240
255
  if entry.kind_of?(Entry)
241
256
  entry
@@ -265,13 +280,20 @@ module Zip
265
280
  # Convenience method for adding the contents of a file to the archive
266
281
  def add(entry, src_path, &continue_on_exists_proc)
267
282
  continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
268
- check_entry_exists(entry, continue_on_exists_proc, "add")
283
+ check_entry_exists(entry, continue_on_exists_proc, 'add')
269
284
  new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
270
285
  new_entry.gather_fileinfo_from_srcpath(src_path)
271
286
  new_entry.dirty = true
272
287
  @entry_set << new_entry
273
288
  end
274
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
+
275
297
  # Removes the specified entry.
276
298
  def remove(entry)
277
299
  @entry_set.delete(get_entry(entry))
@@ -304,7 +326,7 @@ module Zip
304
326
  # Commits changes that has been made since the previous commit to
305
327
  # the zip archive.
306
328
  def commit
307
- return unless commit_required?
329
+ return if name.is_a?(StringIO) || !commit_required?
308
330
  on_success_replace do |tmp_file|
309
331
  ::Zip::OutputStream.open(tmp_file) do |zos|
310
332
  @entry_set.each do |e|
@@ -338,7 +360,7 @@ module Zip
338
360
  @entry_set.each do |e|
339
361
  return true if e.dirty
340
362
  end
341
- @comment != @stored_comment || @entry_set != @stored_entries || @create == ::Zip::File::CREATE
363
+ @comment != @stored_comment || @entry_set != @stored_entries || @create
342
364
  end
343
365
 
344
366
  # Searches for entry with the specified name. Returns nil if
@@ -356,9 +378,7 @@ module Zip
356
378
  # if no entry is found.
357
379
  def get_entry(entry)
358
380
  selected_entry = find_entry(entry)
359
- unless selected_entry
360
- raise Errno::ENOENT, entry
361
- end
381
+ raise Errno::ENOENT, entry unless selected_entry
362
382
  selected_entry.restore_ownership = @restore_ownership
363
383
  selected_entry.restore_permissions = @restore_permissions
364
384
  selected_entry.restore_times = @restore_times
@@ -366,10 +386,8 @@ module Zip
366
386
  end
367
387
 
368
388
  # Creates a directory
369
- def mkdir(entryName, permissionInt = 0755)
370
- if find_entry(entryName)
371
- raise Errno::EEXIST, "File exists - #{entryName}"
372
- end
389
+ def mkdir(entryName, permissionInt = 0o755)
390
+ raise Errno::EEXIST, "File exists - #{entryName}" if find_entry(entryName)
373
391
  entryName = entryName.dup.to_s
374
392
  entryName << '/' unless entryName.end_with?('/')
375
393
  @entry_set << ::Zip::StreamableDirectory.new(@name, entryName, nil, permissionInt)
@@ -377,57 +395,46 @@ module Zip
377
395
 
378
396
  private
379
397
 
380
- def is_directory(newEntry, srcPath)
398
+ def directory?(newEntry, srcPath)
381
399
  srcPathIsDirectory = ::File.directory?(srcPath)
382
- if newEntry.is_directory && !srcPathIsDirectory
400
+ if newEntry.directory? && !srcPathIsDirectory
383
401
  raise ArgumentError,
384
- "entry name '#{newEntry}' indicates directory entry, but "+
385
- "'#{srcPath}' is not a directory"
386
- elsif !newEntry.is_directory && srcPathIsDirectory
387
- newEntry.name += "/"
402
+ "entry name '#{newEntry}' indicates directory entry, but " \
403
+ "'#{srcPath}' is not a directory"
404
+ elsif !newEntry.directory? && srcPathIsDirectory
405
+ newEntry.name += '/'
388
406
  end
389
- newEntry.is_directory && srcPathIsDirectory
407
+ newEntry.directory? && srcPathIsDirectory
390
408
  end
391
409
 
392
410
  def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
393
411
  continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
394
- if @entry_set.include?(entryName)
395
- if continue_on_exists_proc.call
396
- remove get_entry(entryName)
397
- else
398
- raise ::Zip::EntryExistsError,
399
- procedureName + " failed. Entry #{entryName} already exists"
400
- end
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"
401
418
  end
402
419
  end
403
420
 
404
421
  def check_file(path)
405
- unless ::File.readable?(path)
406
- raise Errno::ENOENT, path
407
- end
422
+ raise Errno::ENOENT, path unless ::File.readable?(path)
408
423
  end
409
424
 
410
425
  def on_success_replace
411
- tmpfile = get_tempfile
412
- tmp_filename = tmpfile.path
413
- ObjectSpace.undefine_finalizer(tmpfile)
414
- tmpfile.close
415
- if yield tmp_filename
416
- ::File.rename(tmp_filename, self.name)
417
- if defined?(@exist_file_perms)
418
- ::File.chmod(@exist_file_perms, self.name)
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)
419
435
  end
420
436
  end
421
- ensure
422
- tmpfile.unlink if tmpfile
423
437
  end
424
-
425
- def get_tempfile
426
- temp_file = Tempfile.new(::File.basename(name), ::File.dirname(name))
427
- temp_file.binmode
428
- temp_file
429
- end
430
-
431
438
  end
432
439
  end
433
440
 
@@ -1,21 +1,20 @@
1
1
  require 'zip'
2
2
 
3
3
  module Zip
4
-
5
- # The ZipFileSystem API provides an API for accessing entries in
6
- # a zip archive that is similar to ruby's builtin File and Dir
4
+ # The ZipFileSystem API provides an API for accessing entries in
5
+ # a zip archive that is similar to ruby's builtin File and Dir
7
6
  # classes.
8
7
  #
9
8
  # Requiring 'zip/filesystem' includes this module in Zip::File
10
9
  # making the methods in this module available on Zip::File objects.
11
10
  #
12
- # Using this API the following example creates a new zip file
11
+ # Using this API the following example creates a new zip file
13
12
  # <code>my.zip</code> containing a normal entry with the name
14
13
  # <code>first.txt</code>, a directory entry named <code>mydir</code>
15
14
  # and finally another normal entry named <code>second.txt</code>
16
15
  #
17
16
  # require 'zip/filesystem'
18
- #
17
+ #
19
18
  # Zip::File.open("my.zip", Zip::File::CREATE) {
20
19
  # |zipfile|
21
20
  # zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" }
@@ -23,19 +22,18 @@ module Zip
23
22
  # zipfile.file.open("mydir/second.txt", "w") { |f| f.puts "Hello again" }
24
23
  # }
25
24
  #
26
- # Reading is as easy as writing, as the following example shows. The
25
+ # Reading is as easy as writing, as the following example shows. The
27
26
  # example writes the contents of <code>first.txt</code> from zip archive
28
27
  # <code>my.zip</code> to standard out.
29
28
  #
30
29
  # require 'zip/filesystem'
31
- #
30
+ #
32
31
  # Zip::File.open("my.zip") {
33
32
  # |zipfile|
34
33
  # puts zipfile.file.read("first.txt")
35
34
  # }
36
35
 
37
36
  module FileSystem
38
-
39
37
  def initialize # :nodoc:
40
38
  mappedZip = ZipFileNameMapper.new(self)
41
39
  @zipFsDir = ZipFsDir.new(mappedZip)
@@ -65,24 +63,20 @@ module Zip
65
63
  # The individual methods are not documented due to their
66
64
  # similarity with the methods in File
67
65
  class ZipFsFile
68
-
69
66
  attr_writer :dir
70
- # protected :dir
67
+ # protected :dir
71
68
 
72
69
  class ZipFsStat
73
-
74
70
  class << self
75
-
76
71
  def delegate_to_fs_file(*methods)
77
72
  methods.each do |method|
78
- self.class_eval <<-end_eval, __FILE__, __LINE__ + 1
73
+ class_eval <<-end_eval, __FILE__, __LINE__ + 1
79
74
  def #{method} # def file?
80
75
  @zipFsFile.#{method}(@entryName) # @zipFsFile.file?(@entryName)
81
76
  end # end
82
77
  end_eval
83
78
  end
84
79
  end
85
-
86
80
  end
87
81
 
88
82
  def initialize(zipFsFile, entryName)
@@ -91,15 +85,17 @@ module Zip
91
85
  end
92
86
 
93
87
  def kind_of?(t)
94
- super || t == ::File::Stat
88
+ super || t == ::File::Stat
95
89
  end
96
90
 
97
91
  delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?,
98
- :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime,
99
- :writable_real?, :executable?, :executable_real?, :sticky?, :owned?,
100
- :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime
92
+ :socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime,
93
+ :writable_real?, :executable?, :executable_real?, :sticky?, :owned?,
94
+ :grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime
101
95
 
102
- def blocks; nil; end
96
+ def blocks
97
+ nil
98
+ end
103
99
 
104
100
  def get_entry
105
101
  @zipFsFile.__send__(:get_entry, @entryName)
@@ -108,8 +104,8 @@ module Zip
108
104
 
109
105
  def gid
110
106
  e = get_entry
111
- if e.extra.member? "IUnix"
112
- e.extra["IUnix"].gid || 0
107
+ if e.extra.member? 'IUnix'
108
+ e.extra['IUnix'].gid || 0
113
109
  else
114
110
  0
115
111
  end
@@ -117,43 +113,57 @@ module Zip
117
113
 
118
114
  def uid
119
115
  e = get_entry
120
- if e.extra.member? "IUnix"
121
- e.extra["IUnix"].uid || 0
116
+ if e.extra.member? 'IUnix'
117
+ e.extra['IUnix'].uid || 0
122
118
  else
123
119
  0
124
120
  end
125
121
  end
126
122
 
127
- def ino; 0; end
123
+ def ino
124
+ 0
125
+ end
128
126
 
129
- def dev; 0; end
127
+ def dev
128
+ 0
129
+ end
130
130
 
131
- def rdev; 0; end
131
+ def rdev
132
+ 0
133
+ end
132
134
 
133
- def rdev_major; 0; end
135
+ def rdev_major
136
+ 0
137
+ end
134
138
 
135
- def rdev_minor; 0; end
139
+ def rdev_minor
140
+ 0
141
+ end
136
142
 
137
143
  def ftype
138
144
  if file?
139
- return "file"
145
+ 'file'
140
146
  elsif directory?
141
- return "directory"
147
+ 'directory'
142
148
  else
143
- raise StandardError, "Unknown file type"
149
+ raise StandardError, 'Unknown file type'
144
150
  end
145
151
  end
146
152
 
147
- def nlink; 1; end
153
+ def nlink
154
+ 1
155
+ end
148
156
 
149
- def blksize; nil; end
157
+ def blksize
158
+ nil
159
+ end
150
160
 
151
161
  def mode
152
162
  e = get_entry
153
163
  if e.fstype == 3
154
164
  e.external_file_attributes >> 16
155
165
  else
156
- 33206 # 33206 is equivalent to -rw-rw-rw-
166
+ 33_206 # 33206 is equivalent to -rw-rw-rw-
157
167
  end
158
168
  end
159
169
  end
@@ -163,7 +173,7 @@ module Zip
163
173
  end
164
174
 
165
175
  def get_entry(fileName)
166
- if ! exists?(fileName)
176
+ unless exists?(fileName)
167
177
  raise Errno::ENOENT, "No such file or directory - #{fileName}"
168
178
  end
169
179
  @mappedZip.find_entry(fileName)
@@ -171,77 +181,75 @@ module Zip
171
181
  private :get_entry
172
182
 
173
183
  def unix_mode_cmp(fileName, mode)
174
- begin
175
- e = get_entry(fileName)
176
- e.fstype == 3 && ((e.external_file_attributes >> 16) & mode ) != 0
177
- rescue Errno::ENOENT
178
- false
179
- end
184
+ e = get_entry(fileName)
185
+ e.fstype == 3 && ((e.external_file_attributes >> 16) & mode) != 0
186
+ rescue Errno::ENOENT
187
+ false
180
188
  end
181
189
  private :unix_mode_cmp
182
190
 
183
191
  def exists?(fileName)
184
- expand_path(fileName) == "/" || @mappedZip.find_entry(fileName) != nil
192
+ expand_path(fileName) == '/' || !@mappedZip.find_entry(fileName).nil?
185
193
  end
186
- alias :exist? :exists?
194
+ alias exist? exists?
187
195
 
188
196
  # Permissions not implemented, so if the file exists it is accessible
189
- alias owned? exists?
190
- alias grpowned? exists?
197
+ alias owned? exists?
198
+ alias grpowned? exists?
191
199
 
192
200
  def readable?(fileName)
193
- unix_mode_cmp(fileName, 0444)
201
+ unix_mode_cmp(fileName, 0o444)
194
202
  end
195
- alias readable_real? readable?
203
+ alias readable_real? readable?
196
204
 
197
205
  def writable?(fileName)
198
- unix_mode_cmp(fileName, 0222)
206
+ unix_mode_cmp(fileName, 0o222)
199
207
  end
200
- alias writable_real? writable?
208
+ alias writable_real? writable?
201
209
 
202
210
  def executable?(fileName)
203
- unix_mode_cmp(fileName, 0111)
211
+ unix_mode_cmp(fileName, 0o111)
204
212
  end
205
213
  alias executable_real? executable?
206
214
 
207
215
  def setuid?(fileName)
208
- unix_mode_cmp(fileName, 04000)
216
+ unix_mode_cmp(fileName, 0o4000)
209
217
  end
210
218
 
211
219
  def setgid?(fileName)
212
- unix_mode_cmp(fileName, 02000)
220
+ unix_mode_cmp(fileName, 0o2000)
213
221
  end
214
222
 
215
223
  def sticky?(fileName)
216
- unix_mode_cmp(fileName, 01000)
224
+ unix_mode_cmp(fileName, 0o1000)
217
225
  end
218
226
 
219
227
  def umask(*args)
220
228
  ::File.umask(*args)
221
229
  end
222
230
 
223
- def truncate(fileName, len)
224
- raise StandardError, "truncate not supported"
231
+ def truncate(_fileName, _len)
232
+ raise StandardError, 'truncate not supported'
225
233
  end
226
234
 
227
235
  def directory?(fileName)
228
236
  entry = @mappedZip.find_entry(fileName)
229
- expand_path(fileName) == "/" || (entry != nil && entry.directory?)
237
+ expand_path(fileName) == '/' || (!entry.nil? && entry.directory?)
230
238
  end
231
239
 
232
- def open(fileName, openMode = "r", permissionInt = 0644, &block)
233
- openMode.gsub!("b", "") # ignore b option
240
+ def open(fileName, openMode = 'r', permissionInt = 0o644, &block)
241
+ openMode.delete!('b') # ignore b option
234
242
  case openMode
235
- when "r"
236
- @mappedZip.get_input_stream(fileName, &block)
237
- when "w"
238
- @mappedZip.get_output_stream(fileName, permissionInt, &block)
239
- else
240
- raise StandardError, "openmode '#{openMode} not supported" unless openMode == "r"
243
+ when 'r'
244
+ @mappedZip.get_input_stream(fileName, &block)
245
+ when 'w'
246
+ @mappedZip.get_output_stream(fileName, permissionInt, &block)
247
+ else
248
+ raise StandardError, "openmode '#{openMode} not supported" unless openMode == 'r'
241
249
  end
242
250
  end
243
251
 
244
- def new(fileName, openMode = "r")
252
+ def new(fileName, openMode = 'r')
245
253
  open(fileName, openMode)
246
254
  end
247
255
 
@@ -252,43 +260,41 @@ module Zip
252
260
  # Returns nil for not found and nil for directories
253
261
  def size?(fileName)
254
262
  entry = @mappedZip.find_entry(fileName)
255
- return (entry == nil || entry.directory?) ? nil : entry.size
263
+ entry.nil? || entry.directory? ? nil : entry.size
256
264
  end
257
265
 
258
266
  def chown(ownerInt, groupInt, *filenames)
259
- filenames.each { |fileName|
267
+ filenames.each do |fileName|
260
268
  e = get_entry(fileName)
261
- unless e.extra.member?("IUnix")
262
- e.extra.create("IUnix")
263
- end
264
- e.extra["IUnix"].uid = ownerInt
265
- e.extra["IUnix"].gid = groupInt
266
- }
269
+ e.extra.create('IUnix') unless e.extra.member?('IUnix')
270
+ e.extra['IUnix'].uid = ownerInt
271
+ e.extra['IUnix'].gid = groupInt
272
+ end
267
273
  filenames.size
268
274
  end
269
275
 
270
- def chmod (modeInt, *filenames)
271
- filenames.each { |fileName|
276
+ def chmod(modeInt, *filenames)
277
+ filenames.each do |fileName|
272
278
  e = get_entry(fileName)
273
279
  e.fstype = 3 # force convertion filesystem type to unix
274
280
  e.unix_perms = modeInt
275
281
  e.external_file_attributes = modeInt << 16
276
282
  e.dirty = true
277
- }
283
+ end
278
284
  filenames.size
279
285
  end
280
286
 
281
287
  def zero?(fileName)
282
288
  sz = size(fileName)
283
- sz == nil || sz == 0
289
+ sz.nil? || sz == 0
284
290
  rescue Errno::ENOENT
285
291
  false
286
292
  end
287
293
 
288
294
  def file?(fileName)
289
295
  entry = @mappedZip.find_entry(fileName)
290
- entry != nil && entry.file?
291
- end
296
+ !entry.nil? && entry.file?
297
+ end
292
298
 
293
299
  def dirname(fileName)
294
300
  ::File.dirname(fileName)
@@ -307,9 +313,9 @@ module Zip
307
313
  end
308
314
 
309
315
  def utime(modifiedTime, *fileNames)
310
- fileNames.each { |fileName|
316
+ fileNames.each do |fileName|
311
317
  get_entry(fileName).time = modifiedTime
312
- }
318
+ end
313
319
  end
314
320
 
315
321
  def mtime(fileName)
@@ -318,70 +324,64 @@ module Zip
318
324
 
319
325
  def atime(fileName)
320
326
  e = get_entry(fileName)
321
- if e.extra.member? "UniversalTime"
322
- e.extra["UniversalTime"].atime
323
- elsif e.extra.member? "NTFS"
324
- e.extra["NTFS"].atime
325
- else
326
- nil
327
+ if e.extra.member? 'UniversalTime'
328
+ e.extra['UniversalTime'].atime
329
+ elsif e.extra.member? 'NTFS'
330
+ e.extra['NTFS'].atime
327
331
  end
328
332
  end
329
333
 
330
334
  def ctime(fileName)
331
335
  e = get_entry(fileName)
332
- if e.extra.member? "UniversalTime"
333
- e.extra["UniversalTime"].ctime
334
- elsif e.extra.member? "NTFS"
335
- e.extra["NTFS"].ctime
336
- else
337
- nil
336
+ if e.extra.member? 'UniversalTime'
337
+ e.extra['UniversalTime'].ctime
338
+ elsif e.extra.member? 'NTFS'
339
+ e.extra['NTFS'].ctime
338
340
  end
339
341
  end
340
342
 
341
- def pipe?(filename)
343
+ def pipe?(_filename)
342
344
  false
343
345
  end
344
346
 
345
- def blockdev?(filename)
347
+ def blockdev?(_filename)
346
348
  false
347
349
  end
348
350
 
349
- def chardev?(filename)
351
+ def chardev?(_filename)
350
352
  false
351
353
  end
352
354
 
353
- def symlink?(fileName)
355
+ def symlink?(_fileName)
354
356
  false
355
357
  end
356
358
 
357
- def socket?(fileName)
359
+ def socket?(_fileName)
358
360
  false
359
361
  end
360
362
 
361
363
  def ftype(fileName)
362
- @mappedZip.get_entry(fileName).directory? ? "directory" : "file"
364
+ @mappedZip.get_entry(fileName).directory? ? 'directory' : 'file'
363
365
  end
364
366
 
365
- def readlink(fileName)
366
- raise NotImplementedError, "The readlink() function is not implemented"
367
+ def readlink(_fileName)
368
+ raise NotImplementedError, 'The readlink() function is not implemented'
367
369
  end
368
370
 
369
- def symlink(fileName, symlinkName)
370
- raise NotImplementedError, "The symlink() function is not implemented"
371
+ def symlink(_fileName, _symlinkName)
372
+ raise NotImplementedError, 'The symlink() function is not implemented'
371
373
  end
372
374
 
373
- def link(fileName, symlinkName)
374
- raise NotImplementedError, "The link() function is not implemented"
375
+ def link(_fileName, _symlinkName)
376
+ raise NotImplementedError, 'The link() function is not implemented'
375
377
  end
376
378
 
377
379
  def pipe
378
- raise NotImplementedError, "The pipe() function is not implemented"
380
+ raise NotImplementedError, 'The pipe() function is not implemented'
379
381
  end
380
382
 
381
383
  def stat(fileName)
382
- if ! exists?(fileName)
383
- raise Errno::ENOENT, fileName
384
- end
384
+ raise Errno::ENOENT, fileName unless exists?(fileName)
385
385
  ZipFsStat.new(self, fileName)
386
386
  end
387
387
 
@@ -404,20 +404,19 @@ module Zip
404
404
  end
405
405
 
406
406
  def delete(*args)
407
- args.each {
408
- |fileName|
407
+ args.each do |fileName|
409
408
  if directory?(fileName)
410
409
  raise Errno::EISDIR, "Is a directory - \"#{fileName}\""
411
410
  end
412
411
  @mappedZip.remove(fileName)
413
- }
412
+ end
414
413
  end
415
414
 
416
415
  def rename(fileToRename, newName)
417
416
  @mappedZip.rename(fileToRename, newName) { true }
418
417
  end
419
418
 
420
- alias :unlink :delete
419
+ alias unlink delete
421
420
 
422
421
  def expand_path(aPath)
423
422
  @mappedZip.expand_path(aPath)
@@ -431,7 +430,6 @@ module Zip
431
430
  # The individual methods are not documented due to their
432
431
  # similarity with the methods in Dir
433
432
  class ZipFsDir
434
-
435
433
  def initialize(mappedZip)
436
434
  @mappedZip = mappedZip
437
435
  end
@@ -455,7 +453,9 @@ module Zip
455
453
  dirIt
456
454
  end
457
455
 
458
- def pwd; @mappedZip.pwd; end
456
+ def pwd
457
+ @mappedZip.pwd
458
+ end
459
459
  alias getwd pwd
460
460
 
461
461
  def chdir(aDirectoryName)
@@ -471,8 +471,8 @@ module Zip
471
471
  entries
472
472
  end
473
473
 
474
- def glob(*args,&block)
475
- @mappedZip.glob(*args,&block)
474
+ def glob(*args, &block)
475
+ @mappedZip.glob(*args, &block)
476
476
  end
477
477
 
478
478
  def foreach(aDirectoryName)
@@ -483,11 +483,10 @@ module Zip
483
483
  path << '/' unless path.end_with?('/')
484
484
  path = Regexp.escape(path)
485
485
  subDirEntriesRegex = Regexp.new("^#{path}([^/]+)$")
486
- @mappedZip.each {
487
- |fileName|
486
+ @mappedZip.each do |fileName|
488
487
  match = subDirEntriesRegex.match(fileName)
489
- yield(match[1]) unless match == nil
490
- }
488
+ yield(match[1]) unless match.nil?
489
+ end
491
490
  end
492
491
 
493
492
  def delete(entryName)
@@ -496,17 +495,16 @@ module Zip
496
495
  end
497
496
  @mappedZip.remove(entryName)
498
497
  end
499
- alias rmdir delete
498
+ alias rmdir delete
500
499
  alias unlink delete
501
500
 
502
- def mkdir(entryName, permissionInt = 0755)
501
+ def mkdir(entryName, permissionInt = 0o755)
503
502
  @mappedZip.mkdir(entryName, permissionInt)
504
503
  end
505
504
 
506
- def chroot(*args)
507
- raise NotImplementedError, "The chroot() function is not implemented"
505
+ def chroot(*_args)
506
+ raise NotImplementedError, 'The chroot() function is not implemented'
508
507
  end
509
-
510
508
  end
511
509
 
512
510
  class ZipFsDirIterator # :nodoc:all
@@ -522,27 +520,27 @@ module Zip
522
520
  end
523
521
 
524
522
  def each(&aProc)
525
- raise IOError, "closed directory" if @fileNames == nil
523
+ raise IOError, 'closed directory' if @fileNames.nil?
526
524
  @fileNames.each(&aProc)
527
525
  end
528
526
 
529
527
  def read
530
- raise IOError, "closed directory" if @fileNames == nil
531
- @fileNames[(@index+=1)-1]
528
+ raise IOError, 'closed directory' if @fileNames.nil?
529
+ @fileNames[(@index += 1) - 1]
532
530
  end
533
531
 
534
532
  def rewind
535
- raise IOError, "closed directory" if @fileNames == nil
533
+ raise IOError, 'closed directory' if @fileNames.nil?
536
534
  @index = 0
537
535
  end
538
536
 
539
537
  def seek(anIntegerPosition)
540
- raise IOError, "closed directory" if @fileNames == nil
538
+ raise IOError, 'closed directory' if @fileNames.nil?
541
539
  @index = anIntegerPosition
542
540
  end
543
541
 
544
542
  def tell
545
- raise IOError, "closed directory" if @fileNames == nil
543
+ raise IOError, 'closed directory' if @fileNames.nil?
546
544
  @index
547
545
  end
548
546
  end
@@ -554,7 +552,7 @@ module Zip
554
552
 
555
553
  def initialize(zipFile)
556
554
  @zipFile = zipFile
557
- @pwd = "/"
555
+ @pwd = '/'
558
556
  end
559
557
 
560
558
  attr_accessor :pwd
@@ -575,6 +573,10 @@ module Zip
575
573
  @zipFile.get_output_stream(expand_to_entry(fileName), permissionInt, &aProc)
576
574
  end
577
575
 
576
+ def glob(pattern, *flags, &block)
577
+ @zipFile.glob(expand_to_entry(pattern), *flags, &block)
578
+ end
579
+
578
580
  def read(fileName)
579
581
  @zipFile.read(expand_to_entry(fileName))
580
582
  end
@@ -584,28 +586,27 @@ module Zip
584
586
  end
585
587
 
586
588
  def rename(fileName, newName, &continueOnExistsProc)
587
- @zipFile.rename(expand_to_entry(fileName), expand_to_entry(newName),
589
+ @zipFile.rename(expand_to_entry(fileName), expand_to_entry(newName),
588
590
  &continueOnExistsProc)
589
591
  end
590
592
 
591
- def mkdir(fileName, permissionInt = 0755)
593
+ def mkdir(fileName, permissionInt = 0o755)
592
594
  @zipFile.mkdir(expand_to_entry(fileName), permissionInt)
593
595
  end
594
596
 
595
597
  # Turns entries into strings and adds leading /
596
598
  # and removes trailing slash on directories
597
599
  def each
598
- @zipFile.each {
599
- |e|
600
- yield("/"+e.to_s.chomp("/"))
601
- }
600
+ @zipFile.each do |e|
601
+ yield('/' + e.to_s.chomp('/'))
602
+ end
602
603
  end
603
604
 
604
605
  def expand_path(aPath)
605
- expanded = aPath.start_with?("/") ? aPath : ::File.join(@pwd, aPath)
606
- expanded.gsub!(/\/\.(\/|$)/, "")
607
- expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, "")
608
- expanded.empty? ? "/" : expanded
606
+ expanded = aPath.start_with?('/') ? aPath : ::File.join(@pwd, aPath)
607
+ expanded.gsub!(/\/\.(\/|$)/, '')
608
+ expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '')
609
+ expanded.empty? ? '/' : expanded
609
610
  end
610
611
 
611
612
  private