Rust Bindings for Hexchat Plugin API
This commit is contained in:
		
						commit
						61da531d19
					
				
					 4 changed files with 404 additions and 0 deletions
				
			
		
							
								
								
									
										4
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
 | 
			
		||||
/target/
 | 
			
		||||
**/*.rs.bk
 | 
			
		||||
Cargo.lock
 | 
			
		||||
							
								
								
									
										7
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "hexchat-plugin"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
libc = "0.2"
 | 
			
		||||
							
								
								
									
										196
									
								
								src/internals.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/internals.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,196 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Hexchat Plugin API Bindings for Rust - Internals
 | 
			
		||||
 * Copyright (C) 2018 Soni L.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
//! Implementation details, mostly.
 | 
			
		||||
//!
 | 
			
		||||
//! This also includes the hexchat_plugin struct, from hexchat-plugin.h. Note that we use the
 | 
			
		||||
//! struct even on non-Windows platforms because it's a lot easier that way. Should be safe, tho.
 | 
			
		||||
use libc;
 | 
			
		||||
 | 
			
		||||
// apparently this is the right way to do these
 | 
			
		||||
#[repr(i8)]
 | 
			
		||||
pub enum HexchatList {
 | 
			
		||||
    __One,
 | 
			
		||||
    __Two,
 | 
			
		||||
}
 | 
			
		||||
#[repr(i8)]
 | 
			
		||||
pub enum HexchatHook {
 | 
			
		||||
    __One,
 | 
			
		||||
    __Two,
 | 
			
		||||
}
 | 
			
		||||
#[repr(i8)]
 | 
			
		||||
pub enum HexchatContext {
 | 
			
		||||
    __One,
 | 
			
		||||
    __Two,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// not in hexchat-plugin.h
 | 
			
		||||
#[repr(i8)]
 | 
			
		||||
pub enum PluginGuiHandle {
 | 
			
		||||
    __One,
 | 
			
		||||
    __Two,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct HexchatEventAttrs {
 | 
			
		||||
    server_time_utc: libc::time_t,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type HexchatPlugin = Ph;
 | 
			
		||||
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct Ph {
 | 
			
		||||
    pub hexchat_hook_command: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            pri: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            help_text: *const libc::c_char,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_hook_server: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            pri: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_hook_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            pri: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_hook_timer: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            timeout: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_hook_fd: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            fd: libc::c_int,
 | 
			
		||||
            flags: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(fd: libc::c_int, flags: libc::c_int, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_unhook: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            hook: *const HexchatHook) -> *const libc::c_void>,
 | 
			
		||||
    pub hexchat_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            text: *const libc::c_char)>,
 | 
			
		||||
    pub hexchat_printf: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            format: *const libc::c_char, ...)>,
 | 
			
		||||
    pub hexchat_command: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            command: *const libc::c_char)>,
 | 
			
		||||
    pub hexchat_commandf: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            format: *const libc::c_char, ...)>,
 | 
			
		||||
    pub hexchat_nickcmp: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            s1: *const libc::c_char,
 | 
			
		||||
            s2: *const libc::c_char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_set_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            ctx: *const HexchatContext) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_find_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            servname: *const libc::c_char,
 | 
			
		||||
            channel: *const libc::c_char) -> *const HexchatContext>,
 | 
			
		||||
    pub hexchat_get_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *const HexchatContext>,
 | 
			
		||||
    pub hexchat_get_info: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            id: *const libc::c_char) -> *const libc::c_char>,
 | 
			
		||||
    pub hexchat_get_prefs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            string: *mut *const libc::c_char,
 | 
			
		||||
            integer: *mut libc::c_int) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_list_get: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char) -> *const HexchatList>,
 | 
			
		||||
    pub hexchat_list_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            xlist: *const HexchatList)>,
 | 
			
		||||
    pub hexchat_list_fields: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char) -> *const *const libc::c_char>,
 | 
			
		||||
    pub hexchat_list_next: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            xlist: *const HexchatList) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_list_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            xlist: *const HexchatList,
 | 
			
		||||
            name: *const libc::c_char) -> *const libc::c_char>,
 | 
			
		||||
    pub hexchat_list_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            xlist: *const HexchatList,
 | 
			
		||||
            name: *const libc::c_char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_plugingui_add: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            filename: *const libc::c_char,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            desc: *const libc::c_char,
 | 
			
		||||
            version: *const libc::c_char,
 | 
			
		||||
            reserved: *mut char) -> *const PluginGuiHandle>,
 | 
			
		||||
    pub hexchat_plugingui_remove: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            handle: *const PluginGuiHandle)>,
 | 
			
		||||
    pub hexchat_emit_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            event_name: *const libc::c_char, ...) -> libc::c_int>,
 | 
			
		||||
    // this is VERY NAUGHTY.
 | 
			
		||||
    // TODO see if hexchat's gonna provide a proper userdata field at some point.
 | 
			
		||||
    // it appears this function isn't used anywhere by hexchat so we reuse its pointer.
 | 
			
		||||
    // on linux, it's a dummy anyway.
 | 
			
		||||
    // another option would've been to use one of the printf functions.
 | 
			
		||||
    // TODO test this on platforms hexchat doesn't build on, like AVR.
 | 
			
		||||
    pub userdata: *mut libc::c_void,
 | 
			
		||||
    /*pub hexchat_read_fd: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            src: *const libc::c_void,
 | 
			
		||||
            buf: *mut char,
 | 
			
		||||
            len: *mut libc::c_int) -> libc::c_int>,*/
 | 
			
		||||
    pub hexchat_list_time: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            xlist: *const HexchatList,
 | 
			
		||||
            name: *const libc::c_char) -> libc::time_t>,
 | 
			
		||||
    pub hexchat_gettext: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            msgid: *const libc::c_char) -> *const libc::c_char>,
 | 
			
		||||
    pub hexchat_send_modes: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            targets: *mut *const libc::c_char,
 | 
			
		||||
            ntargets: libc::c_int,
 | 
			
		||||
            modes_per_line: libc::c_int,
 | 
			
		||||
            sign: libc::c_char,
 | 
			
		||||
            mode: libc::c_char)>,
 | 
			
		||||
    pub hexchat_strip: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            string: *const libc::c_char,
 | 
			
		||||
            len: libc::c_int,
 | 
			
		||||
            flags: libc::c_int) -> *const libc::c_char>,
 | 
			
		||||
    pub hexchat_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            ptr: *const libc::c_void)>,
 | 
			
		||||
    pub hexchat_pluginpref_set_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            var: *const libc::c_char,
 | 
			
		||||
            value: *const libc::c_char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_pluginpref_get_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            var: *const libc::c_char,
 | 
			
		||||
            dest: *mut char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_pluginpref_set_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            var: *const libc::c_char,
 | 
			
		||||
            value: libc::c_int) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_pluginpref_get_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            var: *const libc::c_char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_pluginpref_delete: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            var: *const libc::c_char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_pluginpref_list: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            dest: *mut char) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_hook_server_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            pri: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_hook_print_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            name: *const libc::c_char,
 | 
			
		||||
            pri: libc::c_int,
 | 
			
		||||
            /* CALLBACK */
 | 
			
		||||
            callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int>,
 | 
			
		||||
            userdata: *mut libc::c_void) -> *const HexchatHook>,
 | 
			
		||||
    pub hexchat_emit_print_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin, attrs: *const HexchatEventAttrs,
 | 
			
		||||
            event_name: *const libc::c_char, ...) -> libc::c_int>,
 | 
			
		||||
    pub hexchat_event_attrs_create: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *mut HexchatEventAttrs>,
 | 
			
		||||
    pub hexchat_event_attrs_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
 | 
			
		||||
            attrs: *const HexchatEventAttrs)>,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										197
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,197 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Hexchat Plugin API Bindings for Rust
 | 
			
		||||
 * Copyright (C) 2018 Soni L.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as
 | 
			
		||||
 * published by the Free Software Foundation, either version 3 of the
 | 
			
		||||
 * License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
