minitar 0.5.4 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Code-of-Conduct.md +74 -0
- data/Contributing.md +84 -0
- data/History.md +107 -0
- data/Licence.md +15 -0
- data/Manifest.txt +24 -0
- data/README.rdoc +81 -0
- data/Rakefile +46 -107
- data/docs/bsdl.txt +19 -0
- data/docs/ruby.txt +56 -0
- data/lib/archive-tar-minitar.rb +3 -0
- data/lib/archive/tar/minitar.rb +197 -889
- data/lib/archive/tar/minitar/input.rb +212 -0
- data/lib/archive/tar/minitar/output.rb +69 -0
- data/lib/archive/tar/minitar/posix_header.rb +259 -0
- data/lib/archive/tar/minitar/reader.rb +237 -0
- data/lib/archive/tar/minitar/writer.rb +297 -0
- data/lib/minitar.rb +12 -0
- data/test/minitest_helper.rb +11 -0
- data/test/support/tar_test_helpers.rb +119 -0
- data/test/test_tar_header.rb +74 -0
- data/test/test_tar_input.rb +167 -0
- data/test/test_tar_output.rb +53 -0
- data/test/test_tar_reader.rb +148 -0
- data/test/test_tar_writer.rb +190 -0
- metadata +232 -43
- data/ChangeLog +0 -17
- data/Install +0 -6
- data/README +0 -68
- data/bin/minitar +0 -27
- data/lib/archive/tar/minitar/command.rb +0 -814
- data/tests/tc_tar.rb +0 -629
- data/tests/testall.rb +0 -10
@@ -0,0 +1,237 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Archive::Tar::Minitar
|
4
|
+
# The class that reads a tar format archive from a data stream. The data
|
5
|
+
# stream may be sequential or random access, but certain features only work
|
6
|
+
# with random access data streams.
|
7
|
+
class Reader
|
8
|
+
# This marks the EntryStream closed for reading without closing the
|
9
|
+
# actual data stream.
|
10
|
+
module InvalidEntryStream
|
11
|
+
# rubocop:disable Style/SingleLineMethods
|
12
|
+
# rubocop:disable Style/EmptyLineBetweenDefs
|
13
|
+
def read(*); raise ClosedStream; end
|
14
|
+
def getc; raise ClosedStream; end
|
15
|
+
def rewind; raise ClosedStream; end
|
16
|
+
# rubocop:enable Style/EmptyLineBetweenDefs
|
17
|
+
# rubocop:enable Style/SingleLineMethods Style/EmptyLineBetweenDefs
|
18
|
+
end
|
19
|
+
|
20
|
+
# EntryStreams are pseudo-streams on top of the main data stream.
|
21
|
+
class EntryStream
|
22
|
+
Archive::Tar::Minitar::PosixHeader::FIELDS.each do |field|
|
23
|
+
attr_reader field.to_sym
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(header, io)
|
27
|
+
@io = io
|
28
|
+
@name = header.name
|
29
|
+
@mode = header.mode
|
30
|
+
@uid = header.uid
|
31
|
+
@gid = header.gid
|
32
|
+
@size = header.size
|
33
|
+
@mtime = header.mtime
|
34
|
+
@checksum = header.checksum
|
35
|
+
@typeflag = header.typeflag
|
36
|
+
@linkname = header.linkname
|
37
|
+
@magic = header.magic
|
38
|
+
@version = header.version
|
39
|
+
@uname = header.uname
|
40
|
+
@gname = header.gname
|
41
|
+
@devmajor = header.devmajor
|
42
|
+
@devminor = header.devminor
|
43
|
+
@prefix = header.prefix
|
44
|
+
@read = 0
|
45
|
+
@orig_pos =
|
46
|
+
if Archive::Tar::Minitar.seekable?(@io)
|
47
|
+
@io.pos
|
48
|
+
else
|
49
|
+
0
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Reads +len+ bytes (or all remaining data) from the entry. Returns
|
54
|
+
# +nil+ if there is no more data to read.
|
55
|
+
def read(len = nil)
|
56
|
+
return nil if @read >= @size
|
57
|
+
len ||= @size - @read
|
58
|
+
max_read = [len, @size - @read].min
|
59
|
+
ret = @io.read(max_read)
|
60
|
+
@read += ret.size
|
61
|
+
ret
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reads one byte from the entry. Returns +nil+ if there is no more data
|
65
|
+
# to read.
|
66
|
+
def getc
|
67
|
+
return nil if @read >= @size
|
68
|
+
ret = @io.getc
|
69
|
+
@read += 1 if ret
|
70
|
+
ret
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns +true+ if the entry represents a directory.
|
74
|
+
def directory?
|
75
|
+
@typeflag == '5'
|
76
|
+
end
|
77
|
+
alias directory directory?
|
78
|
+
|
79
|
+
# Returns +true+ if the entry represents a plain file.
|
80
|
+
def file?
|
81
|
+
@typeflag == '0' || @typeflag == "\0"
|
82
|
+
end
|
83
|
+
alias file file?
|
84
|
+
|
85
|
+
# Returns +true+ if the current read pointer is at the end of the
|
86
|
+
# EntryStream data.
|
87
|
+
def eof?
|
88
|
+
@read >= @size
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the current read pointer in the EntryStream.
|
92
|
+
def pos
|
93
|
+
@read
|
94
|
+
end
|
95
|
+
|
96
|
+
# Sets the current read pointer to the beginning of the EntryStream.
|
97
|
+
def rewind
|
98
|
+
unless Archive::Tar::Minitar.seekable?(@io, :pos=)
|
99
|
+
raise Archive::Tar::Minitar::NonSeekableStream
|
100
|
+
end
|
101
|
+
@io.pos = @orig_pos
|
102
|
+
@read = 0
|
103
|
+
end
|
104
|
+
|
105
|
+
def bytes_read
|
106
|
+
@read
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the full and proper name of the entry.
|
110
|
+
def full_name
|
111
|
+
if @prefix != ''
|
112
|
+
File.join(@prefix, @name)
|
113
|
+
else
|
114
|
+
@name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Closes the entry.
|
119
|
+
def close
|
120
|
+
invalidate
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def invalidate
|
126
|
+
extend InvalidEntryStream
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# With no associated block, +Reader::open+ is a synonym for
|
131
|
+
# +Reader::new+. If the optional code block is given, it will be passed
|
132
|
+
# the new _writer_ as an argument and the Reader object will
|
133
|
+
# automatically be closed when the block terminates. In this instance,
|
134
|
+
# +Reader::open+ returns the value of the block.
|
135
|
+
def self.open(io)
|
136
|
+
reader = new(io)
|
137
|
+
return reader unless block_given?
|
138
|
+
yield reader
|
139
|
+
ensure
|
140
|
+
reader.close
|
141
|
+
end
|
142
|
+
|
143
|
+
# Iterates over each entry in the provided input. This wraps the common
|
144
|
+
# pattern of:
|
145
|
+
#
|
146
|
+
# Archive::Tar::Minitar::Input.open(io) do |i|
|
147
|
+
# inp.each do |entry|
|
148
|
+
# # ...
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# If a block is not provided, an enumerator will be created with the same
|
153
|
+
# behaviour.
|
154
|
+
#
|
155
|
+
# call-seq:
|
156
|
+
# Archive::Tar::Minitar::Reader.each_entry(io) -> enumerator
|
157
|
+
# Archive::Tar::Minitar::Reader.each_entry(io) { |entry| block } -> obj
|
158
|
+
def self.each_entry(io)
|
159
|
+
return to_enum(__method__, io) unless block_given?
|
160
|
+
|
161
|
+
open(io) do |reader|
|
162
|
+
reader.each_entry do |entry|
|
163
|
+
yield entry
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Creates and returns a new Reader object.
|
169
|
+
def initialize(io)
|
170
|
+
@io = io
|
171
|
+
@init_pos = io.pos
|
172
|
+
end
|
173
|
+
|
174
|
+
# Resets the read pointer to the beginning of data stream. Do not call
|
175
|
+
# this during a #each or #each_entry iteration. This only works with
|
176
|
+
# random access data streams that respond to #rewind and #pos.
|
177
|
+
def rewind
|
178
|
+
if @init_pos.zero?
|
179
|
+
unless Archive::Tar::Minitar.seekable?(@io, :rewind)
|
180
|
+
raise Archive::Tar::Minitar::NonSeekableStream
|
181
|
+
end
|
182
|
+
@io.rewind
|
183
|
+
else
|
184
|
+
unless Archive::Tar::Minitar.seekable?(@io, :pos=)
|
185
|
+
raise Archive::Tar::Minitar::NonSeekableStream
|
186
|
+
end
|
187
|
+
@io.pos = @init_pos
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Iterates through each entry in the data stream.
|
192
|
+
def each_entry
|
193
|
+
return to_enum unless block_given?
|
194
|
+
|
195
|
+
loop do
|
196
|
+
return if @io.eof?
|
197
|
+
|
198
|
+
header = Archive::Tar::Minitar::PosixHeader.from_stream(@io)
|
199
|
+
return if header.empty?
|
200
|
+
|
201
|
+
if header.long_name?
|
202
|
+
name = @io.read(512).rstrip
|
203
|
+
header = PosixHeader.from_stream(@io)
|
204
|
+
return if header.empty?
|
205
|
+
header.name = name
|
206
|
+
end
|
207
|
+
|
208
|
+
entry = EntryStream.new(header, @io)
|
209
|
+
size = entry.size
|
210
|
+
|
211
|
+
yield entry
|
212
|
+
|
213
|
+
skip = (512 - (size % 512)) % 512
|
214
|
+
|
215
|
+
if Archive::Tar::Minitar.seekable?(@io, :seek)
|
216
|
+
# avoid reading...
|
217
|
+
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
218
|
+
else
|
219
|
+
pending = size - entry.bytes_read
|
220
|
+
while pending > 0
|
221
|
+
bread = @io.read([pending, 4096].min).size
|
222
|
+
raise UnexpectedEOF if @io.eof?
|
223
|
+
pending -= bread
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
@io.read(skip) # discard trailing zeros
|
228
|
+
# make sure nobody can use #read, #getc or #rewind anymore
|
229
|
+
entry.close
|
230
|
+
end
|
231
|
+
end
|
232
|
+
alias each each_entry
|
233
|
+
|
234
|
+
def close
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Archive::Tar::Minitar
|
4
|
+
# The class that writes a tar format archive to a data stream.
|
5
|
+
class Writer
|
6
|
+
# The exception raised when the user attempts to write more data to a
|
7
|
+
# BoundedWriteStream than has been allocated.
|
8
|
+
WriteBoundaryOverflow = Class.new(StandardError)
|
9
|
+
|
10
|
+
# A stream wrapper that can only be written to. Any attempt to read
|
11
|
+
# from this restricted stream will result in a NameError being thrown.
|
12
|
+
class WriteOnlyStream
|
13
|
+
def initialize(io)
|
14
|
+
@io = io
|
15
|
+
end
|
16
|
+
|
17
|
+
def write(data)
|
18
|
+
@io.write(data)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private_constant :WriteOnlyStream if respond_to?(:private_constant)
|
23
|
+
|
24
|
+
# A WriteOnlyStream that also has a size limit.
|
25
|
+
class BoundedWriteStream < WriteOnlyStream
|
26
|
+
def self.const_missing(c)
|
27
|
+
case c
|
28
|
+
when :FileOverflow
|
29
|
+
warn 'Writer::BoundedWriteStream::FileOverflow has been renamed ' \
|
30
|
+
'to Writer::WriteBoundaryOverflow'
|
31
|
+
const_set :FileOverflow,
|
32
|
+
Archive::Tar::Minitar::Writer::WriteBoundaryOverflow
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# The maximum number of bytes that may be written to this data
|
39
|
+
# stream.
|
40
|
+
attr_reader :limit
|
41
|
+
# The current total number of bytes written to this data stream.
|
42
|
+
attr_reader :written
|
43
|
+
|
44
|
+
def initialize(io, limit)
|
45
|
+
@io = io
|
46
|
+
@limit = limit
|
47
|
+
@written = 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def write(data)
|
51
|
+
raise WriteBoundaryOverflow if (data.size + @written) > @limit
|
52
|
+
@io.write(data)
|
53
|
+
@written += data.size
|
54
|
+
data.size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private_constant :BoundedWriteStream if respond_to?(:private_constant)
|
59
|
+
|
60
|
+
def self.const_missing(c)
|
61
|
+
case c
|
62
|
+
when :BoundedStream
|
63
|
+
warn 'BoundedStream has been renamed to BoundedWriteStream'
|
64
|
+
const_set(:BoundedStream, BoundedWriteStream)
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# With no associated block, +Writer::open+ is a synonym for +Writer::new+.
|
71
|
+
# If the optional code block is given, it will be passed the new _writer_
|
72
|
+
# as an argument and the Writer object will automatically be closed when
|
73
|
+
# the block terminates. In this instance, +Writer::open+ returns the value
|
74
|
+
# of the block.
|
75
|
+
#
|
76
|
+
# call-seq:
|
77
|
+
# w = Archive::Tar::Minitar::Writer.open(STDOUT)
|
78
|
+
# w.add_file_simple('foo.txt', :size => 3)
|
79
|
+
# w.close
|
80
|
+
#
|
81
|
+
# Archive::Tar::Minitar::Writer.open(STDOUT) do |w|
|
82
|
+
# w.add_file_simple('foo.txt', :size => 3)
|
83
|
+
# end
|
84
|
+
def self.open(io) # :yields Writer:
|
85
|
+
writer = new(io)
|
86
|
+
return writer unless block_given?
|
87
|
+
yield writer
|
88
|
+
ensure
|
89
|
+
writer.close
|
90
|
+
end
|
91
|
+
|
92
|
+
# Creates and returns a new Writer object.
|
93
|
+
def initialize(io)
|
94
|
+
@io = io
|
95
|
+
@closed = false
|
96
|
+
end
|
97
|
+
|
98
|
+
# Adds a file to the archive as +name+. The data can be provided in the
|
99
|
+
# <tt>opts[:data]</tt> or provided to a BoundedWriteStream that is
|
100
|
+
# yielded to the provided block.
|
101
|
+
#
|
102
|
+
# If <tt>opts[:data]</tt> is provided, all other values to +opts+ are
|
103
|
+
# optional. If the data is provided to the yielded BoundedWriteStream,
|
104
|
+
# <tt>opts[:size]</tt> must be provided.
|
105
|
+
#
|
106
|
+
# Valid parameters to +opts+ are:
|
107
|
+
#
|
108
|
+
# <tt>:data</tt>:: Optional. The data to write to the archive.
|
109
|
+
# <tt>:mode</tt>:: The Unix file permissions mode value. If not
|
110
|
+
# provided, defaults to 0644.
|
111
|
+
# <tt>:size</tt>:: The size, in bytes. If <tt>:data</tt> is provided,
|
112
|
+
# this parameter may be ignored (if it is less than
|
113
|
+
# the size of the data provided) or used to add
|
114
|
+
# padding (if it is greater than the size of the data
|
115
|
+
# provided).
|
116
|
+
# <tt>:uid</tt>:: The Unix file owner user ID number.
|
117
|
+
# <tt>:gid</tt>:: The Unix file owner group ID number.
|
118
|
+
# <tt>:mtime</tt>:: File modification time, interpreted as an integer.
|
119
|
+
#
|
120
|
+
# An exception will be raised if the Writer is already closed, or if
|
121
|
+
# more data is written to the BoundedWriteStream than expected.
|
122
|
+
#
|
123
|
+
# call-seq:
|
124
|
+
# writer.add_file_simple('foo.txt', :data => "bar")
|
125
|
+
# writer.add_file_simple('foo.txt', :size => 3) do |w|
|
126
|
+
# w.write("bar")
|
127
|
+
# end
|
128
|
+
def add_file_simple(name, opts = {}) # :yields BoundedWriteStream:
|
129
|
+
raise ClosedStream if @closed
|
130
|
+
name, prefix = split_name(name)
|
131
|
+
|
132
|
+
header = {
|
133
|
+
:prefix => prefix,
|
134
|
+
:name => name,
|
135
|
+
:mode => opts.fetch(:mode, 0o644),
|
136
|
+
:mtime => opts.fetch(:mtime, nil),
|
137
|
+
:gid => opts.fetch(:gid, nil),
|
138
|
+
:uid => opts.fetch(:uid, nil)
|
139
|
+
}
|
140
|
+
|
141
|
+
data = opts.fetch(:data, nil)
|
142
|
+
size = opts.fetch(:size, nil)
|
143
|
+
|
144
|
+
if block_given?
|
145
|
+
if data
|
146
|
+
raise ArgumentError,
|
147
|
+
'Too much data (opts[:data] and block_given?).'
|
148
|
+
end
|
149
|
+
|
150
|
+
raise ArgumentError, 'No size provided' unless size
|
151
|
+
else
|
152
|
+
raise ArgumentError, 'No data provided' unless data
|
153
|
+
|
154
|
+
size = data.size if size < data.size
|
155
|
+
end
|
156
|
+
|
157
|
+
header[:size] = size
|
158
|
+
|
159
|
+
@io.write(PosixHeader.new(header))
|
160
|
+
|
161
|
+
os = BoundedWriteStream.new(@io, opts[:size])
|
162
|
+
if block_given?
|
163
|
+
yield os
|
164
|
+
else
|
165
|
+
os.write(data)
|
166
|
+
end
|
167
|
+
|
168
|
+
min_padding = opts[:size] - os.written
|
169
|
+
@io.write("\0" * min_padding)
|
170
|
+
remainder = (512 - (opts[:size] % 512)) % 512
|
171
|
+
@io.write("\0" * remainder)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Adds a file to the archive as +name+. The data can be provided in the
|
175
|
+
# <tt>opts[:data]</tt> or provided to a yielded +WriteOnlyStream+. The
|
176
|
+
# size of the file will be determined from the amount of data written
|
177
|
+
# to the stream.
|
178
|
+
#
|
179
|
+
# Valid parameters to +opts+ are:
|
180
|
+
#
|
181
|
+
# <tt>:mode</tt>:: The Unix file permissions mode value. If not
|
182
|
+
# provided, defaults to 0644.
|
183
|
+
# <tt>:uid</tt>:: The Unix file owner user ID number.
|
184
|
+
# <tt>:gid</tt>:: The Unix file owner group ID number.
|
185
|
+
# <tt>:mtime</tt>:: File modification time, interpreted as an integer.
|
186
|
+
# <tt>:data</tt>:: Optional. The data to write to the archive.
|
187
|
+
#
|
188
|
+
# If <tt>opts[:data]</tt> is provided, this acts the same as
|
189
|
+
# #add_file_simple. Otherwise, the file's size will be determined from
|
190
|
+
# the amount of data written to the stream.
|
191
|
+
#
|
192
|
+
# For #add_file to be used without <tt>opts[:data]</tt>, the Writer
|
193
|
+
# must be wrapping a stream object that is seekable. Otherwise,
|
194
|
+
# #add_file_simple must be used.
|
195
|
+
#
|
196
|
+
# +opts+ may be modified during the writing of the file to the stream.
|
197
|
+
def add_file(name, opts = {}, &block) # :yields WriteOnlyStream, +opts+:
|
198
|
+
raise ClosedStream if @closed
|
199
|
+
|
200
|
+
return add_file_simple(name, opts, &block) if opts[:data]
|
201
|
+
|
202
|
+
unless Archive::Tar::Minitar.seekable?(@io)
|
203
|
+
raise Archive::Tar::Minitar::NonSeekableStream
|
204
|
+
end
|
205
|
+
|
206
|
+
name, prefix = split_name(name)
|
207
|
+
|
208
|
+
init_pos = @io.pos
|
209
|
+
@io.write("\0" * 512) # placeholder for the header
|
210
|
+
|
211
|
+
yield WriteOnlyStream.new(@io), opts
|
212
|
+
|
213
|
+
size = @io.pos - (init_pos + 512)
|
214
|
+
remainder = (512 - (size % 512)) % 512
|
215
|
+
@io.write("\0" * remainder)
|
216
|
+
|
217
|
+
final_pos, @io.pos = @io.pos, init_pos
|
218
|
+
|
219
|
+
header = {
|
220
|
+
:name => name,
|
221
|
+
:mode => opts[:mode],
|
222
|
+
:mtime => opts[:mtime],
|
223
|
+
:size => size,
|
224
|
+
:gid => opts[:gid],
|
225
|
+
:uid => opts[:uid],
|
226
|
+
:prefix => prefix
|
227
|
+
}
|
228
|
+
@io.write(PosixHeader.new(header))
|
229
|
+
@io.pos = final_pos
|
230
|
+
end
|
231
|
+
|
232
|
+
# Creates a directory entry in the tar.
|
233
|
+
def mkdir(name, opts = {})
|
234
|
+
raise ClosedStream if @closed
|
235
|
+
|
236
|
+
name, prefix = split_name(name)
|
237
|
+
header = {
|
238
|
+
:name => name,
|
239
|
+
:mode => opts[:mode],
|
240
|
+
:typeflag => '5',
|
241
|
+
:size => 0,
|
242
|
+
:gid => opts[:gid],
|
243
|
+
:uid => opts[:uid],
|
244
|
+
:mtime => opts[:mtime],
|
245
|
+
:prefix => prefix
|
246
|
+
}
|
247
|
+
@io.write(PosixHeader.new(header))
|
248
|
+
nil
|
249
|
+
end
|
250
|
+
|
251
|
+
# Passes the #flush method to the wrapped stream, used for buffered
|
252
|
+
# streams.
|
253
|
+
def flush
|
254
|
+
raise ClosedStream if @closed
|
255
|
+
@io.flush if @io.respond_to?(:flush)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Closes the Writer. This does not close the underlying wrapped output
|
259
|
+
# stream.
|
260
|
+
def close
|
261
|
+
return if @closed
|
262
|
+
@io.write("\0" * 1024)
|
263
|
+
@closed = true
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def split_name(name)
|
269
|
+
# TODO: Enable long-filename write support.
|
270
|
+
|
271
|
+
raise FileNameTooLong if name.size > 256
|
272
|
+
|
273
|
+
if name.size <= 100
|
274
|
+
prefix = ''
|
275
|
+
else
|
276
|
+
parts = name.split(/\//)
|
277
|
+
newname = parts.pop
|
278
|
+
|
279
|
+
nxt = ''
|
280
|
+
|
281
|
+
loop do
|
282
|
+
nxt = parts.pop || ''
|
283
|
+
break if newname.size + 1 + nxt.size >= 100
|
284
|
+
newname = "#{nxt}/#{newname}"
|
285
|
+
end
|
286
|
+
|
287
|
+
prefix = (parts + [nxt]).join('/')
|
288
|
+
|
289
|
+
name = newname
|
290
|
+
|
291
|
+
raise FileNameTooLong if name.size > 100 || prefix.size > 155
|
292
|
+
end
|
293
|
+
|
294
|
+
[ name, prefix ]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|