zip-container 2.2.0 → 4.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Changes.rdoc +22 -0
- data/Gemfile +1 -1
- data/Licence.rdoc +1 -1
- data/Rakefile +15 -12
- data/ReadMe.rdoc +20 -8
- data/examples/create-zip-container +7 -8
- data/examples/zip-container-info +4 -4
- data/lib/zip-container/container.rb +49 -23
- data/lib/zip-container/dir.rb +8 -11
- data/lib/zip-container/entries/directory.rb +15 -11
- data/lib/zip-container/entries/entry.rb +38 -29
- data/lib/zip-container/entries/file.rb +19 -15
- data/lib/zip-container/entries/managed.rb +28 -16
- data/lib/zip-container/entries/reserved.rb +2 -2
- data/lib/zip-container/exceptions.rb +20 -13
- data/lib/zip-container/file.rb +16 -16
- data/lib/zip-container/util.rb +3 -3
- data/lib/zip-container/version.rb +4 -3
- data/version.yml +3 -3
- data/zip-container.gemspec +31 -28
- metadata +54 -95
- data/.gitignore +0 -9
- data/.ruby-env +0 -1
- data/.ruby-gemset +0 -2
- data/.ruby-version +0 -2
- data/.travis.yml +0 -17
- data/test/data/compressed_mimetype.container +0 -0
- data/test/data/dirs/dir-mimetype/mimetype/.gitkeep +0 -1
- data/test/data/dirs/empty/mimetype +0 -1
- data/test/data/dirs/managed/dir/.gitkeep +0 -0
- data/test/data/dirs/managed/greeting.txt +0 -1
- data/test/data/dirs/managed/mimetype +0 -1
- data/test/data/dirs/null/.gitkeep +0 -1
- data/test/data/empty.container +0 -0
- data/test/data/empty.zip +0 -0
- data/test/data/example.container +0 -0
- data/test/data/null.file +0 -0
- data/test/data/subclassed.container +0 -0
- data/test/helpers/entry_lists.rb +0 -35
- data/test/tc_create_dir.rb +0 -56
- data/test/tc_create_file.rb +0 -140
- data/test/tc_exceptions.rb +0 -56
- data/test/tc_managed_entries.rb +0 -399
- data/test/tc_read_dir.rb +0 -86
- data/test/tc_read_file.rb +0 -109
- data/test/tc_reserved_names.rb +0 -334
- data/test/tc_util.rb +0 -67
- data/test/ts_container.rb +0 -59
@@ -30,18 +30,23 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
-
|
33
|
+
##
|
34
34
|
module ZipContainer
|
35
35
|
|
36
36
|
# ManagedEntry is the superclass of ManagedDirectory and ManagedFile. It
|
37
37
|
# should not be used directly but may be subclassed if necessary.
|
38
38
|
class ManagedEntry
|
39
|
+
|
39
40
|
include Util
|
40
41
|
|
41
42
|
# The name of the ManagedEntry. For the full path name of this entry use
|
42
43
|
# full_name.
|
43
44
|
attr_reader :name
|
44
45
|
|
46
|
+
# Allows the object in which this entry has been registered to tell it
|
47
|
+
# who it is.
|
48
|
+
attr_writer :parent # :nodoc:
|
49
|
+
|
45
50
|
# :call-seq:
|
46
51
|
# new(name, required) -> ManagedEntry
|
47
52
|
#
|
@@ -60,7 +65,11 @@ module ZipContainer
|
|
60
65
|
#
|
61
66
|
# The fully qualified name of this ManagedEntry.
|
62
67
|
def full_name
|
63
|
-
@parent.is_a?(ZipContainer::Container)
|
68
|
+
if @parent.is_a?(ZipContainer::Container)
|
69
|
+
@name
|
70
|
+
else
|
71
|
+
"#{@parent.full_name}/#{@name}"
|
72
|
+
end
|
64
73
|
end
|
65
74
|
|
66
75
|
# :call-seq:
|
@@ -78,7 +87,11 @@ module ZipContainer
|
|
78
87
|
# Is this ManagedEntry hidden for normal operations?
|
79
88
|
def hidden?
|
80
89
|
# An entry is hidden if its parent is hidden.
|
81
|
-
@parent.is_a?(ZipContainer::Container)
|
90
|
+
if @parent.is_a?(ZipContainer::Container)
|
91
|
+
@hidden
|
92
|
+
else
|
93
|
+
@hidden || @parent.hidden?
|
94
|
+
end
|
82
95
|
end
|
83
96
|
|
84
97
|
# :call-seq:
|
@@ -87,54 +100,51 @@ module ZipContainer
|
|
87
100
|
# Does this ManagedEntry exist in the Container?
|
88
101
|
def exists?
|
89
102
|
container.entries.each do |entry|
|
90
|
-
test =
|
103
|
+
test = entry.ftype == :directory ? "#{full_name}/" : full_name
|
91
104
|
return true if entry.name == test
|
92
105
|
end
|
93
106
|
|
94
107
|
false
|
95
108
|
end
|
96
109
|
|
97
|
-
# :
|
98
|
-
#
|
99
|
-
#
|
100
|
-
|
101
|
-
|
110
|
+
# :call-seq:
|
111
|
+
# verify -> Array
|
112
|
+
#
|
113
|
+
# Verify this ManagedEntry returning a list of reasons why it fails if it
|
114
|
+
# does so. The empty list is returned if verification passes.
|
115
|
+
#
|
116
|
+
# Subclasses should override this method if they require more complex
|
117
|
+
# verification to be done.
|
118
|
+
def verify
|
119
|
+
if @required && !exists?
|
120
|
+
["Entry '#{full_name}' is required but missing."]
|
121
|
+
else
|
122
|
+
[]
|
123
|
+
end
|
102
124
|
end
|
103
|
-
# :startdoc:
|
104
125
|
|
105
126
|
# :call-seq:
|
106
|
-
# verify -> true or false
|
127
|
+
# verify? -> true or false
|
107
128
|
#
|
108
129
|
# Verify this ManagedEntry by checking that it exists if it is required
|
109
130
|
# according to its Container specification and validating its contents if
|
110
131
|
# necessary.
|
111
|
-
def verify
|
112
|
-
|
113
|
-
verify!
|
114
|
-
rescue
|
115
|
-
return false
|
116
|
-
end
|
117
|
-
|
118
|
-
true
|
132
|
+
def verify?
|
133
|
+
verify.empty?
|
119
134
|
end
|
120
135
|
|
121
|
-
protected
|
122
|
-
|
123
136
|
# :call-seq:
|
124
137
|
# verify!
|
125
138
|
#
|
126
139
|
# Verify this ManagedEntry raising a MalformedContainerError if it
|
127
140
|
# fails.
|
128
|
-
#
|
129
|
-
# Subclasses should override this method if they require more complex
|
130
|
-
# verification to be done.
|
131
141
|
def verify!
|
132
|
-
|
133
|
-
|
134
|
-
"but missing.")
|
135
|
-
end
|
142
|
+
messages = verify
|
143
|
+
raise MalformedContainerError, messages unless messages.empty?
|
136
144
|
end
|
137
145
|
|
146
|
+
protected
|
147
|
+
|
138
148
|
# :call-seq:
|
139
149
|
# container -> Container
|
140
150
|
#
|
@@ -142,6 +152,5 @@ module ZipContainer
|
|
142
152
|
def container
|
143
153
|
@parent.is_a?(ZipContainer::Container) ? @parent : @parent.container
|
144
154
|
end
|
145
|
-
|
146
155
|
end
|
147
156
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2013 The University of Manchester, UK.
|
1
|
+
# Copyright (c) 2013-2015 The University of Manchester, UK.
|
2
2
|
#
|
3
3
|
# All rights reserved.
|
4
4
|
#
|
@@ -30,7 +30,7 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
-
|
33
|
+
##
|
34
34
|
module ZipContainer
|
35
35
|
|
36
36
|
# A ManagedFile is used to reserve a filename in a Container namespace.
|
@@ -57,13 +57,13 @@ module ZipContainer
|
|
57
57
|
# word "Boo!".
|
58
58
|
#
|
59
59
|
# valid = Proc.new { |contents| contents == "Boo!" }
|
60
|
-
# ManagedFile.new("Surprize.txt", :
|
61
|
-
# :
|
60
|
+
# ManagedFile.new("Surprize.txt", required: false,
|
61
|
+
# validation_proc: valid)
|
62
62
|
def initialize(name, options = {})
|
63
63
|
options = {
|
64
|
-
:
|
65
|
-
:
|
66
|
-
:
|
64
|
+
required: false,
|
65
|
+
hidden: false,
|
66
|
+
validation_proc: nil
|
67
67
|
}.merge(options)
|
68
68
|
|
69
69
|
super(name, options[:required], options[:hidden])
|
@@ -73,18 +73,23 @@ module ZipContainer
|
|
73
73
|
end
|
74
74
|
|
75
75
|
# :call-seq:
|
76
|
-
# verify
|
76
|
+
# verify -> Array
|
77
77
|
#
|
78
78
|
# Verify this ManagedFile for correctness. The contents are validated if
|
79
79
|
# required.
|
80
80
|
#
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
81
|
+
# If it does not pass verification a list of reasons why it fails is
|
82
|
+
# returned. The empty list is returned if verification passes.
|
83
|
+
def verify
|
84
|
+
messages = super
|
85
|
+
|
86
|
+
valid = exists? ? validate : true
|
87
|
+
unless valid
|
88
|
+
messages <<
|
89
|
+
"The contents of file '#{full_name}' do not pass validation."
|
87
90
|
end
|
91
|
+
|
92
|
+
messages
|
88
93
|
end
|
89
94
|
|
90
95
|
protected
|
@@ -107,6 +112,5 @@ module ZipContainer
|
|
107
112
|
def contents
|
108
113
|
container.read(full_name)
|
109
114
|
end
|
110
|
-
|
111
115
|
end
|
112
116
|
end
|
@@ -30,7 +30,7 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
-
|
33
|
+
##
|
34
34
|
module ZipContainer
|
35
35
|
|
36
36
|
# This module provides support for managed file and directory entries.
|
@@ -49,8 +49,7 @@ module ZipContainer
|
|
49
49
|
return @managed_directories if @managed_directories
|
50
50
|
|
51
51
|
dirs = @directories.values
|
52
|
-
@managed_directories = dirs +
|
53
|
-
dirs.map { |d| d.managed_directories }.flatten
|
52
|
+
@managed_directories = dirs + dirs.map(&:managed_directories).flatten
|
54
53
|
end
|
55
54
|
|
56
55
|
# :call-seq:
|
@@ -58,7 +57,7 @@ module ZipContainer
|
|
58
57
|
#
|
59
58
|
# Return the list of managed directory names.
|
60
59
|
def managed_directory_names
|
61
|
-
@managed_directory_names ||= managed_directories.map
|
60
|
+
@managed_directory_names ||= managed_directories.map(&:full_name)
|
62
61
|
end
|
63
62
|
|
64
63
|
# :call-seq:
|
@@ -91,7 +90,7 @@ module ZipContainer
|
|
91
90
|
# Is the supplied entry/name a managed entry?
|
92
91
|
def managed_entry?(entry, list = managed_entry_names)
|
93
92
|
name = entry_name(entry)
|
94
|
-
list.map
|
93
|
+
list.map(&:downcase).include? name.downcase
|
95
94
|
end
|
96
95
|
|
97
96
|
# :call-seq:
|
@@ -133,8 +132,9 @@ module ZipContainer
|
|
133
132
|
#
|
134
133
|
# Return the list of managed files.
|
135
134
|
def managed_files
|
136
|
-
@managed_files ||=
|
137
|
-
@
|
135
|
+
@managed_files ||=
|
136
|
+
@files.values +
|
137
|
+
@directories.values.map(&:managed_files).flatten
|
138
138
|
end
|
139
139
|
|
140
140
|
# :call-seq:
|
@@ -142,24 +142,36 @@ module ZipContainer
|
|
142
142
|
#
|
143
143
|
# Return the list of managed file names.
|
144
144
|
def managed_file_names
|
145
|
-
@managed_file_names ||= managed_files.map
|
145
|
+
@managed_file_names ||= managed_files.map(&:full_name)
|
146
146
|
end
|
147
147
|
|
148
148
|
# :call-seq:
|
149
|
-
# verify_managed_entries
|
149
|
+
# verify_managed_entries -> Array
|
150
150
|
#
|
151
151
|
# All managed files and directories are checked to make sure that they
|
152
|
-
# exist, if required.
|
153
|
-
def verify_managed_entries
|
152
|
+
# exist and validate, if required.
|
153
|
+
def verify_managed_entries
|
154
|
+
messages = []
|
155
|
+
|
154
156
|
@directories.each_value do |dir|
|
155
|
-
dir.verify
|
157
|
+
messages += dir.verify
|
156
158
|
end
|
157
159
|
|
158
160
|
@files.each_value do |file|
|
159
|
-
file.verify
|
161
|
+
messages += file.verify
|
160
162
|
end
|
161
163
|
|
162
|
-
|
164
|
+
messages
|
165
|
+
end
|
166
|
+
|
167
|
+
# :call-seq:
|
168
|
+
# verify_managed_entries!
|
169
|
+
#
|
170
|
+
# All managed files and directories are checked to make sure that they
|
171
|
+
# exist and validate, if required.
|
172
|
+
def verify_managed_entries!
|
173
|
+
messages = verify_managed_entries
|
174
|
+
raise MalformedContainerError, messages unless messages.empty?
|
163
175
|
end
|
164
176
|
|
165
177
|
protected
|
@@ -193,8 +205,8 @@ module ZipContainer
|
|
193
205
|
# managed files within it.
|
194
206
|
def register_managed_entry(entry)
|
195
207
|
unless entry.is_a?(ManagedDirectory) || entry.is_a?(ManagedFile)
|
196
|
-
raise ArgumentError
|
197
|
-
|
208
|
+
raise ArgumentError, 'The supplied entry must be of type '\
|
209
|
+
'ManagedDirectory or ManagedFile or a subclass of either.'
|
198
210
|
end
|
199
211
|
|
200
212
|
entry.parent = self
|
@@ -30,7 +30,7 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
-
|
33
|
+
##
|
34
34
|
module ZipContainer
|
35
35
|
|
36
36
|
# This module provides support for reserved names.
|
@@ -70,7 +70,7 @@ module ZipContainer
|
|
70
70
|
# Zip::Entry object can be passed in here.
|
71
71
|
def reserved_entry?(entry)
|
72
72
|
name = entry_name(entry)
|
73
|
-
reserved_names.map
|
73
|
+
reserved_names.map(&:downcase).include? name.downcase
|
74
74
|
end
|
75
75
|
|
76
76
|
protected
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2013
|
1
|
+
# Copyright (c) 2013-2015 The University of Manchester, UK.
|
2
2
|
#
|
3
3
|
# All rights reserved.
|
4
4
|
#
|
@@ -30,31 +30,37 @@
|
|
30
30
|
#
|
31
31
|
# Author: Robert Haines
|
32
32
|
|
33
|
-
|
33
|
+
##
|
34
34
|
module ZipContainer
|
35
35
|
|
36
36
|
# The base of all exceptions raised by this library.
|
37
|
-
module
|
37
|
+
module Error
|
38
38
|
end
|
39
39
|
|
40
40
|
# Shadow Zip::Error so the rubyzip API doesn't leak out.
|
41
41
|
ZipError = ::Zip::Error
|
42
|
-
ZipError.send(:include,
|
42
|
+
ZipError.send(:include, Error)
|
43
43
|
|
44
44
|
# This exception is raised when a bad Container is detected.
|
45
45
|
class MalformedContainerError < RuntimeError
|
46
|
-
|
46
|
+
|
47
|
+
include Error
|
47
48
|
|
48
49
|
# :call-seq:
|
49
|
-
# new
|
50
|
+
# new
|
51
|
+
# new(reason)
|
52
|
+
# new(reason_list)
|
50
53
|
#
|
51
|
-
# Create a new MalformedContainerError with an optional reason
|
52
|
-
# the Container
|
54
|
+
# Create a new MalformedContainerError with an optional reason or list of
|
55
|
+
# reasons for why the Container is malformed.
|
53
56
|
def initialize(reason = nil)
|
54
|
-
if reason.nil?
|
55
|
-
super(
|
57
|
+
if reason.nil? || reason.empty?
|
58
|
+
super('Malformed Container.')
|
59
|
+
elsif reason.is_a?(Array)
|
60
|
+
reasons = reason.map { |r| " * #{r}\n" }
|
61
|
+
super("Malformed Container:\n#{reasons}")
|
56
62
|
else
|
57
|
-
super("Malformed Container
|
63
|
+
super("Malformed Container: #{reason}")
|
58
64
|
end
|
59
65
|
end
|
60
66
|
end
|
@@ -62,14 +68,15 @@ module ZipContainer
|
|
62
68
|
# This exception is raised when a clash occurs with a reserved or managed
|
63
69
|
# name.
|
64
70
|
class ReservedNameClashError < RuntimeError
|
65
|
-
|
71
|
+
|
72
|
+
include Error
|
66
73
|
|
67
74
|
# :call-seq:
|
68
75
|
# new(name)
|
69
76
|
#
|
70
77
|
# Create a new ReservedNameClashError with the name of the clash supplied.
|
71
78
|
def initialize(name)
|
72
|
-
super("'#{name}' is reserved for internal use in this ZipContainer
|
79
|
+
super("'#{name}' is reserved for internal use in this ZipContainer.")
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
data/lib/zip-container/file.rb
CHANGED
@@ -49,7 +49,7 @@ module ZipContainer
|
|
49
49
|
|
50
50
|
extend Forwardable
|
51
51
|
def_delegators :@container, :comment, :comment=, :commit_required?, :each,
|
52
|
-
|
52
|
+
:entries, :extract, :get_input_stream, :name, :read, :size
|
53
53
|
|
54
54
|
private_class_method :new
|
55
55
|
|
@@ -74,7 +74,7 @@ module ZipContainer
|
|
74
74
|
# File.create(filename, mimetype) {|container| ...}
|
75
75
|
#
|
76
76
|
# Create a new ZipContainer file on disk with the specified mimetype.
|
77
|
-
def self.create(filename, mimetype
|
77
|
+
def self.create(filename, mimetype)
|
78
78
|
::Zip::OutputStream.open(filename) do |stream|
|
79
79
|
stream.put_next_entry(MIMETYPE_FILE, nil, nil, ::Zip::Entry::STORED)
|
80
80
|
stream.write mimetype
|
@@ -127,7 +127,7 @@ module ZipContainer
|
|
127
127
|
# +continue_on_exists_proc+ parameter.
|
128
128
|
def add(entry, src_path, &continue_on_exists_proc)
|
129
129
|
if reserved_entry?(entry) || managed_directory?(entry)
|
130
|
-
raise ReservedNameClashError
|
130
|
+
raise ReservedNameClashError, entry.to_s
|
131
131
|
end
|
132
132
|
|
133
133
|
@container.add(entry, src_path, &continue_on_exists_proc)
|
@@ -143,12 +143,10 @@ module ZipContainer
|
|
143
143
|
def commit
|
144
144
|
return false unless commit_required?
|
145
145
|
|
146
|
-
if on_disk?
|
147
|
-
@container.commit
|
148
|
-
end
|
146
|
+
@container.commit if on_disk?
|
149
147
|
end
|
150
148
|
|
151
|
-
alias
|
149
|
+
alias close commit
|
152
150
|
|
153
151
|
# :call-seq:
|
154
152
|
# dir -> Zip::ZipFsDir
|
@@ -182,7 +180,7 @@ module ZipContainer
|
|
182
180
|
# can specify <tt>:include_hidden => true</tt> to include hidden entries
|
183
181
|
# in the search.
|
184
182
|
def find_entry(entry_name, options = {})
|
185
|
-
options = {:
|
183
|
+
options = { include_hidden: false }.merge(options)
|
186
184
|
|
187
185
|
unless options[:include_hidden]
|
188
186
|
return if hidden_entry?(entry_name)
|
@@ -199,7 +197,7 @@ module ZipContainer
|
|
199
197
|
# can specify <tt>:include_hidden => true</tt> to include hidden entries
|
200
198
|
# in the search.
|
201
199
|
def get_entry(entry, options = {})
|
202
|
-
options = {:
|
200
|
+
options = { include_hidden: false }.merge(options)
|
203
201
|
|
204
202
|
unless options[:include_hidden]
|
205
203
|
raise Errno::ENOENT, entry if hidden_entry?(entry)
|
@@ -220,7 +218,7 @@ module ZipContainer
|
|
220
218
|
# parameter.
|
221
219
|
def get_output_stream(entry, permission = nil, &block)
|
222
220
|
if reserved_entry?(entry) || managed_directory?(entry)
|
223
|
-
raise ReservedNameClashError
|
221
|
+
raise ReservedNameClashError, entry.to_s
|
224
222
|
end
|
225
223
|
|
226
224
|
@container.get_output_stream(entry, permission, &block)
|
@@ -240,9 +238,9 @@ module ZipContainer
|
|
240
238
|
# <tt>::File::FNM_PATHNAME | ::File::FNM_DOTMATCH</tt>
|
241
239
|
# * +options+ - <tt>:include_hidden => true</tt> will include hidden
|
242
240
|
# entries in the search.
|
243
|
-
def glob(pattern, *params
|
241
|
+
def glob(pattern, *params)
|
244
242
|
flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH
|
245
|
-
options = { :
|
243
|
+
options = { include_hidden: false }
|
246
244
|
|
247
245
|
params.each do |param|
|
248
246
|
case param
|
@@ -256,6 +254,7 @@ module ZipContainer
|
|
256
254
|
entries.map do |entry|
|
257
255
|
next if !options[:include_hidden] && hidden_entry?(entry)
|
258
256
|
next unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags)
|
257
|
+
|
259
258
|
yield(entry) if block_given?
|
260
259
|
entry
|
261
260
|
end.compact
|
@@ -279,9 +278,9 @@ module ZipContainer
|
|
279
278
|
# The new directory will be created with the supplied unix-style
|
280
279
|
# permissions. The default (+0755+) is owner read, write and list; group
|
281
280
|
# read and list; and world read and list.
|
282
|
-
def mkdir(name, permission =
|
281
|
+
def mkdir(name, permission = 0o0755)
|
283
282
|
if reserved_entry?(name) || managed_file?(name)
|
284
|
-
raise ReservedNameClashError
|
283
|
+
raise ReservedNameClashError, name
|
285
284
|
end
|
286
285
|
|
287
286
|
@container.mkdir(name, permission)
|
@@ -303,6 +302,7 @@ module ZipContainer
|
|
303
302
|
# method will do nothing.
|
304
303
|
def remove(entry)
|
305
304
|
return if reserved_entry?(entry)
|
305
|
+
|
306
306
|
@container.remove(entry)
|
307
307
|
end
|
308
308
|
|
@@ -318,7 +318,7 @@ module ZipContainer
|
|
318
318
|
# +continue_on_exists_proc+ parameter.
|
319
319
|
def rename(entry, new_name, &continue_on_exists_proc)
|
320
320
|
return if reserved_entry?(entry)
|
321
|
-
raise ReservedNameClashError
|
321
|
+
raise ReservedNameClashError, new_name if reserved_entry?(new_name)
|
322
322
|
|
323
323
|
@container.rename(entry, new_name, &continue_on_exists_proc)
|
324
324
|
end
|
@@ -332,6 +332,7 @@ module ZipContainer
|
|
332
332
|
# nothing.
|
333
333
|
def replace(entry, src_path)
|
334
334
|
return if reserved_entry?(entry)
|
335
|
+
|
335
336
|
@container.replace(entry, src_path)
|
336
337
|
end
|
337
338
|
|
@@ -479,6 +480,5 @@ module ZipContainer
|
|
479
480
|
# size -> int
|
480
481
|
#
|
481
482
|
# Returns the number of entries in the ZipContainer file.
|
482
|
-
|
483
483
|
end
|
484
484
|
end
|