summaryrefslogtreecommitdiff
path: root/hexchat
diff options
context:
space:
mode:
authorDanny Rawlins <contact@romster.me>2021-10-03 17:41:59 +1100
committerDanny Rawlins <contact@romster.me>2021-10-03 17:41:59 +1100
commited17783fe4d14e236a2c81545d85359c2a82144a (patch)
tree1fc0291a25ec5e7d6ccabe7b5dc95f2c8d87a217 /hexchat
parentb95a5d32e7d035d2a95dfa88eb1c1d09bb7e27b4 (diff)
downloadopt-ed17783fe4d14e236a2c81545d85359c2a82144a.tar.gz
opt-ed17783fe4d14e236a2c81545d85359c2a82144a.tar.xz
hexchat: 2.14.3 -> 2.16.0
Diffstat (limited to 'hexchat')
-rw-r--r--hexchat/.footprint1
-rw-r--r--hexchat/.signature12
-rw-r--r--hexchat/0001-python-cffi.patch4183
-rw-r--r--hexchat/0002-python-3-8.patch13
-rw-r--r--hexchat/0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch44
-rw-r--r--hexchat/0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch30
-rw-r--r--hexchat/Pkgfile21
7 files changed, 8 insertions, 4296 deletions
diff --git a/hexchat/.footprint b/hexchat/.footprint
index cbda981bf..ef5e5a50a 100644
--- a/hexchat/.footprint
+++ b/hexchat/.footprint
@@ -1,7 +1,6 @@
drwxr-xr-x root/root usr/
drwxr-xr-x root/root usr/bin/
-rwxr-xr-x root/root usr/bin/hexchat
--rwxr-xr-x root/root usr/bin/hexchat-text
drwxr-xr-x root/root usr/include/
-rw-r--r-- root/root usr/include/hexchat-plugin.h
drwxr-xr-x root/root usr/lib/
diff --git a/hexchat/.signature b/hexchat/.signature
index 589cda097..4b1194b11 100644
--- a/hexchat/.signature
+++ b/hexchat/.signature
@@ -1,9 +1,5 @@
untrusted comment: verify with /etc/ports/opt.pub
-RWSE3ohX2g5d/REpmuyhzjPUo9CjVOE1x1qWrvjywGxMNFl4CaDkh/xgr5OBO15t8h3oRyjvmooLLZqv3uqwW5P9G6OAzbgi7wE=
-SHA256 (Pkgfile) = f39ca37996246222dc114cb81214ba968393fdfc0caf075177b72da402377208
-SHA256 (.footprint) = aae2ffbaea59726e208254c022815f3e5d1ca9b6c0de656a8bb59ac73443f3a8
-SHA256 (hexchat-2.14.3.tar.xz) = 901a9d13db5a4da69b827f6093306bbd16863dc49016f7668bd3e4506512e882
-SHA256 (0001-python-cffi.patch) = 92af0d106627c9b9716036ce81f697de35f37b4ba2e7bd34244824520e485bba
-SHA256 (0002-python-3-8.patch) = 8d17ce657e744272815b5fb33d8ad959f79ece3294349637eaadcf86d90496fa
-SHA256 (0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch) = 691f344479a8b1186008516f0ebe7e3d482059cd297f58573634fa5a58f3d0ca
-SHA256 (0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch) = a8d6917deec758b3a822471316f0bd7819d3661ef6f657c52953711227a2cbe6
+RWSE3ohX2g5d/etPjrPUciFDHZUV1AJAx9R+pk3Qx1XDdCXhwvbVQvYXTAF4AH9En5/ZHArNM7XSxRV3J+nYOc59UgTBDG8cmwE=
+SHA256 (Pkgfile) = 3e52e5aaefa773e04242a71d75f620bdee8038d12ad11a3402a9036e5487b265
+SHA256 (.footprint) = 39169111ceef0a711b94b006c88d30acd71f21790820c3e42129dc87f463fb00
+SHA256 (hexchat-2.16.0.tar.xz) = 412e31078bb2c5f9664a5b9ae4d7989e27a47f141ee09dce2e1181f8c482dc36
diff --git a/hexchat/0001-python-cffi.patch b/hexchat/0001-python-cffi.patch
deleted file mode 100644
index a4f461617..000000000
--- a/hexchat/0001-python-cffi.patch
+++ /dev/null
@@ -1,4183 +0,0 @@
-diff --git a/.travis.yml b/.travis.yml
-index df0c7e1f..9e226f0c 100644
---- a/.travis.yml
-+++ b/.travis.yml
-@@ -2,7 +2,7 @@ sudo: required
- services: docker
- before_install:
- - docker pull ubuntu:16.04
-- - docker run --privileged --cidfile=/tmp/cid ubuntu:16.04 /bin/sh -c 'apt-get update && apt-get install -y meson/xenial-backports libcanberra-dev libdbus-glib-1-dev libglib2.0-dev libgtk2.0-dev libluajit-5.1-dev libnotify-dev libpci-dev libperl-dev libproxy-dev libssl-dev python3-dev mono-devel desktop-file-utils'
-+ - docker run --privileged --cidfile=/tmp/cid ubuntu:16.04 /bin/sh -c 'apt-get update && apt-get install -y meson/xenial-backports libcanberra-dev libdbus-glib-1-dev libglib2.0-dev libgtk2.0-dev libluajit-5.1-dev libnotify-dev libpci-dev libperl-dev libproxy-dev libssl-dev python3-dev python3-cffi mono-devel desktop-file-utils'
- - docker commit `cat /tmp/cid` hexchat/ubuntu-ci
- - rm -f /tmp/cid
- install:
-diff --git a/meson.build b/meson.build
-index 18baf26e..645e685e 100644
---- a/meson.build
-+++ b/meson.build
-@@ -49,6 +49,10 @@ config_h.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_34')
- config_h.set('HAVE_MEMRCHR', cc.has_function('memrchr'))
- config_h.set('HAVE_STRINGS_H', cc.has_header('strings.h'))
-
-+config_h.set_quoted('HEXCHATLIBDIR',
-+ join_paths(get_option('prefix'), get_option('libdir'), 'hexchat/plugins')
-+)
-+
- if libssl_dep.found()
- config_h.set('HAVE_X509_GET_SIGNATURE_NID',
- cc.has_function('X509_get_signature_nid', dependencies: libssl_dep)
-diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
-new file mode 100644
-index 00000000..567b3493
---- /dev/null
-+++ b/plugins/python/_hexchat.py
-@@ -0,0 +1,386 @@
-+import inspect
-+import sys
-+from contextlib import contextmanager
-+
-+from _hexchat_embedded import ffi, lib
-+
-+__all__ = [
-+ 'EAT_ALL', 'EAT_HEXCHAT', 'EAT_NONE', 'EAT_PLUGIN', 'EAT_XCHAT',
-+ 'PRI_HIGH', 'PRI_HIGHEST', 'PRI_LOW', 'PRI_LOWEST', 'PRI_NORM',
-+ '__doc__', '__version__', 'command', 'del_pluginpref', 'emit_print',
-+ 'find_context', 'get_context', 'get_info',
-+ 'get_list', 'get_lists', 'get_pluginpref', 'get_prefs', 'hook_command',
-+ 'hook_print', 'hook_print_attrs', 'hook_server', 'hook_server_attrs',
-+ 'hook_timer', 'hook_unload', 'list_pluginpref', 'nickcmp', 'prnt',
-+ 'set_pluginpref', 'strip', 'unhook',
-+]
-+
-+__doc__ = 'HexChat Scripting Interface'
-+__version__ = (2, 0)
-+__license__ = 'GPL-2.0+'
-+
-+EAT_NONE = 0
-+EAT_HEXCHAT = 1
-+EAT_XCHAT = EAT_HEXCHAT
-+EAT_PLUGIN = 2
-+EAT_ALL = EAT_HEXCHAT | EAT_PLUGIN
-+
-+PRI_LOWEST = -128
-+PRI_LOW = -64
-+PRI_NORM = 0
-+PRI_HIGH = 64
-+PRI_HIGHEST = 127
-+
-+
-+# We need each module to be able to reference their parent plugin
-+# which is a bit tricky since they all share the exact same module.
-+# Simply navigating up to what module called it seems to actually
-+# be a fairly reliable and simple method of doing so if ugly.
-+def __get_current_plugin():
-+ frame = inspect.stack()[1][0]
-+ while '__plugin' not in frame.f_globals:
-+ frame = frame.f_back
-+ assert frame is not None
-+
-+ return frame.f_globals['__plugin']
-+
-+
-+# Keeping API compat
-+if sys.version_info[0] == 2:
-+ def __decode(string):
-+ return string
-+
-+else:
-+ def __decode(string):
-+ return string.decode()
-+
-+
-+# ------------ API ------------
-+def prnt(string):
-+ lib.hexchat_print(lib.ph, string.encode())
-+
-+
-+def emit_print(event_name, *args, **kwargs):
-+ time = kwargs.pop('time', 0) # For py2 compat
-+ cargs = []
-+ for i in range(4):
-+ arg = args[i].encode() if len(args) > i else b''
-+ cstring = ffi.new('char[]', arg)
-+ cargs.append(cstring)
-+
-+ if time == 0:
-+ return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
-+
-+ attrs = lib.hexchat_event_attrs_create(lib.ph)
-+ attrs.server_time_utc = time
-+ ret = lib.hexchat_emit_print_attrs(lib.ph, attrs, event_name.encode(), *cargs)
-+ lib.hexchat_event_attrs_free(lib.ph, attrs)
-+ return ret
-+
-+
-+# TODO: this shadows itself. command should be changed to cmd
-+def command(command):
-+ lib.hexchat_command(lib.ph, command.encode())
-+
-+
-+def nickcmp(string1, string2):
-+ return lib.hexchat_nickcmp(lib.ph, string1.encode(), string2.encode())
-+
-+
-+def strip(text, length=-1, flags=3):
-+ stripped = lib.hexchat_strip(lib.ph, text.encode(), length, flags)
-+ ret = __decode(ffi.string(stripped))
-+ lib.hexchat_free(lib.ph, stripped)
-+ return ret
-+
-+
-+def get_info(name):
-+ ret = lib.hexchat_get_info(lib.ph, name.encode())
-+ if ret == ffi.NULL:
-+ return None
-+ if name in ('gtkwin_ptr', 'win_ptr'):
-+ # Surely there is a less dumb way?
-+ ptr = repr(ret).rsplit(' ', 1)[1][:-1]
-+ return ptr
-+
-+ return __decode(ffi.string(ret))
-+
-+
-+def get_prefs(name):
-+ string_out = ffi.new('char**')
-+ int_out = ffi.new('int*')
-+ _type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
-+ if _type == 0:
-+ return None
-+
-+ if _type == 1:
-+ return __decode(ffi.string(string_out[0]))
-+
-+ if _type in (2, 3): # XXX: 3 should be a bool, but keeps API
-+ return int_out[0]
-+
-+ raise AssertionError('Out of bounds pref storage')
-+
-+
-+def __cstrarray_to_list(arr):
-+ i = 0
-+ ret = []
-+ while arr[i] != ffi.NULL:
-+ ret.append(ffi.string(arr[i]))
-+ i += 1
-+
-+ return ret
-+
-+
-+__FIELD_CACHE = {}
-+
-+
-+def __get_fields(name):
-+ return __FIELD_CACHE.setdefault(name, __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
-+
-+
-+__FIELD_PROPERTY_CACHE = {}
-+
-+
-+def __cached_decoded_str(string):
-+ return __FIELD_PROPERTY_CACHE.setdefault(string, __decode(string))
-+
-+
-+def get_lists():
-+ return [__cached_decoded_str(field) for field in __get_fields(b'lists')]
-+
-+
-+class ListItem:
-+ def __init__(self, name):
-+ self._listname = name
-+
-+ def __repr__(self):
-+ return '<{} list item at {}>'.format(self._listname, id(self))
-+
-+
-+# done this way for speed
-+if sys.version_info[0] == 2:
-+ def get_getter(name):
-+ return ord(name[0])
-+
-+else:
-+ def get_getter(name):
-+ return name[0]
-+
-+
-+def get_list(name):
-+ # XXX: This function is extremely inefficient and could be interators and
-+ # lazily loaded properties, but for API compat we stay slow
-+ orig_name = name
-+ name = name.encode()
-+
-+ if name not in __get_fields(b'lists'):
-+ raise KeyError('list not available')
-+
-+ list_ = lib.hexchat_list_get(lib.ph, name)
-+ if list_ == ffi.NULL:
-+ return None
-+
-+ ret = []
-+ fields = __get_fields(name)
-+
-+ def string_getter(field):
-+ string = lib.hexchat_list_str(lib.ph, list_, field)
-+ if string != ffi.NULL:
-+ return __decode(ffi.string(string))
-+
-+ return ''
-+
-+ def ptr_getter(field):
-+ if field == b'context':
-+ ptr = lib.hexchat_list_str(lib.ph, list_, field)
-+ ctx = ffi.cast('hexchat_context*', ptr)
-+ return Context(ctx)
-+
-+ return None
-+
-+ getters = {
-+ ord('s'): string_getter,
-+ ord('i'): lambda field: lib.hexchat_list_int(lib.ph, list_, field),
-+ ord('t'): lambda field: lib.hexchat_list_time(lib.ph, list_, field),
-+ ord('p'): ptr_getter,
-+ }
-+
-+ while lib.hexchat_list_next(lib.ph, list_) == 1:
-+ item = ListItem(orig_name)
-+ for _field in fields:
-+ getter = getters.get(get_getter(_field))
-+ if getter is not None:
-+ field_name = _field[1:]
-+ setattr(item, __cached_decoded_str(field_name), getter(field_name))
-+
-+ ret.append(item)
-+
-+ lib.hexchat_list_free(lib.ph, list_)
-+ return ret
-+
-+
-+# TODO: 'command' here shadows command above, and should be renamed to cmd
-+def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
-+ help.encode() if help is not None else ffi.NULL, hook.handle)
-+
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_print(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_server(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_timer(timeout, callback, userdata=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata)
-+ handle = lib.hexchat_hook_timer(lib.ph, timeout, lib._on_timer_hook, hook.handle)
-+ hook.hexchat_hook = handle
-+ return id(hook)
-+
-+
-+def hook_unload(callback, userdata=None):
-+ plugin = __get_current_plugin()
-+ hook = plugin.add_hook(callback, userdata, is_unload=True)
-+ return id(hook)
-+
-+
-+def unhook(handle):
-+ plugin = __get_current_plugin()
-+ return plugin.remove_hook(handle)
-+
-+
-+def set_pluginpref(name, value):
-+ if isinstance(value, str):
-+ return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
-+
-+ if isinstance(value, int):
-+ return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
-+
-+ # XXX: This should probably raise but this keeps API
-+ return False
-+
-+
-+def get_pluginpref(name):
-+ name = name.encode()
-+ string_out = ffi.new('char[512]')
-+ if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) != 1:
-+ return None
-+
-+ string = ffi.string(string_out)
-+ # This API stores everything as a string so we have to figure out what
-+ # its actual type was supposed to be.
-+ if len(string) > 12: # Can't be a number
-+ return __decode(string)
-+
-+ number = lib.hexchat_pluginpref_get_int(lib.ph, name)
-+ if number == -1 and string != b'-1':
-+ return __decode(string)
-+
-+ return number
-+
-+
-+def del_pluginpref(name):
-+ return bool(lib.hexchat_pluginpref_delete(lib.ph, name.encode()))
-+
-+
-+def list_pluginpref():
-+ prefs_str = ffi.new('char[4096]')
-+ if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
-+ return __decode(prefs_str).split(',')
-+
-+ return []
-+
-+
-+class Context:
-+ def __init__(self, ctx):
-+ self._ctx = ctx
-+
-+ def __eq__(self, value):
-+ if not isinstance(value, Context):
-+ return False
-+
-+ return self._ctx == value._ctx
-+
-+ @contextmanager
-+ def __change_context(self):
-+ old_ctx = lib.hexchat_get_context(lib.ph)
-+ if not self.set():
-+ # XXX: Behavior change, previously used wrong context
-+ lib.hexchat_print(lib.ph, b'Context object refers to closed context, ignoring call')
-+ return
-+
-+ yield
-+ lib.hexchat_set_context(lib.ph, old_ctx)
-+
-+ def set(self):
-+ # XXX: API addition, C plugin silently ignored failure
-+ return bool(lib.hexchat_set_context(lib.ph, self._ctx))
-+
-+ def prnt(self, string):
-+ with self.__change_context():
-+ prnt(string)
-+
-+ def emit_print(self, event_name, *args, **kwargs):
-+ time = kwargs.pop('time', 0) # For py2 compat
-+ with self.__change_context():
-+ return emit_print(event_name, *args, time=time)
-+
-+ def command(self, string):
-+ with self.__change_context():
-+ command(string)
-+
-+ def get_info(self, name):
-+ with self.__change_context():
-+ return get_info(name)
-+
-+ def get_list(self, name):
-+ with self.__change_context():
-+ return get_list(name)
-+
-+
-+def get_context():
-+ ctx = lib.hexchat_get_context(lib.ph)
-+ return Context(ctx)
-+
-+
-+def find_context(server=None, channel=None):
-+ server = server.encode() if server is not None else ffi.NULL
-+ channel = channel.encode() if channel is not None else ffi.NULL
-+ ctx = lib.hexchat_find_context(lib.ph, server, channel)
-+ if ctx == ffi.NULL:
-+ return None
-+
-+ return Context(ctx)
-diff --git a/plugins/python/generate_plugin.py b/plugins/python/generate_plugin.py
-new file mode 100755
-index 00000000..5c52b37b
---- /dev/null
-+++ b/plugins/python/generate_plugin.py
-@@ -0,0 +1,89 @@
-+#!/usr/bin/env python3
-+
-+import sys
-+import cffi
-+
-+builder = cffi.FFI()
-+
-+# hexchat-plugin.h
-+with open(sys.argv[1]) as f:
-+ output = []
-+ eat_until_endif = 0
-+ # This is very specific to hexchat-plugin.h, it is not a cpp
-+ for line in f:
-+ if line.startswith('#define'):
-+ continue
-+ elif line.endswith('HEXCHAT_PLUGIN_H\n'):
-+ continue
-+ elif 'time.h' in line:
-+ output.append('typedef int... time_t;')
-+ elif line.startswith('#if'):
-+ eat_until_endif += 1
-+ elif line.startswith('#endif'):
-+ eat_until_endif -= 1
-+ elif eat_until_endif and '_hexchat_context' not in line:
-+ continue
-+ else:
-+ output.append(line)
-+ builder.cdef(''.join(output))
-+
-+builder.embedding_api('''
-+extern "Python" int _on_py_command(char **, char **, void *);
-+extern "Python" int _on_load_command(char **, char **, void *);
-+extern "Python" int _on_unload_command(char **, char **, void *);
-+extern "Python" int _on_reload_command(char **, char **, void *);
-+extern "Python" int _on_say_command(char **, char **, void *);
-+
-+extern "Python" int _on_command_hook(char **, char **, void *);
-+extern "Python" int _on_print_hook(char **, void *);
-+extern "Python" int _on_print_attrs_hook(char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_server_hook(char **, char **, void *);
-+extern "Python" int _on_server_attrs_hook(char **, char **, hexchat_event_attrs *, void *);
-+extern "Python" int _on_timer_hook(void *);
-+
-+extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
-+extern "Python" int _on_plugin_deinit(void);
-+
-+static hexchat_plugin *ph;
-+''')
-+
-+builder.set_source('_hexchat_embedded', '''
-+/* Python's header defines these.. */
-+#undef HAVE_MEMRCHR
-+#undef HAVE_STRINGS_H
-+
-+#include "config.h"
-+#include "hexchat-plugin.h"
-+
-+static hexchat_plugin *ph;
-+CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
-+CFFI_DLLEXPORT int _on_plugin_deinit(void);
-+
-+int hexchat_plugin_init(hexchat_plugin *plugin_handle,
-+ char **name_out, char **description_out,
-+ char **version_out, char *arg)
-+{
-+ if (ph != NULL)
-+ {
-+ puts ("Python plugin already loaded\\n");
-+ return 0; /* Prevent loading twice */
-+ }
-+
-+ ph = plugin_handle;
-+ return _on_plugin_init(name_out, description_out, version_out, arg, HEXCHATLIBDIR);
-+}
-+
-+int hexchat_plugin_deinit(void)
-+{
-+ int ret = _on_plugin_deinit();
-+ ph = NULL;
-+ return ret;
-+}
-+''')
-+
-+# python.py
-+with open(sys.argv[2]) as f:
-+ builder.embedding_init_code(f.read())
-+
-+# python.c
-+builder.emit_c_code(sys.argv[3])
-diff --git a/plugins/python/hexchat.py b/plugins/python/hexchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/hexchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/plugins/python/meson.build b/plugins/python/meson.build
-index e24f0c6f..5fd7ec2f 100644
---- a/plugins/python/meson.build
-+++ b/plugins/python/meson.build
-@@ -1,12 +1,30 @@
- python_opt = get_option('with-python')
- if python_opt.startswith('python3')
-- python_dep = dependency(python_opt, version: '>= 3.3')
-+ # Python 3.8 introduced a new -embed variant
-+ if not python_opt.endswith('-embed')
-+ python_dep = dependency(python_opt + '-embed', version: '>= 3.3', required: false)
-+ if not python_dep.found()
-+ python_dep = dependency(python_opt, version: '>= 3.3')
-+ endif
-+ else
-+ python_dep = dependency(python_opt, version: '>= 3.3')
-+ endif
- else
- python_dep = dependency(python_opt, version: '>= 2.7')
- endif
-
--shared_module('python', 'python.c',
-- dependencies: [libgio_dep, hexchat_plugin_dep, python_dep],
-+python3_source = custom_target('python-bindings',
-+ input: ['../../src/common/hexchat-plugin.h', 'python.py'],
-+ output: 'python.c',
-+ command: [find_program('generate_plugin.py'), '@INPUT@', '@OUTPUT@']
-+)
-+
-+install_data(['_hexchat.py', 'hexchat.py', 'xchat.py'],
-+ install_dir: join_paths(get_option('libdir'), 'hexchat/python')
-+)
-+
-+shared_module('python', python3_source,
-+ dependencies: [hexchat_plugin_dep, python_dep],
- install: true,
- install_dir: plugindir,
- name_prefix: '',
-diff --git a/plugins/python/python.c b/plugins/python/python.c
-deleted file mode 100644
-index 475756ba..00000000
---- a/plugins/python/python.c
-+++ /dev/null
-@@ -1,2837 +0,0 @@
--/*
--* Copyright (c) 2002-2003 Gustavo Niemeyer <niemeyer@conectiva.com>
--*
--* XChat Python Plugin Interface
--*
--* Xchat Python Plugin Interface is free software; you can redistribute
--* it and/or modify it under the terms of the GNU General Public License
--* as published by the Free Software Foundation; either version 2 of the
--* License, or (at your option) any later version.
--*
--* pybot is distributed in the hope that it will be useful,
--* but WITHOUT ANY WARRANTY; without even the implied warranty of
--* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--* GNU General Public License for more details.
--*
--* You should have received a copy of the GNU General Public License
--* along with this file; if not, write to the Free Software
--* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
--*/
--
--/* Thread support
-- * ==============
-- *
-- * The python interpreter has a global interpreter lock. Any thread
-- * executing must acquire it before working with data accessible from
-- * python code. Here we must also care about xchat not being
-- * thread-safe. We do this by using an xchat lock, which protects
-- * xchat instructions from being executed out of time (when this
-- * plugin is not "active").
-- *
-- * When xchat calls python code:
-- * - Change the current_plugin for the executing plugin;
-- * - Release xchat lock
-- * - Acquire the global interpreter lock
-- * - Make the python call
-- * - Release the global interpreter lock
-- * - Acquire xchat lock
-- *
-- * When python code calls xchat:
-- * - Release the global interpreter lock
-- * - Acquire xchat lock
-- * - Restore context, if necessary
-- * - Make the xchat call
-- * - Release xchat lock
-- * - Acquire the global interpreter lock
-- *
-- * Inside a timer, so that individual threads have a chance to run:
-- * - Release xchat lock
-- * - Go ahead threads. Have a nice time!
-- * - Acquire xchat lock
-- *
-- */
--
--#include "config.h"
--
--#include <glib.h>
--#include <glib/gstdio.h>
--#include <string.h>
--#include <stdlib.h>
--#include <sys/types.h>
--
--#ifdef WIN32
--#include <direct.h>
--#else
--#include <unistd.h>
--#include <dirent.h>
--#endif
--
--#include "hexchat-plugin.h"
--#undef _POSIX_C_SOURCE /* Avoid warnings from /usr/include/features.h */
--#undef _XOPEN_SOURCE
--#undef HAVE_MEMRCHR /* Avoid redefinition in Python.h */
--#undef HAVE_STRINGS_H
--#include <Python.h>
--#include <structmember.h>
--#include <pythread.h>
--
--/* Macros to convert version macros into string literals.
-- * The indirect macro is a well-known preprocessor trick to force X to be evaluated before the # operator acts to make it a string literal.
-- * If STRINGIZE were to be directly defined as #X instead, VERSION would be "VERSION_MAJOR" instead of "1".
-- */
--#define STRINGIZE2(X) #X
--#define STRINGIZE(X) STRINGIZE2(X)
--
--/* Version number macros */
--#define VERSION_MAJOR 1
--#define VERSION_MINOR 0
--
--/* Version string macro e.g 1.0/3.3 */
--#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/" \
-- STRINGIZE(PY_MAJOR_VERSION) "." STRINGIZE (PY_MINOR_VERSION)
--
--/* #define's for Python 2 */
--#if PY_MAJOR_VERSION == 2
--#undef PyLong_Check
--#define PyLong_Check PyInt_Check
--#define PyLong_AsLong PyInt_AsLong
--#define PyLong_FromLong PyInt_FromLong
--
--#undef PyUnicode_Check
--#undef PyUnicode_FromString
--#undef PyUnicode_FromFormat
--#define PyUnicode_Check PyString_Check
--#define PyUnicode_AsFormat PyString_AsFormat
--#define PyUnicode_FromFormat PyString_FromFormat
--#define PyUnicode_FromString PyString_FromString
--#define PyUnicode_AsUTF8 PyString_AsString
--
--#ifdef WIN32
--#undef WITH_THREAD
--#endif
--#endif
--
--/* #define for Python 3 */
--#if PY_MAJOR_VERSION == 3
--#define IS_PY3K
--#endif
--
--#define NONE 0
--#define ALLOW_THREADS 1
--#define RESTORE_CONTEXT 2
--
--#ifdef WITH_THREAD
--#define ACQUIRE_XCHAT_LOCK() PyThread_acquire_lock(xchat_lock, 1)
--#define RELEASE_XCHAT_LOCK() PyThread_release_lock(xchat_lock)
--#define BEGIN_XCHAT_CALLS(x) \
-- do { \
-- PyObject *calls_plugin = NULL; \
-- PyThreadState *calls_thread; \
-- if ((x) & RESTORE_CONTEXT) \
-- calls_plugin = Plugin_GetCurrent(); \
-- calls_thread = PyEval_SaveThread(); \
-- ACQUIRE_XCHAT_LOCK(); \
-- if (!((x) & ALLOW_THREADS)) { \
-- PyEval_RestoreThread(calls_thread); \
-- calls_thread = NULL; \
-- } \
-- if (calls_plugin) \
-- hexchat_set_context(ph, \
-- Plugin_GetContext(calls_plugin)); \
-- while (0)
--#define END_XCHAT_CALLS() \
-- RELEASE_XCHAT_LOCK(); \
-- if (calls_thread) \
-- PyEval_RestoreThread(calls_thread); \
-- } while(0)
--#else
--#define ACQUIRE_XCHAT_LOCK()
--#define RELEASE_XCHAT_LOCK()
--#define BEGIN_XCHAT_CALLS(x)
--#define END_XCHAT_CALLS()
--#endif
--
--#ifdef WITH_THREAD
--
--#define BEGIN_PLUGIN(plg) \
-- do { \
-- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
-- RELEASE_XCHAT_LOCK(); \
-- Plugin_AcquireThread(plg); \
-- Plugin_SetContext(plg, begin_plugin_ctx); \
-- } while (0)
--#define END_PLUGIN(plg) \
-- do { \
-- Plugin_ReleaseThread(plg); \
-- ACQUIRE_XCHAT_LOCK(); \
-- } while (0)
--
--#else /* !WITH_THREAD (win32) */
--
--static PyThreadState *pTempThread;
--
--#define BEGIN_PLUGIN(plg) \
-- do { \
-- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
-- RELEASE_XCHAT_LOCK(); \
-- PyEval_AcquireLock(); \
-- pTempThread = PyThreadState_Swap(((PluginObject *)(plg))->tstate); \
-- Plugin_SetContext(plg, begin_plugin_ctx); \
-- } while (0)
--#define END_PLUGIN(plg) \
-- do { \
-- ((PluginObject *)(plg))->tstate = PyThreadState_Swap(pTempThread); \
-- PyEval_ReleaseLock(); \
-- ACQUIRE_XCHAT_LOCK(); \
-- } while (0)
--
--#endif /* !WITH_THREAD */
--
--#define Plugin_Swap(x) \
-- PyThreadState_Swap(((PluginObject *)(x))->tstate)
--#define Plugin_AcquireThread(x) \
-- PyEval_AcquireThread(((PluginObject *)(x))->tstate)
--#define Plugin_ReleaseThread(x) \
-- Util_ReleaseThread(((PluginObject *)(x))->tstate)
--#define Plugin_GetFilename(x) \
-- (((PluginObject *)(x))->filename)
--#define Plugin_GetName(x) \
-- (((PluginObject *)(x))->name)
--#define Plugin_GetVersion(x) \
-- (((PluginObject *)(x))->version)
--#define Plugin_GetDesc(x) \
-- (((PluginObject *)(x))->description)
--#define Plugin_GetHooks(x) \
-- (((PluginObject *)(x))->hooks)
--#define Plugin_GetContext(x) \
-- (((PluginObject *)(x))->context)
--#define Plugin_SetFilename(x, y) \
-- ((PluginObject *)(x))->filename = (y);
--#define Plugin_SetName(x, y) \
-- ((PluginObject *)(x))->name = (y);
--#define Plugin_SetVersion(x, y) \
-- ((PluginObject *)(x))->version = (y);
--#define Plugin_SetDescription(x, y) \
-- ((PluginObject *)(x))->description = (y);
--#define Plugin_SetHooks(x, y) \
-- ((PluginObject *)(x))->hooks = (y);
--#define Plugin_SetContext(x, y) \
-- ((PluginObject *)(x))->context = (y);
--#define Plugin_SetGui(x, y) \
-- ((PluginObject *)(x))->gui = (y);
--
--#define HOOK_XCHAT 1
--#define HOOK_XCHAT_ATTR 2
--#define HOOK_UNLOAD 3
--
--/* ===================================================================== */
--/* Object definitions */
--
--typedef struct {
-- PyObject_HEAD
-- int softspace; /* We need it for print support. */
--} XChatOutObject;
--
--typedef struct {
-- PyObject_HEAD
-- hexchat_context *context;
--} ContextObject;
--
--typedef struct {
-- PyObject_HEAD
-- PyObject *time;
--} AttributeObject;
--
--typedef struct {
-- PyObject_HEAD
-- const char *listname;
-- PyObject *dict;
--} ListItemObject;
--
--typedef struct {
-- PyObject_HEAD
-- char *name;
-- char *version;
-- char *filename;
-- char *description;
-- GSList *hooks;
-- PyThreadState *tstate;
-- hexchat_context *context;
-- void *gui;
--} PluginObject;
--
--typedef struct {
-- int type;
-- PyObject *plugin;
-- PyObject *callback;
-- PyObject *userdata;
-- char *name;
-- void *data; /* A handle, when type == HOOK_XCHAT */
--} Hook;
--
--
--/* ===================================================================== */
--/* Function declarations */
--
--static PyObject *Util_BuildList(char *word[]);
--static PyObject *Util_BuildEOLList(char *word[]);
--static void Util_Autoload(void);
--static char *Util_Expand(char *filename);
--
--static int Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Command(char *word[], char *word_eol[], void *userdata);
--static int Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata);
--static int Callback_Print(char *word[], void *userdata);
--static int Callback_Timer(void *userdata);
--static int Callback_ThreadTimer(void *userdata);
--
--static PyObject *XChatOut_New(void);
--static PyObject *XChatOut_write(PyObject *self, PyObject *args);
--static void XChatOut_dealloc(PyObject *self);
--
--static PyObject *Attribute_New(hexchat_event_attrs *attrs);
--
--static void Context_dealloc(PyObject *self);
--static PyObject *Context_set(ContextObject *self, PyObject *args);
--static PyObject *Context_command(ContextObject *self, PyObject *args);
--static PyObject *Context_prnt(ContextObject *self, PyObject *args);
--static PyObject *Context_get_info(ContextObject *self, PyObject *args);
--static PyObject *Context_get_list(ContextObject *self, PyObject *args);
--static PyObject *Context_compare(ContextObject *a, ContextObject *b, int op);
--static PyObject *Context_FromContext(hexchat_context *context);
--static PyObject *Context_FromServerAndChannel(char *server, char *channel);
--
--static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
--static PyObject *Plugin_GetCurrent(void);
--static PluginObject *Plugin_ByString(char *str);
--static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-- PyObject *userdata, char *name, void *data);
--static Hook *Plugin_FindHook(PyObject *plugin, char *name);
--static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
--static void Plugin_RemoveAllHooks(PyObject *plugin);
--
--static PyObject *Module_hexchat_command(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_prnt(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_context(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_find_context(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_hook_command(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_server(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_print(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_hook_timer(PyObject *self, PyObject *args,
-- PyObject *kwargs);
--static PyObject *Module_hexchat_unhook(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_list(PyObject *self, PyObject *args);
--static PyObject *Module_xchat_get_lists(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_nickcmp(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_strip(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_set(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_get(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args);
--static PyObject *Module_hexchat_pluginpref_list(PyObject *self, PyObject *args);
--
--static void IInterp_Exec(char *command);
--static int IInterp_Cmd(char *word[], char *word_eol[], void *userdata);
--
--static void Command_PyList(void);
--static void Command_PyLoad(char *filename);
--static void Command_PyUnload(char *name);
--static void Command_PyReload(char *name);
--static void Command_PyAbout(void);
--static int Command_Py(char *word[], char *word_eol[], void *userdata);
--
--/* ===================================================================== */
--/* Static declarations and definitions */
--
--static PyTypeObject Plugin_Type;
--static PyTypeObject XChatOut_Type;
--static PyTypeObject Context_Type;
--static PyTypeObject ListItem_Type;
--static PyTypeObject Attribute_Type;
--
--static PyThreadState *main_tstate = NULL;
--static void *thread_timer = NULL;
--
--static hexchat_plugin *ph;
--static GSList *plugin_list = NULL;
--
--static PyObject *interp_plugin = NULL;
--static PyObject *xchatout = NULL;
--
--#ifdef WITH_THREAD
--static PyThread_type_lock xchat_lock = NULL;
--#endif
--
--static const char usage[] = "\
--Usage: /PY LOAD <filename>\n\
-- UNLOAD <filename|name>\n\
-- RELOAD <filename|name>\n\
-- LIST\n\
-- EXEC <command>\n\
-- CONSOLE\n\
-- ABOUT\n\
--\n";
--
--static const char about[] = "HexChat Python interface version " VERSION "\n";
--
--/* ===================================================================== */
--/* Utility functions */
--
--static PyObject *
--Util_BuildList(char *word[])
--{
-- PyObject *list;
-- int listsize = 31;
-- int i;
-- /* Find the last valid array member; there may be intermediate NULLs that
-- * would otherwise cause us to drop some members. */
-- while (listsize > 0 &&
-- (word[listsize] == NULL || word[listsize][0] == 0))
-- listsize--;
-- list = PyList_New(listsize);
-- if (list == NULL) {
-- PyErr_Print();
-- return NULL;
-- }
-- for (i = 1; i <= listsize; i++) {
-- PyObject *o;
-- if (word[i] == NULL) {
-- Py_INCREF(Py_None);
-- o = Py_None;
-- } else {
-- /* This handles word[i][0] == 0 automatically. */
-- o = PyUnicode_FromString(word[i]);
-- }
-- PyList_SetItem(list, i - 1, o);
-- }
-- return list;
--}
--
--static PyObject *
--Util_BuildEOLList(char *word[])
--{
-- PyObject *list;
-- int listsize = 31;
-- int i;
-- char *accum = NULL;
-- char *last = NULL;
--
-- /* Find the last valid array member; there may be intermediate NULLs that
-- * would otherwise cause us to drop some members. */
-- while (listsize > 0 &&
-- (word[listsize] == NULL || word[listsize][0] == 0))
-- listsize--;
-- list = PyList_New(listsize);
-- if (list == NULL) {
-- PyErr_Print();
-- return NULL;
-- }
-- for (i = listsize; i > 0; i--) {
-- char *part = word[i];
-- PyObject *uni_part;
-- if (accum == NULL) {
-- accum = g_strdup (part);
-- } else if (part != NULL && part[0] != 0) {
-- last = accum;
-- accum = g_strjoin(" ", part, last, NULL);
-- g_free (last);
-- last = NULL;
--
-- if (accum == NULL) {
-- Py_DECREF(list);
-- hexchat_print(ph, "Not enough memory to alloc accum"
-- "for python plugin callback");
-- return NULL;
-- }
-- }
-- uni_part = PyUnicode_FromString(accum);
-- PyList_SetItem(list, i - 1, uni_part);
-- }
--
-- g_free (last);
-- g_free (accum);
--
-- return list;
--}
--
--static void
--Util_Autoload_from (const char *dir_name)
--{
-- gchar *oldcwd;
-- const char *entry_name;
-- GDir *dir;
--
-- oldcwd = g_get_current_dir ();
-- if (oldcwd == NULL)
-- return;
-- if (g_chdir(dir_name) != 0)
-- {
-- g_free (oldcwd);
-- return;
-- }
-- dir = g_dir_open (".", 0, NULL);
-- if (dir == NULL)
-- {
-- g_free (oldcwd);
-- return;
-- }
-- while ((entry_name = g_dir_read_name (dir)))
-- {
-- if (g_str_has_suffix (entry_name, ".py"))
-- Command_PyLoad((char*)entry_name);
-- }
-- g_dir_close (dir);
-- g_chdir (oldcwd);
--}
--
--static void
--Util_Autoload()
--{
-- const char *xdir;
-- char *sub_dir;
-- /* we need local filesystem encoding for g_chdir, g_dir_open etc */
--
-- xdir = hexchat_get_info(ph, "configdir");
--
-- /* auto-load from subdirectory addons */
-- sub_dir = g_build_filename (xdir, "addons", NULL);
-- Util_Autoload_from(sub_dir);
-- g_free (sub_dir);
--}
--
--static char *
--Util_Expand(char *filename)
--{
-- char *expanded;
--
-- /* Check if this is an absolute path. */
-- if (g_path_is_absolute(filename)) {
-- if (g_file_test(filename, G_FILE_TEST_EXISTS))
-- return g_strdup(filename);
-- else
-- return NULL;
-- }
--
-- /* Check if it starts with ~/ and expand the home if positive. */
-- if (*filename == '~' && *(filename+1) == '/') {
-- expanded = g_build_filename(g_get_home_dir(),
-- filename+2, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- else {
-- g_free(expanded);
-- return NULL;
-- }
-- }
--
-- /* Check if it's in the current directory. */
-- expanded = g_build_filename(g_get_current_dir(),
-- filename, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- g_free(expanded);
--
-- /* Check if ~/.config/hexchat/addons/<filename> exists. */
-- expanded = g_build_filename(hexchat_get_info(ph, "configdir"),
-- "addons", filename, NULL);
-- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
-- return expanded;
-- g_free(expanded);
--
-- return NULL;
--}
--
--/* Similar to PyEval_ReleaseThread, but accepts NULL thread states. */
--static void
--Util_ReleaseThread(PyThreadState *tstate)
--{
-- PyThreadState *old_tstate;
-- if (tstate == NULL)
-- Py_FatalError("PyEval_ReleaseThread: NULL thread state");
-- old_tstate = PyThreadState_Swap(NULL);
-- if (old_tstate != tstate && old_tstate != NULL)
-- Py_FatalError("PyEval_ReleaseThread: wrong thread state");
-- PyEval_ReleaseLock();
--}
--
--/* ===================================================================== */
--/* Hookable functions. These are the entry points to python code, besides
-- * the load function, and the hooks for interactive interpreter. */
--
--static int
--Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list, *word_eol_list;
-- PyObject *attributes;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildList(word_eol);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- attributes = Attribute_New(attrs);
--
-- if (hook->type == HOOK_XCHAT_ATTR)
-- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
-- word_eol_list, hook->userdata, attributes);
-- else
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
-- Py_DECREF(attributes);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Command(char *word[], char *word_eol[], void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list, *word_eol_list;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildList(word_eol);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list;
-- PyObject *word_eol_list;
-- PyObject *attributes;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildEOLList(word);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- attributes = Attribute_New(attrs);
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
-- word_eol_list, hook->userdata, attributes);
--
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
-- Py_DECREF(attributes);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Print(char *word[], void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- PyObject *word_list;
-- PyObject *word_eol_list;
-- int ret = HEXCHAT_EAT_NONE;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
-- BEGIN_PLUGIN(plugin);
--
-- word_list = Util_BuildList(word);
-- if (word_list == NULL) {
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
-- word_eol_list = Util_BuildEOLList(word);
-- if (word_eol_list == NULL) {
-- Py_DECREF(word_list);
-- END_PLUGIN(plugin);
-- return HEXCHAT_EAT_NONE;
-- }
--
-- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
-- word_eol_list, hook->userdata);
--
-- Py_DECREF(word_list);
-- Py_DECREF(word_eol_list);
--
-- if (retobj == Py_None) {
-- ret = HEXCHAT_EAT_NONE;
-- Py_DECREF(retobj);
-- } else if (retobj) {
-- ret = PyLong_AsLong(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--static int
--Callback_Timer(void *userdata)
--{
-- Hook *hook = (Hook *) userdata;
-- PyObject *retobj;
-- int ret = 0;
-- PyObject *plugin;
--
-- plugin = hook->plugin;
--
-- BEGIN_PLUGIN(hook->plugin);
--
-- retobj = PyObject_CallFunction(hook->callback, "(O)", hook->userdata);
--
-- if (retobj) {
-- ret = PyObject_IsTrue(retobj);
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- }
--
-- /* Returning 0 for this callback unhooks itself. */
-- if (ret == 0)
-- Plugin_RemoveHook(plugin, hook);
--
-- END_PLUGIN(plugin);
--
-- return ret;
--}
--
--#ifdef WITH_THREAD
--static int
--Callback_ThreadTimer(void *userdata)
--{
-- RELEASE_XCHAT_LOCK();
--#ifndef WIN32
-- usleep(1);
--#endif
-- ACQUIRE_XCHAT_LOCK();
-- return 1;
--}
--#endif
--
--/* ===================================================================== */
--/* XChatOut object */
--
--/* We keep this information global, so we can reset it when the
-- * deinit function is called. */
--/* XXX This should be somehow bound to the printing context. */
--static GString *xchatout_buffer = NULL;
--
--static PyObject *
--XChatOut_New()
--{
-- XChatOutObject *xcoobj;
-- xcoobj = PyObject_New(XChatOutObject, &XChatOut_Type);
-- if (xcoobj != NULL)
-- xcoobj->softspace = 0;
-- return (PyObject *) xcoobj;
--}
--
--static void
--XChatOut_dealloc(PyObject *self)
--{
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--/* This is a little bit complex because we have to buffer data
-- * until a \n is received, since xchat breaks the line automatically.
-- * We also crop the last \n for this reason. */
--static PyObject *
--XChatOut_write(PyObject *self, PyObject *args)
--{
-- gboolean add_space;
-- char *data, *pos;
--
-- if (!PyArg_ParseTuple(args, "s:write", &data))
-- return NULL;
-- if (!data || !*data) {
-- Py_RETURN_NONE;
-- }
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- if (((XChatOutObject *)self)->softspace) {
-- add_space = TRUE;
-- ((XChatOutObject *)self)->softspace = 0;
-- } else {
-- add_space = FALSE;
-- }
--
-- g_string_append (xchatout_buffer, data);
--
-- /* If not end of line add space to continue buffer later */
-- if (add_space && xchatout_buffer->str[xchatout_buffer->len - 1] != '\n')
-- {
-- g_string_append_c (xchatout_buffer, ' ');
-- }
--
-- /* If there is an end of line print up to that */
-- if ((pos = strrchr (xchatout_buffer->str, '\n')))
-- {
-- *pos = '\0';
-- hexchat_print (ph, xchatout_buffer->str);
--
-- /* Then remove it from buffer */
-- g_string_erase (xchatout_buffer, 0, pos - xchatout_buffer->str + 1);
-- }
--
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--#define OFF(x) offsetof(XChatOutObject, x)
--
--static PyMemberDef XChatOut_members[] = {
-- {"softspace", T_INT, OFF(softspace), 0},
-- {0}
--};
--
--static PyMethodDef XChatOut_methods[] = {
-- {"write", XChatOut_write, METH_VARARGS},
-- {NULL, NULL}
--};
--
--static PyTypeObject XChatOut_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.XChatOut", /*tp_name*/
-- sizeof(XChatOutObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- XChatOut_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- XChatOut_methods, /*tp_methods*/
-- XChatOut_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* Attribute object */
--
--#undef OFF
--#define OFF(x) offsetof(AttributeObject, x)
--
--static PyMemberDef Attribute_members[] = {
-- {"time", T_OBJECT, OFF(time), 0},
-- {0}
--};
--
--static void
--Attribute_dealloc(PyObject *self)
--{
-- Py_DECREF(((AttributeObject*)self)->time);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Attribute_repr(PyObject *self)
--{
-- return PyUnicode_FromFormat("<Attribute object at %p>", self);
--}
--
--static PyTypeObject Attribute_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Attribute", /*tp_name*/
-- sizeof(AttributeObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- Attribute_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- Attribute_repr, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- Attribute_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--Attribute_New(hexchat_event_attrs *attrs)
--{
-- AttributeObject *attr;
-- attr = PyObject_New(AttributeObject, &Attribute_Type);
-- if (attr != NULL) {
-- attr->time = PyLong_FromLong((long)attrs->server_time_utc);
-- }
-- return (PyObject *) attr;
--}
--
--
--/* ===================================================================== */
--/* Context object */
--
--static void
--Context_dealloc(PyObject *self)
--{
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--Context_set(ContextObject *self, PyObject *args)
--{
-- PyObject *plugin = Plugin_GetCurrent();
-- Plugin_SetContext(plugin, self->context);
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_command(ContextObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:command", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- hexchat_command(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_prnt(ContextObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:prnt", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- hexchat_print(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Context_emit_print(ContextObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *argv[6];
-- char *name;
-- int res;
-- long time = 0;
-- hexchat_event_attrs *attrs;
-- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
-- "arg4", "arg5", "arg6",
-- "time", NULL};
-- memset(&argv, 0, sizeof(char*)*6);
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
-- &argv[0], &argv[1], &argv[2],
-- &argv[3], &argv[4], &argv[5],
-- &time))
-- return NULL;
-- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
-- hexchat_set_context(ph, self->context);
-- attrs = hexchat_event_attrs_create(ph);
-- attrs->server_time_utc = (time_t)time;
--
-- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
-- argv[3], argv[4], argv[5], NULL);
--
-- hexchat_event_attrs_free(ph, attrs);
-- END_XCHAT_CALLS();
-- return PyLong_FromLong(res);
--}
--
--static PyObject *
--Context_get_info(ContextObject *self, PyObject *args)
--{
-- const char *info;
-- char *name;
-- if (!PyArg_ParseTuple(args, "s:get_info", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_set_context(ph, self->context);
-- info = hexchat_get_info(ph, name);
-- END_XCHAT_CALLS();
-- if (info == NULL) {
-- Py_RETURN_NONE;
-- }
-- return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Context_get_list(ContextObject *self, PyObject *args)
--{
-- PyObject *plugin = Plugin_GetCurrent();
-- hexchat_context *saved_context = Plugin_GetContext(plugin);
-- PyObject *ret;
-- Plugin_SetContext(plugin, self->context);
-- ret = Module_xchat_get_list((PyObject*)self, args);
-- Plugin_SetContext(plugin, saved_context);
-- return ret;
--}
--
--/* needed to make context1 == context2 work */
--static PyObject *
--Context_compare(ContextObject *a, ContextObject *b, int op)
--{
-- PyObject *ret;
-- /* check for == */
-- if (op == Py_EQ)
-- ret = (a->context == b->context ? Py_True : Py_False);
-- /* check for != */
-- else if (op == Py_NE)
-- ret = (a->context != b->context ? Py_True : Py_False);
-- /* only makes sense as == and != */
-- else
-- {
-- PyErr_SetString(PyExc_TypeError, "contexts are either equal or not equal");
-- ret = Py_None;
-- }
--
-- Py_INCREF(ret);
-- return ret;
--}
--
--static PyMethodDef Context_methods[] = {
-- {"set", (PyCFunction) Context_set, METH_NOARGS},
-- {"command", (PyCFunction) Context_command, METH_VARARGS},
-- {"prnt", (PyCFunction) Context_prnt, METH_VARARGS},
-- {"emit_print", (PyCFunction) Context_emit_print, METH_VARARGS|METH_KEYWORDS},
-- {"get_info", (PyCFunction) Context_get_info, METH_VARARGS},
-- {"get_list", (PyCFunction) Context_get_list, METH_VARARGS},
-- {NULL, NULL}
--};
--
--static PyTypeObject Context_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Context", /*tp_name*/
-- sizeof(ContextObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- Context_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- (richcmpfunc)Context_compare, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- Context_methods, /*tp_methods*/
-- 0, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--Context_FromContext(hexchat_context *context)
--{
-- ContextObject *ctxobj = PyObject_New(ContextObject, &Context_Type);
-- if (ctxobj != NULL)
-- ctxobj->context = context;
-- return (PyObject *) ctxobj;
--}
--
--static PyObject *
--Context_FromServerAndChannel(char *server, char *channel)
--{
-- ContextObject *ctxobj;
-- hexchat_context *context;
-- BEGIN_XCHAT_CALLS(NONE);
-- context = hexchat_find_context(ph, server, channel);
-- END_XCHAT_CALLS();
-- if (context == NULL)
-- return NULL;
-- ctxobj = PyObject_New(ContextObject, &Context_Type);
-- if (ctxobj == NULL)
-- return NULL;
-- ctxobj->context = context;
-- return (PyObject *) ctxobj;
--}
--
--
--/* ===================================================================== */
--/* ListItem object */
--
--#undef OFF
--#define OFF(x) offsetof(ListItemObject, x)
--
--static PyMemberDef ListItem_members[] = {
-- {"__dict__", T_OBJECT, OFF(dict), 0},
-- {0}
--};
--
--static void
--ListItem_dealloc(PyObject *self)
--{
-- Py_DECREF(((ListItemObject*)self)->dict);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyObject *
--ListItem_repr(PyObject *self)
--{
-- return PyUnicode_FromFormat("<%s list item at %p>",
-- ((ListItemObject*)self)->listname, self);
--}
--
--static PyTypeObject ListItem_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.ListItem", /*tp_name*/
-- sizeof(ListItemObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- ListItem_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- ListItem_repr, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- ListItem_members, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- OFF(dict), /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--static PyObject *
--ListItem_New(const char *listname)
--{
-- ListItemObject *item;
-- item = PyObject_New(ListItemObject, &ListItem_Type);
-- if (item != NULL) {
-- /* listname parameter must be statically allocated. */
-- item->listname = listname;
-- item->dict = PyDict_New();
-- if (item->dict == NULL) {
-- Py_DECREF(item);
-- item = NULL;
-- }
-- }
-- return (PyObject *) item;
--}
--
--
--/* ===================================================================== */
--/* Plugin object */
--
--#define GET_MODULE_DATA(x, force) \
-- o = PyObject_GetAttrString(m, "__module_" #x "__"); \
-- if (o == NULL) { \
-- if (force) { \
-- hexchat_print(ph, "Module has no __module_" #x "__ " \
-- "defined"); \
-- goto error; \
-- } \
-- plugin->x = g_strdup(""); \
-- } else {\
-- if (!PyUnicode_Check(o)) { \
-- hexchat_print(ph, "Variable __module_" #x "__ " \
-- "must be a string"); \
-- goto error; \
-- } \
-- plugin->x = g_strdup(PyUnicode_AsUTF8(o)); \
-- if (plugin->x == NULL) { \
-- hexchat_print(ph, "Not enough memory to allocate " #x); \
-- goto error; \
-- } \
-- }
--
--static PyObject *
--Plugin_GetCurrent()
--{
-- PyObject *plugin;
-- plugin = PySys_GetObject("__plugin__");
-- if (plugin == NULL)
-- PyErr_SetString(PyExc_RuntimeError, "lost sys.__plugin__");
-- return plugin;
--}
--
--static hexchat_plugin *
--Plugin_GetHandle(PluginObject *plugin)
--{
-- /* This works but the issue is that the script must be ran to get
-- * the name of it thus upon first use it will use the wrong handler
-- * work around would be to run a fake script once to get name? */
--#if 0
-- /* return fake handle for pluginpref */
-- if (plugin->gui != NULL)
-- return plugin->gui;
-- else
--#endif
-- return ph;
--}
--
--static PluginObject *
--Plugin_ByString(char *str)
--{
-- GSList *list;
-- PluginObject *plugin;
-- char *basename;
-- list = plugin_list;
-- while (list != NULL) {
-- plugin = (PluginObject *) list->data;
-- basename = g_path_get_basename(plugin->filename);
-- if (basename == NULL)
-- break;
-- if (strcasecmp(plugin->name, str) == 0 ||
-- strcasecmp(plugin->filename, str) == 0 ||
-- strcasecmp(basename, str) == 0) {
-- g_free(basename);
-- return plugin;
-- }
-- g_free(basename);
-- list = list->next;
-- }
-- return NULL;
--}
--
--static Hook *
--Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
-- PyObject *userdata, char *name, void *data)
--{
-- Hook *hook = g_new(Hook, 1);
-- hook->type = type;
-- hook->plugin = plugin;
-- Py_INCREF(callback);
-- hook->callback = callback;
-- Py_INCREF(userdata);
-- hook->userdata = userdata;
-- hook->name = g_strdup (name);
-- hook->data = NULL;
-- Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
-- hook));
--
-- return hook;
--}
--
--static Hook *
--Plugin_FindHook(PyObject *plugin, char *name)
--{
-- Hook *hook = NULL;
-- GSList *plugin_hooks = Plugin_GetHooks(plugin);
--
-- while (plugin_hooks)
-- {
-- if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
-- {
-- hook = (Hook *)plugin_hooks->data;
-- break;
-- }
--
-- plugin_hooks = g_slist_next(plugin_hooks);
-- }
--
-- return hook;
--}
--
--static void
--Plugin_RemoveHook(PyObject *plugin, Hook *hook)
--{
-- GSList *list;
-- /* Is this really a hook of the running plugin? */
-- list = g_slist_find(Plugin_GetHooks(plugin), hook);
-- if (list) {
-- /* Ok, unhook it. */
-- if (hook->type != HOOK_UNLOAD) {
-- /* This is an xchat hook. Unregister it. */
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_unhook(ph, (hexchat_hook*)hook->data);
-- END_XCHAT_CALLS();
-- }
-- Plugin_SetHooks(plugin,
-- g_slist_remove(Plugin_GetHooks(plugin),
-- hook));
-- Py_DECREF(hook->callback);
-- Py_DECREF(hook->userdata);
-- g_free(hook->name);
-- g_free(hook);
-- }
--}
--
--static void
--Plugin_RemoveAllHooks(PyObject *plugin)
--{
-- GSList *list = Plugin_GetHooks(plugin);
-- while (list) {
-- Hook *hook = (Hook *) list->data;
-- if (hook->type != HOOK_UNLOAD) {
-- /* This is an xchat hook. Unregister it. */
-- BEGIN_XCHAT_CALLS(NONE);
-- hexchat_unhook(ph, (hexchat_hook*)hook->data);
-- END_XCHAT_CALLS();
-- }
-- Py_DECREF(hook->callback);
-- Py_DECREF(hook->userdata);
-- g_free(hook->name);
-- g_free(hook);
-- list = list->next;
-- }
-- Plugin_SetHooks(plugin, NULL);
--}
--
--static void
--Plugin_Delete(PyObject *plugin)
--{
-- PyThreadState *tstate = ((PluginObject*)plugin)->tstate;
-- GSList *list = Plugin_GetHooks(plugin);
-- while (list) {
-- Hook *hook = (Hook *) list->data;
-- if (hook->type == HOOK_UNLOAD) {
-- PyObject *retobj;
-- retobj = PyObject_CallFunction(hook->callback, "(O)",
-- hook->userdata);
-- if (retobj) {
-- Py_DECREF(retobj);
-- } else {
-- PyErr_Print();
-- PyErr_Clear();
-- }
-- }
-- list = list->next;
-- }
-- Plugin_RemoveAllHooks(plugin);
-- if (((PluginObject *)plugin)->gui != NULL)
-- hexchat_plugingui_remove(ph, ((PluginObject *)plugin)->gui);
-- Py_DECREF(plugin);
-- /*PyThreadState_Swap(tstate); needed? */
-- Py_EndInterpreter(tstate);
--}
--
--static PyObject *
--Plugin_New(char *filename, PyObject *xcoobj)
--{
-- PluginObject *plugin = NULL;
-- PyObject *m, *o;
--#ifdef IS_PY3K
-- wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
-- char *argv[] = { "<hexchat>", 0 };
--#endif
--
-- if (filename) {
-- char *old_filename = filename;
-- filename = Util_Expand(filename);
-- if (filename == NULL) {
-- hexchat_printf(ph, "File not found: %s", old_filename);
-- return NULL;
-- }
-- }
--
-- /* Allocate plugin structure. */
-- plugin = PyObject_New(PluginObject, &Plugin_Type);
-- if (plugin == NULL) {
-- hexchat_print(ph, "Can't create plugin object");
-- goto error;
-- }
--
-- Plugin_SetName(plugin, NULL);
-- Plugin_SetVersion(plugin, NULL);
-- Plugin_SetFilename(plugin, NULL);
-- Plugin_SetDescription(plugin, NULL);
-- Plugin_SetHooks(plugin, NULL);
-- Plugin_SetContext(plugin, hexchat_get_context(ph));
-- Plugin_SetGui(plugin, NULL);
--
-- /* Start a new interpreter environment for this plugin. */
-- PyEval_AcquireThread(main_tstate);
-- plugin->tstate = Py_NewInterpreter();
-- if (plugin->tstate == NULL) {
-- hexchat_print(ph, "Can't create interpreter state");
-- goto error;
-- }
--
-- PySys_SetArgv(1, argv);
-- PySys_SetObject("__plugin__", (PyObject *) plugin);
--
-- /* Set stdout and stderr to xchatout. */
-- Py_INCREF(xcoobj);
-- PySys_SetObject("stdout", xcoobj);
-- Py_INCREF(xcoobj);
-- PySys_SetObject("stderr", xcoobj);
--
-- if (filename) {
--#ifdef WIN32
-- char *file;
-- if (!g_file_get_contents(filename, &file, NULL, NULL)) {
-- hexchat_printf(ph, "Can't open file %s: %s\n",
-- filename, strerror(errno));
-- goto error;
-- }
--
-- if (PyRun_SimpleString(file) != 0) {
-- hexchat_printf(ph, "Error loading module %s\n",
-- filename);
-- g_free (file);
-- goto error;
-- }
--
-- plugin->filename = filename;
-- filename = NULL;
-- g_free (file);
--#else
-- FILE *fp;
-- plugin->filename = filename;
--
-- /* It's now owned by the plugin. */
-- filename = NULL;
--
-- /* Open the plugin file. */
-- fp = fopen(plugin->filename, "r");
-- if (fp == NULL) {
-- hexchat_printf(ph, "Can't open file %s: %s\n",
-- plugin->filename, strerror(errno));
-- goto error;
-- }
--
-- /* Run the plugin. */
-- if (PyRun_SimpleFile(fp, plugin->filename) != 0) {
-- hexchat_printf(ph, "Error loading module %s\n",
-- plugin->filename);
-- fclose(fp);
-- goto error;
-- }
-- fclose(fp);
--#endif
-- m = PyDict_GetItemString(PyImport_GetModuleDict(),
-- "__main__");
-- if (m == NULL) {
-- hexchat_print(ph, "Can't get __main__ module");
-- goto error;
-- }
-- GET_MODULE_DATA(name, 1);
-- GET_MODULE_DATA(version, 0);
-- GET_MODULE_DATA(description, 0);
-- plugin->gui = hexchat_plugingui_add(ph, plugin->filename,
-- plugin->name,
-- plugin->description,
-- plugin->version, NULL);
-- }
--
-- PyEval_ReleaseThread(plugin->tstate);
--
-- return (PyObject *) plugin;
--
--error:
-- g_free(filename);
--
-- if (plugin) {
-- if (plugin->tstate)
-- Plugin_Delete((PyObject *)plugin);
-- else
-- Py_DECREF(plugin);
-- }
-- PyEval_ReleaseLock();
--
-- return NULL;
--}
--
--static void
--Plugin_dealloc(PluginObject *self)
--{
-- g_free(self->filename);
-- g_free(self->name);
-- g_free(self->version);
-- g_free(self->description);
-- Py_TYPE(self)->tp_free((PyObject *)self);
--}
--
--static PyTypeObject Plugin_Type = {
-- PyVarObject_HEAD_INIT(NULL, 0)
-- "hexchat.Plugin", /*tp_name*/
-- sizeof(PluginObject), /*tp_basicsize*/
-- 0, /*tp_itemsize*/
-- (destructor)Plugin_dealloc, /*tp_dealloc*/
-- 0, /*tp_print*/
-- 0, /*tp_getattr*/
-- 0, /*tp_setattr*/
-- 0, /*tp_compare*/
-- 0, /*tp_repr*/
-- 0, /*tp_as_number*/
-- 0, /*tp_as_sequence*/
-- 0, /*tp_as_mapping*/
-- 0, /*tp_hash*/
-- 0, /*tp_call*/
-- 0, /*tp_str*/
-- PyObject_GenericGetAttr,/*tp_getattro*/
-- PyObject_GenericSetAttr,/*tp_setattro*/
-- 0, /*tp_as_buffer*/
-- Py_TPFLAGS_DEFAULT, /*tp_flags*/
-- 0, /*tp_doc*/
-- 0, /*tp_traverse*/
-- 0, /*tp_clear*/
-- 0, /*tp_richcompare*/
-- 0, /*tp_weaklistoffset*/
-- 0, /*tp_iter*/
-- 0, /*tp_iternext*/
-- 0, /*tp_methods*/
-- 0, /*tp_members*/
-- 0, /*tp_getset*/
-- 0, /*tp_base*/
-- 0, /*tp_dict*/
-- 0, /*tp_descr_get*/
-- 0, /*tp_descr_set*/
-- 0, /*tp_dictoffset*/
-- 0, /*tp_init*/
-- PyType_GenericAlloc, /*tp_alloc*/
-- PyType_GenericNew, /*tp_new*/
-- PyObject_Del, /*tp_free*/
-- 0, /*tp_is_gc*/
--};
--
--
--/* ===================================================================== */
--/* XChat module */
--
--static PyObject *
--Module_hexchat_command(PyObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:command", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- hexchat_command(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_prnt(PyObject *self, PyObject *args)
--{
-- char *text;
-- if (!PyArg_ParseTuple(args, "s:prnt", &text))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- hexchat_print(ph, text);
-- END_XCHAT_CALLS();
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_hexchat_emit_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *argv[6];
-- char *name;
-- int res;
-- long time = 0;
-- hexchat_event_attrs *attrs;
-- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
-- "arg4", "arg5", "arg6",
-- "time", NULL};
-- memset(&argv, 0, sizeof(char*)*6);
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
-- &argv[0], &argv[1], &argv[2],
-- &argv[3], &argv[4], &argv[5],
-- &time))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
-- attrs = hexchat_event_attrs_create(ph);
-- attrs->server_time_utc = (time_t)time;
--
-- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
-- argv[3], argv[4], argv[5], NULL);
--
-- hexchat_event_attrs_free(ph, attrs);
-- END_XCHAT_CALLS();
-- return PyLong_FromLong(res);
--}
--
--static PyObject *
--Module_hexchat_get_info(PyObject *self, PyObject *args)
--{
-- const char *info;
-- char *name;
-- if (!PyArg_ParseTuple(args, "s:get_info", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
-- info = hexchat_get_info(ph, name);
-- END_XCHAT_CALLS();
-- if (info == NULL) {
-- Py_RETURN_NONE;
-- }
-- if (strcmp (name, "gtkwin_ptr") == 0 || strcmp (name, "win_ptr") == 0)
-- return PyUnicode_FromFormat("%p", info); /* format as pointer */
-- else
-- return PyUnicode_FromString(info);
--}
--
--static PyObject *
--Module_xchat_get_prefs(PyObject *self, PyObject *args)
--{
-- PyObject *res;
-- const char *info;
-- int integer;
-- char *name;
-- int type;
-- if (!PyArg_ParseTuple(args, "s:get_prefs", &name))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- type = hexchat_get_prefs(ph, name, &info, &integer);
-- END_XCHAT_CALLS();
-- switch (type) {
-- case 0:
-- Py_INCREF(Py_None);
-- res = Py_None;
-- break;
-- case 1:
-- res = PyUnicode_FromString((char*)info);
-- break;
-- case 2:
-- case 3:
-- res = PyLong_FromLong(integer);
-- break;
-- default:
-- PyErr_Format(PyExc_RuntimeError,
-- "unknown get_prefs type (%d), "
-- "please report", type);
-- res = NULL;
-- break;
-- }
-- return res;
--}
--
--static PyObject *
--Module_hexchat_get_context(PyObject *self, PyObject *args)
--{
-- PyObject *plugin;
-- PyObject *ctxobj;
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- ctxobj = Context_FromContext(Plugin_GetContext(plugin));
-- if (ctxobj == NULL) {
-- Py_RETURN_NONE;
-- }
-- return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_find_context(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *server = NULL;
-- char *channel = NULL;
-- PyObject *ctxobj;
-- char *kwlist[] = {"server", "channel", 0};
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zz:find_context",
-- kwlist, &server, &channel))
-- return NULL;
-- ctxobj = Context_FromServerAndChannel(server, channel);
-- if (ctxobj == NULL) {
-- Py_RETURN_NONE;
-- }
-- return ctxobj;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_set(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- int result;
-- char *var;
-- PyObject *value;
--
-- if (!PyArg_ParseTuple(args, "sO:set_pluginpref", &var, &value))
-- return NULL;
-- if (PyLong_Check(value)) {
-- int intvalue = PyLong_AsLong(value);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_set_int(prefph, var, intvalue);
-- END_XCHAT_CALLS();
-- }
-- else if (PyUnicode_Check(value)) {
-- char *charvalue = PyUnicode_AsUTF8(value);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_set_str(prefph, var, charvalue);
-- END_XCHAT_CALLS();
-- }
-- else
-- result = 0;
-- return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- PyObject *ret;
-- char *var;
-- char retstr[512];
-- int retint;
-- int result;
-- if (!PyArg_ParseTuple(args, "s:get_pluginpref", &var))
-- return NULL;
--
-- /* This will always return numbers as integers. */
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_get_str(prefph, var, retstr);
-- END_XCHAT_CALLS();
-- if (result) {
-- if (strlen (retstr) <= 12) {
-- BEGIN_XCHAT_CALLS(NONE);
-- retint = hexchat_pluginpref_get_int(prefph, var);
-- END_XCHAT_CALLS();
-- if ((retint == -1) && (strcmp(retstr, "-1") != 0))
-- ret = PyUnicode_FromString(retstr);
-- else
-- ret = PyLong_FromLong(retint);
-- } else
-- ret = PyUnicode_FromString(retstr);
-- }
-- else
-- {
-- Py_INCREF(Py_None);
-- ret = Py_None;
-- }
-- return ret;
--}
--
--static PyObject *
--Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- char *var;
-- int result;
-- if (!PyArg_ParseTuple(args, "s:del_pluginpref", &var))
-- return NULL;
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_delete(prefph, var);
-- END_XCHAT_CALLS();
-- return PyBool_FromLong(result);
--}
--
--static PyObject *
--Module_hexchat_pluginpref_list(PyObject *self, PyObject *args)
--{
-- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
-- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
-- char list[4096];
-- char* token;
-- int result;
-- PyObject *pylist;
-- pylist = PyList_New(0);
-- BEGIN_XCHAT_CALLS(NONE);
-- result = hexchat_pluginpref_list(prefph, list);
-- END_XCHAT_CALLS();
-- if (result) {
-- token = strtok(list, ",");
-- while (token != NULL) {
-- PyList_Append(pylist, PyUnicode_FromString(token));
-- token = strtok (NULL, ",");
-- }
-- }
-- return pylist;
--}
--
--static PyObject *
--Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- char *help = NULL;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata",
-- "priority", "help", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oiz:hook_command",
-- kwlist, &name, &callback, &userdata,
-- &priority, &help))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_command(ph, name, priority,
-- Callback_Command, help, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
-- Callback_Server, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_server_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
-- Callback_Server, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_print(ph, name, priority,
-- Callback_Print, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- char *name;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- int priority = HEXCHAT_PRI_NORM;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print_attrs",
-- kwlist, &name, &callback, &userdata,
-- &priority))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, name, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
-- Callback_Print_Attrs, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- int timeout;
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"timeout", "callback", "userdata", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|O:hook_timer",
-- kwlist, &timeout, &callback,
-- &userdata))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- BEGIN_XCHAT_CALLS(NONE);
-- hook->data = (void*)hexchat_hook_timer(ph, timeout,
-- Callback_Timer, hook);
-- END_XCHAT_CALLS();
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
--{
-- PyObject *callback;
-- PyObject *userdata = Py_None;
-- PyObject *plugin;
-- Hook *hook;
-- char *kwlist[] = {"callback", "userdata", 0};
--
-- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:hook_unload",
-- kwlist, &callback, &userdata))
-- return NULL;
--
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
-- if (!PyCallable_Check(callback)) {
-- PyErr_SetString(PyExc_TypeError, "callback is not callable");
-- return NULL;
-- }
--
-- hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
-- if (hook == NULL)
-- return NULL;
--
-- return PyLong_FromVoidPtr(hook);
--}
--
--static PyObject *
--Module_hexchat_unhook(PyObject *self, PyObject *args)
--{
-- PyObject *plugin;
-- PyObject *obj;
-- Hook *hook;
-- if (!PyArg_ParseTuple(args, "O:unhook", &obj))
-- return NULL;
-- plugin = Plugin_GetCurrent();
-- if (plugin == NULL)
-- return NULL;
--
-- if (PyUnicode_Check (obj))
-- {
-- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
-- while (hook)
-- {
-- Plugin_RemoveHook(plugin, hook);
-- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
-- }
-- }
-- else
-- {
-- hook = (Hook *)PyLong_AsVoidPtr(obj);
-- Plugin_RemoveHook(plugin, hook);
-- }
--
-- Py_RETURN_NONE;
--}
--
--static PyObject *
--Module_xchat_get_list(PyObject *self, PyObject *args)
--{
-- hexchat_list *list;
-- PyObject *l;
-- const char *name;
-- const char *const *fields;
-- int i;
--
-- if (!PyArg_ParseTuple(args, "s:get_list", &name))
-- return NULL;
-- /* This function is thread safe, and returns statically
-- * allocated data. */
-- fields = hexchat_list_fields(ph, "lists");
-- for (i = 0; fields[i]; i++) {
-- if (strcmp(fields[i], name) == 0) {
-- /* Use the static allocated one. */
-- name = fields[i];
-- break;
-- }
-- }
-- if (fields[i] == NULL) {
-- PyErr_SetString(PyExc_KeyError, "list not available");
-- return NULL;
-- }
-- l = PyList_New(0);
-- if (l == NULL)
-- return NULL;
-- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
-- list = hexchat_list_get(ph, (char*)name);
-- if (list == NULL)
-- goto error;
-- fields = hexchat_list_fields(ph, (char*)name);
-- while (hexchat_list_next(ph, list)) {
-- PyObject *o = ListItem_New(name);
-- if (o == NULL || PyList_Append(l, o) == -1) {
-- Py_XDECREF(o);
-- goto error;
-- }
-- Py_DECREF(o); /* l is holding a reference */
-- for (i = 0; fields[i]; i++) {
-- const char *fld = fields[i]+1;
-- PyObject *attr = NULL;
-- const char *sattr;
-- int iattr;
-- time_t tattr;
-- switch(fields[i][0]) {
-- case 's':
-- sattr = hexchat_list_str(ph, list, (char*)fld);
-- attr = PyUnicode_FromString(sattr?sattr:"");
-- break;
-- case 'i':
-- iattr = hexchat_list_int(ph, list, (char*)fld);
-- attr = PyLong_FromLong((long)iattr);
-- break;
-- case 't':
-- tattr = hexchat_list_time(ph, list, (char*)fld);
-- attr = PyLong_FromLong((long)tattr);
-- break;
-- case 'p':
-- sattr = hexchat_list_str(ph, list, (char*)fld);
-- if (strcmp(fld, "context") == 0) {
-- attr = Context_FromContext(
-- (hexchat_context*)sattr);
-- break;
-- }
-- default: /* ignore unknown (newly added?) types */
-- continue;
-- }
-- if (attr == NULL)
-- goto error;
-- PyObject_SetAttrString(o, (char*)fld, attr); /* add reference on attr in o */
-- Py_DECREF(attr); /* make o own attr */
-- }
-- }
-- hexchat_list_free(ph, list);
-- goto exit;
--error:
-- if (list)
-- hexchat_list_free(ph, list);
-- Py_DECREF(l);
-- l = NULL;
--
--exit:
-- END_XCHAT_CALLS();
-- return l;
--}
--
--static PyObject *
--Module_xchat_get_lists(PyObject *self, PyObject *args)
--{
-- PyObject *l, *o;
-- const char *const *fields;
-- int i;
-- /* This function is thread safe, and returns statically
-- * allocated data. */
-- fields = hexchat_list_fields(ph, "lists");
-- l = PyList_New(0);
-- if (l == NULL)
-- return NULL;
-- for (i = 0; fields[i]; i++) {
-- o = PyUnicode_FromString(fields[i]);
-- if (o == NULL || PyList_Append(l, o) == -1) {
-- Py_DECREF(l);
-- Py_XDECREF(o);
-- return NULL;
-- }
-- Py_DECREF(o); /* l is holding a reference */
-- }
-- return l;
--}
--
--static PyObject *
--Module_hexchat_nickcmp(PyObject *self, PyObject *args)
--{
-- char *s1, *s2;
-- if (!PyArg_ParseTuple(args, "ss:nickcmp", &s1, &s2))
-- return NULL;
-- return PyLong_FromLong((long) hexchat_nickcmp(ph, s1, s2));
--}
--
--static PyObject *
--Module_hexchat_strip(PyObject *self, PyObject *args)
--{
-- PyObject *result;
-- char *str, *str2;
-- int len = -1, flags = 1 | 2;
-- if (!PyArg_ParseTuple(args, "s|ii:strip", &str, &len, &flags))
-- return NULL;
-- str2 = hexchat_strip(ph, str, len, flags);
-- result = PyUnicode_FromString(str2);
-- hexchat_free(ph, str2);
-- return result;
--}
--
--static PyMethodDef Module_xchat_methods[] = {
-- {"command", Module_hexchat_command,
-- METH_VARARGS},
-- {"prnt", Module_xchat_prnt,
-- METH_VARARGS},
-- {"emit_print", (PyCFunction)Module_hexchat_emit_print,
-- METH_VARARGS|METH_KEYWORDS},
-- {"get_info", Module_hexchat_get_info,
-- METH_VARARGS},
-- {"get_prefs", Module_xchat_get_prefs,
-- METH_VARARGS},
-- {"get_context", Module_hexchat_get_context,
-- METH_NOARGS},
-- {"find_context", (PyCFunction)Module_hexchat_find_context,
-- METH_VARARGS|METH_KEYWORDS},
-- {"set_pluginpref", Module_hexchat_pluginpref_set,
-- METH_VARARGS},
-- {"get_pluginpref", Module_hexchat_pluginpref_get,
-- METH_VARARGS},
-- {"del_pluginpref", Module_hexchat_pluginpref_delete,
-- METH_VARARGS},
-- {"list_pluginpref", Module_hexchat_pluginpref_list,
-- METH_VARARGS},
-- {"hook_command", (PyCFunction)Module_hexchat_hook_command,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_server", (PyCFunction)Module_hexchat_hook_server,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_server_attrs", (PyCFunction)Module_hexchat_hook_server_attrs,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_print", (PyCFunction)Module_hexchat_hook_print,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_print_attrs", (PyCFunction)Module_hexchat_hook_print_attrs,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_timer", (PyCFunction)Module_hexchat_hook_timer,
-- METH_VARARGS|METH_KEYWORDS},
-- {"hook_unload", (PyCFunction)Module_hexchat_hook_unload,
-- METH_VARARGS|METH_KEYWORDS},
-- {"unhook", Module_hexchat_unhook,
-- METH_VARARGS},
-- {"get_list", Module_xchat_get_list,
-- METH_VARARGS},
-- {"get_lists", Module_xchat_get_lists,
-- METH_NOARGS},
-- {"nickcmp", Module_hexchat_nickcmp,
-- METH_VARARGS},
-- {"strip", Module_hexchat_strip,
-- METH_VARARGS},
-- {NULL, NULL}
--};
--
--#ifdef IS_PY3K
--static struct PyModuleDef moduledef = {
-- PyModuleDef_HEAD_INIT,
-- "hexchat", /* m_name */
-- "HexChat Scripting Interface", /* m_doc */
-- -1, /* m_size */
-- Module_xchat_methods, /* m_methods */
-- NULL, /* m_reload */
-- NULL, /* m_traverse */
-- NULL, /* m_clear */
-- NULL, /* m_free */
--};
--
--static struct PyModuleDef xchat_moduledef = {
-- PyModuleDef_HEAD_INIT,
-- "xchat", /* m_name */
-- "HexChat Scripting Interface", /* m_doc */
-- -1, /* m_size */
-- Module_xchat_methods, /* m_methods */
-- NULL, /* m_reload */
-- NULL, /* m_traverse */
-- NULL, /* m_clear */
-- NULL, /* m_free */
--};
--#endif
--
--static PyObject *
--moduleinit_hexchat(void)
--{
-- PyObject *hm;
--#ifdef IS_PY3K
-- hm = PyModule_Create(&moduledef);
--#else
-- hm = Py_InitModule3("hexchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
-- PyModule_AddIntConstant(hm, "EAT_NONE", HEXCHAT_EAT_NONE);
-- PyModule_AddIntConstant(hm, "EAT_HEXCHAT", HEXCHAT_EAT_HEXCHAT);
-- PyModule_AddIntConstant(hm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT); /* for compat */
-- PyModule_AddIntConstant(hm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
-- PyModule_AddIntConstant(hm, "EAT_ALL", HEXCHAT_EAT_ALL);
-- PyModule_AddIntConstant(hm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
-- PyModule_AddIntConstant(hm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
-- PyModule_AddIntConstant(hm, "PRI_NORM", HEXCHAT_PRI_NORM);
-- PyModule_AddIntConstant(hm, "PRI_LOW", HEXCHAT_PRI_LOW);
-- PyModule_AddIntConstant(hm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
-- PyObject_SetAttrString(hm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
-- return hm;
--}
--
--static PyObject *
--moduleinit_xchat(void)
--{
-- PyObject *xm;
--#ifdef IS_PY3K
-- xm = PyModule_Create(&xchat_moduledef);
--#else
-- xm = Py_InitModule3("xchat", Module_xchat_methods, "HexChat Scripting Interface");
--#endif
--
-- PyModule_AddIntConstant(xm, "EAT_NONE", HEXCHAT_EAT_NONE);
-- PyModule_AddIntConstant(xm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT);
-- PyModule_AddIntConstant(xm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
-- PyModule_AddIntConstant(xm, "EAT_ALL", HEXCHAT_EAT_ALL);
-- PyModule_AddIntConstant(xm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
-- PyModule_AddIntConstant(xm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
-- PyModule_AddIntConstant(xm, "PRI_NORM", HEXCHAT_PRI_NORM);
-- PyModule_AddIntConstant(xm, "PRI_LOW", HEXCHAT_PRI_LOW);
-- PyModule_AddIntConstant(xm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
--
-- PyObject_SetAttrString(xm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
--
-- return xm;
--}
--
--#ifdef IS_PY3K
--PyMODINIT_FUNC
--PyInit_hexchat(void)
--{
-- return moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--PyInit_xchat(void)
--{
-- return moduleinit_xchat();
--}
--#else
--PyMODINIT_FUNC
--inithexchat(void)
--{
-- moduleinit_hexchat();
--}
--PyMODINIT_FUNC
--initxchat(void)
--{
-- moduleinit_xchat();
--}
--#endif
--
--/* ===================================================================== */
--/* Python interactive interpreter functions */
--
--static void
--IInterp_Exec(char *command)
--{
-- PyObject *m, *d, *o;
-- char *buffer;
-- int len;
--
-- BEGIN_PLUGIN(interp_plugin);
--
-- m = PyImport_AddModule("__main__");
-- if (m == NULL) {
-- hexchat_print(ph, "Can't get __main__ module");
-- goto fail;
-- }
-- d = PyModule_GetDict(m);
-- len = strlen(command);
--
-- buffer = g_malloc(len + 2);
-- memcpy(buffer, command, len);
-- buffer[len] = '\n';
-- buffer[len+1] = 0;
-- PyRun_SimpleString("import hexchat");
-- o = PyRun_StringFlags(buffer, Py_single_input, d, d, NULL);
-- g_free(buffer);
-- if (o == NULL) {
-- PyErr_Print();
-- goto fail;
-- }
-- Py_DECREF(o);
--
--fail:
-- END_PLUGIN(interp_plugin);
-- return;
--}
--
--static int
--IInterp_Cmd(char *word[], char *word_eol[], void *userdata)
--{
-- char *channel = (char *) hexchat_get_info(ph, "channel");
-- if (channel && channel[0] == '>' && strcmp(channel, ">>python<<") == 0) {
-- hexchat_printf(ph, ">>> %s\n", word_eol[1]);
-- IInterp_Exec(word_eol[1]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--
--/* ===================================================================== */
--/* Python command handling */
--
--static void
--Command_PyList(void)
--{
-- GSList *list;
-- list = plugin_list;
-- if (list == NULL) {
-- hexchat_print(ph, "No python modules loaded");
-- } else {
-- hexchat_print(ph,
-- "Name Version Filename Description\n"
-- "---- ------- -------- -----------\n");
-- while (list != NULL) {
-- PluginObject *plg = (PluginObject *) list->data;
-- char *basename = g_path_get_basename(plg->filename);
-- hexchat_printf(ph, "%-12s %-8s %-20s %-10s\n",
-- plg->name,
-- *plg->version ? plg->version
-- : "<none>",
-- basename,
-- *plg->description ? plg->description
-- : "<none>");
-- g_free(basename);
-- list = list->next;
-- }
-- hexchat_print(ph, "\n");
-- }
--}
--
--static void
--Command_PyLoad(char *filename)
--{
-- PyObject *plugin;
-- RELEASE_XCHAT_LOCK();
-- plugin = Plugin_New(filename, xchatout);
-- ACQUIRE_XCHAT_LOCK();
-- if (plugin)
-- plugin_list = g_slist_append(plugin_list, plugin);
--}
--
--static void
--Command_PyUnload(char *name)
--{
-- PluginObject *plugin = Plugin_ByString(name);
-- if (!plugin) {
-- hexchat_print(ph, "Can't find a python plugin with that name");
-- } else {
-- BEGIN_PLUGIN(plugin);
-- Plugin_Delete((PyObject*)plugin);
-- END_PLUGIN(plugin);
-- plugin_list = g_slist_remove(plugin_list, plugin);
-- }
--}
--
--static void
--Command_PyReload(char *name)
--{
-- PluginObject *plugin = Plugin_ByString(name);
-- if (!plugin) {
-- hexchat_print(ph, "Can't find a python plugin with that name");
-- } else {
-- char *filename = g_strdup(plugin->filename);
-- Command_PyUnload(filename);
-- Command_PyLoad(filename);
-- g_free(filename);
-- }
--}
--
--static void
--Command_PyAbout(void)
--{
-- hexchat_print(ph, about);
--}
--
--static int
--Command_Py(char *word[], char *word_eol[], void *userdata)
--{
-- char *cmd = word[2];
-- int ok = 0;
-- if (strcasecmp(cmd, "LIST") == 0) {
-- ok = 1;
-- Command_PyList();
-- } else if (strcasecmp(cmd, "EXEC") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- IInterp_Exec(word_eol[3]);
-- }
-- } else if (strcasecmp(cmd, "LOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyLoad(word[3]);
-- }
-- } else if (strcasecmp(cmd, "UNLOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyUnload(word[3]);
-- }
-- } else if (strcasecmp(cmd, "RELOAD") == 0) {
-- if (word[3][0]) {
-- ok = 1;
-- Command_PyReload(word[3]);
-- }
-- } else if (strcasecmp(cmd, "CONSOLE") == 0) {
-- ok = 1;
-- hexchat_command(ph, "QUERY >>python<<");
-- } else if (strcasecmp(cmd, "ABOUT") == 0) {
-- ok = 1;
-- Command_PyAbout();
-- }
-- if (!ok)
-- hexchat_print(ph, usage);
-- return HEXCHAT_EAT_ALL;
--}
--
--static int
--Command_Load(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyLoad(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Reload(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyReload(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--static int
--Command_Unload(char *word[], char *word_eol[], void *userdata)
--{
-- int len = strlen(word[2]);
-- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
-- Command_PyUnload(word[2]);
-- return HEXCHAT_EAT_HEXCHAT;
-- }
-- return HEXCHAT_EAT_NONE;
--}
--
--/* ===================================================================== */
--/* Autoload function */
--
--/* ===================================================================== */
--/* (De)initialization functions */
--
--static int initialized = 0;
--static int reinit_tried = 0;
--
--void
--hexchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
--{
-- *name = "Python";
-- *version = VERSION;
-- *desc = "Python scripting interface";
-- if (reserved)
-- *reserved = NULL;
--}
--
--int
--hexchat_plugin_init(hexchat_plugin *plugin_handle,
-- char **plugin_name,
-- char **plugin_desc,
-- char **plugin_version,
-- char *arg)
--{
--#ifdef IS_PY3K
-- wchar_t *argv[] = { L"<hexchat>", 0 };
--#else
-- char *argv[] = { "<hexchat>", 0 };
--#endif
--
-- ph = plugin_handle;
--
-- /* Block double initalization. */
-- if (initialized != 0) {
-- hexchat_print(ph, "Python interface already loaded");
-- /* deinit is called even when init fails, so keep track
-- * of a reinit failure. */
-- reinit_tried++;
-- return 0;
-- }
-- initialized = 1;
--
-- *plugin_name = "Python";
-- *plugin_version = VERSION;
--
-- /* FIXME You can't free this since it's used as long as the plugin's
-- * loaded, but if you unload it, everything belonging to the plugin is
-- * supposed to be freed anyway.
-- */
-- *plugin_desc = g_strdup_printf ("Python %d scripting interface", PY_MAJOR_VERSION);
--
-- /* Initialize python. */
--#ifdef IS_PY3K
-- Py_SetProgramName(L"hexchat");
-- PyImport_AppendInittab("hexchat", PyInit_hexchat);
-- PyImport_AppendInittab("xchat", PyInit_xchat);
--#else
-- Py_SetProgramName("hexchat");
-- PyImport_AppendInittab("hexchat", inithexchat);
-- PyImport_AppendInittab("xchat", initxchat);
--#endif
-- Py_Initialize();
-- PySys_SetArgv(1, argv);
--
-- xchatout_buffer = g_string_new (NULL);
-- xchatout = XChatOut_New();
-- if (xchatout == NULL) {
-- hexchat_print(ph, "Can't allocate xchatout object");
-- return 0;
-- }
--
--#ifdef WITH_THREAD
-- PyEval_InitThreads();
-- xchat_lock = PyThread_allocate_lock();
-- if (xchat_lock == NULL) {
-- hexchat_print(ph, "Can't allocate hexchat lock");
-- Py_DECREF(xchatout);
-- xchatout = NULL;
-- return 0;
-- }
--#endif
--
-- main_tstate = PyEval_SaveThread();
--
-- interp_plugin = Plugin_New(NULL, xchatout);
-- if (interp_plugin == NULL) {
-- hexchat_print(ph, "Plugin_New() failed.\n");
--#ifdef WITH_THREAD
-- PyThread_free_lock(xchat_lock);
--#endif
-- Py_DECREF(xchatout);
-- xchatout = NULL;
-- return 0;
-- }
--
--
-- hexchat_hook_command(ph, "", HEXCHAT_PRI_NORM, IInterp_Cmd, 0, 0);
-- hexchat_hook_command(ph, "PY", HEXCHAT_PRI_NORM, Command_Py, usage, 0);
-- hexchat_hook_command(ph, "LOAD", HEXCHAT_PRI_NORM, Command_Load, 0, 0);
-- hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, Command_Unload, 0, 0);
-- hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, Command_Reload, 0, 0);
--#ifdef WITH_THREAD
-- thread_timer = hexchat_hook_timer(ph, 300, Callback_ThreadTimer, NULL);
--#endif
--
-- hexchat_print(ph, "Python interface loaded\n");
--
-- Util_Autoload();
-- return 1;
--}
--
--int
--hexchat_plugin_deinit(void)
--{
-- GSList *list;
--
-- /* A reinitialization was tried. Just give up and live the
-- * environment as is. We are still alive. */
-- if (reinit_tried) {
-- reinit_tried--;
-- return 1;
-- }
--
-- list = plugin_list;
-- while (list != NULL) {
-- PyObject *plugin = (PyObject *) list->data;
-- BEGIN_PLUGIN(plugin);
-- Plugin_Delete(plugin);
-- END_PLUGIN(plugin);
-- list = list->next;
-- }
-- g_slist_free(plugin_list);
-- plugin_list = NULL;
--
-- /* Reset xchatout buffer. */
-- g_string_free (xchatout_buffer, TRUE);
-- xchatout_buffer = NULL;
--
-- if (interp_plugin) {
-- PyThreadState *tstate = ((PluginObject*)interp_plugin)->tstate;
-- PyThreadState_Swap(tstate);
-- Py_EndInterpreter(tstate);
-- Py_DECREF(interp_plugin);
-- interp_plugin = NULL;
-- }
--
-- /* Switch back to the main thread state. */
-- if (main_tstate) {
-- PyEval_RestoreThread(main_tstate);
-- PyThreadState_Swap(main_tstate);
-- main_tstate = NULL;
-- }
-- Py_Finalize();
--
--#ifdef WITH_THREAD
-- if (thread_timer != NULL) {
-- hexchat_unhook(ph, thread_timer);
-- thread_timer = NULL;
-- }
-- PyThread_free_lock(xchat_lock);
--#endif
--
-- hexchat_print(ph, "Python interface unloaded\n");
-- initialized = 0;
--
-- return 1;
--}
--
-diff --git a/plugins/python/python.def b/plugins/python/python.def
-index 6ce04e98..e560f50f 100644
---- a/plugins/python/python.def
-+++ b/plugins/python/python.def
-@@ -1,4 +1,3 @@
- EXPORTS
- hexchat_plugin_init
- hexchat_plugin_deinit
--hexchat_plugin_get_info
-diff --git a/plugins/python/python.py b/plugins/python/python.py
-new file mode 100644
-index 00000000..30694802
---- /dev/null
-+++ b/plugins/python/python.py
-@@ -0,0 +1,554 @@
-+from __future__ import print_function
-+
-+import importlib
-+import os
-+import pydoc
-+import signal
-+import sys
-+import traceback
-+import weakref
-+from contextlib import contextmanager
-+
-+from _hexchat_embedded import ffi, lib
-+
-+if sys.version_info < (3, 0):
-+ from io import BytesIO as HelpEater
-+else:
-+ from io import StringIO as HelpEater
-+
-+if not hasattr(sys, 'argv'):
-+ sys.argv = ['<hexchat>']
-+
-+VERSION = b'2.0' # Sync with hexchat.__version__
-+PLUGIN_NAME = ffi.new('char[]', b'Python')
-+PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
-+PLUGIN_VERSION = ffi.new('char[]', VERSION)
-+
-+# TODO: Constants should be screaming snake case
-+hexchat = None
-+local_interp = None
-+hexchat_stdout = None
-+plugins = set()
-+
-+
-+@contextmanager
-+def redirected_stdout():
-+ sys.stdout = sys.__stdout__
-+ sys.stderr = sys.__stderr__
-+ yield
-+ sys.stdout = hexchat_stdout
-+ sys.stderr = hexchat_stdout
-+
-+
-+if os.getenv('HEXCHAT_LOG_PYTHON'):
-+ def log(*args):
-+ with redirected_stdout():
-+ print(*args)
-+
-+else:
-+ def log(*args):
-+ pass
-+
-+
-+class Stdout:
-+ def __init__(self):
-+ self.buffer = bytearray()
-+
-+ def write(self, string):
-+ string = string.encode()
-+ idx = string.rfind(b'\n')
-+ if idx != -1:
-+ self.buffer += string[:idx]
-+ lib.hexchat_print(lib.ph, bytes(self.buffer))
-+ self.buffer = bytearray(string[idx + 1:])
-+ else:
-+ self.buffer += string
-+
-+ def isatty(self):
-+ return False
-+
-+
-+class Attribute:
-+ def __init__(self):
-+ self.time = 0
-+
-+ def __repr__(self):
-+ return '<Attribute object at {}>'.format(id(self))
-+
-+
-+class Hook:
-+ def __init__(self, plugin, callback, userdata, is_unload):
-+ self.is_unload = is_unload
-+ self.plugin = weakref.proxy(plugin)
-+ self.callback = callback
-+ self.userdata = userdata
-+ self.hexchat_hook = None
-+ self.handle = ffi.new_handle(weakref.proxy(self))
-+
-+ def __del__(self):
-+ log('Removing hook', id(self))
-+ if self.is_unload is False:
-+ assert self.hexchat_hook is not None
-+ lib.hexchat_unhook(lib.ph, self.hexchat_hook)
-+
-+
-+if sys.version_info[0] == 2:
-+ def compile_file(data, filename):
-+ return compile(data, filename, 'exec', dont_inherit=True)
-+
-+
-+ def compile_line(string):
-+ try:
-+ return compile(string, '<string>', 'eval', dont_inherit=True)
-+
-+ except SyntaxError:
-+ # For some reason `print` is invalid for eval
-+ # This will hide any return value though
-+ return compile(string, '<string>', 'exec', dont_inherit=True)
-+else:
-+ def compile_file(data, filename):
-+ return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
-+
-+
-+ def compile_line(string):
-+ # newline appended to solve unexpected EOF issues
-+ return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True)
-+
-+
-+class Plugin:
-+ def __init__(self):
-+ self.ph = None
-+ self.name = ''
-+ self.filename = ''
-+ self.version = ''
-+ self.description = ''
-+ self.hooks = set()
-+ self.globals = {
-+ '__plugin': weakref.proxy(self),
-+ '__name__': '__main__',
-+ }
-+
-+ def add_hook(self, callback, userdata, is_unload=False):
-+ hook = Hook(self, callback, userdata, is_unload=is_unload)
-+ self.hooks.add(hook)
-+ return hook
-+
-+ def remove_hook(self, hook):
-+ for h in self.hooks:
-+ if id(h) == hook:
-+ ud = h.userdata
-+ self.hooks.remove(h)
-+ return ud
-+
-+ log('Hook not found')
-+ return None
-+
-+ def loadfile(self, filename):
-+ try:
-+ self.filename = filename
-+ with open(filename) as f:
-+ data = f.read()
-+ compiled = compile_file(data, filename)
-+ exec(compiled, self.globals)
-+
-+ try:
-+ self.name = self.globals['__module_name__']
-+
-+ except KeyError:
-+ lib.hexchat_print(lib.ph, b'Failed to load module: __module_name__ must be set')
-+
-+ return False
-+
-+ self.version = self.globals.get('__module_version__', '')
-+ self.description = self.globals.get('__module_description__', '')
-+ self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), self.name.encode(),
-+ self.description.encode(), self.version.encode(), ffi.NULL)
-+
-+ except Exception as e:
-+ lib.hexchat_print(lib.ph, 'Failed to load module: {}'.format(e).encode())
-+ traceback.print_exc()
-+ return False
-+
-+ return True
-+
-+ def __del__(self):
-+ log('unloading', self.filename)
-+ for hook in self.hooks:
-+ if hook.is_unload is True:
-+ try:
-+ hook.callback(hook.userdata)
-+
-+ except Exception as e:
-+ log('Failed to run hook:', e)
-+ traceback.print_exc()
-+
-+ del self.hooks
-+ if self.ph is not None:
-+ lib.hexchat_plugingui_remove(lib.ph, self.ph)
-+
-+
-+if sys.version_info[0] == 2:
-+ def __decode(string):
-+ return string
-+
-+else:
-+ def __decode(string):
-+ return string.decode()
-+
-+
-+# There can be empty entries between non-empty ones so find the actual last value
-+def wordlist_len(words):
-+ for i in range(31, 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'<none>'
-+ description = plugin.description.encode() if plugin.description else b'<none>'
-+ tbl.append((name, version, basename, description))
-+
-+ column_sizes = [
-+ max(len(item) for item in column)
-+ for column in zip(*tbl)
-+ ]
-+
-+ for row in tbl:
-+ lib.hexchat_print(lib.ph, b' '.join(item.ljust(column_sizes[i])
-+ for i, item in enumerate(row)))
-+ lib.hexchat_print(lib.ph, b'')
-+
-+
-+def exec_in_interp(python):
-+ global local_interp
-+
-+ if not python:
-+ return
-+
-+ if local_interp is None:
-+ local_interp = Plugin()
-+ local_interp.locals = {}
-+ local_interp.globals['hexchat'] = hexchat
-+
-+ code = compile_line(python)
-+ try:
-+ ret = eval(code, local_interp.globals, local_interp.locals)
-+ if ret is not None:
-+ lib.hexchat_print(lib.ph, '{}'.format(ret).encode())
-+
-+ except Exception as e:
-+ traceback.print_exc(file=hexchat_stdout)
-+
-+
-+@ffi.def_extern()
-+def _on_load_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ load_filename(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern()
-+def _on_unload_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ unload_name(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern()
-+def _on_reload_command(word, word_eol, userdata):
-+ filename = ffi.string(word[2])
-+ if filename.endswith(b'.py'):
-+ reload_name(__decode(filename))
-+ return 3
-+
-+ return 0
-+
-+
-+@ffi.def_extern(error=3)
-+def _on_py_command(word, word_eol, userdata):
-+ subcmd = __decode(ffi.string(word[2])).lower()
-+
-+ if subcmd == 'exec':
-+ python = __decode(ffi.string(word_eol[3]))
-+ exec_in_interp(python)
-+
-+ elif subcmd == 'load':
-+ filename = __decode(ffi.string(word[3]))
-+ load_filename(filename)
-+
-+ elif subcmd == 'unload':
-+ name = __decode(ffi.string(word[3]))
-+ if not unload_name(name):
-+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
-+
-+ elif subcmd == 'reload':
-+ name = __decode(ffi.string(word[3]))
-+ if not reload_name(name):
-+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
-+
-+ elif subcmd == 'console':
-+ lib.hexchat_command(lib.ph, b'QUERY >>python<<')
-+
-+ elif subcmd == 'list':
-+ list_plugins()
-+
-+ elif subcmd == 'about':
-+ lib.hexchat_print(lib.ph, b'HexChat Python interface version ' + VERSION)
-+
-+ else:
-+ lib.hexchat_command(lib.ph, b'HELP PY')
-+
-+ return 3
-+
-+
-+@ffi.def_extern()
-+def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
-+ global hexchat
-+ global hexchat_stdout
-+
-+ signal.signal(signal.SIGINT, signal.SIG_DFL)
-+
-+ plugin_name[0] = PLUGIN_NAME
-+ plugin_desc[0] = PLUGIN_DESC
-+ plugin_version[0] = PLUGIN_VERSION
-+
-+ try:
-+ libdir = __decode(ffi.string(libdir))
-+ modpath = os.path.join(libdir, '..', 'python')
-+ sys.path.append(os.path.abspath(modpath))
-+ hexchat = importlib.import_module('hexchat')
-+
-+ except (UnicodeDecodeError, ImportError) as e:
-+ lib.hexchat_print(lib.ph, b'Failed to import module: ' + repr(e).encode())
-+
-+ return 0
-+
-+ hexchat_stdout = Stdout()
-+ sys.stdout = hexchat_stdout
-+ sys.stderr = hexchat_stdout
-+ pydoc.help = pydoc.Helper(HelpEater(), HelpEater())
-+
-+ lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'UNLOAD', 0, lib._on_unload_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'RELOAD', 0, lib._on_reload_command, ffi.NULL, ffi.NULL)
-+ lib.hexchat_hook_command(lib.ph, b'PY', 0, lib._on_py_command, b'''Usage: /PY LOAD <filename>
-+ UNLOAD <filename|name>
-+ RELOAD <filename|name>
-+ LIST
-+ EXEC <command>
-+ CONSOLE
-+ ABOUT''', ffi.NULL)
-+
-+ lib.hexchat_print(lib.ph, b'Python interface loaded')
-+ autoload()
-+ return 1
-+
-+
-+@ffi.def_extern()
-+def _on_plugin_deinit():
-+ global local_interp
-+ global hexchat
-+ global hexchat_stdout
-+ global plugins
-+
-+ plugins = set()
-+ local_interp = None
-+ hexchat = None
-+ hexchat_stdout = None
-+ sys.stdout = sys.__stdout__
-+ sys.stderr = sys.__stderr__
-+ pydoc.help = pydoc.Helper()
-+
-+ for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
-+ try:
-+ del sys.modules[mod]
-+
-+ except KeyError:
-+ pass
-+
-+ return 1
-diff --git a/plugins/python/python2.vcxproj b/plugins/python/python2.vcxproj
-index f914a865..0b098112 100644
---- a/plugins/python/python2.vcxproj
-+++ b/plugins/python/python2.vcxproj
-@@ -37,6 +37,9 @@
- <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
- </Link>
-+ <PreBuildEvent>
-+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+ </PreBuildEvent>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
-@@ -48,12 +51,15 @@
- <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
- </Link>
-+ <PreBuildEvent>
-+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+ </PreBuildEvent>
- </ItemDefinitionGroup>
- <ItemGroup>
- <None Include="python.def" />
- </ItemGroup>
- <ItemGroup>
-- <ClCompile Include="python.c" />
-+ <ClCompile Include="$(IntDir)python.c" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- </Project>
-diff --git a/plugins/python/python3.vcxproj b/plugins/python/python3.vcxproj
-index 815dc8b1..5868d3b0 100644
---- a/plugins/python/python3.vcxproj
-+++ b/plugins/python/python3.vcxproj
-@@ -37,6 +37,9 @@
- <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
- </Link>
-+ <PreBuildEvent>
-+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+ </PreBuildEvent>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
-@@ -48,12 +51,20 @@
- <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
- <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
- </Link>
-+ <PreBuildEvent>
-+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
-+ </PreBuildEvent>
- </ItemDefinitionGroup>
- <ItemGroup>
-+ <None Include="generate_plugin.py" />
-+ <None Include="hexchat.py" />
- <None Include="python.def" />
-+ <None Include="python.py" />
-+ <None Include="xchat.py" />
-+ <None Include="_hexchat.py" />
- </ItemGroup>
- <ItemGroup>
-- <ClCompile Include="python.c" />
-+ <ClCompile Include="$(IntDir)python.c" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
--</Project>
-+</Project>
-\ No newline at end of file
-diff --git a/plugins/python/python3.vcxproj.filters b/plugins/python/python3.vcxproj.filters
-index 9165e798..5c5834bb 100644
---- a/plugins/python/python3.vcxproj.filters
-+++ b/plugins/python/python3.vcxproj.filters
-@@ -9,13 +9,26 @@
- </Filter>
- </ItemGroup>
- <ItemGroup>
-- <ClCompile Include="python.c">
-- <Filter>Source Files</Filter>
-- </ClCompile>
-+ <ClCompile Include="$(IntDir)python.c" />
- </ItemGroup>
- <ItemGroup>
- <None Include="python.def">
- <Filter>Resource Files</Filter>
- </None>
-+ <None Include="_hexchat.py">
-+ <Filter>Source Files</Filter>
-+ </None>
-+ <None Include="generate_plugin.py">
-+ <Filter>Source Files</Filter>
-+ </None>
-+ <None Include="hexchat.py">
-+ <Filter>Source Files</Filter>
-+ </None>
-+ <None Include="python.py">
-+ <Filter>Source Files</Filter>
-+ </None>
-+ <None Include="xchat.py">
-+ <Filter>Source Files</Filter>
-+ </None>
- </ItemGroup>
- </Project>
-\ No newline at end of file
-diff --git a/plugins/python/python_style_guide.md b/plugins/python/python_style_guide.md
-new file mode 100644
-index 00000000..41db2474
---- /dev/null
-+++ b/plugins/python/python_style_guide.md
-@@ -0,0 +1,26 @@
-+# HexChat Python Module Style Guide
-+
-+(This is a work in progress).
-+
-+## General rules
-+
-+- PEP8 as general fallback recommendations
-+- Max line length: 120
-+- Avoid overcomplex compound statements. i.e. dont do this: `somevar = x if x == y else z if a == b and c == b else x`
-+
-+## Indentation style
-+
-+### Multi-line functions
-+
-+```python
-+foo(really_long_arg_1,
-+ really_long_arg_2)
-+```
-+
-+### Mutli-line lists/dicts
-+
-+```python
-+foo = {
-+ 'bar': 'baz',
-+}
-+```
-diff --git a/plugins/python/xchat.py b/plugins/python/xchat.py
-new file mode 100644
-index 00000000..6922490b
---- /dev/null
-+++ b/plugins/python/xchat.py
-@@ -0,0 +1 @@
-+from _hexchat import *
-diff --git a/src/common/meson.build b/src/common/meson.build
-index bbb64645..492227b2 100644
---- a/src/common/meson.build
-+++ b/src/common/meson.build
-@@ -93,10 +93,6 @@ endif
-
- if get_option('with-plugin')
- common_deps += libgmodule_dep
-- common_cflags += '-DHEXCHATLIBDIR="@0@"'.format(join_paths(get_option('prefix'),
-- get_option('libdir'),
-- 'hexchat/plugins'))
--
- install_headers('hexchat-plugin.h')
- endif
-
-diff --git a/win32/copy/copy.vcxproj b/win32/copy/copy.vcxproj
-index c508a7f3..72f2c032 100644
---- a/win32/copy/copy.vcxproj
-+++ b/win32/copy/copy.vcxproj
-@@ -64,6 +64,8 @@
- <LuaShare Include="$(DepsRoot)\share\lua\**\*.lua" />
- <LuaShare Include="$(DepsRoot)\share\lua\**\**\*.lua" />
- <Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" />
-+ <None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" />
-+ <None Include="$(Python2Path)\Lib\site-packages\_cffi_backend.pyd" />
-
- <Engines Include="$(DepsRoot)\lib\gtk-2.0\i686-pc-vs14\engines\**\*" />
-
-@@ -91,6 +93,9 @@
- <Copy SourceFiles="@(LuaShare)" DestinationFiles="@(LuaShare->'$(HexChatRel)\share\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
- <Copy SourceFiles="@(LuaLib)" DestinationFiles="@(LuaLib->'$(HexChatRel)\lib\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
- <Copy SourceFiles="@(Typelib)" DestinationFiles="@(Typelib->'$(HexChatRel)\lib\girepository-1.0\%(Filename)%(Extension)')" />
-+ <Copy SourceFiles="..\..\plugins\python\xchat.py" DestinationFolder="$(HexChatRel)\python" />
-+ <Copy SourceFiles="..\..\plugins\python\hexchat.py" DestinationFolder="$(HexChatRel)\python" />
-+ <Copy SourceFiles="..\..\plugins\python\_hexchat.py" DestinationFolder="$(HexChatRel)\python" />
-
- <WriteLinesToFile File="$(HexChatRel)portable-mode" Lines="2" Overwrite="true" />
-
-diff --git a/win32/installer/hexchat.iss.tt b/win32/installer/hexchat.iss.tt
-index e242ee96..3ac5ec41 100644
---- a/win32/installer/hexchat.iss.tt
-+++ b/win32/installer/hexchat.iss.tt
-@@ -164,6 +164,7 @@ Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0";
- Source: "share\lua\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua
- Source: "share\lua\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua
-+Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
-
- Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum
- Source: "plugins\hcexec.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\exec
-@@ -175,11 +176,15 @@ Source: "WinSparkle.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pl
- Source: "plugins\hcwinamp.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\winamp
- Source: "share\system.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\sysinfo
- Source: "plugins\hcsysinfo.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\sysinfo
-+Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
-+
-+Source: "python\*.py"; DestDir: "{app}\python"; Flags: ignoreversion; Components: langs\python
-
- Source: "plugins\hcpython2.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python2
-+Source: "_cffi_backend.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python2
-+
- Source: "plugins\hcpython3.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python3
--Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
--Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
-+Source: "_cffi_backend.cp3*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python3
-
- Source: "hexchat.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
- Source: "hexchat-text.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xctext
diff --git a/hexchat/0002-python-3-8.patch b/hexchat/0002-python-3-8.patch
deleted file mode 100644
index dbfd7f179..000000000
--- a/hexchat/0002-python-3-8.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/meson_options.txt b/meson_options.txt
-index 100a5ee7..c2a2f53c 100644
---- a/meson_options.txt
-+++ b/meson_options.txt
-@@ -48,7 +48,7 @@ option('with-lua', type: 'string', value: 'luajit',
- option('with-perl', type: 'string', value: 'perl',
- description: 'Perl scripting plugin, value is path to perl executable or "false"'
- )
--option('with-python', type: 'string', value: 'python3',
-+option('with-python', type: 'string', value: 'python3-embed',
- description: 'Python scripting plugin. value is pkg-config name to use or "false"'
- )
- option('with-sysinfo', type: 'boolean',
diff --git a/hexchat/0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch b/hexchat/0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
deleted file mode 100644
index 35434eef5..000000000
--- a/hexchat/0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-commit 163608d7fd861c2c4911a38f45be484c88626bdc
-Author: John Levon <levon@movementarian.org>
-Date: Mon Sep 7 17:53:31 2020 +0100
-
- Use pango_font_metrics_get_height() to calculate font height (#2500)
-
-diff --git a/src/fe-gtk/xtext.c b/src/fe-gtk/xtext.c
-index fac0c4e6..418bb4da 100644
---- a/src/fe-gtk/xtext.c
-+++ b/src/fe-gtk/xtext.c
-@@ -283,8 +283,24 @@ backend_font_open (GtkXText *xtext, char *name)
- metrics = pango_context_get_metrics (context, xtext->font->font, lang);
- xtext->font->ascent = pango_font_metrics_get_ascent (metrics) / PANGO_SCALE;
- xtext->font->descent = pango_font_metrics_get_descent (metrics) / PANGO_SCALE;
-+
-+ /*
-+ * In later versions of pango, a font's height should be calculated like
-+ * this to account for line gap; a typical symptom of not doing so is
-+ * cutting off the underscore on some fonts.
-+ */
-+#if PANGO_VERSION_CHECK(1, 44, 0)
-+ xtext->fontsize = pango_font_metrics_get_height (metrics) / PANGO_SCALE + 1;
-+
-+ if (xtext->fontsize == 0)
-+ xtext->fontsize = xtext->font->ascent + xtext->font->descent;
-+#else
-+ xtext->fontsize = xtext->font->ascent + xtext->font->descent;
-+#endif
-+
- pango_font_metrics_unref (metrics);
- }
-+
- static int
- backend_get_text_width_emph (GtkXText *xtext, guchar *str, int len, int emphasis)
- {
-@@ -3479,8 +3495,6 @@ gtk_xtext_set_font (GtkXText *xtext, char *name)
- if (xtext->font == NULL)
- return FALSE;
-
-- xtext->fontsize = xtext->font->ascent + xtext->font->descent;
--
- {
- char *time_str;
- int stamp_size = xtext_get_stamp_str (time(0), &time_str);
diff --git a/hexchat/0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch b/hexchat/0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch
deleted file mode 100644
index 67c2e1e7f..000000000
--- a/hexchat/0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From d6319bd8f21042db62f6c7094066052d894416ef Mon Sep 17 00:00:00 2001
-From: Mateusz Gozdek <mgozdekof@gmail.com>
-Date: Sun, 4 Apr 2021 21:07:30 +0200
-Subject: [PATCH] plugins/lua/lua.c: fix segfault on lua_pop with Lua 5.4.3
-
-Closes #2558
-
-Co-authored-by: "Jan Alexander Steffens (heftig)" <jan.steffens@gmail.com>
-Signed-off-by: Mateusz Gozdek <mgozdekof@gmail.com>
----
- plugins/lua/lua.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/plugins/lua/lua.c b/plugins/lua/lua.c
-index d73fbb230..d1370eafb 100644
---- a/plugins/lua/lua.c
-+++ b/plugins/lua/lua.c
-@@ -1189,11 +1189,11 @@ static void patch_clibs(lua_State *L)
- if(lua_type(L, -2) == LUA_TLIGHTUSERDATA && lua_type(L, -1) == LUA_TTABLE)
- {
- lua_setfield(L, LUA_REGISTRYINDEX, "_CLIBS");
-+ lua_pop(L, 1);
- break;
- }
- lua_pop(L, 1);
- }
-- lua_pop(L, 1);
- }
-
- static GPtrArray *scripts;
diff --git a/hexchat/Pkgfile b/hexchat/Pkgfile
index 58a72070f..1cd29384b 100644
--- a/hexchat/Pkgfile
+++ b/hexchat/Pkgfile
@@ -5,28 +5,16 @@
# Optional: dbus-glib desktop-file-utils enchant libcanberra libnotify libproxy lua pciutils
name=hexchat
-version=2.14.3
-release=2
-source=(https://dl.hexchat.net/hexchat/$name-$version.tar.xz
- 0001-python-cffi.patch
- 0002-python-3-8.patch
- 0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
- 0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch)
+version=2.16.0
+release=1
+source=(https://dl.hexchat.net/hexchat/$name-$version.tar.xz)
build() {
cd $name-$version
- patch -p1 -i $SRC/0001-python-cffi.patch
- patch -p1 -i $SRC/0002-python-3-8.patch
- patch -p1 -i $SRC/0003-Use-pango_font_metrics_get_height-to-calculate-font-height.patch
- patch -p1 -i $SRC/0004-fix_segfault_on_lua_pop_with_Lua_5.4.3.patch
-
# Lua scripting plugin, value is pkg-config name
prt-get isinst lua && PKGMK_HEXCHAT+=' -Dwith-lua=lua' || PKGMK_HEXCHAT+=' -Dwith-lua=false'
- # Support for getting proxy information
- prt-get isinst libproxy || PKGMK_HEXCHAT+=' -Dwith-libproxy=false'
-
# Support for freedesktop notifications
prt-get isinst libnotify || PKGMK_HEXCHAT+=' -Dwith-libnotify=false'
@@ -39,8 +27,7 @@ build() {
prt-get isinst pciutils && PKGMK_HEXCHAT+=' -Dwith-sysinfo=true'
meson build ${PKGMK_HEXCHAT} \
- --prefix /usr \
- -Dwith-text=true
+ --prefix /usr
ninja -C build -j ${JOBS:-1}
DESTDIR="$PKG" ninja -C build install

Generated by cgit