rubyzip 2.3.1 → 3.0.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +422 -0
  3. data/LICENSE.md +24 -0
  4. data/README.md +138 -41
  5. data/Rakefile +11 -7
  6. data/lib/zip/central_directory.rb +169 -123
  7. data/lib/zip/compressor.rb +3 -1
  8. data/lib/zip/constants.rb +29 -21
  9. data/lib/zip/crypto/decrypted_io.rb +4 -2
  10. data/lib/zip/crypto/encryption.rb +4 -2
  11. data/lib/zip/crypto/null_encryption.rb +6 -4
  12. data/lib/zip/crypto/traditional_encryption.rb +8 -6
  13. data/lib/zip/decompressor.rb +4 -3
  14. data/lib/zip/deflater.rb +10 -8
  15. data/lib/zip/dirtyable.rb +32 -0
  16. data/lib/zip/dos_time.rb +51 -5
  17. data/lib/zip/entry.rb +363 -222
  18. data/lib/zip/entry_set.rb +11 -9
  19. data/lib/zip/errors.rb +136 -16
  20. data/lib/zip/extra_field/generic.rb +6 -13
  21. data/lib/zip/extra_field/ntfs.rb +6 -4
  22. data/lib/zip/extra_field/old_unix.rb +3 -1
  23. data/lib/zip/extra_field/universal_time.rb +3 -1
  24. data/lib/zip/extra_field/unix.rb +5 -3
  25. data/lib/zip/extra_field/unknown.rb +33 -0
  26. data/lib/zip/extra_field/zip64.rb +12 -5
  27. data/lib/zip/extra_field.rb +16 -22
  28. data/lib/zip/file.rb +177 -223
  29. data/lib/zip/file_split.rb +91 -0
  30. data/lib/zip/filesystem/dir.rb +86 -0
  31. data/lib/zip/filesystem/directory_iterator.rb +48 -0
  32. data/lib/zip/filesystem/file.rb +262 -0
  33. data/lib/zip/filesystem/file_stat.rb +110 -0
  34. data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
  35. data/lib/zip/filesystem.rb +27 -596
  36. data/lib/zip/inflater.rb +6 -4
  37. data/lib/zip/input_stream.rb +50 -37
  38. data/lib/zip/ioextras/abstract_input_stream.rb +15 -10
  39. data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
  40. data/lib/zip/ioextras.rb +7 -7
  41. data/lib/zip/null_compressor.rb +3 -1
  42. data/lib/zip/null_decompressor.rb +3 -1
  43. data/lib/zip/null_input_stream.rb +3 -1
  44. data/lib/zip/output_stream.rb +53 -47
  45. data/lib/zip/pass_thru_compressor.rb +3 -1
  46. data/lib/zip/pass_thru_decompressor.rb +4 -2
  47. data/lib/zip/streamable_directory.rb +3 -1
  48. data/lib/zip/streamable_stream.rb +3 -0
  49. data/lib/zip/version.rb +3 -1
  50. data/lib/zip.rb +17 -23
  51. data/rubyzip.gemspec +39 -0
  52. data/samples/example.rb +8 -3
  53. data/samples/example_filesystem.rb +3 -2
  54. data/samples/example_recursive.rb +3 -1
  55. data/samples/gtk_ruby_zip.rb +4 -2
  56. data/samples/qtzip.rb +6 -5
  57. data/samples/write_simple.rb +2 -1
  58. data/samples/zipfind.rb +1 -0
  59. metadata +83 -33
  60. data/TODO +0 -15
  61. data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
1
4
  module Zip
2
5
  # InputStream is the basic class for reading zip entries in a
3
6
  # zip file. It is possible to create a InputStream object directly,
@@ -37,9 +40,8 @@ module Zip
37
40
  #
38
41
  # java.util.zip.ZipInputStream is the original inspiration for this
39
42
  # class.
40
-
41
43
  class InputStream
42
- CHUNK_SIZE = 32_768
44
+ CHUNK_SIZE = 32_768 # :nodoc:
43
45
 
44
46
  include ::Zip::IOExtras::AbstractInputStream
