rubyzip 0.9.6.1 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- data/README.md +41 -8
- data/lib/zip/constants.rb +1 -1
- data/lib/zip/dos_time.rb +35 -33
- data/lib/zip/settings.rb +10 -0
- data/lib/zip/zip.rb +2 -0
- data/lib/zip/zip_central_directory.rb +29 -23
- data/lib/zip/zip_entry.rb +101 -102
- data/lib/zip/zip_entry_set.rb +13 -4
- data/lib/zip/zip_extra_field.rb +24 -24
- data/lib/zip/zip_file.rb +49 -51
- data/lib/zip/zip_output_stream.rb +12 -10
- metadata +27 -17
data/README.md
CHANGED
@@ -1,18 +1,49 @@
|
|
1
|
-
rubyzip
|
1
|
+
rubyzip [![Build Status](https://secure.travis-ci.org/aussiegeek/rubyzip.png)](http://travis-ci.org/aussiegeek/rubyzip)
|
2
2
|
=======
|
3
3
|
|
4
4
|
rubyzip is a ruby library for reading and writing zip files.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
rubyzip is available on RubyGems, so:
|
9
|
+
|
10
|
+
```
|
11
|
+
gem install rubyzip
|
12
|
+
```
|
13
|
+
|
14
|
+
Or in your Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'rubyzip'
|
18
|
+
```
|
19
|
+
|
20
|
+
Developing
|
21
|
+
----------
|
22
|
+
|
23
|
+
To run tests you need run next commands:
|
8
24
|
|
9
|
-
|
25
|
+
```
|
26
|
+
bundle install
|
27
|
+
rake
|
28
|
+
```
|
10
29
|
|
30
|
+
Configuration
|
31
|
+
-------------
|
32
|
+
|
33
|
+
By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:
|
34
|
+
|
35
|
+
```
|
36
|
+
Zip.options[:on_exists_proc] = true
|
37
|
+
```
|
11
38
|
|
12
|
-
|
39
|
+
If you're using rubyzip with rails, consider placing this snippet of code in an initializer file such as `config/initializers/rubyzip.rb`
|
40
|
+
|
41
|
+
Additionally, if you want to configure rubyzip to overwrite existing files while creating a .zip file, you can do so with the following:
|
42
|
+
|
43
|
+
```
|
44
|
+
Zip.options[:continue_on_exists_proc] = true
|
45
|
+
```
|
13
46
|
|
14
|
-
rake test
|
15
|
-
[![Build Status](https://secure.travis-ci.org/aussiegeek/rubyzip.png)](http://travis-ci.org/aussiegeek/rubyzip)
|
16
47
|
|
17
48
|
Documentation
|
18
49
|
-------------
|
@@ -62,10 +93,12 @@ http://rdoc.info/github/aussiegeek/rubyzip/master/frames
|
|
62
93
|
Authors
|
63
94
|
-------
|
64
95
|
|
96
|
+
Alexander Simonov ( alex at simonov.me)
|
97
|
+
|
65
98
|
Alan Harper ( alan at aussiegeek.net)
|
66
99
|
|
67
100
|
Thomas Sondergaard (thomas at sondergaard.cc)
|
68
101
|
|
69
102
|
Technorama Ltd. (oss-ruby-zip at technorama.net)
|
70
103
|
|
71
|
-
extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)
|
104
|
+
extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)
|
data/lib/zip/constants.rb
CHANGED
data/lib/zip/dos_time.rb
CHANGED
@@ -1,43 +1,45 @@
|
|
1
|
-
|
1
|
+
module Zip
|
2
|
+
class DOSTime < Time #:nodoc:all
|
2
3
|
|
3
|
-
|
4
|
+
#MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# Register CX, the Time:
|
7
|
+
# Bits 0-4 2 second increments (0-29)
|
8
|
+
# Bits 5-10 minutes (0-59)
|
9
|
+
# bits 11-15 hours (0-24)
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# Register DX, the Date:
|
12
|
+
# Bits 0-4 day (1-31)
|
13
|
+
# bits 5-8 month (1-12)
|
14
|
+
# bits 9-15 year (four digit year minus 1980)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def to_binary_dos_time
|
17
|
+
(sec/2) +
|
18
|
+
(min << 5) +
|
19
|
+
(hour << 11)
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def to_binary_dos_date
|
23
|
+
(day) +
|
24
|
+
(month << 5) +
|
25
|
+
((year - 1980) << 9)
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
# Dos time is only stored with two seconds accuracy
|
29
|
+
def dos_equals(other)
|
30
|
+
to_i/2 == other.to_i/2
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
def self.parse_binary_dos_format(binaryDosDate, binaryDosTime)
|
34
|
+
second = 2 * ( 0b11111 & binaryDosTime)
|
35
|
+
minute = ( 0b11111100000 & binaryDosTime) >> 5
|
36
|
+
hour = (0b1111100000000000 & binaryDosTime) >> 11
|
37
|
+
day = ( 0b11111 & binaryDosDate)
|
38
|
+
month = ( 0b111100000 & binaryDosDate) >> 5
|
39
|
+
year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980
|
40
|
+
begin
|
41
|
+
return self.local(year, month, day, hour, minute, second)
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
data/lib/zip/settings.rb
ADDED
data/lib/zip/zip.rb
CHANGED
@@ -21,41 +21,45 @@ module Zip
|
|
21
21
|
|
22
22
|
def write_to_stream(io) #:nodoc:
|
23
23
|
offset = io.tell
|
24
|
-
@entrySet.
|
24
|
+
@entrySet.each { |entry| entry.write_c_dir_entry(io) }
|
25
25
|
write_e_o_c_d(io, offset)
|
26
26
|
end
|
27
27
|
|
28
28
|
def write_e_o_c_d(io, offset) #:nodoc:
|
29
|
-
|
30
|
-
|
29
|
+
tmp = [
|
30
|
+
END_OF_CENTRAL_DIRECTORY_SIGNATURE,
|
31
31
|
0 , # @numberOfThisDisk
|
32
32
|
0 , # @numberOfDiskWithStartOfCDir
|
33
|
-
@entrySet? @entrySet.size : 0
|
34
|
-
@entrySet? @entrySet.size : 0
|
35
|
-
cdir_size
|
33
|
+
@entrySet? @entrySet.size : 0 ,
|
34
|
+
@entrySet? @entrySet.size : 0 ,
|
35
|
+
cdir_size ,
|
36
36
|
offset ,
|
37
|
-
@comment ? @comment.length : 0
|
38
|
-
|
37
|
+
@comment ? @comment.length : 0
|
38
|
+
]
|
39
|
+
io << tmp.pack('VvvvvVVv')
|
40
|
+
io << @comment
|
39
41
|
end
|
40
42
|
|
41
43
|
private :write_e_o_c_d
|
42
44
|
|
43
45
|
def cdir_size #:nodoc:
|
44
46
|
# does not include eocd
|
45
|
-
@entrySet.inject(0)
|
47
|
+
@entrySet.inject(0) do |value, entry|
|
48
|
+
entry.cdir_header_size + value
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
private :cdir_size
|
49
53
|
|
50
54
|
def read_e_o_c_d(io) #:nodoc:
|
51
55
|
buf = get_e_o_c_d(io)
|
52
|
-
@numberOfThisDisk = ZipEntry
|
53
|
-
@numberOfDiskWithStartOfCDir = ZipEntry
|
54
|
-
@totalNumberOfEntriesInCDirOnThisDisk = ZipEntry
|
55
|
-
@size = ZipEntry
|
56
|
-
@sizeInBytes = ZipEntry
|
57
|
-
@cdirOffset = ZipEntry
|
58
|
-
commentLength = ZipEntry
|
56
|
+
@numberOfThisDisk = ZipEntry.read_zip_short(buf)
|
57
|
+
@numberOfDiskWithStartOfCDir = ZipEntry.read_zip_short(buf)
|
58
|
+
@totalNumberOfEntriesInCDirOnThisDisk = ZipEntry.read_zip_short(buf)
|
59
|
+
@size = ZipEntry.read_zip_short(buf)
|
60
|
+
@sizeInBytes = ZipEntry.read_zip_long(buf)
|
61
|
+
@cdirOffset = ZipEntry.read_zip_long(buf)
|
62
|
+
commentLength = ZipEntry.read_zip_short(buf)
|
59
63
|
@comment = buf.read(commentLength)
|
60
64
|
raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
61
65
|
end
|
@@ -67,9 +71,10 @@ module Zip
|
|
67
71
|
raise ZipError, "Zip consistency problem while reading central directory entry"
|
68
72
|
end
|
69
73
|
@entrySet = ZipEntrySet.new
|
70
|
-
@size.times
|
71
|
-
|
72
|
-
|
74
|
+
@size.times do
|
75
|
+
tmp = ZipEntry.read_c_dir_entry(io)
|
76
|
+
@entrySet << tmp
|
77
|
+
end
|
73
78
|
end
|
74
79
|
|
75
80
|
def read_from_stream(io) #:nodoc:
|
@@ -94,18 +99,19 @@ module Zip
|
|
94
99
|
rescue Errno::EFBIG # FreeBSD 4.5 may raise Errno::EFBIG
|
95
100
|
raise if (retried)
|
96
101
|
retried = true
|
97
|
-
|
98
102
|
io.seek(0, IO::SEEK_SET)
|
99
103
|
retry
|
100
104
|
end
|
101
105
|
|
102
106
|
sigIndex = buf.rindex([END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack('V'))
|
103
107
|
raise ZipError, "Zip end of central directory signature not found" unless sigIndex
|
104
|
-
buf=buf.slice!((sigIndex+4)...(buf.
|
108
|
+
buf = buf.slice!((sigIndex + 4)...(buf.bytesize))
|
109
|
+
|
105
110
|
def buf.read(count)
|
106
111
|
slice!(0, count)
|
107
112
|
end
|
108
|
-
|
113
|
+
|
114
|
+
buf
|
109
115
|
end
|
110
116
|
|
111
117
|
# For iterating over the entries.
|
@@ -127,7 +133,7 @@ module Zip
|
|
127
133
|
return nil
|
128
134
|
end
|
129
135
|
|
130
|
-
def ==
|
136
|
+
def ==(other) #:nodoc:
|
131
137
|
return false unless other.kind_of?(ZipCentralDirectory)
|
132
138
|
@entrySet.entries.sort == other.entries.sort && comment == other.comment
|
133
139
|
end
|
data/lib/zip/zip_entry.rb
CHANGED
@@ -11,7 +11,7 @@ module Zip
|
|
11
11
|
FSTYPE_ATARI = 5
|
12
12
|
FSTYPE_HPFS = 6
|
13
13
|
FSTYPE_MAC = 7
|
14
|
-
FSTYPE_Z_SYSTEM = 8
|
14
|
+
FSTYPE_Z_SYSTEM = 8
|
15
15
|
FSTYPE_CPM = 9
|
16
16
|
FSTYPE_TOPS20 = 10
|
17
17
|
FSTYPE_NTFS = 11
|
@@ -49,40 +49,19 @@ module Zip
|
|
49
49
|
FSTYPE_ATHEOS => 'AtheOS'.freeze,
|
50
50
|
}.freeze
|
51
51
|
|
52
|
-
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
|
52
|
+
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
|
53
53
|
:name, :size, :localHeaderOffset, :zipfile, :fstype, :externalFileAttributes, :gp_flags, :header_signature
|
54
54
|
|
55
55
|
attr_accessor :follow_symlinks
|
56
56
|
attr_accessor :restore_times, :restore_permissions, :restore_ownership
|
57
57
|
attr_accessor :unix_uid, :unix_gid, :unix_perms
|
58
|
-
|
59
58
|
attr_accessor :dirty
|
60
|
-
|
61
59
|
attr_reader :ftype, :filepath # :nodoc:
|
62
60
|
|
63
|
-
|
64
|
-
def name_encoding
|
65
|
-
(@gp_flags & 0b100000000000) != 0 ? "UTF-8" : "CP437//"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Returns the name in the encoding specified by enc
|
69
|
-
def name_in(enc)
|
70
|
-
if RUBY_VERSION >= '1.9'
|
71
|
-
@name.encode(enc)
|
72
|
-
else
|
73
|
-
Iconv.conv(enc, name_encoding, @name)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns the name in the encoding specified by enc
|
78
|
-
def comment_in(enc)
|
79
|
-
name_in(enc)
|
80
|
-
end
|
81
|
-
|
82
|
-
def initialize(zipfile = "", name = "", comment = "", extra = "",
|
61
|
+
def initialize(zipfile = "", name = "", comment = "", extra = "",
|
83
62
|
compressed_size = 0, crc = 0,
|
84
|
-
|
85
|
-
|
63
|
+
compression_method = ZipEntry::DEFLATED, size = 0,
|
64
|
+
time = DOSTime.now)
|
86
65
|
super()
|
87
66
|
if name.start_with?("/")
|
88
67
|
raise ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
@@ -91,6 +70,8 @@ module Zip
|
|
91
70
|
@local_header_size = 0
|
92
71
|
@internalFileAttributes = 1
|
93
72
|
@externalFileAttributes = 0
|
73
|
+
@header_signature = CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
74
|
+
@versionNeededToExtract = VERSION_NEEDED_TO_EXTRACT
|
94
75
|
@version = 52 # this library's version
|
95
76
|
@ftype = nil # unspecified or unknown
|
96
77
|
@filepath = nil
|
@@ -108,7 +89,7 @@ module Zip
|
|
108
89
|
@name = name
|
109
90
|
@size = size
|
110
91
|
@time = time
|
111
|
-
@gp_flags =
|
92
|
+
@gp_flags = 0
|
112
93
|
|
113
94
|
@follow_symlinks = false
|
114
95
|
|
@@ -187,7 +168,7 @@ module Zip
|
|
187
168
|
end
|
188
169
|
|
189
170
|
def cdir_header_size #:nodoc:all
|
190
|
-
CDIR_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.bytesize : 0) +
|
171
|
+
CDIR_ENTRY_STATIC_HEADER_LENGTH + (@name ? @name.bytesize : 0) +
|
191
172
|
(@extra ? @extra.c_dir_size : 0) + (@comment ? @comment.bytesize : 0)
|
192
173
|
end
|
193
174
|
|
@@ -197,7 +178,7 @@ module Zip
|
|
197
178
|
|
198
179
|
# Extracts entry to file destPath (defaults to @name).
|
199
180
|
def extract(destPath = @name, &onExistsProc)
|
200
|
-
onExistsProc ||= proc {
|
181
|
+
onExistsProc ||= proc { Zip.options[:on_exists_proc] }
|
201
182
|
|
202
183
|
if directory?
|
203
184
|
create_directory(destPath, &onExistsProc)
|
@@ -218,24 +199,44 @@ module Zip
|
|
218
199
|
|
219
200
|
protected
|
220
201
|
|
221
|
-
|
222
|
-
io
|
223
|
-
|
202
|
+
class << self
|
203
|
+
def read_zip_short(io) # :nodoc:
|
204
|
+
io.read(2).unpack('v')[0]
|
205
|
+
end
|
206
|
+
|
207
|
+
def read_zip_long(io) # :nodoc:
|
208
|
+
io.read(4).unpack('V')[0]
|
209
|
+
end
|
210
|
+
|
211
|
+
def read_c_dir_entry(io) #:nodoc:all
|
212
|
+
entry = new(io.path)
|
213
|
+
entry.read_c_dir_entry(io)
|
214
|
+
entry
|
215
|
+
rescue ZipError
|
216
|
+
nil
|
217
|
+
end
|
218
|
+
|
219
|
+
def read_local_entry(io)
|
220
|
+
entry = new(io.path)
|
221
|
+
entry.read_local_entry(io)
|
222
|
+
entry
|
223
|
+
rescue ZipError
|
224
|
+
nil
|
225
|
+
end
|
224
226
|
|
225
|
-
def ZipEntry.read_zip_long(io) # :nodoc:
|
226
|
-
io.read(4).unpack('V')[0]
|
227
227
|
end
|
228
|
+
|
228
229
|
public
|
229
230
|
|
230
231
|
LOCAL_ENTRY_SIGNATURE = 0x04034b50
|
231
232
|
LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
|
232
233
|
LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4+4+4
|
233
|
-
VERSION_NEEDED_TO_EXTRACT =
|
234
|
+
VERSION_NEEDED_TO_EXTRACT = 20
|
234
235
|
|
235
236
|
def read_local_entry(io) #:nodoc:all
|
236
237
|
@localHeaderOffset = io.tell
|
237
238
|
staticSizedFieldsBuf = io.read(LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
238
|
-
unless (staticSizedFieldsBuf.size==LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
239
|
+
unless (staticSizedFieldsBuf.size == LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
239
240
|
raise ZipError, "Premature end of file. Not enough data for zip entry local header"
|
240
241
|
end
|
241
242
|
|
@@ -272,21 +273,15 @@ module Zip
|
|
272
273
|
@local_header_size = calculate_local_header_size
|
273
274
|
end
|
274
275
|
|
275
|
-
|
276
|
-
entry = new(io.path)
|
277
|
-
entry.read_local_entry(io)
|
278
|
-
return entry
|
279
|
-
rescue ZipError
|
280
|
-
return nil
|
281
|
-
end
|
276
|
+
|
282
277
|
|
283
278
|
def write_local_entry(io) #:nodoc:all
|
284
279
|
@localHeaderOffset = io.tell
|
285
280
|
|
286
|
-
io <<
|
281
|
+
io <<
|
287
282
|
[LOCAL_ENTRY_SIGNATURE ,
|
288
|
-
|
289
|
-
|
283
|
+
@versionNeededToExtract , # version needed to extract
|
284
|
+
@gp_flags , # @gp_flags ,
|
290
285
|
@compression_method ,
|
291
286
|
@time.to_binary_dos_time , # @lastModTime ,
|
292
287
|
@time.to_binary_dos_date , # @lastModDate ,
|
@@ -304,30 +299,31 @@ module Zip
|
|
304
299
|
|
305
300
|
def read_c_dir_entry(io) #:nodoc:all
|
306
301
|
staticSizedFieldsBuf = io.read(CDIR_ENTRY_STATIC_HEADER_LENGTH)
|
307
|
-
unless (staticSizedFieldsBuf.
|
302
|
+
unless (staticSizedFieldsBuf.bytesize == CDIR_ENTRY_STATIC_HEADER_LENGTH)
|
308
303
|
raise ZipError, "Premature end of file. Not enough data for zip cdir entry header"
|
309
304
|
end
|
310
305
|
|
311
|
-
|
312
|
-
@
|
313
|
-
@
|
314
|
-
@
|
315
|
-
@
|
316
|
-
@
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
@
|
321
|
-
@
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
@
|
328
|
-
@
|
329
|
-
@
|
330
|
-
@
|
306
|
+
|
307
|
+
@header_signature ,
|
308
|
+
@version , # version of encoding software
|
309
|
+
@fstype , # filesystem type
|
310
|
+
@versionNeededToExtract ,
|
311
|
+
@gp_flags ,
|
312
|
+
@compression_method ,
|
313
|
+
lastModTime ,
|
314
|
+
lastModDate ,
|
315
|
+
@crc ,
|
316
|
+
@compressed_size ,
|
317
|
+
@size ,
|
318
|
+
nameLength ,
|
319
|
+
extraLength ,
|
320
|
+
commentLength ,
|
321
|
+
diskNumberStart ,
|
322
|
+
@internalFileAttributes ,
|
323
|
+
@externalFileAttributes ,
|
324
|
+
@localHeaderOffset ,
|
325
|
+
@name ,
|
326
|
+
@extra ,
|
331
327
|
@comment = staticSizedFieldsBuf.unpack('VCCvvvvvVVVvvvvvVV')
|
332
328
|
|
333
329
|
unless (@header_signature == CENTRAL_DIRECTORY_ENTRY_SIGNATURE)
|
@@ -335,13 +331,13 @@ module Zip
|
|
335
331
|
end
|
336
332
|
set_time(lastModDate, lastModTime)
|
337
333
|
|
338
|
-
@name
|
334
|
+
@name = io.read(nameLength)
|
339
335
|
if ZipExtraField === @extra
|
340
336
|
@extra.merge(io.read(extraLength))
|
341
337
|
else
|
342
338
|
@extra = ZipExtraField.new(io.read(extraLength))
|
343
339
|
end
|
344
|
-
@comment
|
340
|
+
@comment = io.read(commentLength)
|
345
341
|
unless (@comment && @comment.bytesize == commentLength)
|
346
342
|
raise ZipError, "Truncated cdir zip entry header"
|
347
343
|
end
|
@@ -358,7 +354,13 @@ module Zip
|
|
358
354
|
when 012
|
359
355
|
@ftype = :symlink
|
360
356
|
else
|
361
|
-
|
357
|
+
#best case guess for whether it is a file or not
|
358
|
+
#Otherwise this would be set to unknown and that entry would never be able to extracted
|
359
|
+
if name_is_directory?
|
360
|
+
@ftype = :directory
|
361
|
+
else
|
362
|
+
@ftype = :file
|
363
|
+
end
|
362
364
|
end
|
363
365
|
else
|
364
366
|
if name_is_directory?
|
@@ -370,13 +372,7 @@ module Zip
|
|
370
372
|
@local_header_size = calculate_local_header_size
|
371
373
|
end
|
372
374
|
|
373
|
-
|
374
|
-
entry = new(io.path)
|
375
|
-
entry.read_c_dir_entry(io)
|
376
|
-
return entry
|
377
|
-
rescue ZipError
|
378
|
-
return nil
|
379
|
-
end
|
375
|
+
|
380
376
|
|
381
377
|
def file_stat(path) # :nodoc:
|
382
378
|
if @follow_symlinks
|
@@ -431,28 +427,31 @@ module Zip
|
|
431
427
|
end
|
432
428
|
end
|
433
429
|
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
430
|
+
tmp = [
|
431
|
+
@header_signature,
|
432
|
+
@version , # version of encoding software
|
433
|
+
@fstype , # filesystem type
|
434
|
+
@versionNeededToExtract , # @versionNeededToExtract ,
|
435
|
+
@gp_flags , # @gp_flags ,
|
436
|
+
@compression_method ,
|
437
|
+
@time.to_binary_dos_time , # @lastModTime ,
|
438
|
+
@time.to_binary_dos_date , # @lastModDate ,
|
439
|
+
@crc ,
|
440
|
+
@compressed_size ,
|
441
|
+
@size ,
|
442
|
+
@name ? @name.bytesize : 0 ,
|
443
|
+
@extra ? @extra.c_dir_length : 0 ,
|
444
|
+
@comment ? @comment.bytesize : 0 ,
|
445
|
+
0 , # disk number start
|
446
|
+
@internalFileAttributes , # file type (binary=0, text=1)
|
447
|
+
@externalFileAttributes , # native filesystem attributes
|
448
|
+
@localHeaderOffset ,
|
449
|
+
@name ,
|
450
|
+
@extra ,
|
451
|
+
@comment
|
452
|
+
]
|
453
|
+
|
454
|
+
io << tmp.pack('VCCvvvvvVVVvvvvvVV')
|
456
455
|
|
457
456
|
io << @name
|
458
457
|
io << (@extra ? @extra.to_c_dir_bin : "")
|
@@ -544,7 +543,7 @@ module Zip
|
|
544
543
|
aZipOutputStream.put_next_entry(self)
|
545
544
|
elsif @filepath
|
546
545
|
aZipOutputStream.put_next_entry(self)
|
547
|
-
get_input_stream { |is| IOExtras.copy_stream(aZipOutputStream, is) }
|
546
|
+
get_input_stream { |is| IOExtras.copy_stream(aZipOutputStream, is) }
|
548
547
|
else
|
549
548
|
aZipOutputStream.copy_raw_entry(self)
|
550
549
|
end
|
@@ -568,7 +567,7 @@ module Zip
|
|
568
567
|
puts "Invalid date/time in zip entry"
|
569
568
|
end
|
570
569
|
|
571
|
-
def write_file(destPath, continueOnExistsProc = proc {
|
570
|
+
def write_file(destPath, continueOnExistsProc = proc { Zip.options[:continue_on_exists_proc] })
|
572
571
|
if ::File.exists?(destPath) && ! yield(self, destPath)
|
573
572
|
raise ZipDestinationFileExistsError,
|
574
573
|
"Destination '#{destPath}' already exists"
|
@@ -635,4 +634,4 @@ end
|
|
635
634
|
|
636
635
|
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
637
636
|
# rubyzip is free software; you can redistribute it and/or
|
638
|
-
# modify it under the terms of the ruby license.
|
637
|
+
# modify it under the terms of the ruby license.
|
data/lib/zip/zip_entry_set.rb
CHANGED
@@ -9,11 +9,15 @@ module Zip
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def include?(entry)
|
12
|
-
@entrySet.include?(entry
|
12
|
+
@entrySet.include?(to_key(entry))
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_entry(entry)
|
16
|
+
@entrySet[to_key(entry)]
|
13
17
|
end
|
14
18
|
|
15
19
|
def <<(entry)
|
16
|
-
@entrySet[entry
|
20
|
+
@entrySet[to_key(entry)] = entry
|
17
21
|
end
|
18
22
|
alias :push :<<
|
19
23
|
|
@@ -24,7 +28,7 @@ module Zip
|
|
24
28
|
alias :length :size
|
25
29
|
|
26
30
|
def delete(entry)
|
27
|
-
@entrySet.delete(entry
|
31
|
+
@entrySet.delete(to_key(entry)) ? entry : nil
|
28
32
|
end
|
29
33
|
|
30
34
|
def each(&aProc)
|
@@ -46,7 +50,7 @@ module Zip
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def parent(entry)
|
49
|
-
@entrySet[entry.parent_as_string]
|
53
|
+
@entrySet[to_key(entry.parent_as_string)]
|
50
54
|
end
|
51
55
|
|
52
56
|
def glob(pattern, flags = ::File::FNM_PATHNAME|::File::FNM_DOTMATCH)
|
@@ -58,6 +62,11 @@ module Zip
|
|
58
62
|
#TODO attr_accessor :auto_create_directories
|
59
63
|
protected
|
60
64
|
attr_accessor :entrySet
|
65
|
+
|
66
|
+
private
|
67
|
+
def to_key(entry)
|
68
|
+
entry.to_s.sub(/\/$/, "")
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
63
72
|
|
data/lib/zip/zip_extra_field.rb
CHANGED
@@ -27,21 +27,21 @@ module Zip
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def ==(other)
|
30
|
-
self.class != other.class
|
31
|
-
each
|
30
|
+
return false if self.class != other.class
|
31
|
+
each do |k, v|
|
32
32
|
v != other[k] and return false
|
33
|
-
|
33
|
+
end
|
34
34
|
true
|
35
35
|
end
|
36
36
|
|
37
37
|
def to_local_bin
|
38
38
|
s = pack_for_local
|
39
|
-
self.class.const_get(:HEADER_ID) + [s.
|
39
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") + s
|
40
40
|
end
|
41
41
|
|
42
42
|
def to_c_dir_bin
|
43
43
|
s = pack_for_c_dir
|
44
|
-
self.class.const_get(:HEADER_ID) + [s.
|
44
|
+
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") + s
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -60,7 +60,7 @@ module Zip
|
|
60
60
|
attr_accessor :atime, :ctime, :mtime, :flag
|
61
61
|
|
62
62
|
def merge(binstr)
|
63
|
-
|
63
|
+
return if binstr.empty?
|
64
64
|
size, content = initial_parse(binstr)
|
65
65
|
size or return
|
66
66
|
@flag, mtime, atime, ctime = content.unpack("CVVV")
|
@@ -103,10 +103,10 @@ module Zip
|
|
103
103
|
attr_accessor :uid, :gid
|
104
104
|
|
105
105
|
def merge(binstr)
|
106
|
-
|
106
|
+
return if binstr.empty?
|
107
107
|
size, content = initial_parse(binstr)
|
108
|
-
# size: 0 for central
|
109
|
-
return if(!
|
108
|
+
# size: 0 for central directory. 4 for local header
|
109
|
+
return if(!size || size == 0)
|
110
110
|
uid, gid = content.unpack("vv")
|
111
111
|
@uid ||= uid
|
112
112
|
@gid ||= gid
|
@@ -132,17 +132,17 @@ module Zip
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def merge(binstr)
|
135
|
-
|
135
|
+
return if binstr.empty?
|
136
136
|
i = 0
|
137
|
-
while i < binstr.
|
137
|
+
while i < binstr.bytesize
|
138
138
|
id = binstr[i,2]
|
139
|
-
len = binstr[i+2,2].to_s.unpack("v")[0]
|
139
|
+
len = binstr[i + 2,2].to_s.unpack("v")[0]
|
140
140
|
if id && ID_MAP.member?(id)
|
141
141
|
field_name = ID_MAP[id].name
|
142
142
|
if self.member?(field_name)
|
143
|
-
self[field_name].mergea(binstr[i, len+4])
|
143
|
+
self[field_name].mergea(binstr[i, len + 4])
|
144
144
|
else
|
145
|
-
field_obj = ID_MAP[id].new(binstr[i, len+4])
|
145
|
+
field_obj = ID_MAP[id].new(binstr[i, len + 4])
|
146
146
|
self[field_name] = field_obj
|
147
147
|
end
|
148
148
|
elsif id
|
@@ -154,13 +154,13 @@ module Zip
|
|
154
154
|
end
|
155
155
|
self["Unknown"] = s
|
156
156
|
end
|
157
|
-
if !
|
157
|
+
if !len || len + 4 > binstr[i..-1].bytesize
|
158
158
|
self["Unknown"] << binstr[i..-1]
|
159
|
-
break
|
159
|
+
break
|
160
160
|
end
|
161
|
-
self["Unknown"] << binstr[i, len+4]
|
161
|
+
self["Unknown"] << binstr[i, len + 4]
|
162
162
|
end
|
163
|
-
i += len+4
|
163
|
+
i += len + 4
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
@@ -180,26 +180,26 @@ module Zip
|
|
180
180
|
|
181
181
|
def to_local_bin
|
182
182
|
s = ""
|
183
|
-
each
|
183
|
+
each do |k, v|
|
184
184
|
s << v.to_local_bin
|
185
|
-
|
185
|
+
end
|
186
186
|
s
|
187
187
|
end
|
188
188
|
alias :to_s :to_local_bin
|
189
189
|
|
190
190
|
def to_c_dir_bin
|
191
191
|
s = ""
|
192
|
-
each
|
192
|
+
each do |k, v|
|
193
193
|
s << v.to_c_dir_bin
|
194
|
-
|
194
|
+
end
|
195
195
|
s
|
196
196
|
end
|
197
197
|
|
198
198
|
def c_dir_length
|
199
|
-
to_c_dir_bin.
|
199
|
+
to_c_dir_bin.bytesize
|
200
200
|
end
|
201
201
|
def local_length
|
202
|
-
to_local_bin.
|
202
|
+
to_local_bin.bytesize
|
203
203
|
end
|
204
204
|
alias :c_dir_size :c_dir_length
|
205
205
|
alias :local_size :local_length
|
data/lib/zip/zip_file.rb
CHANGED
@@ -61,63 +61,64 @@ module Zip
|
|
61
61
|
super()
|
62
62
|
@name = fileName
|
63
63
|
@comment = ""
|
64
|
-
|
65
|
-
::File.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
64
|
+
case
|
65
|
+
when ::File.exists?(fileName) && !buffer
|
66
|
+
::File.open(name, "rb") do |f|
|
67
|
+
read_from_stream(f)
|
68
|
+
end
|
69
|
+
when create
|
70
|
+
@entrySet = ZipEntrySet.new
|
71
|
+
else
|
72
|
+
raise ZipError, "File #{fileName} not found"
|
70
73
|
end
|
71
74
|
@create = create
|
72
75
|
@storedEntries = @entrySet.dup
|
73
|
-
|
76
|
+
@storedComment = @comment
|
74
77
|
@restore_ownership = false
|
75
78
|
@restore_permissions = false
|
76
79
|
@restore_times = true
|
77
80
|
end
|
78
81
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
82
|
+
class << self
|
83
|
+
# Same as #new. If a block is passed the ZipFile object is passed
|
84
|
+
# to the block and is automatically closed afterwards just as with
|
85
|
+
# ruby's builtin File.open method.
|
86
|
+
def open(fileName, create = nil)
|
87
|
+
zf = ZipFile.new(fileName, create)
|
88
|
+
if block_given?
|
89
|
+
begin
|
90
|
+
yield zf
|
91
|
+
ensure
|
92
|
+
zf.close
|
93
|
+
end
|
94
|
+
else
|
95
|
+
zf
|
89
96
|
end
|
90
|
-
else
|
91
|
-
zf
|
92
97
|
end
|
93
|
-
end
|
94
98
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
begin
|
99
|
+
# Same as #open. But outputs data to a buffer instead of a file
|
100
|
+
def add_buffer
|
101
|
+
zf = ZipFile.new('', true, true)
|
99
102
|
yield zf
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
+
zf.write_buffer
|
104
|
+
end
|
105
|
+
|
106
|
+
# Iterates over the contents of the ZipFile. This is more efficient
|
107
|
+
# than using a ZipInputStream since this methods simply iterates
|
108
|
+
# through the entries in the central directory structure in the archive
|
109
|
+
# whereas ZipInputStream jumps through the entire archive accessing the
|
110
|
+
# local entry headers (which contain the same information as the
|
111
|
+
# central directory).
|
112
|
+
def foreach(aZipFileName, &block)
|
113
|
+
open(aZipFileName) do |zipFile|
|
114
|
+
zipFile.each(&block)
|
115
|
+
end
|
103
116
|
end
|
104
117
|
end
|
105
118
|
|
106
119
|
# Returns the zip files comment, if it has one
|
107
120
|
attr_accessor :comment
|
108
121
|
|
109
|
-
# Iterates over the contents of the ZipFile. This is more efficient
|
110
|
-
# than using a ZipInputStream since this methods simply iterates
|
111
|
-
# through the entries in the central directory structure in the archive
|
112
|
-
# whereas ZipInputStream jumps through the entire archive accessing the
|
113
|
-
# local entry headers (which contain the same information as the
|
114
|
-
# central directory).
|
115
|
-
def ZipFile.foreach(aZipFileName, &block)
|
116
|
-
ZipFile.open(aZipFileName) do |zipFile|
|
117
|
-
zipFile.each(&block)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
122
|
# Returns an input stream to the specified entry. If a block is passed
|
122
123
|
# the stream object is passed to the block and the stream is automatically
|
123
124
|
# closed afterwards just as with ruby's builtin File.open method.
|
@@ -152,7 +153,7 @@ module Zip
|
|
152
153
|
|
153
154
|
# Convenience method for adding the contents of a file to the archive
|
154
155
|
def add(entry, srcPath, &continueOnExistsProc)
|
155
|
-
continueOnExistsProc ||= proc {
|
156
|
+
continueOnExistsProc ||= proc { Zip.options[:continue_on_exists_proc] }
|
156
157
|
check_entry_exists(entry, continueOnExistsProc, "add")
|
157
158
|
newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s)
|
158
159
|
newEntry.gather_fileinfo_from_srcpath(srcPath)
|
@@ -183,7 +184,7 @@ module Zip
|
|
183
184
|
|
184
185
|
# Extracts entry to file destPath.
|
185
186
|
def extract(entry, destPath, &onExistsProc)
|
186
|
-
onExistsProc ||= proc {
|
187
|
+
onExistsProc ||= proc { Zip.options[:on_exists_proc] }
|
187
188
|
foundEntry = get_entry(entry)
|
188
189
|
foundEntry.extract(destPath, &onExistsProc)
|
189
190
|
end
|
@@ -191,7 +192,7 @@ module Zip
|
|
191
192
|
# Commits changes that has been made since the previous commit to
|
192
193
|
# the zip archive.
|
193
194
|
def commit
|
194
|
-
return if !
|
195
|
+
return if !commit_required?
|
195
196
|
on_success_replace(name) {
|
196
197
|
|tmpFile|
|
197
198
|
ZipOutputStream.open(tmpFile) {
|
@@ -229,16 +230,13 @@ module Zip
|
|
229
230
|
@entrySet.each do |e|
|
230
231
|
return true if e.dirty
|
231
232
|
end
|
232
|
-
|
233
|
+
@comment != @storedComment || @entrySet != @storedEntries || @create == ZipFile::CREATE
|
233
234
|
end
|
234
235
|
|
235
236
|
# Searches for entry with the specified name. Returns nil if
|
236
237
|
# no entry is found. See also get_entry
|
237
|
-
def find_entry(
|
238
|
-
@entrySet.
|
239
|
-
|e|
|
240
|
-
e.name.sub(/\/$/, "") == entry.to_s.sub(/\/$/, "")
|
241
|
-
}
|
238
|
+
def find_entry(entry_name)
|
239
|
+
@entrySet.find_entry(entry_name)
|
242
240
|
end
|
243
241
|
|
244
242
|
# Searches for an entry just as find_entry, but throws Errno::ENOENT
|
@@ -259,7 +257,7 @@ module Zip
|
|
259
257
|
if find_entry(entryName)
|
260
258
|
raise Errno::EEXIST, "File exists - #{entryName}"
|
261
259
|
end
|
262
|
-
entryName = entryName.to_s
|
260
|
+
entryName = entryName.dup.to_s
|
263
261
|
entryName << '/' unless entryName.end_with?('/')
|
264
262
|
@entrySet << ZipStreamableDirectory.new(@name, entryName, nil, permissionInt)
|
265
263
|
end
|
@@ -279,8 +277,8 @@ module Zip
|
|
279
277
|
end
|
280
278
|
|
281
279
|
def check_entry_exists(entryName, continueOnExistsProc, procedureName)
|
282
|
-
continueOnExistsProc ||= proc {
|
283
|
-
if @entrySet.
|
280
|
+
continueOnExistsProc ||= proc { Zip.options[:continue_on_exists_proc] }
|
281
|
+
if @entrySet.include?(entryName)
|
284
282
|
if continueOnExistsProc.call
|
285
283
|
remove get_entry(entryName)
|
286
284
|
else
|
@@ -74,20 +74,23 @@ module Zip
|
|
74
74
|
update_local_headers
|
75
75
|
write_central_directory
|
76
76
|
@closed = true
|
77
|
-
|
77
|
+
@outputStream
|
78
78
|
end
|
79
79
|
|
80
80
|
# Closes the current entry and opens a new for writing.
|
81
81
|
# +entry+ can be a ZipEntry object or a string.
|
82
82
|
def put_next_entry(entryname, comment = nil, extra = nil, compression_method = ZipEntry::DEFLATED, level = Zlib::DEFAULT_COMPRESSION)
|
83
83
|
raise ZipError, "zip stream is closed" if @closed
|
84
|
-
|
85
|
-
|
84
|
+
if entryname.kind_of?(ZipEntry)
|
85
|
+
new_entry = entryname
|
86
|
+
else
|
87
|
+
new_entry = ZipEntry.new(@fileName, entryname.to_s)
|
88
|
+
end
|
86
89
|
new_entry.comment = comment if !comment.nil?
|
87
90
|
if (!extra.nil?)
|
88
91
|
new_entry.extra = ZipExtraField === extra ? extra : ZipExtraField.new(extra.to_s)
|
89
92
|
end
|
90
|
-
new_entry.compression_method = compression_method
|
93
|
+
new_entry.compression_method = compression_method if !compression_method.nil?
|
91
94
|
init_next_entry(new_entry, level)
|
92
95
|
@currentEntry = new_entry
|
93
96
|
end
|
@@ -110,11 +113,11 @@ module Zip
|
|
110
113
|
end
|
111
114
|
|
112
115
|
private
|
116
|
+
|
113
117
|
def finalize_current_entry
|
114
118
|
return unless @currentEntry
|
115
119
|
finish
|
116
|
-
@currentEntry.compressed_size = @outputStream.tell - @currentEntry.localHeaderOffset -
|
117
|
-
@currentEntry.calculate_local_header_size
|
120
|
+
@currentEntry.compressed_size = @outputStream.tell - @currentEntry.localHeaderOffset - @currentEntry.calculate_local_header_size
|
118
121
|
@currentEntry.size = @compressor.size
|
119
122
|
@currentEntry.crc = @compressor.crc
|
120
123
|
@currentEntry = nil
|
@@ -138,12 +141,11 @@ module Zip
|
|
138
141
|
end
|
139
142
|
|
140
143
|
def update_local_headers
|
141
|
-
pos = @outputStream.
|
142
|
-
@entrySet.each
|
143
|
-
|entry|
|
144
|
+
pos = @outputStream.pos
|
145
|
+
@entrySet.each do |entry|
|
144
146
|
@outputStream.pos = entry.localHeaderOffset
|
145
147
|
entry.write_local_entry(@outputStream)
|
146
|
-
|
148
|
+
end
|
147
149
|
@outputStream.pos = pos
|
148
150
|
end
|
149
151
|
|
metadata
CHANGED
@@ -1,22 +1,27 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyzip
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.6.1
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.9.7
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Alan Harper
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
12
|
+
|
13
|
+
date: 2012-04-07 00:00:00 Z
|
13
14
|
dependencies: []
|
15
|
+
|
14
16
|
description:
|
15
17
|
email: alan@aussiegeek.net
|
16
18
|
executables: []
|
19
|
+
|
17
20
|
extensions: []
|
21
|
+
|
18
22
|
extra_rdoc_files: []
|
19
|
-
|
23
|
+
|
24
|
+
files:
|
20
25
|
- samples/example.rb
|
21
26
|
- samples/example_filesystem.rb
|
22
27
|
- samples/example_recursive.rb
|
@@ -36,6 +41,7 @@ files:
|
|
36
41
|
- lib/zip/null_input_stream.rb
|
37
42
|
- lib/zip/pass_thru_compressor.rb
|
38
43
|
- lib/zip/pass_thru_decompressor.rb
|
44
|
+
- lib/zip/settings.rb
|
39
45
|
- lib/zip/tempfile_bugfixed.rb
|
40
46
|
- lib/zip/zip.rb
|
41
47
|
- lib/zip/zip_central_directory.rb
|
@@ -54,27 +60,31 @@ files:
|
|
54
60
|
- Rakefile
|
55
61
|
homepage: http://github.com/aussiegeek/rubyzip
|
56
62
|
licenses: []
|
63
|
+
|
57
64
|
post_install_message:
|
58
65
|
rdoc_options: []
|
59
|
-
|
66
|
+
|
67
|
+
require_paths:
|
60
68
|
- lib
|
61
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
70
|
none: false
|
63
|
-
requirements:
|
64
|
-
- -
|
65
|
-
- !ruby/object:Gem::Version
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
66
74
|
version: 1.8.7
|
67
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
76
|
none: false
|
69
|
-
requirements:
|
70
|
-
- -
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
version:
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
73
81
|
requirements: []
|
82
|
+
|
74
83
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.8.
|
84
|
+
rubygems_version: 1.8.19
|
76
85
|
signing_key:
|
77
86
|
specification_version: 3
|
78
87
|
summary: rubyzip is a ruby module for reading and writing zip files
|
79
88
|
test_files: []
|
89
|
+
|
80
90
|
has_rdoc:
|