rubyzip 2.3.1 → 3.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +368 -0
  3. data/README.md +122 -39
  4. data/Rakefile +11 -7
  5. data/lib/zip/central_directory.rb +164 -118
  6. data/lib/zip/compressor.rb +3 -1
  7. data/lib/zip/constants.rb +25 -21
  8. data/lib/zip/crypto/decrypted_io.rb +3 -1
  9. data/lib/zip/crypto/encryption.rb +4 -2
  10. data/lib/zip/crypto/null_encryption.rb +5 -3
  11. data/lib/zip/crypto/traditional_encryption.rb +5 -3
  12. data/lib/zip/decompressor.rb +4 -3
  13. data/lib/zip/deflater.rb +10 -8
  14. data/lib/zip/dirtyable.rb +32 -0
  15. data/lib/zip/dos_time.rb +41 -5
  16. data/lib/zip/entry.rb +273 -170
  17. data/lib/zip/entry_set.rb +9 -7
  18. data/lib/zip/errors.rb +115 -16
  19. data/lib/zip/extra_field/generic.rb +3 -10
  20. data/lib/zip/extra_field/ntfs.rb +4 -2
  21. data/lib/zip/extra_field/old_unix.rb +3 -1
  22. data/lib/zip/extra_field/universal_time.rb +3 -1
  23. data/lib/zip/extra_field/unix.rb +5 -3
  24. data/lib/zip/extra_field/unknown.rb +33 -0
  25. data/lib/zip/extra_field/zip64.rb +12 -5
  26. data/lib/zip/extra_field.rb +15 -21
  27. data/lib/zip/file.rb +152 -221
  28. data/lib/zip/file_split.rb +97 -0
  29. data/lib/zip/filesystem/dir.rb +86 -0
  30. data/lib/zip/filesystem/directory_iterator.rb +48 -0
  31. data/lib/zip/filesystem/file.rb +262 -0
  32. data/lib/zip/filesystem/file_stat.rb +110 -0
  33. data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
  34. data/lib/zip/filesystem.rb +26 -595
  35. data/lib/zip/inflater.rb +6 -4
  36. data/lib/zip/input_stream.rb +43 -25
  37. data/lib/zip/ioextras/abstract_input_stream.rb +13 -8
  38. data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
  39. data/lib/zip/ioextras.rb +6 -6
  40. data/lib/zip/null_compressor.rb +3 -1
  41. data/lib/zip/null_decompressor.rb +3 -1
  42. data/lib/zip/null_input_stream.rb +3 -1
  43. data/lib/zip/output_stream.rb +45 -39
  44. data/lib/zip/pass_thru_compressor.rb +3 -1
  45. data/lib/zip/pass_thru_decompressor.rb +4 -2
  46. data/lib/zip/streamable_directory.rb +3 -1
  47. data/lib/zip/streamable_stream.rb +3 -0
  48. data/lib/zip/version.rb +3 -1
  49. data/lib/zip.rb +15 -22
  50. data/rubyzip.gemspec +38 -0
  51. data/samples/example.rb +8 -3
  52. data/samples/example_filesystem.rb +2 -1
  53. data/samples/example_recursive.rb +3 -1
  54. data/samples/gtk_ruby_zip.rb +4 -2
  55. data/samples/qtzip.rb +6 -5
  56. data/samples/write_simple.rb +1 -0
  57. data/samples/zipfind.rb +1 -0
  58. metadata +83 -35
  59. data/TODO +0 -15
  60. data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
data/Rakefile CHANGED
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rake/testtask'
5
+ require 'rdoc/task'
3
6
  require 'rubocop/rake_task'
4
7
 
5
8
  task default: :test
@@ -11,11 +14,12 @@ Rake::TestTask.new(:test) do |test|
11
14
  test.verbose = true
12
15
  end
13
16
 
14
- RuboCop::RakeTask.new
17
+ RDoc::Task.new do |rdoc|
18
+ rdoc.main = 'README.md'
19
+ rdoc.rdoc_files.include('README.md', 'lib/**/*.rb')
20
+ rdoc.options << '--markup=markdown'
21
+ rdoc.options << '--tab-width=2'
22
+ rdoc.options << "-t Rubyzip version #{::Zip::VERSION}"
23
+ end
15
24
 