45
47
 
@@ -49,28 +51,35 @@ module Zip
49
51
  #
50
52
  # @param context [String||IO||StringIO] file path or IO/StringIO object
51
53
  # @param offset [Integer] offset in the IO/StringIO
52
- def initialize(context, offset = 0, decrypter = nil)
54
+ def initialize(context, offset: 0, decrypter: nil)
53
55
  super()
54
- @archive_io = get_io(context, offset)
55
- @decompressor = ::Zip::NullDecompressor
56
- @decrypter = decrypter || ::Zip::NullDecrypter.new
56
+ @archive_io = get_io(context, offset)
57
+ @decompressor = ::Zip::NullDecompressor
58
+ @decrypter = decrypter || ::Zip::NullDecrypter.new
57
59
  @current_entry = nil
60
+ @complete_entry = nil
58
61
  end
59
62
 
63
+ # Close this InputStream. All further IO will raise an IOError.
60
64
  def close
61
65
  @archive_io.close
62
66
  end
63
67
 
64
- # Returns a Entry object. It is necessary to call this
65
- # method on a newly created InputStream before reading from
66
- # the first entry in the archive. Returns nil when there are
67
- # no more entries.
68
+ # Returns an Entry object and positions the stream at the beginning of
69
+ # the entry data. It is necessary to call this method on a newly created
70
+ # InputStream before reading from the first entry in the archive.
71
+ # Returns nil when there are no more entries.
68
72
  def get_next_entry
69
- @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry
73
+ unless @current_entry.nil?
74
+ raise StreamingError, @current_entry if @current_entry.incomplete?
75
+
76
+ @archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET)
77
+ end
78
+
70
79
  open_entry
71
80
  end
72
81
 
73
- # Rewinds the stream to the beginning of the current entry
82
+ # Rewinds the stream to the beginning of the current entry.
74
83
  def rewind
75
84
  return if @current_entry.nil?
76
85
 
@@ -85,12 +94,19 @@ module Zip
85
94
  @decompressor.read(length, outbuf)
86
95
  end
87
96
 
97
+ # Returns the size of the current entry, or `nil` if there isn't one.
98
+ def size
99
+ return if @current_entry.nil?
100
+
101
+ @current_entry.size
102
+ end
103
+
88
104
  class << self
89
105
  # Same as #initialize but if a block is passed the opened
90
106
  # stream is passed to the block and closed when the block
91
107
  # returns.
92
- def open(filename_or_io, offset = 0, decrypter = nil)
93
- zio = new(filename_or_io, offset, decrypter)
108
+ def open(filename_or_io, offset: 0, decrypter: nil)
109
+ zio = new(filename_or_io, offset: offset, decrypter: decrypter)
94
110
  return zio unless block_given?
95
111
 
96
112
  begin
@@ -99,16 +115,11 @@ module Zip
99
115
  zio.close if zio
100
116
  end
101
117
  end
102
-
103
- def open_buffer(filename_or_io, offset = 0)
104
- warn 'open_buffer is deprecated!!! Use open instead!'
105
- ::Zip::InputStream.open(filename_or_io, offset)
106
- end
107
118
  end
108
119
 
109
120
  protected
110
121
 
111
- def get_io(io_or_file, offset = 0)
122
+ def get_io(io_or_file, offset = 0) # :nodoc:
112
123
  if io_or_file.respond_to?(:seek)
113
124
  io = io_or_file.dup
114
125
  io.seek(offset, ::IO::SEEK_SET)
@@ -120,56 +131,58 @@ module Zip
120
131
  end
121
132
  end
122
133
 
123
- def open_entry
134
+ def open_entry # :nodoc:
124
135
  @current_entry = ::Zip::Entry.read_local_entry(@archive_io)
125
- if @current_entry && @current_entry.encrypted? && @decrypter.kind_of?(NullEncrypter)
126
- raise Error, 'password required to decode zip file'
136
+ return if @current_entry.nil?
137
+
138
+ if @current_entry.encrypted? && @decrypter.kind_of?(NullDecrypter)
139
+ raise Error,
140
+ 'A password is required to decode this zip file'
127
141
  end
