From 745892fbddea56040139108277e728b53fd8fc11 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 28 May 2020 03:03:21 +0000 Subject: CTAN sync 202005280303 --- support/texlab/src/tex/miktex.rs | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 support/texlab/src/tex/miktex.rs (limited to 'support/texlab/src/tex/miktex.rs') diff --git a/support/texlab/src/tex/miktex.rs b/support/texlab/src/tex/miktex.rs new file mode 100644 index 0000000000..9f1551df44 --- /dev/null +++ b/support/texlab/src/tex/miktex.rs @@ -0,0 +1,108 @@ +use super::{ + compile, + kpsewhich::{self, KpsewhichError, Resolver}, + Artifacts, CompileError, CompileParams, Distribution, DistributionKind, +}; +use async_trait::async_trait; +use byteorder::{LittleEndian, ReadBytesExt}; +use futures::lock::Mutex; +use std::{ + ffi::OsStr, + io::{self, Cursor}, + mem, + path::{Path, PathBuf}, + sync::Arc, +}; +use tokio::fs; + +#[derive(Debug, Default)] +pub struct Miktex { + resolver: Mutex>, +} + +#[async_trait] +impl Distribution for Miktex { + fn kind(&self) -> DistributionKind { + DistributionKind::Miktex + } + + async fn compile<'a>(&'a self, params: CompileParams<'a>) -> Result { + compile(params).await + } + + async fn load(&self) -> Result<(), KpsewhichError> { + let root_directories = kpsewhich::root_directories().await?; + let resolver = kpsewhich::parse_database(&root_directories, read_database).await?; + mem::replace(&mut *self.resolver.lock().await, Arc::new(resolver)); + Ok(()) + } + + async fn resolver(&self) -> Arc { + let resolver = self.resolver.lock().await; + Arc::clone(&resolver) + } +} + +const DATABASE_PATH: &str = "miktex/data/le"; +const FNDB_SIGNATURE: u32 = 0x42_44_4e_46; +const FNDB_WORD_SIZE: usize = 4; +const FNDB_TABLE_POINTER_OFFSET: usize = 4 * FNDB_WORD_SIZE; +const FNDB_TABLE_SIZE_OFFSET: usize = 6 * FNDB_WORD_SIZE; +const FNDB_ENTRY_SIZE: usize = 4 * FNDB_WORD_SIZE; + +async fn read_database(directory: &Path) -> Result, KpsewhichError> { + let database_directory = directory.join(DATABASE_PATH); + if !database_directory.exists() { + return Ok(Vec::new()); + } + + let mut database = Vec::new(); + let mut files: tokio::fs::ReadDir = fs::read_dir(database_directory).await?; + while let Some(file) = files.next_entry().await? { + if file.path().extension().and_then(OsStr::to_str) == Some("fndb-5") { + let bytes = fs::read(file.path()).await?; + database.extend(parse_database(&bytes).map_err(|_| KpsewhichError::CorruptDatabase)?); + } + } + Ok(database) +} + +fn parse_database(bytes: &[u8]) -> io::Result> { + let mut reader = Cursor::new(bytes); + if reader.read_u32::()? != FNDB_SIGNATURE { + return Err(io::ErrorKind::InvalidData.into()); + } + + reader.set_position(FNDB_TABLE_POINTER_OFFSET as u64); + let table_address = reader.read_u32::()?; + + reader.set_position(FNDB_TABLE_SIZE_OFFSET as u64); + let table_size = reader.read_u32::()?; + + let mut files = Vec::new(); + for i in 0..table_size { + let offset = table_address + i * FNDB_ENTRY_SIZE as u32; + reader.set_position(offset as u64); + let file_name_offset = reader.read_u32::()? as usize; + let directory_offset = reader.read_u32::()? as usize; + let file_name = read_string(bytes, file_name_offset)?; + let directory = read_string(bytes, directory_offset)?; + + let file = PathBuf::from(directory).join(file_name); + files.push(file); + } + + Ok(files) +} + +fn read_string(bytes: &[u8], offset: usize) -> io::Result<&str> { + let mut byte = bytes[offset]; + let mut length = 0; + while byte != 0x00 { + length += 1; + byte = bytes[offset + length]; + } + + std::str::from_utf8(&bytes[offset..offset + length]) + .map_err(|_| io::ErrorKind::InvalidData.into()) +} -- cgit v1.2.3