rubyzip 2.4.1 → 3.0.0.rc1

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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +419 -0
  3. data/LICENSE.md +24 -0
  4. data/README.md +137 -37
  5. data/Rakefile +11 -7
  6. data/lib/zip/central_directory.rb +169 -123
  7. data/lib/zip/compressor.rb +3 -1
  8. data/lib/zip/constants.rb +29 -21
  9. data/lib/zip/crypto/decrypted_io.rb +4 -2
  10. data/lib/zip/crypto/encryption.rb +4 -2
  11. data/lib/zip/crypto/null_encryption.rb +6 -4
  12. data/lib/zip/crypto/traditional_encryption.rb +8 -6
  13. data/lib/zip/decompressor.rb +4 -3
  14. data/lib/zip/deflater.rb +10 -8
  15. data/lib/zip/dirtyable.rb +32 -0
  16. data/lib/zip/dos_time.rb +43 -4
  17. data/lib/zip/entry.rb +333 -242
  18. data/lib/zip/entry_set.rb +11 -9
  19. data/lib/zip/errors.rb +136 -16
  20. data/lib/zip/extra_field/generic.rb +6 -13
  21. data/lib/zip/extra_field/ntfs.rb +6 -4
  22. data/lib/zip/extra_field/old_unix.rb +3 -1
  23. data/lib/zip/extra_field/universal_time.rb +3 -1
  24. data/lib/zip/extra_field/unix.rb +5 -3
  25. data/lib/zip/extra_field/unknown.rb +33 -0
  26. data/lib/zip/extra_field/zip64.rb +12 -5
  27. data/lib/zip/extra_field.rb +16 -22
  28. data/lib/zip/file.rb +166 -264
  29. data/lib/zip/file_split.rb +91 -0
  30. data/lib/zip/filesystem/dir.rb +86 -0
  31. data/lib/zip/filesystem/directory_iterator.rb +48 -0
  32. data/lib/zip/filesystem/file.rb +262 -0
  33. data/lib/zip/filesystem/file_stat.rb +110 -0
  34. data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
  35. data/lib/zip/filesystem.rb +27 -596
  36. data/lib/zip/inflater.rb +7 -5
  37. data/lib/zip/input_stream.rb +50 -50
  38. data/lib/zip/ioextras/abstract_input_stream.rb +16 -11
  39. data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
  40. data/lib/zip/ioextras.rb +7 -7
  41. data/lib/zip/null_compressor.rb +3 -1
  42. data/lib/zip/null_decompressor.rb +3 -1
  43. data/lib/zip/null_input_stream.rb +3 -1
  44. data/lib/zip/output_stream.rb +55 -56
  45. data/lib/zip/pass_thru_compressor.rb +3 -1
  46. data/lib/zip/pass_thru_decompressor.rb +4 -2
  47. data/lib/zip/streamable_directory.rb +3 -1
  48. data/lib/zip/streamable_stream.rb +3 -0
  49. data/lib/zip/version.rb +3 -1
  50. data/lib/zip.rb +18 -22
  51. data/rubyzip.gemspec +39 -0
  52. data/samples/example.rb +8 -3
  53. data/samples/example_filesystem.rb +3 -2
  54. data/samples/example_recursive.rb +3 -1
  55. data/samples/gtk_ruby_zip.rb +4 -2
  56. data/samples/qtzip.rb +6 -5
  57. data/samples/write_simple.rb +2 -1
  58. data/samples/zipfind.rb +1 -0
  59. metadata +87 -51
  60. data/TODO +0 -15
  61. data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
@@ -1,45 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require_relative 'dirtyable'
6
+
1
7
  module Zip
2
- class CentralDirectory
3
- include Enumerable
8
+ class CentralDirectory # :nodoc:
9
+ extend Forwardable
10
+ include Dirtyable
11
+
12
+ END_OF_CD_SIG = 0x06054b50
13
+ ZIP64_END_OF_CD_SIG = 0x06064b50
14
+ ZIP64_EOCD_LOCATOR_SIG = 0x07064b50
4
15
 