128
142
 
129
- if @current_entry && @current_entry.incomplete? && @current_entry.crc == 0 \
130
- && @current_entry.compressed_size == 0 \
131
- && @current_entry.size == 0 && !@complete_entry
132
- raise GPFBit3Error,
133
- 'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \
134
- 'Please use ::Zip::File instead of ::Zip::InputStream'
143
+ if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
144
+ raise StreamingError, @current_entry
135
145
  end
146
+
136
147
  @decrypted_io = get_decrypted_io
137
148
  @decompressor = get_decompressor
138
149
  flush
139
150
  @current_entry
140
151
  end
141
152
 
142
- def get_decrypted_io
153
+ def get_decrypted_io # :nodoc:
143
154
  header = @archive_io.read(@decrypter.header_bytesize)
144
155
  @decrypter.reset!(header)
145
156
 
146
157
  ::Zip::DecryptedIo.new(@archive_io, @decrypter)
147
158
  end
148
159
 
149
- def get_decompressor
160
+ def get_decompressor # :nodoc:
150
161
  return ::Zip::NullDecompressor if @current_entry.nil?
151
162
 
152
163
  decompressed_size =
153
- if @current_entry.incomplete? && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry
164
+ if @current_entry.incomplete? && @current_entry.crc == 0 &&
165
+ @current_entry.size == 0 && @complete_entry
154
166
  @complete_entry.size
155
167
  else
156
168
  @current_entry.size
157
169
  end
158
170
 
159
- decompressor_class = ::Zip::Decompressor.find_by_compression_method(@current_entry.compression_method)
171
+ decompressor_class = ::Zip::Decompressor.find_by_compression_method(
172
+ @current_entry.compression_method
173
+ )
160
174
  if decompressor_class.nil?
161
- raise ::Zip::CompressionMethodError,
162
- "Unsupported compression method #{@current_entry.compression_method}"
175
+ raise ::Zip::CompressionMethodError, @current_entry.compression_method
163
176
  end
164
177
 
165
178
  decompressor_class.new(@decrypted_io, decompressed_size)
166
179
  end
167
180
 
168
- def produce_input
181
+ def produce_input # :nodoc:
169
182
  @decompressor.read(CHUNK_SIZE)
170
183
  end
171
184
 
172
- def input_finished?
185
+ def input_finished? # :nodoc:
173
186
  @decompressor.eof
174
187
  end
175
188
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module IOExtras
4
+ module IOExtras # :nodoc:
3
5
  # Implements many of the convenience methods of IO
4
6
  # such as gets, getc, readline and readlines
5
7
  # depends on: input_finished?, produce_input and read
6
- module AbstractInputStream
8
+ module AbstractInputStream # :nodoc:
7
9
  include Enumerable
8
10
  include FakeIO
9
11
 
@@ -11,15 +13,15 @@ module Zip
11
13
  super
12
14
  @lineno = 0
13
15
  @pos = 0
14
- @output_buffer = ''
16
+ @output_buffer = +''
15
17
  end
16
18
 
17
19
  attr_accessor :lineno
18
20
  attr_reader :pos
19
21
 
20
- def read(number_of_bytes = nil, buf = '')
22
+ def read(number_of_bytes = nil, buf = +'')
21
23
  tbuf = if @output_buffer.bytesize > 0
22
- if number_of_bytes <= @output_buffer.bytesize
24
+ if number_of_bytes && number_of_bytes <= @output_buffer.bytesize
23
25
  @output_buffer.slice!(0, number_of_bytes)
24
26
  else
25
27
  number_of_bytes -= @output_buffer.bytesize if number_of_bytes
@@ -34,7 +36,7 @@ module Zip
34
36
  end
35
37
 
36
38
  if tbuf.nil? || tbuf.empty?
37
- return nil if number_of_bytes
39
+ return nil if number_of_bytes&.positive?
38
40
 
39
41
  return ''
40
42
  end
@@ -74,15 +76,18 @@ module Zip
74
76
  a_sep_string = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}" if a_sep_string.empty?
