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.

Files changed (96) hide show
  1. data/ChangeLog +2335 -0
  2. data/README +54 -0
  3. data/Rakefile +293 -0
  4. data/Releases +98 -0
  5. data/TODO +7 -0
  6. data/bin/gem +11 -0
  7. data/bin/gem_server +111 -0
  8. data/bin/generate_yaml_index.rb +58 -0
  9. data/bin/update_rubygems +18 -0
  10. data/doc/doc.css +73 -0
  11. data/doc/makedoc.rb +4 -0
  12. data/examples/application/an-app.gemspec +26 -0
  13. data/examples/application/bin/myapp +3 -0
  14. data/examples/application/lib/somefunctionality.rb +3 -0
  15. data/gemspecs/README +4 -0
  16. data/gemspecs/cgikit-1.1.0.gemspec +18 -0
  17. data/gemspecs/jabber4r.gemspec +26 -0
  18. data/gemspecs/linguistics.gemspec +22 -0
  19. data/gemspecs/ook.gemspec +21 -0
  20. data/gemspecs/progressbar.gemspec +22 -0
  21. data/gemspecs/redcloth.gemspec +22 -0
  22. data/gemspecs/rublog.gemspec +23 -0
  23. data/gemspecs/ruby-doom.gemspec +21 -0
  24. data/gemspecs/rubyjdwp.gemspec +21 -0
  25. data/gemspecs/statistics.gemspec +21 -0
  26. data/lib/rubygems.rb +353 -0
  27. data/lib/rubygems/builder.rb +54 -0
  28. data/lib/rubygems/cmd_manager.rb +127 -0
  29. data/lib/rubygems/command.rb +191 -0
  30. data/lib/rubygems/config_file.rb +57 -0
  31. data/lib/rubygems/doc_manager.rb +94 -0
  32. data/lib/rubygems/format.rb +65 -0
  33. data/lib/rubygems/gem_commands.rb +925 -0
  34. data/lib/rubygems/gem_runner.rb +23 -0
  35. data/lib/rubygems/installer.rb +621 -0
  36. data/lib/rubygems/loadpath_manager.rb +108 -0
  37. data/lib/rubygems/old_format.rb +150 -0
  38. data/lib/rubygems/open-uri.rb +604 -0
  39. data/lib/rubygems/package.rb +740 -0
  40. data/lib/rubygems/remote_installer.rb +499 -0
  41. data/lib/rubygems/rubygems_version.rb +6 -0
  42. data/lib/rubygems/source_index.rb +130 -0
  43. data/lib/rubygems/specification.rb +613 -0
  44. data/lib/rubygems/user_interaction.rb +176 -0
  45. data/lib/rubygems/validator.rb +148 -0
  46. data/lib/rubygems/version.rb +279 -0
  47. data/lib/ubygems.rb +4 -0
  48. data/pkgs/sources/lib/sources.rb +6 -0
  49. data/pkgs/sources/sources.gemspec +14 -0
  50. data/post-install.rb +75 -0
  51. data/redist/session.gem +433 -0
  52. data/scripts/buildtests.rb +25 -0
  53. data/scripts/gemdoc.rb +62 -0
  54. data/scripts/runtest.rb +17 -0
  55. data/scripts/specdoc.rb +164 -0
  56. data/setup.rb +1360 -0
  57. data/test/bogussources.rb +5 -0
  58. data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
  59. data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
  60. data/test/data/lib/code.rb +1 -0
  61. data/test/data/one/README.one +1 -0
  62. data/test/data/one/lib/one.rb +3 -0
  63. data/test/data/one/one.gemspec +17 -0
  64. data/test/data/one/one.yaml +40 -0
  65. data/test/functional.rb +145 -0
  66. data/test/gemenvironment.rb +45 -0
  67. data/test/gemutilities.rb +18 -0
  68. data/test/insure_session.rb +46 -0
  69. data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
  70. data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
  71. data/test/mockgemui.rb +45 -0
  72. data/test/onegem.rb +23 -0
  73. data/test/simple_gem.rb +66 -0
  74. data/test/test_builder.rb +13 -0
  75. data/test/test_cached_fetcher.rb +60 -0
  76. data/test/test_check_command.rb +28 -0
  77. data/test/test_command.rb +130 -0
  78. data/test/test_configfile.rb +36 -0
  79. data/test/test_format.rb +70 -0
  80. data/test/test_gemloadpaths.rb +45 -0
  81. data/test/test_gempaths.rb +115 -0
  82. data/test/test_loadmanager.rb +40 -0
  83. data/test/test_local_cache.rb +157 -0
  84. data/test/test_package.rb +600 -0
  85. data/test/test_parse_commands.rb +179 -0
  86. data/test/test_process_commands.rb +21 -0
  87. data/test/test_remote_fetcher.rb +162 -0
  88. data/test/test_remote_installer.rb +154 -0
  89. data/test/test_source_index.rb +58 -0
  90. data/test/test_specification.rb +286 -0
  91. data/test/test_validator.rb +53 -0
  92. data/test/test_version_comparison.rb +204 -0
  93. data/test/testgem.rc +6 -0
  94. data/test/user_capture.rb +1 -0
  95. data/test/yaml_data.rb +59 -0
  96. 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
+