rubygems-update 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

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
+