rubyzip 0.9.9 → 1.0.0.beta1
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.
- checksums.yaml +7 -0
- data/NEWS +9 -5
- data/README.md +79 -21
- data/Rakefile +1 -1
- data/lib/zip.rb +52 -0
- data/lib/zip/central_directory.rb +135 -0
- data/lib/zip/constants.rb +57 -7
- data/lib/zip/decompressor.rb +2 -2
- data/lib/zip/deflater.rb +11 -12
- data/lib/zip/dos_time.rb +9 -9
- data/lib/zip/entry.rb +609 -0
- data/lib/zip/entry_set.rb +86 -0
- data/lib/zip/errors.rb +8 -0
- data/lib/zip/extra_field.rb +90 -0
- data/lib/zip/extra_field/generic.rb +43 -0
- data/lib/zip/extra_field/universal_time.rb +47 -0
- data/lib/zip/extra_field/unix.rb +39 -0
- data/lib/zip/{zip_file.rb → file.rb} +140 -61
- data/lib/zip/{zipfilesystem.rb → filesystem.rb} +12 -12
- data/lib/zip/inflater.rb +24 -24
- data/lib/zip/{zip_input_stream.rb → input_stream.rb} +11 -10
- data/lib/zip/ioextras.rb +145 -123
- data/lib/zip/null_compressor.rb +1 -1
- data/lib/zip/null_decompressor.rb +5 -3
- data/lib/zip/null_input_stream.rb +2 -2
- data/lib/zip/{zip_output_stream.rb → output_stream.rb} +43 -41
- data/lib/zip/pass_thru_compressor.rb +2 -2
- data/lib/zip/pass_thru_decompressor.rb +17 -16
- data/lib/zip/{zip_streamable_directory.rb → streamable_directory.rb} +1 -1
- data/lib/zip/{zip_streamable_stream.rb → streamable_stream.rb} +2 -2
- data/lib/zip/version.rb +3 -0
- data/samples/example.rb +27 -5
- data/samples/example_filesystem.rb +2 -2
- data/samples/example_recursive.rb +1 -1
- data/samples/gtkRubyzip.rb +1 -1
- data/samples/qtzip.rb +2 -2
- data/samples/write_simple.rb +1 -1
- data/samples/zipfind.rb +1 -1
- metadata +29 -27
- data/lib/zip/settings.rb +0 -10
- data/lib/zip/tempfile_bugfixed.rb +0 -195
- data/lib/zip/zip.rb +0 -56
- data/lib/zip/zip_central_directory.rb +0 -135
- data/lib/zip/zip_entry.rb +0 -638
- data/lib/zip/zip_entry_set.rb +0 -77
- data/lib/zip/zip_extra_field.rb +0 -213
data/lib/zip/decompressor.rb
CHANGED
data/lib/zip/deflater.rb
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
module Zip
|
2
2
|
class Deflater < Compressor #:nodoc:all
|
3
|
-
|
3
|
+
|
4
|
+
def initialize(output_stream, level = ::Zlib::DEFAULT_COMPRESSION)
|
4
5
|
super()
|
5
|
-
@
|
6
|
-
@
|
7
|
-
@size
|
8
|
-
@crc
|
6
|
+
@output_stream = output_stream
|
7
|
+
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
|
8
|
+
@size = 0
|
9
|
+
@crc = ::Zlib.crc32
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def << (data)
|
12
|
-
val
|
13
|
-
@crc
|
13
|
+
val = data.to_s
|
14
|
+
@crc = Zlib::crc32(val, @crc)
|
14
15
|
@size += val.bytesize
|
15
|
-
@
|
16
|
+
@output_stream << @zlib_deflater.deflate(data)
|
16
17
|
end
|
17
18
|
|
18
19
|
def finish
|
19
|
-
until @
|
20
|
-
@outputStream << @zlibDeflater.finish
|
21
|
-
end
|
20
|
+
@output_stream << @zlib_deflater.finish until @zlib_deflater.finished?
|
22
21
|
end
|
23
22
|
|
24
23
|
attr_reader :size, :crc
|
data/lib/zip/dos_time.rb
CHANGED
@@ -15,14 +15,14 @@ module Zip
|
|
15
15
|
|
16
16
|
def to_binary_dos_time
|
17
17
|
(sec/2) +
|
18
|
-
|
19
|
-
|
18
|
+
(min << 5) +
|
19
|
+
(hour << 11)
|
20
20
|
end
|
21
21
|
|
22
22
|
def to_binary_dos_date
|
23
23
|
(day) +
|
24
|
-
|
25
|
-
|
24
|
+
(month << 5) +
|
25
|
+
((year - 1980) << 9)
|
26
26
|
end
|
27
27
|
|
28
28
|
# Dos time is only stored with two seconds accuracy
|
@@ -31,14 +31,14 @@ module Zip
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.parse_binary_dos_format(binaryDosDate, binaryDosTime)
|
34
|
-
second = 2 * (
|
35
|
-
minute = (
|
34
|
+
second = 2 * (0b11111 & binaryDosTime)
|
35
|
+
minute = (0b11111100000 & binaryDosTime) >> 5
|
36
36
|
hour = (0b1111100000000000 & binaryDosTime) >> 11
|
37
|
-
day = (
|
38
|
-
month = (
|
37
|
+
day = (0b11111 & binaryDosDate)
|
38
|
+
month = (0b111100000 & binaryDosDate) >> 5
|
39
39
|
year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980
|
40
40
|
begin
|
41
|
-
|
41
|
+
self.local(year, month, day, hour, minute, second)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
data/lib/zip/entry.rb
ADDED
@@ -0,0 +1,609 @@
|
|
1
|
+
module Zip
|
2
|
+
class Entry
|
3
|
+
STORED = 0
|
4
|
+
DEFLATED = 8
|
5
|
+
# Language encoding flag (EFS) bit
|
6
|
+
EFS = 0b100000000000
|
7
|
+
|
8
|
+
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
|
9
|
+
:name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes,
|
10
|
+
:gp_flags, :header_signature, :follow_symlinks,
|
11
|
+
:restore_times, :restore_permissions, :restore_ownership,
|
12
|
+
:unix_uid, :unix_gid, :unix_perms,
|
13
|
+
:dirty
|
14
|
+
attr_reader :ftype, :filepath # :nodoc:
|
15
|
+
|
16
|
+
def set_default_vars_values
|
17
|
+
@local_header_offset = 0
|
18
|
+
@local_header_size = 0
|
19
|
+
@internal_file_attributes = 1
|
20
|
+
@external_file_attributes = 0
|
21
|
+
@header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
22
|
+
|
23
|
+
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT
|
24
|
+
@version = 52 # this library's version
|
25
|
+
|
26
|
+
@ftype = nil # unspecified or unknown
|
27
|
+
@filepath = nil
|
28
|
+
@gp_flags = 0
|
29
|
+
if ::Zip.unicode_names
|
30
|
+
@gp_flags |= EFS
|
31
|
+
@version = 63
|
32
|
+
end
|
33
|
+
@follow_symlinks = false
|
34
|
+
|
35
|
+
@restore_times = true
|
36
|
+
@restore_permissions = false
|
37
|
+
@restore_ownership = false
|
38
|
+
# BUG: need an extra field to support uid/gid's
|
39
|
+
@unix_uid = nil
|
40
|
+
@unix_gid = nil
|
41
|
+
@unix_perms = nil
|
42
|
+
#@posix_acl = nil
|
43
|
+
#@ntfs_acl = nil
|
44
|
+
@dirty = false
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_name(name)
|
48
|
+
if name.start_with?('/')
|
49
|
+
raise ::Zip::ZipEntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(*args)
|
54
|
+
name = args[1] || ''
|
55
|
+
check_name(name)
|
56
|
+
|
57
|
+
set_default_vars_values
|
58
|
+
@fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX
|
59
|
+
|
60
|
+
@zipfile = args[0] || ''
|
61
|
+
@name = name
|
62
|
+
@comment = args[2] || ''
|
63
|
+
@extra = args[3] || ''
|
64
|
+
@compressed_size = args[4] || 0
|
65
|
+
@crc = args[5] || 0
|
66
|
+
@compression_method = args[6] || ::Zip::Entry::DEFLATED
|
67
|
+
@size = args[7] || 0
|
68
|
+
@time = args[8] || ::Zip::DOSTime.now
|
69
|
+
|
70
|
+
@ftype = name_is_directory? ? :directory : :file
|
71
|
+
@extra = ::Zip::ExtraField.new(@extra.to_s) unless ::Zip::ExtraField === @extra
|
72
|
+
end
|
73
|
+
|
74
|
+
def time
|
75
|
+
if @extra['UniversalTime']
|
76
|
+
@extra['UniversalTime'].mtime
|
77
|
+
else
|
78
|
+
# Standard time field in central directory has local time
|
79
|
+
# under archive creator. Then, we can't get timezone.
|
80
|
+
@time
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
alias :mtime :time
|
85
|
+
|
86
|
+
def time=(value)
|
87
|
+
unless @extra.member?('UniversalTime')
|
88
|
+
@extra.create('UniversalTime')
|
89
|
+
end
|
90
|
+
@extra['UniversalTime'].mtime = value
|
91
|
+
@time = value
|
92
|
+
end
|
93
|
+
|
94
|
+
def file_type_is?(type)
|
95
|
+
raise ZipInternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
|
96
|
+
@ftype == type
|
97
|
+
end
|
98
|
+
|
99
|
+
# Dynamic checkers
|
100
|
+
%w(directory file symlink).each do |k|
|
101
|
+
define_method "#{k}?" do
|
102
|
+
file_type_is?(k.to_sym)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def name_is_directory? #:nodoc:all
|
107
|
+
@name.end_with?('/')
|
108
|
+
end
|
109
|
+
|
110
|
+
def local_entry_offset #:nodoc:all
|
111
|
+
local_header_offset + @local_header_size
|
112
|
+
end
|
113
|
+
|
114
|
+
def name_size
|
115
|
+
@name ? @name.bytesize : 0
|
116
|
+
end
|
117
|
+
|
118
|
+
def extra_size
|
119
|
+
@extra ? @extra.local_size : 0
|
120
|
+
end
|
121
|
+
|
122
|
+
def comment_size
|
123
|
+
@comment ? @comment.bytesize : 0
|
124
|
+
end
|
125
|
+
|
126
|
+
def calculate_local_header_size #:nodoc:all
|
127
|
+
LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size
|
128
|
+
end
|
129
|
+
|
130
|
+
def cdir_header_size #:nodoc:all
|
131
|
+
CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size +
|
132
|
+
(@extra ? @extra.c_dir_size : 0) + comment_size
|
133
|
+
end
|
134
|
+
|
135
|
+
def next_header_offset #:nodoc:all
|
136
|
+
local_entry_offset + self.compressed_size
|
137
|
+
end
|
138
|
+
|
139
|
+
# Extracts entry to file dest_path (defaults to @name).
|
140
|
+
def extract(dest_path = @name, &block)
|
141
|
+
block ||= proc { ::Zip.on_exists_proc }
|
142
|
+
|
143
|
+
if directory? || file? || symlink?
|
144
|
+
self.__send__("create_#{@ftype}", dest_path, &block)
|
145
|
+
else
|
146
|
+
raise RuntimeError, "unknown file type #{self.inspect}"
|
147
|
+
end
|
148
|
+
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_s
|
153
|
+
@name
|
154
|
+
end
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
class << self
|
159
|
+
def read_zip_short(io) # :nodoc:
|
160
|
+
io.read(2).unpack('v')[0]
|
161
|
+
end
|
162
|
+
|
163
|
+
def read_zip_long(io) # :nodoc:
|
164
|
+
io.read(4).unpack('V')[0]
|
165
|
+
end
|
166
|
+
|
167
|
+
def read_c_dir_entry(io) #:nodoc:all
|
168
|
+
entry = new(io.path)
|
169
|
+
entry.read_c_dir_entry(io)
|
170
|
+
entry
|
171
|
+
rescue ZipError
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def read_local_entry(io)
|
176
|
+
entry = new(io.path)
|
177
|
+
entry.read_local_entry(io)
|
178
|
+
entry
|
179
|
+
rescue ZipError
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
public
|
186
|
+
|
187
|
+
def unpack_local_entry(buf)
|
188
|
+
@header_signature,
|
189
|
+
@version,
|
190
|
+
@fstype,
|
191
|
+
@gp_flags,
|
192
|
+
@compression_method,
|
193
|
+
@last_mod_time,
|
194
|
+
@last_mod_date,
|
195
|
+
@crc,
|
196
|
+
@compressed_size,
|
197
|
+
@size,
|
198
|
+
@name_length,
|
199
|
+
@extra_length = buf.unpack('VCCvvvvVVVvv')
|
200
|
+
end
|
201
|
+
|
202
|
+
def read_local_entry(io) #:nodoc:all
|
203
|
+
@local_header_offset = io.tell
|
204
|
+
|
205
|
+
static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH)
|
206
|
+
|
207
|
+
unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
|
208
|
+
raise ZipError, "Premature end of file. Not enough data for zip entry local header"
|
209
|
+
end
|
210
|
+
|
211
|
+
unpack_local_entry(static_sized_fields_buf)
|
212
|
+
|
213
|
+
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
214
|
+
raise ::Zip::ZipError, "Zip local header magic not found at location '#{local_header_offset}'"
|
215
|
+
end
|
216
|
+
set_time(@last_mod_date, @last_mod_time)
|
217
|
+
|
218
|
+
@name = io.read(@name_length)
|
219
|
+
extra = io.read(@extra_length)
|
220
|
+
|
221
|
+
@name.gsub!('\\', '/')
|
222
|
+
|
223
|
+
if extra && extra.bytesize != @extra_length
|
224
|
+
raise ::Zip::ZipError, "Truncated local zip entry header"
|
225
|
+
else
|
226
|
+
if ::Zip::ExtraField === @extra
|
227
|
+
@extra.merge(extra)
|
228
|
+
else
|
229
|
+
@extra = ::Zip::ExtraField.new(extra)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
@local_header_size = calculate_local_header_size
|
233
|
+
end
|
234
|
+
|
235
|
+
def pack_local_entry
|
236
|
+
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
237
|
+
@version_needed_to_extract, # version needed to extract
|
238
|
+
@gp_flags, # @gp_flags ,
|
239
|
+
@compression_method,
|
240
|
+
@time.to_binary_dos_time, # @last_mod_time ,
|
241
|
+
@time.to_binary_dos_date, # @last_mod_date ,
|
242
|
+
@crc,
|
243
|
+
@compressed_size,
|
244
|
+
@size,
|
245
|
+
name_size,
|
246
|
+
@extra ? @extra.local_length : 0].pack('VvvvvvVVVvv')
|
247
|
+
end
|
248
|
+
|
249
|
+
def write_local_entry(io) #:nodoc:all
|
250
|
+
@local_header_offset = io.tell
|
251
|
+
|
252
|
+
io << pack_local_entry
|
253
|
+
|
254
|
+
io << @name
|
255
|
+
io << (@extra ? @extra.to_local_bin : '')
|
256
|
+
end
|
257
|
+
|
258
|
+
def unpack_c_dir_entry(buf)
|
259
|
+
@header_signature,
|
260
|
+
@version, # version of encoding software
|
261
|
+
@fstype, # filesystem type
|
262
|
+
@version_needed_to_extract,
|
263
|
+
@gp_flags,
|
264
|
+
@compression_method,
|
265
|
+
@last_mod_time,
|
266
|
+
@last_mod_date,
|
267
|
+
@crc,
|
268
|
+
@compressed_size,
|
269
|
+
@size,
|
270
|
+
@name_length,
|
271
|
+
@extra_length,
|
272
|
+
@comment_length,
|
273
|
+
_, # diskNumberStart
|
274
|
+
@internal_file_attributes,
|
275
|
+
@external_file_attributes,
|
276
|
+
@local_header_offset,
|
277
|
+
@name,
|
278
|
+
@extra,
|
279
|
+
@comment = buf.unpack('VCCvvvvvVVVvvvvvVV')
|
280
|
+
end
|
281
|
+
|
282
|
+
def set_ftype_from_c_dir_entry
|
283
|
+
@ftype = case @fstype
|
284
|
+
when ::Zip::FSTYPE_UNIX
|
285
|
+
@unix_perms = (@external_file_attributes >> 16) & 07777
|
286
|
+
case (@external_file_attributes >> 28)
|
287
|
+
when ::Zip::FILE_TYPE_DIR
|
288
|
+
:directory
|
289
|
+
when ::Zip::FILE_TYPE_FILE
|
290
|
+
:file
|
291
|
+
when ::Zip::FILE_TYPE_SYMLINK
|
292
|
+
:symlink
|
293
|
+
else
|
294
|
+
#best case guess for whether it is a file or not
|
295
|
+
#Otherwise this would be set to unknown and that entry would never be able to extracted
|
296
|
+
if name_is_directory?
|
297
|
+
:directory
|
298
|
+
else
|
299
|
+
:file
|
300
|
+
end
|
301
|
+
end
|
302
|
+
else
|
303
|
+
if name_is_directory?
|
304
|
+
:directory
|
305
|
+
else
|
306
|
+
:file
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def check_c_dir_entry_static_header_length(buf)
|
312
|
+
unless buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
313
|
+
raise ZipError, 'Premature end of file. Not enough data for zip cdir entry header'
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def check_c_dir_entry_signature
|
318
|
+
unless header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
319
|
+
raise ZipError, "Zip local header magic not found at location '#{local_header_offset}'"
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def check_c_dir_entry_comment_size
|
324
|
+
unless @comment && @comment.bytesize == @comment_length
|
325
|
+
raise ::Zip::ZipError, "Truncated cdir zip entry header"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def read_c_dir_extra_field(io)
|
330
|
+
if @extra.is_a?(::Zip::ExtraField)
|
331
|
+
@extra.merge(io.read(@extra_length))
|
332
|
+
else
|
333
|
+
@extra = ::Zip::ExtraField.new(io.read(@extra_length))
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def read_c_dir_entry(io) #:nodoc:all
|
338
|
+
static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH)
|
339
|
+
check_c_dir_entry_static_header_length(static_sized_fields_buf)
|
340
|
+
unpack_c_dir_entry(static_sized_fields_buf)
|
341
|
+
check_c_dir_entry_signature
|
342
|
+
set_time(@last_mod_date, @last_mod_time)
|
343
|
+
@name = io.read(@name_length).gsub('\\', '/')
|
344
|
+
read_c_dir_extra_field(io)
|
345
|
+
@comment = io.read(@comment_length)
|
346
|
+
check_c_dir_entry_comment_size
|
347
|
+
set_ftype_from_c_dir_entry
|
348
|
+
@local_header_size = calculate_local_header_size
|
349
|
+
end
|
350
|
+
|
351
|
+
def file_stat(path) # :nodoc:
|
352
|
+
if @follow_symlinks
|
353
|
+
::File::stat(path)
|
354
|
+
else
|
355
|
+
::File::lstat(path)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def get_extra_attributes_from_path(path) # :nodoc:
|
360
|
+
unless Zip::RUNNING_ON_WINDOWS
|
361
|
+
stat = file_stat(path)
|
362
|
+
@unix_uid = stat.uid
|
363
|
+
@unix_gid = stat.gid
|
364
|
+
@unix_perms = stat.mode & 07777
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def set_unix_permissions_on_path(dest_path)
|
369
|
+
# BUG: does not update timestamps into account
|
370
|
+
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
371
|
+
unix_perms_mask = 01777
|
372
|
+
unix_perms_mask = 07777 if @restore_ownership
|
373
|
+
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
|
374
|
+
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
|
375
|
+
# File::utimes()
|
376
|
+
end
|
377
|
+
|
378
|
+
def set_extra_attributes_on_path(dest_path) # :nodoc:
|
379
|
+
return unless (file? || directory?)
|
380
|
+
|
381
|
+
case @fstype
|
382
|
+
when ::Zip::FSTYPE_UNIX
|
383
|
+
set_unix_permissions_on_path(dest_path)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def pack_c_dir_entry
|
388
|
+
[
|
389
|
+
@header_signature,
|
390
|
+
@version, # version of encoding software
|
391
|
+
@fstype, # filesystem type
|
392
|
+
@version_needed_to_extract, # @versionNeededToExtract ,
|
393
|
+
@gp_flags, # @gp_flags ,
|
394
|
+
@compression_method,
|
395
|
+
@time.to_binary_dos_time, # @last_mod_time ,
|
396
|
+
@time.to_binary_dos_date, # @last_mod_date ,
|
397
|
+
@crc,
|
398
|
+
@compressed_size,
|
399
|
+
@size,
|
400
|
+
name_size,
|
401
|
+
@extra ? @extra.c_dir_length : 0,
|
402
|
+
comment_size,
|
403
|
+
0, # disk number start
|
404
|
+
@internal_file_attributes, # file type (binary=0, text=1)
|
405
|
+
@external_file_attributes, # native filesystem attributes
|
406
|
+
@local_header_offset,
|
407
|
+
@name,
|
408
|
+
@extra,
|
409
|
+
@comment
|
410
|
+
].pack('VCCvvvvvVVVvvvvvVV')
|
411
|
+
end
|
412
|
+
|
413
|
+
def write_c_dir_entry(io) #:nodoc:all
|
414
|
+
case @fstype
|
415
|
+
when ::Zip::FSTYPE_UNIX
|
416
|
+
ft = case @ftype
|
417
|
+
when :file
|
418
|
+
@unix_perms ||= 0644
|
419
|
+
::Zip::FILE_TYPE_FILE
|
420
|
+
when :directory
|
421
|
+
@unix_perms ||= 0755
|
422
|
+
::Zip::FILE_TYPE_DIR
|
423
|
+
when :symlink
|
424
|
+
@unix_perms ||= 0755
|
425
|
+
::Zip::FILE_TYPE_SYMLINK
|
426
|
+
end
|
427
|
+
|
428
|
+
unless ft.nil?
|
429
|
+
@external_file_attributes = (ft << 12 | (@unix_perms & 07777)) << 16
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
io << pack_c_dir_entry
|
434
|
+
|
435
|
+
io << @name
|
436
|
+
io << (@extra ? @extra.to_c_dir_bin : '')
|
437
|
+
io << @comment
|
438
|
+
end
|
439
|
+
|
440
|
+
def ==(other)
|
441
|
+
return false unless other.class == self.class
|
442
|
+
# Compares contents of local entry and exposed fields
|
443
|
+
keys_equal = %w(compression_method crc compressed_size size name extra filepath).all? do |k|
|
444
|
+
other.__send__(k.to_sym) == self.__send__(k.to_sym)
|
445
|
+
end
|
446
|
+
keys_equal && self.time.dos_equals(other.time)
|
447
|
+
end
|
448
|
+
|
449
|
+
def <=> (other)
|
450
|
+
self.to_s <=> other.to_s
|
451
|
+
end
|
452
|
+
|
453
|
+
# Returns an IO like object for the given ZipEntry.
|
454
|
+
# Warning: may behave weird with symlinks.
|
455
|
+
def get_input_stream(&block)
|
456
|
+
if @ftype == :directory
|
457
|
+
yield(::Zip::NullInputStream.instance) if block_given?
|
458
|
+
::Zip::NullInputStream.instance
|
459
|
+
elsif @filepath
|
460
|
+
case @ftype
|
461
|
+
when :file
|
462
|
+
::File.open(@filepath, 'rb', &block)
|
463
|
+
when :symlink
|
464
|
+
linkpath = ::File.readlink(@filepath)
|
465
|
+
stringio = ::StringIO.new(linkpath)
|
466
|
+
yield(stringio) if block_given?
|
467
|
+
stringio
|
468
|
+
else
|
469
|
+
raise "unknown @file_type #{@ftype}"
|
470
|
+
end
|
471
|
+
else
|
472
|
+
zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
|
473
|
+
zis.get_next_entry
|
474
|
+
if block_given?
|
475
|
+
begin
|
476
|
+
yield(zis)
|
477
|
+
ensure
|
478
|
+
zis.close
|
479
|
+
end
|
480
|
+
else
|
481
|
+
zis
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def gather_fileinfo_from_srcpath(src_path) # :nodoc:
|
487
|
+
stat = file_stat(src_path)
|
488
|
+
@ftype = case stat.ftype
|
489
|
+
when 'file'
|
490
|
+
if name_is_directory?
|
491
|
+
raise ArgumentError,
|
492
|
+
"entry name '#{newEntry}' indicates directory entry, but "+
|
493
|
+
"'#{src_path}' is not a directory"
|
494
|
+
end
|
495
|
+
:file
|
496
|
+
when 'directory'
|
497
|
+
@name += "/" unless name_is_directory?
|
498
|
+
:directory
|
499
|
+
when 'link'
|
500
|
+
if name_is_directory?
|
501
|
+
raise ArgumentError,
|
502
|
+
"entry name '#{newEntry}' indicates directory entry, but "+
|
503
|
+
"'#{src_path}' is not a directory"
|
504
|
+
end
|
505
|
+
:symlink
|
506
|
+
else
|
507
|
+
raise RuntimeError, "unknown file type: #{src_path.inspect} #{stat.inspect}"
|
508
|
+
end
|
509
|
+
|
510
|
+
@filepath = src_path
|
511
|
+
get_extra_attributes_from_path(@filepath)
|
512
|
+
end
|
513
|
+
|
514
|
+
def write_to_zip_output_stream(zip_output_stream) #:nodoc:all
|
515
|
+
if @ftype == :directory
|
516
|
+
zip_output_stream.put_next_entry(self)
|
517
|
+
elsif @filepath
|
518
|
+
zip_output_stream.put_next_entry(self, nil, nil, nil)
|
519
|
+
get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) }
|
520
|
+
else
|
521
|
+
zip_output_stream.copy_raw_entry(self)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
def parent_as_string
|
526
|
+
entry_name = name.chomp('/')
|
527
|
+
slash_index = entry_name.rindex('/')
|
528
|
+
slash_index ? entry_name.slice(0, slash_index+1) : nil
|
529
|
+
end
|
530
|
+
|
531
|
+
def get_raw_input_stream(&block)
|
532
|
+
::File.open(@zipfile, "rb", &block)
|
533
|
+
end
|
534
|
+
|
535
|
+
private
|
536
|
+
|
537
|
+
def set_time(binary_dos_date, binary_dos_time)
|
538
|
+
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
|
539
|
+
rescue ArgumentError
|
540
|
+
puts "Invalid date/time in zip entry"
|
541
|
+
end
|
542
|
+
|
543
|
+
def create_file(dest_path, continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
544
|
+
if ::File.exists?(dest_path) && !yield(self, dest_path)
|
545
|
+
raise ::Zip::ZipDestinationFileExistsError,
|
546
|
+
"Destination '#{dest_path}' already exists"
|
547
|
+
end
|
548
|
+
::File.open(dest_path, "wb") do |os|
|
549
|
+
get_input_stream do |is|
|
550
|
+
set_extra_attributes_on_path(dest_path)
|
551
|
+
|
552
|
+
buf = ''
|
553
|
+
while buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)
|
554
|
+
os << buf
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
def create_directory(dest_path)
|
561
|
+
return if ::File.directory?(dest_path)
|
562
|
+
if ::File.exists?(dest_path)
|
563
|
+
if block_given? && yield(self, dest_path)
|
564
|
+
::FileUtils::rm_f dest_path
|
565
|
+
else
|
566
|
+
raise ::Zip::ZipDestinationFileExistsError,
|
567
|
+
"Cannot create directory '#{dest_path}'. "+
|
568
|
+
"A file already exists with that name"
|
569
|
+
end
|
570
|
+
end
|
571
|
+
::FileUtils.mkdir_p(dest_path)
|
572
|
+
set_extra_attributes_on_path(dest_path)
|
573
|
+
end
|
574
|
+
|
575
|
+
# BUG: create_symlink() does not use &block
|
576
|
+
def create_symlink(dest_path)
|
577
|
+
stat = nil
|
578
|
+
begin
|
579
|
+
stat = ::File.lstat(dest_path)
|
580
|
+
rescue Errno::ENOENT
|
581
|
+
end
|
582
|
+
|
583
|
+
io = get_input_stream
|
584
|
+
linkto = io.read
|
585
|
+
|
586
|
+
if stat
|
587
|
+
if stat.symlink?
|
588
|
+
if ::File.readlink(dest_path) == linkto
|
589
|
+
return
|
590
|
+
else
|
591
|
+
raise ZipDestinationFileExistsError,
|
592
|
+
"Cannot create symlink '#{dest_path}'. "+
|
593
|
+
"A symlink already exists with that name"
|
594
|
+
end
|
595
|
+
else
|
596
|
+
raise ZipDestinationFileExistsError,
|
597
|
+
"Cannot create symlink '#{dest_path}'. "+
|
598
|
+
"A file already exists with that name"
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
::File.symlink(linkto, dest_path)
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
608
|
+
# rubyzip is free software; you can redistribute it and/or
|
609
|
+
# modify it under the terms of the ruby license.
|