Merge branch 'io_reform'

This commit is contained in:
Mathijs van de Nes 2015-03-01 11:57:36 +01:00
commit cd52df6a18
16 changed files with 579 additions and 459 deletions

View file

@ -1,7 +1,7 @@
[package] [package]
name = "zip" name = "zip"
version = "0.0.12" version = "0.1.0"
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"] authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
license = "MIT" license = "MIT"
repository = "https://github.com/mvdnes/zip-rs.git" repository = "https://github.com/mvdnes/zip-rs.git"
@ -10,6 +10,6 @@ Library to support the reading and writing of zip files.
""" """
[dependencies] [dependencies]
flate2 = "^0.1" flate2 = "^0.2"
bzip2 = "^0.1" bzip2 = "^0.2"
time = "*" time = "*"

View file

@ -1,8 +1,9 @@
#![feature(old_path, old_io, env)] #![feature(old_path, io, fs, env)]
extern crate zip; extern crate zip;
use std::old_io; use std::io;
use std::fs;
fn main() fn main()
{ {
@ -13,40 +14,41 @@ fn main()
return; return;
} }
let fname = Path::new(&*args[1]); 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()); 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); create_directory(outpath);
} }
else { 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 outfile = fs::File::create(&outpath).unwrap();
let mut reader = zipcontainer.read_file(file).unwrap(); io::copy(reader, &mut outfile).unwrap();
old_io::util::copy(&mut reader, &mut outfile).unwrap();
old_io::fs::chmod(&outpath, old_io::USER_FILE).unwrap();
} }
fn create_directory(outpath: Path) 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 fn sanitize_filename(filename: &str) -> Path

View file

@ -1,4 +1,6 @@
#![feature(old_path, old_io, env)] #![feature(old_path, io, fs, env)]
use std::io::prelude::*;
extern crate zip; extern crate zip;
@ -11,17 +13,17 @@ fn main()
return; return;
} }
let fname = Path::new(&*args[1]); 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, Ok(file) => file,
None => { println!("File test/lorem_ipsum.txt not found"); return } Err(..) => { println!("File test/lorem_ipsum.txt not found"); return }
}; };
let data = zipcontainer.read_file(file).unwrap().read_to_end().unwrap(); let mut contents = String::new();
let contents = String::from_utf8(data).unwrap(); file.read_to_string(&mut contents).unwrap();
println!("{}", contents); println!("{}", contents);
} }

View file

@ -1,4 +1,6 @@
#![feature(old_io, old_path, env)] #![feature(io, fs, old_path, env)]
use std::io::prelude::*;
extern crate zip; extern crate zip;
@ -22,7 +24,7 @@ fn main()
fn doit(filename: &str) -> zip::result::ZipResult<()> fn doit(filename: &str) -> zip::result::ZipResult<()>
{ {
let path = Path::new(filename); 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); let mut zip = zip::ZipWriter::new(file);

View file

@ -37,5 +37,5 @@ pub enum CompressionMethod
/// PPMd version I, Rev 1 /// PPMd version I, Rev 1
PPMdI1 = 98, PPMdI1 = 98,
/// Unknown (invalid) compression /// Unknown (invalid) compression
Unknown = 100000, Unknown = 10000,
} }

View file

@ -1,6 +1,7 @@
//! Helper module to compute a CRC32 checksum //! Helper module to compute a CRC32 checksum
use std::old_io; use std::io;
use std::io::prelude::*;
static CRC32_TABLE : [u32; 256] = [ static CRC32_TABLE : [u32; 256] = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@ -69,7 +70,7 @@ pub struct Crc32Reader<R>
check: u32, check: u32,
} }
impl<R: Reader> Crc32Reader<R> impl<R> Crc32Reader<R>
{ {
/// Get a new Crc32Reader which check the inner reader against checksum. /// Get a new Crc32Reader which check the inner reader against checksum.
pub fn new(inner: R, checksum: u32) -> Crc32Reader<R> 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) 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, 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), Err(e) => return Err(e),
}; };
self.crc = update(self.crc, &buf[0..count]); self.crc = update(self.crc, &buf[0..count]);

