class Minitar::Reader
streams.
sequential or random access, but certain features only work with random access data
The class that reads a tar format archive from a data stream. The data stream may be
def self.each_entry(io)
Minitar::Reader.each_entry(io) -> enumerator
:call-seq:
If a block is not provided, an enumerator will be created with the same behaviour.
end
end
# ...
inp.each do |entry|
Minitar::Input.open(io) do |i|
Iterates over each entry in the provided input. This wraps the common pattern of:
def self.each_entry(io) return to_enum(__method__, io) unless block_given? Input.open(io) do |reader| reader.each_entry do |entry| yield entry end end end
def self.open(io)
the Reader object will automatically be closed when the block terminates. In this
optional code block is given, it will be passed the new _writer_ as an argument and
With no associated block, +Reader::open+ is a synonym for +Reader::new+. If the
def self.open(io) reader = new(io) return reader unless block_given? # This exception context must remain, otherwise the stream closes on open even if # a block is not given. begin yield reader ensure reader.close end end
def close
def close end
def closed? = false
def closed? = false
def each_entry
def each_entry return to_enum unless block_given? loop do return if @io.eof? header = Minitar::PosixHeader.from_stream(@io) raise Minitar::InvalidTarStream unless header.valid? return if header.empty? raise Minitar::InvalidTarStream if header.size < 0 if header.long_name? name_block = (header.size / 512.0).ceil * 512 long_name = @io.read(name_block).rstrip header = PosixHeader.from_stream(@io) return if header.empty? header.long_name = long_name elsif header.pax_header? pax_header = PaxHeader.from_stream(@io, header) header = PosixHeader.from_stream(@io) return if header.empty? header.size = pax_header.size if pax_header.size end entry = EntryStream.new(header, @io) size = entry.size yield entry skip = (512 - (size % 512)) % 512 if Minitar.seekable?(@io, :seek) # avoid reading... try_seek(size - entry.bytes_read) else pending = size - entry.bytes_read while pending > 0 bread = @io.read([pending, 4096].min).bytesize raise UnexpectedEOF if @io.eof? pending -= bread end end @io.read(skip) # discard trailing zeros # make sure nobody can use #read, #getc or #rewind anymore entry.close end end
def initialize(io)
def initialize(io) @io = io @init_pos = begin io.pos rescue nil end end
def rewind
a #each or #each_entry iteration. This only works with random access data streams
Resets the read pointer to the beginning of data stream. Do not call this during
def rewind if @init_pos.zero? raise Minitar::NonSeekableStream unless Minitar.seekable?(@io, :rewind) @io.rewind else raise Minitar::NonSeekableStream unless Minitar.seekable?(@io, :pos=) @io.pos = @init_pos end end
def try_seek(bytes)
def try_seek(bytes) @io.seek(bytes, IO::SEEK_CUR) rescue RangeError # This happens when skipping the large entry and the skipping entry size exceeds # maximum allowed size (varies by platform and underlying IO object). max = RbConfig::LIMITS.fetch("INT_MAX", 2147483647) skipped = 0 while skipped < bytes to_skip = [bytes - skipped, max].min @io.seek(to_skip, IO::SEEK_CUR) skipped += to_skip end end