zip_tricks 4.2.3 → 4.2.4

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.
@@ -1,43 +0,0 @@
1
- require_relative '../spec_helper'
2
-
3
- describe ZipTricks::WriteAndTell do
4
- it 'maintains the count of bytes written' do
5
- adapter = described_class.new('')
6
- expect(adapter.tell).to be_zero
7
-
8
- adapter << 'hello'
9
- adapter << ''
10
- adapter << '!'
11
- expect(adapter.tell).to eq(6)
12
- end
13
-
14
- it 'is able to write frozen String objects in different encodings, converting them to binary' do
15
- strs = [
16
- [12, 123, 0, 3].pack("C*"),
17
- "текста кусок",
18
- "текста замороженный кусок".freeze,
19
- [12, 123, 0, 3].pack("C*"),
20
- ]
21
-
22
- buf = 'превед'.force_encoding(Encoding::BINARY)
23
- writer = described_class.new(buf)
24
- strs.each {|s| writer << s }
25
- expect(writer.tell).to eq(79)
26
- expect(buf.bytesize).to eq(91) # It already contained some bytes
27
- end
28
-
29
- it 'advances the internal pointer using advance_position_by' do
30
- str = ''
31
-
32
- adapter = described_class.new(str)
33
- expect(adapter.tell).to be_zero
34
-
35
- adapter << 'hello'
36
- adapter << ''
37
- adapter << '!'
38
- expect(adapter.tell).to eq(6)
39
- adapter.advance_position_by(128981)
40
- expect(adapter.tell).to eq(6 + 128981)
41
- expect(str).to eq('hello!')
42
- end
43
- end
@@ -1,419 +0,0 @@
1
- require_relative '../spec_helper'
2
- require_relative '../../testing/support'
3
-
4
- describe ZipTricks::ZipWriter do
5
- class ByteReader < Struct.new(:io)
6
- def initialize(io)
7
- super(io).tap { io.rewind }
8
- end
9
-
10
- def read_2b
11
- read_n(2).unpack('v').first
12
- end
13
-
14
- def read_2c
15
- read_n(2).unpack('CC').first
16
- end
17
-
18
- def read_4b
19
- read_n(4).unpack('V').first
20
- end
21
-
22
- def read_8b
23
- read_n(8).unpack('Q<').first
24
- end
25
-
26
- def read_n(n)
27
- io.read(n).tap {|r|
28
- raise "Expected to read #{n} bytes, but read() returned nil" if r.nil?
29
- raise "Expected to read #{n} bytes, but read #{r.bytesize} instead" if r.bytesize != n
30
- }
31
- end
32
-
33
- # For conveniently going to a specific signature
34
- def seek_to_start_of_signature(signature)
35
- io.rewind
36
- signature_encoded = [signature].pack('V')
37
- idx = io.read.index(signature_encoded)
38
- raise "Could not find the signature #{signature} in the buffer" unless idx
39
- io.seek(idx, IO::SEEK_SET)
40
- end
41
- end
42
-
43
- describe '#write_local_file_header' do
44
- it 'writes the local file header for an entry that does not require Zip64' do
45
- buf = StringIO.new
46
- mtime = Time.utc(2016, 7, 17, 13, 48)
47
-
48
- subject = ZipTricks::ZipWriter.new
49
- subject.write_local_file_header(io: buf, gp_flags: 12, crc32: 456, compressed_size: 768, uncompressed_size: 901, mtime: mtime, filename: 'foo.bin', storage_mode: 8)
50
-
51
- br = ByteReader.new(buf)
52
- expect(br.read_4b).to eq(0x04034b50) # Signature
53
- expect(br.read_2b).to eq(20) # Version needed to extract
54
- expect(br.read_2b).to eq(12) # gp flags
55
- expect(br.read_2b).to eq(8) # storage mode
56
- expect(br.read_2b).to eq(28160) # DOS time
57
- expect(br.read_2b).to eq(18673) # DOS date
58
- expect(br.read_4b).to eq(456) # CRC32
59
- expect(br.read_4b).to eq(768) # compressed size
60
- expect(br.read_4b).to eq(901) # uncompressed size
61
- expect(br.read_2b).to eq(7) # filename size
62
- expect(br.read_2b).to eq(0) # extra fields size
63
- expect(br.read_n(7)).to eq('foo.bin') # extra fields size
64
- expect(buf).to be_eof
65
- end
66
-
67
- it 'writes the local file header for an entry that does require Zip64 based on uncompressed size (with the Zip64 extra)' do
68
- buf = StringIO.new
69
- mtime = Time.utc(2016, 7, 17, 13, 48)
70
-
71
- subject = ZipTricks::ZipWriter.new
72
- subject.write_local_file_header(io: buf, gp_flags: 12, crc32: 456, compressed_size: 768, uncompressed_size: 0xFFFFFFFF+1, mtime: mtime, filename: 'foo.bin', storage_mode: 8)
73
-
74
- br = ByteReader.new(buf)
75
- expect(br.read_4b).to eq(0x04034b50) # Signature
76
- expect(br.read_2b).to eq(45) # Version needed to extract
77
- expect(br.read_2b).to eq(12) # gp flags
78
- expect(br.read_2b).to eq(8) # storage mode
79
- expect(br.read_2b).to eq(28160) # DOS time
80
- expect(br.read_2b).to eq(18673) # DOS date
81
- expect(br.read_4b).to eq(456) # CRC32
82
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
83
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
84
- expect(br.read_2b).to eq(7) # filename size
85
- expect(br.read_2b).to eq(20) # extra fields size
86
- expect(br.read_n(7)).to eq('foo.bin') # extra fields size
87
-
88
- expect(buf).not_to be_eof
89
-
90
- expect(br.read_2b).to eq(1) # Zip64 extra tag
91
- expect(br.read_2b).to eq(16) # Size of the Zip64 extra payload
92
- expect(br.read_8b).to eq(0xFFFFFFFF+1) # uncompressed size
93
- expect(br.read_8b).to eq(768) # compressed size
94
- end
95
-
96
- it 'writes the local file header for an entry that does require Zip64 based on compressed size (with the Zip64 extra)' do
97
- buf = StringIO.new
98
- mtime = Time.utc(2016, 7, 17, 13, 48)
99
-
100
- subject = ZipTricks::ZipWriter.new
101
- subject.write_local_file_header(io: buf, gp_flags: 12, crc32: 456, compressed_size: 0xFFFFFFFF+1, uncompressed_size: 768, mtime: mtime, filename: 'foo.bin', storage_mode: 8)
102
-
103
- br = ByteReader.new(buf)
104
- expect(br.read_4b).to eq(0x04034b50) # Signature
105
- expect(br.read_2b).to eq(45) # Version needed to extract
106
- expect(br.read_2b).to eq(12) # gp flags
107
- expect(br.read_2b).to eq(8) # storage mode
108
- expect(br.read_2b).to eq(28160) # DOS time
109
- expect(br.read_2b).to eq(18673) # DOS date
110
- expect(br.read_4b).to eq(456) # CRC32
111
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
112
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
113
- expect(br.read_2b).to eq(7) # filename size
114
- expect(br.read_2b).to eq(20) # extra fields size
115
- expect(br.read_n(7)).to eq('foo.bin') # extra fields size
116
-
117
- expect(buf).not_to be_eof
118
-
119
- expect(br.read_2b).to eq(1) # Zip64 extra tag
120
- expect(br.read_2b).to eq(16) # Size of the Zip64 extra payload
121
- expect(br.read_8b).to eq(768) # uncompressed size
122
- expect(br.read_8b).to eq(0xFFFFFFFF+1) # compressed size
123
- end
124
- end
125
-
126
- describe '#write_data_descriptor' do
127
- it 'writes 4-byte sizes into the data descriptor for standard file sizes' do
128
- buf = StringIO.new
129
-
130
- subject.write_data_descriptor(io: buf, crc32: 123, compressed_size: 89821, uncompressed_size: 990912)
131
-
132
- br = ByteReader.new(buf)
133
- expect(br.read_4b).to eq(0x08074b50) # Signature
134
- expect(br.read_4b).to eq(123) # CRC32
135
- expect(br.read_4b).to eq(89821) # compressed size
136
- expect(br.read_4b).to eq(990912) # uncompressed size
137
- expect(buf).to be_eof
138
- end
139
-
140
- it 'writes 8-byte sizes into the data descriptor for Zip64 compressed file size' do
141
- buf = StringIO.new
142
-
143
- subject.write_data_descriptor(io: buf, crc32: 123, compressed_size: 0xFFFFFFFF + 1, uncompressed_size: 990912)
144
-
145
- br = ByteReader.new(buf)
146
- expect(br.read_4b).to eq(0x08074b50) # Signature
147
- expect(br.read_4b).to eq(123) # CRC32
148
- expect(br.read_8b).to eq(0xFFFFFFFF + 1) # compressed size
149
- expect(br.read_8b).to eq(990912) # uncompressed size
150
- expect(buf).to be_eof
151
- end
152
-
153
- it 'writes 8-byte sizes into the data descriptor for Zip64 uncompressed file size' do
154
- buf = StringIO.new
155
-
156
- subject.write_data_descriptor(io: buf, crc32: 123, compressed_size: 123, uncompressed_size: 0xFFFFFFFF + 1)
157
-
158
- br = ByteReader.new(buf)
159
- expect(br.read_4b).to eq(0x08074b50) # Signature
160
- expect(br.read_4b).to eq(123) # CRC32
161
- expect(br.read_8b).to eq(123) # compressed size
162
- expect(br.read_8b).to eq(0xFFFFFFFF + 1) # uncompressed size
163
- expect(buf).to be_eof
164
- end
165
- end
166
-
167
- describe '#write_central_directory_file_header' do
168
- it 'writes the file header for a small-ish entry' do
169
- buf = StringIO.new
170
-
171
- subject.write_central_directory_file_header(io: buf, local_file_header_location: 898921,
172
- gp_flags: 555, storage_mode: 23,
173
- compressed_size: 901, uncompressed_size: 909102,
174
- mtime: Time.utc(2016, 2, 2, 14, 00), crc32: 89765,
175
- filename: 'a-file.txt', external_attrs: 123)
176
-
177
- br = ByteReader.new(buf)
178
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
179
- expect(br.read_2b).to eq(820) # version made by
180
- expect(br.read_2b).to eq(20) # version need to extract
181
- expect(br.read_2b).to eq(555) # general purpose bit flag (explicitly set to bogus value to ensure we pass it through)
182
- expect(br.read_2b).to eq(23) # compression method (explicitly set to bogus value)
183
- expect(br.read_2b).to eq(28672) # last mod file time
184
- expect(br.read_2b).to eq(18498) # last mod file date
185
- expect(br.read_4b).to eq(89765) # crc32
186
- expect(br.read_4b).to eq(901) # compressed size
187
- expect(br.read_4b).to eq(909102) # uncompressed size
188
- expect(br.read_2b).to eq(10) # filename length
189
- expect(br.read_2b).to eq(0) # extra field length
190
- expect(br.read_2b).to eq(0) # file comment
191
- expect(br.read_2b).to eq(0) # disk number, must be blanked to the maximum value because of The Unarchiver bug
192
- expect(br.read_2b).to eq(0) # internal file attributes
193
- expect(br.read_4b).to eq(2175008768) # external file attributes
194
- expect(br.read_4b).to eq(898921) # relative offset of local header
195
- expect(br.read_n(10)).to eq('a-file.txt') # the filename
196
- end
197
-
198
- it 'writes the file header for an entry that requires Zip64 extra because of the uncompressed size' do
199
- buf = StringIO.new
200
-
201
- subject.write_central_directory_file_header(io: buf, local_file_header_location: 898921,
202
- gp_flags: 555, storage_mode: 23,
203
- compressed_size: 901, uncompressed_size: 0xFFFFFFFFF + 3,
204
- mtime: Time.utc(2016, 2, 2, 14, 00), crc32: 89765,
205
- filename: 'a-file.txt', external_attrs: 123)
206
-
207
- br = ByteReader.new(buf)
208
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
209
- expect(br.read_2b).to eq(820) # version made by
210
- expect(br.read_2b).to eq(45) # version need to extract
211
- expect(br.read_2b).to eq(555) # general purpose bit flag (explicitly set to bogus value to ensure we pass it through)
212
- expect(br.read_2b).to eq(23) # compression method (explicitly set to bogus value)
213
- expect(br.read_2b).to eq(28672) # last mod file time
214
- expect(br.read_2b).to eq(18498) # last mod file date
215
- expect(br.read_4b).to eq(89765) # crc32
216
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
217
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
218
- expect(br.read_2b).to eq(10) # filename length
219
- expect(br.read_2b).to eq(32) # extra field length
220
- expect(br.read_2b).to eq(0) # file comment
221
- expect(br.read_2b).to eq(0xFFFF) # disk number, must be blanked to the maximum value because of The Unarchiver bug
222
- expect(br.read_2b).to eq(0) # internal file attributes
223
- expect(br.read_4b).to eq(2175008768) # external file attributes
224
- expect(br.read_4b).to eq(0xFFFFFFFF) # relative offset of local header
225
- expect(br.read_n(10)).to eq('a-file.txt') # the filename
226
-
227
- expect(buf).not_to be_eof
228
- expect(br.read_2b).to eq(1) # Zip64 extra tag
229
- expect(br.read_2b).to eq(28) # Size of the Zip64 extra payload
230
- expect(br.read_8b).to eq(0xFFFFFFFFF + 3) # uncompressed size
231
- expect(br.read_8b).to eq(901) # compressed size
232
- expect(br.read_8b).to eq(898921) # local file header location
233
- end
234
-
235
- it 'writes the file header for an entry that requires Zip64 extra because of the compressed size' do
236
- buf = StringIO.new
237
-
238
- subject.write_central_directory_file_header(io: buf, local_file_header_location: 898921,
239
- gp_flags: 555, storage_mode: 23,
240
- compressed_size: 0xFFFFFFFFF + 3, uncompressed_size: 901, # the worst compression scheme in the universe
241
- mtime: Time.utc(2016, 2, 2, 14, 00), crc32: 89765,
242
- filename: 'a-file.txt', external_attrs: 123)
243
-
244
- br = ByteReader.new(buf)
245
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
246
- expect(br.read_2b).to eq(820) # version made by
247
- expect(br.read_2b).to eq(45) # version need to extract
248
- expect(br.read_2b).to eq(555) # general purpose bit flag (explicitly set to bogus value to ensure we pass it through)
249
- expect(br.read_2b).to eq(23) # compression method (explicitly set to bogus value)
250
- expect(br.read_2b).to eq(28672) # last mod file time
251
- expect(br.read_2b).to eq(18498) # last mod file date
252
- expect(br.read_4b).to eq(89765) # crc32
253
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
254
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
255
- expect(br.read_2b).to eq(10) # filename length
256
- expect(br.read_2b).to eq(32) # extra field length
257
- expect(br.read_2b).to eq(0) # file comment
258
- expect(br.read_2b).to eq(0xFFFF) # disk number, must be blanked to the maximum value because of The Unarchiver bug
259
- expect(br.read_2b).to eq(0) # internal file attributes
260
- expect(br.read_4b).to eq(2175008768) # external file attributes
261
- expect(br.read_4b).to eq(0xFFFFFFFF) # relative offset of local header
262
- expect(br.read_n(10)).to eq('a-file.txt') # the filename
263
-
264
- expect(buf).not_to be_eof
265
- expect(br.read_2b).to eq(1) # Zip64 extra tag
266
- expect(br.read_2b).to eq(28) # Size of the Zip64 extra payload
267
- expect(br.read_8b).to eq(901) # uncompressed size
268
- expect(br.read_8b).to eq(0xFFFFFFFFF + 3) # compressed size
269
- expect(br.read_8b).to eq(898921) # local file header location
270
- end
271
-
272
- it 'writes the file header for an entry that requires Zip64 extra because of the local file header offset being beyound 4GB' do
273
- buf = StringIO.new
274
-
275
- subject.write_central_directory_file_header(io: buf, local_file_header_location: 0xFFFFFFFFF + 1,
276
- gp_flags: 555, storage_mode: 23,
277
- compressed_size: 8981, uncompressed_size: 819891, # the worst compression scheme in the universe
278
- mtime: Time.utc(2016, 2, 2, 14, 00), crc32: 89765,
279
- filename: 'a-file.txt', external_attrs: 123)
280
-
281
- br = ByteReader.new(buf)
282
- expect(br.read_4b).to eq(0x02014b50) # Central directory entry sig
283
- expect(br.read_2b).to eq(820) # version made by
284
- expect(br.read_2b).to eq(45) # version need to extract
285
- expect(br.read_2b).to eq(555) # general purpose bit flag (explicitly set to bogus value to ensure we pass it through)
286
- expect(br.read_2b).to eq(23) # compression method (explicitly set to bogus value)
287
- expect(br.read_2b).to eq(28672) # last mod file time
288
- expect(br.read_2b).to eq(18498) # last mod file date
289
- expect(br.read_4b).to eq(89765) # crc32
290
- expect(br.read_4b).to eq(0xFFFFFFFF) # compressed size
291
- expect(br.read_4b).to eq(0xFFFFFFFF) # uncompressed size
292
- expect(br.read_2b).to eq(10) # filename length
293
- expect(br.read_2b).to eq(32) # extra field length
294
- expect(br.read_2b).to eq(0) # file comment
295
- expect(br.read_2b).to eq(0xFFFF) # disk number, must be blanked to the maximum value because of The Unarchiver bug
296
- expect(br.read_2b).to eq(0) # internal file attributes
297
- expect(br.read_4b).to eq(2175008768) # external file attributes
298
- expect(br.read_4b).to eq(0xFFFFFFFF) # relative offset of local header
299
- expect(br.read_n(10)).to eq('a-file.txt') # the filename
300
-
301
- expect(buf).not_to be_eof
302
- expect(br.read_2b).to eq(1) # Zip64 extra tag
303
- expect(br.read_2b).to eq(28) # Size of the Zip64 extra payload
304
- expect(br.read_8b).to eq(819891) # uncompressed size
305
- expect(br.read_8b).to eq(8981) # compressed size
306
- expect(br.read_8b).to eq(0xFFFFFFFFF + 1) # local file header location
307
- end
308
- end
309
-
310
- describe '#write_end_of_central_directory' do
311
- it 'writes out the EOCD with all markers for a small ZIP file with just a few entries' do
312
- buf = StringIO.new
313
-
314
- num_files = rand(8..190)
315
- subject.write_end_of_central_directory(io: buf, start_of_central_directory_location: 9091211,
316
- central_directory_size: 9091, num_files_in_archive: num_files)
317
-
318
- br = ByteReader.new(buf)
319
- expect(br.read_4b).to eq(0x06054b50) # EOCD signature
320
- expect(br.read_2b).to eq(0) # number of this disk
321
- expect(br.read_2b).to eq(0) # number of the disk with the EOCD record
322
- expect(br.read_2b).to eq(num_files) # number of files on this disk
323
- expect(br.read_2b).to eq(num_files) # number of files in central directory total (for all disks)
324
- expect(br.read_4b).to eq(9091) # size of the central directory (cdir records for all files)
325
- expect(br.read_4b).to eq(9091211) # start of central directory offset from the beginning of file/disk
326
-
327
- comment_length = br.read_2b
328
- expect(comment_length).not_to be_zero
329
-
330
- expect(br.read_n(comment_length)).to match(/ZipTricks/)
331
- end
332
-
333
- it 'writes out the custom comment' do
334
- buf = ''
335
- comment = 'Ohai mate'
336
- subject.write_end_of_central_directory(io: buf, start_of_central_directory_location: 9091211,
337
- central_directory_size: 9091, num_files_in_archive: 4, comment: comment)
338
-
339
- size_and_comment = buf[((comment.bytesize + 2) * -1)..-1]
340
- comment_size = size_and_comment.unpack('v')[0]
341
- expect(comment_size).to eq(comment.bytesize)
342
- end
343
-
344
- it 'writes out the Zip64 EOCD as well if the central directory is located beyound 4GB in the archive' do
345
- buf = StringIO.new
346
-
347
- num_files = rand(8..190)
348
- subject.write_end_of_central_directory(io: buf, start_of_central_directory_location: 0xFFFFFFFF + 3,
349
- central_directory_size: 9091, num_files_in_archive: num_files)
350
-
351
- br = ByteReader.new(buf)
352
-
353
- expect(br.read_4b).to eq(0x06064b50) # Zip64 EOCD signature
354
- expect(br.read_8b).to eq(44) # Zip64 EOCD record size
355
- expect(br.read_2b).to eq(820) # Version made by
356
- expect(br.read_2b).to eq(45) # Version needed to extract
357
- expect(br.read_4b).to eq(0) # Number of this disk
358
- expect(br.read_4b).to eq(0) # Number of the disk with the Zip64 EOCD record
359
- expect(br.read_8b).to eq(num_files) # Number of entries in the central directory of this disk
360
- expect(br.read_8b).to eq(num_files) # Number of entries in the central directories of all disks
361
- expect(br.read_8b).to eq(9091) # Central directory size
362
- expect(br.read_8b).to eq(0xFFFFFFFF + 3) # Start of central directory location
363
-
364
- expect(br.read_4b).to eq(0x07064b50) # Zip64 EOCD locator signature
365
- expect(br.read_4b).to eq(0) # Number of the disk with the EOCD locator signature
366
- expect(br.read_8b).to eq((0xFFFFFFFF + 3) + 9091) # Where the Zip64 EOCD record starts
367
- expect(br.read_4b).to eq(1) # Total number of disks
368
-
369
- # Then the usual EOCD record
370
- expect(br.read_4b).to eq(0x06054b50) # EOCD signature
371
- expect(br.read_2b).to eq(0) # number of this disk
372
- expect(br.read_2b).to eq(0) # number of the disk with the EOCD record
373
- expect(br.read_2b).to eq(0xFFFF) # number of files on this disk
374
- expect(br.read_2b).to eq(0xFFFF) # number of files in central directory total (for all disks)
375
- expect(br.read_4b).to eq(0xFFFFFFFF) # size of the central directory (cdir records for all files)
376
- expect(br.read_4b).to eq(0xFFFFFFFF) # start of central directory offset from the beginning of file/disk
377
-
378
- comment_length = br.read_2b
379
- expect(comment_length).not_to be_zero
380
- expect(br.read_n(comment_length)).to match(/ZipTricks/)
381
- end
382
-
383
- it 'writes out the Zip64 EOCD if the archive has more than 0xFFFF files' do
384
- buf = StringIO.new
385
-
386
- subject.write_end_of_central_directory(io: buf, start_of_central_directory_location: 123,
387
- central_directory_size: 9091, num_files_in_archive: 0xFFFF + 1)
388
-
389
- br = ByteReader.new(buf)
390
-
391
- expect(br.read_4b).to eq(0x06064b50) # Zip64 EOCD signature
392
- br.read_8b
393
- br.read_2b
394
- br.read_2b
395
- br.read_4b
396
- br.read_4b
397
- expect(br.read_8b).to eq(0xFFFF + 1) # Number of entries in the central directory of this disk
398
- expect(br.read_8b).to eq(0xFFFF + 1) # Number of entries in the central directories of all disks
399
- end
400
-
401
- it 'writes out the Zip64 EOCD if the central directory size exceeds 0xFFFFFFFF' do
402
- buf = StringIO.new
403
-
404
- subject.write_end_of_central_directory(io: buf, start_of_central_directory_location: 123,
405
- central_directory_size: 0xFFFFFFFF + 2, num_files_in_archive: 5)
406
-
407
- br = ByteReader.new(buf)
408
-
409
- expect(br.read_4b).to eq(0x06064b50) # Zip64 EOCD signature
410
- br.read_8b
411
- br.read_2b
412
- br.read_2b
413
- br.read_4b
414
- br.read_4b
415
- expect(br.read_8b).to eq(5) # Number of entries in the central directory of this disk
416
- expect(br.read_8b).to eq(5) # Number of entries in the central directories of all disks
417
- end
418
- end
419
- end