From 89940dd60636ec3e5ba84d734724a2867ea34c1a Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 17 Oct 2020 20:50:29 +0300 Subject: [PATCH] cli-docs: compress included text --- Cargo.lock | 1 + Cargo.toml | 4 +++- build.rs | 32 ++++++++++++++++++++++--------- src/bin.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9be4948e..616c34d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1061,6 +1061,7 @@ dependencies = [ "bincode", "bitflags 1.2.1", "crossbeam", + "flate2", "futures", "indexmap", "libc", diff --git a/Cargo.toml b/Cargo.toml index d7dd6328..1e73817a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,11 +57,13 @@ svg_crate = { version = "0.8.0", optional = true, package = "svg" } futures = "0.3.5" async-task = "3.0.0" num_cpus = "1.12.0" +flate2 = { version = "1.0.16", optional = true } [build-dependencies] syn = { version = "1.0.31", features = [] } quote = "^1.0" proc-macro2 = "1.0.18" +flate2 = { version = "1.0.16", optional = true } [profile.release] lto = "fat" @@ -80,7 +82,7 @@ sqlite3 = ["melib/sqlite3"] smtp = ["melib/smtp"] regexp = ["pcre2"] dbus-notifications = ["notify-rust",] -cli-docs = [] +cli-docs = ["flate2"] svgscreenshot = ["svg_crate"] gpgme = ["melib/gpgme"] diff --git a/build.rs b/build.rs index d7190f9d..968b1c9c 100644 --- a/build.rs +++ b/build.rs @@ -37,6 +37,8 @@ fn main() { ]); #[cfg(feature = "cli-docs")] { + use flate2::Compression; + use flate2::GzBuilder; const MANDOC_OPTS: &[&'static str] = &["-T", "utf8", "-I", "os=Generated by mandoc(1)"]; use std::env; use std::fs::File; @@ -45,7 +47,7 @@ fn main() { use std::process::Command; let out_dir = env::var("OUT_DIR").unwrap(); let mut out_dir_path = Path::new(&out_dir).to_path_buf(); - out_dir_path.push("meli.txt"); + out_dir_path.push("meli.txt.gz"); let output = Command::new("mandoc") .args(MANDOC_OPTS) @@ -54,11 +56,15 @@ fn main() { .or_else(|_| Command::new("man").arg("-l").arg("docs/meli.1").output()) .unwrap(); - let mut file = File::create(&out_dir_path).unwrap(); - file.write_all(&output.stdout).unwrap(); + let file = File::create(&out_dir_path).unwrap(); + let mut gz = GzBuilder::new() + .comment(output.stdout.len().to_string().into_bytes()) + .write(file, Compression::default()); + gz.write_all(&output.stdout).unwrap(); + gz.finish().unwrap(); out_dir_path.pop(); - out_dir_path.push("meli.conf.txt"); + out_dir_path.push("meli.conf.txt.gz"); let output = Command::new("mandoc") .args(MANDOC_OPTS) .arg("docs/meli.conf.5") @@ -70,11 +76,15 @@ fn main() { .output() }) .unwrap(); - let mut file = File::create(&out_dir_path).unwrap(); - file.write_all(&output.stdout).unwrap(); + let file = File::create(&out_dir_path).unwrap(); + let mut gz = GzBuilder::new() + .comment(output.stdout.len().to_string().into_bytes()) + .write(file, Compression::default()); + gz.write_all(&output.stdout).unwrap(); + gz.finish().unwrap(); out_dir_path.pop(); - out_dir_path.push("meli-themes.txt"); + out_dir_path.push("meli-themes.txt.gz"); let output = Command::new("mandoc") .args(MANDOC_OPTS) .arg("docs/meli-themes.5") @@ -86,7 +96,11 @@ fn main() { .output() }) .unwrap(); - let mut file = File::create(&out_dir_path).unwrap(); - file.write_all(&output.stdout).unwrap(); + let file = File::create(&out_dir_path).unwrap(); + let mut gz = GzBuilder::new() + .comment(output.stdout.len().to_string().into_bytes()) + .write(file, Compression::default()); + gz.write_all(&output.stdout).unwrap(); + gz.finish().unwrap(); } } diff --git a/src/bin.rs b/src/bin.rs index fb4957b6..a6a9f125 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -150,7 +150,7 @@ fn parse_manpage(src: &str) -> Result { use structopt::StructOpt; -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] /// Choose manpage enum ManPages { /// meli(1) @@ -206,6 +206,9 @@ enum SubCommand { struct ManOpt { #[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))] page: ManPages, + /// If true, output text in stdout instead of spawning $PAGER. + #[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")] + no_raw: Option>, } fn main() { @@ -251,13 +254,51 @@ fn run_app(opt: Opt) -> Result<()> { } #[cfg(feature = "cli-docs")] Some(SubCommand::Man(manopt)) => { - let _page = manopt.page; - const MANPAGES: [&'static str; 3] = [ - include_str!(concat!(env!("OUT_DIR"), "/meli.txt")), - include_str!(concat!(env!("OUT_DIR"), "/meli.conf.txt")), - include_str!(concat!(env!("OUT_DIR"), "/meli-themes.txt")), + let ManOpt { page, no_raw } = manopt; + const MANPAGES: [&'static [u8]; 3] = [ + include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")), + include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")), + include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")), ]; - println!("{}", MANPAGES[_page as usize]); + use flate2::bufread::GzDecoder; + use std::io::prelude::*; + let mut gz = GzDecoder::new(MANPAGES[page as usize]); + let mut v = String::with_capacity( + str::parse::(unsafe { + std::str::from_utf8_unchecked(gz.header().unwrap().comment().unwrap()) + }) + .expect(&format!( + "{:?} was not compressed with size comment header", + page + )), + ); + gz.read_to_string(&mut v)?; + + if let Some(no_raw) = no_raw { + match no_raw { + Some(true) => {} + None if (unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }) => {} + Some(false) | None => { + println!("{}", &v); + return Ok(()); + } + } + } else { + if unsafe { libc::isatty(libc::STDOUT_FILENO) != 1 } { + println!("{}", &v); + return Ok(()); + } + } + + use std::process::{Command, Stdio}; + let mut handle = Command::new(std::env::var("PAGER").unwrap_or("more".to_string())) + .stdin(Stdio::piped()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn()?; + handle.stdin.take().unwrap().write_all(v.as_bytes())?; + handle.wait()?; + return Ok(()); } #[cfg(not(feature = "cli-docs"))]