rubyzip 2.4.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +495 -0
- data/LICENSE.md +24 -0
- data/README.md +180 -40
- data/Rakefile +15 -13
- data/lib/zip/central_directory.rb +179 -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 +21 -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 +10 -6
- 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 +392 -266
- data/lib/zip/entry_set.rb +11 -9
- data/lib/zip/errors.rb +136 -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 +98 -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 +25 -22
- 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 +86 -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,43 @@ 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, out_string = nil)
|
|
104
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
105
|
+
|
|
106
|
+
output = produce_input(maxlen)
|
|
107
|
+
|
|
108
|
+
if out_string.nil?
|
|
109
|
+
output.force_encoding(Encoding::ASCII_8BIT)
|
|
110
|
+
else
|
|
111
|
+
encoding = out_string.encoding
|
|
112
|
+
out_string.replace(output).force_encoding(encoding)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Returns the size of the current entry, or `nil` if there isn't one.
|
|
117
|
+
def size
|
|
118
|
+
return if @current_entry.nil?
|
|
119
|
+
|
|
120
|
+
@current_entry.size
|
|
92
121
|
end
|
|
93
122
|
|
|
94
123
|
class << self
|
|
95
124
|
# Same as #initialize but if a block is passed the opened
|
|
96
125
|
# stream is passed to the block and closed when the block
|
|
97
126
|
# 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)
|
|
127
|
+
def open(filename_or_io, offset: 0, decrypter: nil)
|
|
128
|
+
zio = new(filename_or_io, offset: offset, decrypter: decrypter)
|
|
106
129
|
return zio unless block_given?
|
|
107
130
|
|
|
108
131
|
begin
|
|
@@ -111,17 +134,11 @@ module Zip
|
|
|
111
134
|
zio.close if zio
|
|
112
135
|
end
|
|
113
136
|
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
137
|
end
|
|
121
138
|
|
|
122
139
|
protected
|
|
123
140
|
|
|
124
|
-
def get_io(io_or_file, offset = 0)
|
|
141
|
+
def get_io(io_or_file, offset = 0) # :nodoc:
|
|
125
142
|
if io_or_file.respond_to?(:seek)
|
|
126
143
|
io = io_or_file.dup
|
|
127
144
|
io.seek(offset, ::IO::SEEK_SET)
|
|
@@ -133,57 +150,78 @@ module Zip
|
|
|
133
150
|
end
|
|
134
151
|
end
|
|
135
152
|
|
|
136
|
-
def open_entry
|
|
153
|
+
def open_entry # :nodoc:
|
|
137
154
|
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
|
138
|
-
if @current_entry
|
|
139
|
-
raise Error, 'password required to decode zip file'
|
|
140
|
-
end
|
|
155
|
+
return if @current_entry.nil?
|
|
141
156
|
|
|
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'
|
|
157
|
+
if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
|
|
158
|
+
raise StreamingError, @current_entry
|
|
148
159
|
end
|
|
149
|
-
|
|
150
|
-
@decompressor =
|
|
160
|
+
|
|
161
|
+
@decompressor = assemble_io
|
|
151
162
|
flush
|
|
152
163
|
@current_entry
|
|
153
164
|
end
|
|
154
165
|
|
|
155
|
-
def
|
|
166
|
+
def assemble_io # :nodoc:
|
|
167
|
+
io = if @current_entry.encrypted?
|
|
168
|
+
raise Error, 'A password is required to decode this zip file.' if @decrypter.nil?
|
|
169
|
+
|
|
170
|
+
get_decrypted_io
|
|
171
|
+
else
|
|
172
|
+
@archive_io
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
get_decompressor(io)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def get_decrypted_io # :nodoc:
|
|
156
179
|
header = @archive_io.read(@decrypter.header_bytesize)
|
|
157
180
|
@decrypter.reset!(header)
|
|
158
181
|
|
|
159
|
-
|
|
182
|
+
compressed_size =
|
|
183
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
184
|
+
@current_entry.compressed_size == 0 && @complete_entry
|
|
185
|
+
@complete_entry.compressed_size
|
|
186
|
+
else
|
|
187
|
+
@current_entry.compressed_size
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if @decrypter.kind_of?(::Zip::AESDecrypter)
|
|
191
|
+
compressed_size -= @decrypter.header_bytesize
|
|
192
|
+
compressed_size -= ::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
|
|
160
196
|
end
|
|
161
197
|
|
|
162
|
-
def get_decompressor
|
|
198
|
+
def get_decompressor(io) # :nodoc:
|
|
163
199
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
164
200
|
|
|
165
201
|
decompressed_size =
|
|
166
|
-
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
202
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
203
|
+
@current_entry.size == 0 && @complete_entry
|
|
167
204
|
@complete_entry.size
|
|
168
205
|
else
|
|
169
206
|
@current_entry.size
|
|
170
207
|
end
|
|
171
208
|
|
|
172
|
-
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
209
|
+
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
210
|
+
@current_entry.compression_method
|
|
211
|
+
)
|
|
173
212
|
if decompressor_class.nil?
|
|
174
|
-
raise ::Zip::CompressionMethodError,
|
|
175
|
-
"Unsupported compression method #{@current_entry.compression_method}"
|
|
213
|
+
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
176
214
|
end
|
|
177
215
|
|
|
178
|
-
decompressor_class.new(
|
|
216
|
+
decompressor_class.new(io, decompressed_size)
|
|
179
217
|
end
|
|
180
218
|
|
|
181
|
-
def produce_input
|
|
182
|
-
@decompressor.read(
|
|
219
|
+
def produce_input(maxlen = CHUNK_SIZE) # :nodoc:
|
|
220
|
+
@decompressor.read(maxlen)
|
|
183
221
|
end
|
|
184
222
|
|
|
185
|
-
def input_finished?
|
|
186
|
-
@decompressor.eof
|
|
223
|
+
def input_finished? # :nodoc:
|
|
224
|
+
@decompressor.eof?
|
|
187
225
|
end
|
|
188
226
|
end
|
|
189
227
|
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
|
|