From 42e6fa41a8f3903e2b8b05baf212b6d2c405d950 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Sat, 26 Jun 2021 12:53:52 -0300 Subject: Clean up a bit --- src/lib.rs | 211 +++++++++++-------------------------------------------------- 1 file changed, 37 insertions(+), 174 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 465ff5e..b64cac4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,13 +158,21 @@ #[doc(hidden)] pub extern crate libc; +mod eat; +mod infoid; mod internals; +mod pluginfo; + +pub use eat::*; +pub use infoid::InfoId; + +use pluginfo::PluginInfo; -use std::borrow::Cow; use std::cell::Cell; use std::ffi::{CString, CStr}; use std::marker::PhantomData; use std::mem; +use std::mem::ManuallyDrop; use std::ops; use std::panic::catch_unwind; use std::ptr; @@ -180,16 +188,6 @@ use std::time::{SystemTime, UNIX_EPOCH, Duration}; // Consts -// EAT_* -/// Equivalent to HEXCHAT_EAT_NONE. -pub const EAT_NONE: Eat = Eat { do_eat: 0 }; -/// Equivalent to HEXCHAT_EAT_HEXCHAT. -pub const EAT_HEXCHAT: Eat = Eat { do_eat: 1 }; -/// Equivalent to HEXCHAT_EAT_PLUGIN. -pub const EAT_PLUGIN: Eat = Eat { do_eat: 2 }; -/// Equivalent to HEXCHAT_EAT_ALL. -pub const EAT_ALL: Eat = Eat { do_eat: 1 | 2 }; - // PRI_* /// Equivalent to HEXCHAT_PRI_HIGHEST pub const PRI_HIGHEST: i32 = 127; @@ -212,7 +210,8 @@ pub trait Plugin { /// Called to deinitialize the plugin. /// /// This is always called immediately prior to Drop::drop. - fn deinit(&self, _ph: &mut PluginHandle) { + fn deinit(&self, ph: &mut PluginHandle) { + let _ = ph; } } @@ -305,15 +304,6 @@ pub struct EventAttrs<'a> { _dummy: PhantomData<&'a ()>, } -/// A status indicator for event callbacks. Indicates whether to continue processing, eat hexchat, -/// eat plugin, or eat all. -/// -/// Returned by most hooks. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct Eat { - do_eat: i32, -} - /// A command hook handle. #[must_use = "Hooks must be stored somewhere and are automatically unhooked on Drop"] pub struct CommandHookHandle { @@ -364,77 +354,10 @@ pub struct InvalidContextError R, R>(F); // Enums -/// A hexchat_get_info key. -#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Copy, Clone)] -pub enum InfoId<'a> { - /// Returns the away message, or `None` if the user is not away. - Away, - /// Returns the current channel name. - Channel, - /// Returns the current charset. - Charset, - /// Returns the hexchat configuration directory, e.g. `/home/user/.config/hexchat`. - Configdir, - /// Returns the text event format string for the given text event name. - EventText(&'a str), - /// Returns the (real) hostname of the current server. - Host, - /// Returns the contents of the input box. - Inputbox, - /// Returns the library directory, e.g. `/usr/lib/hexchat`. - /// - /// May not always work, as this string isn't necessarily UTF-8, but local file system - /// encoding. - Libdirfs, - /// Returns the channel modes, if known, or `None`. - Modes, - /// Returns the current network name, or `None`. - Network, - /// Returns the user's current nick. - Nick, - /// Returns the user's nickserv password, if any, or `None` - Nickserv, - /// Returns the current server name, or `None` if you are not connected. - Server, - /// Returns the current channel topic. - Topic, - /// Returns the HexChat version string. - Version, - /// Returns the window status: "active", "hidden" or "normal". - WinStatus, -} - // ***** // // impls // // ***** // -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 R, R> InvalidContextError { pub fn get_closure(self) -> F { self.0 @@ -961,10 +884,7 @@ impl PluginHandle { } /// Returns information on the current context. - /// - /// Note: `InfoId::Libdirfs` may return `None` or broken results if the result wouldn't be (valid) UTF-8. - // TODO this should be `id: InfoId`. fix in 0.3 - pub fn get_info(&mut self, id: &InfoId) -> Option { + pub fn get_info(&mut self, id: InfoId) -> Option { let ph = self.ph; let id_cstring = CString::new(&*id.name()).unwrap(); unsafe { @@ -973,11 +893,7 @@ impl PluginHandle { 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() - } + Some(s.expect("non-utf8 word - broken hexchat")) } } } @@ -1206,7 +1122,7 @@ impl<'a> EnsureValidContext<'a> { pub fn hook_timer(&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 { + pub fn get_info(&mut self, id: InfoId) -> Option { self.ph.get_info(id) } } @@ -1252,12 +1168,8 @@ fn cstr(b: &'static [u8]) -> *const libc::c_char { /// /// This function is unsafe because `ptr` must hame come from `Rc::into_raw`. unsafe fn rc_clone_from_raw(ptr: *const T) -> Rc { - // this is a bit confusing to read, but basically, we get an Rc from the raw ptr, and increment - // the refcount. - // The construct mem::forget(rc.clone()) increments the refcount. - let rc = Rc::from_raw(ptr); - mem::forget(rc.clone()); - rc + let rc = ManuallyDrop::new(Rc::from_raw(ptr)); + Rc::clone(&rc) } /// Converts a **valid** context pointer into a Context (Rust-managed) struct. @@ -1330,67 +1242,6 @@ impl Drop for HexchatEventAttrsHelper { } } -/// Holds name, desc, vers -// This is kinda naughty - we modify these values after returning from hexchat_plugin_init, during -// the deinitialization. -// However, if my reading of the HexChat code is correct, this is "ok". -#[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 { - /// Creates a PluginInfo. - /// - /// # Panics - /// - /// This function explicitly doesn't panic. Call unwrap() on the result instead. - fn new(name: *mut *const libc::c_char, desc: *mut *const libc::c_char, vers: *mut *const libc::c_char) -> Option { - 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) }) - } - } - - /// Creates a PluginInfo without checking the arguments. - /// - /// # Safety - /// - /// This function is unsafe, as it doesn't check the validity of the arguments. You're expected - /// to only pass in non-aliased non-null pointers. Use new if unsure. - 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 - } - } - - /// Drop relevant CStrings. - /// - /// # Safety - /// - /// This function is unsafe because calling it may trigger Undefined Behaviour. See also - /// [CString::from_raw]. - /// - /// [from_raw]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.from_raw - 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); - } - } -} - /// Plugin data stored in the hexchat plugin_handle. struct PhUserdata { plug: Box, @@ -1404,12 +1255,22 @@ struct PhUserdata { /// This function is unsafe because it doesn't check if the pointer is valid. /// /// Improper use of this function can leak memory. -unsafe fn put_userdata(ph: *mut internals::Ph, ud: Box) { - (*ph).userdata = Box::into_raw(ud) as *mut libc::c_void; +unsafe fn put_userdata(ph: *mut internals::Ph, ud: Rc) { + (*ph).userdata = Rc::into_raw(ud) as *mut libc::c_void; } -// unsafe fn get_userdata(ph: *mut internals::Ph) -> *const PhUserdata { -// (*ph).userdata as *const _ +// /// Clones the userdata from the plugin handle. +// /// +// /// # Safety +// /// +// /// This function is unsafe because it doesn't check if the pointer is valid. +// unsafe fn clone_userdata(ph: *mut internals::Ph) -> Option> { +// let ptr = (*ph).userdata as *const PhUserdata; +// if ptr.is_null() { +// None +// } else { +// Some(rc_clone_from_raw(ptr)) +// } // } /// Pops the userdata from the plugin handle. @@ -1417,8 +1278,8 @@ unsafe fn put_userdata(ph: *mut internals::Ph, ud: Box) { /// # Safety /// /// This function is unsafe because it doesn't check if the pointer is valid. -unsafe fn pop_userdata(ph: *mut internals::Ph) -> Box { - Box::from_raw(mem::replace(&mut (*ph).userdata, ptr::null_mut()) as *mut PhUserdata) +unsafe fn pop_userdata(ph: *mut internals::Ph) -> Rc { + Rc::from_raw(mem::replace(&mut (*ph).userdata, ptr::null_mut()) as *mut PhUserdata) } // *********************** // @@ -1455,6 +1316,8 @@ pub unsafe fn hexchat_plugin_init(plugin_handle: *mut libc::c_void, // no filename specified for some reason, but we can still load String::new() // empty string }; + // TODO use filename + let _ = filename; // these may be null, unless initialization is successful. // we set them to null as markers. *plugin_name = ptr::null(); @@ -1484,13 +1347,13 @@ pub unsafe fn hexchat_plugin_init(plugin_handle: *mut libc::c_void, } else { return 0; }; - let r: thread::Result>> = { + let r: thread::Result>> = { catch_unwind(move || { let mut pluginhandle = PluginHandle::new(ph, 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 })) + Some(Rc::new(PhUserdata { plug: Box::new(plug), pluginfo })) } else { // TODO log: forgot to call register None @@ -1538,7 +1401,7 @@ pub unsafe fn hexchat_plugin_deinit(plugin_handle: *mut libc::c_void) -> libc { let mut ausinfo = ::std::panic::AssertUnwindSafe(&mut info); safe_to_unload = if catch_unwind(move || { - let userdata = *pop_userdata(ph); + let userdata = pop_userdata(ph); **ausinfo = Some(userdata.pluginfo); userdata.plug.deinit(&mut PluginHandle::new(ph, userdata.pluginfo)); }).is_ok() { 1 } else { 0 }; -- cgit 1.4.1