5
- END_OF_CDS = 0x06054b50
6
- ZIP64_END_OF_CDS = 0x06064b50
7
- ZIP64_EOCD_LOCATOR = 0x07064b50
8
- MAX_END_OF_CDS_SIZE = 65_536 + 18
9
16
  STATIC_EOCD_SIZE = 22
17
+ ZIP64_STATIC_EOCD_SIZE = 56
18
+ ZIP64_EOCD_LOC_SIZE = 20
19
+ MAX_FILE_COMMENT_SIZE = (1 << 16) - 1
20
+ MAX_END_OF_CD_SIZE =
21
+ MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE + ZIP64_EOCD_LOC_SIZE
10
22
 
11
- attr_reader :comment
23
+ attr_accessor :comment
12
24
 
13
- # Returns an Enumerable containing the entries.
14
- def entries
15
- @entry_set.entries
16
- end
25
+ def_delegators :@entry_set,
26
+ :<<, :delete, :each, :entries, :find_entry, :glob,
27
+ :include?, :size
28
+
29
+ mark_dirty :<<, :comment=, :delete
17
30
 
18
- def initialize(entries = EntrySet.new, comment = '') #:nodoc:
19
- super()
31
+ def initialize(entries = EntrySet.new, comment = '') # :nodoc:
32
+ super(dirty_on_create: false)
20
33
  @entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
21
34
  @comment = comment
22
35
  end
23
36
 
24
- def write_to_stream(io) #:nodoc:
37
+ def read_from_stream(io)
38
+ read_eocds(io)
39
+ read_central_directory_entries(io)
40
+ end
41
+
42
+ def write_to_stream(io) # :nodoc:
25
43
  cdir_offset = io.tell
26
44
  @entry_set.each { |entry| entry.write_c_dir_entry(io) }
27
45
  eocd_offset = io.tell
28
46
  cdir_size = eocd_offset - cdir_offset
29
- if ::Zip.write_zip64_support
30
- need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF
31
- need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] }
32
- if need_zip64_eocd
33
- write_64_e_o_c_d(io, cdir_offset, cdir_size)
34
- write_64_eocd_locator(io, eocd_offset)
35
- end
47
+ if Zip.write_zip64_support &&
48
+ (cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF)
49
+ write_64_e_o_c_d(io, cdir_offset, cdir_size)
50
+ write_64_eocd_locator(io, eocd_offset)
36
51
  end
37
52
  write_e_o_c_d(io, cdir_offset, cdir_size)
38
53
  end
39
54
 
