use std::any::Any; use std::default::Default; use std::error::Error; use std::ffi::CString; use std::io::{self, Read}; use std::mem; use std::path::Path; use std::ptr; use std::slice; use libc::{c_void, ssize_t}; use libarchive3_sys::ffi; use archive::{Entry, ReadCompression, ReadFilter, ReadFormat, Handle}; use error::{ArchiveResult, ArchiveError}; const BLOCK_SIZE: usize = 10240; unsafe extern "C" fn stream_read_callback(handle: *mut ffi::Struct_archive, data: *mut c_void, buff: *mut *const c_void) -> ssize_t { let pipe: &mut Pipe = &mut *(data as *mut Pipe); *buff = pipe.buffer.as_mut_ptr() as *mut c_void; match pipe.read_bytes() { Ok(size) => size as ssize_t, Err(e) => { let desc = CString::new(e.description()).unwrap(); ffi::archive_set_error(handle, e.raw_os_error().unwrap_or(0), desc.as_ptr()); -1 as ssize_t } } } pub trait Reader : Handle { fn entry(&mut self) -> &mut ReaderEntry; fn header_position(&self) -> i64 { unsafe { ffi::archive_read_header_position(self.handle()) } } fn next_header(&mut self) -> Option<&mut ReaderEntry> { let res = unsafe { ffi::archive_read_next_header(self.handle(), &mut self.entry().handle) }; if res == 0 { Some(self.entry()) } else { None } } fn read_block(&self) -> ArchiveResult> { let mut buff = ptr::null(); let mut size = 0; let mut offset = 0; unsafe { match ffi::archive_read_data_block(self.handle(), &mut buff, &mut size, &mut offset) { ffi::ARCHIVE_EOF => Ok(None), ffi::ARCHIVE_OK => Ok(Some(slice::from_raw_parts(buff as *const u8, size))), _ => Err(ArchiveError::Sys(self.err_code(), self.err_msg())), } } } } pub struct FileReader { handle: *mut ffi::Struct_archive, entry: ReaderEntry, } pub struct StreamReader { handle: *mut ffi::Struct_archive, entry: ReaderEntry, _pipe: Box, } pub struct Builder { handle: *mut ffi::Struct_archive, consumed: bool, } pub struct ReaderEntry { handle: *mut ffi::Struct_archive_entry, } struct Pipe { reader: Box, buffer: Vec, } impl Pipe { fn new(src: T) -> Self { Pipe { reader: Box::new(src), buffer: vec![0; 8192], } } fn read_bytes(&mut self) -> io::Result { self.reader.read(&mut self.buffer[..]) } } impl FileReader { pub fn open>(mut builder: Builder, file: T) -> ArchiveResult { try!(builder.check_consumed()); let c_file = CString::new(file.as_ref().to_string_lossy().as_bytes()).unwrap(); unsafe { match ffi::archive_read_open_filename(builder.handle(), c_file.as_ptr(), BLOCK_SIZE) { ffi::ARCHIVE_OK => { builder.consume(); Ok(Self::new(builder.handle())) } _ => Err(ArchiveError::from(&builder as &Handle)), } } } fn new(handle: *mut ffi::Struct_archive) -> Self { FileReader { handle: handle, entry: ReaderEntry::default(), } } } impl Handle for FileReader { unsafe fn handle(&self) -> *mut ffi::Struct_archive { self.handle } } impl Reader for FileReader { fn entry(&mut self) -> &mut ReaderEntry { &mut self.entry } } impl Drop for FileReader { fn drop(&mut self) { unsafe { ffi::archive_read_close(self.handle()); // jw todo: close here? ffi::archive_read_free(self.handle()); } } } impl StreamReader { pub fn open(mut builder: Builder, src: T) -> ArchiveResult { unsafe { let mut pipe = Box::new(Pipe::new(src)); let pipe_ptr: *mut c_void = &mut *pipe as *mut Pipe as *mut c_void; match ffi::archive_read_open(builder.handle(), pipe_ptr, None, Some(stream_read_callback), None) { ffi::ARCHIVE_OK => { let reader = StreamReader { handle: builder.handle(), entry: ReaderEntry::default(), _pipe: pipe, }; builder.consume(); Ok(reader) } _ => { builder.consume(); Err(ArchiveError::from(&builder as &Handle)) } } } } } impl Handle for StreamReader { unsafe fn handle(&self) -> *mut ffi::Struct_archive { self.handle } } impl Reader for StreamReader { fn entry(&mut self) -> &mut ReaderEntry { &mut self.entry } } impl Drop for StreamReader { fn drop(&mut self) { unsafe { ffi::archive_read_close(self.handle()); // jw todo: close here? ffi::archive_read_free(self.handle()); } } } impl Builder { pub fn new() -> Self { Builder::default() } pub fn support_compression(&mut self, compression: ReadCompression) -> ArchiveResult<()> { let result = match compression { ReadCompression::All => unsafe { ffi::archive_read_support_compression_all(self.handle) }, ReadCompression::Bzip2 => unsafe { ffi::archive_read_support_compression_bzip2(self.handle) }, ReadCompression::Compress => unsafe { ffi::archive_read_support_compression_compress(self.handle) }, ReadCompression::Gzip => unsafe { ffi::archive_read_support_compression_gzip(self.handle) }, ReadCompression::Lzip => unsafe { ffi::archive_read_support_compression_lzip(self.handle) }, ReadCompression::Lzma => unsafe { ffi::archive_read_support_compression_lzma(self.handle) }, ReadCompression::None => unsafe { ffi::archive_read_support_compression_none(self.handle) }, ReadCompression::Program(prog) => { let c_prog = CString::new(prog).unwrap(); unsafe { ffi::archive_read_support_compression_program(self.handle, c_prog.as_ptr()) } } ReadCompression::Rpm => unsafe { ffi::archive_read_support_compression_rpm(self.handle) }, ReadCompression::Uu => unsafe { ffi::archive_read_support_compression_uu(self.handle) }, ReadCompression::Xz => unsafe { ffi::archive_read_support_compression_xz(self.handle) }, }; match result { ffi::ARCHIVE_OK => Ok(()), _ => ArchiveResult::from(self as &Handle), } } pub fn support_filter(&mut self, filter: ReadFilter) -> ArchiveResult<()> { let result = match filter { ReadFilter::All => unsafe { ffi::archive_read_support_filter_all(self.handle) }, ReadFilter::Bzip2 => unsafe { ffi::archive_read_support_filter_bzip2(self.handle) }, ReadFilter::Compress => unsafe { ffi::archive_read_support_filter_compress(self.handle) }, ReadFilter::Grzip => unsafe { ffi::archive_read_support_filter_grzip(self.handle) }, ReadFilter::Gzip => unsafe { ffi::archive_read_support_filter_gzip(self.handle) }, ReadFilter::Lrzip => unsafe { ffi::archive_read_support_filter_lrzip(self.handle) }, ReadFilter::Lzip => unsafe { ffi::archive_read_support_filter_lzip(self.handle) }, ReadFilter::Lzma => unsafe { ffi::archive_read_support_filter_lzma(self.handle) }, ReadFilter::Lzop => unsafe { ffi::archive_read_support_filter_lzop(self.handle) }, ReadFilter::None => unsafe { ffi::archive_read_support_filter_none(self.handle) }, ReadFilter::Program(prog) => { let c_prog = CString::new(prog).unwrap(); unsafe { ffi::archive_read_support_filter_program(self.handle, c_prog.as_ptr()) } } ReadFilter::ProgramSignature(prog, cb, size) => { let c_prog = CString::new(prog).unwrap(); unsafe { ffi::archive_read_support_filter_program_signature(self.handle, c_prog.as_ptr(), mem::transmute(cb), size) } } ReadFilter::Rpm => unsafe { ffi::archive_read_support_filter_rpm(self.handle) }, ReadFilter::Uu => unsafe { ffi::archive_read_support_filter_uu(self.handle) }, ReadFilter::Xz => unsafe { ffi::archive_read_support_filter_xz(self.handle) }, }; match result { ffi::ARCHIVE_OK => Ok(()), _ => ArchiveResult::from(self as &Handle), } } pub fn support_format(&self, format: ReadFormat) -> ArchiveResult<()> { let result = match format { ReadFormat::SevenZip => unsafe { ffi::archive_read_support_format_7zip(self.handle()) }, ReadFormat::All => unsafe { ffi::archive_read_support_format_all(self.handle()) }, ReadFormat::Ar => unsafe { ffi::archive_read_support_format_ar(self.handle()) }, ReadFormat::Cab => unsafe { ffi::archive_read_support_format_cab(self.handle()) }, ReadFormat::Cpio => unsafe { ffi::archive_read_support_format_cpio(self.handle()) }, ReadFormat::Empty => unsafe { ffi::archive_read_support_format_empty(self.handle()) }, ReadFormat::Gnutar => unsafe { ffi::archive_read_support_format_gnutar(self.handle()) }, ReadFormat::Iso9660 => unsafe { ffi::archive_read_support_format_iso9660(self.handle()) }, ReadFormat::Lha => unsafe { ffi::archive_read_support_format_lha(self.handle()) }, ReadFormat::Mtree => unsafe { ffi::archive_read_support_format_mtree(self.handle()) }, ReadFormat::Rar => unsafe { ffi::archive_read_support_format_rar(self.handle()) }, ReadFormat::Raw => unsafe { ffi::archive_read_support_format_raw(self.handle()) }, ReadFormat::Tar => unsafe { ffi::archive_read_support_format_tar(self.handle()) }, ReadFormat::Xar => unsafe { ffi::archive_read_support_format_xar(self.handle()) }, ReadFormat::Zip => unsafe { ffi::archive_read_support_format_zip(self.handle()) }, }; match result { ffi::ARCHIVE_OK => Ok(()), _ => ArchiveResult::from(self as &Handle), } } pub fn open_file>(self, file: T) -> ArchiveResult { try!(self.check_consumed()); FileReader::open(self, file) } pub fn open_stream(self, src: T) -> ArchiveResult { try!(self.check_consumed()); StreamReader::open(self, src) } fn check_consumed(&self) -> ArchiveResult<()> { if self.consumed { Err(ArchiveError::Consumed) } else { Ok(()) } } fn consume(&mut self) { self.consumed = true; } } impl Handle for Builder { unsafe fn handle(&self) -> *mut ffi::Struct_archive { self.handle } } impl Drop for Builder { fn drop(&mut self) { if !self.consumed { unsafe { ffi::archive_read_free(self.handle); } } } } impl Default for Builder { fn default() -> Self { unsafe { let handle = ffi::archive_read_new(); if handle.is_null() { panic!("Allocation error"); } Builder { handle: handle, consumed: false, } } } } impl ReaderEntry { pub fn new(handle: *mut ffi::Struct_archive_entry) -> Self { ReaderEntry { handle: handle } } } impl Default for ReaderEntry { fn default() -> Self { ReaderEntry { handle: ptr::null_mut() } } } impl Entry for ReaderEntry { unsafe fn entry(&self) -> *mut ffi::Struct_archive_entry { self.handle } }