#![crate_type = "lib"]
#[macro_use]
extern crate bitflags;
#[macro_use]
pub mod macros;
extern crate libc;
use libc::{c_char, size_t};
mod api;
pub mod version;
pub use version::version;
pub mod flags;
pub use flags::Flags;
#[cfg(test)]
mod tests;
use std::{
error,
ffi::{CStr, CString},
fmt::Display,
path::Path,
ptr, str,
};
fn db_filenames<P: AsRef<Path>>(filenames: &[P]) -> *const c_char {
match filenames.len() {
0 => ptr::null(),
1 => CString::new(filenames[0].as_ref().to_string_lossy().into_owned())
.unwrap()
.into_raw(),
_ => unimplemented!(),
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct FileMagicError {
pub desc: String,
}
impl error::Error for FileMagicError {
fn description(&self) -> &str {
"internal libmagic error"
}
}
impl Display for FileMagicError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.desc)
}
}
pub struct Magic {
magic: *const api::Magic,
}
impl Drop for Magic {
fn drop(&mut self) {
unsafe { api::magic_close(self.magic) }
}
}
impl Magic {
fn last_error(&self) -> Option<FileMagicError> {
let cookie = self.magic;
unsafe {
let e = api::magic_error(cookie);
if e.is_null() {
None
} else {
let slice = CStr::from_ptr(e).to_bytes();
Some(self::FileMagicError {
desc: str::from_utf8(slice).unwrap().to_string(),
})
}
}
}
fn magic_failure(&self) -> FileMagicError {
match self.last_error() {
Some(e) => e,
None => self::FileMagicError {
desc: "unknown error".to_string(),
},
}
}
pub fn error(&self) -> Option<String> {
unsafe {
let str = api::magic_error(self.magic);
if str.is_null() {
None
} else {
let slice = CStr::from_ptr(str).to_bytes();
Some(str::from_utf8(slice).unwrap().to_string())
}
}
}
pub fn file<P: AsRef<Path>>(&self, filename: P) -> Result<String, FileMagicError> {
let cookie = self.magic;
let f = CString::new(filename.as_ref().to_string_lossy().into_owned())
.unwrap()
.into_raw();
unsafe {
let str = api::magic_file(cookie, f);
if str.is_null() {
Err(self.magic_failure())
} else {
let slice = CStr::from_ptr(str).to_bytes();
Ok(str::from_utf8(slice).unwrap().to_string())
}
}
}
pub fn buffer(&self, buffer: &[u8]) -> Result<String, FileMagicError> {
let buffer_len = buffer.len() as size_t;
let pbuffer = buffer.as_ptr();
unsafe {
let str = api::magic_buffer(self.magic, pbuffer, buffer_len);
if str.is_null() {
Err(self.magic_failure())
} else {
let slice = CStr::from_ptr(str).to_bytes();
Ok(str::from_utf8(slice).unwrap().to_string())
}
}
}
pub fn check<P: AsRef<Path>>(&self, filenames: &[P]) -> Result<(), FileMagicError> {
let cookie = self.magic;
let db_filenames = db_filenames(filenames);
let ret;
unsafe {
ret = api::magic_check(cookie, db_filenames);
}
if 0 == ret {
Ok(())
} else {
Err(self.magic_failure())
}
}
pub fn compile<P: AsRef<Path>>(&self, filenames: &[P]) -> Result<(), FileMagicError> {
let cookie = self.magic;
let db_filenames = db_filenames(filenames);
let ret;
unsafe {
ret = api::magic_compile(cookie, db_filenames);
}
if 0 == ret {
Ok(())
} else {
Err(self.magic_failure())
}
}
pub fn list<P: AsRef<Path>>(&self, filenames: &[P]) -> Result<(), FileMagicError> {
let cookie = self.magic;
let db_filenames = db_filenames(filenames);
let ret;
unsafe {
ret = api::magic_list(cookie, db_filenames);
}
if 0 == ret {
Ok(())
} else {
Err(self.magic_failure())
}
}
pub fn set_flags(&self, flags: Flags) -> bool {
unsafe { api::magic_setflags(self.magic, flags.bits()) != -1 }
}
pub fn open(flags: Flags) -> Result<Magic, FileMagicError> {
let cookie;
unsafe {
cookie = api::magic_open((flags | Flags::ERROR).bits());
}
if cookie.is_null() {
Err(self::FileMagicError {
desc: "errno".to_string(),
})
} else {
Ok(Magic { magic: cookie })
}
}
pub fn load<P: AsRef<Path>>(&self, magic_databases: &[P]) -> Result<(), FileMagicError> {
let cookie = self.magic;
let db_filenames = db_filenames(magic_databases);
let ret;
unsafe {
ret = api::magic_load(cookie, db_filenames);
}
if 0 == ret {
Ok(())
} else {
Err(self.magic_failure())
}
}
}