View file

@ -3,25 +3,22 @@
#![feature(unsafe_destructor)] #![feature(unsafe_destructor)]
#![warn(missing_docs)] #![warn(missing_docs)]
#![feature(core, collections, old_io, std_misc)] #![feature(core, old_io, io)]
extern crate time; extern crate time;
extern crate flate2; extern crate flate2;
extern crate bzip2; extern crate bzip2;
pub use reader::ZipReader; pub use read::ZipArchive;
pub use writer::ZipWriter; pub use write::ZipWriter;
pub use compression::CompressionMethod; pub use compression::CompressionMethod;
pub use types::ZipFile;
mod util; mod util;
mod spec; mod spec;
mod reader_spec;
mod writer_spec;
mod crc32; mod crc32;
mod reader;
mod types; mod types;
pub mod compression; pub mod read;
mod writer; mod compression;
pub mod write;
mod cp437; mod cp437;
pub mod result; pub mod result;

308
src/read.rs Normal file
View 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)
}
}

View file

@ -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()
}
}

View file

@ -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(())
}

View file

@ -1,6 +1,6 @@
//! Error types that can be emitted from this library //! Error types that can be emitted from this library
use std::old_io::IoError; use std::io;
use std::error; use std::error;
use std::fmt; use std::fmt;
@ -12,16 +12,16 @@ pub type ZipResult<T> = Result<T, ZipError>;
pub enum ZipError pub enum ZipError
{ {
/// An Error caused by I/O /// An Error caused by I/O
Io(IoError), Io(io::Error),
/// This file is probably not a zipfile. The argument is enclosed. /// This file is probably not a zip archive
InvalidZipFile(&'static str), InvalidArchive(&'static str),
/// This file is unsupported. The reason is enclosed. /// This archive is not supported
UnsupportedZipFile(&'static str), UnsupportedArchive(&'static str),
/// The ZipReader is not available. /// The requested file could not be found in the archive
ReaderUnavailable, FileNotFound,
} }
impl ZipError impl ZipError
@ -36,19 +36,19 @@ impl ZipError
ZipError::Io(ref io_err) => { ZipError::Io(ref io_err) => {
("Io Error: ".to_string() + io_err.description()).into_cow() ("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() (self.description().to_string() + ": " + msg).into_cow()
}, },
ZipError::ReaderUnavailable => { ZipError::FileNotFound => {
self.description().into_cow() 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) ZipError::Io(err)
} }
@ -69,9 +69,9 @@ impl error::Error for ZipError
match *self match *self
{ {
ZipError::Io(ref io_err) => io_err.description(), ZipError::Io(ref io_err) => io_err.description(),
ZipError::InvalidZipFile(..) => "Invalid Zip File", ZipError::InvalidArchive(..) => "Invalid Zip archive",
ZipError::UnsupportedZipFile(..) => "Unsupported Zip File", ZipError::UnsupportedArchive(..) => "Unsupported Zip archive",
ZipError::ReaderUnavailable => "No reader available", ZipError::FileNotFound => "Specified file not found in archive",
} }
} }

View file

@ -1,6 +1,8 @@
use std::old_io; use std::io;
use std::io::prelude::*;
use result::{ZipResult, ZipError}; use result::{ZipResult, ZipError};
use std::iter::range_step_inclusive; use std::iter::range_step_inclusive;
use util::{ReadIntExt, WriteIntExt};
pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50; pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50; pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
@ -19,12 +21,12 @@ pub struct CentralDirectoryEnd
impl 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()); let magic = try!(reader.read_le_u32());
if magic != CENTRAL_DIRECTORY_END_SIGNATURE 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_number = try!(reader.read_le_u16());
let disk_with_central_directory = 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 header_size = 22;
let bytes_between_magic_and_comment_size = header_size - 6; let bytes_between_magic_and_comment_size = header_size - 6;
try!(reader.seek(0, old_io::SeekEnd)); let file_length = try!(reader.seek(io::SeekFrom::End(0))) as i64;
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); 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) 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 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; let comment_length = try!(reader.read_le_u16()) as i64;
if file_length - pos - header_size == comment_length 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); 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_u32(CENTRAL_DIRECTORY_END_SIGNATURE));
try!(writer.write_le_u16(self.disk_number)); try!(writer.write_le_u16(self.disk_number));

