summaryrefslogtreecommitdiff
path: root/hexchat/0001-python-cffi.patch
blob: a4f4616171c032284c4a85f25f0bf70680d0cccf (plain)
    1 diff --git a/.travis.yml b/.travis.yml
    2 index df0c7e1f..9e226f0c 100644
    3 --- a/.travis.yml
    4 +++ b/.travis.yml
    5 @@ -2,7 +2,7 @@ sudo: required
    6  services: docker
    7  before_install:
    8      - docker pull ubuntu:16.04
    9 -    - 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'
   10 +    - 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'
   11      - docker commit `cat /tmp/cid` hexchat/ubuntu-ci
   12      - rm -f /tmp/cid
   13  install:
   14 diff --git a/meson.build b/meson.build
   15 index 18baf26e..645e685e 100644
   16 --- a/meson.build
   17 +++ b/meson.build
   18 @@ -49,6 +49,10 @@ config_h.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_34')
   19  config_h.set('HAVE_MEMRCHR', cc.has_function('memrchr'))
   20  config_h.set('HAVE_STRINGS_H', cc.has_header('strings.h'))
   21  
   22 +config_h.set_quoted('HEXCHATLIBDIR',
   23 +  join_paths(get_option('prefix'), get_option('libdir'), 'hexchat/plugins')
   24 +)
   25 +
   26  if libssl_dep.found()
   27    config_h.set('HAVE_X509_GET_SIGNATURE_NID',
   28      cc.has_function('X509_get_signature_nid', dependencies: libssl_dep)
   29 diff --git a/plugins/python/_hexchat.py b/plugins/python/_hexchat.py
   30 new file mode 100644
   31 index 00000000..567b3493
   32 --- /dev/null
   33 +++ b/plugins/python/_hexchat.py
   34 @@ -0,0 +1,386 @@
   35 +import inspect
   36 +import sys
   37 +from contextlib import contextmanager
   38 +
   39 +from _hexchat_embedded import ffi, lib
   40 +
   41 +__all__ = [
   42 +    'EAT_ALL', 'EAT_HEXCHAT', 'EAT_NONE', 'EAT_PLUGIN', 'EAT_XCHAT',
   43 +    'PRI_HIGH', 'PRI_HIGHEST', 'PRI_LOW', 'PRI_LOWEST', 'PRI_NORM',
   44 +    '__doc__', '__version__', 'command', 'del_pluginpref', 'emit_print',
   45 +    'find_context', 'get_context', 'get_info',
   46 +    'get_list', 'get_lists', 'get_pluginpref', 'get_prefs', 'hook_command',
   47 +    'hook_print', 'hook_print_attrs', 'hook_server', 'hook_server_attrs',
   48 +    'hook_timer', 'hook_unload', 'list_pluginpref', 'nickcmp', 'prnt',
   49 +    'set_pluginpref', 'strip', 'unhook',
   50 +]
   51 +
   52 +__doc__ = 'HexChat Scripting Interface'
   53 +__version__ = (2, 0)
   54 +__license__ = 'GPL-2.0+'
   55 +
   56 +EAT_NONE = 0
   57 +EAT_HEXCHAT = 1
   58 +EAT_XCHAT = EAT_HEXCHAT
   59 +EAT_PLUGIN = 2
   60 +EAT_ALL = EAT_HEXCHAT | EAT_PLUGIN
   61 +
   62 +PRI_LOWEST = -128
   63 +PRI_LOW = -64
   64 +PRI_NORM = 0
   65 +PRI_HIGH = 64
   66 +PRI_HIGHEST = 127
   67 +
   68 +
   69 +# We need each module to be able to reference their parent plugin
   70 +# which is a bit tricky since they all share the exact same module.
   71 +# Simply navigating up to what module called it seems to actually
   72 +# be a fairly reliable and simple method of doing so if ugly.
   73 +def __get_current_plugin():
   74 +    frame = inspect.stack()[1][0]
   75 +    while '__plugin' not in frame.f_globals:
   76 +        frame = frame.f_back
   77 +        assert frame is not None
   78 +
   79 +    return frame.f_globals['__plugin']
   80 +
   81 +
   82 +# Keeping API compat
   83 +if sys.version_info[0] == 2:
   84 +    def __decode(string):
   85 +        return string
   86 +
   87 +else:
   88 +    def __decode(string):
   89 +        return string.decode()
   90 +
   91 +
   92 +# ------------ API ------------
   93 +def prnt(string):
   94 +    lib.hexchat_print(lib.ph, string.encode())
   95 +
   96 +
   97 +def emit_print(event_name, *args, **kwargs):
   98 +    time = kwargs.pop('time', 0)  # For py2 compat
   99 +    cargs = []
  100 +    for i in range(4):
  101 +        arg = args[i].encode() if len(args) > i else b''
  102 +        cstring = ffi.new('char[]', arg)
  103 +        cargs.append(cstring)
  104 +
  105 +    if time == 0:
  106 +        return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
  107 +
  108 +    attrs = lib.hexchat_event_attrs_create(lib.ph)
  109 +    attrs.server_time_utc = time
  110 +    ret = lib.hexchat_emit_print_attrs(lib.ph, attrs, event_name.encode(), *cargs)
  111 +    lib.hexchat_event_attrs_free(lib.ph, attrs)
  112 +    return ret
  113 +
  114 +
  115 +# TODO: this shadows itself. command should be changed to cmd
  116 +def command(command):
  117 +    lib.hexchat_command(lib.ph, command.encode())
  118 +
  119 +
  120 +def nickcmp(string1, string2):
  121 +    return lib.hexchat_nickcmp(lib.ph, string1.encode(), string2.encode())
  122 +
  123 +
  124 +def strip(text, length=-1, flags=3):
  125 +    stripped = lib.hexchat_strip(lib.ph, text.encode(), length, flags)
  126 +    ret = __decode(ffi.string(stripped))
  127 +    lib.hexchat_free(lib.ph, stripped)
  128 +    return ret
  129 +
  130 +
  131 +def get_info(name):
  132 +    ret = lib.hexchat_get_info(lib.ph, name.encode())
  133 +    if ret == ffi.NULL:
  134 +        return None
  135 +    if name in ('gtkwin_ptr', 'win_ptr'):
  136 +        # Surely there is a less dumb way?
  137 +        ptr = repr(ret).rsplit(' ', 1)[1][:-1]
  138 +        return ptr
  139 +
  140 +    return __decode(ffi.string(ret))
  141 +
  142 +
  143 +def get_prefs(name):
  144 +    string_out = ffi.new('char**')
  145 +    int_out = ffi.new('int*')
  146 +    _type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
  147 +    if _type == 0:
  148 +        return None
  149 +
  150 +    if _type == 1:
  151 +        return __decode(ffi.string(string_out[0]))
  152 +
  153 +    if _type in (2, 3):  # XXX: 3 should be a bool, but keeps API
  154 +        return int_out[0]
  155 +
  156 +    raise AssertionError('Out of bounds pref storage')
  157 +
  158 +
  159 +def __cstrarray_to_list(arr):
  160 +    i = 0
  161 +    ret = []
  162 +    while arr[i] != ffi.NULL:
  163 +        ret.append(ffi.string(arr[i]))
  164 +        i += 1
  165 +
  166 +    return ret
  167 +
  168 +
  169 +__FIELD_CACHE = {}
  170 +
  171 +
  172 +def __get_fields(name):
  173 +    return __FIELD_CACHE.setdefault(name, __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
  174 +
  175 +
  176 +__FIELD_PROPERTY_CACHE = {}
  177 +
  178 +
  179 +def __cached_decoded_str(string):
  180 +    return __FIELD_PROPERTY_CACHE.setdefault(string, __decode(string))
  181 +
  182 +
  183 +def get_lists():
  184 +    return [__cached_decoded_str(field) for field in __get_fields(b'lists')]
  185 +
  186 +
  187 +class ListItem:
  188 +    def __init__(self, name):
  189 +        self._listname = name
  190 +
  191 +    def __repr__(self):
  192 +        return '<{} list item at {}>'.format(self._listname, id(self))
  193 +
  194 +
  195 +# done this way for speed
  196 +if sys.version_info[0] == 2:
  197 +    def get_getter(name):
  198 +        return ord(name[0])
  199 +
  200 +else:
  201 +    def get_getter(name):
  202 +        return name[0]
  203 +
  204 +
  205 +def get_list(name):
  206 +    # XXX: This function is extremely inefficient and could be interators and
  207 +    # lazily loaded properties, but for API compat we stay slow
  208 +    orig_name = name
  209 +    name = name.encode()
  210 +
  211 +    if name not in __get_fields(b'lists'):
  212 +        raise KeyError('list not available')
  213 +
  214 +    list_ = lib.hexchat_list_get(lib.ph, name)
  215 +    if list_ == ffi.NULL:
  216 +        return None
  217 +
  218 +    ret = []
  219 +    fields = __get_fields(name)
  220 +
  221 +    def string_getter(field):
  222 +        string = lib.hexchat_list_str(lib.ph, list_, field)
  223 +        if string != ffi.NULL:
  224 +            return __decode(ffi.string(string))
  225 +
  226 +        return ''
  227 +
  228 +    def ptr_getter(field):
  229 +        if field == b'context':
  230 +            ptr = lib.hexchat_list_str(lib.ph, list_, field)
  231 +            ctx = ffi.cast('hexchat_context*', ptr)
  232 +            return Context(ctx)
  233 +
  234 +        return None
  235 +
  236 +    getters = {
  237 +        ord('s'): string_getter,
  238 +        ord('i'): lambda field: lib.hexchat_list_int(lib.ph, list_, field),
  239 +        ord('t'): lambda field: lib.hexchat_list_time(lib.ph, list_, field),
  240 +        ord('p'): ptr_getter,
  241 +    }
  242 +
  243 +    while lib.hexchat_list_next(lib.ph, list_) == 1:
  244 +        item = ListItem(orig_name)
  245 +        for _field in fields:
  246 +            getter = getters.get(get_getter(_field))
  247 +            if getter is not None:
  248 +                field_name = _field[1:]
  249 +                setattr(item, __cached_decoded_str(field_name), getter(field_name))
  250 +
  251 +        ret.append(item)
  252 +
  253 +    lib.hexchat_list_free(lib.ph, list_)
  254 +    return ret
  255 +
  256 +
  257 +# TODO: 'command' here shadows command above, and should be renamed to cmd
  258 +def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
  259 +    plugin = __get_current_plugin()
  260 +    hook = plugin.add_hook(callback, userdata)
  261 +    handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
  262 +                                      help.encode() if help is not None else ffi.NULL, hook.handle)
  263 +
  264 +    hook.hexchat_hook = handle
  265 +    return id(hook)
  266 +
  267 +
  268 +def hook_print(name, callback, userdata=None, priority=PRI_NORM):
  269 +    plugin = __get_current_plugin()
  270 +    hook = plugin.add_hook(callback, userdata)
  271 +    handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook, hook.handle)
  272 +    hook.hexchat_hook = handle
  273 +    return id(hook)
  274 +
  275 +
  276 +def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
  277 +    plugin = __get_current_plugin()
  278 +    hook = plugin.add_hook(callback, userdata)
  279 +    handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook, hook.handle)
  280 +    hook.hexchat_hook = handle
  281 +    return id(hook)
  282 +
  283 +
  284 +def hook_server(name, callback, userdata=None, priority=PRI_NORM):
  285 +    plugin = __get_current_plugin()
  286 +    hook = plugin.add_hook(callback, userdata)
  287 +    handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook, hook.handle)
  288 +    hook.hexchat_hook = handle
  289 +    return id(hook)
  290 +
  291 +
  292 +def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
  293 +    plugin = __get_current_plugin()
  294 +    hook = plugin.add_hook(callback, userdata)
  295 +    handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook, hook.handle)
  296 +    hook.hexchat_hook = handle
  297 +    return id(hook)
  298 +
  299 +
  300 +def hook_timer(timeout, callback, userdata=None):
  301 +    plugin = __get_current_plugin()
  302 +    hook = plugin.add_hook(callback, userdata)
  303 +    handle = lib.hexchat_hook_timer(lib.ph, timeout, lib._on_timer_hook, hook.handle)
  304 +    hook.hexchat_hook = handle
  305 +    return id(hook)
  306 +
  307 +
  308 +def hook_unload(callback, userdata=None):
  309 +    plugin = __get_current_plugin()
  310 +    hook = plugin.add_hook(callback, userdata, is_unload=True)
  311 +    return id(hook)
  312 +
  313 +
  314 +def unhook(handle):
  315 +    plugin = __get_current_plugin()
  316 +    return plugin.remove_hook(handle)
  317 +
  318 +
  319 +def set_pluginpref(name, value):
  320 +    if isinstance(value, str):
  321 +        return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
  322 +
  323 +    if isinstance(value, int):
  324 +        return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
  325 +
  326 +    # XXX: This should probably raise but this keeps API
  327 +    return False
  328 +
  329 +
  330 +def get_pluginpref(name):
  331 +    name = name.encode()
  332 +    string_out = ffi.new('char[512]')
  333 +    if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) != 1:
  334 +        return None
  335 +
  336 +    string = ffi.string(string_out)
  337 +    # This API stores everything as a string so we have to figure out what
  338 +    # its actual type was supposed to be.
  339 +    if len(string) > 12:  # Can't be a number
  340 +        return __decode(string)
  341 +
  342 +    number = lib.hexchat_pluginpref_get_int(lib.ph, name)
  343 +    if number == -1 and string != b'-1':
  344 +        return __decode(string)
  345 +
  346 +    return number
  347 +
  348 +
  349 +def del_pluginpref(name):
  350 +    return bool(lib.hexchat_pluginpref_delete(lib.ph, name.encode()))
  351 +
  352 +
  353 +def list_pluginpref():
  354 +    prefs_str = ffi.new('char[4096]')
  355 +    if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
  356 +        return __decode(prefs_str).split(',')
  357 +
  358 +    return []
  359 +
  360 +
  361 +class Context:
  362 +    def __init__(self, ctx):
  363 +        self._ctx = ctx
  364 +
  365 +    def __eq__(self, value):
  366 +        if not isinstance(value, Context):
  367 +            return False
  368 +
  369 +        return self._ctx == value._ctx
  370 +
  371 +    @contextmanager
  372 +    def __change_context(self):
  373 +        old_ctx = lib.hexchat_get_context(lib.ph)
  374 +        if not self.set():
  375 +            # XXX: Behavior change, previously used wrong context
  376 +            lib.hexchat_print(lib.ph, b'Context object refers to closed context, ignoring call')
  377 +            return
  378 +
  379 +        yield
  380 +        lib.hexchat_set_context(lib.ph, old_ctx)
  381 +
  382 +    def set(self):
  383 +        # XXX: API addition, C plugin silently ignored failure
  384 +        return bool(lib.hexchat_set_context(lib.ph, self._ctx))
  385 +
  386 +    def prnt(self, string):
  387 +        with self.__change_context():
  388 +            prnt(string)
  389 +
  390 +    def emit_print(self, event_name, *args, **kwargs):
  391 +        time = kwargs.pop('time', 0)  # For py2 compat
  392 +        with self.__change_context():
  393 +            return emit_print(event_name, *args, time=time)
  394 +
  395 +    def command(self, string):
  396 +        with self.__change_context():
  397 +            command(string)
  398 +
  399 +    def get_info(self, name):
  400 +        with self.__change_context():
  401 +            return get_info(name)
  402 +
  403 +    def get_list(self, name):
  404 +        with self.__change_context():
  405 +            return get_list(name)
  406 +
  407 +
  408 +def get_context():
  409 +    ctx = lib.hexchat_get_context(lib.ph)
  410 +    return Context(ctx)
  411 +
  412 +
  413 +def find_context(server=None, channel=None):
  414 +    server = server.encode() if server is not None else ffi.NULL
  415 +    channel = channel.encode() if channel is not None else ffi.NULL
  416 +    ctx = lib.hexchat_find_context(lib.ph, server, channel)
  417 +    if ctx == ffi.NULL:
  418 +        return None
  419 +
  420 +    return Context(ctx)
  421 diff --git a/plugins/python/generate_plugin.py b/plugins/python/generate_plugin.py
  422 new file mode 100755
  423 index 00000000..5c52b37b
  424 --- /dev/null
  425 +++ b/plugins/python/generate_plugin.py
  426 @@ -0,0 +1,89 @@
  427 +#!/usr/bin/env python3
  428 +
  429 +import sys
  430 +import cffi
  431 +
  432 +builder = cffi.FFI()
  433 +
  434 +# hexchat-plugin.h
  435 +with open(sys.argv[1]) as f:
  436 +    output = []
  437 +    eat_until_endif = 0
  438 +    # This is very specific to hexchat-plugin.h, it is not a cpp
  439 +    for line in f:
  440 +        if line.startswith('#define'):
  441 +            continue
  442 +        elif line.endswith('HEXCHAT_PLUGIN_H\n'):
  443 +            continue
  444 +        elif 'time.h' in line:
  445 +            output.append('typedef int... time_t;')
  446 +        elif line.startswith('#if'):
  447 +            eat_until_endif += 1
  448 +        elif line.startswith('#endif'):
  449 +            eat_until_endif -= 1
  450 +        elif eat_until_endif and '_hexchat_context' not in line:
  451 +            continue
  452 +        else:
  453 +            output.append(line)
  454 +    builder.cdef(''.join(output))
  455 +
  456 +builder.embedding_api('''
  457 +extern "Python" int _on_py_command(char **, char **, void *);
  458 +extern "Python" int _on_load_command(char **, char **, void *);
  459 +extern "Python" int _on_unload_command(char **, char **, void *);
  460 +extern "Python" int _on_reload_command(char **, char **, void *);
  461 +extern "Python" int _on_say_command(char **, char **, void *);
  462 +
  463 +extern "Python" int _on_command_hook(char **, char **, void *);
  464 +extern "Python" int _on_print_hook(char **, void *);
  465 +extern "Python" int _on_print_attrs_hook(char **, hexchat_event_attrs *, void *);
  466 +extern "Python" int _on_server_hook(char **, char **, void *);
  467 +extern "Python" int _on_server_attrs_hook(char **, char **, hexchat_event_attrs *, void *);
  468 +extern "Python" int _on_timer_hook(void *);
  469 +
  470 +extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
  471 +extern "Python" int _on_plugin_deinit(void);
  472 +
  473 +static hexchat_plugin *ph;
  474 +''')
  475 +
  476 +builder.set_source('_hexchat_embedded', '''
  477 +/* Python's header defines these.. */
  478 +#undef HAVE_MEMRCHR
  479 +#undef HAVE_STRINGS_H
  480 +
  481 +#include "config.h"
  482 +#include "hexchat-plugin.h"
  483 +
  484 +static hexchat_plugin *ph;
  485 +CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
  486 +CFFI_DLLEXPORT int _on_plugin_deinit(void);
  487 +
  488 +int hexchat_plugin_init(hexchat_plugin *plugin_handle,
  489 +                        char **name_out, char **description_out,
  490 +                        char **version_out, char *arg)
  491 +{
  492 +    if (ph != NULL)
  493 +    {
  494 +        puts ("Python plugin already loaded\\n");
  495 +        return 0; /* Prevent loading twice */
  496 +    }
  497 +
  498 +    ph = plugin_handle;
  499 +    return _on_plugin_init(name_out, description_out, version_out, arg, HEXCHATLIBDIR);
  500 +}
  501 +
  502 +int hexchat_plugin_deinit(void)
  503 +{
  504 +    int ret = _on_plugin_deinit();
  505 +    ph = NULL;
  506 +    return ret;
  507 +}
  508 +''')
  509 +
  510 +# python.py
  511 +with open(sys.argv[2]) as f:
  512 +    builder.embedding_init_code(f.read())
  513 +
  514 +# python.c
  515 +builder.emit_c_code(sys.argv[3])
  516 diff --git a/plugins/python/hexchat.py b/plugins/python/hexchat.py
  517 new file mode 100644
  518 index 00000000..6922490b
  519 --- /dev/null
  520 +++ b/plugins/python/hexchat.py
  521 @@ -0,0 +1 @@
  522 +from _hexchat import *
  523 diff --git a/plugins/python/meson.build b/plugins/python/meson.build
  524 index e24f0c6f..5fd7ec2f 100644
  525 --- a/plugins/python/meson.build
  526 +++ b/plugins/python/meson.build
  527 @@ -1,12 +1,30 @@
  528  python_opt = get_option('with-python')
  529  if python_opt.startswith('python3')
  530 -  python_dep = dependency(python_opt, version: '>= 3.3')
  531 +  # Python 3.8 introduced a new -embed variant
  532 +  if not python_opt.endswith('-embed')
  533 +    python_dep = dependency(python_opt + '-embed', version: '>= 3.3', required: false)
  534 +    if not python_dep.found()
  535 +      python_dep = dependency(python_opt, version: '>= 3.3')
  536 +    endif
  537 +  else
  538 +    python_dep = dependency(python_opt, version: '>= 3.3')
  539 +  endif
  540  else
  541    python_dep = dependency(python_opt, version: '>= 2.7')
  542  endif
  543  
  544 -shared_module('python', 'python.c',
  545 -  dependencies: [libgio_dep, hexchat_plugin_dep, python_dep],
  546 +python3_source = custom_target('python-bindings',
  547 +  input: ['../../src/common/hexchat-plugin.h', 'python.py'],
  548 +  output: 'python.c',
  549 +  command: [find_program('generate_plugin.py'), '@INPUT@', '@OUTPUT@']
  550 +)
  551 +
  552 +install_data(['_hexchat.py', 'hexchat.py', 'xchat.py'],
  553 +  install_dir: join_paths(get_option('libdir'), 'hexchat/python')
  554 +)
  555 +
  556 +shared_module('python', python3_source,
  557 +  dependencies: [hexchat_plugin_dep, python_dep],
  558    install: true,
  559    install_dir: plugindir,
  560    name_prefix: '',
  561 diff --git a/plugins/python/python.c b/plugins/python/python.c
  562 deleted file mode 100644
  563 index 475756ba..00000000
  564 --- a/plugins/python/python.c
  565 +++ /dev/null
  566 @@ -1,2837 +0,0 @@
  567 -/*
  568 -* Copyright (c) 2002-2003  Gustavo Niemeyer <niemeyer@conectiva.com>
  569 -*
  570 -* XChat Python Plugin Interface
  571 -*
  572 -* Xchat Python Plugin Interface is free software; you can redistribute
  573 -* it and/or modify it under the terms of the GNU General Public License
  574 -* as published by the Free Software Foundation; either version 2 of the
  575 -* License, or (at your option) any later version.
  576 -*
  577 -* pybot is distributed in the hope that it will be useful,
  578 -* but WITHOUT ANY WARRANTY; without even the implied warranty of
  579 -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  580 -* GNU General Public License for more details.
  581 -*
  582 -* You should have received a copy of the GNU General Public License
  583 -* along with this file; if not, write to the Free Software
  584 -* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  585 -*/
  586 -
  587 -/* Thread support
  588 - * ==============
  589 - *
  590 - * The python interpreter has a global interpreter lock. Any thread
  591 - * executing must acquire it before working with data accessible from
  592 - * python code. Here we must also care about xchat not being
  593 - * thread-safe. We do this by using an xchat lock, which protects
  594 - * xchat instructions from being executed out of time (when this
  595 - * plugin is not "active").
  596 - *
  597 - * When xchat calls python code:
  598 - *   - Change the current_plugin for the executing plugin;
  599 - *   - Release xchat lock
  600 - *   - Acquire the global interpreter lock
  601 - *   - Make the python call
  602 - *   - Release the global interpreter lock
  603 - *   - Acquire xchat lock
  604 - *
  605 - * When python code calls xchat:
  606 - *   - Release the global interpreter lock
  607 - *   - Acquire xchat lock
  608 - *   - Restore context, if necessary
  609 - *   - Make the xchat call
  610 - *   - Release xchat lock
  611 - *   - Acquire the global interpreter lock
  612 - *
  613 - * Inside a timer, so that individual threads have a chance to run:
  614 - *   - Release xchat lock
  615 - *   - Go ahead threads. Have a nice time!
  616 - *   - Acquire xchat lock
  617 - *
  618 - */
  619 -
  620 -#include "config.h"
  621 -
  622 -#include <glib.h>
  623 -#include <glib/gstdio.h>
  624 -#include <string.h>
  625 -#include <stdlib.h>
  626 -#include <sys/types.h>
  627 -
  628 -#ifdef WIN32
  629 -#include <direct.h>
  630 -#else
  631 -#include <unistd.h>
  632 -#include <dirent.h>
  633 -#endif
  634 -
  635 -#include "hexchat-plugin.h"
  636 -#undef _POSIX_C_SOURCE	/* Avoid warnings from /usr/include/features.h */
  637 -#undef _XOPEN_SOURCE
  638 -#undef HAVE_MEMRCHR /* Avoid redefinition in Python.h */
  639 -#undef HAVE_STRINGS_H
  640 -#include <Python.h>
  641 -#include <structmember.h>
  642 -#include <pythread.h>
  643 -
  644 -/* Macros to convert version macros into string literals.
  645 - * 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.
  646 - * If STRINGIZE were to be directly defined as #X instead, VERSION would be "VERSION_MAJOR" instead of "1".
  647 - */
  648 -#define STRINGIZE2(X) #X
  649 -#define STRINGIZE(X) STRINGIZE2(X)
  650 -
  651 -/* Version number macros */
  652 -#define VERSION_MAJOR 1
  653 -#define VERSION_MINOR 0
  654 -
  655 -/* Version string macro e.g 1.0/3.3 */
  656 -#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/" \
  657 -				STRINGIZE(PY_MAJOR_VERSION) "." STRINGIZE (PY_MINOR_VERSION)
  658 -
  659 -/* #define's for Python 2 */
  660 -#if PY_MAJOR_VERSION == 2
  661 -#undef PyLong_Check
  662 -#define PyLong_Check PyInt_Check
  663 -#define PyLong_AsLong PyInt_AsLong
  664 -#define PyLong_FromLong PyInt_FromLong
  665 -
  666 -#undef PyUnicode_Check
  667 -#undef PyUnicode_FromString
  668 -#undef PyUnicode_FromFormat
  669 -#define PyUnicode_Check PyString_Check
  670 -#define PyUnicode_AsFormat PyString_AsFormat
  671 -#define PyUnicode_FromFormat PyString_FromFormat
  672 -#define PyUnicode_FromString PyString_FromString
  673 -#define PyUnicode_AsUTF8 PyString_AsString
  674 -
  675 -#ifdef WIN32
  676 -#undef WITH_THREAD
  677 -#endif
  678 -#endif
  679 -
  680 -/* #define for Python 3 */
  681 -#if PY_MAJOR_VERSION == 3
  682 -#define IS_PY3K
  683 -#endif
  684 -
  685 -#define NONE 0
  686 -#define ALLOW_THREADS 1
  687 -#define RESTORE_CONTEXT 2
  688 -
  689 -#ifdef WITH_THREAD
  690 -#define ACQUIRE_XCHAT_LOCK() PyThread_acquire_lock(xchat_lock, 1)
  691 -#define RELEASE_XCHAT_LOCK() PyThread_release_lock(xchat_lock)
  692 -#define BEGIN_XCHAT_CALLS(x) \
  693 -	do { \
  694 -		PyObject *calls_plugin = NULL; \
  695 -		PyThreadState *calls_thread; \
  696 -		if ((x) & RESTORE_CONTEXT) \
  697 -			calls_plugin = Plugin_GetCurrent(); \
  698 -		calls_thread = PyEval_SaveThread(); \
  699 -		ACQUIRE_XCHAT_LOCK(); \
  700 -		if (!((x) & ALLOW_THREADS)) { \
  701 -			PyEval_RestoreThread(calls_thread); \
  702 -			calls_thread = NULL; \
  703 -		} \
  704 -		if (calls_plugin) \
  705 -			hexchat_set_context(ph, \
  706 -				Plugin_GetContext(calls_plugin)); \
  707 -		while (0)
  708 -#define END_XCHAT_CALLS() \
  709 -		RELEASE_XCHAT_LOCK(); \
  710 -		if (calls_thread) \
  711 -			PyEval_RestoreThread(calls_thread); \
  712 -	} while(0)
  713 -#else
  714 -#define ACQUIRE_XCHAT_LOCK()
  715 -#define RELEASE_XCHAT_LOCK()
  716 -#define BEGIN_XCHAT_CALLS(x)
  717 -#define END_XCHAT_CALLS()
  718 -#endif
  719 -
  720 -#ifdef WITH_THREAD
  721 -
  722 -#define BEGIN_PLUGIN(plg) \
  723 -	do { \
  724 -	hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
  725 -	RELEASE_XCHAT_LOCK(); \
  726 -	Plugin_AcquireThread(plg); \
  727 -	Plugin_SetContext(plg, begin_plugin_ctx); \
  728 -	} while (0)
  729 -#define END_PLUGIN(plg) \
  730 -	do { \
  731 -	Plugin_ReleaseThread(plg); \
  732 -	ACQUIRE_XCHAT_LOCK(); \
  733 -	} while (0)
  734 -
  735 -#else /* !WITH_THREAD (win32) */
  736 -
  737 -static PyThreadState *pTempThread;
  738 -
  739 -#define BEGIN_PLUGIN(plg) \
  740 -	do { \
  741 -	hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
  742 -	RELEASE_XCHAT_LOCK(); \
  743 -	PyEval_AcquireLock(); \
  744 -	pTempThread = PyThreadState_Swap(((PluginObject *)(plg))->tstate); \
  745 -	Plugin_SetContext(plg, begin_plugin_ctx); \
  746 -	} while (0)
  747 -#define END_PLUGIN(plg) \
  748 -	do { \
  749 -	((PluginObject *)(plg))->tstate = PyThreadState_Swap(pTempThread); \
  750 -	PyEval_ReleaseLock(); \
  751 -	ACQUIRE_XCHAT_LOCK(); \
  752 -	} while (0)
  753 -
  754 -#endif /* !WITH_THREAD */
  755 -
  756 -#define Plugin_Swap(x) \
  757 -	PyThreadState_Swap(((PluginObject *)(x))->tstate)
  758 -#define Plugin_AcquireThread(x) \
  759 -	PyEval_AcquireThread(((PluginObject *)(x))->tstate)
  760 -#define Plugin_ReleaseThread(x) \
  761 -	Util_ReleaseThread(((PluginObject *)(x))->tstate)
  762 -#define Plugin_GetFilename(x) \
  763 -	(((PluginObject *)(x))->filename)
  764 -#define Plugin_GetName(x) \
  765 -	(((PluginObject *)(x))->name)
  766 -#define Plugin_GetVersion(x) \
  767 -	(((PluginObject *)(x))->version)
  768 -#define Plugin_GetDesc(x) \
  769 -	(((PluginObject *)(x))->description)
  770 -#define Plugin_GetHooks(x) \
  771 -	(((PluginObject *)(x))->hooks)
  772 -#define Plugin_GetContext(x) \
  773 -	(((PluginObject *)(x))->context)
  774 -#define Plugin_SetFilename(x, y) \
  775 -	((PluginObject *)(x))->filename = (y);
  776 -#define Plugin_SetName(x, y) \
  777 -	((PluginObject *)(x))->name = (y);
  778 -#define Plugin_SetVersion(x, y) \
  779 -	((PluginObject *)(x))->version = (y);
  780 -#define Plugin_SetDescription(x, y) \
  781 -	((PluginObject *)(x))->description = (y);
  782 -#define Plugin_SetHooks(x, y) \
  783 -	((PluginObject *)(x))->hooks = (y);
  784 -#define Plugin_SetContext(x, y) \
  785 -	((PluginObject *)(x))->context = (y);
  786 -#define Plugin_SetGui(x, y) \
  787 -	((PluginObject *)(x))->gui = (y);
  788 -
  789 -#define HOOK_XCHAT  1
  790 -#define HOOK_XCHAT_ATTR 2
  791 -#define HOOK_UNLOAD 3
  792 -
  793 -/* ===================================================================== */
  794 -/* Object definitions */
  795 -
  796 -typedef struct {
  797 -	PyObject_HEAD
  798 -	int softspace; /* We need it for print support. */
  799 -} XChatOutObject;
  800 -
  801 -typedef struct {
  802 -	PyObject_HEAD
  803 -	hexchat_context *context;
  804 -} ContextObject;
  805 -
  806 -typedef struct {
  807 -	PyObject_HEAD
  808 -	PyObject *time;
  809 -} AttributeObject;
  810 -
  811 -typedef struct {
  812 -	PyObject_HEAD
  813 -	const char *listname;
  814 -	PyObject *dict;
  815 -} ListItemObject;
  816 -
  817 -typedef struct {
  818 -	PyObject_HEAD
  819 -	char *name;
  820 -	char *version;
  821 -	char *filename;
  822 -	char *description;
  823 -	GSList *hooks;
  824 -	PyThreadState *tstate;
  825 -	hexchat_context *context;
  826 -	void *gui;
  827 -} PluginObject;
  828 -
  829 -typedef struct {
  830 -	int type;
  831 -	PyObject *plugin;
  832 -	PyObject *callback;
  833 -	PyObject *userdata;
  834 -	char *name;
  835 -	void *data; /* A handle, when type == HOOK_XCHAT */
  836 -} Hook;
  837 -
  838 -
  839 -/* ===================================================================== */
  840 -/* Function declarations */
  841 -
  842 -static PyObject *Util_BuildList(char *word[]);
  843 -static PyObject *Util_BuildEOLList(char *word[]);
  844 -static void Util_Autoload(void);
  845 -static char *Util_Expand(char *filename);
  846 -
  847 -static int Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata);
  848 -static int Callback_Command(char *word[], char *word_eol[], void *userdata);
  849 -static int Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata);
  850 -static int Callback_Print(char *word[], void *userdata);
  851 -static int Callback_Timer(void *userdata);
  852 -static int Callback_ThreadTimer(void *userdata);
  853 -
  854 -static PyObject *XChatOut_New(void);
  855 -static PyObject *XChatOut_write(PyObject *self, PyObject *args);
  856 -static void XChatOut_dealloc(PyObject *self);
  857 -
  858 -static PyObject *Attribute_New(hexchat_event_attrs *attrs);
  859 -
  860 -static void Context_dealloc(PyObject *self);
  861 -static PyObject *Context_set(ContextObject *self, PyObject *args);
  862 -static PyObject *Context_command(ContextObject *self, PyObject *args);
  863 -static PyObject *Context_prnt(ContextObject *self, PyObject *args);
  864 -static PyObject *Context_get_info(ContextObject *self, PyObject *args);
  865 -static PyObject *Context_get_list(ContextObject *self, PyObject *args);
  866 -static PyObject *Context_compare(ContextObject *a, ContextObject *b, int op);
  867 -static PyObject *Context_FromContext(hexchat_context *context);
  868 -static PyObject *Context_FromServerAndChannel(char *server, char *channel);
  869 -
  870 -static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
  871 -static PyObject *Plugin_GetCurrent(void);
  872 -static PluginObject *Plugin_ByString(char *str);
  873 -static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
  874 -			    PyObject *userdata, char *name, void *data);
  875 -static Hook *Plugin_FindHook(PyObject *plugin, char *name);
  876 -static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
  877 -static void Plugin_RemoveAllHooks(PyObject *plugin);
  878 -
  879 -static PyObject *Module_hexchat_command(PyObject *self, PyObject *args);
  880 -static PyObject *Module_xchat_prnt(PyObject *self, PyObject *args);
  881 -static PyObject *Module_hexchat_get_context(PyObject *self, PyObject *args);
  882 -static PyObject *Module_hexchat_find_context(PyObject *self, PyObject *args,
  883 -					   PyObject *kwargs);
  884 -static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
  885 -static PyObject *Module_hexchat_hook_command(PyObject *self, PyObject *args,
  886 -					   PyObject *kwargs);
  887 -static PyObject *Module_hexchat_hook_server(PyObject *self, PyObject *args,
  888 -					  PyObject *kwargs);
  889 -static PyObject *Module_hexchat_hook_print(PyObject *self, PyObject *args,
  890 -					 PyObject *kwargs);
  891 -static PyObject *Module_hexchat_hook_timer(PyObject *self, PyObject *args,
  892 -					 PyObject *kwargs);
  893 -static PyObject *Module_hexchat_unhook(PyObject *self, PyObject *args);
  894 -static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
  895 -static PyObject *Module_xchat_get_list(PyObject *self, PyObject *args);
  896 -static PyObject *Module_xchat_get_lists(PyObject *self, PyObject *args);
  897 -static PyObject *Module_hexchat_nickcmp(PyObject *self, PyObject *args);
  898 -static PyObject *Module_hexchat_strip(PyObject *self, PyObject *args);
  899 -static PyObject *Module_hexchat_pluginpref_set(PyObject *self, PyObject *args);
  900 -static PyObject *Module_hexchat_pluginpref_get(PyObject *self, PyObject *args);
  901 -static PyObject *Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args);
  902 -static PyObject *Module_hexchat_pluginpref_list(PyObject *self, PyObject *args);
  903 -
  904 -static void IInterp_Exec(char *command);
  905 -static int IInterp_Cmd(char *word[], char *word_eol[], void *userdata);
  906 -
  907 -static void Command_PyList(void);
  908 -static void Command_PyLoad(char *filename);
  909 -static void Command_PyUnload(char *name);
  910 -static void Command_PyReload(char *name);
  911 -static void Command_PyAbout(void);
  912 -static int Command_Py(char *word[], char *word_eol[], void *userdata);
  913 -
  914 -/* ===================================================================== */
  915 -/* Static declarations and definitions */
  916 -
  917 -static PyTypeObject Plugin_Type;
  918 -static PyTypeObject XChatOut_Type;
  919 -static PyTypeObject Context_Type;
  920 -static PyTypeObject ListItem_Type;
  921 -static PyTypeObject Attribute_Type;
  922 -
  923 -static PyThreadState *main_tstate = NULL;
  924 -static void *thread_timer = NULL;
  925 -
  926 -static hexchat_plugin *ph;
  927 -static GSList *plugin_list = NULL;
  928 -
  929 -static PyObject *interp_plugin = NULL;
  930 -static PyObject *xchatout = NULL;
  931 -
  932 -#ifdef WITH_THREAD
  933 -static PyThread_type_lock xchat_lock = NULL;
  934 -#endif
  935 -
  936 -static const char usage[] = "\
  937 -Usage: /PY LOAD   <filename>\n\
  938 -           UNLOAD <filename|name>\n\
  939 -           RELOAD <filename|name>\n\
  940 -           LIST\n\
  941 -           EXEC <command>\n\
  942 -           CONSOLE\n\
  943 -           ABOUT\n\
  944 -\n";
  945 -
  946 -static const char about[] = "HexChat Python interface version " VERSION "\n";
  947 -
  948 -/* ===================================================================== */
  949 -/* Utility functions */
  950 -
  951 -static PyObject *
  952 -Util_BuildList(char *word[])
  953 -{
  954 -	PyObject *list;
  955 -	int listsize = 31;
  956 -	int i;
  957 -	/* Find the last valid array member; there may be intermediate NULLs that
  958 -	 * would otherwise cause us to drop some members. */
  959 -	while (listsize > 0 &&
  960 -	       (word[listsize] == NULL || word[listsize][0] == 0))
  961 -		listsize--;
  962 -	list = PyList_New(listsize);
  963 -	if (list == NULL) {
  964 -		PyErr_Print();
  965 -		return NULL;
  966 -	}
  967 -	for (i = 1; i <= listsize; i++) {
  968 -		PyObject *o;
  969 -		if (word[i] == NULL) {
  970 -			Py_INCREF(Py_None);
  971 -			o = Py_None;
  972 -		} else {
  973 -			/* This handles word[i][0] == 0 automatically. */
  974 -			o = PyUnicode_FromString(word[i]);
  975 -		}
  976 -		PyList_SetItem(list, i - 1, o);
  977 -	}
  978 -	return list;
  979 -}
  980 -
  981 -static PyObject *
  982 -Util_BuildEOLList(char *word[])
  983 -{
  984 -	PyObject *list;
  985 -	int listsize = 31;
  986 -	int i;
  987 -	char *accum = NULL;
  988 -	char *last = NULL;
  989 -
  990 -	/* Find the last valid array member; there may be intermediate NULLs that
  991 -	 * would otherwise cause us to drop some members. */
  992 -	while (listsize > 0 &&
  993 -	       (word[listsize] == NULL || word[listsize][0] == 0))
  994 -		listsize--;
  995 -	list = PyList_New(listsize);
  996 -	if (list == NULL) {
  997 -		PyErr_Print();
  998 -		return NULL;
  999 -	}
 1000 -	for (i = listsize; i > 0; i--) {
 1001 -		char *part = word[i];
 1002 -		PyObject *uni_part;
 1003 -		if (accum == NULL) {
 1004 -			accum = g_strdup (part);
 1005 -		} else if (part != NULL && part[0] != 0) {
 1006 -			last = accum;
 1007 -			accum = g_strjoin(" ", part, last, NULL);
 1008 -			g_free (last);
 1009 -			last = NULL;
 1010 -
 1011 -			if (accum == NULL) {
 1012 -				Py_DECREF(list);
 1013 -				hexchat_print(ph, "Not enough memory to alloc accum"
 1014 -				              "for python plugin callback");
 1015 -				return NULL;
 1016 -			}
 1017 -		}
 1018 -		uni_part = PyUnicode_FromString(accum);
 1019 -		PyList_SetItem(list, i - 1, uni_part);
 1020 -	}
 1021 -
 1022 -	g_free (last);
 1023 -	g_free (accum);
 1024 -
 1025 -	return list;
 1026 -}
 1027 -
 1028 -static void
 1029 -Util_Autoload_from (const char *dir_name)
 1030 -{
 1031 -	gchar *oldcwd;
 1032 -	const char *entry_name;
 1033 -	GDir *dir;
 1034 -
 1035 -	oldcwd = g_get_current_dir ();
 1036 -	if (oldcwd == NULL)
 1037 -		return;
 1038 -	if (g_chdir(dir_name) != 0)
 1039 -	{
 1040 -		g_free (oldcwd);
 1041 -		return;
 1042 -	}
 1043 -	dir = g_dir_open (".", 0, NULL);
 1044 -	if (dir == NULL)
 1045 -	{
 1046 -		g_free (oldcwd);
 1047 -		return;
 1048 -	}
 1049 -	while ((entry_name = g_dir_read_name (dir)))
 1050 -	{
 1051 -		if (g_str_has_suffix (entry_name, ".py"))
 1052 -			Command_PyLoad((char*)entry_name);
 1053 -	}
 1054 -	g_dir_close (dir);
 1055 -	g_chdir (oldcwd);
 1056 -}
 1057 -
 1058 -static void
 1059 -Util_Autoload()
 1060 -{
 1061 -	const char *xdir;
 1062 -	char *sub_dir;
 1063 -	/* we need local filesystem encoding for g_chdir, g_dir_open etc */
 1064 -
 1065 -	xdir = hexchat_get_info(ph, "configdir");
 1066 -
 1067 -	/* auto-load from subdirectory addons */
 1068 -	sub_dir = g_build_filename (xdir, "addons", NULL);
 1069 -	Util_Autoload_from(sub_dir);
 1070 -	g_free (sub_dir);
 1071 -}
 1072 -
 1073 -static char *
 1074 -Util_Expand(char *filename)
 1075 -{
 1076 -	char *expanded;
 1077 -
 1078 -	/* Check if this is an absolute path. */
 1079 -	if (g_path_is_absolute(filename)) {
 1080 -		if (g_file_test(filename, G_FILE_TEST_EXISTS))
 1081 -			return g_strdup(filename);
 1082 -		else
 1083 -			return NULL;
 1084 -	}
 1085 -
 1086 -	/* Check if it starts with ~/ and expand the home if positive. */
 1087 -	if (*filename == '~' && *(filename+1) == '/') {
 1088 -		expanded = g_build_filename(g_get_home_dir(),
 1089 -					    filename+2, NULL);
 1090 -		if (g_file_test(expanded, G_FILE_TEST_EXISTS))
 1091 -			return expanded;
 1092 -		else {
 1093 -			g_free(expanded);
 1094 -			return NULL;
 1095 -		}
 1096 -	}
 1097 -
 1098 -	/* Check if it's in the current directory. */
 1099 -	expanded = g_build_filename(g_get_current_dir(),
 1100 -				    filename, NULL);
 1101 -	if (g_file_test(expanded, G_FILE_TEST_EXISTS))
 1102 -		return expanded;
 1103 -	g_free(expanded);
 1104 -
 1105 -	/* Check if ~/.config/hexchat/addons/<filename> exists. */
 1106 -	expanded = g_build_filename(hexchat_get_info(ph, "configdir"),
 1107 -				    "addons", filename, NULL);
 1108 -	if (g_file_test(expanded, G_FILE_TEST_EXISTS))
 1109 -		return expanded;
 1110 -	g_free(expanded);
 1111 -
 1112 -	return NULL;
 1113 -}
 1114 -
 1115 -/* Similar to PyEval_ReleaseThread, but accepts NULL thread states. */
 1116 -static void
 1117 -Util_ReleaseThread(PyThreadState *tstate)
 1118 -{
 1119 -	PyThreadState *old_tstate;
 1120 -	if (tstate == NULL)
 1121 -		Py_FatalError("PyEval_ReleaseThread: NULL thread state");
 1122 -	old_tstate = PyThreadState_Swap(NULL);
 1123 -	if (old_tstate != tstate && old_tstate != NULL)
 1124 -		Py_FatalError("PyEval_ReleaseThread: wrong thread state");
 1125 -	PyEval_ReleaseLock();
 1126 -}
 1127 -
 1128 -/* ===================================================================== */
 1129 -/* Hookable functions. These are the entry points to python code, besides
 1130 - * the load function, and the hooks for interactive interpreter. */
 1131 -
 1132 -static int
 1133 -Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
 1134 -{
 1135 -	Hook *hook = (Hook *) userdata;
 1136 -	PyObject *retobj;
 1137 -	PyObject *word_list, *word_eol_list;
 1138 -	PyObject *attributes;
 1139 -	int ret = HEXCHAT_EAT_NONE;
 1140 -	PyObject *plugin;
 1141 -
 1142 -	plugin = hook->plugin;
 1143 -	BEGIN_PLUGIN(plugin);
 1144 -
 1145 -	word_list = Util_BuildList(word);
 1146 -	if (word_list == NULL) {
 1147 -		END_PLUGIN(plugin);
 1148 -		return HEXCHAT_EAT_NONE;
 1149 -	}
 1150 -	word_eol_list = Util_BuildList(word_eol);
 1151 -	if (word_eol_list == NULL) {
 1152 -		Py_DECREF(word_list);
 1153 -		END_PLUGIN(plugin);
 1154 -		return HEXCHAT_EAT_NONE;
 1155 -	}
 1156 -
 1157 -	attributes = Attribute_New(attrs);
 1158 -
 1159 -	if (hook->type == HOOK_XCHAT_ATTR)
 1160 -		retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
 1161 -					       word_eol_list, hook->userdata, attributes);
 1162 -	else
 1163 -		retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
 1164 -					       word_eol_list, hook->userdata);
 1165 -	Py_DECREF(word_list);
 1166 -	Py_DECREF(word_eol_list);
 1167 -	Py_DECREF(attributes);
 1168 -
 1169 -	if (retobj == Py_None) {
 1170 -		ret = HEXCHAT_EAT_NONE;
 1171 -		Py_DECREF(retobj);
 1172 -	} else if (retobj) {
 1173 -		ret = PyLong_AsLong(retobj);
 1174 -		Py_DECREF(retobj);
 1175 -	} else {
 1176 -		PyErr_Print();
 1177 -	}
 1178 -
 1179 -	END_PLUGIN(plugin);
 1180 -
 1181 -	return ret;
 1182 -}
 1183 -
 1184 -static int
 1185 -Callback_Command(char *word[], char *word_eol[], void *userdata)
 1186 -{
 1187 -	Hook *hook = (Hook *) userdata;
 1188 -	PyObject *retobj;
 1189 -	PyObject *word_list, *word_eol_list;
 1190 -	int ret = HEXCHAT_EAT_NONE;
 1191 -	PyObject *plugin;
 1192 -
 1193 -	plugin = hook->plugin;
 1194 -	BEGIN_PLUGIN(plugin);
 1195 -
 1196 -	word_list = Util_BuildList(word);
 1197 -	if (word_list == NULL) {
 1198 -		END_PLUGIN(plugin);
 1199 -		return HEXCHAT_EAT_NONE;
 1200 -	}
 1201 -	word_eol_list = Util_BuildList(word_eol);
 1202 -	if (word_eol_list == NULL) {
 1203 -		Py_DECREF(word_list);
 1204 -		END_PLUGIN(plugin);
 1205 -		return HEXCHAT_EAT_NONE;
 1206 -	}
 1207 -
 1208 -	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
 1209 -				       word_eol_list, hook->userdata);
 1210 -	Py_DECREF(word_list);
 1211 -	Py_DECREF(word_eol_list);
 1212 -
 1213 -	if (retobj == Py_None) {
 1214 -		ret = HEXCHAT_EAT_NONE;
 1215 -		Py_DECREF(retobj);
 1216 -	} else if (retobj) {
 1217 -		ret = PyLong_AsLong(retobj);
 1218 -		Py_DECREF(retobj);
 1219 -	} else {
 1220 -		PyErr_Print();
 1221 -	}
 1222 -
 1223 -	END_PLUGIN(plugin);
 1224 -
 1225 -	return ret;
 1226 -}
 1227 -
 1228 -static int
 1229 -Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
 1230 -{
 1231 -	Hook *hook = (Hook *) userdata;
 1232 -	PyObject *retobj;
 1233 -	PyObject *word_list;
 1234 -	PyObject *word_eol_list;
 1235 -	PyObject *attributes;
 1236 -	int ret = HEXCHAT_EAT_NONE;
 1237 -	PyObject *plugin;
 1238 -
 1239 -	plugin = hook->plugin;
 1240 -	BEGIN_PLUGIN(plugin);
 1241 -
 1242 -	word_list = Util_BuildList(word);
 1243 -	if (word_list == NULL) {
 1244 -		END_PLUGIN(plugin);
 1245 -		return HEXCHAT_EAT_NONE;
 1246 -	}
 1247 -	word_eol_list = Util_BuildEOLList(word);
 1248 -	if (word_eol_list == NULL) {
 1249 -		Py_DECREF(word_list);
 1250 -		END_PLUGIN(plugin);
 1251 -		return HEXCHAT_EAT_NONE;
 1252 -	}
 1253 -
 1254 -	attributes = Attribute_New(attrs);
 1255 -
 1256 -	retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
 1257 -					    word_eol_list, hook->userdata, attributes);
 1258 -
 1259 -	Py_DECREF(word_list);
 1260 -	Py_DECREF(word_eol_list);
 1261 -	Py_DECREF(attributes);
 1262 -
 1263 -	if (retobj == Py_None) {
 1264 -		ret = HEXCHAT_EAT_NONE;
 1265 -		Py_DECREF(retobj);
 1266 -	} else if (retobj) {
 1267 -		ret = PyLong_AsLong(retobj);
 1268 -		Py_DECREF(retobj);
 1269 -	} else {
 1270 -		PyErr_Print();
 1271 -	}
 1272 -
 1273 -	END_PLUGIN(plugin);
 1274 -
 1275 -	return ret;
 1276 -}
 1277 -
 1278 -static int
 1279 -Callback_Print(char *word[], void *userdata)
 1280 -{
 1281 -	Hook *hook = (Hook *) userdata;
 1282 -	PyObject *retobj;
 1283 -	PyObject *word_list;
 1284 -	PyObject *word_eol_list;
 1285 -	int ret = HEXCHAT_EAT_NONE;
 1286 -	PyObject *plugin;
 1287 -
 1288 -	plugin = hook->plugin;
 1289 -	BEGIN_PLUGIN(plugin);
 1290 -
 1291 -	word_list = Util_BuildList(word);
 1292 -	if (word_list == NULL) {
 1293 -		END_PLUGIN(plugin);
 1294 -		return HEXCHAT_EAT_NONE;
 1295 -	}
 1296 -	word_eol_list = Util_BuildEOLList(word);
 1297 -	if (word_eol_list == NULL) {
 1298 -		Py_DECREF(word_list);
 1299 -		END_PLUGIN(plugin);
 1300 -		return HEXCHAT_EAT_NONE;
 1301 -	}
 1302 -
 1303 -	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
 1304 -					       word_eol_list, hook->userdata);
 1305 -
 1306 -	Py_DECREF(word_list);
 1307 -	Py_DECREF(word_eol_list);
 1308 -
 1309 -	if (retobj == Py_None) {
 1310 -		ret = HEXCHAT_EAT_NONE;
 1311 -		Py_DECREF(retobj);
 1312 -	} else if (retobj) {
 1313 -		ret = PyLong_AsLong(retobj);
 1314 -		Py_DECREF(retobj);
 1315 -	} else {
 1316 -		PyErr_Print();
 1317 -	}
 1318 -
 1319 -	END_PLUGIN(plugin);
 1320 -
 1321 -	return ret;
 1322 -}
 1323 -
 1324 -static int
 1325 -Callback_Timer(void *userdata)
 1326 -{
 1327 -	Hook *hook = (Hook *) userdata;
 1328 -	PyObject *retobj;
 1329 -	int ret = 0;
 1330 -	PyObject *plugin;
 1331 -
 1332 -	plugin = hook->plugin;
 1333 -
 1334 -	BEGIN_PLUGIN(hook->plugin);
 1335 -
 1336 -	retobj = PyObject_CallFunction(hook->callback, "(O)", hook->userdata);
 1337 -
 1338 -	if (retobj) {
 1339 -		ret = PyObject_IsTrue(retobj);
 1340 -		Py_DECREF(retobj);
 1341 -	} else {
 1342 -		PyErr_Print();
 1343 -	}
 1344 -
 1345 -	/* Returning 0 for this callback unhooks itself. */
 1346 -	if (ret == 0)
 1347 -		Plugin_RemoveHook(plugin, hook);
 1348 -
 1349 -	END_PLUGIN(plugin);
 1350 -
 1351 -	return ret;
 1352 -}
 1353 -
 1354 -#ifdef WITH_THREAD
 1355 -static int
 1356 -Callback_ThreadTimer(void *userdata)
 1357 -{
 1358 -	RELEASE_XCHAT_LOCK();
 1359 -#ifndef WIN32
 1360 -	usleep(1);
 1361 -#endif
 1362 -	ACQUIRE_XCHAT_LOCK();
 1363 -	return 1;
 1364 -}
 1365 -#endif
 1366 -
 1367 -/* ===================================================================== */
 1368 -/* XChatOut object */
 1369 -
 1370 -/* We keep this information global, so we can reset it when the
 1371 - * deinit function is called. */
 1372 -/* XXX This should be somehow bound to the printing context. */
 1373 -static GString *xchatout_buffer = NULL;
 1374 -
 1375 -static PyObject *
 1376 -XChatOut_New()
 1377 -{
 1378 -	XChatOutObject *xcoobj;
 1379 -	xcoobj = PyObject_New(XChatOutObject, &XChatOut_Type);
 1380 -	if (xcoobj != NULL)
 1381 -		xcoobj->softspace = 0;
 1382 -	return (PyObject *) xcoobj;
 1383 -}
 1384 -
 1385 -static void
 1386 -XChatOut_dealloc(PyObject *self)
 1387 -{
 1388 -	Py_TYPE(self)->tp_free((PyObject *)self);
 1389 -}
 1390 -
 1391 -/* This is a little bit complex because we have to buffer data
 1392 - * until a \n is received, since xchat breaks the line automatically.
 1393 - * We also crop the last \n for this reason. */
 1394 -static PyObject *
 1395 -XChatOut_write(PyObject *self, PyObject *args)
 1396 -{
 1397 -	gboolean add_space;
 1398 -	char *data, *pos;
 1399 -
 1400 -	if (!PyArg_ParseTuple(args, "s:write", &data))
 1401 -		return NULL;
 1402 -	if (!data || !*data) {
 1403 -		Py_RETURN_NONE;
 1404 -	}
 1405 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
 1406 -	if (((XChatOutObject *)self)->softspace) {
 1407 -		add_space = TRUE;
 1408 -		((XChatOutObject *)self)->softspace = 0;
 1409 -	} else {
 1410 -		add_space = FALSE;
 1411 -	}
 1412 -
 1413 -	g_string_append (xchatout_buffer, data);
 1414 -
 1415 -	/* If not end of line add space to continue buffer later */
 1416 -	if (add_space && xchatout_buffer->str[xchatout_buffer->len - 1] != '\n')
 1417 -	{
 1418 -		g_string_append_c (xchatout_buffer, ' ');
 1419 -	}
 1420 -
 1421 -	/* If there is an end of line print up to that */
 1422 -	if ((pos = strrchr (xchatout_buffer->str, '\n')))
 1423 -	{
 1424 -		*pos = '\0';
 1425 -		hexchat_print (ph, xchatout_buffer->str);
 1426 -
 1427 -		/* Then remove it from buffer */
 1428 -		g_string_erase (xchatout_buffer, 0, pos - xchatout_buffer->str + 1);
 1429 -	}
 1430 -
 1431 -	END_XCHAT_CALLS();
 1432 -	Py_RETURN_NONE;
 1433 -}
 1434 -
 1435 -#define OFF(x) offsetof(XChatOutObject, x)
 1436 -
 1437 -static PyMemberDef XChatOut_members[] = {
 1438 -	{"softspace", T_INT, OFF(softspace), 0},
 1439 -	{0}
 1440 -};
 1441 -
 1442 -static PyMethodDef XChatOut_methods[] = {
 1443 -	{"write", XChatOut_write, METH_VARARGS},
 1444 -	{NULL, NULL}
 1445 -};
 1446 -
 1447 -static PyTypeObject XChatOut_Type = {
 1448 -	PyVarObject_HEAD_INIT(NULL, 0)
 1449 -	"hexchat.XChatOut",	/*tp_name*/
 1450 -	sizeof(XChatOutObject),	/*tp_basicsize*/
 1451 -	0,			/*tp_itemsize*/
 1452 -	XChatOut_dealloc,	/*tp_dealloc*/
 1453 -	0,			/*tp_print*/
 1454 -	0,			/*tp_getattr*/
 1455 -	0,			/*tp_setattr*/
 1456 -	0,			/*tp_compare*/
 1457 -	0,			/*tp_repr*/
 1458 -	0,			/*tp_as_number*/
 1459 -	0,			/*tp_as_sequence*/
 1460 -	0,			/*tp_as_mapping*/
 1461 -	0,			/*tp_hash*/
 1462 -        0,                      /*tp_call*/
 1463 -        0,                      /*tp_str*/
 1464 -        PyObject_GenericGetAttr,/*tp_getattro*/
 1465 -        PyObject_GenericSetAttr,/*tp_setattro*/
 1466 -        0,                      /*tp_as_buffer*/
 1467 -        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
 1468 -        0,                      /*tp_doc*/
 1469 -        0,                      /*tp_traverse*/
 1470 -        0,                      /*tp_clear*/
 1471 -        0,                      /*tp_richcompare*/
 1472 -        0,                      /*tp_weaklistoffset*/
 1473 -        0,                      /*tp_iter*/
 1474 -        0,                      /*tp_iternext*/
 1475 -        XChatOut_methods,       /*tp_methods*/
 1476 -        XChatOut_members,       /*tp_members*/
 1477 -        0,                      /*tp_getset*/
 1478 -        0,                      /*tp_base*/
 1479 -        0,                      /*tp_dict*/
 1480 -        0,                      /*tp_descr_get*/
 1481 -        0,                      /*tp_descr_set*/
 1482 -        0,                      /*tp_dictoffset*/
 1483 -        0,                      /*tp_init*/
 1484 -        PyType_GenericAlloc,    /*tp_alloc*/
 1485 -        PyType_GenericNew,      /*tp_new*/
 1486 -      	PyObject_Del,          /*tp_free*/
 1487 -        0,                      /*tp_is_gc*/
 1488 -};
 1489 -
 1490 -
 1491 -/* ===================================================================== */
 1492 -/* Attribute object */
 1493 -
 1494 -#undef OFF
 1495 -#define OFF(x) offsetof(AttributeObject, x)
 1496 -
 1497 -static PyMemberDef Attribute_members[] = {
 1498 -	{"time", T_OBJECT, OFF(time), 0},
 1499 -	{0}
 1500 -};
 1501 -
 1502 -static void
 1503 -Attribute_dealloc(PyObject *self)
 1504 -{
 1505 -	Py_DECREF(((AttributeObject*)self)->time);
 1506 -	Py_TYPE(self)->tp_free((PyObject *)self);
 1507 -}
 1508 -
 1509 -static PyObject *
 1510 -Attribute_repr(PyObject *self)
 1511 -{
 1512 -	return PyUnicode_FromFormat("<Attribute object at %p>", self);
 1513 -}
 1514 -
 1515 -static PyTypeObject Attribute_Type = {
 1516 -	PyVarObject_HEAD_INIT(NULL, 0)
 1517 -	"hexchat.Attribute",	/*tp_name*/
 1518 -	sizeof(AttributeObject),	/*tp_basicsize*/
 1519 -	0,			/*tp_itemsize*/
 1520 -	Attribute_dealloc,	/*tp_dealloc*/
 1521 -	0,			/*tp_print*/
 1522 -	0,			/*tp_getattr*/
 1523 -	0,			/*tp_setattr*/
 1524 -	0,			/*tp_compare*/
 1525 -	Attribute_repr,		/*tp_repr*/
 1526 -	0,			/*tp_as_number*/
 1527 -	0,			/*tp_as_sequence*/
 1528 -	0,			/*tp_as_mapping*/
 1529 -	0,			/*tp_hash*/
 1530 -        0,                      /*tp_call*/
 1531 -        0,                      /*tp_str*/
 1532 -        PyObject_GenericGetAttr,/*tp_getattro*/
 1533 -        PyObject_GenericSetAttr,/*tp_setattro*/
 1534 -        0,                      /*tp_as_buffer*/
 1535 -        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
 1536 -        0,                      /*tp_doc*/
 1537 -        0,                      /*tp_traverse*/
 1538 -        0,                      /*tp_clear*/
 1539 -        0,                      /*tp_richcompare*/
 1540 -        0,                      /*tp_weaklistoffset*/
 1541 -        0,                      /*tp_iter*/
 1542 -        0,                      /*tp_iternext*/
 1543 -        0,                      /*tp_methods*/
 1544 -        Attribute_members,		/*tp_members*/
 1545 -        0,                      /*tp_getset*/
 1546 -        0,                      /*tp_base*/
 1547 -        0,                      /*tp_dict*/
 1548 -        0,                      /*tp_descr_get*/
 1549 -        0,                      /*tp_descr_set*/
 1550 -        0,						/*tp_dictoffset*/
 1551 -        0,                      /*tp_init*/
 1552 -        PyType_GenericAlloc,    /*tp_alloc*/
 1553 -        PyType_GenericNew,      /*tp_new*/
 1554 -      	PyObject_Del,          /*tp_free*/
 1555 -        0,                      /*tp_is_gc*/
 1556 -};
 1557 -
 1558 -static PyObject *
 1559 -Attribute_New(hexchat_event_attrs *attrs)
 1560 -{
 1561 -	AttributeObject *attr;
 1562 -	attr = PyObject_New(AttributeObject, &Attribute_Type);
 1563 -	if (attr != NULL) {
 1564 -		attr->time = PyLong_FromLong((long)attrs->server_time_utc);
 1565 -	}
 1566 -	return (PyObject *) attr;
 1567 -}
 1568 -
 1569 -
 1570 -/* ===================================================================== */
 1571 -/* Context object */
 1572 -
 1573 -static void
 1574 -Context_dealloc(PyObject *self)
 1575 -{
 1576 -	Py_TYPE(self)->tp_free((PyObject *)self);
 1577 -}
 1578 -
 1579 -static PyObject *
 1580 -Context_set(ContextObject *self, PyObject *args)
 1581 -{
 1582 -	PyObject *plugin = Plugin_GetCurrent();
 1583 -	Plugin_SetContext(plugin, self->context);
 1584 -	Py_RETURN_NONE;
 1585 -}
 1586 -
 1587 -static PyObject *
 1588 -Context_command(ContextObject *self, PyObject *args)
 1589 -{
 1590 -	char *text;
 1591 -	if (!PyArg_ParseTuple(args, "s:command", &text))
 1592 -		return NULL;
 1593 -	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
 1594 -	hexchat_set_context(ph, self->context);
 1595 -	hexchat_command(ph, text);
 1596 -	END_XCHAT_CALLS();
 1597 -	Py_RETURN_NONE;
 1598 -}
 1599 -
 1600 -static PyObject *
 1601 -Context_prnt(ContextObject *self, PyObject *args)
 1602 -{
 1603 -	char *text;
 1604 -	if (!PyArg_ParseTuple(args, "s:prnt", &text))
 1605 -		return NULL;
 1606 -	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
 1607 -	hexchat_set_context(ph, self->context);
 1608 -	hexchat_print(ph, text);
 1609 -	END_XCHAT_CALLS();
 1610 -	Py_RETURN_NONE;
 1611 -}
 1612 -
 1613 -static PyObject *
 1614 -Context_emit_print(ContextObject *self, PyObject *args, PyObject *kwargs)
 1615 -{
 1616 -	char *argv[6];
 1617 -	char *name;
 1618 -	int res;
 1619 -	long time = 0;
 1620 -	hexchat_event_attrs *attrs;
 1621 -	char *kwlist[] = {"name", "arg1", "arg2", "arg3",
 1622 -					"arg4", "arg5", "arg6", 
 1623 -					"time", NULL};
 1624 -	memset(&argv, 0, sizeof(char*)*6);
 1625 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
 1626 -			      &argv[0], &argv[1], &argv[2],
 1627 -			      &argv[3], &argv[4], &argv[5],
 1628 -				  &time))
 1629 -		return NULL;
 1630 -	BEGIN_XCHAT_CALLS(ALLOW_THREADS);
 1631 -	hexchat_set_context(ph, self->context);
 1632 -	attrs = hexchat_event_attrs_create(ph);
 1633 -	attrs->server_time_utc = (time_t)time; 
 1634 -	
 1635 -	res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
 1636 -					 argv[3], argv[4], argv[5], NULL);
 1637 -
 1638 -	hexchat_event_attrs_free(ph, attrs);
 1639 -	END_XCHAT_CALLS();
 1640 -	return PyLong_FromLong(res);
 1641 -}
 1642 -
 1643 -static PyObject *
 1644 -Context_get_info(ContextObject *self, PyObject *args)
 1645 -{
 1646 -	const char *info;
 1647 -	char *name;
 1648 -	if (!PyArg_ParseTuple(args, "s:get_info", &name))
 1649 -		return NULL;
 1650 -	BEGIN_XCHAT_CALLS(NONE);
 1651 -	hexchat_set_context(ph, self->context);
 1652 -	info = hexchat_get_info(ph, name);
 1653 -	END_XCHAT_CALLS();
 1654 -	if (info == NULL) {
 1655 -		Py_RETURN_NONE;
 1656 -	}
 1657 -	return PyUnicode_FromString(info);
 1658 -}
 1659 -
 1660 -static PyObject *
 1661 -Context_get_list(ContextObject *self, PyObject *args)
 1662 -{
 1663 -	PyObject *plugin = Plugin_GetCurrent();
 1664 -	hexchat_context *saved_context = Plugin_GetContext(plugin);
 1665 -	PyObject *ret;
 1666 -	Plugin_SetContext(plugin, self->context);
 1667 -	ret = Module_xchat_get_list((PyObject*)self, args);
 1668 -	Plugin_SetContext(plugin, saved_context);
 1669 -	return ret;
 1670 -}
 1671 -
 1672 -/* needed to make context1 == context2 work */
 1673 -static PyObject *
 1674 -Context_compare(ContextObject *a, ContextObject *b, int op)
 1675 -{
 1676 -	PyObject *ret;
 1677 -	/* check for == */
 1678 -	if (op == Py_EQ)
 1679 -		ret = (a->context == b->context ? Py_True : Py_False);
 1680 -	/* check for != */
 1681 -	else if (op == Py_NE)
 1682 -		ret = (a->context != b->context ? Py_True : Py_False);
 1683 -	/* only makes sense as == and != */
 1684 -	else
 1685 -	{
 1686 -		PyErr_SetString(PyExc_TypeError, "contexts are either equal or not equal");
 1687 -		ret = Py_None;
 1688 -	}
 1689 -
 1690 -	Py_INCREF(ret);
 1691 -	return ret;
 1692 -}
 1693 -
 1694 -static PyMethodDef Context_methods[] = {
 1695 -	{"set", (PyCFunction) Context_set, METH_NOARGS},
 1696 -	{"command", (PyCFunction) Context_command, METH_VARARGS},
 1697 -	{"prnt", (PyCFunction) Context_prnt, METH_VARARGS},
 1698 -	{"emit_print", (PyCFunction) Context_emit_print, METH_VARARGS|METH_KEYWORDS},
 1699 -	{"get_info", (PyCFunction) Context_get_info, METH_VARARGS},
 1700 -	{"get_list", (PyCFunction) Context_get_list, METH_VARARGS},
 1701 -	{NULL, NULL}
 1702 -};
 1703 -
 1704 -static PyTypeObject Context_Type = {
 1705 -	PyVarObject_HEAD_INIT(NULL, 0)
 1706 -	"hexchat.Context",	/*tp_name*/
 1707 -	sizeof(ContextObject),	/*tp_basicsize*/
 1708 -	0,			/*tp_itemsize*/
 1709 -	Context_dealloc,        /*tp_dealloc*/
 1710 -	0,			/*tp_print*/
 1711 -	0,			/*tp_getattr*/
 1712 -	0,			/*tp_setattr*/
 1713 -	0,			/*tp_compare*/
 1714 -	0,			/*tp_repr*/
 1715 -	0,			/*tp_as_number*/
 1716 -	0,			/*tp_as_sequence*/
 1717 -	0,			/*tp_as_mapping*/
 1718 -	0,			/*tp_hash*/
 1719 -        0,                      /*tp_call*/
 1720 -        0,                      /*tp_str*/
 1721 -        PyObject_GenericGetAttr,/*tp_getattro*/
 1722 -        PyObject_GenericSetAttr,/*tp_setattro*/
 1723 -        0,                      /*tp_as_buffer*/
 1724 -        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
 1725 -        0,                      /*tp_doc*/
 1726 -        0,                      /*tp_traverse*/
 1727 -        0,                      /*tp_clear*/
 1728 -        (richcmpfunc)Context_compare,    /*tp_richcompare*/
 1729 -        0,                      /*tp_weaklistoffset*/
 1730 -        0,                      /*tp_iter*/
 1731 -        0,                      /*tp_iternext*/
 1732 -        Context_methods,        /*tp_methods*/
 1733 -        0,                      /*tp_members*/
 1734 -        0,                      /*tp_getset*/
 1735 -        0,                      /*tp_base*/
 1736 -        0,                      /*tp_dict*/
 1737 -        0,                      /*tp_descr_get*/
 1738 -        0,                      /*tp_descr_set*/
 1739 -        0,                      /*tp_dictoffset*/
 1740 -        0,                      /*tp_init*/
 1741 -        PyType_GenericAlloc,    /*tp_alloc*/
 1742 -        PyType_GenericNew,      /*tp_new*/
 1743 -      	PyObject_Del,          /*tp_free*/
 1744 -        0,                      /*tp_is_gc*/
 1745 -};
 1746 -
 1747 -static PyObject *
 1748 -Context_FromContext(hexchat_context *context)
 1749 -{
 1750 -	ContextObject *ctxobj = PyObject_New(ContextObject, &Context_Type);
 1751 -	if (ctxobj != NULL)
 1752 -		ctxobj->context = context;
 1753 -	return (PyObject *) ctxobj;
 1754 -}
 1755 -
 1756 -static PyObject *
 1757 -Context_FromServerAndChannel(char *server, char *channel)
 1758 -{
 1759 -	ContextObject *ctxobj;
 1760 -	hexchat_context *context;
 1761 -	BEGIN_XCHAT_CALLS(NONE);
 1762 -	context = hexchat_find_context(ph, server, channel);
 1763 -	END_XCHAT_CALLS();
 1764 -	if (context == NULL)
 1765 -		return NULL;
 1766 -	ctxobj = PyObject_New(ContextObject, &Context_Type);
 1767 -	if (ctxobj == NULL)
 1768 -		return NULL;
 1769 -	ctxobj->context = context;
 1770 -	return (PyObject *) ctxobj;
 1771 -}
 1772 -
 1773 -
 1774 -/* ===================================================================== */
 1775 -/* ListItem object */
 1776 -
 1777 -#undef OFF
 1778 -#define OFF(x) offsetof(ListItemObject, x)
 1779 -
 1780 -static PyMemberDef ListItem_members[] = {
 1781 -	{"__dict__", T_OBJECT, OFF(dict), 0},
 1782 -	{0}
 1783 -};
 1784 -
 1785 -static void
 1786 -ListItem_dealloc(PyObject *self)
 1787 -{
 1788 -	Py_DECREF(((ListItemObject*)self)->dict);
 1789 -	Py_TYPE(self)->tp_free((PyObject *)self);
 1790 -}
 1791 -
 1792 -static PyObject *
 1793 -ListItem_repr(PyObject *self)
 1794 -{
 1795 -	return PyUnicode_FromFormat("<%s list item at %p>",
 1796 -			    	   ((ListItemObject*)self)->listname, self);
 1797 -}
 1798 -
 1799 -static PyTypeObject ListItem_Type = {
 1800 -	PyVarObject_HEAD_INIT(NULL, 0)
 1801 -	"hexchat.ListItem",	/*tp_name*/
 1802 -	sizeof(ListItemObject),	/*tp_basicsize*/
 1803 -	0,			/*tp_itemsize*/
 1804 -	ListItem_dealloc,	/*tp_dealloc*/
 1805 -	0,			/*tp_print*/
 1806 -	0,			/*tp_getattr*/
 1807 -	0,			/*tp_setattr*/
 1808 -	0,			/*tp_compare*/
 1809 -	ListItem_repr,		/*tp_repr*/
 1810 -	0,			/*tp_as_number*/
 1811 -	0,			/*tp_as_sequence*/
 1812 -	0,			/*tp_as_mapping*/
 1813 -	0,			/*tp_hash*/
 1814 -        0,                      /*tp_call*/
 1815 -        0,                      /*tp_str*/
 1816 -        PyObject_GenericGetAttr,/*tp_getattro*/
 1817 -        PyObject_GenericSetAttr,/*tp_setattro*/
 1818 -        0,                      /*tp_as_buffer*/
 1819 -        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
 1820 -        0,                      /*tp_doc*/
 1821 -        0,                      /*tp_traverse*/
 1822 -        0,                      /*tp_clear*/
 1823 -        0,                      /*tp_richcompare*/
 1824 -        0,                      /*tp_weaklistoffset*/
 1825 -        0,                      /*tp_iter*/
 1826 -        0,                      /*tp_iternext*/
 1827 -        0,                      /*tp_methods*/
 1828 -        ListItem_members,       /*tp_members*/
 1829 -        0,                      /*tp_getset*/
 1830 -        0,                      /*tp_base*/
 1831 -        0,                      /*tp_dict*/
 1832 -        0,                      /*tp_descr_get*/
 1833 -        0,                      /*tp_descr_set*/
 1834 -        OFF(dict),              /*tp_dictoffset*/
 1835 -        0,                      /*tp_init*/
 1836 -        PyType_GenericAlloc,    /*tp_alloc*/
 1837 -        PyType_GenericNew,      /*tp_new*/
 1838 -      	PyObject_Del,          /*tp_free*/
 1839 -        0,                      /*tp_is_gc*/
 1840 -};
 1841 -
 1842 -static PyObject *
 1843 -ListItem_New(const char *listname)
 1844 -{
 1845 -	ListItemObject *item;
 1846 -	item = PyObject_New(ListItemObject, &ListItem_Type);
 1847 -	if (item != NULL) {
 1848 -		/* listname parameter must be statically allocated. */
 1849 -		item->listname = listname;
 1850 -		item->dict = PyDict_New();
 1851 -		if (item->dict == NULL) {
 1852 -			Py_DECREF(item);
 1853 -			item = NULL;
 1854 -		}
 1855 -	}
 1856 -	return (PyObject *) item;
 1857 -}
 1858 -
 1859 -
 1860 -/* ===================================================================== */
 1861 -/* Plugin object */
 1862 -
 1863 -#define GET_MODULE_DATA(x, force) \
 1864 -	o = PyObject_GetAttrString(m, "__module_" #x "__"); \
 1865 -	if (o == NULL) { \
 1866 -		if (force) { \
 1867 -			hexchat_print(ph, "Module has no __module_" #x "__ " \
 1868 -					"defined"); \
 1869 -			goto error; \
 1870 -		} \
 1871 -		plugin->x = g_strdup(""); \
 1872 -	} else {\
 1873 -		if (!PyUnicode_Check(o)) { \
 1874 -			hexchat_print(ph, "Variable __module_" #x "__ " \
 1875 -					"must be a string"); \
 1876 -			goto error; \
 1877 -		} \
 1878 -		plugin->x = g_strdup(PyUnicode_AsUTF8(o)); \
 1879 -		if (plugin->x == NULL) { \
 1880 -			hexchat_print(ph, "Not enough memory to allocate " #x); \
 1881 -			goto error; \
 1882 -		} \
 1883 -	}
 1884 -
 1885 -static PyObject *
 1886 -Plugin_GetCurrent()
 1887 -{
 1888 -	PyObject *plugin;
 1889 -	plugin = PySys_GetObject("__plugin__");
 1890 -	if (plugin == NULL)
 1891 -		PyErr_SetString(PyExc_RuntimeError, "lost sys.__plugin__");
 1892 -	return plugin;
 1893 -}
 1894 -
 1895 -static hexchat_plugin *
 1896 -Plugin_GetHandle(PluginObject *plugin)
 1897 -{
 1898 -	/* This works but the issue is that the script must be ran to get
 1899 -	 * the name of it thus upon first use it will use the wrong handler
 1900 -	 * work around would be to run a fake script once to get name? */
 1901 -#if 0
 1902 -	/* return fake handle for pluginpref */
 1903 -	if (plugin->gui != NULL)
 1904 -		return plugin->gui;
 1905 -	else
 1906 -#endif
 1907 -		return ph;
 1908 -}
 1909 -
 1910 -static PluginObject *
 1911 -Plugin_ByString(char *str)
 1912 -{
 1913 -	GSList *list;
 1914 -	PluginObject *plugin;
 1915 -	char *basename;
 1916 -	list = plugin_list;
 1917 -	while (list != NULL) {
 1918 -		plugin = (PluginObject *) list->data;
 1919 -		basename = g_path_get_basename(plugin->filename);
 1920 -		if (basename == NULL)
 1921 -			break;
 1922 -		if (strcasecmp(plugin->name, str) == 0 ||
 1923 -		    strcasecmp(plugin->filename, str) == 0 ||
 1924 -		    strcasecmp(basename, str) == 0) {
 1925 -			g_free(basename);
 1926 -			return plugin;
 1927 -		}
 1928 -		g_free(basename);
 1929 -		list = list->next;
 1930 -	}
 1931 -	return NULL;
 1932 -}
 1933 -
 1934 -static Hook *
 1935 -Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
 1936 -	       PyObject *userdata, char *name, void *data)
 1937 -{
 1938 -	Hook *hook = g_new(Hook, 1);
 1939 -	hook->type = type;
 1940 -	hook->plugin = plugin;
 1941 -	Py_INCREF(callback);
 1942 -	hook->callback = callback;
 1943 -	Py_INCREF(userdata);
 1944 -	hook->userdata = userdata;
 1945 -	hook->name = g_strdup (name);
 1946 -	hook->data = NULL;
 1947 -	Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
 1948 -					       hook));
 1949 -
 1950 -	return hook;
 1951 -}
 1952 -
 1953 -static Hook *
 1954 -Plugin_FindHook(PyObject *plugin, char *name)
 1955 -{
 1956 -	Hook *hook = NULL;
 1957 -	GSList *plugin_hooks = Plugin_GetHooks(plugin);
 1958 -	
 1959 -	while (plugin_hooks)
 1960 -	{
 1961 -		if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
 1962 -		{
 1963 -			hook = (Hook *)plugin_hooks->data;
 1964 -			break;
 1965 -		}
 1966 -		
 1967 -		plugin_hooks = g_slist_next(plugin_hooks);
 1968 -	}
 1969 -
 1970 -	return hook;
 1971 -}
 1972 -
 1973 -static void
 1974 -Plugin_RemoveHook(PyObject *plugin, Hook *hook)
 1975 -{
 1976 -	GSList *list;
 1977 -	/* Is this really a hook of the running plugin? */
 1978 -	list = g_slist_find(Plugin_GetHooks(plugin), hook);
 1979 -	if (list) {
 1980 -		/* Ok, unhook it. */
 1981 -		if (hook->type != HOOK_UNLOAD) {
 1982 -			/* This is an xchat hook. Unregister it. */
 1983 -			BEGIN_XCHAT_CALLS(NONE);
 1984 -			hexchat_unhook(ph, (hexchat_hook*)hook->data);
 1985 -			END_XCHAT_CALLS();
 1986 -		}
 1987 -		Plugin_SetHooks(plugin,
 1988 -				g_slist_remove(Plugin_GetHooks(plugin),
 1989 -					       hook));
 1990 -		Py_DECREF(hook->callback);
 1991 -		Py_DECREF(hook->userdata);
 1992 -		g_free(hook->name);
 1993 -		g_free(hook);
 1994 -	}
 1995 -}
 1996 -
 1997 -static void
 1998 -Plugin_RemoveAllHooks(PyObject *plugin)
 1999 -{
 2000 -	GSList *list = Plugin_GetHooks(plugin);
 2001 -	while (list) {
 2002 -		Hook *hook = (Hook *) list->data;
 2003 -		if (hook->type != HOOK_UNLOAD) {
 2004 -			/* This is an xchat hook. Unregister it. */
 2005 -			BEGIN_XCHAT_CALLS(NONE);
 2006 -			hexchat_unhook(ph, (hexchat_hook*)hook->data);
 2007 -			END_XCHAT_CALLS();
 2008 -		}
 2009 -		Py_DECREF(hook->callback);
 2010 -		Py_DECREF(hook->userdata);
 2011 -		g_free(hook->name);
 2012 -		g_free(hook);
 2013 -		list = list->next;
 2014 -	}
 2015 -	Plugin_SetHooks(plugin, NULL);
 2016 -}
 2017 -
 2018 -static void
 2019 -Plugin_Delete(PyObject *plugin)
 2020 -{
 2021 -	PyThreadState *tstate = ((PluginObject*)plugin)->tstate;
 2022 -	GSList *list = Plugin_GetHooks(plugin);
 2023 -	while (list) {
 2024 -		Hook *hook = (Hook *) list->data;
 2025 -		if (hook->type == HOOK_UNLOAD) {
 2026 -			PyObject *retobj;
 2027 -			retobj = PyObject_CallFunction(hook->callback, "(O)",
 2028 -						       hook->userdata);
 2029 -			if (retobj) {
 2030 -				Py_DECREF(retobj);
 2031 -			} else {
 2032 -				PyErr_Print();
 2033 -				PyErr_Clear();
 2034 -			}
 2035 -		}
 2036 -		list = list->next;
 2037 -	}
 2038 -	Plugin_RemoveAllHooks(plugin);
 2039 -	if (((PluginObject *)plugin)->gui != NULL)
 2040 -		hexchat_plugingui_remove(ph, ((PluginObject *)plugin)->gui);
 2041 -	Py_DECREF(plugin);
 2042 -	/*PyThreadState_Swap(tstate); needed? */
 2043 -	Py_EndInterpreter(tstate);
 2044 -}
 2045 -
 2046 -static PyObject *
 2047 -Plugin_New(char *filename, PyObject *xcoobj)
 2048 -{
 2049 -	PluginObject *plugin = NULL;
 2050 -	PyObject *m, *o;
 2051 -#ifdef IS_PY3K
 2052 -	wchar_t *argv[] = { L"<hexchat>", 0 };
 2053 -#else
 2054 -	char *argv[] = { "<hexchat>", 0 };
 2055 -#endif
 2056 -
 2057 -	if (filename) {
 2058 -		char *old_filename = filename;
 2059 -		filename = Util_Expand(filename);
 2060 -		if (filename == NULL) {
 2061 -			hexchat_printf(ph, "File not found: %s", old_filename);
 2062 -			return NULL;
 2063 -		}
 2064 -	}
 2065 -
 2066 -	/* Allocate plugin structure. */
 2067 -	plugin = PyObject_New(PluginObject, &Plugin_Type);
 2068 -	if (plugin == NULL) {
 2069 -		hexchat_print(ph, "Can't create plugin object");
 2070 -		goto error;
 2071 -	}
 2072 -
 2073 -	Plugin_SetName(plugin, NULL);
 2074 -	Plugin_SetVersion(plugin, NULL);
 2075 -	Plugin_SetFilename(plugin, NULL);
 2076 -	Plugin_SetDescription(plugin, NULL);
 2077 -	Plugin_SetHooks(plugin, NULL);
 2078 -	Plugin_SetContext(plugin, hexchat_get_context(ph));
 2079 -	Plugin_SetGui(plugin, NULL);
 2080 -
 2081 -	/* Start a new interpreter environment for this plugin. */
 2082 -	PyEval_AcquireThread(main_tstate);
 2083 -	plugin->tstate = Py_NewInterpreter();
 2084 -	if (plugin->tstate == NULL) {
 2085 -		hexchat_print(ph, "Can't create interpreter state");
 2086 -		goto error;
 2087 -	}
 2088 -
 2089 -	PySys_SetArgv(1, argv);
 2090 -	PySys_SetObject("__plugin__", (PyObject *) plugin);
 2091 -
 2092 -	/* Set stdout and stderr to xchatout. */
 2093 -	Py_INCREF(xcoobj);
 2094 -	PySys_SetObject("stdout", xcoobj);
 2095 -	Py_INCREF(xcoobj);
 2096 -	PySys_SetObject("stderr", xcoobj);
 2097 -
 2098 -	if (filename) {
 2099 -#ifdef WIN32
 2100 -		char *file;
 2101 -		if (!g_file_get_contents(filename, &file, NULL, NULL)) {
 2102 -			hexchat_printf(ph, "Can't open file %s: %s\n",
 2103 -				     filename, strerror(errno));
 2104 -			goto error;
 2105 -		}
 2106 -
 2107 -		if (PyRun_SimpleString(file) != 0) {
 2108 -			hexchat_printf(ph, "Error loading module %s\n",
 2109 -				     filename);
 2110 -			g_free (file);
 2111 -			goto error;
 2112 -		}
 2113 -
 2114 -		plugin->filename = filename;
 2115 -		filename = NULL;
 2116 -		g_free (file);
 2117 -#else
 2118 -		FILE *fp;
 2119 -		plugin->filename = filename;
 2120 -
 2121 -		/* It's now owned by the plugin. */
 2122 -		filename = NULL;
 2123 -
 2124 -		/* Open the plugin file. */
 2125 -		fp = fopen(plugin->filename, "r");
 2126 -		if (fp == NULL) {
 2127 -			hexchat_printf(ph, "Can't open file %s: %s\n",
 2128 -				     plugin->filename, strerror(errno));
 2129 -			goto error;
 2130 -		}
 2131 -
 2132 -		/* Run the plugin. */
 2133 -		if (PyRun_SimpleFile(fp, plugin->filename) != 0) {
 2134 -			hexchat_printf(ph, "Error loading module %s\n",
 2135 -				     plugin->filename);
 2136 -			fclose(fp);
 2137 -			goto error;
 2138 -		}
 2139 -		fclose(fp);
 2140 -#endif
 2141 -		m = PyDict_GetItemString(PyImport_GetModuleDict(),
 2142 -					 "__main__");
 2143 -		if (m == NULL) {
 2144 -			hexchat_print(ph, "Can't get __main__ module");
 2145 -			goto error;
 2146 -		}
 2147 -		GET_MODULE_DATA(name, 1);
 2148 -		GET_MODULE_DATA(version, 0);
 2149 -		GET_MODULE_DATA(description, 0);
 2150 -		plugin->gui = hexchat_plugingui_add(ph, plugin->filename,
 2151 -						  plugin->name,
 2152 -						  plugin->description,
 2153 -						  plugin->version, NULL);
 2154 -	}
 2155 -
 2156 -	PyEval_ReleaseThread(plugin->tstate);
 2157 -
 2158 -	return (PyObject *) plugin;
 2159 -
 2160 -error:
 2161 -	g_free(filename);
 2162 -
 2163 -	if (plugin) {
 2164 -		if (plugin->tstate)
 2165 -			Plugin_Delete((PyObject *)plugin);
 2166 -		else
 2167 -			Py_DECREF(plugin);
 2168 -	}
 2169 -	PyEval_ReleaseLock();
 2170 -
 2171 -	return NULL;
 2172 -}
 2173 -
 2174 -static void
 2175 -Plugin_dealloc(PluginObject *self)
 2176 -{
 2177 -	g_free(self->filename);
 2178 -	g_free(self->name);
 2179 -	g_free(self->version);
 2180 -	g_free(self->description);
 2181 -	Py_TYPE(self)->tp_free((PyObject *)self);
 2182 -}
 2183 -
 2184 -static PyTypeObject Plugin_Type = {
 2185 -	PyVarObject_HEAD_INIT(NULL, 0)
 2186 -	"hexchat.Plugin",		/*tp_name*/
 2187 -	sizeof(PluginObject),	/*tp_basicsize*/
 2188 -	0,			/*tp_itemsize*/
 2189 -	(destructor)Plugin_dealloc, /*tp_dealloc*/
 2190 -	0,			/*tp_print*/
 2191 -	0,			/*tp_getattr*/
 2192 -	0,			/*tp_setattr*/
 2193 -	0,			/*tp_compare*/
 2194 -	0,			/*tp_repr*/
 2195 -	0,			/*tp_as_number*/
 2196 -	0,			/*tp_as_sequence*/
 2197 -	0,			/*tp_as_mapping*/
 2198 -	0,			/*tp_hash*/
 2199 -        0,                      /*tp_call*/
 2200 -        0,                      /*tp_str*/
 2201 -        PyObject_GenericGetAttr,/*tp_getattro*/
 2202 -        PyObject_GenericSetAttr,/*tp_setattro*/
 2203 -        0,                      /*tp_as_buffer*/
 2204 -        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
 2205 -        0,                      /*tp_doc*/
 2206 -        0,                      /*tp_traverse*/
 2207 -        0,                      /*tp_clear*/
 2208 -        0,                      /*tp_richcompare*/
 2209 -        0,                      /*tp_weaklistoffset*/
 2210 -        0,                      /*tp_iter*/
 2211 -        0,                      /*tp_iternext*/
 2212 -        0,                      /*tp_methods*/
 2213 -        0,                      /*tp_members*/
 2214 -        0,                      /*tp_getset*/
 2215 -        0,                      /*tp_base*/
 2216 -        0,                      /*tp_dict*/
 2217 -        0,                      /*tp_descr_get*/
 2218 -        0,                      /*tp_descr_set*/
 2219 -        0,                      /*tp_dictoffset*/
 2220 -        0,                      /*tp_init*/
 2221 -        PyType_GenericAlloc,    /*tp_alloc*/
 2222 -        PyType_GenericNew,      /*tp_new*/
 2223 -      	PyObject_Del,          /*tp_free*/
 2224 -        0,                      /*tp_is_gc*/
 2225 -};
 2226 -
 2227 -
 2228 -/* ===================================================================== */
 2229 -/* XChat module */
 2230 -
 2231 -static PyObject *
 2232 -Module_hexchat_command(PyObject *self, PyObject *args)
 2233 -{
 2234 -	char *text;
 2235 -	if (!PyArg_ParseTuple(args, "s:command", &text))
 2236 -		return NULL;
 2237 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
 2238 -	hexchat_command(ph, text);
 2239 -	END_XCHAT_CALLS();
 2240 -	Py_RETURN_NONE;
 2241 -}
 2242 -
 2243 -static PyObject *
 2244 -Module_xchat_prnt(PyObject *self, PyObject *args)
 2245 -{
 2246 -	char *text;
 2247 -	if (!PyArg_ParseTuple(args, "s:prnt", &text))
 2248 -		return NULL;
 2249 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
 2250 -	hexchat_print(ph, text);
 2251 -	END_XCHAT_CALLS();
 2252 -	Py_RETURN_NONE;
 2253 -}
 2254 -
 2255 -static PyObject *
 2256 -Module_hexchat_emit_print(PyObject *self, PyObject *args, PyObject *kwargs)
 2257 -{
 2258 -	char *argv[6];
 2259 -	char *name;
 2260 -	int res;
 2261 -	long time = 0;
 2262 -	hexchat_event_attrs *attrs;
 2263 -	char *kwlist[] = {"name", "arg1", "arg2", "arg3",
 2264 -					"arg4", "arg5", "arg6", 
 2265 -					"time", NULL};
 2266 -	memset(&argv, 0, sizeof(char*)*6);
 2267 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
 2268 -			      &argv[0], &argv[1], &argv[2],
 2269 -			      &argv[3], &argv[4], &argv[5],
 2270 -				  &time))
 2271 -		return NULL;
 2272 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
 2273 -	attrs = hexchat_event_attrs_create(ph);
 2274 -	attrs->server_time_utc = (time_t)time; 
 2275 -	
 2276 -	res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
 2277 -					 argv[3], argv[4], argv[5], NULL);
 2278 -
 2279 -	hexchat_event_attrs_free(ph, attrs);
 2280 -	END_XCHAT_CALLS();
 2281 -	return PyLong_FromLong(res);
 2282 -}
 2283 -
 2284 -static PyObject *
 2285 -Module_hexchat_get_info(PyObject *self, PyObject *args)
 2286 -{
 2287 -	const char *info;
 2288 -	char *name;
 2289 -	if (!PyArg_ParseTuple(args, "s:get_info", &name))
 2290 -		return NULL;
 2291 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
 2292 -	info = hexchat_get_info(ph, name);
 2293 -	END_XCHAT_CALLS();
 2294 -	if (info == NULL) {
 2295 -		Py_RETURN_NONE;
 2296 -	}
 2297 -	if (strcmp (name, "gtkwin_ptr") == 0 || strcmp (name, "win_ptr") == 0)
 2298 -		return PyUnicode_FromFormat("%p", info); /* format as pointer */
 2299 -	else
 2300 -		return PyUnicode_FromString(info);
 2301 -}
 2302 -
 2303 -static PyObject *
 2304 -Module_xchat_get_prefs(PyObject *self, PyObject *args)
 2305 -{
 2306 -	PyObject *res;
 2307 -	const char *info;
 2308 -	int integer;
 2309 -	char *name;
 2310 -	int type;
 2311 -	if (!PyArg_ParseTuple(args, "s:get_prefs", &name))
 2312 -		return NULL;
 2313 -	BEGIN_XCHAT_CALLS(NONE);
 2314 -	type = hexchat_get_prefs(ph, name, &info, &integer);
 2315 -	END_XCHAT_CALLS();
 2316 -	switch (type) {
 2317 -		case 0:
 2318 -			Py_INCREF(Py_None);
 2319 -			res = Py_None;
 2320 -			break;
 2321 -		case 1:
 2322 -			res = PyUnicode_FromString((char*)info);
 2323 -			break;
 2324 -		case 2:
 2325 -		case 3:
 2326 -			res = PyLong_FromLong(integer);
 2327 -			break;
 2328 -		default:
 2329 -			PyErr_Format(PyExc_RuntimeError,
 2330 -				     "unknown get_prefs type (%d), "
 2331 -				     "please report", type);
 2332 -			res = NULL;
 2333 -			break;
 2334 -	}
 2335 -	return res;
 2336 -}
 2337 -
 2338 -static PyObject *
 2339 -Module_hexchat_get_context(PyObject *self, PyObject *args)
 2340 -{
 2341 -	PyObject *plugin;
 2342 -	PyObject *ctxobj;
 2343 -	plugin = Plugin_GetCurrent();
 2344 -	if (plugin == NULL)
 2345 -		return NULL;
 2346 -	ctxobj = Context_FromContext(Plugin_GetContext(plugin));
 2347 -	if (ctxobj == NULL) {
 2348 -		Py_RETURN_NONE;
 2349 -	}
 2350 -	return ctxobj;
 2351 -}
 2352 -
 2353 -static PyObject *
 2354 -Module_hexchat_find_context(PyObject *self, PyObject *args, PyObject *kwargs)
 2355 -{
 2356 -	char *server = NULL;
 2357 -	char *channel = NULL;
 2358 -	PyObject *ctxobj;
 2359 -	char *kwlist[] = {"server", "channel", 0};
 2360 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zz:find_context",
 2361 -					 kwlist, &server, &channel))
 2362 -		return NULL;
 2363 -	ctxobj = Context_FromServerAndChannel(server, channel);
 2364 -	if (ctxobj == NULL) {
 2365 -		Py_RETURN_NONE;
 2366 -	}
 2367 -	return ctxobj;
 2368 -}
 2369 -
 2370 -static PyObject *
 2371 -Module_hexchat_pluginpref_set(PyObject *self, PyObject *args)
 2372 -{
 2373 -	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
 2374 -	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
 2375 -	int result;
 2376 -	char *var;
 2377 -	PyObject *value;
 2378 -		
 2379 -	if (!PyArg_ParseTuple(args, "sO:set_pluginpref", &var, &value))
 2380 -		return NULL;
 2381 -	if (PyLong_Check(value)) {
 2382 -		int intvalue = PyLong_AsLong(value);
 2383 -		BEGIN_XCHAT_CALLS(NONE);
 2384 -		result = hexchat_pluginpref_set_int(prefph, var, intvalue);
 2385 -		END_XCHAT_CALLS();
 2386 -	}
 2387 -	else if (PyUnicode_Check(value)) {
 2388 -		char *charvalue = PyUnicode_AsUTF8(value);
 2389 -		BEGIN_XCHAT_CALLS(NONE);
 2390 -		result = hexchat_pluginpref_set_str(prefph, var, charvalue);
 2391 -		END_XCHAT_CALLS();
 2392 -	}
 2393 -	else
 2394 -		result = 0;
 2395 -	return PyBool_FromLong(result);
 2396 -}
 2397 -
 2398 -static PyObject *
 2399 -Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
 2400 -{
 2401 -	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
 2402 -	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
 2403 -	PyObject *ret;
 2404 -	char *var;
 2405 -	char retstr[512];
 2406 -	int retint;
 2407 -	int result;
 2408 -	if (!PyArg_ParseTuple(args, "s:get_pluginpref", &var))
 2409 -		return NULL;
 2410 -		
 2411 -	/* This will always return numbers as integers. */
 2412 -	BEGIN_XCHAT_CALLS(NONE);
 2413 -	result = hexchat_pluginpref_get_str(prefph, var, retstr);
 2414 -	END_XCHAT_CALLS();
 2415 -	if (result) {
 2416 -		if (strlen (retstr) <= 12) {
 2417 -			BEGIN_XCHAT_CALLS(NONE);
 2418 -			retint = hexchat_pluginpref_get_int(prefph, var);
 2419 -			END_XCHAT_CALLS();
 2420 -			if ((retint == -1) && (strcmp(retstr, "-1") != 0))
 2421 -				ret = PyUnicode_FromString(retstr);
 2422 -			else
 2423 -				ret = PyLong_FromLong(retint);
 2424 -		} else
 2425 -			ret = PyUnicode_FromString(retstr);
 2426 -	}
 2427 -	else
 2428 -	{
 2429 -		Py_INCREF(Py_None);
 2430 -		ret = Py_None;
 2431 -	}
 2432 -	return ret;
 2433 -}
 2434 -
 2435 -static PyObject *
 2436 -Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args)
 2437 -{
 2438 -	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
 2439 -	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
 2440 -	char *var;
 2441 -	int result;
 2442 -	if (!PyArg_ParseTuple(args, "s:del_pluginpref", &var))
 2443 -		return NULL;
 2444 -	BEGIN_XCHAT_CALLS(NONE);
 2445 -	result = hexchat_pluginpref_delete(prefph, var);
 2446 -	END_XCHAT_CALLS();
 2447 -	return PyBool_FromLong(result);
 2448 -}
 2449 -
 2450 -static PyObject *
 2451 -Module_hexchat_pluginpref_list(PyObject *self, PyObject *args)
 2452 -{
 2453 -	PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
 2454 -	hexchat_plugin *prefph = Plugin_GetHandle(plugin);
 2455 -	char list[4096];
 2456 -	char* token;
 2457 -	int result;
 2458 -	PyObject *pylist;
 2459 -	pylist = PyList_New(0);
 2460 -	BEGIN_XCHAT_CALLS(NONE);
 2461 -	result = hexchat_pluginpref_list(prefph, list);
 2462 -	END_XCHAT_CALLS();
 2463 -	if (result) {
 2464 -		token = strtok(list, ",");
 2465 -		while (token != NULL) {
 2466 -			PyList_Append(pylist, PyUnicode_FromString(token));
 2467 -			token = strtok (NULL, ",");
 2468 -		}
 2469 -	}
 2470 -	return pylist;
 2471 -}
 2472 -
 2473 -static PyObject *
 2474 -Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
 2475 -{
 2476 -	char *name;
 2477 -	PyObject *callback;
 2478 -	PyObject *userdata = Py_None;
 2479 -	int priority = HEXCHAT_PRI_NORM;
 2480 -	char *help = NULL;
 2481 -	PyObject *plugin;
 2482 -	Hook *hook;
 2483 -	char *kwlist[] = {"name", "callback", "userdata",
 2484 -			  "priority", "help", 0};
 2485 -
 2486 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oiz:hook_command",
 2487 -					 kwlist, &name, &callback, &userdata,
 2488 -					 &priority, &help))
 2489 -		return NULL;
 2490 -
 2491 -	plugin = Plugin_GetCurrent();
 2492 -	if (plugin == NULL)
 2493 -		return NULL;
 2494 -	if (!PyCallable_Check(callback)) {
 2495 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2496 -		return NULL;
 2497 -	}
 2498 -
 2499 -	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
 2500 -	if (hook == NULL)
 2501 -		return NULL;
 2502 -
 2503 -	BEGIN_XCHAT_CALLS(NONE);
 2504 -	hook->data = (void*)hexchat_hook_command(ph, name, priority,
 2505 -					       Callback_Command, help, hook);
 2506 -	END_XCHAT_CALLS();
 2507 -
 2508 -	return PyLong_FromVoidPtr(hook);
 2509 -}
 2510 -
 2511 -static PyObject *
 2512 -Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
 2513 -{
 2514 -	char *name;
 2515 -	PyObject *callback;
 2516 -	PyObject *userdata = Py_None;
 2517 -	int priority = HEXCHAT_PRI_NORM;
 2518 -	PyObject *plugin;
 2519 -	Hook *hook;
 2520 -	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
 2521 -
 2522 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
 2523 -					 kwlist, &name, &callback, &userdata,
 2524 -					 &priority))
 2525 -		return NULL;
 2526 -
 2527 -	plugin = Plugin_GetCurrent();
 2528 -	if (plugin == NULL)
 2529 -		return NULL;
 2530 -	if (!PyCallable_Check(callback)) {
 2531 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2532 -		return NULL;
 2533 -	}
 2534 -
 2535 -	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
 2536 -	if (hook == NULL)
 2537 -		return NULL;
 2538 -
 2539 -	BEGIN_XCHAT_CALLS(NONE);
 2540 -	hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
 2541 -					      Callback_Server, hook);
 2542 -	END_XCHAT_CALLS();
 2543 -
 2544 -	return PyLong_FromVoidPtr(hook);
 2545 -}
 2546 -
 2547 -static PyObject *
 2548 -Module_hexchat_hook_server_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
 2549 -{
 2550 -	char *name;
 2551 -	PyObject *callback;
 2552 -	PyObject *userdata = Py_None;
 2553 -	int priority = HEXCHAT_PRI_NORM;
 2554 -	PyObject *plugin;
 2555 -	Hook *hook;
 2556 -	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
 2557 -
 2558 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
 2559 -					 kwlist, &name, &callback, &userdata,
 2560 -					 &priority))
 2561 -		return NULL;
 2562 -
 2563 -	plugin = Plugin_GetCurrent();
 2564 -	if (plugin == NULL)
 2565 -		return NULL;
 2566 -	if (!PyCallable_Check(callback)) {
 2567 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2568 -		return NULL;
 2569 -	}
 2570 -
 2571 -	hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, NULL, NULL);
 2572 -	if (hook == NULL)
 2573 -		return NULL;
 2574 -
 2575 -	BEGIN_XCHAT_CALLS(NONE);
 2576 -	hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
 2577 -					      Callback_Server, hook);
 2578 -	END_XCHAT_CALLS();
 2579 -
 2580 -	return PyLong_FromVoidPtr(hook);
 2581 -}
 2582 -
 2583 -static PyObject *
 2584 -Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
 2585 -{
 2586 -	char *name;
 2587 -	PyObject *callback;
 2588 -	PyObject *userdata = Py_None;
 2589 -	int priority = HEXCHAT_PRI_NORM;
 2590 -	PyObject *plugin;
 2591 -	Hook *hook;
 2592 -	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
 2593 -
 2594 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print",
 2595 -					 kwlist, &name, &callback, &userdata,
 2596 -					 &priority))
 2597 -		return NULL;
 2598 -
 2599 -	plugin = Plugin_GetCurrent();
 2600 -	if (plugin == NULL)
 2601 -		return NULL;
 2602 -	if (!PyCallable_Check(callback)) {
 2603 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2604 -		return NULL;
 2605 -	}
 2606 -
 2607 -	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
 2608 -	if (hook == NULL)
 2609 -		return NULL;
 2610 -
 2611 -	BEGIN_XCHAT_CALLS(NONE);
 2612 -	hook->data = (void*)hexchat_hook_print(ph, name, priority,
 2613 -					     Callback_Print, hook);
 2614 -	END_XCHAT_CALLS();
 2615 -
 2616 -	return PyLong_FromVoidPtr(hook);
 2617 -}
 2618 -
 2619 -static PyObject *
 2620 -Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
 2621 -{
 2622 -	char *name;
 2623 -	PyObject *callback;
 2624 -	PyObject *userdata = Py_None;
 2625 -	int priority = HEXCHAT_PRI_NORM;
 2626 -	PyObject *plugin;
 2627 -	Hook *hook;
 2628 -	char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
 2629 -
 2630 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print_attrs",
 2631 -					 kwlist, &name, &callback, &userdata,
 2632 -					 &priority))
 2633 -		return NULL;
 2634 -
 2635 -	plugin = Plugin_GetCurrent();
 2636 -	if (plugin == NULL)
 2637 -		return NULL;
 2638 -	if (!PyCallable_Check(callback)) {
 2639 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2640 -		return NULL;
 2641 -	}
 2642 -
 2643 -	hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, name, NULL);
 2644 -	if (hook == NULL)
 2645 -		return NULL;
 2646 -
 2647 -	BEGIN_XCHAT_CALLS(NONE);
 2648 -	hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
 2649 -					     Callback_Print_Attrs, hook);
 2650 -	END_XCHAT_CALLS();
 2651 -
 2652 -	return PyLong_FromVoidPtr(hook);
 2653 -}
 2654 -
 2655 -static PyObject *
 2656 -Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
 2657 -{
 2658 -	int timeout;
 2659 -	PyObject *callback;
 2660 -	PyObject *userdata = Py_None;
 2661 -	PyObject *plugin;
 2662 -	Hook *hook;
 2663 -	char *kwlist[] = {"timeout", "callback", "userdata", 0};
 2664 -
 2665 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|O:hook_timer",
 2666 -					 kwlist, &timeout, &callback,
 2667 -					 &userdata))
 2668 -		return NULL;
 2669 -
 2670 -	plugin = Plugin_GetCurrent();
 2671 -	if (plugin == NULL)
 2672 -		return NULL;
 2673 -	if (!PyCallable_Check(callback)) {
 2674 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2675 -		return NULL;
 2676 -	}
 2677 -
 2678 -	hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
 2679 -	if (hook == NULL)
 2680 -		return NULL;
 2681 -
 2682 -	BEGIN_XCHAT_CALLS(NONE);
 2683 -	hook->data = (void*)hexchat_hook_timer(ph, timeout,
 2684 -					     Callback_Timer, hook);
 2685 -	END_XCHAT_CALLS();
 2686 -
 2687 -	return PyLong_FromVoidPtr(hook);
 2688 -}
 2689 -
 2690 -static PyObject *
 2691 -Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
 2692 -{
 2693 -	PyObject *callback;
 2694 -	PyObject *userdata = Py_None;
 2695 -	PyObject *plugin;
 2696 -	Hook *hook;
 2697 -	char *kwlist[] = {"callback", "userdata", 0};
 2698 -
 2699 -	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:hook_unload",
 2700 -					 kwlist, &callback, &userdata))
 2701 -		return NULL;
 2702 -
 2703 -	plugin = Plugin_GetCurrent();
 2704 -	if (plugin == NULL)
 2705 -		return NULL;
 2706 -	if (!PyCallable_Check(callback)) {
 2707 -		PyErr_SetString(PyExc_TypeError, "callback is not callable");
 2708 -		return NULL;
 2709 -	}
 2710 -
 2711 -	hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
 2712 -	if (hook == NULL)
 2713 -		return NULL;
 2714 -
 2715 -	return PyLong_FromVoidPtr(hook);
 2716 -}
 2717 -
 2718 -static PyObject *
 2719 -Module_hexchat_unhook(PyObject *self, PyObject *args)
 2720 -{
 2721 -	PyObject *plugin;
 2722 -	PyObject *obj;
 2723 -	Hook *hook;
 2724 -	if (!PyArg_ParseTuple(args, "O:unhook", &obj))
 2725 -		return NULL;
 2726 -	plugin = Plugin_GetCurrent();
 2727 -	if (plugin == NULL)
 2728 -		return NULL;
 2729 -
 2730 -	if (PyUnicode_Check (obj))
 2731 -	{
 2732 -		hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
 2733 -		while (hook)
 2734 -		{
 2735 -			Plugin_RemoveHook(plugin, hook);
 2736 -			hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
 2737 -		}
 2738 -	}
 2739 -	else
 2740 -	{
 2741 -		hook = (Hook *)PyLong_AsVoidPtr(obj);
 2742 -		Plugin_RemoveHook(plugin, hook);
 2743 -	}	
 2744 -
 2745 -	Py_RETURN_NONE;
 2746 -}
 2747 -
 2748 -static PyObject *
 2749 -Module_xchat_get_list(PyObject *self, PyObject *args)
 2750 -{
 2751 -	hexchat_list *list;
 2752 -	PyObject *l;
 2753 -	const char *name;
 2754 -	const char *const *fields;
 2755 -	int i;
 2756 -
 2757 -	if (!PyArg_ParseTuple(args, "s:get_list", &name))
 2758 -		return NULL;
 2759 -	/* This function is thread safe, and returns statically
 2760 -	 * allocated data. */
 2761 -	fields = hexchat_list_fields(ph, "lists");
 2762 -	for (i = 0; fields[i]; i++) {
 2763 -		if (strcmp(fields[i], name) == 0) {
 2764 -			/* Use the static allocated one. */
 2765 -			name = fields[i];
 2766 -			break;
 2767 -		}
 2768 -	}
 2769 -	if (fields[i] == NULL) {
 2770 -		PyErr_SetString(PyExc_KeyError, "list not available");
 2771 -		return NULL;
 2772 -	}
 2773 -	l = PyList_New(0);
 2774 -	if (l == NULL)
 2775 -		return NULL;
 2776 -	BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
 2777 -	list = hexchat_list_get(ph, (char*)name);
 2778 -	if (list == NULL)
 2779 -		goto error;
 2780 -	fields = hexchat_list_fields(ph, (char*)name);
 2781 -	while (hexchat_list_next(ph, list)) {
 2782 -		PyObject *o = ListItem_New(name);
 2783 -		if (o == NULL || PyList_Append(l, o) == -1) {
 2784 -			Py_XDECREF(o);
 2785 -			goto error;
 2786 -		}
 2787 -		Py_DECREF(o); /* l is holding a reference */
 2788 -		for (i = 0; fields[i]; i++) {
 2789 -			const char *fld = fields[i]+1;
 2790 -			PyObject *attr = NULL;
 2791 -			const char *sattr;
 2792 -			int iattr;
 2793 -			time_t tattr;
 2794 -			switch(fields[i][0]) {
 2795 -			case 's':
 2796 -				sattr = hexchat_list_str(ph, list, (char*)fld);
 2797 -				attr = PyUnicode_FromString(sattr?sattr:"");
 2798 -				break;
 2799 -			case 'i':
 2800 -				iattr = hexchat_list_int(ph, list, (char*)fld);
 2801 -				attr = PyLong_FromLong((long)iattr);
 2802 -				break;
 2803 -			case 't':
 2804 -				tattr = hexchat_list_time(ph, list, (char*)fld);
 2805 -				attr = PyLong_FromLong((long)tattr);
 2806 -				break;
 2807 -			case 'p':
 2808 -				sattr = hexchat_list_str(ph, list, (char*)fld);
 2809 -				if (strcmp(fld, "context") == 0) {
 2810 -					attr = Context_FromContext(
 2811 -						(hexchat_context*)sattr);
 2812 -					break;
 2813 -				}
 2814 -			default: /* ignore unknown (newly added?) types */
 2815 -				continue;
 2816 -			}
 2817 -			if (attr == NULL)
 2818 -				goto error;
 2819 -			PyObject_SetAttrString(o, (char*)fld, attr); /* add reference on attr in o */
 2820 -			Py_DECREF(attr); /* make o own attr */
 2821 -		}
 2822 -	}
 2823 -	hexchat_list_free(ph, list);
 2824 -	goto exit;
 2825 -error:
 2826 -	if (list)
 2827 -		hexchat_list_free(ph, list);
 2828 -	Py_DECREF(l);
 2829 -	l = NULL;
 2830 -
 2831 -exit:
 2832 -	END_XCHAT_CALLS();
 2833 -	return l;
 2834 -}
 2835 -
 2836 -static PyObject *
 2837 -Module_xchat_get_lists(PyObject *self, PyObject *args)
 2838 -{
 2839 -	PyObject *l, *o;
 2840 -	const char *const *fields;
 2841 -	int i;
 2842 -	/* This function is thread safe, and returns statically
 2843 -	 * allocated data. */
 2844 -	fields = hexchat_list_fields(ph, "lists");
 2845 -	l = PyList_New(0);
 2846 -	if (l == NULL)
 2847 -		return NULL;
 2848 -	for (i = 0; fields[i]; i++) {
 2849 -		o = PyUnicode_FromString(fields[i]);
 2850 -		if (o == NULL || PyList_Append(l, o) == -1) {
 2851 -			Py_DECREF(l);
 2852 -			Py_XDECREF(o);
 2853 -			return NULL;
 2854 -		}
 2855 -		Py_DECREF(o); /* l is holding a reference */
 2856 -	}
 2857 -	return l;
 2858 -}
 2859 -
 2860 -static PyObject *
 2861 -Module_hexchat_nickcmp(PyObject *self, PyObject *args)
 2862 -{
 2863 -	char *s1, *s2;
 2864 -	if (!PyArg_ParseTuple(args, "ss:nickcmp", &s1, &s2))
 2865 -		return NULL;
 2866 -	return PyLong_FromLong((long) hexchat_nickcmp(ph, s1, s2));
 2867 -}
 2868 -
 2869 -static PyObject *
 2870 -Module_hexchat_strip(PyObject *self, PyObject *args)
 2871 -{
 2872 -	PyObject *result;
 2873 -	char *str, *str2;
 2874 -	int len = -1, flags = 1 | 2;
 2875 -	if (!PyArg_ParseTuple(args, "s|ii:strip", &str, &len, &flags))
 2876 -		return NULL;
 2877 -	str2 = hexchat_strip(ph, str, len, flags);
 2878 -	result = PyUnicode_FromString(str2);
 2879 -	hexchat_free(ph, str2);
 2880 -	return result;
 2881 -}
 2882 -
 2883 -static PyMethodDef Module_xchat_methods[] = {
 2884 -	{"command",		Module_hexchat_command,
 2885 -		METH_VARARGS},
 2886 -	{"prnt",		Module_xchat_prnt,
 2887 -		METH_VARARGS},
 2888 -	{"emit_print",		(PyCFunction)Module_hexchat_emit_print,
 2889 -		METH_VARARGS|METH_KEYWORDS},
 2890 -	{"get_info",		Module_hexchat_get_info,
 2891 -		METH_VARARGS},
 2892 -	{"get_prefs",		Module_xchat_get_prefs,
 2893 -		METH_VARARGS},
 2894 -	{"get_context",		Module_hexchat_get_context,
 2895 -		METH_NOARGS},
 2896 -	{"find_context",	(PyCFunction)Module_hexchat_find_context,
 2897 -		METH_VARARGS|METH_KEYWORDS},
 2898 -	{"set_pluginpref", Module_hexchat_pluginpref_set,
 2899 -		METH_VARARGS},
 2900 -	{"get_pluginpref", Module_hexchat_pluginpref_get,
 2901 -		METH_VARARGS},
 2902 -	{"del_pluginpref", Module_hexchat_pluginpref_delete,
 2903 -		METH_VARARGS},
 2904 -	{"list_pluginpref", Module_hexchat_pluginpref_list,
 2905 -		METH_VARARGS},
 2906 -	{"hook_command",	(PyCFunction)Module_hexchat_hook_command,
 2907 -		METH_VARARGS|METH_KEYWORDS},
 2908 -	{"hook_server",		(PyCFunction)Module_hexchat_hook_server,
 2909 -		METH_VARARGS|METH_KEYWORDS},
 2910 -	{"hook_server_attrs",		(PyCFunction)Module_hexchat_hook_server_attrs,
 2911 -		METH_VARARGS|METH_KEYWORDS},
 2912 -	{"hook_print",		(PyCFunction)Module_hexchat_hook_print,
 2913 -		METH_VARARGS|METH_KEYWORDS},
 2914 -	{"hook_print_attrs",		(PyCFunction)Module_hexchat_hook_print_attrs,
 2915 -		METH_VARARGS|METH_KEYWORDS},
 2916 -	{"hook_timer",		(PyCFunction)Module_hexchat_hook_timer,
 2917 -		METH_VARARGS|METH_KEYWORDS},
 2918 -	{"hook_unload",		(PyCFunction)Module_hexchat_hook_unload,
 2919 -		METH_VARARGS|METH_KEYWORDS},
 2920 -	{"unhook",		Module_hexchat_unhook,
 2921 -		METH_VARARGS},
 2922 -	{"get_list",		Module_xchat_get_list,
 2923 -		METH_VARARGS},
 2924 -	{"get_lists",		Module_xchat_get_lists,
 2925 -		METH_NOARGS},
 2926 -	{"nickcmp",		Module_hexchat_nickcmp,
 2927 -		METH_VARARGS},
 2928 -	{"strip",		Module_hexchat_strip,
 2929 -		METH_VARARGS},
 2930 -	{NULL, NULL}
 2931 -};
 2932 -
 2933 -#ifdef IS_PY3K
 2934 -static struct PyModuleDef moduledef = {
 2935 -	PyModuleDef_HEAD_INIT,
 2936 -	"hexchat",     /* m_name */
 2937 -	"HexChat Scripting Interface",  /* m_doc */
 2938 -	-1,                  /* m_size */
 2939 -	Module_xchat_methods,    /* m_methods */
 2940 -	NULL,                /* m_reload */
 2941 -	NULL,                /* m_traverse */
 2942 -	NULL,                /* m_clear */
 2943 -	NULL,                /* m_free */
 2944 -};
 2945 -
 2946 -static struct PyModuleDef xchat_moduledef = {
 2947 -	PyModuleDef_HEAD_INIT,
 2948 -	"xchat",     /* m_name */
 2949 -	"HexChat Scripting Interface",  /* m_doc */
 2950 -	-1,                  /* m_size */
 2951 -	Module_xchat_methods,    /* m_methods */
 2952 -	NULL,                /* m_reload */
 2953 -	NULL,                /* m_traverse */
 2954 -	NULL,                /* m_clear */
 2955 -	NULL,                /* m_free */
 2956 -};
 2957 -#endif
 2958 -
 2959 -static PyObject *
 2960 -moduleinit_hexchat(void)
 2961 -{
 2962 -	PyObject *hm;
 2963 -#ifdef IS_PY3K
 2964 -		hm = PyModule_Create(&moduledef);
 2965 -#else
 2966 -    hm = Py_InitModule3("hexchat", Module_xchat_methods, "HexChat Scripting Interface");
 2967 -#endif
 2968 -
 2969 -	PyModule_AddIntConstant(hm, "EAT_NONE", HEXCHAT_EAT_NONE);
 2970 -	PyModule_AddIntConstant(hm, "EAT_HEXCHAT", HEXCHAT_EAT_HEXCHAT);
 2971 -	PyModule_AddIntConstant(hm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT); /* for compat */
 2972 -	PyModule_AddIntConstant(hm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
 2973 -	PyModule_AddIntConstant(hm, "EAT_ALL", HEXCHAT_EAT_ALL);
 2974 -	PyModule_AddIntConstant(hm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
 2975 -	PyModule_AddIntConstant(hm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
 2976 -	PyModule_AddIntConstant(hm, "PRI_NORM", HEXCHAT_PRI_NORM);
 2977 -	PyModule_AddIntConstant(hm, "PRI_LOW", HEXCHAT_PRI_LOW);
 2978 -	PyModule_AddIntConstant(hm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
 2979 -
 2980 -	PyObject_SetAttrString(hm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
 2981 -
 2982 -	return hm;
 2983 -}
 2984 -
 2985 -static PyObject *
 2986 -moduleinit_xchat(void)
 2987 -{
 2988 -	PyObject *xm;
 2989 -#ifdef IS_PY3K
 2990 -		xm = PyModule_Create(&xchat_moduledef);
 2991 -#else
 2992 -    xm = Py_InitModule3("xchat", Module_xchat_methods, "HexChat Scripting Interface");
 2993 -#endif
 2994 -
 2995 -	PyModule_AddIntConstant(xm, "EAT_NONE", HEXCHAT_EAT_NONE);
 2996 -	PyModule_AddIntConstant(xm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT);
 2997 -	PyModule_AddIntConstant(xm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
 2998 -	PyModule_AddIntConstant(xm, "EAT_ALL", HEXCHAT_EAT_ALL);
 2999 -	PyModule_AddIntConstant(xm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
 3000 -	PyModule_AddIntConstant(xm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
 3001 -	PyModule_AddIntConstant(xm, "PRI_NORM", HEXCHAT_PRI_NORM);
 3002 -	PyModule_AddIntConstant(xm, "PRI_LOW", HEXCHAT_PRI_LOW);
 3003 -	PyModule_AddIntConstant(xm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
 3004 -
 3005 -	PyObject_SetAttrString(xm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
 3006 -
 3007 -	return xm;
 3008 -}
 3009 -
 3010 -#ifdef IS_PY3K
 3011 -PyMODINIT_FUNC
 3012 -PyInit_hexchat(void)
 3013 -{
 3014 -    return moduleinit_hexchat();
 3015 -}
 3016 -PyMODINIT_FUNC
 3017 -PyInit_xchat(void)
 3018 -{
 3019 -    return moduleinit_xchat();
 3020 -}
 3021 -#else
 3022 -PyMODINIT_FUNC
 3023 -inithexchat(void)
 3024 -{
 3025 -		moduleinit_hexchat();
 3026 -}
 3027 -PyMODINIT_FUNC
 3028 -initxchat(void)
 3029 -{
 3030 -		moduleinit_xchat();
 3031 -}
 3032 -#endif
 3033 -
 3034 -/* ===================================================================== */
 3035 -/* Python interactive interpreter functions */
 3036 -
 3037 -static void
 3038 -IInterp_Exec(char *command)
 3039 -{
 3040 -	PyObject *m, *d, *o;
 3041 -	char *buffer;
 3042 -	int len;
 3043 -
 3044 -	BEGIN_PLUGIN(interp_plugin);
 3045 -
 3046 -	m = PyImport_AddModule("__main__");
 3047 -	if (m == NULL) {
 3048 -		hexchat_print(ph, "Can't get __main__ module");
 3049 -		goto fail;
 3050 -	}
 3051 -	d = PyModule_GetDict(m);
 3052 -	len = strlen(command);
 3053 -
 3054 -	buffer = g_malloc(len + 2);
 3055 -	memcpy(buffer, command, len);
 3056 -	buffer[len] = '\n';
 3057 -	buffer[len+1] = 0;
 3058 -	PyRun_SimpleString("import hexchat");
 3059 -	o = PyRun_StringFlags(buffer, Py_single_input, d, d, NULL);
 3060 -	g_free(buffer);
 3061 -	if (o == NULL) {
 3062 -		PyErr_Print();
 3063 -		goto fail;
 3064 -	}
 3065 -	Py_DECREF(o);
 3066 -
 3067 -fail:
 3068 -	END_PLUGIN(interp_plugin);
 3069 -	return;
 3070 -}
 3071 -
 3072 -static int
 3073 -IInterp_Cmd(char *word[], char *word_eol[], void *userdata)
 3074 -{
 3075 -	char *channel = (char *) hexchat_get_info(ph, "channel");
 3076 -	if (channel && channel[0] == '>' && strcmp(channel, ">>python<<") == 0) {
 3077 -		hexchat_printf(ph, ">>> %s\n", word_eol[1]);
 3078 -		IInterp_Exec(word_eol[1]);
 3079 -		return HEXCHAT_EAT_HEXCHAT;
 3080 -	}
 3081 -	return HEXCHAT_EAT_NONE;
 3082 -}
 3083 -
 3084 -
 3085 -/* ===================================================================== */
 3086 -/* Python command handling */
 3087 -
 3088 -static void
 3089 -Command_PyList(void)
 3090 -{
 3091 -	GSList *list;
 3092 -	list = plugin_list;
 3093 -	if (list == NULL) {
 3094 -		hexchat_print(ph, "No python modules loaded");
 3095 -	} else {
 3096 -		hexchat_print(ph,
 3097 -		   "Name         Version  Filename             Description\n"
 3098 -		   "----         -------  --------             -----------\n");
 3099 -		while (list != NULL) {
 3100 -			PluginObject *plg = (PluginObject *) list->data;
 3101 -			char *basename = g_path_get_basename(plg->filename);
 3102 -			hexchat_printf(ph, "%-12s %-8s %-20s %-10s\n",
 3103 -				     plg->name,
 3104 -				     *plg->version ? plg->version
 3105 -				     		  : "<none>",
 3106 -				     basename,
 3107 -				     *plg->description ? plg->description
 3108 -				     		      : "<none>");
 3109 -			g_free(basename);
 3110 -			list = list->next;
 3111 -		}
 3112 -		hexchat_print(ph, "\n");
 3113 -	}
 3114 -}
 3115 -
 3116 -static void
 3117 -Command_PyLoad(char *filename)
 3118 -{
 3119 -	PyObject *plugin;
 3120 -	RELEASE_XCHAT_LOCK();
 3121 -	plugin = Plugin_New(filename, xchatout);
 3122 -	ACQUIRE_XCHAT_LOCK();
 3123 -	if (plugin)
 3124 -		plugin_list = g_slist_append(plugin_list, plugin);
 3125 -}
 3126 -
 3127 -static void
 3128 -Command_PyUnload(char *name)
 3129 -{
 3130 -	PluginObject *plugin = Plugin_ByString(name);
 3131 -	if (!plugin) {
 3132 -		hexchat_print(ph, "Can't find a python plugin with that name");
 3133 -	} else {
 3134 -		BEGIN_PLUGIN(plugin);
 3135 -		Plugin_Delete((PyObject*)plugin);
 3136 -		END_PLUGIN(plugin);
 3137 -		plugin_list = g_slist_remove(plugin_list, plugin);
 3138 -	}
 3139 -}
 3140 -
 3141 -static void
 3142 -Command_PyReload(char *name)
 3143 -{
 3144 -	PluginObject *plugin = Plugin_ByString(name);
 3145 -	if (!plugin) {
 3146 -		hexchat_print(ph, "Can't find a python plugin with that name");
 3147 -	} else {
 3148 -		char *filename = g_strdup(plugin->filename);
 3149 -		Command_PyUnload(filename);
 3150 -		Command_PyLoad(filename);
 3151 -		g_free(filename);
 3152 -	}
 3153 -}
 3154 -
 3155 -static void
 3156 -Command_PyAbout(void)
 3157 -{
 3158 -	hexchat_print(ph, about);
 3159 -}
 3160 -
 3161 -static int
 3162 -Command_Py(char *word[], char *word_eol[], void *userdata)
 3163 -{
 3164 -	char *cmd = word[2];
 3165 -	int ok = 0;
 3166 -	if (strcasecmp(cmd, "LIST") == 0) {
 3167 -		ok = 1;
 3168 -		Command_PyList();
 3169 -	} else if (strcasecmp(cmd, "EXEC") == 0) {
 3170 -		if (word[3][0]) {
 3171 -			ok = 1;
 3172 -			IInterp_Exec(word_eol[3]);
 3173 -		}
 3174 -	} else if (strcasecmp(cmd, "LOAD") == 0) {
 3175 -		if (word[3][0]) {
 3176 -			ok = 1;
 3177 -			Command_PyLoad(word[3]);
 3178 -		}
 3179 -	} else if (strcasecmp(cmd, "UNLOAD") == 0) {
 3180 -		if (word[3][0]) {
 3181 -			ok = 1;
 3182 -			Command_PyUnload(word[3]);
 3183 -		}
 3184 -	} else if (strcasecmp(cmd, "RELOAD") == 0) {
 3185 -		if (word[3][0]) {
 3186 -			ok = 1;
 3187 -			Command_PyReload(word[3]);
 3188 -		}
 3189 -	} else if (strcasecmp(cmd, "CONSOLE") == 0) {
 3190 -		ok = 1;
 3191 -		hexchat_command(ph, "QUERY >>python<<");
 3192 -	} else if (strcasecmp(cmd, "ABOUT") == 0) {
 3193 -		ok = 1;
 3194 -		Command_PyAbout();
 3195 -	}
 3196 -	if (!ok)
 3197 -		hexchat_print(ph, usage);
 3198 -	return HEXCHAT_EAT_ALL;
 3199 -}
 3200 -
 3201 -static int
 3202 -Command_Load(char *word[], char *word_eol[], void *userdata)
 3203 -{
 3204 -	int len = strlen(word[2]);
 3205 -	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
 3206 -		Command_PyLoad(word[2]);
 3207 -		return HEXCHAT_EAT_HEXCHAT;
 3208 -	}
 3209 -	return HEXCHAT_EAT_NONE;
 3210 -}
 3211 -
 3212 -static int
 3213 -Command_Reload(char *word[], char *word_eol[], void *userdata)
 3214 -{
 3215 -	int len = strlen(word[2]);
 3216 -	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
 3217 -	Command_PyReload(word[2]);
 3218 -	return HEXCHAT_EAT_HEXCHAT;
 3219 -	}
 3220 -	return HEXCHAT_EAT_NONE;
 3221 -}
 3222 -
 3223 -static int
 3224 -Command_Unload(char *word[], char *word_eol[], void *userdata)
 3225 -{
 3226 -	int len = strlen(word[2]);
 3227 -	if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
 3228 -		Command_PyUnload(word[2]);
 3229 -		return HEXCHAT_EAT_HEXCHAT;
 3230 -	}
 3231 -	return HEXCHAT_EAT_NONE;
 3232 -}
 3233 -
 3234 -/* ===================================================================== */
 3235 -/* Autoload function */
 3236 -
 3237 -/* ===================================================================== */
 3238 -/* (De)initialization functions */
 3239 -
 3240 -static int initialized = 0;
 3241 -static int reinit_tried = 0;
 3242 -
 3243 -void
 3244 -hexchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
 3245 -{
 3246 -	*name = "Python";
 3247 -	*version = VERSION;
 3248 -	*desc = "Python scripting interface";
 3249 -   if (reserved)
 3250 -      *reserved = NULL;
 3251 -}
 3252 -
 3253 -int
 3254 -hexchat_plugin_init(hexchat_plugin *plugin_handle,
 3255 -		  char **plugin_name,
 3256 -		  char **plugin_desc,
 3257 -		  char **plugin_version,
 3258 -		  char *arg)
 3259 -{
 3260 -#ifdef IS_PY3K
 3261 -	wchar_t *argv[] = { L"<hexchat>", 0 };
 3262 -#else
 3263 -	char *argv[] = { "<hexchat>", 0 };
 3264 -#endif
 3265 -
 3266 -	ph = plugin_handle;
 3267 -
 3268 -	/* Block double initalization. */
 3269 -	if (initialized != 0) {
 3270 -		hexchat_print(ph, "Python interface already loaded");
 3271 -		/* deinit is called even when init fails, so keep track
 3272 -		 * of a reinit failure. */
 3273 -		reinit_tried++;
 3274 -		return 0;
 3275 -	}
 3276 -	initialized = 1;
 3277 -
 3278 -	*plugin_name = "Python";
 3279 -	*plugin_version = VERSION;
 3280 -
 3281 -	/* FIXME You can't free this since it's used as long as the plugin's
 3282 -	 * loaded, but if you unload it, everything belonging to the plugin is
 3283 -	 * supposed to be freed anyway.
 3284 -	 */
 3285 -	*plugin_desc = g_strdup_printf ("Python %d scripting interface", PY_MAJOR_VERSION);
 3286 -
 3287 -	/* Initialize python. */
 3288 -#ifdef IS_PY3K
 3289 -	Py_SetProgramName(L"hexchat");
 3290 -	PyImport_AppendInittab("hexchat", PyInit_hexchat);
 3291 -	PyImport_AppendInittab("xchat", PyInit_xchat);
 3292 -#else
 3293 -	Py_SetProgramName("hexchat");
 3294 -	PyImport_AppendInittab("hexchat", inithexchat);
 3295 -	PyImport_AppendInittab("xchat", initxchat);
 3296 -#endif
 3297 -	Py_Initialize();
 3298 -	PySys_SetArgv(1, argv);
 3299 -
 3300 -	xchatout_buffer = g_string_new (NULL);
 3301 -	xchatout = XChatOut_New();
 3302 -	if (xchatout == NULL) {
 3303 -		hexchat_print(ph, "Can't allocate xchatout object");
 3304 -		return 0;
 3305 -	}
 3306 -
 3307 -#ifdef WITH_THREAD
 3308 -	PyEval_InitThreads();
 3309 -	xchat_lock = PyThread_allocate_lock();
 3310 -	if (xchat_lock == NULL) {
 3311 -		hexchat_print(ph, "Can't allocate hexchat lock");
 3312 -		Py_DECREF(xchatout);
 3313 -		xchatout = NULL;
 3314 -		return 0;
 3315 -	}
 3316 -#endif
 3317 -
 3318 -	main_tstate = PyEval_SaveThread();
 3319 -
 3320 -	interp_plugin = Plugin_New(NULL, xchatout);
 3321 -	if (interp_plugin == NULL) {
 3322 -		hexchat_print(ph, "Plugin_New() failed.\n");
 3323 -#ifdef WITH_THREAD
 3324 -		PyThread_free_lock(xchat_lock);
 3325 -#endif
 3326 -		Py_DECREF(xchatout);
 3327 -		xchatout = NULL;
 3328 -		return 0;
 3329 -	}
 3330 -
 3331 -
 3332 -	hexchat_hook_command(ph, "", HEXCHAT_PRI_NORM, IInterp_Cmd, 0, 0);
 3333 -	hexchat_hook_command(ph, "PY", HEXCHAT_PRI_NORM, Command_Py, usage, 0);
 3334 -	hexchat_hook_command(ph, "LOAD", HEXCHAT_PRI_NORM, Command_Load, 0, 0);
 3335 -	hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, Command_Unload, 0, 0);
 3336 -	hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, Command_Reload, 0, 0);
 3337 -#ifdef WITH_THREAD
 3338 -	thread_timer = hexchat_hook_timer(ph, 300, Callback_ThreadTimer, NULL);
 3339 -#endif
 3340 -
 3341 -	hexchat_print(ph, "Python interface loaded\n");
 3342 -
 3343 -	Util_Autoload();
 3344 -	return 1;
 3345 -}
 3346 -
 3347 -int
 3348 -hexchat_plugin_deinit(void)
 3349 -{
 3350 -	GSList *list;
 3351 -
 3352 -	/* A reinitialization was tried. Just give up and live the
 3353 -	 * environment as is. We are still alive. */
 3354 -	if (reinit_tried) {
 3355 -		reinit_tried--;
 3356 -		return 1;
 3357 -	}
 3358 -
 3359 -	list = plugin_list;
 3360 -	while (list != NULL) {
 3361 -		PyObject *plugin = (PyObject *) list->data;
 3362 -		BEGIN_PLUGIN(plugin);
 3363 -		Plugin_Delete(plugin);
 3364 -		END_PLUGIN(plugin);
 3365 -		list = list->next;
 3366 -	}
 3367 -	g_slist_free(plugin_list);
 3368 -	plugin_list = NULL;
 3369 -
 3370 -	/* Reset xchatout buffer. */
 3371 -	g_string_free (xchatout_buffer, TRUE);
 3372 -	xchatout_buffer = NULL;
 3373 -
 3374 -	if (interp_plugin) {
 3375 -		PyThreadState *tstate = ((PluginObject*)interp_plugin)->tstate;
 3376 -		PyThreadState_Swap(tstate);
 3377 -		Py_EndInterpreter(tstate);
 3378 -		Py_DECREF(interp_plugin);
 3379 -		interp_plugin = NULL;
 3380 -	}
 3381 -
 3382 -	/* Switch back to the main thread state. */
 3383 -	if (main_tstate) {
 3384 -		PyEval_RestoreThread(main_tstate);
 3385 -		PyThreadState_Swap(main_tstate);
 3386 -		main_tstate = NULL;
 3387 -	}
 3388 -	Py_Finalize();
 3389 -
 3390 -#ifdef WITH_THREAD
 3391 -	if (thread_timer != NULL) {
 3392 -		hexchat_unhook(ph, thread_timer);
 3393 -		thread_timer = NULL;
 3394 -	}
 3395 -	PyThread_free_lock(xchat_lock);
 3396 -#endif
 3397 -
 3398 -	hexchat_print(ph, "Python interface unloaded\n");
 3399 -	initialized = 0;
 3400 -
 3401 -	return 1;
 3402 -}
 3403 -
 3404 diff --git a/plugins/python/python.def b/plugins/python/python.def
 3405 index 6ce04e98..e560f50f 100644
 3406 --- a/plugins/python/python.def
 3407 +++ b/plugins/python/python.def
 3408 @@ -1,4 +1,3 @@
 3409  EXPORTS 
 3410  hexchat_plugin_init 
 3411  hexchat_plugin_deinit 
 3412 -hexchat_plugin_get_info 
 3413 diff --git a/plugins/python/python.py b/plugins/python/python.py
 3414 new file mode 100644
 3415 index 00000000..30694802
 3416 --- /dev/null
 3417 +++ b/plugins/python/python.py
 3418 @@ -0,0 +1,554 @@
 3419 +from __future__ import print_function
 3420 +
 3421 +import importlib
 3422 +import os
 3423 +import pydoc
 3424 +import signal
 3425 +import sys
 3426 +import traceback
 3427 +import weakref
 3428 +from contextlib import contextmanager
 3429 +
 3430 +from _hexchat_embedded import ffi, lib
 3431 +
 3432 +if sys.version_info < (3, 0):
 3433 +    from io import BytesIO as HelpEater
 3434 +else:
 3435 +    from io import StringIO as HelpEater
 3436 +
 3437 +if not hasattr(sys, 'argv'):
 3438 +    sys.argv = ['<hexchat>']
 3439 +
 3440 +VERSION = b'2.0'  # Sync with hexchat.__version__
 3441 +PLUGIN_NAME = ffi.new('char[]', b'Python')
 3442 +PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
 3443 +PLUGIN_VERSION = ffi.new('char[]', VERSION)
 3444 +
 3445 +# TODO: Constants should be screaming snake case
 3446 +hexchat = None
 3447 +local_interp = None
 3448 +hexchat_stdout = None
 3449 +plugins = set()
 3450 +
 3451 +
 3452 +@contextmanager
 3453 +def redirected_stdout():
 3454 +    sys.stdout = sys.__stdout__
 3455 +    sys.stderr = sys.__stderr__
 3456 +    yield
 3457 +    sys.stdout = hexchat_stdout
 3458 +    sys.stderr = hexchat_stdout
 3459 +
 3460 +
 3461 +if os.getenv('HEXCHAT_LOG_PYTHON'):
 3462 +    def log(*args):
 3463 +        with redirected_stdout():
 3464 +            print(*args)
 3465 +
 3466 +else:
 3467 +    def log(*args):
 3468 +        pass
 3469 +
 3470 +
 3471 +class Stdout:
 3472 +    def __init__(self):
 3473 +        self.buffer = bytearray()
 3474 +
 3475 +    def write(self, string):
 3476 +        string = string.encode()
 3477 +        idx = string.rfind(b'\n')
 3478 +        if idx != -1:
 3479 +            self.buffer += string[:idx]
 3480 +            lib.hexchat_print(lib.ph, bytes(self.buffer))
 3481 +            self.buffer = bytearray(string[idx + 1:])
 3482 +        else:
 3483 +            self.buffer += string
 3484 +
 3485 +    def isatty(self):
 3486 +        return False
 3487 +
 3488 +
 3489 +class Attribute:
 3490 +    def __init__(self):
 3491 +        self.time = 0
 3492 +
 3493 +    def __repr__(self):
 3494 +        return '<Attribute object at {}>'.format(id(self))
 3495 +
 3496 +
 3497 +class Hook:
 3498 +    def __init__(self, plugin, callback, userdata, is_unload):
 3499 +        self.is_unload = is_unload
 3500 +        self.plugin = weakref.proxy(plugin)
 3501 +        self.callback = callback
 3502 +        self.userdata = userdata
 3503 +        self.hexchat_hook = None
 3504 +        self.handle = ffi.new_handle(weakref.proxy(self))
 3505 +
 3506 +    def __del__(self):
 3507 +        log('Removing hook', id(self))
 3508 +        if self.is_unload is False:
 3509 +            assert self.hexchat_hook is not None
 3510 +            lib.hexchat_unhook(lib.ph, self.hexchat_hook)
 3511 +
 3512 +
 3513 +if sys.version_info[0] == 2:
 3514 +    def compile_file(data, filename):
 3515 +        return compile(data, filename, 'exec', dont_inherit=True)
 3516 +
 3517 +
 3518 +    def compile_line(string):
 3519 +        try:
 3520 +            return compile(string, '<string>', 'eval', dont_inherit=True)
 3521 +
 3522 +        except SyntaxError:
 3523 +            # For some reason `print` is invalid for eval
 3524 +            # This will hide any return value though
 3525 +            return compile(string, '<string>', 'exec', dont_inherit=True)
 3526 +else:
 3527 +    def compile_file(data, filename):
 3528 +        return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
 3529 +
 3530 +
 3531 +    def compile_line(string):
 3532 +        # newline appended to solve unexpected EOF issues
 3533 +        return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True)
 3534 +
 3535 +
 3536 +class Plugin:
 3537 +    def __init__(self):
 3538 +        self.ph = None
 3539 +        self.name = ''
 3540 +        self.filename = ''
 3541 +        self.version = ''
 3542 +        self.description = ''
 3543 +        self.hooks = set()
 3544 +        self.globals = {
 3545 +            '__plugin': weakref.proxy(self),
 3546 +            '__name__': '__main__',
 3547 +        }
 3548 +
 3549 +    def add_hook(self, callback, userdata, is_unload=False):
 3550 +        hook = Hook(self, callback, userdata, is_unload=is_unload)
 3551 +        self.hooks.add(hook)
 3552 +        return hook
 3553 +
 3554 +    def remove_hook(self, hook):
 3555 +        for h in self.hooks:
 3556 +            if id(h) == hook:
 3557 +                ud = h.userdata
 3558 +                self.hooks.remove(h)
 3559 +                return ud
 3560 +
 3561 +        log('Hook not found')
 3562 +        return None
 3563 +
 3564 +    def loadfile(self, filename):
 3565 +        try:
 3566 +            self.filename = filename
 3567 +            with open(filename) as f:
 3568 +                data = f.read()
 3569 +            compiled = compile_file(data, filename)
 3570 +            exec(compiled, self.globals)
 3571 +
 3572 +            try:
 3573 +                self.name = self.globals['__module_name__']
 3574 +
 3575 +            except KeyError:
 3576 +                lib.hexchat_print(lib.ph, b'Failed to load module: __module_name__ must be set')
 3577 +
 3578 +                return False
 3579 +
 3580 +            self.version = self.globals.get('__module_version__', '')
 3581 +            self.description = self.globals.get('__module_description__', '')
 3582 +            self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), self.name.encode(),
 3583 +                                                self.description.encode(), self.version.encode(), ffi.NULL)
 3584 +
 3585 +        except Exception as e:
 3586 +            lib.hexchat_print(lib.ph, 'Failed to load module: {}'.format(e).encode())
 3587 +            traceback.print_exc()
 3588 +            return False
 3589 +
 3590 +        return True
 3591 +
 3592 +    def __del__(self):
 3593 +        log('unloading', self.filename)
 3594 +        for hook in self.hooks:
 3595 +            if hook.is_unload is True:
 3596 +                try:
 3597 +                    hook.callback(hook.userdata)
 3598 +
 3599 +                except Exception as e:
 3600 +                    log('Failed to run hook:', e)
 3601 +                    traceback.print_exc()
 3602 +
 3603 +        del self.hooks
 3604 +        if self.ph is not None:
 3605 +            lib.hexchat_plugingui_remove(lib.ph, self.ph)
 3606 +
 3607 +
 3608 +if sys.version_info[0] == 2:
 3609 +    def __decode(string):
 3610 +        return string
 3611 +
 3612 +else:
 3613 +    def __decode(string):
 3614 +        return string.decode()
 3615 +
 3616 +
 3617 +# There can be empty entries between non-empty ones so find the actual last value
 3618 +def wordlist_len(words):
 3619 +    for i in range(31, 1, -1):
 3620 +        if ffi.string(words[i]):
 3621 +            return i
 3622 +
 3623 +    return 0
 3624 +
 3625 +
 3626 +def create_wordlist(words):
 3627 +    size = wordlist_len(words)
 3628 +    return [__decode(ffi.string(words[i])) for i in range(1, size + 1)]
 3629 +
 3630 +
 3631 +# This function only exists for compat reasons with the C plugin
 3632 +# It turns the word list from print hooks into a word_eol list
 3633 +# This makes no sense to do...
 3634 +def create_wordeollist(words):
 3635 +    words = reversed(words)
 3636 +    accum = None
 3637 +    ret = []
 3638 +    for word in words:
 3639 +        if accum is None:
 3640 +            accum = word
 3641 +
 3642 +        elif word:
 3643 +            last = accum
 3644 +            accum = ' '.join((word, last))
 3645 +
 3646 +        ret.insert(0, accum)
 3647 +
 3648 +    return ret
 3649 +
 3650 +
 3651 +def to_cb_ret(value):
 3652 +    if value is None:
 3653 +        return 0
 3654 +
 3655 +    return int(value)
 3656 +
 3657 +
 3658 +@ffi.def_extern()
 3659 +def _on_command_hook(word, word_eol, userdata):
 3660 +    hook = ffi.from_handle(userdata)
 3661 +    word = create_wordlist(word)
 3662 +    word_eol = create_wordlist(word_eol)
 3663 +    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
 3664 +
 3665 +
 3666 +@ffi.def_extern()
 3667 +def _on_print_hook(word, userdata):
 3668 +    hook = ffi.from_handle(userdata)
 3669 +    word = create_wordlist(word)
 3670 +    word_eol = create_wordeollist(word)
 3671 +    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
 3672 +
 3673 +
 3674 +@ffi.def_extern()
 3675 +def _on_print_attrs_hook(word, attrs, userdata):
 3676 +    hook = ffi.from_handle(userdata)
 3677 +    word = create_wordlist(word)
 3678 +    word_eol = create_wordeollist(word)
 3679 +    attr = Attribute()
 3680 +    attr.time = attrs.server_time_utc
 3681 +    return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
 3682 +
 3683 +
 3684 +@ffi.def_extern()
 3685 +def _on_server_hook(word, word_eol, userdata):
 3686 +    hook = ffi.from_handle(userdata)
 3687 +    word = create_wordlist(word)
 3688 +    word_eol = create_wordlist(word_eol)
 3689 +    return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
 3690 +
 3691 +
 3692 +@ffi.def_extern()
 3693 +def _on_server_attrs_hook(word, word_eol, attrs, userdata):
 3694 +    hook = ffi.from_handle(userdata)
 3695 +    word = create_wordlist(word)
 3696 +    word_eol = create_wordlist(word_eol)
 3697 +    attr = Attribute()
 3698 +    attr.time = attrs.server_time_utc
 3699 +    return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
 3700 +
 3701 +
 3702 +@ffi.def_extern()
 3703 +def _on_timer_hook(userdata):
 3704 +    hook = ffi.from_handle(userdata)
 3705 +    if hook.callback(hook.userdata) is True:
 3706 +        return 1
 3707 +
 3708 +    hook.is_unload = True  # Don't unhook
 3709 +    for h in hook.plugin.hooks:
 3710 +        if h == hook:
 3711 +            hook.plugin.hooks.remove(h)
 3712 +            break
 3713 +
 3714 +    return 0
 3715 +
 3716 +
 3717 +@ffi.def_extern(error=3)
 3718 +def _on_say_command(word, word_eol, userdata):
 3719 +    channel = ffi.string(lib.hexchat_get_info(lib.ph, b'channel'))
 3720 +    if channel == b'>>python<<':
 3721 +        python = ffi.string(word_eol[1])
 3722 +        lib.hexchat_print(lib.ph, b'>>> ' + python)
 3723 +        exec_in_interp(__decode(python))
 3724 +        return 1
 3725 +
 3726 +    return 0
 3727 +
 3728 +
 3729 +def load_filename(filename):
 3730 +    filename = os.path.expanduser(filename)
 3731 +    if not os.path.isabs(filename):
 3732 +        configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
 3733 +
 3734 +        filename = os.path.join(configdir, 'addons', filename)
 3735 +
 3736 +    if filename and not any(plugin.filename == filename for plugin in plugins):
 3737 +        plugin = Plugin()
 3738 +        if plugin.loadfile(filename):
 3739 +            plugins.add(plugin)
 3740 +            return True
 3741 +
 3742 +    return False
 3743 +
 3744 +
 3745 +def unload_name(name):
 3746 +    if name:
 3747 +        for plugin in plugins:
 3748 +            if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
 3749 +                plugins.remove(plugin)
 3750 +                return True
 3751 +
 3752 +    return False
 3753 +
 3754 +
 3755 +def reload_name(name):
 3756 +    if name:
 3757 +        for plugin in plugins:
 3758 +            if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
 3759 +                filename = plugin.filename
 3760 +                plugins.remove(plugin)
 3761 +                return load_filename(filename)
 3762 +
 3763 +    return False
 3764 +
 3765 +
 3766 +@contextmanager
 3767 +def change_cwd(path):
 3768 +    old_cwd = os.getcwd()
 3769 +    os.chdir(path)
 3770 +    yield
 3771 +    os.chdir(old_cwd)
 3772 +
 3773 +
 3774 +def autoload():
 3775 +    configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
 3776 +    addondir = os.path.join(configdir, 'addons')
 3777 +    try:
 3778 +        with change_cwd(addondir):  # Maintaining old behavior
 3779 +            for f in os.listdir(addondir):
 3780 +                if f.endswith('.py'):
 3781 +                    log('Autoloading', f)
 3782 +                    # TODO: Set cwd
 3783 +                    load_filename(os.path.join(addondir, f))
 3784 +
 3785 +    except FileNotFoundError as e:
 3786 +        log('Autoload failed', e)
 3787 +
 3788 +
 3789 +def list_plugins():
 3790 +    if not plugins:
 3791 +        lib.hexchat_print(lib.ph, b'No python modules loaded')
 3792 +        return
 3793 +
 3794 +    tbl_headers = [b'Name', b'Version', b'Filename', b'Description']
 3795 +    tbl = [
 3796 +        tbl_headers,
 3797 +        [(b'-' * len(s)) for s in tbl_headers]
 3798 +    ]
 3799 +
 3800 +    for plugin in plugins:
 3801 +        basename = os.path.basename(plugin.filename).encode()
 3802 +        name = plugin.name.encode()
 3803 +        version = plugin.version.encode() if plugin.version else b'<none>'
 3804 +        description = plugin.description.encode() if plugin.description else b'<none>'
 3805 +        tbl.append((name, version, basename, description))
 3806 +
 3807 +    column_sizes = [
 3808 +        max(len(item) for item in column)
 3809 +        for column in zip(*tbl)
 3810 +    ]
 3811 +
 3812 +    for row in tbl:
 3813 +        lib.hexchat_print(lib.ph, b' '.join(item.ljust(column_sizes[i])
 3814 +                                            for i, item in enumerate(row)))
 3815 +    lib.hexchat_print(lib.ph, b'')
 3816 +
 3817 +
 3818 +def exec_in_interp(python):
 3819 +    global local_interp
 3820 +
 3821 +    if not python:
 3822 +        return
 3823 +
 3824 +    if local_interp is None:
 3825 +        local_interp = Plugin()
 3826 +        local_interp.locals = {}
 3827 +        local_interp.globals['hexchat'] = hexchat
 3828 +
 3829 +    code = compile_line(python)
 3830 +    try:
 3831 +        ret = eval(code, local_interp.globals, local_interp.locals)
 3832 +        if ret is not None:
 3833 +            lib.hexchat_print(lib.ph, '{}'.format(ret).encode())
 3834 +
 3835 +    except Exception as e:
 3836 +        traceback.print_exc(file=hexchat_stdout)
 3837 +
 3838 +
 3839 +@ffi.def_extern()
 3840 +def _on_load_command(word, word_eol, userdata):
 3841 +    filename = ffi.string(word[2])
 3842 +    if filename.endswith(b'.py'):
 3843 +        load_filename(__decode(filename))
 3844 +        return 3
 3845 +
 3846 +    return 0
 3847 +
 3848 +
 3849 +@ffi.def_extern()
 3850 +def _on_unload_command(word, word_eol, userdata):
 3851 +    filename = ffi.string(word[2])
 3852 +    if filename.endswith(b'.py'):
 3853 +        unload_name(__decode(filename))
 3854 +        return 3
 3855 +
 3856 +    return 0
 3857 +
 3858 +
 3859 +@ffi.def_extern()
 3860 +def _on_reload_command(word, word_eol, userdata):
 3861 +    filename = ffi.string(word[2])
 3862 +    if filename.endswith(b'.py'):
 3863 +        reload_name(__decode(filename))
 3864 +        return 3
 3865 +
 3866 +    return 0
 3867 +
 3868 +
 3869 +@ffi.def_extern(error=3)
 3870 +def _on_py_command(word, word_eol, userdata):
 3871 +    subcmd = __decode(ffi.string(word[2])).lower()
 3872 +
 3873 +    if subcmd == 'exec':
 3874 +        python = __decode(ffi.string(word_eol[3]))
 3875 +        exec_in_interp(python)
 3876 +
 3877 +    elif subcmd == 'load':
 3878 +        filename = __decode(ffi.string(word[3]))
 3879 +        load_filename(filename)
 3880 +
 3881 +    elif subcmd == 'unload':
 3882 +        name = __decode(ffi.string(word[3]))
 3883 +        if not unload_name(name):
 3884 +            lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
 3885 +
 3886 +    elif subcmd == 'reload':
 3887 +        name = __decode(ffi.string(word[3]))
 3888 +        if not reload_name(name):
 3889 +            lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
 3890 +
 3891 +    elif subcmd == 'console':
 3892 +        lib.hexchat_command(lib.ph, b'QUERY >>python<<')
 3893 +
 3894 +    elif subcmd == 'list':
 3895 +        list_plugins()
 3896 +
 3897 +    elif subcmd == 'about':
 3898 +        lib.hexchat_print(lib.ph, b'HexChat Python interface version ' + VERSION)
 3899 +
 3900 +    else:
 3901 +        lib.hexchat_command(lib.ph, b'HELP PY')
 3902 +
 3903 +    return 3
 3904 +
 3905 +
 3906 +@ffi.def_extern()
 3907 +def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
 3908 +    global hexchat
 3909 +    global hexchat_stdout
 3910 +
 3911 +    signal.signal(signal.SIGINT, signal.SIG_DFL)
 3912 +
 3913 +    plugin_name[0] = PLUGIN_NAME
 3914 +    plugin_desc[0] = PLUGIN_DESC
 3915 +    plugin_version[0] = PLUGIN_VERSION
 3916 +
 3917 +    try:
 3918 +        libdir = __decode(ffi.string(libdir))
 3919 +        modpath = os.path.join(libdir, '..', 'python')
 3920 +        sys.path.append(os.path.abspath(modpath))
 3921 +        hexchat = importlib.import_module('hexchat')
 3922 +
 3923 +    except (UnicodeDecodeError, ImportError) as e:
 3924 +        lib.hexchat_print(lib.ph, b'Failed to import module: ' + repr(e).encode())
 3925 +
 3926 +        return 0
 3927 +
 3928 +    hexchat_stdout = Stdout()
 3929 +    sys.stdout = hexchat_stdout
 3930 +    sys.stderr = hexchat_stdout
 3931 +    pydoc.help = pydoc.Helper(HelpEater(), HelpEater())
 3932 +
 3933 +    lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
 3934 +    lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
 3935 +    lib.hexchat_hook_command(lib.ph, b'UNLOAD', 0, lib._on_unload_command, ffi.NULL, ffi.NULL)
 3936 +    lib.hexchat_hook_command(lib.ph, b'RELOAD', 0, lib._on_reload_command, ffi.NULL, ffi.NULL)
 3937 +    lib.hexchat_hook_command(lib.ph, b'PY', 0, lib._on_py_command, b'''Usage: /PY LOAD   <filename>
 3938 +           UNLOAD <filename|name>
 3939 +           RELOAD <filename|name>
 3940 +           LIST
 3941 +           EXEC <command>
 3942 +           CONSOLE
 3943 +           ABOUT''', ffi.NULL)
 3944 +
 3945 +    lib.hexchat_print(lib.ph, b'Python interface loaded')
 3946 +    autoload()
 3947 +    return 1
 3948 +
 3949 +
 3950 +@ffi.def_extern()
 3951 +def _on_plugin_deinit():
 3952 +    global local_interp
 3953 +    global hexchat
 3954 +    global hexchat_stdout
 3955 +    global plugins
 3956 +
 3957 +    plugins = set()
 3958 +    local_interp = None
 3959 +    hexchat = None
 3960 +    hexchat_stdout = None
 3961 +    sys.stdout = sys.__stdout__
 3962 +    sys.stderr = sys.__stderr__
 3963 +    pydoc.help = pydoc.Helper()
 3964 +
 3965 +    for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
 3966 +        try:
 3967 +            del sys.modules[mod]
 3968 +
 3969 +        except KeyError:
 3970 +            pass
 3971 +
 3972 +    return 1
 3973 diff --git a/plugins/python/python2.vcxproj b/plugins/python/python2.vcxproj
 3974 index f914a865..0b098112 100644
 3975 --- a/plugins/python/python2.vcxproj
 3976 +++ b/plugins/python/python2.vcxproj
 3977 @@ -37,6 +37,9 @@
 3978        <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
 3979        <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
 3980      </Link>
 3981 +    <PreBuildEvent>
 3982 +      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
 3983 +    </PreBuildEvent>
 3984    </ItemDefinitionGroup>
 3985    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
 3986      <ClCompile>
 3987 @@ -48,12 +51,15 @@
 3988        <AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
 3989        <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
 3990      </Link>
 3991 +    <PreBuildEvent>
 3992 +      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
 3993 +    </PreBuildEvent>
 3994    </ItemDefinitionGroup>
 3995    <ItemGroup>
 3996      <None Include="python.def" />
 3997    </ItemGroup>
 3998    <ItemGroup>
 3999 -    <ClCompile Include="python.c" />
 4000 +    <ClCompile Include="$(IntDir)python.c" />
 4001    </ItemGroup>
 4002    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
 4003  </Project>
 4004 diff --git a/plugins/python/python3.vcxproj b/plugins/python/python3.vcxproj
 4005 index 815dc8b1..5868d3b0 100644
 4006 --- a/plugins/python/python3.vcxproj
 4007 +++ b/plugins/python/python3.vcxproj
 4008 @@ -37,6 +37,9 @@
 4009        <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
 4010        <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
 4011      </Link>
 4012 +    <PreBuildEvent>
 4013 +      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
 4014 +    </PreBuildEvent>
 4015    </ItemDefinitionGroup>
 4016    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
 4017      <ClCompile>
 4018 @@ -48,12 +51,20 @@
 4019        <AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
 4020        <AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
 4021      </Link>
 4022 +    <PreBuildEvent>
 4023 +      <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
 4024 +    </PreBuildEvent>
 4025    </ItemDefinitionGroup>
 4026    <ItemGroup>
 4027 +    <None Include="generate_plugin.py" />
 4028 +    <None Include="hexchat.py" />
 4029      <None Include="python.def" />
 4030 +    <None Include="python.py" />
 4031 +    <None Include="xchat.py" />
 4032 +    <None Include="_hexchat.py" />
 4033    </ItemGroup>
 4034    <ItemGroup>
 4035 -    <ClCompile Include="python.c" />
 4036 +    <ClCompile Include="$(IntDir)python.c" />
 4037    </ItemGroup>
 4038    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
 4039 -</Project>
 4040 +</Project>
 4041 \ No newline at end of file
 4042 diff --git a/plugins/python/python3.vcxproj.filters b/plugins/python/python3.vcxproj.filters
 4043 index 9165e798..5c5834bb 100644
 4044 --- a/plugins/python/python3.vcxproj.filters
 4045 +++ b/plugins/python/python3.vcxproj.filters
 4046 @@ -9,13 +9,26 @@
 4047      </Filter>
 4048    </ItemGroup>
 4049    <ItemGroup>
 4050 -    <ClCompile Include="python.c">
 4051 -      <Filter>Source Files</Filter>
 4052 -    </ClCompile>
 4053 +    <ClCompile Include="$(IntDir)python.c" />
 4054    </ItemGroup>
 4055    <ItemGroup>
 4056      <None Include="python.def">
 4057        <Filter>Resource Files</Filter>
 4058      </None>
 4059 +    <None Include="_hexchat.py">
 4060 +      <Filter>Source Files</Filter>
 4061 +    </None>
 4062 +    <None Include="generate_plugin.py">
 4063 +      <Filter>Source Files</Filter>
 4064 +    </None>
 4065 +    <None Include="hexchat.py">
 4066 +      <Filter>Source Files</Filter>
 4067 +    </None>
 4068 +    <None Include="python.py">
 4069 +      <Filter>Source Files</Filter>
 4070 +    </None>
 4071 +    <None Include="xchat.py">
 4072 +      <Filter>Source Files</Filter>
 4073 +    </None>
 4074    </ItemGroup>
 4075  </Project>
 4076 \ No newline at end of file
 4077 diff --git a/plugins/python/python_style_guide.md b/plugins/python/python_style_guide.md
 4078 new file mode 100644
 4079 index 00000000..41db2474
 4080 --- /dev/null
 4081 +++ b/plugins/python/python_style_guide.md
 4082 @@ -0,0 +1,26 @@
 4083 +# HexChat Python Module Style Guide
 4084 +
 4085 +(This is a work in progress).
 4086 +
 4087 +## General rules
 4088 +
 4089 +- PEP8 as general fallback recommendations
 4090 +- Max line length: 120
 4091 +- Avoid overcomplex compound statements. i.e. dont do this: `somevar = x if x == y else z if a == b and c == b else x`
 4092 +
 4093 +## Indentation style
 4094 +
 4095 +### Multi-line functions
 4096 +
 4097 +```python
 4098 +foo(really_long_arg_1,
 4099 +    really_long_arg_2)
 4100 +```
 4101 +
 4102 +### Mutli-line lists/dicts
 4103 +
 4104 +```python
 4105 +foo = {
 4106 +    'bar': 'baz',
 4107 +}
 4108 +```
 4109 diff --git a/plugins/python/xchat.py b/plugins/python/xchat.py
 4110 new file mode 100644
 4111 index 00000000..6922490b
 4112 --- /dev/null
 4113 +++ b/plugins/python/xchat.py
 4114 @@ -0,0 +1 @@
 4115 +from _hexchat import *
 4116 diff --git a/src/common/meson.build b/src/common/meson.build
 4117 index bbb64645..492227b2 100644
 4118 --- a/src/common/meson.build
 4119 +++ b/src/common/meson.build
 4120 @@ -93,10 +93,6 @@ endif
 4121  
 4122  if get_option('with-plugin')
 4123    common_deps += libgmodule_dep
 4124 -  common_cflags += '-DHEXCHATLIBDIR="@0@"'.format(join_paths(get_option('prefix'),
 4125 -                                                  get_option('libdir'),
 4126 -                                                  'hexchat/plugins'))
 4127 -
 4128    install_headers('hexchat-plugin.h')
 4129  endif
 4130  
 4131 diff --git a/win32/copy/copy.vcxproj b/win32/copy/copy.vcxproj
 4132 index c508a7f3..72f2c032 100644
 4133 --- a/win32/copy/copy.vcxproj
 4134 +++ b/win32/copy/copy.vcxproj
 4135 @@ -64,6 +64,8 @@
 4136      <LuaShare Include="$(DepsRoot)\share\lua\**\*.lua" />
 4137      <LuaShare Include="$(DepsRoot)\share\lua\**\**\*.lua" />
 4138      <Typelib Include="$(DepsRoot)\lib\girepository-1.0\*.typelib" />
 4139 +    <None Include="$(Python3Path)\Lib\site-packages\_cffi_backend.*.pyd" />
 4140 +    <None Include="$(Python2Path)\Lib\site-packages\_cffi_backend.pyd" />
 4141  
 4142      <Engines Include="$(DepsRoot)\lib\gtk-2.0\i686-pc-vs14\engines\**\*" />
 4143  
 4144 @@ -91,6 +93,9 @@
 4145      <Copy SourceFiles="@(LuaShare)" DestinationFiles="@(LuaShare->'$(HexChatRel)\share\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
 4146      <Copy SourceFiles="@(LuaLib)" DestinationFiles="@(LuaLib->'$(HexChatRel)\lib\lua\%(RecursiveDir)%(Filename)%(Extension)')" />
 4147      <Copy SourceFiles="@(Typelib)" DestinationFiles="@(Typelib->'$(HexChatRel)\lib\girepository-1.0\%(Filename)%(Extension)')" />
 4148 +    <Copy SourceFiles="..\..\plugins\python\xchat.py" DestinationFolder="$(HexChatRel)\python" />
 4149 +    <Copy SourceFiles="..\..\plugins\python\hexchat.py" DestinationFolder="$(HexChatRel)\python" />
 4150 +    <Copy SourceFiles="..\..\plugins\python\_hexchat.py" DestinationFolder="$(HexChatRel)\python" />
 4151  
 4152      <WriteLinesToFile File="$(HexChatRel)portable-mode" Lines="2" Overwrite="true" />
 4153  
 4154 diff --git a/win32/installer/hexchat.iss.tt b/win32/installer/hexchat.iss.tt
 4155 index e242ee96..3ac5ec41 100644
 4156 --- a/win32/installer/hexchat.iss.tt
 4157 +++ b/win32/installer/hexchat.iss.tt
 4158 @@ -164,6 +164,7 @@ Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0";
 4159  Source: "share\lua\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua
 4160  Source: "share\lua\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua
 4161  Source: "share\lua\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua
 4162 +Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
 4163  
 4164  Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum
 4165  Source: "plugins\hcexec.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\exec
 4166 @@ -175,11 +176,15 @@ Source: "WinSparkle.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pl
 4167  Source: "plugins\hcwinamp.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\winamp
 4168  Source: "share\system.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\sysinfo
 4169  Source: "plugins\hcsysinfo.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\sysinfo
 4170 +Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
 4171 +
 4172 +Source: "python\*.py"; DestDir: "{app}\python"; Flags: ignoreversion; Components: langs\python
 4173  
 4174  Source: "plugins\hcpython2.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python2
 4175 +Source: "_cffi_backend.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python2
 4176 +
 4177  Source: "plugins\hcpython3.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python\python3
 4178 -Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl
 4179 -Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua
 4180 +Source: "_cffi_backend.cp3*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python\python3
 4181  
 4182  Source: "hexchat.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs
 4183  Source: "hexchat-text.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xctext

Generated by cgit