75
77
 
76
78
  buffer_index = 0
77
- over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes)
79
+ over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes
78
80
  while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit
79
81
  buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max
80
82
  return @output_buffer.empty? ? nil : flush if input_finished?
81
83
 
82
84
  @output_buffer << produce_input
83
- over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes)
85
+ over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes
84
86
  end
85
- sep_index = [match_index + a_sep_string.bytesize, number_of_bytes || @output_buffer.bytesize].min
87
+ sep_index = [
88
+ match_index + a_sep_string.bytesize,
89
+ number_of_bytes || @output_buffer.bytesize
90
+ ].min
86
91
  @pos += sep_index
87
92
  @output_buffer.slice!(0...sep_index)
88
93
  end
@@ -93,7 +98,7 @@ module Zip
93
98
 
94
99
  def flush
95
100
  ret_val = @output_buffer
96
- @output_buffer = ''
101
+ @output_buffer = +''
97
102
  ret_val
98
103
  end
99
104
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module IOExtras
4
+ module IOExtras # :nodoc:
3
5
  # Implements many of the output convenience methods of IO.
4
6
  # relies on <<
5
- module AbstractOutputStream
7
+ module AbstractOutputStream # :nodoc:
6
8
  include FakeIO
7
9
 
8
10
  def write(data)
@@ -11,7 +13,7 @@ module Zip
11
13
  end
12
14
 
13
15
  def print(*params)
14
- self << params.join($OUTPUT_FIELD_SEPARATOR) << $OUTPUT_RECORD_SEPARATOR.to_s
16
+ self << params.join << $OUTPUT_RECORD_SEPARATOR.to_s
15
17
  end
16
18
 
17
19
  def printf(a_format_string, *params)
data/lib/zip/ioextras.rb CHANGED
@@ -1,26 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module IOExtras #:nodoc:
4
+ module IOExtras # :nodoc:
3
5
  CHUNK_SIZE = 131_072
4
6
 
5
- RANGE_ALL = 0..-1
6
-
7
7
  class << self
8
8
  def copy_stream(ostream, istream)
9
- ostream.write(istream.read(CHUNK_SIZE, '')) until istream.eof?
9
+ ostream.write(istream.read(CHUNK_SIZE, +'')) until istream.eof?
10
10
  end
11
11
 
12
12
  def copy_stream_n(ostream, istream, nbytes)
13
13
  toread = nbytes
14
14
  while toread > 0 && !istream.eof?
15
- tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread
16
- ostream.write(istream.read(tr, ''))
15
+ tr = [toread, CHUNK_SIZE].min
16
+ ostream.write(istream.read(tr, +''))
17
17
  toread -= tr
18
18
  end
19
19
  end
20
20
  end
21
21
 
22
22
  # Implements kind_of? in order to pretend to be an IO object
23
- module FakeIO
23
+ module FakeIO # :nodoc:
24
24
  def kind_of?(object)
25
25
  object == IO || super
26
26
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class NullCompressor < Compressor #:nodoc:all
4
+ class NullCompressor < Compressor # :nodoc:all
3
5
  include Singleton
4
6
 
5
7
  def <<(_data)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullDecompressor #:nodoc:all
4
+ module NullDecompressor # :nodoc:all
3
5
  module_function
4
6
 
5
7
  def read(_length = nil, _outbuf = nil)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullInputStream #:nodoc:all
4
+ module NullInputStream # :nodoc:all
3
5
  include ::Zip::NullDecompressor
4
6
  include ::Zip::IOExtras::AbstractInputStream
5
7
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ ##
1
6
  module Zip
2
7
  # ZipOutputStream is the basic class for writing zip files. It is
3
8
  # possible to create a ZipOutputStream object directly, passing
@@ -16,50 +21,49 @@ module Zip
16
21
  #
17
22
  # java.util.zip.ZipOutputStream is the original inspiration for this
18
23
  # class.
19
-
20
24
  class OutputStream
25
+ extend Forwardable
21
26
  include ::Zip::IOExtras::AbstractOutputStream
