minitar 0.5.4 → 0.6
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.
- 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
|