40
- def write_e_o_c_d(io, offset, cdir_size) #:nodoc:
55
+ # Reads the End of Central Directory Record (and the Zip64 equivalent if
56
+ # needs be) and returns the number of entries in the archive. This is a
57
+ # convenience method that avoids reading in all of the entry data to get a
58
+ # very quick entry count.
59
+ def count_entries(io)
60
+ read_eocds(io)
61
+ @size
62
+ end
63
+
64
+ def ==(other) # :nodoc:
65
+ return false unless other.kind_of?(CentralDirectory)
66
+
67
+ @entry_set.entries.sort == other.entries.sort && comment == other.comment
68
+ end
69
+
70
+ private
71
+
72
+ def write_e_o_c_d(io, offset, cdir_size) # :nodoc:
41
73
  tmp = [
42
- END_OF_CDS,
74
+ END_OF_CD_SIG,
43
75
  0, # @numberOfThisDisk
44
76
  0, # @numberOfDiskWithStartOfCDir
45
77
  @entry_set ? [@entry_set.size, 0xFFFF].min : 0,
@@ -52,11 +84,9 @@ module Zip
52
84
  io << @comment
53
85
  end
54
86
 
55
- private :write_e_o_c_d
56
-
57
- def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
87
+ def write_64_e_o_c_d(io, offset, cdir_size) # :nodoc:
58
88
  tmp = [
59
- ZIP64_END_OF_CDS,
89
+ ZIP64_END_OF_CD_SIG,
60
90
  44, # size of zip64 end of central directory record (excludes signature and field itself)
61
91
  VERSION_MADE_BY,
62
92
  VERSION_NEEDED_TO_EXTRACT_ZIP64,
@@ -70,11 +100,9 @@ module Zip
70
100
  io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
71
101
  end
72
102
 
73
- private :write_64_e_o_c_d
74
-
75
103
  def write_64_eocd_locator(io, zip64_eocd_offset)
76
104
  tmp = [
77
- ZIP64_EOCD_LOCATOR,
105
+ ZIP64_EOCD_LOCATOR_SIG,
78
106
  0, # number of disk containing the start of zip64 eocd record
79
107
  zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk
80
108
  1 # total number of disks
@@ -82,127 +110,145 @@ module Zip
82
110
  io << tmp.pack('VVQ<V')
83
111
  end
84
112
 
85
- private :write_64_eocd_locator
86
-
87
- def read_64_e_o_c_d(buf) #:nodoc:
88
- buf = get_64_e_o_c_d(buf)
89
- @size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf)
90
- @version_made_by = Entry.read_zip_short(buf)
91
- @version_needed_for_extract = Entry.read_zip_short(buf)
92
- @number_of_this_disk = Entry.read_zip_long(buf)
93
- @number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf)
94
- @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf)
95
- @size = Entry.read_zip_64_long(buf)
96
- @size_in_bytes = Entry.read_zip_64_long(buf)
97
- @cdir_offset = Entry.read_zip_64_long(buf)
98
- @zip_64_extensible = buf.slice!(0, buf.bytesize)
99
- raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
113
+ def unpack_64_e_o_c_d(buffer) # :nodoc:
114
+ _, # ZIP64_END_OF_CD_SIG. We know we have this at this point.
115
+ @size_of_zip64_e_o_c_d,
116
+ @version_made_by,
117
+ @version_needed_for_extract,
118
+ @number_of_this_disk,
119
+ @number_of_disk_with_start_of_cdir,
120
+ @total_number_of_entries_in_cdir_on_this_disk,
121
+ @size,
122
+ @size_in_bytes,
123
+ @cdir_offset = buffer.unpack('VQ<vvVVQ<Q<Q<Q<')
124
+
125
+ zip64_extensible_data_size =
126
+ @size_of_zip64_e_o_c_d - ZIP64_STATIC_EOCD_SIZE + 12
127
+ @zip64_extensible_data = if zip64_extensible_data_size.zero?
128
+ ''
129
+ else
130
+ buffer.slice(
131
+ ZIP64_STATIC_EOCD_SIZE,
132
+ zip64_extensible_data_size
133
+ )
134
+ end
135
+ end
136
+
137
+ def unpack_64_eocd_locator(buffer) # :nodoc:
138
+ _, # ZIP64_EOCD_LOCATOR_SIG. We know we have this at this point.
139
+ _, zip64_eocd_offset, = buffer.unpack('VVQ<V')
140
+
141
+ zip64_eocd_offset
100
142
  end
101
143
 
102
- def read_e_o_c_d(buf) #:nodoc:
103
- buf = get_e_o_c_d(buf)
104
- @number_of_this_disk = Entry.read_zip_short(buf)
105
- @number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf)
106
- @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf)
107
- @size = Entry.read_zip_short(buf)
108
- @size_in_bytes = Entry.read_zip_long(buf)
109
- @cdir_offset = Entry.read_zip_long(buf)
110
- comment_length = Entry.read_zip_short(buf)
111
- @comment = if comment_length.to_i <= 0
112
- buf.slice!(0, buf.size)
113
- else
114
- buf.read(comment_length)
115
- end
116
- raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
144
+ def unpack_e_o_c_d(buffer) # :nodoc:
145
+ _, # END_OF_CD_SIG. We know we have this at this point.
146
+ num_disk,
147
+ num_disk_cdir,
148
+ num_cdir_disk,
149
+ num_entries,
150
+ size_in_bytes,
151
+ cdir_offset,
152
+ comment_length = buffer.unpack('VvvvvVVv')
153
+
154
+ @number_of_this_disk = num_disk unless num_disk == 0xFFFF
155
+ @number_of_disk_with_start_of_cdir = num_disk_cdir unless num_disk_cdir == 0xFFFF
156
+ @total_number_of_entries_in_cdir_on_this_disk = num_cdir_disk unless num_cdir_disk == 0xFFFF
157
+ @size = num_entries unless num_entries == 0xFFFF
158
+ @size_in_bytes = size_in_bytes unless size_in_bytes == 0xFFFFFFFF
159
+ @cdir_offset = cdir_offset unless cdir_offset == 0xFFFFFFFF
160
+
161
+ @comment = if comment_length.positive?
162
+ buffer.slice(STATIC_EOCD_SIZE, comment_length)
163
+ else
164
+ ''
165
+ end
117
166
  end