16
- # Rake::TestTask.new(:zip64_full_test) do |test|
17
- # test.libs << File.join(File.dirname(__FILE__), 'lib')
18
- # test.libs << File.join(File.dirname(__FILE__), 'test')
19
- # test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb')
20
- # test.verbose = true
21
- # end
25
+ RuboCop::RakeTask.new
@@ -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
31
  def initialize(entries = EntrySet.new, comment = '') #:nodoc:
19
- super()
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
 
37
+ def read_from_stream(io)
38
+ read_eocds(io)
39
+ read_central_directory_entries(io)
40
+ end
41
+
24
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
 
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
+
40
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
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
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
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
144
204
 
145
- buf = buf.slice!((sig_index + 4)..(buf.bytesize))
205
+ head, _, _, _, _, _, _, _, _, _, n_len, e_len = buf.unpack('VCCvvvvVVVvv')
206
+ return '' unless head == ::Zip::LOCAL_ENTRY_SIGNATURE
146
207
 
147
- def buf.read(count)
148
- slice!(0, count)
149
- end
150
-
151
- buf
208
+ io.seek(n_len, IO::SEEK_CUR) # Skip over the entry name.
209
+ io.read(e_len)
152
210
  end
153
211
 
154
- def zip64_file?(buf)
155
- buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
156
- end
212
+ def read_eocds(io) #:nodoc:
213
+ base_location, data = eocd_data(io)
157
214
 
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
165
- end
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
166
217
 
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
218
+ zip64_eocd_locator = data.rindex([ZIP64_EOCD_LOCATOR_SIG].pack('V'))
170
219
 
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
220
+ if zip64_eocd_locator
221
+ zip64_eocd_location = data.rindex([ZIP64_END_OF_CD_SIG].pack('V'))
173
222
 
174
- buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
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
175
233
 
176
- def buf.read(count)
177
- slice!(0, count)
178
- end
234
+ io.seek(zip64_eocd_location, IO::SEEK_SET)
235
+ io.read(base_location + zip64_eocd_locator - zip64_eocd_location)
236
+ end
179
237
 
180
- buf
181
- end
182
-
183
- # For iterating over the entries.
184
- def each(&a_proc)
185
- @entry_set.each(&a_proc)
186
- end
187
-
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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
4
  RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
3
5
 
@@ -11,6 +13,8 @@ module Zip
11
13
  VERSION_NEEDED_TO_EXTRACT = 20
12
14
  VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
13
15
 
16
+ SPLIT_FILE_SIGNATURE = 0x08074b50
17
+
14
18
  FILE_TYPE_FILE = 0o10
15
19
  FILE_TYPE_DIR = 0o04
16
20
  FILE_TYPE_SYMLINK = 0o12
@@ -38,27 +42,27 @@ module Zip
38
42
  FSTYPE_ATHEOS = 30
39
43
 
40
44
  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
45
+ FSTYPE_FAT => 'FAT',
46
+ FSTYPE_AMIGA => 'Amiga',
47
+ FSTYPE_VMS => 'VMS (Vax or Alpha AXP)',
48
+ FSTYPE_UNIX => 'Unix',
49
+ FSTYPE_VM_CMS => 'VM/CMS',
50
+ FSTYPE_ATARI => 'Atari ST',
51
+ FSTYPE_HPFS => 'OS/2 or NT HPFS',
52
+ FSTYPE_MAC => 'Macintosh',
53
+ FSTYPE_Z_SYSTEM => 'Z-System',
54
+ FSTYPE_CPM => 'CP/M',
55
+ FSTYPE_TOPS20 => 'TOPS-20',
56
+ FSTYPE_NTFS => 'NTFS',
57
+ FSTYPE_QDOS => 'SMS/QDOS',
58
+ FSTYPE_ACORN => 'Acorn RISC OS',
59
+ FSTYPE_VFAT => 'Win32 VFAT',
60
+ FSTYPE_MVS => 'MVS',
61
+ FSTYPE_BEOS => 'BeOS',
62
+ FSTYPE_TANDEM => 'Tandem NSK',
63
+ FSTYPE_THEOS => 'Theos',
64
+ FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)',
65
+ FSTYPE_ATHEOS => 'AtheOS'
62
66
  }.freeze
63
67
 
64
68
  COMPRESSION_METHOD_STORE = 0
@@ -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)
@@ -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)
@@ -27,7 +29,7 @@ module Zip
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!
@@ -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)
@@ -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