rubyzip 2.4.1 → 3.5.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.
- checksums.yaml +4 -4
- data/Changelog.md +525 -0
- data/LICENSE.md +24 -0
- data/README.md +222 -43
- data/Rakefile +15 -13
- data/lib/rubyzip.rb +3 -0
- data/lib/zip/central_directory.rb +194 -125
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +29 -21
- data/lib/zip/crypto/aes_encryption.rb +120 -0
- data/lib/zip/crypto/decrypted_io.rb +27 -15
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +5 -13
- data/lib/zip/crypto/traditional_encryption.rb +13 -7
- data/lib/zip/decompressor.rb +4 -3
- data/lib/zip/deflater.rb +12 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +45 -5
- data/lib/zip/entry.rb +396 -269
- data/lib/zip/entry_set.rb +11 -9
- data/lib/zip/errors.rb +152 -16
- data/lib/zip/extra_field/aes.rb +50 -0
- data/lib/zip/extra_field/generic.rb +10 -11
- data/lib/zip/extra_field/ntfs.rb +6 -4
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +3 -1
- data/lib/zip/extra_field/unix.rb +5 -3
- data/lib/zip/extra_field/unknown.rb +35 -0
- data/lib/zip/extra_field/zip64.rb +19 -5
- data/lib/zip/extra_field.rb +25 -23
- data/lib/zip/file.rb +174 -267
- data/lib/zip/file_split.rb +91 -0
- data/lib/zip/filesystem/dir.rb +86 -0
- data/lib/zip/filesystem/directory_iterator.rb +48 -0
- data/lib/zip/filesystem/file.rb +263 -0
- data/lib/zip/filesystem/file_stat.rb +110 -0
- data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
- data/lib/zip/filesystem.rb +27 -596
- data/lib/zip/inflater.rb +13 -10
- data/lib/zip/input_stream.rb +99 -60
- data/lib/zip/ioextras/abstract_input_stream.rb +146 -58
- data/lib/zip/ioextras/abstract_output_stream.rb +13 -3
- data/lib/zip/ioextras.rb +7 -7
- data/lib/zip/null_compressor.rb +3 -1
- data/lib/zip/null_decompressor.rb +6 -3
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +60 -57
- data/lib/zip/pass_thru_compressor.rb +3 -1
- data/lib/zip/pass_thru_decompressor.rb +12 -9
- data/lib/zip/streamable_directory.rb +3 -1
- data/lib/zip/streamable_stream.rb +4 -1
- data/lib/zip/version.rb +4 -1
- data/lib/zip.rb +28 -23
- data/rubyzip.gemspec +39 -0
- data/samples/example.rb +8 -3
- data/samples/example_filesystem.rb +3 -2
- data/samples/example_recursive.rb +3 -1
- data/samples/gtk_ruby_zip.rb +5 -3
- data/samples/qtzip.rb +7 -6
- data/samples/write_simple.rb +2 -1
- data/samples/zipfind.rb +1 -0
- metadata +87 -52
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
data/lib/zip/input_stream.rb
CHANGED
|
@@ -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,34 +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,
|
|
54
|
+
def initialize(context, offset: 0, decrypter: nil)
|
|
53
55
|
super()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
offset = dep_offset if offset.zero?
|
|
60
|
-
@archive_io = get_io(context, offset)
|
|
61
|
-
@decompressor = ::Zip::NullDecompressor
|
|
62
|
-
@decrypter = decrypter || dep_decrypter || ::Zip::NullDecrypter.new
|
|
56
|
+
@archive_io = get_io(context, offset)
|
|
57
|
+
@decompressor = ::Zip::NullDecompressor
|
|
58
|
+
@decrypter = decrypter
|
|
63
59
|
@current_entry = nil
|
|
60
|
+
@complete_entry = nil
|
|
64
61
|
end
|
|
65
62
|
|
|
63
|
+
# Close this InputStream. All further IO will raise an IOError.
|
|
66
64
|
def close
|
|
67
65
|
@archive_io.close
|
|
68
66
|
end
|
|
69
67
|
|
|
70
|
-
# Returns
|
|
71
|
-
# method on a newly created
|
|
72
|
-
# the first entry in the archive.
|
|
73
|
-
# 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.
|
|
74
72
|
def get_next_entry
|
|
75
|
-
@
|
|
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
|
+
|
|
76
79
|
open_entry
|
|
77
80
|
end
|
|
78
81
|
|
|
79
|
-
# Rewinds the stream to the beginning of the current entry
|
|
82
|
+
# Rewinds the stream to the beginning of the current entry.
|
|
80
83
|
def rewind
|
|
81
84
|
return if @current_entry.nil?
|
|
82
85
|
|
|
@@ -86,23 +89,44 @@ module Zip
|
|
|
86
89
|
open_entry
|
|
87
90
|
end
|
|
88
91
|
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
# Modelled after IO#sysread.
|
|
93
|
+
#
|
|
94
|
+
# Reads up to maxlen bytes from the stream; returns a string
|
|
95
|
+
# (either a new string or the given out_string).
|
|
96
|
+
# Its encoding is the unchanged encoding of out_string, if out_string is
|
|
97
|
+
# given; ASCII-8BIT, otherwise. Output contains maxlen bytes from the
|
|
98
|
+
# stream, if available; otherwise contains all available bytes, if any
|
|
99
|
+
# available; otherwise is an empty string.
|
|
100
|
+
#
|
|
101
|
+
# This method should not be used with buffered input stream-reader methods,
|
|
102
|
+
# such as #read, #readline, #gets.
|
|
103
|
+
def sysread(maxlen = nil, out_string = nil)
|
|
104
|
+
# Remove the default value for maxlen for version 4.
|
|
105
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
106
|
+
|
|
107
|
+
output = produce_input(maxlen)
|
|
108
|
+
|
|
109
|
+
if out_string.nil?
|
|
110
|
+
output.force_encoding(Encoding::ASCII_8BIT)
|
|
111
|
+
else
|
|
112
|
+
encoding = out_string.encoding
|
|
113
|
+
out_string.replace(output).force_encoding(encoding)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns the size of the current entry, or `nil` if there isn't one.
|
|
118
|
+
def size
|
|
119
|
+
return if @current_entry.nil?
|
|
120
|
+
|
|
121
|
+
@current_entry.size
|
|
92
122
|
end
|
|
93
123
|
|
|
94
124
|
class << self
|
|
95
125
|
# Same as #initialize but if a block is passed the opened
|
|
96
126
|
# stream is passed to the block and closed when the block
|
|
97
127
|
# returns.
|
|
98
|
-
def open(filename_or_io,
|
|
99
|
-
|
|
100
|
-
Zip.warn_about_v3_api('Zip::InputStream.new')
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
offset = dep_offset if offset.zero?
|
|
104
|
-
|
|
105
|
-
zio = new(filename_or_io, offset: offset, decrypter: decrypter || dep_decrypter)
|
|
128
|
+
def open(filename_or_io, offset: 0, decrypter: nil)
|
|
129
|
+
zio = new(filename_or_io, offset: offset, decrypter: decrypter)
|
|
106
130
|
return zio unless block_given?
|
|
107
131
|
|
|
108
132
|
begin
|
|
@@ -111,17 +135,11 @@ module Zip
|
|
|
111
135
|
zio.close if zio
|
|
112
136
|
end
|
|
113
137
|
end
|
|
114
|
-
|
|
115
|
-
def open_buffer(filename_or_io, offset = 0)
|
|
116
|
-
Zip.warn_about_v3_api('Zip::InputStream.open_buffer')
|
|
117
|
-
|
|
118
|
-
::Zip::InputStream.open(filename_or_io, offset)
|
|
119
|
-
end
|
|
120
138
|
end
|
|
121
139
|
|
|
122
140
|
protected
|
|
123
141
|
|
|
124
|
-
def get_io(io_or_file, offset = 0)
|
|
142
|
+
def get_io(io_or_file, offset = 0) # :nodoc:
|
|
125
143
|
if io_or_file.respond_to?(:seek)
|
|
126
144
|
io = io_or_file.dup
|
|
127
145
|
io.seek(offset, ::IO::SEEK_SET)
|
|
@@ -133,57 +151,78 @@ module Zip
|
|
|
133
151
|
end
|
|
134
152
|
end
|
|
135
153
|
|
|
136
|
-
def open_entry
|
|
154
|
+
def open_entry # :nodoc:
|
|
137
155
|
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
|
138
|
-
if @current_entry
|
|
139
|
-
raise Error, 'password required to decode zip file'
|
|
140
|
-
end
|
|
156
|
+
return if @current_entry.nil?
|
|
141
157
|
|
|
142
|
-
if @current_entry
|
|
143
|
-
|
|
144
|
-
&& @current_entry.size == 0 && !@complete_entry
|
|
145
|
-
raise GPFBit3Error,
|
|
146
|
-
'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \
|
|
147
|
-
'Please use ::Zip::File instead of ::Zip::InputStream'
|
|
158
|
+
if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
|
|
159
|
+
raise StreamingError, @current_entry
|
|
148
160
|
end
|
|
149
|
-
|
|
150
|
-
@decompressor =
|
|
161
|
+
|
|
162
|
+
@decompressor = assemble_io
|
|
151
163
|
flush
|
|
152
164
|
@current_entry
|
|
153
165
|
end
|
|
154
166
|
|
|
155
|
-
def
|
|
167
|
+
def assemble_io # :nodoc:
|
|
168
|
+
io = if @current_entry.encrypted?
|
|
169
|
+
raise Error, 'A password is required to decode this zip file.' if @decrypter.nil?
|
|
170
|
+
|
|
171
|
+
get_decrypted_io
|
|
172
|
+
else
|
|
173
|
+
@archive_io
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
get_decompressor(io)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def get_decrypted_io # :nodoc:
|
|
156
180
|
header = @archive_io.read(@decrypter.header_bytesize)
|
|
157
181
|
@decrypter.reset!(header)
|
|
158
182
|
|
|
159
|
-
|
|
183
|
+
compressed_size =
|
|
184
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
185
|
+
@current_entry.compressed_size == 0 && @complete_entry
|
|
186
|
+
@complete_entry.compressed_size
|
|
187
|
+
else
|
|
188
|
+
@current_entry.compressed_size
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
if @decrypter.kind_of?(::Zip::AESDecrypter)
|
|
192
|
+
compressed_size -= @decrypter.header_bytesize
|
|
193
|
+
compressed_size -= ::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
|
|
160
197
|
end
|
|
161
198
|
|
|
162
|
-
def get_decompressor
|
|
199
|
+
def get_decompressor(io) # :nodoc:
|
|
163
200
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
164
201
|
|
|
165
202
|
decompressed_size =
|
|
166
|
-
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
203
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
204
|
+
@current_entry.size == 0 && @complete_entry
|
|
167
205
|
@complete_entry.size
|
|
168
206
|
else
|
|
169
207
|
@current_entry.size
|
|
170
208
|
end
|
|
171
209
|
|
|
172
|
-
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
210
|
+
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
211
|
+
@current_entry.compression_method
|
|
212
|
+
)
|
|
173
213
|
if decompressor_class.nil?
|
|
174
|
-
raise ::Zip::CompressionMethodError,
|
|
175
|
-
"Unsupported compression method #{@current_entry.compression_method}"
|
|
214
|
+
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
176
215
|
end
|
|
177
216
|
|
|
178
|
-
decompressor_class.new(
|
|
217
|
+
decompressor_class.new(io, decompressed_size)
|
|
179
218
|
end
|
|
180
219
|
|
|
181
|
-
def produce_input
|
|
182
|
-
@decompressor.read(
|
|
220
|
+
def produce_input(maxlen = CHUNK_SIZE) # :nodoc:
|
|
221
|
+
@decompressor.read(maxlen)
|
|
183
222
|
end
|
|
184
223
|
|
|
185
|
-
def input_finished?
|
|
186
|
-
@decompressor.eof
|
|
224
|
+
def input_finished? # :nodoc:
|
|
225
|
+
@decompressor.eof?
|
|
187
226
|
end
|
|
188
227
|
end
|
|
189
228
|
end
|
|
@@ -1,122 +1,210 @@
|
|
|
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
|
-
# such as gets, getc, readline and readlines
|
|
6
|
+
# such as gets, getc, read, readline and readlines
|
|
5
7
|
# depends on: input_finished?, produce_input and read
|
|
6
8
|
module AbstractInputStream
|
|
7
9
|
include Enumerable
|
|
8
10
|
include FakeIO
|
|
9
11
|
|
|
10
|
-
def initialize
|
|
12
|
+
def initialize # :nodoc:
|
|
11
13
|
super
|
|
12
14
|
@lineno = 0
|
|
13
15
|
@pos = 0
|
|
14
|
-
@output_buffer = ''.b
|
|
16
|
+
@output_buffer = +''.b
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Returns (or sets) the current line number in the decompressed
|
|
20
|
+
# (possibly decrypted) data stream. See the Line Number documentation
|
|
21
|
+
# for the IO class for more information.
|
|
17
22
|
attr_accessor :lineno
|
|
23
|
+
|
|
24
|
+
# Returns the current position (in bytes) in the decompressed (possibly
|
|
25
|
+
# decrypted) data stream.
|
|
18
26
|
attr_reader :pos
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
# Reads bytes from the stream decompressed (possibly decrypted) data
|
|
29
|
+
# stream. If `maxlen` is `nil`, reads all bytes; otherwise, reads up to
|
|
30
|
+
# `maxlen` bytes. If `maxlen` is zero, returns an empty string.
|
|
31
|
+
#
|
|
32
|
+
# Returns a string (either a new string or the given `out_string`)
|
|
33
|
+
# containing the bytes read. The string's encoding is the unchanged
|
|
34
|
+
# encoding of `out_string`, if `out_string` is given; `ASCII-8BIT`,
|
|
35
|
+
# otherwise.
|
|
36
|
+
def read(maxlen = nil, out_string = nil) # rubocop:disable Metrics/PerceivedComplexity
|
|
37
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
38
|
+
|
|
21
39
|
tbuf = if @output_buffer.bytesize > 0
|
|
22
|
-
if
|
|
23
|
-
@output_buffer.slice!(0,
|
|
40
|
+
if maxlen && maxlen <= @output_buffer.bytesize
|
|
41
|
+
@output_buffer.slice!(0, maxlen)
|
|
24
42
|
else
|
|
25
|
-
|
|
26
|
-
rbuf =
|
|
43
|
+
maxlen -= @output_buffer.bytesize if maxlen
|
|
44
|
+
rbuf = produce_input(maxlen)
|
|
27
45
|
out = @output_buffer
|
|
28
46
|
out << rbuf if rbuf
|
|
29
|
-
@output_buffer = ''.b
|
|
47
|
+
@output_buffer = +''.b
|
|
30
48
|
out
|
|
31
49
|
end
|
|
32
50
|
else
|
|
33
|
-
|
|
51
|
+
produce_input(maxlen)
|
|
34
52
|
end
|
|
35
53
|
|
|
36
54
|
if tbuf.nil? || tbuf.empty?
|
|
37
|
-
return nil if
|
|
55
|
+
return nil if maxlen&.positive?
|
|
38
56
|
|
|
39
57
|
return ''
|
|
40
58
|
end
|
|
41
59
|
|
|
42
60
|
@pos += tbuf.length
|
|
43
61
|
|
|
44
|
-
if
|
|
45
|
-
|
|
62
|
+
if out_string.nil?
|
|
63
|
+
tbuf.force_encoding(Encoding::ASCII_8BIT)
|
|
46
64
|
else
|
|
47
|
-
|
|
65
|
+
encoding = out_string.encoding
|
|
66
|
+
out_string.replace(tbuf).force_encoding(encoding)
|
|
48
67
|
end
|
|
49
|
-
buf
|
|
50
68
|
end
|
|
51
69
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
# Reads and returns all remaining lines from the stream. See the Line IO
|
|
71
|
+
# documentation in the IO class for more information.
|
|
72
|
+
#
|
|
73
|
+
# With no arguments given, returns lines as determined by line
|
|
74
|
+
# separator `$/`, or `nil` if none.
|
|
75
|
+
#
|
|
76
|
+
# With only string argument `sep` given, returns lines as
|
|
77
|
+
# determined by line separator `sep`, or `nil` if none. See the
|
|
78
|
+
# Line Separator documentation in the IO class for more information.
|
|
79
|
+
# The two special values for `sep` (`nil` and `""`) are honoured.
|
|
80
|
+
#
|
|
81
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
82
|
+
# in each line; see the Line Limit documentation in the IO class for more
|
|
83
|
+
# information.
|
|
84
|
+
#
|
|
85
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
86
|
+
#
|
|
87
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
88
|
+
# are to be omitted.
|
|
89
|
+
def readlines(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
90
|
+
each(sep, limit, chomp: chomp).to_a
|
|
56
91
|
end
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
# Reads and returns a line from the stream. See the Line IO
|
|
94
|
+
# documentation in the IO class for more information.
|
|
95
|
+
#
|
|
96
|
+
# With no arguments given, returns the next line as determined by line
|
|
97
|
+
# separator `$/`, or `nil` if none.
|
|
98
|
+
#
|
|
99
|
+
# With only string argument `sep` given, returns the next line as
|
|
100
|
+
# determined by line separator `sep`, or `nil` if none. See the
|
|
101
|
+
# Line Separator documentation in the IO class for more information.
|
|
102
|
+
# The two special values for `sep` (`nil` and `""`) are honoured.
|
|
103
|
+
#
|
|
104
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
105
|
+
# in the line; see the Line Limit documentation in the IO class for more
|
|
106
|
+
# information.
|
|
107
|
+
#
|
|
108
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
109
|
+
#
|
|
110
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
111
|
+
# are to be omitted.
|
|
112
|
+
def gets(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
113
|
+
if sep.nil?
|
|
114
|
+
return nil if eof?
|
|
115
|
+
|
|
116
|
+
@lineno = @lineno.next
|
|
117
|
+
return read(limit)
|
|
118
|
+
end
|
|
60
119
|
|
|
61
|
-
if
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
elsif
|
|
65
|
-
|
|
66
|
-
a_sep_string = $INPUT_RECORD_SEPARATOR
|
|
67
|
-
else
|
|
68
|
-
number_of_bytes = nil
|
|
69
|
-
a_sep_string = a_sep_string.to_str if a_sep_string
|
|
120
|
+
if sep.respond_to?(:to_int)
|
|
121
|
+
limit = sep.to_int
|
|
122
|
+
sep = $INPUT_RECORD_SEPARATOR
|
|
123
|
+
elsif sep&.empty?
|
|
124
|
+
sep = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}"
|
|
70
125
|
end
|
|
71
126
|
|
|
72
|
-
|
|
127
|
+
buffer_index = 0
|
|
128
|
+
while (sep_index = @output_buffer.index(sep, buffer_index)).nil?
|
|
129
|
+
break if limit && @output_buffer.bytesize >= limit
|
|
73
130
|
|
|
74
|
-
|
|
131
|
+
if input_finished?
|
|
132
|
+
return nil if @output_buffer.empty?
|
|
75
133
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return @output_buffer.empty? ? nil : flush if input_finished?
|
|
134
|
+
@lineno = @lineno.next
|
|
135
|
+
@pos += @output_buffer.bytesize
|
|
136
|
+
return @output_buffer.slice!(0..)
|
|
137
|
+
end
|
|
81
138
|
|
|
139
|
+
buffer_index = [buffer_index, @output_buffer.bytesize - sep.bytesize].max
|
|
82
140
|
@output_buffer << produce_input
|
|
83
|
-
over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes)
|
|
84
141
|
end
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
142
|
+
|
|
143
|
+
limit ||= @output_buffer.bytesize
|
|
144
|
+
cut_index = sep_index ? [sep_index + sep.bytesize, limit].min : limit
|
|
145
|
+
@lineno = @lineno.next
|
|
146
|
+
@pos += cut_index
|
|
147
|
+
chomp ? @output_buffer.slice!(0, cut_index).chomp(sep) : @output_buffer.slice!(0, cut_index)
|
|
88
148
|
end
|
|
89
149
|
|
|
90
|
-
def ungetc(byte)
|
|
150
|
+
def ungetc(byte) # :nodoc:
|
|
91
151
|
@output_buffer = byte.chr + @output_buffer
|
|
92
152
|
end
|
|
93
153
|
|
|
94
|
-
def flush
|
|
95
|
-
|
|
96
|
-
@output_buffer = ''.b
|
|
97
|
-
ret_val
|
|
154
|
+
def flush # :nodoc:
|
|
155
|
+
@output_buffer.slice!(0..)
|
|
98
156
|
end
|
|
99
157
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
158
|
+
# Reads a line as with #gets, but raises `EOFError` if already at
|
|
159
|
+
# end-of-stream.
|
|
160
|
+
#
|
|
161
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
162
|
+
# are to be omitted.
|
|
163
|
+
def readline(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
164
|
+
raise EOFError if eof?
|
|
103
165
|
|
|
104
|
-
|
|
166
|
+
gets(sep, limit, chomp: chomp)
|
|
105
167
|
end
|
|
106
168
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
# Calls the block with each remaining line read from the stream.
|
|
170
|
+
# Does nothing if already at end-of-stream. See the Line IO
|
|
171
|
+
# documentation in the IO class for more information.
|
|
172
|
+
#
|
|
173
|
+
# With no arguments given, reads lines as determined by line separator
|
|
174
|
+
# `$/`. With only string argument `sep` given, reads lines as determined
|
|
175
|
+
# by line separator `sep`. See the Line Separator documentation in the
|
|
176
|
+
# IO class for more information. The two special values for `sep`
|
|
177
|
+
# (`nil` and `""`) are honoured.
|
|
178
|
+
#
|
|
179
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
180
|
+
# in each line; see the Line Limit documentation in the IO class for
|
|
181
|
+
# more information.
|
|
182
|
+
#
|
|
183
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
184
|
+
#
|
|
185
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
186
|
+
# are to be omitted.
|
|
187
|
+
#
|
|
188
|
+
# Returns an `Enumerator` if no block is given.
|
|
189
|
+
def each(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
190
|
+
return to_enum(:each, sep, limit, chomp: chomp) unless block_given?
|
|
191
|
+
|
|
192
|
+
while (line = gets(sep, limit, chomp: chomp))
|
|
193
|
+
yield line
|
|
194
|
+
end
|
|
111
195
|
end
|
|
112
196
|
|
|
113
|
-
alias each
|
|
197
|
+
alias each_line each
|
|
114
198
|
|
|
115
|
-
|
|
199
|
+
# Returns `true` if the stream is positioned at its end, `false`
|
|
200
|
+
# otherwise. See Position documentation in the IO class for more
|
|
201
|
+
# information.
|
|
202
|
+
def eof?
|
|
116
203
|
@output_buffer.empty? && input_finished?
|
|
117
204
|
end
|
|
118
205
|
|
|
119
|
-
|
|
206
|
+
# Alias for compatibility. Remove for version 4.
|
|
207
|
+
alias eof eof? # :nodoc:
|
|
120
208
|
end
|
|
121
209
|
end
|
|
122
210
|
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
|
|
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
|
|
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, ''
|
|
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
|
|
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
|
data/lib/zip/null_compressor.rb
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
module NullDecompressor
|
|
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
|
-
|
|
15
|
+
# Alias for compatibility. Remove for version 4.
|
|
16
|
+
alias eof eof?
|
|
14
17
|
end
|
|
15
18
|
end
|
|
16
19
|
|