mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
31c94ffeb5
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@23659 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
265 lines
6.2 KiB
Ruby
265 lines
6.2 KiB
Ruby
#++
|
|
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
|
# See LICENSE.txt for additional licensing information.
|
|
#--
|
|
|
|
##
|
|
#--
|
|
# struct tarfile_entry_posix {
|
|
# char name[100]; # ASCII + (Z unless filled)
|
|
# char mode[8]; # 0 padded, octal, null
|
|
# char uid[8]; # ditto
|
|
# char gid[8]; # ditto
|
|
# char size[12]; # 0 padded, octal, null
|
|
# char mtime[12]; # 0 padded, octal, null
|
|
# char checksum[8]; # 0 padded, octal, null, space
|
|
# char typeflag[1]; # file: "0" dir: "5"
|
|
# char linkname[100]; # ASCII + (Z unless filled)
|
|
# char magic[6]; # "ustar\0"
|
|
# char version[2]; # "00"
|
|
# char uname[32]; # ASCIIZ
|
|
# char gname[32]; # ASCIIZ
|
|
# char devmajor[8]; # 0 padded, octal, null
|
|
# char devminor[8]; # o padded, octal, null
|
|
# char prefix[155]; # ASCII + (Z unless filled)
|
|
# };
|
|
#++
|
|
# A header for a tar file
|
|
|
|
class Gem::Package::TarHeader
|
|
|
|
##
|
|
# Fields in the tar header
|
|
|
|
FIELDS = [
|
|
:checksum,
|
|
:devmajor,
|
|
:devminor,
|
|
:gid,
|
|
:gname,
|
|
:linkname,
|
|
:magic,
|
|
:mode,
|
|
:mtime,
|
|
:name,
|
|
:prefix,
|
|
:size,
|
|
:typeflag,
|
|
:uid,
|
|
:uname,
|
|
:version,
|
|
]
|
|
|
|
##
|
|
# Pack format for a tar header
|
|
|
|
PACK_FORMAT = 'a100' + # name
|
|
'a8' + # mode
|
|
'a8' + # uid
|
|
'a8' + # gid
|
|
'a12' + # size
|
|
'a12' + # mtime
|
|
'a7a' + # chksum
|
|
'a' + # typeflag
|
|
'a100' + # linkname
|
|
'a6' + # magic
|
|
'a2' + # version
|
|
'a32' + # uname
|
|
'a32' + # gname
|
|
'a8' + # devmajor
|
|
'a8' + # devminor
|
|
'a155' # prefix
|
|
|
|
##
|
|
# Unpack format for a tar header
|
|
|
|
UNPACK_FORMAT = 'A100' + # name
|
|
'A8' + # mode
|
|
'A8' + # uid
|
|
'A8' + # gid
|
|
'A12' + # size
|
|
'A12' + # mtime
|
|
'A8' + # checksum
|
|
'A' + # typeflag
|
|
'A100' + # linkname
|
|
'A6' + # magic
|
|
'A2' + # version
|
|
'A32' + # uname
|
|
'A32' + # gname
|
|
'A8' + # devmajor
|
|
'A8' + # devminor
|
|
'A155' # prefix
|
|
|
|
attr_reader(*FIELDS)
|
|
|
|
##
|
|
# Creates a tar header from IO +stream+
|
|
|
|
def self.from(stream)
|
|
header = stream.read 512
|
|
empty = (header == "\0" * 512)
|
|
|
|
fields = header.unpack UNPACK_FORMAT
|
|
|
|
name = fields.shift
|
|
mode = fields.shift.oct
|
|
uid = fields.shift.oct
|
|
gid = fields.shift.oct
|
|
size = fields.shift.oct
|
|
mtime = fields.shift.oct
|
|
checksum = fields.shift.oct
|
|
typeflag = fields.shift
|
|
linkname = fields.shift
|
|
magic = fields.shift
|
|
version = fields.shift.oct
|
|
uname = fields.shift
|
|
gname = fields.shift
|
|
devmajor = fields.shift.oct
|
|
devminor = fields.shift.oct
|
|
prefix = fields.shift
|
|
|
|
new :name => name,
|
|
:mode => mode,
|
|
:uid => uid,
|
|
:gid => gid,
|
|
:size => size,
|
|
:mtime => mtime,
|
|
:checksum => checksum,
|
|
:typeflag => typeflag,
|
|
:linkname => linkname,
|
|
:magic => magic,
|
|
:version => version,
|
|
:uname => uname,
|
|
:gname => gname,
|
|
:devmajor => devmajor,
|
|
:devminor => devminor,
|
|
:prefix => prefix,
|
|
|
|
:empty => empty
|
|
|
|
# HACK unfactor for Rubinius
|
|
#new :name => fields.shift,
|
|
# :mode => fields.shift.oct,
|
|
# :uid => fields.shift.oct,
|
|
# :gid => fields.shift.oct,
|
|
# :size => fields.shift.oct,
|
|
# :mtime => fields.shift.oct,
|
|
# :checksum => fields.shift.oct,
|
|
# :typeflag => fields.shift,
|
|
# :linkname => fields.shift,
|
|
# :magic => fields.shift,
|
|
# :version => fields.shift.oct,
|
|
# :uname => fields.shift,
|
|
# :gname => fields.shift,
|
|
# :devmajor => fields.shift.oct,
|
|
# :devminor => fields.shift.oct,
|
|
# :prefix => fields.shift,
|
|
|
|
# :empty => empty
|
|
end
|
|
|
|
##
|
|
# Creates a new TarHeader using +vals+
|
|
|
|
def initialize(vals)
|
|
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
|
|
raise ArgumentError, ":name, :size, :prefix and :mode required"
|
|
end
|
|
|
|
vals[:uid] ||= 0
|
|
vals[:gid] ||= 0
|
|
vals[:mtime] ||= 0
|
|
vals[:checksum] ||= ""
|
|
vals[:typeflag] ||= "0"
|
|
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
|
|
|
|
##
|
|
# Is the tar entry empty?
|
|
|
|
def empty?
|
|
@empty
|
|
end
|
|
|
|
def ==(other) # :nodoc:
|
|
self.class === other and
|
|
@checksum == other.checksum and
|
|
@devmajor == other.devmajor and
|
|
@devminor == other.devminor and
|
|
@gid == other.gid and
|
|
@gname == other.gname and
|
|
@linkname == other.linkname and
|
|
@magic == other.magic and
|
|
@mode == other.mode and
|
|
@mtime == other.mtime and
|
|
@name == other.name and
|
|
@prefix == other.prefix and
|
|
@size == other.size and
|
|
@typeflag == other.typeflag and
|
|
@uid == other.uid and
|
|
@uname == other.uname and
|
|
@version == other.version
|
|
end
|
|
|
|
def to_s # :nodoc:
|
|
update_checksum
|
|
header
|
|
end
|
|
|
|
##
|
|
# Updates the TarHeader's checksum
|
|
|
|
def update_checksum
|
|
header = header " " * 8
|
|
@checksum = oct calculate_checksum(header), 6
|
|
end
|
|
|
|
private
|
|
|
|
def calculate_checksum(header)
|
|
header.unpack("C*").inject { |a, b| a + b }
|
|
end
|
|
|
|
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
|
|
|
|
def oct(num, len)
|
|
"%0#{len}o" % num
|
|
end
|
|
|
|
end
|
|
|