Implemented a simple filename reader
This commit is contained in:
parent
181993afd1
commit
3668fa5e33
6 changed files with 127 additions and 51 deletions
|
@ -4,5 +4,5 @@ name = "zip"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
||||||
|
|
||||||
[dependencies.flatestream]
|
[dependencies.flate2]
|
||||||
git = "https://github.com/mvdnes/flatestream-rs.git"
|
git = "https://github.com/alexcrichton/flate2-rs.git"
|
||||||
|
|
|
@ -4,14 +4,11 @@ fn main()
|
||||||
{
|
{
|
||||||
let args = std::os::args();
|
let args = std::os::args();
|
||||||
let fname = Path::new(args[1].as_slice());
|
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();
|
let mut zipcontainer = zip::reader::ZipContainer::new(file).unwrap();
|
||||||
println!("{}", header);
|
for i in zipcontainer.files()
|
||||||
|
|
||||||
file.seek(header.central_directory_offset as i64, std::io::SeekSet).unwrap();
|
|
||||||
for i in range(0, header.number_of_files_on_this_disk)
|
|
||||||
{
|
{
|
||||||
println!("{}", zip::spec::CentralDirectoryHeader::parse(&mut file).unwrap());
|
println!("{}", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
|
||||||
}
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
#[phase(plugin, link)] extern crate log;
|
#[phase(plugin, link)] extern crate log;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate flatestream;
|
extern crate flate2;
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
pub mod spec;
|
mod spec;
|
||||||
pub mod crc32;
|
pub mod crc32;
|
||||||
|
pub mod reader;
|
||||||
|
|
88
src/reader.rs
Normal file
88
src/reader.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use spec;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{IoResult, IoError};
|
||||||
|
|
||||||
|
pub struct ZipContainer<T>
|
||||||
|
{
|
||||||
|
inner: T,
|
||||||
|
files: Vec<ZipFile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ZipFile
|
||||||
|
{
|
||||||
|
central_header: spec::CentralDirectoryHeader,
|
||||||
|
local_header: spec::LocalFileHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ZipFileNames<'a, T:'a>
|
||||||
|
{
|
||||||
|
container: &'a mut ZipContainer<T>,
|
||||||
|
pos: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unsupported_zip_error<T>(detail: Option<String>) -> IoResult<T>
|
||||||
|
{
|
||||||
|
Err(IoError
|
||||||
|
{
|
||||||
|
kind: io::OtherIoError,
|
||||||
|
desc: "This ZIP file is not supported",
|
||||||
|
detail: detail,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Reader+Seek> ZipContainer<T>
|
||||||
|
{
|
||||||
|
pub fn new(inner: T) -> IoResult<ZipContainer<T>>
|
||||||
|
{
|
||||||
|
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<ZipFile>
|
||||||
|
{
|
||||||
|
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<String> for ZipFileNames<'a, T>
|
||||||
|
{
|
||||||
|
fn next(&mut self) -> Option<String>
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
src/spec.rs
57
src/spec.rs
|
@ -143,32 +143,32 @@ impl DataDescriptor
|
||||||
#[deriving(Show)]
|
#[deriving(Show)]
|
||||||
pub struct CentralDirectoryHeader
|
pub struct CentralDirectoryHeader
|
||||||
{
|
{
|
||||||
made_by: u16,
|
pub made_by: u16,
|
||||||
version_needed: u16,
|
pub version_needed: u16,
|
||||||
|
|
||||||
// general purpose flags
|
// general purpose flags
|
||||||
encrypted: bool, // bit 0
|
pub encrypted: bool, // bit 0
|
||||||
// bit 1 & 2 unused
|
// bit 1 & 2 unused
|
||||||
has_descriptor: bool, // bit 3
|
pub has_descriptor: bool, // bit 3
|
||||||
// bit 4 unused
|
// bit 4 unused
|
||||||
is_compressed_patch: bool, // bit 5
|
pub is_compressed_patch: bool, // bit 5
|
||||||
strong_encryption: bool, // bit 6
|
pub strong_encryption: bool, // bit 6
|
||||||
// bit 7 - 10 unused
|
// bit 7 - 10 unused
|
||||||
is_utf8: bool, // bit 11
|
pub is_utf8: bool, // bit 11
|
||||||
// bit 12 unused
|
// bit 12 unused
|
||||||
is_masked: bool, // bit 13
|
pub is_masked: bool, // bit 13
|
||||||
// bit 14 & 15 unused
|
// bit 14 & 15 unused
|
||||||
|
|
||||||
compression_method: CompressionMethod,
|
pub compression_method: CompressionMethod,
|
||||||
last_modified_time: Tm,
|
pub last_modified_time: Tm,
|
||||||
crc32: u32,
|
pub crc32: u32,
|
||||||
compressed_size: u32,
|
pub compressed_size: u32,
|
||||||
uncompressed_size: u32,
|
pub uncompressed_size: u32,
|
||||||
file_name: Vec<u8>,
|
pub file_name: Vec<u8>,
|
||||||
extra_field: Vec<u8>,
|
pub extra_field: Vec<u8>,
|
||||||
file_comment: Vec<u8>,
|
pub file_comment: Vec<u8>,
|
||||||
disk_number: u16,
|
pub disk_number: u16,
|
||||||
file_offset: u32,
|
pub file_offset: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CentralDirectoryHeader
|
impl CentralDirectoryHeader
|
||||||
|
@ -231,13 +231,13 @@ impl CentralDirectoryHeader
|
||||||
#[deriving(Show)]
|
#[deriving(Show)]
|
||||||
pub struct CentralDirectoryEnd
|
pub struct CentralDirectoryEnd
|
||||||
{
|
{
|
||||||
number_of_disks: u16,
|
pub number_of_disks: u16,
|
||||||
disk_with_central_directory: u16,
|
pub disk_with_central_directory: u16,
|
||||||
pub number_of_files_on_this_disk: u16,
|
pub number_of_files_on_this_disk: u16,
|
||||||
number_of_files: u16,
|
pub number_of_files: u16,
|
||||||
central_directory_size: u32,
|
pub central_directory_size: u32,
|
||||||
pub central_directory_offset: u32,
|
pub central_directory_offset: u32,
|
||||||
zip_file_comment: Vec<u8>,
|
pub zip_file_comment: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CentralDirectoryEnd
|
impl CentralDirectoryEnd
|
||||||
|
@ -275,16 +275,19 @@ impl CentralDirectoryEnd
|
||||||
pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
|
pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
|
||||||
{
|
{
|
||||||
let header_size = 22;
|
let header_size = 22;
|
||||||
|
let bytes_between_magic_and_comment_size = header_size - 6;
|
||||||
try!(reader.seek(0, io::SeekEnd));
|
try!(reader.seek(0, io::SeekEnd));
|
||||||
let filelength = try!(reader.tell()) as i64;
|
let file_length = try!(reader.tell()) as i64;
|
||||||
for pos in range_step_inclusive(filelength - header_size, 0, -1)
|
|
||||||
|
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));
|
try!(reader.seek(pos, io::SeekSet));
|
||||||
if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
|
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;
|
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));
|
try!(reader.seek(pos, io::SeekSet));
|
||||||
return CentralDirectoryEnd::parse(reader);
|
return CentralDirectoryEnd::parse(reader);
|
||||||
|
|
Loading…
Add table
Reference in a new issue