Page Menu
Home
Solus
Search
Configure Global Search
Log In
Files
F11050982
D11931.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
131 KB
Referenced Files
None
Subscribers
None
D11931.diff
View Options
diff --git a/abi_symbols b/abi_symbols
--- a/abi_symbols
+++ b/abi_symbols
@@ -1,14 +1,30 @@
checksum.so:hexchat_plugin_deinit
checksum.so:hexchat_plugin_init
+fishlim.so:base64_fish_len
+fishlim.so:base64_len
+fishlim.so:cbc_len
+fishlim.so:decrypt_raw_message
fishlim.so:dh1080_compute_key
fishlim.so:dh1080_deinit
fishlim.so:dh1080_generate_key
fishlim.so:dh1080_init
+fishlim.so:ecb_len
+fishlim.so:encoded_len
+fishlim.so:fish_base64_decode
+fishlim.so:fish_base64_encode
+fishlim.so:fish_cipher
fishlim.so:fish_decrypt
fishlim.so:fish_decrypt_from_nick
+fishlim.so:fish_decrypt_str
fishlim.so:fish_encrypt
fishlim.so:fish_encrypt_for_nick
+fishlim.so:fish_nick_has_key
+fishlim.so:foreach_utf8_data_chunks
fishlim.so:get_config_filename
+fishlim.so:get_my_info
+fishlim.so:get_my_own_host
+fishlim.so:get_my_own_prefix
+fishlim.so:get_prefix_length
fishlim.so:hexchat_plugin_deinit
fishlim.so:hexchat_plugin_get_info
fishlim.so:hexchat_plugin_init
@@ -18,6 +34,9 @@
fishlim.so:keystore_delete_nick
fishlim.so:keystore_get_key
fishlim.so:keystore_store_key
+fishlim.so:max_text_command_len
+hexchat:CHANNEL_FLAG_BALLOON
+hexchat:CHANNEL_FLAG_BALLOON_UNSET
hexchat:PrintText
hexchat:PrintTextRaw
hexchat:PrintTextTimeStamp
@@ -426,7 +445,6 @@
hexchat:languages
hexchat:lastact_getfirst
hexchat:lastact_update
-hexchat:libproxy_factory
hexchat:list_addentry
hexchat:list_delentry
hexchat:list_free
@@ -460,6 +478,7 @@
hexchat:menu_streq
hexchat:menu_toggle_item
hexchat:menu_urlmenu
+hexchat:message_tags_data_free
hexchat:mg_apply_setup
hexchat:mg_bring_tofront
hexchat:mg_bring_tofront_sess
diff --git a/abi_used_libs b/abi_used_libs
--- a/abi_used_libs
+++ b/abi_used_libs
@@ -12,11 +12,9 @@
libgobject-2.0.so.0
libgtk-x11-2.0.so.0
libluajit-5.1.so.2
-libnotify.so.4
libpango-1.0.so.0
libpci.so.3
libperl.so
-libproxy.so.1
libpthread.so.0
libpython3.9.so.1.0
libssl.so.1.1
diff --git a/abi_used_symbols b/abi_used_symbols
--- a/abi_used_symbols
+++ b/abi_used_symbols
@@ -124,9 +124,6 @@
libcrypto.so.1.1:ASN1_STRING_length
libcrypto.so.1.1:ASN1_STRING_type
libcrypto.so.1.1:ASN1_TIME_print
-libcrypto.so.1.1:BF_decrypt
-libcrypto.so.1.1:BF_encrypt
-libcrypto.so.1.1:BF_set_key
libcrypto.so.1.1:BIO_ctrl
libcrypto.so.1.1:BIO_free
libcrypto.so.1.1:BIO_new
@@ -148,8 +145,17 @@
libcrypto.so.1.1:DHparams_dup
libcrypto.so.1.1:ERR_error_string
libcrypto.so.1.1:ERR_get_error
+libcrypto.so.1.1:EVP_CIPHER_CTX_free
+libcrypto.so.1.1:EVP_CIPHER_CTX_new
+libcrypto.so.1.1:EVP_CIPHER_CTX_set_key_length
+libcrypto.so.1.1:EVP_CIPHER_CTX_set_padding
+libcrypto.so.1.1:EVP_CipherFinal_ex
+libcrypto.so.1.1:EVP_CipherInit_ex
+libcrypto.so.1.1:EVP_CipherUpdate
libcrypto.so.1.1:EVP_PKEY_bits
libcrypto.so.1.1:EVP_PKEY_free
+libcrypto.so.1.1:EVP_bf_cbc
+libcrypto.so.1.1:EVP_bf_ecb
libcrypto.so.1.1:EVP_sha256
libcrypto.so.1.1:GENERAL_NAME_free
libcrypto.so.1.1:HMAC
@@ -160,6 +166,7 @@
libcrypto.so.1.1:OPENSSL_sk_pop_free
libcrypto.so.1.1:OPENSSL_sk_value
libcrypto.so.1.1:OPENSSL_sk_zero
+libcrypto.so.1.1:RAND_bytes
libcrypto.so.1.1:SHA256
libcrypto.so.1.1:SHA256_Final
libcrypto.so.1.1:SHA256_Init
@@ -234,7 +241,9 @@
libgdk-x11-2.0.so.0:gdk_x11_display_get_xdisplay
libgdk-x11-2.0.so.0:gdk_x11_display_string_to_compound_text
libgdk-x11-2.0.so.0:gdk_x11_free_compound_text
+libgdk_pixbuf-2.0.so.0:gdk_pixbuf_get_height
libgdk_pixbuf-2.0.so.0:gdk_pixbuf_get_type
+libgdk_pixbuf-2.0.so.0:gdk_pixbuf_get_width
libgdk_pixbuf-2.0.so.0:gdk_pixbuf_new_from_file
libgdk_pixbuf-2.0.so.0:gdk_pixbuf_new_from_resource
libgdk_pixbuf-2.0.so.0:gdk_pixbuf_scale_simple
@@ -244,7 +253,10 @@
libgio-2.0.so.0:g_data_input_stream_read_line_finish
libgio-2.0.so.0:g_data_input_stream_read_line_utf8
libgio-2.0.so.0:g_data_input_stream_set_newline_type
+libgio-2.0.so.0:g_dbus_proxy_call
+libgio-2.0.so.0:g_dbus_proxy_call_finish
libgio-2.0.so.0:g_dbus_proxy_call_sync
+libgio-2.0.so.0:g_dbus_proxy_new_for_bus_sync
libgio-2.0.so.0:g_dbus_proxy_new_sync
libgio-2.0.so.0:g_file_append_to
libgio-2.0.so.0:g_file_get_parent
@@ -276,6 +288,8 @@
libgio-2.0.so.0:g_output_stream_write
libgio-2.0.so.0:g_output_stream_write_async
libgio-2.0.so.0:g_output_stream_write_finish
+libgio-2.0.so.0:g_proxy_resolver_get_default
+libgio-2.0.so.0:g_proxy_resolver_lookup
libgio-2.0.so.0:g_resolver_free_addresses
libgio-2.0.so.0:g_resolver_get_default
libgio-2.0.so.0:g_resolver_get_type
@@ -298,7 +312,9 @@
libglib-2.0.so.0:g_ascii_strncasecmp
libglib-2.0.so.0:g_ascii_strtoll
libglib-2.0.so.0:g_ascii_strtoull
+libglib-2.0.so.0:g_ascii_tolower
libglib-2.0.so.0:g_assertion_message_expr
+libglib-2.0.so.0:g_base64_decode
libglib-2.0.so.0:g_base64_decode_inplace
libglib-2.0.so.0:g_base64_encode
libglib-2.0.so.0:g_build_filename
@@ -366,15 +382,14 @@
libglib-2.0.so.0:g_key_file_new
libglib-2.0.so.0:g_key_file_remove_group
libglib-2.0.so.0:g_key_file_save_to_file
+libglib-2.0.so.0:g_key_file_set_integer
libglib-2.0.so.0:g_key_file_set_string
libglib-2.0.so.0:g_list_append
libglib-2.0.so.0:g_list_delete_link
libglib-2.0.so.0:g_list_find
-libglib-2.0.so.0:g_list_find_custom
libglib-2.0.so.0:g_list_first
libglib-2.0.so.0:g_list_foreach
libglib-2.0.so.0:g_list_free
-libglib-2.0.so.0:g_list_free_full
libglib-2.0.so.0:g_list_index
libglib-2.0.so.0:g_list_last
libglib-2.0.so.0:g_list_length
@@ -477,6 +492,7 @@
libglib-2.0.so.0:g_string_insert_c
libglib-2.0.so.0:g_string_new
libglib-2.0.so.0:g_string_new_len
+libglib-2.0.so.0:g_string_printf
libglib-2.0.so.0:g_string_sized_new
libglib-2.0.so.0:g_string_truncate
libglib-2.0.so.0:g_strjoinv
@@ -498,6 +514,7 @@
libglib-2.0.so.0:g_unichar_isdigit
libglib-2.0.so.0:g_unichar_to_utf8
libglib-2.0.so.0:g_unlink
+libglib-2.0.so.0:g_uri_escape_string
libglib-2.0.so.0:g_utf8_casefold
libglib-2.0.so.0:g_utf8_collate
libglib-2.0.so.0:g_utf8_collate_key
@@ -511,10 +528,18 @@
libglib-2.0.so.0:g_utf8_strlen
libglib-2.0.so.0:g_utf8_strncpy
libglib-2.0.so.0:g_utf8_strrchr
+libglib-2.0.so.0:g_utf8_validate
+libglib-2.0.so.0:g_variant_builder_add
+libglib-2.0.so.0:g_variant_builder_close
+libglib-2.0.so.0:g_variant_builder_end
+libglib-2.0.so.0:g_variant_builder_init
+libglib-2.0.so.0:g_variant_builder_open
+libglib-2.0.so.0:g_variant_get
libglib-2.0.so.0:g_variant_get_boolean
libglib-2.0.so.0:g_variant_get_child_value
libglib-2.0.so.0:g_variant_new_string
libglib-2.0.so.0:g_variant_new_tuple
+libglib-2.0.so.0:g_variant_type_checked_
libglib-2.0.so.0:g_variant_unref
libglib-2.0.so.0:g_vsnprintf
libglib-2.0.so.0:g_warn_message
@@ -1074,13 +1099,6 @@
libluajit-5.1.so.2:lua_touserdata
libluajit-5.1.so.2:lua_type
libluajit-5.1.so.2:lua_typename
-libnotify.so.4:notify_get_server_caps
-libnotify.so.4:notify_init
-libnotify.so.4:notify_is_initted
-libnotify.so.4:notify_notification_new
-libnotify.so.4:notify_notification_set_hint
-libnotify.so.4:notify_notification_show
-libnotify.so.4:notify_uninit
libpango-1.0.so.0:pango_attr_background_new
libpango-1.0.so.0:pango_attr_foreground_new
libpango-1.0.so.0:pango_attr_iterator_destroy
@@ -1095,6 +1113,7 @@
libpango-1.0.so.0:pango_attr_list_ref
libpango-1.0.so.0:pango_attr_list_unref
libpango-1.0.so.0:pango_attr_scale_new
+libpango-1.0.so.0:pango_attr_strikethrough_new
libpango-1.0.so.0:pango_attr_style_new
libpango-1.0.so.0:pango_attr_underline_color_new
libpango-1.0.so.0:pango_attr_underline_new
@@ -1173,9 +1192,6 @@
libperl.so:perl_destruct
libperl.so:perl_free
libperl.so:perl_parse
-libproxy.so.1:px_proxy_factory_free
-libproxy.so.1:px_proxy_factory_get_proxies
-libproxy.so.1:px_proxy_factory_new
libpthread.so.0:__errno_location
libpthread.so.0:accept
libpthread.so.0:close
diff --git a/files/0005-Default-to-Solus.patch b/files/0002-Default-to-Solus.patch
rename from files/0005-Default-to-Solus.patch
rename to files/0002-Default-to-Solus.patch
diff --git a/files/0002-Use-pango_font_metrics_get_height-to-calculate-font-height.patch b/files/0002-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
deleted file mode 100644
--- a/files/0002-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-commit 163608d7fd861c2c4911a38f45be484c88626bdc
-Author: John Levon <levon@movementarian.org>
-Date: Mon Sep 7 17:53:31 2020 +0100
-
- Use pango_font_metrics_get_height() to calculate font height (#2500)
-
-diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
-index fac0c4e6..418bb4da 100644
---- a/src/fe-gtk/xtext.c
-+++ b/src/fe-gtk/xtext.c
-@@ -283,8 +283,24 @@ backend_font_open (GtkXText *xtext, char *name)
- metrics = pango_context_get_metrics (context, xtext->font->font, lang);
- xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
- xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
-+
-+ /*
-+ * In later versions of pango, a font's height should be calculated like
-+ * this to account for line gap; a typical symptom of not doing so is
-+ * cutting off the underscore on some fonts.
-+ */
-+#if PANGO_VERSION_CHECK(1, 44, 0)
-+ xtext->fontsize = pango_font_metrics_get_height (metrics) / PANGO_SCALE + 1;
-+
-+ if (xtext->fontsize == 0)
-+ xtext->fontsize = xtext->font->ascent + xtext->font->descent;
-+#else
-+ xtext->fontsize = xtext->font->ascent + xtext->font->descent;
-+#endif
-+
- pango_font_metrics_unref (metrics);
- }
-+
- static int
- backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis)
- {
-@@ -3479,8 +3495,6 @@ gtk_xtext_set_font (GtkXText *xtext, char *name)
- if (xtext->font == NULL)
- return FALSE;
-
-- xtext->fontsize = xtext->font->ascent + xtext->font->descent;
--
- {
- char *time_str;
- int stamp_size = xtext_get_stamp_str (time(0), &time_str);
diff --git a/files/0003-Add-Libera-Chat-to-network-list.patch b/files/0003-Add-Libera-Chat-to-network-list.patch
deleted file mode 100644
--- a/files/0003-Add-Libera-Chat-to-network-list.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-From a25f2381689d2c2279a0e43b33f6c0ec8305a096 Mon Sep 17 00:00:00 2001
-From: Panagiotis Vasilopoulos <hello@alwayslivid.com>
-Date: Wed, 19 May 2021 16:25:16 +0300
-Subject: [PATCH] Add Libera Chat to network list
-
----
- src/common/servlist.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/common/servlist.c b/src/common/servlist.c
-index e44a3f2df..33bd80f2d 100644
---- a/src/common/servlist.c
-+++ b/src/common/servlist.c
-@@ -239,6 +239,9 @@ static const struct defaultserver def[] =
- /* Self signed */
- {0, "irc.librairc.net"},
-
-+ {"Libera Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
-+ {0, "irc.libera.chat"},
-+
- #ifdef USE_OPENSSL
- {"LinkNet", 0},
- {0, "irc.link-net.org/+7000"},
diff --git a/files/0004-Change-default-network-to-libera-chat.patch b/files/0004-Change-default-network-to-libera-chat.patch
deleted file mode 100644
--- a/files/0004-Change-default-network-to-libera-chat.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From d3545f37cd5f551ed8bc0ab7b20e5c8140adc0a6 Mon Sep 17 00:00:00 2001
-From: Patrick Griffis <pgriffis@igalia.com>
-Date: Sun, 23 May 2021 21:15:43 -0500
-Subject: [PATCH] Change default network to Libera.Chat
-
----
- src/common/servlist.c | 4 ++--
- src/fe-gtk/joind.c | 2 +-
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/src/common/servlist.c b/src/common/servlist.c
-index de75c8b9a..93557f975 100644
---- a/src/common/servlist.c
-+++ b/src/common/servlist.c
-@@ -240,7 +240,7 @@ static const struct defaultserver def[] =
- /* Self signed */
- {0, "irc.librairc.net"},
-
-- {"Libera Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
-+ {"Libera.Chat", 0, 0, 0, LOGIN_SASL, 0, TRUE},
- {0, "irc.libera.chat"},
-
- #ifdef USE_OPENSSL
-@@ -942,7 +942,7 @@ servlist_load_defaults (void)
- {
- int i = 0, j = 0;
- ircnet *net = NULL;
-- guint def_hash = g_str_hash ("freenode");
-+ guint def_hash = g_str_hash ("Libera.Chat");
-
- while (1)
- {
-diff --git a/src/fe-gtk/joind.c b/src/fe-gtk/joind.c
-index f1d3da504..ce3cbcaec 100644
---- a/src/fe-gtk/joind.c
-+++ b/src/fe-gtk/joind.c
-@@ -247,7 +247,7 @@ joind_show_dialog (server *serv)
- G_CALLBACK (joind_ok_cb), serv);
-
- if (serv->network)
-- if (g_ascii_strcasecmp(((ircnet*)serv->network)->name, "freenode") == 0)
-+ if (g_ascii_strcasecmp(((ircnet*)serv->network)->name, "Libera.Chat") == 0)
- {
- gtk_entry_set_text (GTK_ENTRY (entry1), "#hexchat");
- }
diff --git a/files/0006-python-cffi.patch b/files/0006-python-cffi.patch
deleted file mode 100644
--- a/files/0006-python-cffi.patch
+++ /dev/null
@@ -1,4034 +0,0 @@
-diff --git a/meson.build b/meson.build
-index 18baf26e..645e685e 100644
---- a/meson.build
-+++ b/meson.build
-@@ -49,6 +49,10 @@ config_h.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_34')
- config_h.set('HAVE_MEMRCHR', cc.has_function('memrchr'))
- config_h.set('HAVE_STRINGS_H', cc.has_header('strings.h'))
-
-+config_h.set_quoted('HEXCHATLIBDIR',
-+ join_paths(get_option('prefix'), get_option('libdir'), 'hexchat/plugins')
-+)
-+
- if libssl_dep.found()
- config_h.set('HAVE_X509_GET_SIGNATURE_NID',
- cc.has_function('X509_get_signature_nid', dependencies: libssl_dep)
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-new file mode 100644
-index 00000000..567b3493
---- /dev/null
-+++ b/plugins/python/_hexchat.py
-@@ -0,0 +1,386 @@
-+import inspect
-+import sys
-+from contextlib import contextmanager
-+
-+from _hexchat_embedded import ffi, lib
-+
-+__all__ = [
-+ 'EAT_ALL', 'EAT_HEXCHAT', 'EAT_NONE', 'EAT_PLUGIN', 'EAT_XCHAT',
-+ 'PRI_HIGH', 'PRI_HIGHEST', 'PRI_LOW', 'PRI_LOWEST', 'PRI_NORM',
-+ '__doc__', '__version__', 'command', 'del_pluginpref', 'emit_print',
-+ 'find_context', 'get_context', 'get_info',
-+ 'get_list', 'get_lists', 'get_pluginpref', 'get_prefs', 'hook_command',
-+ 'hook_print', 'hook_print_attrs', 'hook_server', 'hook_server_attrs',
-+ 'hook_timer', 'hook_unload', 'list_pluginpref', 'nickcmp', 'prnt',
-+ 'set_pluginpref', 'strip', 'unhook',
-+]
-+
-+__doc__ = 'HexChat Scripting Interface'
-+__version__ = (2, 0)
-+__license__ = 'GPL-2.0+'
-+
-+EAT_NONE = 0
-+EAT_HEXCHAT = 1
-+EAT_XCHAT = EAT_HEXCHAT
-+EAT_PLUGIN = 2
-+EAT_ALL = EAT_HEXCHAT | EAT_PLUGIN
-+
-+PRI_LOWEST = -128
-+PRI_LOW = -64
-+PRI_NORM = 0
-+PRI_HIGH = 64
-+PRI_HIGHEST = 127
-+
-+
-+# We need each module to be able to reference their parent plugin
-+# which is a bit tricky since they all share the exact same module.
-+# Simply navigating up to what module called it seems to actually
-+# be a fairly reliable and simple method of doing so if ugly.
-+def __get_current_plugin():
-+ frame = inspect.stack()[1][0]
-+ while '__plugin' not in frame.f_globals:
-+ frame = frame.f_back
-+ assert frame is not None
-+
-+ return frame.f_globals['__plugin']
-+
-+
-+# Keeping API compat
-+if sys.version_info[0] == 2:
-+ def __decode(string):
-+ return string
-+
-+else:
-+ def __decode(string):
-+ return string.decode()
-+
-+
-+# ------------ API ------------
-+def prnt(string):
-+ lib.hexchat_print(lib.ph, string.encode())
-+
-+
-+def emit_print(event_name, *args, **kwargs):
-+ time = kwargs.pop('time', 0) # For py2 compat
-+ cargs = []
-+ for i in range(4):
-+ arg = args[i].encode() if len(args) > i else b''
-+ cstring = ffi.new('char[]', arg)
-+ cargs.append(cstring)
-+
-+ if time == 0:
-+ return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
-+
-+ attrs = lib.hexchat_event_attrs_create(lib.ph)
-+ attrs.server_time_utc = time
-+ ret = lib.hexchat_emit_print_attrs(lib.ph, attrs, event_name.encode(), *cargs)
-+ lib.hexchat_event_attrs_free(lib.ph, attrs)
-+ return ret
-+
-+
-+# TODO: this shadows itself. command should be changed to cmd
-+def command(command):
-+ lib.hexchat_command(lib.ph, command.encode())
-+
-+
-+def nickcmp(string1, string2):
-+ return lib.hexchat_nickcmp(lib.ph, string1.encode(), string2.encode())
-+
-+
-+def strip(text, length=-1, flags=3):
-+ stripped = lib.hexchat_strip(lib.ph, text.encode(), length, flags)
-+ ret = __decode(ffi.string(stripped))
-+ lib.hexchat_free(lib.ph, stripped)
-+ return ret
-+
-+
-+def get_info(name):
-+ ret = lib.hexchat_get_info(lib.ph, name.encode())
-+ if ret == ffi.NULL:
-+ return None
-+ if name in ('gtkwin_ptr', 'win_ptr'):
-+ # Surely there is a less dumb way?
-+ ptr = repr(ret).rsplit(' ', 1)[1][:-1]
-+ return ptr
-+
-+ return __decode(ffi.string(ret))
-+
-+
-+def get_prefs(name):
-+ string_out = ffi.new('char**')
-+ int_out = ffi.new('int*')
-+ _type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
-+ if _type == 0:
-+ return None
-+
-+ if _type == 1:
-+ return __decode(ffi.string(string_out[0]))
-+
-+ if _type in (2, 3): # XXX: 3 should be a bool, but keeps API
-+ return int_out[0]
-+
-+ raise AssertionError('Out of bounds pref storage')
-+
-+
-+def __cstrarray_to_list(arr):
-+ i = 0
-+ ret = []
-+ while arr[i] != ffi.NULL:
-+ ret.append(ffi.string(arr[i]))
-+ i += 1
-+
-+ return ret
-+
-+
-+__FIELD_CACHE = {}
-+
-+
-+def __get_fields(name):
-+ return __FIELD_CACHE.setdefault(name, __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
-+
-+
-+__FIELD_PROPERTY_CACHE = {}
-+
-+
-+def __cached_decoded_str(string):
-+ return __FIELD_PROPERTY_CACHE.setdefault(string, __decode(string))
-+
-+
-+def get_lists():
-+ return [__cached_decoded_str(field) for field in __get_fields(b'lists')]
-+
-+
-+class ListItem:
-+ def __init__(self, name):
-+ self._listname = name
-+
-+ def __repr__(self):
-+ return '<{} list item at {}>'.format(self._listname, id(self))
-+
-+
-+# done this way for speed
-+if sys.version_info[0] == 2:
-+ def get_getter(name):
-+ return ord(name[0])
-+
-+else:
-+ def get_getter(name):
-+ return name[0]
-+
-+
-+def get_list(name):
-+ # XXX: This function is extremely inefficient and could be interators and
-+ # lazily loaded properties, but for API compat we stay slow
-+ orig_name = name
-+ name = name.encode()
-+
-+ if name not in __get_fields(b'lists'):
-+ raise KeyError('list not available')
-+
-+ list_ = lib.hexchat_list_get(lib.ph, name)
-+ if list_ == ffi.NULL:
-+ return None
-+
-+ ret = []
-+ fields = __get_fields(name)
-+
-+ def string_getter(field):
-+ string = lib.hexchat_list_str(lib.ph, list_, field)
-+ if string != ffi.NULL:
-+ return __decode(ffi.string(string))
-+
-+ return ''
-+
-+ def ptr_getter(field):
-+ if field == b'context':
-+ ptr = lib.hexchat_list_str(lib.ph, list_, field)
-+ ctx = ffi.cast('hexchat_context*', ptr)
-+ return Context(ctx)
-+
-+ return None
-+
-+ getters = {
-+ ord('s'): string_getter,
-+ ord('i'): lambda field: lib.hexchat_list_int(lib.ph, list_, field),
-+ ord('t'): lambda field: lib.hexchat_list_time(lib.ph, list_, field),
-+ ord('p'): ptr_getter,
-+ }
-+
-+ while lib.hexchat_list_next(lib.ph, list_) == 1:
-+ item = ListItem(orig_name)
-+ for _field in fields:
-+ getter = getters.get(get_getter(_field))
-+ if getter is not None:
-+ field_name = _field[1:]
-+ setattr(item, __cached_decoded_str(field_name), getter(field_name))
-+
-+ ret.append(item)
-+
-+ lib.hexchat_list_free(lib.ph, list_)
-+ return ret
-+
-+
-+# TODO: 'command' here shadows command above, and should be renamed to cmd
-+def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
-+ help.encode() if help is not None else ffi.NULL, hook.handle)
-+
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_print(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_server(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_timer(timeout, callback, userdata=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_timer(lib.ph, timeout, lib._on_timer_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_unload(callback, userdata=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata, is_unload=True)
-+ return id(hook)
-+
-+
-+def unhook(handle):
-+ plugin = __get_current_plugin()
-+ return plugin.remove_hook(handle)
-+
-+
-+def set_pluginpref(name, value):
-+ if isinstance(value, str):
-+ return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
-+
-+ if isinstance(value, int):
-+ return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
-+
-+ # XXX: This should probably raise but this keeps API
-+ return False
-+
-+
-+def get_pluginpref(name):
-+ name = name.encode()
-+ string_out = ffi.new('char[512]')
-+ if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) != 1:
-+ return None
-+
-+ string = ffi.string(string_out)
-+ # This API stores everything as a string so we have to figure out what
-+ # its actual type was supposed to be.
-+ if len(string) > 12: # Can't be a number
-+ return __decode(string)
-+
-+ number = lib.hexchat_pluginpref_get_int(lib.ph, name)
-+ if number == -1 and string != b'-1':
-+ return __decode(string)
-+
-+ return number
-+
-+
-+def del_pluginpref(name):
-+ return bool(lib.hexchat_pluginpref_delete(lib.ph, name.encode()))
-+
-+
-+def list_pluginpref():
-+ prefs_str = ffi.new('char[4096]')
-+ if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
-+ return __decode(prefs_str).split(',')
-+
-+ return []
-+
-+
-+class Context:
-+ def __init__(self, ctx):
-+ self._ctx = ctx
-+
-+ def __eq__(self, value):
-+ if not isinstance(value, Context):
-+ return False
-+
-+ return self._ctx == value._ctx
-+
-+ @contextmanager
-+ def __change_context(self):
-+ old_ctx = lib.hexchat_get_context(lib.ph)
-+ if not self.set():
-+ # XXX: Behavior change, previously used wrong context
-+ lib.hexchat_print(lib.ph, b'Context object refers to closed context, ignoring call')
-+ return
-+
-+ yield
-+ lib.hexchat_set_context(lib.ph, old_ctx)
-+
-+ def set(self):
-+ # XXX: API addition, C plugin silently ignored failure
-+ return bool(lib.hexchat_set_context(lib.ph, self._ctx))
-+
-+ def prnt(self, string):
-+ with self.__change_context():
-+ prnt(string)
-+
-+ def emit_print(self, event_name, *args, **kwargs):
-+ time = kwargs.pop('time', 0) # For py2 compat
-+ with self.__change_context():
-+ return emit_print(event_name, *args, time=time)
-+
-+ def command(self, string):
-+ with self.__change_context():
-+ command(string)
-+
-+ def get_info(self, name):
-+ with self.__change_context():
-+ return get_info(name)
-+
-+ def get_list(self, name):
-+ with self.__change_context():
-+ return get_list(name)
-+
-+
-+def get_context():
-+ ctx = lib.hexchat_get_context(lib.ph)
-+ return Context(ctx)
-+
-+
-+def find_context(server=None, channel=None):
-+ server = server.encode() if server is not None else ffi.NULL
-+ channel = channel.encode() if channel is not None else ffi.NULL
-+ ctx = lib.hexchat_find_context(lib.ph, server, channel)
-+ if ctx == ffi.NULL:
-+ return None
-+
-+ return Context(ctx)
-diff --git a/plugins/python/generate_plugin.py b/plugins/python/generate_plugin.py
-new file mode 100755
-index 00000000..5c52b37b
---- /dev/null
-+++ b/plugins/python/generate_plugin.py
-@@ -0,0 +1,89 @@
-+#!/usr/bin/env python3
-+
-+import sys
-+import cffi
-+
-+builder = cffi.FFI()
-+
-+# hexchat-plugin.h
-+with open(sys.argv[1]) as f:
-+ output = []
-+ eat_until_endif = 0
-+ # This is very specific to hexchat-plugin.h, it is not a cpp
-+ for line in f:
-+ if line.startswith('#define'):
-+ continue
-+ elif line.endswith('HEXCHAT_PLUGIN_H\n'):
-+ continue
-+ elif 'time.h' in line:
-+ output.append('typedef int... time_t;')
-+ elif line.startswith('#if'):
-+ eat_until_endif += 1
-+ elif line.startswith('#endif'):
-+ eat_until_endif -= 1
-+ elif eat_until_endif and '_hexchat_context' not in line:
-+ continue
-+ else:
-+ output.append(line)
-+ builder.cdef(''.join(output))
-+
-+builder.embedding_api('''
-+extern "Python" int _on_py_command(char **, char **, void *);
-+extern "Python" int _on_load_command(char **, char **, void *);
-+extern "Python" int _on_unload_command(char **, char **, void *);
-+extern "Python" int _on_reload_command(char **, char **, void *);
-+extern "Python" int _on_say_command(char **, char **, void *);
-+
-+extern "Python" int _on_command_hook(char **, char **, void *);
-+extern "Python" int _on_print_hook(char **, void *);
-+extern "Python" int _on_print_attrs_hook(char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_server_hook(char **, char **, void *);
-+extern "Python" int _on_server_attrs_hook(char **, char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_timer_hook(void *);
-+
-+extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
-+extern "Python" int _on_plugin_deinit(void);
-+
-+static hexchat_plugin *ph;
-+''')
-+
-+builder.set_source('_hexchat_embedded', '''
-+/* Python's header defines these.. */
-+#undef HAVE_MEMRCHR
-+#undef HAVE_STRINGS_H
-+
-+#include "config.h"
-+#include "hexchat-plugin.h"
-+
-+static hexchat_plugin *ph;
-+CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
-+CFFI_DLLEXPORT int _on_plugin_deinit(void);
-+
-+int hexchat_plugin_init(hexchat_plugin *plugin_handle,
-+ char **name_out, char **description_out,
-+ char **version_out, char *arg)
-+{
-+ if (ph != NULL)
-+ {
-+ puts ("Python plugin already loaded\\n");
-+ return 0; /* Prevent loading twice */
-+ }
-+
-+ ph = plugin_handle;
-+ return _on_plugin_init(name_out, description_out, version_out, arg, HEXCHATLIBDIR);
-+}
-+
-+int hexchat_plugin_deinit(void)
-+{
-+ int ret = _on_plugin_deinit();
-+ ph = NULL;
-+ return ret;
-+}
-+''')
-+
-+# python.py
-+with open(sys.argv[2]) as f:
-+ builder.embedding_init_code(f.read())
-+
-+# python.c
-+builder.emit_c_code(sys.argv[3])
-diff --git a/plugins/python/hexchat.py b/plugins/python/hexchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/hexchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/plugins/python/meson.build b/plugins/python/meson.build
-index e24f0c6f..5fd7ec2f 100644
---- a/plugins/python/meson.build
-+++ b/plugins/python/meson.build
-@@ -1,12 +1,30 @@
- python_opt = get_option('with-python')
- if python_opt.startswith('python3')
-- python_dep = dependency(python_opt, version: '>= 3.3')
-+ # Python 3.8 introduced a new -embed variant
-+ if not python_opt.endswith('-embed')
-+ python_dep = dependency(python_opt + '-embed', version: '>= 3.3', required: false)
-+ if not python_dep.found()
-+ python_dep = dependency(python_opt, version: '>= 3.3')
-+ endif
-+ else
-+ python_dep = dependency(python_opt, version: '>= 3.3')
-+ endif
- else
- python_dep = dependency(python_opt, version: '>= 2.7')
- endif
-
--shared_module('python', 'python.c',
-- dependencies: [libgio_dep, hexchat_plugin_dep, python_dep],
-+python3_source = custom_target('python-bindings',
-+ input: ['../../src/common/hexchat-plugin.h', 'python.py'],
-+ output: 'python.c',
-+ command: [find_program('generate_plugin.py'), '@INPUT@', '@OUTPUT@']
-+)
-+
-+install_data(['_hexchat.py', 'hexchat.py', 'xchat.py'],
-+ install_dir: join_paths(get_option('libdir'), 'hexchat/python')
-+)
-+
-+shared_module('python', python3_source,
-+ dependencies: [hexchat_plugin_dep, python_dep],
- install: true,
- install_dir: plugindir,
- name_prefix: '',
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-deleted file mode 100644
-index 475756ba..00000000
---- a/plugins/python/python.c
-+++ /dev/null
-@@ -1,2837 +0,0 @@
--/*
--* Copyright (c) 2002-2003 Gustavo Niemeyer <niemeyer@conectiva.com>
--*
--* XChat Python Plugin Interface
--*
--* Xchat Python Plugin Interface is free software; you can redistribute
--* it and/or modify it under the terms of the GNU General Public License
--* as published by the Free Software Foundation; either version 2 of the
--* License, or (at your option) any later version.
--*
--* pybot 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 General Public License for more details.
--*
--* You should have received a copy of the GNU General Public License
--* along with this file; if not, write to the Free Software
--* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
--*/
--
--/* Thread support
-- * ==============
-- *
-- * The python interpreter has a global interpreter lock. Any thread
-- * executing must acquire it before working with data accessible from
-- * python code. Here we must also care about xchat not being
-- * thread-safe. We do this by using an xchat lock, which protects
-- * xchat instructions from being executed out of time (when this
-- * plugin is not "active").
-- *
-- * When xchat calls python code:
-- * - Change the current_plugin for the executing plugin;
-- * - Release xchat lock
-- * - Acquire the global interpreter lock
-- * - Make the python call
-- * - Release the global interpreter lock
-- * - Acquire xchat lock
-- *
-- * When python code calls xchat:
-- * - Release the global interpreter lock
-- * - Acquire xchat lock
-- * - Restore context, if necessary
-- * - Make the xchat call
-- * - Release xchat lock
-- * - Acquire the global interpreter lock
-- *
-- * Inside a timer, so that individual threads have a chance to run:
-- * - Release xchat lock
-- * - Go ahead threads. Have a nice time!
-- * - Acquire xchat lock
-- *
-- */
--
--#include "config.h"
--
--#include <glib.h>
--#include <glib/gstdio.h>
--#include <string.h>
--#include <stdlib.h>
--#include <sys/types.h>
--
--#ifdef WIN32
--#include <direct.h>
--#else
--#include <unistd.h>
--#include <dirent.h>
--#endif
--
--#include "hexchat-plugin.h"
--#undef _POSIX_C_SOURCE /* Avoid warnings from /usr/include/features.h */
--#undef _XOPEN_SOURCE
--#undef HAVE_MEMRCHR /* Avoid redefinition in Python.h */
--#undef HAVE_STRINGS_H
--#include <Python.h>
--#include <structmember.h>
--#include <pythread.h>
--
--/* Macros to convert version macros into string literals.
-- * The indirect macro is a well-known preprocessor trick to force X to be evaluated before the # operator acts to make it a string literal.
-- * If STRINGIZE were to be directly defined as #X instead, VERSION would be "VERSION_MAJOR" instead of "1".
-- */
--#define STRINGIZE2(X) #X
--#define STRINGIZE(X) STRINGIZE2(X)
--
--/* Version number macros */
--#define VERSION_MAJOR 1
--#define VERSION_MINOR 0
--
--/* Version string macro e.g 1.0/3.3 */
--#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/" \
-- STRINGIZE(PY_MAJOR_VERSION) "." STRINGIZE (PY_MINOR_VERSION)
--
--/* #define's for Python 2 */
--#if PY_MAJOR_VERSION == 2
--#undef PyLong_Check
--#define PyLong_Check PyInt_Check
--#define PyLong_AsLong PyInt_AsLong
--#define PyLong_FromLong PyInt_FromLong
--
--#undef PyUnicode_Check
--#undef PyUnicode_FromString
--#undef PyUnicode_FromFormat
--#define PyUnicode_Check PyString_Check
--#define PyUnicode_AsFormat PyString_AsFormat
--#define PyUnicode_FromFormat PyString_FromFormat
--#define PyUnicode_FromString PyString_FromString
--#define PyUnicode_AsUTF8 PyString_AsString
--
--#ifdef WIN32
--#undef WITH_THREAD
--#endif
--#endif
--
--/* #define for Python 3 */
--#if PY_MAJOR_VERSION == 3
--#define IS_PY3K
--#endif
--
--#define NONE 0
--#define ALLOW_THREADS 1
--#define RESTORE_CONTEXT 2
--
--#ifdef WITH_THREAD
--#define ACQUIRE_XCHAT_LOCK() PyThread_acquire_lock(xchat_lock, 1)
--#define RELEASE_XCHAT_LOCK() PyThread_release_lock(xchat_lock)
--#define BEGIN_XCHAT_CALLS(x) \
-- do { \
-- PyObject *calls_plugin = NULL; \
-- PyThreadState *calls_thread; \
-- if ((x) & RESTORE_CONTEXT) \
-- calls_plugin = Plugin_GetCurrent(); \
-- calls_thread = PyEval_SaveThread(); \
-- ACQUIRE_XCHAT_LOCK(); \
-- if (!((x) & ALLOW_THREADS)) { \
-- PyEval_RestoreThread(calls_thread); \
-- calls_thread = NULL; \
-- } \
-- if (calls_plugin) \
-- hexchat_set_context(ph, \
-- Plugin_GetContext(calls_plugin)); \
-- while (0)
--#define END_XCHAT_CALLS() \
-- RELEASE_XCHAT_LOCK(); \
-- if (calls_thread) \
-- PyEval_RestoreThread(calls_thread); \
-- } while(0)
--#else
--#define ACQUIRE_XCHAT_LOCK()
--#define RELEASE_XCHAT_LOCK()
--#define BEGIN_XCHAT_CALLS(x)
--#define END_XCHAT_CALLS()
--#endif
--
--#ifdef WITH_THREAD
--
--#define BEGIN_PLUGIN(plg) \
-- do { \
-- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
-- RELEASE_XCHAT_LOCK(); \
-- Plugin_AcquireThread(plg); \
-- Plugin_SetContext(plg, begin_plugin_ctx); \
-- } while (0)
--#define END_PLUGIN(plg) \
-- do { \
-- Plugin_ReleaseThread(plg); \
-- ACQUIRE_XCHAT_LOCK(); \
-- } while (0)
--
--#else /* !WITH_THREAD (win32) */
--
--static PyThreadState *pTempThread;
--
--#define BEGIN_PLUGIN(plg) \
-- do { \
-- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
-- RELEASE_XCHAT_LOCK(); \
-- PyEval_AcquireLock(); \
-- pTempThread = PyThreadState_Swap(((PluginObject *)(plg))->tstate); \
-- Plugin_SetContext(plg, begin_plugin_ctx); \
-- } while (0)
--#define END_PLUGIN(plg) \
-- do { \
-- ((PluginObject *)(plg))->tstate = PyThreadState_Swap(pTempThread); \
-- PyEval_ReleaseLock(); \
-- ACQUIRE_XCHAT_LOCK(); \
-- } while (0)
--
--#endif /* !WITH_THREAD */
--
--#define Plugin_Swap(x) \
-- PyThreadState_Swap(((PluginObject *)(x))->tstate)
--#define Plugin_AcquireThread(x) \
-- PyEval_AcquireThread(((PluginObject *)(x))->tstate)
--#define Plugin_ReleaseThread(x) \
-- Util_ReleaseThread(((PluginObject *)(x))->tstate)
--#define Plugin_GetFilename(x) \
-- (((PluginObject *)(x))->filename)
--#define Plugin_GetName(x) \
-- (((PluginObject *)(x))->name)
--#define Plugin_GetVersion(x) \
-- (((PluginObject *)(x))->version)
--#define Plugin_GetDesc(x) \
-- (((PluginObject *)(x))->description)
--#define Plugin_GetHooks(x) \
-- (((PluginObject *)(x))->hooks)
--#define Plugin_GetContext(x) \
-- (((PluginObject *)(x))->context)
--#define Plugin_SetFilename(x, y) \
-- ((PluginObject *)(x))->filename = (y);
--#define Plugin_SetName(x, y) \
-- ((PluginObject *)(x))->name = (y);
--#define Plugin_SetVersion(x, y) \
-- ((PluginObject *)(x))->version = (y);
--#define Plugin_SetDescription(x, y) \
-- ((PluginObject *)(x))->description = (y);
--#define Plugin_SetHooks(x, y) \
-- ((PluginObject *)(x))->hooks = (y);
--#define Plugin_SetContext(x, y) \
-- ((PluginObject *)(x))->context = (y);
--#define Plugin_SetGui(x, y) \
-- ((PluginObject *)(x))->gui = (y);
--
--#define HOOK_XCHAT 1
--#define HOOK_XCHAT_ATTR 2
--#define HOOK_UNLOAD 3
--
--/* ===================================================================== */
--/* Object definitions */
--
--typedef struct {
-- PyObject_HEAD
-- int softspace; /* We need it for print support. */
--} XChatOutObject;
--
--typedef struct {
-- PyObject_HEAD
-- hexchat_context *context;
--} ContextObject;
--
--typedef struct {
-- PyObject_HEAD
-- PyObject *time;
--} AttributeObject;
--
--typedef struct {
-- PyObject_HEAD
-- const char *listname;
-- PyObject *dict;
--} ListItemObject;
--
--typedef struct {
-- PyObject_HEAD
-- char *name;
-- char *version;
-- char *filename;
-- char *description;
-- GSList *hooks;
-- PyThreadState *tstate;
-- hexchat_context *context;
-- void *gui;
--} PluginObject;
--
--typedef struct {
-- int type;
-- PyObject *plugin;
-- PyObject *callback;
-- PyObject *userdata;
-- char *name;
-- void *data; /* A handle, when type == HOOK_XCHAT */
--} Hook;
--
--
--/* ===================================================================== */
--/* Function declarations */
--
--static PyObject *Util_BuildList(char *word[]);
--static PyObject *Util_BuildEOLList(char *word[]);
--static void Util_Autoload(void);
--static char *Util_Expand(char *filename);
--
--static int Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Command(char *word[], char *word_eol[], void *userdata);
--static int Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Print(char *word[], void *userdata);
--static int Callback_Timer(void *userdata);
--static int Callback_ThreadTimer(void *userdata);
--
--static PyObject *XChatOut_New(void);
--static PyObject *XChatOut_write(PyObject *self, PyObject *args);
--static void XChatOut_dealloc(PyObject *self);
--
--static PyObject *Attribute_New(hexchat_event_attrs *attrs);
--
--static void Context_dealloc(PyObject *self);
--static PyObject *Context_set(ContextObject *self, PyObject *args);
--static PyObject *Context_command(ContextObject *self, PyObject *args);
--static PyObject *Context_prnt(ContextObject *self, PyObject *args);
--static PyObject *Context_get_info(ContextObject *self, PyObject *args);
--static PyObject *Context_get_list(ContextObject *self, PyObject *args);
--static PyObject *Context_compare(ContextObject *a, ContextObject *b, int op);
--static PyObject *Context_FromContext(hexchat_context *context);
--static PyObject *Context_FromServerAndChannel(char *server, char *channel);
--
--static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
--static PyObject *Plugin_GetCurrent(void);
--static PluginObject *Plugin_ByString(char *str);
--static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-- PyObject *userdata, char *name, void *data);
--static Hook *Plugin_FindHook(PyObject *plugin, char *name);
--static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
--static void Plugin_RemoveAllHooks(PyObject *plugin);
--
--static PyObject *Module_hexchat_command(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_prnt(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_context(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_find_context(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_hook_command(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_server(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_print(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_timer(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_unhook(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_list(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_lists(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_nickcmp(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_strip(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_set(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_get(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_list(PyObject *self, PyObject *args);
--
--static void IInterp_Exec(char *command);
--static int IInterp_Cmd(char *word[], char *word_eol[], void *userdata);
--
--static void Command_PyList(void);
--static void Command_PyLoad(char *filename);
--static void Command_PyUnload(char *name);
--static void Command_PyReload(char *name);
--static void Command_PyAbout(void);
--static int Command_Py(char *word[], char *word_eol[], void *userdata);
--
--/* ===================================================================== */
--/* Static declarations and definitions */
--
--static PyTypeObject Plugin_Type;
--static PyTypeObject XChatOut_Type;
--static PyTypeObject Context_Type;
--static PyTypeObject ListItem_Type;
--static PyTypeObject Attribute_Type;
--
--static PyThreadState *main_tstate = NULL;
--static void *thread_timer = NULL;
--
--static hexchat_plugin *ph;
--static GSList *plugin_list = NULL;
--
--static PyObject *interp_plugin = NULL;
--static PyObject *xchatout = NULL;
--
--#ifdef WITH_THREAD
--static PyThread_type_lock xchat_lock = NULL;
--#endif
--
--static const char usage[] = "\
--Usage: /PY LOAD <filename>\n\
-- UNLOAD <filename|name>\n\
-- RELOAD <filename|name>\n\
-- LIST\n\
-- EXEC <command>\n\
-- CONSOLE\n\
-- ABOUT\n\
--\n";
--
--static const char about[] = "HexChat Python interface version " VERSION "\n";
--
--/* ===================================================================== */
--/* Utility functions */
--
--static PyObject *
--Util_BuildList(char *word[])
--{
-- PyObject *list;
-- int listsize = 31;
-- int i;
-- /* Find the last valid array member; there may be intermediate NULLs that
-- * would otherwise cause us to drop some members. */
-- while (listsize > 0 &&
-- (word[listsize] == NULL || word[listsize][0] == 0))
-- listsize--;
-- list = PyList_New(listsize);
-- if (list == NULL) {
-- PyErr_Print();
-- return NULL;
-- }
-- for (i = 1; i <= listsize; i++) {
-- PyObject *o;
-- if (word[i] == NULL) {
-- Py_INCREF(Py_None);
-- o = Py_None;
-- } else {
-- /* This handles word[i][0] == 0 automatically. */
-- o = PyUnicode_FromString(word[i]);
-- }
-- PyList_SetItem(list, i - 1, o);
-- }
-- return list;
--}
--
--static PyObject *
--Util_BuildEOLList(char *word[])
--{
-- PyObject *list;
-- int listsize = 31;
-- int i;
-- char *accum = NULL;
-- char *last = NULL;
--
-- /* Find the last valid array member; there may be intermediate NULLs that
-- * would otherwise cause us to drop some members. */
-- while (listsize > 0 &&
-- (word[listsize] == NULL || word[listsize][0] == 0))
-- listsize--;
-- list = PyList_New(listsize);
-- if (list == NULL) {
-- PyErr_Print();
-- return NULL;
-- }
-- for (i = listsize; i > 0; i--) {
-- char *part = word[i];
-- PyObject *uni_part;
-- if (accum == NULL) {
-- accum = g_strdup (part);
-- } else if (part != NULL && part[0] != 0) {
-- last = accum;
-- accum = g_strjoin(" ", part, last, NULL);
-- g_free (last);
-- last = NULL;
--
-- if (accum == NULL) {
-- Py_DECREF(list);
-- hexchat_print(ph, "Not enough memory to alloc accum"
-- "for python plugin callback");
-- return NULL;
-- }
-- }
-- uni_part = PyUnicode_FromString(accum);
-- PyList_SetItem(list, i - 1, uni_part);
-- }
--
-- g_free (last);
-- g_free (accum);
--
-- return list;
--}
--
--static void
--Util_Autoload_from (const char *dir_name)
--{
-- gchar *oldcwd;
-- const char *entry_name;
-- GDir *dir;
--
-- oldcwd = g_get_current_dir ();
-- if (oldcwd == NULL)
-- return;
-- if (g_chdir(dir_name) != 0)
-- {
-- g_free (oldcwd);
-- return;
-- }
-- dir = g_dir_open (".", 0, NULL);
-- if (dir == NULL)
-- {
-- g_free (oldcwd);
-- return;
-- }
-- while ((entry_name = g_dir_read_name (dir)))
-- {
-- if (g_str_has_suffix (entry_name, ".py"))
-- Command_PyLoad((char*)entry_name);
-- }
-- g_dir_close (dir);
-- g_chdir (oldcwd);
--}
--
--static void
--Util_Autoload()
--{
-- const char *xdir;
-- char *sub_dir;
-- /* we need local filesystem encoding for g_chdir, g_dir_open etc */
--
-- xdir = hexchat_get_info(ph, "configdir");
--
-- /* auto-load from subdirectory addons */
-- sub_dir = g_build_filename (xdir, "addons", NULL);
-- Util_Autoload_from(sub_dir);
-- g_free (sub_dir);
--}
--
--static char *
--Util_Expand(char *filename)
--{
-- char *expanded;
--
-- /* Check if this is an absolute path. */
-- if (g_path_is_absolute(filename)) {
-- if (g_file_test(filename, G_FILE_TEST_EXISTS))
-- return g_strdup(filename);
-- else
-- return NULL;
-- }
--
-- /* Check if it starts with ~/ and expand the home if positive. */
-- if (*filename == '~' && *(filename+1) == '/') {
-- expanded = g_build_filename(g_get_home_dir(),
-- filename+2, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- else {
-- g_free(expanded);
-- return NULL;
-- }
-- }
--
-- /* Check if it's in the current directory. */
-- expanded = g_build_filename(g_get_current_dir(),
-- filename, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- g_free(expanded);
--
-- /* Check if ~/.config/hexchat/addons/<filename> exists. */
-- expanded = g_build_filename(hexchat_get_info(ph, "configdir"),
-- "addons", filename, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- g_free(expanded);
--
-- return NULL;
--}
--
--/* Similar to PyEval_ReleaseThread, but accepts NULL thread states. */
--static void
--Util_ReleaseThread(PyThreadState *tstate)
--{
-- PyThreadState *old_tstate;
-- if (tstate == NULL)
-- Py_FatalError("PyEval_ReleaseThread: NULL thread state");
-- old_tstate = PyThreadState_Swap(NULL);
-- if (old_tstate != tstate && old_tstate != NULL)
-- Py_FatalError("PyEval_ReleaseThread: wrong thread state");
-- PyEval_ReleaseLock();
--}
--
--/* ===================================================================== */
--/* Hookable functions. These are the entry points to python code, besides
-- * the load function, and the hooks for interactive interpreter. */
--
--static int
--Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list, *word_eol_list;
-- PyObject *attributes;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildList(word_eol);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- attributes = Attribute_New(attrs);
--
-- if (hook->type == HOOK_XCHAT_ATTR)
-- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
-- word_eol_list, hook->userdata, attributes);
-- else
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
-- Py_DECREF(attributes);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Command(char *word[], char *word_eol[], void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list, *word_eol_list;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildList(word_eol);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list;
-- PyObject *word_eol_list;
-- PyObject *attributes;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildEOLList(word);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- attributes = Attribute_New(attrs);
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
-- word_eol_list, hook->userdata, attributes);
--
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
-- Py_DECREF(attributes);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Print(char *word[], void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list;
-- PyObject *word_eol_list;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildEOLList(word);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
--
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Timer(void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- int ret = 0;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
--
-- BEGIN_PLUGIN(hook->plugin);
--
-- retobj = PyObject_CallFunction(hook->callback, "(O)", hook->userdata);
--
-- if (retobj) {
-- ret = PyObject_IsTrue(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- /* Returning 0 for this callback unhooks itself. */
-- if (ret == 0)
-- Plugin_RemoveHook(plugin, hook);
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--#ifdef WITH_THREAD
--static int
--Callback_ThreadTimer(void *userdata)
--{
-- RELEASE_XCHAT_LOCK();
--#ifndef WIN32
-- usleep(1);
--#endif
-- ACQUIRE_XCHAT_LOCK();
-- return 1;
--}
--#endif
--
--/* ===================================================================== */
--/* XChatOut object */
--
--/* We keep this information global, so we can reset it when the
-- * deinit function is called. */
--/* XXX This should be somehow bound to the printing context. */
--static GString *xchatout_buffer = NULL;
--
--static PyObject *
--XChatOut_New()
--{
-- XChatOutObject *xcoobj;
-- xcoobj = PyObject_New(XChatOutObject, &XChatOut_Type);
-- if (xcoobj != NULL)
-- xcoobj->softspace = 0;
-- return (PyObject *) xcoobj;
--}
--
--static void
--XChatOut_dealloc(PyObject *self)
--{
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--/* This is a little bit complex because we have to buffer data
-- * until a \n is received, since xchat breaks the line automatically.
-- * We also crop the last \n for this reason. */
--static PyObject *
--XChatOut_write(PyObject *self, PyObject *args)
--{
-- gboolean add_space;
-- char *data, *pos;
--
-- if (!PyArg_ParseTuple(args, "s:write", &data))
-- return NULL;
-- if (!data || !*data) {
-- Py_RETURN_NONE;
-- }
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- if (((XChatOutObject *)self)->softspace) {
-- add_space = TRUE;
-- ((XChatOutObject *)self)->softspace = 0;
-- } else {
-- add_space = FALSE;
-- }
--
-- g_string_append (xchatout_buffer, data);
--
-- /* If not end of line add space to continue buffer later */
-- if (add_space && xchatout_buffer->str[xchatout_buffer->len - 1] != '\n')
-- {
-- g_string_append_c (xchatout_buffer, ' ');
-- }
--
-- /* If there is an end of line print up to that */
-- if ((pos = strrchr (xchatout_buffer->str, '\n')))
-- {
-- *pos = '\0';
-- hexchat_print (ph, xchatout_buffer->str);
--
-- /* Then remove it from buffer */
-- g_string_erase (xchatout_buffer, 0, pos - xchatout_buffer->str + 1);
-- }
--
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--#define OFF(x) offsetof(XChatOutObject, x)
--
--static PyMemberDef XChatOut_members[] = {
-- {"softspace", T_INT, OFF(softspace), 0},
-- {0}
--};
--
--static PyMethodDef XChatOut_methods[] = {
-- {"write", XChatOut_write, METH_VARARGS},
-- {NULL, NULL}
--};
--
--static PyTypeObject XChatOut_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.XChatOut", /*tp_name*/
-- sizeof(XChatOutObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- XChatOut_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- XChatOut_methods, /*tp_methods*/
-- XChatOut_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* Attribute object */
--
--#undef OFF
--#define OFF(x) offsetof(AttributeObject, x)
--
--static PyMemberDef Attribute_members[] = {
-- {"time", T_OBJECT, OFF(time), 0},
-- {0}
--};
--
--static void
--Attribute_dealloc(PyObject *self)
--{
-- Py_DECREF(((AttributeObject*)self)->time);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Attribute_repr(PyObject *self)
--{
-- return PyUnicode_FromFormat("<Attribute object at %p>", self);
--}
--
--static PyTypeObject Attribute_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Attribute", /*tp_name*/
-- sizeof(AttributeObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- Attribute_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- Attribute_repr, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- Attribute_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--Attribute_New(hexchat_event_attrs *attrs)
--{
-- AttributeObject *attr;
-- attr = PyObject_New(AttributeObject, &Attribute_Type);
-- if (attr != NULL) {
-- attr->time = PyLong_FromLong((long)attrs->server_time_utc);
-- }
-- return (PyObject *) attr;
--}
--
--
--/* ===================================================================== */
--/* Context object */
--
--static void
--Context_dealloc(PyObject *self)
--{
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Context_set(ContextObject *self, PyObject *args)
--{
-- PyObject *plugin = Plugin_GetCurrent();
-- Plugin_SetContext(plugin, self->context);
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_command(ContextObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:command", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- hexchat_command(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_prnt(ContextObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:prnt", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- hexchat_print(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_emit_print(ContextObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *argv[6];
-- char *name;
-- int res;
-- long time = 0;
-- hexchat_event_attrs *attrs;
-- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
-- "arg4", "arg5", "arg6",
-- "time", NULL};
-- memset(&argv, 0, sizeof(char*)*6);
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
-- &argv[0], &argv[1], &argv[2],
-- &argv[3], &argv[4], &argv[5],
-- &time))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- attrs = hexchat_event_attrs_create(ph);
-- attrs->server_time_utc = (time_t)time;
--
-- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
-- argv[3], argv[4], argv[5], NULL);
--
-- hexchat_event_attrs_free(ph, attrs);
-- END_XCHAT_CALLS();
-- return PyLong_FromLong(res);
--}
--
--static PyObject *
--Context_get_info(ContextObject *self, PyObject *args)
--{
-- const char *info;
-- char *name;
-- if (!PyArg_ParseTuple(args, "s:get_info", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_set_context(ph, self->context);
-- info = hexchat_get_info(ph, name);
-- END_XCHAT_CALLS();
-- if (info == NULL) {
-- Py_RETURN_NONE;
-- }
-- return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Context_get_list(ContextObject *self, PyObject *args)
--{
-- PyObject *plugin = Plugin_GetCurrent();
-- hexchat_context *saved_context = Plugin_GetContext(plugin);
-- PyObject *ret;
-- Plugin_SetContext(plugin, self->context);
-- ret = Module_xchat_get_list((PyObject*)self, args);
-- Plugin_SetContext(plugin, saved_context);
-- return ret;
--}
--
--/* needed to make context1 == context2 work */
--static PyObject *
--Context_compare(ContextObject *a, ContextObject *b, int op)
--{
-- PyObject *ret;
-- /* check for == */
-- if (op == Py_EQ)
-- ret = (a->context == b->context ? Py_True : Py_False);
-- /* check for != */
-- else if (op == Py_NE)
-- ret = (a->context != b->context ? Py_True : Py_False);
-- /* only makes sense as == and != */
-- else
-- {
-- PyErr_SetString(PyExc_TypeError, "contexts are either equal or not equal");
-- ret = Py_None;
-- }
--
-- Py_INCREF(ret);
-- return ret;
--}
--
--static PyMethodDef Context_methods[] = {
-- {"set", (PyCFunction) Context_set, METH_NOARGS},
-- {"command", (PyCFunction) Context_command, METH_VARARGS},
-- {"prnt", (PyCFunction) Context_prnt, METH_VARARGS},
-- {"emit_print", (PyCFunction) Context_emit_print, METH_VARARGS|METH_KEYWORDS},
-- {"get_info", (PyCFunction) Context_get_info, METH_VARARGS},
-- {"get_list", (PyCFunction) Context_get_list, METH_VARARGS},
-- {NULL, NULL}
--};
--
--static PyTypeObject Context_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Context", /*tp_name*/
-- sizeof(ContextObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- Context_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- (richcmpfunc)Context_compare, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- Context_methods, /*tp_methods*/
-- 0, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--Context_FromContext(hexchat_context *context)
--{
-- ContextObject *ctxobj = PyObject_New(ContextObject, &Context_Type);
-- if (ctxobj != NULL)
-- ctxobj->context = context;
-- return (PyObject *) ctxobj;
--}
--
--static PyObject *
--Context_FromServerAndChannel(char *server, char *channel)
--{
-- ContextObject *ctxobj;
-- hexchat_context *context;
-- BEGIN_XCHAT_CALLS(NONE);
-- context = hexchat_find_context(ph, server, channel);
-- END_XCHAT_CALLS();
-- if (context == NULL)
-- return NULL;
-- ctxobj = PyObject_New(ContextObject, &Context_Type);
-- if (ctxobj == NULL)
-- return NULL;
-- ctxobj->context = context;
-- return (PyObject *) ctxobj;
--}
--
--
--/* ===================================================================== */
--/* ListItem object */
--
--#undef OFF
--#define OFF(x) offsetof(ListItemObject, x)
--
--static PyMemberDef ListItem_members[] = {
-- {"__dict__", T_OBJECT, OFF(dict), 0},
-- {0}
--};
--
--static void
--ListItem_dealloc(PyObject *self)
--{
-- Py_DECREF(((ListItemObject*)self)->dict);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--ListItem_repr(PyObject *self)
--{
-- return PyUnicode_FromFormat("<%s list item at %p>",
-- ((ListItemObject*)self)->listname, self);
--}
--
--static PyTypeObject ListItem_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.ListItem", /*tp_name*/
-- sizeof(ListItemObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- ListItem_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- ListItem_repr, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- ListItem_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- OFF(dict), /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--ListItem_New(const char *listname)
--{
-- ListItemObject *item;
-- item = PyObject_New(ListItemObject, &ListItem_Type);
-- if (item != NULL) {
-- /* listname parameter must be statically allocated. */
-- item->listname = listname;
-- item->dict = PyDict_New();
-- if (item->dict == NULL) {
-- Py_DECREF(item);
-- item = NULL;
-- }
-- }
-- return (PyObject *) item;
--}
--
--
--/* ===================================================================== */
--/* Plugin object */
--
--#define GET_MODULE_DATA(x, force) \
-- o = PyObject_GetAttrString(m, "__module_" #x "__"); \
-- if (o == NULL) { \
-- if (force) { \
-- hexchat_print(ph, "Module has no __module_" #x "__ " \
-- "defined"); \
-- goto error; \
-- } \
-- plugin->x = g_strdup(""); \
-- } else {\
-- if (!PyUnicode_Check(o)) { \
-- hexchat_print(ph, "Variable __module_" #x "__ " \
-- "must be a string"); \
-- goto error; \
-- } \
-- plugin->x = g_strdup(PyUnicode_AsUTF8(o)); \
-- if (plugin->x == NULL) { \
-- hexchat_print(ph, "Not enough memory to allocate " #x); \
-- goto error; \
-- } \
-- }
--
--static PyObject *
--Plugin_GetCurrent()
--{
-- PyObject *plugin;
-- plugin = PySys_GetObject("__plugin__");
-- if (plugin == NULL)
-- PyErr_SetString(PyExc_RuntimeError, "lost sys.__plugin__");
-- return plugin;
--}
--
--static hexchat_plugin *
--Plugin_GetHandle(PluginObject *plugin)
--{
-- /* This works but the issue is that the script must be ran to get
-- * the name of it thus upon first use it will use the wrong handler
-- * work around would be to run a fake script once to get name? */
--#if 0
-- /* return fake handle for pluginpref */
-- if (plugin->gui != NULL)
-- return plugin->gui;
-- else
--#endif
-- return ph;
--}
--
--static PluginObject *
--Plugin_ByString(char *str)
--{
-- GSList *list;
-- PluginObject *plugin;
-- char *basename;
-- list = plugin_list;
-- while (list != NULL) {
-- plugin = (PluginObject *) list->data;
-- basename = g_path_get_basename(plugin->filename);
-- if (basename == NULL)
-- break;
-- if (strcasecmp(plugin->name, str) == 0 ||
-- strcasecmp(plugin->filename, str) == 0 ||
-- strcasecmp(basename, str) == 0) {
-- g_free(basename);
-- return plugin;
-- }
-- g_free(basename);
-- list = list->next;
-- }
-- return NULL;
--}
--
--static Hook *
--Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-- PyObject *userdata, char *name, void *data)
--{
-- Hook *hook = g_new(Hook, 1);
-- hook->type = type;
-- hook->plugin = plugin;
-- Py_INCREF(callback);
-- hook->callback = callback;
-- Py_INCREF(userdata);
-- hook->userdata = userdata;
-- hook->name = g_strdup (name);
-- hook->data = NULL;
-- Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
-- hook));
--
-- return hook;
--}
--
--static Hook *
--Plugin_FindHook(PyObject *plugin, char *name)
--{
-- Hook *hook = NULL;
-- GSList *plugin_hooks = Plugin_GetHooks(plugin);
--
-- while (plugin_hooks)
-- {
-- if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
-- {
-- hook = (Hook *)plugin_hooks->data;
-- break;
-- }
--
-- plugin_hooks = g_slist_next(plugin_hooks);
-- }
--
-- return hook;
--}
--
--static void
--Plugin_RemoveHook(PyObject *plugin, Hook *hook)
--{
-- GSList *list;
-- /* Is this really a hook of the running plugin? */
-- list = g_slist_find(Plugin_GetHooks(plugin), hook);
-- if (list) {
-- /* Ok, unhook it. */
-- if (hook->type != HOOK_UNLOAD) {
-- /* This is an xchat hook. Unregister it. */
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_unhook(ph, (hexchat_hook*)hook->data);
-- END_XCHAT_CALLS();
-- }
-- Plugin_SetHooks(plugin,
-- g_slist_remove(Plugin_GetHooks(plugin),
-- hook));
-- Py_DECREF(hook->callback);
-- Py_DECREF(hook->userdata);
-- g_free(hook->name);
-- g_free(hook);
-- }
--}
--
--static void
--Plugin_RemoveAllHooks(PyObject *plugin)
--{
-- GSList *list = Plugin_GetHooks(plugin);
-- while (list) {
-- Hook *hook = (Hook *) list->data;
-- if (hook->type != HOOK_UNLOAD) {
-- /* This is an xchat hook. Unregister it. */
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_unhook(ph, (hexchat_hook*)hook->data);
-- END_XCHAT_CALLS();
-- }
-- Py_DECREF(hook->callback);
-- Py_DECREF(hook->userdata);
-- g_free(hook->name);
-- g_free(hook);
-- list = list->next;
-- }
-- Plugin_SetHooks(plugin, NULL);
--}
--
--static void
--Plugin_Delete(PyObject *plugin)
--{
-- PyThreadState *tstate = ((PluginObject*)plugin)->tstate;
-- GSList *list = Plugin_GetHooks(plugin);
-- while (list) {
-- Hook *hook = (Hook *) list->data;
-- if (hook->type == HOOK_UNLOAD) {
-- PyObject *retobj;
-- retobj = PyObject_CallFunction(hook->callback, "(O)",
-- hook->userdata);
-- if (retobj) {
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- PyErr_Clear();
-- }
-- }
-- list = list->next;
-- }
-- Plugin_RemoveAllHooks(plugin);
-- if (((PluginObject *)plugin)->gui != NULL)
-- hexchat_plugingui_remove(ph, ((PluginObject *)plugin)->gui);
-- Py_DECREF(plugin);
-- /*PyThreadState_Swap(tstate); needed? */
-- Py_EndInterpreter(tstate);
--}
--
--static PyObject *
--Plugin_New(char *filename, PyObject *xcoobj)
--{
-- PluginObject *plugin = NULL;
-- PyObject *m, *o;
--#ifdef IS_PY3K
-- wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
-- char *argv[] = { "<hexchat>", 0 };
--#endif
--
-- if (filename) {
-- char *old_filename = filename;
-- filename = Util_Expand(filename);
-- if (filename == NULL) {
-- hexchat_printf(ph, "File not found: %s", old_filename);
-- return NULL;
-- }
-- }
--
-- /* Allocate plugin structure. */
-- plugin = PyObject_New(PluginObject, &Plugin_Type);
-- if (plugin == NULL) {
-- hexchat_print(ph, "Can't create plugin object");
-- goto error;
-- }
--
-- Plugin_SetName(plugin, NULL);
-- Plugin_SetVersion(plugin, NULL);
-- Plugin_SetFilename(plugin, NULL);
-- Plugin_SetDescription(plugin, NULL);
-- Plugin_SetHooks(plugin, NULL);
-- Plugin_SetContext(plugin, hexchat_get_context(ph));
-- Plugin_SetGui(plugin, NULL);
--
-- /* Start a new interpreter environment for this plugin. */
-- PyEval_AcquireThread(main_tstate);
-- plugin->tstate = Py_NewInterpreter();
-- if (plugin->tstate == NULL) {
-- hexchat_print(ph, "Can't create interpreter state");
-- goto error;
-- }
--
-- PySys_SetArgv(1, argv);
-- PySys_SetObject("__plugin__", (PyObject *) plugin);
--
-- /* Set stdout and stderr to xchatout. */
-- Py_INCREF(xcoobj);
-- PySys_SetObject("stdout", xcoobj);
-- Py_INCREF(xcoobj);
-- PySys_SetObject("stderr", xcoobj);
--
-- if (filename) {
--#ifdef WIN32
-- char *file;
-- if (!g_file_get_contents(filename, &file, NULL, NULL)) {
-- hexchat_printf(ph, "Can't open file %s: %s\n",
-- filename, strerror(errno));
-- goto error;
-- }
--
-- if (PyRun_SimpleString(file) != 0) {
-- hexchat_printf(ph, "Error loading module %s\n",
-- filename);
-- g_free (file);
-- goto error;
-- }
--
-- plugin->filename = filename;
-- filename = NULL;
-- g_free (file);
--#else
-- FILE *fp;
-- plugin->filename = filename;
--
-- /* It's now owned by the plugin. */
-- filename = NULL;
--
-- /* Open the plugin file. */
-- fp = fopen(plugin->filename, "r");
-- if (fp == NULL) {
-- hexchat_printf(ph, "Can't open file %s: %s\n",
-- plugin->filename, strerror(errno));
-- goto error;
-- }
--
-- /* Run the plugin. */
-- if (PyRun_SimpleFile(fp, plugin->filename) != 0) {
-- hexchat_printf(ph, "Error loading module %s\n",
-- plugin->filename);
-- fclose(fp);
-- goto error;
-- }
-- fclose(fp);
--#endif
-- m = PyDict_GetItemString(PyImport_GetModuleDict(),
-- "__main__");
-- if (m == NULL) {
-- hexchat_print(ph, "Can't get __main__ module");
-- goto error;
-- }
-- GET_MODULE_DATA(name, 1);
-- GET_MODULE_DATA(version, 0);
-- GET_MODULE_DATA(description, 0);
-- plugin->gui = hexchat_plugingui_add(ph, plugin->filename,
-- plugin->name,
-- plugin->description,
-- plugin->version, NULL);
-- }
--
-- PyEval_ReleaseThread(plugin->tstate);
--
-- return (PyObject *) plugin;
--
--error:
-- g_free(filename);
--
-- if (plugin) {
-- if (plugin->tstate)
-- Plugin_Delete((PyObject *)plugin);
-- else
-- Py_DECREF(plugin);
-- }
-- PyEval_ReleaseLock();
--
-- return NULL;
--}
--
--static void
--Plugin_dealloc(PluginObject *self)
--{
-- g_free(self->filename);
-- g_free(self->name);
-- g_free(self->version);
-- g_free(self->description);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyTypeObject Plugin_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Plugin", /*tp_name*/
-- sizeof(PluginObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- (destructor)Plugin_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- 0, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* XChat module */
--
--static PyObject *
--Module_hexchat_command(PyObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:command", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- hexchat_command(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_prnt(PyObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:prnt", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- hexchat_print(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_hexchat_emit_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *argv[6];
-- char *name;
-- int res;
-- long time = 0;
-- hexchat_event_attrs *attrs;
-- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
-- "arg4", "arg5", "arg6",
-- "time", NULL};
-- memset(&argv, 0, sizeof(char*)*6);
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
-- &argv[0], &argv[1], &argv[2],
-- &argv[3], &argv[4], &argv[5],
-- &time))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- attrs = hexchat_event_attrs_create(ph);
-- attrs->server_time_utc = (time_t)time;
--
-- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
-- argv[3], argv[4], argv[5], NULL);
--
-- hexchat_event_attrs_free(ph, attrs);
-- END_XCHAT_CALLS();
-- return PyLong_FromLong(res);
--}
--
--static PyObject *
--Module_hexchat_get_info(PyObject *self, PyObject *args)
--{
-- const char *info;
-- char *name;
-- if (!PyArg_ParseTuple(args, "s:get_info", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
-- info = hexchat_get_info(ph, name);
-- END_XCHAT_CALLS();
-- if (info == NULL) {
-- Py_RETURN_NONE;
-- }
-- if (strcmp (name, "gtkwin_ptr") == 0 || strcmp (name, "win_ptr") == 0)
-- return PyUnicode_FromFormat("%p", info); /* format as pointer */
-- else
-- return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Module_xchat_get_prefs(PyObject *self, PyObject *args)
--{
-- PyObject *res;
-- const char *info;
-- int integer;
-- char *name;
-- int type;
-- if (!PyArg_ParseTuple(args, "s:get_prefs", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- type = hexchat_get_prefs(ph, name, &info, &integer);
-- END_XCHAT_CALLS();
-- switch (type) {
-- case 0:
-- Py_INCREF(Py_None);
-- res = Py_None;
-- break;
-- case 1:
-- res = PyUnicode_FromString((char*)info);
-- break;
-- case 2:
-- case 3:
-- res = PyLong_FromLong(integer);
-- break;
-- default:
-- PyErr_Format(PyExc_RuntimeError,
-- "unknown get_prefs type (%d), "
-- "please report", type);
-- res = NULL;
-- break;
-- }
-- return res;
--}
--
--static PyObject *
--Module_hexchat_get_context(PyObject *self, PyObject *args)
--{
-- PyObject *plugin;
-- PyObject *ctxobj;
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- ctxobj = Context_FromContext(Plugin_GetContext(plugin));
-- if (ctxobj == NULL) {
-- Py_RETURN_NONE;
-- }
-- return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_find_context(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *server = NULL;
-- char *channel = NULL;
-- PyObject *ctxobj;
-- char *kwlist[] = {"server", "channel", 0};
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zz:find_context",
-- kwlist, &server, &channel))
-- return NULL;
-- ctxobj = Context_FromServerAndChannel(server, channel);
-- if (ctxobj == NULL) {
-- Py_RETURN_NONE;
-- }
-- return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_set(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- int result;
-- char *var;
-- PyObject *value;
--
-- if (!PyArg_ParseTuple(args, "sO:set_pluginpref", &var, &value))
-- return NULL;
-- if (PyLong_Check(value)) {
-- int intvalue = PyLong_AsLong(value);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_set_int(prefph, var, intvalue);
-- END_XCHAT_CALLS();
-- }
-- else if (PyUnicode_Check(value)) {
-- char *charvalue = PyUnicode_AsUTF8(value);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_set_str(prefph, var, charvalue);
-- END_XCHAT_CALLS();
-- }
-- else
-- result = 0;
-- return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- PyObject *ret;
-- char *var;
-- char retstr[512];
-- int retint;
-- int result;
-- if (!PyArg_ParseTuple(args, "s:get_pluginpref", &var))
-- return NULL;
--
-- /* This will always return numbers as integers. */
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_get_str(prefph, var, retstr);
-- END_XCHAT_CALLS();
-- if (result) {
-- if (strlen (retstr) <= 12) {
-- BEGIN_XCHAT_CALLS(NONE);
-- retint = hexchat_pluginpref_get_int(prefph, var);
-- END_XCHAT_CALLS();
-- if ((retint == -1) && (strcmp(retstr, "-1") != 0))
-- ret = PyUnicode_FromString(retstr);
-- else
-- ret = PyLong_FromLong(retint);
-- } else
-- ret = PyUnicode_FromString(retstr);
-- }
-- else
-- {
-- Py_INCREF(Py_None);
-- ret = Py_None;
-- }
-- return ret;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- char *var;
-- int result;
-- if (!PyArg_ParseTuple(args, "s:del_pluginpref", &var))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_delete(prefph, var);
-- END_XCHAT_CALLS();
-- return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_list(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- char list[4096];
-- char* token;
-- int result;
-- PyObject *pylist;
-- pylist = PyList_New(0);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_list(prefph, list);
-- END_XCHAT_CALLS();
-- if (result) {
-- token = strtok(list, ",");
-- while (token != NULL) {
-- PyList_Append(pylist, PyUnicode_FromString(token));
-- token = strtok (NULL, ",");
-- }
-- }
-- return pylist;
--}
--
--static PyObject *
--Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- char *help = NULL;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata",
-- "priority", "help", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oiz:hook_command",
-- kwlist, &name, &callback, &userdata,
-- &priority, &help))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_command(ph, name, priority,
-- Callback_Command, help, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
-- Callback_Server, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
-- Callback_Server, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_print(ph, name, priority,
-- Callback_Print, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print_attrs",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
-- Callback_Print_Attrs, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- int timeout;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"timeout", "callback", "userdata", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|O:hook_timer",
-- kwlist, &timeout, &callback,
-- &userdata))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_timer(ph, timeout,
-- Callback_Timer, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"callback", "userdata", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:hook_unload",
-- kwlist, &callback, &userdata))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_unhook(PyObject *self, PyObject *args)
--{
-- PyObject *plugin;
-- PyObject *obj;
-- Hook *hook;
-- if (!PyArg_ParseTuple(args, "O:unhook", &obj))
-- return NULL;
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
--
-- if (PyUnicode_Check (obj))
-- {
-- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
-- while (hook)
-- {
-- Plugin_RemoveHook(plugin, hook);
-- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
-- }
-- }
-- else
-- {
-- hook = (Hook *)PyLong_AsVoidPtr(obj);
-- Plugin_RemoveHook(plugin, hook);
-- }
--
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_get_list(PyObject *self, PyObject *args)
--{
-- hexchat_list *list;
-- PyObject *l;
-- const char *name;
-- const char *const *fields;
-- int i;
--
-- if (!PyArg_ParseTuple(args, "s:get_list", &name))
-- return NULL;
-- /* This function is thread safe, and returns statically
-- * allocated data. */
-- fields = hexchat_list_fields(ph, "lists");
-- for (i = 0; fields[i]; i++) {
-- if (strcmp(fields[i], name) == 0) {
-- /* Use the static allocated one. */
-- name = fields[i];
-- break;
-- }
-- }
-- if (fields[i] == NULL) {
-- PyErr_SetString(PyExc_KeyError, "list not available");
-- return NULL;
-- }
-- l = PyList_New(0);
-- if (l == NULL)
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
-- list = hexchat_list_get(ph, (char*)name);
-- if (list == NULL)
-- goto error;
-- fields = hexchat_list_fields(ph, (char*)name);
-- while (hexchat_list_next(ph, list)) {
-- PyObject *o = ListItem_New(name);
-- if (o == NULL || PyList_Append(l, o) == -1) {
-- Py_XDECREF(o);
-- goto error;
-- }
-- Py_DECREF(o); /* l is holding a reference */
-- for (i = 0; fields[i]; i++) {
-- const char *fld = fields[i]+1;
-- PyObject *attr = NULL;
-- const char *sattr;
-- int iattr;
-- time_t tattr;
-- switch(fields[i][0]) {
-- case 's':
-- sattr = hexchat_list_str(ph, list, (char*)fld);
-- attr = PyUnicode_FromString(sattr?sattr:"");
-- break;
-- case 'i':
-- iattr = hexchat_list_int(ph, list, (char*)fld);
-- attr = PyLong_FromLong((long)iattr);
-- break;
-- case 't':
-- tattr = hexchat_list_time(ph, list, (char*)fld);
-- attr = PyLong_FromLong((long)tattr);
-- break;
-- case 'p':
-- sattr = hexchat_list_str(ph, list, (char*)fld);
-- if (strcmp(fld, "context") == 0) {
-- attr = Context_FromContext(
-- (hexchat_context*)sattr);
-- break;
-- }
-- default: /* ignore unknown (newly added?) types */
-- continue;
-- }
-- if (attr == NULL)
-- goto error;
-- PyObject_SetAttrString(o, (char*)fld, attr); /* add reference on attr in o */
-- Py_DECREF(attr); /* make o own attr */
-- }
-- }
-- hexchat_list_free(ph, list);
-- goto exit;
--error:
-- if (list)
-- hexchat_list_free(ph, list);
-- Py_DECREF(l);
-- l = NULL;
--
--exit:
-- END_XCHAT_CALLS();
-- return l;
--}
--
--static PyObject *
--Module_xchat_get_lists(PyObject *self, PyObject *args)
--{
-- PyObject *l, *o;
-- const char *const *fields;
-- int i;
-- /* This function is thread safe, and returns statically
-- * allocated data. */
-- fields = hexchat_list_fields(ph, "lists");
-- l = PyList_New(0);
-- if (l == NULL)
-- return NULL;
-- for (i = 0; fields[i]; i++) {
-- o = PyUnicode_FromString(fields[i]);
-- if (o == NULL || PyList_Append(l, o) == -1) {
-- Py_DECREF(l);
-- Py_XDECREF(o);
-- return NULL;
-- }
-- Py_DECREF(o); /* l is holding a reference */
-- }
-- return l;
--}
--
--static PyObject *
--Module_hexchat_nickcmp(PyObject *self, PyObject *args)
--{
-- char *s1, *s2;
-- if (!PyArg_ParseTuple(args, "ss:nickcmp", &s1, &s2))
-- return NULL;
-- return PyLong_FromLong((long) hexchat_nickcmp(ph, s1, s2));
--}
--
--static PyObject *
--Module_hexchat_strip(PyObject *self, PyObject *args)
--{
-- PyObject *result;
-- char *str, *str2;
-- int len = -1, flags = 1 | 2;
-- if (!PyArg_ParseTuple(args, "s|ii:strip", &str, &len, &flags))
-- return NULL;
-- str2 = hexchat_strip(ph, str, len, flags);
-- result = PyUnicode_FromString(str2);
-- hexchat_free(ph, str2);
-- return result;
--}
--
--static PyMethodDef Module_xchat_methods[] = {
-- {"command", Module_hexchat_command,
-- METH_VARARGS},
-- {"prnt", Module_xchat_prnt,
-- METH_VARARGS},
-- {"emit_print", (PyCFunction)Module_hexchat_emit_print,
-- METH_VARARGS|METH_KEYWORDS},
-- {"get_info", Module_hexchat_get_info,
-- METH_VARARGS},
-- {"get_prefs", Module_xchat_get_prefs,
-- METH_VARARGS},
-- {"get_context", Module_hexchat_get_context,
-- METH_NOARGS},
-- {"find_context", (PyCFunction)Module_hexchat_find_context,
-- METH_VARARGS|METH_KEYWORDS},
-- {"set_pluginpref", Module_hexchat_pluginpref_set,
-- METH_VARARGS},
-- {"get_pluginpref", Module_hexchat_pluginpref_get,
-- METH_VARARGS},
-- {"del_pluginpref", Module_hexchat_pluginpref_delete,
-- METH_VARARGS},
-- {"list_pluginpref", Module_hexchat_pluginpref_list,
-- METH_VARARGS},
-- {"hook_command", (PyCFunction)Module_hexchat_hook_command,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_server", (PyCFunction)Module_hexchat_hook_server,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_server_attrs", (PyCFunction)Module_hexchat_hook_server_attrs,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_print", (PyCFunction)Module_hexchat_hook_print,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_print_attrs", (PyCFunction)Module_hexchat_hook_print_attrs,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_timer", (PyCFunction)Module_hexchat_hook_timer,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_unload", (PyCFunction)Module_hexchat_hook_unload,
-- METH_VARARGS|METH_KEYWORDS},
-- {"unhook", Module_hexchat_unhook,
-- METH_VARARGS},
-- {"get_list", Module_xchat_get_list,
-- METH_VARARGS},
-- {"get_lists", Module_xchat_get_lists,
-- METH_NOARGS},
-- {"nickcmp", Module_hexchat_nickcmp,
-- METH_VARARGS},
-- {"strip", Module_hexchat_strip,
-- METH_VARARGS},
-- {NULL, NULL}
--};
--
--#ifdef IS_PY3K
--static struct PyModuleDef moduledef = {
-- PyModuleDef_HEAD_INIT,
-- "hexchat", /* m_name */
-- "HexChat Scripting Interface", /* m_doc */
-- -1, /* m_size */
-- Module_xchat_methods, /* m_methods */
-- NULL, /* m_reload */
-- NULL, /* m_traverse */
-- NULL, /* m_clear */
-- NULL, /* m_free */
--};
--
--static struct PyModuleDef xchat_moduledef = {
-- PyModuleDef_HEAD_INIT,
-- "xchat", /* m_name */
-- "HexChat Scripting Interface", /* m_doc */
-- -1, /* m_size */
-- Module_xchat_methods, /* m_methods */
-- NULL, /* m_reload */
-- NULL, /* m_traverse */
-- NULL, /* m_clear */
-- NULL, /* m_free */
--};
--#endif
--
--static PyObject *
--moduleinit_hexchat(void)
--{
-- PyObject *hm;
--#ifdef IS_PY3K
-- hm = PyModule_Create(&moduledef);
--#else
-- hm = Py_InitModule3("hexchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
-- PyModule_AddIntConstant(hm, "EAT_NONE", HEXCHAT_EAT_NONE);
-- PyModule_AddIntConstant(hm, "EAT_HEXCHAT", HEXCHAT_EAT_HEXCHAT);
-- PyModule_AddIntConstant(hm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT); /* for compat */
-- PyModule_AddIntConstant(hm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
-- PyModule_AddIntConstant(hm, "EAT_ALL", HEXCHAT_EAT_ALL);
-- PyModule_AddIntConstant(hm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
-- PyModule_AddIntConstant(hm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
-- PyModule_AddIntConstant(hm, "PRI_NORM", HEXCHAT_PRI_NORM);
-- PyModule_AddIntConstant(hm, "PRI_LOW", HEXCHAT_PRI_LOW);
-- PyModule_AddIntConstant(hm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
-- PyObject_SetAttrString(hm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
-- return hm;
--}
--
--static PyObject *
--moduleinit_xchat(void)
--{
-- PyObject *xm;
--#ifdef IS_PY3K
-- xm = PyModule_Create(&xchat_moduledef);
--#else
-- xm = Py_InitModule3("xchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
-- PyModule_AddIntConstant(xm, "EAT_NONE", HEXCHAT_EAT_NONE);
-- PyModule_AddIntConstant(xm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT);
-- PyModule_AddIntConstant(xm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
-- PyModule_AddIntConstant(xm, "EAT_ALL", HEXCHAT_EAT_ALL);
-- PyModule_AddIntConstant(xm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
-- PyModule_AddIntConstant(xm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
-- PyModule_AddIntConstant(xm, "PRI_NORM", HEXCHAT_PRI_NORM);
-- PyModule_AddIntConstant(xm, "PRI_LOW", HEXCHAT_PRI_LOW);
-- PyModule_AddIntConstant(xm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
-- PyObject_SetAttrString(xm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
-- return xm;
--}
--
--#ifdef IS_PY3K
--PyMODINIT_FUNC
--PyInit_hexchat(void)
--{
-- return moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--PyInit_xchat(void)
--{
-- return moduleinit_xchat();
--}
--#else
--PyMODINIT_FUNC
--inithexchat(void)
--{
-- moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--initxchat(void)
--{
-- moduleinit_xchat();
--}
--#endif
--
--/* ===================================================================== */
--/* Python interactive interpreter functions */
--
--static void
--IInterp_Exec(char *command)
--{
-- PyObject *m, *d, *o;
-- char *buffer;
-- int len;
--
-- BEGIN_PLUGIN(interp_plugin);
--
-- m = PyImport_AddModule("__main__");
-- if (m == NULL) {
-- hexchat_print(ph, "Can't get __main__ module");
-- goto fail;
-- }
-- d = PyModule_GetDict(m);
-- len = strlen(command);
--
-- buffer = g_malloc(len + 2);
-- memcpy(buffer, command, len);
-- buffer[len] = '\n';
-- buffer[len+1] = 0;
-- PyRun_SimpleString("import hexchat");
-- o = PyRun_StringFlags(buffer, Py_single_input, d, d, NULL);
-- g_free(buffer);
-- if (o == NULL) {
-- PyErr_Print();
-- goto fail;
-- }
-- Py_DECREF(o);
--
--fail:
-- END_PLUGIN(interp_plugin);
-- return;
--}
--
--static int
--IInterp_Cmd(char *word[], char *word_eol[], void *userdata)
--{
-- char *channel = (char *) hexchat_get_info(ph, "channel");
-- if (channel && channel[0] == '>' && strcmp(channel, ">>python<<") == 0) {
-- hexchat_printf(ph, ">>> %s\n", word_eol[1]);
-- IInterp_Exec(word_eol[1]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--
--/* ===================================================================== */
--/* Python command handling */
--
--static void
--Command_PyList(void)
--{
-- GSList *list;
-- list = plugin_list;
-- if (list == NULL) {
-- hexchat_print(ph, "No python modules loaded");
-- } else {
-- hexchat_print(ph,
-- "Name Version Filename Description\n"
-- "---- ------- -------- -----------\n");
-- while (list != NULL) {
-- PluginObject *plg = (PluginObject *) list->data;
-- char *basename = g_path_get_basename(plg->filename);
-- hexchat_printf(ph, "%-12s %-8s %-20s %-10s\n",
-- plg->name,
-- *plg->version ? plg->version
-- : "<none>",
-- basename,
-- *plg->description ? plg->description
-- : "<none>");
-- g_free(basename);
-- list = list->next;
-- }
-- hexchat_print(ph, "\n");
-- }
--}
--
--static void
--Command_PyLoad(char *filename)
--{
-- PyObject *plugin;
-- RELEASE_XCHAT_LOCK();
-- plugin = Plugin_New(filename, xchatout);
-- ACQUIRE_XCHAT_LOCK();
-- if (plugin)
-- plugin_list = g_slist_append(plugin_list, plugin);
--}
--
--static void
--Command_PyUnload(char *name)
--{
-- PluginObject *plugin = Plugin_ByString(name);
-- if (!plugin) {
-- hexchat_print(ph, "Can't find a python plugin with that name");
-- } else {
-- BEGIN_PLUGIN(plugin);
-- Plugin_Delete((PyObject*)plugin);
-- END_PLUGIN(plugin);
-- plugin_list = g_slist_remove(plugin_list, plugin);
-- }
--}
--
--static void
--Command_PyReload(char *name)
--{
-- PluginObject *plugin = Plugin_ByString(name);
-- if (!plugin) {
-- hexchat_print(ph, "Can't find a python plugin with that name");
-- } else {
-- char *filename = g_strdup(plugin->filename);
-- Command_PyUnload(filename);
-- Command_PyLoad(filename);
-- g_free(filename);
-- }
--}
--
--static void
--Command_PyAbout(void)
--{
-- hexchat_print(ph, about);
--}
--
--static int
--Command_Py(char *word[], char *word_eol[], void *userdata)
--{
-- char *cmd = word[2];
-- int ok = 0;
-- if (strcasecmp(cmd, "LIST") == 0) {
-- ok = 1;
-- Command_PyList();
-- } else if (strcasecmp(cmd, "EXEC") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- IInterp_Exec(word_eol[3]);
-- }
-- } else if (strcasecmp(cmd, "LOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyLoad(word[3]);
-- }
-- } else if (strcasecmp(cmd, "UNLOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyUnload(word[3]);
-- }
-- } else if (strcasecmp(cmd, "RELOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyReload(word[3]);
-- }
-- } else if (strcasecmp(cmd, "CONSOLE") == 0) {
-- ok = 1;
-- hexchat_command(ph, "QUERY >>python<<");
-- } else if (strcasecmp(cmd, "ABOUT") == 0) {
-- ok = 1;
-- Command_PyAbout();
-- }
-- if (!ok)
-- hexchat_print(ph, usage);
-- return HEXCHAT_EAT_ALL;
--}
--
--static int
--Command_Load(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyLoad(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Reload(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyReload(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Unload(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyUnload(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--/* ===================================================================== */
--/* Autoload function */
--
--/* ===================================================================== */
--/* (De)initialization functions */
--
--static int initialized = 0;
--static int reinit_tried = 0;
--
--void
--hexchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
--{
-- *name = "Python";
-- *version = VERSION;
-- *desc = "Python scripting interface";
-- if (reserved)
-- *reserved = NULL;
--}
--
--int
--hexchat_plugin_init(hexchat_plugin *plugin_handle,
-- char **plugin_name,
-- char **plugin_desc,
-- char **plugin_version,
-- char *arg)
--{
--#ifdef IS_PY3K
-- wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
-- char *argv[] = { "<hexchat>", 0 };
--#endif
--
-- ph = plugin_handle;
--
-- /* Block double initalization. */
-- if (initialized != 0) {
-- hexchat_print(ph, "Python interface already loaded");
-- /* deinit is called even when init fails, so keep track
-- * of a reinit failure. */
-- reinit_tried++;
-- return 0;
-- }
-- initialized = 1;
--
-- *plugin_name = "Python";
-- *plugin_version = VERSION;
--
-- /* FIXME You can't free this since it's used as long as the plugin's
-- * loaded, but if you unload it, everything belonging to the plugin is
-- * supposed to be freed anyway.
-- */
-- *plugin_desc = g_strdup_printf ("Python %d scripting interface", PY_MAJOR_VERSION);
--
-- /* Initialize python. */
--#ifdef IS_PY3K
-- Py_SetProgramName(L"hexchat");
-- PyImport_AppendInittab("hexchat", PyInit_hexchat);
-- PyImport_AppendInittab("xchat", PyInit_xchat);
--#else
-- Py_SetProgramName("hexchat");
-- PyImport_AppendInittab("hexchat", inithexchat);
-- PyImport_AppendInittab("xchat", initxchat);
--#endif
-- Py_Initialize();
-- PySys_SetArgv(1, argv);
--
-- xchatout_buffer = g_string_new (NULL);
-- xchatout = XChatOut_New();
-- if (xchatout == NULL) {
-- hexchat_print(ph, "Can't allocate xchatout object");
-- return 0;
-- }
--
--#ifdef WITH_THREAD
-- PyEval_InitThreads();
-- xchat_lock = PyThread_allocate_lock();
-- if (xchat_lock == NULL) {
-- hexchat_print(ph, "Can't allocate hexchat lock");
-- Py_DECREF(xchatout);
-- xchatout = NULL;
-- return 0;
-- }
--#endif
--
-- main_tstate = PyEval_SaveThread();
--
-- interp_plugin = Plugin_New(NULL, xchatout);
-- if (interp_plugin == NULL) {
-- hexchat_print(ph, "Plugin_New() failed.\n");
--#ifdef WITH_THREAD
-- PyThread_free_lock(xchat_lock);
--#endif
-- Py_DECREF(xchatout);
-- xchatout = NULL;
-- return 0;
-- }
--
--
-- hexchat_hook_command(ph, "", HEXCHAT_PRI_NORM, IInterp_Cmd, 0, 0);
-- hexchat_hook_command(ph, "PY", HEXCHAT_PRI_NORM, Command_Py, usage, 0);
-- hexchat_hook_command(ph, "LOAD", HEXCHAT_PRI_NORM, Command_Load, 0, 0);
-- hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, Command_Unload, 0, 0);
-- hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, Command_Reload, 0, 0);
--#ifdef WITH_THREAD
-- thread_timer = hexchat_hook_timer(ph, 300, Callback_ThreadTimer, NULL);
--#endif
--
-- hexchat_print(ph, "Python interface loaded\n");
--
-- Util_Autoload();
-- return 1;
--}
--
--int
--hexchat_plugin_deinit(void)
--{
-- GSList *list;
--
-- /* A reinitialization was tried. Just give up and live the
-- * environment as is. We are still alive. */
-- if (reinit_tried) {
-- reinit_tried--;
-- return 1;
-- }
--
-- list = plugin_list;
-- while (list != NULL) {
-- PyObject *plugin = (PyObject *) list->data;
-- BEGIN_PLUGIN(plugin);
-- Plugin_Delete(plugin);
-- END_PLUGIN(plugin);
-- list = list->next;
-- }
-- g_slist_free(plugin_list);
-- plugin_list = NULL;
--
-- /* Reset xchatout buffer. */
-- g_string_free (xchatout_buffer, TRUE);
-- xchatout_buffer = NULL;
--
-- if (interp_plugin) {
-- PyThreadState *tstate = ((PluginObject*)interp_plugin)->tstate;
-- PyThreadState_Swap(tstate);
-- Py_EndInterpreter(tstate);
-- Py_DECREF(interp_plugin);
-- interp_plugin = NULL;
-- }
--
-- /* Switch back to the main thread state. */
-- if (main_tstate) {
-- PyEval_RestoreThread(main_tstate);
-- PyThreadState_Swap(main_tstate);
-- main_tstate = NULL;
-- }
-- Py_Finalize();
--
--#ifdef WITH_THREAD
-- if (thread_timer != NULL) {
-- hexchat_unhook(ph, thread_timer);
-- thread_timer = NULL;
-- }
-- PyThread_free_lock(xchat_lock);
--#endif
--
-- hexchat_print(ph, "Python interface unloaded\n");
-- initialized = 0;
--
-- return 1;
--}
--
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-new file mode 100644
-index 00000000..30694802
---- /dev/null
-+++ b/plugins/python/python.py
-@@ -0,0 +1,554 @@
-+from __future__ import print_function
-+
-+import importlib
-+import os
-+import pydoc
-+import signal
-+import sys
-+import traceback
-+import weakref
-+from contextlib import contextmanager
-+
-+from _hexchat_embedded import ffi, lib
-+
-+if sys.version_info < (3, 0):
-+ from io import BytesIO as HelpEater
-+else:
-+ from io import StringIO as HelpEater
-+
-+if not hasattr(sys, 'argv'):
-+ sys.argv = ['<hexchat>']
-+
-+VERSION = b'2.0' # Sync with hexchat.__version__
-+PLUGIN_NAME = ffi.new('char[]', b'Python')
-+PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
-+PLUGIN_VERSION = ffi.new('char[]', VERSION)
-+
-+# TODO: Constants should be screaming snake case
-+hexchat = None
-+local_interp = None
-+hexchat_stdout = None
-+plugins = set()
-+
-+
-+@contextmanager
-+def redirected_stdout():
-+ sys.stdout = sys.__stdout__
-+ sys.stderr = sys.__stderr__
-+ yield
-+ sys.stdout = hexchat_stdout
-+ sys.stderr = hexchat_stdout
-+
-+
-+if os.getenv('HEXCHAT_LOG_PYTHON'):
-+ def log(*args):
-+ with redirected_stdout():
-+ print(*args)
-+
-+else:
-+ def log(*args):
-+ pass
-+
-+
-+class Stdout:
-+ def __init__(self):
-+ self.buffer = bytearray()
-+
-+ def write(self, string):
-+ string = string.encode()
-+ idx = string.rfind(b'\n')
-+ if idx != -1:
-+ self.buffer += string[:idx]
-+ lib.hexchat_print(lib.ph, bytes(self.buffer))
-+ self.buffer = bytearray(string[idx + 1:])
-+ else:
-+ self.buffer += string
-+
-+ def isatty(self):
-+ return False
-+
-+
-+class Attribute:
-+ def __init__(self):
-+ self.time = 0
-+
-+ def __repr__(self):
-+ return '<Attribute object at {}>'.format(id(self))
-+
-+
-+class Hook:
-+ def __init__(self, plugin, callback, userdata, is_unload):
-+ self.is_unload = is_unload
-+ self.plugin = weakref.proxy(plugin)
-+ self.callback = callback
-+ self.userdata = userdata
-+ self.hexchat_hook = None
-+ self.handle = ffi.new_handle(weakref.proxy(self))
-+
-+ def __del__(self):
-+ log('Removing hook', id(self))
-+ if self.is_unload is False:
-+ assert self.hexchat_hook is not None
-+ lib.hexchat_unhook(lib.ph, self.hexchat_hook)
-+
-+
-+if sys.version_info[0] == 2:
-+ def compile_file(data, filename):
-+ return compile(data, filename, 'exec', dont_inherit=True)
-+
-+
-+ def compile_line(string):
-+ try:
-+ return compile(string, '<string>', 'eval', dont_inherit=True)
-+
-+ except SyntaxError:
-+ # For some reason `print` is invalid for eval
-+ # This will hide any return value though
-+ return compile(string, '<string>', 'exec', dont_inherit=True)
-+else:
-+ def compile_file(data, filename):
-+ return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
-+
-+
-+ def compile_line(string):
-+ # newline appended to solve unexpected EOF issues
-+ return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True)
-+
-+
-+class Plugin:
-+ def __init__(self):
-+ self.ph = None
-+ self.name = ''
-+ self.filename = ''
-+ self.version = ''
-+ self.description = ''
-+ self.hooks = set()
-+ self.globals = {
-+ '__plugin': weakref.proxy(self),
-+ '__name__': '__main__',
-+ }
-+
-+ def add_hook(self, callback, userdata, is_unload=False):
-+ hook = Hook(self, callback, userdata, is_unload=is_unload)
-+ self.hooks.add(hook)
-+ return hook
-+
-+ def remove_hook(self, hook):
-+ for h in self.hooks:
-+ if id(h) == hook:
-+ ud = h.userdata
-+ self.hooks.remove(h)
-+ return ud
-+
-+ log('Hook not found')
-+ return None
-+
-+ def loadfile(self, filename):
-+ try:
-+ self.filename = filename
-+ with open(filename) as f:
-+ data = f.read()
-+ compiled = compile_file(data, filename)
-+ exec(compiled, self.globals)
-+
-+ try:
-+ self.name = self.globals['__module_name__']
-+
-+ except KeyError:
-+ lib.hexchat_print(lib.ph, b'Failed to load module: __module_name__ must be set')
-+
-+ return False
-+
-+ self.version = self.globals.get('__module_version__', '')
-+ self.description = self.globals.get('__module_description__', '')
-+ self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), self.name.encode(),
-+ self.description.encode(), self.version.encode(), ffi.NULL)
-+
-+ except Exception as e:
-+ lib.hexchat_print(lib.ph, 'Failed to load module: {}'.format(e).encode())
-+ traceback.print_exc()
-+ return False
-+
-+ return True
-+
-+ def __del__(self):
-+ log('unloading', self.filename)
-+ for hook in self.hooks:
-+ if hook.is_unload is True:
-+ try:
-+ hook.callback(hook.userdata)
-+
-+ except Exception as e:
-+ log('Failed to run hook:', e)
-+ traceback.print_exc()
-+
-+ del self.hooks
-+ if self.ph is not None:
-+ lib.hexchat_plugingui_remove(lib.ph, self.ph)
-+
-+
-+if sys.version_info[0] == 2:
-+ def __decode(string):
-+ return string
-+
-+else:
-+ def __decode(string):
-+ return string.decode()
-+
-+
-+# There can be empty entries between non-empty ones so find the actual last value
-+def wordlist_len(words):
-+ for i in range(31, 0, -1):
-+ if ffi.string(words[i]):
-+ return i
-+
-+ return 0
-+
-+
-+def create_wordlist(words):
-+ size = wordlist_len(words)
-+ return [__decode(ffi.string(words[i])) for i in range(1, size + 1)]
-+
-+
-+# This function only exists for compat reasons with the C plugin
-+# It turns the word list from print hooks into a word_eol list
-+# This makes no sense to do...
-+def create_wordeollist(words):
-+ words = reversed(words)
-+ accum = None
-+ ret = []
-+ for word in words:
-+ if accum is None:
-+ accum = word
-+
-+ elif word:
-+ last = accum
-+ accum = ' '.join((word, last))
-+
-+ ret.insert(0, accum)
-+
-+ return ret
-+
-+
-+def to_cb_ret(value):
-+ if value is None:
-+ return 0
-+
-+ return int(value)
-+
-+
-+@ffi.def_extern()
-+def _on_command_hook(word, word_eol, userdata):
-+ hook = ffi.from_handle(userdata)
-+ word = create_wordlist(word)
-+ word_eol = create_wordlist(word_eol)
-+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+@ffi.def_extern()
-+def _on_print_hook(word, userdata):
-+ hook = ffi.from_handle(userdata)
-+ word = create_wordlist(word)
-+ word_eol = create_wordeollist(word)
-+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+@ffi.def_extern()
-+def _on_print_attrs_hook(word, attrs, userdata):
-+ hook = ffi.from_handle(userdata)
-+ word = create_wordlist(word)
-+ word_eol = create_wordeollist(word)
-+ attr = Attribute()
-+ attr.time = attrs.server_time_utc
-+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
-+
-+
-+@ffi.def_extern()
-+def _on_server_hook(word, word_eol, userdata):
-+ hook = ffi.from_handle(userdata)
-+ word = create_wordlist(word)
-+ word_eol = create_wordlist(word_eol)
-+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
-+
-+
-+@ffi.def_extern()
-+def _on_server_attrs_hook(word, word_eol, attrs, userdata):
-+ hook = ffi.from_handle(userdata)
-+ word = create_wordlist(word)
-+ word_eol = create_wordlist(word_eol)
-+ attr = Attribute()
-+ attr.time = attrs.server_time_utc
-+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
-+
-+
-+@ffi.def_extern()
-+def _on_timer_hook(userdata):
-+ hook = ffi.from_handle(userdata)
-+ if hook.callback(hook.userdata) is True:
-+ return 1
-+
-+ hook.is_unload = True # Don't unhook
-+ for h in hook.plugin.hooks:
-+ if h == hook:
-+ hook.plugin.hooks.remove(h)
-+ break
-+
-+ return 0
-+
-+
-+@ffi.def_extern(error=3)
-+def _on_say_command(word, word_eol, userdata):
-+ channel = ffi.string(lib.hexchat_get_info(lib.ph, b'channel'))
-+ if channel == b'>>python<<':
-+ python = ffi.string(word_eol[1])
-+ lib.hexchat_print(lib.ph, b'>>> ' + python)
-+ exec_in_interp(__decode(python))
-+ return 1
-+
-+ return 0
-+
-+
-+def load_filename(filename):
-+ filename = os.path.expanduser(filename)
-+ if not os.path.isabs(filename):
-+ configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
-+
-+ filename = os.path.join(configdir, 'addons', filename)
-+
-+ if filename and not any(plugin.filename == filename for plugin in plugins):
-+ plugin = Plugin()
-+ if plugin.loadfile(filename):
-+ plugins.add(plugin)
-+ return True
-+
-+ return False
-+
-+
-+def unload_name(name):
-+ if name:
-+ for plugin in plugins:
-+ if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
-+ plugins.remove(plugin)
-+ return True
-+
-+ return False
-+
-+
-+def reload_name(name):
-+ if name:
-+ for plugin in plugins:
-+ if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
-+ filename = plugin.filename
-+ plugins.remove(plugin)
-+ return load_filename(filename)
-+
-+ return False
-+
-+
-+@contextmanager
-+def change_cwd(path):
-+ old_cwd = os.getcwd()
-+ os.chdir(path)
-+ yield
-+ os.chdir(old_cwd)
-+
-+
-+def autoload():
-+ configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
-+ addondir = os.path.join(configdir, 'addons')
-+ try:
-+ with change_cwd(addondir): # Maintaining old behavior
-+ for f in os.listdir(addondir):
-+ if f.endswith('.py'):
-+ log('Autoloading', f)
-+ # TODO: Set cwd
-+ load_filename(os.path.join(addondir, f))
-+
-+ except FileNotFoundError as e:
-+ log('Autoload failed', e)
-+
-+
-+def list_plugins():
-+ if not plugins:
-+ lib.hexchat_print(lib.ph, b'No python modules loaded')
-+ return
-+
-+ tbl_headers = [b'Name', b'Version', b'Filename', b'Description']
-+ tbl = [
-+ tbl_headers,
-+ [(b'-' * len(s)) for s in tbl_headers]
-+ ]
-+
-+ for plugin in plugins:
-+ basename = os.path.basename(plugin.filename).encode()
-+ name = plugin.name.encode()
-+ version = plugin.version.encode() if plugin.version else b'<none>'
-+ description = plugin.description.encode() if plugin.description else b'<none>'
-+ tbl.append((name, version, basename, description))
-+
-+ column_sizes = [
-+ max(len(item) for item in column)
-+ for column in zip(*tbl)
-+ ]
-+
-+ for row in tbl:
-+ lib.hexchat_print(lib.ph, b' '.join(item.ljust(column_sizes[i])
-+ for i, item in enumerate(row)))
-+ lib.hexchat_print(lib.ph, b'')
-+
-+
-+def exec_in_interp(python):
-+ global local_interp
-+
-+ if not python:
-+ return
-+
-+ if local_interp is None:
-+ local_interp = Plugin()
-+ local_interp.locals = {}
-+ local_interp.globals['hexchat'] = hexchat
-+
-+ code = compile_line(python)
-+ try:
-+ ret = eval(code, local_interp.globals, local_interp.locals)
-+ if ret is not None:
-+ lib.hexchat_print(lib.ph, '{}'.format(ret).encode())
-+
-+ except Exception as e:
-+ traceback.print_exc(file=hexchat_stdout)
-+
-+
-+@ffi.def_extern()
-+def _on_load_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ load_filename(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern()
-+def _on_unload_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ unload_name(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern()
-+def _on_reload_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ reload_name(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern(error=3)
-+def _on_py_command(word, word_eol, userdata):
-+ subcmd = __decode(ffi.string(word[2])).lower()
-+
-+ if subcmd == 'exec':
-+ python = __decode(ffi.string(word_eol[3]))
-+ exec_in_interp(python)
-+
-+ elif subcmd == 'load':
-+ filename = __decode(ffi.string(word[3]))
-+ load_filename(filename)
-+
-+ elif subcmd == 'unload':
-+ name = __decode(ffi.string(word[3]))
-+ if not unload_name(name):
-+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
-+
-+ elif subcmd == 'reload':
-+ name = __decode(ffi.string(word[3]))
-+ if not reload_name(name):
-+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
-+
-+ elif subcmd == 'console':
-+ lib.hexchat_command(lib.ph, b'QUERY >>python<<')
-+
-+ elif subcmd == 'list':
-+ list_plugins()
-+
-+ elif subcmd == 'about':
-+ lib.hexchat_print(lib.ph, b'HexChat Python interface version ' + VERSION)
-+
-+ else:
-+ lib.hexchat_command(lib.ph, b'HELP PY')
-+
-+ return 3
-+
-+
-+@ffi.def_extern()
-+def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
-+ global hexchat
-+ global hexchat_stdout
-+
-+ signal.signal(signal.SIGINT, signal.SIG_DFL)
-+
-+ plugin_name[0] = PLUGIN_NAME
-+ plugin_desc[0] = PLUGIN_DESC
-+ plugin_version[0] = PLUGIN_VERSION
-+
-+ try:
-+ libdir = __decode(ffi.string(libdir))
-+ modpath = os.path.join(libdir, '..', 'python')
-+ sys.path.append(os.path.abspath(modpath))
-+ hexchat = importlib.import_module('hexchat')
-+
-+ except (UnicodeDecodeError, ImportError) as e:
-+ lib.hexchat_print(lib.ph, b'Failed to import module: ' + repr(e).encode())
-+
-+ return 0
-+
-+ hexchat_stdout = Stdout()
-+ sys.stdout = hexchat_stdout
-+ sys.stderr = hexchat_stdout
-+ pydoc.help = pydoc.Helper(HelpEater(), HelpEater())
-+
-+ lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'UNLOAD', 0, lib._on_unload_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'RELOAD', 0, lib._on_reload_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'PY', 0, lib._on_py_command, b'''Usage: /PY LOAD <filename>
-+ UNLOAD <filename|name>
-+ RELOAD <filename|name>
-+ LIST
-+ EXEC <command>
-+ CONSOLE
-+ ABOUT''', ffi.NULL)
-+
-+ lib.hexchat_print(lib.ph, b'Python interface loaded')
-+ autoload()
-+ return 1
-+
-+
-+@ffi.def_extern()
-+def _on_plugin_deinit():
-+ global local_interp
-+ global hexchat
-+ global hexchat_stdout
-+ global plugins
-+
-+ plugins = set()
-+ local_interp = None
-+ hexchat = None
-+ hexchat_stdout = None
-+ sys.stdout = sys.__stdout__
-+ sys.stderr = sys.__stderr__
-+ pydoc.help = pydoc.Helper()
-+
-+ for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
-+ try:
-+ del sys.modules[mod]
-+
-+ except KeyError:
-+ pass
-+
-+ return 1
-diff --git a/plugins/python/python_style_guide.md b/plugins/python/python_style_guide.md
-new file mode 100644
-index 00000000..41db2474
---- /dev/null
-+++ b/plugins/python/python_style_guide.md
-@@ -0,0 +1,26 @@
-+# HexChat Python Module Style Guide
-+
-+(This is a work in progress).
-+
-+## General rules
-+
-+- PEP8 as general fallback recommendations
-+- Max line length: 120
-+- Avoid overcomplex compound statements. i.e. dont do this: `somevar = x if x == y else z if a == b and c == b else x`
-+
-+## Indentation style
-+
-+### Multi-line functions
-+
-+```python
-+foo(really_long_arg_1,
-+ really_long_arg_2)
-+```
-+
-+### Mutli-line lists/dicts
-+
-+```python
-+foo = {
-+ 'bar': 'baz',
-+}
-+```
-diff --git a/plugins/python/xchat.py b/plugins/python/xchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/xchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/src/common/meson.build b/src/common/meson.build
-index bbb64645..492227b2 100644
---- a/src/common/meson.build
-+++ b/src/common/meson.build
-@@ -93,10 +93,6 @@ endif
-
- if get_option('with-plugin')
- common_deps += libgmodule_dep
-- common_cflags += '-DHEXCHATLIBDIR="@0@"'.format(join_paths(get_option('prefix'),
-- get_option('libdir'),
-- 'hexchat/plugins'))
--
- install_headers('hexchat-plugin.h')
- endif
-
-diff --git a/win32/installer/hexchat.iss.tt b/win32/installer/hexchat.iss.tt
-index e242ee96..3ac5ec41 100644
---- a/win32/installer/hexchat.iss.tt
-+++ b/win32/installer/hexchat.iss.tt
-@@ -164,6 +164,7 @@ Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0";
- Source: "share\lua\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua
-+Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
-
- Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum
- Source: "plugins\hcexec.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\exec
-@@ -175,11 +176,15 @@ Source: "WinSparkle.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pl
- Source: "plugins\hcwinamp.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\winamp
- Source: "share\system.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\sysinfo
- Source: "plugins\hcsysinfo.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\sysinfo
-+Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
-+
-+Source: "python\*.py"; DestDir: "{app}\python"; Flags: ignoreversion; Components: langs\python
-
- Source: "plugins\hcpython2.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python2
-+Source: "_cffi_backend.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python2
-+
- Source: "plugins\hcpython3.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python3
--Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
--Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
-+Source: "_cffi_backend.cp3*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python3
-
- Source: "hexchat.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
- Source: "hexchat-text.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xctext
diff --git a/files/0007-python-fix-exception-with-list-pluginpref.patch b/files/0007-python-fix-exception-with-list-pluginpref.patch
deleted file mode 100644
--- a/files/0007-python-fix-exception-with-list-pluginpref.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 090fd29acf4af0d8e13fcf2861b14a356db72641 Mon Sep 17 00:00:00 2001
-From: Sbgodin <christophe.henry@sbgodin.fr>
-Date: Sun, 7 Mar 2021 12:51:45 +0000
-Subject: [PATCH] python: Fix exception with list_pluginpref()
-
-__decode cannot work (with Python3) because prefs_str has no attribute 'decode'.
-
-Related to https://github.com/hexchat/hexchat/issues/2531
----
- plugins/python/_hexchat.py | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-index 567b34934..5e4b0c65f 100644
---- a/plugins/python/_hexchat.py
-+++ b/plugins/python/_hexchat.py
-@@ -319,7 +319,7 @@ def del_pluginpref(name):
- def list_pluginpref():
- prefs_str = ffi.new('char[4096]')
- if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
-- return __decode(prefs_str).split(',')
-+ return __decode(ffi.string(prefs_str)).split(',')
-
- return []
-
diff --git a/files/series b/files/series
--- a/files/series
+++ b/files/series
@@ -1,7 +1,2 @@
0001-common-cfgfiles-Apply-Solus-defaults.patch
-0002-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
-0003-Add-Libera-Chat-to-network-list.patch
-0004-Change-default-network-to-libera-chat.patch
-0005-Default-to-Solus.patch
-0006-python-cffi.patch
-0007-python-fix-exception-with-list-pluginpref.patch
+0002-Default-to-Solus.patch
diff --git a/package.yml b/package.yml
--- a/package.yml
+++ b/package.yml
@@ -1,9 +1,9 @@
name : hexchat
-version : 2.14.3
-release : 36
+version : 2.16.0
+release : 37
homepage : https://hexchat.github.io/
source :
- - https://dl.hexchat.net/hexchat/hexchat-2.14.3.tar.xz : 901a9d13db5a4da69b827f6093306bbd16863dc49016f7668bd3e4506512e882
+ - https://dl.hexchat.net/hexchat/hexchat-2.16.0.tar.xz : 412e31078bb2c5f9664a5b9ae4d7989e27a47f141ee09dce2e1181f8c482dc36
license : GPL-2.0-or-later
component :
- network.irc
@@ -21,25 +21,23 @@
- /usr/share/mime/packages/io.github.Hexchat.ThemeManager.xml
builddeps :
- pkgconfig(dbus-glib-1)
- - pkgconfig(gtk+-3.0)
- pkgconfig(iso-codes)
- pkgconfig(libcanberra)
- - pkgconfig(libnotify)
- pkgconfig(libpci)
- - pkgconfig(libproxy-1.0)
- pkgconfig(luajit)
- pkgconfig(python3-embed)
- mono-msbuild
- python-cffi
rundeps :
- enchant
+ - gtk2-engine-murrine
- python-cffi
- theme-manager :
- hexchat
- mono
setup : |
%apply_patches
- %meson_configure -Dwith-theme-manager=true
+ %meson_configure -Dtheme-manager=true
build : |
%ninja_build
install : |
diff --git a/pspec_x86_64.xml b/pspec_x86_64.xml
--- a/pspec_x86_64.xml
+++ b/pspec_x86_64.xml
@@ -3,8 +3,8 @@
<Name>hexchat</Name>
<Homepage>https://hexchat.github.io/</Homepage>
<Packager>
- <Name>Reilly Brogan</Name>
- <Email>solus@reillybrogan.com</Email>
+ <Name>Algent Albrahimi</Name>
+ <Email>algent@protonmail.com</Email>
</Packager>
<License>GPL-2.0-or-later</License>
<PartOf>network.irc</PartOf>
@@ -95,7 +95,7 @@
<Description xml:lang="en">HexChat is an IRC client based on XChat, but unlike XChat it’s completely free for both Windows and Unix-like systems.</Description>
<PartOf>programming.devel</PartOf>
<RuntimeDependencies>
- <Dependency release="36">hexchat</Dependency>
+ <Dependency release="37">hexchat</Dependency>
</RuntimeDependencies>
<Files>
<Path fileType="header">/usr/include/hexchat-plugin.h</Path>
@@ -108,7 +108,7 @@
<Description xml:lang="en">A theme manager for the hexchat IRC client</Description>
<PartOf>network.irc</PartOf>
<RuntimeDependencies>
- <Dependency releaseFrom="36">hexchat</Dependency>
+ <Dependency releaseFrom="37">hexchat</Dependency>
</RuntimeDependencies>
<Files>
<Path fileType="executable">/usr/bin/thememan</Path>
@@ -118,12 +118,12 @@
</Files>
</Package>
<History>
- <Update release="36">
- <Date>2021-08-09</Date>
- <Version>2.14.3</Version>
+ <Update release="37">
+ <Date>2021-10-02</Date>
+ <Version>2.16.0</Version>
<Comment>Packaging update</Comment>
- <Name>Reilly Brogan</Name>
- <Email>solus@reillybrogan.com</Email>
+ <Name>Algent Albrahimi</Name>
+ <Email>algent@protonmail.com</Email>
</Update>
</History>
</PISI>
\ No newline at end of file
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Aug 11, 7:40 PM (2 h, 55 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5826968
Default Alt Text
D11931.diff (131 KB)
Attached To
Mode
D11931: Update hexchat to 2.16.0
Attached
Detach File
Event Timeline
Log In to Comment