118
167
 
119
- def read_central_directory_entries(io) #:nodoc:
168
+ def read_central_directory_entries(io) # :nodoc:
169
+ # `StringIO` doesn't raise `EINVAL` if you seek beyond the current end,
170
+ # so we need to catch that *and* query `io#eof?` here.
171
+ eof = false
120
172
  begin
121
173
  io.seek(@cdir_offset, IO::SEEK_SET)
122
174
  rescue Errno::EINVAL
123
- raise Error, 'Zip consistency problem while reading central directory entry'
175
+ eof = true
124
176
  end
177
+ raise Error, 'Zip consistency problem while reading central directory entry' if eof || io.eof?
178
+
125
179
  @entry_set = EntrySet.new
126
180
  @size.times do
127
- @entry_set << Entry.read_c_dir_entry(io)
128
- end
129
- end
181
+ entry = Entry.read_c_dir_entry(io)
182
+ next unless entry
183
+
184
+ offset = if entry.zip64?
185
+ entry.extra['Zip64'].relative_header_offset
186
+ else
187
+ entry.local_header_offset
188
+ end
189
+
190
+ unless offset.nil?
191
+ io_save = io.tell
192
+ io.seek(offset, IO::SEEK_SET)
193
+ entry.read_extra_field(read_local_extra_field(io), local: true)
194
+ io.seek(io_save, IO::SEEK_SET)
195
+ end
130
196
 
131
- def read_from_stream(io) #:nodoc:
132
- buf = start_buf(io)
133
- if zip64_file?(buf)
134
- read_64_e_o_c_d(buf)
135
- else
136
- read_e_o_c_d(buf)
197
+ @entry_set << entry
137
198
  end
138
- read_central_directory_entries(io)
139
199
  end
140
200
 
141
- def get_e_o_c_d(buf) #:nodoc:
142
- sig_index = buf.rindex([END_OF_CDS].pack('V'))
143
- raise Error, 'Zip end of central directory signature not found' unless sig_index
144
-
145
- buf = buf.slice!((sig_index + 4)..(buf.bytesize))
146
-
147
- def buf.read(count)
148
- slice!(0, count)
149
- end
150
-
151
- buf
152
- end
201
+ def read_local_extra_field(io)
202
+ buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || ''
203
+ return '' unless buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
153
204
 
154
- def zip64_file?(buf)
155
- buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
156
- end
205
+ head, _, _, _, _, _, _, _, _, _, n_len, e_len = buf.unpack('VCCvvvvVVVvv')
206
+ return '' unless head == ::Zip::LOCAL_ENTRY_SIGNATURE
157
207
 
158
- def start_buf(io)
159
- begin
160
- io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
161
- rescue Errno::EINVAL
162
- io.seek(0, IO::SEEK_SET)
163
- end
164
- io.read
208
+ io.seek(n_len, IO::SEEK_CUR) # Skip over the entry name.
209
+ io.read(e_len)
165
210
  end
166
211
 
167
- def get_64_e_o_c_d(buf) #:nodoc:
168
- zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
169
- raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
212
+ def read_eocds(io) # :nodoc:
213
+ base_location, data = eocd_data(io)
170
214
 
171
- zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
172
- raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
215
+ eocd_location = data.rindex([END_OF_CD_SIG].pack('V'))
216
+ raise Error, 'Zip end of central directory signature not found' unless eocd_location
173
217
 
