Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / rubygems / package / tar_writer.rb
blob6e11440e22364c574c58dfcea75e2afe78c2047f
1 #++
2 # Copyright (C) 2004 Mauricio Julio Fernández Pradier
3 # See LICENSE.txt for additional licensing information.
4 #--
6 require 'rubygems/package'
8 class Gem::Package::TarWriter
10   class FileOverflow < StandardError; end
12   class BoundedStream
14     attr_reader :limit, :written
16     def initialize(io, limit)
17       @io = io
18       @limit = limit
19       @written = 0
20     end
22     def write(data)
23       if data.size + @written > @limit
24         raise FileOverflow, "You tried to feed more data than fits in the file."
25       end
26       @io.write data
27       @written += data.size
28       data.size
29     end
31   end
33   class RestrictedStream
35     def initialize(io)
36       @io = io
37     end
39     def write(data)
40       @io.write data
41     end
43   end
45   def self.new(io)
46     writer = super
48     return writer unless block_given?
50     begin
51       yield writer
52     ensure
53       writer.close
54     end
56     nil
57   end
59   def initialize(io)
60     @io = io
61     @closed = false
62   end
64   def add_file(name, mode)
65     check_closed
67     raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
69     name, prefix = split_name name
71     init_pos = @io.pos
72     @io.write "\0" * 512 # placeholder for the header
74     yield RestrictedStream.new(@io) if block_given?
76     size = @io.pos - init_pos - 512
78     remainder = (512 - (size % 512)) % 512
79     @io.write "\0" * remainder
81     final_pos = @io.pos
82     @io.pos = init_pos
84     header = Gem::Package::TarHeader.new :name => name, :mode => mode,
85                                          :size => size, :prefix => prefix
87     @io.write header
88     @io.pos = final_pos
90     self
91   end
93   def add_file_simple(name, mode, size)
94     check_closed
96     name, prefix = split_name name
98     header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
99                                          :size => size, :prefix => prefix).to_s
101     @io.write header
102     os = BoundedStream.new @io, size
104     yield os if block_given?
106     min_padding = size - os.written
107     @io.write("\0" * min_padding)
109     remainder = (512 - (size % 512)) % 512
110     @io.write("\0" * remainder)
112     self
113   end
115   def check_closed
116     raise IOError, "closed #{self.class}" if closed?
117   end
119   def close
120     check_closed
122     @io.write "\0" * 1024
123     flush
125     @closed = true
126   end
128   def closed?
129     @closed
130   end
132   def flush
133     check_closed
135     @io.flush if @io.respond_to? :flush
136   end
138   def mkdir(name, mode)
139     check_closed
141     name, prefix = split_name(name)
143     header = Gem::Package::TarHeader.new :name => name, :mode => mode,
144                                          :typeflag => "5", :size => 0,
145                                          :prefix => prefix
147     @io.write header
149     self
150   end
152   def split_name(name) # :nodoc:
153     raise Gem::Package::TooLongFileName if name.size > 256
155     if name.size <= 100 then
156       prefix = ""
157     else
158       parts = name.split(/\//)
159       newname = parts.pop
160       nxt = ""
162       loop do
163         nxt = parts.pop
164         break if newname.size + 1 + nxt.size > 100
165         newname = nxt + "/" + newname
166       end
168       prefix = (parts + [nxt]).join "/"
169       name = newname
171       if name.size > 100 or prefix.size > 155 then
172         raise Gem::Package::TooLongFileName 
173       end
174     end
176     return name, prefix
177   end