From e0cee28da1c61c81a8414ef911feedc4d4c1ad83 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jan 11 2022 18:12:24 +0000 Subject: import hexchat-2.16.0-1.el9 --- diff --git a/.gitignore b/.gitignore index ee339c5..f0629db 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/hexchat-2.14.3.tar.xz +SOURCES/hexchat-2.16.0.tar.xz diff --git a/.hexchat.metadata b/.hexchat.metadata index a713581..c712ff2 100644 --- a/.hexchat.metadata +++ b/.hexchat.metadata @@ -1 +1 @@ -a219796d70023b675e5ea24af9f22beb9e855abb SOURCES/hexchat-2.14.3.tar.xz +9867719f068ae867b3ca790dcf15ef3df83c7148 SOURCES/hexchat-2.16.0.tar.xz diff --git a/SOURCES/090fd29acf4af0d8e13fcf2861b14a356db72641.patch b/SOURCES/090fd29acf4af0d8e13fcf2861b14a356db72641.patch deleted file mode 100644 index f5bbc41..0000000 --- a/SOURCES/090fd29acf4af0d8e13fcf2861b14a356db72641.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 090fd29acf4af0d8e13fcf2861b14a356db72641 Mon Sep 17 00:00:00 2001 -From: Sbgodin -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/SOURCES/163608d7fd861c2c4911a38f45be484c88626bdc.patch b/SOURCES/163608d7fd861c2c4911a38f45be484c88626bdc.patch deleted file mode 100644 index 871eabc..0000000 --- a/SOURCES/163608d7fd861c2c4911a38f45be484c88626bdc.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 163608d7fd861c2c4911a38f45be484c88626bdc Mon Sep 17 00:00:00 2001 -From: John Levon -Date: Mon, 7 Sep 2020 17:53:31 +0100 -Subject: [PATCH] Use pango_font_metrics_get_height() to calculate font height - (#2500) - ---- - src/fe-gtk/xtext.c | 18 ++++++++++++++++-- - 1 file changed, 16 insertions(+), 2 deletions(-) - -diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c -index fac0c4e6d..418bb4da5 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/SOURCES/5deb69591992d4fede9090b60d3dc847612a4d60.patch b/SOURCES/5deb69591992d4fede9090b60d3dc847612a4d60.patch deleted file mode 100644 index fdfc74f..0000000 --- a/SOURCES/5deb69591992d4fede9090b60d3dc847612a4d60.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 5deb69591992d4fede9090b60d3dc847612a4d60 Mon Sep 17 00:00:00 2001 -From: Patrick Griffis -Date: Wed, 11 Mar 2020 11:07:56 -0700 -Subject: [PATCH] build: Better support building against python 3.8+ - -Closes #2441 ---- - plugins/python/meson.build | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/plugins/python/meson.build b/plugins/python/meson.build -index 2ad5128e5..eb762134a 100644 ---- a/plugins/python/meson.build -+++ b/plugins/python/meson.build -@@ -1,6 +1,12 @@ - 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) -+ endif -+ if not python_dep.found() -+ python_dep = dependency(python_opt, version: '>= 3.3') -+ endif - else - python_dep = dependency(python_opt, version: '>= 2.7') - endif diff --git a/SOURCES/hexchat-Avoid-direct-use-of-libproxy.patch b/SOURCES/hexchat-Avoid-direct-use-of-libproxy.patch deleted file mode 100644 index 0957f30..0000000 --- a/SOURCES/hexchat-Avoid-direct-use-of-libproxy.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 54321bae1feaf0845d75f67245c95795651277f5 Mon Sep 17 00:00:00 2001 -From: Michael Catanzaro -Date: Mon, 12 Jul 2021 08:38:02 -0500 -Subject: [PATCH] Avoid direct use of libproxy - -Since hexchat already depends on GLib, it's better to use GProxyResolver -instead. This might use libproxy, or not, as appropriate. - -P.S. This removes a memory safety issue because proxy_list is allocated -using malloc(), not g_malloc(), and therefore using g_strfreev() is -incorrect. The proper way to free the proxy list returned by libproxy -is to use px_proxy_factory_free_proxies() (but nobody does that because -it was added in libproxy 0.4.16, which is somewhat recent). ---- - meson.build | 1 - - meson_options.txt | 3 --- - src/common/hexchat.c | 16 ---------------- - src/common/meson.build | 4 ---- - src/common/server.c | 26 ++++++++++++++++---------- - src/fe-gtk/setup.c | 2 -- - 6 files changed, 16 insertions(+), 36 deletions(-) - -diff --git a/meson.build b/meson.build -index 18baf26e126e..381e3fd87ac6 100644 ---- a/meson.build -+++ b/meson.build -@@ -33,7 +33,6 @@ config_h.set10('ENABLE_NLS', true) - - # Optional features - config_h.set('USE_OPENSSL', get_option('with-ssl')) --config_h.set('USE_LIBPROXY', get_option('with-libproxy')) - config_h.set('USE_LIBCANBERRA', get_option('with-libcanberra')) - config_h.set('USE_DBUS', get_option('with-dbus')) - config_h.set('USE_PLUGIN', get_option('with-plugin')) -diff --git a/meson_options.txt b/meson_options.txt -index 100a5ee72ad7..ad03d6bc58ef 100644 ---- a/meson_options.txt -+++ b/meson_options.txt -@@ -13,9 +13,6 @@ option('with-plugin', type: 'boolean', - option('with-dbus', type: 'boolean', - description: 'Support used for single-instance and scripting interface, Unix only' - ) --option('with-libproxy', type: 'boolean', -- description: 'Support for getting proxy information, Unix only' --) - option('with-libnotify', type: 'boolean', - description: 'Support for freedesktop notifications, Unix only' - ) -diff --git a/src/common/hexchat.c b/src/common/hexchat.c -index e9a9a7fc9fcd..a98fcafba2f7 100644 ---- a/src/common/hexchat.c -+++ b/src/common/hexchat.c -@@ -57,10 +57,6 @@ - #include /* for g_type_init() */ - #endif - --#ifdef USE_LIBPROXY --#include --#endif -- - GSList *popup_list = 0; - GSList *button_list = 0; - GSList *dlgbutton_list = 0; -@@ -111,10 +107,6 @@ struct session *current_tab; - struct session *current_sess = 0; - struct hexchatprefs prefs; - --#ifdef USE_LIBPROXY --pxProxyFactory *libproxy_factory; --#endif -- - /* - * Update the priority queue of the "interesting sessions" - * (sess_list_by_lastact). -@@ -1101,10 +1093,6 @@ main (int argc, char *argv[]) - hexchat_remote (); - #endif - --#ifdef USE_LIBPROXY -- libproxy_factory = px_proxy_factory_new (); --#endif -- - #ifdef WIN32 - coinit_result = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); - if (SUCCEEDED (coinit_result)) -@@ -1147,10 +1135,6 @@ main (int argc, char *argv[]) - } - #endif - --#ifdef USE_LIBPROXY -- px_proxy_factory_free (libproxy_factory); --#endif -- - #ifdef WIN32 - WSACleanup (); - #endif -diff --git a/src/common/meson.build b/src/common/meson.build -index bbb646459522..5885ec9e47c1 100644 ---- a/src/common/meson.build -+++ b/src/common/meson.build -@@ -77,10 +77,6 @@ if get_option('with-ssl') - common_deps += libssl_dep - endif - --if get_option('with-libproxy') -- common_deps += dependency('libproxy-1.0') --endif -- - if get_option('with-libcanberra') - common_deps += dependency('libcanberra', version: '>= 0.22') - endif -diff --git a/src/common/server.c b/src/common/server.c -index 3db0a9635fc5..a96e81f43d15 100644 ---- a/src/common/server.c -+++ b/src/common/server.c -@@ -61,10 +61,6 @@ - #include "ssl.h" - #endif - --#ifdef USE_LIBPROXY --#include --#endif -- - #ifdef USE_OPENSSL - /* local variables */ - static struct session *g_sess = NULL; -@@ -78,9 +74,15 @@ static void server_disconnect (session * sess, int sendquit, int err); - static int server_cleanup (server * serv); - static void server_connect (server *serv, char *hostname, int port, int no_login); - --#ifdef USE_LIBPROXY --extern pxProxyFactory *libproxy_factory; --#endif -+static void -+write_error (char *message, GError **error) -+{ -+ if (error == NULL || *error == NULL) { -+ return; -+ } -+ g_printerr ("%s: %s\n", message, (*error)->message); -+ g_clear_error (error); -+} - - /* actually send to the socket. This might do a character translation or - send via SSL. server/dcc both use this function. */ -@@ -1365,14 +1367,16 @@ server_child (server * serv) - - if (!serv->dont_use_proxy) /* blocked in serverlist? */ - { --#ifdef USE_LIBPROXY - if (prefs.hex_net_proxy_type == 5) - { - char **proxy_list; - char *url, *proxy; -+ GProxyResolver *resolver; -+ GError *error = NULL; - -+ resolver = g_proxy_resolver_get_default (); - url = g_strdup_printf ("irc://%s:%d", hostname, port); -- proxy_list = px_proxy_factory_get_proxies (libproxy_factory, url); -+ proxy_list = g_proxy_resolver_lookup (resolver, url, NULL, &error); - - if (proxy_list) { - /* can use only one */ -@@ -1385,6 +1389,8 @@ server_child (server * serv) - proxy_type = 3; - else if (!strncmp (proxy, "socks", 5)) - proxy_type = 2; -+ } else { -+ write_error ("Failed to lookup proxy", &error); - } - - if (proxy_type) { -@@ -1399,7 +1405,7 @@ server_child (server * serv) - g_strfreev (proxy_list); - g_free (url); - } --#endif -+ - if (prefs.hex_net_proxy_host[0] && - prefs.hex_net_proxy_type > 0 && - prefs.hex_net_proxy_use != 2) /* proxy is NOT dcc-only */ -diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c -index 3d003eefc776..a7e3a15cac52 100644 ---- a/src/fe-gtk/setup.c -+++ b/src/fe-gtk/setup.c -@@ -614,9 +614,7 @@ static const char *const proxytypes[] = - N_("SOCKS4"), - N_("SOCKS5"), - N_("HTTP"), --#ifdef USE_LIBPROXY - N_("Auto"), --#endif - NULL - }; - --- -2.31.1 - diff --git a/SOURCES/python-plugin-from-master.patch b/SOURCES/python-plugin-from-master.patch deleted file mode 100644 index 80bc599..0000000 --- a/SOURCES/python-plugin-from-master.patch +++ /dev/null @@ -1,4067 +0,0 @@ -diff --color -Nur hexchat-2.14.3/plugins/python/generate_plugin.py hexchat/plugins/python/generate_plugin.py ---- hexchat-2.14.3/plugins/python/generate_plugin.py 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/generate_plugin.py 2020-11-06 15:03:00.401820501 -0800 -@@ -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 --color -Nur hexchat-2.14.3/plugins/python/_hexchat.py hexchat/plugins/python/_hexchat.py ---- hexchat-2.14.3/plugins/python/_hexchat.py 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/_hexchat.py 2020-11-06 15:03:00.401820501 -0800 -@@ -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 --color -Nur hexchat-2.14.3/plugins/python/hexchat.py hexchat/plugins/python/hexchat.py ---- hexchat-2.14.3/plugins/python/hexchat.py 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/hexchat.py 2020-11-06 15:03:00.402820502 -0800 -@@ -0,0 +1 @@ -+from _hexchat import * -diff --color -Nur hexchat-2.14.3/plugins/python/meson.build hexchat/plugins/python/meson.build ---- hexchat-2.14.3/plugins/python/meson.build 2020-11-06 15:05:12.122924612 -0800 -+++ hexchat/plugins/python/meson.build 2020-11-06 15:03:00.402820502 -0800 -@@ -3,16 +3,28 @@ - # 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) -- endif -- if not python_dep.found() -+ 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 --color -Nur hexchat-2.14.3/plugins/python/python2.vcxproj hexchat/plugins/python/python2.vcxproj ---- hexchat-2.14.3/plugins/python/python2.vcxproj 2019-12-20 22:43:47.652401700 -0800 -+++ hexchat/plugins/python/python2.vcxproj 2020-11-06 15:03:00.402820502 -0800 -@@ -37,6 +37,9 @@ - "$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies) - $(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories) - -+ -+ "$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c" -+ - - - -@@ -48,12 +51,15 @@ - "$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies) - $(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories) - -+ -+ "$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c" -+ - - - - - -- -+ - - - -diff --color -Nur hexchat-2.14.3/plugins/python/python3.vcxproj hexchat/plugins/python/python3.vcxproj ---- hexchat-2.14.3/plugins/python/python3.vcxproj 2019-12-20 22:43:47.652401700 -0800 -+++ hexchat/plugins/python/python3.vcxproj 2020-11-06 15:03:00.403820502 -0800 -@@ -37,6 +37,9 @@ - "$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies) - $(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories) - -+ -+ "$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c" -+ - - - -@@ -48,12 +51,20 @@ - "$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies) - $(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories) - -+ -+ "$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c" -+ - - -+ -+ - -+ -+ -+ - - -- -+ - - -- -+ -\ No newline at end of file -diff --color -Nur hexchat-2.14.3/plugins/python/python3.vcxproj.filters hexchat/plugins/python/python3.vcxproj.filters ---- hexchat-2.14.3/plugins/python/python3.vcxproj.filters 2019-12-20 22:43:47.652401700 -0800 -+++ hexchat/plugins/python/python3.vcxproj.filters 2020-11-06 15:03:00.403820502 -0800 -@@ -9,13 +9,26 @@ - - - -- -- Source Files -- -+ - - - - Resource Files - -+ -+ Source Files -+ -+ -+ Source Files -+ -+ -+ Source Files -+ -+ -+ Source Files -+ -+ -+ Source Files -+ - - -\ No newline at end of file -diff --color -Nur hexchat-2.14.3/plugins/python/python.c hexchat/plugins/python/python.c ---- hexchat-2.14.3/plugins/python/python.c 2019-12-20 22:43:47.652401700 -0800 -+++ hexchat/plugins/python/python.c 1969-12-31 16:00:00.000000000 -0800 -@@ -1,2837 +0,0 @@ --/* --* Copyright (c) 2002-2003 Gustavo Niemeyer --* --* 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 --#include --#include --#include --#include -- --#ifdef WIN32 --#include --#else --#include --#include --#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 --#include --#include -- --/* 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 \n\ -- UNLOAD \n\ -- RELOAD \n\ -- LIST\n\ -- EXEC \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/ 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("", 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"", 0 }; --#else -- char *argv[] = { "", 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 -- : "", -- basename, -- *plg->description ? plg->description -- : ""); -- 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"", 0 }; --#else -- char *argv[] = { "", 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 --color -Nur hexchat-2.14.3/plugins/python/python.def hexchat/plugins/python/python.def ---- hexchat-2.14.3/plugins/python/python.def 2019-12-20 22:43:47.652401700 -0800 -+++ hexchat/plugins/python/python.def 2020-11-06 15:03:00.402820502 -0800 -@@ -1,4 +1,3 @@ - EXPORTS - hexchat_plugin_init - hexchat_plugin_deinit --hexchat_plugin_get_info -diff --color -Nur hexchat-2.14.3/plugins/python/python.py hexchat/plugins/python/python.py ---- hexchat-2.14.3/plugins/python/python.py 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/python.py 2020-11-06 15:03:00.402820502 -0800 -@@ -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 = [''] -+ -+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 ''.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, '', 'eval', dont_inherit=True) -+ -+ except SyntaxError: -+ # For some reason `print` is invalid for eval -+ # This will hide any return value though -+ return compile(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', '', '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, 1, -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'' -+ description = plugin.description.encode() if plugin.description else b'' -+ 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 -+ UNLOAD -+ RELOAD -+ LIST -+ EXEC -+ 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 --color -Nur hexchat-2.14.3/plugins/python/python_style_guide.md hexchat/plugins/python/python_style_guide.md ---- hexchat-2.14.3/plugins/python/python_style_guide.md 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/python_style_guide.md 2020-11-06 15:03:00.403820502 -0800 -@@ -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 --color -Nur hexchat-2.14.3/plugins/python/xchat.py hexchat/plugins/python/xchat.py ---- hexchat-2.14.3/plugins/python/xchat.py 1969-12-31 16:00:00.000000000 -0800 -+++ hexchat/plugins/python/xchat.py 2020-11-06 15:03:00.403820502 -0800 -@@ -0,0 +1 @@ -+from _hexchat import * diff --git a/SPECS/hexchat.spec b/SPECS/hexchat.spec index b991b2b..4d14ed3 100644 --- a/SPECS/hexchat.spec +++ b/SPECS/hexchat.spec @@ -2,22 +2,12 @@ Summary: A popular and easy to use graphical IRC (chat) client Name: hexchat -Version: 2.14.3 -Release: 17%{?dist} +Version: 2.16.0 +Release: 1%{?dist} License: GPLv2+ URL: https://hexchat.github.io Source: https://dl.hexchat.net/hexchat/%{name}-%{version}.tar.xz -# Patch to link better to python 3.8 -Patch1: https://github.com/hexchat/hexchat/commit/5deb69591992d4fede9090b60d3dc847612a4d60.patch -Patch2: python-plugin-from-master.patch -Patch3: https://github.com/hexchat/hexchat/commit/163608d7fd861c2c4911a38f45be484c88626bdc.patch -Patch4: https://github.com/hexchat/hexchat/commit/090fd29acf4af0d8e13fcf2861b14a356db72641.patch - -# https://github.com/hexchat/hexchat/pull/2609 -# https://bugzilla.redhat.com/show_bug.cgi?id=1981509 -Patch5: hexchat-Avoid-direct-use-of-libproxy.patch - BuildRequires: gcc BuildRequires: meson BuildRequires: hicolor-icon-theme @@ -25,7 +15,6 @@ BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(gtk+-2.0) BuildRequires: pkgconfig(dbus-glib-1) BuildRequires: pkgconfig(libcanberra) -BuildRequires: pkgconfig(libnotify) BuildRequires: pkgconfig(iso-codes) BuildRequires: pkgconfig(openssl) BuildRequires: pkgconfig(python3) @@ -84,6 +73,10 @@ This package contains the development files for %{name}. %{_libdir}/pkgconfig/hexchat-plugin.pc %changelog +* Mon Dec 13 2021 Debarshi Ray - 2.16.0-1 +- Update to 2.16.0 +Resolves: #1965883 + * Mon Aug 09 2021 Mohan Boddu - 2.14.3-17 - Rebuilt for IMA sigs, glibc 2.34, aarch64 flags Related: rhbz#1991688