#![deny(missing_docs)]
mod bindings;
use crate::bindings::*;
use std::ffi::{CStr, CString};
use std::fmt;
use std::mem;
use std::os::raw::c_void;
use std::path::Path;
use std::ptr::null_mut;
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
#[repr(u32)]
pub enum OutputType {
Memory = TCC_OUTPUT_MEMORY,
Exe = TCC_OUTPUT_EXE,
Dll = TCC_OUTPUT_DLL,
Obj = TCC_OUTPUT_OBJ,
Preprocess = TCC_OUTPUT_PREPROCESS,
}
extern "C" fn callback(user_data: *mut c_void, string: *const i8) {
let tcc = unsafe { &mut *(user_data as *mut Tcc) };
if let Some(ref func) = tcc.error_func {
let string = unsafe { CStr::from_ptr(string) }.to_str().unwrap();
func(string);
}
}
pub struct Tcc {
inner: *mut TCCState,
error_func: Option<Box<dyn Fn(&str)>>,
}
impl fmt::Debug for Tcc {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "Tcc")
}
}
impl Default for Tcc {
fn default() -> Tcc {
Tcc::new()
}
}
impl Tcc {
pub fn new() -> Self {
Tcc {
inner: unsafe { tcc_new() },
error_func: None,
}
}
pub fn set_lib_path<P: AsRef<Path>>(&mut self, path: P) {
let path = path.as_ref().to_str().unwrap();
unsafe { tcc_set_lib_path(self.inner, path.as_ptr() as *const i8) }
}
pub fn set_error_func(&mut self, error_func: Option<Box<dyn Fn(&str)>>) {
unsafe {
tcc_set_error_func(
self.inner,
self as *mut Tcc as *mut c_void,
if error_func.is_some() {
Some(callback)
} else {
None
},
)
};
self.error_func = error_func;
}
pub fn set_options(&mut self, options: &str) {
let options = CString::new(options).unwrap();
unsafe { tcc_set_options(self.inner, options.as_ptr()) };
}
pub fn add_include_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ()> {
let path = path.as_ref().to_str().unwrap();
let ret = unsafe { tcc_add_include_path(self.inner, path.as_ptr() as *const i8) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn add_sysinclude_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ()> {
let path = path.as_ref().to_str().unwrap();
let ret = unsafe { tcc_add_sysinclude_path(self.inner, path.as_ptr() as *const i8) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn define_symbol(&mut self, sym: &str, value: &str) {
let sym = CString::new(sym).unwrap();
let value = CString::new(value).unwrap();
unsafe { tcc_define_symbol(self.inner, sym.as_ptr(), value.as_ptr()) }
}
pub fn undefine_symbol(&mut self, sym: &str) {
let sym = CString::new(sym).unwrap();
unsafe { tcc_undefine_symbol(self.inner, sym.as_ptr()) }
}
pub fn add_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ()> {
let path = path.as_ref().to_str().unwrap();
let ret = unsafe { tcc_add_file(self.inner, path.as_ptr() as *const i8) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn compile_string(&mut self, buf: &str) -> Result<(), ()> {
let buf = CString::new(buf).unwrap();
let ret = unsafe { tcc_compile_string(self.inner, buf.as_ptr()) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn set_output_type(&mut self, output_type: OutputType) -> Result<(), ()> {
let ret = unsafe { tcc_set_output_type(self.inner, output_type as i32) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn add_library_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ()> {
let path = path.as_ref().to_str().unwrap();
let ret = unsafe { tcc_add_library_path(self.inner, path.as_ptr() as *const i8) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn add_library(&mut self, name: &str) -> Result<(), ()> {
let name = CString::new(name).unwrap();
let ret = unsafe { tcc_add_library(self.inner, name.as_ptr()) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn add_symbol<S: Symbol>(&mut self, name: &str, symbol: &S) -> Result<(), ()> {
let name = CString::new(name).unwrap();
let ret = unsafe { tcc_add_symbol(self.inner, name.as_ptr(), symbol.as_ptr()) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn output_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ()> {
let path = path.as_ref().to_str().unwrap();
let ret = unsafe { tcc_output_file(self.inner, path.as_ptr() as *const i8) };
if ret == -1 {
return Err(());
}
Ok(())
}
pub fn run(&mut self, args: &[&str]) -> Result<(), ()> {
let argc = args.len();
let mut argv: Vec<*mut i8> = args
.iter()
.map(|string| CString::new(*string).unwrap().into_raw())
.collect();
let ret = unsafe { tcc_run(self.inner, argc as i32, argv.as_mut_ptr()) };
if ret == -1 {
return Err(());
}
let _: Vec<CString> = argv
.into_iter()
.map(|ptr| unsafe { CString::from_raw(ptr) })
.collect();
Ok(())
}
pub fn relocate(self) -> Result<RelocatedTcc, Tcc> {
let len = unsafe { tcc_relocate(self.inner, null_mut()) };
if len == -1 {
return Err(self);
}
let mut memory = Vec::with_capacity(len as usize);
let ret = unsafe { tcc_relocate(self.inner, memory.as_mut_ptr() as *mut c_void) };
if ret == -1 {
return Err(self);
}
unsafe { memory.set_len(len as usize) }
let relocated = RelocatedTcc {
inner: self.inner,
memory: Rc::new(memory),
};
mem::forget(self);
Ok(relocated)
}
}
impl Drop for Tcc {
fn drop(&mut self) {
unsafe { tcc_delete(self.inner) }
}
}
pub struct RelocatedTcc {
inner: *mut TCCState,
memory: Rc<Vec<u8>>,
}
impl RelocatedTcc {
pub fn get_symbol(&mut self, name: &str) -> Result<CSymbol, ()> {
let name = CString::new(name).unwrap();
let ptr = unsafe { tcc_get_symbol(self.inner, name.as_ptr()) };
if ptr.is_null() {
return Err(());
}
Ok(CSymbol {
ptr: ptr,
name: name.into_string().map_err(|_| ())?,
memory: self.memory.clone(),
})
}
}
impl fmt::Debug for RelocatedTcc {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "RelocatedTcc")
}
}
impl Drop for RelocatedTcc {
fn drop(&mut self) {
unsafe { tcc_delete(self.inner) }
}
}
pub trait Symbol {
fn as_ptr(&self) -> *const c_void;
}
pub struct CSymbol {
ptr: *const c_void,
name: String,
#[allow(dead_code)]
memory: Rc<Vec<u8>>,
}
impl fmt::Debug for CSymbol {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "CSymbol({:?} at {:p})", self.name, self.ptr)
}
}
impl Symbol for CSymbol {
fn as_ptr(&self) -> *const c_void {
self.ptr
}
}
impl Symbol for *const c_void {
fn as_ptr(&self) -> *const c_void {
*self
}
}