174
- buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
218
+ zip64_eocd_locator = data.rindex([ZIP64_EOCD_LOCATOR_SIG].pack('V'))
175
219
 
176
- def buf.read(count)
177
- slice!(0, count)
178
- end
220
+ if zip64_eocd_locator
221
+ zip64_eocd_location = data.rindex([ZIP64_END_OF_CD_SIG].pack('V'))
179
222
 
180
- buf
181
- end
223
+ zip64_eocd_data =
224
+ if zip64_eocd_location
225
+ data.slice(zip64_eocd_location..zip64_eocd_locator)
226
+ else
227
+ zip64_eocd_location = unpack_64_eocd_locator(
228
+ data.slice(zip64_eocd_locator..eocd_location)
229
+ )
230
+ unless zip64_eocd_location
231
+ raise Error, 'Zip64 end of central directory signature not found'
232
+ end
182
233
 
183
- # For iterating over the entries.
184
- def each(&a_proc)
185
- @entry_set.each(&a_proc)
186
- end
234
+ io.seek(zip64_eocd_location, IO::SEEK_SET)
235
+ io.read(base_location + zip64_eocd_locator - zip64_eocd_location)
236
+ end
187
237
 
188
- # Returns the number of entries in the central directory (and
189
- # consequently in the zip archive).
190
- def size
191
- @entry_set.size
192
- end
238
+ unpack_64_e_o_c_d(zip64_eocd_data)
239
+ end
193
240
 
194
- def self.read_from_stream(io) #:nodoc:
195
- cdir = new
196
- cdir.read_from_stream(io)
197
- cdir
198
- rescue Error
199
- nil
241
+ unpack_e_o_c_d(data.slice(eocd_location..-1))
200
242
  end
201
243
 
202
- def ==(other) #:nodoc:
203
- return false unless other.kind_of?(CentralDirectory)
244
+ def eocd_data(io)
245
+ begin
246
+ io.seek(-MAX_END_OF_CD_SIZE, IO::SEEK_END)
247
+ rescue Errno::EINVAL
248
+ io.seek(0, IO::SEEK_SET)
249
+ end
204
250
 
205
- @entry_set.entries.sort == other.entries.sort && comment == other.comment
251
+ [io.tell, io.read]
206
252
  end
207
253
  end
208
254
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Compressor #:nodoc:all
4
+ class Compressor # :nodoc:all
3
5
  def finish; end
4
6
  end
5
7
  end
data/lib/zip/constants.rb CHANGED
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
4
+ # :stopdoc:
5
+
2
6
  RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
3
7
 
4
8
  CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
@@ -11,6 +15,8 @@ module Zip
11
15
  VERSION_NEEDED_TO_EXTRACT = 20
12
16
  VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
13
17
 
18
+ SPLIT_FILE_SIGNATURE = 0x08074b50
19
+
14
20
  FILE_TYPE_FILE = 0o10
15
21
  FILE_TYPE_DIR = 0o04
16
22
  FILE_TYPE_SYMLINK = 0o12
@@ -38,27 +44,27 @@ module Zip
38
44
  FSTYPE_ATHEOS = 30
39
45
 
