rubygems-update 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- data/ChangeLog +2335 -0
- data/README +54 -0
- data/Rakefile +293 -0
- data/Releases +98 -0
- data/TODO +7 -0
- data/bin/gem +11 -0
- data/bin/gem_server +111 -0
- data/bin/generate_yaml_index.rb +58 -0
- data/bin/update_rubygems +18 -0
- data/doc/doc.css +73 -0
- data/doc/makedoc.rb +4 -0
- data/examples/application/an-app.gemspec +26 -0
- data/examples/application/bin/myapp +3 -0
- data/examples/application/lib/somefunctionality.rb +3 -0
- data/gemspecs/README +4 -0
- data/gemspecs/cgikit-1.1.0.gemspec +18 -0
- data/gemspecs/jabber4r.gemspec +26 -0
- data/gemspecs/linguistics.gemspec +22 -0
- data/gemspecs/ook.gemspec +21 -0
- data/gemspecs/progressbar.gemspec +22 -0
- data/gemspecs/redcloth.gemspec +22 -0
- data/gemspecs/rublog.gemspec +23 -0
- data/gemspecs/ruby-doom.gemspec +21 -0
- data/gemspecs/rubyjdwp.gemspec +21 -0
- data/gemspecs/statistics.gemspec +21 -0
- data/lib/rubygems.rb +353 -0
- data/lib/rubygems/builder.rb +54 -0
- data/lib/rubygems/cmd_manager.rb +127 -0
- data/lib/rubygems/command.rb +191 -0
- data/lib/rubygems/config_file.rb +57 -0
- data/lib/rubygems/doc_manager.rb +94 -0
- data/lib/rubygems/format.rb +65 -0
- data/lib/rubygems/gem_commands.rb +925 -0
- data/lib/rubygems/gem_runner.rb +23 -0
- data/lib/rubygems/installer.rb +621 -0
- data/lib/rubygems/loadpath_manager.rb +108 -0
- data/lib/rubygems/old_format.rb +150 -0
- data/lib/rubygems/open-uri.rb +604 -0
- data/lib/rubygems/package.rb +740 -0
- data/lib/rubygems/remote_installer.rb +499 -0
- data/lib/rubygems/rubygems_version.rb +6 -0
- data/lib/rubygems/source_index.rb +130 -0
- data/lib/rubygems/specification.rb +613 -0
- data/lib/rubygems/user_interaction.rb +176 -0
- data/lib/rubygems/validator.rb +148 -0
- data/lib/rubygems/version.rb +279 -0
- data/lib/ubygems.rb +4 -0
- data/pkgs/sources/lib/sources.rb +6 -0
- data/pkgs/sources/sources.gemspec +14 -0
- data/post-install.rb +75 -0
- data/redist/session.gem +433 -0
- data/scripts/buildtests.rb +25 -0
- data/scripts/gemdoc.rb +62 -0
- data/scripts/runtest.rb +17 -0
- data/scripts/specdoc.rb +164 -0
- data/setup.rb +1360 -0
- data/test/bogussources.rb +5 -0
- data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
- data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
- data/test/data/lib/code.rb +1 -0
- data/test/data/one/README.one +1 -0
- data/test/data/one/lib/one.rb +3 -0
- data/test/data/one/one.gemspec +17 -0
- data/test/data/one/one.yaml +40 -0
- data/test/functional.rb +145 -0
- data/test/gemenvironment.rb +45 -0
- data/test/gemutilities.rb +18 -0
- data/test/insure_session.rb +46 -0
- data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
- data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
- data/test/mockgemui.rb +45 -0
- data/test/onegem.rb +23 -0
- data/test/simple_gem.rb +66 -0
- data/test/test_builder.rb +13 -0
- data/test/test_cached_fetcher.rb +60 -0
- data/test/test_check_command.rb +28 -0
- data/test/test_command.rb +130 -0
- data/test/test_configfile.rb +36 -0
- data/test/test_format.rb +70 -0
- data/test/test_gemloadpaths.rb +45 -0
- data/test/test_gempaths.rb +115 -0
- data/test/test_loadmanager.rb +40 -0
- data/test/test_local_cache.rb +157 -0
- data/test/test_package.rb +600 -0
- data/test/test_parse_commands.rb +179 -0
- data/test/test_process_commands.rb +21 -0
- data/test/test_remote_fetcher.rb +162 -0
- data/test/test_remote_installer.rb +154 -0
- data/test/test_source_index.rb +58 -0
- data/test/test_specification.rb +286 -0
- data/test/test_validator.rb +53 -0
- data/test/test_version_comparison.rb +204 -0
- data/test/testgem.rc +6 -0
- data/test/user_capture.rb +1 -0
- data/test/yaml_data.rb +59 -0
- metadata +151 -0
@@ -0,0 +1,740 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier
|
3
|
+
# See LICENSE.txt for additional licensing information.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
require 'yaml/syck'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'zlib'
|
10
|
+
require 'digest/md5'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'find'
|
13
|
+
require 'stringio'
|
14
|
+
|
15
|
+
require 'rubygems/specification'
|
16
|
+
|
17
|
+
module Gem
|
18
|
+
|
19
|
+
# Wrapper for FileUtils meant to provide logging and additional operations if
|
20
|
+
# needed.
|
21
|
+
class FileOperations
|
22
|
+
extend FileUtils
|
23
|
+
class << self
|
24
|
+
# additional methods not implemented in FileUtils
|
25
|
+
end
|
26
|
+
def initialize(logger = nil)
|
27
|
+
@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(meth, *args, &block)
|
31
|
+
case
|
32
|
+
when FileUtils.respond_to?(meth)
|
33
|
+
@logger.log "#{meth}: #{args}" if @logger
|
34
|
+
FileUtils.send meth, *args, &block
|
35
|
+
when FileOperations.respond_to?(meth)
|
36
|
+
@logger.log "#{meth}: #{args}" if @logger
|
37
|
+
FileOperations.send meth, *args, &block
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
module Package
|
46
|
+
|
47
|
+
class NonSeekableIO < StandardError; end
|
48
|
+
class ArgumentError < ::ArgumentError; end
|
49
|
+
class ClosedIO < StandardError; end
|
50
|
+
class BadCheckSum < StandardError; end
|
51
|
+
class TooLongFileName < StandardError; end
|
52
|
+
|
53
|
+
module FSyncDir
|
54
|
+
private
|
55
|
+
def fsync_dir(dirname)
|
56
|
+
# make sure this hits the disc
|
57
|
+
begin
|
58
|
+
dir = open(dirname, "r")
|
59
|
+
dir.fsync
|
60
|
+
rescue # ignore IOError if it's an unpatched (old) Ruby
|
61
|
+
ensure
|
62
|
+
dir.close if dir rescue nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class TarHeader
|
68
|
+
FIELDS = [:name, :mode, :uid, :gid, :size, :mtime, :checksum, :typeflag,
|
69
|
+
:linkname, :magic, :version, :uname, :gname, :devmajor,
|
70
|
+
:devminor, :prefix]
|
71
|
+
FIELDS.each {|x| attr_reader x}
|
72
|
+
|
73
|
+
def self.new_from_stream(stream)
|
74
|
+
data = stream.read(512)
|
75
|
+
fields = data.unpack( "A100" + # record name
|
76
|
+
"A8A8A8" + # mode, uid, gid
|
77
|
+
"A12A12" + # size, mtime
|
78
|
+
"A8A" + # checksum, typeflag
|
79
|
+
"A100" + # linkname
|
80
|
+
"A6A2" + # magic, version
|
81
|
+
"A32" + # uname
|
82
|
+
"A32" + # gname
|
83
|
+
"A8A8" + # devmajor, devminor
|
84
|
+
"A155" # prefix
|
85
|
+
)
|
86
|
+
name = fields.shift
|
87
|
+
mode = fields.shift.oct
|
88
|
+
uid = fields.shift.oct
|
89
|
+
gid = fields.shift.oct
|
90
|
+
size = fields.shift.oct
|
91
|
+
mtime = fields.shift.oct
|
92
|
+
checksum = fields.shift.oct
|
93
|
+
typeflag = fields.shift
|
94
|
+
linkname = fields.shift
|
95
|
+
magic = fields.shift
|
96
|
+
version = fields.shift.oct
|
97
|
+
uname = fields.shift
|
98
|
+
gname = fields.shift
|
99
|
+
devmajor = fields.shift.oct
|
100
|
+
devminor = fields.shift.oct
|
101
|
+
prefix = fields.shift
|
102
|
+
|
103
|
+
empty = (data == "\0" * 512)
|
104
|
+
|
105
|
+
new(:name=>name, :mode=>mode, :uid=>uid, :gid=>gid, :size=>size,
|
106
|
+
:mtime=>mtime, :checksum=>checksum, :typeflag=>typeflag, :magic=>magic,
|
107
|
+
:version=>version, :uname=>uname, :gname=>gname, :devmajor=>devmajor,
|
108
|
+
:devminor=>devminor, :prefix=>prefix, :empty => empty )
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(vals)
|
112
|
+
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
|
113
|
+
raise Package::ArgumentError
|
114
|
+
end
|
115
|
+
vals[:uid] ||= 0
|
116
|
+
vals[:gid] ||= 0
|
117
|
+
vals[:mtime] ||= 0
|
118
|
+
vals[:checksum] ||= ""
|
119
|
+
vals[:typeflag] ||= "0"
|
120
|
+
vals[:magic] ||= "ustar"
|
121
|
+
vals[:version] ||= "00"
|
122
|
+
vals[:uname] ||= "wheel"
|
123
|
+
vals[:gname] ||= "wheel"
|
124
|
+
vals[:devmajor] ||= 0
|
125
|
+
vals[:devminor] ||= 0
|
126
|
+
FIELDS.each {|x| instance_variable_set "@#{x.to_s}", vals[x]}
|
127
|
+
@empty = vals[:empty]
|
128
|
+
end
|
129
|
+
|
130
|
+
def empty?
|
131
|
+
@empty
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_s
|
135
|
+
update_checksum
|
136
|
+
header(checksum)
|
137
|
+
end
|
138
|
+
|
139
|
+
def update_checksum
|
140
|
+
h = header(" " * 8)
|
141
|
+
@checksum = oct(calculate_checksum(h), 6)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def oct(num, len)
|
146
|
+
"%0#{len}o" % num
|
147
|
+
end
|
148
|
+
|
149
|
+
def calculate_checksum(hdr)
|
150
|
+
hdr.unpack("C*").inject{|a,b| a+b}
|
151
|
+
end
|
152
|
+
|
153
|
+
def header(chksum)
|
154
|
+
# struct tarfile_entry_posix {
|
155
|
+
# char name[100]; # ASCII + (Z unless filled)
|
156
|
+
# char mode[8]; # 0 padded, octal, null
|
157
|
+
# char uid[8]; # ditto
|
158
|
+
# char gid[8]; # ditto
|
159
|
+
# char size[12]; # 0 padded, octal, null
|
160
|
+
# char mtime[12]; # 0 padded, octal, null
|
161
|
+
# char checksum[8]; # 0 padded, octal, null, space
|
162
|
+
# char typeflag[1]; # file: "0" dir: "5"
|
163
|
+
# char linkname[100]; # ASCII + (Z unless filled)
|
164
|
+
# char magic[6]; # "ustar\0"
|
165
|
+
# char version[2]; # "00"
|
166
|
+
# char uname[32]; # ASCIIZ
|
167
|
+
# char gname[32]; # ASCIIZ
|
168
|
+
# char devmajor[8]; # 0 padded, octal, null
|
169
|
+
# char devminor[8]; # o padded, octal, null
|
170
|
+
# char prefix[155]; # ASCII + (Z unless filled)
|
171
|
+
# };
|
172
|
+
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
173
|
+
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
174
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
175
|
+
str = arr.pack("a100a8a8a8a12a12" + # name, mode, uid, gid, size, mtime
|
176
|
+
"a7aaa100a6a2" + # chksum, typeflag, linkname, magic, version
|
177
|
+
"a32a32a8a8a155") # uname, gname, devmajor, devminor, prefix
|
178
|
+
str + "\0" * ((512 - str.size) % 512)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class TarWriter
|
183
|
+
class FileOverflow < StandardError; end
|
184
|
+
class BlockNeeded < StandardError; end
|
185
|
+
|
186
|
+
class BoundedStream
|
187
|
+
attr_reader :limit, :written
|
188
|
+
def initialize(io, limit)
|
189
|
+
@io = io
|
190
|
+
@limit = limit
|
191
|
+
@written = 0
|
192
|
+
end
|
193
|
+
|
194
|
+
def write(data)
|
195
|
+
if data.size + @written > @limit
|
196
|
+
raise FileOverflow,
|
197
|
+
"You tried to feed more data than fits in the file."
|
198
|
+
end
|
199
|
+
@io.write data
|
200
|
+
@written += data.size
|
201
|
+
data.size
|
202
|
+
end
|
203
|
+
end
|
204
|
+
class RestrictedStream
|
205
|
+
def initialize(anIO)
|
206
|
+
@io = anIO
|
207
|
+
end
|
208
|
+
|
209
|
+
def write(data)
|
210
|
+
@io.write data
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.new(anIO)
|
215
|
+
writer = super(anIO)
|
216
|
+
return writer unless block_given?
|
217
|
+
begin
|
218
|
+
yield writer
|
219
|
+
ensure
|
220
|
+
writer.close
|
221
|
+
end
|
222
|
+
nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def initialize(anIO)
|
226
|
+
@io = anIO
|
227
|
+
@closed = false
|
228
|
+
end
|
229
|
+
|
230
|
+
def add_file_simple(name, mode, size)
|
231
|
+
raise BlockNeeded unless block_given?
|
232
|
+
raise ClosedIO if @closed
|
233
|
+
name, prefix = split_name(name)
|
234
|
+
header = TarHeader.new(:name => name, :mode => mode,
|
235
|
+
:size => size, :prefix => prefix).to_s
|
236
|
+
@io.write header
|
237
|
+
os = BoundedStream.new(@io, size)
|
238
|
+
yield os
|
239
|
+
#FIXME: what if an exception is raised in the block?
|
240
|
+
min_padding = size - os.written
|
241
|
+
@io.write("\0" * min_padding)
|
242
|
+
remainder = (512 - (size % 512)) % 512
|
243
|
+
@io.write("\0" * remainder)
|
244
|
+
end
|
245
|
+
|
246
|
+
def add_file(name, mode)
|
247
|
+
raise BlockNeeded unless block_given?
|
248
|
+
raise ClosedIO if @closed
|
249
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
250
|
+
name, prefix = split_name(name)
|
251
|
+
init_pos = @io.pos
|
252
|
+
@io.write "\0" * 512 # placeholder for the header
|
253
|
+
yield RestrictedStream.new(@io)
|
254
|
+
#FIXME: what if an exception is raised in the block?
|
255
|
+
#FIXME: what if an exception is raised in the block?
|
256
|
+
size = @io.pos - init_pos - 512
|
257
|
+
remainder = (512 - (size % 512)) % 512
|
258
|
+
@io.write("\0" * remainder)
|
259
|
+
final_pos = @io.pos
|
260
|
+
@io.pos = init_pos
|
261
|
+
header = TarHeader.new(:name => name, :mode => mode,
|
262
|
+
:size => size, :prefix => prefix).to_s
|
263
|
+
@io.write header
|
264
|
+
@io.pos = final_pos
|
265
|
+
end
|
266
|
+
|
267
|
+
def mkdir(name, mode)
|
268
|
+
raise ClosedIO if @closed
|
269
|
+
name, prefix = split_name(name)
|
270
|
+
header = TarHeader.new(:name => name, :mode => mode, :typeflag => "5",
|
271
|
+
:size => 0, :prefix => prefix).to_s
|
272
|
+
@io.write header
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
|
276
|
+
def flush
|
277
|
+
raise ClosedIO if @closed
|
278
|
+
@io.flush if @io.respond_to? :flush
|
279
|
+
end
|
280
|
+
|
281
|
+
def close
|
282
|
+
#raise ClosedIO if @closed
|
283
|
+
return if @closed
|
284
|
+
@io.write "\0" * 1024
|
285
|
+
@closed = true
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
def split_name name
|
290
|
+
raise TooLongFileName if name.size > 256
|
291
|
+
if name.size <= 100
|
292
|
+
prefix = ""
|
293
|
+
else
|
294
|
+
parts = name.split(/\//)
|
295
|
+
newname = parts.pop
|
296
|
+
nxt = ""
|
297
|
+
loop do
|
298
|
+
nxt = parts.pop
|
299
|
+
break if newname.size + 1 + nxt.size > 100
|
300
|
+
newname = nxt + "/" + newname
|
301
|
+
end
|
302
|
+
prefix = (parts + [nxt]).join "/"
|
303
|
+
name = newname
|
304
|
+
raise TooLongFileName if name.size > 100 || prefix.size > 155
|
305
|
+
end
|
306
|
+
return name, prefix
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class TarReader
|
311
|
+
include Gem::Package
|
312
|
+
class UnexpectedEOF < StandardError; end
|
313
|
+
module InvalidEntry
|
314
|
+
def read(len=nil); raise ClosedIO; end
|
315
|
+
def getc; raise ClosedIO; end
|
316
|
+
def rewind; raise ClosedIO; end
|
317
|
+
end
|
318
|
+
class Entry
|
319
|
+
TarHeader::FIELDS.each{|x| attr_reader x}
|
320
|
+
|
321
|
+
def initialize(header, anIO)
|
322
|
+
@io = anIO
|
323
|
+
@name = header.name
|
324
|
+
@mode = header.mode
|
325
|
+
@uid = header.uid
|
326
|
+
@gid = header.gid
|
327
|
+
@size = header.size
|
328
|
+
@mtime = header.mtime
|
329
|
+
@checksum = header.checksum
|
330
|
+
@typeflag = header.typeflag
|
331
|
+
@linkname = header.linkname
|
332
|
+
@magic = header.magic
|
333
|
+
@version = header.version
|
334
|
+
@uname = header.uname
|
335
|
+
@gname = header.gname
|
336
|
+
@devmajor = header.devmajor
|
337
|
+
@devminor = header.devminor
|
338
|
+
@prefix = header.prefix
|
339
|
+
@read = 0
|
340
|
+
@orig_pos = @io.pos
|
341
|
+
end
|
342
|
+
|
343
|
+
def read(len = nil)
|
344
|
+
return nil if @read >= @size
|
345
|
+
len ||= @size - @read
|
346
|
+
max_read = [len, @size - @read].min
|
347
|
+
ret = @io.read(max_read)
|
348
|
+
@read += ret.size
|
349
|
+
ret
|
350
|
+
end
|
351
|
+
|
352
|
+
def getc
|
353
|
+
return nil if @read >= @size
|
354
|
+
ret = @io.getc
|
355
|
+
@read += 1 if ret
|
356
|
+
ret
|
357
|
+
end
|
358
|
+
|
359
|
+
def is_directory?
|
360
|
+
@typeflag == "5"
|
361
|
+
end
|
362
|
+
|
363
|
+
def is_file?
|
364
|
+
@typeflag == "0"
|
365
|
+
end
|
366
|
+
|
367
|
+
def eof?
|
368
|
+
@read >= @size
|
369
|
+
end
|
370
|
+
|
371
|
+
def pos
|
372
|
+
@read
|
373
|
+
end
|
374
|
+
|
375
|
+
def rewind
|
376
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
377
|
+
@io.pos = @orig_pos
|
378
|
+
@read = 0
|
379
|
+
end
|
380
|
+
|
381
|
+
alias_method :is_directory, :is_directory?
|
382
|
+
alias_method :is_file, :is_file
|
383
|
+
|
384
|
+
def bytes_read
|
385
|
+
@read
|
386
|
+
end
|
387
|
+
|
388
|
+
def full_name
|
389
|
+
if @prefix != ""
|
390
|
+
File.join(@prefix, @name)
|
391
|
+
else
|
392
|
+
@name
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def close
|
397
|
+
invalidate
|
398
|
+
end
|
399
|
+
|
400
|
+
private
|
401
|
+
def invalidate
|
402
|
+
extend InvalidEntry
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def self.new(anIO)
|
407
|
+
reader = super(anIO)
|
408
|
+
return reader unless block_given?
|
409
|
+
begin
|
410
|
+
yield reader
|
411
|
+
ensure
|
412
|
+
reader.close
|
413
|
+
end
|
414
|
+
nil
|
415
|
+
end
|
416
|
+
|
417
|
+
def initialize(anIO)
|
418
|
+
@io = anIO
|
419
|
+
@init_pos = anIO.pos
|
420
|
+
end
|
421
|
+
|
422
|
+
def each(&block)
|
423
|
+
each_entry(&block)
|
424
|
+
end
|
425
|
+
|
426
|
+
# do not call this during a #each or #each_entry iteration
|
427
|
+
def rewind
|
428
|
+
if @init_pos == 0
|
429
|
+
raise NonSeekableIO unless @io.respond_to? :rewind
|
430
|
+
@io.rewind
|
431
|
+
else
|
432
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
433
|
+
@io.pos = @init_pos
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def each_entry
|
438
|
+
loop do
|
439
|
+
return if @io.eof?
|
440
|
+
header = TarHeader.new_from_stream(@io)
|
441
|
+
return if header.empty?
|
442
|
+
entry = Entry.new header, @io
|
443
|
+
size = entry.size
|
444
|
+
yield entry
|
445
|
+
skip = (512 - (size % 512)) % 512
|
446
|
+
if @io.respond_to? :seek
|
447
|
+
# avoid reading...
|
448
|
+
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
449
|
+
else
|
450
|
+
pending = size - entry.bytes_read
|
451
|
+
while pending > 0
|
452
|
+
bread = @io.read([pending, 4096].min).size
|
453
|
+
raise UnexpectedEOF if @io.eof?
|
454
|
+
pending -= bread
|
455
|
+
end
|
456
|
+
end
|
457
|
+
@io.read(skip) # discard trailing zeros
|
458
|
+
# make sure nobody can use #read, #getc or #rewind anymore
|
459
|
+
entry.close
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def close
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
class TarInput
|
468
|
+
include FSyncDir
|
469
|
+
include Enumerable
|
470
|
+
attr_reader :metadata
|
471
|
+
class << self; private :new end
|
472
|
+
|
473
|
+
def initialize(io)
|
474
|
+
@io = io
|
475
|
+
@tarreader = TarReader.new @io
|
476
|
+
has_meta = false
|
477
|
+
@tarreader.each do |entry|
|
478
|
+
case entry.full_name
|
479
|
+
when "metadata"
|
480
|
+
# (GS) Changed to line below: @metadata = YAML.load(entry.read) rescue nil
|
481
|
+
@metadata = load_gemspec(entry.read)
|
482
|
+
has_meta = true
|
483
|
+
break
|
484
|
+
when "metadata.gz"
|
485
|
+
begin
|
486
|
+
gzis = Zlib::GzipReader.new entry
|
487
|
+
# YAML wants an instance of IO
|
488
|
+
# (GS) Changed to line below: @metadata = YAML.load(gzis) rescue nil
|
489
|
+
@metadata = load_gemspec(gzis)
|
490
|
+
has_meta = true
|
491
|
+
ensure
|
492
|
+
gzis.close
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
@tarreader.rewind
|
497
|
+
@fileops = FileOperations.new
|
498
|
+
raise RuntimeError, "No metadata found!" unless has_meta
|
499
|
+
end
|
500
|
+
|
501
|
+
# Attempt to YAML-load a gemspec from the given _io_ parameter. Return nil if it fails.
|
502
|
+
def load_gemspec(io)
|
503
|
+
Gem::Specification.from_yaml(io)
|
504
|
+
rescue Gem::Exception
|
505
|
+
nil
|
506
|
+
end
|
507
|
+
|
508
|
+
def self.open(filename, &block)
|
509
|
+
open_from_io(File.open(filename, "rb"), &block)
|
510
|
+
end
|
511
|
+
|
512
|
+
def self.open_from_io(io, &block)
|
513
|
+
raise "Want a block" unless block_given?
|
514
|
+
begin
|
515
|
+
is = new(io)
|
516
|
+
yield is
|
517
|
+
ensure
|
518
|
+
is.close if is
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def each(&block)
|
523
|
+
@tarreader.each do |entry|
|
524
|
+
next unless entry.full_name == "data.tar.gz"
|
525
|
+
is = zipped_stream(entry)
|
526
|
+
begin
|
527
|
+
TarReader.new(is) do |inner|
|
528
|
+
inner.each(&block)
|
529
|
+
end
|
530
|
+
ensure
|
531
|
+
is.close if is
|
532
|
+
end
|
533
|
+
end
|
534
|
+
@tarreader.rewind
|
535
|
+
end
|
536
|
+
|
537
|
+
# Return an IO stream for the zipped entry.
|
538
|
+
#
|
539
|
+
# If zlib is earlier than 1.2.1, then read the entry into memory
|
540
|
+
# and create a string IO object from it. This avoids a "buffer
|
541
|
+
# error" problem on windows when using an earlier version of zlib.
|
542
|
+
# This problem has not been observed in versions of zlib 1.2.1 or
|
543
|
+
# later.
|
544
|
+
def zipped_stream(entry)
|
545
|
+
if Zlib::ZLIB_VERSION < '1.2.1'
|
546
|
+
zis = Zlib::GzipReader.new entry
|
547
|
+
dis = zis.read
|
548
|
+
is = StringIO.new(dis)
|
549
|
+
else
|
550
|
+
is = Zlib::GzipReader.new entry
|
551
|
+
end
|
552
|
+
ensure
|
553
|
+
zis.finish if zis
|
554
|
+
end
|
555
|
+
|
556
|
+
def extract_entry(destdir, entry, expected_md5sum = nil)
|
557
|
+
if entry.is_directory?
|
558
|
+
dest = File.join(destdir, entry.full_name)
|
559
|
+
if file_class.dir? dest
|
560
|
+
@fileops.chmod entry.mode, dest, :verbose=>false
|
561
|
+
else
|
562
|
+
@fileops.mkdir_p(dest, :mode => entry.mode, :verbose=>false)
|
563
|
+
end
|
564
|
+
fsync_dir dest
|
565
|
+
fsync_dir File.join(dest, "..")
|
566
|
+
return
|
567
|
+
end
|
568
|
+
# it's a file
|
569
|
+
md5 = Digest::MD5.new if expected_md5sum
|
570
|
+
destdir = File.join(destdir, File.dirname(entry.full_name))
|
571
|
+
@fileops.mkdir_p(destdir, :mode => 0755, :verbose=>false)
|
572
|
+
destfile = File.join(destdir, File.basename(entry.full_name))
|
573
|
+
@fileops.chmod(0600, destfile, :verbose=>false) rescue nil # Errno::ENOENT
|
574
|
+
file_class.open(destfile, "wb", entry.mode) do |os|
|
575
|
+
loop do
|
576
|
+
data = entry.read(4096)
|
577
|
+
break unless data
|
578
|
+
md5 << data if expected_md5sum
|
579
|
+
os.write(data)
|
580
|
+
end
|
581
|
+
os.fsync
|
582
|
+
end
|
583
|
+
@fileops.chmod(entry.mode, destfile, :verbose=>false)
|
584
|
+
fsync_dir File.dirname(destfile)
|
585
|
+
fsync_dir File.join(File.dirname(destfile), "..")
|
586
|
+
if expected_md5sum && expected_md5sum != md5.hexdigest
|
587
|
+
raise BadCheckSum
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def close
|
592
|
+
@io.close
|
593
|
+
@tarreader.close
|
594
|
+
end
|
595
|
+
|
596
|
+
private
|
597
|
+
|
598
|
+
def file_class
|
599
|
+
File
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
class TarOutput
|
604
|
+
|
605
|
+
class << self; private :new end
|
606
|
+
|
607
|
+
class << self
|
608
|
+
|
609
|
+
end
|
610
|
+
|
611
|
+
def initialize(io)
|
612
|
+
@io = io
|
613
|
+
@external = TarWriter.new @io
|
614
|
+
end
|
615
|
+
|
616
|
+
def external_handle
|
617
|
+
@external
|
618
|
+
end
|
619
|
+
|
620
|
+
def self.open(filename, &block)
|
621
|
+
io = File.open(filename, "wb")
|
622
|
+
open_from_io(io, &block)
|
623
|
+
nil
|
624
|
+
end
|
625
|
+
|
626
|
+
def self.open_from_io(io, &block)
|
627
|
+
outputter = new(io)
|
628
|
+
metadata = nil
|
629
|
+
set_meta = lambda{|x| metadata = x}
|
630
|
+
raise "Want a block" unless block_given?
|
631
|
+
begin
|
632
|
+
outputter.external_handle.add_file("data.tar.gz", 0644) do |inner|
|
633
|
+
begin
|
634
|
+
os = Zlib::GzipWriter.new inner
|
635
|
+
TarWriter.new(os) do |inner_tar_stream|
|
636
|
+
klass = class <<inner_tar_stream; self end
|
637
|
+
klass.send(:define_method, :metadata=, &set_meta)
|
638
|
+
block.call inner_tar_stream
|
639
|
+
end
|
640
|
+
ensure
|
641
|
+
os.flush
|
642
|
+
os.finish
|
643
|
+
#os.close
|
644
|
+
end
|
645
|
+
end
|
646
|
+
outputter.external_handle.add_file("metadata.gz", 0644) do |os|
|
647
|
+
begin
|
648
|
+
gzos = Zlib::GzipWriter.new os
|
649
|
+
gzos.write metadata
|
650
|
+
ensure
|
651
|
+
gzos.flush
|
652
|
+
gzos.finish
|
653
|
+
end
|
654
|
+
end
|
655
|
+
ensure
|
656
|
+
outputter.close
|
657
|
+
end
|
658
|
+
nil
|
659
|
+
end
|
660
|
+
|
661
|
+
def close
|
662
|
+
@external.close
|
663
|
+
@io.close
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end # module Package
|
667
|
+
|
668
|
+
|
669
|
+
module Package
|
670
|
+
#FIXME: refactor the following 2 methods
|
671
|
+
def self.open(dest, mode = "r", &block)
|
672
|
+
raise "Block needed" unless block_given?
|
673
|
+
|
674
|
+
case mode
|
675
|
+
when "r"
|
676
|
+
TarInput.open(dest, &block)
|
677
|
+
when "w"
|
678
|
+
TarOutput.open(dest, &block)
|
679
|
+
else
|
680
|
+
raise "Unknown Package open mode"
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
def self.open_from_io(io, mode = "r", &block)
|
685
|
+
raise "Block needed" unless block_given?
|
686
|
+
|
687
|
+
case mode
|
688
|
+
when "r"
|
689
|
+
TarInput.open_from_io(io, &block)
|
690
|
+
when "w"
|
691
|
+
TarOutput.open_from_io(io, &block)
|
692
|
+
else
|
693
|
+
raise "Unknown Package open mode"
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
def self.pack(src, destname)
|
698
|
+
TarOutput.open(destname) do |outp|
|
699
|
+
dir_class.chdir(src) do
|
700
|
+
outp.metadata = (file_class.read("RPA/metadata") rescue nil)
|
701
|
+
find_class.find('.') do |entry|
|
702
|
+
case
|
703
|
+
when file_class.file?(entry)
|
704
|
+
entry.sub!(%r{\./}, "")
|
705
|
+
next if entry =~ /\ARPA\//
|
706
|
+
stat = File.stat(entry)
|
707
|
+
outp.add_file_simple(entry, stat.mode, stat.size) do |os|
|
708
|
+
file_class.open(entry, "rb") do |f|
|
709
|
+
os.write(f.read(4096)) until f.eof?
|
710
|
+
end
|
711
|
+
end
|
712
|
+
when file_class.dir?(entry)
|
713
|
+
entry.sub!(%r{\./}, "")
|
714
|
+
next if entry == "RPA"
|
715
|
+
outp.mkdir(entry, file_class.stat(entry).mode)
|
716
|
+
else
|
717
|
+
raise "Don't know how to pack this yet!"
|
718
|
+
end
|
719
|
+
end
|
720
|
+
end
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
class << self
|
725
|
+
def file_class
|
726
|
+
File
|
727
|
+
end
|
728
|
+
|
729
|
+
def dir_class
|
730
|
+
Dir
|
731
|
+
end
|
732
|
+
|
733
|
+
def find_class
|
734
|
+
Find
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
end
|
740
|
+
|