4068 lines
112 KiB
Diff
4068 lines
112 KiB
Diff
diff --color -Nur hexchat-2.14.3/plugins/python/generate_plugin.py hexchat/plugins/python/generate_plugin.py
|
|
--- hexchat-2.14.3/plugins/python/generate_plugin.py 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/generate_plugin.py 2020-11-06 15:03:00.401820501 -0800
|
|
@@ -0,0 +1,89 @@
|
|
+#!/usr/bin/env python3
|
|
+
|
|
+import sys
|
|
+import cffi
|
|
+
|
|
+builder = cffi.FFI()
|
|
+
|
|
+# hexchat-plugin.h
|
|
+with open(sys.argv[1]) as f:
|
|
+ output = []
|
|
+ eat_until_endif = 0
|
|
+ # This is very specific to hexchat-plugin.h, it is not a cpp
|
|
+ for line in f:
|
|
+ if line.startswith('#define'):
|
|
+ continue
|
|
+ elif line.endswith('HEXCHAT_PLUGIN_H\n'):
|
|
+ continue
|
|
+ elif 'time.h' in line:
|
|
+ output.append('typedef int... time_t;')
|
|
+ elif line.startswith('#if'):
|
|
+ eat_until_endif += 1
|
|
+ elif line.startswith('#endif'):
|
|
+ eat_until_endif -= 1
|
|
+ elif eat_until_endif and '_hexchat_context' not in line:
|
|
+ continue
|
|
+ else:
|
|
+ output.append(line)
|
|
+ builder.cdef(''.join(output))
|
|
+
|
|
+builder.embedding_api('''
|
|
+extern "Python" int _on_py_command(char **, char **, void *);
|
|
+extern "Python" int _on_load_command(char **, char **, void *);
|
|
+extern "Python" int _on_unload_command(char **, char **, void *);
|
|
+extern "Python" int _on_reload_command(char **, char **, void *);
|
|
+extern "Python" int _on_say_command(char **, char **, void *);
|
|
+
|
|
+extern "Python" int _on_command_hook(char **, char **, void *);
|
|
+extern "Python" int _on_print_hook(char **, void *);
|
|
+extern "Python" int _on_print_attrs_hook(char **, hexchat_event_attrs *, void *);
|
|
+extern "Python" int _on_server_hook(char **, char **, void *);
|
|
+extern "Python" int _on_server_attrs_hook(char **, char **, hexchat_event_attrs *, void *);
|
|
+extern "Python" int _on_timer_hook(void *);
|
|
+
|
|
+extern "Python" int _on_plugin_init(char **, char **, char **, char *, char *);
|
|
+extern "Python" int _on_plugin_deinit(void);
|
|
+
|
|
+static hexchat_plugin *ph;
|
|
+''')
|
|
+
|
|
+builder.set_source('_hexchat_embedded', '''
|
|
+/* Python's header defines these.. */
|
|
+#undef HAVE_MEMRCHR
|
|
+#undef HAVE_STRINGS_H
|
|
+
|
|
+#include "config.h"
|
|
+#include "hexchat-plugin.h"
|
|
+
|
|
+static hexchat_plugin *ph;
|
|
+CFFI_DLLEXPORT int _on_plugin_init(char **, char **, char **, char *, char *);
|
|
+CFFI_DLLEXPORT int _on_plugin_deinit(void);
|
|
+
|
|
+int hexchat_plugin_init(hexchat_plugin *plugin_handle,
|
|
+ char **name_out, char **description_out,
|
|
+ char **version_out, char *arg)
|
|
+{
|
|
+ if (ph != NULL)
|
|
+ {
|
|
+ puts ("Python plugin already loaded\\n");
|
|
+ return 0; /* Prevent loading twice */
|
|
+ }
|
|
+
|
|
+ ph = plugin_handle;
|
|
+ return _on_plugin_init(name_out, description_out, version_out, arg, HEXCHATLIBDIR);
|
|
+}
|
|
+
|
|
+int hexchat_plugin_deinit(void)
|
|
+{
|
|
+ int ret = _on_plugin_deinit();
|
|
+ ph = NULL;
|
|
+ return ret;
|
|
+}
|
|
+''')
|
|
+
|
|
+# python.py
|
|
+with open(sys.argv[2]) as f:
|
|
+ builder.embedding_init_code(f.read())
|
|
+
|
|
+# python.c
|
|
+builder.emit_c_code(sys.argv[3])
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/_hexchat.py hexchat/plugins/python/_hexchat.py
|
|
--- hexchat-2.14.3/plugins/python/_hexchat.py 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/_hexchat.py 2020-11-06 15:03:00.401820501 -0800
|
|
@@ -0,0 +1,386 @@
|
|
+import inspect
|
|
+import sys
|
|
+from contextlib import contextmanager
|
|
+
|
|
+from _hexchat_embedded import ffi, lib
|
|
+
|
|
+__all__ = [
|
|
+ 'EAT_ALL', 'EAT_HEXCHAT', 'EAT_NONE', 'EAT_PLUGIN', 'EAT_XCHAT',
|
|
+ 'PRI_HIGH', 'PRI_HIGHEST', 'PRI_LOW', 'PRI_LOWEST', 'PRI_NORM',
|
|
+ '__doc__', '__version__', 'command', 'del_pluginpref', 'emit_print',
|
|
+ 'find_context', 'get_context', 'get_info',
|
|
+ 'get_list', 'get_lists', 'get_pluginpref', 'get_prefs', 'hook_command',
|
|
+ 'hook_print', 'hook_print_attrs', 'hook_server', 'hook_server_attrs',
|
|
+ 'hook_timer', 'hook_unload', 'list_pluginpref', 'nickcmp', 'prnt',
|
|
+ 'set_pluginpref', 'strip', 'unhook',
|
|
+]
|
|
+
|
|
+__doc__ = 'HexChat Scripting Interface'
|
|
+__version__ = (2, 0)
|
|
+__license__ = 'GPL-2.0+'
|
|
+
|
|
+EAT_NONE = 0
|
|
+EAT_HEXCHAT = 1
|
|
+EAT_XCHAT = EAT_HEXCHAT
|
|
+EAT_PLUGIN = 2
|
|
+EAT_ALL = EAT_HEXCHAT | EAT_PLUGIN
|
|
+
|
|
+PRI_LOWEST = -128
|
|
+PRI_LOW = -64
|
|
+PRI_NORM = 0
|
|
+PRI_HIGH = 64
|
|
+PRI_HIGHEST = 127
|
|
+
|
|
+
|
|
+# We need each module to be able to reference their parent plugin
|
|
+# which is a bit tricky since they all share the exact same module.
|
|
+# Simply navigating up to what module called it seems to actually
|
|
+# be a fairly reliable and simple method of doing so if ugly.
|
|
+def __get_current_plugin():
|
|
+ frame = inspect.stack()[1][0]
|
|
+ while '__plugin' not in frame.f_globals:
|
|
+ frame = frame.f_back
|
|
+ assert frame is not None
|
|
+
|
|
+ return frame.f_globals['__plugin']
|
|
+
|
|
+
|
|
+# Keeping API compat
|
|
+if sys.version_info[0] == 2:
|
|
+ def __decode(string):
|
|
+ return string
|
|
+
|
|
+else:
|
|
+ def __decode(string):
|
|
+ return string.decode()
|
|
+
|
|
+
|
|
+# ------------ API ------------
|
|
+def prnt(string):
|
|
+ lib.hexchat_print(lib.ph, string.encode())
|
|
+
|
|
+
|
|
+def emit_print(event_name, *args, **kwargs):
|
|
+ time = kwargs.pop('time', 0) # For py2 compat
|
|
+ cargs = []
|
|
+ for i in range(4):
|
|
+ arg = args[i].encode() if len(args) > i else b''
|
|
+ cstring = ffi.new('char[]', arg)
|
|
+ cargs.append(cstring)
|
|
+
|
|
+ if time == 0:
|
|
+ return lib.hexchat_emit_print(lib.ph, event_name.encode(), *cargs)
|
|
+
|
|
+ attrs = lib.hexchat_event_attrs_create(lib.ph)
|
|
+ attrs.server_time_utc = time
|
|
+ ret = lib.hexchat_emit_print_attrs(lib.ph, attrs, event_name.encode(), *cargs)
|
|
+ lib.hexchat_event_attrs_free(lib.ph, attrs)
|
|
+ return ret
|
|
+
|
|
+
|
|
+# TODO: this shadows itself. command should be changed to cmd
|
|
+def command(command):
|
|
+ lib.hexchat_command(lib.ph, command.encode())
|
|
+
|
|
+
|
|
+def nickcmp(string1, string2):
|
|
+ return lib.hexchat_nickcmp(lib.ph, string1.encode(), string2.encode())
|
|
+
|
|
+
|
|
+def strip(text, length=-1, flags=3):
|
|
+ stripped = lib.hexchat_strip(lib.ph, text.encode(), length, flags)
|
|
+ ret = __decode(ffi.string(stripped))
|
|
+ lib.hexchat_free(lib.ph, stripped)
|
|
+ return ret
|
|
+
|
|
+
|
|
+def get_info(name):
|
|
+ ret = lib.hexchat_get_info(lib.ph, name.encode())
|
|
+ if ret == ffi.NULL:
|
|
+ return None
|
|
+ if name in ('gtkwin_ptr', 'win_ptr'):
|
|
+ # Surely there is a less dumb way?
|
|
+ ptr = repr(ret).rsplit(' ', 1)[1][:-1]
|
|
+ return ptr
|
|
+
|
|
+ return __decode(ffi.string(ret))
|
|
+
|
|
+
|
|
+def get_prefs(name):
|
|
+ string_out = ffi.new('char**')
|
|
+ int_out = ffi.new('int*')
|
|
+ _type = lib.hexchat_get_prefs(lib.ph, name.encode(), string_out, int_out)
|
|
+ if _type == 0:
|
|
+ return None
|
|
+
|
|
+ if _type == 1:
|
|
+ return __decode(ffi.string(string_out[0]))
|
|
+
|
|
+ if _type in (2, 3): # XXX: 3 should be a bool, but keeps API
|
|
+ return int_out[0]
|
|
+
|
|
+ raise AssertionError('Out of bounds pref storage')
|
|
+
|
|
+
|
|
+def __cstrarray_to_list(arr):
|
|
+ i = 0
|
|
+ ret = []
|
|
+ while arr[i] != ffi.NULL:
|
|
+ ret.append(ffi.string(arr[i]))
|
|
+ i += 1
|
|
+
|
|
+ return ret
|
|
+
|
|
+
|
|
+__FIELD_CACHE = {}
|
|
+
|
|
+
|
|
+def __get_fields(name):
|
|
+ return __FIELD_CACHE.setdefault(name, __cstrarray_to_list(lib.hexchat_list_fields(lib.ph, name)))
|
|
+
|
|
+
|
|
+__FIELD_PROPERTY_CACHE = {}
|
|
+
|
|
+
|
|
+def __cached_decoded_str(string):
|
|
+ return __FIELD_PROPERTY_CACHE.setdefault(string, __decode(string))
|
|
+
|
|
+
|
|
+def get_lists():
|
|
+ return [__cached_decoded_str(field) for field in __get_fields(b'lists')]
|
|
+
|
|
+
|
|
+class ListItem:
|
|
+ def __init__(self, name):
|
|
+ self._listname = name
|
|
+
|
|
+ def __repr__(self):
|
|
+ return '<{} list item at {}>'.format(self._listname, id(self))
|
|
+
|
|
+
|
|
+# done this way for speed
|
|
+if sys.version_info[0] == 2:
|
|
+ def get_getter(name):
|
|
+ return ord(name[0])
|
|
+
|
|
+else:
|
|
+ def get_getter(name):
|
|
+ return name[0]
|
|
+
|
|
+
|
|
+def get_list(name):
|
|
+ # XXX: This function is extremely inefficient and could be interators and
|
|
+ # lazily loaded properties, but for API compat we stay slow
|
|
+ orig_name = name
|
|
+ name = name.encode()
|
|
+
|
|
+ if name not in __get_fields(b'lists'):
|
|
+ raise KeyError('list not available')
|
|
+
|
|
+ list_ = lib.hexchat_list_get(lib.ph, name)
|
|
+ if list_ == ffi.NULL:
|
|
+ return None
|
|
+
|
|
+ ret = []
|
|
+ fields = __get_fields(name)
|
|
+
|
|
+ def string_getter(field):
|
|
+ string = lib.hexchat_list_str(lib.ph, list_, field)
|
|
+ if string != ffi.NULL:
|
|
+ return __decode(ffi.string(string))
|
|
+
|
|
+ return ''
|
|
+
|
|
+ def ptr_getter(field):
|
|
+ if field == b'context':
|
|
+ ptr = lib.hexchat_list_str(lib.ph, list_, field)
|
|
+ ctx = ffi.cast('hexchat_context*', ptr)
|
|
+ return Context(ctx)
|
|
+
|
|
+ return None
|
|
+
|
|
+ getters = {
|
|
+ ord('s'): string_getter,
|
|
+ ord('i'): lambda field: lib.hexchat_list_int(lib.ph, list_, field),
|
|
+ ord('t'): lambda field: lib.hexchat_list_time(lib.ph, list_, field),
|
|
+ ord('p'): ptr_getter,
|
|
+ }
|
|
+
|
|
+ while lib.hexchat_list_next(lib.ph, list_) == 1:
|
|
+ item = ListItem(orig_name)
|
|
+ for _field in fields:
|
|
+ getter = getters.get(get_getter(_field))
|
|
+ if getter is not None:
|
|
+ field_name = _field[1:]
|
|
+ setattr(item, __cached_decoded_str(field_name), getter(field_name))
|
|
+
|
|
+ ret.append(item)
|
|
+
|
|
+ lib.hexchat_list_free(lib.ph, list_)
|
|
+ return ret
|
|
+
|
|
+
|
|
+# TODO: 'command' here shadows command above, and should be renamed to cmd
|
|
+def hook_command(command, callback, userdata=None, priority=PRI_NORM, help=None):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_command(lib.ph, command.encode(), priority, lib._on_command_hook,
|
|
+ help.encode() if help is not None else ffi.NULL, hook.handle)
|
|
+
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_print(name, callback, userdata=None, priority=PRI_NORM):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_print(lib.ph, name.encode(), priority, lib._on_print_hook, hook.handle)
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_print_attrs(name, callback, userdata=None, priority=PRI_NORM):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_print_attrs(lib.ph, name.encode(), priority, lib._on_print_attrs_hook, hook.handle)
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_server(name, callback, userdata=None, priority=PRI_NORM):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_server(lib.ph, name.encode(), priority, lib._on_server_hook, hook.handle)
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_server_attrs(name, callback, userdata=None, priority=PRI_NORM):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_server_attrs(lib.ph, name.encode(), priority, lib._on_server_attrs_hook, hook.handle)
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_timer(timeout, callback, userdata=None):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata)
|
|
+ handle = lib.hexchat_hook_timer(lib.ph, timeout, lib._on_timer_hook, hook.handle)
|
|
+ hook.hexchat_hook = handle
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def hook_unload(callback, userdata=None):
|
|
+ plugin = __get_current_plugin()
|
|
+ hook = plugin.add_hook(callback, userdata, is_unload=True)
|
|
+ return id(hook)
|
|
+
|
|
+
|
|
+def unhook(handle):
|
|
+ plugin = __get_current_plugin()
|
|
+ return plugin.remove_hook(handle)
|
|
+
|
|
+
|
|
+def set_pluginpref(name, value):
|
|
+ if isinstance(value, str):
|
|
+ return bool(lib.hexchat_pluginpref_set_str(lib.ph, name.encode(), value.encode()))
|
|
+
|
|
+ if isinstance(value, int):
|
|
+ return bool(lib.hexchat_pluginpref_set_int(lib.ph, name.encode(), value))
|
|
+
|
|
+ # XXX: This should probably raise but this keeps API
|
|
+ return False
|
|
+
|
|
+
|
|
+def get_pluginpref(name):
|
|
+ name = name.encode()
|
|
+ string_out = ffi.new('char[512]')
|
|
+ if lib.hexchat_pluginpref_get_str(lib.ph, name, string_out) != 1:
|
|
+ return None
|
|
+
|
|
+ string = ffi.string(string_out)
|
|
+ # This API stores everything as a string so we have to figure out what
|
|
+ # its actual type was supposed to be.
|
|
+ if len(string) > 12: # Can't be a number
|
|
+ return __decode(string)
|
|
+
|
|
+ number = lib.hexchat_pluginpref_get_int(lib.ph, name)
|
|
+ if number == -1 and string != b'-1':
|
|
+ return __decode(string)
|
|
+
|
|
+ return number
|
|
+
|
|
+
|
|
+def del_pluginpref(name):
|
|
+ return bool(lib.hexchat_pluginpref_delete(lib.ph, name.encode()))
|
|
+
|
|
+
|
|
+def list_pluginpref():
|
|
+ prefs_str = ffi.new('char[4096]')
|
|
+ if lib.hexchat_pluginpref_list(lib.ph, prefs_str) == 1:
|
|
+ return __decode(prefs_str).split(',')
|
|
+
|
|
+ return []
|
|
+
|
|
+
|
|
+class Context:
|
|
+ def __init__(self, ctx):
|
|
+ self._ctx = ctx
|
|
+
|
|
+ def __eq__(self, value):
|
|
+ if not isinstance(value, Context):
|
|
+ return False
|
|
+
|
|
+ return self._ctx == value._ctx
|
|
+
|
|
+ @contextmanager
|
|
+ def __change_context(self):
|
|
+ old_ctx = lib.hexchat_get_context(lib.ph)
|
|
+ if not self.set():
|
|
+ # XXX: Behavior change, previously used wrong context
|
|
+ lib.hexchat_print(lib.ph, b'Context object refers to closed context, ignoring call')
|
|
+ return
|
|
+
|
|
+ yield
|
|
+ lib.hexchat_set_context(lib.ph, old_ctx)
|
|
+
|
|
+ def set(self):
|
|
+ # XXX: API addition, C plugin silently ignored failure
|
|
+ return bool(lib.hexchat_set_context(lib.ph, self._ctx))
|
|
+
|
|
+ def prnt(self, string):
|
|
+ with self.__change_context():
|
|
+ prnt(string)
|
|
+
|
|
+ def emit_print(self, event_name, *args, **kwargs):
|
|
+ time = kwargs.pop('time', 0) # For py2 compat
|
|
+ with self.__change_context():
|
|
+ return emit_print(event_name, *args, time=time)
|
|
+
|
|
+ def command(self, string):
|
|
+ with self.__change_context():
|
|
+ command(string)
|
|
+
|
|
+ def get_info(self, name):
|
|
+ with self.__change_context():
|
|
+ return get_info(name)
|
|
+
|
|
+ def get_list(self, name):
|
|
+ with self.__change_context():
|
|
+ return get_list(name)
|
|
+
|
|
+
|
|
+def get_context():
|
|
+ ctx = lib.hexchat_get_context(lib.ph)
|
|
+ return Context(ctx)
|
|
+
|
|
+
|
|
+def find_context(server=None, channel=None):
|
|
+ server = server.encode() if server is not None else ffi.NULL
|
|
+ channel = channel.encode() if channel is not None else ffi.NULL
|
|
+ ctx = lib.hexchat_find_context(lib.ph, server, channel)
|
|
+ if ctx == ffi.NULL:
|
|
+ return None
|
|
+
|
|
+ return Context(ctx)
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/hexchat.py hexchat/plugins/python/hexchat.py
|
|
--- hexchat-2.14.3/plugins/python/hexchat.py 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/hexchat.py 2020-11-06 15:03:00.402820502 -0800
|
|
@@ -0,0 +1 @@
|
|
+from _hexchat import *
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/meson.build hexchat/plugins/python/meson.build
|
|
--- hexchat-2.14.3/plugins/python/meson.build 2020-11-06 15:05:12.122924612 -0800
|
|
+++ hexchat/plugins/python/meson.build 2020-11-06 15:03:00.402820502 -0800
|
|
@@ -3,16 +3,28 @@
|
|
# Python 3.8 introduced a new -embed variant
|
|
if not python_opt.endswith('-embed')
|
|
python_dep = dependency(python_opt + '-embed', version: '>= 3.3', required: false)
|
|
- endif
|
|
- if not python_dep.found()
|
|
+ if not python_dep.found()
|
|
+ python_dep = dependency(python_opt, version: '>= 3.3')
|
|
+ endif
|
|
+ else
|
|
python_dep = dependency(python_opt, version: '>= 3.3')
|
|
endif
|
|
else
|
|
python_dep = dependency(python_opt, version: '>= 2.7')
|
|
endif
|
|
|
|
-shared_module('python', 'python.c',
|
|
- dependencies: [libgio_dep, hexchat_plugin_dep, python_dep],
|
|
+python3_source = custom_target('python-bindings',
|
|
+ input: ['../../src/common/hexchat-plugin.h', 'python.py'],
|
|
+ output: 'python.c',
|
|
+ command: [find_program('generate_plugin.py'), '@INPUT@', '@OUTPUT@']
|
|
+)
|
|
+
|
|
+install_data(['_hexchat.py', 'hexchat.py', 'xchat.py'],
|
|
+ install_dir: join_paths(get_option('libdir'), 'hexchat/python')
|
|
+)
|
|
+
|
|
+shared_module('python', python3_source,
|
|
+ dependencies: [hexchat_plugin_dep, python_dep],
|
|
install: true,
|
|
install_dir: plugindir,
|
|
name_prefix: '',
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python2.vcxproj hexchat/plugins/python/python2.vcxproj
|
|
--- hexchat-2.14.3/plugins/python/python2.vcxproj 2019-12-20 22:43:47.652401700 -0800
|
|
+++ hexchat/plugins/python/python2.vcxproj 2020-11-06 15:03:00.402820502 -0800
|
|
@@ -37,6 +37,9 @@
|
|
<AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
|
|
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
</Link>
|
|
+ <PreBuildEvent>
|
|
+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
|
|
+ </PreBuildEvent>
|
|
</ItemDefinitionGroup>
|
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
<ClCompile>
|
|
@@ -48,12 +51,15 @@
|
|
<AdditionalDependencies>"$(Python2Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
|
|
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python2Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
</Link>
|
|
+ <PreBuildEvent>
|
|
+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
|
|
+ </PreBuildEvent>
|
|
</ItemDefinitionGroup>
|
|
<ItemGroup>
|
|
<None Include="python.def" />
|
|
</ItemGroup>
|
|
<ItemGroup>
|
|
- <ClCompile Include="python.c" />
|
|
+ <ClCompile Include="$(IntDir)python.c" />
|
|
</ItemGroup>
|
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
</Project>
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python3.vcxproj hexchat/plugins/python/python3.vcxproj
|
|
--- hexchat-2.14.3/plugins/python/python3.vcxproj 2019-12-20 22:43:47.652401700 -0800
|
|
+++ hexchat/plugins/python/python3.vcxproj 2020-11-06 15:03:00.403820502 -0800
|
|
@@ -37,6 +37,9 @@
|
|
<AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
|
|
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
</Link>
|
|
+ <PreBuildEvent>
|
|
+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
|
|
+ </PreBuildEvent>
|
|
</ItemDefinitionGroup>
|
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
<ClCompile>
|
|
@@ -48,12 +51,20 @@
|
|
<AdditionalDependencies>"$(Python3Lib).lib";$(DepLibs);%(AdditionalDependencies)</AdditionalDependencies>
|
|
<AdditionalLibraryDirectories>$(DepsRoot)\lib;$(Python3Path)\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
</Link>
|
|
+ <PreBuildEvent>
|
|
+ <Command>"$(Python3Path)\python.exe" generate_plugin.py ..\..\src\common\hexchat-plugin.h python.py "$(IntDir)python.c"</Command>
|
|
+ </PreBuildEvent>
|
|
</ItemDefinitionGroup>
|
|
<ItemGroup>
|
|
+ <None Include="generate_plugin.py" />
|
|
+ <None Include="hexchat.py" />
|
|
<None Include="python.def" />
|
|
+ <None Include="python.py" />
|
|
+ <None Include="xchat.py" />
|
|
+ <None Include="_hexchat.py" />
|
|
</ItemGroup>
|
|
<ItemGroup>
|
|
- <ClCompile Include="python.c" />
|
|
+ <ClCompile Include="$(IntDir)python.c" />
|
|
</ItemGroup>
|
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
-</Project>
|
|
+</Project>
|
|
\ No newline at end of file
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python3.vcxproj.filters hexchat/plugins/python/python3.vcxproj.filters
|
|
--- hexchat-2.14.3/plugins/python/python3.vcxproj.filters 2019-12-20 22:43:47.652401700 -0800
|
|
+++ hexchat/plugins/python/python3.vcxproj.filters 2020-11-06 15:03:00.403820502 -0800
|
|
@@ -9,13 +9,26 @@
|
|
</Filter>
|
|
</ItemGroup>
|
|
<ItemGroup>
|
|
- <ClCompile Include="python.c">
|
|
- <Filter>Source Files</Filter>
|
|
- </ClCompile>
|
|
+ <ClCompile Include="$(IntDir)python.c" />
|
|
</ItemGroup>
|
|
<ItemGroup>
|
|
<None Include="python.def">
|
|
<Filter>Resource Files</Filter>
|
|
</None>
|
|
+ <None Include="_hexchat.py">
|
|
+ <Filter>Source Files</Filter>
|
|
+ </None>
|
|
+ <None Include="generate_plugin.py">
|
|
+ <Filter>Source Files</Filter>
|
|
+ </None>
|
|
+ <None Include="hexchat.py">
|
|
+ <Filter>Source Files</Filter>
|
|
+ </None>
|
|
+ <None Include="python.py">
|
|
+ <Filter>Source Files</Filter>
|
|
+ </None>
|
|
+ <None Include="xchat.py">
|
|
+ <Filter>Source Files</Filter>
|
|
+ </None>
|
|
</ItemGroup>
|
|
</Project>
|
|
\ No newline at end of file
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python.c hexchat/plugins/python/python.c
|
|
--- hexchat-2.14.3/plugins/python/python.c 2019-12-20 22:43:47.652401700 -0800
|
|
+++ hexchat/plugins/python/python.c 1969-12-31 16:00:00.000000000 -0800
|
|
@@ -1,2837 +0,0 @@
|
|
-/*
|
|
-* Copyright (c) 2002-2003 Gustavo Niemeyer <niemeyer@conectiva.com>
|
|
-*
|
|
-* XChat Python Plugin Interface
|
|
-*
|
|
-* Xchat Python Plugin Interface is free software; you can redistribute
|
|
-* it and/or modify it under the terms of the GNU General Public License
|
|
-* as published by the Free Software Foundation; either version 2 of the
|
|
-* License, or (at your option) any later version.
|
|
-*
|
|
-* pybot is distributed in the hope that it will be useful,
|
|
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
-* GNU General Public License for more details.
|
|
-*
|
|
-* You should have received a copy of the GNU General Public License
|
|
-* along with this file; if not, write to the Free Software
|
|
-* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
-*/
|
|
-
|
|
-/* Thread support
|
|
- * ==============
|
|
- *
|
|
- * The python interpreter has a global interpreter lock. Any thread
|
|
- * executing must acquire it before working with data accessible from
|
|
- * python code. Here we must also care about xchat not being
|
|
- * thread-safe. We do this by using an xchat lock, which protects
|
|
- * xchat instructions from being executed out of time (when this
|
|
- * plugin is not "active").
|
|
- *
|
|
- * When xchat calls python code:
|
|
- * - Change the current_plugin for the executing plugin;
|
|
- * - Release xchat lock
|
|
- * - Acquire the global interpreter lock
|
|
- * - Make the python call
|
|
- * - Release the global interpreter lock
|
|
- * - Acquire xchat lock
|
|
- *
|
|
- * When python code calls xchat:
|
|
- * - Release the global interpreter lock
|
|
- * - Acquire xchat lock
|
|
- * - Restore context, if necessary
|
|
- * - Make the xchat call
|
|
- * - Release xchat lock
|
|
- * - Acquire the global interpreter lock
|
|
- *
|
|
- * Inside a timer, so that individual threads have a chance to run:
|
|
- * - Release xchat lock
|
|
- * - Go ahead threads. Have a nice time!
|
|
- * - Acquire xchat lock
|
|
- *
|
|
- */
|
|
-
|
|
-#include "config.h"
|
|
-
|
|
-#include <glib.h>
|
|
-#include <glib/gstdio.h>
|
|
-#include <string.h>
|
|
-#include <stdlib.h>
|
|
-#include <sys/types.h>
|
|
-
|
|
-#ifdef WIN32
|
|
-#include <direct.h>
|
|
-#else
|
|
-#include <unistd.h>
|
|
-#include <dirent.h>
|
|
-#endif
|
|
-
|
|
-#include "hexchat-plugin.h"
|
|
-#undef _POSIX_C_SOURCE /* Avoid warnings from /usr/include/features.h */
|
|
-#undef _XOPEN_SOURCE
|
|
-#undef HAVE_MEMRCHR /* Avoid redefinition in Python.h */
|
|
-#undef HAVE_STRINGS_H
|
|
-#include <Python.h>
|
|
-#include <structmember.h>
|
|
-#include <pythread.h>
|
|
-
|
|
-/* Macros to convert version macros into string literals.
|
|
- * The indirect macro is a well-known preprocessor trick to force X to be evaluated before the # operator acts to make it a string literal.
|
|
- * If STRINGIZE were to be directly defined as #X instead, VERSION would be "VERSION_MAJOR" instead of "1".
|
|
- */
|
|
-#define STRINGIZE2(X) #X
|
|
-#define STRINGIZE(X) STRINGIZE2(X)
|
|
-
|
|
-/* Version number macros */
|
|
-#define VERSION_MAJOR 1
|
|
-#define VERSION_MINOR 0
|
|
-
|
|
-/* Version string macro e.g 1.0/3.3 */
|
|
-#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/" \
|
|
- STRINGIZE(PY_MAJOR_VERSION) "." STRINGIZE (PY_MINOR_VERSION)
|
|
-
|
|
-/* #define's for Python 2 */
|
|
-#if PY_MAJOR_VERSION == 2
|
|
-#undef PyLong_Check
|
|
-#define PyLong_Check PyInt_Check
|
|
-#define PyLong_AsLong PyInt_AsLong
|
|
-#define PyLong_FromLong PyInt_FromLong
|
|
-
|
|
-#undef PyUnicode_Check
|
|
-#undef PyUnicode_FromString
|
|
-#undef PyUnicode_FromFormat
|
|
-#define PyUnicode_Check PyString_Check
|
|
-#define PyUnicode_AsFormat PyString_AsFormat
|
|
-#define PyUnicode_FromFormat PyString_FromFormat
|
|
-#define PyUnicode_FromString PyString_FromString
|
|
-#define PyUnicode_AsUTF8 PyString_AsString
|
|
-
|
|
-#ifdef WIN32
|
|
-#undef WITH_THREAD
|
|
-#endif
|
|
-#endif
|
|
-
|
|
-/* #define for Python 3 */
|
|
-#if PY_MAJOR_VERSION == 3
|
|
-#define IS_PY3K
|
|
-#endif
|
|
-
|
|
-#define NONE 0
|
|
-#define ALLOW_THREADS 1
|
|
-#define RESTORE_CONTEXT 2
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
-#define ACQUIRE_XCHAT_LOCK() PyThread_acquire_lock(xchat_lock, 1)
|
|
-#define RELEASE_XCHAT_LOCK() PyThread_release_lock(xchat_lock)
|
|
-#define BEGIN_XCHAT_CALLS(x) \
|
|
- do { \
|
|
- PyObject *calls_plugin = NULL; \
|
|
- PyThreadState *calls_thread; \
|
|
- if ((x) & RESTORE_CONTEXT) \
|
|
- calls_plugin = Plugin_GetCurrent(); \
|
|
- calls_thread = PyEval_SaveThread(); \
|
|
- ACQUIRE_XCHAT_LOCK(); \
|
|
- if (!((x) & ALLOW_THREADS)) { \
|
|
- PyEval_RestoreThread(calls_thread); \
|
|
- calls_thread = NULL; \
|
|
- } \
|
|
- if (calls_plugin) \
|
|
- hexchat_set_context(ph, \
|
|
- Plugin_GetContext(calls_plugin)); \
|
|
- while (0)
|
|
-#define END_XCHAT_CALLS() \
|
|
- RELEASE_XCHAT_LOCK(); \
|
|
- if (calls_thread) \
|
|
- PyEval_RestoreThread(calls_thread); \
|
|
- } while(0)
|
|
-#else
|
|
-#define ACQUIRE_XCHAT_LOCK()
|
|
-#define RELEASE_XCHAT_LOCK()
|
|
-#define BEGIN_XCHAT_CALLS(x)
|
|
-#define END_XCHAT_CALLS()
|
|
-#endif
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
-
|
|
-#define BEGIN_PLUGIN(plg) \
|
|
- do { \
|
|
- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
|
|
- RELEASE_XCHAT_LOCK(); \
|
|
- Plugin_AcquireThread(plg); \
|
|
- Plugin_SetContext(plg, begin_plugin_ctx); \
|
|
- } while (0)
|
|
-#define END_PLUGIN(plg) \
|
|
- do { \
|
|
- Plugin_ReleaseThread(plg); \
|
|
- ACQUIRE_XCHAT_LOCK(); \
|
|
- } while (0)
|
|
-
|
|
-#else /* !WITH_THREAD (win32) */
|
|
-
|
|
-static PyThreadState *pTempThread;
|
|
-
|
|
-#define BEGIN_PLUGIN(plg) \
|
|
- do { \
|
|
- hexchat_context *begin_plugin_ctx = hexchat_get_context(ph); \
|
|
- RELEASE_XCHAT_LOCK(); \
|
|
- PyEval_AcquireLock(); \
|
|
- pTempThread = PyThreadState_Swap(((PluginObject *)(plg))->tstate); \
|
|
- Plugin_SetContext(plg, begin_plugin_ctx); \
|
|
- } while (0)
|
|
-#define END_PLUGIN(plg) \
|
|
- do { \
|
|
- ((PluginObject *)(plg))->tstate = PyThreadState_Swap(pTempThread); \
|
|
- PyEval_ReleaseLock(); \
|
|
- ACQUIRE_XCHAT_LOCK(); \
|
|
- } while (0)
|
|
-
|
|
-#endif /* !WITH_THREAD */
|
|
-
|
|
-#define Plugin_Swap(x) \
|
|
- PyThreadState_Swap(((PluginObject *)(x))->tstate)
|
|
-#define Plugin_AcquireThread(x) \
|
|
- PyEval_AcquireThread(((PluginObject *)(x))->tstate)
|
|
-#define Plugin_ReleaseThread(x) \
|
|
- Util_ReleaseThread(((PluginObject *)(x))->tstate)
|
|
-#define Plugin_GetFilename(x) \
|
|
- (((PluginObject *)(x))->filename)
|
|
-#define Plugin_GetName(x) \
|
|
- (((PluginObject *)(x))->name)
|
|
-#define Plugin_GetVersion(x) \
|
|
- (((PluginObject *)(x))->version)
|
|
-#define Plugin_GetDesc(x) \
|
|
- (((PluginObject *)(x))->description)
|
|
-#define Plugin_GetHooks(x) \
|
|
- (((PluginObject *)(x))->hooks)
|
|
-#define Plugin_GetContext(x) \
|
|
- (((PluginObject *)(x))->context)
|
|
-#define Plugin_SetFilename(x, y) \
|
|
- ((PluginObject *)(x))->filename = (y);
|
|
-#define Plugin_SetName(x, y) \
|
|
- ((PluginObject *)(x))->name = (y);
|
|
-#define Plugin_SetVersion(x, y) \
|
|
- ((PluginObject *)(x))->version = (y);
|
|
-#define Plugin_SetDescription(x, y) \
|
|
- ((PluginObject *)(x))->description = (y);
|
|
-#define Plugin_SetHooks(x, y) \
|
|
- ((PluginObject *)(x))->hooks = (y);
|
|
-#define Plugin_SetContext(x, y) \
|
|
- ((PluginObject *)(x))->context = (y);
|
|
-#define Plugin_SetGui(x, y) \
|
|
- ((PluginObject *)(x))->gui = (y);
|
|
-
|
|
-#define HOOK_XCHAT 1
|
|
-#define HOOK_XCHAT_ATTR 2
|
|
-#define HOOK_UNLOAD 3
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Object definitions */
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- int softspace; /* We need it for print support. */
|
|
-} XChatOutObject;
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- hexchat_context *context;
|
|
-} ContextObject;
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- PyObject *time;
|
|
-} AttributeObject;
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- const char *listname;
|
|
- PyObject *dict;
|
|
-} ListItemObject;
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- char *name;
|
|
- char *version;
|
|
- char *filename;
|
|
- char *description;
|
|
- GSList *hooks;
|
|
- PyThreadState *tstate;
|
|
- hexchat_context *context;
|
|
- void *gui;
|
|
-} PluginObject;
|
|
-
|
|
-typedef struct {
|
|
- int type;
|
|
- PyObject *plugin;
|
|
- PyObject *callback;
|
|
- PyObject *userdata;
|
|
- char *name;
|
|
- void *data; /* A handle, when type == HOOK_XCHAT */
|
|
-} Hook;
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Function declarations */
|
|
-
|
|
-static PyObject *Util_BuildList(char *word[]);
|
|
-static PyObject *Util_BuildEOLList(char *word[]);
|
|
-static void Util_Autoload(void);
|
|
-static char *Util_Expand(char *filename);
|
|
-
|
|
-static int Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata);
|
|
-static int Callback_Command(char *word[], char *word_eol[], void *userdata);
|
|
-static int Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata);
|
|
-static int Callback_Print(char *word[], void *userdata);
|
|
-static int Callback_Timer(void *userdata);
|
|
-static int Callback_ThreadTimer(void *userdata);
|
|
-
|
|
-static PyObject *XChatOut_New(void);
|
|
-static PyObject *XChatOut_write(PyObject *self, PyObject *args);
|
|
-static void XChatOut_dealloc(PyObject *self);
|
|
-
|
|
-static PyObject *Attribute_New(hexchat_event_attrs *attrs);
|
|
-
|
|
-static void Context_dealloc(PyObject *self);
|
|
-static PyObject *Context_set(ContextObject *self, PyObject *args);
|
|
-static PyObject *Context_command(ContextObject *self, PyObject *args);
|
|
-static PyObject *Context_prnt(ContextObject *self, PyObject *args);
|
|
-static PyObject *Context_get_info(ContextObject *self, PyObject *args);
|
|
-static PyObject *Context_get_list(ContextObject *self, PyObject *args);
|
|
-static PyObject *Context_compare(ContextObject *a, ContextObject *b, int op);
|
|
-static PyObject *Context_FromContext(hexchat_context *context);
|
|
-static PyObject *Context_FromServerAndChannel(char *server, char *channel);
|
|
-
|
|
-static PyObject *Plugin_New(char *filename, PyObject *xcoobj);
|
|
-static PyObject *Plugin_GetCurrent(void);
|
|
-static PluginObject *Plugin_ByString(char *str);
|
|
-static Hook *Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
|
|
- PyObject *userdata, char *name, void *data);
|
|
-static Hook *Plugin_FindHook(PyObject *plugin, char *name);
|
|
-static void Plugin_RemoveHook(PyObject *plugin, Hook *hook);
|
|
-static void Plugin_RemoveAllHooks(PyObject *plugin);
|
|
-
|
|
-static PyObject *Module_hexchat_command(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_xchat_prnt(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_get_context(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_find_context(PyObject *self, PyObject *args,
|
|
- PyObject *kwargs);
|
|
-static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_hook_command(PyObject *self, PyObject *args,
|
|
- PyObject *kwargs);
|
|
-static PyObject *Module_hexchat_hook_server(PyObject *self, PyObject *args,
|
|
- PyObject *kwargs);
|
|
-static PyObject *Module_hexchat_hook_print(PyObject *self, PyObject *args,
|
|
- PyObject *kwargs);
|
|
-static PyObject *Module_hexchat_hook_timer(PyObject *self, PyObject *args,
|
|
- PyObject *kwargs);
|
|
-static PyObject *Module_hexchat_unhook(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_get_info(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_xchat_get_list(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_xchat_get_lists(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_nickcmp(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_strip(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_pluginpref_set(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_pluginpref_get(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args);
|
|
-static PyObject *Module_hexchat_pluginpref_list(PyObject *self, PyObject *args);
|
|
-
|
|
-static void IInterp_Exec(char *command);
|
|
-static int IInterp_Cmd(char *word[], char *word_eol[], void *userdata);
|
|
-
|
|
-static void Command_PyList(void);
|
|
-static void Command_PyLoad(char *filename);
|
|
-static void Command_PyUnload(char *name);
|
|
-static void Command_PyReload(char *name);
|
|
-static void Command_PyAbout(void);
|
|
-static int Command_Py(char *word[], char *word_eol[], void *userdata);
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Static declarations and definitions */
|
|
-
|
|
-static PyTypeObject Plugin_Type;
|
|
-static PyTypeObject XChatOut_Type;
|
|
-static PyTypeObject Context_Type;
|
|
-static PyTypeObject ListItem_Type;
|
|
-static PyTypeObject Attribute_Type;
|
|
-
|
|
-static PyThreadState *main_tstate = NULL;
|
|
-static void *thread_timer = NULL;
|
|
-
|
|
-static hexchat_plugin *ph;
|
|
-static GSList *plugin_list = NULL;
|
|
-
|
|
-static PyObject *interp_plugin = NULL;
|
|
-static PyObject *xchatout = NULL;
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
-static PyThread_type_lock xchat_lock = NULL;
|
|
-#endif
|
|
-
|
|
-static const char usage[] = "\
|
|
-Usage: /PY LOAD <filename>\n\
|
|
- UNLOAD <filename|name>\n\
|
|
- RELOAD <filename|name>\n\
|
|
- LIST\n\
|
|
- EXEC <command>\n\
|
|
- CONSOLE\n\
|
|
- ABOUT\n\
|
|
-\n";
|
|
-
|
|
-static const char about[] = "HexChat Python interface version " VERSION "\n";
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Utility functions */
|
|
-
|
|
-static PyObject *
|
|
-Util_BuildList(char *word[])
|
|
-{
|
|
- PyObject *list;
|
|
- int listsize = 31;
|
|
- int i;
|
|
- /* Find the last valid array member; there may be intermediate NULLs that
|
|
- * would otherwise cause us to drop some members. */
|
|
- while (listsize > 0 &&
|
|
- (word[listsize] == NULL || word[listsize][0] == 0))
|
|
- listsize--;
|
|
- list = PyList_New(listsize);
|
|
- if (list == NULL) {
|
|
- PyErr_Print();
|
|
- return NULL;
|
|
- }
|
|
- for (i = 1; i <= listsize; i++) {
|
|
- PyObject *o;
|
|
- if (word[i] == NULL) {
|
|
- Py_INCREF(Py_None);
|
|
- o = Py_None;
|
|
- } else {
|
|
- /* This handles word[i][0] == 0 automatically. */
|
|
- o = PyUnicode_FromString(word[i]);
|
|
- }
|
|
- PyList_SetItem(list, i - 1, o);
|
|
- }
|
|
- return list;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Util_BuildEOLList(char *word[])
|
|
-{
|
|
- PyObject *list;
|
|
- int listsize = 31;
|
|
- int i;
|
|
- char *accum = NULL;
|
|
- char *last = NULL;
|
|
-
|
|
- /* Find the last valid array member; there may be intermediate NULLs that
|
|
- * would otherwise cause us to drop some members. */
|
|
- while (listsize > 0 &&
|
|
- (word[listsize] == NULL || word[listsize][0] == 0))
|
|
- listsize--;
|
|
- list = PyList_New(listsize);
|
|
- if (list == NULL) {
|
|
- PyErr_Print();
|
|
- return NULL;
|
|
- }
|
|
- for (i = listsize; i > 0; i--) {
|
|
- char *part = word[i];
|
|
- PyObject *uni_part;
|
|
- if (accum == NULL) {
|
|
- accum = g_strdup (part);
|
|
- } else if (part != NULL && part[0] != 0) {
|
|
- last = accum;
|
|
- accum = g_strjoin(" ", part, last, NULL);
|
|
- g_free (last);
|
|
- last = NULL;
|
|
-
|
|
- if (accum == NULL) {
|
|
- Py_DECREF(list);
|
|
- hexchat_print(ph, "Not enough memory to alloc accum"
|
|
- "for python plugin callback");
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
- uni_part = PyUnicode_FromString(accum);
|
|
- PyList_SetItem(list, i - 1, uni_part);
|
|
- }
|
|
-
|
|
- g_free (last);
|
|
- g_free (accum);
|
|
-
|
|
- return list;
|
|
-}
|
|
-
|
|
-static void
|
|
-Util_Autoload_from (const char *dir_name)
|
|
-{
|
|
- gchar *oldcwd;
|
|
- const char *entry_name;
|
|
- GDir *dir;
|
|
-
|
|
- oldcwd = g_get_current_dir ();
|
|
- if (oldcwd == NULL)
|
|
- return;
|
|
- if (g_chdir(dir_name) != 0)
|
|
- {
|
|
- g_free (oldcwd);
|
|
- return;
|
|
- }
|
|
- dir = g_dir_open (".", 0, NULL);
|
|
- if (dir == NULL)
|
|
- {
|
|
- g_free (oldcwd);
|
|
- return;
|
|
- }
|
|
- while ((entry_name = g_dir_read_name (dir)))
|
|
- {
|
|
- if (g_str_has_suffix (entry_name, ".py"))
|
|
- Command_PyLoad((char*)entry_name);
|
|
- }
|
|
- g_dir_close (dir);
|
|
- g_chdir (oldcwd);
|
|
-}
|
|
-
|
|
-static void
|
|
-Util_Autoload()
|
|
-{
|
|
- const char *xdir;
|
|
- char *sub_dir;
|
|
- /* we need local filesystem encoding for g_chdir, g_dir_open etc */
|
|
-
|
|
- xdir = hexchat_get_info(ph, "configdir");
|
|
-
|
|
- /* auto-load from subdirectory addons */
|
|
- sub_dir = g_build_filename (xdir, "addons", NULL);
|
|
- Util_Autoload_from(sub_dir);
|
|
- g_free (sub_dir);
|
|
-}
|
|
-
|
|
-static char *
|
|
-Util_Expand(char *filename)
|
|
-{
|
|
- char *expanded;
|
|
-
|
|
- /* Check if this is an absolute path. */
|
|
- if (g_path_is_absolute(filename)) {
|
|
- if (g_file_test(filename, G_FILE_TEST_EXISTS))
|
|
- return g_strdup(filename);
|
|
- else
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- /* Check if it starts with ~/ and expand the home if positive. */
|
|
- if (*filename == '~' && *(filename+1) == '/') {
|
|
- expanded = g_build_filename(g_get_home_dir(),
|
|
- filename+2, NULL);
|
|
- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
|
|
- return expanded;
|
|
- else {
|
|
- g_free(expanded);
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Check if it's in the current directory. */
|
|
- expanded = g_build_filename(g_get_current_dir(),
|
|
- filename, NULL);
|
|
- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
|
|
- return expanded;
|
|
- g_free(expanded);
|
|
-
|
|
- /* Check if ~/.config/hexchat/addons/<filename> exists. */
|
|
- expanded = g_build_filename(hexchat_get_info(ph, "configdir"),
|
|
- "addons", filename, NULL);
|
|
- if (g_file_test(expanded, G_FILE_TEST_EXISTS))
|
|
- return expanded;
|
|
- g_free(expanded);
|
|
-
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-/* Similar to PyEval_ReleaseThread, but accepts NULL thread states. */
|
|
-static void
|
|
-Util_ReleaseThread(PyThreadState *tstate)
|
|
-{
|
|
- PyThreadState *old_tstate;
|
|
- if (tstate == NULL)
|
|
- Py_FatalError("PyEval_ReleaseThread: NULL thread state");
|
|
- old_tstate = PyThreadState_Swap(NULL);
|
|
- if (old_tstate != tstate && old_tstate != NULL)
|
|
- Py_FatalError("PyEval_ReleaseThread: wrong thread state");
|
|
- PyEval_ReleaseLock();
|
|
-}
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Hookable functions. These are the entry points to python code, besides
|
|
- * the load function, and the hooks for interactive interpreter. */
|
|
-
|
|
-static int
|
|
-Callback_Server(char *word[], char *word_eol[], hexchat_event_attrs *attrs, void *userdata)
|
|
-{
|
|
- Hook *hook = (Hook *) userdata;
|
|
- PyObject *retobj;
|
|
- PyObject *word_list, *word_eol_list;
|
|
- PyObject *attributes;
|
|
- int ret = HEXCHAT_EAT_NONE;
|
|
- PyObject *plugin;
|
|
-
|
|
- plugin = hook->plugin;
|
|
- BEGIN_PLUGIN(plugin);
|
|
-
|
|
- word_list = Util_BuildList(word);
|
|
- if (word_list == NULL) {
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
- word_eol_list = Util_BuildList(word_eol);
|
|
- if (word_eol_list == NULL) {
|
|
- Py_DECREF(word_list);
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
-
|
|
- attributes = Attribute_New(attrs);
|
|
-
|
|
- if (hook->type == HOOK_XCHAT_ATTR)
|
|
- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
|
|
- word_eol_list, hook->userdata, attributes);
|
|
- else
|
|
- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
|
|
- word_eol_list, hook->userdata);
|
|
- Py_DECREF(word_list);
|
|
- Py_DECREF(word_eol_list);
|
|
- Py_DECREF(attributes);
|
|
-
|
|
- if (retobj == Py_None) {
|
|
- ret = HEXCHAT_EAT_NONE;
|
|
- Py_DECREF(retobj);
|
|
- } else if (retobj) {
|
|
- ret = PyLong_AsLong(retobj);
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- }
|
|
-
|
|
- END_PLUGIN(plugin);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int
|
|
-Callback_Command(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- Hook *hook = (Hook *) userdata;
|
|
- PyObject *retobj;
|
|
- PyObject *word_list, *word_eol_list;
|
|
- int ret = HEXCHAT_EAT_NONE;
|
|
- PyObject *plugin;
|
|
-
|
|
- plugin = hook->plugin;
|
|
- BEGIN_PLUGIN(plugin);
|
|
-
|
|
- word_list = Util_BuildList(word);
|
|
- if (word_list == NULL) {
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
- word_eol_list = Util_BuildList(word_eol);
|
|
- if (word_eol_list == NULL) {
|
|
- Py_DECREF(word_list);
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
-
|
|
- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
|
|
- word_eol_list, hook->userdata);
|
|
- Py_DECREF(word_list);
|
|
- Py_DECREF(word_eol_list);
|
|
-
|
|
- if (retobj == Py_None) {
|
|
- ret = HEXCHAT_EAT_NONE;
|
|
- Py_DECREF(retobj);
|
|
- } else if (retobj) {
|
|
- ret = PyLong_AsLong(retobj);
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- }
|
|
-
|
|
- END_PLUGIN(plugin);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int
|
|
-Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
|
|
-{
|
|
- Hook *hook = (Hook *) userdata;
|
|
- PyObject *retobj;
|
|
- PyObject *word_list;
|
|
- PyObject *word_eol_list;
|
|
- PyObject *attributes;
|
|
- int ret = HEXCHAT_EAT_NONE;
|
|
- PyObject *plugin;
|
|
-
|
|
- plugin = hook->plugin;
|
|
- BEGIN_PLUGIN(plugin);
|
|
-
|
|
- word_list = Util_BuildList(word);
|
|
- if (word_list == NULL) {
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
- word_eol_list = Util_BuildEOLList(word);
|
|
- if (word_eol_list == NULL) {
|
|
- Py_DECREF(word_list);
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
-
|
|
- attributes = Attribute_New(attrs);
|
|
-
|
|
- retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
|
|
- word_eol_list, hook->userdata, attributes);
|
|
-
|
|
- Py_DECREF(word_list);
|
|
- Py_DECREF(word_eol_list);
|
|
- Py_DECREF(attributes);
|
|
-
|
|
- if (retobj == Py_None) {
|
|
- ret = HEXCHAT_EAT_NONE;
|
|
- Py_DECREF(retobj);
|
|
- } else if (retobj) {
|
|
- ret = PyLong_AsLong(retobj);
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- }
|
|
-
|
|
- END_PLUGIN(plugin);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int
|
|
-Callback_Print(char *word[], void *userdata)
|
|
-{
|
|
- Hook *hook = (Hook *) userdata;
|
|
- PyObject *retobj;
|
|
- PyObject *word_list;
|
|
- PyObject *word_eol_list;
|
|
- int ret = HEXCHAT_EAT_NONE;
|
|
- PyObject *plugin;
|
|
-
|
|
- plugin = hook->plugin;
|
|
- BEGIN_PLUGIN(plugin);
|
|
-
|
|
- word_list = Util_BuildList(word);
|
|
- if (word_list == NULL) {
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
- word_eol_list = Util_BuildEOLList(word);
|
|
- if (word_eol_list == NULL) {
|
|
- Py_DECREF(word_list);
|
|
- END_PLUGIN(plugin);
|
|
- return HEXCHAT_EAT_NONE;
|
|
- }
|
|
-
|
|
- retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
|
|
- word_eol_list, hook->userdata);
|
|
-
|
|
- Py_DECREF(word_list);
|
|
- Py_DECREF(word_eol_list);
|
|
-
|
|
- if (retobj == Py_None) {
|
|
- ret = HEXCHAT_EAT_NONE;
|
|
- Py_DECREF(retobj);
|
|
- } else if (retobj) {
|
|
- ret = PyLong_AsLong(retobj);
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- }
|
|
-
|
|
- END_PLUGIN(plugin);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int
|
|
-Callback_Timer(void *userdata)
|
|
-{
|
|
- Hook *hook = (Hook *) userdata;
|
|
- PyObject *retobj;
|
|
- int ret = 0;
|
|
- PyObject *plugin;
|
|
-
|
|
- plugin = hook->plugin;
|
|
-
|
|
- BEGIN_PLUGIN(hook->plugin);
|
|
-
|
|
- retobj = PyObject_CallFunction(hook->callback, "(O)", hook->userdata);
|
|
-
|
|
- if (retobj) {
|
|
- ret = PyObject_IsTrue(retobj);
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- }
|
|
-
|
|
- /* Returning 0 for this callback unhooks itself. */
|
|
- if (ret == 0)
|
|
- Plugin_RemoveHook(plugin, hook);
|
|
-
|
|
- END_PLUGIN(plugin);
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
-static int
|
|
-Callback_ThreadTimer(void *userdata)
|
|
-{
|
|
- RELEASE_XCHAT_LOCK();
|
|
-#ifndef WIN32
|
|
- usleep(1);
|
|
-#endif
|
|
- ACQUIRE_XCHAT_LOCK();
|
|
- return 1;
|
|
-}
|
|
-#endif
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* XChatOut object */
|
|
-
|
|
-/* We keep this information global, so we can reset it when the
|
|
- * deinit function is called. */
|
|
-/* XXX This should be somehow bound to the printing context. */
|
|
-static GString *xchatout_buffer = NULL;
|
|
-
|
|
-static PyObject *
|
|
-XChatOut_New()
|
|
-{
|
|
- XChatOutObject *xcoobj;
|
|
- xcoobj = PyObject_New(XChatOutObject, &XChatOut_Type);
|
|
- if (xcoobj != NULL)
|
|
- xcoobj->softspace = 0;
|
|
- return (PyObject *) xcoobj;
|
|
-}
|
|
-
|
|
-static void
|
|
-XChatOut_dealloc(PyObject *self)
|
|
-{
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
-}
|
|
-
|
|
-/* This is a little bit complex because we have to buffer data
|
|
- * until a \n is received, since xchat breaks the line automatically.
|
|
- * We also crop the last \n for this reason. */
|
|
-static PyObject *
|
|
-XChatOut_write(PyObject *self, PyObject *args)
|
|
-{
|
|
- gboolean add_space;
|
|
- char *data, *pos;
|
|
-
|
|
- if (!PyArg_ParseTuple(args, "s:write", &data))
|
|
- return NULL;
|
|
- if (!data || !*data) {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
|
|
- if (((XChatOutObject *)self)->softspace) {
|
|
- add_space = TRUE;
|
|
- ((XChatOutObject *)self)->softspace = 0;
|
|
- } else {
|
|
- add_space = FALSE;
|
|
- }
|
|
-
|
|
- g_string_append (xchatout_buffer, data);
|
|
-
|
|
- /* If not end of line add space to continue buffer later */
|
|
- if (add_space && xchatout_buffer->str[xchatout_buffer->len - 1] != '\n')
|
|
- {
|
|
- g_string_append_c (xchatout_buffer, ' ');
|
|
- }
|
|
-
|
|
- /* If there is an end of line print up to that */
|
|
- if ((pos = strrchr (xchatout_buffer->str, '\n')))
|
|
- {
|
|
- *pos = '\0';
|
|
- hexchat_print (ph, xchatout_buffer->str);
|
|
-
|
|
- /* Then remove it from buffer */
|
|
- g_string_erase (xchatout_buffer, 0, pos - xchatout_buffer->str + 1);
|
|
- }
|
|
-
|
|
- END_XCHAT_CALLS();
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-#define OFF(x) offsetof(XChatOutObject, x)
|
|
-
|
|
-static PyMemberDef XChatOut_members[] = {
|
|
- {"softspace", T_INT, OFF(softspace), 0},
|
|
- {0}
|
|
-};
|
|
-
|
|
-static PyMethodDef XChatOut_methods[] = {
|
|
- {"write", XChatOut_write, METH_VARARGS},
|
|
- {NULL, NULL}
|
|
-};
|
|
-
|
|
-static PyTypeObject XChatOut_Type = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "hexchat.XChatOut", /*tp_name*/
|
|
- sizeof(XChatOutObject), /*tp_basicsize*/
|
|
- 0, /*tp_itemsize*/
|
|
- XChatOut_dealloc, /*tp_dealloc*/
|
|
- 0, /*tp_print*/
|
|
- 0, /*tp_getattr*/
|
|
- 0, /*tp_setattr*/
|
|
- 0, /*tp_compare*/
|
|
- 0, /*tp_repr*/
|
|
- 0, /*tp_as_number*/
|
|
- 0, /*tp_as_sequence*/
|
|
- 0, /*tp_as_mapping*/
|
|
- 0, /*tp_hash*/
|
|
- 0, /*tp_call*/
|
|
- 0, /*tp_str*/
|
|
- PyObject_GenericGetAttr,/*tp_getattro*/
|
|
- PyObject_GenericSetAttr,/*tp_setattro*/
|
|
- 0, /*tp_as_buffer*/
|
|
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
- 0, /*tp_doc*/
|
|
- 0, /*tp_traverse*/
|
|
- 0, /*tp_clear*/
|
|
- 0, /*tp_richcompare*/
|
|
- 0, /*tp_weaklistoffset*/
|
|
- 0, /*tp_iter*/
|
|
- 0, /*tp_iternext*/
|
|
- XChatOut_methods, /*tp_methods*/
|
|
- XChatOut_members, /*tp_members*/
|
|
- 0, /*tp_getset*/
|
|
- 0, /*tp_base*/
|
|
- 0, /*tp_dict*/
|
|
- 0, /*tp_descr_get*/
|
|
- 0, /*tp_descr_set*/
|
|
- 0, /*tp_dictoffset*/
|
|
- 0, /*tp_init*/
|
|
- PyType_GenericAlloc, /*tp_alloc*/
|
|
- PyType_GenericNew, /*tp_new*/
|
|
- PyObject_Del, /*tp_free*/
|
|
- 0, /*tp_is_gc*/
|
|
-};
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Attribute object */
|
|
-
|
|
-#undef OFF
|
|
-#define OFF(x) offsetof(AttributeObject, x)
|
|
-
|
|
-static PyMemberDef Attribute_members[] = {
|
|
- {"time", T_OBJECT, OFF(time), 0},
|
|
- {0}
|
|
-};
|
|
-
|
|
-static void
|
|
-Attribute_dealloc(PyObject *self)
|
|
-{
|
|
- Py_DECREF(((AttributeObject*)self)->time);
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Attribute_repr(PyObject *self)
|
|
-{
|
|
- return PyUnicode_FromFormat("<Attribute object at %p>", self);
|
|
-}
|
|
-
|
|
-static PyTypeObject Attribute_Type = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "hexchat.Attribute", /*tp_name*/
|
|
- sizeof(AttributeObject), /*tp_basicsize*/
|
|
- 0, /*tp_itemsize*/
|
|
- Attribute_dealloc, /*tp_dealloc*/
|
|
- 0, /*tp_print*/
|
|
- 0, /*tp_getattr*/
|
|
- 0, /*tp_setattr*/
|
|
- 0, /*tp_compare*/
|
|
- Attribute_repr, /*tp_repr*/
|
|
- 0, /*tp_as_number*/
|
|
- 0, /*tp_as_sequence*/
|
|
- 0, /*tp_as_mapping*/
|
|
- 0, /*tp_hash*/
|
|
- 0, /*tp_call*/
|
|
- 0, /*tp_str*/
|
|
- PyObject_GenericGetAttr,/*tp_getattro*/
|
|
- PyObject_GenericSetAttr,/*tp_setattro*/
|
|
- 0, /*tp_as_buffer*/
|
|
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
- 0, /*tp_doc*/
|
|
- 0, /*tp_traverse*/
|
|
- 0, /*tp_clear*/
|
|
- 0, /*tp_richcompare*/
|
|
- 0, /*tp_weaklistoffset*/
|
|
- 0, /*tp_iter*/
|
|
- 0, /*tp_iternext*/
|
|
- 0, /*tp_methods*/
|
|
- Attribute_members, /*tp_members*/
|
|
- 0, /*tp_getset*/
|
|
- 0, /*tp_base*/
|
|
- 0, /*tp_dict*/
|
|
- 0, /*tp_descr_get*/
|
|
- 0, /*tp_descr_set*/
|
|
- 0, /*tp_dictoffset*/
|
|
- 0, /*tp_init*/
|
|
- PyType_GenericAlloc, /*tp_alloc*/
|
|
- PyType_GenericNew, /*tp_new*/
|
|
- PyObject_Del, /*tp_free*/
|
|
- 0, /*tp_is_gc*/
|
|
-};
|
|
-
|
|
-static PyObject *
|
|
-Attribute_New(hexchat_event_attrs *attrs)
|
|
-{
|
|
- AttributeObject *attr;
|
|
- attr = PyObject_New(AttributeObject, &Attribute_Type);
|
|
- if (attr != NULL) {
|
|
- attr->time = PyLong_FromLong((long)attrs->server_time_utc);
|
|
- }
|
|
- return (PyObject *) attr;
|
|
-}
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Context object */
|
|
-
|
|
-static void
|
|
-Context_dealloc(PyObject *self)
|
|
-{
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_set(ContextObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *plugin = Plugin_GetCurrent();
|
|
- Plugin_SetContext(plugin, self->context);
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_command(ContextObject *self, PyObject *args)
|
|
-{
|
|
- char *text;
|
|
- if (!PyArg_ParseTuple(args, "s:command", &text))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
|
|
- hexchat_set_context(ph, self->context);
|
|
- hexchat_command(ph, text);
|
|
- END_XCHAT_CALLS();
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_prnt(ContextObject *self, PyObject *args)
|
|
-{
|
|
- char *text;
|
|
- if (!PyArg_ParseTuple(args, "s:prnt", &text))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
|
|
- hexchat_set_context(ph, self->context);
|
|
- hexchat_print(ph, text);
|
|
- END_XCHAT_CALLS();
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_emit_print(ContextObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *argv[6];
|
|
- char *name;
|
|
- int res;
|
|
- long time = 0;
|
|
- hexchat_event_attrs *attrs;
|
|
- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
|
|
- "arg4", "arg5", "arg6",
|
|
- "time", NULL};
|
|
- memset(&argv, 0, sizeof(char*)*6);
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
|
|
- &argv[0], &argv[1], &argv[2],
|
|
- &argv[3], &argv[4], &argv[5],
|
|
- &time))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(ALLOW_THREADS);
|
|
- hexchat_set_context(ph, self->context);
|
|
- attrs = hexchat_event_attrs_create(ph);
|
|
- attrs->server_time_utc = (time_t)time;
|
|
-
|
|
- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
|
|
- argv[3], argv[4], argv[5], NULL);
|
|
-
|
|
- hexchat_event_attrs_free(ph, attrs);
|
|
- END_XCHAT_CALLS();
|
|
- return PyLong_FromLong(res);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_get_info(ContextObject *self, PyObject *args)
|
|
-{
|
|
- const char *info;
|
|
- char *name;
|
|
- if (!PyArg_ParseTuple(args, "s:get_info", &name))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hexchat_set_context(ph, self->context);
|
|
- info = hexchat_get_info(ph, name);
|
|
- END_XCHAT_CALLS();
|
|
- if (info == NULL) {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- return PyUnicode_FromString(info);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_get_list(ContextObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *plugin = Plugin_GetCurrent();
|
|
- hexchat_context *saved_context = Plugin_GetContext(plugin);
|
|
- PyObject *ret;
|
|
- Plugin_SetContext(plugin, self->context);
|
|
- ret = Module_xchat_get_list((PyObject*)self, args);
|
|
- Plugin_SetContext(plugin, saved_context);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* needed to make context1 == context2 work */
|
|
-static PyObject *
|
|
-Context_compare(ContextObject *a, ContextObject *b, int op)
|
|
-{
|
|
- PyObject *ret;
|
|
- /* check for == */
|
|
- if (op == Py_EQ)
|
|
- ret = (a->context == b->context ? Py_True : Py_False);
|
|
- /* check for != */
|
|
- else if (op == Py_NE)
|
|
- ret = (a->context != b->context ? Py_True : Py_False);
|
|
- /* only makes sense as == and != */
|
|
- else
|
|
- {
|
|
- PyErr_SetString(PyExc_TypeError, "contexts are either equal or not equal");
|
|
- ret = Py_None;
|
|
- }
|
|
-
|
|
- Py_INCREF(ret);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static PyMethodDef Context_methods[] = {
|
|
- {"set", (PyCFunction) Context_set, METH_NOARGS},
|
|
- {"command", (PyCFunction) Context_command, METH_VARARGS},
|
|
- {"prnt", (PyCFunction) Context_prnt, METH_VARARGS},
|
|
- {"emit_print", (PyCFunction) Context_emit_print, METH_VARARGS|METH_KEYWORDS},
|
|
- {"get_info", (PyCFunction) Context_get_info, METH_VARARGS},
|
|
- {"get_list", (PyCFunction) Context_get_list, METH_VARARGS},
|
|
- {NULL, NULL}
|
|
-};
|
|
-
|
|
-static PyTypeObject Context_Type = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "hexchat.Context", /*tp_name*/
|
|
- sizeof(ContextObject), /*tp_basicsize*/
|
|
- 0, /*tp_itemsize*/
|
|
- Context_dealloc, /*tp_dealloc*/
|
|
- 0, /*tp_print*/
|
|
- 0, /*tp_getattr*/
|
|
- 0, /*tp_setattr*/
|
|
- 0, /*tp_compare*/
|
|
- 0, /*tp_repr*/
|
|
- 0, /*tp_as_number*/
|
|
- 0, /*tp_as_sequence*/
|
|
- 0, /*tp_as_mapping*/
|
|
- 0, /*tp_hash*/
|
|
- 0, /*tp_call*/
|
|
- 0, /*tp_str*/
|
|
- PyObject_GenericGetAttr,/*tp_getattro*/
|
|
- PyObject_GenericSetAttr,/*tp_setattro*/
|
|
- 0, /*tp_as_buffer*/
|
|
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
- 0, /*tp_doc*/
|
|
- 0, /*tp_traverse*/
|
|
- 0, /*tp_clear*/
|
|
- (richcmpfunc)Context_compare, /*tp_richcompare*/
|
|
- 0, /*tp_weaklistoffset*/
|
|
- 0, /*tp_iter*/
|
|
- 0, /*tp_iternext*/
|
|
- Context_methods, /*tp_methods*/
|
|
- 0, /*tp_members*/
|
|
- 0, /*tp_getset*/
|
|
- 0, /*tp_base*/
|
|
- 0, /*tp_dict*/
|
|
- 0, /*tp_descr_get*/
|
|
- 0, /*tp_descr_set*/
|
|
- 0, /*tp_dictoffset*/
|
|
- 0, /*tp_init*/
|
|
- PyType_GenericAlloc, /*tp_alloc*/
|
|
- PyType_GenericNew, /*tp_new*/
|
|
- PyObject_Del, /*tp_free*/
|
|
- 0, /*tp_is_gc*/
|
|
-};
|
|
-
|
|
-static PyObject *
|
|
-Context_FromContext(hexchat_context *context)
|
|
-{
|
|
- ContextObject *ctxobj = PyObject_New(ContextObject, &Context_Type);
|
|
- if (ctxobj != NULL)
|
|
- ctxobj->context = context;
|
|
- return (PyObject *) ctxobj;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Context_FromServerAndChannel(char *server, char *channel)
|
|
-{
|
|
- ContextObject *ctxobj;
|
|
- hexchat_context *context;
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- context = hexchat_find_context(ph, server, channel);
|
|
- END_XCHAT_CALLS();
|
|
- if (context == NULL)
|
|
- return NULL;
|
|
- ctxobj = PyObject_New(ContextObject, &Context_Type);
|
|
- if (ctxobj == NULL)
|
|
- return NULL;
|
|
- ctxobj->context = context;
|
|
- return (PyObject *) ctxobj;
|
|
-}
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* ListItem object */
|
|
-
|
|
-#undef OFF
|
|
-#define OFF(x) offsetof(ListItemObject, x)
|
|
-
|
|
-static PyMemberDef ListItem_members[] = {
|
|
- {"__dict__", T_OBJECT, OFF(dict), 0},
|
|
- {0}
|
|
-};
|
|
-
|
|
-static void
|
|
-ListItem_dealloc(PyObject *self)
|
|
-{
|
|
- Py_DECREF(((ListItemObject*)self)->dict);
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-ListItem_repr(PyObject *self)
|
|
-{
|
|
- return PyUnicode_FromFormat("<%s list item at %p>",
|
|
- ((ListItemObject*)self)->listname, self);
|
|
-}
|
|
-
|
|
-static PyTypeObject ListItem_Type = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "hexchat.ListItem", /*tp_name*/
|
|
- sizeof(ListItemObject), /*tp_basicsize*/
|
|
- 0, /*tp_itemsize*/
|
|
- ListItem_dealloc, /*tp_dealloc*/
|
|
- 0, /*tp_print*/
|
|
- 0, /*tp_getattr*/
|
|
- 0, /*tp_setattr*/
|
|
- 0, /*tp_compare*/
|
|
- ListItem_repr, /*tp_repr*/
|
|
- 0, /*tp_as_number*/
|
|
- 0, /*tp_as_sequence*/
|
|
- 0, /*tp_as_mapping*/
|
|
- 0, /*tp_hash*/
|
|
- 0, /*tp_call*/
|
|
- 0, /*tp_str*/
|
|
- PyObject_GenericGetAttr,/*tp_getattro*/
|
|
- PyObject_GenericSetAttr,/*tp_setattro*/
|
|
- 0, /*tp_as_buffer*/
|
|
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
- 0, /*tp_doc*/
|
|
- 0, /*tp_traverse*/
|
|
- 0, /*tp_clear*/
|
|
- 0, /*tp_richcompare*/
|
|
- 0, /*tp_weaklistoffset*/
|
|
- 0, /*tp_iter*/
|
|
- 0, /*tp_iternext*/
|
|
- 0, /*tp_methods*/
|
|
- ListItem_members, /*tp_members*/
|
|
- 0, /*tp_getset*/
|
|
- 0, /*tp_base*/
|
|
- 0, /*tp_dict*/
|
|
- 0, /*tp_descr_get*/
|
|
- 0, /*tp_descr_set*/
|
|
- OFF(dict), /*tp_dictoffset*/
|
|
- 0, /*tp_init*/
|
|
- PyType_GenericAlloc, /*tp_alloc*/
|
|
- PyType_GenericNew, /*tp_new*/
|
|
- PyObject_Del, /*tp_free*/
|
|
- 0, /*tp_is_gc*/
|
|
-};
|
|
-
|
|
-static PyObject *
|
|
-ListItem_New(const char *listname)
|
|
-{
|
|
- ListItemObject *item;
|
|
- item = PyObject_New(ListItemObject, &ListItem_Type);
|
|
- if (item != NULL) {
|
|
- /* listname parameter must be statically allocated. */
|
|
- item->listname = listname;
|
|
- item->dict = PyDict_New();
|
|
- if (item->dict == NULL) {
|
|
- Py_DECREF(item);
|
|
- item = NULL;
|
|
- }
|
|
- }
|
|
- return (PyObject *) item;
|
|
-}
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Plugin object */
|
|
-
|
|
-#define GET_MODULE_DATA(x, force) \
|
|
- o = PyObject_GetAttrString(m, "__module_" #x "__"); \
|
|
- if (o == NULL) { \
|
|
- if (force) { \
|
|
- hexchat_print(ph, "Module has no __module_" #x "__ " \
|
|
- "defined"); \
|
|
- goto error; \
|
|
- } \
|
|
- plugin->x = g_strdup(""); \
|
|
- } else {\
|
|
- if (!PyUnicode_Check(o)) { \
|
|
- hexchat_print(ph, "Variable __module_" #x "__ " \
|
|
- "must be a string"); \
|
|
- goto error; \
|
|
- } \
|
|
- plugin->x = g_strdup(PyUnicode_AsUTF8(o)); \
|
|
- if (plugin->x == NULL) { \
|
|
- hexchat_print(ph, "Not enough memory to allocate " #x); \
|
|
- goto error; \
|
|
- } \
|
|
- }
|
|
-
|
|
-static PyObject *
|
|
-Plugin_GetCurrent()
|
|
-{
|
|
- PyObject *plugin;
|
|
- plugin = PySys_GetObject("__plugin__");
|
|
- if (plugin == NULL)
|
|
- PyErr_SetString(PyExc_RuntimeError, "lost sys.__plugin__");
|
|
- return plugin;
|
|
-}
|
|
-
|
|
-static hexchat_plugin *
|
|
-Plugin_GetHandle(PluginObject *plugin)
|
|
-{
|
|
- /* This works but the issue is that the script must be ran to get
|
|
- * the name of it thus upon first use it will use the wrong handler
|
|
- * work around would be to run a fake script once to get name? */
|
|
-#if 0
|
|
- /* return fake handle for pluginpref */
|
|
- if (plugin->gui != NULL)
|
|
- return plugin->gui;
|
|
- else
|
|
-#endif
|
|
- return ph;
|
|
-}
|
|
-
|
|
-static PluginObject *
|
|
-Plugin_ByString(char *str)
|
|
-{
|
|
- GSList *list;
|
|
- PluginObject *plugin;
|
|
- char *basename;
|
|
- list = plugin_list;
|
|
- while (list != NULL) {
|
|
- plugin = (PluginObject *) list->data;
|
|
- basename = g_path_get_basename(plugin->filename);
|
|
- if (basename == NULL)
|
|
- break;
|
|
- if (strcasecmp(plugin->name, str) == 0 ||
|
|
- strcasecmp(plugin->filename, str) == 0 ||
|
|
- strcasecmp(basename, str) == 0) {
|
|
- g_free(basename);
|
|
- return plugin;
|
|
- }
|
|
- g_free(basename);
|
|
- list = list->next;
|
|
- }
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-static Hook *
|
|
-Plugin_AddHook(int type, PyObject *plugin, PyObject *callback,
|
|
- PyObject *userdata, char *name, void *data)
|
|
-{
|
|
- Hook *hook = g_new(Hook, 1);
|
|
- hook->type = type;
|
|
- hook->plugin = plugin;
|
|
- Py_INCREF(callback);
|
|
- hook->callback = callback;
|
|
- Py_INCREF(userdata);
|
|
- hook->userdata = userdata;
|
|
- hook->name = g_strdup (name);
|
|
- hook->data = NULL;
|
|
- Plugin_SetHooks(plugin, g_slist_append(Plugin_GetHooks(plugin),
|
|
- hook));
|
|
-
|
|
- return hook;
|
|
-}
|
|
-
|
|
-static Hook *
|
|
-Plugin_FindHook(PyObject *plugin, char *name)
|
|
-{
|
|
- Hook *hook = NULL;
|
|
- GSList *plugin_hooks = Plugin_GetHooks(plugin);
|
|
-
|
|
- while (plugin_hooks)
|
|
- {
|
|
- if (g_strcmp0 (((Hook *)plugin_hooks->data)->name, name) == 0)
|
|
- {
|
|
- hook = (Hook *)plugin_hooks->data;
|
|
- break;
|
|
- }
|
|
-
|
|
- plugin_hooks = g_slist_next(plugin_hooks);
|
|
- }
|
|
-
|
|
- return hook;
|
|
-}
|
|
-
|
|
-static void
|
|
-Plugin_RemoveHook(PyObject *plugin, Hook *hook)
|
|
-{
|
|
- GSList *list;
|
|
- /* Is this really a hook of the running plugin? */
|
|
- list = g_slist_find(Plugin_GetHooks(plugin), hook);
|
|
- if (list) {
|
|
- /* Ok, unhook it. */
|
|
- if (hook->type != HOOK_UNLOAD) {
|
|
- /* This is an xchat hook. Unregister it. */
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hexchat_unhook(ph, (hexchat_hook*)hook->data);
|
|
- END_XCHAT_CALLS();
|
|
- }
|
|
- Plugin_SetHooks(plugin,
|
|
- g_slist_remove(Plugin_GetHooks(plugin),
|
|
- hook));
|
|
- Py_DECREF(hook->callback);
|
|
- Py_DECREF(hook->userdata);
|
|
- g_free(hook->name);
|
|
- g_free(hook);
|
|
- }
|
|
-}
|
|
-
|
|
-static void
|
|
-Plugin_RemoveAllHooks(PyObject *plugin)
|
|
-{
|
|
- GSList *list = Plugin_GetHooks(plugin);
|
|
- while (list) {
|
|
- Hook *hook = (Hook *) list->data;
|
|
- if (hook->type != HOOK_UNLOAD) {
|
|
- /* This is an xchat hook. Unregister it. */
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hexchat_unhook(ph, (hexchat_hook*)hook->data);
|
|
- END_XCHAT_CALLS();
|
|
- }
|
|
- Py_DECREF(hook->callback);
|
|
- Py_DECREF(hook->userdata);
|
|
- g_free(hook->name);
|
|
- g_free(hook);
|
|
- list = list->next;
|
|
- }
|
|
- Plugin_SetHooks(plugin, NULL);
|
|
-}
|
|
-
|
|
-static void
|
|
-Plugin_Delete(PyObject *plugin)
|
|
-{
|
|
- PyThreadState *tstate = ((PluginObject*)plugin)->tstate;
|
|
- GSList *list = Plugin_GetHooks(plugin);
|
|
- while (list) {
|
|
- Hook *hook = (Hook *) list->data;
|
|
- if (hook->type == HOOK_UNLOAD) {
|
|
- PyObject *retobj;
|
|
- retobj = PyObject_CallFunction(hook->callback, "(O)",
|
|
- hook->userdata);
|
|
- if (retobj) {
|
|
- Py_DECREF(retobj);
|
|
- } else {
|
|
- PyErr_Print();
|
|
- PyErr_Clear();
|
|
- }
|
|
- }
|
|
- list = list->next;
|
|
- }
|
|
- Plugin_RemoveAllHooks(plugin);
|
|
- if (((PluginObject *)plugin)->gui != NULL)
|
|
- hexchat_plugingui_remove(ph, ((PluginObject *)plugin)->gui);
|
|
- Py_DECREF(plugin);
|
|
- /*PyThreadState_Swap(tstate); needed? */
|
|
- Py_EndInterpreter(tstate);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Plugin_New(char *filename, PyObject *xcoobj)
|
|
-{
|
|
- PluginObject *plugin = NULL;
|
|
- PyObject *m, *o;
|
|
-#ifdef IS_PY3K
|
|
- wchar_t *argv[] = { L"<hexchat>", 0 };
|
|
-#else
|
|
- char *argv[] = { "<hexchat>", 0 };
|
|
-#endif
|
|
-
|
|
- if (filename) {
|
|
- char *old_filename = filename;
|
|
- filename = Util_Expand(filename);
|
|
- if (filename == NULL) {
|
|
- hexchat_printf(ph, "File not found: %s", old_filename);
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Allocate plugin structure. */
|
|
- plugin = PyObject_New(PluginObject, &Plugin_Type);
|
|
- if (plugin == NULL) {
|
|
- hexchat_print(ph, "Can't create plugin object");
|
|
- goto error;
|
|
- }
|
|
-
|
|
- Plugin_SetName(plugin, NULL);
|
|
- Plugin_SetVersion(plugin, NULL);
|
|
- Plugin_SetFilename(plugin, NULL);
|
|
- Plugin_SetDescription(plugin, NULL);
|
|
- Plugin_SetHooks(plugin, NULL);
|
|
- Plugin_SetContext(plugin, hexchat_get_context(ph));
|
|
- Plugin_SetGui(plugin, NULL);
|
|
-
|
|
- /* Start a new interpreter environment for this plugin. */
|
|
- PyEval_AcquireThread(main_tstate);
|
|
- plugin->tstate = Py_NewInterpreter();
|
|
- if (plugin->tstate == NULL) {
|
|
- hexchat_print(ph, "Can't create interpreter state");
|
|
- goto error;
|
|
- }
|
|
-
|
|
- PySys_SetArgv(1, argv);
|
|
- PySys_SetObject("__plugin__", (PyObject *) plugin);
|
|
-
|
|
- /* Set stdout and stderr to xchatout. */
|
|
- Py_INCREF(xcoobj);
|
|
- PySys_SetObject("stdout", xcoobj);
|
|
- Py_INCREF(xcoobj);
|
|
- PySys_SetObject("stderr", xcoobj);
|
|
-
|
|
- if (filename) {
|
|
-#ifdef WIN32
|
|
- char *file;
|
|
- if (!g_file_get_contents(filename, &file, NULL, NULL)) {
|
|
- hexchat_printf(ph, "Can't open file %s: %s\n",
|
|
- filename, strerror(errno));
|
|
- goto error;
|
|
- }
|
|
-
|
|
- if (PyRun_SimpleString(file) != 0) {
|
|
- hexchat_printf(ph, "Error loading module %s\n",
|
|
- filename);
|
|
- g_free (file);
|
|
- goto error;
|
|
- }
|
|
-
|
|
- plugin->filename = filename;
|
|
- filename = NULL;
|
|
- g_free (file);
|
|
-#else
|
|
- FILE *fp;
|
|
- plugin->filename = filename;
|
|
-
|
|
- /* It's now owned by the plugin. */
|
|
- filename = NULL;
|
|
-
|
|
- /* Open the plugin file. */
|
|
- fp = fopen(plugin->filename, "r");
|
|
- if (fp == NULL) {
|
|
- hexchat_printf(ph, "Can't open file %s: %s\n",
|
|
- plugin->filename, strerror(errno));
|
|
- goto error;
|
|
- }
|
|
-
|
|
- /* Run the plugin. */
|
|
- if (PyRun_SimpleFile(fp, plugin->filename) != 0) {
|
|
- hexchat_printf(ph, "Error loading module %s\n",
|
|
- plugin->filename);
|
|
- fclose(fp);
|
|
- goto error;
|
|
- }
|
|
- fclose(fp);
|
|
-#endif
|
|
- m = PyDict_GetItemString(PyImport_GetModuleDict(),
|
|
- "__main__");
|
|
- if (m == NULL) {
|
|
- hexchat_print(ph, "Can't get __main__ module");
|
|
- goto error;
|
|
- }
|
|
- GET_MODULE_DATA(name, 1);
|
|
- GET_MODULE_DATA(version, 0);
|
|
- GET_MODULE_DATA(description, 0);
|
|
- plugin->gui = hexchat_plugingui_add(ph, plugin->filename,
|
|
- plugin->name,
|
|
- plugin->description,
|
|
- plugin->version, NULL);
|
|
- }
|
|
-
|
|
- PyEval_ReleaseThread(plugin->tstate);
|
|
-
|
|
- return (PyObject *) plugin;
|
|
-
|
|
-error:
|
|
- g_free(filename);
|
|
-
|
|
- if (plugin) {
|
|
- if (plugin->tstate)
|
|
- Plugin_Delete((PyObject *)plugin);
|
|
- else
|
|
- Py_DECREF(plugin);
|
|
- }
|
|
- PyEval_ReleaseLock();
|
|
-
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-static void
|
|
-Plugin_dealloc(PluginObject *self)
|
|
-{
|
|
- g_free(self->filename);
|
|
- g_free(self->name);
|
|
- g_free(self->version);
|
|
- g_free(self->description);
|
|
- Py_TYPE(self)->tp_free((PyObject *)self);
|
|
-}
|
|
-
|
|
-static PyTypeObject Plugin_Type = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "hexchat.Plugin", /*tp_name*/
|
|
- sizeof(PluginObject), /*tp_basicsize*/
|
|
- 0, /*tp_itemsize*/
|
|
- (destructor)Plugin_dealloc, /*tp_dealloc*/
|
|
- 0, /*tp_print*/
|
|
- 0, /*tp_getattr*/
|
|
- 0, /*tp_setattr*/
|
|
- 0, /*tp_compare*/
|
|
- 0, /*tp_repr*/
|
|
- 0, /*tp_as_number*/
|
|
- 0, /*tp_as_sequence*/
|
|
- 0, /*tp_as_mapping*/
|
|
- 0, /*tp_hash*/
|
|
- 0, /*tp_call*/
|
|
- 0, /*tp_str*/
|
|
- PyObject_GenericGetAttr,/*tp_getattro*/
|
|
- PyObject_GenericSetAttr,/*tp_setattro*/
|
|
- 0, /*tp_as_buffer*/
|
|
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
- 0, /*tp_doc*/
|
|
- 0, /*tp_traverse*/
|
|
- 0, /*tp_clear*/
|
|
- 0, /*tp_richcompare*/
|
|
- 0, /*tp_weaklistoffset*/
|
|
- 0, /*tp_iter*/
|
|
- 0, /*tp_iternext*/
|
|
- 0, /*tp_methods*/
|
|
- 0, /*tp_members*/
|
|
- 0, /*tp_getset*/
|
|
- 0, /*tp_base*/
|
|
- 0, /*tp_dict*/
|
|
- 0, /*tp_descr_get*/
|
|
- 0, /*tp_descr_set*/
|
|
- 0, /*tp_dictoffset*/
|
|
- 0, /*tp_init*/
|
|
- PyType_GenericAlloc, /*tp_alloc*/
|
|
- PyType_GenericNew, /*tp_new*/
|
|
- PyObject_Del, /*tp_free*/
|
|
- 0, /*tp_is_gc*/
|
|
-};
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* XChat module */
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_command(PyObject *self, PyObject *args)
|
|
-{
|
|
- char *text;
|
|
- if (!PyArg_ParseTuple(args, "s:command", &text))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
|
|
- hexchat_command(ph, text);
|
|
- END_XCHAT_CALLS();
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_xchat_prnt(PyObject *self, PyObject *args)
|
|
-{
|
|
- char *text;
|
|
- if (!PyArg_ParseTuple(args, "s:prnt", &text))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
|
|
- hexchat_print(ph, text);
|
|
- END_XCHAT_CALLS();
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_emit_print(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *argv[6];
|
|
- char *name;
|
|
- int res;
|
|
- long time = 0;
|
|
- hexchat_event_attrs *attrs;
|
|
- char *kwlist[] = {"name", "arg1", "arg2", "arg3",
|
|
- "arg4", "arg5", "arg6",
|
|
- "time", NULL};
|
|
- memset(&argv, 0, sizeof(char*)*6);
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ssssssl:print_event", kwlist, &name,
|
|
- &argv[0], &argv[1], &argv[2],
|
|
- &argv[3], &argv[4], &argv[5],
|
|
- &time))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT|ALLOW_THREADS);
|
|
- attrs = hexchat_event_attrs_create(ph);
|
|
- attrs->server_time_utc = (time_t)time;
|
|
-
|
|
- res = hexchat_emit_print_attrs(ph, attrs, name, argv[0], argv[1], argv[2],
|
|
- argv[3], argv[4], argv[5], NULL);
|
|
-
|
|
- hexchat_event_attrs_free(ph, attrs);
|
|
- END_XCHAT_CALLS();
|
|
- return PyLong_FromLong(res);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_get_info(PyObject *self, PyObject *args)
|
|
-{
|
|
- const char *info;
|
|
- char *name;
|
|
- if (!PyArg_ParseTuple(args, "s:get_info", &name))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
|
|
- info = hexchat_get_info(ph, name);
|
|
- END_XCHAT_CALLS();
|
|
- if (info == NULL) {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- if (strcmp (name, "gtkwin_ptr") == 0 || strcmp (name, "win_ptr") == 0)
|
|
- return PyUnicode_FromFormat("%p", info); /* format as pointer */
|
|
- else
|
|
- return PyUnicode_FromString(info);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_xchat_get_prefs(PyObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *res;
|
|
- const char *info;
|
|
- int integer;
|
|
- char *name;
|
|
- int type;
|
|
- if (!PyArg_ParseTuple(args, "s:get_prefs", &name))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- type = hexchat_get_prefs(ph, name, &info, &integer);
|
|
- END_XCHAT_CALLS();
|
|
- switch (type) {
|
|
- case 0:
|
|
- Py_INCREF(Py_None);
|
|
- res = Py_None;
|
|
- break;
|
|
- case 1:
|
|
- res = PyUnicode_FromString((char*)info);
|
|
- break;
|
|
- case 2:
|
|
- case 3:
|
|
- res = PyLong_FromLong(integer);
|
|
- break;
|
|
- default:
|
|
- PyErr_Format(PyExc_RuntimeError,
|
|
- "unknown get_prefs type (%d), "
|
|
- "please report", type);
|
|
- res = NULL;
|
|
- break;
|
|
- }
|
|
- return res;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_get_context(PyObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *plugin;
|
|
- PyObject *ctxobj;
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- ctxobj = Context_FromContext(Plugin_GetContext(plugin));
|
|
- if (ctxobj == NULL) {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- return ctxobj;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_find_context(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *server = NULL;
|
|
- char *channel = NULL;
|
|
- PyObject *ctxobj;
|
|
- char *kwlist[] = {"server", "channel", 0};
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|zz:find_context",
|
|
- kwlist, &server, &channel))
|
|
- return NULL;
|
|
- ctxobj = Context_FromServerAndChannel(server, channel);
|
|
- if (ctxobj == NULL) {
|
|
- Py_RETURN_NONE;
|
|
- }
|
|
- return ctxobj;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_pluginpref_set(PyObject *self, PyObject *args)
|
|
-{
|
|
- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
|
|
- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
|
|
- int result;
|
|
- char *var;
|
|
- PyObject *value;
|
|
-
|
|
- if (!PyArg_ParseTuple(args, "sO:set_pluginpref", &var, &value))
|
|
- return NULL;
|
|
- if (PyLong_Check(value)) {
|
|
- int intvalue = PyLong_AsLong(value);
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- result = hexchat_pluginpref_set_int(prefph, var, intvalue);
|
|
- END_XCHAT_CALLS();
|
|
- }
|
|
- else if (PyUnicode_Check(value)) {
|
|
- char *charvalue = PyUnicode_AsUTF8(value);
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- result = hexchat_pluginpref_set_str(prefph, var, charvalue);
|
|
- END_XCHAT_CALLS();
|
|
- }
|
|
- else
|
|
- result = 0;
|
|
- return PyBool_FromLong(result);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_pluginpref_get(PyObject *self, PyObject *args)
|
|
-{
|
|
- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
|
|
- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
|
|
- PyObject *ret;
|
|
- char *var;
|
|
- char retstr[512];
|
|
- int retint;
|
|
- int result;
|
|
- if (!PyArg_ParseTuple(args, "s:get_pluginpref", &var))
|
|
- return NULL;
|
|
-
|
|
- /* This will always return numbers as integers. */
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- result = hexchat_pluginpref_get_str(prefph, var, retstr);
|
|
- END_XCHAT_CALLS();
|
|
- if (result) {
|
|
- if (strlen (retstr) <= 12) {
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- retint = hexchat_pluginpref_get_int(prefph, var);
|
|
- END_XCHAT_CALLS();
|
|
- if ((retint == -1) && (strcmp(retstr, "-1") != 0))
|
|
- ret = PyUnicode_FromString(retstr);
|
|
- else
|
|
- ret = PyLong_FromLong(retint);
|
|
- } else
|
|
- ret = PyUnicode_FromString(retstr);
|
|
- }
|
|
- else
|
|
- {
|
|
- Py_INCREF(Py_None);
|
|
- ret = Py_None;
|
|
- }
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_pluginpref_delete(PyObject *self, PyObject *args)
|
|
-{
|
|
- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
|
|
- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
|
|
- char *var;
|
|
- int result;
|
|
- if (!PyArg_ParseTuple(args, "s:del_pluginpref", &var))
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- result = hexchat_pluginpref_delete(prefph, var);
|
|
- END_XCHAT_CALLS();
|
|
- return PyBool_FromLong(result);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_pluginpref_list(PyObject *self, PyObject *args)
|
|
-{
|
|
- PluginObject *plugin = (PluginObject*)Plugin_GetCurrent();
|
|
- hexchat_plugin *prefph = Plugin_GetHandle(plugin);
|
|
- char list[4096];
|
|
- char* token;
|
|
- int result;
|
|
- PyObject *pylist;
|
|
- pylist = PyList_New(0);
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- result = hexchat_pluginpref_list(prefph, list);
|
|
- END_XCHAT_CALLS();
|
|
- if (result) {
|
|
- token = strtok(list, ",");
|
|
- while (token != NULL) {
|
|
- PyList_Append(pylist, PyUnicode_FromString(token));
|
|
- token = strtok (NULL, ",");
|
|
- }
|
|
- }
|
|
- return pylist;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_command(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *name;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- int priority = HEXCHAT_PRI_NORM;
|
|
- char *help = NULL;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"name", "callback", "userdata",
|
|
- "priority", "help", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oiz:hook_command",
|
|
- kwlist, &name, &callback, &userdata,
|
|
- &priority, &help))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_command(ph, name, priority,
|
|
- Callback_Command, help, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_server(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *name;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- int priority = HEXCHAT_PRI_NORM;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
|
|
- kwlist, &name, &callback, &userdata,
|
|
- &priority))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
|
|
- Callback_Server, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_server_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *name;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- int priority = HEXCHAT_PRI_NORM;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_server",
|
|
- kwlist, &name, &callback, &userdata,
|
|
- &priority))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, NULL, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_server_attrs(ph, name, priority,
|
|
- Callback_Server, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *name;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- int priority = HEXCHAT_PRI_NORM;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print",
|
|
- kwlist, &name, &callback, &userdata,
|
|
- &priority))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, name, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_print(ph, name, priority,
|
|
- Callback_Print, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- char *name;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- int priority = HEXCHAT_PRI_NORM;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"name", "callback", "userdata", "priority", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|Oi:hook_print_attrs",
|
|
- kwlist, &name, &callback, &userdata,
|
|
- &priority))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT_ATTR, plugin, callback, userdata, name, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
|
|
- Callback_Print_Attrs, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_timer(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- int timeout;
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"timeout", "callback", "userdata", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO|O:hook_timer",
|
|
- kwlist, &timeout, &callback,
|
|
- &userdata))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_XCHAT, plugin, callback, userdata, NULL, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- BEGIN_XCHAT_CALLS(NONE);
|
|
- hook->data = (void*)hexchat_hook_timer(ph, timeout,
|
|
- Callback_Timer, hook);
|
|
- END_XCHAT_CALLS();
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_hook_unload(PyObject *self, PyObject *args, PyObject *kwargs)
|
|
-{
|
|
- PyObject *callback;
|
|
- PyObject *userdata = Py_None;
|
|
- PyObject *plugin;
|
|
- Hook *hook;
|
|
- char *kwlist[] = {"callback", "userdata", 0};
|
|
-
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:hook_unload",
|
|
- kwlist, &callback, &userdata))
|
|
- return NULL;
|
|
-
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
- if (!PyCallable_Check(callback)) {
|
|
- PyErr_SetString(PyExc_TypeError, "callback is not callable");
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- hook = Plugin_AddHook(HOOK_UNLOAD, plugin, callback, userdata, NULL, NULL);
|
|
- if (hook == NULL)
|
|
- return NULL;
|
|
-
|
|
- return PyLong_FromVoidPtr(hook);
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_unhook(PyObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *plugin;
|
|
- PyObject *obj;
|
|
- Hook *hook;
|
|
- if (!PyArg_ParseTuple(args, "O:unhook", &obj))
|
|
- return NULL;
|
|
- plugin = Plugin_GetCurrent();
|
|
- if (plugin == NULL)
|
|
- return NULL;
|
|
-
|
|
- if (PyUnicode_Check (obj))
|
|
- {
|
|
- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
|
|
- while (hook)
|
|
- {
|
|
- Plugin_RemoveHook(plugin, hook);
|
|
- hook = Plugin_FindHook(plugin, PyUnicode_AsUTF8 (obj));
|
|
- }
|
|
- }
|
|
- else
|
|
- {
|
|
- hook = (Hook *)PyLong_AsVoidPtr(obj);
|
|
- Plugin_RemoveHook(plugin, hook);
|
|
- }
|
|
-
|
|
- Py_RETURN_NONE;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_xchat_get_list(PyObject *self, PyObject *args)
|
|
-{
|
|
- hexchat_list *list;
|
|
- PyObject *l;
|
|
- const char *name;
|
|
- const char *const *fields;
|
|
- int i;
|
|
-
|
|
- if (!PyArg_ParseTuple(args, "s:get_list", &name))
|
|
- return NULL;
|
|
- /* This function is thread safe, and returns statically
|
|
- * allocated data. */
|
|
- fields = hexchat_list_fields(ph, "lists");
|
|
- for (i = 0; fields[i]; i++) {
|
|
- if (strcmp(fields[i], name) == 0) {
|
|
- /* Use the static allocated one. */
|
|
- name = fields[i];
|
|
- break;
|
|
- }
|
|
- }
|
|
- if (fields[i] == NULL) {
|
|
- PyErr_SetString(PyExc_KeyError, "list not available");
|
|
- return NULL;
|
|
- }
|
|
- l = PyList_New(0);
|
|
- if (l == NULL)
|
|
- return NULL;
|
|
- BEGIN_XCHAT_CALLS(RESTORE_CONTEXT);
|
|
- list = hexchat_list_get(ph, (char*)name);
|
|
- if (list == NULL)
|
|
- goto error;
|
|
- fields = hexchat_list_fields(ph, (char*)name);
|
|
- while (hexchat_list_next(ph, list)) {
|
|
- PyObject *o = ListItem_New(name);
|
|
- if (o == NULL || PyList_Append(l, o) == -1) {
|
|
- Py_XDECREF(o);
|
|
- goto error;
|
|
- }
|
|
- Py_DECREF(o); /* l is holding a reference */
|
|
- for (i = 0; fields[i]; i++) {
|
|
- const char *fld = fields[i]+1;
|
|
- PyObject *attr = NULL;
|
|
- const char *sattr;
|
|
- int iattr;
|
|
- time_t tattr;
|
|
- switch(fields[i][0]) {
|
|
- case 's':
|
|
- sattr = hexchat_list_str(ph, list, (char*)fld);
|
|
- attr = PyUnicode_FromString(sattr?sattr:"");
|
|
- break;
|
|
- case 'i':
|
|
- iattr = hexchat_list_int(ph, list, (char*)fld);
|
|
- attr = PyLong_FromLong((long)iattr);
|
|
- break;
|
|
- case 't':
|
|
- tattr = hexchat_list_time(ph, list, (char*)fld);
|
|
- attr = PyLong_FromLong((long)tattr);
|
|
- break;
|
|
- case 'p':
|
|
- sattr = hexchat_list_str(ph, list, (char*)fld);
|
|
- if (strcmp(fld, "context") == 0) {
|
|
- attr = Context_FromContext(
|
|
- (hexchat_context*)sattr);
|
|
- break;
|
|
- }
|
|
- default: /* ignore unknown (newly added?) types */
|
|
- continue;
|
|
- }
|
|
- if (attr == NULL)
|
|
- goto error;
|
|
- PyObject_SetAttrString(o, (char*)fld, attr); /* add reference on attr in o */
|
|
- Py_DECREF(attr); /* make o own attr */
|
|
- }
|
|
- }
|
|
- hexchat_list_free(ph, list);
|
|
- goto exit;
|
|
-error:
|
|
- if (list)
|
|
- hexchat_list_free(ph, list);
|
|
- Py_DECREF(l);
|
|
- l = NULL;
|
|
-
|
|
-exit:
|
|
- END_XCHAT_CALLS();
|
|
- return l;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_xchat_get_lists(PyObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *l, *o;
|
|
- const char *const *fields;
|
|
- int i;
|
|
- /* This function is thread safe, and returns statically
|
|
- * allocated data. */
|
|
- fields = hexchat_list_fields(ph, "lists");
|
|
- l = PyList_New(0);
|
|
- if (l == NULL)
|
|
- return NULL;
|
|
- for (i = 0; fields[i]; i++) {
|
|
- o = PyUnicode_FromString(fields[i]);
|
|
- if (o == NULL || PyList_Append(l, o) == -1) {
|
|
- Py_DECREF(l);
|
|
- Py_XDECREF(o);
|
|
- return NULL;
|
|
- }
|
|
- Py_DECREF(o); /* l is holding a reference */
|
|
- }
|
|
- return l;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_nickcmp(PyObject *self, PyObject *args)
|
|
-{
|
|
- char *s1, *s2;
|
|
- if (!PyArg_ParseTuple(args, "ss:nickcmp", &s1, &s2))
|
|
- return NULL;
|
|
- return PyLong_FromLong((long) hexchat_nickcmp(ph, s1, s2));
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-Module_hexchat_strip(PyObject *self, PyObject *args)
|
|
-{
|
|
- PyObject *result;
|
|
- char *str, *str2;
|
|
- int len = -1, flags = 1 | 2;
|
|
- if (!PyArg_ParseTuple(args, "s|ii:strip", &str, &len, &flags))
|
|
- return NULL;
|
|
- str2 = hexchat_strip(ph, str, len, flags);
|
|
- result = PyUnicode_FromString(str2);
|
|
- hexchat_free(ph, str2);
|
|
- return result;
|
|
-}
|
|
-
|
|
-static PyMethodDef Module_xchat_methods[] = {
|
|
- {"command", Module_hexchat_command,
|
|
- METH_VARARGS},
|
|
- {"prnt", Module_xchat_prnt,
|
|
- METH_VARARGS},
|
|
- {"emit_print", (PyCFunction)Module_hexchat_emit_print,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"get_info", Module_hexchat_get_info,
|
|
- METH_VARARGS},
|
|
- {"get_prefs", Module_xchat_get_prefs,
|
|
- METH_VARARGS},
|
|
- {"get_context", Module_hexchat_get_context,
|
|
- METH_NOARGS},
|
|
- {"find_context", (PyCFunction)Module_hexchat_find_context,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"set_pluginpref", Module_hexchat_pluginpref_set,
|
|
- METH_VARARGS},
|
|
- {"get_pluginpref", Module_hexchat_pluginpref_get,
|
|
- METH_VARARGS},
|
|
- {"del_pluginpref", Module_hexchat_pluginpref_delete,
|
|
- METH_VARARGS},
|
|
- {"list_pluginpref", Module_hexchat_pluginpref_list,
|
|
- METH_VARARGS},
|
|
- {"hook_command", (PyCFunction)Module_hexchat_hook_command,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_server", (PyCFunction)Module_hexchat_hook_server,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_server_attrs", (PyCFunction)Module_hexchat_hook_server_attrs,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_print", (PyCFunction)Module_hexchat_hook_print,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_print_attrs", (PyCFunction)Module_hexchat_hook_print_attrs,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_timer", (PyCFunction)Module_hexchat_hook_timer,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"hook_unload", (PyCFunction)Module_hexchat_hook_unload,
|
|
- METH_VARARGS|METH_KEYWORDS},
|
|
- {"unhook", Module_hexchat_unhook,
|
|
- METH_VARARGS},
|
|
- {"get_list", Module_xchat_get_list,
|
|
- METH_VARARGS},
|
|
- {"get_lists", Module_xchat_get_lists,
|
|
- METH_NOARGS},
|
|
- {"nickcmp", Module_hexchat_nickcmp,
|
|
- METH_VARARGS},
|
|
- {"strip", Module_hexchat_strip,
|
|
- METH_VARARGS},
|
|
- {NULL, NULL}
|
|
-};
|
|
-
|
|
-#ifdef IS_PY3K
|
|
-static struct PyModuleDef moduledef = {
|
|
- PyModuleDef_HEAD_INIT,
|
|
- "hexchat", /* m_name */
|
|
- "HexChat Scripting Interface", /* m_doc */
|
|
- -1, /* m_size */
|
|
- Module_xchat_methods, /* m_methods */
|
|
- NULL, /* m_reload */
|
|
- NULL, /* m_traverse */
|
|
- NULL, /* m_clear */
|
|
- NULL, /* m_free */
|
|
-};
|
|
-
|
|
-static struct PyModuleDef xchat_moduledef = {
|
|
- PyModuleDef_HEAD_INIT,
|
|
- "xchat", /* m_name */
|
|
- "HexChat Scripting Interface", /* m_doc */
|
|
- -1, /* m_size */
|
|
- Module_xchat_methods, /* m_methods */
|
|
- NULL, /* m_reload */
|
|
- NULL, /* m_traverse */
|
|
- NULL, /* m_clear */
|
|
- NULL, /* m_free */
|
|
-};
|
|
-#endif
|
|
-
|
|
-static PyObject *
|
|
-moduleinit_hexchat(void)
|
|
-{
|
|
- PyObject *hm;
|
|
-#ifdef IS_PY3K
|
|
- hm = PyModule_Create(&moduledef);
|
|
-#else
|
|
- hm = Py_InitModule3("hexchat", Module_xchat_methods, "HexChat Scripting Interface");
|
|
-#endif
|
|
-
|
|
- PyModule_AddIntConstant(hm, "EAT_NONE", HEXCHAT_EAT_NONE);
|
|
- PyModule_AddIntConstant(hm, "EAT_HEXCHAT", HEXCHAT_EAT_HEXCHAT);
|
|
- PyModule_AddIntConstant(hm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT); /* for compat */
|
|
- PyModule_AddIntConstant(hm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
|
|
- PyModule_AddIntConstant(hm, "EAT_ALL", HEXCHAT_EAT_ALL);
|
|
- PyModule_AddIntConstant(hm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
|
|
- PyModule_AddIntConstant(hm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
|
|
- PyModule_AddIntConstant(hm, "PRI_NORM", HEXCHAT_PRI_NORM);
|
|
- PyModule_AddIntConstant(hm, "PRI_LOW", HEXCHAT_PRI_LOW);
|
|
- PyModule_AddIntConstant(hm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
|
|
-
|
|
- PyObject_SetAttrString(hm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
|
|
-
|
|
- return hm;
|
|
-}
|
|
-
|
|
-static PyObject *
|
|
-moduleinit_xchat(void)
|
|
-{
|
|
- PyObject *xm;
|
|
-#ifdef IS_PY3K
|
|
- xm = PyModule_Create(&xchat_moduledef);
|
|
-#else
|
|
- xm = Py_InitModule3("xchat", Module_xchat_methods, "HexChat Scripting Interface");
|
|
-#endif
|
|
-
|
|
- PyModule_AddIntConstant(xm, "EAT_NONE", HEXCHAT_EAT_NONE);
|
|
- PyModule_AddIntConstant(xm, "EAT_XCHAT", HEXCHAT_EAT_HEXCHAT);
|
|
- PyModule_AddIntConstant(xm, "EAT_PLUGIN", HEXCHAT_EAT_PLUGIN);
|
|
- PyModule_AddIntConstant(xm, "EAT_ALL", HEXCHAT_EAT_ALL);
|
|
- PyModule_AddIntConstant(xm, "PRI_HIGHEST", HEXCHAT_PRI_HIGHEST);
|
|
- PyModule_AddIntConstant(xm, "PRI_HIGH", HEXCHAT_PRI_HIGH);
|
|
- PyModule_AddIntConstant(xm, "PRI_NORM", HEXCHAT_PRI_NORM);
|
|
- PyModule_AddIntConstant(xm, "PRI_LOW", HEXCHAT_PRI_LOW);
|
|
- PyModule_AddIntConstant(xm, "PRI_LOWEST", HEXCHAT_PRI_LOWEST);
|
|
-
|
|
- PyObject_SetAttrString(xm, "__version__", Py_BuildValue("(ii)", VERSION_MAJOR, VERSION_MINOR));
|
|
-
|
|
- return xm;
|
|
-}
|
|
-
|
|
-#ifdef IS_PY3K
|
|
-PyMODINIT_FUNC
|
|
-PyInit_hexchat(void)
|
|
-{
|
|
- return moduleinit_hexchat();
|
|
-}
|
|
-PyMODINIT_FUNC
|
|
-PyInit_xchat(void)
|
|
-{
|
|
- return moduleinit_xchat();
|
|
-}
|
|
-#else
|
|
-PyMODINIT_FUNC
|
|
-inithexchat(void)
|
|
-{
|
|
- moduleinit_hexchat();
|
|
-}
|
|
-PyMODINIT_FUNC
|
|
-initxchat(void)
|
|
-{
|
|
- moduleinit_xchat();
|
|
-}
|
|
-#endif
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Python interactive interpreter functions */
|
|
-
|
|
-static void
|
|
-IInterp_Exec(char *command)
|
|
-{
|
|
- PyObject *m, *d, *o;
|
|
- char *buffer;
|
|
- int len;
|
|
-
|
|
- BEGIN_PLUGIN(interp_plugin);
|
|
-
|
|
- m = PyImport_AddModule("__main__");
|
|
- if (m == NULL) {
|
|
- hexchat_print(ph, "Can't get __main__ module");
|
|
- goto fail;
|
|
- }
|
|
- d = PyModule_GetDict(m);
|
|
- len = strlen(command);
|
|
-
|
|
- buffer = g_malloc(len + 2);
|
|
- memcpy(buffer, command, len);
|
|
- buffer[len] = '\n';
|
|
- buffer[len+1] = 0;
|
|
- PyRun_SimpleString("import hexchat");
|
|
- o = PyRun_StringFlags(buffer, Py_single_input, d, d, NULL);
|
|
- g_free(buffer);
|
|
- if (o == NULL) {
|
|
- PyErr_Print();
|
|
- goto fail;
|
|
- }
|
|
- Py_DECREF(o);
|
|
-
|
|
-fail:
|
|
- END_PLUGIN(interp_plugin);
|
|
- return;
|
|
-}
|
|
-
|
|
-static int
|
|
-IInterp_Cmd(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- char *channel = (char *) hexchat_get_info(ph, "channel");
|
|
- if (channel && channel[0] == '>' && strcmp(channel, ">>python<<") == 0) {
|
|
- hexchat_printf(ph, ">>> %s\n", word_eol[1]);
|
|
- IInterp_Exec(word_eol[1]);
|
|
- return HEXCHAT_EAT_HEXCHAT;
|
|
- }
|
|
- return HEXCHAT_EAT_NONE;
|
|
-}
|
|
-
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Python command handling */
|
|
-
|
|
-static void
|
|
-Command_PyList(void)
|
|
-{
|
|
- GSList *list;
|
|
- list = plugin_list;
|
|
- if (list == NULL) {
|
|
- hexchat_print(ph, "No python modules loaded");
|
|
- } else {
|
|
- hexchat_print(ph,
|
|
- "Name Version Filename Description\n"
|
|
- "---- ------- -------- -----------\n");
|
|
- while (list != NULL) {
|
|
- PluginObject *plg = (PluginObject *) list->data;
|
|
- char *basename = g_path_get_basename(plg->filename);
|
|
- hexchat_printf(ph, "%-12s %-8s %-20s %-10s\n",
|
|
- plg->name,
|
|
- *plg->version ? plg->version
|
|
- : "<none>",
|
|
- basename,
|
|
- *plg->description ? plg->description
|
|
- : "<none>");
|
|
- g_free(basename);
|
|
- list = list->next;
|
|
- }
|
|
- hexchat_print(ph, "\n");
|
|
- }
|
|
-}
|
|
-
|
|
-static void
|
|
-Command_PyLoad(char *filename)
|
|
-{
|
|
- PyObject *plugin;
|
|
- RELEASE_XCHAT_LOCK();
|
|
- plugin = Plugin_New(filename, xchatout);
|
|
- ACQUIRE_XCHAT_LOCK();
|
|
- if (plugin)
|
|
- plugin_list = g_slist_append(plugin_list, plugin);
|
|
-}
|
|
-
|
|
-static void
|
|
-Command_PyUnload(char *name)
|
|
-{
|
|
- PluginObject *plugin = Plugin_ByString(name);
|
|
- if (!plugin) {
|
|
- hexchat_print(ph, "Can't find a python plugin with that name");
|
|
- } else {
|
|
- BEGIN_PLUGIN(plugin);
|
|
- Plugin_Delete((PyObject*)plugin);
|
|
- END_PLUGIN(plugin);
|
|
- plugin_list = g_slist_remove(plugin_list, plugin);
|
|
- }
|
|
-}
|
|
-
|
|
-static void
|
|
-Command_PyReload(char *name)
|
|
-{
|
|
- PluginObject *plugin = Plugin_ByString(name);
|
|
- if (!plugin) {
|
|
- hexchat_print(ph, "Can't find a python plugin with that name");
|
|
- } else {
|
|
- char *filename = g_strdup(plugin->filename);
|
|
- Command_PyUnload(filename);
|
|
- Command_PyLoad(filename);
|
|
- g_free(filename);
|
|
- }
|
|
-}
|
|
-
|
|
-static void
|
|
-Command_PyAbout(void)
|
|
-{
|
|
- hexchat_print(ph, about);
|
|
-}
|
|
-
|
|
-static int
|
|
-Command_Py(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- char *cmd = word[2];
|
|
- int ok = 0;
|
|
- if (strcasecmp(cmd, "LIST") == 0) {
|
|
- ok = 1;
|
|
- Command_PyList();
|
|
- } else if (strcasecmp(cmd, "EXEC") == 0) {
|
|
- if (word[3][0]) {
|
|
- ok = 1;
|
|
- IInterp_Exec(word_eol[3]);
|
|
- }
|
|
- } else if (strcasecmp(cmd, "LOAD") == 0) {
|
|
- if (word[3][0]) {
|
|
- ok = 1;
|
|
- Command_PyLoad(word[3]);
|
|
- }
|
|
- } else if (strcasecmp(cmd, "UNLOAD") == 0) {
|
|
- if (word[3][0]) {
|
|
- ok = 1;
|
|
- Command_PyUnload(word[3]);
|
|
- }
|
|
- } else if (strcasecmp(cmd, "RELOAD") == 0) {
|
|
- if (word[3][0]) {
|
|
- ok = 1;
|
|
- Command_PyReload(word[3]);
|
|
- }
|
|
- } else if (strcasecmp(cmd, "CONSOLE") == 0) {
|
|
- ok = 1;
|
|
- hexchat_command(ph, "QUERY >>python<<");
|
|
- } else if (strcasecmp(cmd, "ABOUT") == 0) {
|
|
- ok = 1;
|
|
- Command_PyAbout();
|
|
- }
|
|
- if (!ok)
|
|
- hexchat_print(ph, usage);
|
|
- return HEXCHAT_EAT_ALL;
|
|
-}
|
|
-
|
|
-static int
|
|
-Command_Load(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- int len = strlen(word[2]);
|
|
- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
|
|
- Command_PyLoad(word[2]);
|
|
- return HEXCHAT_EAT_HEXCHAT;
|
|
- }
|
|
- return HEXCHAT_EAT_NONE;
|
|
-}
|
|
-
|
|
-static int
|
|
-Command_Reload(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- int len = strlen(word[2]);
|
|
- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
|
|
- Command_PyReload(word[2]);
|
|
- return HEXCHAT_EAT_HEXCHAT;
|
|
- }
|
|
- return HEXCHAT_EAT_NONE;
|
|
-}
|
|
-
|
|
-static int
|
|
-Command_Unload(char *word[], char *word_eol[], void *userdata)
|
|
-{
|
|
- int len = strlen(word[2]);
|
|
- if (len > 3 && strcasecmp(".py", word[2]+len-3) == 0) {
|
|
- Command_PyUnload(word[2]);
|
|
- return HEXCHAT_EAT_HEXCHAT;
|
|
- }
|
|
- return HEXCHAT_EAT_NONE;
|
|
-}
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* Autoload function */
|
|
-
|
|
-/* ===================================================================== */
|
|
-/* (De)initialization functions */
|
|
-
|
|
-static int initialized = 0;
|
|
-static int reinit_tried = 0;
|
|
-
|
|
-void
|
|
-hexchat_plugin_get_info(char **name, char **desc, char **version, void **reserved)
|
|
-{
|
|
- *name = "Python";
|
|
- *version = VERSION;
|
|
- *desc = "Python scripting interface";
|
|
- if (reserved)
|
|
- *reserved = NULL;
|
|
-}
|
|
-
|
|
-int
|
|
-hexchat_plugin_init(hexchat_plugin *plugin_handle,
|
|
- char **plugin_name,
|
|
- char **plugin_desc,
|
|
- char **plugin_version,
|
|
- char *arg)
|
|
-{
|
|
-#ifdef IS_PY3K
|
|
- wchar_t *argv[] = { L"<hexchat>", 0 };
|
|
-#else
|
|
- char *argv[] = { "<hexchat>", 0 };
|
|
-#endif
|
|
-
|
|
- ph = plugin_handle;
|
|
-
|
|
- /* Block double initalization. */
|
|
- if (initialized != 0) {
|
|
- hexchat_print(ph, "Python interface already loaded");
|
|
- /* deinit is called even when init fails, so keep track
|
|
- * of a reinit failure. */
|
|
- reinit_tried++;
|
|
- return 0;
|
|
- }
|
|
- initialized = 1;
|
|
-
|
|
- *plugin_name = "Python";
|
|
- *plugin_version = VERSION;
|
|
-
|
|
- /* FIXME You can't free this since it's used as long as the plugin's
|
|
- * loaded, but if you unload it, everything belonging to the plugin is
|
|
- * supposed to be freed anyway.
|
|
- */
|
|
- *plugin_desc = g_strdup_printf ("Python %d scripting interface", PY_MAJOR_VERSION);
|
|
-
|
|
- /* Initialize python. */
|
|
-#ifdef IS_PY3K
|
|
- Py_SetProgramName(L"hexchat");
|
|
- PyImport_AppendInittab("hexchat", PyInit_hexchat);
|
|
- PyImport_AppendInittab("xchat", PyInit_xchat);
|
|
-#else
|
|
- Py_SetProgramName("hexchat");
|
|
- PyImport_AppendInittab("hexchat", inithexchat);
|
|
- PyImport_AppendInittab("xchat", initxchat);
|
|
-#endif
|
|
- Py_Initialize();
|
|
- PySys_SetArgv(1, argv);
|
|
-
|
|
- xchatout_buffer = g_string_new (NULL);
|
|
- xchatout = XChatOut_New();
|
|
- if (xchatout == NULL) {
|
|
- hexchat_print(ph, "Can't allocate xchatout object");
|
|
- return 0;
|
|
- }
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
- PyEval_InitThreads();
|
|
- xchat_lock = PyThread_allocate_lock();
|
|
- if (xchat_lock == NULL) {
|
|
- hexchat_print(ph, "Can't allocate hexchat lock");
|
|
- Py_DECREF(xchatout);
|
|
- xchatout = NULL;
|
|
- return 0;
|
|
- }
|
|
-#endif
|
|
-
|
|
- main_tstate = PyEval_SaveThread();
|
|
-
|
|
- interp_plugin = Plugin_New(NULL, xchatout);
|
|
- if (interp_plugin == NULL) {
|
|
- hexchat_print(ph, "Plugin_New() failed.\n");
|
|
-#ifdef WITH_THREAD
|
|
- PyThread_free_lock(xchat_lock);
|
|
-#endif
|
|
- Py_DECREF(xchatout);
|
|
- xchatout = NULL;
|
|
- return 0;
|
|
- }
|
|
-
|
|
-
|
|
- hexchat_hook_command(ph, "", HEXCHAT_PRI_NORM, IInterp_Cmd, 0, 0);
|
|
- hexchat_hook_command(ph, "PY", HEXCHAT_PRI_NORM, Command_Py, usage, 0);
|
|
- hexchat_hook_command(ph, "LOAD", HEXCHAT_PRI_NORM, Command_Load, 0, 0);
|
|
- hexchat_hook_command(ph, "UNLOAD", HEXCHAT_PRI_NORM, Command_Unload, 0, 0);
|
|
- hexchat_hook_command(ph, "RELOAD", HEXCHAT_PRI_NORM, Command_Reload, 0, 0);
|
|
-#ifdef WITH_THREAD
|
|
- thread_timer = hexchat_hook_timer(ph, 300, Callback_ThreadTimer, NULL);
|
|
-#endif
|
|
-
|
|
- hexchat_print(ph, "Python interface loaded\n");
|
|
-
|
|
- Util_Autoload();
|
|
- return 1;
|
|
-}
|
|
-
|
|
-int
|
|
-hexchat_plugin_deinit(void)
|
|
-{
|
|
- GSList *list;
|
|
-
|
|
- /* A reinitialization was tried. Just give up and live the
|
|
- * environment as is. We are still alive. */
|
|
- if (reinit_tried) {
|
|
- reinit_tried--;
|
|
- return 1;
|
|
- }
|
|
-
|
|
- list = plugin_list;
|
|
- while (list != NULL) {
|
|
- PyObject *plugin = (PyObject *) list->data;
|
|
- BEGIN_PLUGIN(plugin);
|
|
- Plugin_Delete(plugin);
|
|
- END_PLUGIN(plugin);
|
|
- list = list->next;
|
|
- }
|
|
- g_slist_free(plugin_list);
|
|
- plugin_list = NULL;
|
|
-
|
|
- /* Reset xchatout buffer. */
|
|
- g_string_free (xchatout_buffer, TRUE);
|
|
- xchatout_buffer = NULL;
|
|
-
|
|
- if (interp_plugin) {
|
|
- PyThreadState *tstate = ((PluginObject*)interp_plugin)->tstate;
|
|
- PyThreadState_Swap(tstate);
|
|
- Py_EndInterpreter(tstate);
|
|
- Py_DECREF(interp_plugin);
|
|
- interp_plugin = NULL;
|
|
- }
|
|
-
|
|
- /* Switch back to the main thread state. */
|
|
- if (main_tstate) {
|
|
- PyEval_RestoreThread(main_tstate);
|
|
- PyThreadState_Swap(main_tstate);
|
|
- main_tstate = NULL;
|
|
- }
|
|
- Py_Finalize();
|
|
-
|
|
-#ifdef WITH_THREAD
|
|
- if (thread_timer != NULL) {
|
|
- hexchat_unhook(ph, thread_timer);
|
|
- thread_timer = NULL;
|
|
- }
|
|
- PyThread_free_lock(xchat_lock);
|
|
-#endif
|
|
-
|
|
- hexchat_print(ph, "Python interface unloaded\n");
|
|
- initialized = 0;
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python.def hexchat/plugins/python/python.def
|
|
--- hexchat-2.14.3/plugins/python/python.def 2019-12-20 22:43:47.652401700 -0800
|
|
+++ hexchat/plugins/python/python.def 2020-11-06 15:03:00.402820502 -0800
|
|
@@ -1,4 +1,3 @@
|
|
EXPORTS
|
|
hexchat_plugin_init
|
|
hexchat_plugin_deinit
|
|
-hexchat_plugin_get_info
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python.py hexchat/plugins/python/python.py
|
|
--- hexchat-2.14.3/plugins/python/python.py 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/python.py 2020-11-06 15:03:00.402820502 -0800
|
|
@@ -0,0 +1,554 @@
|
|
+from __future__ import print_function
|
|
+
|
|
+import importlib
|
|
+import os
|
|
+import pydoc
|
|
+import signal
|
|
+import sys
|
|
+import traceback
|
|
+import weakref
|
|
+from contextlib import contextmanager
|
|
+
|
|
+from _hexchat_embedded import ffi, lib
|
|
+
|
|
+if sys.version_info < (3, 0):
|
|
+ from io import BytesIO as HelpEater
|
|
+else:
|
|
+ from io import StringIO as HelpEater
|
|
+
|
|
+if not hasattr(sys, 'argv'):
|
|
+ sys.argv = ['<hexchat>']
|
|
+
|
|
+VERSION = b'2.0' # Sync with hexchat.__version__
|
|
+PLUGIN_NAME = ffi.new('char[]', b'Python')
|
|
+PLUGIN_DESC = ffi.new('char[]', b'Python %d.%d scripting interface' % (sys.version_info[0], sys.version_info[1]))
|
|
+PLUGIN_VERSION = ffi.new('char[]', VERSION)
|
|
+
|
|
+# TODO: Constants should be screaming snake case
|
|
+hexchat = None
|
|
+local_interp = None
|
|
+hexchat_stdout = None
|
|
+plugins = set()
|
|
+
|
|
+
|
|
+@contextmanager
|
|
+def redirected_stdout():
|
|
+ sys.stdout = sys.__stdout__
|
|
+ sys.stderr = sys.__stderr__
|
|
+ yield
|
|
+ sys.stdout = hexchat_stdout
|
|
+ sys.stderr = hexchat_stdout
|
|
+
|
|
+
|
|
+if os.getenv('HEXCHAT_LOG_PYTHON'):
|
|
+ def log(*args):
|
|
+ with redirected_stdout():
|
|
+ print(*args)
|
|
+
|
|
+else:
|
|
+ def log(*args):
|
|
+ pass
|
|
+
|
|
+
|
|
+class Stdout:
|
|
+ def __init__(self):
|
|
+ self.buffer = bytearray()
|
|
+
|
|
+ def write(self, string):
|
|
+ string = string.encode()
|
|
+ idx = string.rfind(b'\n')
|
|
+ if idx != -1:
|
|
+ self.buffer += string[:idx]
|
|
+ lib.hexchat_print(lib.ph, bytes(self.buffer))
|
|
+ self.buffer = bytearray(string[idx + 1:])
|
|
+ else:
|
|
+ self.buffer += string
|
|
+
|
|
+ def isatty(self):
|
|
+ return False
|
|
+
|
|
+
|
|
+class Attribute:
|
|
+ def __init__(self):
|
|
+ self.time = 0
|
|
+
|
|
+ def __repr__(self):
|
|
+ return '<Attribute object at {}>'.format(id(self))
|
|
+
|
|
+
|
|
+class Hook:
|
|
+ def __init__(self, plugin, callback, userdata, is_unload):
|
|
+ self.is_unload = is_unload
|
|
+ self.plugin = weakref.proxy(plugin)
|
|
+ self.callback = callback
|
|
+ self.userdata = userdata
|
|
+ self.hexchat_hook = None
|
|
+ self.handle = ffi.new_handle(weakref.proxy(self))
|
|
+
|
|
+ def __del__(self):
|
|
+ log('Removing hook', id(self))
|
|
+ if self.is_unload is False:
|
|
+ assert self.hexchat_hook is not None
|
|
+ lib.hexchat_unhook(lib.ph, self.hexchat_hook)
|
|
+
|
|
+
|
|
+if sys.version_info[0] == 2:
|
|
+ def compile_file(data, filename):
|
|
+ return compile(data, filename, 'exec', dont_inherit=True)
|
|
+
|
|
+
|
|
+ def compile_line(string):
|
|
+ try:
|
|
+ return compile(string, '<string>', 'eval', dont_inherit=True)
|
|
+
|
|
+ except SyntaxError:
|
|
+ # For some reason `print` is invalid for eval
|
|
+ # This will hide any return value though
|
|
+ return compile(string, '<string>', 'exec', dont_inherit=True)
|
|
+else:
|
|
+ def compile_file(data, filename):
|
|
+ return compile(data, filename, 'exec', optimize=2, dont_inherit=True)
|
|
+
|
|
+
|
|
+ def compile_line(string):
|
|
+ # newline appended to solve unexpected EOF issues
|
|
+ return compile(string + '\n', '<string>', 'single', optimize=2, dont_inherit=True)
|
|
+
|
|
+
|
|
+class Plugin:
|
|
+ def __init__(self):
|
|
+ self.ph = None
|
|
+ self.name = ''
|
|
+ self.filename = ''
|
|
+ self.version = ''
|
|
+ self.description = ''
|
|
+ self.hooks = set()
|
|
+ self.globals = {
|
|
+ '__plugin': weakref.proxy(self),
|
|
+ '__name__': '__main__',
|
|
+ }
|
|
+
|
|
+ def add_hook(self, callback, userdata, is_unload=False):
|
|
+ hook = Hook(self, callback, userdata, is_unload=is_unload)
|
|
+ self.hooks.add(hook)
|
|
+ return hook
|
|
+
|
|
+ def remove_hook(self, hook):
|
|
+ for h in self.hooks:
|
|
+ if id(h) == hook:
|
|
+ ud = h.userdata
|
|
+ self.hooks.remove(h)
|
|
+ return ud
|
|
+
|
|
+ log('Hook not found')
|
|
+ return None
|
|
+
|
|
+ def loadfile(self, filename):
|
|
+ try:
|
|
+ self.filename = filename
|
|
+ with open(filename) as f:
|
|
+ data = f.read()
|
|
+ compiled = compile_file(data, filename)
|
|
+ exec(compiled, self.globals)
|
|
+
|
|
+ try:
|
|
+ self.name = self.globals['__module_name__']
|
|
+
|
|
+ except KeyError:
|
|
+ lib.hexchat_print(lib.ph, b'Failed to load module: __module_name__ must be set')
|
|
+
|
|
+ return False
|
|
+
|
|
+ self.version = self.globals.get('__module_version__', '')
|
|
+ self.description = self.globals.get('__module_description__', '')
|
|
+ self.ph = lib.hexchat_plugingui_add(lib.ph, filename.encode(), self.name.encode(),
|
|
+ self.description.encode(), self.version.encode(), ffi.NULL)
|
|
+
|
|
+ except Exception as e:
|
|
+ lib.hexchat_print(lib.ph, 'Failed to load module: {}'.format(e).encode())
|
|
+ traceback.print_exc()
|
|
+ return False
|
|
+
|
|
+ return True
|
|
+
|
|
+ def __del__(self):
|
|
+ log('unloading', self.filename)
|
|
+ for hook in self.hooks:
|
|
+ if hook.is_unload is True:
|
|
+ try:
|
|
+ hook.callback(hook.userdata)
|
|
+
|
|
+ except Exception as e:
|
|
+ log('Failed to run hook:', e)
|
|
+ traceback.print_exc()
|
|
+
|
|
+ del self.hooks
|
|
+ if self.ph is not None:
|
|
+ lib.hexchat_plugingui_remove(lib.ph, self.ph)
|
|
+
|
|
+
|
|
+if sys.version_info[0] == 2:
|
|
+ def __decode(string):
|
|
+ return string
|
|
+
|
|
+else:
|
|
+ def __decode(string):
|
|
+ return string.decode()
|
|
+
|
|
+
|
|
+# There can be empty entries between non-empty ones so find the actual last value
|
|
+def wordlist_len(words):
|
|
+ for i in range(31, 1, -1):
|
|
+ if ffi.string(words[i]):
|
|
+ return i
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+def create_wordlist(words):
|
|
+ size = wordlist_len(words)
|
|
+ return [__decode(ffi.string(words[i])) for i in range(1, size + 1)]
|
|
+
|
|
+
|
|
+# This function only exists for compat reasons with the C plugin
|
|
+# It turns the word list from print hooks into a word_eol list
|
|
+# This makes no sense to do...
|
|
+def create_wordeollist(words):
|
|
+ words = reversed(words)
|
|
+ accum = None
|
|
+ ret = []
|
|
+ for word in words:
|
|
+ if accum is None:
|
|
+ accum = word
|
|
+
|
|
+ elif word:
|
|
+ last = accum
|
|
+ accum = ' '.join((word, last))
|
|
+
|
|
+ ret.insert(0, accum)
|
|
+
|
|
+ return ret
|
|
+
|
|
+
|
|
+def to_cb_ret(value):
|
|
+ if value is None:
|
|
+ return 0
|
|
+
|
|
+ return int(value)
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_command_hook(word, word_eol, userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ word = create_wordlist(word)
|
|
+ word_eol = create_wordlist(word_eol)
|
|
+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_print_hook(word, userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ word = create_wordlist(word)
|
|
+ word_eol = create_wordeollist(word)
|
|
+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_print_attrs_hook(word, attrs, userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ word = create_wordlist(word)
|
|
+ word_eol = create_wordeollist(word)
|
|
+ attr = Attribute()
|
|
+ attr.time = attrs.server_time_utc
|
|
+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_server_hook(word, word_eol, userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ word = create_wordlist(word)
|
|
+ word_eol = create_wordlist(word_eol)
|
|
+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata))
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_server_attrs_hook(word, word_eol, attrs, userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ word = create_wordlist(word)
|
|
+ word_eol = create_wordlist(word_eol)
|
|
+ attr = Attribute()
|
|
+ attr.time = attrs.server_time_utc
|
|
+ return to_cb_ret(hook.callback(word, word_eol, hook.userdata, attr))
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_timer_hook(userdata):
|
|
+ hook = ffi.from_handle(userdata)
|
|
+ if hook.callback(hook.userdata) is True:
|
|
+ return 1
|
|
+
|
|
+ hook.is_unload = True # Don't unhook
|
|
+ for h in hook.plugin.hooks:
|
|
+ if h == hook:
|
|
+ hook.plugin.hooks.remove(h)
|
|
+ break
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+@ffi.def_extern(error=3)
|
|
+def _on_say_command(word, word_eol, userdata):
|
|
+ channel = ffi.string(lib.hexchat_get_info(lib.ph, b'channel'))
|
|
+ if channel == b'>>python<<':
|
|
+ python = ffi.string(word_eol[1])
|
|
+ lib.hexchat_print(lib.ph, b'>>> ' + python)
|
|
+ exec_in_interp(__decode(python))
|
|
+ return 1
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+def load_filename(filename):
|
|
+ filename = os.path.expanduser(filename)
|
|
+ if not os.path.isabs(filename):
|
|
+ configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
|
|
+
|
|
+ filename = os.path.join(configdir, 'addons', filename)
|
|
+
|
|
+ if filename and not any(plugin.filename == filename for plugin in plugins):
|
|
+ plugin = Plugin()
|
|
+ if plugin.loadfile(filename):
|
|
+ plugins.add(plugin)
|
|
+ return True
|
|
+
|
|
+ return False
|
|
+
|
|
+
|
|
+def unload_name(name):
|
|
+ if name:
|
|
+ for plugin in plugins:
|
|
+ if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
|
|
+ plugins.remove(plugin)
|
|
+ return True
|
|
+
|
|
+ return False
|
|
+
|
|
+
|
|
+def reload_name(name):
|
|
+ if name:
|
|
+ for plugin in plugins:
|
|
+ if name in (plugin.name, plugin.filename, os.path.basename(plugin.filename)):
|
|
+ filename = plugin.filename
|
|
+ plugins.remove(plugin)
|
|
+ return load_filename(filename)
|
|
+
|
|
+ return False
|
|
+
|
|
+
|
|
+@contextmanager
|
|
+def change_cwd(path):
|
|
+ old_cwd = os.getcwd()
|
|
+ os.chdir(path)
|
|
+ yield
|
|
+ os.chdir(old_cwd)
|
|
+
|
|
+
|
|
+def autoload():
|
|
+ configdir = __decode(ffi.string(lib.hexchat_get_info(lib.ph, b'configdir')))
|
|
+ addondir = os.path.join(configdir, 'addons')
|
|
+ try:
|
|
+ with change_cwd(addondir): # Maintaining old behavior
|
|
+ for f in os.listdir(addondir):
|
|
+ if f.endswith('.py'):
|
|
+ log('Autoloading', f)
|
|
+ # TODO: Set cwd
|
|
+ load_filename(os.path.join(addondir, f))
|
|
+
|
|
+ except FileNotFoundError as e:
|
|
+ log('Autoload failed', e)
|
|
+
|
|
+
|
|
+def list_plugins():
|
|
+ if not plugins:
|
|
+ lib.hexchat_print(lib.ph, b'No python modules loaded')
|
|
+ return
|
|
+
|
|
+ tbl_headers = [b'Name', b'Version', b'Filename', b'Description']
|
|
+ tbl = [
|
|
+ tbl_headers,
|
|
+ [(b'-' * len(s)) for s in tbl_headers]
|
|
+ ]
|
|
+
|
|
+ for plugin in plugins:
|
|
+ basename = os.path.basename(plugin.filename).encode()
|
|
+ name = plugin.name.encode()
|
|
+ version = plugin.version.encode() if plugin.version else b'<none>'
|
|
+ description = plugin.description.encode() if plugin.description else b'<none>'
|
|
+ tbl.append((name, version, basename, description))
|
|
+
|
|
+ column_sizes = [
|
|
+ max(len(item) for item in column)
|
|
+ for column in zip(*tbl)
|
|
+ ]
|
|
+
|
|
+ for row in tbl:
|
|
+ lib.hexchat_print(lib.ph, b' '.join(item.ljust(column_sizes[i])
|
|
+ for i, item in enumerate(row)))
|
|
+ lib.hexchat_print(lib.ph, b'')
|
|
+
|
|
+
|
|
+def exec_in_interp(python):
|
|
+ global local_interp
|
|
+
|
|
+ if not python:
|
|
+ return
|
|
+
|
|
+ if local_interp is None:
|
|
+ local_interp = Plugin()
|
|
+ local_interp.locals = {}
|
|
+ local_interp.globals['hexchat'] = hexchat
|
|
+
|
|
+ code = compile_line(python)
|
|
+ try:
|
|
+ ret = eval(code, local_interp.globals, local_interp.locals)
|
|
+ if ret is not None:
|
|
+ lib.hexchat_print(lib.ph, '{}'.format(ret).encode())
|
|
+
|
|
+ except Exception as e:
|
|
+ traceback.print_exc(file=hexchat_stdout)
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_load_command(word, word_eol, userdata):
|
|
+ filename = ffi.string(word[2])
|
|
+ if filename.endswith(b'.py'):
|
|
+ load_filename(__decode(filename))
|
|
+ return 3
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_unload_command(word, word_eol, userdata):
|
|
+ filename = ffi.string(word[2])
|
|
+ if filename.endswith(b'.py'):
|
|
+ unload_name(__decode(filename))
|
|
+ return 3
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_reload_command(word, word_eol, userdata):
|
|
+ filename = ffi.string(word[2])
|
|
+ if filename.endswith(b'.py'):
|
|
+ reload_name(__decode(filename))
|
|
+ return 3
|
|
+
|
|
+ return 0
|
|
+
|
|
+
|
|
+@ffi.def_extern(error=3)
|
|
+def _on_py_command(word, word_eol, userdata):
|
|
+ subcmd = __decode(ffi.string(word[2])).lower()
|
|
+
|
|
+ if subcmd == 'exec':
|
|
+ python = __decode(ffi.string(word_eol[3]))
|
|
+ exec_in_interp(python)
|
|
+
|
|
+ elif subcmd == 'load':
|
|
+ filename = __decode(ffi.string(word[3]))
|
|
+ load_filename(filename)
|
|
+
|
|
+ elif subcmd == 'unload':
|
|
+ name = __decode(ffi.string(word[3]))
|
|
+ if not unload_name(name):
|
|
+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
|
|
+
|
|
+ elif subcmd == 'reload':
|
|
+ name = __decode(ffi.string(word[3]))
|
|
+ if not reload_name(name):
|
|
+ lib.hexchat_print(lib.ph, b'Can\'t find a python plugin with that name')
|
|
+
|
|
+ elif subcmd == 'console':
|
|
+ lib.hexchat_command(lib.ph, b'QUERY >>python<<')
|
|
+
|
|
+ elif subcmd == 'list':
|
|
+ list_plugins()
|
|
+
|
|
+ elif subcmd == 'about':
|
|
+ lib.hexchat_print(lib.ph, b'HexChat Python interface version ' + VERSION)
|
|
+
|
|
+ else:
|
|
+ lib.hexchat_command(lib.ph, b'HELP PY')
|
|
+
|
|
+ return 3
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_plugin_init(plugin_name, plugin_desc, plugin_version, arg, libdir):
|
|
+ global hexchat
|
|
+ global hexchat_stdout
|
|
+
|
|
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
+
|
|
+ plugin_name[0] = PLUGIN_NAME
|
|
+ plugin_desc[0] = PLUGIN_DESC
|
|
+ plugin_version[0] = PLUGIN_VERSION
|
|
+
|
|
+ try:
|
|
+ libdir = __decode(ffi.string(libdir))
|
|
+ modpath = os.path.join(libdir, '..', 'python')
|
|
+ sys.path.append(os.path.abspath(modpath))
|
|
+ hexchat = importlib.import_module('hexchat')
|
|
+
|
|
+ except (UnicodeDecodeError, ImportError) as e:
|
|
+ lib.hexchat_print(lib.ph, b'Failed to import module: ' + repr(e).encode())
|
|
+
|
|
+ return 0
|
|
+
|
|
+ hexchat_stdout = Stdout()
|
|
+ sys.stdout = hexchat_stdout
|
|
+ sys.stderr = hexchat_stdout
|
|
+ pydoc.help = pydoc.Helper(HelpEater(), HelpEater())
|
|
+
|
|
+ lib.hexchat_hook_command(lib.ph, b'', 0, lib._on_say_command, ffi.NULL, ffi.NULL)
|
|
+ lib.hexchat_hook_command(lib.ph, b'LOAD', 0, lib._on_load_command, ffi.NULL, ffi.NULL)
|
|
+ lib.hexchat_hook_command(lib.ph, b'UNLOAD', 0, lib._on_unload_command, ffi.NULL, ffi.NULL)
|
|
+ lib.hexchat_hook_command(lib.ph, b'RELOAD', 0, lib._on_reload_command, ffi.NULL, ffi.NULL)
|
|
+ lib.hexchat_hook_command(lib.ph, b'PY', 0, lib._on_py_command, b'''Usage: /PY LOAD <filename>
|
|
+ UNLOAD <filename|name>
|
|
+ RELOAD <filename|name>
|
|
+ LIST
|
|
+ EXEC <command>
|
|
+ CONSOLE
|
|
+ ABOUT''', ffi.NULL)
|
|
+
|
|
+ lib.hexchat_print(lib.ph, b'Python interface loaded')
|
|
+ autoload()
|
|
+ return 1
|
|
+
|
|
+
|
|
+@ffi.def_extern()
|
|
+def _on_plugin_deinit():
|
|
+ global local_interp
|
|
+ global hexchat
|
|
+ global hexchat_stdout
|
|
+ global plugins
|
|
+
|
|
+ plugins = set()
|
|
+ local_interp = None
|
|
+ hexchat = None
|
|
+ hexchat_stdout = None
|
|
+ sys.stdout = sys.__stdout__
|
|
+ sys.stderr = sys.__stderr__
|
|
+ pydoc.help = pydoc.Helper()
|
|
+
|
|
+ for mod in ('_hexchat', 'hexchat', 'xchat', '_hexchat_embedded'):
|
|
+ try:
|
|
+ del sys.modules[mod]
|
|
+
|
|
+ except KeyError:
|
|
+ pass
|
|
+
|
|
+ return 1
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/python_style_guide.md hexchat/plugins/python/python_style_guide.md
|
|
--- hexchat-2.14.3/plugins/python/python_style_guide.md 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/python_style_guide.md 2020-11-06 15:03:00.403820502 -0800
|
|
@@ -0,0 +1,26 @@
|
|
+# HexChat Python Module Style Guide
|
|
+
|
|
+(This is a work in progress).
|
|
+
|
|
+## General rules
|
|
+
|
|
+- PEP8 as general fallback recommendations
|
|
+- Max line length: 120
|
|
+- Avoid overcomplex compound statements. i.e. dont do this: `somevar = x if x == y else z if a == b and c == b else x`
|
|
+
|
|
+## Indentation style
|
|
+
|
|
+### Multi-line functions
|
|
+
|
|
+```python
|
|
+foo(really_long_arg_1,
|
|
+ really_long_arg_2)
|
|
+```
|
|
+
|
|
+### Mutli-line lists/dicts
|
|
+
|
|
+```python
|
|
+foo = {
|
|
+ 'bar': 'baz',
|
|
+}
|
|
+```
|
|
diff --color -Nur hexchat-2.14.3/plugins/python/xchat.py hexchat/plugins/python/xchat.py
|
|
--- hexchat-2.14.3/plugins/python/xchat.py 1969-12-31 16:00:00.000000000 -0800
|
|
+++ hexchat/plugins/python/xchat.py 2020-11-06 15:03:00.403820502 -0800
|
|
@@ -0,0 +1 @@
|
|
+from _hexchat import *
|