rubyzip 2.3.2 → 3.2.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +485 -0
  3. data/LICENSE.md +24 -0
  4. data/README.md +192 -44
  5. data/Rakefile +15 -13
  6. data/lib/zip/central_directory.rb +179 -125
  7. data/lib/zip/compressor.rb +3 -1
  8. data/lib/zip/constants.rb +29 -21
  9. data/lib/zip/crypto/aes_encryption.rb +120 -0
  10. data/lib/zip/crypto/decrypted_io.rb +20 -14
  11. data/lib/zip/crypto/encryption.rb +4 -2
  12. data/lib/zip/crypto/null_encryption.rb +5 -13
  13. data/lib/zip/crypto/traditional_encryption.rb +10 -6
  14. data/lib/zip/decompressor.rb +4 -3
  15. data/lib/zip/deflater.rb +12 -8
  16. data/lib/zip/dirtyable.rb +32 -0
  17. data/lib/zip/dos_time.rb +53 -6
  18. data/lib/zip/entry.rb +404 -238
  19. data/lib/zip/entry_set.rb +11 -9
  20. data/lib/zip/errors.rb +136 -16
  21. data/lib/zip/extra_field/aes.rb +50 -0
  22. data/lib/zip/extra_field/generic.rb +10 -11
  23. data/lib/zip/extra_field/ntfs.rb +6 -4
  24. data/lib/zip/extra_field/old_unix.rb +3 -1
  25. data/lib/zip/extra_field/universal_time.rb +3 -1
  26. data/lib/zip/extra_field/unix.rb +5 -3
  27. data/lib/zip/extra_field/unknown.rb +35 -0
  28. data/lib/zip/extra_field/zip64.rb +19 -5
  29. data/lib/zip/extra_field.rb +25 -23
  30. data/lib/zip/file.rb +185 -226
  31. data/lib/zip/file_split.rb +91 -0
  32. data/lib/zip/filesystem/dir.rb +86 -0
  33. data/lib/zip/filesystem/directory_iterator.rb +48 -0
  34. data/lib/zip/filesystem/file.rb +263 -0
  35. data/lib/zip/filesystem/file_stat.rb +110 -0
  36. data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
  37. data/lib/zip/filesystem.rb +27 -596
  38. data/lib/zip/inflater.rb +10 -7
  39. data/lib/zip/input_stream.rb +76 -44
  40. data/lib/zip/ioextras/abstract_input_stream.rb +18 -12
  41. data/lib/zip/ioextras/abstract_output_stream.rb +13 -3
  42. data/lib/zip/ioextras.rb +7 -7
  43. data/lib/zip/null_compressor.rb +3 -1
  44. data/lib/zip/null_decompressor.rb +6 -3
  45. data/lib/zip/null_input_stream.rb +3 -1
  46. data/lib/zip/output_stream.rb +58 -48
  47. data/lib/zip/pass_thru_compressor.rb +3 -1
  48. data/lib/zip/pass_thru_decompressor.rb +8 -5
  49. data/lib/zip/streamable_directory.rb +3 -1
  50. data/lib/zip/streamable_stream.rb +4 -1
  51. data/lib/zip/version.rb +4 -1
  52. data/lib/zip.rb +25 -3
  53. data/rubyzip.gemspec +39 -0
  54. data/samples/example.rb +8 -3
  55. data/samples/example_filesystem.rb +3 -2
  56. data/samples/example_recursive.rb +3 -1
  57. data/samples/gtk_ruby_zip.rb +5 -3
  58. data/samples/qtzip.rb +7 -6
  59. data/samples/write_simple.rb +2 -1
  60. data/samples/zipfind.rb +1 -0
  61. metadata +81 -49
  62. data/TODO +0 -15
  63. 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
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
 
@@ -81,16 +90,23 @@ module Zip
81
90
  end
82
91
 
83
92
  # Modeled after IO.sysread