40
46
  FSTYPES = {
41
- FSTYPE_FAT => 'FAT'.freeze,
42
- FSTYPE_AMIGA => 'Amiga'.freeze,
43
- FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze,
44
- FSTYPE_UNIX => 'Unix'.freeze,
45
- FSTYPE_VM_CMS => 'VM/CMS'.freeze,
46
- FSTYPE_ATARI => 'Atari ST'.freeze,
47
- FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze,
48
- FSTYPE_MAC => 'Macintosh'.freeze,
49
- FSTYPE_Z_SYSTEM => 'Z-System'.freeze,
50
- FSTYPE_CPM => 'CP/M'.freeze,
51
- FSTYPE_TOPS20 => 'TOPS-20'.freeze,
52
- FSTYPE_NTFS => 'NTFS'.freeze,
53
- FSTYPE_QDOS => 'SMS/QDOS'.freeze,
54
- FSTYPE_ACORN => 'Acorn RISC OS'.freeze,
55
- FSTYPE_VFAT => 'Win32 VFAT'.freeze,
56
- FSTYPE_MVS => 'MVS'.freeze,
57
- FSTYPE_BEOS => 'BeOS'.freeze,
58
- FSTYPE_TANDEM => 'Tandem NSK'.freeze,
59
- FSTYPE_THEOS => 'Theos'.freeze,
60
- FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
61
- FSTYPE_ATHEOS => 'AtheOS'.freeze
47
+ FSTYPE_FAT => 'FAT',
48
+ FSTYPE_AMIGA => 'Amiga',
49
+ FSTYPE_VMS => 'VMS (Vax or Alpha AXP)',
50
+ FSTYPE_UNIX => 'Unix',
51
+ FSTYPE_VM_CMS => 'VM/CMS',
52
+ FSTYPE_ATARI => 'Atari ST',
53
+ FSTYPE_HPFS => 'OS/2 or NT HPFS',
54
+ FSTYPE_MAC => 'Macintosh',
55
+ FSTYPE_Z_SYSTEM => 'Z-System',
56
+ FSTYPE_CPM => 'CP/M',
57
+ FSTYPE_TOPS20 => 'TOPS-20',
58
+ FSTYPE_NTFS => 'NTFS',
59
+ FSTYPE_QDOS => 'SMS/QDOS',
60
+ FSTYPE_ACORN => 'Acorn RISC OS',
61
+ FSTYPE_VFAT => 'Win32 VFAT',
62
+ FSTYPE_MVS => 'MVS',
63
+ FSTYPE_BEOS => 'BeOS',
64
+ FSTYPE_TANDEM => 'Tandem NSK',
65
+ FSTYPE_THEOS => 'Theos',
66
+ FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)',
67
+ FSTYPE_ATHEOS => 'AtheOS'
62
68
  }.freeze
63
69
 
64
70
  COMPRESSION_METHOD_STORE = 0
@@ -112,4 +118,6 @@ module Zip
112
118
  COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
113
119
  COMPRESSION_METHOD_AES => 'AES encryption'
114
120
  }.freeze
121
+
122
+ # :startdoc:
115
123
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class DecryptedIo #:nodoc:all
4
+ class DecryptedIo # :nodoc:all
3
5
  CHUNK_SIZE = 32_768
4
6
 
5
7
  def initialize(io, decrypter)
@@ -16,7 +18,7 @@ module Zip
16
18
  buffer << produce_input
17
19
  end
18
20
 
19
- outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize)))
21
+ outbuf.replace(buffer.slice!(0...(length || buffer.bytesize)))
20
22
  end
21
23
 
22
24
  private
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Encrypter #:nodoc:all
4
+ class Encrypter # :nodoc:all
3
5
  end
4
6
 
5
- class Decrypter
7
+ class Decrypter # :nodoc:all
6
8
  end
7
9
  end
8
10
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullEncryption
4
+ module NullEncryption # :nodoc:
3
5
  def header_bytesize
4
6
  0
5
7
  end
@@ -9,7 +11,7 @@ module Zip
9
11
  end
10
12
  end
11
13
 
12
- class NullEncrypter < Encrypter
14
+ class NullEncrypter < Encrypter # :nodoc:
13
15
  include NullEncryption
14
16
 
15
17
  def header(_mtime)
@@ -20,14 +22,14 @@ module Zip
20
22
  data
21
23
  end
22
24
 
23
- def data_descriptor(_crc32, _compressed_size, _uncomprssed_size)
25
+ def data_descriptor(_crc32, _compressed_size, _uncompressed_size)
24
26
  ''
25
27
  end
26
28
 
27
29
  def reset!; end
28
30
  end
29
31
 
30
- class NullDecrypter < Decrypter
32
+ class NullDecrypter < Decrypter # :nodoc:
31
33
  include NullEncryption
32
34
 
33
35
  def decrypt(data)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module TraditionalEncryption
4
+ module TraditionalEncryption # :nodoc:
3
5
  def initialize(password)
