class Archive::Reader

Constants

BLOCK_SIZE

Public Class Methods

new(params = {}) click to toggle source

@param [Hash] params @option params [Object] :command @option params [String] :file_name @option params [String] :memory @option params [#call] :reader

Calls superclass method Archive::BaseArchive::new
# File lib/ffi_libarchive/reader.rb, line 60
def initialize(params = {})
  super C.method(:archive_read_new), C.method(:archive_read_free)

  begin
    init_compression params[:command]
    init_format

    if params[:file_name]
      init_for_filename params[:file_name]
    elsif params[:memory]
      init_for_memory params[:memory]
    elsif params[:reader]
      init_for_stream params[:reader]
    end
  rescue StandardError
    close
    raise
  end
end
open_filename(file_name, command = nil) { |reader| ... } click to toggle source

@param [String] file_name @return [Reader] @yieldparam [Reader]

# File lib/ffi_libarchive/reader.rb, line 10
def self.open_filename(file_name, command = nil)
  if block_given?
    reader = open_filename file_name, command
    begin
      yield reader
    ensure
      reader.close if reader.respond_to?(:close)
    end
  else
    new file_name: file_name, command: command
  end
end
open_memory(string, command = nil) { |reader| ... } click to toggle source

@param [String] string @return [Reader] @yieldparam [Reader]

# File lib/ffi_libarchive/reader.rb, line 26
def self.open_memory(string, command = nil)
  if block_given?
    reader = open_memory string, command
    begin
      yield reader
    ensure
      reader.close if reader.respond_to?(:close)
    end
  else
    new memory: string, command: command
  end
end
open_stream(stream, command = nil) { |reader| ... } click to toggle source

@param [#call] stream @return [Reader] @yieldparam [Reader]

# File lib/ffi_libarchive/reader.rb, line 42
def self.open_stream(stream, command = nil)
  if block_given?
    reader = open_stream stream, command
    begin
      yield reader
    ensure
      reader.close if reader.respond_to?(:close)
    end
  else
    new reader: stream, command: command
  end
end

Private Class Methods

new(alloc, free) click to toggle source

@param [Method] alloc @param [Method] free

# File lib/ffi_libarchive/archive.rb, line 150
def initialize(alloc, free)
  raise ArgumentError, 'Invalid methods' unless alloc.respond_to?(:call) && free.respond_to?(:call)

  @archive = alloc.call
  raise Error, 'No archive open' unless @archive

  @archive_free = [free]
  ObjectSpace.define_finalizer(self, method(:close).to_proc)
end

Public Instance Methods

close() click to toggle source
Calls superclass method Archive::BaseArchive#close
# File lib/ffi_libarchive/reader.rb, line 171
def close
  super
  @read_callback = nil
  @skip_callback = nil
  @seek_callback = nil
end
each_entry() { |entry| ... } click to toggle source

@yieldparam [Entry]

# File lib/ffi_libarchive/reader.rb, line 111
def each_entry
  while (entry = next_header)
    yield entry
  end
end
each_entry_skip_data() { |entry| ... } click to toggle source

@yieldparam [Entry] entry

# File lib/ffi_libarchive/reader.rb, line 126
def each_entry_skip_data
  while (entry = next_header)
    begin
      yield entry
    ensure
      C.archive_read_data_skip archive
    end
  end
end
each_entry_with_data(size = C::DATA_BUFFER_SIZE) { |entry, read_data(size)| ... } click to toggle source

@yieldparam [Entry] entry @yieldparam [String] data

# File lib/ffi_libarchive/reader.rb, line 119
def each_entry_with_data(size = C::DATA_BUFFER_SIZE)
  while (entry = next_header)
    yield entry, read_data(size)
  end
end
extract(entry, flags = 0) click to toggle source

@param [Entry] entry @param [Integer] flags see ::EXTRACT_*

# File lib/ffi_libarchive/reader.rb, line 82
def extract(entry, flags = 0)
  raise ArgumentError, 'Expected Archive::Entry as first argument' unless entry.is_a? Entry
  raise ArgumentError, 'Expected Integer as second argument' unless flags.is_a? Integer

  flags |= EXTRACT_FFLAGS
  raise Error, self if C.archive_read_extract(archive, entry.entry, flags) != C::OK
end
header_position() click to toggle source

Retrieve the byte offset in UNCOMPRESSED data where last-read header started. @return [Integer]

# File lib/ffi_libarchive/reader.rb, line 92
def header_position
  C.archive_read_header_position archive
end
next_header() click to toggle source

@return [Entry]

# File lib/ffi_libarchive/reader.rb, line 97
def next_header
  entry_ptr = FFI::MemoryPointer.new(:pointer)

  case C.archive_read_next_header(archive, entry_ptr)
  when C::OK
    Entry.from_pointer entry_ptr.get_pointer(0)
  when C::EOF
    nil
  else
    raise Error, self
  end
end
read_data(size = C::DATA_BUFFER_SIZE) { |chunk| ... } click to toggle source

@return [String, Integer] @yieldparam [String] chunk

# File lib/ffi_libarchive/reader.rb, line 138
def read_data(size = C::DATA_BUFFER_SIZE)
  raise ArgumentError, "Buffer size must be > 0 (was: #{size})" if !size.is_a?(Integer) || size <= 0

  data   = nil
  buffer = FFI::MemoryPointer.new(:char, size)
  len    = 0

  while (n = C.archive_read_data(archive, buffer, size)) != 0
    # TODO: C::FATAL, C::WARN, C::RETRY
    raise Error, self if n < 0

    chunk = buffer.get_bytes(0, n)
    if block_given?
      yield chunk
    elsif data
      data << chunk
    else
      data = chunk.dup
    end

    len += n
  end

  data || len
end
save_data(file_name) click to toggle source

@param [String] file_name

# File lib/ffi_libarchive/reader.rb, line 165
def save_data(file_name)
  File.open(file_name, 'wb') do |f|
    raise Error, self if C.archive_read_data_into_fd(archive, f.fileno) != C::OK
  end
end

Protected Instance Methods

init_compression(command) click to toggle source
# File lib/ffi_libarchive/reader.rb, line 180
def init_compression(command)
  if command && !(cmd = command.to_s).empty?
    raise Error, self if C.archive_read_support_compression_program(archive, cmd) != C::OK
  elsif C.respond_to?(:archive_read_support_filter_all)
    raise Error, self if C.archive_read_support_filter_all(archive) != C::OK
  elsif C.archive_read_support_compression_all(archive) != C::OK
    raise Error, self
  end
end
init_for_filename(file_name) click to toggle source
# File lib/ffi_libarchive/reader.rb, line 196
def init_for_filename(file_name)
  raise Error, self if C.archive_read_open_filename(archive, file_name, BLOCK_SIZE) != C::OK
end
init_for_memory(string) click to toggle source
# File lib/ffi_libarchive/reader.rb, line 200
def init_for_memory(string)
  buffer = Utils.get_memory_ptr(string)
  raise Error, self if C.archive_read_open_memory(archive, buffer, string.bytesize) != C::OK
end
init_for_stream(reader) click to toggle source
# File lib/ffi_libarchive/reader.rb, line 205
def init_for_stream(reader)
  @read_callback = proc do |_ar, _client_data, buffer|
    # @type [String]
    data = reader.call

    if data.is_a?(String)
      buffer.put_pointer 0, Utils.get_memory_ptr(data)
      data.bytesize
    else
      0
    end
  end
  raise Error, self if C.archive_read_set_read_callback(archive, @read_callback) != C::OK

  if reader.respond_to?(:skip)
    @skip_callback = proc { |_ar, _client_data, request| reader.skip(request) }
    raise Error, self if C.archive_read_set_skip_callback(archive, @skip_callback) != C::OK
  end

  if reader.respond_to?(:seek)
    @seek_callback = proc { |_ar, _client_data, offset, whence| reader.seek(offset, whence) }
    raise Error, self if C.archive_read_set_seek_callback(archive, @seek_callback) != C::OK
  end

  # Required or open1 will segfault, even though the callback data is not used.
  raise Error, self if C.archive_read_set_callback_data(archive, FFI::Pointer::NULL) != C::OK
  raise Error, self if C.archive_read_open1(archive) != C::OK
end
init_format() click to toggle source
# File lib/ffi_libarchive/reader.rb, line 190
def init_format
  raise Error, self if C.archive_read_support_format_all(archive) != C::OK
end