#[cfg(debug_assertions)]
use backtrace::Backtrace;
use libc::c_char;
use std::ffi::CString;
use std::marker::PhantomPinned;
use std::panic;
use std::panic::{catch_unwind, UnwindSafe};
use std::pin::Pin;
use std::ptr::null;
use std::ptr::NonNull;
use crate::hexchat::Hexchat;
use crate::hook::*;
use crate::utils::*;
#[cfg(feature = "threadsafe")]
use crate::thread_facilities::*;
pub type InitFn = dyn FnOnce(&'static Hexchat) -> i32 + UnwindSafe;
pub type DeinitFn = dyn FnOnce(&'static Hexchat) -> i32 + UnwindSafe;
pub type InfoFn = dyn FnOnce() -> PluginInfo + UnwindSafe;
static mut PLUGIN_INFO: Option<PluginInfo> = None;
pub(crate) static mut PHEXCHAT: *const Hexchat = null::<Hexchat>();
#[macro_export]
macro_rules! dll_entry_points {
( $info:ident, $init:ident, $deinit:ident ) => {
#[no_mangle]
pub extern "C"
fn hexchat_plugin_get_info(name : *mut *const i8,
desc : *mut *const i8,
version : *mut *const i8,
reserved : *mut *const i8)
{
hexchat_api::lib_get_info(name,
desc,
version,
Box::new($info));
}
#[no_mangle]
pub extern "C"
fn hexchat_plugin_init(hexchat : &'static Hexchat,
name : *mut *const i8,
desc : *mut *const i8,
version : *mut *const i8
) -> i32
{
hexchat_api::lib_hexchat_plugin_init(hexchat,
name,
desc,
version,
Box::new($init),
Box::new($info))
}
#[no_mangle]
pub extern "C"
fn hexchat_plugin_deinit(hexchat : &'static Hexchat) -> i32
{
hexchat_api::lib_hexchat_plugin_deinit(hexchat, Box::new($deinit))
}
};
}
struct PluginInfoData {
name : CString,
version : CString,
description : CString,
pname : NonNull<CString>,
pversion : NonNull<CString>,
pdescription : NonNull<CString>,
_pin : PhantomPinned,
}
pub struct PluginInfo {
data: Pin<Box<PluginInfoData>>,
}
impl PluginInfo {
pub fn new(name: &str, version: &str, description: &str) -> PluginInfo
{
let pi = PluginInfoData {
name : str2cstring(name),
version : str2cstring(version),
description : str2cstring(description),
pname : NonNull::dangling(),
pversion : NonNull::dangling(),
pdescription : NonNull::dangling(),
_pin : PhantomPinned,
};
let mut boxed = Box::pin(pi);
let sname = NonNull::from(&boxed.name);
let sversion = NonNull::from(&boxed.version);
let sdescription = NonNull::from(&boxed.description);
unsafe {
let mut_ref: Pin<&mut PluginInfoData> = Pin::as_mut(&mut boxed);
let unchecked = Pin::get_unchecked_mut(mut_ref);
unchecked.pname = sname;
unchecked.pversion = sversion;
unchecked.pdescription = sdescription;
}
PluginInfo { data: boxed }
}
}
#[doc(hidden)]
pub fn lib_hexchat_plugin_get_info(name : *mut *const i8,
desc : *mut *const i8,
version : *mut *const i8,
_reserved : *mut *const i8,
callback : Box<InfoFn>)
{
lib_get_info(name, desc, version, callback);
}
#[doc(hidden)]
pub fn lib_hexchat_plugin_init(hexchat : &'static Hexchat,
name : *mut *const c_char,
desc : *mut *const c_char,
version : *mut *const c_char,
init_cb : Box<InitFn>,
info_cb : Box<InfoFn>)
-> i32
{
unsafe { PHEXCHAT = hexchat; }
set_panic_hook(hexchat);
lib_get_info(name, desc, version, info_cb);
catch_unwind(|| {
Hook::init();
#[cfg(feature = "threadsafe")]
main_thread_init();
init_cb(hexchat)
}).unwrap_or(0)
}
#[doc(hidden)]
pub fn lib_hexchat_plugin_deinit(hexchat : &'static Hexchat,
callback : Box<DeinitFn>)
-> i32
{
let result = catch_unwind(|| {
#[cfg(feature = "threadsafe")]
main_thread_deinit();
let retval = callback(hexchat);
Hook::deinit();
unsafe { PLUGIN_INFO = None; }
retval
}).unwrap_or(0);
let _ = panic::take_hook();
result
}
#[inline]
#[doc(hidden)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn lib_get_info(name : *mut *const c_char,
desc : *mut *const c_char,
vers : *mut *const c_char,
callback : Box<InfoFn>)
{
unsafe {
if PLUGIN_INFO.is_none() {
let pi = callback();
PLUGIN_INFO = Some(pi);
}
if let Some(info) = &PLUGIN_INFO {
*name = info.data.pname.as_ref().as_ptr();
*vers = info.data.pversion.as_ref().as_ptr();
*desc = info.data.description.as_ref().as_ptr();
}
}
}
fn set_panic_hook(hexchat: &'static Hexchat) {
panic::set_hook(Box::new(move |panic_info| {
#[cfg(debug_assertions)]
let mut loc = String::new();
let plugin_name;
unsafe {
if let Some(plugin_info) = &PLUGIN_INFO {
plugin_name = plugin_info.data.name.to_str().unwrap();
} else {
plugin_name = "a Rust plugin";
}
}
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
hexchat.print(&format!("\x0304<<Panicked!>>\t{:?}", s));
} else {
hexchat.print(&format!("\x0304<<Panicked!>>\t{:?}", panic_info));
}
if let Some(location) = panic_info.location() {
hexchat.print(
&format!("\x0313Panic occured in {} in file '{}' at line {:?}.",
plugin_name,
location.file(),
location.line()));
#[cfg(debug_assertions)]
{ loc = format!("{}:{:?}", location.file(), location.line()); }
}
#[cfg(debug_assertions)]
{
let mut trace = vec![];
let mut begin = 0;
let mut end = 0;
let bt = Backtrace::new();
let btstr = format!("{:?}", bt);
for line in btstr.lines() {
let line = String::from(line);
if begin == 0 && !loc.is_empty() && line.contains(&loc) {
trace.push(format!("\x1F\x0313{}", line));
begin = end;
} else {
trace.push(format!("\x0304{}", line));
}
end += 1;
}
begin = if begin == 0 { 0 } else { begin - 1 };
hexchat.print(&trace[begin..end].join("\n"));
}
}));
}