rubyzip 2.3.2 → 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 +192 -44
- 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 +53 -6
- data/lib/zip/entry.rb +405 -240
- 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 +185 -226
- 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 -47
- 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 +58 -48
- 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 -3
- 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 +81 -49
- 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,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
|
|
54
|
+
def initialize(context, offset: 0, decrypter: nil)
|
|
53
55
|
super()
|
|
54
|
-
@archive_io
|
|
55
|
-
@decompressor
|
|
56
|
-
@decrypter
|
|
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
|
|
65
|
-
# method on a newly created
|
|
66
|
-
# the first entry in the archive.
|
|
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
|
-
@
|
|
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
|
|
|
@@ -80,17 +89,43 @@ module Zip
|
|
|
80
89
|
open_entry
|
|
81
90
|
end
|
|
82
91
|
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
86
121
|
end
|
|
87
122
|
|
|
88
123
|
class << self
|
|
89
124
|
# Same as #initialize but if a block is passed the opened
|
|
90
125
|
# stream is passed to the block and closed when the block
|
|
91
126
|
# returns.
|
|
92
|
-
def open(filename_or_io, offset
|
|
93
|
-
zio = new(filename_or_io, offset, decrypter)
|
|
127
|
+
def open(filename_or_io, offset: 0, decrypter: nil)
|
|
128
|
+
zio = new(filename_or_io, offset: offset, decrypter: decrypter)
|
|
94
129
|
return zio unless block_given?
|
|
95
130
|
|
|
96
131
|
begin
|
|
@@ -99,16 +134,11 @@ module Zip
|
|
|
99
134
|
zio.close if zio
|
|
100
135
|
end
|
|
101
136
|
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
137
|
end
|
|
108
138
|
|
|
109
139
|
protected
|
|
110
140
|
|
|
111
|
-
def get_io(io_or_file, offset = 0)
|
|
141
|
+
def get_io(io_or_file, offset = 0) # :nodoc:
|
|
112
142
|
if io_or_file.respond_to?(:seek)
|
|
113
143
|
io = io_or_file.dup
|
|
114
144
|
io.seek(offset, ::IO::SEEK_SET)
|
|
@@ -120,57 +150,78 @@ module Zip
|
|
|
120
150
|
end
|
|
121
151
|
end
|
|
122
152
|
|
|
123
|
-
def open_entry
|
|
153
|
+
def open_entry # :nodoc:
|
|
124
154
|
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
|
125
|
-
if @current_entry
|
|
126
|
-
raise Error, 'password required to decode zip file'
|
|
127
|
-
end
|
|
155
|
+
return if @current_entry.nil?
|
|
128
156
|
|
|
129
|
-
if @current_entry
|
|
130
|
-
|
|
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'
|
|
157
|
+
if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
|
|
158
|
+
raise StreamingError, @current_entry
|
|
135
159
|
end
|
|
136
|
-
|
|
137
|
-
@decompressor =
|
|
160
|
+
|
|
161
|
+
@decompressor = assemble_io
|
|
138
162
|
flush
|
|
139
163
|
@current_entry
|
|
140
164
|
end
|
|
141
165
|
|
|
142
|
-
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:
|
|
143
179
|
header = @archive_io.read(@decrypter.header_bytesize)
|
|
144
180
|
@decrypter.reset!(header)
|
|
145
181
|
|
|
146
|
-
|
|
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)
|
|
147
196
|
end
|
|
148
197
|
|
|
149
|
-
def get_decompressor
|
|
198
|
+
def get_decompressor(io) # :nodoc:
|
|
150
199
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
151
200
|
|
|
152
201
|
decompressed_size =
|
|
153
|
-
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
202
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
203
|
+
@current_entry.size == 0 && @complete_entry
|
|
154
204
|
@complete_entry.size
|
|
155
205
|
else
|
|
156
206
|
@current_entry.size
|
|
157
207
|
end
|
|
158
208
|
|
|
159
|
-
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
209
|
+
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
210
|
+
@current_entry.compression_method
|
|
211
|
+
)
|
|
160
212
|
if decompressor_class.nil?
|
|
161
|
-
raise ::Zip::CompressionMethodError,
|
|
162
|
-
"Unsupported compression method #{@current_entry.compression_method}"
|
|
213
|
+
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
163
214
|
end
|
|
164
215
|
|
|
165
|
-
decompressor_class.new(
|
|
216
|
+
decompressor_class.new(io, decompressed_size)
|
|
166
217
|
end
|
|
167
218
|
|
|
168
|
-
def produce_input
|
|
169
|
-
@decompressor.read(
|
|
219
|
+
def produce_input(maxlen = CHUNK_SIZE) # :nodoc:
|
|
220
|
+
@decompressor.read(maxlen)
|
|
170
221
|
end
|
|
171
222
|
|
|
172
|
-
def input_finished?
|
|
173
|
-
@decompressor.eof
|
|
223
|
+
def input_finished? # :nodoc:
|
|
224
|
+
@decompressor.eof?
|
|
174
225
|
end
|
|
175
226
|
end
|
|
176
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 = ''
|
|
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 = ''
|
|
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 = ''
|
|
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, '')) 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
|
|
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
|
|