22
27
 
23
- attr_accessor :comment
28
+ def_delegators :@cdir, :comment, :comment=
24
29
 
25
30
  # Opens the indicated zip file. If a file with that name already
26
31
  # exists it will be overwritten.
27
- def initialize(file_name, stream = false, encrypter = nil)
32
+ def initialize(file_name, stream: false, encrypter: nil)
28
33
  super()
29
34
  @file_name = file_name
30
35
  @output_stream = if stream
31
- iostream = @file_name.dup
36
+ iostream = Zip::RUNNING_ON_WINDOWS ? @file_name : @file_name.dup
32
37
  iostream.reopen(@file_name)
33
38
  iostream.rewind
34
39
  iostream
35
40
  else
36
41
  ::File.new(@file_name, 'wb')
37
42
  end
38
- @entry_set = ::Zip::EntrySet.new
43
+ @cdir = ::Zip::CentralDirectory.new
39
44
  @compressor = ::Zip::NullCompressor.instance
40
45
  @encrypter = encrypter || ::Zip::NullEncrypter.new
41
46
  @closed = false
42
47
  @current_entry = nil
43
- @comment = nil
44
48
  end
45
49
 
46
- # Same as #initialize but if a block is passed the opened
47
- # stream is passed to the block and closed when the block
48
- # returns.
49
50
  class << self
50
- def open(file_name, encrypter = nil)
51
+ # Same as #initialize but if a block is passed the opened
52
+ # stream is passed to the block and closed when the block
53
+ # returns.
54
+ def open(file_name, encrypter: nil)
51
55
  return new(file_name) unless block_given?
52
56
 
53
- zos = new(file_name, false, encrypter)
57
+ zos = new(file_name, stream: false, encrypter: encrypter)
54
58
  yield zos
55
59
  ensure
56
60
  zos.close if zos
57
61
  end
58
62
 
59
63
  # Same as #open but writes to a filestream instead
60
- def write_buffer(io = ::StringIO.new(''), encrypter = nil)
64
+ def write_buffer(io = ::StringIO.new, encrypter: nil)
61
65
  io.binmode if io.respond_to?(:binmode)
62
- zos = new(io, true, encrypter)
66
+ zos = new(io, stream: true, encrypter: encrypter)
63
67
  yield zos
64
68
  zos.close_buffer
65
69
  end
@@ -71,7 +75,7 @@ module Zip
71
75
 
72
76
  finalize_current_entry
73
77
  update_local_headers
74
- write_central_directory
78
+ @cdir.write_to_stream(@output_stream)
75
79
  @output_stream.close
76
80
  @closed = true
77
81
  end
@@ -82,37 +86,41 @@ module Zip
82
86
 
83
87
  finalize_current_entry
84
88
  update_local_headers
85
- write_central_directory
89
+ @cdir.write_to_stream(@output_stream)
86
90
  @closed = true
91
+ @output_stream.flush
87
92
  @output_stream
88
93
  end
89
94
 
90
95
  # Closes the current entry and opens a new for writing.
91
96
  # +entry+ can be a ZipEntry object or a string.
92
- def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression)
97
+ def put_next_entry(
98
+ entry_name, comment = '', extra = ExtraField.new,
99
+ compression_method = Entry::DEFLATED, level = Zip.default_compression
100
+ )
93
101
  raise Error, 'zip stream is closed' if @closed
94
102
 
95
- new_entry = if entry_name.kind_of?(Entry)
96
- entry_name
97
- else
98
- Entry.new(@file_name, entry_name.to_s)
99
- end
100
- new_entry.comment = comment unless comment.nil?
101
- unless extra.nil?
102
- new_entry.extra = extra.kind_of?(ExtraField) ? extra : ExtraField.new(extra.to_s)
103
- end
104
- new_entry.compression_method = compression_method unless compression_method.nil?
105
- init_next_entry(new_entry, level)
103
+ new_entry =
104
+ if entry_name.kind_of?(Entry) || entry_name.kind_of?(StreamableStream)
105
+ entry_name
106
+ else
107
+ Entry.new(
108
+ @file_name, entry_name.to_s, comment: comment, extra: extra,
109
+ compression_method: compression_method, compression_level: level
110
+ )
111
+ end
112
+
113
+ init_next_entry(new_entry)
106
114
  @current_entry = new_entry