84
- def sysread(length = nil, outbuf = '')
93
+ def sysread(length = nil, outbuf = +'')
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,57 +131,78 @@ 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'
127
- end
136
+ return if @current_entry.nil?
128
137
 
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'
138
+ if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
139
+ raise StreamingError, @current_entry
135
140
  end
136
- @decrypted_io = get_decrypted_io
137
- @decompressor = get_decompressor
141
+
142
+ @decompressor = assemble_io
138
143
  flush
139
144
  @current_entry
140
145
  end
141
146
 
142
- def get_decrypted_io
147
+ def assemble_io # :nodoc:
148
+ io = if @current_entry.encrypted?
149
+ raise Error, 'A password is required to decode this zip file.' if @decrypter.nil?
150
+
151
+ get_decrypted_io
152
+ else
153
+ @archive_io
154
+ end
155
+
156
+ get_decompressor(io)
157
+ end
158
+
159
+ def get_decrypted_io # :nodoc:
143
160
  header = @archive_io.read(@decrypter.header_bytesize)
144
161
  @decrypter.reset!(header)
145
162
 
146
- ::Zip::DecryptedIo.new(@archive_io, @decrypter)
163
+ compressed_size =
164
+ if @current_entry.incomplete? && @current_entry.crc == 0 &&
165
+ @current_entry.compressed_size == 0 && @complete_entry
166
+ @complete_entry.compressed_size
167
+ else
168
+ @current_entry.compressed_size
169
+ end
170
+
171
+ if @decrypter.kind_of?(::Zip::AESDecrypter)
172
+ compressed_size -= @decrypter.header_bytesize
173
+ compressed_size -= ::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH
174
+ end
175
+
176
+ ::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
147
177
  end
148
178
 
149
- def get_decompressor
179
+ def get_decompressor(io) # :nodoc:
150
180
  return ::Zip::NullDecompressor if @current_entry.nil?
151
181
 
152
182
  decompressed_size =
153
- if @current_entry.incomplete? && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry
183
+ if @current_entry.incomplete? && @current_entry.crc == 0 &&
184
+ @current_entry.size == 0 && @complete_entry
154
185
  @complete_entry.size
155
186
  else
156
187
  @current_entry.size
157
188
  end
158
189
 
159
- decompressor_class = ::Zip::Decompressor.find_by_compression_method(@current_entry.compression_method)
190
+ decompressor_class = ::Zip::Decompressor.find_by_compression_method(
191
+ @current_entry.compression_method
192
+ )
160
193
  if decompressor_class.nil?
161
- raise ::Zip::CompressionMethodError,
162
- "Unsupported compression method #{@current_entry.compression_method}"
194
+ raise ::Zip::CompressionMethodError, @current_entry.compression_method
163
195
  end
164
196
 
165
- decompressor_class.new(@decrypted_io, decompressed_size)
197
+ decompressor_class.new(io, decompressed_size)
166
198
  end
167
199
 
168
- def produce_input
200
+ def produce_input # :nodoc:
169
201
  @decompressor.read(CHUNK_SIZE)
170
202
  end
171
203
 
172
- def input_finished?
173
- @decompressor.eof
204
+ def input_finished? # :nodoc:
205
+ @decompressor.eof?
174
206
  end
175
207
  end
176
208
  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
 
@@ -112,11 +117,12 @@ module Zip
112
117
 
113
118
  alias each each_line
114
119
 
115
- def eof
120
+ def eof?
116
121
  @output_buffer.empty? && input_finished?
117
122
  end
118
123
 
119
- alias eof? eof
124
+ # Alias for compatibility. Remove for version 4.
125
+ alias eof eof?
120
126
  end
121
127
  end
122
128
  end
@@ -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,11 +13,19 @@ 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
17
+
18
+ # Deflate doesn't like `nil`s or empty strings!
19
+ unless $OUTPUT_RECORD_SEPARATOR.nil? || $OUTPUT_RECORD_SEPARATOR.empty?
20
+ self << $OUTPUT_RECORD_SEPARATOR
21
+ end
22
+
23
+ nil
15
24
  end