View file

@ -2,9 +2,8 @@
use time; use time;
#[derive(Clone)]
/// Structure representing a ZIP file. /// Structure representing a ZIP file.
pub struct ZipFile pub struct ZipFileData
{ {
/// True if the file is encrypted. /// True if the file is encrypted.
pub encrypted: bool, pub encrypted: bool,

View file

@ -1,6 +1,7 @@
use time; use time;
use time::Tm; 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 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 (time.tm_mday | ((time.tm_mon + 1) << 5) | ((time.tm_year - 80) << 9)) as u16
} }
pub struct RefMutReader<'a, R:'a> /// Additional integer write methods for a io::Read
{ pub trait WriteIntExt {
inner: RefMut<'a, R>, /// 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> impl<W: Write> WriteIntExt for W {
{ fn write_le_u32(&mut self, val: u32) -> io::Result<()> {
pub fn new(inner: RefMut<'a, R>) -> RefMutReader<'a, R> let mut buf = [0u8; 4];
{ let v = val;
RefMutReader { inner: inner, } 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 fill_exact<R: Read>(reader: &mut R, buf: &mut [u8]) -> io::Result<()> {
{ let mut idx = 0;
fn read(&mut self, buf: &mut [u8]) -> ::std::old_io::IoResult<usize> while idx < buf.len() {
{ match reader.read(&mut buf[idx..]) {
self.inner.read(buf) 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)
} }
} }

View file

@ -1,20 +1,26 @@
//! Structs for creating a new zip archive
use compression::CompressionMethod; use compression::CompressionMethod;
use types::ZipFile; use types::ZipFileData;
use spec; use spec;
use writer_spec;
use crc32; use crc32;
use result::{ZipResult, ZipError}; use result::{ZipResult, ZipError};
use std::default::Default; use std::default::Default;
use std::old_io; use std::io;
use std::io::prelude::*;
use std::mem; use std::mem;
use std::error::Error;
use std::ascii::AsciiExt;
use time; use time;
use flate2; use flate2;
use flate2::FlateWriter; use flate2::FlateWriteExt;
use flate2::writer::DeflateEncoder; use flate2::write::DeflateEncoder;
use bzip2; use bzip2;
use bzip2::writer::BzCompressor; use bzip2::writer::BzCompressor;
use util;
use util::WriteIntExt;
enum GenericZipWriter<W> enum GenericZipWriter<W: Write + io::Seek>
{ {
Closed, Closed,
Storer(W), Storer(W),
@ -27,13 +33,15 @@ enum GenericZipWriter<W>
/// ``` /// ```
/// fn doit() -> zip::result::ZipResult<()> /// fn doit() -> zip::result::ZipResult<()>
/// { /// {
/// use std::io::Write;
///
/// // For this example we write to a buffer, but normally you should use a File /// // For this example we write to a buffer, but normally you should use a File
/// let mut buf = [0u8; 65536]; /// let mut buf: &mut [u8] = &mut [0u8; 65536];
/// let w = std::old_io::BufWriter::new(&mut buf); /// let mut w = std::io::Cursor::new(buf);
/// let mut zip = zip::ZipWriter::new(w); /// let mut zip = zip::ZipWriter::new(w);
/// ///
/// try!(zip.start_file("hello_world.txt", zip::CompressionMethod::Stored)); /// 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) /// // Optionally finish the zip. (this is also done on drop)
/// try!(zip.finish()); /// try!(zip.finish());
@ -43,10 +51,10 @@ enum GenericZipWriter<W>
/// ///
/// println!("Result: {:?}", doit()); /// println!("Result: {:?}", doit());
/// ``` /// ```
pub struct ZipWriter<W> pub struct ZipWriter<W: Write + io::Seek>
{ {
inner: GenericZipWriter<W>, inner: GenericZipWriter<W>,
files: Vec<ZipFile>, files: Vec<ZipFileData>,
stats: ZipWriterStats, stats: ZipWriterStats,
} }
@ -58,18 +66,29 @@ struct ZipWriterStats
bytes_written: u64, 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); self.stats.update(buf);
match self.inner match self.inner
{ {
GenericZipWriter::Storer(ref mut w) => w.write_all(buf), GenericZipWriter::Storer(ref mut w) => w.write(buf),
GenericZipWriter::Deflater(ref mut w) => w.write_all(buf), GenericZipWriter::Deflater(ref mut w) => w.write(buf),
GenericZipWriter::Bzip2(ref mut w) => w.write_all(buf), GenericZipWriter::Bzip2(ref mut w) => w.write(buf),
GenericZipWriter::Closed => Err(old_io::standard_error(old_io::Closed)), 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. /// Initializes the ZipWriter.
/// ///
@ -105,9 +124,9 @@ impl<W: Writer+Seek> ZipWriter<W>
{ {
let writer = self.inner.get_plain(); 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, encrypted: false,
compression_method: compression, compression_method: compression,
@ -115,14 +134,14 @@ impl<W: Writer+Seek> ZipWriter<W>
crc32: 0, crc32: 0,
compressed_size: 0, compressed_size: 0,
uncompressed_size: 0, uncompressed_size: 0,
file_name: String::from_str(name), file_name: name.to_string(),
file_comment: String::new(), file_comment: String::new(),
header_start: header_start, header_start: header_start,
data_start: 0, 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; self.stats.start = header_end;
file.data_start = header_end; file.data_start = header_end;
@ -149,10 +168,10 @@ impl<W: Writer+Seek> ZipWriter<W>
}; };
file.crc32 = self.stats.crc32; file.crc32 = self.stats.crc32;
file.uncompressed_size = self.stats.bytes_written; 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!(update_local_file_header(writer, file));
try!(writer.seek(0, old_io::SeekEnd)); try!(writer.seek(io::SeekFrom::End(0)));
Ok(()) Ok(())
} }
@ -174,12 +193,12 @@ impl<W: Writer+Seek> ZipWriter<W>
{ {
let writer = self.inner.get_plain(); 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() 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 let footer = spec::CentralDirectoryEnd
{ {
@ -200,20 +219,20 @@ impl<W: Writer+Seek> ZipWriter<W>
} }
#[unsafe_destructor] #[unsafe_destructor]
impl<W: Writer+Seek> Drop for ZipWriter<W> impl<W: Write+io::Seek> Drop for ZipWriter<W>
{ {
fn drop(&mut self) fn drop(&mut self)
{ {
if !self.inner.is_closed() if !self.inner.is_closed()
{ {
if let Err(e) = self.finalize() { 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<()> fn switch_to(&mut self, compression: CompressionMethod) -> ZipResult<()>
{ {
@ -222,15 +241,15 @@ impl<W: Writer+Seek> GenericZipWriter<W>
GenericZipWriter::Storer(w) => w, GenericZipWriter::Storer(w) => w,
GenericZipWriter::Deflater(w) => try!(w.finish()), GenericZipWriter::Deflater(w) => try!(w.finish()),
GenericZipWriter::Bzip2(w) => match w.into_inner() { Ok(r) => r, Err((_, err)) => try!(Err(err)) }, 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 *self = match compression
{ {
CompressionMethod::Stored => GenericZipWriter::Storer(bare), CompressionMethod::Stored => GenericZipWriter::Storer(bare),
CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::CompressionLevel::Default)), CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::Compression::Default)),
CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::CompressionLevel::Default)), CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::Compress::Default)),
_ => return Err(ZipError::UnsupportedZipFile("Unsupported compression")), _ => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
}; };
Ok(()) 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)
}

View file

@ -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())
}