107
115
  end
108
116
 
109
- def copy_raw_entry(entry)
117
+ def copy_raw_entry(entry) # :nodoc:
110
118
  entry = entry.dup
111
119
  raise Error, 'zip stream is closed' if @closed
112
120
  raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry)
113
121
 
114
122
  finalize_current_entry
115
- @entry_set << entry
123
+ @cdir << entry
116
124
  src_pos = entry.local_header_offset
117
125
  entry.write_local_entry(@output_stream)
118
126
  @compressor = NullCompressor.instance
@@ -131,55 +139,53 @@ module Zip
131
139
  return unless @current_entry
132
140
 
133
141
  finish
134
- @current_entry.compressed_size = @output_stream.tell - \
135
- @current_entry.local_header_offset - \
142
+ @current_entry.compressed_size = @output_stream.tell -
143
+ @current_entry.local_header_offset -
136
144
  @current_entry.calculate_local_header_size
137
145
  @current_entry.size = @compressor.size
138
146
  @current_entry.crc = @compressor.crc
139
- @output_stream << @encrypter.data_descriptor(@current_entry.crc, @current_entry.compressed_size, @current_entry.size)
147
+ @output_stream << @encrypter.data_descriptor(
148
+ @current_entry.crc,
149
+ @current_entry.compressed_size,
150
+ @current_entry.size
151
+ )
140
152
  @current_entry.gp_flags |= @encrypter.gp_flags
141
153
  @current_entry = nil
142
154
  @compressor = ::Zip::NullCompressor.instance
143
155
  end
144
156
 
145
- def init_next_entry(entry, level = Zip.default_compression)
157
+ def init_next_entry(entry)
146
158
  finalize_current_entry
147
- @entry_set << entry
159
+ @cdir << entry
148
160
  entry.write_local_entry(@output_stream)
149
161
  @encrypter.reset!
150
162
  @output_stream << @encrypter.header(entry.mtime)
151
- @compressor = get_compressor(entry, level)
163
+ @compressor = get_compressor(entry)
152
164
  end
153
165
 
154
- def get_compressor(entry, level)
166
+ def get_compressor(entry)
155
167
  case entry.compression_method
156
168
  when Entry::DEFLATED
157
- ::Zip::Deflater.new(@output_stream, level, @encrypter)
169
+ ::Zip::Deflater.new(@output_stream, entry.compression_level, @encrypter)
158
170
  when Entry::STORED
159
171
  ::Zip::PassThruCompressor.new(@output_stream)
160
172
  else
161
- raise ::Zip::CompressionMethodError,
162
- "Invalid compression method: '#{entry.compression_method}'"
173
+ raise ::Zip::CompressionMethodError, entry.compression_method
163
174
  end
164
175
  end
165
176
 
166
177
  def update_local_headers
167
178
  pos = @output_stream.pos
168
- @entry_set.each do |entry|
179
+ @cdir.each do |entry|
169
180
  @output_stream.pos = entry.local_header_offset
170
- entry.write_local_entry(@output_stream, true)
181
+ entry.write_local_entry(@output_stream, rewrite: true)
171
182
  end
172
183
  @output_stream.pos = pos
173
184
  end
174
185
 
175
- def write_central_directory
176
- cdir = CentralDirectory.new(@entry_set, @comment)
177
- cdir.write_to_stream(@output_stream)
178
- end
179
-
180
186
  protected
181
187
 
182
- def finish
188
+ def finish # :nodoc:
183
189
  @compressor.finish
184
190
  end
185
191
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class PassThruCompressor < Compressor #:nodoc:all
4
+ class PassThruCompressor < Compressor # :nodoc:all
3
5
  def initialize(output_stream)
4
6
  super()