16
25
 
17
26
  def printf(a_format_string, *params)
18
27
  self << format(a_format_string, *params)
28
+ nil
19
29
  end
20
30
 
21
31
  def putc(an_object)
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,16 +1,19 @@
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)
6
8
  nil
7
9
  end
8
10
 
9
- def eof
11
+ def eof?
10
12
  true
11
13
  end
12
14
 
13
- alias eof? eof
15
+ # Alias for compatibility. Remove for version 4.
16
+ alias eof eof?
14
17
  end
15
18
  end
16
19
 
@@ -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,52 @@ 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, suppress_extra_fields: false)
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
46
+ @suppress_extra_fields = suppress_extra_fields
41
47
  @closed = false
42
48
  @current_entry = nil
43
- @comment = nil
44
49
  end
45
50
 
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
51
  class << self
50
- def open(file_name, encrypter = nil)
52
+ # Same as #initialize but if a block is passed the opened
53
+ # stream is passed to the block and closed when the block
54
+ # returns.
55
+ def open(file_name, encrypter: nil, suppress_extra_fields: false)
51
56
  return new(file_name) unless block_given?
52
57
 
53
- zos = new(file_name, false, encrypter)
58
+ zos = new(file_name, stream: false, encrypter: encrypter,
59
+ suppress_extra_fields: suppress_extra_fields)
54
60
  yield zos
55
61
  ensure
56
62
  zos.close if zos
57
63
  end
58
64
 
59
65
  # Same as #open but writes to a filestream instead
60
- def write_buffer(io = ::StringIO.new(''), encrypter = nil)
66
+ def write_buffer(io = ::StringIO.new, encrypter: nil, suppress_extra_fields: false)
61
67
  io.binmode if io.respond_to?(:binmode)
62
- zos = new(io, true, encrypter)
68
+ zos = new(io, stream: true, encrypter: encrypter,
69
+ suppress_extra_fields: suppress_extra_fields)
63
70
  yield zos
64
71
  zos.close_buffer
65
72
  end
@@ -71,7 +78,7 @@ module Zip
71
78
 
72
79
  finalize_current_entry
73
80
  update_local_headers
74
- write_central_directory
81
+ @cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
75
82
  @output_stream.close
76
83
  @closed = true
77
84
  end
@@ -82,37 +89,41 @@ module Zip
82
89
 
83
90
  finalize_current_entry
84
91
  update_local_headers
85
- write_central_directory
92
+ @cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
86
93
  @closed = true
94
+ @output_stream.flush
87
95
  @output_stream
88
96
  end
89
97
 
90
98
  # Closes the current entry and opens a new for writing.
91
99
  # +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)
100
+ def put_next_entry(
101
+ entry_name, comment = '', extra = ExtraField.new,
102
+ compression_method = Entry::DEFLATED, level = Zip.default_compression
103
+ )
93
104
  raise Error, 'zip stream is closed' if @closed
94
105
 
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)
106
+ new_entry =
107
+ if entry_name.kind_of?(Entry) || entry_name.kind_of?(StreamableStream)
108
+ entry_name
109
+ else
110
+ Entry.new(
111
+ @file_name, entry_name.to_s, comment: comment, extra: extra,
112
+ compression_method: compression_method, compression_level: level
113
+ )
114
+ end
115
+
116
+ init_next_entry(new_entry)
106
117
  @current_entry = new_entry
107
118
  end
108
119
 
109
- def copy_raw_entry(entry)
120
+ def copy_raw_entry(entry) # :nodoc:
110
121
  entry = entry.dup
111
122
  raise Error, 'zip stream is closed' if @closed
112
123
  raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry)
113
124
 
114
125
  finalize_current_entry
115
- @entry_set << entry
126
+ @cdir << entry
116
127
  src_pos = entry.local_header_offset
117
128
  entry.write_local_entry(@output_stream)
