module Zip # ZipInputStream is the basic class for reading zip entries in a # zip file. It is possible to create a ZipInputStream object directly, # passing the zip file name to the constructor, but more often than not # the ZipInputStream will be obtained from a ZipFile (perhaps using the # ZipFileSystem interface) object for a particular entry in the zip # archive. # # A ZipInputStream inherits IOExtras::AbstractInputStream in order # to provide an IO-like interface for reading from a single zip # entry. Beyond methods for mimicking an IO-object it contains # the method get_next_entry for iterating through the entries of # an archive. get_next_entry returns a ZipEntry object that describes # the zip entry the ZipInputStream is currently reading from. # # Example that creates a zip archive with ZipOutputStream and reads it # back again with a ZipInputStream. # # require 'zip/zip' # # Zip::ZipOutputStream::open("my.zip") { # |io| # # io.put_next_entry("first_entry.txt") # io.write "Hello world!" # # io.put_next_entry("adir/first_entry.txt") # io.write "Hello again!" # } # # # Zip::ZipInputStream::open("my.zip") { # |io| # # while (entry = io.get_next_entry) # puts "Contents of #{entry.name}: '#{io.read}'" # end # } # # java.util.zip.ZipInputStream is the original inspiration for this # class. class InputStream include ::Zip::IOExtras::AbstractInputStream # Opens the indicated zip file. An exception is thrown # if the specified offset in the specified filename is # not a local zip entry header. def initialize(filename, offset = 0, io = nil) super() if (io.nil?) @archiveIO = ::File.open(filename, "rb") @archiveIO.seek(offset, IO::SEEK_SET) else @archiveIO = io end @decompressor = NullDecompressor.instance @currentEntry = nil end def close @archiveIO.close end # Same as #initialize but if a block is passed the opened # stream is passed to the block and closed when the block # returns. def InputStream.open(filename) return new(filename) unless block_given? zio = new(filename) yield zio ensure zio.close if zio end def InputStream.open_buffer(io) return new('',0,io) unless block_given? zio = new('',0,io) yield zio ensure zio.close if zio end # Returns a ZipEntry object. It is necessary to call this # method on a newly created ZipInputStream before reading from # the first entry in the archive. Returns nil when there are # no more entries. def get_next_entry @archiveIO.seek(@currentEntry.next_header_offset, IO::SEEK_SET) if @currentEntry open_entry end # Rewinds the stream to the beginning of the current entry def rewind return if @currentEntry.nil? @lineno = 0 @pos = 0 @archiveIO.seek(@currentEntry.local_header_offset, IO::SEEK_SET) open_entry end # Modeled after IO.sysread def sysread(numberOfBytes = nil, buf = nil) @decompressor.sysread(numberOfBytes, buf) end def eof @output_buffer.empty? && @decompressor.eof end alias :eof? :eof protected def open_entry @currentEntry = Entry.read_local_entry(@archiveIO) if @currentEntry.nil? @decompressor = NullDecompressor.instance elsif @currentEntry.compression_method == Entry::STORED @decompressor = PassThruDecompressor.new(@archiveIO, @currentEntry.size) elsif @currentEntry.compression_method == Entry::DEFLATED @decompressor = Inflater.new(@archiveIO) else raise ZipCompressionMethodError, "Unsupported compression method #{@currentEntry.compression_method}" end flush return @currentEntry end def produce_input @decompressor.produce_input end def input_finished? @decompressor.input_finished? end end end # Copyright (C) 2002, 2003 Thomas Sondergaard # rubyzip is free software; you can redistribute it and/or # modify it under the terms of the ruby license.