diff --git a/Cargo.toml b/Cargo.toml index e2531ce00..792603915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,17 +23,6 @@ build = "src/build.rs" all-features = true rustdoc-args = ["--cfg", "docsrs"] -[workspace] -members = [ - ".", - "cli", - "fuzz", -] -default-members = [ - ".", -] -resolver = "2" - [workspace.dependencies] arbitrary = { version = "1.3.2", features = ["derive"] } time = { version = "0.3.36", default-features = false } @@ -75,7 +64,7 @@ getrandom = { version = "0.2.15", features = ["js", "std"] } walkdir = "2.5.0" time = { workspace = true, features = ["formatting", "macros"] } anyhow = "1" -clap = { version = "4", features = ["derive"] } +clap = { version = "=4.4.18", features = ["derive"] } tempdir = "0.3.7" [features] @@ -115,16 +104,3 @@ harness = false [[bench]] name = "merge_archive" harness = false - -# Reduce the size of the zip-cli binary. -[profile.release] -strip = true -# This is necessary for fuzzing, which can only use dev or release profiles, and breaks if LTO -# is specified. -lto = false -opt-level = "z" - -[profile.release-lto] -inherits = "release" -# This slightly reduces the size of the output binary. -lto = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8e70b38ec..62b415e1e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -15,6 +15,12 @@ Binary for creation and manipulation of zip files. """ edition = "2021" +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[lib] + [[bin]] name = "zip-cli" @@ -23,12 +29,38 @@ clap = { version = "4.5.15", features = ["derive"] } eyre = "0.6" [dependencies.zip] -workspace = true -features = [ +path = ".." +default-features = false + +[features] +aes-crypto = ["zip/aes-crypto"] +bzip2 = ["zip/bzip2"] +chrono = ["zip/chrono"] +deflate64 = ["zip/deflate64"] +deflate = ["zip/deflate"] +deflate-flate2 = ["zip/deflate-flate2"] +deflate-zlib = ["zip/deflate-zlib"] +deflate-zlib-ng = ["zip/deflate-zlib-ng"] +deflate-zopfli = ["zip/deflate-zopfli"] +lzma = ["zip/lzma"] +time = ["zip/time"] +xz = ["zip/xz"] +zstd = ["zip/zstd"] +default = [ + "aes-crypto", "bzip2", "deflate64", "deflate", "lzma", - "zstd", + "time", "xz", + "zstd", ] + + +# Reduce the size of the zip-cli binary. +[profile.release] +strip = true +lto = true +opt-level = 3 +codegen-units = 1 diff --git a/cli/clite/Cargo.toml b/cli/clite/Cargo.toml new file mode 100644 index 000000000..25a019119 --- /dev/null +++ b/cli/clite/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "zip-clite" +version = "0.0.1" +authors = [ + "Danny McClanahan ", +] +license = "MIT" +repository = "https://github.com/zip-rs/zip2.git" +keywords = ["zip", "archive", "compression", "cli"] +categories = ["command-line-utilities", "compression", "filesystem", "development-tools::build-utils"] +# Keep this up to date with clap! +rust-version = "1.74.0" +description = """ +Binary for creation and manipulation of zip files. +""" +edition = "2021" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "zip-clite" + +[dependencies] +clap = { version = "4.5.15", features = ["derive"] } +eyre = "0.6" + +[dependencies.zip-cli] +path = ".." +default-features = false +features = ["deflate-flate2", "deflate-zlib"] + +# Reduce the size of the zip-cli binary. +[profile.release] +strip = true +lto = true +opt-level = "s" +codegen-units = 1 diff --git a/cli/clite/src/main.rs b/cli/clite/src/main.rs new file mode 100644 index 000000000..af2067fea --- /dev/null +++ b/cli/clite/src/main.rs @@ -0,0 +1,28 @@ +use std::io; + +use clap::{error::ErrorKind, Parser}; +use eyre::Report; + +use zip_cli::args::*; +use zip_cli::compress::execute_compress; +use zip_cli::ErrHandle; + +fn main() -> Result<(), Report> { + let ZipCli { verbose, command } = match ZipCli::try_parse() { + Ok(args) => args, + Err(e) => match e.kind() { + ErrorKind::Format | ErrorKind::Io | ErrorKind::InvalidUtf8 => return Err(e.into()), + _ => e.exit(), + }, + }; + let mut err = if verbose { + ErrHandle::Output(io::stderr()) + } else { + ErrHandle::NoOutput + }; + + match command { + ZipCommand::Info | ZipCommand::Extract => Ok(()), + ZipCommand::Compress(compress) => execute_compress(&mut err, compress), + } +} diff --git a/cli/src/args.rs b/cli/src/args.rs index 222c9f5b0..7fdcb1d01 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -33,12 +33,15 @@ pub enum CompressionMethodArg { /// uncompressed Stored, /// with deflate (default) - Deflate, + Deflate, /* requires having zip/_deflate-any set to compile */ /// with deflate64 + #[cfg(feature = "deflate64")] Deflate64, /// with bzip2 + #[cfg(feature = "bzip2")] Bzip2, /// with zstd + #[cfg(feature = "zstd")] Zstd, } diff --git a/cli/src/compress.rs b/cli/src/compress.rs index 71ca66245..a85ad2d8d 100644 --- a/cli/src/compress.rs +++ b/cli/src/compress.rs @@ -55,9 +55,9 @@ fn enter_recursive_dir_entries( .unwrap_or_else(|| path_to_string(root).into()) .trim_end_matches('/') .to_string(); - write!( + writeln!( err, - "writing top-level directory entry for {base_dirname:?}\n" + "writing top-level directory entry for {base_dirname:?}" )?; writer.add_directory(&base_dirname, options)?; @@ -78,29 +78,26 @@ fn enter_recursive_dir_entries( let file_type = dir_entry.file_type()?; if file_type.is_symlink() { let target: String = path_to_string(fs::read_link(dir_entry.path())?).into(); - write!( + writeln!( err, - "writing recursive symlink entry with name {full_path:?} and target {target:?}\n" + "writing recursive symlink entry with name {full_path:?} and target {target:?}" )?; writer.add_symlink(full_path, target, options)?; } else if file_type.is_file() { - write!( - err, - "writing recursive file entry with name {full_path:?}\n" - )?; + writeln!(err, "writing recursive file entry with name {full_path:?}")?; writer.start_file(full_path, options)?; let mut f = fs::File::open(dir_entry.path())?; io::copy(&mut f, writer)?; } else { assert!(file_type.is_dir()); - write!( + writeln!( err, - "writing recursive directory entry with name {full_path:?}\n" + "writing recursive directory entry with name {full_path:?}" )?; writer.add_directory(full_path, options)?; - write!( + writeln!( err, - "adding subdirectories depth-first for recursive directory entry {entry_basename:?}\n" + "adding subdirectories depth-first for recursive directory entry {entry_basename:?}" )?; let new_readdir = fs::read_dir(dir_entry.path())?; readdir_stack.push((new_readdir, entry_basename)); @@ -123,13 +120,13 @@ pub fn execute_compress( let out = match output_path { Some(path) => { - write!(err, "writing compressed zip to output file path {path:?}\n")?; + writeln!(err, "writing compressed zip to output file path {path:?}")?; OutputHandle::File(fs::File::create(path)?) } None => { - write!( + writeln!( err, - "writing to stdout and buffering compressed zip in memory\n" + "writing to stdout and buffering compressed zip in memory" )?; if io::stdout().is_terminal() && !allow_stdout { return Err(eyre!("stdout is a tty, but --stdout was not set")); @@ -142,7 +139,7 @@ pub fn execute_compress( let mut options = SimpleFileOptions::default() .compression_method(CompressionMethod::Deflated) .large_file(false); - write!(err, "default zip entry options: {options:?}\n")?; + writeln!(err, "default zip entry options: {options:?}")?; let mut last_name: Option = None; let mut symlink_flag: bool = false; @@ -152,27 +149,30 @@ pub fn execute_compress( let method = match method { CompressionMethodArg::Stored => CompressionMethod::Stored, CompressionMethodArg::Deflate => CompressionMethod::Deflated, + #[cfg(feature = "deflate64")] CompressionMethodArg::Deflate64 => CompressionMethod::Deflate64, + #[cfg(feature = "bzip2")] CompressionMethodArg::Bzip2 => CompressionMethod::Bzip2, + #[cfg(feature = "zstd")] CompressionMethodArg::Zstd => CompressionMethod::Zstd, }; - write!(err, "setting compression method {method:?}\n")?; + writeln!(err, "setting compression method {method:?}")?; options = options.compression_method(method); } CompressionArg::Level(CompressionLevel(level)) => { - write!(err, "setting compression level {level:?}\n")?; + writeln!(err, "setting compression level {level:?}")?; options = options.compression_level(Some(level)); } CompressionArg::Mode(Mode(mode)) => { - write!(err, "setting file mode {mode:#o}\n")?; + writeln!(err, "setting file mode {mode:#o}")?; options = options.unix_permissions(mode); } CompressionArg::LargeFile(large_file) => { - write!(err, "setting large file flag to {large_file:?}\n")?; + writeln!(err, "setting large file flag to {large_file:?}")?; options = options.large_file(large_file); } CompressionArg::Name(name) => { - write!(err, "setting name of next entry to {name:?}\n")?; + writeln!(err, "setting name of next entry to {name:?}")?; if let Some(last_name) = last_name { return Err(eyre!( "got two names before an entry: {last_name} and {name}" @@ -181,7 +181,7 @@ pub fn execute_compress( last_name = Some(name); } CompressionArg::Dir => { - write!(err, "writing dir entry\n")?; + writeln!(err, "writing dir entry")?; if symlink_flag { return Err(eyre!("symlink flag provided before dir entry with ")); } @@ -191,7 +191,7 @@ pub fn execute_compress( writer.add_directory(dirname, options)?; } CompressionArg::Symlink => { - write!(err, "setting symlink flag for next entry\n")?; + writeln!(err, "setting symlink flag for next entry")?; if symlink_flag { /* TODO: make this a warning? */ return Err(eyre!("symlink flag provided twice before entry")); @@ -207,18 +207,18 @@ pub fn execute_compress( let target = data .into_string() .map_err(|target| eyre!("failed to decode symlink target {target:?}"))?; - write!( + writeln!( err, - "writing immediate symlink entry with name {name:?} and target {target:?}\n" + "writing immediate symlink entry with name {name:?} and target {target:?}" )?; /* TODO: .add_symlink() should support OsString targets! */ writer.add_symlink(name, target, options)?; symlink_flag = false; } else { /* This is a file entry. */ - write!( + writeln!( err, - "writing immediate file entry with name {name:?} and data {data:?}\n" + "writing immediate file entry with name {name:?} and data {data:?}" )?; let data = data.into_encoded_bytes(); writer.start_file(name, options)?; @@ -232,14 +232,14 @@ pub fn execute_compress( if symlink_flag { /* This is a symlink entry. */ let target: String = path_to_string(fs::read_link(&path)?).into(); - write!(err, "writing symlink entry from path {path:?} with name {name:?} and target {target:?}\n")?; + writeln!(err, "writing symlink entry from path {path:?} with name {name:?} and target {target:?}")?; writer.add_symlink(name, target, options)?; symlink_flag = false; } else { /* This is a file entry. */ - write!( + writeln!( err, - "writing file entry from path {path:?} with name {name:?}\n" + "writing file entry from path {path:?} with name {name:?}" )?; writer.start_file(name, options)?; let mut f = fs::File::open(path)?; @@ -250,9 +250,9 @@ pub fn execute_compress( if symlink_flag { return Err(eyre!("symlink flag provided before recursive dir entry")); } - write!( + writeln!( err, - "writing recursive dir entries for path {r:?} with name {last_name:?}\n" + "writing recursive dir entries for path {r:?} with name {last_name:?}" )?; enter_recursive_dir_entries(err, last_name.take(), &r, &mut writer, options)?; } @@ -272,21 +272,21 @@ pub fn execute_compress( let file_type = fs::symlink_metadata(&pos_arg)?.file_type(); if file_type.is_symlink() { let target = fs::read_link(&pos_arg)?; - write!( + writeln!( err, - "writing positional symlink entry with path {pos_arg:?} and target {target:?}\n" + "writing positional symlink entry with path {pos_arg:?} and target {target:?}" )?; writer.add_symlink_from_path(pos_arg, target, options)?; } else if file_type.is_file() { - write!(err, "writing positional file entry with path {pos_arg:?}\n")?; + writeln!(err, "writing positional file entry with path {pos_arg:?}")?; writer.start_file_from_path(&pos_arg, options)?; let mut f = fs::File::open(pos_arg)?; io::copy(&mut f, &mut writer)?; } else { assert!(file_type.is_dir()); - write!( + writeln!( err, - "writing positional recursive dir entry for {pos_arg:?}\n" + "writing positional recursive dir entry for {pos_arg:?}" )?; enter_recursive_dir_entries(err, None, &pos_arg, &mut writer, options)?; } diff --git a/cli/src/lib.rs b/cli/src/lib.rs new file mode 100755 index 000000000..58c2517fb --- /dev/null +++ b/cli/src/lib.rs @@ -0,0 +1,33 @@ +//! ??? + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +/* #![warn(missing_docs)] */ + +use std::io; + +pub mod args; +pub mod compress; + +pub enum ErrHandle { + Output(W), + NoOutput, +} + +impl io::Write for ErrHandle +where + W: io::Write, +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + match self { + Self::Output(w) => w.write(buf), + Self::NoOutput => Ok(0), + } + } + + fn flush(&mut self) -> io::Result<()> { + match self { + Self::Output(w) => w.flush(), + Self::NoOutput => Ok(()), + } + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 5d62588d4..af2067fea 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -3,35 +3,9 @@ use std::io; use clap::{error::ErrorKind, Parser}; use eyre::Report; -mod args; -use args::*; - -mod compress; -use compress::execute_compress; - -pub enum ErrHandle { - Output(W), - NoOutput, -} - -impl io::Write for ErrHandle -where - W: io::Write, -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - match self { - Self::Output(w) => w.write(buf), - Self::NoOutput => Ok(0), - } - } - - fn flush(&mut self) -> io::Result<()> { - match self { - Self::Output(w) => w.flush(), - Self::NoOutput => Ok(()), - } - } -} +use zip_cli::args::*; +use zip_cli::compress::execute_compress; +use zip_cli::ErrHandle; fn main() -> Result<(), Report> { let ZipCli { verbose, command } = match ZipCli::try_parse() { diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index fb7b99ae0..be1552cd0 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,17 +9,23 @@ edition = "2018" cargo-fuzz = true [dependencies] -zip.workspace = true - libfuzzer-sys = "0.4" -arbitrary.workspace = true +arbitrary = { version = "1.3.2", features = ["derive"] } replace_with = "0.1.7" tikv-jemallocator = "0.6.0" +[dependencies.zip] +path = ".." +default-features = false + [features] zip_defaults = ["zip/default"] default = ["zip_defaults"] +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + [[bin]] name = "fuzz_read" path = "fuzz_targets/fuzz_read.rs"