2010-11-30 16:27:59 +08:00
|
|
|
module Zip
|
2013-06-03 15:56:24 +08:00
|
|
|
class CentralDirectory
|
2010-11-30 16:27:59 +08:00
|
|
|
include Enumerable
|
2011-11-18 04:53:04 +08:00
|
|
|
|
2013-08-27 04:26:14 +08:00
|
|
|
END_OF_CDS = 0x06054b50
|
|
|
|
ZIP64_END_OF_CDS = 0x06064b50
|
|
|
|
ZIP64_EOCD_LOCATOR = 0x07064b50
|
|
|
|
MAX_END_OF_CDS_SIZE = 65536 + 18
|
|
|
|
STATIC_EOCD_SIZE = 22
|
2010-11-30 16:27:59 +08:00
|
|
|
|
|
|
|
attr_reader :comment
|
|
|
|
|
|
|
|
# Returns an Enumerable containing the entries.
|
|
|
|
def entries
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.entries
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
|
2013-08-27 04:26:14 +08:00
|
|
|
def initialize(entries = EntrySet.new, comment = '') #:nodoc:
|
2010-11-30 16:27:59 +08:00
|
|
|
super()
|
2013-06-03 15:56:24 +08:00
|
|
|
@entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
|
2013-08-27 04:26:14 +08:00
|
|
|
@comment = comment
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
def write_to_stream(io) #:nodoc:
|
2010-11-30 16:27:59 +08:00
|
|
|
offset = io.tell
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
|
2010-11-30 16:27:59 +08:00
|
|
|
write_e_o_c_d(io, offset)
|
|
|
|
end
|
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
def write_e_o_c_d(io, offset) #:nodoc:
|
2012-02-14 06:03:34 +08:00
|
|
|
tmp = [
|
2013-08-27 04:26:14 +08:00
|
|
|
END_OF_CDS,
|
2013-06-03 02:33:03 +08:00
|
|
|
0, # @numberOfThisDisk
|
|
|
|
0, # @numberOfDiskWithStartOfCDir
|
|
|
|
@entry_set ? @entry_set.size : 0,
|
|
|
|
@entry_set ? @entry_set.size : 0,
|
|
|
|
cdir_size,
|
|
|
|
offset,
|
2012-02-14 06:03:34 +08:00
|
|
|
@comment ? @comment.length : 0
|
|
|
|
]
|
|
|
|
io << tmp.pack('VvvvvVVv')
|
|
|
|
io << @comment
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2013-06-03 02:33:03 +08:00
|
|
|
|
2010-11-30 16:27:59 +08:00
|
|
|
private :write_e_o_c_d
|
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
def cdir_size #:nodoc:
|
2013-08-27 04:26:14 +08:00
|
|
|
# does not include eocd
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.inject(0) do |value, entry|
|
2012-02-14 06:03:34 +08:00
|
|
|
entry.cdir_header_size + value
|
|
|
|
end
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2013-06-03 02:33:03 +08:00
|
|
|
|
2010-11-30 16:27:59 +08:00
|
|
|
private :cdir_size
|
|
|
|
|
2013-08-27 04:26:14 +08:00
|
|
|
def read_64_e_o_c_d(buf) #:nodoc:
|
|
|
|
buf = get_64_e_o_c_d(buf)
|
|
|
|
@size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf)
|
|
|
|
@version_made_by = Entry.read_zip_short(buf)
|
|
|
|
@version_needed_for_extract = Entry.read_zip_short(buf)
|
|
|
|
@number_of_this_disk = Entry.read_zip_long(buf)
|
|
|
|
@number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf)
|
|
|
|
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf)
|
|
|
|
@size = Entry.read_zip_64_long(buf)
|
|
|
|
@size_in_bytes = Entry.read_zip_64_long(buf)
|
|
|
|
@cdir_offset = Entry.read_zip_64_long(buf)
|
|
|
|
@zip_64_extensible = buf.slice!(0, buf.bytesize)
|
|
|
|
raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_e_o_c_d(buf) #:nodoc:
|
|
|
|
buf = get_e_o_c_d(buf)
|
|
|
|
@number_of_this_disk = Entry.read_zip_short(buf)
|
|
|
|
@number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf)
|
|
|
|
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf)
|
|
|
|
@size = Entry.read_zip_short(buf)
|
|
|
|
@size_in_bytes = Entry.read_zip_long(buf)
|
|
|
|
@cdir_offset = Entry.read_zip_long(buf)
|
|
|
|
comment_length = Entry.read_zip_short(buf)
|
|
|
|
@comment = if comment_length <= 0
|
|
|
|
buf.slice!(0, buf.size)
|
|
|
|
else
|
|
|
|
buf.read(comment_length)
|
|
|
|
end
|
2010-11-30 16:27:59 +08:00
|
|
|
raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
|
|
|
end
|
2011-11-18 04:53:04 +08:00
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
def read_central_directory_entries(io) #:nodoc:
|
2010-11-30 16:27:59 +08:00
|
|
|
begin
|
2013-08-27 04:26:14 +08:00
|
|
|
io.seek(@cdir_offset, IO::SEEK_SET)
|
2010-11-30 16:27:59 +08:00
|
|
|
rescue Errno::EINVAL
|
2011-11-18 04:53:04 +08:00
|
|
|
raise ZipError, "Zip consistency problem while reading central directory entry"
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2013-06-03 15:56:24 +08:00
|
|
|
@entry_set = EntrySet.new
|
2012-02-14 00:55:08 +08:00
|
|
|
@size.times do
|
2013-08-27 04:26:14 +08:00
|
|
|
@entry_set << Entry.read_c_dir_entry(io)
|
2012-02-14 00:55:08 +08:00
|
|
|
end
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2011-11-18 04:53:04 +08:00
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
def read_from_stream(io) #:nodoc:
|
2013-08-27 04:26:14 +08:00
|
|
|
buf = start_buf(io)
|
|
|
|
if self.zip64_file?(buf)
|
|
|
|
read_64_e_o_c_d(buf)
|
|
|
|
else
|
|
|
|
read_e_o_c_d(buf)
|
|
|
|
end
|
2010-11-30 16:27:59 +08:00
|
|
|
read_central_directory_entries(io)
|
|
|
|
end
|
2011-11-18 04:53:04 +08:00
|
|
|
|
2013-08-27 04:26:14 +08:00
|
|
|
def get_e_o_c_d(buf) #:nodoc:
|
|
|
|
sig_index = buf.rindex([END_OF_CDS].pack('V'))
|
|
|
|
raise ZipError, "Zip end of central directory signature not found" unless sig_index
|
|
|
|
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
|
|
|
|
|
|
|
def buf.read(count)
|
|
|
|
slice!(0, count)
|
|
|
|
end
|
|
|
|
|
|
|
|
buf
|
|
|
|
end
|
|
|
|
|
|
|
|
def zip64_file?(buf)
|
|
|
|
buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
|
|
|
end
|
|
|
|
|
|
|
|
def start_buf(io)
|
2010-11-30 16:27:59 +08:00
|
|
|
begin
|
2013-08-27 04:26:14 +08:00
|
|
|
io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
|
2010-11-30 16:27:59 +08:00
|
|
|
rescue Errno::EINVAL
|
2011-11-18 04:53:04 +08:00
|
|
|
io.seek(0, IO::SEEK_SET)
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2013-08-27 04:26:14 +08:00
|
|
|
io.read
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_64_e_o_c_d(buf) #:nodoc:
|
|
|
|
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
|
|
|
raise ZipError, "Zip64 end of central directory signature not found" unless zip_64_start
|
|
|
|
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
|
|
|
raise ZipError, "Zip64 end of central directory signature locator not found" unless zip_64_locator
|
|
|
|
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
2012-02-14 00:55:08 +08:00
|
|
|
|
2010-11-30 16:27:59 +08:00
|
|
|
def buf.read(count)
|
2011-11-18 04:53:04 +08:00
|
|
|
slice!(0, count)
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
2012-02-14 00:55:08 +08:00
|
|
|
|
|
|
|
buf
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# For iterating over the entries.
|
|
|
|
def each(&proc)
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.each(&proc)
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the number of entries in the central directory (and
|
|
|
|
# consequently in the zip archive).
|
|
|
|
def size
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.size
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
|
2013-08-27 04:26:14 +08:00
|
|
|
def self.read_from_stream(io) #:nodoc:
|
2013-06-03 02:33:03 +08:00
|
|
|
cdir = new
|
2010-11-30 16:27:59 +08:00
|
|
|
cdir.read_from_stream(io)
|
|
|
|
return cdir
|
|
|
|
rescue ZipError
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2012-02-14 00:55:08 +08:00
|
|
|
def ==(other) #:nodoc:
|
2013-06-03 15:56:24 +08:00
|
|
|
return false unless other.kind_of?(CentralDirectory)
|
2013-06-03 02:33:03 +08:00
|
|
|
@entry_set.entries.sort == other.entries.sort && comment == other.comment
|
2010-11-30 16:27:59 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-03 02:33:03 +08:00
|
|
|
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
|
|
# rubyzip is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the ruby license.
|