diff options
Diffstat (limited to 'lib/rubygems/package/tar_writer.rb')
-rw-r--r-- | lib/rubygems/package/tar_writer.rb | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb new file mode 100644 index 0000000000..6e11440e22 --- /dev/null +++ b/lib/rubygems/package/tar_writer.rb @@ -0,0 +1,180 @@ +#++ +# Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier +# See LICENSE.txt for additional licensing information. +#-- + +require 'rubygems/package' + +class Gem::Package::TarWriter + + class FileOverflow < StandardError; end + + class BoundedStream + + attr_reader :limit, :written + + def initialize(io, limit) + @io = io + @limit = limit + @written = 0 + end + + def write(data) + if data.size + @written > @limit + raise FileOverflow, "You tried to feed more data than fits in the file." + end + @io.write data + @written += data.size + data.size + end + + end + + class RestrictedStream + + def initialize(io) + @io = io + end + + def write(data) + @io.write data + end + + end + + def self.new(io) + writer = super + + return writer unless block_given? + + begin + yield writer + ensure + writer.close + end + + nil + end + + def initialize(io) + @io = io + @closed = false + end + + def add_file(name, mode) + check_closed + + raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos= + + name, prefix = split_name name + + init_pos = @io.pos + @io.write "\0" * 512 # placeholder for the header + + yield RestrictedStream.new(@io) if block_given? + + size = @io.pos - init_pos - 512 + + remainder = (512 - (size % 512)) % 512 + @io.write "\0" * remainder + + final_pos = @io.pos + @io.pos = init_pos + + header = Gem::Package::TarHeader.new :name => name, :mode => mode, + :size => size, :prefix => prefix + + @io.write header + @io.pos = final_pos + + self + end + + def add_file_simple(name, mode, size) + check_closed + + name, prefix = split_name name + + header = Gem::Package::TarHeader.new(:name => name, :mode => mode, + :size => size, :prefix => prefix).to_s + + @io.write header + os = BoundedStream.new @io, size + + yield os if block_given? + + min_padding = size - os.written + @io.write("\0" * min_padding) + + remainder = (512 - (size % 512)) % 512 + @io.write("\0" * remainder) + + self + end + + def check_closed + raise IOError, "closed #{self.class}" if closed? + end + + def close + check_closed + + @io.write "\0" * 1024 + flush + + @closed = true + end + + def closed? + @closed + end + + def flush + check_closed + + @io.flush if @io.respond_to? :flush + end + + def mkdir(name, mode) + check_closed + + name, prefix = split_name(name) + + header = Gem::Package::TarHeader.new :name => name, :mode => mode, + :typeflag => "5", :size => 0, + :prefix => prefix + + @io.write header + + self + end + + def split_name(name) # :nodoc: + raise Gem::Package::TooLongFileName if name.size > 256 + + if name.size <= 100 then + prefix = "" + else + parts = name.split(/\//) + newname = parts.pop + nxt = "" + + loop do + nxt = parts.pop + break if newname.size + 1 + nxt.size > 100 + newname = nxt + "/" + newname + end + + prefix = (parts + [nxt]).join "/" + name = newname + + if name.size > 100 or prefix.size > 155 then + raise Gem::Package::TooLongFileName + end + end + + return name, prefix + end + +end + |