118
129
  @compressor = NullCompressor.instance
@@ -131,55 +142,54 @@ module Zip
131
142
  return unless @current_entry
132
143
 
133
144
  finish
134
- @current_entry.compressed_size = @output_stream.tell - \
135
- @current_entry.local_header_offset - \
145
+ @current_entry.compressed_size = @output_stream.tell -
146
+ @current_entry.local_header_offset -
136
147
  @current_entry.calculate_local_header_size
137
148
  @current_entry.size = @compressor.size
138
149
  @current_entry.crc = @compressor.crc
139
- @output_stream << @encrypter.data_descriptor(@current_entry.crc, @current_entry.compressed_size, @current_entry.size)
150
+ @output_stream << @encrypter.data_descriptor(
151
+ @current_entry.crc,
152
+ @current_entry.compressed_size,
153
+ @current_entry.size
154
+ )
140
155
  @current_entry.gp_flags |= @encrypter.gp_flags
141
156
  @current_entry = nil
142
157
  @compressor = ::Zip::NullCompressor.instance
143
158
  end
144
159
 
145
- def init_next_entry(entry, level = Zip.default_compression)
160
+ def init_next_entry(entry)
146
161
  finalize_current_entry
147
- @entry_set << entry
148
- entry.write_local_entry(@output_stream)
162
+ @cdir << entry
163
+ entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields)
149
164
  @encrypter.reset!
150
165
  @output_stream << @encrypter.header(entry.mtime)
151
- @compressor = get_compressor(entry, level)
166
+ @compressor = get_compressor(entry)
152
167
  end
153
168
 
154
- def get_compressor(entry, level)
169
+ def get_compressor(entry)
155
170
  case entry.compression_method
156
171
  when Entry::DEFLATED
157
- ::Zip::Deflater.new(@output_stream, level, @encrypter)
172
+ ::Zip::Deflater.new(@output_stream, entry.compression_level, @encrypter)
158
173
  when Entry::STORED
159
174
  ::Zip::PassThruCompressor.new(@output_stream)
160
175
  else
161
- raise ::Zip::CompressionMethodError,
162
- "Invalid compression method: '#{entry.compression_method}'"
176
+ raise ::Zip::CompressionMethodError, entry.compression_method
163
177
  end
164
178
  end
165
179
 
166
180
  def update_local_headers
167
181
  pos = @output_stream.pos
168
- @entry_set.each do |entry|
182
+ @cdir.each do |entry|
169
183
  @output_stream.pos = entry.local_header_offset
170
- entry.write_local_entry(@output_stream, true)
184
+ entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields,
185
+ rewrite: true)
171
186
  end
172
187
  @output_stream.pos = pos
173
188
  end
174
189
 
175
- def write_central_directory
176
- cdir = CentralDirectory.new(@entry_set, @comment)
177
- cdir.write_to_stream(@output_stream)
178
- end
179
-
180
190
  protected
181
191
 
182
- def finish
192
+ def finish # :nodoc:
183
193
  @compressor.finish
184
194
  end
185
195
 
@@ -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,12 +1,14 @@
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 = '')
9
- return (length.nil? || length.zero? ? '' : nil) if eof
10
+ def read(length = nil, outbuf = +'')
11
+ return (length.nil? || length.zero? ? '' : nil) if eof?
10
12
 
11
13
  if length.nil? || (@read_so_far + length) > decompressed_size
12
14
  length = decompressed_size - @read_so_far
@@ -16,11 +18,12 @@ module Zip
16
18
  input_stream.read(length, outbuf)
17
19
  end
18
20
 
19
- def eof
21
+ def eof?
20
22
  @read_so_far >= decompressed_size
21
23
  end
22
24
 
23
- alias eof? eof
25
+ # Alias for compatibility. Remove for version 4.
26
+ alias eof eof?
24
27
  end
25
28
 
26
29
  ::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_STORE, ::Zip::PassThruDecompressor)
@@ -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