use flate2::{Compression, GzBuilder};
use flate2::read::{GzDecoder};
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::io::{Read, SeekFrom, Write};
use std::path::PathBuf;
use std::ptr;
use digest::DigestData;
use errors::Result;
use io::{IoProvider, IoStack, InputFeatures, InputHandle, OpenResult, OutputHandle};
use status::StatusBackend;
use self::file_format::{format_to_extension, FileFormat};
mod c_api;
mod file_format;
pub mod io_api;
pub mod kpse_api;
pub mod md5_api;
pub mod tex;
pub mod xdvipdfmx;
pub mod bibtex;
pub use self::tex::TexEngine;
pub use self::xdvipdfmx::XdvipdfmxEngine;
pub use self::bibtex::BibtexEngine;
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
pub enum AccessPattern {
Read,
Written,
ReadThenWritten,
WrittenThenRead,
}
#[derive(Clone,Debug,Eq,PartialEq)]
pub struct FileSummary {
pub access_pattern: AccessPattern,
pub read_digest: Option<DigestData>,
pub write_digest: Option<DigestData>,
}
impl FileSummary {
pub fn new(access_pattern: AccessPattern) -> FileSummary {
FileSummary {
access_pattern: access_pattern,
read_digest: None,
write_digest: None,
}
}
}
struct ExecutionState<'a, I: 'a + IoProvider> {
io: &'a mut I,
summaries: Option<&'a mut HashMap<OsString, FileSummary>>,
status: &'a mut StatusBackend,
input_handles: Vec<Box<InputHandle>>,
output_handles: Vec<Box<OutputHandle>>,
}
impl<'a, I: 'a + IoProvider> ExecutionState<'a, I> {
pub fn new (io: &'a mut I, summaries: Option<&'a mut HashMap<OsString, FileSummary>>,
status: &'a mut StatusBackend) -> ExecutionState<'a, I> {
ExecutionState {
io: io,
summaries: summaries,
status: status,
output_handles: Vec::new(),
input_handles: Vec::new(),
}
}
fn input_open_name_format(&mut self, name: &OsStr, format: FileFormat) -> OpenResult<InputHandle> {
let r = self.io.input_open_name(name, self.status);
if let OpenResult::NotAvailable = r {
} else {
return r;
}
let mut ext = PathBuf::from(name);
let mut ename = OsString::from(match ext.file_name() {
Some(s) => s,
None => return OpenResult::NotAvailable
});
ename.push(format_to_extension(format));
ext.set_file_name(ename);
return self.io.input_open_name(&ext.into_os_string(), self.status);
}
fn input_open_name_format_gz(&mut self, name: &OsStr, format: FileFormat,
is_gz: bool) -> OpenResult<InputHandle> {
let base = self.input_open_name_format(name, format);
if !is_gz {
return base;
}
match base {
OpenResult::Ok(ih) => {
match GzDecoder::new(ih) {
Ok(dr) => OpenResult::Ok(InputHandle::new(name, dr)),
Err(e) => OpenResult::Err(e.into()),
}
},
_ => base
}
}
fn output_open(&mut self, name: &OsStr, is_gz: bool) -> *const OutputHandle {
let mut oh = match self.io.output_open_name(name) {
OpenResult::Ok(oh) => oh,
OpenResult::NotAvailable => return ptr::null(),
OpenResult::Err(e) => {
tt_warning!(self.status, "open of output {} failed", name.to_string_lossy(); e);
return ptr::null()
}
};
let name = oh.name().to_os_string();
if is_gz {
oh = OutputHandle::new(&name, GzBuilder::new().write(oh, Compression::Default));
}
if let Some(ref mut summaries) = self.summaries {
if {
if let Some(summ) = summaries.get_mut(&name) {
summ.access_pattern = match summ.access_pattern {
AccessPattern::Read => AccessPattern::ReadThenWritten,
c => c, };
false } else {
true }
} {
summaries.insert(name, FileSummary::new(AccessPattern::Written));
}
}
self.output_handles.push(Box::new(oh));
&*self.output_handles[self.output_handles.len()-1]
}
fn output_open_stdout(&mut self) -> *const OutputHandle {
let oh = match self.io.output_open_stdout() {
OpenResult::Ok(oh) => oh,
OpenResult::NotAvailable => return ptr::null(),
OpenResult::Err(e) => {
tt_warning!(self.status, "open of stdout failed"; e);
return ptr::null()
}
};
if let Some(ref mut summaries) = self.summaries {
if {
if let Some(summ) = summaries.get_mut(OsStr::new("")) {
summ.access_pattern = match summ.access_pattern {
AccessPattern::Read => AccessPattern::ReadThenWritten,
c => c, };
false } else {
true }
} {
summaries.insert(OsString::from(""), FileSummary::new(AccessPattern::Written));
}
}
self.output_handles.push(Box::new(oh));
&*self.output_handles[self.output_handles.len()-1]
}
fn output_write(&mut self, handle: *mut OutputHandle, buf: &[u8]) -> bool {
let rhandle: &mut OutputHandle = unsafe { &mut *handle };
let result = rhandle.write_all(buf);
match result {
Ok(_) => false,
Err(e) => {
tt_warning!(self.status, "write failed"; e.into());
true
}
}
}
fn output_flush(&mut self, handle: *mut OutputHandle) -> bool {
let rhandle: &mut OutputHandle = unsafe { &mut *handle };
let result = rhandle.flush();
match result {
Ok(_) => false,
Err(e) => {
tt_warning!(self.status, "flush failed"; e.into());
true
}
}
}
fn output_close(&mut self, handle: *mut OutputHandle) -> bool {
let len = self.output_handles.len();
for i in 0..len {
let p: *const OutputHandle = &*self.output_handles[i];
if p == handle {
let oh = self.output_handles.swap_remove(i);
let (name, digest) = oh.into_name_digest();
if let Some(ref mut summaries) = self.summaries {
let mut summ = summaries.get_mut(&name).expect("closing file that wasn't opened?");
summ.write_digest = Some(digest);
}
break;
}
}
false
}
fn input_open(&mut self, name: &OsStr, format: FileFormat, is_gz: bool) -> *const InputHandle {
let ih = match self.input_open_name_format_gz(name, format, is_gz) {
OpenResult::Ok(ih) => ih,
OpenResult::NotAvailable => {
if let Some(ref mut summaries) = self.summaries {
if {
if let Some(summ) = summaries.get_mut(name) {
summ.access_pattern = match summ.access_pattern {
AccessPattern::Written => AccessPattern::WrittenThenRead,
c => c, };
false } else {
true }
} {
let mut fs = FileSummary::new(AccessPattern::Read);
fs.read_digest = Some(DigestData::of_nothing());
summaries.insert(name.to_os_string(), fs);
}
}
return ptr::null();
},
OpenResult::Err(e) => {
tt_warning!(self.status, "open of input {} failed", name.to_string_lossy(); e);
return ptr::null();
},
};
let name = ih.name().to_os_string();
if let Some(ref mut summaries) = self.summaries {
if {
if let Some(summ) = summaries.get_mut(&name) {
summ.access_pattern = match summ.access_pattern {
AccessPattern::Written => AccessPattern::WrittenThenRead,
c => c, };
false } else {
true }
} {
summaries.insert(name, FileSummary::new(AccessPattern::Read));
}
}
self.input_handles.push(Box::new(ih));
&*self.input_handles[self.input_handles.len()-1]
}
fn input_get_size(&mut self, handle: *mut InputHandle) -> usize {
let rhandle: &mut InputHandle = unsafe { &mut *handle };
match rhandle.get_size() {
Ok(s) => s,
Err(e) => {
tt_warning!(self.status, "failed to get the size of an input"; e);
0
}
}
}
fn input_seek(&mut self, handle: *mut InputHandle, pos: SeekFrom) -> u64 {
let rhandle: &mut InputHandle = unsafe { &mut *handle };
match rhandle.try_seek(pos) {
Ok(pos) => pos,
Err(e) => {
tt_warning!(self.status, "input seek failed"; e);
0
}
}
}
fn input_read(&mut self, handle: *mut InputHandle, buf: &mut [u8]) -> Result<()> {
let rhandle: &mut InputHandle = unsafe { &mut *handle };
Ok(rhandle.read_exact(buf)?)
}
fn input_close(&mut self, handle: *mut InputHandle) -> bool {
let len = self.input_handles.len();
for i in 0..len {
let p: *const InputHandle = &*self.input_handles[i];
if p == handle {
let ih = self.input_handles.swap_remove(i);
let (name, digest_opt) = ih.into_name_digest();
if let Some(ref mut summaries) = self.summaries {
let mut summ = summaries.get_mut(&name).expect("closing file that wasn't opened?");
if summ.read_digest.is_none() {
summ.read_digest = digest_opt;
}
}
return false;
}
}
panic!("unexpected handle {:?}", handle);
}
}
static mut GLOBAL_ENGINE_PTR: *mut () = 0 as *mut _;
fn with_global_state<F, T> (f: F) -> T where F: FnOnce(&mut ExecutionState<IoStack>) -> T {
unsafe { f(&mut *(GLOBAL_ENGINE_PTR as *mut ExecutionState<IoStack>)) }
}
unsafe fn assign_global_state<F, T> (state: &mut ExecutionState<IoStack>, f: F) -> T where F: FnOnce() -> T {
GLOBAL_ENGINE_PTR = state as *mut ExecutionState<IoStack> as *mut ();
let rv = f();
GLOBAL_ENGINE_PTR = 0 as *mut _;
rv
}