4
6
  @password = password
5
7
  reset_keys!
@@ -26,7 +28,7 @@ module Zip
26
28
 
27
29
  def update_keys(num)
28
30
  @key0 = ~Zlib.crc32(num, ~@key0)
29
- @key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
31
+ @key1 = (((@key1 + (@key0 & 0xff)) * 134_775_813) + 1) & 0xffffffff
30
32
  @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
31
33
  end
32
34
 
@@ -36,7 +38,7 @@ module Zip
36
38
  end
37
39
  end
38
40
 
39
- class TraditionalEncrypter < Encrypter
41
+ class TraditionalEncrypter < Encrypter # :nodoc:
40
42
  include TraditionalEncryption
41
43
 
42
44
  def header(mtime)
@@ -53,8 +55,8 @@ module Zip
53
55
  data.unpack('C*').map { |x| encode x }.pack('C*')
54
56
  end
55
57
 
56
- def data_descriptor(crc32, compressed_size, uncomprssed_size)
57
- [0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV')
58
+ def data_descriptor(crc32, compressed_size, uncompressed_size)
59
+ [0x08074b50, crc32, compressed_size, uncompressed_size].pack('VVVV')
58
60
  end
59
61
 
60
62
  def reset!
@@ -70,7 +72,7 @@ module Zip
70
72
  end
71
73
  end
72
74
 
73
- class TraditionalDecrypter < Decrypter
75
+ class TraditionalDecrypter < Decrypter # :nodoc:
74
76
  include TraditionalEncryption
75
77
 
76
78
  def decrypt(data)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Decompressor #:nodoc:all
4
+ class Decompressor # :nodoc:all
3
5
  CHUNK_SIZE = 32_768
4
6
 
5
7
  def self.decompressor_classes
@@ -14,8 +16,7 @@ module Zip
14
16
  decompressor_classes[compression_method]
15
17
  end
16
18
 
17
- attr_reader :input_stream
18
- attr_reader :decompressed_size
19
+ attr_reader :decompressed_size, :input_stream
19
20
 
20
21
  def initialize(input_stream, decompressed_size = nil)
21
22
  super()
data/lib/zip/deflater.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Deflater < Compressor #:nodoc:all
4
+ class Deflater < Compressor # :nodoc:all
3
5
  def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
4
6
  super()
5
7
  @output_stream = output_stream
@@ -13,16 +15,16 @@ module Zip
13
15
  val = data.to_s
14
16
  @crc = Zlib.crc32(val, @crc)
15
17
  @size += val.bytesize
16
- buffer = @zlib_deflater.deflate(data)
17
- if buffer.empty?
18
- @output_stream
19
- else
20
- @output_stream << @encrypter.encrypt(buffer)
21
- end
18
+ buffer = @zlib_deflater.deflate(data, Zlib::SYNC_FLUSH)
19
+ return @output_stream if buffer.empty?
20
+
21
+ @output_stream << @encrypter.encrypt(buffer)
22
22
  end
23
23
 
24
24
  def finish
25
- @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
25
+ buffer = @zlib_deflater.finish
26
+ @output_stream << @encrypter.encrypt(buffer) unless buffer.empty?
27
+ @zlib_deflater.close
26
28
  end
27
29
 
28
30
  attr_reader :size, :crc
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zip
4
+ module Dirtyable # :nodoc:all
5
+ def initialize(dirty_on_create: true)
6
+ @dirty = dirty_on_create
7
+ end
8
+
9
+ def dirty?
10
+ @dirty
11
+ end
12
+
13
+ module ClassMethods # :nodoc:
14
+ def mark_dirty(*symbols) # :nodoc:
15
+ # Move the original method and call it after we've set the dirty flag.
16
+ symbols.each do |symbol|
17
+ orig_name = "orig_#{symbol}"
18
+ alias_method orig_name, symbol
19
+
20
+ define_method(symbol) do |param|
21
+ @dirty = true
22
+ send(orig_name, param)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.included(base)
29
+ base.extend(ClassMethods)
30
+ end
31
+ end
32
+ end