summaryrefslogtreecommitdiffstats
path: root/third_party/rust/zip
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/zip
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/zip')
-rw-r--r--third_party/rust/zip/.cargo-checksum.json1
-rw-r--r--third_party/rust/zip/Cargo.toml46
-rw-r--r--third_party/rust/zip/LICENSE21
-rw-r--r--third_party/rust/zip/README.md65
-rw-r--r--third_party/rust/zip/appveyor.yml45
-rw-r--r--third_party/rust/zip/examples/extract.rs57
-rw-r--r--third_party/rust/zip/examples/extract_lorem.rs33
-rw-r--r--third_party/rust/zip/examples/stdin_info.rs31
-rw-r--r--third_party/rust/zip/examples/write_dir.rs96
-rw-r--r--third_party/rust/zip/examples/write_sample.rs75
-rw-r--r--third_party/rust/zip/script/doc-upload.cfg3
-rw-r--r--third_party/rust/zip/src/compression.rs114
-rw-r--r--third_party/rust/zip/src/cp437.rs212
-rw-r--r--third_party/rust/zip/src/crc32.rs128
-rw-r--r--third_party/rust/zip/src/lib.rs24
-rw-r--r--third_party/rust/zip/src/read.rs691
-rw-r--r--third_party/rust/zip/src/result.rs94
-rw-r--r--third_party/rust/zip/src/spec.rs190
-rw-r--r--third_party/rust/zip/src/types.rs136
-rw-r--r--third_party/rust/zip/src/write.rs558
-rw-r--r--third_party/rust/zip/tests/data/invalid_offset.zipbin0 -> 1241 bytes
-rw-r--r--third_party/rust/zip/tests/data/mimetype.zipbin0 -> 159 bytes
-rw-r--r--third_party/rust/zip/tests/data/zip64_demo.zipbin0 -> 224 bytes
-rw-r--r--third_party/rust/zip/tests/invalid_date.rs31
24 files changed, 2651 insertions, 0 deletions
diff --git a/third_party/rust/zip/.cargo-checksum.json b/third_party/rust/zip/.cargo-checksum.json
new file mode 100644
index 0000000000..4ba5203f3e
--- /dev/null
+++ b/third_party/rust/zip/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"00f64858860f669e881bf7e797c7f75bcdeb11e299bc9c7344fe3082ccb8b628","LICENSE":"6ac8711fb340c62ce0a4ecd463342d3fa0e8e70de697c863a2e1c0c53006003c","README.md":"face441ad06a7ef87477d5e96f3c456bef8f16662b03b5084d6fed7705fc4e99","appveyor.yml":"5c2aabf9d1fd01ca3dc33a14241bc0ce4a7327d2cbc3c3af350334bf5d107315","examples/extract.rs":"53b555617192330bd2f95235b82749905ad52617a5a3ba2648dbafbfff8a49a0","examples/extract_lorem.rs":"4e4e70927d2928a411ce4abe37ef4924c7156c4fd2697527c01fbd1797a76969","examples/stdin_info.rs":"54a9ce97b75a83c81de56c46cfe477dbd34dd98f739eb767f329b0e6dcb7490f","examples/write_dir.rs":"1a39da7b5d31997bf353a56d009ecdcf31da765f9ab191b20792e1904a594183","examples/write_sample.rs":"72a7c2db8074d20651ba04d0a49a7d6fb213ca674c63ab48db506deb69cba64c","script/doc-upload.cfg":"10684c8f5ef7e7388cfc4742ab9a060fe8f7fc1c51a63ede3c2443260ecc2d5e","src/compression.rs":"64ea510cfac9c9e5fed605a56fa44a7407df5cc51d4f63180bb2d4f2b307fdb2","src/cp437.rs":"7954c5b7df6f72fe3fe2c377c5de09973c2fa704c7ae0274a5490594e4b69517","src/crc32.rs":"3b503ee23047a4950e79f6956d0615f58c3cd8ded7f1fcd3f64e46dca37871da","src/lib.rs":"0afaf385ce7c7102016d2ba4c66c9cc7384ea710892477ed66bd1f560d83a732","src/read.rs":"d40a0b60c3b2dff73d6161e6d798a0e4aa0546a5f825bcddf6cf1bf52bf5366c","src/result.rs":"1006e46206a5b1491ed1d4fde0955a391df78cd42aa9582784b19170332a3783","src/spec.rs":"39b5e115745d962978e55ffb63ae2d62d0176c4c5ab457eea907a7aa33bf4e18","src/types.rs":"e2d7d3757013ca3ab149ee58fb5813fbeb7834e168870b28474816b47b869e61","src/write.rs":"59e3aff9339c9a16597101a5761391e93cc42eb99fb8c9057a6ee1e4e134da86","tests/data/invalid_offset.zip":"c5534a1803145f6344b04c437d436bd583852c78dd3937f4a73a2a39aa2d76b2","tests/data/mimetype.zip":"4bc6fd161d3e844b3e2b7f10fd0f9077ff565bb39fd2bfa34e7f8e9cd62db06d","tests/data/zip64_demo.zip":"223072a480f60d6a700e427b294e575ae2d6265f3e9881b647927f31d96c7e01","tests/invalid_date.rs":"e78b63b469d1f768bb452fefb84b013d3f9a4bf71dba2f9e12ff9e9c6ff8fd25"},"package":"36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"} \ No newline at end of file
diff --git a/third_party/rust/zip/Cargo.toml b/third_party/rust/zip/Cargo.toml
new file mode 100644
index 0000000000..c8ea4e0927
--- /dev/null
+++ b/third_party/rust/zip/Cargo.toml
@@ -0,0 +1,46 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "zip"
+version = "0.4.2"
+authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
+description = "Library to support the reading and writing of zip files.\n"
+documentation = "http://mvdnes.github.io/rust-docs/zip-rs/zip/index.html"
+keywords = ["zip", "archive"]
+license = "MIT"
+repository = "https://github.com/mvdnes/zip-rs.git"
+[dependencies.bzip2]
+version = "0.3"
+optional = true
+
+[dependencies.flate2]
+version = "1.0"
+optional = true
+default-features = false
+
+[dependencies.msdos_time]
+version = "0.1"
+
+[dependencies.podio]
+version = "0.1"
+
+[dependencies.time]
+version = "0.1"
+[dev-dependencies.walkdir]
+version = "1.0"
+
+[features]
+default = ["bzip2", "deflate"]
+deflate = ["flate2", "flate2/rust_backend"]
+deflate-miniz = ["flate2", "flate2/miniz-sys"]
+deflate-zlib = ["flate2", "flate2/zlib"]
diff --git a/third_party/rust/zip/LICENSE b/third_party/rust/zip/LICENSE
new file mode 100644
index 0000000000..b2d7f7bbdc
--- /dev/null
+++ b/third_party/rust/zip/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Mathijs van de Nes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/third_party/rust/zip/README.md b/third_party/rust/zip/README.md
new file mode 100644
index 0000000000..330d9affa7
--- /dev/null
+++ b/third_party/rust/zip/README.md
@@ -0,0 +1,65 @@
+zip-rs
+======
+
+[![Build Status](https://travis-ci.org/mvdnes/zip-rs.svg?branch=master)](https://travis-ci.org/mvdnes/zip-rs)
+[![Build status](https://ci.appveyor.com/api/projects/status/gsnpqcodg19iu253/branch/master?svg=true)](https://ci.appveyor.com/project/mvdnes/zip-rs/branch/master)
+[![Crates.io version](https://img.shields.io/crates/v/zip.svg)](https://crates.io/crates/zip)
+
+[Documentation](http://mvdnes.github.io/rust-docs/zip-rs/zip/index.html)
+
+Info
+----
+
+A zip library for rust which supports reading and writing of simple ZIP files.
+
+Supported compression formats:
+
+* stored (i.e. none)
+* deflate
+* bzip2 (optional, enabled by default)
+
+Currently unsupported zip extensions:
+
+* Encryption
+* Multi-disk
+
+We aim to support rust versions 1.20+.
+
+Usage
+-----
+
+With all default features:
+
+```toml
+[dependencies]
+zip = "0.4"
+```
+
+Without the default features:
+
+```toml
+[dependencies]
+zip = { version = "0.4", default-features = false }
+```
+
+You can further control the backend of `deflate` compression method with these features:
+* `deflate` (enabled by default) uses [miniz_oxide](https://github.com/Frommi/miniz_oxide)
+* `deflate-miniz` uses [miniz](https://github.com/richgel999/miniz)
+* `deflate-zlib` uses zlib
+
+For example:
+
+```toml
+[dependencies]
+zip = { version = "0.4", features = ["deflate-zlib"], default-features = false }
+```
+
+Examples
+--------
+
+See the [examples directory](examples) for:
+ * How to write a file to a zip.
+ * how to write a directory of files to a zip (using [walkdir](https://github.com/BurntSushi/walkdir)).
+ * How to extract a zip file.
+ * How to extract a single file from a zip.
+ * How to read a zip from the standard input.
diff --git a/third_party/rust/zip/appveyor.yml b/third_party/rust/zip/appveyor.yml
new file mode 100644
index 0000000000..b9450e3c9e
--- /dev/null
+++ b/third_party/rust/zip/appveyor.yml
@@ -0,0 +1,45 @@
+# Appveyor configuration template for Rust using rustup for Rust installation
+# https://github.com/starkat99/appveyor-rust
+
+os: Visual Studio 2015
+
+environment:
+ matrix:
+
+ # Stable 64-bit MSVC
+ - channel: stable
+ target: x86_64-pc-windows-msvc
+ # Stable 32-bit MSVC
+ - channel: stable
+ target: i686-pc-windows-msvc
+ # Stable 64-bit GNU
+ - channel: stable
+ target: x86_64-pc-windows-gnu
+ # Stable 32-bit GNU
+ - channel: stable
+ target: i686-pc-windows-gnu
+
+install:
+ - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
+ - rustup-init -yv --default-toolchain %channel% --default-host %target%
+ - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ - ps: |
+ if ($env:target -like "*-gnu" -And $env:target -like "x86_64-*") {
+ Start-FileDownload "http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/5.1.0/threads-win32/seh/x86_64-5.1.0-release-win32-seh-rt_v4-rev0.7z/download" -FileName mingw64.7z
+ 7z x -oC:\ mingw64.7z > $null
+ $env:path = "C:\mingw64\bin;" + $env:path
+ gcc --version
+ }
+ elseif ($env:target -like "*-gnu") {
+ $env:path = "C:\mingw\bin;" + $env:path
+ gcc --version
+ }
+ - rustc -vV
+ - cargo -vV
+
+build: false
+
+test_script:
+ - cargo test
+ - cargo test --no-default-features
+ - cargo test --no-default-features --features "deflate-miniz"
diff --git a/third_party/rust/zip/examples/extract.rs b/third_party/rust/zip/examples/extract.rs
new file mode 100644
index 0000000000..3d3a0939d5
--- /dev/null
+++ b/third_party/rust/zip/examples/extract.rs
@@ -0,0 +1,57 @@
+extern crate zip;
+
+use std::io;
+use std::fs;
+
+fn main() {
+ std::process::exit(real_main());
+}
+
+fn real_main() -> i32 {
+ let args: Vec<_> = std::env::args().collect();
+ if args.len() < 2 {
+ println!("Usage: {} <filename>", args[0]);
+ return 1;
+ }
+ let fname = std::path::Path::new(&*args[1]);
+ let file = fs::File::open(&fname).unwrap();
+
+ let mut archive = zip::ZipArchive::new(file).unwrap();
+
+ for i in 0..archive.len() {
+ let mut file = archive.by_index(i).unwrap();
+ let outpath = file.sanitized_name();
+
+ {
+ let comment = file.comment();
+ if !comment.is_empty() {
+ println!("File {} comment: {}", i, comment);
+ }
+ }
+
+ if (&*file.name()).ends_with('/') {
+ println!("File {} extracted to \"{}\"", i, outpath.as_path().display());
+ fs::create_dir_all(&outpath).unwrap();
+ } else {
+ println!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
+ if let Some(p) = outpath.parent() {
+ if !p.exists() {
+ fs::create_dir_all(&p).unwrap();
+ }
+ }
+ let mut outfile = fs::File::create(&outpath).unwrap();
+ io::copy(&mut file, &mut outfile).unwrap();
+ }
+
+ // Get and Set permissions
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+
+ if let Some(mode) = file.unix_mode() {
+ fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
+ }
+ }
+ }
+ return 0;
+}
diff --git a/third_party/rust/zip/examples/extract_lorem.rs b/third_party/rust/zip/examples/extract_lorem.rs
new file mode 100644
index 0000000000..eb49bbe307
--- /dev/null
+++ b/third_party/rust/zip/examples/extract_lorem.rs
@@ -0,0 +1,33 @@
+use std::io::prelude::*;
+
+extern crate zip;
+
+fn main()
+{
+ std::process::exit(real_main());
+}
+
+fn real_main() -> i32
+{
+ let args: Vec<_> = std::env::args().collect();
+ if args.len() < 2 {
+ println!("Usage: {} <filename>", args[0]);
+ return 1;
+ }
+ let fname = std::path::Path::new(&*args[1]);
+ let zipfile = std::fs::File::open(&fname).unwrap();
+
+ let mut archive = zip::ZipArchive::new(zipfile).unwrap();
+
+ let mut file = match archive.by_name("test/lorem_ipsum.txt")
+ {
+ Ok(file) => file,
+ Err(..) => { println!("File test/lorem_ipsum.txt not found"); return 2;}
+ };
+
+ let mut contents = String::new();
+ file.read_to_string(&mut contents).unwrap();
+ println!("{}", contents);
+
+ return 0;
+}
diff --git a/third_party/rust/zip/examples/stdin_info.rs b/third_party/rust/zip/examples/stdin_info.rs
new file mode 100644
index 0000000000..910ba8e85e
--- /dev/null
+++ b/third_party/rust/zip/examples/stdin_info.rs
@@ -0,0 +1,31 @@
+extern crate zip;
+
+use std::io::{self, Read};
+
+fn main() {
+ std::process::exit(real_main());
+}
+
+fn real_main() -> i32 {
+ let stdin = io::stdin();
+ let mut stdin_handle = stdin.lock();
+ let mut buf = [0u8; 16];
+
+ loop {
+ match zip::read::read_zipfile_from_stream(&mut stdin_handle) {
+ Ok(Some(mut file)) => {
+ println!("{}: {} bytes ({} bytes packed)", file.name(), file.size(), file.compressed_size());
+ match file.read(&mut buf) {
+ Ok(n) => println!("The first {} bytes are: {:?}", n, &buf[0..n]),
+ Err(e) => println!("Could not read the file: {:?}", e),
+ };
+ },
+ Ok(None) => break,
+ Err(e) => {
+ println!("Error encountered while reading zip: {:?}", e);
+ return 1;
+ },
+ }
+ }
+ return 0;
+}
diff --git a/third_party/rust/zip/examples/write_dir.rs b/third_party/rust/zip/examples/write_dir.rs
new file mode 100644
index 0000000000..0e6f59bd24
--- /dev/null
+++ b/third_party/rust/zip/examples/write_dir.rs
@@ -0,0 +1,96 @@
+extern crate zip;
+extern crate walkdir;
+
+use std::io::prelude::*;
+use std::io::{Write, Seek};
+use std::iter::Iterator;
+use zip::write::FileOptions;
+use zip::result::ZipError;
+
+use walkdir::{WalkDir, DirEntry};
+use std::path::Path;
+use std::fs::File;
+
+fn main() {
+ std::process::exit(real_main());
+}
+
+const METHOD_STORED : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
+
+#[cfg(feature = "flate2")]
+const METHOD_DEFLATED : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
+#[cfg(not(feature = "flate2"))]
+const METHOD_DEFLATED : Option<zip::CompressionMethod> = None;
+
+#[cfg(feature = "bzip2")]
+const METHOD_BZIP2 : Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
+#[cfg(not(feature = "bzip2"))]
+const METHOD_BZIP2 : Option<zip::CompressionMethod> = None;
+
+fn real_main() -> i32 {
+ let args: Vec<_> = std::env::args().collect();
+ if args.len() < 3 {
+ println!("Usage: {} <source_directory> <destination_zipfile>",
+ args[0]);
+ return 1;
+ }
+
+ let src_dir = &*args[1];
+ let dst_file = &*args[2];
+ for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
+ if method.is_none() { continue }
+ match doit(src_dir, dst_file, method.unwrap()) {
+ Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
+ Err(e) => println!("Error: {:?}", e),
+ }
+ }
+
+ return 0;
+}
+
+fn zip_dir<T>(it: &mut Iterator<Item=DirEntry>, prefix: &str, writer: T, method: zip::CompressionMethod)
+ -> zip::result::ZipResult<()>
+ where T: Write+Seek
+{
+ let mut zip = zip::ZipWriter::new(writer);
+ let options = FileOptions::default()
+ .compression_method(method)
+ .unix_permissions(0o755);
+
+ let mut buffer = Vec::new();
+ for entry in it {
+ let path = entry.path();
+ let name = path.strip_prefix(Path::new(prefix))
+ .unwrap()
+ .to_str()
+ .unwrap();
+
+ if path.is_file() {
+ println!("adding {:?} as {:?} ...", path, name);
+ zip.start_file(name, options)?;
+ let mut f = File::open(path)?;
+
+ f.read_to_end(&mut buffer)?;
+ zip.write_all(&*buffer)?;
+ buffer.clear();
+ }
+ }
+ zip.finish()?;
+ Result::Ok(())
+}
+
+fn doit(src_dir: &str, dst_file: &str, method: zip::CompressionMethod) -> zip::result::ZipResult<()> {
+ if !Path::new(src_dir).is_dir() {
+ return Err(ZipError::FileNotFound);
+ }
+
+ let path = Path::new(dst_file);
+ let file = File::create(&path).unwrap();
+
+ let walkdir = WalkDir::new(src_dir.to_string());
+ let it = walkdir.into_iter();
+
+ zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
+
+ Ok(())
+} \ No newline at end of file
diff --git a/third_party/rust/zip/examples/write_sample.rs b/third_party/rust/zip/examples/write_sample.rs
new file mode 100644
index 0000000000..427bae64a0
--- /dev/null
+++ b/third_party/rust/zip/examples/write_sample.rs
@@ -0,0 +1,75 @@
+use std::io::prelude::*;
+use zip::write::FileOptions;
+
+extern crate zip;
+
+fn main()
+{
+ std::process::exit(real_main());
+}
+
+fn real_main() -> i32
+{
+ let args: Vec<_> = std::env::args().collect();
+ if args.len() < 2 {
+ println!("Usage: {} <filename>", args[0]);
+ return 1;
+ }
+
+ let filename = &*args[1];
+ match doit(filename)
+ {
+ Ok(_) => println!("File written to {}", filename),
+ Err(e) => println!("Error: {:?}", e),
+ }
+
+ return 0;
+}
+
+fn doit(filename: &str) -> zip::result::ZipResult<()>
+{
+ let path = std::path::Path::new(filename);
+ let file = std::fs::File::create(&path).unwrap();
+
+ let mut zip = zip::ZipWriter::new(file);
+
+ try!(zip.add_directory("test/", FileOptions::default()));
+
+ let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored).unix_permissions(0o755);
+ try!(zip.start_file("test/☃.txt", options));
+ try!(zip.write_all(b"Hello, World!\n"));
+
+ try!(zip.start_file("test/lorem_ipsum.txt", FileOptions::default()));
+ try!(zip.write_all(LOREM_IPSUM));
+
+ try!(zip.finish());
+ Ok(())
+}
+
+const LOREM_IPSUM : &'static [u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tellus elit, tristique vitae mattis egestas, ultricies vitae risus. Quisque sit amet quam ut urna aliquet
+molestie. Proin blandit ornare dui, a tempor nisl accumsan in. Praesent a consequat felis. Morbi metus diam, auctor in auctor vel, feugiat id odio. Curabitur ex ex,
+dictum quis auctor quis, suscipit id lorem. Aliquam vestibulum dolor nec enim vehicula, porta tristique augue tincidunt. Vivamus ut gravida est. Sed pellentesque, dolor
+vitae tristique consectetur, neque lectus pulvinar dui, sed feugiat purus diam id lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
+inceptos himenaeos. Maecenas feugiat velit in ex ultrices scelerisque id id neque.
+
+Phasellus sed nisi in augue sodales pulvinar ut et leo. Pellentesque eget leo vitae massa bibendum sollicitudin. Curabitur erat lectus, congue quis auctor sed, aliquet
+bibendum est. Ut porta ultricies turpis at maximus. Cras non lobortis justo. Duis rutrum magna sed velit facilisis, et sagittis metus laoreet. Pellentesque quam ligula,
+dapibus vitae mauris quis, dapibus cursus leo. Sed sit amet condimentum eros. Nulla vestibulum enim sit amet lorem pharetra, eu fringilla nisl posuere. Sed tristique non
+nibh at viverra. Vivamus sed accumsan lacus, nec pretium eros. Mauris elementum arcu eu risus fermentum, tempor ullamcorper neque aliquam. Sed tempor in erat eu
+suscipit. In euismod in libero in facilisis. Donec sagittis, odio et fermentum dignissim, risus justo pretium nibh, eget vestibulum lectus metus vel lacus.
+
+Quisque feugiat, magna ac feugiat ullamcorper, augue justo consequat felis, ut fermentum arcu lorem vitae ligula. Quisque iaculis tempor maximus. In quis eros ac tellus
+aliquam placerat quis id tellus. Donec non gravida nulla. Morbi faucibus neque sed faucibus aliquam. Sed accumsan mattis nunc, non interdum justo. Cras vitae facilisis
+leo. Fusce sollicitudin ultrices sagittis. Maecenas eget massa id lorem dignissim ultrices non et ligula. Pellentesque aliquam mi ac neque tempus ornare. Morbi non enim
+vulputate quam ullamcorper finibus id non neque. Quisque malesuada commodo lorem, ut ornare velit iaculis rhoncus. Mauris vel maximus ex.
+
+Morbi eleifend blandit diam, non vulputate ante iaculis in. Donec pellentesque augue id enim suscipit, eget suscipit lacus commodo. Ut vel ex vitae elit imperdiet
+vulputate. Nunc eu mattis orci, ut pretium sem. Nam vitae purus mollis ante tempus malesuada a at magna. Integer mattis lectus non luctus lobortis. In a cursus quam,
+eget faucibus sem.
+
+Donec vitae condimentum nisi, non efficitur massa. Praesent sed mi in massa sollicitudin iaculis. Pellentesque a libero ultrices, sodales lacus eu, ornare dui. In
+laoreet est nec dolor aliquam consectetur. Integer iaculis felis venenatis libero pulvinar, ut pretium odio interdum. Donec in nisi eu dolor varius vestibulum eget vel
+nunc. Morbi a venenatis quam, in vehicula justo. Nam risus dui, auctor eu accumsan at, sagittis ac lectus. Mauris iaculis dignissim interdum. Cras cursus dapibus auctor.
+Donec sagittis massa vitae tortor viverra vehicula. Mauris fringilla nunc eu lorem ultrices placerat. Maecenas posuere porta quam at semper. Praesent eu bibendum eros.
+Nunc congue sollicitudin ante, sollicitudin lacinia magna cursus vitae.
+";
diff --git a/third_party/rust/zip/script/doc-upload.cfg b/third_party/rust/zip/script/doc-upload.cfg
new file mode 100644
index 0000000000..cd512bae54
--- /dev/null
+++ b/third_party/rust/zip/script/doc-upload.cfg
@@ -0,0 +1,3 @@
+PROJECT_NAME=zip-rs
+DOCS_REPO=mvdnes/rust-docs.git
+DOC_RUST_VERSION=stable
diff --git a/third_party/rust/zip/src/compression.rs b/third_party/rust/zip/src/compression.rs
new file mode 100644
index 0000000000..79c41a8f46
--- /dev/null
+++ b/third_party/rust/zip/src/compression.rs
@@ -0,0 +1,114 @@
+//! Possible ZIP compression methods.
+
+use std::fmt;
+
+/// Compression methods for the contents of a ZIP file.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum CompressionMethod
+{
+ /// The file is stored (no compression)
+ Stored,
+ /// The file is Deflated
+ #[cfg(feature = "flate2")]
+ Deflated,
+ /// File is compressed using BZIP2 algorithm
+ #[cfg(feature = "bzip2")]
+ Bzip2,
+ /// Unsupported compression method
+ Unsupported(u16),
+}
+
+impl CompressionMethod {
+ /// Converts an u16 to its corresponding CompressionMethod
+ pub fn from_u16(val: u16) -> CompressionMethod {
+ match val {
+ 0 => CompressionMethod::Stored,
+ #[cfg(feature = "flate2")]
+ 8 => CompressionMethod::Deflated,
+ #[cfg(feature = "bzip2")]
+ 12 => CompressionMethod::Bzip2,
+ v => CompressionMethod::Unsupported(v),
+ }
+ }
+
+ /// Converts a CompressionMethod to a u16
+ pub fn to_u16(self) -> u16 {
+ match self {
+ CompressionMethod::Stored => 0,
+ #[cfg(feature = "flate2")]
+ CompressionMethod::Deflated => 8,
+ #[cfg(feature = "bzip2")]
+ CompressionMethod::Bzip2 => 12,
+ CompressionMethod::Unsupported(v) => v,
+ }
+ }
+}
+
+impl fmt::Display for CompressionMethod {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // Just duplicate what the Debug format looks like, i.e, the enum key:
+ write!(f, "{:?}", self)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::CompressionMethod;
+
+ #[test]
+ fn from_eq_to() {
+ for v in 0..(::std::u16::MAX as u32 + 1)
+ {
+ let from = CompressionMethod::from_u16(v as u16);
+ let to = from.to_u16() as u32;
+ assert_eq!(v, to);
+ }
+ }
+
+ #[cfg(all(not(feature = "bzip2"), feature = "flate2"))]
+ fn methods() -> Vec<CompressionMethod> {
+ vec![CompressionMethod::Stored, CompressionMethod::Deflated]
+ }
+
+ #[cfg(all(not(feature = "flate2"), feature = "bzip2"))]
+ fn methods() -> Vec<CompressionMethod> {
+ vec![CompressionMethod::Stored, CompressionMethod::Bzip2]
+ }
+
+ #[cfg(all(feature = "bzip2", feature = "flate2"))]
+ fn methods() -> Vec<CompressionMethod> {
+ vec![CompressionMethod::Stored, CompressionMethod::Deflated, CompressionMethod::Bzip2]
+ }
+
+ #[cfg(all(not(feature = "bzip2"), not(feature = "flate2")))]
+ fn methods() -> Vec<CompressionMethod> {
+ vec![CompressionMethod::Stored]
+ }
+
+ #[test]
+ fn to_eq_from() {
+ fn check_match(method: CompressionMethod) {
+ let to = method.to_u16();
+ let from = CompressionMethod::from_u16(to);
+ let back = from.to_u16();
+ assert_eq!(to, back);
+ }
+
+ for method in methods() {
+ check_match(method);
+ }
+ }
+
+ #[test]
+ fn to_display_fmt() {
+ fn check_match(method: CompressionMethod) {
+ let debug_str = format!("{:?}", method);
+ let display_str = format!("{}", method);
+ assert_eq!(debug_str, display_str);
+ }
+
+ for method in methods() {
+ check_match(method);
+ }
+ }
+}
diff --git a/third_party/rust/zip/src/cp437.rs b/third_party/rust/zip/src/cp437.rs
new file mode 100644
index 0000000000..202dc8fbb8
--- /dev/null
+++ b/third_party/rust/zip/src/cp437.rs
@@ -0,0 +1,212 @@
+//! Convert a string in IBM codepage 437 to UTF-8
+
+/// Trait to convert IBM codepage 437 to the target type
+pub trait FromCp437 {
+ /// Target type
+ type Target;
+
+ /// Function that does the conversion from cp437.
+ /// Gennerally allocations will be avoided if all data falls into the ASCII range.
+ fn from_cp437(self) -> Self::Target;
+}
+
+impl<'a> FromCp437 for &'a [u8] {
+ type Target = ::std::borrow::Cow<'a, str>;
+
+ fn from_cp437(self) -> Self::Target
+ {
+ if self.iter().all(|c| *c < 0x80) {
+ ::std::str::from_utf8(self).unwrap().into()
+ }
+ else {
+ self.iter().map(|c| to_char(*c)).collect::<String>().into()
+ }
+ }
+}
+
+impl FromCp437 for Vec<u8> {
+ type Target = String;
+
+ fn from_cp437(self) -> Self::Target {
+ if self.iter().all(|c| *c < 0x80) {
+ String::from_utf8(self).unwrap()
+ }
+ else {
+ self.into_iter().map(|c| to_char(c)).collect()
+ }
+ }
+}
+
+fn to_char(input: u8) -> char
+{
+ let output = match input
+ {
+ 0x00 ... 0x7f => input as u32,
+ 0x80 => 0x00c7,
+ 0x81 => 0x00fc,
+ 0x82 => 0x00e9,
+ 0x83 => 0x00e2,
+ 0x84 => 0x00e4,
+ 0x85 => 0x00e0,
+ 0x86 => 0x00e5,
+ 0x87 => 0x00e7,
+ 0x88 => 0x00ea,
+ 0x89 => 0x00eb,
+ 0x8a => 0x00e8,
+ 0x8b => 0x00ef,
+ 0x8c => 0x00ee,
+ 0x8d => 0x00ec,
+ 0x8e => 0x00c4,
+ 0x8f => 0x00c5,
+ 0x90 => 0x00c9,
+ 0x91 => 0x00e6,
+ 0x92 => 0x00c6,
+ 0x93 => 0x00f4,
+ 0x94 => 0x00f6,
+ 0x95 => 0x00f2,
+ 0x96 => 0x00fb,
+ 0x97 => 0x00f9,
+ 0x98 => 0x00ff,
+ 0x99 => 0x00d6,
+ 0x9a => 0x00dc,
+ 0x9b => 0x00a2,
+ 0x9c => 0x00a3,
+ 0x9d => 0x00a5,
+ 0x9e => 0x20a7,
+ 0x9f => 0x0192,
+ 0xa0 => 0x00e1,
+ 0xa1 => 0x00ed,
+ 0xa2 => 0x00f3,
+ 0xa3 => 0x00fa,
+ 0xa4 => 0x00f1,
+ 0xa5 => 0x00d1,
+ 0xa6 => 0x00aa,
+ 0xa7 => 0x00ba,
+ 0xa8 => 0x00bf,
+ 0xa9 => 0x2310,
+ 0xaa => 0x00ac,
+ 0xab => 0x00bd,
+ 0xac => 0x00bc,
+ 0xad => 0x00a1,
+ 0xae => 0x00ab,
+ 0xaf => 0x00bb,
+ 0xb0 => 0x2591,
+ 0xb1 => 0x2592,
+ 0xb2 => 0x2593,
+ 0xb3 => 0x2502,
+ 0xb4 => 0x2524,
+ 0xb5 => 0x2561,
+ 0xb6 => 0x2562,
+ 0xb7 => 0x2556,
+ 0xb8 => 0x2555,
+ 0xb9 => 0x2563,
+ 0xba => 0x2551,
+ 0xbb => 0x2557,
+ 0xbc => 0x255d,
+ 0xbd => 0x255c,
+ 0xbe => 0x255b,
+ 0xbf => 0x2510,
+ 0xc0 => 0x2514,
+ 0xc1 => 0x2534,
+ 0xc2 => 0x252c,
+ 0xc3 => 0x251c,
+ 0xc4 => 0x2500,
+ 0xc5 => 0x253c,
+ 0xc6 => 0x255e,
+ 0xc7 => 0x255f,
+ 0xc8 => 0x255a,
+ 0xc9 => 0x2554,
+ 0xca => 0x2569,
+ 0xcb => 0x2566,
+ 0xcc => 0x2560,
+ 0xcd => 0x2550,
+ 0xce => 0x256c,
+ 0xcf => 0x2567,
+ 0xd0 => 0x2568,
+ 0xd1 => 0x2564,
+ 0xd2 => 0x2565,
+ 0xd3 => 0x2559,
+ 0xd4 => 0x2558,
+ 0xd5 => 0x2552,
+ 0xd6 => 0x2553,
+ 0xd7 => 0x256b,
+ 0xd8 => 0x256a,
+ 0xd9 => 0x2518,
+ 0xda => 0x250c,
+ 0xdb => 0x2588,
+ 0xdc => 0x2584,
+ 0xdd => 0x258c,
+ 0xde => 0x2590,
+ 0xdf => 0x2580,
+ 0xe0 => 0x03b1,
+ 0xe1 => 0x00df,
+ 0xe2 => 0x0393,
+ 0xe3 => 0x03c0,
+ 0xe4 => 0x03a3,
+ 0xe5 => 0x03c3,
+ 0xe6 => 0x00b5,
+ 0xe7 => 0x03c4,
+ 0xe8 => 0x03a6,
+ 0xe9 => 0x0398,
+ 0xea => 0x03a9,
+ 0xeb => 0x03b4,
+ 0xec => 0x221e,
+ 0xed => 0x03c6,
+ 0xee => 0x03b5,
+ 0xef => 0x2229,
+ 0xf0 => 0x2261,
+ 0xf1 => 0x00b1,
+ 0xf2 => 0x2265,
+ 0xf3 => 0x2264,
+ 0xf4 => 0x2320,
+ 0xf5 => 0x2321,
+ 0xf6 => 0x00f7,
+ 0xf7 => 0x2248,
+ 0xf8 => 0x00b0,
+ 0xf9 => 0x2219,
+ 0xfa => 0x00b7,
+ 0xfb => 0x221a,
+ 0xfc => 0x207f,
+ 0xfd => 0x00b2,
+ 0xfe => 0x25a0,
+ 0xff => 0x00a0,
+ _ => unreachable!(),
+ };
+ ::std::char::from_u32(output).unwrap()
+}
+
+#[cfg(test)]
+mod test
+{
+ #[test]
+ fn to_char_valid()
+ {
+ for i in 0x00_u32 .. 0x100
+ {
+ super::to_char(i as u8);
+ }
+ }
+
+ #[test]
+ fn ascii() {
+ for i in 0x00 .. 0x80 {
+ assert_eq!(super::to_char(i), i as char);
+ }
+ }
+
+ #[test]
+ fn example_slice() {
+ use super::FromCp437;
+ let data = b"Cura\x87ao";
+ assert!(::std::str::from_utf8(data).is_err());
+ assert_eq!(data.from_cp437(), "Curaçao");
+ }
+
+ #[test]
+ fn example_vec() {
+ use super::FromCp437;
+ let data = vec![0xCC, 0xCD, 0xCD, 0xB9];
+ assert!(String::from_utf8(data.clone()).is_err());
+ assert_eq!(&data.from_cp437(), "╠══╣");
+ }
+}
diff --git a/third_party/rust/zip/src/crc32.rs b/third_party/rust/zip/src/crc32.rs
new file mode 100644
index 0000000000..35e6b2c583
--- /dev/null
+++ b/third_party/rust/zip/src/crc32.rs
@@ -0,0 +1,128 @@
+//! Helper module to compute a CRC32 checksum
+
+use std::io;
+use std::io::prelude::*;
+
+const CRC32_TABLE : [u32; 256] = [
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+];
+
+/// Update the checksum prev based upon the contents of buf.
+pub fn update(prev: u32, buf: &[u8]) -> u32
+{
+ let mut crc = !prev;
+
+ for &byte in buf.iter()
+ {
+ crc = CRC32_TABLE[((crc as u8) ^ byte) as usize] ^ (crc >> 8);
+ }
+
+ !crc
+}
+
+/// Reader that validates the CRC32 when it reaches the EOF.
+pub struct Crc32Reader<R>
+{
+ inner: R,
+ crc: u32,
+ check: u32,
+}
+
+impl<R> Crc32Reader<R>
+{
+ /// Get a new Crc32Reader which check the inner reader against checksum.
+ pub fn new(inner: R, checksum: u32) -> Crc32Reader<R>
+ {
+ Crc32Reader
+ {
+ inner: inner,
+ crc: 0,
+ check: checksum,
+ }
+ }
+
+ fn check_matches(&self) -> bool
+ {
+ self.check == self.crc
+ }
+
+ pub fn into_inner(self) -> R {
+ self.inner
+ }
+}
+
+impl<R: Read> Read for Crc32Reader<R>
+{
+ 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")) },
+ Ok(n) => n,
+ Err(e) => return Err(e),
+ };
+ self.crc = update(self.crc, &buf[0..count]);
+ Ok(count)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn samples() {
+ assert_eq!(super::update(0, b""), 0);
+
+ // test vectors from the iPXE project (input and output are bitwise negated)
+ assert_eq!(super::update(!0x12345678, b""), !0x12345678);
+ assert_eq!(super::update(!0xffffffff, b"hello world"), !0xf2b5ee7a);
+ assert_eq!(super::update(!0xffffffff, b"hello"), !0xc9ef5979);
+ assert_eq!(super::update(!0xc9ef5979, b" world"), !0xf2b5ee7a);
+
+ // Some vectors found on Rosetta code
+ assert_eq!(super::update(0, b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), 0x190A55AD);
+ assert_eq!(super::update(0, b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), 0xFF6CAB0B);
+ assert_eq!(super::update(0, b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"), 0x91267E8A);
+ }
+}
diff --git a/third_party/rust/zip/src/lib.rs b/third_party/rust/zip/src/lib.rs
new file mode 100644
index 0000000000..f694dfc0db
--- /dev/null
+++ b/third_party/rust/zip/src/lib.rs
@@ -0,0 +1,24 @@
+//! A basic ZipReader/Writer crate
+
+#![warn(missing_docs)]
+
+#[cfg(feature = "bzip2")]
+extern crate bzip2;
+#[cfg(feature = "flate2")]
+extern crate flate2;
+extern crate msdos_time;
+extern crate podio;
+extern crate time;
+
+pub use read::ZipArchive;
+pub use write::ZipWriter;
+pub use compression::CompressionMethod;
+
+mod spec;
+mod crc32;
+mod types;
+pub mod read;
+mod compression;
+pub mod write;
+mod cp437;
+pub mod result;
diff --git a/third_party/rust/zip/src/read.rs b/third_party/rust/zip/src/read.rs
new file mode 100644
index 0000000000..85f76652c0
--- /dev/null
+++ b/third_party/rust/zip/src/read.rs
@@ -0,0 +1,691 @@
+//! 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::borrow::Cow;
+
+use podio::{ReadPodExt, LittleEndian};
+use types::{ZipFileData, System};
+use cp437::FromCp437;
+use msdos_time::{TmMsDosExt, MsDosDateTime};
+
+#[cfg(feature = "flate2")]
+use flate2;
+#[cfg(feature = "flate2")]
+use flate2::read::DeflateDecoder;
+
+#[cfg(feature = "bzip2")]
+use bzip2::read::BzDecoder;
+
+mod ffi {
+ pub const S_IFDIR: u32 = 0o0040000;
+ pub const S_IFREG: u32 = 0o0100000;
+}
+
+const TM_1980_01_01 : ::time::Tm = ::time::Tm {
+ tm_sec: 0,
+ tm_min: 0,
+ tm_hour: 0,
+ tm_mday: 1,
+ tm_mon: 0,
+ tm_year: 80,
+ tm_wday: 2,
+ tm_yday: 0,
+ tm_isdst: -1,
+ tm_utcoff: 0,
+ tm_nsec: 0
+};
+
+/// 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 0..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());
+/// ```
+#[derive(Debug)]
+pub struct ZipArchive<R: Read + io::Seek>
+{
+ reader: R,
+ files: Vec<ZipFileData>,
+ names_map: HashMap<String, usize>,
+ offset: u64,
+ comment: Vec<u8>,
+}
+
+enum ZipFileReader<'a> {
+ NoReader,
+ Stored(Crc32Reader<io::Take<&'a mut Read>>),
+ #[cfg(feature = "flate2")]
+ Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
+ #[cfg(feature = "bzip2")]
+ Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
+}
+
+/// A struct for reading a zip file
+pub struct ZipFile<'a> {
+ data: Cow<'a, ZipFileData>,
+ reader: ZipFileReader<'a>,
+}
+
+fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
+{
+ Err(ZipError::UnsupportedArchive(detail))
+}
+
+
+fn make_reader<'a>(
+ compression_method: ::compression::CompressionMethod,
+ crc32: u32,
+ reader: io::Take<&'a mut io::Read>)
+ -> ZipResult<ZipFileReader<'a>> {
+
+ match compression_method {
+ CompressionMethod::Stored =>
+ {
+ Ok(ZipFileReader::Stored(Crc32Reader::new(
+ reader,
+ crc32)))
+ },
+ #[cfg(feature = "flate2")]
+ CompressionMethod::Deflated =>
+ {
+ let deflate_reader = DeflateDecoder::new(reader);
+ Ok(ZipFileReader::Deflated(Crc32Reader::new(
+ deflate_reader,
+ crc32)))
+ },
+ #[cfg(feature = "bzip2")]
+ CompressionMethod::Bzip2 =>
+ {
+ let bzip2_reader = BzDecoder::new(reader);
+ Ok(ZipFileReader::Bzip2(Crc32Reader::new(
+ bzip2_reader,
+ crc32)))
+ },
+ _ => unsupported_zip_error("Compression method not supported"),
+ }
+}
+
+impl<R: Read+io::Seek> ZipArchive<R>
+{
+ /// Get the directory start offset and number of files. This is done in a
+ /// separate function to ease the control flow design.
+ fn get_directory_counts(reader: &mut R,
+ footer: &spec::CentralDirectoryEnd,
+ cde_start_pos: u64) -> ZipResult<(u64, u64, usize)> {
+ // See if there's a ZIP64 footer. The ZIP64 locator if present will
+ // have its signature 20 bytes in front of the standard footer. The
+ // standard footer, in turn, is 22+N bytes large, where N is the
+ // comment length. Therefore:
+ let zip64locator = if reader.seek(io::SeekFrom::End(-(20 + 22 + footer.zip_file_comment.len() as i64))).is_ok() {
+ match spec::Zip64CentralDirectoryEndLocator::parse(reader) {
+ Ok(loc) => Some(loc),
+ Err(ZipError::InvalidArchive(_)) => {
+ // No ZIP64 header; that's actually fine. We're done here.
+ None
+ },
+ Err(e) => {
+ // Yikes, a real problem
+ return Err(e);
+ }
+ }
+ }
+ else {
+ // Empty Zip files will have nothing else so this error might be fine. If
+ // not, we'll find out soon.
+ None
+ };
+
+ match zip64locator {
+ None => {
+ // Some zip files have data prepended to them, resulting in the
+ // offsets all being too small. Get the amount of error by comparing
+ // the actual file position we found the CDE at with the offset
+ // recorded in the CDE.
+ let archive_offset = cde_start_pos.checked_sub(footer.central_directory_size as u64)
+ .and_then(|x| x.checked_sub(footer.central_directory_offset as u64))
+ .ok_or(ZipError::InvalidArchive("Invalid central directory size or offset"))?;
+
+ let directory_start = footer.central_directory_offset as u64 + archive_offset;
+ let number_of_files = footer.number_of_files_on_this_disk as usize;
+ return Ok((archive_offset, directory_start, number_of_files));
+ },
+ Some(locator64) => {
+ // If we got here, this is indeed a ZIP64 file.
+
+ if footer.disk_number as u32 != locator64.disk_with_central_directory {
+ return unsupported_zip_error("Support for multi-disk files is not implemented")
+ }
+
+ // We need to reassess `archive_offset`. We know where the ZIP64
+ // central-directory-end structure *should* be, but unfortunately we
+ // don't know how to precisely relate that location to our current
+ // actual offset in the file, since there may be junk at its
+ // beginning. Therefore we need to perform another search, as in
+ // read::CentralDirectoryEnd::find_and_parse, except now we search
+ // forward.
+
+ let search_upper_bound = cde_start_pos
+ .checked_sub(60) // minimum size of Zip64CentralDirectoryEnd + Zip64CentralDirectoryEndLocator
+ .ok_or(ZipError::InvalidArchive("File cannot contain ZIP64 central directory end"))?;
+ let (footer, archive_offset) = spec::Zip64CentralDirectoryEnd::find_and_parse(
+ reader,
+ locator64.end_of_central_directory_offset,
+ search_upper_bound)?;
+
+ 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 + archive_offset;
+ Ok((archive_offset, directory_start, footer.number_of_files as usize))
+ },
+ }
+ }
+
+ /// Opens a Zip archive and parses the central directory
+ pub fn new(mut reader: R) -> ZipResult<ZipArchive<R>> {
+ let (footer, cde_start_pos) = 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 (archive_offset, directory_start, number_of_files) =
+ try!(Self::get_directory_counts(&mut reader, &footer, cde_start_pos));
+
+ let mut files = Vec::new();
+ let mut names_map = HashMap::new();
+
+ if let Err(_) = reader.seek(io::SeekFrom::Start(directory_start)) {
+ return Err(ZipError::InvalidArchive("Could not seek to start of central directory"));
+ }
+
+ for _ in 0 .. number_of_files
+ {
+ let file = try!(central_header_to_zip_file(&mut reader, archive_offset));
+ names_map.insert(file.file_name.clone(), files.len());
+ files.push(file);
+ }
+
+ Ok(ZipArchive {
+ reader: reader,
+ files: files,
+ names_map: names_map,
+ offset: archive_offset,
+ comment: footer.zip_file_comment,
+ })
+ }
+
+ /// Number of files contained in this zip.
+ ///
+ /// ```
+ /// fn iter() {
+ /// let mut zip = zip::ZipArchive::new(std::io::Cursor::new(vec![])).unwrap();
+ ///
+ /// for i in 0..zip.len() {
+ /// let mut file = zip.by_index(i).unwrap();
+ /// // Do something with file i
+ /// }
+ /// }
+ /// ```
+ pub fn len(&self) -> usize
+ {
+ self.files.len()
+ }
+
+ /// Get the offset from the beginning of the underlying reader that this zip begins at, in bytes.
+ ///
+ /// Normally this value is zero, but if the zip has arbitrary data prepended to it, then this value will be the size
+ /// of that prepended data.
+ pub fn offset(&self) -> u64 {
+ self.offset
+ }
+
+ /// 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;
+
+ 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);
+
+ Ok(ZipFile { reader: try!(make_reader(data.compression_method, data.crc32, limit_reader)), data: Cow::Borrowed(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, archive_offset: u64) -> ZipResult<ZipFileData>
+{
+ // Parse central header
+ let signature = try!(reader.read_u32::<LittleEndian>());
+ if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE
+ {
+ return Err(ZipError::InvalidArchive("Invalid Central Directory header"))
+ }
+
+ let version_made_by = try!(reader.read_u16::<LittleEndian>());
+ let _version_to_extract = try!(reader.read_u16::<LittleEndian>());
+ let flags = try!(reader.read_u16::<LittleEndian>());
+ let encrypted = flags & 1 == 1;
+ let is_utf8 = flags & (1 << 11) != 0;
+ let compression_method = try!(reader.read_u16::<LittleEndian>());
+ let last_mod_time = try!(reader.read_u16::<LittleEndian>());
+ let last_mod_date = try!(reader.read_u16::<LittleEndian>());
+ let crc32 = try!(reader.read_u32::<LittleEndian>());
+ let compressed_size = try!(reader.read_u32::<LittleEndian>());
+ let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
+ let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+ let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+ let file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+ let _disk_number = try!(reader.read_u16::<LittleEndian>());
+ let _internal_file_attributes = try!(reader.read_u16::<LittleEndian>());
+ let external_file_attributes = try!(reader.read_u32::<LittleEndian>());
+ let mut offset = try!(reader.read_u32::<LittleEndian>()) as u64;
+ let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
+ let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
+ let file_comment_raw = try!(ReadPodExt::read_exact(reader, file_comment_length));
+
+ // Account for shifted zip offsets.
+ offset += archive_offset;
+
+ let file_name = match is_utf8
+ {
+ true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
+ false => file_name_raw.clone().from_cp437(),
+ };
+ let file_comment = match is_utf8
+ {
+ true => String::from_utf8_lossy(&*file_comment_raw).into_owned(),
+ false => file_comment_raw.from_cp437(),
+ };
+
+ // Construct the result
+ let mut result = ZipFileData
+ {
+ system: System::from_u8((version_made_by >> 8) as u8),
+ version_made_by: version_made_by as u8,
+ encrypted: encrypted,
+ compression_method: CompressionMethod::from_u16(compression_method),
+ last_modified_time: ::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date)).unwrap_or(TM_1980_01_01),
+ crc32: crc32,
+ compressed_size: compressed_size as u64,
+ uncompressed_size: uncompressed_size as u64,
+ file_name: file_name,
+ file_name_raw: file_name_raw,
+ file_comment: file_comment,
+ header_start: offset,
+ data_start: 0,
+ external_attributes: external_file_attributes,
+ };
+
+ match parse_extra_field(&mut result, &*extra_field) {
+ Ok(..) | Err(ZipError::Io(..)) => {},
+ Err(e) => try!(Err(e)),
+ }
+
+ // Remember end of central header
+ let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
+
+ // Parse local header
+ try!(reader.seek(io::SeekFrom::Start(result.header_start)));
+ let signature = try!(reader.read_u32::<LittleEndian>());
+ 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_u16::<LittleEndian>()) as u64;
+ let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
+ let magic_and_header = 4 + 22 + 2 + 2;
+ result.data_start = result.header_start + magic_and_header + file_name_length + extra_field_length;
+
+ // 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_u16::<LittleEndian>());
+ let len = try!(reader.read_u16::<LittleEndian>());
+ match kind
+ {
+ // Zip64 extended information extra field
+ 0x0001 => {
+ file.uncompressed_size = try!(reader.read_u64::<LittleEndian>());
+ file.compressed_size = try!(reader.read_u64::<LittleEndian>());
+ try!(reader.read_u64::<LittleEndian>()); // relative header offset
+ try!(reader.read_u32::<LittleEndian>()); // disk start number
+ },
+ _ => { try!(reader.seek(io::SeekFrom::Current(len as i64))); },
+ };
+ }
+ Ok(())
+}
+
+fn get_reader<'a>(reader: &'a mut ZipFileReader) -> &'a mut Read {
+ match *reader {
+ ZipFileReader::NoReader => panic!("ZipFileReader was in an invalid state"),
+ ZipFileReader::Stored(ref mut r) => r as &mut Read,
+ #[cfg(feature = "flate2")]
+ ZipFileReader::Deflated(ref mut r) => r as &mut Read,
+ #[cfg(feature = "bzip2")]
+ ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
+ }
+}
+
+/// Methods for retreiving information on zip files
+impl<'a> ZipFile<'a> {
+ fn get_reader(&mut self) -> &mut Read {
+ get_reader(&mut self.reader)
+ }
+ /// Get the version of the file
+ pub fn version_made_by(&self) -> (u8, u8) {
+ (self.data.version_made_by / 10, self.data.version_made_by % 10)
+ }
+ /// Get the name of the file
+ pub fn name(&self) -> &str {
+ &*self.data.file_name
+ }
+ /// Get the name of the file, in the raw (internal) byte representation.
+ pub fn name_raw(&self) -> &[u8] {
+ &*self.data.file_name_raw
+ }
+ /// Get the name of the file in a sanitized form. It truncates the name to the first NULL byte,
+ /// removes a leading '/' and removes '..' parts.
+ pub fn sanitized_name(&self) -> ::std::path::PathBuf {
+ self.data.file_name_sanitized()
+ }
+ /// 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
+ }
+ /// Get unix mode for the file
+ pub fn unix_mode(&self) -> Option<u32> {
+ if self.data.external_attributes == 0 {
+ return None;
+ }
+
+ match self.data.system {
+ System::Unix => {
+ Some(self.data.external_attributes >> 16)
+ },
+ System::Dos => {
+ // Interpret MSDOS directory bit
+ let mut mode = if 0x10 == (self.data.external_attributes & 0x10) {
+ ffi::S_IFDIR | 0o0775
+ } else {
+ ffi::S_IFREG | 0o0664
+ };
+ if 0x01 == (self.data.external_attributes & 0x01) {
+ // Read-only bit; strip write permissions
+ mode &= 0o0555;
+ }
+ Some(mode)
+ },
+ _ => None,
+ }
+ }
+ /// Get the CRC32 hash of the original file
+ pub fn crc32(&self) -> u32 {
+ self.data.crc32
+ }
+
+ /// Get the starting offset of the data of the compressed file
+ pub fn data_start(&self) -> u64 {
+ self.data.data_start
+ }
+}
+
+impl<'a> Read for ZipFile<'a> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.get_reader().read(buf)
+ }
+}
+
+impl<'a> Drop for ZipFile<'a> {
+ fn drop(&mut self) {
+ // self.data is Owned, this reader is constructed by a streaming reader.
+ // In this case, we want to exhaust the reader so that the next file is accessible.
+ if let Cow::Owned(_) = self.data {
+ let mut buffer = [0; 1<<16];
+
+ // Get the inner `Take` reader so all decompression and CRC calculation is skipped.
+ let innerreader = ::std::mem::replace(&mut self.reader, ZipFileReader::NoReader);
+ let mut reader = match innerreader {
+ ZipFileReader::NoReader => panic!("ZipFileReader was in an invalid state"),
+ ZipFileReader::Stored(crcreader) => crcreader.into_inner(),
+ #[cfg(feature = "flate2")]
+ ZipFileReader::Deflated(crcreader) => crcreader.into_inner().into_inner(),
+ #[cfg(feature = "bzip2")]
+ ZipFileReader::Bzip2(crcreader) => crcreader.into_inner().into_inner(),
+ };
+
+ loop {
+ match reader.read(&mut buffer) {
+ Ok(0) => break,
+ Ok(_) => (),
+ Err(e) => panic!("Could not consume all of the output of the current ZipFile: {:?}", e),
+ }
+ }
+ }
+ }
+}
+
+/// Read ZipFile structures from a non-seekable reader.
+///
+/// This is an alternative method to read a zip file. If possible, use the ZipArchive functions
+/// as some information will be missing when reading this manner.
+///
+/// Reads a file header from the start of the stream. Will return `Ok(Some(..))` if a file is
+/// present at the start of the stream. Returns `Ok(None)` if the start of the central directory
+/// is encountered. No more files should be read after this.
+///
+/// The Drop implementation of ZipFile ensures that the reader will be correctly positioned after
+/// the structure is done.
+///
+/// Missing fields are:
+/// * `comment`: set to an empty string
+/// * `data_start`: set to 0
+/// * `external_attributes`: `unix_mode()`: will return None
+pub fn read_zipfile_from_stream<'a, R: io::Read>(reader: &'a mut R) -> ZipResult<Option<ZipFile>> {
+ let signature = try!(reader.read_u32::<LittleEndian>());
+
+ match signature {
+ spec::LOCAL_FILE_HEADER_SIGNATURE => (),
+ spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE => return Ok(None),
+ _ => return Err(ZipError::InvalidArchive("Invalid local file header")),
+ }
+
+ let version_made_by = try!(reader.read_u16::<LittleEndian>());
+ let flags = try!(reader.read_u16::<LittleEndian>());
+ let encrypted = flags & 1 == 1;
+ let is_utf8 = flags & (1 << 11) != 0;
+ let using_data_descriptor = flags & (1 << 3) != 0;
+ let compression_method = CompressionMethod::from_u16(try!(reader.read_u16::<LittleEndian>()));
+ let last_mod_time = try!(reader.read_u16::<LittleEndian>());
+ let last_mod_date = try!(reader.read_u16::<LittleEndian>());
+ let crc32 = try!(reader.read_u32::<LittleEndian>());
+ let compressed_size = try!(reader.read_u32::<LittleEndian>());
+ let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
+ let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+ let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+
+ let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
+ let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
+
+ let file_name = match is_utf8
+ {
+ true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
+ false => file_name_raw.clone().from_cp437(),
+ };
+
+ let mut result = ZipFileData
+ {
+ system: System::from_u8((version_made_by >> 8) as u8),
+ version_made_by: version_made_by as u8,
+ encrypted: encrypted,
+ compression_method: compression_method,
+ last_modified_time: ::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date)).unwrap_or(TM_1980_01_01),
+ crc32: crc32,
+ compressed_size: compressed_size as u64,
+ uncompressed_size: uncompressed_size as u64,
+ file_name: file_name,
+ file_name_raw: file_name_raw,
+ file_comment: String::new(), // file comment is only available in the central directory
+ // header_start and data start are not available, but also don't matter, since seeking is
+ // not available.
+ header_start: 0,
+ data_start: 0,
+ // The external_attributes field is only available in the central directory.
+ // We set this to zero, which should be valid as the docs state 'If input came
+ // from standard input, this field is set to zero.'
+ external_attributes: 0,
+ };
+
+ match parse_extra_field(&mut result, &extra_field) {
+ Ok(..) | Err(ZipError::Io(..)) => {},
+ Err(e) => try!(Err(e)),
+ }
+
+ if encrypted {
+ return unsupported_zip_error("Encrypted files are not supported")
+ }
+ if using_data_descriptor {
+ return unsupported_zip_error("The file length is not available in the local header");
+ }
+
+ let limit_reader = (reader as &'a mut io::Read).take(result.compressed_size as u64);
+
+ let result_crc32 = result.crc32;
+ let result_compression_method = result.compression_method;
+ Ok(Some(ZipFile {
+ data: Cow::Owned(result),
+ reader: try!(make_reader(result_compression_method, result_crc32, limit_reader))
+ }))
+}
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn invalid_offset() {
+ use std::io;
+ use super::ZipArchive;
+
+ let mut v = Vec::new();
+ v.extend_from_slice(include_bytes!("../tests/data/invalid_offset.zip"));
+ let reader = ZipArchive::new(io::Cursor::new(v));
+ assert!(reader.is_err());
+ }
+
+ #[test]
+ fn zip64_with_leading_junk() {
+ use std::io;
+ use super::ZipArchive;
+
+ let mut v = Vec::new();
+ v.extend_from_slice(include_bytes!("../tests/data/zip64_demo.zip"));
+ let reader = ZipArchive::new(io::Cursor::new(v)).unwrap();
+ assert!(reader.len() == 1);
+ }
+
+ #[test]
+ fn zip_comment() {
+ use std::io;
+ use super::ZipArchive;
+
+ let mut v = Vec::new();
+ v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
+ let reader = ZipArchive::new(io::Cursor::new(v)).unwrap();
+ assert!(reader.comment == b"zip-rs");
+ }
+
+ #[test]
+ fn zip_read_streaming() {
+ use std::io;
+ use super::read_zipfile_from_stream;
+
+ let mut v = Vec::new();
+ v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
+ let mut reader = io::Cursor::new(v);
+ loop {
+ match read_zipfile_from_stream(&mut reader).unwrap() {
+ None => break,
+ _ => (),
+ }
+ }
+ }
+}
diff --git a/third_party/rust/zip/src/result.rs b/third_party/rust/zip/src/result.rs
new file mode 100644
index 0000000000..d82259ea3d
--- /dev/null
+++ b/third_party/rust/zip/src/result.rs
@@ -0,0 +1,94 @@
+//! Error types that can be emitted from this library
+
+use std::convert;
+use std::error;
+use std::fmt;
+use std::io;
+
+/// Generic result type with ZipError as its error variant
+pub type ZipResult<T> = Result<T, ZipError>;
+
+/// Error type for Zip
+#[derive(Debug)]
+pub enum ZipError
+{
+ /// An Error caused by I/O
+ Io(io::Error),
+
+ /// This file is probably not a zip archive
+ InvalidArchive(&'static str),
+
+ /// This archive is not supported
+ UnsupportedArchive(&'static str),
+
+ /// The requested file could not be found in the archive
+ FileNotFound,
+}
+
+impl ZipError
+{
+ fn detail(&self) -> ::std::borrow::Cow<str>
+ {
+ use std::error::Error;
+
+ match *self
+ {
+ ZipError::Io(ref io_err) => {
+ ("Io Error: ".to_string() + (io_err as &error::Error).description()).into()
+ },
+ ZipError::InvalidArchive(msg) | ZipError::UnsupportedArchive(msg) => {
+ (self.description().to_string() + ": " + msg).into()
+ },
+ ZipError::FileNotFound => {
+ self.description().into()
+ },
+ }
+ }
+}
+
+impl convert::From<io::Error> for ZipError
+{
+ fn from(err: io::Error) -> ZipError
+ {
+ ZipError::Io(err)
+ }
+}
+
+impl convert::From<ZipError> for io::Error
+{
+ fn from(err: ZipError) -> io::Error
+ {
+ io::Error::new(io::ErrorKind::Other, err)
+ }
+}
+
+impl fmt::Display for ZipError
+{
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>
+ {
+ fmt.write_str(&*self.detail())
+ }
+}
+
+impl error::Error for ZipError
+{
+ fn description(&self) -> &str
+ {
+ match *self
+ {
+ ZipError::Io(ref io_err) => (io_err as &error::Error).description(),
+ ZipError::InvalidArchive(..) => "Invalid Zip archive",
+ ZipError::UnsupportedArchive(..) => "Unsupported Zip archive",
+ ZipError::FileNotFound => "Specified file not found in archive",
+ }
+ }
+
+ fn cause(&self) -> Option<&error::Error>
+ {
+ match *self
+ {
+ ZipError::Io(ref io_err) => Some(io_err as &error::Error),
+ _ => None,
+ }
+ }
+}
diff --git a/third_party/rust/zip/src/spec.rs b/third_party/rust/zip/src/spec.rs
new file mode 100644
index 0000000000..2f5466f744
--- /dev/null
+++ b/third_party/rust/zip/src/spec.rs
@@ -0,0 +1,190 @@
+use std::io;
+use std::io::prelude::*;
+use result::{ZipResult, ZipError};
+use podio::{ReadPodExt, WritePodExt, LittleEndian};
+
+pub const LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
+pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
+const CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;
+pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06064b50;
+const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE : u32 = 0x07064b50;
+
+pub struct CentralDirectoryEnd
+{
+ pub disk_number: u16,
+ pub disk_with_central_directory: u16,
+ pub number_of_files_on_this_disk: u16,
+ pub number_of_files: u16,
+ pub central_directory_size: u32,
+ pub central_directory_offset: u32,
+ pub zip_file_comment: Vec<u8>,
+}
+
+impl CentralDirectoryEnd
+{
+ pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>
+ {
+ let magic = try!(reader.read_u32::<LittleEndian>());
+ if magic != CENTRAL_DIRECTORY_END_SIGNATURE
+ {
+ return Err(ZipError::InvalidArchive("Invalid digital signature header"))
+ }
+ let disk_number = try!(reader.read_u16::<LittleEndian>());
+ let disk_with_central_directory = try!(reader.read_u16::<LittleEndian>());
+ let number_of_files_on_this_disk = try!(reader.read_u16::<LittleEndian>());
+ let number_of_files = try!(reader.read_u16::<LittleEndian>());
+ let central_directory_size = try!(reader.read_u32::<LittleEndian>());
+ let central_directory_offset = try!(reader.read_u32::<LittleEndian>());
+ let zip_file_comment_length = try!(reader.read_u16::<LittleEndian>()) as usize;
+ let zip_file_comment = try!(ReadPodExt::read_exact(reader, zip_file_comment_length));
+
+ Ok(CentralDirectoryEnd
+ {
+ disk_number: disk_number,
+ disk_with_central_directory: disk_with_central_directory,
+ number_of_files_on_this_disk: number_of_files_on_this_disk,
+ number_of_files: number_of_files,
+ central_directory_size: central_directory_size,
+ central_directory_offset: central_directory_offset,
+ zip_file_comment: zip_file_comment,
+ })
+ }
+
+ pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T) -> ZipResult<(CentralDirectoryEnd, u64)>
+ {
+ const HEADER_SIZE: u64 = 22;
+ const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
+ let file_length = try!(reader.seek(io::SeekFrom::End(0)));
+
+ let search_upper_bound = file_length.checked_sub(HEADER_SIZE + ::std::u16::MAX as u64).unwrap_or(0);
+
+ if file_length < HEADER_SIZE {
+ return Err(ZipError::InvalidArchive("Invalid zip header"));
+ }
+
+ let mut pos = file_length - HEADER_SIZE;
+ while pos >= search_upper_bound
+ {
+ try!(reader.seek(io::SeekFrom::Start(pos as u64)));
+ if try!(reader.read_u32::<LittleEndian>()) == CENTRAL_DIRECTORY_END_SIGNATURE
+ {
+ try!(reader.seek(io::SeekFrom::Current(BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64)));
+ let comment_length = try!(reader.read_u16::<LittleEndian>()) as u64;
+ if file_length - pos - HEADER_SIZE == comment_length
+ {
+ let cde_start_pos = try!(reader.seek(io::SeekFrom::Start(pos as u64)));
+ return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
+ }
+ }
+ pos = match pos.checked_sub(1) {
+ Some(p) => p,
+ None => break,
+ };
+ }
+ Err(ZipError::InvalidArchive("Could not find central directory end"))
+ }
+
+ pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()>
+ {
+ try!(writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE));
+ try!(writer.write_u16::<LittleEndian>(self.disk_number));
+ try!(writer.write_u16::<LittleEndian>(self.disk_with_central_directory));
+ try!(writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk));
+ try!(writer.write_u16::<LittleEndian>(self.number_of_files));
+ try!(writer.write_u32::<LittleEndian>(self.central_directory_size));
+ try!(writer.write_u32::<LittleEndian>(self.central_directory_offset));
+ try!(writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16));
+ try!(writer.write_all(&self.zip_file_comment));
+ Ok(())
+ }
+}
+
+pub struct Zip64CentralDirectoryEndLocator
+{
+ pub disk_with_central_directory: u32,
+ pub end_of_central_directory_offset: u64,
+ pub number_of_disks: u32,
+}
+
+impl Zip64CentralDirectoryEndLocator
+{
+ pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>
+ {
+ let magic = try!(reader.read_u32::<LittleEndian>());
+ if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE
+ {
+ return Err(ZipError::InvalidArchive("Invalid zip64 locator digital signature header"))
+ }
+ let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
+ let end_of_central_directory_offset = try!(reader.read_u64::<LittleEndian>());
+ let number_of_disks = try!(reader.read_u32::<LittleEndian>());
+
+ Ok(Zip64CentralDirectoryEndLocator
+ {
+ disk_with_central_directory: disk_with_central_directory,
+ end_of_central_directory_offset: end_of_central_directory_offset,
+ number_of_disks: number_of_disks,
+ })
+ }
+}
+
+pub struct Zip64CentralDirectoryEnd
+{
+ pub version_made_by: u16,
+ pub version_needed_to_extract: u16,
+ pub disk_number: u32,
+ pub disk_with_central_directory: u32,
+ pub number_of_files_on_this_disk: u64,
+ pub number_of_files: u64,
+ pub central_directory_size: u64,
+ pub central_directory_offset: u64,
+ //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
+}
+
+impl Zip64CentralDirectoryEnd
+{
+ pub fn find_and_parse<T: Read+io::Seek>(reader: &mut T,
+ nominal_offset: u64,
+ search_upper_bound: u64) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>
+ {
+ let mut pos = nominal_offset;
+
+ while pos <= search_upper_bound
+ {
+ reader.seek(io::SeekFrom::Start(pos))?;
+
+ if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE
+ {
+ let archive_offset = pos - nominal_offset;
+
+ let _record_size = try!(reader.read_u64::<LittleEndian>());
+ // We would use this value if we did anything with the "zip64 extensible data sector".
+
+ let version_made_by = try!(reader.read_u16::<LittleEndian>());
+ let version_needed_to_extract = try!(reader.read_u16::<LittleEndian>());
+ let disk_number = try!(reader.read_u32::<LittleEndian>());
+ let disk_with_central_directory = try!(reader.read_u32::<LittleEndian>());
+ let number_of_files_on_this_disk = try!(reader.read_u64::<LittleEndian>());
+ let number_of_files = try!(reader.read_u64::<LittleEndian>());
+ let central_directory_size = try!(reader.read_u64::<LittleEndian>());
+ let central_directory_offset = try!(reader.read_u64::<LittleEndian>());
+
+ return Ok((Zip64CentralDirectoryEnd
+ {
+ version_made_by: version_made_by,
+ version_needed_to_extract: version_needed_to_extract,
+ disk_number: disk_number,
+ disk_with_central_directory: disk_with_central_directory,
+ number_of_files_on_this_disk: number_of_files_on_this_disk,
+ number_of_files: number_of_files,
+ central_directory_size: central_directory_size,
+ central_directory_offset: central_directory_offset,
+ }, archive_offset));
+ }
+
+ pos += 1;
+ }
+
+ Err(ZipError::InvalidArchive("Could not find ZIP64 central directory end"))
+ }
+}
diff --git a/third_party/rust/zip/src/types.rs b/third_party/rust/zip/src/types.rs
new file mode 100644
index 0000000000..a8609b3b20
--- /dev/null
+++ b/third_party/rust/zip/src/types.rs
@@ -0,0 +1,136 @@
+//! Types that specify what is contained in a ZIP.
+
+use time;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum System
+{
+ Dos = 0,
+ Unix = 3,
+ Unknown,
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+impl System {
+ pub fn from_u8(system: u8) -> System
+ {
+ use self::System::*;
+
+ match system {
+ 0 => Dos,
+ 3 => Unix,
+ _ => Unknown,
+ }
+ }
+}
+
+pub const DEFAULT_VERSION: u8 = 46;
+
+/// Structure representing a ZIP file.
+#[derive(Debug, Clone)]
+pub struct ZipFileData
+{
+ /// Compatibility of the file attribute information
+ pub system: System,
+ /// Specification version
+ pub version_made_by: u8,
+ /// True if the file is encrypted.
+ pub encrypted: bool,
+ /// Compression method used to store the file
+ pub compression_method: ::compression::CompressionMethod,
+ /// Last modified time. This will only have a 2 second precision.
+ pub last_modified_time: time::Tm,
+ /// CRC32 checksum
+ pub crc32: u32,
+ /// Size of the file in the ZIP
+ pub compressed_size: u64,
+ /// Size of the file when extracted
+ pub uncompressed_size: u64,
+ /// Name of the file
+ pub file_name: String,
+ /// Raw file name. To be used when file_name was incorrectly decoded.
+ pub file_name_raw: Vec<u8>,
+ /// File comment
+ pub file_comment: String,
+ /// Specifies where the local header of the file starts
+ pub header_start: u64,
+ /// Specifies where the compressed data of the file starts
+ pub data_start: u64,
+ /// External file attributes
+ pub external_attributes: u32,
+}
+
+impl ZipFileData {
+ pub fn file_name_sanitized(&self) -> ::std::path::PathBuf {
+ let no_null_filename = match self.file_name.find('\0') {
+ Some(index) => &self.file_name[0..index],
+ None => &self.file_name,
+ }.to_string();
+
+ // zip files can contain both / and \ as separators regardless of the OS
+ // and as we want to return a sanitized PathBuf that only supports the
+ // OS separator let's convert incompatible separators to compatible ones
+ let separator = ::std::path::MAIN_SEPARATOR;
+ let opposite_separator = match separator {
+ '/' => '\\',
+ '\\' | _ => '/',
+ };
+ let filename =
+ no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string());
+
+ ::std::path::Path::new(&filename)
+ .components()
+ .filter(|component| match *component {
+ ::std::path::Component::Normal(..) => true,
+ _ => false,
+ })
+ .fold(::std::path::PathBuf::new(), |mut path, ref cur| {
+ path.push(cur.as_os_str());
+ path
+ })
+ }
+
+ pub fn version_needed(&self) -> u16 {
+ match self.compression_method {
+ #[cfg(feature = "bzip2")]
+ ::compression::CompressionMethod::Bzip2 => 46,
+ _ => 20,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn system() {
+ use super::System;
+ assert_eq!(System::Dos as u16, 0u16);
+ assert_eq!(System::Unix as u16, 3u16);
+ assert_eq!(System::from_u8(0), System::Dos);
+ assert_eq!(System::from_u8(3), System::Unix);
+ }
+
+ #[test]
+ fn sanitize() {
+ use super::*;
+ let file_name = "/path/../../../../etc/./passwd\0/etc/shadow".to_string();
+ let data = ZipFileData {
+ system: System::Dos,
+ version_made_by: 0,
+ encrypted: false,
+ compression_method: ::compression::CompressionMethod::Stored,
+ last_modified_time: time::empty_tm(),
+ crc32: 0,
+ compressed_size: 0,
+ uncompressed_size: 0,
+ file_name: file_name.clone(),
+ file_name_raw: file_name.into_bytes(),
+ file_comment: String::new(),
+ header_start: 0,
+ data_start: 0,
+ external_attributes: 0,
+ };
+ assert_eq!(data.file_name_sanitized(), ::std::path::PathBuf::from("path/etc/passwd"));
+ }
+}
diff --git a/third_party/rust/zip/src/write.rs b/third_party/rust/zip/src/write.rs
new file mode 100644
index 0000000000..a7d172c316
--- /dev/null
+++ b/third_party/rust/zip/src/write.rs
@@ -0,0 +1,558 @@
+//! Structs for creating a new zip archive
+
+use compression::CompressionMethod;
+use types::{ZipFileData, System, DEFAULT_VERSION};
+use spec;
+use crc32;
+use result::{ZipResult, ZipError};
+use std::default::Default;
+use std::io;
+use std::io::prelude::*;
+use std::mem;
+use time;
+use podio::{WritePodExt, LittleEndian};
+use msdos_time::TmMsDosExt;
+
+#[cfg(feature = "flate2")]
+use flate2;
+#[cfg(feature = "flate2")]
+use flate2::write::DeflateEncoder;
+
+#[cfg(feature = "bzip2")]
+use bzip2;
+#[cfg(feature = "bzip2")]
+use bzip2::write::BzEncoder;
+#[allow(unused_imports)] // Rust <1.23 compat
+use std::ascii::AsciiExt;
+
+enum GenericZipWriter<W: Write + io::Seek>
+{
+ Closed,
+ Storer(W),
+ #[cfg(feature = "flate2")]
+ Deflater(DeflateEncoder<W>),
+ #[cfg(feature = "bzip2")]
+ Bzip2(BzEncoder<W>),
+}
+
+/// Generator for ZIP files.
+///
+/// ```
+/// 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: &mut [u8] = &mut [0u8; 65536];
+/// let mut w = std::io::Cursor::new(buf);
+/// let mut zip = zip::ZipWriter::new(w);
+///
+/// let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
+/// try!(zip.start_file("hello_world.txt", options));
+/// try!(zip.write(b"Hello, World!"));
+///
+/// // Optionally finish the zip. (this is also done on drop)
+/// try!(zip.finish());
+///
+/// Ok(())
+/// }
+///
+/// println!("Result: {:?}", doit().unwrap());
+/// ```
+pub struct ZipWriter<W: Write + io::Seek>
+{
+ inner: GenericZipWriter<W>,
+ files: Vec<ZipFileData>,
+ stats: ZipWriterStats,
+}
+
+#[derive(Default)]
+struct ZipWriterStats
+{
+ crc32: u32,
+ start: u64,
+ bytes_written: u64,
+}
+
+/// Metadata for a file to be written
+#[derive(Copy, Clone)]
+pub struct FileOptions {
+ compression_method: CompressionMethod,
+ last_modified_time: time::Tm,
+ permissions: Option<u32>,
+}
+
+impl FileOptions {
+ #[cfg(feature = "flate2")]
+ /// Construct a new FileOptions object
+ pub fn default() -> FileOptions {
+ FileOptions {
+ compression_method: CompressionMethod::Deflated,
+ last_modified_time: time::now(),
+ permissions: None,
+ }
+ }
+
+ #[cfg(not(feature = "flate2"))]
+ /// Construct a new FileOptions object
+ pub fn default() -> FileOptions {
+ FileOptions {
+ compression_method: CompressionMethod::Stored,
+ last_modified_time: time::now(),
+ permissions: None,
+ }
+ }
+
+ /// Set the compression method for the new file
+ ///
+ /// The default is `CompressionMethod::Deflated`. If the deflate compression feature is
+ /// disabled, `CompressionMethod::Stored` becomes the default.
+ /// otherwise.
+ pub fn compression_method(mut self, method: CompressionMethod) -> FileOptions {
+ self.compression_method = method;
+ self
+ }
+
+ /// Set the last modified time
+ ///
+ /// The default is the current timestamp
+ pub fn last_modified_time(mut self, mod_time: time::Tm) -> FileOptions {
+ self.last_modified_time = mod_time;
+ self
+ }
+
+ /// Set the permissions for the new file.
+ ///
+ /// The format is represented with unix-style permissions.
+ /// The default is `0o644`, which represents `rw-r--r--` for files,
+ /// and `0o755`, which represents `rwxr-xr-x` for directories
+ pub fn unix_permissions(mut self, mode: u32) -> FileOptions {
+ self.permissions = Some(mode & 0o777);
+ self
+ }
+}
+
+impl<W: Write+io::Seek> Write for ZipWriter<W>
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize>
+ {
+ if self.files.len() == 0 { return Err(io::Error::new(io::ErrorKind::Other, "No file has been started")) }
+ match self.inner.ref_mut()
+ {
+ Some(ref mut w) => {
+ let write_result = w.write(buf);
+ if let Ok(count) = write_result {
+ self.stats.update(&buf[0..count]);
+ }
+ write_result
+
+ }
+ None => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed")),
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()>
+ {
+ match self.inner.ref_mut()
+ {
+ Some(ref mut w) => w.flush(),
+ None => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed")),
+ }
+ }
+}
+
+impl ZipWriterStats
+{
+ fn update(&mut self, buf: &[u8])
+ {
+ self.crc32 = crc32::update(self.crc32, buf);
+ self.bytes_written += buf.len() as u64;
+ }
+}
+
+impl<W: Write+io::Seek> ZipWriter<W>
+{
+ /// Initializes the ZipWriter.
+ ///
+ /// Before writing to this object, the start_file command should be called.
+ pub fn new(inner: W) -> ZipWriter<W>
+ {
+ ZipWriter
+ {
+ inner: GenericZipWriter::Storer(inner),
+ files: Vec::new(),
+ stats: Default::default(),
+ }
+ }
+
+ /// Start a new file for with the requested options.
+ fn start_entry<S>(&mut self, name: S, options: FileOptions) -> ZipResult<()>
+ where S: Into<String>
+ {
+ try!(self.finish_file());
+
+ {
+ let writer = self.inner.get_plain();
+ let header_start = try!(writer.seek(io::SeekFrom::Current(0)));
+
+ let permissions = options.permissions.unwrap_or(0o100644);
+ let file_name = name.into();
+ let file_name_raw = file_name.clone().into_bytes();
+ let mut file = ZipFileData
+ {
+ system: System::Unix,
+ version_made_by: DEFAULT_VERSION,
+ encrypted: false,
+ compression_method: options.compression_method,
+ last_modified_time: options.last_modified_time,
+ crc32: 0,
+ compressed_size: 0,
+ uncompressed_size: 0,
+ file_name: file_name,
+ file_name_raw: file_name_raw,
+ file_comment: String::new(),
+ header_start: header_start,
+ data_start: 0,
+ external_attributes: permissions << 16,
+ };
+ try!(write_local_file_header(writer, &file));
+
+ let header_end = try!(writer.seek(io::SeekFrom::Current(0)));
+ self.stats.start = header_end;
+ file.data_start = header_end;
+
+ self.stats.bytes_written = 0;
+ self.stats.crc32 = 0;
+
+ self.files.push(file);
+ }
+
+ try!(self.inner.switch_to(options.compression_method));
+
+ Ok(())
+ }
+
+ fn finish_file(&mut self) -> ZipResult<()>
+ {
+ try!(self.inner.switch_to(CompressionMethod::Stored));
+ let writer = self.inner.get_plain();
+
+ let file = match self.files.last_mut()
+ {
+ None => return Ok(()),
+ Some(f) => f,
+ };
+ file.crc32 = self.stats.crc32;
+ file.uncompressed_size = self.stats.bytes_written;
+
+ let file_end = try!(writer.seek(io::SeekFrom::Current(0)));
+ file.compressed_size = file_end - self.stats.start;
+
+ try!(update_local_file_header(writer, file));
+ try!(writer.seek(io::SeekFrom::Start(file_end)));
+ Ok(())
+ }
+
+ /// Starts a file.
+ pub fn start_file<S>(&mut self, name: S, mut options: FileOptions) -> ZipResult<()>
+ where S: Into<String>
+ {
+ if options.permissions.is_none() {
+ options.permissions = Some(0o644);
+ }
+ *options.permissions.as_mut().unwrap() |= 0o100000;
+ try!(self.start_entry(name, options));
+ Ok(())
+ }
+
+ /// Add a directory entry.
+ ///
+ /// You should not write data to the file afterwards.
+ pub fn add_directory<S>(&mut self, name: S, mut options: FileOptions) -> ZipResult<()>
+ where S: Into<String>
+ {
+ if options.permissions.is_none() {
+ options.permissions = Some(0o755);
+ }
+ *options.permissions.as_mut().unwrap() |= 0o40000;
+ options.compression_method = CompressionMethod::Stored;
+ try!(self.start_entry(name, options));
+ Ok(())
+ }
+
+ /// Finish the last file and write all other zip-structures
+ ///
+ /// This will return the writer, but one should normally not append any data to the end of the file.
+ /// Note that the zipfile will also be finished on drop.
+ pub fn finish(&mut self) -> ZipResult<W>
+ {
+ try!(self.finalize());
+ let inner = mem::replace(&mut self.inner, GenericZipWriter::Closed);
+ Ok(inner.unwrap())
+ }
+
+ fn finalize(&mut self) -> ZipResult<()>
+ {
+ try!(self.finish_file());
+
+ {
+ let writer = self.inner.get_plain();
+
+ let central_start = try!(writer.seek(io::SeekFrom::Current(0)));
+ for file in self.files.iter()
+ {
+ try!(write_central_directory_header(writer, file));
+ }
+ let central_size = try!(writer.seek(io::SeekFrom::Current(0))) - central_start;
+
+ let footer = spec::CentralDirectoryEnd
+ {
+ disk_number: 0,
+ disk_with_central_directory: 0,
+ number_of_files_on_this_disk: self.files.len() as u16,
+ number_of_files: self.files.len() as u16,
+ central_directory_size: central_size as u32,
+ central_directory_offset: central_start as u32,
+ zip_file_comment: b"zip-rs".to_vec(),
+ };
+
+ try!(footer.write(writer));
+ }
+
+ Ok(())
+ }
+}
+
+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 io::stderr(), "ZipWriter drop failed: {:?}", e);
+ }
+ }
+ }
+}
+
+impl<W: Write+io::Seek> GenericZipWriter<W>
+{
+ fn switch_to(&mut self, compression: CompressionMethod) -> ZipResult<()>
+ {
+ match self.current_compression() {
+ Some(method) if method == compression => return Ok(()),
+ None => try!(Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed"))),
+ _ => {},
+ }
+
+ let bare = match mem::replace(self, GenericZipWriter::Closed)
+ {
+ GenericZipWriter::Storer(w) => w,
+ #[cfg(feature = "flate2")]
+ GenericZipWriter::Deflater(w) => try!(w.finish()),
+ #[cfg(feature = "bzip2")]
+ GenericZipWriter::Bzip2(w) => try!(w.finish()),
+ GenericZipWriter::Closed => try!(Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed"))),
+ };
+
+ *self = match compression
+ {
+ CompressionMethod::Stored => GenericZipWriter::Storer(bare),
+ #[cfg(feature = "flate2")]
+ CompressionMethod::Deflated => GenericZipWriter::Deflater(DeflateEncoder::new(bare, flate2::Compression::default())),
+ #[cfg(feature = "bzip2")]
+ CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzEncoder::new(bare, bzip2::Compression::Default)),
+ CompressionMethod::Unsupported(..) => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
+ };
+
+ Ok(())
+ }
+
+ fn ref_mut(&mut self) -> Option<&mut Write> {
+ match *self {
+ GenericZipWriter::Storer(ref mut w) => Some(w as &mut Write),
+ #[cfg(feature = "flate2")]
+ GenericZipWriter::Deflater(ref mut w) => Some(w as &mut Write),
+ #[cfg(feature = "bzip2")]
+ GenericZipWriter::Bzip2(ref mut w) => Some(w as &mut Write),
+ GenericZipWriter::Closed => None,
+ }
+ }
+
+ fn is_closed(&self) -> bool
+ {
+ match *self
+ {
+ GenericZipWriter::Closed => true,
+ _ => false,
+ }
+ }
+
+ fn get_plain(&mut self) -> &mut W
+ {
+ match *self
+ {
+ GenericZipWriter::Storer(ref mut w) => w,
+ _ => panic!("Should have switched to stored beforehand"),
+ }
+ }
+
+ fn current_compression(&self) -> Option<CompressionMethod> {
+ match *self {
+ GenericZipWriter::Storer(..) => Some(CompressionMethod::Stored),
+ #[cfg(feature = "flate2")]
+ GenericZipWriter::Deflater(..) => Some(CompressionMethod::Deflated),
+ #[cfg(feature = "bzip2")]
+ GenericZipWriter::Bzip2(..) => Some(CompressionMethod::Bzip2),
+ GenericZipWriter::Closed => None,
+ }
+ }
+
+ fn unwrap(self) -> W
+ {
+ match self
+ {
+ GenericZipWriter::Storer(w) => w,
+ _ => panic!("Should have switched to stored beforehand"),
+ }
+ }
+}
+
+fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
+{
+ // local file header signature
+ try!(writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE));
+ // version needed to extract
+ try!(writer.write_u16::<LittleEndian>(file.version_needed()));
+ // general purpose bit flag
+ let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
+ try!(writer.write_u16::<LittleEndian>(flag));
+ // Compression method
+ try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
+ // last mod file time and last mod file date
+ let msdos_datetime = try!(file.last_modified_time.to_msdos());
+ try!(writer.write_u16::<LittleEndian>(msdos_datetime.timepart));
+ try!(writer.write_u16::<LittleEndian>(msdos_datetime.datepart));
+ // crc-32
+ try!(writer.write_u32::<LittleEndian>(file.crc32));
+ // compressed size
+ try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
+ // uncompressed size
+ try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
+ // file name length
+ try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
+ // extra field length
+ let extra_field = try!(build_extra_field(file));
+ try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
+ // file name
+ try!(writer.write_all(file.file_name.as_bytes()));
+ // extra field
+ try!(writer.write_all(&extra_field));
+
+ Ok(())
+}
+
+fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
+{
+ const CRC32_OFFSET : u64 = 14;
+ try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
+ try!(writer.write_u32::<LittleEndian>(file.crc32));
+ try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
+ try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
+ Ok(())
+}
+
+fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
+{
+ // central file header signature
+ try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
+ // version made by
+ let version_made_by = (file.system as u16) << 8 | (file.version_made_by as u16);
+ try!(writer.write_u16::<LittleEndian>(version_made_by));
+ // version needed to extract
+ try!(writer.write_u16::<LittleEndian>(file.version_needed()));
+ // general puprose bit flag
+ let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
+ try!(writer.write_u16::<LittleEndian>(flag));
+ // compression method
+ try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
+ // last mod file time + date
+ let msdos_datetime = try!(file.last_modified_time.to_msdos());
+ try!(writer.write_u16::<LittleEndian>(msdos_datetime.timepart));
+ try!(writer.write_u16::<LittleEndian>(msdos_datetime.datepart));
+ // crc-32
+ try!(writer.write_u32::<LittleEndian>(file.crc32));
+ // compressed size
+ try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
+ // uncompressed size
+ try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
+ // file name length
+ try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
+ // extra field length
+ let extra_field = try!(build_extra_field(file));
+ try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
+ // file comment length
+ try!(writer.write_u16::<LittleEndian>(0));
+ // disk number start
+ try!(writer.write_u16::<LittleEndian>(0));
+ // internal file attribytes
+ try!(writer.write_u16::<LittleEndian>(0));
+ // external file attributes
+ try!(writer.write_u32::<LittleEndian>(file.external_attributes));
+ // relative offset of local header
+ try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
+ // file name
+ try!(writer.write_all(file.file_name.as_bytes()));
+ // extra field
+ try!(writer.write_all(&extra_field));
+ // file comment
+ // <none>
+
+ Ok(())
+}
+
+fn build_extra_field(_file: &ZipFileData) -> ZipResult<Vec<u8>>
+{
+ let writer = Vec::new();
+ // Future work
+ Ok(writer)
+}
+
+#[cfg(test)]
+mod test {
+ use std::io;
+ use std::io::Write;
+ use time;
+ use super::{FileOptions, ZipWriter};
+ use compression::CompressionMethod;
+
+ #[test]
+ fn write_empty_zip() {
+ let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
+ let result = writer.finish().unwrap();
+ assert_eq!(result.get_ref().len(), 28);
+ let v: Vec<u8> = vec![80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 122, 105, 112, 45, 114, 115];
+ assert_eq!(result.get_ref(), &v);
+ }
+
+ #[test]
+ fn write_mimetype_zip() {
+ let mut writer = ZipWriter::new(io::Cursor::new(Vec::new()));
+ let mut mtime = time::empty_tm();
+ mtime.tm_year = 80;
+ mtime.tm_mday = 1;
+ let options = FileOptions {
+ compression_method: CompressionMethod::Stored,
+ last_modified_time: mtime,
+ permissions: Some(33188),
+ };
+ writer.start_file("mimetype", options).unwrap();
+ writer.write(b"application/vnd.oasis.opendocument.text").unwrap();
+ let result = writer.finish().unwrap();
+ assert_eq!(result.get_ref().len(), 159);
+ let mut v = Vec::new();
+ v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
+ assert_eq!(result.get_ref(), &v);
+ }
+}
diff --git a/third_party/rust/zip/tests/data/invalid_offset.zip b/third_party/rust/zip/tests/data/invalid_offset.zip
new file mode 100644
index 0000000000..9ff2e07fa8
--- /dev/null
+++ b/third_party/rust/zip/tests/data/invalid_offset.zip
Binary files differ
diff --git a/third_party/rust/zip/tests/data/mimetype.zip b/third_party/rust/zip/tests/data/mimetype.zip
new file mode 100644
index 0000000000..78f98224e8
--- /dev/null
+++ b/third_party/rust/zip/tests/data/mimetype.zip
Binary files differ
diff --git a/third_party/rust/zip/tests/data/zip64_demo.zip b/third_party/rust/zip/tests/data/zip64_demo.zip
new file mode 100644
index 0000000000..f2ceee303e
--- /dev/null
+++ b/third_party/rust/zip/tests/data/zip64_demo.zip
Binary files differ
diff --git a/third_party/rust/zip/tests/invalid_date.rs b/third_party/rust/zip/tests/invalid_date.rs
new file mode 100644
index 0000000000..87c436b57c
--- /dev/null
+++ b/third_party/rust/zip/tests/invalid_date.rs
@@ -0,0 +1,31 @@
+extern crate zip;
+
+use zip::read::ZipArchive;
+use std::io::Cursor;
+
+const BUF : &[u8] = &[
+ 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x12, 0x00, 0x1c, 0x00, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69,
+ 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f,
+ 0x55, 0x54, 0x09, 0x00, 0x03, 0xf4, 0x5c, 0x88, 0x5a, 0xf4, 0x5c, 0x88,
+ 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04,
+ 0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x1e, 0x03, 0x0a, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, // time part: 0 seconds, 0 minutes, 0 hours
+ 0x00, 0x00, // date part: day 0 (invalid), month 0 (invalid), year 0 (1980)
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xed, 0x41, 0x00, 0x00,
+ 0x00, 0x00, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x74, 0x69,
+ 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x55, 0x54, 0x05, 0x00,
+ 0x03, 0xf4, 0x5c, 0x88, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8,
+ 0x03, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00
+];
+
+#[test]
+fn main() {
+ let _archive = ZipArchive::new(Cursor::new(BUF)).unwrap();
+}