//! <!-- TODO (placeholder) -->
 | 
			
		||||
 | 
			
		||||
#[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;
 | 
			
		||||
 | 
			
		||||
const EMPTY_CSTRING_DATA: &[u8] = b"\0";
 | 
			
		||||
 | 
			
		||||
/// A hexchat plugin
 | 
			
		||||
pub trait Plugin {
 | 
			
		||||
    fn init(&mut self, &mut PluginHandle) -> bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A handle for a hexchat plugin
 | 
			
		||||
pub struct PluginHandle {
 | 
			
		||||
    ph: *mut internals::Ph,
 | 
			
		||||
    filename: Option<CString>,
 | 
			
		||||
    registered: bool,
 | 
			
		||||
    fakehandle: *const internals::PluginGuiHandle,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PluginHandle {
 | 
			
		||||
    pub fn register(&mut self, name: &str, desc: &str, ver: &str) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let ph = &mut *self.ph;
 | 
			
		||||
            if let Some(hexchat_plugingui_add) = ph.hexchat_plugingui_add {
 | 
			
		||||
                let filename = self.filename.take().expect("Attempt to re-register a plugin");
 | 
			
		||||
                let name = CString::new(name).unwrap();
 | 
			
		||||
                let desc = CString::new(desc).unwrap();
 | 
			
		||||
                let ver = CString::new(ver).unwrap();
 | 
			
		||||
                self.fakehandle = hexchat_plugingui_add(self.ph, filename.as_ptr(), name.as_ptr(), desc.as_ptr(), ver.as_ptr(), ::std::ptr::null_mut());
 | 
			
		||||
            }
 | 
			
		||||
            self.registered = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[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 {
 | 
			
		||||
    if plugin_handle.is_null() {
 | 
			
		||||
        // we can't really do anything here.
 | 
			
		||||
        eprintln!("hexchat_plugin_init called with a null plugin_handle. This is an error!");
 | 
			
		||||
        // TODO maybe call abort.
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    let ph = &mut *(plugin_handle as *mut internals::Ph);
 | 
			
		||||
    // clear the "userdata" field first thing - if the deinit function gets called (wrong hexchat
 | 
			
		||||
    // version, other issues), we don't wanna try to drop the hexchat_dummy or hexchat_read_fd
 | 
			
		||||
    // function as if it were a Box!
 | 
			
		||||
    ph.userdata = ::std::ptr::null_mut();
 | 
			
		||||
    // we set these to empty strings because rust strings aren't nul-terminated. which means we
 | 
			
		||||
    // need to *allocate* nul-terminated strings for the real values, and hexchat has no way of
 | 
			
		||||
    // freeing these.
 | 
			
		||||
    // BUT before we set them, read the filename off plugin_name - we'll need it!
 | 
			
		||||
    // (TODO figure out how to make this NOT break the plugins list)
 | 
			
		||||
    let filename = CStr::from_ptr(*plugin_name).to_owned();
 | 
			
		||||
    let empty_cstr = CStr::from_bytes_with_nul_unchecked(EMPTY_CSTRING_DATA).as_ptr();
 | 
			
		||||
    *plugin_name = empty_cstr;
 | 
			
		||||
    *plugin_desc = empty_cstr;
 | 
			
		||||
    *plugin_version = empty_cstr;
 | 
			
		||||
    // do some version checks for safety
 | 
			
		||||
    if let Some(hexchat_get_info) = ph.hexchat_get_info {
 | 
			
		||||
        let ver: *const libc::c_char = hexchat_get_info(ph, CStr::from_bytes_with_nul_unchecked(b"version\0").as_ptr());
 | 
			
		||||
        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);
 | 
			
		||||
            let b = iter.next().map(i32::from_str).and_then(Result::ok);
 | 
			
		||||
            let c = iter.next().map(i32::from_str).and_then(Result::ok);
 | 
			
		||||
            if !match a.unwrap_or(0) {
 | 
			
		||||
                0 | 1 => false,
 | 
			
		||||
                2 => match b.unwrap_or(0) {
 | 
			
		||||
                    // range patterns are a bit broken
 | 
			
		||||
                    0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 => false,
 | 
			
		||||
                    9 => match c.unwrap_or(0) {
 | 
			
		||||
                        0 | 1 | 2 | 3 | 4 | 5 => false,
 | 
			
		||||
                        6 => 
 | 
			
		||||
                            // min acceptable version = 2.9.6
 | 
			
		||||
                            true,
 | 
			
		||||
                        _ => true,
 | 
			
		||||
                    },
 | 
			
		||||
                    _ => true,
 | 
			
		||||
                },
 | 
			
		||||
                _ => true,
 | 
			
		||||
            } {
 | 
			
		||||
                // TODO print: "error loading plugin: hexchat too old"?
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let r: thread::Result<Option<Box<_>>> = catch_unwind(|| {
 | 
			
		||||
        let mut plug = T::default();
 | 
			
		||||
        let mut pluginhandle = PluginHandle {
 | 
			
		||||
            ph: plugin_handle as *mut _,
 | 
			
		||||
            registered: false,
 | 
			
		||||
            filename: Some(filename),
 | 
			
		||||
            fakehandle: ::std::ptr::null(),
 | 
			
		||||
        };
 | 
			
		||||
        if plug.init(&mut pluginhandle) {
 | 
			
		||||
            if pluginhandle.registered {
 | 
			
		||||
                Some(Box::new((plug, pluginhandle.fakehandle)))
 | 
			
		||||
            } else {
 | 
			
		||||
                // TODO log: forgot to call register
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    if r.is_ok() {
 | 
			
		||||
        if let Ok(Some(plug)) = r {
 | 
			
		||||
            ph.userdata = Box::into_raw(plug) as *mut libc::c_void;
 | 
			
		||||
            1
 | 
			
		||||
        } else {
 | 
			
		||||
            0
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        // TODO try to log panic?
 | 
			
		||||
        0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[doc(hidden)]
 | 
			
		||||
pub unsafe fn hexchat_plugin_deinit<T>(plugin_handle: *mut libc::c_void) where T: Plugin {
 | 
			
		||||
    // plugin_handle should never be null, but just in case...
 | 
			
		||||
    if !plugin_handle.is_null() {
 | 
			
		||||
        let ph = &mut *(plugin_handle as *mut internals::Ph);
 | 
			
		||||
        if !ph.userdata.is_null() {
 | 
			
		||||
            // 
 | 
			
		||||
            let userdata = ph.userdata;
 | 
			
		||||
            ph.userdata = ::std::ptr::null_mut();
 | 
			
		||||
            // we use an explicit drop (instead of an implicit one) so this is less confusing/weird
 | 
			
		||||
            // to read.
 | 
			
		||||
            catch_unwind(|| {
 | 
			
		||||
                let ph = &mut *(plugin_handle as *mut internals::Ph);
 | 
			
		||||
                let (plug, fakehandle) = *Box::from_raw(userdata as *mut (T, *const internals::PluginGuiHandle));
 | 
			
		||||
                if let Some(hexchat_plugingui_remove) = ph.hexchat_plugingui_remove {
 | 
			
		||||
                    hexchat_plugingui_remove(ph, fakehandle);
 | 
			
		||||
                }
 | 
			
		||||
                ::std::mem::drop(plug); // suppress compiler warnings
 | 
			
		||||
            }).ok();
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        eprintln!("hexchat_plugin_deinit called with a null plugin_handle. This is an error!");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Exports a hexchat plugin.
 | 
			
		||||
#[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);
 | 
			
		||||
        }
 | 
			
		||||
        // unlike what the documentation states, there's no need to define hexchat_plugin_get_info.
 | 
			
		||||
        // so we don't. it'd be impossible to make it work well with rust anyway.
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue