gel 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/exe/gel +13 -0
- data/lib/gel.rb +22 -0
- data/lib/gel/catalog.rb +153 -0
- data/lib/gel/catalog/common.rb +82 -0
- data/lib/gel/catalog/compact_index.rb +152 -0
- data/lib/gel/catalog/dependency_index.rb +125 -0
- data/lib/gel/catalog/legacy_index.rb +157 -0
- data/lib/gel/catalog/marshal_hacks.rb +16 -0
- data/lib/gel/command.rb +86 -0
- data/lib/gel/command/config.rb +11 -0
- data/lib/gel/command/env.rb +7 -0
- data/lib/gel/command/exec.rb +66 -0
- data/lib/gel/command/help.rb +7 -0
- data/lib/gel/command/install.rb +7 -0
- data/lib/gel/command/install_gem.rb +16 -0
- data/lib/gel/command/lock.rb +34 -0
- data/lib/gel/command/ruby.rb +10 -0
- data/lib/gel/command/shell_setup.rb +25 -0
- data/lib/gel/command/stub.rb +12 -0
- data/lib/gel/command/update.rb +11 -0
- data/lib/gel/compatibility.rb +4 -0
- data/lib/gel/compatibility/bundler.rb +54 -0
- data/lib/gel/compatibility/bundler/cli.rb +6 -0
- data/lib/gel/compatibility/bundler/friendly_errors.rb +3 -0
- data/lib/gel/compatibility/bundler/setup.rb +4 -0
- data/lib/gel/compatibility/rubygems.rb +192 -0
- data/lib/gel/compatibility/rubygems/command.rb +4 -0
- data/lib/gel/compatibility/rubygems/dependency_installer.rb +0 -0
- data/lib/gel/compatibility/rubygems/gem_runner.rb +6 -0
- data/lib/gel/config.rb +80 -0
- data/lib/gel/db.rb +294 -0
- data/lib/gel/direct_gem.rb +29 -0
- data/lib/gel/environment.rb +592 -0
- data/lib/gel/error.rb +104 -0
- data/lib/gel/gemfile_parser.rb +144 -0
- data/lib/gel/gemspec_parser.rb +95 -0
- data/lib/gel/git_catalog.rb +38 -0
- data/lib/gel/git_depot.rb +119 -0
- data/lib/gel/httpool.rb +148 -0
- data/lib/gel/installer.rb +251 -0
- data/lib/gel/lock_loader.rb +164 -0
- data/lib/gel/lock_parser.rb +64 -0
- data/lib/gel/locked_store.rb +126 -0
- data/lib/gel/multi_store.rb +96 -0
- data/lib/gel/package.rb +156 -0
- data/lib/gel/package/inspector.rb +23 -0
- data/lib/gel/package/installer.rb +267 -0
- data/lib/gel/path_catalog.rb +44 -0
- data/lib/gel/pinboard.rb +140 -0
- data/lib/gel/pub_grub/preference_strategy.rb +82 -0
- data/lib/gel/pub_grub/source.rb +153 -0
- data/lib/gel/runtime.rb +27 -0
- data/lib/gel/store.rb +205 -0
- data/lib/gel/store_catalog.rb +31 -0
- data/lib/gel/store_gem.rb +80 -0
- data/lib/gel/stub_set.rb +51 -0
- data/lib/gel/support/gem_platform.rb +225 -0
- data/lib/gel/support/gem_requirement.rb +264 -0
- data/lib/gel/support/gem_version.rb +398 -0
- data/lib/gel/support/tar.rb +13 -0
- data/lib/gel/support/tar/tar_header.rb +229 -0
- data/lib/gel/support/tar/tar_reader.rb +123 -0
- data/lib/gel/support/tar/tar_reader/entry.rb +154 -0
- data/lib/gel/support/tar/tar_writer.rb +339 -0
- data/lib/gel/tail_file.rb +205 -0
- data/lib/gel/version.rb +5 -0
- data/lib/gel/work_pool.rb +143 -0
- data/man/man1/gel-exec.1 +16 -0
- data/man/man1/gel-install.1 +16 -0
- data/man/man1/gel.1 +30 -0
- 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 ... ∞
|
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
|