archive-zip 0.3.0 → 0.4.0
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.
- data/HACKING +25 -42
- data/NEWS +25 -0
- data/README +2 -2
- data/Rakefile +202 -0
- data/TODO +5 -0
- data/default.mspec +8 -0
- data/lib/archive/support/binary_stringio.rb +23 -0
- data/lib/archive/support/integer.rb +13 -0
- data/lib/archive/support/io-like.rb +3 -1
- data/lib/archive/support/ioextensions.rb +16 -0
- data/lib/archive/support/iowindow.rb +10 -18
- data/lib/archive/support/time.rb +2 -0
- data/lib/archive/support/zlib.rb +298 -71
- data/lib/archive/zip.rb +161 -139
- data/lib/archive/zip/codec.rb +2 -0
- data/lib/archive/zip/codec/deflate.rb +59 -11
- data/lib/archive/zip/codec/null_encryption.rb +75 -14
- data/lib/archive/zip/codec/store.rb +75 -26
- data/lib/archive/zip/codec/traditional_encryption.rb +146 -35
- data/lib/archive/zip/data_descriptor.rb +6 -4
- data/lib/archive/zip/entry.rb +184 -132
- data/lib/archive/zip/error.rb +2 -0
- data/lib/archive/zip/extra_field.rb +20 -6
- data/lib/archive/zip/extra_field/extended_timestamp.rb +141 -60
- data/lib/archive/zip/extra_field/raw.rb +70 -12
- data/lib/archive/zip/extra_field/unix.rb +58 -16
- data/lib/archive/zip/version.rb +6 -0
- data/spec/archive/zip/codec/deflate/compress/checksum_spec.rb +42 -0
- data/spec/archive/zip/codec/deflate/compress/close_spec.rb +44 -0
- data/spec/archive/zip/codec/deflate/compress/crc32_spec.rb +21 -0
- data/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb +67 -0
- data/spec/archive/zip/codec/deflate/compress/new_spec.rb +37 -0
- data/spec/archive/zip/codec/deflate/compress/open_spec.rb +46 -0
- data/spec/archive/zip/codec/deflate/compress/write_spec.rb +109 -0
- data/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb +18 -0
- data/spec/archive/zip/codec/deflate/decompress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb +18 -0
- data/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb +67 -0
- data/spec/archive/zip/codec/deflate/decompress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/deflate/decompress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/deflate/fixtures/classes.rb +25 -0
- data/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin +1 -0
- data/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin +0 -0
- data/spec/archive/zip/codec/deflate/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb +33 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb +14 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb +27 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb +24 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb +25 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb +57 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb +21 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb +33 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb +14 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb +27 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb +26 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb +50 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb +29 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb +29 -0
- data/spec/archive/zip/codec/null_encryption/fixtures/classes.rb +12 -0
- data/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/store/compress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb +68 -0
- data/spec/archive/zip/codec/store/compress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/store/compress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/store/compress/rewind_spec.rb +26 -0
- data/spec/archive/zip/codec/store/compress/seek_spec.rb +50 -0
- data/spec/archive/zip/codec/store/compress/tell_spec.rb +29 -0
- data/spec/archive/zip/codec/store/compress/write_spec.rb +29 -0
- data/spec/archive/zip/codec/store/decompress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb +68 -0
- data/spec/archive/zip/codec/store/decompress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/store/decompress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/store/decompress/read_spec.rb +24 -0
- data/spec/archive/zip/codec/store/decompress/rewind_spec.rb +25 -0
- data/spec/archive/zip/codec/store/decompress/seek_spec.rb +57 -0
- data/spec/archive/zip/codec/store/decompress/tell_spec.rb +21 -0
- data/spec/archive/zip/codec/store/fixtures/classes.rb +12 -0
- data/spec/archive/zip/codec/store/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb +64 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb +18 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb +39 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb +126 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb +38 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb +82 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb +25 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb +64 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb +18 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb +39 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb +41 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb +75 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb +42 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb +127 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb +27 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin +0 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt +10 -0
- data/spec/binary_stringio/new_spec.rb +34 -0
- data/spec/binary_stringio/set_encoding_spec.rb +14 -0
- data/spec/ioextensions/read_exactly_spec.rb +50 -0
- data/spec/zlib/fixtures/classes.rb +65 -0
- data/spec/zlib/fixtures/compressed_file.bin +1 -0
- data/spec/zlib/fixtures/compressed_file_gzip.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_huffman.bin +2 -0
- data/spec/zlib/fixtures/compressed_file_minmem.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_minwin.bin +1 -0
- data/spec/zlib/fixtures/compressed_file_nocomp.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_raw.bin +1 -0
- data/spec/zlib/fixtures/raw_file.txt +10 -0
- data/spec/zlib/zreader/checksum_spec.rb +40 -0
- data/spec/zlib/zreader/close_spec.rb +14 -0
- data/spec/zlib/zreader/compressed_size_spec.rb +18 -0
- data/spec/zlib/zreader/new_spec.rb +41 -0
- data/spec/zlib/zreader/open_spec.rb +49 -0
- data/spec/zlib/zreader/read_spec.rb +47 -0
- data/spec/zlib/zreader/rewind_spec.rb +23 -0
- data/spec/zlib/zreader/seek_spec.rb +55 -0
- data/spec/zlib/zreader/tell_spec.rb +21 -0
- data/spec/zlib/zreader/uncompressed_size_spec.rb +18 -0
- data/spec/zlib/zwriter/checksum_spec.rb +41 -0
- data/spec/zlib/zwriter/close_spec.rb +14 -0
- data/spec/zlib/zwriter/compressed_size_spec.rb +19 -0
- data/spec/zlib/zwriter/new_spec.rb +64 -0
- data/spec/zlib/zwriter/open_spec.rb +68 -0
- data/spec/zlib/zwriter/rewind_spec.rb +26 -0
- data/spec/zlib/zwriter/seek_spec.rb +54 -0
- data/spec/zlib/zwriter/tell_spec.rb +29 -0
- data/spec/zlib/zwriter/uncompressed_size_spec.rb +19 -0
- data/spec/zlib/zwriter/write_spec.rb +28 -0
- data/spec_helper.rb +49 -0
- metadata +296 -74
- data/MANIFEST +0 -27
- data/lib/archive/support/io.rb +0 -14
- data/lib/archive/support/stringio.rb +0 -22
data/lib/archive/zip.rb
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
# encoding: UTF-8
|
|
2
2
|
|
|
3
3
|
require 'fileutils'
|
|
4
4
|
require 'set'
|
|
5
5
|
require 'tempfile'
|
|
6
6
|
|
|
7
|
-
require 'archive/support/
|
|
7
|
+
require 'archive/support/ioextensions'
|
|
8
8
|
require 'archive/support/iowindow'
|
|
9
|
-
require 'archive/support/stringio'
|
|
10
9
|
require 'archive/support/time'
|
|
11
10
|
require 'archive/support/zlib'
|
|
12
11
|
require 'archive/zip/codec'
|
|
@@ -44,20 +43,61 @@ module Archive # :nodoc:
|
|
|
44
43
|
DD_SIGNATURE = "PK\x7\x8" # 0x08074b50
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
# Creates or possibly updates an archive using _paths_ for new contents.
|
|
47
|
+
#
|
|
48
|
+
# If _archive_ is a String, it is treated as a file path which will receive
|
|
49
|
+
# the archive contents. If the file already exists, it is assumed to be an
|
|
50
|
+
# archive and will be updated "in place". Otherwise, a new archive
|
|
51
|
+
# is created. The archive will be closed once written.
|
|
52
|
+
#
|
|
53
|
+
# If _archive_ has any other kind of value, it is treated as a writable
|
|
54
|
+
# IO-like object which will be left open after the completion of this
|
|
55
|
+
# method.
|
|
56
|
+
#
|
|
57
|
+
# <b>NOTE:</b> No attempt is made to prevent adding multiple entries with
|
|
58
|
+
# the same archive path.
|
|
59
|
+
#
|
|
60
|
+
# See the instance method #archive for more information about _paths_ and
|
|
61
|
+
# _options_.
|
|
62
|
+
def self.archive(archive, paths, options = {})
|
|
63
|
+
if archive.kind_of?(String) && File.exist?(archive) then
|
|
64
|
+
# Update the archive "in place".
|
|
65
|
+
tmp_archive_path = nil
|
|
66
|
+
File.open(archive) do |archive_in|
|
|
67
|
+
Tempfile.open(*File.split(archive_in.path).reverse) do |archive_out|
|
|
68
|
+
# Save off the path so that the temporary file can be renamed to the
|
|
69
|
+
# archive file later.
|
|
70
|
+
tmp_archive_path = archive_out.path
|
|
71
|
+
# Ensure the file is in binary mode for Windows.
|
|
72
|
+
archive_out.binmode
|
|
73
|
+
# Update the archive.
|
|
74
|
+
open(archive_in, :r) do |z_in|
|
|
75
|
+
open(archive_out, :w) do |z_out|
|
|
76
|
+
z_in.each { |entry| z_out << entry }
|
|
77
|
+
z_out.archive(paths, options)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
# Set more reasonable permissions than those set by Tempfile.
|
|
83
|
+
File.chmod(0666 & ~File.umask, tmp_archive_path)
|
|
84
|
+
# Replace the input archive with the output archive.
|
|
85
|
+
File.rename(tmp_archive_path, archive)
|
|
86
|
+
else
|
|
87
|
+
open(archive, :w) { |z| z.archive(paths, options) }
|
|
88
|
+
end
|
|
53
89
|
end
|
|
54
90
|
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
91
|
+
# Extracts the entries from an archive to _destination_.
|
|
92
|
+
#
|
|
93
|
+
# If _archive_ is a String, it is treated as a file path pointing to an
|
|
94
|
+
# existing archive file. Otherwise, it is treated as a seekable and
|
|
95
|
+
# readable IO-like object.
|
|
96
|
+
#
|
|
97
|
+
# See the instance method #extract for more information about _destination_
|
|
98
|
+
# and _options_.
|
|
99
|
+
def self.extract(archive, destination, options = {})
|
|
100
|
+
open(archive, :r) { |z| z.extract(destination, options) }
|
|
61
101
|
end
|
|
62
102
|
|
|
63
103
|
# Calls #new with the given arguments and yields the resulting Zip instance
|
|
@@ -65,8 +105,8 @@ module Archive # :nodoc:
|
|
|
65
105
|
# Zip instance is closed.
|
|
66
106
|
#
|
|
67
107
|
# This is a synonym for #new if no block is given.
|
|
68
|
-
def self.open(
|
|
69
|
-
zf = new(
|
|
108
|
+
def self.open(archive, mode = :r)
|
|
109
|
+
zf = new(archive, mode)
|
|
70
110
|
return zf unless block_given?
|
|
71
111
|
|
|
72
112
|
begin
|
|
@@ -76,140 +116,121 @@ module Archive # :nodoc:
|
|
|
76
116
|
end
|
|
77
117
|
end
|
|
78
118
|
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
# Opens an existing archive and/or creates a new archive.
|
|
120
|
+
#
|
|
121
|
+
# If _archive_ is a String, it will be treated as a file path; otherwise, it
|
|
122
|
+
# is assumed to be an IO-like object with the necessary read or write
|
|
123
|
+
# support depending on the setting of _mode_. IO-like objects are not
|
|
124
|
+
# closed when the archive is closed, but files opened from file paths are.
|
|
125
|
+
# Set _mode_ to <tt>:r</tt> or <tt>"r"</tt> to read the archive, and set it
|
|
126
|
+
# to <tt>:w</tt> or <tt>"w"</tt> to write the archive.
|
|
127
|
+
#
|
|
128
|
+
# <b>NOTE:</b> The #close method must be called in order to save any
|
|
129
|
+
# modifications to the archive. Due to limitations in the Ruby finalization
|
|
130
|
+
# capabilities, the #close method is _not_ automatically called when this
|
|
131
|
+
# object is garbage collected. Make sure to call #close when finished with
|
|
132
|
+
# this object.
|
|
133
|
+
def initialize(archive, mode = :r)
|
|
134
|
+
@archive = archive
|
|
135
|
+
mode = mode.to_sym
|
|
136
|
+
if mode == :r || mode == :w then
|
|
137
|
+
@mode = mode
|
|
138
|
+
else
|
|
139
|
+
raise ArgumentError, "illegal access mode #{mode}"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
@close_delegate = false
|
|
143
|
+
if @archive.kind_of?(String) then
|
|
144
|
+
@close_delegate = true
|
|
145
|
+
if mode == :r then
|
|
146
|
+
@archive = File.open(@archive, 'rb')
|
|
147
|
+
else
|
|
148
|
+
@archive = File.open(@archive, 'wb')
|
|
149
|
+
end
|
|
95
150
|
end
|
|
96
|
-
@
|
|
97
|
-
@archive_out = archive_out
|
|
98
|
-
@entries = {}
|
|
99
|
-
@dirty = false
|
|
151
|
+
@entries = []
|
|
100
152
|
@comment = ''
|
|
101
153
|
@closed = false
|
|
102
|
-
if ! @archive_path.nil? && File.exist?(@archive_path) then
|
|
103
|
-
@archive_in = File.new(@archive_path, 'rb')
|
|
104
|
-
parse(@archive_in)
|
|
105
|
-
end
|
|
106
154
|
end
|
|
107
155
|
|
|
108
156
|
# A comment string for the ZIP archive.
|
|
109
157
|
attr_accessor :comment
|
|
110
158
|
|
|
111
|
-
#
|
|
112
|
-
#
|
|
159
|
+
# Closes the archive.
|
|
160
|
+
#
|
|
161
|
+
# Failure to close the archive by calling this method may result in a loss
|
|
162
|
+
# of data for writable archives.
|
|
163
|
+
#
|
|
164
|
+
# <b>NOTE:</b> The underlying stream is only closed if the archive was
|
|
165
|
+
# opened with a String for the _archive_ parameter.
|
|
113
166
|
#
|
|
114
167
|
# Raises Archive::Zip::IOError if called more than once.
|
|
115
168
|
def close
|
|
116
169
|
raise IOError, 'closed archive' if closed?
|
|
117
170
|
|
|
118
|
-
if
|
|
119
|
-
#
|
|
120
|
-
|
|
121
|
-
# Update the archive "in place".
|
|
122
|
-
tmp_archive_path = nil
|
|
123
|
-
Tempfile.open(*File.split(@archive_path).reverse) do |archive_out|
|
|
124
|
-
# Ensure the file is in binary mode for Windows.
|
|
125
|
-
archive_out.binmode
|
|
126
|
-
# Save off the path so that the temporary file can be renamed to the
|
|
127
|
-
# archive file later.
|
|
128
|
-
tmp_archive_path = archive_out.path
|
|
129
|
-
dump(archive_out)
|
|
130
|
-
end
|
|
131
|
-
File.chmod(0666 & ~File.umask, tmp_archive_path)
|
|
132
|
-
elsif @archive_out.kind_of?(String) then
|
|
133
|
-
# Open a new archive to receive the data.
|
|
134
|
-
File.open(@archive_out, 'wb') do |archive_out|
|
|
135
|
-
dump(archive_out)
|
|
136
|
-
end
|
|
137
|
-
else
|
|
138
|
-
# Assume the given object is an IO-like object and dump the archive
|
|
139
|
-
# contents to it.
|
|
140
|
-
dump(@archive_out)
|
|
141
|
-
end
|
|
142
|
-
@archive_in.close unless @archive_in.nil?
|
|
143
|
-
# The rename must happen after the original archive is closed when
|
|
144
|
-
# running on Windows since that platform does not allow a file which is
|
|
145
|
-
# in use to be replaced as is required when trying to update the archive
|
|
146
|
-
# "in place".
|
|
147
|
-
File.rename(tmp_archive_path, @archive_path) if @archive_out.nil?
|
|
148
|
-
elsif ! @archive_in.nil? then
|
|
149
|
-
@archive_in.close
|
|
171
|
+
if writable? then
|
|
172
|
+
# Write the new archive contents.
|
|
173
|
+
dump(@archive)
|
|
150
174
|
end
|
|
151
175
|
|
|
152
|
-
|
|
176
|
+
# Note that we only close delegate streams which are opened by us so that
|
|
177
|
+
# the user may do so for other delegate streams at his/her discretion.
|
|
178
|
+
@archive.close if @close_delegate
|
|
179
|
+
|
|
180
|
+
@closed = true
|
|
153
181
|
nil
|
|
154
182
|
end
|
|
155
183
|
|
|
156
|
-
# Returns +true+ if the ZIP archive is closed, false otherwise.
|
|
184
|
+
# Returns +true+ if the ZIP archive is closed, +false+ otherwise.
|
|
157
185
|
def closed?
|
|
158
186
|
@closed
|
|
159
187
|
end
|
|
160
188
|
|
|
161
|
-
#
|
|
162
|
-
|
|
163
|
-
|
|
189
|
+
# Returns +true+ if the ZIP archive is readable, +false+ otherwise.
|
|
190
|
+
def readable?
|
|
191
|
+
@mode == :r
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Returns +true+ if the ZIP archive is writable, +false+ otherwise.
|
|
195
|
+
def writable?
|
|
196
|
+
@mode == :w
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Iterates through each entry of a readable ZIP archive in turn yielding
|
|
200
|
+
# each one to the given block.
|
|
164
201
|
#
|
|
165
|
-
# Raises Archive::Zip::IOError if called after
|
|
202
|
+
# Raises Archive::Zip::IOError if called on a non-readable archive or after
|
|
203
|
+
# the archive is closed.
|
|
166
204
|
def each(&b)
|
|
167
|
-
raise IOError, '
|
|
205
|
+
raise IOError, 'non-readable archive' unless readable?
|
|
206
|
+
raise IOError, 'closed archive' if closed?
|
|
168
207
|
|
|
169
|
-
@
|
|
208
|
+
unless @parse_complete then
|
|
209
|
+
parse(@archive)
|
|
210
|
+
@parse_complete = true
|
|
211
|
+
end
|
|
212
|
+
@entries.each(&b)
|
|
170
213
|
end
|
|
171
214
|
|
|
172
|
-
#
|
|
173
|
-
#
|
|
215
|
+
# Adds _entry_ into a writable ZIP archive.
|
|
216
|
+
#
|
|
217
|
+
# <b>NOTE:</b> No attempt is made to prevent adding multiple entries with
|
|
218
|
+
# the same archive path.
|
|
174
219
|
#
|
|
175
|
-
# Raises Archive::Zip::IOError if called after
|
|
220
|
+
# Raises Archive::Zip::IOError if called on a non-writable archive or after
|
|
221
|
+
# the archive is closed.
|
|
176
222
|
def add_entry(entry)
|
|
177
|
-
raise IOError, '
|
|
223
|
+
raise IOError, 'non-writable archive' unless writable?
|
|
224
|
+
raise IOError, 'closed archive' if closed?
|
|
178
225
|
unless entry.kind_of?(Entry) then
|
|
179
226
|
raise ArgumentError, 'Archive::Zip::Entry instance required'
|
|
180
227
|
end
|
|
181
228
|
|
|
182
|
-
@entries
|
|
183
|
-
@dirty = true
|
|
229
|
+
@entries << entry
|
|
184
230
|
self
|
|
185
231
|
end
|
|
186
232
|
alias :<< :add_entry
|
|
187
233
|
|
|
188
|
-
# Look up an entry based on the zip path located in _zip_path_. Returns
|
|
189
|
-
# +nil+ if no entry is found.
|
|
190
|
-
def get_entry(zip_path)
|
|
191
|
-
@entries[zip_path]
|
|
192
|
-
end
|
|
193
|
-
alias :[] :get_entry
|
|
194
|
-
|
|
195
|
-
# Removes an entry from the ZIP file and returns the entry or +nil+ if no
|
|
196
|
-
# entry was found to remove. If _entry_ is an instance of
|
|
197
|
-
# Archive::Zip::Entry, the zip_path attribute is used to find the entry to
|
|
198
|
-
# remove; otherwise, _entry_ is assumed to be a zip path matching an entry
|
|
199
|
-
# in the ZIP archive.
|
|
200
|
-
#
|
|
201
|
-
# Raises Archive::Zip::IOError if called after #close.
|
|
202
|
-
def remove_entry(entry)
|
|
203
|
-
raise IOError, 'closed archive' if @closed
|
|
204
|
-
|
|
205
|
-
zip_path = entry
|
|
206
|
-
zip_path = entry.zip_path if entry.kind_of?(Entry)
|
|
207
|
-
entry = @entries.delete(zip_path)
|
|
208
|
-
entry = entry[1] unless entry.nil?
|
|
209
|
-
@dirty ||= ! entry.nil?
|
|
210
|
-
entry
|
|
211
|
-
end
|
|
212
|
-
|
|
213
234
|
# Adds _paths_ to the archive. _paths_ may be either a single path or an
|
|
214
235
|
# Array of paths. The files and directories referenced by _paths_ are added
|
|
215
236
|
# using their respective basenames as their zip paths. The exception to
|
|
@@ -267,10 +288,13 @@ module Archive # :nodoc:
|
|
|
267
288
|
# Any other options which are supported by Archive::Zip::Entry.from_file are
|
|
268
289
|
# also supported.
|
|
269
290
|
#
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
# Archive::Zip::
|
|
291
|
+
# <b>NOTE:</b> No attempt is made to prevent adding multiple entries with
|
|
292
|
+
# the same archive path.
|
|
293
|
+
#
|
|
294
|
+
# Raises Archive::Zip::IOError if called on a non-writable archive or after
|
|
295
|
+
# the archive is closed. Raises Archive::Zip::EntryError if the
|
|
296
|
+
# <b>:on_error</b> option is either unset or indicates that the error should
|
|
297
|
+
# be raised and Archive::Zip::Entry.from_file raises an error.
|
|
274
298
|
#
|
|
275
299
|
# == Example
|
|
276
300
|
#
|
|
@@ -317,7 +341,8 @@ module Archive # :nodoc:
|
|
|
317
341
|
# zip-test/dir1/
|
|
318
342
|
# zip-test/dir2/
|
|
319
343
|
def archive(paths, options = {})
|
|
320
|
-
raise IOError, '
|
|
344
|
+
raise IOError, 'non-writable archive' unless writable?
|
|
345
|
+
raise IOError, 'closed archive' if closed?
|
|
321
346
|
|
|
322
347
|
# Ensure that paths is an enumerable.
|
|
323
348
|
paths = [paths] unless paths.kind_of?(Enumerable)
|
|
@@ -472,7 +497,8 @@ module Archive # :nodoc:
|
|
|
472
497
|
# Any other options which are supported by Archive::Zip::Entry#extract are
|
|
473
498
|
# also supported.
|
|
474
499
|
#
|
|
475
|
-
# Raises Archive::Zip::IOError if called after
|
|
500
|
+
# Raises Archive::Zip::IOError if called on a non-readable archive or after
|
|
501
|
+
# the archive is closed.
|
|
476
502
|
#
|
|
477
503
|
# == Example
|
|
478
504
|
#
|
|
@@ -529,7 +555,8 @@ module Archive # :nodoc:
|
|
|
529
555
|
# +- dir1
|
|
530
556
|
# +- dir2
|
|
531
557
|
def extract(destination, options = {})
|
|
532
|
-
raise IOError, '
|
|
558
|
+
raise IOError, 'non-readable archive' unless readable?
|
|
559
|
+
raise IOError, 'closed archive' if closed?
|
|
533
560
|
|
|
534
561
|
# Ensure that unspecified options have default values.
|
|
535
562
|
options[:directories] = true unless options.has_key?(:directories)
|
|
@@ -627,23 +654,16 @@ module Archive # :nodoc:
|
|
|
627
654
|
|
|
628
655
|
private
|
|
629
656
|
|
|
630
|
-
# <b>NOTE:</b> For now _io_ MUST be seekable
|
|
631
|
-
# +true+ from its seekable? method. See IO#seekable?.
|
|
632
|
-
#
|
|
633
|
-
# Raises Archive::Zip::IOError if _io_ is not seekable.
|
|
657
|
+
# <b>NOTE:</b> For now _io_ MUST be seekable.
|
|
634
658
|
def parse(io)
|
|
635
|
-
# Error out if the IO object is not confirmed seekable.
|
|
636
|
-
raise Zip::IOError, 'non-seekable IO object given' unless io.respond_to?(:seekable?) and io.seekable?
|
|
637
|
-
|
|
638
659
|
socd_pos = find_central_directory(io)
|
|
639
660
|
io.seek(socd_pos)
|
|
640
661
|
# Parse each entry in the central directory.
|
|
641
662
|
loop do
|
|
642
|
-
signature =
|
|
663
|
+
signature = IOExtensions.read_exactly(io, 4)
|
|
643
664
|
break unless signature == CFH_SIGNATURE
|
|
644
|
-
|
|
665
|
+
@entries << Zip::Entry.parse(io)
|
|
645
666
|
end
|
|
646
|
-
@dirty = false
|
|
647
667
|
# Maybe add support for digital signatures and ZIP64 records... Later
|
|
648
668
|
|
|
649
669
|
nil
|
|
@@ -665,9 +685,12 @@ module Archive # :nodoc:
|
|
|
665
685
|
eocd_offset = -22
|
|
666
686
|
loop do
|
|
667
687
|
io.seek(eocd_offset, IO::SEEK_END)
|
|
668
|
-
if
|
|
688
|
+
if IOExtensions.read_exactly(io, 4) == EOCD_SIGNATURE then
|
|
669
689
|
io.seek(16, IO::SEEK_CUR)
|
|
670
|
-
|
|
690
|
+
if IOExtensions.read_exactly(io, 2).unpack('v')[0] ==
|
|
691
|
+
(eocd_offset + 22).abs then
|
|
692
|
+
break
|
|
693
|
+
end
|
|
671
694
|
end
|
|
672
695
|
eocd_offset -= 1
|
|
673
696
|
end
|
|
@@ -676,7 +699,7 @@ module Archive # :nodoc:
|
|
|
676
699
|
# Now, jump into the location in the record which contains a pointer to
|
|
677
700
|
# the start of the central directory record and return the value.
|
|
678
701
|
io.seek(eocd_offset + 16, IO::SEEK_END)
|
|
679
|
-
return
|
|
702
|
+
return IOExtensions.read_exactly(io, 4).unpack('V')[0]
|
|
680
703
|
rescue Errno::EINVAL
|
|
681
704
|
raise Zip::UnzipError, 'unable to locate end-of-central-directory record'
|
|
682
705
|
end
|
|
@@ -686,12 +709,11 @@ module Archive # :nodoc:
|
|
|
686
709
|
# bytes written.
|
|
687
710
|
def dump(io)
|
|
688
711
|
bytes_written = 0
|
|
689
|
-
entries
|
|
690
|
-
entries.each do |entry|
|
|
712
|
+
@entries.each do |entry|
|
|
691
713
|
bytes_written += entry.dump_local_file_record(io, bytes_written)
|
|
692
714
|
end
|
|
693
715
|
central_directory_offset = bytes_written
|
|
694
|
-
entries.each do |entry|
|
|
716
|
+
@entries.each do |entry|
|
|
695
717
|
bytes_written += entry.dump_central_file_record(io)
|
|
696
718
|
end
|
|
697
719
|
central_directory_length = bytes_written - central_directory_offset
|
|
@@ -700,8 +722,8 @@ module Archive # :nodoc:
|
|
|
700
722
|
[
|
|
701
723
|
0,
|
|
702
724
|
0,
|
|
703
|
-
entries.length,
|
|
704
|
-
entries.length,
|
|
725
|
+
@entries.length,
|
|
726
|
+
@entries.length,
|
|
705
727
|
central_directory_length,
|
|
706
728
|
central_directory_offset,
|
|
707
729
|
comment.length
|
data/lib/archive/zip/codec.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
1
3
|
require 'archive/support/zlib'
|
|
2
4
|
require 'archive/zip/codec'
|
|
3
5
|
require 'archive/zip/data_descriptor'
|
|
@@ -40,8 +42,17 @@ module Archive; class Zip; module Codec
|
|
|
40
42
|
# compression to be applied to the data stream.
|
|
41
43
|
def initialize(io, compression_level)
|
|
42
44
|
super(io, compression_level, -Zlib::MAX_WBITS)
|
|
45
|
+
@crc32 = 0
|
|
43
46
|
end
|
|
44
47
|
|
|
48
|
+
# The CRC32 checksum of the uncompressed data written using this object.
|
|
49
|
+
#
|
|
50
|
+
# <b>NOTE:</b> Anything still in the internal write buffer has not been
|
|
51
|
+
# processed, so calling #flush prior to examining this attribute may be
|
|
52
|
+
# necessary for an accurate computation.
|
|
53
|
+
attr_reader :crc32
|
|
54
|
+
alias :checksum :crc32
|
|
55
|
+
|
|
45
56
|
# Closes this object so that further write operations will fail. If
|
|
46
57
|
# _close_delegate_ is +true+, the delegate object used as a data sink will
|
|
47
58
|
# also be closed using its close method.
|
|
@@ -50,15 +61,29 @@ module Archive; class Zip; module Codec
|
|
|
50
61
|
delegate.close if close_delegate
|
|
51
62
|
end
|
|
52
63
|
|
|
53
|
-
# Returns an instance of Archive::Zip::
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
64
|
+
# Returns an instance of Archive::Zip::DataDescriptor with information
|
|
65
|
+
# regarding the data which has passed through this object to the delegate
|
|
66
|
+
# object. The close or flush methods should be called before using this
|
|
67
|
+
# method in order to ensure that any possibly buffered data is flushed to
|
|
68
|
+
# the delegate object; otherwise, the contents of the data descriptor may
|
|
69
|
+
# be inaccurate.
|
|
59
70
|
def data_descriptor
|
|
60
71
|
DataDescriptor.new(crc32, compressed_size, uncompressed_size)
|
|
61
72
|
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
77
|
+
result = super(offset, whence)
|
|
78
|
+
@crc32 = 0 if whence == IO::SEEK_SET
|
|
79
|
+
result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def unbuffered_write(string)
|
|
83
|
+
result = super(string)
|
|
84
|
+
@crc32 = Zlib.crc32(string, @crc32)
|
|
85
|
+
result
|
|
86
|
+
end
|
|
62
87
|
end
|
|
63
88
|
|
|
64
89
|
# Archive::Zip::Codec::Deflate::Decompress extends Zlib::ZReader in order to
|
|
@@ -92,8 +117,17 @@ module Archive; class Zip; module Codec
|
|
|
92
117
|
# method, this class' _rewind_ method will be enabled.
|
|
93
118
|
def initialize(io)
|
|
94
119
|
super(io, -Zlib::MAX_WBITS)
|
|
120
|
+
@crc32 = 0
|
|
95
121
|
end
|
|
96
122
|
|
|
123
|
+
# The CRC32 checksum of the uncompressed data read using this object.
|
|
124
|
+
#
|
|
125
|
+
# <b>NOTE:</b> The contents of the internal read buffer are immediately
|
|
126
|
+
# processed any time the internal buffer is filled, so this checksum is
|
|
127
|
+
# only accurate if all data has been read out of this object.
|
|
128
|
+
attr_reader :crc32
|
|
129
|
+
alias :checksum :crc32
|
|
130
|
+
|
|
97
131
|
# Closes this object so that further read operations will fail. If
|
|
98
132
|
# _close_delegate_ is +true+, the delegate object used as a data source
|
|
99
133
|
# will also be closed using its close method.
|
|
@@ -102,14 +136,28 @@ module Archive; class Zip; module Codec
|
|
|
102
136
|
delegate.close if close_delegate
|
|
103
137
|
end
|
|
104
138
|
|
|
105
|
-
# Returns an instance of Archive::Zip::
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
139
|
+
# Returns an instance of Archive::Zip::DataDescriptor with information
|
|
140
|
+
# regarding the data which has passed through this object from the
|
|
141
|
+
# delegate object. It is recommended to call the close method before
|
|
142
|
+
# calling this in order to ensure that no further read operations change
|
|
143
|
+
# the state of this object.
|
|
110
144
|
def data_descriptor
|
|
111
145
|
DataDescriptor.new(crc32, compressed_size, uncompressed_size)
|
|
112
146
|
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def unbuffered_read(length)
|
|
151
|
+
result = super(length)
|
|
152
|
+
@crc32 = Zlib.crc32(result, @crc32)
|
|
153
|
+
result
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
157
|
+
result = super(offset, whence)
|
|
158
|
+
@crc32 = 0 if whence == IO::SEEK_SET
|
|
159
|
+
result
|
|
160
|
+
end
|
|
113
161
|
end
|
|
114
162
|
|
|
115
163
|
# The numeric identifier assigned to this compression codec by the ZIP
|