class Gem::Package::TarHeader

A header for a tar file

Constants

FIELDS

Fields in the tar header

PACK_FORMAT

Pack format for a tar header

UNPACK_FORMAT

Unpack format for a tar header

Public Class Methods

from(stream) click to toggle source

Creates a tar header from IO stream

# File lib/rubygems/package/tar_header.rb, line 101
def self.from(stream)
  header = stream.read 512
  empty = (EMPTY_HEADER == header)

  fields = header.unpack UNPACK_FORMAT

  new :name => fields.shift,
      :mode => strict_oct(fields.shift),
      :uid => oct_or_256based(fields.shift),
      :gid => oct_or_256based(fields.shift),
      :size => strict_oct(fields.shift),
      :mtime => strict_oct(fields.shift),
      :checksum => strict_oct(fields.shift),
      :typeflag => fields.shift,
      :linkname => fields.shift,
      :magic => fields.shift,
      :version => strict_oct(fields.shift),
      :uname => fields.shift,
      :gname => fields.shift,
      :devmajor => strict_oct(fields.shift),
      :devminor => strict_oct(fields.shift),
      :prefix => fields.shift,

      :empty => empty
end
new(vals) click to toggle source

Creates a new TarHeader using vals

# File lib/rubygems/package/tar_header.rb, line 145
def initialize(vals)
  unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
    raise ArgumentError, ":name, :size, :prefix and :mode required"
  end

  vals[:uid] ||= 0
  vals[:gid] ||= 0
  vals[:mtime] ||= 0
  vals[:checksum] ||= ""
  vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
  vals[:magic] ||= "ustar"
  vals[:version] ||= "00"
  vals[:uname] ||= "wheel"
  vals[:gname] ||= "wheel"
  vals[:devmajor] ||= 0
  vals[:devminor] ||= 0

  FIELDS.each do |name|
    instance_variable_set "@#{name}", vals[name]
  end

  @empty = vals[:empty]
end
oct_or_256based(str) click to toggle source
# File lib/rubygems/package/tar_header.rb, line 133
def self.oct_or_256based(str)
  # \x80 flags a positive 256-based number
  # \ff flags a negative 256-based number
  # In case we have a match, parse it as a signed binary value
  # in big-endian order, except that the high-order bit is ignored.
  return str.unpack("N2").last if str =~ /\A[\x80\xff]/n
  strict_oct(str)
end
strict_oct(str) click to toggle source
# File lib/rubygems/package/tar_header.rb, line 127
def self.strict_oct(str)
  return str.strip.oct if str.strip =~ /\A[0-7]*\z/

  raise ArgumentError, "#{str.inspect} is not an octal string"
end

Public Instance Methods

empty?() click to toggle source

Is the tar entry empty?

# File lib/rubygems/package/tar_header.rb, line 172
def empty?
  @empty
end
update_checksum() click to toggle source

Updates the TarHeader’s checksum

# File lib/rubygems/package/tar_header.rb, line 204
def update_checksum
  header = header " " * 8
  @checksum = oct calculate_checksum(header), 6
end

Private Instance Methods

calculate_checksum(header) click to toggle source
# File lib/rubygems/package/tar_header.rb, line 211
def calculate_checksum(header)
  header.sum(0)
end
header(checksum = @checksum) click to toggle source
# File lib/rubygems/package/tar_header.rb, line 215
def header(checksum = @checksum)
  header = [
    name,
    oct(mode, 7),
    oct(uid, 7),
    oct(gid, 7),
    oct(size, 11),
    oct(mtime, 11),
    checksum,
    " ",
    typeflag,
    linkname,
    magic,
    oct(version, 2),
    uname,
    gname,
    oct(devmajor, 7),
    oct(devminor, 7),
    prefix,
  ]

  header = header.pack PACK_FORMAT

  header << ("\0" * ((512 - header.size) % 512))
end
oct(num, len) click to toggle source
# File lib/rubygems/package/tar_header.rb, line 241
def oct(num, len)
  "%0#{len}o" % num
end