From 3668fa5e33f5a365d664eca2f65eeaf519e2de6a Mon Sep 17 00:00:00 2001 From: Mathijs van de Nes Date: Tue, 9 Sep 2014 13:00:18 +0200 Subject: [PATCH] Implemented a simple filename reader --- Cargo.toml | 4 +- src/bin/parse_foot.rs | 11 ++---- src/bin/parse_head.rs | 13 ------- src/lib.rs | 5 ++- src/reader.rs | 88 +++++++++++++++++++++++++++++++++++++++++++ src/spec.rs | 57 +++++++++++++++------------- 6 files changed, 127 insertions(+), 51 deletions(-) delete mode 100644 src/bin/parse_head.rs create mode 100644 src/reader.rs diff --git a/Cargo.toml b/Cargo.toml index e99a5aa5..f6d12305 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,5 @@ name = "zip" version = "0.0.1" authors = ["Mathijs van de Nes "] -[dependencies.flatestream] -git = "https://github.com/mvdnes/flatestream-rs.git" +[dependencies.flate2] +git = "https://github.com/alexcrichton/flate2-rs.git" diff --git a/src/bin/parse_foot.rs b/src/bin/parse_foot.rs index 5eed2c8f..296b451c 100644 --- a/src/bin/parse_foot.rs +++ b/src/bin/parse_foot.rs @@ -4,14 +4,11 @@ fn main() { let args = std::os::args(); let fname = Path::new(args[1].as_slice()); - let mut file = std::io::File::open(&fname); + let file = std::io::File::open(&fname); - let header = zip::spec::CentralDirectoryEnd::find_and_parse(&mut file).unwrap(); - println!("{}", header); - - file.seek(header.central_directory_offset as i64, std::io::SeekSet).unwrap(); - for i in range(0, header.number_of_files_on_this_disk) + let mut zipcontainer = zip::reader::ZipContainer::new(file).unwrap(); + for i in zipcontainer.files() { - println!("{}", zip::spec::CentralDirectoryHeader::parse(&mut file).unwrap()); + println!("{}", i) } } diff --git a/src/bin/parse_head.rs b/src/bin/parse_head.rs deleted file mode 100644 index 271d8ff0..00000000 --- a/src/bin/parse_head.rs +++ /dev/null @@ -1,13 +0,0 @@ -extern crate zip; - -fn main() -{ - let args = std::os::args(); - let fname = Path::new(args[1].as_slice()); - let mut file = std::io::File::open(&fname); - - let header = zip::spec::LocalFileHeader::parse(&mut file).unwrap(); - println!("{}", header); - println!("{:x}", header.crc32); - println!("{}", String::from_utf8(header.file_name.clone())); -} diff --git a/src/lib.rs b/src/lib.rs index b22a1949..958a656c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,8 +2,9 @@ #[phase(plugin, link)] extern crate log; extern crate time; -extern crate flatestream; +extern crate flate2; mod util; -pub mod spec; +mod spec; pub mod crc32; +pub mod reader; diff --git a/src/reader.rs b/src/reader.rs new file mode 100644 index 00000000..54523d86 --- /dev/null +++ b/src/reader.rs @@ -0,0 +1,88 @@ +use spec; +use std::io; +use std::io::{IoResult, IoError}; + +pub struct ZipContainer +{ + inner: T, + files: Vec, +} + +struct ZipFile +{ + central_header: spec::CentralDirectoryHeader, + local_header: spec::LocalFileHeader, +} + +struct ZipFileNames<'a, T:'a> +{ + container: &'a mut ZipContainer, + pos: uint, +} + +fn unsupported_zip_error(detail: Option) -> IoResult +{ + Err(IoError + { + kind: io::OtherIoError, + desc: "This ZIP file is not supported", + detail: detail, + }) +} + +impl ZipContainer +{ + pub fn new(inner: T) -> IoResult> + { + let mut result = ZipContainer { inner: inner, files: Vec::new() }; + let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut result.inner)); + + if footer.number_of_disks > 1 { return unsupported_zip_error(Some("Support for multi-disk files is not implemented".to_string())) } + + let directory_start = footer.central_directory_offset as i64; + let number_of_files = footer.number_of_files_on_this_disk as uint; + + let mut files = Vec::with_capacity(number_of_files); + + try!(result.inner.seek(directory_start, io::SeekSet)); + for i in range(0, number_of_files) + { + files.push(try!(ZipContainer::parse_directory(&mut result.inner))); + } + + result.files = files; + Ok(result) + } + + fn parse_directory(reader: &mut T) -> IoResult + { + let cdh = try!(spec::CentralDirectoryHeader::parse(reader)); + let pos = try!(reader.tell()) as i64; + try!(reader.seek(cdh.file_offset as i64, io::SeekSet)); + let lfh = try!(spec::LocalFileHeader::parse(reader)); + try!(reader.seek(pos, io::SeekSet)); + Ok(ZipFile { central_header: cdh, local_header: lfh } ) + } + + pub fn files<'a>(&'a mut self) -> ZipFileNames<'a, T> + { + ZipFileNames { container: self, pos: 0 } + } +} + +impl<'a, T> Iterator for ZipFileNames<'a, T> +{ + fn next(&mut self) -> Option + { + self.pos += 1; + if self.pos - 1 >= self.container.files.len() + { + None + } + else + { + let fname = self.container.files.get(self.pos - 1).central_header.file_name.clone(); + String::from_utf8(fname).ok() + } + } +} diff --git a/src/spec.rs b/src/spec.rs index 04fe9302..76eaa6eb 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -143,32 +143,32 @@ impl DataDescriptor #[deriving(Show)] pub struct CentralDirectoryHeader { - made_by: u16, - version_needed: u16, + pub made_by: u16, + pub version_needed: u16, // general purpose flags - encrypted: bool, // bit 0 + pub encrypted: bool, // bit 0 // bit 1 & 2 unused - has_descriptor: bool, // bit 3 + pub has_descriptor: bool, // bit 3 // bit 4 unused - is_compressed_patch: bool, // bit 5 - strong_encryption: bool, // bit 6 + pub is_compressed_patch: bool, // bit 5 + pub strong_encryption: bool, // bit 6 // bit 7 - 10 unused - is_utf8: bool, // bit 11 + pub is_utf8: bool, // bit 11 // bit 12 unused - is_masked: bool, // bit 13 + pub is_masked: bool, // bit 13 // bit 14 & 15 unused - compression_method: CompressionMethod, - last_modified_time: Tm, - crc32: u32, - compressed_size: u32, - uncompressed_size: u32, - file_name: Vec, - extra_field: Vec, - file_comment: Vec, - disk_number: u16, - file_offset: u32, + pub compression_method: CompressionMethod, + pub last_modified_time: Tm, + pub crc32: u32, + pub compressed_size: u32, + pub uncompressed_size: u32, + pub file_name: Vec, + pub extra_field: Vec, + pub file_comment: Vec, + pub disk_number: u16, + pub file_offset: u32, } impl CentralDirectoryHeader @@ -231,13 +231,13 @@ impl CentralDirectoryHeader #[deriving(Show)] pub struct CentralDirectoryEnd { - number_of_disks: u16, - disk_with_central_directory: u16, + pub number_of_disks: u16, + pub disk_with_central_directory: u16, pub number_of_files_on_this_disk: u16, - number_of_files: u16, - central_directory_size: u32, + pub number_of_files: u16, + pub central_directory_size: u32, pub central_directory_offset: u32, - zip_file_comment: Vec, + pub zip_file_comment: Vec, } impl CentralDirectoryEnd @@ -275,16 +275,19 @@ impl CentralDirectoryEnd pub fn find_and_parse(reader: &mut T) -> IoResult { let header_size = 22; + let bytes_between_magic_and_comment_size = header_size - 6; try!(reader.seek(0, io::SeekEnd)); - let filelength = try!(reader.tell()) as i64; - for pos in range_step_inclusive(filelength - header_size, 0, -1) + let file_length = try!(reader.tell()) as i64; + + let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64); + for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1) { try!(reader.seek(pos, io::SeekSet)); if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE { - try!(reader.seek(header_size - 6, io::SeekCur)); + try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur)); let comment_length = try!(reader.read_le_u16()) as i64; - if filelength - pos - header_size == comment_length + if file_length - pos - header_size == comment_length { try!(reader.seek(pos, io::SeekSet)); return CentralDirectoryEnd::parse(reader);