Re-enable spec/library for full CI runs.
[rbx.git] / lib / rubygems / package / tar_header.rb
blobc194cc05301a22e1ed1751e83e43629d83ffb785
1 #++
2 # Copyright (C) 2004 Mauricio Julio Fernández Pradier
3 # See LICENSE.txt for additional licensing information.
4 #--
6 require 'rubygems/package'
8 ##
9 #--
10 # struct tarfile_entry_posix {
11 #   char name[100];     # ASCII + (Z unless filled)
12 #   char mode[8];       # 0 padded, octal, null
13 #   char uid[8];        # ditto
14 #   char gid[8];        # ditto
15 #   char size[12];      # 0 padded, octal, null
16 #   char mtime[12];     # 0 padded, octal, null
17 #   char checksum[8];   # 0 padded, octal, null, space
18 #   char typeflag[1];   # file: "0"  dir: "5"
19 #   char linkname[100]; # ASCII + (Z unless filled)
20 #   char magic[6];      # "ustar\0"
21 #   char version[2];    # "00"
22 #   char uname[32];     # ASCIIZ
23 #   char gname[32];     # ASCIIZ
24 #   char devmajor[8];   # 0 padded, octal, null
25 #   char devminor[8];   # o padded, octal, null
26 #   char prefix[155];   # ASCII + (Z unless filled)
27 # };
28 #++
30 class Gem::Package::TarHeader
32   FIELDS = [
33     :checksum,
34     :devmajor,
35     :devminor,
36     :gid,
37     :gname,
38     :linkname,
39     :magic,
40     :mode,
41     :mtime,
42     :name,
43     :prefix,
44     :size,
45     :typeflag,
46     :uid,
47     :uname,
48     :version,
49   ]
51   PACK_FORMAT = 'a100' + # name
52                 'a8'   + # mode
53                 'a8'   + # uid
54                 'a8'   + # gid
55                 'a12'  + # size
56                 'a12'  + # mtime
57                 'a7a'  + # chksum
58                 'a'    + # typeflag
59                 'a100' + # linkname
60                 'a6'   + # magic
61                 'a2'   + # version
62                 'a32'  + # uname
63                 'a32'  + # gname
64                 'a8'   + # devmajor
65                 'a8'   + # devminor
66                 'a155'   # prefix
68   UNPACK_FORMAT = 'A100' + # name
69                   'A8'   + # mode
70                   'A8'   + # uid
71                   'A8'   + # gid
72                   'A12'  + # size
73                   'A12'  + # mtime
74                   'A8'   + # checksum
75                   'A'    + # typeflag
76                   'A100' + # linkname
77                   'A6'   + # magic
78                   'A2'   + # version
79                   'A32'  + # uname
80                   'A32'  + # gname
81                   'A8'   + # devmajor
82                   'A8'   + # devminor
83                   'A155'   # prefix
85   attr_reader(*FIELDS)
87   def self.from(stream)
88     header = stream.read 512
89     empty = (header == "\0" * 512)
91     fields = header.unpack UNPACK_FORMAT
93     name     = fields.shift
94     mode     = fields.shift.oct
95     uid      = fields.shift.oct
96     gid      = fields.shift.oct
97     size     = fields.shift.oct
98     mtime    = fields.shift.oct
99     checksum = fields.shift.oct
100     typeflag = fields.shift
101     linkname = fields.shift
102     magic    = fields.shift
103     version  = fields.shift.oct
104     uname    = fields.shift
105     gname    = fields.shift
106     devmajor = fields.shift.oct
107     devminor = fields.shift.oct
108     prefix   = fields.shift
110     new :name     => name,
111         :mode     => mode,
112         :uid      => uid,
113         :gid      => gid,
114         :size     => size,
115         :mtime    => mtime,
116         :checksum => checksum,
117         :typeflag => typeflag,
118         :linkname => linkname,
119         :magic    => magic,
120         :version  => version,
121         :uname    => uname,
122         :gname    => gname,
123         :devmajor => devmajor,
124         :devminor => devminor,
125         :prefix   => prefix,
127         :empty    => empty
129     # HACK unfactor for Rubinius
130     #new :name     => fields.shift,
131     #    :mode     => fields.shift.oct,
132     #    :uid      => fields.shift.oct,
133     #    :gid      => fields.shift.oct,
134     #    :size     => fields.shift.oct,
135     #    :mtime    => fields.shift.oct,
136     #    :checksum => fields.shift.oct,
137     #    :typeflag => fields.shift,
138     #    :linkname => fields.shift,
139     #    :magic    => fields.shift,
140     #    :version  => fields.shift.oct,
141     #    :uname    => fields.shift,
142     #    :gname    => fields.shift,
143     #    :devmajor => fields.shift.oct,
144     #    :devminor => fields.shift.oct,
145     #    :prefix   => fields.shift,
147     #    :empty => empty
148   end
150   def initialize(vals)
151     unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
152       raise ArgumentError, ":name, :size, :prefix and :mode required"
153     end
155     vals[:uid] ||= 0
156     vals[:gid] ||= 0
157     vals[:mtime] ||= 0
158     vals[:checksum] ||= ""
159     vals[:typeflag] ||= "0"
160     vals[:magic] ||= "ustar"
161     vals[:version] ||= "00"
162     vals[:uname] ||= "wheel"
163     vals[:gname] ||= "wheel"
164     vals[:devmajor] ||= 0
165     vals[:devminor] ||= 0
167     FIELDS.each do |name|
168       instance_variable_set "@#{name}", vals[name]
169     end
171     @empty = vals[:empty]
172   end
174   def empty?
175     @empty
176   end
178   def ==(other)
179     self.class === other and
180     @checksum == other.checksum and
181     @devmajor == other.devmajor and
182     @devminor == other.devminor and
183     @gid      == other.gid      and
184     @gname    == other.gname    and
185     @linkname == other.linkname and
186     @magic    == other.magic    and
187     @mode     == other.mode     and
188     @mtime    == other.mtime    and
189     @name     == other.name     and
190     @prefix   == other.prefix   and
191     @size     == other.size     and
192     @typeflag == other.typeflag and
193     @uid      == other.uid      and
194     @uname    == other.uname    and
195     @version  == other.version
196   end
198   def to_s
199     update_checksum
200     header
201   end
203   def update_checksum
204     header = header " " * 8
205     @checksum = oct calculate_checksum(header), 6
206   end
208   private
210   def calculate_checksum(header)
211     header.unpack("C*").inject { |a, b| a + b }
212   end
214   def header(checksum = @checksum)
215     header = [
216       name,
217       oct(mode, 7),
218       oct(uid, 7),
219       oct(gid, 7),
220       oct(size, 11),
221       oct(mtime, 11),
222       checksum,
223       " ",
224       typeflag,
225       linkname,
226       magic,
227       oct(version, 2),
228       uname,
229       gname,
230       oct(devmajor, 7),
231       oct(devminor, 7),
232       prefix
233     ]
235     header = header.pack PACK_FORMAT
236                   
237     header << ("\0" * ((512 - header.size) % 512))
238   end
240   def oct(num, len)
241     "%0#{len}o" % num
242   end