gel 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +39 -0
  5. data/exe/gel +13 -0
  6. data/lib/gel.rb +22 -0
  7. data/lib/gel/catalog.rb +153 -0
  8. data/lib/gel/catalog/common.rb +82 -0
  9. data/lib/gel/catalog/compact_index.rb +152 -0
  10. data/lib/gel/catalog/dependency_index.rb +125 -0
  11. data/lib/gel/catalog/legacy_index.rb +157 -0
  12. data/lib/gel/catalog/marshal_hacks.rb +16 -0
  13. data/lib/gel/command.rb +86 -0
  14. data/lib/gel/command/config.rb +11 -0
  15. data/lib/gel/command/env.rb +7 -0
  16. data/lib/gel/command/exec.rb +66 -0
  17. data/lib/gel/command/help.rb +7 -0
  18. data/lib/gel/command/install.rb +7 -0
  19. data/lib/gel/command/install_gem.rb +16 -0
  20. data/lib/gel/command/lock.rb +34 -0
  21. data/lib/gel/command/ruby.rb +10 -0
  22. data/lib/gel/command/shell_setup.rb +25 -0
  23. data/lib/gel/command/stub.rb +12 -0
  24. data/lib/gel/command/update.rb +11 -0
  25. data/lib/gel/compatibility.rb +4 -0
  26. data/lib/gel/compatibility/bundler.rb +54 -0
  27. data/lib/gel/compatibility/bundler/cli.rb +6 -0
  28. data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
  29. data/lib/gel/compatibility/bundler/setup.rb +4 -0
  30. data/lib/gel/compatibility/rubygems.rb +192 -0
  31. data/lib/gel/compatibility/rubygems/command.rb +4 -0
  32. data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
  33. data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
  34. data/lib/gel/config.rb +80 -0
  35. data/lib/gel/db.rb +294 -0
  36. data/lib/gel/direct_gem.rb +29 -0
  37. data/lib/gel/environment.rb +592 -0
  38. data/lib/gel/error.rb +104 -0
  39. data/lib/gel/gemfile_parser.rb +144 -0
  40. data/lib/gel/gemspec_parser.rb +95 -0
  41. data/lib/gel/git_catalog.rb +38 -0
  42. data/lib/gel/git_depot.rb +119 -0
  43. data/lib/gel/httpool.rb +148 -0
  44. data/lib/gel/installer.rb +251 -0
  45. data/lib/gel/lock_loader.rb +164 -0
  46. data/lib/gel/lock_parser.rb +64 -0
  47. data/lib/gel/locked_store.rb +126 -0
  48. data/lib/gel/multi_store.rb +96 -0
  49. data/lib/gel/package.rb +156 -0
  50. data/lib/gel/package/inspector.rb +23 -0
  51. data/lib/gel/package/installer.rb +267 -0
  52. data/lib/gel/path_catalog.rb +44 -0
  53. data/lib/gel/pinboard.rb +140 -0
  54. data/lib/gel/pub_grub/preference_strategy.rb +82 -0
  55. data/lib/gel/pub_grub/source.rb +153 -0
  56. data/lib/gel/runtime.rb +27 -0
  57. data/lib/gel/store.rb +205 -0
  58. data/lib/gel/store_catalog.rb +31 -0
  59. data/lib/gel/store_gem.rb +80 -0
  60. data/lib/gel/stub_set.rb +51 -0
  61. data/lib/gel/support/gem_platform.rb +225 -0
  62. data/lib/gel/support/gem_requirement.rb +264 -0
  63. data/lib/gel/support/gem_version.rb +398 -0
  64. data/lib/gel/support/tar.rb +13 -0
  65. data/lib/gel/support/tar/tar_header.rb +229 -0
  66. data/lib/gel/support/tar/tar_reader.rb +123 -0
  67. data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
  68. data/lib/gel/support/tar/tar_writer.rb +339 -0
  69. data/lib/gel/tail_file.rb +205 -0
  70. data/lib/gel/version.rb +5 -0
  71. data/lib/gel/work_pool.rb +143 -0
  72. data/man/man1/gel-exec.1 +16 -0
  73. data/man/man1/gel-install.1 +16 -0
  74. data/man/man1/gel.1 +30 -0
  75. metadata +131 -0
