#[macro_use]
extern crate clap;
use std::fs::OpenOptions;
use std::io::{self, Read, Write};
use anyhow::{Context, Result};
use memmap::MmapMut;
use uclcli::{decompress, decompress_into_buffer, ucl_init};
const DEFAULT_BUFFER_SIZE: u32 = 512 * 1024 * 1024;
fn main() -> Result<()> {
let matches = clap_app!(unucl =>
(version: "0.1")
(author: "Kjell Braden <kjell.braden@bmw.de>")
(about: "libucl (NRV) decompressor")
(@arg INPUT: -i --input [FILE] "Sets the input file to use [defaults to stdin]")
(@arg OUTPUT: -o --output [FILE] "Sets the output file to use [defaults to stdout]")
(@arg bufsize: -b --buffersize [SIZE] "Sets the decompression buffer size - set this if you know how much data to expect after decompression [defaults to 512MB]")
)
.get_matches();
ucl_init();
let buffer_size = matches
.value_of("bufsize")
.map(|x| x.parse::<u32>().context("failed to parse --buffersize"))
.unwrap_or(Ok(DEFAULT_BUFFER_SIZE))?;
let mut input: Box<dyn Read> = match matches.value_of("INPUT") {
Some(path) => Box::new(
OpenOptions::new()
.read(true)
.open(&path)
.context("could not open input file")?,
),
None => Box::new(io::stdin()),
};
let mut inbuffer = Vec::new();
input.read_to_end(&mut inbuffer)?;
let output_filename = matches.value_of("OUTPUT");
match output_filename {
Some(path) => {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.context("could not create output file")?;
file.set_len(buffer_size.into())
.context("could not resize output file")?;
let numbytes = unsafe {
let mut mmap = MmapMut::map_mut(&file).context("failed to map output file")?;
let nb =
decompress_into_buffer(&inbuffer, &mut mmap).context("decompression failed")?;
mmap.flush().context("failed to write output")?;
nb
};
file.set_len(numbytes.into())
.context("failed to truncate output file")?;
}
None => {
let dst = decompress(&inbuffer, buffer_size).context("decompression failed")?;
io::stdout().write_all(&dst)?;
}
}
Ok(())
}