Merge branch 'io_reform'
This commit is contained in:
commit
cd52df6a18
16 changed files with 579 additions and 459 deletions
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "zip"
|
||||
version = "0.0.12"
|
||||
version = "0.1.0"
|
||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/mvdnes/zip-rs.git"
|
||||
|
@ -10,6 +10,6 @@ Library to support the reading and writing of zip files.
|
|||
"""
|
||||
|
||||
[dependencies]
|
||||
flate2 = "^0.1"
|
||||
bzip2 = "^0.1"
|
||||
flate2 = "^0.2"
|
||||
bzip2 = "^0.2"
|
||||
time = "*"
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#![feature(old_path, old_io, env)]
|
||||
#![feature(old_path, io, fs, env)]
|
||||
|
||||
extern crate zip;
|
||||
|
||||
use std::old_io;
|
||||
use std::io;
|
||||
use std::fs;
|
||||
|
||||
fn main()
|
||||
{
|
||||
|
@ -13,40 +14,41 @@ fn main()
|
|||
return;
|
||||
}
|
||||
let fname = Path::new(&*args[1]);
|
||||
let file = old_io::File::open(&fname).unwrap();
|
||||
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); }
|
||||
}
|
||||
|
||||
old_io::fs::mkdir_recursive(&outpath.dir_path(), old_io::USER_DIR).unwrap();
|
||||
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<old_io::File>, file: &zip::ZipFile, outpath: Path)
|
||||
fn write_file(reader: &mut zip::read::ZipFileReader, outpath: Path)
|
||||
{
|
||||
let mut outfile = old_io::File::create(&outpath);
|
||||
let mut reader = zipcontainer.read_file(file).unwrap();
|
||||
old_io::util::copy(&mut reader, &mut outfile).unwrap();
|
||||
old_io::fs::chmod(&outpath, old_io::USER_FILE).unwrap();
|
||||
let mut outfile = fs::File::create(&outpath).unwrap();
|
||||
io::copy(reader, &mut outfile).unwrap();
|
||||
}
|
||||
|
||||
fn create_directory(outpath: Path)
|
||||
{
|
||||
old_io::fs::mkdir_recursive(&outpath, old_io::USER_DIR).unwrap();
|
||||
fs::create_dir_all(&outpath).unwrap();
|
||||
}
|
||||
|
||||
fn sanitize_filename(filename: &str) -> Path
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#![feature(old_path, old_io, env)]
|
||||
#![feature(old_path, io, fs, env)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
|
||||
extern crate zip;
|
||||
|
||||
|
@ -11,17 +13,17 @@ fn main()
|
|||
return;
|
||||
}
|
||||
let fname = Path::new(&*args[1]);
|
||||
let file = std::old_io::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 data = zipcontainer.read_file(file).unwrap().read_to_end().unwrap();
|
||||
let contents = String::from_utf8(data).unwrap();
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).unwrap();
|
||||
println!("{}", contents);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#![feature(old_io, old_path, env)]
|
||||
#![feature(io, fs, old_path, env)]
|
||||
|
||||
use std::io::prelude::*;
|
||||
|
||||
extern crate zip;
|
||||
|
||||
|
@ -22,7 +24,7 @@ fn main()
|
|||
fn doit(filename: &str) -> zip::result::ZipResult<()>
|
||||
{
|
||||
let path = Path::new(filename);
|
||||
let file = std::old_io::File::create(&path).unwrap();
|
||||
let file = std::fs::File::create(&path).unwrap();
|
||||
|
||||
let mut zip = zip::ZipWriter::new(file);
|
||||
|
||||
|
|
|
@ -37,5 +37,5 @@ pub enum CompressionMethod
|
|||
/// PPMd version I, Rev 1
|
||||
PPMdI1 = 98,
|
||||
/// Unknown (invalid) compression
|
||||
Unknown = 100000,
|
||||
Unknown = 10000,
|
||||
}
|
||||
|
|
16
src/crc32.rs
16
src/crc32.rs
|
@ -1,6 +1,7 @@
|
|||
//! Helper module to compute a CRC32 checksum
|
||||
|
||||
use std::old_io;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
static CRC32_TABLE : [u32; 256] = [
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
|
@ -69,7 +70,7 @@ pub struct Crc32Reader<R>
|
|||
check: u32,
|
||||
}
|
||||
|
||||
impl<R: Reader> Crc32Reader<R>
|
||||
impl<R> Crc32Reader<R>
|
||||
{
|
||||
/// Get a new Crc32Reader which check the inner reader against checksum.
|
||||
pub fn new(inner: R, checksum: u32) -> Crc32Reader<R>
|
||||
|
@ -88,19 +89,14 @@ impl<R: Reader> Crc32Reader<R>
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Reader> Reader for Crc32Reader<R>
|
||||
impl<R: Read> Read for Crc32Reader<R>
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> old_io::IoResult<usize>
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>
|
||||
{
|
||||
let count = match self.inner.read(buf)
|
||||
{
|
||||
Ok(0) if !self.check_matches() => { return Err(io::Error::new(io::ErrorKind::Other, "Invalid checksum", None)) },
|
||||
Ok(n) => n,
|
||||
Err(ref e) if e.kind == old_io::EndOfFile =>
|
||||
{
|
||||
return
|
||||
if self.check_matches() { Err(e.clone()) }
|
||||
else { Err(old_io::IoError { kind: old_io::OtherIoError, desc: "Invalid checksum", detail: None, }) }
|
||||
},
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
self.crc = update(self.crc, &buf[0..count]);
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -3,25 +3,22 @@
|
|||
#![feature(unsafe_destructor)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![feature(core, collections, old_io, std_misc)]
|
||||
#![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;
|
||||
mod reader;
|
||||
mod types;
|
||||
pub mod compression;
|
||||
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)
|
||||
}
|
||||
}
|
155
src/reader.rs
155
src/reader.rs
|
@ -1,155 +0,0 @@
|
|||
use crc32::Crc32Reader;
|
||||
use types::ZipFile;
|
||||
use compression::CompressionMethod;
|
||||
use spec;
|
||||
use reader_spec;
|
||||
use result::{ZipResult, ZipError};
|
||||
use std::old_io;
|
||||
use std::cell::{RefCell, BorrowState};
|
||||
use std::collections::HashMap;
|
||||
use flate2::FlateReader;
|
||||
use bzip2::reader::BzDecompressor;
|
||||
|
||||
/// Wrapper for reading the contents of a ZIP file.
|
||||
///
|
||||
/// ```
|
||||
/// fn doit() -> zip::result::ZipResult<()>
|
||||
/// {
|
||||
/// // For demonstration purposes we read from an empty buffer.
|
||||
/// // Normally a File object would be used.
|
||||
/// let buf = [0u8; 128];
|
||||
/// let mut reader = std::old_io::BufReader::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.read_byte());
|
||||
/// println!("{}", first_byte);
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// println!("Result: {:?}", doit());
|
||||
/// ```
|
||||
pub struct ZipReader<T>
|
||||
{
|
||||
inner: RefCell<T>,
|
||||
files: Vec<ZipFile>,
|
||||
names_map: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
|
||||
{
|
||||
Err(ZipError::UnsupportedZipFile(detail))
|
||||
}
|
||||
|
||||
impl<T: Reader+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 i64;
|
||||
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(directory_start, old_io::SeekSet));
|
||||
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) -> ::std::slice::Iter<ZipFile>
|
||||
{
|
||||
(&*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<Reader+'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 i64;
|
||||
|
||||
if file.encrypted
|
||||
{
|
||||
return unsupported_zip_error("Encrypted files are not supported")
|
||||
}
|
||||
|
||||
try!(inner_reader.seek(pos, old_io::SeekSet));
|
||||
let refmut_reader = ::util::RefMutReader::new(inner_reader);
|
||||
let limit_reader = old_io::util::LimitReader::new(refmut_reader, file.compressed_size as usize);
|
||||
|
||||
let reader = match file.compression_method
|
||||
{
|
||||
CompressionMethod::Stored =>
|
||||
{
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
limit_reader,
|
||||
file.crc32))
|
||||
as Box<Reader>
|
||||
},
|
||||
CompressionMethod::Deflated =>
|
||||
{
|
||||
let deflate_reader = limit_reader.deflate_decode();
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
deflate_reader,
|
||||
file.crc32))
|
||||
as Box<Reader>
|
||||
},
|
||||
CompressionMethod::Bzip2 =>
|
||||
{
|
||||
let bzip2_reader = BzDecompressor::new(limit_reader);
|
||||
Box::new(
|
||||
Crc32Reader::new(
|
||||
bzip2_reader,
|
||||
file.crc32))
|
||||
as Box<Reader>
|
||||
},
|
||||
_ => 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()
|
||||
}
|
||||
|
||||
/// Deprecated method equal to `into_inner()`
|
||||
#[deprecated="renamed to into_inner()"]
|
||||
pub fn unwrap(self) -> T
|
||||
{
|
||||
self.into_inner()
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
use std::old_io;
|
||||
use std::num::FromPrimitive;
|
||||
use result::{ZipResult, ZipError};
|
||||
use types::ZipFile;
|
||||
use compression::CompressionMethod;
|
||||
use spec;
|
||||
use util;
|
||||
|
||||
pub fn central_header_to_zip_file<R: Reader+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 i64;
|
||||
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.tell()) as i64;
|
||||
|
||||
// Parse local header
|
||||
try!(reader.seek(offset, old_io::SeekSet));
|
||||
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(22, old_io::SeekCur));
|
||||
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(return_position, old_io::SeekSet));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_extra_field(_file: &mut ZipFile, data: &[u8]) -> ZipResult<()>
|
||||
{
|
||||
let mut reader = old_io::BufReader::new(data);
|
||||
while !reader.eof()
|
||||
{
|
||||
let kind = try!(reader.read_le_u16());
|
||||
let len = try!(reader.read_le_u16());
|
||||
match kind
|
||||
{
|
||||
_ => try!(reader.seek(len as i64, old_io::SeekCur)),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//! Error types that can be emitted from this library
|
||||
|
||||
use std::old_io::IoError;
|
||||
use std::io;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -12,16 +12,16 @@ pub type ZipResult<T> = Result<T, ZipError>;
|
|||
pub enum ZipError
|
||||
{
|
||||
/// An Error caused by I/O
|
||||
Io(IoError),
|
||||
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,19 +36,19 @@ 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()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::FromError<IoError> for ZipError
|
||||
impl error::FromError<io::Error> for ZipError
|
||||
{
|
||||
fn from_error(err: IoError) -> ZipError
|
||||
fn from_error(err: io::Error) -> ZipError
|
||||
{
|
||||
ZipError::Io(err)
|
||||
}
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
23
src/spec.rs
23
src/spec.rs
|
@ -1,6 +1,8 @@
|
|||
use std::old_io;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use result::{ZipResult, ZipError};
|
||||
use std::iter::range_step_inclusive;
|
||||
use util::{ReadIntExt, WriteIntExt};
|
||||
|
||||
pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
|
||||
pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
|
||||
|
@ -19,12 +21,12 @@ pub struct CentralDirectoryEnd
|
|||
|
||||
impl CentralDirectoryEnd
|
||||
{
|
||||
pub fn parse<T: Reader>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
|
||||
pub fn parse<T: Read>(reader: &mut T) -> ZipResult<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());
|
||||
|
@ -47,32 +49,31 @@ impl CentralDirectoryEnd
|
|||
})
|
||||
}
|
||||
|
||||
pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
|
||||
pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
|
||||
{
|
||||
let header_size = 22;
|
||||
let bytes_between_magic_and_comment_size = header_size - 6;
|
||||
try!(reader.seek(0, old_io::SeekEnd));
|
||||
let file_length = try!(reader.tell()) as i64;
|
||||
let file_length = try!(reader.seek(io::SeekFrom::End(0))) 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, old_io::SeekSet));
|
||||
try!(reader.seek(io::SeekFrom::Start(pos as u64)));
|
||||
if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
|
||||
{
|
||||
try!(reader.seek(bytes_between_magic_and_comment_size, old_io::SeekCur));
|
||||
try!(reader.seek(io::SeekFrom::Current(bytes_between_magic_and_comment_size)));
|
||||
let comment_length = try!(reader.read_le_u16()) as i64;
|
||||
if file_length - pos - header_size == comment_length
|
||||
{
|
||||
try!(reader.seek(pos, old_io::SeekSet));
|
||||
try!(reader.seek(io::SeekFrom::Start(pos as u64)));
|
||||
return CentralDirectoryEnd::parse(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(ZipError::UnsupportedZipFile("Could not find central directory end"))
|
||||
Err(ZipError::InvalidArchive("Could not find central directory end"))
|
||||
}
|
||||
|
||||
pub fn write<T: Writer>(&self, writer: &mut T) -> ZipResult<()>
|
||||
pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
|
||||
{
|
||||
try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE));
|
||||
try!(writer.write_le_u16(self.disk_number));
|
||||
|
|
|
@ -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,
|
||||
|
|
87
src/util.rs
87
src/util.rs
|
@ -1,6 +1,7 @@
|
|||
use time;
|
||||
use time::Tm;
|
||||
use std::cell::RefMut;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
|
||||
pub fn msdos_datetime_to_tm(time: u16, date: u16) -> Tm
|
||||
{
|
||||
|
@ -40,23 +41,81 @@ 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>,
|
||||
/// Additional integer write methods for a io::Read
|
||||
pub trait WriteIntExt {
|
||||
/// Write a u32 in little-endian mode
|
||||
fn write_le_u32(&mut self, u32) -> io::Result<()>;
|
||||
/// Write a u16 in little-endian mode
|
||||
fn write_le_u16(&mut self, u16) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<'a, R: Reader> RefMutReader<'a, R>
|
||||
{
|
||||
pub fn new(inner: RefMut<'a, R>) -> RefMutReader<'a, R>
|
||||
{
|
||||
RefMutReader { inner: inner, }
|
||||
impl<W: Write> WriteIntExt for W {
|
||||
fn write_le_u32(&mut self, val: u32) -> io::Result<()> {
|
||||
let mut buf = [0u8; 4];
|
||||
let v = val;
|
||||
buf[0] = ((v >> 0) & 0xFF) as u8;
|
||||
buf[1] = ((v >> 8) & 0xFF) as u8;
|
||||
buf[2] = ((v >> 16) & 0xFF) as u8;
|
||||
buf[3] = ((v >> 24) & 0xFF) as u8;
|
||||
self.write_all(&buf)
|
||||
}
|
||||
|
||||
fn write_le_u16(&mut self, val: u16) -> io::Result<()> {
|
||||
let mut buf = [0u8; 2];
|
||||
let v = val;
|
||||
buf[0] = ((v >> 0) & 0xFF) as u8;
|
||||
buf[1] = ((v >> 8) & 0xFF) as u8;
|
||||
self.write_all(&buf)
|
||||
}
|
||||
}
|
||||
/// Additional integer write methods for a io::Read
|
||||
pub trait ReadIntExt {
|
||||
/// Read a u32 in little-endian mode
|
||||
fn read_le_u32(&mut self) -> io::Result<u32>;
|
||||
/// Read a u16 in little-endian mode
|
||||
fn read_le_u16(&mut self) -> io::Result<u16>;
|
||||
/// Read exactly n bytes
|
||||
fn read_exact(&mut self, usize) -> io::Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
impl<'a, R: Reader> Reader for RefMutReader<'a, R>
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> ::std::old_io::IoResult<usize>
|
||||
{
|
||||
self.inner.read(buf)
|
||||
fn fill_exact<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
|
||||
let mut idx = 0;
|
||||
while idx < buf.len() {
|
||||
match reader.read(&mut buf[idx..]) {
|
||||
Err(v) => return Err(v),
|
||||
Ok(0) => return Err(io::Error::new(io::ErrorKind::ResourceUnavailable, "Could not fill the buffer", None)),
|
||||
Ok(i) => idx += i,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<R: Read> ReadIntExt for R {
|
||||
fn read_le_u32(&mut self) -> io::Result<u32> {
|
||||
let mut buf = [0u8; 4];
|
||||
try!(fill_exact(self, &mut buf));
|
||||
|
||||
Ok(
|
||||
buf[0] as u32
|
||||
| ((buf[1] as u32) << 8)
|
||||
| ((buf[2] as u32) << 16)
|
||||
| ((buf[3] as u32) << 24)
|
||||
)
|
||||
}
|
||||
|
||||
fn read_le_u16(&mut self) -> io::Result<u16> {
|
||||
let mut buf = [0u8; 2];
|
||||
try!(fill_exact(self, &mut buf));
|
||||
|
||||
Ok(
|
||||
buf[0] as u16
|
||||
| ((buf[1] as u16) << 8)
|
||||
)
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, n: usize) -> io::Result<Vec<u8>> {
|
||||
let mut res = vec![0u8; n];
|
||||
try!(fill_exact(self, &mut res));
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
//! Structs for creating a new zip archive
|
||||
|
||||
use compression::CompressionMethod;
|
||||
use types::ZipFile;
|
||||
use types::ZipFileData;
|
||||
use spec;
|
||||
use writer_spec;
|
||||
use crc32;
|
||||
use result::{ZipResult, ZipError};
|
||||
use std::default::Default;
|
||||
use std::old_io;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::mem;
|
||||
use std::error::Error;
|
||||
use std::ascii::AsciiExt;
|
||||
use time;
|
||||
use flate2;
|
||||
use flate2::FlateWriter;
|
||||
use flate2::writer::DeflateEncoder;
|
||||
use flate2::FlateWriteExt;
|
||||
use flate2::write::DeflateEncoder;
|
||||
use bzip2;
|
||||
use bzip2::writer::BzCompressor;
|
||||
use util;
|
||||
use util::WriteIntExt;
|
||||
|
||||
enum GenericZipWriter<W>
|
||||
enum GenericZipWriter<W: Write + io::Seek>
|
||||
{
|
||||
Closed,
|
||||
Storer(W),
|
||||
|
@ -27,13 +33,15 @@ enum GenericZipWriter<W>
|
|||
/// ```
|
||||
/// fn doit() -> zip::result::ZipResult<()>
|
||||
/// {
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// // For this example we write to a buffer, but normally you should use a File
|
||||
/// let mut buf = [0u8; 65536];
|
||||
/// let w = std::old_io::BufWriter::new(&mut buf);
|
||||
/// let mut buf: &mut [u8] = &mut [0u8; 65536];
|
||||
/// let mut w = std::io::Cursor::new(buf);
|
||||
/// let mut zip = zip::ZipWriter::new(w);
|
||||
///
|
||||
/// try!(zip.start_file("hello_world.txt", zip::CompressionMethod::Stored));
|
||||
/// try!(zip.write_all(b"Hello, World!"));
|
||||
/// try!(zip.write(b"Hello, World!"));
|
||||
///
|
||||
/// // Optionally finish the zip. (this is also done on drop)
|
||||
/// try!(zip.finish());
|
||||
|
@ -43,10 +51,10 @@ enum GenericZipWriter<W>
|
|||
///
|
||||
/// println!("Result: {:?}", doit());
|
||||
/// ```
|
||||
pub struct ZipWriter<W>
|
||||
pub struct ZipWriter<W: Write + io::Seek>
|
||||
{
|
||||
inner: GenericZipWriter<W>,
|
||||
files: Vec<ZipFile>,
|
||||
files: Vec<ZipFileData>,
|
||||
stats: ZipWriterStats,
|
||||
}
|
||||
|
||||
|
@ -58,18 +66,29 @@ struct ZipWriterStats
|
|||
bytes_written: u64,
|
||||
}
|
||||
|
||||
impl<W: Writer+Seek> Writer for ZipWriter<W>
|
||||
impl<W: Write+io::Seek> Write for ZipWriter<W>
|
||||
{
|
||||
fn write_all(&mut self, buf: &[u8]) -> old_io::IoResult<()>
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>
|
||||
{
|
||||
if self.files.len() == 0 { return Err(old_io::IoError { kind: old_io::OtherIoError, desc: "No file has been started", detail: None, }) }
|
||||
if self.files.len() == 0 { return Err(io::Error::new(io::ErrorKind::Other, "No file has been started", None)) }
|
||||
self.stats.update(buf);
|
||||
match self.inner
|
||||
{
|
||||
GenericZipWriter::Storer(ref mut w) => w.write_all(buf),
|
||||
GenericZipWriter::Deflater(ref mut w) => w.write_all(buf),
|
||||
GenericZipWriter::Bzip2(ref mut w) => w.write_all(buf),
|
||||
GenericZipWriter::Closed => Err(old_io::standard_error(old_io::Closed)),
|
||||
GenericZipWriter::Storer(ref mut w) => w.write(buf),
|
||||
GenericZipWriter::Deflater(ref mut w) => w.write(buf),
|
||||
GenericZipWriter::Bzip2(ref mut w) => w.write(buf),
|
||||
GenericZipWriter::Closed => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()>
|
||||
{
|
||||
let result = self.finalize();
|
||||
self.inner = GenericZipWriter::Closed;
|
||||
match result {
|
||||
Ok(..) => Ok(()),
|
||||
Err(ZipError::Io(io_err)) => Err(io_err),
|
||||
Err(zip_err) => Err(io::Error::new(io::ErrorKind::Other, "A zip error occured", Some(zip_err.description().to_string()))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +102,7 @@ impl ZipWriterStats
|
|||
}
|
||||
}
|
||||
|
||||
impl<W: Writer+Seek> ZipWriter<W>
|
||||
impl<W: Write+io::Seek> ZipWriter<W>
|
||||
{
|
||||
/// Initializes the ZipWriter.
|
||||
///
|
||||
|
@ -105,9 +124,9 @@ impl<W: Writer+Seek> ZipWriter<W>
|
|||
|
||||
{
|
||||
let writer = self.inner.get_plain();
|
||||
let header_start = try!(writer.tell());
|
||||
let header_start = try!(writer.seek(io::SeekFrom::Current(0)));
|
||||
|
||||
let mut file = ZipFile
|
||||
let mut file = ZipFileData
|
||||
{
|
||||
encrypted: false,
|
||||
compression_method: compression,
|
||||
|
@ -115,14 +134,14 @@ impl<W: Writer+Seek> ZipWriter<W>
|
|||
crc32: 0,
|
||||
compressed_size: 0,
|
||||
uncompressed_size: 0,
|
||||
file_name: String::from_str(name),
|
||||
file_name: name.to_string(),
|
||||
file_comment: String::new(),
|
||||
header_start: header_start,
|
||||
data_start: 0,
|
||||
};
|
||||
try!(writer_spec::write_local_file_header(writer, &file));
|
||||
try!(write_local_file_header(writer, &file));
|
||||
|
||||
let header_end = try!(writer.tell());
|
||||
let header_end = try!(writer.seek(io::SeekFrom::Current(0)));
|
||||
self.stats.start = header_end;
|
||||
file.data_start = header_end;
|
||||
|
||||
|
@ -149,10 +168,10 @@ impl<W: Writer+Seek> ZipWriter<W>
|
|||
};
|
||||
file.crc32 = self.stats.crc32;
|
||||
file.uncompressed_size = self.stats.bytes_written;
|
||||
file.compressed_size = try!(writer.tell()) - self.stats.start;
|
||||
file.compressed_size = try!(writer.seek(io::SeekFrom::Current(0))) - self.stats.start;
|
||||
|
||||
try!(writer_spec::update_local_file_header(writer, file));
|
||||
try!(writer.seek(0, old_io::SeekEnd));
|
||||
try!(update_local_file_header(writer, file));
|
||||
try!(writer.seek(io::SeekFrom::End(0)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -174,12 +193,12 @@ impl<W: Writer+Seek> ZipWriter<W>
|
|||
{
|
||||
let writer = self.inner.get_plain();
|
||||
|
||||
let central_start = try!(writer.tell());
|
||||
let central_start = try!(writer.seek(io::SeekFrom::Current(0)));
|
||||
for file in self.files.iter()
|
||||
{
|
||||
try!(writer_spec::write_central_directory_header(writer, file));
|
||||
try!(write_central_directory_header(writer, file));
|
||||
}
|
||||
let central_size = try!(writer.tell()) - central_start;
|
||||
let central_size = try!(writer.seek(io::SeekFrom::Current(0))) - central_start;
|
||||
|
||||
let footer = spec::CentralDirectoryEnd
|
||||
{
|
||||
|
@ -200,20 +219,20 @@ impl<W: Writer+Seek> ZipWriter<W>
|
|||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<W: Writer+Seek> Drop for ZipWriter<W>
|
||||
impl<W: Write+io::Seek> Drop for ZipWriter<W>
|
||||
{
|
||||
fn drop(&mut self)
|
||||
{
|
||||
if !self.inner.is_closed()
|
||||
{
|
||||
if let Err(e) = self.finalize() {
|
||||
let _ = write!(&mut old_io::stdio::stderr(), "ZipWriter drop failed: {:?}", e);
|
||||
let _ = write!(&mut ::std::old_io::stdio::stderr(), "ZipWriter drop failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Writer+Seek> GenericZipWriter<W>
|
||||
impl<W: Write+io::Seek> GenericZipWriter<W>
|
||||
{
|
||||
fn switch_to(&mut self, compression: CompressionMethod) -> ZipResult<()>
|
||||
{
|
||||
|
@ -222,15 +241,15 @@ impl<W: Writer+Seek> GenericZipWriter<W>
|
|||
GenericZipWriter::Storer(w) => w,
|
||||
GenericZipWriter::Deflater(w) => try!(w.finish()),
|
||||
GenericZipWriter::Bzip2(w) => match w.into_inner() { Ok(r) => r, Err((_, err)) => try!(Err(err)) },
|
||||
GenericZipWriter::Closed => try!(Err(old_io::standard_error(old_io::Closed))),
|
||||
GenericZipWriter::Closed => try!(Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None))),
|
||||
};
|
||||
|
||||
*self = match compression
|
||||
{
|
||||
CompressionMethod::Stored => GenericZipWriter::Storer(bare),
|
||||
CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::CompressionLevel::Default)),
|
||||
CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::CompressionLevel::Default)),
|
||||
_ => return Err(ZipError::UnsupportedZipFile("Unsupported compression")),
|
||||
CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::Compression::Default)),
|
||||
CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::Compress::Default)),
|
||||
_ => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
@ -263,3 +282,68 @@ impl<W: Writer+Seek> GenericZipWriter<W>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
|
||||
try!(writer.write_le_u16(flag));
|
||||
try!(writer.write_le_u16(file.compression_method as u16));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
|
||||
let extra_field = try!(build_extra_field(file));
|
||||
try!(writer.write_le_u16(extra_field.len() as u16));
|
||||
try!(writer.write_all(file.file_name.as_bytes()));
|
||||
try!(writer.write_all(extra_field.as_slice()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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)));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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));
|
||||
try!(writer.write_le_u16(20));
|
||||
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
|
||||
try!(writer.write_le_u16(flag));
|
||||
try!(writer.write_le_u16(file.compression_method as u16));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
|
||||
let extra_field = try!(build_extra_field(file));
|
||||
try!(writer.write_le_u16(extra_field.len() as u16));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u32(0));
|
||||
try!(writer.write_le_u32(file.header_start as u32));
|
||||
try!(writer.write_all(file.file_name.as_bytes()));
|
||||
try!(writer.write_all(extra_field.as_slice()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_extra_field(_file: &ZipFileData) -> ZipResult<Vec<u8>>
|
||||
{
|
||||
let writer = Vec::new();
|
||||
// Future work
|
||||
Ok(writer)
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
use std::old_io;
|
||||
use std::ascii::AsciiExt;
|
||||
use types::ZipFile;
|
||||
use result::ZipResult;
|
||||
use spec;
|
||||
use util;
|
||||
|
||||
pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
{
|
||||
try!(writer.write_le_u32(spec::LOCAL_FILE_HEADER_SIGNATURE));
|
||||
try!(writer.write_le_u16(20));
|
||||
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
|
||||
try!(writer.write_le_u16(flag));
|
||||
try!(writer.write_le_u16(file.compression_method as u16));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
|
||||
let extra_field = try!(build_extra_field(file));
|
||||
try!(writer.write_le_u16(extra_field.len() as u16));
|
||||
try!(writer.write_all(file.file_name.as_bytes()));
|
||||
try!(writer.write_all(extra_field.as_slice()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_local_file_header<T: Writer+Seek>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
{
|
||||
static CRC32_OFFSET : i64 = 14;
|
||||
try!(writer.seek(file.header_start as i64 + CRC32_OFFSET, old_io::SeekSet));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile) -> ZipResult<()>
|
||||
{
|
||||
try!(writer.write_le_u32(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
|
||||
try!(writer.write_le_u16(0x14FF));
|
||||
try!(writer.write_le_u16(20));
|
||||
let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
|
||||
try!(writer.write_le_u16(flag));
|
||||
try!(writer.write_le_u16(file.compression_method as u16));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
|
||||
try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
|
||||
try!(writer.write_le_u32(file.crc32));
|
||||
try!(writer.write_le_u32(file.compressed_size as u32));
|
||||
try!(writer.write_le_u32(file.uncompressed_size as u32));
|
||||
try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
|
||||
let extra_field = try!(build_extra_field(file));
|
||||
try!(writer.write_le_u16(extra_field.len() as u16));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u16(0));
|
||||
try!(writer.write_le_u32(0));
|
||||
try!(writer.write_le_u32(file.header_start as u32));
|
||||
try!(writer.write_all(file.file_name.as_bytes()));
|
||||
try!(writer.write_all(extra_field.as_slice()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_extra_field(_file: &ZipFile) -> ZipResult<Vec<u8>>
|
||||
{
|
||||
let writer = old_io::MemWriter::new();
|
||||
// Future work
|
||||
Ok(writer.into_inner())
|
||||
}
|
Loading…
Add table
Reference in a new issue