@@ -0,0 +1,398 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Copyright (c) Chad Fowler, Rich Kilmer, Jim Weirich and others.
4
+ # Portions copyright (c) Engine Yard and Andre Arko
5
+ #
6
+ # Redistributed under the terms of the MIT license
7
+ #
8
+ ##
9
+ # The Version class processes string versions into comparable
10
+ # values. A version string should normally be a series of numbers
11
+ # separated by periods. Each part (digits separated by periods) is
12
+ # considered its own number, and these are used for sorting. So for
13
+ # instance, 3.10 sorts higher than 3.2 because ten is greater than
14
+ # two.
15
+ #
16
+ # If any part contains letters (currently only a-z are supported) then
17
+ # that version is considered prerelease. Versions with a prerelease
18
+ # part in the Nth part sort less than versions with N-1
19
+ # parts. Prerelease parts are sorted alphabetically using the normal
20
+ # Ruby string sorting rules. If a prerelease part contains both
21
+ # letters and numbers, it will be broken into multiple parts to
22
+ # provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is
23
+ # greater than 1.0.a9).
24
+ #
25
+ # Prereleases sort between real releases (newest to oldest):
26
+ #
27
+ # 1. 1.0
28
+ # 2. 1.0.b1
29
+ # 3. 1.0.a.2
30
+ # 4. 0.9
31
+ #
32
+ # If you want to specify a version restriction that includes both prereleases
33
+ # and regular releases of the 1.x series this is the best way:
34
+ #
35
+ # s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
36
+ #
37
+ # == How Software Changes
38
+ #
39
+ # Users expect to be able to specify a version constraint that gives them
40
+ # some reasonable expectation that new versions of a library will work with
41
+ # their software if the version constraint is true, and not work with their
42
+ # software if the version constraint is false. In other words, the perfect
43
+ # system will accept all compatible versions of the library and reject all
44
+ # incompatible versions.
45
+ #
46
+ # Libraries change in 3 ways (well, more than 3, but stay focused here!).
47
+ #
48
+ # 1. The change may be an implementation detail only and have no effect on
49
+ # the client software.
50
+ # 2. The change may add new features, but do so in a way that client software
51
+ # written to an earlier version is still compatible.
52
+ # 3. The change may change the public interface of the library in such a way
53
+ # that old software is no longer compatible.
54
+ #
55
+ # Some examples are appropriate at this point. Suppose I have a Stack class
56
+ # that supports a <tt>push</tt> and a <tt>pop</tt> method.
57
+ #
58
+ # === Examples of Category 1 changes:
59
+ #
60
+ # * Switch from an array based implementation to a linked-list based
61
+ # implementation.
62
+ # * Provide an automatic (and transparent) backing store for large stacks.
63
+ #
64
+ # === Examples of Category 2 changes might be:
65
+ #
66
+ # * Add a <tt>depth</tt> method to return the current depth of the stack.
67
+ # * Add a <tt>top</tt> method that returns the current top of stack (without
68
+ # changing the stack).
69
+ # * Change <tt>push</tt> so that it returns the item pushed (previously it
70
+ # had no usable return value).
71
+ #
72
+ # === Examples of Category 3 changes might be:
73
+ #
74
+ # * Changes <tt>pop</tt> so that it no longer returns a value (you must use
75
+ # <tt>top</tt> to get the top of the stack).
76
+ # * Rename the methods to <tt>push_item</tt> and <tt>pop_item</tt>.
77
+ #
78
+ # == RubyGems Rational Versioning
79
+ #
80
+ # * Versions shall be represented by three non-negative integers, separated
81
+ # by periods (e.g. 3.1.4). The first integers is the "major" version
82
+ # number, the second integer is the "minor" version number, and the third
83
+ # integer is the "build" number.
84
+ #
85
+ # * A category 1 change (implementation detail) will increment the build
86
+ # number.
87
+ #
88
+ # * A category 2 change (backwards compatible) will increment the minor
89
+ # version number and reset the build number.
90
+ #
91
+ # * A category 3 change (incompatible) will increment the major build number
92
+ # and reset the minor and build numbers.
93
+ #
94
+ # * Any "public" release of a gem should have a different version. Normally
95
+ # that means incrementing the build number. This means a developer can
96
+ # generate builds all day long, but as soon as they make a public release,
97
+ # the version must be updated.
98
+ #
99
+ # === Examples
100
+ #
101
+ # Let's work through a project lifecycle using our Stack example from above.
102
+ #
103
+ # Version 0.0.1:: The initial Stack class is release.
104
+ # Version 0.0.2:: Switched to a linked=list implementation because it is
105
+ # cooler.
106
+ # Version 0.1.0:: Added a <tt>depth</tt> method.
107
+ # Version 1.0.0:: Added <tt>top</tt> and made <tt>pop</tt> return nil
108
+ # (<tt>pop</tt> used to return the old top item).
109
+ # Version 1.1.0:: <tt>push</tt> now returns the value pushed (it used it
110
+ # return nil).
111
+ # Version 1.1.1:: Fixed a bug in the linked list implementation.
112
+ # Version 1.1.2:: Fixed a bug introduced in the last fix.
113
+ #
114
+ # Client A needs a stack with basic push/pop capability. They write to the
115
+ # original interface (no <tt>top</tt>), so their version constraint looks like:
116
+ #
117
+ # gem 'stack', '>= 0.0'
118
+ #
119
+ # Essentially, any version is OK with Client A. An incompatible change to
120
+ # the library will cause them grief, but they are willing to take the chance
121
+ # (we call Client A optimistic).
122
+ #
123
+ # Client B is just like Client A except for two things: (1) They use the
124
+ # <tt>depth</tt> method and (2) they are worried about future
125
+ # incompatibilities, so they write their version constraint like this:
126
+ #
127
+ # gem 'stack', '~> 0.1'
128
+ #
129
+ # The <tt>depth</tt> method was introduced in version 0.1.0, so that version
130
+ # or anything later is fine, as long as the version stays below version 1.0
131
+ # where incompatibilities are introduced. We call Client B pessimistic
132
+ # because they are worried about incompatible future changes (it is OK to be
133
+ # pessimistic!).
134
+ #
135
+ # == Preventing Version Catastrophe:
136
+ #
137
+ # From: http://blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.html
138
+ #
139
+ # Let's say you're depending on the fnord gem version 2.y.z. If you
140
+ # specify your dependency as ">= 2.0.0" then, you're good, right? What
141
+ # happens if fnord 3.0 comes out and it isn't backwards compatible
142
+ # with 2.y.z? Your stuff will break as a result of using ">=". The
143
+ # better route is to specify your dependency with an "approximate" version
144
+ # specifier ("~>"). They're a tad confusing, so here is how the dependency
145
+ # specifiers work:
146
+ #
147
+ # Specification From ... To (exclusive)
148
+ # ">= 3.0" 3.0 ... &infin;
149
+ # "~> 3.0" 3.0 ... 4.0
150
+ # "~> 3.0.0" 3.0.0 ... 3.1
151
+ # "~> 3.5" 3.5 ... 4.0
152
+ # "~> 3.5.0" 3.5.0 ... 3.6
153
+ # "~> 3" 3.0 ... 4.0
154
+ #
155
+ # For the last example, single-digit versions are automatically extended with
156
+ # a zero to give a sensible result.
157
+
158
+ class Gel::Support::GemVersion
159
+ include Comparable
160
+
161
+ VERSION_PATTERN = '[0-9]+(?>\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
162
+ ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/ # :nodoc:
163
+
164
+ ##
165
+ # A string representation of this Version.
166
+
167
+ def version
168
+ @version.dup
169
+ end
170
+
171
+ alias to_s version
172
+
173
+ ##
174
+ # True if the +version+ string matches RubyGems' requirements.
175
+
176
+ def self.correct? version
177
+ !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
178
+ end
179
+
180
+ ##
181
+ # Factory method to create a Version object. Input may be a Version
182
+ # or a String. Intended to simplify client code.
183
+ #
184
+ # ver1 = Version.create('1.3.17') # -> (Version object)
185
+ # ver2 = Version.create(ver1) # -> (ver1)
186
+ # ver3 = Version.create(nil) # -> nil
187
+
188
+ def self.create input
189
+ if self === input then # check yourself before you wreck yourself
190
+ input
191
+ elsif input.nil? then
192
+ nil
193
+ else
194
+ new input
195
+ end
196
+ end
197
+
198
+ @@all = {}
199
+
200
+ def self.new version # :nodoc:
201
+ return super unless Gel::Support::GemVersion == self
202
+
203
+ @@all[version] ||= super
204
+ end
205
+
206
+ ##
207
+ # Constructs a Version from the +version+ string. A version string is a
208
+ # series of digits or ASCII letters separated by dots.
209
+
210
+ def initialize version
211
+ version = version.to_s.strip
212
+
213
+ unless self.class.correct?(version)
214
+ raise ArgumentError, "Malformed version number string #{version}"
215
+ end
216
+
217
+ # If version is an empty string convert it to 0
218
+ version = "0" if version.empty?
219
+
220
+ @version = version.gsub("-",".pre.")
221
+ @segments = nil
222
+ end
223
+
224
+ ##
225
+ # Return a new version object where the next to the last revision
226
+ # number is one greater (e.g., 5.3.1 => 5.4).
227
+ #
228
+ # Pre-release (alpha) parts, e.g, 5.3.1.b.2 => 5.4, are ignored.
229
+
230
+ def bump
231
+ @bump ||= begin
232
+ segments = self.segments
233
+ segments.pop while segments.any? { |s| String === s }
234
+ segments.pop if segments.size > 1
235
+
236
+ segments[-1] = segments[-1].succ
237
+ self.class.new segments.join(".")
238
+ end
239
+ end
240
+
241
+ ##
242
+ # A Version is only eql? to another version if it's specified to the
243
+ # same precision. Version "1.0" is not the same as version "1".
244
+
245
+ def eql? other
246
+ self.class === other and @version == other._version
247
+ end
248
+
249
+ def hash # :nodoc:
250
+ canonical_segments.hash
251
+ end
252
+
253
+ def init_with coder # :nodoc:
254
+ yaml_initialize coder.tag, coder.map
255
+ end
256
+
257
+ def inspect # :nodoc:
258
+ "#<#{self.class} #{version.inspect}>"
259
+ end
260
+
261
+ ##
262
+ # Dump only the raw version string, not the complete object. It's a
263
+ # string for backwards (RubyGems 1.3.5 and earlier) compatibility.
264
+
265
+ def marshal_dump
266
+ [version]
267
+ end
268
+
269
+ ##
270
+ # Load custom marshal format. It's a string for backwards (RubyGems
271
+ # 1.3.5 and earlier) compatibility.
272
+
273
+ def marshal_load array
274
+ initialize array[0]
275
+ end
276
+
277
+ def yaml_initialize(tag, map) # :nodoc:
278
+ @version = map['version']
279
+ @segments = nil
280
+ @hash = nil
281
+ end
282
+
283
+ def to_yaml_properties # :nodoc:
284
+ ["@version"]
285
+ end
286
+
287
+ def encode_with coder # :nodoc:
288
+ coder.add 'version', @version
289
+ end
290
+
291
+ ##
292
+ # A version is considered a prerelease if it contains a letter.
293
+
294
+ def prerelease?
295
+ unless instance_variable_defined? :@prerelease
296
+ @prerelease = !!(@version =~ /[a-zA-Z]/)
297
+ end
298
+ @prerelease
299
+ end
300
+
301
+ def pretty_print q # :nodoc:
302
+ q.text "Gel::Support::GemVersion.new(#{version.inspect})"
303
+ end
304
+
305
+ ##
306
+ # The release for this version (e.g. 1.2.0.a -> 1.2.0).
307
+ # Non-prerelease versions return themselves.
308
+
309
+ def release
310
+ @release ||= if prerelease?
311
+ segments = self.segments
312
+ segments.pop while segments.any? { |s| String === s }
313
+ self.class.new segments.join('.')
314
+ else
315
+ self
316
+ end
317
+ end
318
+
319
+ def segments # :nodoc:
320
+ _segments.dup
321
+ end
322
+
323
+ ##
324
+ # A recommended version for use with a ~> Requirement.
325
+
326
+ def approximate_recommendation
327
+ segments = self.segments
328
+
329
+ segments.pop while segments.any? { |s| String === s }
330
+ segments.pop while segments.size > 2
331
+ segments.push 0 while segments.size < 2
332
+
333
+ "~> #{segments.join(".")}"
334
+ end
335
+
336
+ ##
337
+ # Compares this version with +other+ returning -1, 0, or 1 if the
338
+ # other version is larger, the same, or smaller than this
339
+ # one. Attempts to compare to something that's not a
340
+ # <tt>Gem::Version</tt> return +nil+.
341
+
342
+ def <=> other
343
+ return unless Gel::Support::GemVersion === other
344
+ return 0 if @version == other._version || canonical_segments == other.canonical_segments
345
+
346
+ lhsegments = _segments
347
+ rhsegments = other._segments
348
+
349
+ lhsize = lhsegments.size
350
+ rhsize = rhsegments.size
351
+ limit = (lhsize > rhsize ? lhsize : rhsize) - 1
352
+
353
+ i = 0
354
+
355
+ while i <= limit
356
+ lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
357
+ i += 1
358
+
359
+ next if lhs == rhs
360
+ return -1 if String === lhs && Numeric === rhs
361
+ return 1 if Numeric === lhs && String === rhs
362
+
363
+ return lhs <=> rhs
364
+ end
365
+
366
+ return 0
367
+ end
368
+
369
+ def canonical_segments
370
+ @canonical_segments ||=
371
+ _split_segments.map! do |segments|
372
+ segments.reverse_each.drop_while {|s| s == 0 }.reverse
373
+ end.reduce(&:concat)
374
+ end
375
+
376
+ protected
377
+
378
+ def _version
379
+ @version
380
+ end
381
+
382
+ def _segments
383
+ # segments is lazy so it can pick up version values that come from
384
+ # old marshaled versions, which don't go through marshal_load.
385
+ # since this version object is cached in @@all, its @segments should be frozen
386
+
387
+ @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
388
+ /^\d+$/ =~ s ? s.to_i : s
389
+ end.freeze
390
+ end
391
+
392
+ def _split_segments
393
+ string_start = _segments.index {|s| s.is_a?(String) }
394
+ string_segments = segments
395
+ numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
396
+ return numeric_segments, string_segments
397
+ end
398
+ end
@@ -0,0 +1,13 @@
1
+ module Gel::Support
2
+ module Tar
3
+ class Error < ::RuntimeError; end
4
+
5
+ class NonSeekableIO < Error; end
6
+ class TooLongFileName < Error; end
7
+ class TarInvalidError < Error; end
8
+ end
9
+ end
10
+
11
+ require_relative "tar/tar_header"
12
+ require_relative "tar/tar_reader"
13
+ require_relative "tar/tar_writer"
@@ -0,0 +1,229 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ #--
4
+ # Copyright (C) 2004 Mauricio Julio Fernández Pradier
5
+ # Redistributed under the terms of the MIT license.
6
+ #++
7
+
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
+ #++
29
+ # A header for a tar file
30
+
31
+ class Gel::Support::Tar::TarHeader
32
+
33
+ ##
34
+ # Fields in the tar header
35
+
36
+ FIELDS = [
37
+ :checksum,
38
+ :devmajor,
39
+ :devminor,
40
+ :gid,
41
+ :gname,
42
+ :linkname,
43
+ :magic,
44
+ :mode,
45
+ :mtime,
46
+ :name,
47
+ :prefix,
48
+ :size,
49
+ :typeflag,
50
+ :uid,
51
+ :uname,
52
+ :version,
53
+ ]
54
+
55
+ ##
56
+ # Pack format for a tar header
57
+
58
+ PACK_FORMAT = 'a100' + # name
59
+ 'a8' + # mode
60
+ 'a8' + # uid
61
+ 'a8' + # gid
62
+ 'a12' + # size
63
+ 'a12' + # mtime
64
+ 'a7a' + # chksum
65
+ 'a' + # typeflag
66
+ 'a100' + # linkname
67
+ 'a6' + # magic
68
+ 'a2' + # version
69
+ 'a32' + # uname
70
+ 'a32' + # gname
71
+ 'a8' + # devmajor
72
+ 'a8' + # devminor
73
+ 'a155' # prefix
74
+
75
+ ##
76
+ # Unpack format for a tar header
77
+
78
+ UNPACK_FORMAT = 'A100' + # name
79
+ 'A8' + # mode
80
+ 'A8' + # uid
81
+ 'A8' + # gid
82
+ 'A12' + # size
83
+ 'A12' + # mtime
84
+ 'A8' + # checksum
85
+ 'A' + # typeflag
86
+ 'A100' + # linkname
87
+ 'A6' + # magic
88
+ 'A2' + # version
89
+ 'A32' + # uname
90
+ 'A32' + # gname
91
+ 'A8' + # devmajor
92
+ 'A8' + # devminor
93
+ 'A155' # prefix
94
+
95
+ attr_reader(*FIELDS)
96
+
97
+ ##
98
+ # Creates a tar header from IO +stream+
99
+
100
+ def self.from(stream)
101
+ header = stream.read 512
102
+ empty = (header == "\0" * 512)
103
+
104
+ fields = header.unpack UNPACK_FORMAT
105
+
106
+ new :name => fields.shift,
107
+ :mode => fields.shift.oct,
108
+ :uid => fields.shift.oct,
109
+ :gid => fields.shift.oct,
110
+ :size => fields.shift.oct,
111
+ :mtime => fields.shift.oct,
112
+ :checksum => fields.shift.oct,
113
+ :typeflag => fields.shift,
114
+ :linkname => fields.shift,
115
+ :magic => fields.shift,
116
+ :version => fields.shift.oct,
117
+ :uname => fields.shift,
118
+ :gname => fields.shift,
119
+ :devmajor => fields.shift.oct,
120
+ :devminor => fields.shift.oct,
121
+ :prefix => fields.shift,
122
+
123
+ :empty => empty
124
+ end
125
+
126
+ ##
127
+ # Creates a new TarHeader using +vals+
128
+
129
+ def initialize(vals)
130
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
131
+ raise ArgumentError, ":name, :size, :prefix and :mode required"
132
+ end
133
+
134
+ vals[:uid] ||= 0
135
+ vals[:gid] ||= 0
136
+ vals[:mtime] ||= 0
137
+ vals[:checksum] ||= ""
138
+ vals[:typeflag] = "0" if vals[:typeflag].nil? || vals[:typeflag].empty?
139
+ vals[:magic] ||= "ustar"
140
+ vals[:version] ||= "00"
141
+ vals[:uname] ||= "wheel"
142
+ vals[:gname] ||= "wheel"
143
+ vals[:devmajor] ||= 0
144
+ vals[:devminor] ||= 0
145
+
146
+ FIELDS.each do |name|
147
+ instance_variable_set "@#{name}", vals[name]
148
+ end
149
+
150
+ @empty = vals[:empty]
151
+ end
152
+
153
+ ##
154
+ # Is the tar entry empty?
155
+
156
+ def empty?
157
+ @empty
158
+ end
159
+
160
+ def ==(other) # :nodoc:
161
+ self.class === other and
162
+ @checksum == other.checksum and
163
+ @devmajor == other.devmajor and
164
+ @devminor == other.devminor and
165
+ @gid == other.gid and
166
+ @gname == other.gname and
167
+ @linkname == other.linkname and
168
+ @magic == other.magic and
169
+ @mode == other.mode and
170
+ @mtime == other.mtime and
171
+ @name == other.name and
172
+ @prefix == other.prefix and
173
+ @size == other.size and
174
+ @typeflag == other.typeflag and
175
+ @uid == other.uid and
176
+ @uname == other.uname and
177
+ @version == other.version
178
+ end
179
+
180
+ def to_s # :nodoc:
181
+ update_checksum
182
+ header
183
+ end
184
+
185
+ ##
186
+ # Updates the TarHeader's checksum
187
+
188
+ def update_checksum
189
+ header = header " " * 8
190
+ @checksum = oct calculate_checksum(header), 6
191
+ end
192
+
193
+ private
194
+
195
+ def calculate_checksum(header)
196
+ header.unpack("C*").inject { |a, b| a + b }
197
+ end
198
+
199
+ def header(checksum = @checksum)
200
+ header = [
201
+ name,
202
+ oct(mode, 7),
203
+ oct(uid, 7),
204
+ oct(gid, 7),
205
+ oct(size, 11),
206
+ oct(mtime, 11),
207
+ checksum,
208
+ " ",
209
+ typeflag,
210
+ linkname,
211
+ magic,
212
+ oct(version, 2),
213
+ uname,
214
+ gname,
215
+ oct(devmajor, 7),
216
+ oct(devminor, 7),
217
+ prefix
218
+ ]
219
+
220
+ header = header.pack PACK_FORMAT
221
+
222
+ header << ("\0" * ((512 - header.size) % 512))
223
+ end
224
+
225
+ def oct(num, len)
226
+ "%0#{len}o" % num
227
+ end
228
+
229
+ end