summary refs log tree commit diff stats
path: root/plugins/python
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/python')
-rw-r--r--plugins/python/python.c145
1 files changed, 121 insertions, 24 deletions
diff --git a/plugins/python/python.c b/plugins/python/python.c
index 90dbb22d..7bedcad9 100644
--- a/plugins/python/python.c
+++ b/plugins/python/python.c
@@ -72,15 +72,32 @@
 #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
 
-#if PY_MAJOR_VERSION == 2
+/* Version string macro */
 #ifdef WIN32
-#undef WITH_THREAD
-#define VERSION "1.0/2.7"	/* Linked to python27.dll */
+#if PY_MAJOR_VERSION == 2
+#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/2.7"	/* Linked to python27.dll */
+#elif PY_MAJOR_VERSION == 3
+#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "/3.3"	/* Linked to python33.dll */
+#endif
 #endif
 
+#ifndef VERSION
+#define VERSION STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR)
+#endif
+
+/* #define's for Python 2 */
+#if PY_MAJOR_VERSION == 2
 #undef PyLong_Check
 #define PyLong_Check PyInt_Check
 #define PyLong_AsLong PyInt_AsLong
@@ -95,15 +112,14 @@
 #define PyUnicode_FromString PyString_FromString
 #define PyUnicode_AsUTF8 PyString_AsString
 
-#else
-#define IS_PY3K
 #ifdef WIN32
-#define VERSION "1.0/3.3"	/* Linked to python33.dll */
+#undef WITH_THREAD
 #endif
 #endif
 
-#ifndef VERSION
-#define VERSION "1.0"
+/* #define for Python 3 */
+#if PY_MAJOR_VERSION == 3
+#define IS_PY3K
 #endif
 
 #define NONE 0
@@ -269,7 +285,8 @@ 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(char *word[], hexchat_event_attrs *attrs, 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);
 
@@ -365,9 +382,6 @@ Usage: /PY LOAD   <filename>\n\
            ABOUT\n\
 \n";
 
-/* Remove if/when HexChat supports this command for plugins */
-static const char reload[] = "Usage: RELOAD <name>, reloads a python script";
-
 static const char about[] = "HexChat Python " PY_VERSION " Interface Version " VERSION "\n";
 
 /* ===================================================================== */
@@ -593,10 +607,8 @@ Callback_Command(char *word[], char *word_eol[], void *userdata)
 	return ret;
 }
 
-/* No Callback_Server() here. We use Callback_Command() as well. */
-
 static int
-Callback_Print(char *word[], hexchat_event_attrs *attrs, void *userdata)
+Callback_Print_Attrs(char *word[], hexchat_event_attrs *attrs, void *userdata)
 {
 	Hook *hook = (Hook *) userdata;
 	PyObject *retobj;
@@ -662,12 +674,8 @@ Callback_Print(char *word[], hexchat_event_attrs *attrs, void *userdata)
 
 	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);
+	retobj = PyObject_CallFunction(hook->callback, "(OOOO)", word_list,
+					    word_eol_list, hook->userdata, attributes);
 
 	Py_DECREF(word_list);
 	Py_DECREF(word_eol_list);
@@ -691,6 +699,94 @@ Callback_Print(char *word[], hexchat_event_attrs *attrs, void *userdata)
 }
 
 static int
+Callback_Print(char *word[], void *userdata)
+{
+	Hook *hook = (Hook *) userdata;
+	PyObject *retobj;
+	PyObject *word_list;
+	PyObject *word_eol_list;
+	char **word_eol;
+	char *word_eol_raw;
+	int listsize = 0;
+	int next = 0;
+	int i;
+	int ret = 0;
+	PyObject *plugin;
+
+	/* Cut off the message identifier. */
+	word += 1;
+
+	/* HexChat doesn't provide a word_eol for print events, so we
+	 * build our own here. */
+	while (word[listsize] && word[listsize][0])
+		listsize++;
+	word_eol = (char **) g_malloc(sizeof(char*)*(listsize+1));
+	if (word_eol == NULL) {
+		hexchat_print(ph, "Not enough memory to alloc word_eol "
+				"for python plugin callback.");
+		return 0;
+	}
+	/* First build a word clone, but NULL terminated. */
+	memcpy(word_eol, word, listsize*sizeof(char*));
+	word_eol[listsize] = NULL;
+	/* Then join it. */
+	word_eol_raw = g_strjoinv(" ", word_eol);
+	if (word_eol_raw == NULL) {
+		hexchat_print(ph, "Not enough memory to alloc word_eol_raw "
+				"for python plugin callback.");
+		return 0;
+	}
+	/* And rebuild the real word_eol. */
+	for (i = 0; i != listsize; i++) {
+		word_eol[i] = word_eol_raw+next;
+		next += strlen(word[i])+1;
+	}
+	word_eol[i] = "";
+
+	plugin = hook->plugin;
+	BEGIN_PLUGIN(plugin);
+
+	word_list = Util_BuildList(word);
+	if (word_list == NULL) {
+		g_free(word_eol_raw);
+		g_free(word_eol);
+		END_PLUGIN(plugin);
+		return 0;
+	}
+	word_eol_list = Util_BuildList(word_eol);
+	if (word_eol_list == NULL) {
+		g_free(word_eol_raw);
+		g_free(word_eol);
+		Py_DECREF(word_list);
+		END_PLUGIN(plugin);
+		return 0;
+	}
+
+
+	retobj = PyObject_CallFunction(hook->callback, "(OOO)", word_list,
+					       word_eol_list, hook->userdata);
+
+	Py_DECREF(word_list);
+	Py_DECREF(word_eol_list);
+
+	g_free(word_eol_raw);
+	g_free(word_eol);
+	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;
@@ -896,6 +992,7 @@ static PyTypeObject XChatOut_Type = {
 /* ===================================================================== */
 /* Attribute object */
 
+#undef OFF
 #define OFF(x) offsetof(AttributeObject, x)
 
 static PyMemberDef Attribute_members[] = {
@@ -2028,7 +2125,7 @@ Module_hexchat_hook_print(PyObject *self, PyObject *args, PyObject *kwargs)
 		return NULL;
 
 	BEGIN_XCHAT_CALLS(NONE);
-	hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
+	hook->data = (void*)hexchat_hook_print(ph, name, priority,
 					     Callback_Print, hook);
 	END_XCHAT_CALLS();
 
@@ -2065,7 +2162,7 @@ Module_hexchat_hook_print_attrs(PyObject *self, PyObject *args, PyObject *kwargs
 
 	BEGIN_XCHAT_CALLS(NONE);
 	hook->data = (void*)hexchat_hook_print_attrs(ph, name, priority,
-					     Callback_Print, hook);
+					     Callback_Print_Attrs, hook);
 	END_XCHAT_CALLS();
 
 	return PyLong_FromVoidPtr(hook);
@@ -2755,7 +2852,7 @@ hexchat_plugin_init(hexchat_plugin *plugin_handle,
 	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, reload, 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