#[doc(hidden)]
pub extern crate libc;
mod internals;
use std::panic::catch_unwind;
use std::thread;
use std::ffi::{CString, CStr};
use std::str::FromStr;
use std::mem;
use std::ptr;
use std::marker::PhantomData;
use std::ops;
use std::rc::Rc;
use std::cell::Cell;
use std::borrow::Cow;
pub trait Plugin {
fn init(&self, ph: &mut PluginHandle, arg: Option<&str>) -> bool;
fn deinit(&self, _ph: &mut PluginHandle) {
}
}
pub struct PluginHandle {
ph: *mut internals::Ph,
info: PluginInfo,
}
pub struct Word<'a> {
word: Vec<&'a str>
}
pub struct WordEol<'a> {
word_eol: Vec<&'a str>
}
pub struct EnsureValidContext<'a> {
ph: &'a mut PluginHandle,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Eat {
do_eat: i32,
}
pub const EAT_NONE: Eat = Eat { do_eat: 0 };
pub const EAT_HEXCHAT: Eat = Eat { do_eat: 1 };
pub const EAT_PLUGIN: Eat = Eat { do_eat: 2 };
pub const EAT_ALL: Eat = Eat { do_eat: 1 | 2 };
pub struct CommandHookHandle {
ph: *mut internals::Ph,
hh: *const internals::HexchatHook,
_f: PhantomData<Rc<CommandHookUd>>,
}
pub struct ServerHookHandle {
ph: *mut internals::Ph,
hh: *const internals::HexchatHook,
_f: PhantomData<Rc<ServerHookUd>>,
}
pub struct PrintHookHandle {
ph: *mut internals::Ph,
hh: *const internals::HexchatHook,
_f: PhantomData<Rc<PrintHookUd>>,
}
pub struct TimerHookHandle {
ph: *mut internals::Ph,
hh: *const internals::HexchatHook,
alive: Rc<Cell<bool>>,
_f: PhantomData<Rc<TimerHookUd>>,
}
pub struct Context {
ctx: *const internals::HexchatContext,
}
pub struct InvalidContextError<F: FnOnce(EnsureValidContext) -> R, R>(F);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)]
pub enum InfoId<'a> {
Away,
Channel,
Charset,
Configdir,
EventText(&'a str),
Host,
Inputbox,
Libdirfs,
Modes,
Network,
Nick,
Nickserv,
Server,
Topic,
Version,
WinStatus,
}
impl<'a> InfoId<'a> {
pub fn name(&self) -> Cow<'static, str> {
match *self {
InfoId::EventText(s) => {
let mut eventtext: String = "event_text ".into();
eventtext.push_str(&s);
eventtext.into()
},
InfoId::Away => "away".into(),
InfoId::Channel => "channel".into(),
InfoId::Charset => "charset".into(),
InfoId::Configdir => "configdir".into(),
InfoId::Host => "host".into(),
InfoId::Inputbox => "inputbox".into(),
InfoId::Libdirfs => "libdirfs".into(),
InfoId::Modes => "modes".into(),
InfoId::Network => "network".into(),
InfoId::Nick => "nick".into(),
InfoId::Nickserv => "nickserv".into(),
InfoId::Server => "server".into(),
InfoId::Topic => "topic".into(),
InfoId::Version => "version".into(),
InfoId::WinStatus => "win_status".into(),
}
}
}
impl<F: FnOnce(EnsureValidContext) -> R, R> InvalidContextError<F, R> {
pub fn get_closure(self) -> F {
self.0
}
}
impl Drop for CommandHookHandle {
fn drop(&mut self) {
unsafe {
let b = ((*self.ph).hexchat_unhook)(self.ph, self.hh) as *mut CommandHookUd;
mem::drop(Rc::from_raw(b));
}
}
}
impl Drop for ServerHookHandle {
fn drop(&mut self) {
unsafe {
let b = ((*self.ph).hexchat_unhook)(self.ph, self.hh) as *mut ServerHookUd;
mem::drop(Rc::from_raw(b));
}
}
}
impl Drop for PrintHookHandle {
fn drop(&mut self) {
unsafe {
let b = ((*self.ph).hexchat_unhook)(self.ph, self.hh) as *mut PrintHookUd;
mem::drop(Rc::from_raw(b));
}
}
}
impl Drop for TimerHookHandle {
fn drop(&mut self) {
if !self.alive.get() {
return;
}
self.alive.set(false);
unsafe {
let b = ((*self.ph).hexchat_unhook)(self.ph, self.hh) as *mut TimerHookUd;
mem::drop(Rc::from_raw(b));
}
}
}
impl<'a> Word<'a> {
unsafe fn new(word: *const *const libc::c_char) -> Word<'a> {
let mut vec = vec![];
for i in 1..32 {
let w = *word.offset(i);
if !w.is_null() {
vec.push(CStr::from_ptr(w).to_str().expect("non-utf8 word - broken hexchat"))
}
}
while let Some(&"") = vec.last() {
vec.pop();
}
Word { word: vec }
}
}
impl<'a> ops::Deref for Word<'a> {
type Target = [&'a str];
fn deref(&self) -> &[&'a str] {
&self.word
}
}
impl<'a> WordEol<'a> {
unsafe fn new(word_eol: *const *const libc::c_char) -> WordEol<'a> {
let mut vec = vec![];
for i in 1..32 {
let w = *word_eol.offset(i);
if !w.is_null() {
vec.push(CStr::from_ptr(w).to_str().expect("non-utf8 word_eol - broken hexchat"))
}
}
while let Some(&"") = vec.last() {
vec.pop();
}
WordEol { word_eol: vec }
}
}
impl<'a> ops::Deref for WordEol<'a> {
type Target = [&'a str];
fn deref(&self) -> &[&'a str] {
&self.word_eol
}
}
impl PluginHandle {
fn new(ph: *mut internals::Ph, info: PluginInfo) -> PluginHandle {
PluginHandle {
ph, info
}
}
pub fn register(&mut self, name: &str, desc: &str, ver: &str) {
unsafe {
let info = self.info;
if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() {
panic!("Attempt to re-register a plugin");
}
let name = CString::new(name).unwrap();
let desc = CString::new(desc).unwrap();
let ver = CString::new(ver).unwrap();
(*info.name) = name.into_raw();
(*info.desc) = desc.into_raw();
(*info.vers) = ver.into_raw();
}
}
pub fn get_name(&self) -> &str {
unsafe {
let info = self.info;
if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() {
std::str::from_utf8_unchecked(CStr::from_ptr(*info.name).to_bytes())
} else {
panic!("Attempt to get the name of a plugin that was not yet registered.");
}
}
}
pub fn get_description(&self) -> &str {
unsafe {
let info = self.info;
if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() {
std::str::from_utf8_unchecked(CStr::from_ptr(*info.desc).to_bytes())
} else {
panic!("Attempt to get the description of a plugin that was not yet registered.");
}
}
}
pub fn get_version(&self) -> &str {
unsafe {
let info = self.info;
if !(*info.name).is_null() || !(*info.desc).is_null() || !(*info.vers).is_null() {
std::str::from_utf8_unchecked(CStr::from_ptr(*info.vers).to_bytes())
} else {
panic!("Attempt to get the version of a plugin that was not yet registered.");
}
}
}
pub fn ensure_valid_context<F, R>(&mut self, f: F) -> R where F: FnOnce(EnsureValidContext) -> R {
let ctx = self.get_context();
let res = self.with_context(&ctx, f);
match res {
Result::Ok(r @ _) => r,
Result::Err(e @ _) => {
let nctx = self.find_valid_context().expect("ensure_valid_context failed (find_valid_context failed), was hexchat closing?");
self.with_context(&nctx, e.get_closure()).ok().expect("ensure_valid_context failed, was hexchat closing?")
}
}
}
pub fn get_context(&mut self) -> Context {
unsafe {
Context { ctx: ((*self.ph).hexchat_get_context)(self.ph) }
}
}
pub fn set_context(&mut self, ctx: &Context) -> bool {
unsafe {
((*self.ph).hexchat_set_context)(self.ph, ctx.ctx) != 0
}
}
#[inline]
pub fn with_context<F, R>(&mut self, ctx: &Context, f: F) -> Result<R, InvalidContextError<F, R>> where F: FnOnce(EnsureValidContext) -> R {
if !self.set_context(ctx) {
Err(InvalidContextError(f))
} else {
Ok(f(EnsureValidContext { ph: self }))
}
}
pub fn hook_command<F>(&mut self, cmd: &str, cb: F, pri: i32, help: Option<&str>) -> CommandHookHandle where F: Fn(&mut PluginHandle, Word, WordEol) -> Eat + 'static + ::std::panic::RefUnwindSafe {
unsafe extern "C" fn callback(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, ud: *mut libc::c_void) -> libc::c_int {
let f: Rc<CommandHookUd> = rc_clone_from_raw(ud as *const CommandHookUd);
let ph = f.1;
match catch_unwind(move || {
let word = Word::new(word);
let word_eol = WordEol::new(word_eol);
(f.0)(&mut PluginHandle::new(f.1, f.2), word, word_eol).do_eat as libc::c_int
}) {
Result::Ok(v @ _) => v,
Result::Err(e @ _) => {
if let Some(estr) = e.downcast_ref::<&str>() {
hexchat_print_str(ph, estr, false);
} else if let Some(estring) = e.downcast_ref::<String>() {
hexchat_print_str(ph, &estring, false);
}
0 }
}
}
let b: Rc<CommandHookUd> = Rc::new((Box::new(cb), self.ph, self.info));
let name = CString::new(cmd).unwrap();
let help_text = help.map(CString::new).map(Result::unwrap);
let bp = Rc::into_raw(b);
unsafe {
let res = ((*self.ph).hexchat_hook_command)(self.ph, name.as_ptr(), pri as libc::c_int, callback, help_text.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()), bp as *mut _);
assert!(!res.is_null());
CommandHookHandle { ph: self.ph, hh: res, _f: PhantomData }
}
}
pub fn hook_server<F>(&mut self, cmd: &str, cb: F, pri: i32) -> ServerHookHandle where F: Fn(&mut PluginHandle, Word, WordEol) -> Eat + 'static + ::std::panic::RefUnwindSafe {
unsafe extern "C" fn callback(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, ud: *mut libc::c_void) -> libc::c_int {
let f: Rc<ServerHookUd> = rc_clone_from_raw(ud as *const ServerHookUd);
let ph = f.1;
match catch_unwind(move || {
let word = Word::new(word);
let word_eol = WordEol::new(word_eol);
(f.0)(&mut PluginHandle::new(f.1, f.2), word, word_eol).do_eat as libc::c_int
}) {
Result::Ok(v @ _) => v,
Result::Err(e @ _) => {
if let Some(estr) = e.downcast_ref::<&str>() {
hexchat_print_str(ph, estr, false);
} else if let Some(estring) = e.downcast_ref::<String>() {
hexchat_print_str(ph, &estring, false);
}
0 }
}
}
let b: Rc<ServerHookUd> = Rc::new((Box::new(cb), self.ph, self.info));
let name = CString::new(cmd).unwrap();
let bp = Rc::into_raw(b);
unsafe {
let res = ((*self.ph).hexchat_hook_server)(self.ph, name.as_ptr(), pri as libc::c_int, callback, bp as *mut _);
assert!(!res.is_null());
ServerHookHandle { ph: self.ph, hh: res, _f: PhantomData }
}
}
pub fn hook_print<F>(&mut self, name: &str, cb: F, pri: i32) -> PrintHookHandle where F: Fn(&mut PluginHandle, Word) -> Eat + 'static + ::std::panic::RefUnwindSafe {
unsafe extern "C" fn callback(word: *const *const libc::c_char, ud: *mut libc::c_void) -> libc::c_int {
let f: Rc<PrintHookUd> = rc_clone_from_raw(ud as *const PrintHookUd);
let ph = f.1;
match catch_unwind(move || {
let word = Word::new(word);
(f.0)(&mut PluginHandle::new(f.1, f.2), word).do_eat as libc::c_int
}) {
Result::Ok(v @ _) => v,
Result::Err(e @ _) => {
if let Some(estr) = e.downcast_ref::<&str>() {
hexchat_print_str(ph, estr, false);
} else if let Some(estring) = e.downcast_ref::<String>() {
hexchat_print_str(ph, &estring, false);
}
0 }
}
}
let b: Rc<PrintHookUd> = Rc::new((Box::new(cb), self.ph, self.info));
let name = CString::new(name).unwrap();
let bp = Rc::into_raw(b);
unsafe {
let res = ((*self.ph).hexchat_hook_print)(self.ph, name.as_ptr(), pri as libc::c_int, callback, bp as *mut _);
assert!(!res.is_null());
PrintHookHandle { ph: self.ph, hh: res, _f: PhantomData }
}
}
pub fn hook_timer<F>(&mut self, timeout: i32, cb: F) -> TimerHookHandle where F: Fn(&mut PluginHandle) -> bool + 'static + ::std::panic::RefUnwindSafe {
unsafe extern "C" fn callback(ud: *mut libc::c_void) -> libc::c_int {
let f: Rc<TimerHookUd> = rc_clone_from_raw(ud as *const TimerHookUd);
let alive = f.1.clone(); let f = f.0.clone();
let ph = f.1;
match catch_unwind(move || {
(f.0)(&mut PluginHandle::new(f.1, f.2))
}) {
Result::Ok(true) => 1,
Result::Ok(false) => {
if !alive.get() {
return 0;
}
alive.set(false);
mem::drop(Rc::from_raw(ud as *const TimerHookUd));
0
},
Result::Err(e @ _) => {
if let Some(estr) = e.downcast_ref::<&str>() {
hexchat_print_str(ph, estr, false);
} else if let Some(estring) = e.downcast_ref::<String>() {
hexchat_print_str(ph, &estring, false);
}
if !alive.get() {
return 0;
}
alive.set(false);
mem::drop(Rc::from_raw(ud as *const TimerHookUd));
0
}
}
}
let alive = Rc::new(Cell::new(true));
let b: Rc<TimerHookUd> = Rc::new((Rc::new((Box::new(cb), self.ph, self.info)), alive.clone()));
let bp = Rc::into_raw(b);
unsafe {
let res = ((*self.ph).hexchat_hook_timer)(self.ph, timeout as libc::c_int, callback, bp as *mut _);
assert!(!res.is_null());
TimerHookHandle { ph: self.ph, hh: res, alive, _f: PhantomData }
}
}
pub fn print(&mut self, s: &str) {
unsafe {
hexchat_print_str(self.ph, s, true);
}
}
pub fn get_info(&mut self, id: &InfoId) -> Option<String> {
let ph = self.ph;
let id_cstring = CString::new(&*id.name()).unwrap();
unsafe {
let res = ((*ph).hexchat_get_info)(ph, id_cstring.as_ptr());
if res.is_null() {
None
} else {
let s = CStr::from_ptr(res).to_owned().into_string();
if *id != InfoId::Libdirfs {
Some(s.expect("non-utf8 word - broken hexchat"))
} else {
s.ok()
}
}
}
}
fn find_valid_context(&mut self) -> Option<Context> {
unsafe {
let ph = self.ph;
#[allow(unused_mut)]
let mut list = ((*ph).hexchat_list_get)(ph, cstr(b"channels\0"));
if ((*ph).hexchat_list_next)(ph, list) != 0 {
let ctx = ((*ph).hexchat_list_str)(ph, list, cstr(b"context\0")) as *const internals::HexchatContext;
((*ph).hexchat_list_free)(ph, list);
Some(Context { ctx })
} else {
((*ph).hexchat_list_free)(ph, list);
None
}
}
}
}
impl<'a> EnsureValidContext<'a> {
pub fn find_context(&mut self, servname: Option<&str>, channel: Option<&str>) -> Option<Context> {
let ph = self.ph.ph;
let servname = servname.map(|x| CString::new(x).unwrap());
let channel = channel.map(|x| CString::new(x).unwrap());
let ctx = unsafe {
let sptr = servname.map(|x| x.as_ptr()).unwrap_or(ptr::null());
let cptr = channel.map(|x| x.as_ptr()).unwrap_or(ptr::null());
((*ph).hexchat_find_context)(ph, sptr, cptr)
};
if ctx.is_null() {
None
} else {
Some(Context { ctx })
}
}
pub fn nickcmp(&mut self, nick1: &str, nick2: &str) -> ::std::cmp::Ordering {
use std::cmp::Ordering;
let ph = self.ph.ph;
let nick1 = CString::new(nick1).unwrap();
let nick2 = CString::new(nick2).unwrap();
let res = unsafe {
((*ph).hexchat_nickcmp)(ph, nick1.as_ptr(), nick2.as_ptr())
};
if res < 0 {
Ordering::Less
} else if res > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub fn send_modes<'b, I: IntoIterator<Item=&'b str>>(&mut self, iter: I, mpl: i32, sign: char, mode: char) {
let ph = self.ph.ph;
assert!(sign == '+' || sign == '-', "sign must be + or -");
assert!(mode.is_ascii(), "mode must be ascii");
assert!(mpl >= 0, "mpl must be non-negative");
let v: Vec<CString> = iter.into_iter().map(|s| CString::new(s).unwrap()).collect();
let mut v2: Vec<*const libc::c_char> = (&v).iter().map(|x| x.as_ptr()).collect();
let arr: &mut [*const libc::c_char] = &mut *v2;
unsafe {
((*ph).hexchat_send_modes)(ph, arr.as_mut_ptr(), arr.len() as libc::c_int,
mpl as libc::c_int, sign as libc::c_char, mode as libc::c_char)
}
}
pub fn command(self, cmd: &str) {
let ph = self.ph.ph;
let cmd = CString::new(cmd).unwrap();
unsafe {
((*ph).hexchat_command)(ph, cmd.as_ptr())
}
}
pub fn emit_print(self) {
unimplemented!()
}
pub fn emit_print_attrs(self) {
unimplemented!()
}
pub fn get_context(&mut self) -> Context {
self.ph.get_context()
}
pub fn set_context(&mut self, ctx: &Context) -> bool {
self.ph.set_context(ctx)
}
pub fn print(&mut self, s: &str) {
self.ph.print(s)
}
pub fn hook_command<F>(&mut self, cmd: &str, cb: F, pri: i32, help: Option<&str>) -> CommandHookHandle where F: Fn(&mut PluginHandle, Word, WordEol) -> Eat + 'static + ::std::panic::RefUnwindSafe {
self.ph.hook_command(cmd, cb, pri, help)
}
pub fn hook_server<F>(&mut self, cmd: &str, cb: F, pri: i32) -> ServerHookHandle where F: Fn(&mut PluginHandle, Word, WordEol) -> Eat + 'static + ::std::panic::RefUnwindSafe {
self.ph.hook_server(cmd, cb, pri)
}
pub fn hook_print<F>(&mut self, name: &str, cb: F, pri: i32) -> PrintHookHandle where F: Fn(&mut PluginHandle, Word) -> Eat + 'static + ::std::panic::RefUnwindSafe {
self.ph.hook_print(name, cb, pri)
}
pub fn hook_timer<F>(&mut self, timeout: i32, cb: F) -> TimerHookHandle where F: Fn(&mut PluginHandle) -> bool + 'static + ::std::panic::RefUnwindSafe {
self.ph.hook_timer(timeout, cb)
}
pub fn get_info(&mut self, id: &InfoId) -> Option<String> {
self.ph.get_info(id)
}
}
type CommandHookUd = (Box<Fn(&mut PluginHandle, Word, WordEol) -> Eat + ::std::panic::RefUnwindSafe>, *mut internals::Ph, PluginInfo);
type ServerHookUd = (Box<Fn(&mut PluginHandle, Word, WordEol) -> Eat + ::std::panic::RefUnwindSafe>, *mut internals::Ph, PluginInfo);
type PrintHookUd = (Box<Fn(&mut PluginHandle, Word) -> Eat + ::std::panic::RefUnwindSafe>, *mut internals::Ph, PluginInfo);
type TimerHookUd = (Rc<(Box<Fn(&mut PluginHandle) -> bool + ::std::panic::RefUnwindSafe>, *mut internals::Ph, PluginInfo)>, Rc<Cell<bool>>);
const EMPTY_CSTRING_DATA: &[u8] = b"\0";
fn cstr(b: &'static [u8]) -> *const libc::c_char {
CStr::from_bytes_with_nul(b).unwrap().as_ptr()
}
unsafe fn rc_clone_from_raw<T>(ptr: *const T) -> Rc<T> {
let rc = Rc::from_raw(ptr);
mem::forget(rc.clone());
rc
}
unsafe fn hexchat_print_str(ph: *mut internals::Ph, s: &str, panic_on_nul: bool) {
match CString::new(s) {
Result::Ok(cs @ _) => {
let csr: &CStr = &cs;
((*ph).hexchat_print)(ph, csr.as_ptr())
},
e @ _ => if panic_on_nul {e.unwrap();}, }
}
#[derive(Copy, Clone)]
struct PluginInfo {
name: *mut *const libc::c_char,
desc: *mut *const libc::c_char,
vers: *mut *const libc::c_char,
}
impl PluginInfo {
fn new(name: *mut *const libc::c_char, desc: *mut *const libc::c_char, vers: *mut *const libc::c_char) -> Option<PluginInfo> {
if name.is_null() || desc.is_null() || vers.is_null() || name == desc || desc == vers || name == vers {
None
} else {
Some(unsafe { PluginInfo::new_unchecked(name, desc, vers) })
}
}
unsafe fn new_unchecked(name: *mut *const libc::c_char, desc: *mut *const libc::c_char, vers: *mut *const libc::c_char) -> PluginInfo {
PluginInfo {
name, desc, vers
}
}
unsafe fn drop_info(&mut self) {
if !(*self.name).is_null() {
mem::drop(CString::from_raw(*self.name as *mut _));
*self.name = cstr(EMPTY_CSTRING_DATA);
}
if !(*self.desc).is_null() {
mem::drop(CString::from_raw(*self.desc as *mut _));
*self.desc = cstr(EMPTY_CSTRING_DATA);
}
if !(*self.vers).is_null() {
mem::drop(CString::from_raw(*self.vers as *mut _));
*self.vers = cstr(EMPTY_CSTRING_DATA);
}
}
}
struct PhUserdata {
plug: Box<Plugin>,
pluginfo: PluginInfo,
}
unsafe fn put_userdata(ph: *mut internals::Ph, ud: Box<PhUserdata>) {
(*ph).userdata = Box::into_raw(ud) as *mut libc::c_void;
}
unsafe fn pop_userdata(ph: *mut internals::Ph) -> Box<PhUserdata> {
Box::from_raw(mem::replace(&mut (*ph).userdata, ptr::null_mut()) as *mut PhUserdata)
}
#[doc(hidden)]
pub unsafe fn hexchat_plugin_init<T>(plugin_handle: *mut libc::c_void,
plugin_name: *mut *const libc::c_char,
plugin_desc: *mut *const libc::c_char,
plugin_version: *mut *const libc::c_char,
arg: *const libc::c_char) -> libc::c_int
where T: Plugin + Default + 'static {
if plugin_handle.is_null() || plugin_name.is_null() || plugin_desc.is_null() || plugin_version.is_null() {
eprintln!("hexchat_plugin_init called with a null pointer that shouldn't be null - broken hexchat");
return 0;
}
let ph = plugin_handle as *mut internals::Ph;
(*ph).userdata = ptr::null_mut();
let filename = if !(*plugin_name).is_null() {
if let Ok(fname) = CStr::from_ptr(*plugin_name).to_owned().into_string() {
fname
} else {
eprintln!("failed to convert filename to utf8 - broken hexchat");
return 0;
}
} else {
String::new() };
*plugin_name = ptr::null();
*plugin_desc = ptr::null();
*plugin_version = ptr::null();
{
let ver = ((*ph).hexchat_get_info)(ph, cstr(b"version\0")); let cstr = CStr::from_ptr(ver);
if let Ok(ver) = cstr.to_str() {
let mut iter = ver.split('.');
let a = iter.next().map(i32::from_str).and_then(Result::ok).unwrap_or(0);
let b = iter.next().map(i32::from_str).and_then(Result::ok).unwrap_or(0);
let c = iter.next().map(i32::from_str).and_then(Result::ok).unwrap_or(0);
if !(a > 2 || (a == 2 && (b > 9 || (b == 9 && (c > 6 || (c == 6)))))) {
return 0;
}
} else {
return 0;
}
}
let mut pluginfo = if let Some(pluginfo) = PluginInfo::new(plugin_name, plugin_desc, plugin_version) {
pluginfo
} else {
return 0;
};
let r: thread::Result<Option<Box<_>>> = {
catch_unwind(move || {
let mut pluginhandle = PluginHandle {
ph: ph,
info: pluginfo,
};
let plug = T::default();
if plug.init(&mut pluginhandle, if !arg.is_null() { Some(CStr::from_ptr(arg).to_str().expect("arg not valid utf-8 - broken hexchat")) } else { None }) {
if !(pluginfo.name.is_null() || pluginfo.desc.is_null() || pluginfo.vers.is_null()) {
Some(Box::new(PhUserdata { plug: Box::new(plug), pluginfo }))
} else {
None
}
} else {
None
}
})
};
match r {
Result::Ok(Option::Some(plug @ _)) => {
if (*plugin_name).is_null() || (*plugin_desc).is_null() || (*plugin_version).is_null() {
pluginfo.drop_info();
0
} else {
put_userdata(ph, plug);
1
}
},
r @ _ => {
if let Err(_) = r {
}
0
},
}
}
#[doc(hidden)]
pub unsafe fn hexchat_plugin_deinit<T>(plugin_handle: *mut libc::c_void) where T: Plugin {
if !plugin_handle.is_null() {
let ph = plugin_handle as *mut internals::Ph;
if !(*ph).userdata.is_null() {
{
let mut info: Option<PluginInfo> = None;
{
let mut ausinfo = ::std::panic::AssertUnwindSafe(&mut info);
catch_unwind(move || {
let userdata = *pop_userdata(ph);
**ausinfo = Some(userdata.pluginfo);
userdata.plug.deinit(&mut PluginHandle { ph, info: userdata.pluginfo });
}).ok();
}
if let Some(mut info) = info {
info.drop_info();
} else {
eprintln!("I have no idea tbh, I didn't know `pop_userdata` could panic!");
}
}
} else {
eprintln!("null userdata in hexchat_plugin_deinit - broken hexchat or broken hexchat-plugin.rs");
}
} else {
eprintln!("hexchat_plugin_deinit called with a null plugin_handle - broken hexchat");
}
}
#[macro_export]
macro_rules! hexchat_plugin {
($t:ty) => {
#[no_mangle]
pub unsafe extern "C" fn hexchat_plugin_init(plugin_handle: *mut $crate::libc::c_void,
plugin_name: *mut *const $crate::libc::c_char,
plugin_desc: *mut *const $crate::libc::c_char,
plugin_version: *mut *const $crate::libc::c_char,
arg: *const $crate::libc::c_char) -> $crate::libc::c_int {
$crate::hexchat_plugin_init::<$t>(plugin_handle, plugin_name, plugin_desc, plugin_version, arg)
}
#[no_mangle]
pub unsafe extern "C" fn hexchat_plugin_deinit(plugin_handle: *mut $crate::libc::c_void) {
$crate::hexchat_plugin_deinit::<$t>(plugin_handle);
}
};
}