Large refactoring, mostly of the reader
- Combined reader and reader_spec into read - Alter the iteration protocol for a zip archive - Modify some names
This commit is contained in:
parent
e67f019517
commit
d9b83af57c
13 changed files with 356 additions and 347 deletions
|
@ -16,32 +16,34 @@ fn main()
|
|||
let fname = Path::new(&*args[1]);
|
||||
let file = fs::File::open(&fname).unwrap();
|
||||
|
||||
let zipcontainer = zip::ZipReader::new(file).unwrap();
|
||||
let mut archive = zip::ZipArchive::new(file).unwrap();
|
||||
|
||||
for file in zipcontainer.files()
|
||||
for i in 1..archive.len()
|
||||
{
|
||||
let outpath = sanitize_filename(&*file.file_name);
|
||||
let mut file = archive.by_index(i).unwrap();
|
||||
let outpath = sanitize_filename(file.name());
|
||||
println!("{}", outpath.display());
|
||||
|
||||
let comment = &file.file_comment;
|
||||
if comment.len() > 0 { println!(" File comment: {}", comment); }
|
||||
{
|
||||
let comment = file.comment();
|
||||
if comment.len() > 0 { println!(" File comment: {}", comment); }
|
||||
}
|
||||
|
||||
fs::create_dir_all(&outpath.dir_path()).unwrap();
|
||||
|
||||
if (&*file.file_name).ends_with("/") {
|
||||
if (&*file.name()).ends_with("/") {
|
||||
create_directory(outpath);
|
||||
}
|
||||
else {
|
||||
write_file(&zipcontainer, file, outpath);
|
||||
write_file(&mut file, outpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_file(zipcontainer: &zip::ZipReader<fs::File>, file: &zip::ZipFile, outpath: Path)
|
||||
fn write_file(reader: &mut zip::read::ZipFileReader, outpath: Path)
|
||||
{
|
||||
let mut outfile = fs::File::create(&outpath).unwrap();
|
||||
let mut reader = zipcontainer.read_file(file).unwrap();
|
||||
io::copy(&mut reader, &mut outfile).unwrap();
|
||||
io::copy(reader, &mut outfile).unwrap();
|
||||
}
|
||||
|
||||
fn create_directory(outpath: Path)
|
||||
|
|
|
@ -13,17 +13,17 @@ fn main()
|
|||
return;
|
||||
}
|
||||
let fname = Path::new(&*args[1]);
|
||||
let file = std::fs::File::open(&fname).unwrap();
|
||||
let zipfile = std::fs::File::open(&fname).unwrap();
|
||||
|
||||
let zipcontainer = zip::ZipReader::new(file).unwrap();
|
||||
let mut archive = zip::ZipArchive::new(zipfile).unwrap();
|
||||
|
||||
let file = match zipcontainer.get("test/lorem_ipsum.txt")
|
||||
let mut file = match archive.by_name("test/lorem_ipsum.txt")
|
||||
{
|
||||
Some(file) => file,
|
||||
None => { println!("File test/lorem_ipsum.txt not found"); return }
|
||||
Ok(file) => file,
|
||||
Err(..) => { println!("File test/lorem_ipsum.txt not found"); return }
|
||||
};
|
||||
|
||||
let mut contents = String::new();
|
||||
zipcontainer.read_file(file).unwrap().read_to_string(&mut contents).unwrap();
|
||||
file.read_to_string(&mut contents).unwrap();
|
||||
println!("{}", contents);
|
||||
}
|
||||
|
|
|
@ -37,5 +37,5 @@ pub enum CompressionMethod
|
|||
/// PPMd version I, Rev 1
|
||||
PPMdI1 = 98,
|
||||
/// Unknown (invalid) compression
|
||||
Unknown = 100000,
|
||||
Unknown = 10000,
|
||||
}
|
||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -3,25 +3,23 @@
|
|||
#![feature(unsafe_destructor)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![feature(core, old_io, std_misc, io)]
|
||||
#![feature(core, old_io, io)]
|
||||
|
||||
extern crate time;
|
||||
extern crate flate2;
|
||||
extern crate bzip2;
|
||||
|
||||
pub use reader::ZipReader;
|
||||
pub use writer::ZipWriter;
|
||||
pub use read::ZipArchive;
|
||||
pub use write::ZipWriter;
|
||||
pub use compression::CompressionMethod;
|
||||
pub use types::ZipFile;
|
||||
|
||||
mod util;
|
||||
mod spec;
|
||||
mod reader_spec;
|
||||
mod writer_spec;
|
||||
mod crc32;
|
||||
pub mod reader;
|
||||
mod types;
|
||||
pub mod compression;
|
||||
pub mod writer;
|
||||
pub mod read;
|
||||
mod compression;
|
||||
pub mod write;
|
||||
mod cp437;
|
||||
pub mod result;
|
||||
|
|
308
src/read.rs
Normal file
308
src/read.rs
Normal file
|
@ -0,0 +1,308 @@
|
|||
//! Structs for reading a ZIP archive
|
||||
|
||||
use crc32::Crc32Reader;
|
||||
use compression::CompressionMethod;
|
||||
use spec;
|
||||
use result::{ZipResult, ZipError};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::num::FromPrimitive;
|
||||
use flate2;
|
||||
use flate2::FlateReadExt;
|
||||
use bzip2::reader::BzDecompressor;
|
||||
use util;
|
||||
use util::ReadIntExt;
|
||||
use types::ZipFileData;
|
||||
|
||||
/// Wrapper for reading the contents of a ZIP file.
|
||||
///
|
||||
/// ```
|
||||
/// fn doit() -> zip::result::ZipResult<()>
|
||||
/// {
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// // For demonstration purposes we read from an empty buffer.
|
||||
/// // Normally a File object would be used.
|
||||
/// let buf: &[u8] = &[0u8; 128];
|
||||
/// let mut reader = std::io::Cursor::new(buf);
|
||||
///
|
||||
/// let mut zip = try!(zip::ZipArchive::new(reader));
|
||||
///
|
||||
/// for i in 1..zip.len()
|
||||
/// {
|
||||
/// let mut file = zip.by_index(i).unwrap();
|
||||
/// println!("Filename: {}", file.name());
|
||||
/// let first_byte = try!(file.bytes().next().unwrap());
|
||||
/// println!("{}", first_byte);
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// println!("Result: {:?}", doit());
|
||||
/// ```
|
||||
pub struct ZipArchive<R: Read + io::Seek>
|
||||
{
|
||||
reader: R,
|
||||
files: Vec<ZipFileData>,
|
||||
names_map: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
enum ZipFileReader<'a> {
|
||||
Stored(Crc32Reader<io::Take<&'a mut Read>>),
|
||||
Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
|
||||
Bzip2(Crc32Reader<BzDecompressor<io::Take<&'a mut Read>>>),
|
||||
}
|
||||
|
||||
/// A struct for reading a zip file
|
||||
pub struct ZipFile<'a> {
|
||||
data: &'a ZipFileData,
|
||||
reader: ZipFileReader<'a>,
|
||||
}
|
||||
|
||||
fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
|
||||
{
|
||||
Err(ZipError::UnsupportedArchive(detail))
|
||||
}
|
||||
|
||||
impl<R: Read+io::Seek> ZipArchive<R>
|
||||
{
|
||||
/// Opens a Zip archive and parses the central directory
|
||||
pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
|
||||
let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));
|
||||
|
||||
if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }
|
||||
|
||||
let directory_start = footer.central_directory_offset as u64;
|
||||
let number_of_files = footer.number_of_files_on_this_disk as usize;
|
||||
|
||||
let mut files = Vec::with_capacity(number_of_files);
|
||||
let mut names_map = HashMap::new();
|
||||
|
||||
try!(reader.seek(io::SeekFrom::Start(directory_start)));
|
||||
for _ in (0 .. number_of_files)
|
||||
{
|
||||
let file = try!(central_header_to_zip_file(&mut reader));
|
||||
names_map.insert(file.file_name.clone(), files.len());
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
Ok(ZipArchive { reader: reader, files: files, names_map: names_map })
|
||||
}
|
||||
|
||||
/// Number of files contained in this zip.
|
||||
///
|
||||
/// ```
|
||||
/// fn iter() {
|
||||
/// let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
|
||||
///
|
||||
/// for i in 1..zip.len() {
|
||||
/// let mut file = zip.by_index(i).unwrap();
|
||||
/// // Do something with file i
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn len(&self) -> usize
|
||||
{
|
||||
self.files.len()
|
||||
}
|
||||
|
||||
/// Search for a file entry by name
|
||||
pub fn by_name<'a>(&'a mut self, name: &str) -> ZipResult<ZipFile<'a>>
|
||||
{
|
||||
let index = match self.names_map.get(name) {
|
||||
Some(index) => *index,
|
||||
None => { return Err(ZipError::FileNotFound); },
|
||||
};
|
||||
self.by_index(index)
|
||||
}
|
||||
|
||||
/// Get a contained file by index
|
||||
pub fn by_index<'a>(&'a mut self, file_number: usize) -> ZipResult<ZipFile<'a>>
|
||||
{
|
||||
if file_number >= self.files.len() { return Err(ZipError::FileNotFound); }
|
||||
let ref data = self.files[file_number];
|
||||
let pos = data.data_start as u64;
|
||||
|
||||
if data.encrypted
|
||||
{
|
||||
return unsupported_zip_error("Encrypted files are not supported")
|
||||
}
|
||||
|
||||
try!(self.reader.seek(io::SeekFrom::Start(pos)));
|
||||
let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size as u64);
|
||||
|
||||
let reader = match data.compression_method
|
||||
{
|
||||
CompressionMethod::Stored =>
|
||||
{
|
||||
ZipFileReader::Stored(Crc32Reader::new(
|
||||
limit_reader,
|
||||
data.crc32))
|
||||
},
|
||||
CompressionMethod::Deflated =>
|
||||
{
|
||||
let deflate_reader = limit_reader.deflate_decode();
|
||||
ZipFileReader::Deflated(Crc32Reader::new(
|
||||
deflate_reader,
|
||||
data.crc32))
|
||||
},
|
||||
CompressionMethod::Bzip2 =>
|
||||
{
|
||||
let bzip2_reader = BzDecompressor::new(limit_reader);
|
||||
ZipFileReader::Bzip2(Crc32Reader::new(
|
||||
bzip2_reader,
|
||||
data.crc32))
|
||||
},
|
||||
_ => return unsupported_zip_error("Compression method not supported"),
|
||||
};
|
||||
Ok(ZipFile { reader: reader, data: data })
|
||||
}
|
||||
|
||||
/// Unwrap and return the inner reader object
|
||||
///
|
||||
/// The position of the reader is undefined.
|
||||
pub fn into_inner(self) -> R
|
||||
{
|
||||
self.reader
|
||||
}
|
||||
}
|
||||
|
||||
fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFileData>
|
||||
{
|
||||
// Parse central header
|
||||
let signature = try!(reader.read_le_u32());
|
||||
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
|
||||
{
|
||||
return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
|
||||
}
|
||||
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u16());
|
||||
let flags = try!(reader.read_le_u16());
|
||||
let encrypted = flags & 1 == 1;
|
||||
let is_utf8 = flags & (1 << 11) != 0;
|
||||
let compression_method = try!(reader.read_le_u16());
|
||||
let last_mod_time = try!(reader.read_le_u16());
|
||||
let last_mod_date = try!(reader.read_le_u16());
|
||||
let crc32 = try!(reader.read_le_u32());
|
||||
let compressed_size = try!(reader.read_le_u32());
|
||||
let uncompressed_size = try!(reader.read_le_u32());
|
||||
let file_name_length = try!(reader.read_le_u16()) as usize;
|
||||
let extra_field_length = try!(reader.read_le_u16()) as usize;
|
||||
let file_comment_length = try!(reader.read_le_u16()) as usize;
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u32());
|
||||
let offset = try!(reader.read_le_u32()) as u64;
|
||||
let file_name_raw = try!(reader.read_exact(file_name_length));
|
||||
let extra_field = try!(reader.read_exact(extra_field_length));
|
||||
let file_comment_raw = try!(reader.read_exact(file_comment_length));
|
||||
|
||||
let file_name = match is_utf8
|
||||
{
|
||||
true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
|
||||
false => ::cp437::to_string(&*file_name_raw),
|
||||
};
|
||||
let file_comment = match is_utf8
|
||||
{
|
||||
true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
|
||||
false => ::cp437::to_string(&*file_comment_raw),
|
||||
};
|
||||
|
||||
// Remember end of central header
|
||||
let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
|
||||
|
||||
// Parse local header
|
||||
try!(reader.seek(io::SeekFrom::Start(offset)));
|
||||
let signature = try!(reader.read_le_u32());
|
||||
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
|
||||
{
|
||||
return Err(ZipError::InvalidArchive("Invalid local file header"))
|
||||
}
|
||||
|
||||
try!(reader.seek(io::SeekFrom::Current(22)));
|
||||
let file_name_length = try!(reader.read_le_u16()) as u64;
|
||||
let extra_field_length = try!(reader.read_le_u16()) as u64;
|
||||
let magic_and_header = 4 + 22 + 2 + 2;
|
||||
let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;
|
||||
|
||||
// Construct the result
|
||||
let mut result = ZipFileData
|
||||
{
|
||||
encrypted: encrypted,
|
||||
compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(CompressionMethod::Unknown),
|
||||
last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
|
||||
crc32: crc32,
|
||||
compressed_size: compressed_size as u64,
|
||||
uncompressed_size: uncompressed_size as u64,
|
||||
file_name: file_name,
|
||||
file_comment: file_comment,
|
||||
header_start: offset as u64,
|
||||
data_start: data_start,
|
||||
};
|
||||
|
||||
try!(parse_extra_field(&mut result, &*extra_field));
|
||||
|
||||
// Go back after the central header
|
||||
try!(reader.seek(io::SeekFrom::Start(return_position)));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
|
||||
{
|
||||
let mut reader = io::Cursor::new(data);
|
||||
|
||||
while (reader.position() as usize) < data.len()
|
||||
{
|
||||
let kind = try!(reader.read_le_u16());
|
||||
let len = try!(reader.read_le_u16());
|
||||
match kind
|
||||
{
|
||||
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Methods for retreiving information on zip files
|
||||
impl<'a> ZipFile<'a> {
|
||||
fn get_reader(&mut self) -> &mut Read {
|
||||
match self.reader {
|
||||
ZipFileReader::Stored(ref mut r) => r as &mut Read,
|
||||
ZipFileReader::Deflated(ref mut r) => r as &mut Read,
|
||||
ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
|
||||
}
|
||||
}
|
||||
/// Get the name of the file
|
||||
pub fn name(&self) -> &str {
|
||||
&*self.data.file_name
|
||||
}
|
||||
/// Get the comment of the file
|
||||
pub fn comment(&self) -> &str {
|
||||
&*self.data.file_comment
|
||||
}
|
||||
/// Get the compression method used to store the file
|
||||
pub fn compression(&self) -> CompressionMethod {
|
||||
self.data.compression_method
|
||||
}
|
||||
/// Get the size of the file in the archive
|
||||
pub fn compressed_size(&self) -> u64 {
|
||||
self.data.compressed_size
|
||||
}
|
||||
/// Get the size of the file when uncompressed
|
||||
pub fn size(&self) -> u64 {
|
||||
self.data.uncompressed_size
|
||||
}
|
||||
/// Get the time the file was last modified
|
||||
pub fn last_modified(&self) -> ::time::Tm {
|
||||
self.data.last_modified_time
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for ZipFile<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.get_reader().read(buf)
|
||||
}
|
||||
}
|
169
src/reader.rs
169
src/reader.rs
|
@ -1,169 +0,0 @@
|
|||
//! Structs for reading a ZIP archive
|
||||
|
||||
use crc32::Crc32Reader;
|
||||
use types::ZipFile;
|
||||
use compression::CompressionMethod;
|
||||
use spec;
|
||||
use reader_spec;
|
||||
use result::{ZipResult, ZipError};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::cell::{RefCell, BorrowState};
|
||||
use std::collections::HashMap;
|
||||
use flate2::FlateReadExt;
|
||||
use bzip2::reader::BzDecompressor;
|
||||
|
||||
/// Wrapper for reading the contents of a ZIP file.
|
||||
///
|
||||
/// ```
|
||||
/// fn doit() -> zip::result::ZipResult<()>
|
||||
/// {
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// // For demonstration purposes we read from an empty buffer.
|
||||
/// // Normally a File object would be used.
|
||||
/// let buf: &[u8] = &[0u8; 128];
|
||||
/// let mut reader = std::io::Cursor::new(buf);
|
||||
///
|
||||
/// let zip = try!(zip::ZipReader::new(reader));
|
||||
///
|
||||
/// for file in zip.files()
|
||||
/// {
|
||||
/// println!("Filename: {}", file.file_name);
|
||||
/// let mut file_reader = try!(zip.read_file(file));
|
||||
/// let first_byte = try!(file_reader.bytes().next().unwrap());
|
||||
/// println!("{}", first_byte);
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// println!("Result: {:?}", doit());
|
||||
/// ```
|
||||
pub struct ZipReader<T>
|
||||
{
|
||||
inner: RefCell<T>,
|
||||
files: Vec<ZipFile>,
|
||||
names_map: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
/// Iterator over the files contained in a zip archive
|
||||
pub struct ZipFileIterator<'a>
|
||||
{
|
||||
inner: ::std::slice::Iter<'a, ZipFile>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ZipFileIterator<'a> {
|
||||
type Item = &'a ZipFile;
|
||||
fn next(&mut self) -> Option<&'a ZipFile> {
|
||||
self.inner.next()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
|
||||
{
|
||||
Err(ZipError::UnsupportedZipFile(detail))
|
||||
}
|
||||
|
||||
impl<T: Read+io::Seek> ZipReader<T>
|
||||
{
|
||||
/// Opens a ZIP file and parses the content headers.
|
||||
pub fn new(mut reader: T) -> ZipResult<ZipReader<T>>
|
||||
{
|
||||
let footer = try!(spec::CentralDirectoryEnd::find_and_parse(&mut reader));
|
||||
|
||||
if footer.disk_number != footer.disk_with_central_directory { return unsupported_zip_error("Support for multi-disk files is not implemented") }
|
||||
|
||||
let directory_start = footer.central_directory_offset as u64;
|
||||
let number_of_files = footer.number_of_files_on_this_disk as usize;
|
||||
|
||||
let mut files = Vec::with_capacity(number_of_files);
|
||||
let mut names_map = HashMap::new();
|
||||
|
||||
try!(reader.seek(io::SeekFrom::Start(directory_start)));
|
||||
for _ in (0 .. number_of_files)
|
||||
{
|
||||
let file = try!(reader_spec::central_header_to_zip_file(&mut reader));
|
||||
names_map.insert(file.file_name.clone(), files.len());
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
Ok(ZipReader { inner: RefCell::new(reader), files: files, names_map: names_map })
|
||||
}
|
||||
|
||||
/// An iterator over the information of all contained files.
|
||||
pub fn files(&self) -> ZipFileIterator
|
||||
{
|
||||
ZipFileIterator { inner: (&*self.files).iter(), }
|
||||
}
|
||||
|
||||
/// Search for a file entry by name
|
||||
pub fn get(&self, name: &str) -> Option<&ZipFile>
|
||||
{
|
||||
self.names_map.get(name).map(|index| &self.files[*index])
|
||||
}
|
||||
|
||||
/// Gets a reader for a contained zipfile.
|
||||
///
|
||||
/// May return `ReaderUnavailable` if there is another reader borrowed.
|
||||
pub fn read_file<'a>(&'a self, file: &ZipFile) -> ZipResult<Box<Read+'a>>
|
||||
{
|
||||
let mut inner_reader = match self.inner.borrow_state()
|
||||
{
|
||||
BorrowState::Unused => self.inner.borrow_mut(),
|
||||
_ => return Err(ZipError::ReaderUnavailable),
|
||||
};
|
||||
let pos = file.data_start as u64;
|
||||
|
||||
if file.encrypted
|
||||
{
|
||||
return unsupported_zip_error("Encrypted files are not supported")
|
||||
}
|
||||
|
||||
try!(inner_reader.seek(io::SeekFrom::Start(pos)));
|
||||
let refmut_reader = ::util::RefMutReader::new(inner_reader);
|
||||
let limit_reader = refmut_reader.take(file.compressed_size as u64);
|
||||
|
||||
let reader = match file.compression_method
|
||||
{
|
||||
CompressionMethod::Stored =>
|
||||
{
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
limit_reader,
|
||||
file.crc32))
|
||||
as Box<Read>
|
||||
},
|
||||
CompressionMethod::Deflated =>
|
||||
{
|
||||
let deflate_reader = limit_reader.deflate_decode();
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
deflate_reader,
|
||||
file.crc32))
|
||||
as Box<Read>
|
||||
},
|
||||
CompressionMethod::Bzip2 =>
|
||||
{
|
||||
let bzip2_reader = BzDecompressor::new(limit_reader);
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
bzip2_reader,
|
||||
file.crc32))
|
||||
as Box<Read>
|
||||
},
|
||||
_ => return unsupported_zip_error("Compression method not supported"),
|
||||
};
|
||||
Ok(reader)
|
||||
}
|
||||
|
||||
/// Unwrap and return the inner reader object
|
||||
///
|
||||
/// The position of the reader is undefined.
|
||||
pub fn into_inner(self) -> T
|
||||
{
|
||||
self.inner.into_inner()
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::num::FromPrimitive;
|
||||
use result::{ZipResult, ZipError};
|
||||
use types::ZipFile;
|
||||
use compression::CompressionMethod;
|
||||
use spec;
|
||||
use util;
|
||||
use util::ReadIntExt;
|
||||
|
||||
pub fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R) -> ZipResult<ZipFile>
|
||||
{
|
||||
// Parse central header
|
||||
let signature = try!(reader.read_le_u32());
|
||||
if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
|
||||
{
|
||||
return Err(ZipError::InvalidZipFile("Invalid Central Directory header"))
|
||||
}
|
||||
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u16());
|
||||
let flags = try!(reader.read_le_u16());
|
||||
let encrypted = flags & 1 == 1;
|
||||
let is_utf8 = flags & (1 << 11) != 0;
|
||||
let compression_method = try!(reader.read_le_u16());
|
||||
let last_mod_time = try!(reader.read_le_u16());
|
||||
let last_mod_date = try!(reader.read_le_u16());
|
||||
let crc32 = try!(reader.read_le_u32());
|
||||
let compressed_size = try!(reader.read_le_u32());
|
||||
let uncompressed_size = try!(reader.read_le_u32());
|
||||
let file_name_length = try!(reader.read_le_u16()) as usize;
|
||||
let extra_field_length = try!(reader.read_le_u16()) as usize;
|
||||
let file_comment_length = try!(reader.read_le_u16()) as usize;
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u16());
|
||||
try!(reader.read_le_u32());
|
||||
let offset = try!(reader.read_le_u32()) as u64;
|
||||
let file_name_raw = try!(reader.read_exact(file_name_length));
|
||||
let extra_field = try!(reader.read_exact(extra_field_length));
|
||||
let file_comment_raw = try!(reader.read_exact(file_comment_length));
|
||||
|
||||
let file_name = match is_utf8
|
||||
{
|
||||
true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
|
||||
false => ::cp437::to_string(&*file_name_raw),
|
||||
};
|
||||
let file_comment = match is_utf8
|
||||
{
|
||||
true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
|
||||
false => ::cp437::to_string(&*file_comment_raw),
|
||||
};
|
||||
|
||||
// Remember end of central header
|
||||
let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
|
||||
|
||||
// Parse local header
|
||||
try!(reader.seek(io::SeekFrom::Start(offset)));
|
||||
let signature = try!(reader.read_le_u32());
|
||||
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
|
||||
{
|
||||
return Err(ZipError::InvalidZipFile("Invalid local file header"))
|
||||
}
|
||||
|
||||
try!(reader.seek(io::SeekFrom::Current(22)));
|
||||
let file_name_length = try!(reader.read_le_u16()) as u64;
|
||||
let extra_field_length = try!(reader.read_le_u16()) as u64;
|
||||
let magic_and_header = 4 + 22 + 2 + 2;
|
||||
let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;
|
||||
|
||||
// Construct the result
|
||||
let mut result = ZipFile
|
||||
{
|
||||
encrypted: encrypted,
|
||||
compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(CompressionMethod::Unknown),
|
||||
last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
|
||||
crc32: crc32,
|
||||
compressed_size: compressed_size as u64,
|
||||
uncompressed_size: uncompressed_size as u64,
|
||||
file_name: file_name,
|
||||
file_comment: file_comment,
|
||||
header_start: offset as u64,
|
||||
data_start: data_start,
|
||||
};
|
||||
|
||||
try!(parse_extra_field(&mut result, &*extra_field));
|
||||
|
||||
// Go back after the central header
|
||||
try!(reader.seek(io::SeekFrom::Start(return_position)));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_extra_field(_file: &mut ZipFile, data: &[u8]) -> ZipResult<()>
|
||||
{
|
||||
let mut reader = io::Cursor::new(data);
|
||||
|
||||
while (reader.position() as usize) < data.len()
|
||||
{
|
||||
let kind = try!(reader.read_le_u16());
|
||||
let len = try!(reader.read_le_u16());
|
||||
match kind
|
||||
{
|
||||
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -14,14 +14,14 @@ pub enum ZipError
|
|||
/// An Error caused by I/O
|
||||
Io(io::Error),
|
||||
|
||||
/// This file is probably not a zipfile. The argument is enclosed.
|
||||
InvalidZipFile(&'static str),
|
||||
/// This file is probably not a zip archive
|
||||
InvalidArchive(&'static str),
|
||||
|
||||
/// This file is unsupported. The reason is enclosed.
|
||||
UnsupportedZipFile(&'static str),
|
||||
/// This archive is not supported
|
||||
UnsupportedArchive(&'static str),
|
||||
|
||||
/// The ZipReader is not available.
|
||||
ReaderUnavailable,
|
||||
/// The requested file could not be found in the archive
|
||||
FileNotFound,
|
||||
}
|
||||
|
||||
impl ZipError
|
||||
|
@ -36,10 +36,10 @@ impl ZipError
|
|||
ZipError::Io(ref io_err) => {
|
||||
("Io Error: ".to_string() + io_err.description()).into_cow()
|
||||
},
|
||||
ZipError::InvalidZipFile(msg) | ZipError::UnsupportedZipFile(msg) => {
|
||||
ZipError::InvalidArchive(msg) | ZipError::UnsupportedArchive(msg) => {
|
||||
(self.description().to_string() + ": " + msg).into_cow()
|
||||
},
|
||||
ZipError::ReaderUnavailable => {
|
||||
ZipError::FileNotFound => {
|
||||
self.description().into_cow()
|
||||
},
|
||||
}
|
||||
|
@ -69,9 +69,9 @@ impl error::Error for ZipError
|
|||
match *self
|
||||
{
|
||||
ZipError::Io(ref io_err) => io_err.description(),
|
||||
ZipError::InvalidZipFile(..) => "Invalid Zip File",
|
||||
ZipError::UnsupportedZipFile(..) => "Unsupported Zip File",
|
||||
ZipError::ReaderUnavailable => "No reader available",
|
||||
ZipError::InvalidArchive(..) => "Invalid Zip archive",
|
||||
ZipError::UnsupportedArchive(..) => "Unsupported Zip archive",
|
||||
ZipError::FileNotFound => "Specified file not found in archive",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ impl CentralDirectoryEnd
|
|||
let magic = try!(reader.read_le_u32());
|
||||
if magic != CENTRAL_DIRECTORY_END_SIGNATURE
|
||||
{
|
||||
return Err(ZipError::UnsupportedZipFile("Invalid digital signature header"))
|
||||
return Err(ZipError::InvalidArchive("Invalid digital signature header"))
|
||||
}
|
||||
let disk_number = try!(reader.read_le_u16());
|
||||
let disk_with_central_directory = try!(reader.read_le_u16());
|
||||
|
@ -70,7 +70,7 @@ impl CentralDirectoryEnd
|
|||
}
|
||||
}
|
||||
}
|
||||
Err(ZipError::UnsupportedZipFile("Could not find central directory end"))
|
||||
Err(ZipError::InvalidArchive("Could not find central directory end"))
|
||||
}
|
||||
|
||||
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
use time;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Structure representing a ZIP file.
|
||||
pub struct ZipFile
|
||||
pub struct ZipFileData
|
||||
{
|
||||
/// True if the file is encrypted.
|
||||
pub encrypted: bool,
|
||||
|
|
22
src/util.rs
22
src/util.rs
|
@ -1,6 +1,5 @@
|
|||
use time;
|
||||
use time::Tm;
|
||||
use std::cell::RefMut;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
|
@ -42,27 +41,6 @@ pub fn tm_to_msdos_date(time: Tm) -> u16
|
|||
(time.tm_mday | ((time.tm_mon + 1) << 5) | ((time.tm_year - 80) << 9)) as u16
|
||||
}
|
||||
|
||||
pub struct RefMutReader<'a, R:'a>
|
||||
{
|
||||
inner: RefMut<'a, R>,
|
||||
}
|
||||
|
||||
impl<'a, R> RefMutReader<'a, R>
|
||||
{
|
||||
pub fn new(inner: RefMut<'a, R>) -> RefMutReader<'a, R>
|
||||
{
|
||||
RefMutReader { inner: inner, }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: Read> Read for RefMutReader<'a, R>
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional integer write methods for a io::Read
|
||||
pub trait WriteIntExt {
|
||||
/// Write a u32 in little-endian mode
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Structs for creating a new zip archive
|
||||
|
||||
use compression::CompressionMethod;
|
||||
use types::ZipFile;
|
||||
use types::ZipFileData;
|
||||
use spec;
|
||||
use writer_spec;
|
||||
use crc32;
|
||||
|
@ -51,7 +51,7 @@ enum GenericZipWriter<W: Write + io::Seek>
|
|||
pub struct ZipWriter<W: Write + io::Seek>
|
||||
{
|
||||
inner: GenericZipWriter<W>,
|
||||
files: Vec<ZipFile>,
|
||||
files: Vec<ZipFileData>,
|
||||
stats: ZipWriterStats,
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ impl<W: Write+io::Seek> ZipWriter<W>
|
|||
let writer = self.inner.get_plain();
|
||||
let header_start = try!(writer.seek(io::SeekFrom::Current(0)));
|
||||
|
||||
let mut file = ZipFile
|
||||
let mut file = ZipFileData
|
||||
{
|
||||
encrypted: false,
|
||||
compression_method: compression,
|
||||
|
@ -246,7 +246,7 @@ impl<W: Write+io::Seek> GenericZipWriter<W>
|
|||
CompressionMethod::Stored => GenericZipWriter::Storer(bare),
|
||||
CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::Compression::Default)),
|
||||
CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::Compress::Default)),
|
||||
_ => return Err(ZipError::UnsupportedZipFile("Unsupported compression")),
|
||||
_ => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
|
||||
};
|
||||
|
||||
Ok(())
|
|
@ -1,13 +1,13 @@
|
|||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::ascii::AsciiExt;
|
||||
use types::ZipFile;
|
||||
use types::ZipFileData;
|
||||
use result::ZipResult;
|
||||
use spec;
|
||||
use util;
|
||||
use util::WriteIntExt;
|
||||
|
||||
pub fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
pub fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
|
||||
{
|
||||
try!(writer.write_le_u32(spec::LOCAL_FILE_HEADER_SIGNATURE));
|
||||
try!(writer.write_le_u16(20));
|
||||
|
@ -28,7 +28,7 @@ pub fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFile) -> ZipR
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
pub fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
|
||||
{
|
||||
static CRC32_OFFSET : u64 = 14;
|
||||
try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
|
||||
|
@ -38,7 +38,7 @@ pub fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFil
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
pub fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
|
||||
{
|
||||
try!(writer.write_le_u32(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
|
||||
try!(writer.write_le_u16(0x14FF));
|
||||
|
@ -65,7 +65,7 @@ pub fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFile)
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn build_extra_field(_file: &ZipFile) -> ZipResult<Vec<u8>>
|
||||
fn build_extra_field(_file: &ZipFileData) -> ZipResult<Vec<u8>>
|
||||
{
|
||||
let writer = Vec::new();
|
||||
// Future work
|
||||
|
|
Loading…
Add table
Reference in a new issue