5
7
  @output_stream = output_stream
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class PassThruDecompressor < Decompressor #:nodoc:all
4
+ class PassThruDecompressor < Decompressor # :nodoc:all
3
5
  def initialize(*args)
4
6
  super
5
7
  @read_so_far = 0
6
8
  end
7
9
 
8
- def read(length = nil, outbuf = '')
10
+ def read(length = nil, outbuf = +'')
9
11
  return (length.nil? || length.zero? ? '' : nil) if eof
10
12
 
11
13
  if length.nil? || (@read_so_far + length) > decompressed_size
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class StreamableDirectory < Entry
4
+ class StreamableDirectory < Entry # :nodoc:
3
5
  def initialize(zipfile, entry, src_path = nil, permission = nil)
4
6
  super(zipfile, entry)
5
7
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
4
  class StreamableStream < DelegateClass(Entry) # :nodoc:all
3
5
  def initialize(entry)
@@ -42,6 +44,7 @@ module Zip
42
44
  end
43
45
 
44
46
  def clean_up
47
+ super
45
48
  @temp_file.unlink
46
49
  end
47
50
  end
data/lib/zip/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- VERSION = '2.3.1'
4
+ VERSION = '3.0.0' # :nodoc:
3
5
  end
data/lib/zip.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'English'
2
4
  require 'delegate'
3
5
  require 'singleton'
@@ -33,26 +35,12 @@ require 'zip/streamable_stream'
33
35
  require 'zip/streamable_directory'
34
36
  require 'zip/errors'
35
37
 
38
+ # Rubyzip is a ruby module for reading and writing zip files.
39
+ #
40
+ # The main entry points are File, InputStream and OutputStream. For a
41
+ # file/directory interface in the style of the standard ruby ::File and
42
+ # ::Dir APIs then `require 'zip/filesystem'` and see FileSystem.
36
43
  module Zip
37
- BANNER = [
38
- 'RubyZip 3.0 is coming!',
39
- '**********************',
40
- '',
41
- 'The public API of some Rubyzip classes has been modernized to use named',
42
- 'parameters for optional arguments. Please check your usage of the',
43
- 'following classes:',
44
- ' * `Zip::File`',
45
- ' * `Zip::Entry`',
46
- ' * `Zip::InputStream`',
47
- ' * `Zip::OutputStream`',
48
- '',
49
- 'Please ensure that your Gemfiles and .gemspecs are suitably restrictive',
50
- 'to avoid an unexpected breakage when 3.0 is released (e.g. ~> 2.3.0).',
51
- 'See https://github.com/rubyzip/rubyzip for details. The Changelog also',
52
- 'lists other enhancements and bugfixes that have been implemented since',
53
- 'version 2.3.0.'
54
- ].freeze
55
-
56
44
  extend self
57
45
  attr_accessor :unicode_names,
58
46
  :on_exists_proc,
@@ -65,21 +53,27 @@ module Zip
65
53
  :force_entry_names_encoding,
66
54
  :validate_entry_sizes
67
55
 
68
- def reset!
69
- warn BANNER.join("\n") unless BANNER.empty?
56
+ DEFAULT_RESTORE_OPTIONS = {
57
+ restore_ownership: false,
58
+ restore_permissions: true,
59
+ restore_times: true
60
+ }.freeze # :nodoc:
70
61
 
62
+ def reset! # :nodoc:
71
63
  @_ran_once = false
72
64
  @unicode_names = false
73
65
  @on_exists_proc = false
74
66
  @continue_on_exists_proc = false
75
67
  @sort_entries = false
76
- @default_compression = ::Zlib::DEFAULT_COMPRESSION
77
- @write_zip64_support = false
68
+ @default_compression = Zlib::DEFAULT_COMPRESSION
69
+ @write_zip64_support = true
78
70
  @warn_invalid_date = true
79
71
  @case_insensitive_match = false
72
+ @force_entry_names_encoding = nil
80
73
  @validate_entry_sizes = true
81
74
  end
82
75
 
76
+ # Set options for RubyZip in one block.
83
77
  def setup
84
78
  yield self unless @_ran_once
85
79
  @_ran_once = true