extern crate brotli;
extern crate core;
use std::env;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::fs::File;
use brotli::concat::{BroCatli, BroCatliResult};
fn usage() {
writeln!(&mut ::std::io::stderr(), "Usage: [-w<window_size>] filename0 filename1 filename2...").unwrap();
}
fn read_no_interrupt<R:Read>(r: &mut R, buf: &mut [u8]) -> Result<usize, io::Error> {
loop {
match r.read(buf) {
Err(e) => {
match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => return Err(e),
}
}
Ok(cur_read) => return Ok(cur_read),
}
}
}
fn write_no_interrupt<W:Write>(w: &mut W, mut buf: &[u8]) -> Result<usize, io::Error> {
let mut total_read = 0usize;
loop {
match w.write(buf) {
Err(e) => {
match e.kind() {
io::ErrorKind::Interrupted => continue,
_ => return Err(e),
}
},
Ok(cur_read) => {
buf = &buf[cur_read..];
total_read += cur_read;
if buf.len() == 0 {
return Ok(total_read);
}
},
}
}
}
fn main() {
let mut window_size: Option<u8> = None;
let mut double_dash = false;
let mut buffer_size = 4096usize;
let mut filenames = Vec::<String>::new();
let mut ostream = io::stdout();
if env::args_os().len() > 1 {
for argument in env::args().skip(1) {
if argument.starts_with("-w") && !double_dash {
window_size = Some(argument.trim_matches('-').trim_matches('w').parse::<i32>().unwrap() as u8);
continue;
}
if argument.starts_with("-bs") && !double_dash {
buffer_size = argument.trim_matches('-').trim_matches('b').trim_matches('s').parse::<usize>().unwrap();
continue;
}
if argument == "--" {
double_dash = true;
continue;
}
filenames.push(argument);
}
} else {
usage();
return;
}
let mut ibuffer = vec![0u8; buffer_size];
let mut obuffer = vec![0u8; buffer_size];
let mut ooffset = 0;
let mut ioffset;
let mut bro_cat_li = match window_size {
Some(ws) => BroCatli::new_with_window_size(ws),
None => BroCatli::new(),
};
for filename in filenames {
bro_cat_li.new_brotli_file();
let mut input_file = match File::open(&Path::new(&filename)) {
Err(why) => panic!("couldn't open {:}\n{:}", filename, why),
Ok(file) => file,
};
loop {
ioffset = 0;
match read_no_interrupt(&mut input_file, &mut ibuffer[..]) {
Err(e) => panic!(e),
Ok(cur_read) => {
if cur_read == 0 {
break;
}
loop {
match bro_cat_li.stream(&ibuffer[..cur_read], &mut ioffset,
&mut obuffer[..], &mut ooffset) {
BroCatliResult::NeedsMoreOutput => {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {assert_eq!(count, ooffset);},
}
ooffset = 0;
},
BroCatliResult::NeedsMoreInput => {
break;
},
BroCatliResult::Success => {
panic!("Unexpected state: Success when streaming before finish");
}
failure => {
panic!("Failed to concatenate files on {:} {:?}", filename, failure);
},
}
}
}
}
}
}
loop {
match bro_cat_li.finish(&mut obuffer[..], &mut ooffset) {
BroCatliResult::NeedsMoreOutput => {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {assert_eq!(count, ooffset);},
}
ooffset = 0;
},
BroCatliResult::NeedsMoreInput => {
panic!("Unexpected EOF");
},
BroCatliResult::Success => {
if ooffset != 0 {
match write_no_interrupt(&mut ostream, &obuffer[..ooffset]) {
Err(why) => panic!("couldn't write: {:}", why),
Ok(count) => {assert_eq!(count, ooffset);},
}
}
break;
}
failure => {
panic!(failure)
}
}
}
}