--- a/fix-module-messages Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-# HG changeset patch
-# Parent 8e120e293a253ab7c0cbfb85ac74ab12428cf0f7
-
-diff -r 8e120e293a25 mcabber/mcabber/commands.c
---- a/mcabber/mcabber/commands.c Sat Jul 28 18:45:45 2012 +0300
-+++ b/mcabber/mcabber/commands.c Sat Jul 28 19:00:28 2012 +0300
-@@ -3251,16 +3251,18 @@
- ++name;
- }
-
-- if (!strcmp(args[0], "load"))
-+ if (!strcmp(args[0], "load")) {
- error = module_load(name, TRUE, force);
-- else if (!strcmp(args[0], "unload"))
-+ if (error)
-+ scr_log_print(LPRINT_LOGNORM, "Module '%s' loading error: %s", name, error);
-+ } else if (!strcmp(args[0], "unload")) {
- error = module_unload(name, TRUE, force);
-- else if (!strcmp(args[0], "info"))
-+ if (error)
-+ scr_log_print(LPRINT_LOGNORM, "Module '%s' unloading error: %s", name, error);
-+ } else if (!strcmp(args[0], "info"))
- module_info_print(name);
- else
-- error = "Unknown subcommand";
-- if (error)
-- scr_LogPrint(LPRINT_LOGNORM, "Error: %s.", error);
-+ scr_log_print(LPRINT_LOGNORM, "Error: module: Unknown subcommand.");
- }
- free_arg_lst(args);
- #else
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fix-module-messages.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,31 @@
+# HG changeset patch
+# Parent 8e120e293a253ab7c0cbfb85ac74ab12428cf0f7
+
+diff -r 8e120e293a25 mcabber/mcabber/commands.c
+--- a/mcabber/mcabber/commands.c Sat Jul 28 18:45:45 2012 +0300
++++ b/mcabber/mcabber/commands.c Sat Jul 28 19:00:28 2012 +0300
+@@ -3251,16 +3251,18 @@
+ ++name;
+ }
+
+- if (!strcmp(args[0], "load"))
++ if (!strcmp(args[0], "load")) {
+ error = module_load(name, TRUE, force);
+- else if (!strcmp(args[0], "unload"))
++ if (error)
++ scr_log_print(LPRINT_LOGNORM, "Module '%s' loading error: %s", name, error);
++ } else if (!strcmp(args[0], "unload")) {
+ error = module_unload(name, TRUE, force);
+- else if (!strcmp(args[0], "info"))
++ if (error)
++ scr_log_print(LPRINT_LOGNORM, "Module '%s' unloading error: %s", name, error);
++ } else if (!strcmp(args[0], "info"))
+ module_info_print(name);
+ else
+- error = "Unknown subcommand";
+- if (error)
+- scr_LogPrint(LPRINT_LOGNORM, "Error: %s.", error);
++ scr_log_print(LPRINT_LOGNORM, "Error: module: Unknown subcommand.");
+ }
+ free_arg_lst(args);
+ #else
--- a/fix-receipts Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-# HG changeset patch
-# Parent 99db5d4ed047b3dbe54727c1df311531f611f31f
-Fix receipts handling according to recent XEP updates
-
- * add lm_get_uid() @xmpp_helper.h
- * identify receipts by id attribute of 'received' subelement
- instead of stanza id
-
-diff -r 99db5d4ed047 mcabber/mcabber/hbuf.c
---- a/mcabber/mcabber/hbuf.c Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/hbuf.c Mon Apr 30 23:10:28 2012 +0300
-@@ -525,7 +525,7 @@
- // hbuf_remove_receipt(hbuf, xep184)
- // Remove the Receipt Flag for the message with the given xep184 id
- // Returns TRUE if it was found and removed, otherwise FALSE
--gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184)
-+gboolean hbuf_remove_receipt(GList *hbuf, gconstpointer xep184)
- {
- hbuf_block *blk;
-
-@@ -533,7 +533,8 @@
-
- for ( ; hbuf; hbuf = g_list_previous(hbuf)) {
- blk = (hbuf_block*)(hbuf->data);
-- if (blk->prefix.xep184 == xep184) {
-+ if (!g_strcmp0 (blk->prefix.xep184, xep184)) {
-+ g_free (blk->prefix.xep184);
- blk->prefix.xep184 = NULL;
- blk->prefix.flags ^= HBB_PREFIX_RECEIPT;
- return TRUE;
-diff -r 99db5d4ed047 mcabber/mcabber/hbuf.h
---- a/mcabber/mcabber/hbuf.h Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/hbuf.h Mon Apr 30 23:10:28 2012 +0300
-@@ -50,7 +50,7 @@
- GList *hbuf_jump_date(GList *hbuf, time_t t);
- GList *hbuf_jump_percent(GList *hbuf, int pc);
- GList *hbuf_jump_readmark(GList *hbuf);
--gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184);
-+gboolean hbuf_remove_receipt(GList *hbuf, gconstpointer xep184);
- void hbuf_set_readmark(GList *hbuf, gboolean action);
- void hbuf_remove_trailing_readmark(GList *hbuf);
-
-diff -r 99db5d4ed047 mcabber/mcabber/screen.c
---- a/mcabber/mcabber/screen.c Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/screen.c Mon Apr 30 23:10:28 2012 +0300
-@@ -2239,7 +2239,7 @@
- scr_show_window(jidto, FALSE);
- }
-
--void scr_remove_receipt_flag(const char *bjid, gpointer xep184)
-+void scr_remove_receipt_flag(const char *bjid, gconstpointer xep184)
- {
- winbuf *win_entry = scr_search_window(bjid, FALSE);
- if (win_entry) {
-diff -r 99db5d4ed047 mcabber/mcabber/screen.h
---- a/mcabber/mcabber/screen.h Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/screen.h Mon Apr 30 23:10:28 2012 +0300
-@@ -108,7 +108,7 @@
- void scr_update_main_status(int forceupdate);
- void scr_update_chat_status(int forceupdate);
- void scr_roster_visibility(int status);
--void scr_remove_receipt_flag(const char *jidto, gpointer xep184);
-+void scr_remove_receipt_flag(const char *jidto, gconstpointer xep184);
- void scr_show_buddy_window(void);
- int scr_buddy_buffer_exists(const char *jid);
- void scr_update_buddy_window(void);
-diff -r 99db5d4ed047 mcabber/mcabber/xmpp.c
---- a/mcabber/mcabber/xmpp.c Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/xmpp.c Mon Apr 30 23:10:28 2012 +0300
-@@ -295,15 +295,6 @@
- g_slist_free(resources);
- }
-
--static LmHandlerResult cb_xep184(LmMessageHandler *h, LmConnection *c,
-- LmMessage *m, gpointer user_data)
--{
-- char *from = jidtodisp(lm_message_get_from(m));
-- scr_remove_receipt_flag(from, h);
-- g_free(from);
-- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
--}
--
- // xmpp_send_msg(jid, text, type, subject,
- // otrinject, *encrypted, type_overwrite)
- // When encrypted is not NULL, the function set *encrypted to 1 if the
-@@ -432,7 +423,8 @@
- lm_message_node_set_attribute
- (lm_message_node_add_child(x->node, "request", NULL),
- "xmlns", NS_RECEIPTS);
-- *xep184 = lm_message_handler_new(cb_xep184, NULL, NULL);
-+ *xep184 = lm_get_uid ();
-+ lm_message_node_set_attribute (x->node, "id", (const gchar *)*xep184);
- }
- g_free(barejid);
-
-@@ -499,11 +491,7 @@
- if (mystatus != invisible)
- #endif
- update_last_use();
-- if (xep184 && *xep184) {
-- lm_connection_send_with_reply(lconnection, x, *xep184, NULL);
-- lm_message_handler_unref(*xep184);
-- } else
-- lm_connection_send(lconnection, x, NULL);
-+ lm_connection_send(lconnection, x, NULL);
- lm_message_unref(x);
- }
-
-@@ -1305,6 +1293,16 @@
- lm_message_unref(rcvd);
- }
-
-+ { // xep184 receipt confirmation
-+ LmMessageNode *received = lm_message_node_get_child (m->node, "received");
-+ if (received && !g_strcmp0 (lm_message_node_get_attribute (received, "xmlns"), NS_RECEIPTS)) {
-+ char *jid = jidtodisp(from);
-+ const char *id = lm_message_node_get_attribute (received, "id");
-+ scr_remove_receipt_flag(jid, id);
-+ g_free(jid);
-+ }
-+ }
-+
- if (from) {
- x = lm_message_node_find_xmlns(m->node, NS_MUC_USER);
- if (x && !strcmp(x->name, "x"))
-diff -r 99db5d4ed047 mcabber/mcabber/xmpp_helper.c
---- a/mcabber/mcabber/xmpp_helper.c Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/xmpp_helper.c Mon Apr 30 23:10:28 2012 +0300
-@@ -72,7 +72,6 @@
- {0, NULL, NULL, NULL, NULL}
- };
-
--
- #ifdef MODULES_ENABLE
- static GSList *xmpp_additional_features = NULL;
- static char *ver, *ver_notavail;
-@@ -104,6 +103,13 @@
- }
- #endif
-
-+// The caller must g_free this after use
-+gchar *lm_get_uid ()
-+{
-+ static guint xmpp_uid = 0;
-+ return g_strdup_printf ("mc%u", ++xmpp_uid);
-+}
-+
- const gchar* lm_message_node_get_child_value(LmMessageNode *node,
- const gchar *child)
- {
-diff -r 99db5d4ed047 mcabber/mcabber/xmpp_helper.h
---- a/mcabber/mcabber/xmpp_helper.h Thu Mar 01 20:18:00 2012 +0100
-+++ b/mcabber/mcabber/xmpp_helper.h Mon Apr 30 23:10:28 2012 +0300
-@@ -29,6 +29,8 @@
- void xmpp_del_feature (const char *xmlns);
- #endif
-
-+gchar *lm_get_uid (void);
-+
- LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns);
- LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
- const char *xmlns);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fix-receipts.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,162 @@
+# HG changeset patch
+# Parent 99db5d4ed047b3dbe54727c1df311531f611f31f
+Fix receipts handling according to recent XEP updates
+
+ * add lm_get_uid() @xmpp_helper.h
+ * identify receipts by id attribute of 'received' subelement
+ instead of stanza id
+
+diff -r 99db5d4ed047 mcabber/mcabber/hbuf.c
+--- a/mcabber/mcabber/hbuf.c Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/hbuf.c Mon Apr 30 23:10:28 2012 +0300
+@@ -525,7 +525,7 @@
+ // hbuf_remove_receipt(hbuf, xep184)
+ // Remove the Receipt Flag for the message with the given xep184 id
+ // Returns TRUE if it was found and removed, otherwise FALSE
+-gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184)
++gboolean hbuf_remove_receipt(GList *hbuf, gconstpointer xep184)
+ {
+ hbuf_block *blk;
+
+@@ -533,7 +533,8 @@
+
+ for ( ; hbuf; hbuf = g_list_previous(hbuf)) {
+ blk = (hbuf_block*)(hbuf->data);
+- if (blk->prefix.xep184 == xep184) {
++ if (!g_strcmp0 (blk->prefix.xep184, xep184)) {
++ g_free (blk->prefix.xep184);
+ blk->prefix.xep184 = NULL;
+ blk->prefix.flags ^= HBB_PREFIX_RECEIPT;
+ return TRUE;
+diff -r 99db5d4ed047 mcabber/mcabber/hbuf.h
+--- a/mcabber/mcabber/hbuf.h Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/hbuf.h Mon Apr 30 23:10:28 2012 +0300
+@@ -50,7 +50,7 @@
+ GList *hbuf_jump_date(GList *hbuf, time_t t);
+ GList *hbuf_jump_percent(GList *hbuf, int pc);
+ GList *hbuf_jump_readmark(GList *hbuf);
+-gboolean hbuf_remove_receipt(GList *hbuf, gpointer xep184);
++gboolean hbuf_remove_receipt(GList *hbuf, gconstpointer xep184);
+ void hbuf_set_readmark(GList *hbuf, gboolean action);
+ void hbuf_remove_trailing_readmark(GList *hbuf);
+
+diff -r 99db5d4ed047 mcabber/mcabber/screen.c
+--- a/mcabber/mcabber/screen.c Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/screen.c Mon Apr 30 23:10:28 2012 +0300
+@@ -2239,7 +2239,7 @@
+ scr_show_window(jidto, FALSE);
+ }
+
+-void scr_remove_receipt_flag(const char *bjid, gpointer xep184)
++void scr_remove_receipt_flag(const char *bjid, gconstpointer xep184)
+ {
+ winbuf *win_entry = scr_search_window(bjid, FALSE);
+ if (win_entry) {
+diff -r 99db5d4ed047 mcabber/mcabber/screen.h
+--- a/mcabber/mcabber/screen.h Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/screen.h Mon Apr 30 23:10:28 2012 +0300
+@@ -108,7 +108,7 @@
+ void scr_update_main_status(int forceupdate);
+ void scr_update_chat_status(int forceupdate);
+ void scr_roster_visibility(int status);
+-void scr_remove_receipt_flag(const char *jidto, gpointer xep184);
++void scr_remove_receipt_flag(const char *jidto, gconstpointer xep184);
+ void scr_show_buddy_window(void);
+ int scr_buddy_buffer_exists(const char *jid);
+ void scr_update_buddy_window(void);
+diff -r 99db5d4ed047 mcabber/mcabber/xmpp.c
+--- a/mcabber/mcabber/xmpp.c Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/xmpp.c Mon Apr 30 23:10:28 2012 +0300
+@@ -295,15 +295,6 @@
+ g_slist_free(resources);
+ }
+
+-static LmHandlerResult cb_xep184(LmMessageHandler *h, LmConnection *c,
+- LmMessage *m, gpointer user_data)
+-{
+- char *from = jidtodisp(lm_message_get_from(m));
+- scr_remove_receipt_flag(from, h);
+- g_free(from);
+- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+-}
+-
+ // xmpp_send_msg(jid, text, type, subject,
+ // otrinject, *encrypted, type_overwrite)
+ // When encrypted is not NULL, the function set *encrypted to 1 if the
+@@ -432,7 +423,8 @@
+ lm_message_node_set_attribute
+ (lm_message_node_add_child(x->node, "request", NULL),
+ "xmlns", NS_RECEIPTS);
+- *xep184 = lm_message_handler_new(cb_xep184, NULL, NULL);
++ *xep184 = lm_get_uid ();
++ lm_message_node_set_attribute (x->node, "id", (const gchar *)*xep184);
+ }
+ g_free(barejid);
+
+@@ -499,11 +491,7 @@
+ if (mystatus != invisible)
+ #endif
+ update_last_use();
+- if (xep184 && *xep184) {
+- lm_connection_send_with_reply(lconnection, x, *xep184, NULL);
+- lm_message_handler_unref(*xep184);
+- } else
+- lm_connection_send(lconnection, x, NULL);
++ lm_connection_send(lconnection, x, NULL);
+ lm_message_unref(x);
+ }
+
+@@ -1305,6 +1293,16 @@
+ lm_message_unref(rcvd);
+ }
+
++ { // xep184 receipt confirmation
++ LmMessageNode *received = lm_message_node_get_child (m->node, "received");
++ if (received && !g_strcmp0 (lm_message_node_get_attribute (received, "xmlns"), NS_RECEIPTS)) {
++ char *jid = jidtodisp(from);
++ const char *id = lm_message_node_get_attribute (received, "id");
++ scr_remove_receipt_flag(jid, id);
++ g_free(jid);
++ }
++ }
++
+ if (from) {
+ x = lm_message_node_find_xmlns(m->node, NS_MUC_USER);
+ if (x && !strcmp(x->name, "x"))
+diff -r 99db5d4ed047 mcabber/mcabber/xmpp_helper.c
+--- a/mcabber/mcabber/xmpp_helper.c Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/xmpp_helper.c Mon Apr 30 23:10:28 2012 +0300
+@@ -72,7 +72,6 @@
+ {0, NULL, NULL, NULL, NULL}
+ };
+
+-
+ #ifdef MODULES_ENABLE
+ static GSList *xmpp_additional_features = NULL;
+ static char *ver, *ver_notavail;
+@@ -104,6 +103,13 @@
+ }
+ #endif
+
++// The caller must g_free this after use
++gchar *lm_get_uid ()
++{
++ static guint xmpp_uid = 0;
++ return g_strdup_printf ("mc%u", ++xmpp_uid);
++}
++
+ const gchar* lm_message_node_get_child_value(LmMessageNode *node,
+ const gchar *child)
+ {
+diff -r 99db5d4ed047 mcabber/mcabber/xmpp_helper.h
+--- a/mcabber/mcabber/xmpp_helper.h Thu Mar 01 20:18:00 2012 +0100
++++ b/mcabber/mcabber/xmpp_helper.h Mon Apr 30 23:10:28 2012 +0300
+@@ -29,6 +29,8 @@
+ void xmpp_del_feature (const char *xmlns);
+ #endif
+
++gchar *lm_get_uid (void);
++
+ LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns);
+ LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node,
+ const char *xmlns);
--- a/fix-typo Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-# HG changeset patch
-# Parent e9b6e168a45e12200f486929e5786ffd64ac143e
-s/Copyrigth/Copyright/ and so on
-
-diff -r e9b6e168a45e mcabber/mcabber/events.c
---- a/mcabber/mcabber/events.c Wed Jul 18 00:24:13 2012 +0200
-+++ b/mcabber/mcabber/events.c Wed Jul 18 08:31:55 2012 +0300
-@@ -2,7 +2,7 @@
- * events.c -- Events fonctions
- *
- * Copyright (C) 2006-2010 Mikael Berthe <mikael@lilotux.net>
-- * Copyrigth (C) 2010 Myhailo Danylenko <isbear@ukrposte.net>
-+ * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
-diff -r e9b6e168a45e mcabber/mcabber/fifo.c
---- a/mcabber/mcabber/fifo.c Wed Jul 18 00:24:13 2012 +0200
-+++ b/mcabber/mcabber/fifo.c Wed Jul 18 08:31:55 2012 +0300
-@@ -2,7 +2,7 @@
- * fifo.c -- Read commands from a named pipe
- *
- * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
-- * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
-+ * Copyright (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
-diff -r e9b6e168a45e mcabber/mcabber/help.c
---- a/mcabber/mcabber/help.c Wed Jul 18 00:24:13 2012 +0200
-+++ b/mcabber/mcabber/help.c Wed Jul 18 08:31:55 2012 +0300
-@@ -2,7 +2,7 @@
- * help.c -- Help command
- *
- * Copyright (C) 2006-2010 Mikael Berthe <mikael@lilotux.net>
-- * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
-+ * Copyright (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
-diff -r e9b6e168a45e mcabber/mcabber/xmpp_muc.c
---- a/mcabber/mcabber/xmpp_muc.c Wed Jul 18 00:24:13 2012 +0200
-+++ b/mcabber/mcabber/xmpp_muc.c Wed Jul 18 08:31:55 2012 +0300
-@@ -3,7 +3,7 @@
- *
- * Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de>
- * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
-- * Copyrigth (C) 2010 Myhailo Danylenko <isbear@ukrposte.net>
-+ * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/fix-typo.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,52 @@
+# HG changeset patch
+# Parent e9b6e168a45e12200f486929e5786ffd64ac143e
+s/Copyrigth/Copyright/ and so on
+
+diff -r e9b6e168a45e mcabber/mcabber/events.c
+--- a/mcabber/mcabber/events.c Wed Jul 18 00:24:13 2012 +0200
++++ b/mcabber/mcabber/events.c Wed Jul 18 08:31:55 2012 +0300
+@@ -2,7 +2,7 @@
+ * events.c -- Events fonctions
+ *
+ * Copyright (C) 2006-2010 Mikael Berthe <mikael@lilotux.net>
+- * Copyrigth (C) 2010 Myhailo Danylenko <isbear@ukrposte.net>
++ * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+diff -r e9b6e168a45e mcabber/mcabber/fifo.c
+--- a/mcabber/mcabber/fifo.c Wed Jul 18 00:24:13 2012 +0200
++++ b/mcabber/mcabber/fifo.c Wed Jul 18 08:31:55 2012 +0300
+@@ -2,7 +2,7 @@
+ * fifo.c -- Read commands from a named pipe
+ *
+ * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
+- * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
++ * Copyright (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+diff -r e9b6e168a45e mcabber/mcabber/help.c
+--- a/mcabber/mcabber/help.c Wed Jul 18 00:24:13 2012 +0200
++++ b/mcabber/mcabber/help.c Wed Jul 18 08:31:55 2012 +0300
+@@ -2,7 +2,7 @@
+ * help.c -- Help command
+ *
+ * Copyright (C) 2006-2010 Mikael Berthe <mikael@lilotux.net>
+- * Copyrigth (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
++ * Copyright (C) 2009 Myhailo Danylenko <isbear@ukrpost.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+diff -r e9b6e168a45e mcabber/mcabber/xmpp_muc.c
+--- a/mcabber/mcabber/xmpp_muc.c Wed Jul 18 00:24:13 2012 +0200
++++ b/mcabber/mcabber/xmpp_muc.c Wed Jul 18 08:31:55 2012 +0300
+@@ -3,7 +3,7 @@
+ *
+ * Copyright (C) 2008-2010 Frank Zschockelt <mcabber@freakysoft.de>
+ * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
+- * Copyrigth (C) 2010 Myhailo Danylenko <isbear@ukrposte.net>
++ * Copyright (C) 2010 Myhailo Danylenko <isbear@ukrpost.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
--- a/guard-xmpp-password Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-Guard XMPP password with option guard
-
- * instead of filtering access to stored in public place
- restricted data, better store restricted data somewhere else
- * add xmpp_init() and xmpp_have_password (non-api)
-
-diff -r c290165bc534 mcabber/mcabber/commands.c
---- a/mcabber/mcabber/commands.c Mon Apr 30 23:37:25 2012 +0300
-+++ b/mcabber/mcabber/commands.c Mon Apr 30 23:38:20 2012 +0300
-@@ -2272,10 +2272,8 @@
-
- static void list_option_cb(char *k, char *v, void *f)
- {
-- if (strcmp(k, "password")) {
-- GSList **list = f;
-- *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
-- }
-+ GSList **list = f;
-+ *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
- }
-
- static void do_set(char *arg)
-diff -r c290165bc534 mcabber/mcabber/main.c
---- a/mcabber/mcabber/main.c Mon Apr 30 23:37:25 2012 +0300
-+++ b/mcabber/mcabber/main.c Mon Apr 30 23:38:20 2012 +0300
-@@ -381,6 +381,7 @@
- scr_init_locale_charset();
- ut_init_debug();
- help_init();
-+ xmpp_init();
-
- /* Parsing config file... */
- ret = cfg_read_file(configFile, TRUE);
-@@ -417,8 +418,9 @@
-
- /* If no password is stored, we ask for it before entering
- ncurses mode -- unless the username is unknown. */
-- if (settings_opt_get("jid") && !settings_opt_get("password")) {
-+ if (settings_opt_get("jid") && !xmpp_have_password) {
- char *pwd = ask_password("your Jabber password");
-+ /* Will be intercepted by guard */
- settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
- g_free(pwd);
- }
-diff -r c290165bc534 mcabber/mcabber/xmpp.c
---- a/mcabber/mcabber/xmpp.c Mon Apr 30 23:37:25 2012 +0300
-+++ b/mcabber/mcabber/xmpp.c Mon Apr 30 23:38:20 2012 +0300
-@@ -23,6 +23,8 @@
- */
- #include <stdlib.h>
- #include <string.h>
-+#include <sys/mman.h>
-+#include <errno.h>
-
- #include "xmpp.h"
- #include "xmpp_helper.h"
-@@ -53,6 +55,9 @@
- static enum imstatus mywantedstatus = available;
- gchar *mystatusmsg;
-
-+static char *xmpp_password = NULL;
-+gboolean xmpp_have_password = FALSE;
-+
- char imstatus2char[imstatus_size+1] = {
- '_', 'o', 'f', 'd', 'n', 'a', 'i', '\0'
- };
-@@ -892,16 +897,15 @@
- GError *error = NULL;
-
- if (success) {
-- const char *password, *resource;
-+ const char *resource;
- char *username;
- username = jid_get_username(settings_opt_get("jid"));
-- password = settings_opt_get("password");
- resource = strchr(lm_connection_get_jid(connection),
- JID_RESOURCE_SEPARATOR);
- if (resource)
- resource++;
-
-- if (!lm_connection_authenticate(lconnection, username, password, resource,
-+ if (!lm_connection_authenticate(lconnection, username, xmpp_password, resource,
- connection_auth_cb, NULL, FALSE, &error)) {
- scr_LogPrint(LPRINT_LOGNORM, "Failed to authenticate: %s",
- error->message);
-@@ -1780,13 +1784,37 @@
- }
- }
-
-+static gchar *xmpp_password_guard(const gchar *key, const gchar *new_value)
-+{
-+ if (xmpp_password) {
-+ size_t len = strlen(xmpp_password);
-+ memset(xmpp_password, '\0', len);
-+ if (munlock(xmpp_password, len))
-+ scr_LogPrint(LPRINT_DEBUG, "password guard: Cannot unlock memory: %s.",
-+ strerror(errno));
-+ g_free(xmpp_password);
-+ }
-+ xmpp_password = g_strdup(new_value);
-+ if (xmpp_password) {
-+ if (mlock(xmpp_password, strlen(xmpp_password)))
-+ scr_LogPrint(LPRINT_DEBUG, "password guard: Cannot lock memory: %s.",
-+ strerror(errno));
-+ xmpp_have_password = TRUE;
-+ }
-+ return NULL;
-+}
-+
-+void xmpp_init(void)
-+{
-+ settings_set_guard("password", xmpp_password_guard);
-+}
-
- // xmpp_connect()
- // Return a non-zero value if there's an obvious problem
- // (no JID, no password, etc.)
- gint xmpp_connect(void)
- {
-- const char *userjid, *password, *resource, *servername, *ssl_fpr;
-+ const char *userjid, *resource, *servername, *ssl_fpr;
- char *dynresource = NULL;
- char fpr[16];
- const char *proxy_host;
-@@ -1803,7 +1831,6 @@
-
- servername = settings_opt_get("server");
- userjid = settings_opt_get("jid");
-- password = settings_opt_get("password");
- resource = settings_opt_get("resource");
- proxy_host = settings_opt_get("proxy_host");
- ssl_fpr = settings_opt_get("ssl_fingerprint");
-@@ -1812,7 +1839,7 @@
- scr_LogPrint(LPRINT_LOGNORM, "Your JID has not been specified!");
- return -1;
- }
-- if (!password) {
-+ if (!xmpp_password) {
- scr_LogPrint(LPRINT_LOGNORM, "Your password has not been specified!");
- return -1;
- }
-diff -r c290165bc534 mcabber/mcabber/xmpp.h
---- a/mcabber/mcabber/xmpp.h Mon Apr 30 23:37:25 2012 +0300
-+++ b/mcabber/mcabber/xmpp.h Mon Apr 30 23:38:20 2012 +0300
-@@ -33,7 +33,9 @@
-
- extern LmConnection* lconnection;
- extern LmSSL* lssl;
-+extern gboolean xmpp_have_password; /* private */
-
-+void xmpp_init(void); /* private */
- int xmpp_connect(void);
- void xmpp_disconnect(void);
- gboolean xmpp_is_online(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/guard-xmpp-password.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,154 @@
+Guard XMPP password with option guard
+
+ * instead of filtering access to stored in public place
+ restricted data, better store restricted data somewhere else
+ * add xmpp_init() and xmpp_have_password (non-api)
+
+diff -r c290165bc534 mcabber/mcabber/commands.c
+--- a/mcabber/mcabber/commands.c Mon Apr 30 23:37:25 2012 +0300
++++ b/mcabber/mcabber/commands.c Mon Apr 30 23:38:20 2012 +0300
+@@ -2272,10 +2272,8 @@
+
+ static void list_option_cb(char *k, char *v, void *f)
+ {
+- if (strcmp(k, "password")) {
+- GSList **list = f;
+- *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
+- }
++ GSList **list = f;
++ *list = g_slist_insert_sorted(*list, k, (GCompareFunc)strcmp);
+ }
+
+ static void do_set(char *arg)
+diff -r c290165bc534 mcabber/mcabber/main.c
+--- a/mcabber/mcabber/main.c Mon Apr 30 23:37:25 2012 +0300
++++ b/mcabber/mcabber/main.c Mon Apr 30 23:38:20 2012 +0300
+@@ -381,6 +381,7 @@
+ scr_init_locale_charset();
+ ut_init_debug();
+ help_init();
++ xmpp_init();
+
+ /* Parsing config file... */
+ ret = cfg_read_file(configFile, TRUE);
+@@ -417,8 +418,9 @@
+
+ /* If no password is stored, we ask for it before entering
+ ncurses mode -- unless the username is unknown. */
+- if (settings_opt_get("jid") && !settings_opt_get("password")) {
++ if (settings_opt_get("jid") && !xmpp_have_password) {
+ char *pwd = ask_password("your Jabber password");
++ /* Will be intercepted by guard */
+ settings_set(SETTINGS_TYPE_OPTION, "password", pwd);
+ g_free(pwd);
+ }
+diff -r c290165bc534 mcabber/mcabber/xmpp.c
+--- a/mcabber/mcabber/xmpp.c Mon Apr 30 23:37:25 2012 +0300
++++ b/mcabber/mcabber/xmpp.c Mon Apr 30 23:38:20 2012 +0300
+@@ -23,6 +23,8 @@
+ */
+ #include <stdlib.h>
+ #include <string.h>
++#include <sys/mman.h>
++#include <errno.h>
+
+ #include "xmpp.h"
+ #include "xmpp_helper.h"
+@@ -53,6 +55,9 @@
+ static enum imstatus mywantedstatus = available;
+ gchar *mystatusmsg;
+
++static char *xmpp_password = NULL;
++gboolean xmpp_have_password = FALSE;
++
+ char imstatus2char[imstatus_size+1] = {
+ '_', 'o', 'f', 'd', 'n', 'a', 'i', '\0'
+ };
+@@ -892,16 +897,15 @@
+ GError *error = NULL;
+
+ if (success) {
+- const char *password, *resource;
++ const char *resource;
+ char *username;
+ username = jid_get_username(settings_opt_get("jid"));
+- password = settings_opt_get("password");
+ resource = strchr(lm_connection_get_jid(connection),
+ JID_RESOURCE_SEPARATOR);
+ if (resource)
+ resource++;
+
+- if (!lm_connection_authenticate(lconnection, username, password, resource,
++ if (!lm_connection_authenticate(lconnection, username, xmpp_password, resource,
+ connection_auth_cb, NULL, FALSE, &error)) {
+ scr_LogPrint(LPRINT_LOGNORM, "Failed to authenticate: %s",
+ error->message);
+@@ -1780,13 +1784,37 @@
+ }
+ }
+
++static gchar *xmpp_password_guard(const gchar *key, const gchar *new_value)
++{
++ if (xmpp_password) {
++ size_t len = strlen(xmpp_password);
++ memset(xmpp_password, '\0', len);
++ if (munlock(xmpp_password, len))
++ scr_LogPrint(LPRINT_DEBUG, "password guard: Cannot unlock memory: %s.",
++ strerror(errno));
++ g_free(xmpp_password);
++ }
++ xmpp_password = g_strdup(new_value);
++ if (xmpp_password) {
++ if (mlock(xmpp_password, strlen(xmpp_password)))
++ scr_LogPrint(LPRINT_DEBUG, "password guard: Cannot lock memory: %s.",
++ strerror(errno));
++ xmpp_have_password = TRUE;
++ }
++ return NULL;
++}
++
++void xmpp_init(void)
++{
++ settings_set_guard("password", xmpp_password_guard);
++}
+
+ // xmpp_connect()
+ // Return a non-zero value if there's an obvious problem
+ // (no JID, no password, etc.)
+ gint xmpp_connect(void)
+ {
+- const char *userjid, *password, *resource, *servername, *ssl_fpr;
++ const char *userjid, *resource, *servername, *ssl_fpr;
+ char *dynresource = NULL;
+ char fpr[16];
+ const char *proxy_host;
+@@ -1803,7 +1831,6 @@
+
+ servername = settings_opt_get("server");
+ userjid = settings_opt_get("jid");
+- password = settings_opt_get("password");
+ resource = settings_opt_get("resource");
+ proxy_host = settings_opt_get("proxy_host");
+ ssl_fpr = settings_opt_get("ssl_fingerprint");
+@@ -1812,7 +1839,7 @@
+ scr_LogPrint(LPRINT_LOGNORM, "Your JID has not been specified!");
+ return -1;
+ }
+- if (!password) {
++ if (!xmpp_password) {
+ scr_LogPrint(LPRINT_LOGNORM, "Your password has not been specified!");
+ return -1;
+ }
+diff -r c290165bc534 mcabber/mcabber/xmpp.h
+--- a/mcabber/mcabber/xmpp.h Mon Apr 30 23:37:25 2012 +0300
++++ b/mcabber/mcabber/xmpp.h Mon Apr 30 23:38:20 2012 +0300
+@@ -33,7 +33,9 @@
+
+ extern LmConnection* lconnection;
+ extern LmSSL* lssl;
++extern gboolean xmpp_have_password; /* private */
+
++void xmpp_init(void); /* private */
+ int xmpp_connect(void);
+ void xmpp_disconnect(void);
+ gboolean xmpp_is_online(void);
--- a/modularize-extcmd Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,386 +0,0 @@
-Convert extcmd into a module
-
-diff -r 27d19b6bc194 mcabber/configure.ac
---- a/mcabber/configure.ac Mon Apr 30 23:36:55 2012 +0300
-+++ b/mcabber/configure.ac Mon Apr 30 23:37:25 2012 +0300
-@@ -283,6 +283,7 @@
- modules/beep/Makefile
- modules/xttitle/Makefile
- modules/fifo/Makefile
-+ modules/eventcmd/Makefile
- modules/urlregex/Makefile
- doc/Makefile
- doc/guide/Makefile
-diff -r 27d19b6bc194 mcabber/mcabber/Makefile.am
---- a/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:55 2012 +0300
-+++ b/mcabber/mcabber/Makefile.am Mon Apr 30 23:37:25 2012 +0300
-@@ -7,7 +7,7 @@
- xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
- xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
- xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
-- caps.c caps.h help.c help.h extcmd.c extcmd.h
-+ caps.c caps.h help.c help.h
-
- if OTR
- mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
-@@ -55,7 +55,7 @@
-
- mcabberincludedir = $(includedir)/mcabber
- else
--mcabber_SOURCES += fifo.c fifo.h
-+mcabber_SOURCES += fifo.c fifo.h extcmd.c extcmd.h
- endif
-
- #SUBDIRS =
-diff -r 27d19b6bc194 mcabber/mcabber/hooks.c
---- a/mcabber/mcabber/hooks.c Mon Apr 30 23:36:55 2012 +0300
-+++ b/mcabber/mcabber/hooks.c Mon Apr 30 23:37:25 2012 +0300
-@@ -36,7 +36,10 @@
- #include "utf8.h"
- #include "commands.h"
- #include "main.h"
--#include "extcmd.h"
-+
-+#ifndef MODULES_ENABLE
-+# include "extcmd.h"
-+#endif
-
- #ifdef MODULES_ENABLE
- #include <glib.h>
-@@ -189,7 +192,9 @@
- int is_groupchat = FALSE; // groupchat message
- int is_room = FALSE; // window is a room window
- int log_muc_conf = FALSE;
-+#ifndef MODULES_ENABLE
- int active_window = FALSE;
-+#endif
- int message_flags = 0;
- guint rtype = ROSTER_TYPE_USER;
- char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
-@@ -397,6 +402,7 @@
- }
- #endif
-
-+#ifndef MODULES_ENABLE
- if (settings_opt_get_int("events_ignore_active_window") &&
- current_buddy && scr_get_chatmode()) {
- gpointer bud = BUDDATA(current_buddy);
-@@ -415,6 +421,7 @@
- hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
- is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED,
- wmsg);
-+#endif
-
- // Beep, if enabled:
- // - if it's a private message
-@@ -491,8 +498,10 @@
- }
- #endif
-
-+#ifndef MODULES_ENABLE
- // External command
- hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
-+#endif
-
- g_free(bmsg);
- g_free(mmsg);
-@@ -578,9 +587,11 @@
- }
- #endif
-
-+#ifndef MODULES_ENABLE
- // External command
- newstatus[0] = toupper(newstatus[0]);
- hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, newstatus, status_msg);
-+#endif
- }
-
- void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
-@@ -704,11 +715,13 @@
- prev_muc_unread = muc_unread;
- prev_muc_attention = muc_attention;
-
-+#ifndef MODULES_ENABLE
- /* Call external command */
- str_unread = g_strdup_printf("%u %u %u %u", unread_count, attention_count,
- muc_unread, muc_attention);
- hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str_unread, NULL);
- g_free(str_unread);
-+#endif
- }
-
- // hk_presence_subscription_request(jid, message)
-diff -r 27d19b6bc194 mcabber/mcabber/main.c
---- a/mcabber/mcabber/main.c Mon Apr 30 23:36:55 2012 +0300
-+++ b/mcabber/mcabber/main.c Mon Apr 30 23:37:25 2012 +0300
-@@ -44,7 +44,10 @@
- #include "xmpp.h"
- #include "help.h"
- #include "events.h"
--#include "extcmd.h"
-+
-+#ifndef MODULES_ENABLE
-+# include "extcmd.h"
-+#endif
-
- #ifndef MODULES_ENABLE
- # include "fifo.h"
-@@ -443,9 +446,11 @@
- }
- #endif
-
-+#ifndef MODULES_ENABLE
- optstring = settings_opt_get("events_command");
- if (optstring)
- hk_ext_cmd_init(optstring);
-+#endif
-
- optstring = settings_opt_get("roster_display_filter");
- if (optstring)
-diff -r 27d19b6bc194 mcabber/modules/Makefile.am
---- a/mcabber/modules/Makefile.am Mon Apr 30 23:36:55 2012 +0300
-+++ b/mcabber/modules/Makefile.am Mon Apr 30 23:37:25 2012 +0300
-@@ -1,1 +1,1 @@
--SUBDIRS = beep xttitle fifo urlregex
-+SUBDIRS = beep xttitle fifo eventcmd urlregex
-diff -r 27d19b6bc194 mcabber/modules/eventcmd/Makefile.am
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/modules/eventcmd/Makefile.am Mon Apr 30 23:37:25 2012 +0300
-@@ -0,0 +1,12 @@
-+
-+if INSTALL_HEADERS
-+pkglib_LTLIBRARIES = libeventcmd.la
-+libeventcmd_la_SOURCES = eventcmd.c $(top_srcdir)/mcabber/extcmd.c $(top_srcdir)/mcabber/extcmd.h
-+libeventcmd_la_LDFLAGS = -module -avoid-version -shared
-+
-+LDADD = $(GLIB_LIBS)
-+AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(LOUDMOUTH_CFLAGS) \
-+ $(GPGME_CFLAGS) $(LIBOTR_CFLAGS) \
-+ $(ENCHANT_CFLAGS)
-+endif
-+
-diff -r 27d19b6bc194 mcabber/modules/eventcmd/eventcmd.c
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/modules/eventcmd/eventcmd.c Mon Apr 30 23:37:25 2012 +0300
-@@ -0,0 +1,221 @@
-+
-+/* Copyright 2009 Myhailo Danylenko
-+ *
-+ * This file is part of mcabber
-+ *
-+ * mcabber 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.
-+ *
-+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+#include <glib.h>
-+#include <gmodule.h>
-+#include <string.h>
-+#include <stdlib.h>
-+#include <ctype.h> // tolower
-+#include <unistd.h>
-+
-+#include <mcabber/settings.h>
-+#include <mcabber/hooks.h>
-+#include <mcabber/roster.h>
-+#include <mcabber/utils.h>
-+#include <mcabber/logprint.h>
-+#include <mcabber/extcmd.h>
-+#include <mcabber/modules.h>
-+#include <mcabber/screen.h>
-+#include <mcabber/config.h>
-+
-+static void ecm_init(void);
-+static void ecm_uninit(void);
-+
-+module_info_t info_eventcmd = {
-+ .branch = MCABBER_BRANCH,
-+ .api = MCABBER_API_VERSION,
-+ .version = MCABBER_VERSION,
-+ .requires = NULL,
-+ .init = ecm_init,
-+ .uninit = ecm_uninit,
-+ .description = "External command execution on events\n"
-+ "Recognizes options events_command (required), events_ignore_active_window, "
-+ "event_log_files, event_log_dir, eventcmd_use_nickname, eventcmd_checkstatus.",
-+ .next = NULL,
-+};
-+
-+static guint eventcmd_hpmi_hid = 0;
-+static guint eventcmd_hmo_hid = 0;
-+static guint eventcmd_hsc_hid = 0;
-+static guint eventcmd_hulc_hid = 0;
-+
-+static guint eventcmd_hpmih(const gchar *name, hk_arg_t *args,
-+ gpointer userdata)
-+{
-+ gboolean active_window = FALSE;
-+ gboolean is_groupchat = FALSE;
-+ gboolean timestamp = FALSE;
-+ const char *bjid = NULL;
-+ hk_arg_t *arg = args;
-+ const char *wmsg = NULL;
-+
-+ while (arg->name) {
-+ if (!strcmp(arg->name, "jid"))
-+ bjid = arg->value;
-+ else if (!strcmp(arg->name, "groupchat"))
-+ is_groupchat = strcmp(arg->value, "true") ? FALSE : TRUE;
-+ else if (!strcmp(arg->name, "delayed") && *(arg->value))
-+ timestamp = TRUE;
-+ else if (!strcmp(arg->name, "message"))
-+ wmsg = arg->value;
-+ arg++;
-+ }
-+
-+ if (!bjid) {
-+ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: post-message-in: No jid found.");
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+ }
-+
-+ if (settings_opt_get_int("events_ignore_active_window") &&
-+ current_buddy && scr_get_chatmode()) {
-+ gpointer bud = BUDDATA(current_buddy);
-+ if (bud) {
-+ const char *cjid = buddy_getjid(bud);
-+ if (cjid && !strcasecmp(cjid, bjid))
-+ active_window = TRUE;
-+ }
-+ }
-+
-+ if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
-+ is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED, wmsg);
-+
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+}
-+
-+static guint eventcmd_hmoh(const gchar *name, hk_arg_t *args, gpointer userdata)
-+{
-+ const char *bjid = NULL;
-+ hk_arg_t *arg = args;
-+
-+ while (arg->name) {
-+ if (!strcmp(arg->name, "jid")) {
-+ bjid = arg->value;
-+ break;
-+ }
-+ arg++;
-+ }
-+
-+ if (!bjid) {
-+ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: message-out: No jid found.");
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+ }
-+
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
-+
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+}
-+
-+static guint eventcmd_hsch(const gchar *name, hk_arg_t *args, gpointer userdata)
-+{
-+ const char *bjid = NULL;
-+ char status[2] = { '?', '\0' };
-+ hk_arg_t *arg = args;
-+ const char *message = NULL;
-+
-+ while (arg->name) {
-+ if (!strcmp(arg->name, "jid"))
-+ bjid = arg->value;
-+ else if (!strcmp(arg->name, "new_status"))
-+ status[0] = toupper (arg->value[0]);
-+ else if (!strcmp(arg->name, "message") && arg->value && *(arg->value))
-+ message = arg->value;
-+ arg++;
-+ }
-+
-+ if (!bjid) {
-+ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: status-change: No jid found.");
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+ }
-+
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, status, message);
-+
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+}
-+
-+static guint eventcmd_hulch(const gchar *name, hk_arg_t *args, gpointer userdata)
-+{
-+ hk_arg_t *arg = args;
-+ const gchar *unread = NULL;
-+ const gchar *attention = NULL;
-+ const gchar *muc_unread = NULL;
-+ const gchar *muc_attention = NULL;
-+
-+ while (arg->name) {
-+ if (!strcmp(arg->name, "unread"))
-+ unread = arg->value;
-+ else if (!strcmp(arg->name, "attention"))
-+ attention = arg->value;
-+ else if (!strcmp(arg->name, "muc_unread"))
-+ muc_unread = arg->value;
-+ else if (!strcmp(arg->name, "muc_attention"))
-+ muc_attention = arg->value;
-+ arg++;
-+ }
-+
-+ if (!unread || !attention || !muc_unread || !muc_attention) {
-+ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: unread-list-change: Missing parameter.");
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+ }
-+
-+ {
-+ gchar *str = g_strdup_printf("%s %s %s %s", unread, attention, muc_unread,
-+ muc_attention);
-+ hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str, NULL);
-+ g_free(str);
-+ }
-+
-+ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-+}
-+
-+const gchar *g_module_check_init(GModule *module)
-+{
-+ const char *command = settings_opt_get("events_command");
-+ if (!command)
-+ return "events_command not set";
-+ hk_ext_cmd_init(command);
-+ return NULL;
-+}
-+
-+void g_module_unload(GModule *module)
-+{
-+ hk_ext_cmd_init(NULL);
-+}
-+
-+static void ecm_init(void)
-+{
-+ eventcmd_hpmi_hid = hk_add_handler(eventcmd_hpmih, HOOK_POST_MESSAGE_IN,
-+ G_PRIORITY_LOW, NULL);
-+ eventcmd_hmo_hid = hk_add_handler(eventcmd_hmoh, HOOK_MESSAGE_OUT,
-+ G_PRIORITY_LOW, NULL);
-+ eventcmd_hsc_hid = hk_add_handler(eventcmd_hsch, HOOK_STATUS_CHANGE,
-+ G_PRIORITY_LOW, NULL);
-+ eventcmd_hulc_hid = hk_add_handler(eventcmd_hulch, HOOK_UNREAD_LIST_CHANGE,
-+ G_PRIORITY_LOW, NULL);
-+
-+}
-+
-+static void ecm_uninit(void)
-+{
-+ hk_del_handler(HOOK_POST_MESSAGE_IN, eventcmd_hpmi_hid);
-+ hk_del_handler(HOOK_MESSAGE_OUT, eventcmd_hmo_hid);
-+ hk_del_handler(HOOK_STATUS_CHANGE, eventcmd_hsc_hid);
-+ hk_del_handler(HOOK_UNREAD_LIST_CHANGE, eventcmd_hulc_hid);
-+}
-+
-+/* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modularize-extcmd.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,386 @@
+Convert extcmd into a module
+
+diff -r 27d19b6bc194 mcabber/configure.ac
+--- a/mcabber/configure.ac Mon Apr 30 23:36:55 2012 +0300
++++ b/mcabber/configure.ac Mon Apr 30 23:37:25 2012 +0300
+@@ -283,6 +283,7 @@
+ modules/beep/Makefile
+ modules/xttitle/Makefile
+ modules/fifo/Makefile
++ modules/eventcmd/Makefile
+ modules/urlregex/Makefile
+ doc/Makefile
+ doc/guide/Makefile
+diff -r 27d19b6bc194 mcabber/mcabber/Makefile.am
+--- a/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:55 2012 +0300
++++ b/mcabber/mcabber/Makefile.am Mon Apr 30 23:37:25 2012 +0300
+@@ -7,7 +7,7 @@
+ xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
+ xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
+ xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
+- caps.c caps.h help.c help.h extcmd.c extcmd.h
++ caps.c caps.h help.c help.h
+
+ if OTR
+ mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
+@@ -55,7 +55,7 @@
+
+ mcabberincludedir = $(includedir)/mcabber
+ else
+-mcabber_SOURCES += fifo.c fifo.h
++mcabber_SOURCES += fifo.c fifo.h extcmd.c extcmd.h
+ endif
+
+ #SUBDIRS =
+diff -r 27d19b6bc194 mcabber/mcabber/hooks.c
+--- a/mcabber/mcabber/hooks.c Mon Apr 30 23:36:55 2012 +0300
++++ b/mcabber/mcabber/hooks.c Mon Apr 30 23:37:25 2012 +0300
+@@ -36,7 +36,10 @@
+ #include "utf8.h"
+ #include "commands.h"
+ #include "main.h"
+-#include "extcmd.h"
++
++#ifndef MODULES_ENABLE
++# include "extcmd.h"
++#endif
+
+ #ifdef MODULES_ENABLE
+ #include <glib.h>
+@@ -189,7 +192,9 @@
+ int is_groupchat = FALSE; // groupchat message
+ int is_room = FALSE; // window is a room window
+ int log_muc_conf = FALSE;
++#ifndef MODULES_ENABLE
+ int active_window = FALSE;
++#endif
+ int message_flags = 0;
+ guint rtype = ROSTER_TYPE_USER;
+ char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
+@@ -397,6 +402,7 @@
+ }
+ #endif
+
++#ifndef MODULES_ENABLE
+ if (settings_opt_get_int("events_ignore_active_window") &&
+ current_buddy && scr_get_chatmode()) {
+ gpointer bud = BUDDATA(current_buddy);
+@@ -415,6 +421,7 @@
+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
+ is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED,
+ wmsg);
++#endif
+
+ // Beep, if enabled:
+ // - if it's a private message
+@@ -491,8 +498,10 @@
+ }
+ #endif
+
++#ifndef MODULES_ENABLE
+ // External command
+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
++#endif
+
+ g_free(bmsg);
+ g_free(mmsg);
+@@ -578,9 +587,11 @@
+ }
+ #endif
+
++#ifndef MODULES_ENABLE
+ // External command
+ newstatus[0] = toupper(newstatus[0]);
+ hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, newstatus, status_msg);
++#endif
+ }
+
+ void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
+@@ -704,11 +715,13 @@
+ prev_muc_unread = muc_unread;
+ prev_muc_attention = muc_attention;
+
++#ifndef MODULES_ENABLE
+ /* Call external command */
+ str_unread = g_strdup_printf("%u %u %u %u", unread_count, attention_count,
+ muc_unread, muc_attention);
+ hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str_unread, NULL);
+ g_free(str_unread);
++#endif
+ }
+
+ // hk_presence_subscription_request(jid, message)
+diff -r 27d19b6bc194 mcabber/mcabber/main.c
+--- a/mcabber/mcabber/main.c Mon Apr 30 23:36:55 2012 +0300
++++ b/mcabber/mcabber/main.c Mon Apr 30 23:37:25 2012 +0300
+@@ -44,7 +44,10 @@
+ #include "xmpp.h"
+ #include "help.h"
+ #include "events.h"
+-#include "extcmd.h"
++
++#ifndef MODULES_ENABLE
++# include "extcmd.h"
++#endif
+
+ #ifndef MODULES_ENABLE
+ # include "fifo.h"
+@@ -443,9 +446,11 @@
+ }
+ #endif
+
++#ifndef MODULES_ENABLE
+ optstring = settings_opt_get("events_command");
+ if (optstring)
+ hk_ext_cmd_init(optstring);
++#endif
+
+ optstring = settings_opt_get("roster_display_filter");
+ if (optstring)
+diff -r 27d19b6bc194 mcabber/modules/Makefile.am
+--- a/mcabber/modules/Makefile.am Mon Apr 30 23:36:55 2012 +0300
++++ b/mcabber/modules/Makefile.am Mon Apr 30 23:37:25 2012 +0300
+@@ -1,1 +1,1 @@
+-SUBDIRS = beep xttitle fifo urlregex
++SUBDIRS = beep xttitle fifo eventcmd urlregex
+diff -r 27d19b6bc194 mcabber/modules/eventcmd/Makefile.am
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/modules/eventcmd/Makefile.am Mon Apr 30 23:37:25 2012 +0300
+@@ -0,0 +1,12 @@
++
++if INSTALL_HEADERS
++pkglib_LTLIBRARIES = libeventcmd.la
++libeventcmd_la_SOURCES = eventcmd.c $(top_srcdir)/mcabber/extcmd.c $(top_srcdir)/mcabber/extcmd.h
++libeventcmd_la_LDFLAGS = -module -avoid-version -shared
++
++LDADD = $(GLIB_LIBS)
++AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(LOUDMOUTH_CFLAGS) \
++ $(GPGME_CFLAGS) $(LIBOTR_CFLAGS) \
++ $(ENCHANT_CFLAGS)
++endif
++
+diff -r 27d19b6bc194 mcabber/modules/eventcmd/eventcmd.c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/modules/eventcmd/eventcmd.c Mon Apr 30 23:37:25 2012 +0300
+@@ -0,0 +1,221 @@
++
++/* Copyright 2009 Myhailo Danylenko
++ *
++ * This file is part of mcabber
++ *
++ * mcabber 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.
++ *
++ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <glib.h>
++#include <gmodule.h>
++#include <string.h>
++#include <stdlib.h>
++#include <ctype.h> // tolower
++#include <unistd.h>
++
++#include <mcabber/settings.h>
++#include <mcabber/hooks.h>
++#include <mcabber/roster.h>
++#include <mcabber/utils.h>
++#include <mcabber/logprint.h>
++#include <mcabber/extcmd.h>
++#include <mcabber/modules.h>
++#include <mcabber/screen.h>
++#include <mcabber/config.h>
++
++static void ecm_init(void);
++static void ecm_uninit(void);
++
++module_info_t info_eventcmd = {
++ .branch = MCABBER_BRANCH,
++ .api = MCABBER_API_VERSION,
++ .version = MCABBER_VERSION,
++ .requires = NULL,
++ .init = ecm_init,
++ .uninit = ecm_uninit,
++ .description = "External command execution on events\n"
++ "Recognizes options events_command (required), events_ignore_active_window, "
++ "event_log_files, event_log_dir, eventcmd_use_nickname, eventcmd_checkstatus.",
++ .next = NULL,
++};
++
++static guint eventcmd_hpmi_hid = 0;
++static guint eventcmd_hmo_hid = 0;
++static guint eventcmd_hsc_hid = 0;
++static guint eventcmd_hulc_hid = 0;
++
++static guint eventcmd_hpmih(const gchar *name, hk_arg_t *args,
++ gpointer userdata)
++{
++ gboolean active_window = FALSE;
++ gboolean is_groupchat = FALSE;
++ gboolean timestamp = FALSE;
++ const char *bjid = NULL;
++ hk_arg_t *arg = args;
++ const char *wmsg = NULL;
++
++ while (arg->name) {
++ if (!strcmp(arg->name, "jid"))
++ bjid = arg->value;
++ else if (!strcmp(arg->name, "groupchat"))
++ is_groupchat = strcmp(arg->value, "true") ? FALSE : TRUE;
++ else if (!strcmp(arg->name, "delayed") && *(arg->value))
++ timestamp = TRUE;
++ else if (!strcmp(arg->name, "message"))
++ wmsg = arg->value;
++ arg++;
++ }
++
++ if (!bjid) {
++ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: post-message-in: No jid found.");
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++ }
++
++ if (settings_opt_get_int("events_ignore_active_window") &&
++ current_buddy && scr_get_chatmode()) {
++ gpointer bud = BUDDATA(current_buddy);
++ if (bud) {
++ const char *cjid = buddy_getjid(bud);
++ if (cjid && !strcasecmp(cjid, bjid))
++ active_window = TRUE;
++ }
++ }
++
++ if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
++ is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED, wmsg);
++
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++}
++
++static guint eventcmd_hmoh(const gchar *name, hk_arg_t *args, gpointer userdata)
++{
++ const char *bjid = NULL;
++ hk_arg_t *arg = args;
++
++ while (arg->name) {
++ if (!strcmp(arg->name, "jid")) {
++ bjid = arg->value;
++ break;
++ }
++ arg++;
++ }
++
++ if (!bjid) {
++ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: message-out: No jid found.");
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++ }
++
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
++
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++}
++
++static guint eventcmd_hsch(const gchar *name, hk_arg_t *args, gpointer userdata)
++{
++ const char *bjid = NULL;
++ char status[2] = { '?', '\0' };
++ hk_arg_t *arg = args;
++ const char *message = NULL;
++
++ while (arg->name) {
++ if (!strcmp(arg->name, "jid"))
++ bjid = arg->value;
++ else if (!strcmp(arg->name, "new_status"))
++ status[0] = toupper (arg->value[0]);
++ else if (!strcmp(arg->name, "message") && arg->value && *(arg->value))
++ message = arg->value;
++ arg++;
++ }
++
++ if (!bjid) {
++ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: status-change: No jid found.");
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++ }
++
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, status, message);
++
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++}
++
++static guint eventcmd_hulch(const gchar *name, hk_arg_t *args, gpointer userdata)
++{
++ hk_arg_t *arg = args;
++ const gchar *unread = NULL;
++ const gchar *attention = NULL;
++ const gchar *muc_unread = NULL;
++ const gchar *muc_attention = NULL;
++
++ while (arg->name) {
++ if (!strcmp(arg->name, "unread"))
++ unread = arg->value;
++ else if (!strcmp(arg->name, "attention"))
++ attention = arg->value;
++ else if (!strcmp(arg->name, "muc_unread"))
++ muc_unread = arg->value;
++ else if (!strcmp(arg->name, "muc_attention"))
++ muc_attention = arg->value;
++ arg++;
++ }
++
++ if (!unread || !attention || !muc_unread || !muc_attention) {
++ scr_LogPrint(LPRINT_LOGNORM, "eventcmd: unread-list-change: Missing parameter.");
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++ }
++
++ {
++ gchar *str = g_strdup_printf("%s %s %s %s", unread, attention, muc_unread,
++ muc_attention);
++ hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str, NULL);
++ g_free(str);
++ }
++
++ return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
++}
++
++const gchar *g_module_check_init(GModule *module)
++{
++ const char *command = settings_opt_get("events_command");
++ if (!command)
++ return "events_command not set";
++ hk_ext_cmd_init(command);
++ return NULL;
++}
++
++void g_module_unload(GModule *module)
++{
++ hk_ext_cmd_init(NULL);
++}
++
++static void ecm_init(void)
++{
++ eventcmd_hpmi_hid = hk_add_handler(eventcmd_hpmih, HOOK_POST_MESSAGE_IN,
++ G_PRIORITY_LOW, NULL);
++ eventcmd_hmo_hid = hk_add_handler(eventcmd_hmoh, HOOK_MESSAGE_OUT,
++ G_PRIORITY_LOW, NULL);
++ eventcmd_hsc_hid = hk_add_handler(eventcmd_hsch, HOOK_STATUS_CHANGE,
++ G_PRIORITY_LOW, NULL);
++ eventcmd_hulc_hid = hk_add_handler(eventcmd_hulch, HOOK_UNREAD_LIST_CHANGE,
++ G_PRIORITY_LOW, NULL);
++
++}
++
++static void ecm_uninit(void)
++{
++ hk_del_handler(HOOK_POST_MESSAGE_IN, eventcmd_hpmi_hid);
++ hk_del_handler(HOOK_MESSAGE_OUT, eventcmd_hmo_hid);
++ hk_del_handler(HOOK_STATUS_CHANGE, eventcmd_hsc_hid);
++ hk_del_handler(HOOK_UNREAD_LIST_CHANGE, eventcmd_hulc_hid);
++}
++
++/* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
--- a/roster-state-colors Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-Change roster item colors on chatstate change
-
-diff -r 7222f7b386cb mcabber/contrib/themes/light.txt
---- a/mcabber/contrib/themes/light.txt Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/contrib/themes/light.txt Mon Apr 30 23:38:36 2012 +0300
-@@ -13,6 +13,5 @@
- set color_bgrostersel = green
- set color_rostersel = yellow
- set color_rosterselmsg = red
--set color_rosternewmsg = red
-
- # vim:set ft=conf:
-diff -r 7222f7b386cb mcabber/contrib/themes/light2.txt
---- a/mcabber/contrib/themes/light2.txt Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/contrib/themes/light2.txt Mon Apr 30 23:38:36 2012 +0300
-@@ -13,6 +13,5 @@
- set color_bgrostersel = black
- set color_rostersel = brightgreen
- set color_rosterselmsg = red
--set color_rosternewmsg = red
-
- # vim:set ft=conf:
-diff -r 7222f7b386cb mcabber/contrib/themes/oliver.txt
---- a/mcabber/contrib/themes/oliver.txt Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/contrib/themes/oliver.txt Mon Apr 30 23:38:36 2012 +0300
-@@ -13,6 +13,5 @@
- set color_bgrostersel = yellow
- set color_rostersel = blue
- set color_rosterselmsg = red
--set color_rosternewmsg = red
-
- # vim:set ft=conf:
-diff -r 7222f7b386cb mcabber/doc/help/en/hlp_color.txt
---- a/mcabber/doc/help/en/hlp_color.txt Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/doc/help/en/hlp_color.txt Mon Apr 30 23:38:36 2012 +0300
-@@ -11,6 +11,7 @@
- Set a color rule (or overwrite, if it already exists). The status is string containing all statuses the roster item can have for the rule to match, or * if any status is OK. Wildcard is the file-matching wildcard that will be applied to JID. Color is the wanted color.
- If color is -, the rule is removed.
- If more than one rule matches, the color from the last created (not overwritten) is used.
-+ Also mask can contain symbols '!', '#', '+' and '.', that represent buddies with urgent condition, with new messages, that are typing and that have ceased typing. These symbols have higher priority than status ones.
- /color mucnick nick (color|-)
- Marks the nick to be colored by given color. If a MUC has colored nicks, this one will be used. If color is -, the color is marked as chosen automatically, which means it will not be used in 'preset' coloring mode, but will stay the same in 'on' coloring mode.
- /color muc (jid|.|*) [on|preset|off|-]
-diff -r 7222f7b386cb mcabber/doc/help/uk/hlp_color.txt
---- a/mcabber/doc/help/uk/hlp_color.txt Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/doc/help/uk/hlp_color.txt Mon Apr 30 23:38:36 2012 +0300
-@@ -11,6 +11,7 @@
- Додає нове чи змінює існуюче правило кольору. "Статус" складений з літер статусу, до яких докладається це правило (наприклад "n_d?"). Зірочка ("*") відповідає будь-якому статусу. "Маска" подібна до файлової маски й докладається до jid. Правило встановлює колір контактів, що йому відповідають.
- Щоб вилучити правило, вкажіть колір "-".
- Коли є декілька правил, що відповідають контакту, спрацьовує останнє додане (_не_ змінене).
-+ Також рядок статусів може містити символи '!', '#', '+' та '.', що відповідають користувачам, які відповідно мають негайні події, нові повідомлення, друкують та друкували але спинилися. Ці символи мають більший пріоритет ніж символи статусів.
- /color muc (jid|.|*) [on|preset|off|-]
- Встановлює режим забарвлення прізвиськ у чаті. Якщо замість jid вказано * режим докладається до всіх чатів, для яких не визначено особистого правила (з вказанням jid).
- У режимі on забарвлюються усі прізвиська, у режимі preset - лише ті, для яких колір встановлено командою /color mucnick, а режим off вимикає забарвлення прізвиськ. Режим - прибирає з вказаних jid особисті режими забарвлення. Тоді до них докладається глобальний. Глобальний режим прибрати не можна.
-diff -r 7222f7b386cb mcabber/mcabber/screen.c
---- a/mcabber/mcabber/screen.c Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/mcabber/screen.c Mon Apr 30 23:38:36 2012 +0300
-@@ -435,7 +435,7 @@
- "roster",
- "rostersel",
- "rosterselmsg",
-- "rosternewmsg",
-+ "rosternewmsg", /* not removing this to not break binary compatibility with mainstream */
- "info",
- "msgin",
- "readmark",
-@@ -504,10 +504,6 @@
- init_pair(i+1, ((color) ? find_color(color) : COLOR_RED),
- find_color(backselected));
- break;
-- case COLOR_ROSTERNMSG:
-- init_pair(i+1, ((color) ? find_color(color) : COLOR_RED),
-- find_color(background));
-- break;
- case COLOR_INFO:
- init_pair(i+1, ((color) ? find_color(color) : COLOR_WHITE),
- find_color(background));
-@@ -805,6 +801,21 @@
- settings_set_guard("color_rosterselmsg", scr_color_guard);
- settings_set_guard("color_rosternewmsg", scr_color_guard);
-
-+ { // Add default rule only if user has not defined one already
-+ GSList *rel;
-+ gboolean found = FALSE;
-+ for (rel = rostercolrules; rel; rel = rel->next) {
-+ rostercolor *rule = rel->data;
-+ if ((strchr(rule->status, '#') || strchr(rule->status, '!'))
-+ && (!strcmp(rule->wildcard, "*"))) {
-+ found = TRUE;
-+ break;
-+ }
-+ }
-+ if (!found)
-+ scr_roster_color("#!", "*", "red");
-+ }
-+
- getmaxyx(stdscr, maxY, maxX);
- Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
- // Note scr_draw_main_window() should be called early after scr_init_curses()
-@@ -2071,6 +2082,10 @@
- // for unfolded groups.
- if (ismsg && (!isgrp || ishid)) {
- pending = '#';
-+ // Attention sign?
-+ if ((ismuc && isurg >= ui_attn_sign_prio_level_muc) ||
-+ (!ismuc && isurg >= ui_attn_sign_prio_level))
-+ pending = '!';
- }
-
- if (ismuc) {
-@@ -2094,24 +2109,22 @@
- for (n = 0; n < maxx; n++)
- waddch(rosterWnd, ' ');
- } else {
-- if (pending == '#')
-- wattrset(rosterWnd, get_color(COLOR_ROSTERNMSG));
-- else {
-- int color = get_color(COLOR_ROSTER);
-- if ((!isspe) && (!isgrp)) { // Look for color rules
-- GSList *head;
-- const char *bjid = buddy_getjid(BUDDATA(buddy));
-- for (head = rostercolrules; head; head = g_slist_next(head)) {
-- rostercolor *rc = head->data;
-- if (g_pattern_match_string(rc->compiled, bjid) &&
-- (!strcmp("*", rc->status) || strchr(rc->status, status))) {
-- color = compose_color(rc->color);
-- break;
-- }
-+ int color = get_color(COLOR_ROSTER);
-+ if ((!isspe) && (!isgrp)) { // Look for color rules
-+ GSList *head;
-+ const char *jid = buddy_getjid(BUDDATA(buddy));
-+ for (head = rostercolrules; head; head = g_slist_next(head)) {
-+ rostercolor *rc = head->data;
-+ if (g_pattern_match_string(rc->compiled, jid) &&
-+ (!strcmp("*", rc->status) ||
-+ strchr(rc->status, pending) ||
-+ strchr(rc->status, status))) {
-+ color = compose_color(rc->color);
-+ break;
- }
- }
-- wattrset(rosterWnd, color);
- }
-+ wattrset(rosterWnd, color);
- }
-
- if (Roster_Width > prefix_length)
-@@ -2119,13 +2132,6 @@
- else
- name[0] = 0;
-
-- if (pending == '#') {
-- // Attention sign?
-- if ((ismuc && isurg >= ui_attn_sign_prio_level_muc) ||
-- (!ismuc && isurg >= ui_attn_sign_prio_level))
-- pending = '!';
-- }
--
- if (isgrp) {
- if (ishid) {
- int group_count = 0;
-diff -r 7222f7b386cb mcabber/mcabber/screen.h
---- a/mcabber/mcabber/screen.h Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/mcabber/screen.h Mon Apr 30 23:38:36 2012 +0300
-@@ -46,7 +46,7 @@
- COLOR_ROSTER,
- COLOR_ROSTERSEL,
- COLOR_ROSTERSELNMSG,
-- COLOR_ROSTERNMSG,
-+ COLOR_ROSTERNMSG, /* not removing this to not break binary compatibility with mainstream */
- COLOR_INFO,
- COLOR_MSGIN,
- COLOR_READMARK,
-diff -r 7222f7b386cb mcabber/mcabberrc.example
---- a/mcabber/mcabberrc.example Mon Apr 30 23:38:20 2012 +0300
-+++ b/mcabber/mcabberrc.example Mon Apr 30 23:38:36 2012 +0300
-@@ -395,7 +395,6 @@
- # bgrostersel: background color of the selected roster item
- # rostersel: text color of the selected roster item
- # rosterselmsg:text color of the selected roster item, if there is a new msg
--# rosternewmsg: text color of items with unread messages
- #
- #set color_background = black
- #set color_general = white
-@@ -409,15 +408,24 @@
- #set color_bgrostersel = cyan
- #set color_rostersel = blue
- #set color_rosterselmsg = red
--#set color_rosternewmsg = red
- #set color_readmark = red
-
--# You can color roster items by their status and JID. For example, to have
--# all roster items white, just all contacts from jabber.org that are away,
--# not available or do not disturb yellow, you do this:
-+# You can color roster items by their status, state and JID. For example,
-+# to have all roster items white, just all contacts from jabber.org that
-+# are away, not available or do not disturb yellow, you do this:
- #
- #color roster * * white
- #color roster adn *@jabber.org yellow
-+#
-+# You can specify '!', '#', '+' and '.' in status mask, they will take
-+# precedence over status letters and will select buddies accordingly with
-+# urgent condition, with new messages, that are typing and that ceased
-+# typing. By default there exists one rule:
-+#
-+#color roster !# * red
-+#
-+# However, if you define a rule with either '!' or '#', this default rule
-+# will not be added.
-
- # You can let mcabber color nicks in MUC.
- # These colors will by used automatically:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/roster-state-colors.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,214 @@
+Change roster item colors on chatstate change
+
+diff -r 7222f7b386cb mcabber/contrib/themes/light.txt
+--- a/mcabber/contrib/themes/light.txt Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/contrib/themes/light.txt Mon Apr 30 23:38:36 2012 +0300
+@@ -13,6 +13,5 @@
+ set color_bgrostersel = green
+ set color_rostersel = yellow
+ set color_rosterselmsg = red
+-set color_rosternewmsg = red
+
+ # vim:set ft=conf:
+diff -r 7222f7b386cb mcabber/contrib/themes/light2.txt
+--- a/mcabber/contrib/themes/light2.txt Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/contrib/themes/light2.txt Mon Apr 30 23:38:36 2012 +0300
+@@ -13,6 +13,5 @@
+ set color_bgrostersel = black
+ set color_rostersel = brightgreen
+ set color_rosterselmsg = red
+-set color_rosternewmsg = red
+
+ # vim:set ft=conf:
+diff -r 7222f7b386cb mcabber/contrib/themes/oliver.txt
+--- a/mcabber/contrib/themes/oliver.txt Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/contrib/themes/oliver.txt Mon Apr 30 23:38:36 2012 +0300
+@@ -13,6 +13,5 @@
+ set color_bgrostersel = yellow
+ set color_rostersel = blue
+ set color_rosterselmsg = red
+-set color_rosternewmsg = red
+
+ # vim:set ft=conf:
+diff -r 7222f7b386cb mcabber/doc/help/en/hlp_color.txt
+--- a/mcabber/doc/help/en/hlp_color.txt Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/doc/help/en/hlp_color.txt Mon Apr 30 23:38:36 2012 +0300
+@@ -11,6 +11,7 @@
+ Set a color rule (or overwrite, if it already exists). The status is string containing all statuses the roster item can have for the rule to match, or * if any status is OK. Wildcard is the file-matching wildcard that will be applied to JID. Color is the wanted color.
+ If color is -, the rule is removed.
+ If more than one rule matches, the color from the last created (not overwritten) is used.
++ Also mask can contain symbols '!', '#', '+' and '.', that represent buddies with urgent condition, with new messages, that are typing and that have ceased typing. These symbols have higher priority than status ones.
+ /color mucnick nick (color|-)
+ Marks the nick to be colored by given color. If a MUC has colored nicks, this one will be used. If color is -, the color is marked as chosen automatically, which means it will not be used in 'preset' coloring mode, but will stay the same in 'on' coloring mode.
+ /color muc (jid|.|*) [on|preset|off|-]
+diff -r 7222f7b386cb mcabber/doc/help/uk/hlp_color.txt
+--- a/mcabber/doc/help/uk/hlp_color.txt Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/doc/help/uk/hlp_color.txt Mon Apr 30 23:38:36 2012 +0300
+@@ -11,6 +11,7 @@
+ Додає нове чи змінює існуюче правило кольору. "Статус" складений з літер статусу, до яких докладається це правило (наприклад "n_d?"). Зірочка ("*") відповідає будь-якому статусу. "Маска" подібна до файлової маски й докладається до jid. Правило встановлює колір контактів, що йому відповідають.
+ Щоб вилучити правило, вкажіть колір "-".
+ Коли є декілька правил, що відповідають контакту, спрацьовує останнє додане (_не_ змінене).
++ Також рядок статусів може містити символи '!', '#', '+' та '.', що відповідають користувачам, які відповідно мають негайні події, нові повідомлення, друкують та друкували але спинилися. Ці символи мають більший пріоритет ніж символи статусів.
+ /color muc (jid|.|*) [on|preset|off|-]
+ Встановлює режим забарвлення прізвиськ у чаті. Якщо замість jid вказано * режим докладається до всіх чатів, для яких не визначено особистого правила (з вказанням jid).
+ У режимі on забарвлюються усі прізвиська, у режимі preset - лише ті, для яких колір встановлено командою /color mucnick, а режим off вимикає забарвлення прізвиськ. Режим - прибирає з вказаних jid особисті режими забарвлення. Тоді до них докладається глобальний. Глобальний режим прибрати не можна.
+diff -r 7222f7b386cb mcabber/mcabber/screen.c
+--- a/mcabber/mcabber/screen.c Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/mcabber/screen.c Mon Apr 30 23:38:36 2012 +0300
+@@ -435,7 +435,7 @@
+ "roster",
+ "rostersel",
+ "rosterselmsg",
+- "rosternewmsg",
++ "rosternewmsg", /* not removing this to not break binary compatibility with mainstream */
+ "info",
+ "msgin",
+ "readmark",
+@@ -504,10 +504,6 @@
+ init_pair(i+1, ((color) ? find_color(color) : COLOR_RED),
+ find_color(backselected));
+ break;
+- case COLOR_ROSTERNMSG:
+- init_pair(i+1, ((color) ? find_color(color) : COLOR_RED),
+- find_color(background));
+- break;
+ case COLOR_INFO:
+ init_pair(i+1, ((color) ? find_color(color) : COLOR_WHITE),
+ find_color(background));
+@@ -805,6 +801,21 @@
+ settings_set_guard("color_rosterselmsg", scr_color_guard);
+ settings_set_guard("color_rosternewmsg", scr_color_guard);
+
++ { // Add default rule only if user has not defined one already
++ GSList *rel;
++ gboolean found = FALSE;
++ for (rel = rostercolrules; rel; rel = rel->next) {
++ rostercolor *rule = rel->data;
++ if ((strchr(rule->status, '#') || strchr(rule->status, '!'))
++ && (!strcmp(rule->wildcard, "*"))) {
++ found = TRUE;
++ break;
++ }
++ }
++ if (!found)
++ scr_roster_color("#!", "*", "red");
++ }
++
+ getmaxyx(stdscr, maxY, maxX);
+ Log_Win_Height = DEFAULT_LOG_WIN_HEIGHT;
+ // Note scr_draw_main_window() should be called early after scr_init_curses()
+@@ -2071,6 +2082,10 @@
+ // for unfolded groups.
+ if (ismsg && (!isgrp || ishid)) {
+ pending = '#';
++ // Attention sign?
++ if ((ismuc && isurg >= ui_attn_sign_prio_level_muc) ||
++ (!ismuc && isurg >= ui_attn_sign_prio_level))
++ pending = '!';
+ }
+
+ if (ismuc) {
+@@ -2094,24 +2109,22 @@
+ for (n = 0; n < maxx; n++)
+ waddch(rosterWnd, ' ');
+ } else {
+- if (pending == '#')
+- wattrset(rosterWnd, get_color(COLOR_ROSTERNMSG));
+- else {
+- int color = get_color(COLOR_ROSTER);
+- if ((!isspe) && (!isgrp)) { // Look for color rules
+- GSList *head;
+- const char *bjid = buddy_getjid(BUDDATA(buddy));
+- for (head = rostercolrules; head; head = g_slist_next(head)) {
+- rostercolor *rc = head->data;
+- if (g_pattern_match_string(rc->compiled, bjid) &&
+- (!strcmp("*", rc->status) || strchr(rc->status, status))) {
+- color = compose_color(rc->color);
+- break;
+- }
++ int color = get_color(COLOR_ROSTER);
++ if ((!isspe) && (!isgrp)) { // Look for color rules
++ GSList *head;
++ const char *jid = buddy_getjid(BUDDATA(buddy));
++ for (head = rostercolrules; head; head = g_slist_next(head)) {
++ rostercolor *rc = head->data;
++ if (g_pattern_match_string(rc->compiled, jid) &&
++ (!strcmp("*", rc->status) ||
++ strchr(rc->status, pending) ||
++ strchr(rc->status, status))) {
++ color = compose_color(rc->color);
++ break;
+ }
+ }
+- wattrset(rosterWnd, color);
+ }
++ wattrset(rosterWnd, color);
+ }
+
+ if (Roster_Width > prefix_length)
+@@ -2119,13 +2132,6 @@
+ else
+ name[0] = 0;
+
+- if (pending == '#') {
+- // Attention sign?
+- if ((ismuc && isurg >= ui_attn_sign_prio_level_muc) ||
+- (!ismuc && isurg >= ui_attn_sign_prio_level))
+- pending = '!';
+- }
+-
+ if (isgrp) {
+ if (ishid) {
+ int group_count = 0;
+diff -r 7222f7b386cb mcabber/mcabber/screen.h
+--- a/mcabber/mcabber/screen.h Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/mcabber/screen.h Mon Apr 30 23:38:36 2012 +0300
+@@ -46,7 +46,7 @@
+ COLOR_ROSTER,
+ COLOR_ROSTERSEL,
+ COLOR_ROSTERSELNMSG,
+- COLOR_ROSTERNMSG,
++ COLOR_ROSTERNMSG, /* not removing this to not break binary compatibility with mainstream */
+ COLOR_INFO,
+ COLOR_MSGIN,
+ COLOR_READMARK,
+diff -r 7222f7b386cb mcabber/mcabberrc.example
+--- a/mcabber/mcabberrc.example Mon Apr 30 23:38:20 2012 +0300
++++ b/mcabber/mcabberrc.example Mon Apr 30 23:38:36 2012 +0300
+@@ -395,7 +395,6 @@
+ # bgrostersel: background color of the selected roster item
+ # rostersel: text color of the selected roster item
+ # rosterselmsg:text color of the selected roster item, if there is a new msg
+-# rosternewmsg: text color of items with unread messages
+ #
+ #set color_background = black
+ #set color_general = white
+@@ -409,15 +408,24 @@
+ #set color_bgrostersel = cyan
+ #set color_rostersel = blue
+ #set color_rosterselmsg = red
+-#set color_rosternewmsg = red
+ #set color_readmark = red
+
+-# You can color roster items by their status and JID. For example, to have
+-# all roster items white, just all contacts from jabber.org that are away,
+-# not available or do not disturb yellow, you do this:
++# You can color roster items by their status, state and JID. For example,
++# to have all roster items white, just all contacts from jabber.org that
++# are away, not available or do not disturb yellow, you do this:
+ #
+ #color roster * * white
+ #color roster adn *@jabber.org yellow
++#
++# You can specify '!', '#', '+' and '.' in status mask, they will take
++# precedence over status letters and will select buddies accordingly with
++# urgent condition, with new messages, that are typing and that ceased
++# typing. By default there exists one rule:
++#
++#color roster !# * red
++#
++# However, if you define a rule with either '!' or '#', this default rule
++# will not be added.
+
+ # You can let mcabber color nicks in MUC.
+ # These colors will by used automatically:
--- a/separate-extcmd Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,459 +0,0 @@
-Move extcmd code from hooks
-
-diff -r 64da54766f99 mcabber/mcabber/Makefile.am
---- a/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:31 2012 +0300
-+++ b/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:55 2012 +0300
-@@ -7,7 +7,7 @@
- xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
- xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
- xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
-- caps.c caps.h help.c help.h
-+ caps.c caps.h help.c help.h extcmd.c extcmd.h
-
- if OTR
- mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
-@@ -42,6 +42,7 @@
- xmpp_iq.h xmpp_iqrequest.h \
- xmpp_muc.h xmpp_s10n.h \
- caps.h fifo.h help.h modules.h api.h \
-+ extcmd.h \
- $(top_srcdir)/include/config.h
-
- if OTR
-diff -r 64da54766f99 mcabber/mcabber/extcmd.c
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/extcmd.c Mon Apr 30 23:36:55 2012 +0300
-@@ -0,0 +1,121 @@
-+/*
-+ * extcmd.c -- External event handler command
-+ *
-+ * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
-+ *
-+ * This program 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.
-+ *
-+ * This program 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 program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-+ * USA
-+ */
-+
-+#include <stdlib.h>
-+#include <string.h>
-+#include <sys/types.h>
-+#include <unistd.h>
-+#include <glib.h>
-+
-+#include "screen.h"
-+#include "roster.h"
-+#include "settings.h"
-+#include "utils.h"
-+#include "utf8.h"
-+
-+static char *extcmd = NULL;
-+
-+// hk_ext_cmd_init()
-+// Initialize external command variable.
-+// Can be called with parameter NULL to reset and free memory
-+void hk_ext_cmd_init(const char *command)
-+{
-+ if (extcmd) {
-+ g_free(extcmd);
-+ extcmd = NULL;
-+ }
-+ if (command)
-+ extcmd = expand_filename(command);
-+}
-+
-+// hk_ext_cmd()
-+// Launch an external command (process) for the given event.
-+// For now, data should be NULL.
-+void hk_ext_cmd(const char *name, const char *arg_type, const char *arg_info, const char *data)
-+{
-+ pid_t pid;
-+ char *arg_data = NULL;
-+ char *datafname = NULL;
-+
-+ if (!arg_type || !arg_info) return;
-+
-+ if (*name && settings_opt_get_int("eventcmd_use_nickname"))
-+ name = roster_getname(name);
-+
-+ if (data && settings_opt_get_int("event_log_files")) {
-+ int fd;
-+ const char *prefix;
-+ char *prefix_xp = NULL;
-+ char *data_locale;
-+
-+ data_locale = from_utf8(data);
-+ prefix = settings_opt_get("event_log_dir");
-+ if (prefix)
-+ prefix = prefix_xp = expand_filename(prefix);
-+ else
-+ prefix = ut_get_tmpdir();
-+ datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
-+ g_free(prefix_xp);
-+
-+ // XXX Some old systems may require us to set umask first.
-+ fd = mkstemp(datafname);
-+ if (fd == -1) {
-+ g_free(datafname);
-+ datafname = NULL;
-+ scr_LogPrint(LPRINT_LOGNORM,
-+ "Unable to create temp file for external command.");
-+ } else {
-+ size_t data_locale_len = strlen(data_locale);
-+ ssize_t a = write(fd, data_locale, data_locale_len);
-+ ssize_t b = write(fd, "\n", 1);
-+ if ((size_t)a != data_locale_len || b != 1) {
-+ g_free(datafname);
-+ datafname = NULL;
-+ scr_LogPrint(LPRINT_LOGNORM,
-+ "Unable to write to temp file for external command.");
-+ }
-+ close(fd);
-+ arg_data = datafname;
-+ }
-+ g_free(data_locale);
-+ }
-+
-+ if ((pid=fork()) == -1) {
-+ scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
-+ g_free(datafname);
-+ return;
-+ }
-+
-+ if (pid == 0) { // child
-+ // Close standard file descriptors
-+ close(STDIN_FILENO);
-+ close(STDOUT_FILENO);
-+ close(STDERR_FILENO);
-+ if (execl(extcmd, extcmd, arg_type, arg_info, name, arg_data,
-+ (char *)NULL) == -1) {
-+ // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
-+ exit(1);
-+ }
-+ }
-+ g_free(datafname);
-+}
-+
-+/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
-diff -r 64da54766f99 mcabber/mcabber/extcmd.h
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/extcmd.h Mon Apr 30 23:36:55 2012 +0300
-@@ -0,0 +1,15 @@
-+#ifndef __MCABBER_EXTCMD_H__
-+#define __MCABBER_EXTCMD_H__ 1
-+
-+#define EXT_CMD_TYPE_MESSAGE "MSG"
-+#define EXT_CMD_TYPE_STATUS "STATUS"
-+#define EXT_CMD_TYPE_UNREAD "UNREAD"
-+
-+#define EXT_CMD_INFO_RECEIVED "IN"
-+#define EXT_CMD_INFO_SENT "OUT"
-+#define EXT_CMD_INFO_GROUPCHAT "MUC"
-+
-+void hk_ext_cmd_init(const char *command);
-+void hk_ext_cmd(const char *bjid, const char *type, const char *info, const char *data);
-+
-+#endif
-diff -r 64da54766f99 mcabber/mcabber/hooks.c
---- a/mcabber/mcabber/hooks.c Mon Apr 30 23:36:31 2012 +0300
-+++ b/mcabber/mcabber/hooks.c Mon Apr 30 23:36:55 2012 +0300
-@@ -24,6 +24,7 @@
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
-+#include <ctype.h>
-
- #include "hooks.h"
- #include "screen.h"
-@@ -35,6 +36,7 @@
- #include "utf8.h"
- #include "commands.h"
- #include "main.h"
-+#include "extcmd.h"
-
- #ifdef MODULES_ENABLE
- #include <glib.h>
-@@ -177,8 +179,6 @@
- }
- #endif
-
--static char *extcmd;
--
- static const char *COMMAND_ME = "/me ";
-
- void hk_message_in(const char *bjid, const char *resname,
-@@ -195,7 +195,6 @@
- char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
- GSList *roster_usr;
- unsigned mucnicklen = 0;
-- const char *ename = NULL;
- gboolean attention = FALSE, mucprivmsg = FALSE;
- gboolean error_msg_subtype = (type == LM_MESSAGE_SUB_TYPE_ERROR);
- #ifdef MODULES_ENABLE
-@@ -373,19 +372,6 @@
- (!is_room || (is_groupchat && log_muc_conf && !timestamp)))
- hlog_write_message(bjid, timestamp, 0, wmsg);
-
-- if (settings_opt_get_int("events_ignore_active_window") &&
-- current_buddy && scr_get_chatmode()) {
-- gpointer bud = BUDDATA(current_buddy);
-- if (bud) {
-- const char *cjid = buddy_getjid(bud);
-- if (cjid && !strcasecmp(cjid, bjid))
-- active_window = TRUE;
-- }
-- }
--
-- if (settings_opt_get_int("eventcmd_use_nickname"))
-- ename = roster_getname(bjid);
--
- // Display the sender in the log window
- if ((!is_groupchat) && !(message_flags & HBB_PREFIX_ERR) &&
- settings_opt_get_int("log_display_sender")) {
-@@ -411,12 +397,24 @@
- }
- #endif
-
-+ if (settings_opt_get_int("events_ignore_active_window") &&
-+ current_buddy && scr_get_chatmode()) {
-+ gpointer bud = BUDDATA(current_buddy);
-+ if (bud) {
-+ const char *cjid = buddy_getjid(bud);
-+ if (cjid && !strcasecmp(cjid, bjid))
-+ active_window = TRUE;
-+ }
-+ }
-+
- // External command
- // - We do not call hk_ext_cmd() for history lines in MUC
- // - We do call hk_ext_cmd() for private messages in a room
- // - We do call hk_ext_cmd() for messages to the current window
- if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
-- hk_ext_cmd(ename ? ename : bjid, (is_groupchat ? 'G' : 'M'), 'R', wmsg);
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
-+ is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED,
-+ wmsg);
-
- // Beep, if enabled:
- // - if it's a private message
-@@ -494,7 +492,7 @@
- #endif
-
- // External command
-- hk_ext_cmd(bjid, 'M', 'S', NULL);
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
-
- g_free(bmsg);
- g_free(mmsg);
-@@ -509,10 +507,7 @@
- char *bn;
- char *logsmsg;
- const char *rn = (resname ? resname : "");
-- const char *ename = NULL;
--
-- if (settings_opt_get_int("eventcmd_use_nickname"))
-- ename = roster_getname(bjid);
-+ char newstatus[2] = { '?', '\0' };
-
- oldstat = roster_getstatus(bjid, resname);
-
-@@ -564,27 +559,28 @@
- scr_draw_roster();
- hlog_write_status(bjid, timestamp, status, status_msg);
-
-+ newstatus[0] = imstatus2char[status];
-+
- #ifdef MODULES_ENABLE
- {
- char os[2] = " \0";
-- char ns[2] = " \0";
- hk_arg_t args[] = {
- { "jid", bjid },
- { "resource", rn },
- { "old_status", os },
-- { "new_status", ns },
-+ { "new_status", newstatus },
- { "message", status_msg ? status_msg : "" },
- { NULL, NULL },
- };
- os[0] = imstatus2char[oldstat];
-- ns[0] = imstatus2char[status];
-
- hk_run_handlers(HOOK_STATUS_CHANGE, args);
- }
- #endif
-
- // External command
-- hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
-+ newstatus[0] = toupper(newstatus[0]);
-+ hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, newstatus, status_msg);
- }
-
- void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
-@@ -711,7 +707,7 @@
- /* Call external command */
- str_unread = g_strdup_printf("%u %u %u %u", unread_count, attention_count,
- muc_unread, muc_attention);
-- hk_ext_cmd("", 'U', (guchar)MIN(255, unread_count), str_unread);
-+ hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str_unread, NULL);
- g_free(str_unread);
- }
-
-@@ -751,123 +747,4 @@
- return 0;
- }
-
--
--/* External commands */
--
--// hk_ext_cmd_init()
--// Initialize external command variable.
--// Can be called with parameter NULL to reset and free memory.
--void hk_ext_cmd_init(const char *command)
--{
-- if (extcmd) {
-- g_free(extcmd);
-- extcmd = NULL;
-- }
-- if (command)
-- extcmd = expand_filename(command);
--}
--
--// hk_ext_cmd()
--// Launch an external command (process) for the given event.
--// For now, data should be NULL.
--void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data)
--{
-- pid_t pid;
-- const char *arg_type = NULL;
-- const char *arg_info = NULL;
-- const char *arg_data = NULL;
-- char status_str[2];
-- char *datafname = NULL;
--
-- if (!extcmd) return;
--
-- // Prepare arg_* (external command parameters)
-- switch (type) {
-- case 'M': /* Normal message */
-- arg_type = "MSG";
-- if (info == 'R')
-- arg_info = "IN";
-- else if (info == 'S')
-- arg_info = "OUT";
-- break;
-- case 'G': /* Groupchat message */
-- arg_type = "MSG";
-- arg_info = "MUC";
-- break;
-- case 'S': /* Status change */
-- arg_type = "STATUS";
-- if (strchr(imstatus2char, tolower(info))) {
-- status_str[0] = toupper(info);
-- status_str[1] = 0;
-- arg_info = status_str;
-- }
-- break;
-- case 'U': /* Unread buffer count */
-- arg_type = "UNREAD";
-- arg_info = data;
-- break;
-- default:
-- return;
-- }
--
-- if (!arg_type || !arg_info) return;
--
-- if (strchr("MG", type) && data && settings_opt_get_int("event_log_files")) {
-- int fd;
-- const char *prefix;
-- char *prefix_xp = NULL;
-- char *data_locale;
--
-- data_locale = from_utf8(data);
-- prefix = settings_opt_get("event_log_dir");
-- if (prefix)
-- prefix = prefix_xp = expand_filename(prefix);
-- else
-- prefix = ut_get_tmpdir();
-- datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
-- g_free(prefix_xp);
--
-- // XXX Some old systems may require us to set umask first.
-- fd = mkstemp(datafname);
-- if (fd == -1) {
-- g_free(datafname);
-- datafname = NULL;
-- scr_LogPrint(LPRINT_LOGNORM,
-- "Unable to create temp file for external command.");
-- } else {
-- size_t data_locale_len = strlen(data_locale);
-- ssize_t a = write(fd, data_locale, data_locale_len);
-- ssize_t b = write(fd, "\n", 1);
-- if ((size_t)a != data_locale_len || b != 1) {
-- g_free(datafname);
-- datafname = NULL;
-- scr_LogPrint(LPRINT_LOGNORM,
-- "Unable to write to temp file for external command.");
-- }
-- close(fd);
-- arg_data = datafname;
-- }
-- g_free(data_locale);
-- }
--
-- if ((pid=fork()) == -1) {
-- scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
-- g_free(datafname);
-- return;
-- }
--
-- if (pid == 0) { // child
-- // Close standard file descriptors
-- close(STDIN_FILENO);
-- close(STDOUT_FILENO);
-- close(STDERR_FILENO);
-- if (execl(extcmd, extcmd, arg_type, arg_info, bjid, arg_data,
-- (char *)NULL) == -1) {
-- // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
-- exit(1);
-- }
-- }
-- g_free(datafname);
--}
--
- /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
-diff -r 64da54766f99 mcabber/mcabber/hooks.h
---- a/mcabber/mcabber/hooks.h Mon Apr 30 23:36:31 2012 +0300
-+++ b/mcabber/mcabber/hooks.h Mon Apr 30 23:36:55 2012 +0300
-@@ -66,9 +66,6 @@
- guint hk_subscription(LmMessageSubType mstype, const gchar *bjid,
- const gchar *msg);
-
--void hk_ext_cmd_init(const char *command);
--void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data);
--
- #endif /* __MCABBER_HOOKS_H__ */
-
- /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
-diff -r 64da54766f99 mcabber/mcabber/main.c
---- a/mcabber/mcabber/main.c Mon Apr 30 23:36:31 2012 +0300
-+++ b/mcabber/mcabber/main.c Mon Apr 30 23:36:55 2012 +0300
-@@ -44,6 +44,7 @@
- #include "xmpp.h"
- #include "help.h"
- #include "events.h"
-+#include "extcmd.h"
-
- #ifndef MODULES_ENABLE
- # include "fifo.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/separate-extcmd.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,459 @@
+Move extcmd code from hooks
+
+diff -r 64da54766f99 mcabber/mcabber/Makefile.am
+--- a/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:31 2012 +0300
++++ b/mcabber/mcabber/Makefile.am Mon Apr 30 23:36:55 2012 +0300
+@@ -7,7 +7,7 @@
+ xmpp.c xmpp.h xmpp_helper.c xmpp_helper.h xmpp_defines.h \
+ xmpp_iq.c xmpp_iq.h xmpp_iqrequest.c xmpp_iqrequest.h \
+ xmpp_muc.c xmpp_muc.h xmpp_s10n.c xmpp_s10n.h \
+- caps.c caps.h help.c help.h
++ caps.c caps.h help.c help.h extcmd.c extcmd.h
+
+ if OTR
+ mcabber_SOURCES += otr.c otr.h nohtml.c nohtml.h
+@@ -42,6 +42,7 @@
+ xmpp_iq.h xmpp_iqrequest.h \
+ xmpp_muc.h xmpp_s10n.h \
+ caps.h fifo.h help.h modules.h api.h \
++ extcmd.h \
+ $(top_srcdir)/include/config.h
+
+ if OTR
+diff -r 64da54766f99 mcabber/mcabber/extcmd.c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/extcmd.c Mon Apr 30 23:36:55 2012 +0300
+@@ -0,0 +1,121 @@
++/*
++ * extcmd.c -- External event handler command
++ *
++ * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
++ *
++ * This program 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.
++ *
++ * This program 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 program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <glib.h>
++
++#include "screen.h"
++#include "roster.h"
++#include "settings.h"
++#include "utils.h"
++#include "utf8.h"
++
++static char *extcmd = NULL;
++
++// hk_ext_cmd_init()
++// Initialize external command variable.
++// Can be called with parameter NULL to reset and free memory
++void hk_ext_cmd_init(const char *command)
++{
++ if (extcmd) {
++ g_free(extcmd);
++ extcmd = NULL;
++ }
++ if (command)
++ extcmd = expand_filename(command);
++}
++
++// hk_ext_cmd()
++// Launch an external command (process) for the given event.
++// For now, data should be NULL.
++void hk_ext_cmd(const char *name, const char *arg_type, const char *arg_info, const char *data)
++{
++ pid_t pid;
++ char *arg_data = NULL;
++ char *datafname = NULL;
++
++ if (!arg_type || !arg_info) return;
++
++ if (*name && settings_opt_get_int("eventcmd_use_nickname"))
++ name = roster_getname(name);
++
++ if (data && settings_opt_get_int("event_log_files")) {
++ int fd;
++ const char *prefix;
++ char *prefix_xp = NULL;
++ char *data_locale;
++
++ data_locale = from_utf8(data);
++ prefix = settings_opt_get("event_log_dir");
++ if (prefix)
++ prefix = prefix_xp = expand_filename(prefix);
++ else
++ prefix = ut_get_tmpdir();
++ datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
++ g_free(prefix_xp);
++
++ // XXX Some old systems may require us to set umask first.
++ fd = mkstemp(datafname);
++ if (fd == -1) {
++ g_free(datafname);
++ datafname = NULL;
++ scr_LogPrint(LPRINT_LOGNORM,
++ "Unable to create temp file for external command.");
++ } else {
++ size_t data_locale_len = strlen(data_locale);
++ ssize_t a = write(fd, data_locale, data_locale_len);
++ ssize_t b = write(fd, "\n", 1);
++ if ((size_t)a != data_locale_len || b != 1) {
++ g_free(datafname);
++ datafname = NULL;
++ scr_LogPrint(LPRINT_LOGNORM,
++ "Unable to write to temp file for external command.");
++ }
++ close(fd);
++ arg_data = datafname;
++ }
++ g_free(data_locale);
++ }
++
++ if ((pid=fork()) == -1) {
++ scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
++ g_free(datafname);
++ return;
++ }
++
++ if (pid == 0) { // child
++ // Close standard file descriptors
++ close(STDIN_FILENO);
++ close(STDOUT_FILENO);
++ close(STDERR_FILENO);
++ if (execl(extcmd, extcmd, arg_type, arg_info, name, arg_data,
++ (char *)NULL) == -1) {
++ // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
++ exit(1);
++ }
++ }
++ g_free(datafname);
++}
++
++/* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */
+diff -r 64da54766f99 mcabber/mcabber/extcmd.h
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/extcmd.h Mon Apr 30 23:36:55 2012 +0300
+@@ -0,0 +1,15 @@
++#ifndef __MCABBER_EXTCMD_H__
++#define __MCABBER_EXTCMD_H__ 1
++
++#define EXT_CMD_TYPE_MESSAGE "MSG"
++#define EXT_CMD_TYPE_STATUS "STATUS"
++#define EXT_CMD_TYPE_UNREAD "UNREAD"
++
++#define EXT_CMD_INFO_RECEIVED "IN"
++#define EXT_CMD_INFO_SENT "OUT"
++#define EXT_CMD_INFO_GROUPCHAT "MUC"
++
++void hk_ext_cmd_init(const char *command);
++void hk_ext_cmd(const char *bjid, const char *type, const char *info, const char *data);
++
++#endif
+diff -r 64da54766f99 mcabber/mcabber/hooks.c
+--- a/mcabber/mcabber/hooks.c Mon Apr 30 23:36:31 2012 +0300
++++ b/mcabber/mcabber/hooks.c Mon Apr 30 23:36:55 2012 +0300
+@@ -24,6 +24,7 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include <unistd.h>
++#include <ctype.h>
+
+ #include "hooks.h"
+ #include "screen.h"
+@@ -35,6 +36,7 @@
+ #include "utf8.h"
+ #include "commands.h"
+ #include "main.h"
++#include "extcmd.h"
+
+ #ifdef MODULES_ENABLE
+ #include <glib.h>
+@@ -177,8 +179,6 @@
+ }
+ #endif
+
+-static char *extcmd;
+-
+ static const char *COMMAND_ME = "/me ";
+
+ void hk_message_in(const char *bjid, const char *resname,
+@@ -195,7 +195,6 @@
+ char *wmsg = NULL, *bmsg = NULL, *mmsg = NULL;
+ GSList *roster_usr;
+ unsigned mucnicklen = 0;
+- const char *ename = NULL;
+ gboolean attention = FALSE, mucprivmsg = FALSE;
+ gboolean error_msg_subtype = (type == LM_MESSAGE_SUB_TYPE_ERROR);
+ #ifdef MODULES_ENABLE
+@@ -373,19 +372,6 @@
+ (!is_room || (is_groupchat && log_muc_conf && !timestamp)))
+ hlog_write_message(bjid, timestamp, 0, wmsg);
+
+- if (settings_opt_get_int("events_ignore_active_window") &&
+- current_buddy && scr_get_chatmode()) {
+- gpointer bud = BUDDATA(current_buddy);
+- if (bud) {
+- const char *cjid = buddy_getjid(bud);
+- if (cjid && !strcasecmp(cjid, bjid))
+- active_window = TRUE;
+- }
+- }
+-
+- if (settings_opt_get_int("eventcmd_use_nickname"))
+- ename = roster_getname(bjid);
+-
+ // Display the sender in the log window
+ if ((!is_groupchat) && !(message_flags & HBB_PREFIX_ERR) &&
+ settings_opt_get_int("log_display_sender")) {
+@@ -411,12 +397,24 @@
+ }
+ #endif
+
++ if (settings_opt_get_int("events_ignore_active_window") &&
++ current_buddy && scr_get_chatmode()) {
++ gpointer bud = BUDDATA(current_buddy);
++ if (bud) {
++ const char *cjid = buddy_getjid(bud);
++ if (cjid && !strcasecmp(cjid, bjid))
++ active_window = TRUE;
++ }
++ }
++
+ // External command
+ // - We do not call hk_ext_cmd() for history lines in MUC
+ // - We do call hk_ext_cmd() for private messages in a room
+ // - We do call hk_ext_cmd() for messages to the current window
+ if (!active_window && ((is_groupchat && !timestamp) || !is_groupchat))
+- hk_ext_cmd(ename ? ename : bjid, (is_groupchat ? 'G' : 'M'), 'R', wmsg);
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE,
++ is_groupchat ? EXT_CMD_INFO_GROUPCHAT : EXT_CMD_INFO_RECEIVED,
++ wmsg);
+
+ // Beep, if enabled:
+ // - if it's a private message
+@@ -494,7 +492,7 @@
+ #endif
+
+ // External command
+- hk_ext_cmd(bjid, 'M', 'S', NULL);
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_MESSAGE, EXT_CMD_INFO_SENT, NULL);
+
+ g_free(bmsg);
+ g_free(mmsg);
+@@ -509,10 +507,7 @@
+ char *bn;
+ char *logsmsg;
+ const char *rn = (resname ? resname : "");
+- const char *ename = NULL;
+-
+- if (settings_opt_get_int("eventcmd_use_nickname"))
+- ename = roster_getname(bjid);
++ char newstatus[2] = { '?', '\0' };
+
+ oldstat = roster_getstatus(bjid, resname);
+
+@@ -564,27 +559,28 @@
+ scr_draw_roster();
+ hlog_write_status(bjid, timestamp, status, status_msg);
+
++ newstatus[0] = imstatus2char[status];
++
+ #ifdef MODULES_ENABLE
+ {
+ char os[2] = " \0";
+- char ns[2] = " \0";
+ hk_arg_t args[] = {
+ { "jid", bjid },
+ { "resource", rn },
+ { "old_status", os },
+- { "new_status", ns },
++ { "new_status", newstatus },
+ { "message", status_msg ? status_msg : "" },
+ { NULL, NULL },
+ };
+ os[0] = imstatus2char[oldstat];
+- ns[0] = imstatus2char[status];
+
+ hk_run_handlers(HOOK_STATUS_CHANGE, args);
+ }
+ #endif
+
+ // External command
+- hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL);
++ newstatus[0] = toupper(newstatus[0]);
++ hk_ext_cmd(bjid, EXT_CMD_TYPE_STATUS, newstatus, status_msg);
+ }
+
+ void hk_mystatuschange(time_t timestamp, enum imstatus old_status,
+@@ -711,7 +707,7 @@
+ /* Call external command */
+ str_unread = g_strdup_printf("%u %u %u %u", unread_count, attention_count,
+ muc_unread, muc_attention);
+- hk_ext_cmd("", 'U', (guchar)MIN(255, unread_count), str_unread);
++ hk_ext_cmd("", EXT_CMD_TYPE_UNREAD, str_unread, NULL);
+ g_free(str_unread);
+ }
+
+@@ -751,123 +747,4 @@
+ return 0;
+ }
+
+-
+-/* External commands */
+-
+-// hk_ext_cmd_init()
+-// Initialize external command variable.
+-// Can be called with parameter NULL to reset and free memory.
+-void hk_ext_cmd_init(const char *command)
+-{
+- if (extcmd) {
+- g_free(extcmd);
+- extcmd = NULL;
+- }
+- if (command)
+- extcmd = expand_filename(command);
+-}
+-
+-// hk_ext_cmd()
+-// Launch an external command (process) for the given event.
+-// For now, data should be NULL.
+-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data)
+-{
+- pid_t pid;
+- const char *arg_type = NULL;
+- const char *arg_info = NULL;
+- const char *arg_data = NULL;
+- char status_str[2];
+- char *datafname = NULL;
+-
+- if (!extcmd) return;
+-
+- // Prepare arg_* (external command parameters)
+- switch (type) {
+- case 'M': /* Normal message */
+- arg_type = "MSG";
+- if (info == 'R')
+- arg_info = "IN";
+- else if (info == 'S')
+- arg_info = "OUT";
+- break;
+- case 'G': /* Groupchat message */
+- arg_type = "MSG";
+- arg_info = "MUC";
+- break;
+- case 'S': /* Status change */
+- arg_type = "STATUS";
+- if (strchr(imstatus2char, tolower(info))) {
+- status_str[0] = toupper(info);
+- status_str[1] = 0;
+- arg_info = status_str;
+- }
+- break;
+- case 'U': /* Unread buffer count */
+- arg_type = "UNREAD";
+- arg_info = data;
+- break;
+- default:
+- return;
+- }
+-
+- if (!arg_type || !arg_info) return;
+-
+- if (strchr("MG", type) && data && settings_opt_get_int("event_log_files")) {
+- int fd;
+- const char *prefix;
+- char *prefix_xp = NULL;
+- char *data_locale;
+-
+- data_locale = from_utf8(data);
+- prefix = settings_opt_get("event_log_dir");
+- if (prefix)
+- prefix = prefix_xp = expand_filename(prefix);
+- else
+- prefix = ut_get_tmpdir();
+- datafname = g_strdup_printf("%s/mcabber-%d.XXXXXX", prefix, getpid());
+- g_free(prefix_xp);
+-
+- // XXX Some old systems may require us to set umask first.
+- fd = mkstemp(datafname);
+- if (fd == -1) {
+- g_free(datafname);
+- datafname = NULL;
+- scr_LogPrint(LPRINT_LOGNORM,
+- "Unable to create temp file for external command.");
+- } else {
+- size_t data_locale_len = strlen(data_locale);
+- ssize_t a = write(fd, data_locale, data_locale_len);
+- ssize_t b = write(fd, "\n", 1);
+- if ((size_t)a != data_locale_len || b != 1) {
+- g_free(datafname);
+- datafname = NULL;
+- scr_LogPrint(LPRINT_LOGNORM,
+- "Unable to write to temp file for external command.");
+- }
+- close(fd);
+- arg_data = datafname;
+- }
+- g_free(data_locale);
+- }
+-
+- if ((pid=fork()) == -1) {
+- scr_LogPrint(LPRINT_LOGNORM, "Fork error, cannot launch external command.");
+- g_free(datafname);
+- return;
+- }
+-
+- if (pid == 0) { // child
+- // Close standard file descriptors
+- close(STDIN_FILENO);
+- close(STDOUT_FILENO);
+- close(STDERR_FILENO);
+- if (execl(extcmd, extcmd, arg_type, arg_info, bjid, arg_data,
+- (char *)NULL) == -1) {
+- // scr_LogPrint(LPRINT_LOGNORM, "Cannot execute external command.");
+- exit(1);
+- }
+- }
+- g_free(datafname);
+-}
+-
+ /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
+diff -r 64da54766f99 mcabber/mcabber/hooks.h
+--- a/mcabber/mcabber/hooks.h Mon Apr 30 23:36:31 2012 +0300
++++ b/mcabber/mcabber/hooks.h Mon Apr 30 23:36:55 2012 +0300
+@@ -66,9 +66,6 @@
+ guint hk_subscription(LmMessageSubType mstype, const gchar *bjid,
+ const gchar *msg);
+
+-void hk_ext_cmd_init(const char *command);
+-void hk_ext_cmd(const char *bjid, guchar type, guchar info, const char *data);
+-
+ #endif /* __MCABBER_HOOKS_H__ */
+
+ /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2: For Vim users... */
+diff -r 64da54766f99 mcabber/mcabber/main.c
+--- a/mcabber/mcabber/main.c Mon Apr 30 23:36:31 2012 +0300
++++ b/mcabber/mcabber/main.c Mon Apr 30 23:36:55 2012 +0300
+@@ -44,6 +44,7 @@
+ #include "xmpp.h"
+ #include "help.h"
+ #include "events.h"
++#include "extcmd.h"
+
+ #ifndef MODULES_ENABLE
+ # include "fifo.h"
--- a/series Sat Jul 28 19:37:32 2012 +0300
+++ b/series Sat Jul 28 19:42:13 2012 +0300
@@ -1,13 +1,13 @@
-fix-typo
-fix-receipts
-update-pc
-warning-fixes
-fix-module-messages
-switch-to-experimental
-separate-extcmd
-modularize-extcmd
-guard-xmpp-password
-roster-state-colors
-add-cmake
-use-gslice
-templates
+fix-typo.diff
+fix-receipts.diff
+update-pc.diff
+warning-fixes.diff
+fix-module-messages.diff
+switch-to-experimental.diff
+separate-extcmd.diff
+modularize-extcmd.diff
+guard-xmpp-password.diff
+roster-state-colors.diff
+add-cmake.diff
+use-gslice.diff
+templates.diff
--- a/switch-to-experimental Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-[local] Switch to experimental branch and api version
-
-diff -r bd387d7410d0 mcabber/ChangeLog.api
---- a/mcabber/ChangeLog.api Mon Apr 30 23:21:22 2012 +0300
-+++ b/mcabber/ChangeLog.api Mon Apr 30 23:36:31 2012 +0300
-@@ -1,3 +1,13 @@
-+
-+experimental (35)
-+
-+ * Change branch to "experimental".
-+ * Api as defined by dev:23
-+ * Add MCABBER_BRANCH_EXPERIMENTAL define to api.h
-+ * MQ Patch: switch-to-experimental
-+
-+ -- Myhailo Danylenko, 2012-04-30
-+
- dev (23)
- * Changeset 8dc418af3e72
- Add buddy_(get|set)activeresource() functions
-@@ -16,6 +26,12 @@
-
- -- Hermitifier, 2011-10-03
-
-+experimental (34)
-+
-+ * Api as defined by dev:20
-+
-+ -- Myhailo Danylenko, 2011-06-01
-+
- dev (20)
-
- * Add cmd_set_safe() and cmd_is_safe()
-diff -r bd387d7410d0 mcabber/configure.ac
---- a/mcabber/configure.ac Mon Apr 30 23:21:22 2012 +0300
-+++ b/mcabber/configure.ac Mon Apr 30 23:36:31 2012 +0300
-@@ -272,7 +272,7 @@
- AM_CONDITIONAL([INSTALL_HEADERS], [test x$enable_modules != xno])
-
- # Prepare some config.h variables
--AC_DEFINE([MCABBER_BRANCH], "dev", [Mcabber branch])
-+AC_DEFINE([MCABBER_BRANCH], "experimental", [Mcabber branch])
- AC_DEFINE([MCABBER_VERSION], "AC_PACKAGE_VERSION", [Mcabber version string])
-
- # We need _GNU_SOURCE for strptime() and strcasestr()
-diff -r bd387d7410d0 mcabber/mcabber/api.h
---- a/mcabber/mcabber/api.h Mon Apr 30 23:21:22 2012 +0300
-+++ b/mcabber/mcabber/api.h Mon Apr 30 23:36:31 2012 +0300
-@@ -3,10 +3,10 @@
-
- #include <mcabber/config.h> // For MCABBER_BRANCH
-
--#define MCABBER_API_VERSION 23
--#define MCABBER_API_MIN 21
-+#define MCABBER_API_VERSION 35
-+#define MCABBER_API_MIN 35
-
--#define MCABBER_BRANCH_DEV 1
-+#define MCABBER_BRANCH_EXPERIMENTAL 1
-
- #define MCABBER_API_HAVE_CMD_ID 1
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/switch-to-experimental.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,61 @@
+[local] Switch to experimental branch and api version
+
+diff -r bd387d7410d0 mcabber/ChangeLog.api
+--- a/mcabber/ChangeLog.api Mon Apr 30 23:21:22 2012 +0300
++++ b/mcabber/ChangeLog.api Mon Apr 30 23:36:31 2012 +0300
+@@ -1,3 +1,13 @@
++
++experimental (35)
++
++ * Change branch to "experimental".
++ * Api as defined by dev:23
++ * Add MCABBER_BRANCH_EXPERIMENTAL define to api.h
++ * MQ Patch: switch-to-experimental
++
++ -- Myhailo Danylenko, 2012-04-30
++
+ dev (23)
+ * Changeset 8dc418af3e72
+ Add buddy_(get|set)activeresource() functions
+@@ -16,6 +26,12 @@
+
+ -- Hermitifier, 2011-10-03
+
++experimental (34)
++
++ * Api as defined by dev:20
++
++ -- Myhailo Danylenko, 2011-06-01
++
+ dev (20)
+
+ * Add cmd_set_safe() and cmd_is_safe()
+diff -r bd387d7410d0 mcabber/configure.ac
+--- a/mcabber/configure.ac Mon Apr 30 23:21:22 2012 +0300
++++ b/mcabber/configure.ac Mon Apr 30 23:36:31 2012 +0300
+@@ -272,7 +272,7 @@
+ AM_CONDITIONAL([INSTALL_HEADERS], [test x$enable_modules != xno])
+
+ # Prepare some config.h variables
+-AC_DEFINE([MCABBER_BRANCH], "dev", [Mcabber branch])
++AC_DEFINE([MCABBER_BRANCH], "experimental", [Mcabber branch])
+ AC_DEFINE([MCABBER_VERSION], "AC_PACKAGE_VERSION", [Mcabber version string])
+
+ # We need _GNU_SOURCE for strptime() and strcasestr()
+diff -r bd387d7410d0 mcabber/mcabber/api.h
+--- a/mcabber/mcabber/api.h Mon Apr 30 23:21:22 2012 +0300
++++ b/mcabber/mcabber/api.h Mon Apr 30 23:36:31 2012 +0300
+@@ -3,10 +3,10 @@
+
+ #include <mcabber/config.h> // For MCABBER_BRANCH
+
+-#define MCABBER_API_VERSION 23
+-#define MCABBER_API_MIN 21
++#define MCABBER_API_VERSION 35
++#define MCABBER_API_MIN 35
+
+-#define MCABBER_BRANCH_DEV 1
++#define MCABBER_BRANCH_EXPERIMENTAL 1
+
+ #define MCABBER_API_HAVE_CMD_ID 1
+
--- a/templates Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1346 +0,0 @@
-# HG changeset patch
-# Parent 456d832740146c5c9a0428c9ca0a1a0c431481b0
-[work-in-progress] Use templates for statusbars
-
-diff -r 456d83274014 mcabber/CMakeLists.txt
---- a/mcabber/CMakeLists.txt Fri Jul 20 17:29:53 2012 +0300
-+++ b/mcabber/CMakeLists.txt Fri Jul 20 17:30:20 2012 +0300
-@@ -154,8 +154,8 @@
-
- ## Define targets
- set ( mcabber_SUBSYSTEMS
-- caps commands compl events hbuf help histolog hooks
-- modules nohtml otr pgp roster screen settings utf8 utils
-+ caps commands compl events hbuf help histolog hooks modules
-+ nohtml otr parser pgp roster screen settings templates utf8 utils
- xmpp xmpp_helper xmpp_iq xmpp_iqrequest xmpp_muc xmpp_s10n )
- if ( NOT MODULES_ENABLE )
- list ( APPEND mcabber_SUBSYSTEMS extcmd fifo )
-diff -r 456d83274014 mcabber/mcabber/parser.c
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/parser.c Fri Jul 20 17:30:20 2012 +0300
-@@ -0,0 +1,853 @@
-+
-+/* Copyright 2012 Myhailo Danylenko
-+ *
-+ * This file is part of mcabber
-+ *
-+ * mcabber 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.
-+ *
-+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+#include <glib.h>
-+
-+#include <ctype.h> // is*
-+#include <stdlib.h> // strtol
-+#include <string.h> // strchr
-+
-+#include "parser.h"
-+
-+#include "config.h"
-+
-+//
-+// Private types
-+//
-+
-+typedef enum {
-+ in_string,
-+ in_escape,
-+ in_var,
-+ in_plainvarstart,
-+ in_plainvar,
-+ in_prevarname,
-+ in_varnamestart,
-+ in_varname,
-+ in_varchop,
-+ in_varstrip,
-+ in_varstripchop,
-+ in_varslash,
-+ in_varmatch,
-+ in_varpresubst,
-+ in_varsubst,
-+ in_varcolon,
-+ in_varminus,
-+ in_varplus,
-+ in_varpos,
-+ in_postvarpos,
-+ in_varlen,
-+ in_varsubstring,
-+ in_varend,
-+ in_evalstart,
-+ in_mathexpand,
-+ in_dblparen_eol,
-+ nostate,
-+} tmpl_parser_state_t;
-+
-+const static char * const tmpl_statenames [ ] = {
-+ "in string",
-+ "in escape",
-+ "in var",
-+ "in plainvarstart",
-+ "in plainvar",
-+ "in prevarname",
-+ "in varnamestart",
-+ "in varname",
-+ "in varchop",
-+ "in varstrip",
-+ "in varstripchop",
-+ "in varslash",
-+ "in varmatch",
-+ "in varpresubst",
-+ "in varsubst",
-+ "in varcolon",
-+ "in varminus",
-+ "in varplus",
-+ "in varpos",
-+ "in postvarpos",
-+ "in varlen",
-+ "in varsubstring",
-+ "in varend",
-+ "in evalstart",
-+ "in mathexpand",
-+ "in dblparen eol",
-+ "wrong state",
-+};
-+
-+typedef enum {
-+ no_eol = 0x0,
-+ brace_eol = 0x1,
-+ slash_eol = 0x2,
-+ colon_eol = 0x4,
-+ paren_eol = 0x8,
-+ dblparen_eol = 0x10,
-+ balance_parens = 0x20,
-+} tmpl_parser_flags_t;
-+
-+typedef enum {
-+ no_operation,
-+ expand_var,
-+ value_length,
-+} tmpl_var_operation_t;
-+
-+typedef enum {
-+ no_op,
-+ noempty_op,
-+ paren_op,
-+ plus_op,
-+ minus_op,
-+ remainder_op,
-+ division_op,
-+ multiplication_op,
-+} tmpl_math_op_t;
-+
-+typedef enum {
-+ in_preargument,
-+ in_hexoctzero,
-+ in_number,
-+ in_variable,
-+ in_postparen,
-+ in_op,
-+} tmpl_math_state_t;
-+
-+//
-+// Code
-+//
-+
-+GQuark tmpl_gerror_quark ( void )
-+{
-+ return g_quark_from_static_string ( "tmpl_gerror_quark" );
-+}
-+
-+// match tmpl_glob ( string, pattern, greedy, right-to-left )
-+// Performs matching of given pattern against given string,
-+// returns first substring in input, that matched. In pattern,
-+// there are currently two tokens recognized: '*' and '?'.
-+const gchar *tmpl_glob ( const char *str, gsize length, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len )
-+{
-+ *ret_len = 0;
-+ if ( rtl ) { // rtl
-+ const char *p = pat + patlen - 1;
-+ const char *s = str + length - 1;
-+ const char *matchend = NULL;
-+ const char *patrollback = p;
-+ const char *strrollback = s;
-+ while ( p >= pat && s >= str ) {
-+ if ( *p == '*' ) {
-+ if ( matchend == NULL && patrollback != NULL )
-+ matchend = strrollback;
-+ patrollback = NULL;
-+ strrollback = NULL;
-+ p --;
-+ } else if ( *p == '?' || *p == *s ) {
-+ if ( patrollback == NULL ) {
-+ patrollback = p;
-+ strrollback = s;
-+ }
-+ p --;
-+ s --;
-+ } else if ( patrollback != NULL ) {
-+ p = patrollback;
-+ s = -- strrollback;
-+ } else {
-+ s --;
-+ }
-+ }
-+ // now s, p or both have finished run
-+ if ( s >= str && patrollback == NULL ) // s remains and final star
-+ s = str - 1; // value at the end of loop
-+ if ( p >= pat ) { // p remains
-+ while ( *p == '*' && p >= pat ) // ignore empty stars
-+ p --;
-+ if ( p >= pat ) // symbols remain in pattern, no match
-+ return NULL;
-+ }
-+ if ( matchend == NULL && patrollback != NULL ) // no stars
-+ matchend = strrollback;
-+ if ( matchend != NULL ) {
-+ *ret_len = matchend - s;
-+ return s + 1;
-+ } else
-+ return NULL;
-+ } else { // ltr
-+ const char * const pe = pat + patlen;
-+ const char * const se = str + length;
-+ const char *p = pat;
-+ const char *s = str;
-+ const char *matchstart = NULL;
-+ const char *patrollback = p;
-+ const char *strrollback = s;
-+ while ( p < pe && s < se ) {
-+ if ( *p == '*' ) {
-+ if ( matchstart == NULL && patrollback != NULL )
-+ matchstart = strrollback;
-+ patrollback = NULL;
-+ strrollback = NULL;
-+ p ++;
-+ } else if ( *p == '?' || *p == *s ) {
-+ if ( patrollback == NULL ) {
-+ patrollback = p;
-+ strrollback = s;
-+ }
-+ p ++;
-+ s ++;
-+ } else if ( patrollback != NULL ) { // start/nomatch-recovery eat
-+ p = patrollback;
-+ s = ++ strrollback;
-+ } else { // star-eat
-+ s ++;
-+ }
-+ }
-+ // now s, p or both have finished run
-+ if ( s < se && patrollback == NULL ) // s remains and final star
-+ s = se;
-+ if ( p < pe ) { // p remains
-+ while ( *p == '*' && p < pe ) // ignore empty stars
-+ p ++;
-+ if ( p < pe ) // symbols remain in pattern, no match
-+ return NULL;
-+ }
-+ if ( matchstart == NULL && patrollback != NULL ) // no stars
-+ matchstart = strrollback;
-+ if ( matchstart != NULL )
-+ *ret_len = s - matchstart;
-+ return matchstart;
-+ }
-+}
-+
-+// match tmpl_greedy_glob ( string, pattern, right-to-left )
-+// The same as above, but greedy.
-+const gchar *tmpl_greedy_glob ( const char *str, gsize length, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len )
-+{
-+ const char * const pe = pat + patlen;
-+ const char *p = pat;
-+ while ( p < pe ) {
-+ if ( *p == '*' ) {
-+ gsize llen = 0;
-+ const char *left = tmpl_glob ( str, length, pat, p - pat, FALSE, &llen );
-+ if ( left != NULL ) {
-+ gsize rlen = 0;
-+ const char *right = tmpl_glob ( left + llen, str + length - ( left + llen ), p + 1, pe - p - 1, TRUE, &rlen );
-+ if ( right != NULL ) {
-+ *ret_len = right + rlen - left;
-+ return left;
-+ }
-+ }
-+ return NULL;
-+ }
-+ p ++;
-+ }
-+ return tmpl_glob ( str, length, pat, patlen, rtl, ret_len );
-+}
-+
-+// - + * / % ( )
-+static gssize tmpl_math_expand_internal ( const char *str, gsize length, tmpl_variable_callback_t get_val, gpointer udata, GError **error, tmpl_math_op_t bound, gsize *proc_len )
-+{
-+ g_assert ( error != NULL && *error == NULL );
-+
-+ gssize result = 0;
-+ const char * p = str;
-+ const char * const e = str + length;
-+ tmpl_math_state_t state = in_preargument;
-+ const char * varname = NULL; // variable name start
-+ gsize base = 10; // number base
-+ gboolean invert = FALSE; // number change sign
-+ gboolean errstate = bound >= noempty_op; // indicates erroneous state
-+
-+ // process first argument
-+ while ( p < e && *error == NULL ) {
-+ if ( state == in_preargument ) { // initial space-skipping state
-+ if ( isspace ( *p ) )
-+ p ++;
-+ else if ( *p == '-' ) {
-+ invert = ! invert;
-+ errstate = TRUE;
-+ p ++;
-+ } else if ( *p == '+' ) {
-+ errstate = TRUE;
-+ p ++;
-+ } else if ( *p == '(' ) {
-+ gsize processed = 0;
-+ p ++;
-+ result = tmpl_math_expand_internal ( p, e - p, get_val, udata, error, paren_op, &processed );
-+ if ( *error == NULL )
-+ state = in_postparen;
-+ errstate = TRUE;
-+ p += processed;
-+ } else if ( *p == '0' ) {
-+ result = 0;
-+ errstate = FALSE;
-+ state = in_hexoctzero;
-+ p ++;
-+ } else if ( isdigit ( *p ) ) {
-+ result = 0;
-+ base = 10;
-+ state = in_number;
-+ } else if ( isalpha ( *p ) || *p == '_' ) {
-+ state = in_variable;
-+ errstate = FALSE;
-+ varname = p;
-+ p ++;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGARG, "Wrong symbol in math argument" );
-+ } else if ( state == in_hexoctzero ) {
-+ if ( *p == 'x' ) {
-+ errstate = TRUE;
-+ base = 16;
-+ p ++;
-+ } else
-+ base = 8;
-+ state = in_number;
-+ } else if ( state == in_number ) {
-+ short d = -1;
-+ if ( *p == '0' )
-+ d = 0;
-+ else if ( *p == '1' )
-+ d = 1;
-+ else if ( *p == '2' )
-+ d = 2;
-+ else if ( *p == '3' )
-+ d = 3;
-+ else if ( *p == '4' )
-+ d = 4;
-+ else if ( *p == '5' )
-+ d = 5;
-+ else if ( *p == '6' )
-+ d = 6;
-+ else if ( *p == '7' )
-+ d = 7;
-+ else if ( base <= 8 )
-+ ;
-+ else if ( *p == '8' )
-+ d = 8;
-+ else if ( *p == '9' )
-+ d = 9;
-+ else if ( base <= 10 )
-+ ;
-+ else if ( *p == 'a' || *p == 'A' )
-+ d = 10;
-+ else if ( *p == 'b' || *p == 'B' )
-+ d = 11;
-+ else if ( *p == 'c' || *p == 'C' )
-+ d = 12;
-+ else if ( *p == 'd' || *p == 'D' )
-+ d = 13;
-+ else if ( *p == 'e' || *p == 'E' )
-+ d = 14;
-+ else if ( *p == 'f' || *p == 'F' )
-+ d = 15;
-+ if ( d >= 0 ) {
-+ errstate = FALSE;
-+ result = result * base + d;
-+ p ++;
-+ } else if ( ! errstate )
-+ state = in_op;
-+ else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGNUMBER, "Too short math number" );
-+ } else if ( state == in_variable ) {
-+ if ( ! ( isalnum ( *p ) || *p == '_' ) ) { // XXX vars with '-' have to be expanded earlier
-+ gsize vlen = 0;
-+ const char * value = get_val ( varname, p - varname, udata, &vlen );
-+ // TODO control recursion level
-+ result = tmpl_math_expand_internal ( value, vlen, get_val, udata, error, no_op, NULL );
-+ if ( *error == NULL )
-+ state = in_op;
-+ } else
-+ p ++;
-+ } else if ( state == in_postparen ) {
-+ if ( *p == ')' ) {
-+ errstate = FALSE;
-+ p ++;
-+ state = in_op;
-+ } else
-+ g_assert_not_reached ();
-+ } else if ( state == in_op )
-+ break;
-+ else
-+ g_assert_not_reached ();
-+ }
-+
-+ if ( *error == NULL ) {
-+ // in_preargument - empty argument, possibly +-+- - 0 / errstate
-+ // in_hexoctzero - 0 - 0
-+ // in_number - 0, 0x, 01, 0x1, 1 - failure at 0x (errstate), invert
-+ // in_variable - varname - get var, invert
-+ // in_postparen - eof - failure (errstate)
-+ // in_op - ok - invert, continue parsing
-+ if ( errstate )
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGSTATE, "Wrong state at the end of first math argument parsing" );
-+ else {
-+ if ( state == in_op ) {
-+ if ( invert )
-+ result = - result;
-+
-+ // process operator+second argument pairs
-+ while ( p < e && *error == NULL ) {
-+ tmpl_math_op_t op = no_op;
-+ if ( isspace ( *p ) )
-+ p ++;
-+ else if ( *p == '+' ) // set op
-+ op = plus_op;
-+ else if ( *p == '-' )
-+ op = minus_op;
-+ else if ( *p == '/' )
-+ op = division_op;
-+ else if ( *p == '*' )
-+ op = multiplication_op;
-+ else if ( *p == '%' )
-+ op = remainder_op;
-+ else if ( *p == ')' ) {
-+ if ( bound < paren_op )
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHUNBALPAREN, "Unbalanced parens in math expression" );
-+ else
-+ break;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGOP, "Wrong math operator '%c'", *p );
-+
-+ if ( op != no_op ) {
-+ // lower priority operator, returning result
-+ if ( op < bound )
-+ break;
-+
-+ p ++;
-+ gsize processed = 0;
-+ gssize arg = tmpl_math_expand_internal ( p, e - p, get_val, udata, error, op, &processed );
-+ if ( *error == NULL ) {
-+ if ( op == plus_op )
-+ result += arg;
-+ else if ( op == minus_op )
-+ result -= arg;
-+ else if ( op == division_op )
-+ result /= arg;
-+ else if ( op == multiplication_op )
-+ result *= arg;
-+ else if ( op == remainder_op )
-+ result %= arg;
-+ else
-+ g_assert_not_reached ();
-+ }
-+ p += processed;
-+ }
-+ }
-+ } else if ( state == in_variable ) {
-+ gsize vlen = 0;
-+ const char * value = get_val ( varname, p - varname, udata, &vlen );
-+ result = tmpl_math_expand_internal ( value, vlen, get_val, udata, error, no_op, NULL );
-+ if ( *error == NULL && invert )
-+ result = - result;
-+ } else if ( invert )
-+ result = - result;
-+ // XXX may add long conditional with g_assert here, but I'm lazy
-+ }
-+ }
-+
-+ // return result
-+ if ( proc_len != NULL )
-+ *proc_len = p - str;
-+ return result;
-+}
-+
-+// result tmpl_math_expand ( plainexpanded string, callback )
-+// Performs mathematical expansion on given string.
-+// Note, that it does not perform variable/quote/etc expansion,
-+// if you want it, you should do that first.
-+gssize tmpl_math_expand ( const char *str, gsize length, tmpl_variable_callback_t get_val, gpointer udata, GError **ret_err )
-+{
-+ g_return_val_if_fail ( ret_err == NULL || *ret_err == NULL, -1 ); // XXX
-+
-+ GError * error = NULL;
-+ gssize result = tmpl_math_expand_internal ( str, length, get_val, udata, &error, no_op, NULL );
-+ if ( error != NULL )
-+ g_propagate_error ( ret_err, error );
-+ return result;
-+}
-+
-+// expansion tmpl_expand ( template, callback )
-+// Parse template, substitute shell-like expressions:
-+// * $var ${var}
-+// * ${var##pat} ${var#pat} ${var%%pat} ${var%pat}
-+// * ${var/pat} ${var/pat/repl} ${var//pat/repl} ${var/%pat/repl} ${var/#pat/repl}
-+// * ${var:+tmpl} ${var:-tmpl}
-+// * ${var:uint} ${var:uint:uint}
-+// * ${#var} ${!var} ${!var[operation]}
-+// * \n \t \e \$ \\ \X
-+// Callback will be called to obtain variable values.
-+// Variable name should be: /^[[:alnum:]][[:alnum:]_-]*$/
-+// For pattern rules see tmpl_glob ().
-+static gchar *tmpl_expand_internal ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **error, tmpl_parser_flags_t flags, gsize *proc_len )
-+{
-+ g_assert ( error != NULL && *error == NULL );
-+
-+ if ( template == NULL || len == 0 )
-+ return NULL;
-+
-+ GString *result = g_string_new ( NULL );
-+ const char *p = template;
-+ const char * const e = template + len;
-+ tmpl_parser_state_t state = in_string;
-+ const char *a1s = p; // string, varname
-+ const gchar *value = NULL; // variable value
-+ gsize vlen = 0; // variable value length
-+ tmpl_var_operation_t operation = no_operation;
-+ gchar *pattern = NULL;
-+ gsize plen = 0;
-+ gboolean rtl = FALSE; // strip/chop
-+ gboolean greedy = FALSE; // strip/chop
-+ gboolean multiglob = FALSE; // replace
-+ gsize parencount = 0;
-+ gssize subsoffset = -1;
-+ gssize subslength = -1;
-+
-+ while ( p < e && *error == NULL ) {
-+ if ( state == in_string ) { // nothing special
-+ if ( *p == '\\' ) { // escape next char
-+ g_string_append_len ( result, a1s, p - a1s );
-+ state = in_escape;
-+ } else if ( *p == '$' ) { // start variable
-+ g_string_append_len ( result, a1s, p - a1s );
-+ state = in_var;
-+ } else if ( flags & balance_parens && *p == '(' ) {
-+ parencount ++;
-+ } else if ( *p == ')' ) {
-+ if ( flags & balance_parens && parencount > 0 ) {
-+ parencount --;
-+ } else if ( flags & dblparen_eol ) {
-+ g_string_append_len ( result, a1s, p - a1s );
-+ state = in_dblparen_eol;
-+ } else if ( flags & balance_parens )
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Unbalanced parentheses" );
-+ } else if ( ( flags & brace_eol && *p == '}' ) ||
-+ ( flags & slash_eol && *p == '/' ) ||
-+ ( flags & colon_eol && parencount == 0 && *p == ':' ) )
-+ break;
-+ p ++;
-+ } else if ( state == in_escape ) { // escape (on escaped char)
-+ if ( *p == 'n' )
-+ g_string_append_c ( result, '\n' );
-+ else if ( *p == 't' )
-+ g_string_append_c ( result, '\t' );
-+ else if ( *p == 'e' ) // for some experimentation with colors...
-+ g_string_append_c ( result, '\033' );
-+ else
-+ g_string_append_c ( result, *p );
-+ p ++;
-+ state = in_string;
-+ a1s = p;
-+ } else if ( state == in_var ) { // some variable
-+ if ( *p == '{' ) { // enclosed variable
-+ state = in_prevarname;
-+ p ++;
-+ } else if ( *p == '(' ) {
-+ state = in_evalstart;
-+ p ++;
-+ } else // unenclosed variable
-+ state = in_plainvarstart;
-+ } else if ( state == in_plainvarstart ) {
-+ if ( isalpha ( *p ) || *p == '_' ) {
-+ state = in_plainvar;
-+ a1s = p;
-+ p ++;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVARNAME, "Wrong symbol at the start of variable name" );
-+ } else if ( state == in_plainvar ) { // unenclosed variable
-+ if ( ! ( isalnum ( *p ) || *p == '-' || *p == '_' ) ) {
-+ gsize vlen = 0;
-+ const gchar *value = get_val ( a1s, p - a1s, udata, &vlen );
-+ g_string_append_len ( result, value, vlen );
-+ state = in_string;
-+ a1s = p;
-+ } else
-+ p ++;
-+ } else if ( state == in_prevarname ) { // allow ! and # at varname start
-+ if ( *p == '!' ) {
-+ operation = expand_var;
-+ p++;
-+ } else if ( *p == '#' ) {
-+ operation = value_length;
-+ p++;
-+ } else
-+ operation = no_operation;
-+ state = in_varnamestart;
-+ } else if ( state == in_varnamestart ) {
-+ if ( isalpha ( *p ) || *p == '_' ) {
-+ state = in_varname;
-+ a1s = p;
-+ p ++;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVARNAME, "Wrong symbol at start of variable name" );
-+ } else if ( state == in_varname ) { // enclosed variable name
-+ if ( ! ( isalnum ( *p ) || *p == '-' || *p == '_' ) ) {
-+ value = get_val ( a1s, p - a1s, udata, &vlen );
-+ if ( operation == value_length ) {
-+ if ( *p == '}' ) {
-+ g_string_append_printf ( result, "%lu", vlen );
-+ state = in_varend;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVAREXP, "Wrong variable expansion expression" );
-+ } else {
-+ if ( operation == expand_var && value != NULL && vlen > 0 )
-+ value = get_val ( value, vlen, udata, &vlen );
-+ if ( *p == '}' ) { // end of variable
-+ g_string_append_len ( result, value, vlen );
-+ state = in_varend;
-+ } else if ( *p == '#' ) { // strip expression
-+ state = in_varstrip;
-+ p ++;
-+ } else if ( *p == '%' ) { // chop expression
-+ state = in_varchop;
-+ p ++;
-+ } else if ( *p == '/' ) { // replace expression
-+ state = in_varslash;
-+ p ++;
-+ } else if ( *p == ':' ) { // substring expression
-+ state = in_varcolon;
-+ p ++;
-+ } else // wrong symbols
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVAREXP, "Wrong variable expansion expression" );
-+ }
-+ } else
-+ p ++;
-+ } else if ( state == in_varstrip ) { // one of strip expressions
-+ if ( *p == '#' ) {
-+ greedy = TRUE;
-+ p ++;
-+ }
-+ rtl = FALSE;
-+ state = in_varstripchop;
-+ } else if ( state == in_varchop ) { // one of chop expressions
-+ if ( *p == '%' ) {
-+ greedy = TRUE;
-+ p ++;
-+ }
-+ rtl = TRUE;
-+ state = in_varstripchop;
-+ } else if ( state == in_varstripchop ) { // pattern expressions
-+ gsize elen = 0;
-+ gsize processed = 0;
-+ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol, &processed );
-+ if ( *error == NULL ) {
-+ gsize mlen = 0;
-+ const char *match = NULL;
-+ if ( greedy )
-+ match = tmpl_greedy_glob ( value, vlen, expansion, elen, rtl, &mlen );
-+ else
-+ match = tmpl_glob ( value, vlen, expansion, elen, rtl, &mlen );
-+ if ( mlen > 0 ) {
-+ if ( rtl ) { // % and %%
-+ if ( match + mlen == value + vlen )
-+ vlen -= mlen;
-+ } else if ( match == value ) { // # and ##
-+ value = match + mlen;
-+ vlen -= mlen;
-+ }
-+ }
-+ g_string_append_len ( result, value, vlen );
-+ }
-+ g_free ( expansion );
-+ p += processed;
-+ state = in_varend;
-+ } else if ( state == in_varslash ) { // replace expression
-+ multiglob = FALSE;
-+ rtl = FALSE;
-+ if ( *p == '#' )
-+ p ++;
-+ else if ( *p == '%' ) {
-+ rtl = TRUE;
-+ p ++;
-+ } else if ( *p == '/' ) {
-+ multiglob = TRUE;
-+ p ++;
-+ }
-+ state = in_varmatch;
-+ } else if ( state == in_varmatch ) { // match part of replace expression
-+ gsize processed = 0;
-+ pattern = tmpl_expand_internal ( p, e - p, get_val, udata, &plen, error, brace_eol | slash_eol, &processed );
-+ p += processed;
-+ state = in_varpresubst;
-+ } else if ( state == in_varpresubst ) { // skip slash
-+ if ( *p == '/' )
-+ p ++;
-+ state = in_varsubst;
-+ } else if ( state == in_varsubst ) { // replace expression
-+ gsize processed = 0;
-+ gsize slen = 0;
-+ gchar *subst = tmpl_expand_internal ( p, e - p, get_val, udata, &slen, error, brace_eol, &processed );
-+ if ( *error == NULL ) {
-+ const char * start = value;
-+ const char * const ve = value + vlen;
-+ while ( start < ve ) {
-+ gsize mlen = 0;
-+ const char *match = tmpl_glob ( start, ve - start, pattern, plen, rtl, &mlen );
-+ if ( mlen > 0 ) {
-+ g_string_append_len ( result, start, match - start );
-+ g_string_append_len ( result, subst, slen );
-+ start = match + mlen;
-+ } else
-+ break;
-+ if ( ! multiglob )
-+ break;
-+ }
-+ g_string_append_len ( result, start, ve - start );
-+ }
-+ g_free ( subst );
-+ g_free ( pattern );
-+ pattern = NULL;
-+ p += processed;
-+ state = in_varend;
-+ } else if ( state == in_varcolon ) { // substring or substitution expression
-+ if ( *p == '-' ) {
-+ state = in_varminus;
-+ p ++;
-+ } else if ( *p == '+' ) {
-+ state = in_varplus;
-+ p ++;
-+ } else {
-+ subsoffset = -1;
-+ subslength = -1;
-+ state = in_varpos;
-+ }
-+ } else if ( state == in_varminus || state == in_varplus ) { // zero and non-zero substitution
-+ gsize elen = 0;
-+ gsize processed = 0;
-+ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol, &processed );
-+ if ( *error == NULL ) {
-+ if ( ( vlen == 0 && state == in_varminus ) || ( vlen > 0 && state == in_varplus ) )
-+ g_string_append_len ( result, expansion, elen );
-+ else if ( vlen > 0 && state == in_varminus )
-+ g_string_append_len ( result, value, vlen );
-+ }
-+ g_free ( expansion );
-+ p += processed;
-+ state = in_varend;
-+ } else if ( state == in_varpos ) { // substring expression
-+ gsize elen = 0;
-+ gsize processed = 0;
-+ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, colon_eol | brace_eol | balance_parens, &processed );
-+ if ( *error == NULL ) {
-+ subsoffset = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
-+ if ( subsoffset < 0 )
-+ subsoffset = 0;
-+ }
-+ g_free ( expansion );
-+ p += processed;
-+ state = in_postvarpos;
-+ } else if ( state == in_postvarpos ) {
-+ if ( *p == ':' ) {
-+ state = in_varlen;
-+ p ++;
-+ } else if ( *p == '}' )
-+ state = in_varsubstring;
-+ else
-+ g_assert_not_reached ();
-+ } else if ( state == in_varlen ) {
-+ gsize elen = 0;
-+ gsize processed = 0;
-+ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol | balance_parens, &processed );
-+ if ( *error == NULL ) {
-+ subslength = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
-+ if ( subslength < 0 )
-+ subslength = 0;
-+ }
-+ g_free ( expansion );
-+ p += processed;
-+ state = in_varsubstring;
-+ } else if ( state == in_varsubstring ) {
-+ g_assert ( subsoffset >= 0 );
-+ if ( subslength == -1 )
-+ subslength = vlen;
-+ if ( subsoffset < vlen ) {
-+ if ( subsoffset + subslength > vlen )
-+ g_string_append_len ( result, value + subsoffset, vlen - subsoffset );
-+ else
-+ g_string_append_len ( result, value + subsoffset, subslength );
-+ }
-+ state = in_varend;
-+ } else if ( state == in_varend ) { // end of enclosed variable
-+ if ( *p == '}' ) {
-+ p ++;
-+ state = in_string;
-+ a1s = p;
-+ } else
-+ g_assert_not_reached (); // this state must be used only on '}' or EOL
-+ } else if ( state == in_evalstart ) {
-+ if ( *p == '(' ) {
-+ p ++;
-+ state = in_mathexpand;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Command evaluation is not supported" );
-+ } else if ( state == in_mathexpand ) {
-+ gsize elen = 0;
-+ gsize processed = 0;
-+ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, dblparen_eol | balance_parens, &processed );
-+ if ( *error == NULL ) {
-+ gssize value = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
-+ if ( *error == NULL )
-+ g_string_append_printf ( result, "%li", value );
-+ }
-+ g_free ( expansion );
-+ p += processed;
-+ state = in_string;
-+ a1s = p;
-+ } else if ( state == in_dblparen_eol ) {
-+ if ( *p == ')' ) {
-+ p ++;
-+ break;
-+ } else
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Unbalanced parentheses" );
-+ } else
-+ g_assert_not_reached ();
-+ }
-+
-+ if ( *error == NULL ) {
-+ if ( state == in_plainvar ) { // plain variable at the end of template
-+ gsize vlen = 0;
-+ const gchar *value = get_val ( a1s, p - a1s, udata, &vlen );
-+ g_string_append_len ( result, value, vlen );
-+ } else if ( state == in_string ) // end of string
-+ g_string_append_len ( result, a1s, p - a1s );
-+ else if ( state != in_dblparen_eol )
-+ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGENDSTATE, "Parser stopped in incorrect state '%s'", state < nostate ? tmpl_statenames [ state ] : "wrong state" );
-+ }
-+
-+ if ( pattern != NULL )
-+ g_free ( pattern );
-+
-+ if ( proc_len != NULL )
-+ *proc_len = p - template;
-+ if ( ret_len != NULL )
-+ *ret_len = result -> len;
-+ return g_string_free ( result, FALSE );
-+}
-+
-+//
-+// public wrappers
-+//
-+
-+gchar *tmpl_expand ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **ret_err )
-+{
-+ g_return_val_if_fail ( ret_err == NULL || *ret_err == NULL, NULL );
-+
-+ GError * error = NULL;
-+ gchar * result = tmpl_expand_internal ( template, len, get_val, udata, ret_len, &error, no_eol, NULL );
-+ if ( error != NULL )
-+ g_propagate_error ( ret_err, error );
-+ return result;
-+}
-+
-+/* vim: se ts=4 sw=4: */
-diff -r 456d83274014 mcabber/mcabber/parser.h
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/parser.h Fri Jul 20 17:30:20 2012 +0300
-@@ -0,0 +1,77 @@
-+
-+#ifndef MCABBER_PARSER_H
-+#define MCABBER_PARSER_H
-+
-+/* Copyright 2012 Myhailo Danylenko
-+ *
-+ * This file is part of mcabber
-+ *
-+ * mcabber 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.
-+ *
-+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+#include <glib.h>
-+
-+#define TMPL_GERROR_QUARK tmpl_gerror_quark ()
-+
-+#define TMPL_EWRONGVAREXP ( 0x01 )
-+#define TMPL_EWRONGVARNAME ( 0x03 )
-+#define TMPL_EUNBALPAREN ( 0x04 )
-+#define TMPL_EWRONGENDSTATE ( 0x02 )
-+
-+#define TMPL_EMATHWRONGARG ( 0x05 )
-+#define TMPL_EMATHWRONGNUMBER ( 0x06 )
-+#define TMPL_EMATHUNBALPAREN ( 0x07 )
-+#define TMPL_EMATHWRONGOP ( 0x08 )
-+#define TMPL_EMATHWRONGSTATE ( 0x09 )
-+
-+typedef const char *(*tmpl_variable_callback_t) ( const gchar *name, gsize len, gpointer udata, gsize *ret_len );
-+
-+GQuark tmpl_gerror_quark ( void );
-+
-+// match tmpl_glob ( string, pattern, right-to-left )
-+// Performs matching of given pattern against given string,
-+// returns substring, that matched. In pattern, there are currently
-+// two tokens recognized: '*' and '?'. Two flags determine, if
-+// '*' should be greedy and which end of string must match the
-+// pattern (i.e. have anchor).
-+const char *tmpl_glob ( const char *str, gsize strlen, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len );
-+
-+// match tmpl_greedy_glob ( string, pattern, rigt-to-left )
-+// The same, as above, but greedy.
-+const char *tmpl_greedy_glob ( const char *str, gsize strlen, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len );
-+
-+// result tmpl_math_expand ( string, callback )
-+// Performs mathematical expansion of given string.
-+// Note, that $var expressions are not recognized, you have to
-+// supply already expanded string here.
-+// Supported operators:
-+// ( ) + - * / %
-+gssize tmpl_math_expand ( const char *str, gsize len, tmpl_variable_callback_t get_val, gpointer udata, GError **error );
-+
-+// expansion tmpl_expand ( template, callback )
-+// Parse template, substitute shell-like expressions:
-+// * $var ${var}
-+// * ${var##pat} ${var#pat} ${var%%pat} ${var%pat}
-+// * ${var/pat} ${var/pat/repl} ${var//pat/repl} ${var/%pat/repl} ${var/#pat/repl}
-+// * ${var:+tmpl} ${var:-tmpl}
-+// * ${var:mathexp} ${var:mathexp:mathexp}
-+// * ${#var} ${!var} ${!var[operation]}
-+// * $(( mathexp ))
-+// * \n \t \e \$ \\ \X
-+// Callback will be called to obtain variable values.
-+// Variable name should be: /^[[:alnum:]][[:alnum:]_-]*$/
-+// For pattern rules see mms_glob ().
-+gchar *tmpl_expand ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **error );
-+
-+# endif
-+
-diff -r 456d83274014 mcabber/mcabber/templates.c
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/templates.c Fri Jul 20 17:30:20 2012 +0300
-@@ -0,0 +1,277 @@
-+
-+/* Copyright 2012 Myhailo Danylenko
-+ *
-+ * This file is part of mcabber
-+ *
-+ * mcabber 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.
-+ *
-+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+#include <glib.h>
-+
-+#include <string.h> // strlen
-+
-+#include "logprint.h"
-+#include "settings.h"
-+#include "main.h" // main_context
-+#include "templates.h"
-+#include "parser.h"
-+
-+//
-+// globals
-+//
-+
-+typedef void (*tmpl_callback_t) ( const gchar * expansion, gpointer udata );
-+
-+typedef struct {
-+ gchar * name;
-+ tmpl_callback_t callback;
-+ gpointer userdata;
-+ gboolean in_use;
-+ GSList * guards;
-+ gboolean changed;
-+ gchar * prev_expansion;
-+} template_t;
-+
-+typedef struct {
-+ gchar * name;
-+ GSList * templates;
-+} tmpl_guard_t;
-+
-+static GHashTable * tmpl_templates = NULL;
-+static GHashTable * tmpl_guards = NULL;
-+static guint tmpl_attached_id = 0;
-+
-+//
-+// predeclarations
-+//
-+
-+static gchar *tmpl_guard ( const gchar *key, const gchar *new_value );
-+
-+//
-+// code
-+//
-+
-+// [cb] drops template from guard's 'templates' list
-+static void tmpl_unguard ( gpointer data, gpointer udata )
-+{
-+ tmpl_guard_t *guard = data;
-+ template_t *template = udata;
-+ guard -> templates = g_slist_remove ( guard -> templates, template );
-+}
-+
-+// [destructor cb] releases guard hash table entry
-+static void tmpl_free_guard ( gpointer data )
-+{
-+ tmpl_guard_t *guard = data;
-+ settings_del_guard ( guard -> name );
-+ g_slist_free ( guard -> templates );
-+ g_free ( guard -> name );
-+ g_slice_free ( tmpl_guard_t, guard );
-+}
-+
-+// [destructor cb] releases taken guards and frees command
-+static void tmpl_free_template ( gpointer data )
-+{
-+ template_t *template = data;
-+ g_slist_foreach ( template -> guards, tmpl_unguard, template );
-+ g_slist_free ( template -> guards );
-+ g_free ( template -> name );
-+ g_free ( template -> prev_expansion );
-+ g_slice_free ( template_t, template );
-+}
-+
-+// install guard (name must be glib-allocated string)
-+static void tmpl_install_guard ( gchar *name, template_t *template, settings_guard_t callback )
-+{
-+ tmpl_guard_t *guard = g_hash_table_lookup ( tmpl_guards, name );
-+ if ( guard == NULL ) {
-+ if ( ! settings_set_guard ( name, callback ) ) {
-+ scr_log_print ( LPRINT_LOGNORM, "Error: Unable to install guard for variable '%s' for template '%s'.", name, template -> name );
-+ g_free ( name );
-+ } else {
-+ guard = g_slice_new ( tmpl_guard_t );
-+ guard -> name = name;
-+ guard -> templates = NULL;
-+ g_hash_table_replace ( tmpl_guards, guard -> name, guard ); // to be sure
-+ }
-+ } else
-+ g_free ( name );
-+ if ( ! g_slist_find ( template -> guards, guard ) ) {
-+ template -> guards = g_slist_prepend ( template -> guards, guard );
-+ guard -> templates = g_slist_prepend ( guard -> templates, template );
-+ }
-+}
-+
-+// [parser cb] provides mcabber option values & reinstalls guards
-+static const char *tmpl_get_var ( const gchar *name, gsize len, gpointer udata, gsize *ret_len )
-+{
-+ const char *result = NULL;
-+ if ( name != NULL && len > 0 ) {
-+ template_t * template = udata;
-+ gchar * var = g_strndup ( name, len );
-+ result = settings_opt_get ( var );
-+ // consumes var
-+ tmpl_install_guard ( var, template, tmpl_guard );
-+ }
-+ if ( ret_len != NULL ) {
-+ if ( result != NULL )
-+ *ret_len = strlen ( result );
-+ else
-+ *ret_len = 0;
-+ }
-+ return result;
-+}
-+
-+// [cb] mark unused guards for removal
-+static gboolean tmpl_drop_unused_guards ( gpointer key, gpointer value, gpointer udata )
-+{
-+ tmpl_guard_t * guard = value;
-+ if ( guard -> templates == NULL )
-+ return TRUE;
-+ return FALSE;
-+}
-+
-+// [cb] mark deleted templates for removal, reevaluate changed
-+static gboolean reevaluate_template ( gpointer key, gpointer value, gpointer udata )
-+{
-+ template_t * template = value;
-+
-+ if ( ! template -> in_use )
-+ return TRUE;
-+
-+ if ( template -> changed ) {
-+ const gchar * expression = settings_opt_get ( template -> name );
-+ gchar * expansion = NULL;
-+ // release guards (but do not free them)
-+ g_slist_foreach ( template -> guards, tmpl_unguard, template );
-+ g_slist_free ( template -> guards );
-+ template -> guards = NULL;
-+ // re-install guards & get updated expansion
-+ if ( expression != NULL ) {
-+ GError *error = NULL;
-+ expansion = tmpl_expand ( expression, strlen ( expression ), tmpl_get_var, template, NULL, &error );
-+ if ( error != NULL ) {
-+ scr_log_print ( LPRINT_LOGNORM, "Error: Expansion error on template '%s': %s.\nExpansion stopped at: '%s'", template -> name, error -> message, expansion );
-+ g_error_free ( error );
-+ g_free ( expansion );
-+ expansion = NULL;
-+ }
-+ }
-+ // re-install guard on template itself
-+ tmpl_install_guard ( g_strdup ( template -> name ), template, tmpl_guard );
-+ template -> changed = FALSE;
-+ // pass result to callback
-+ if ( g_strcmp0 ( expansion, template -> prev_expansion ) ) {
-+ g_free ( template -> prev_expansion );
-+ template -> prev_expansion = expansion;
-+ template -> callback ( expansion, template -> userdata );
-+ } else
-+ g_free ( expansion );
-+ }
-+
-+ return FALSE;
-+}
-+
-+// [idle cb] update commands/guards & call cbs when necessary
-+static gboolean reevaluate_templates ( gpointer data )
-+{
-+ // allow reschedule in a process of reevaluation
-+ tmpl_attached_id = 0;
-+ // drop removed & reevaluate changed templates
-+ g_hash_table_foreach_remove ( tmpl_templates, reevaluate_template, NULL );
-+ // free unused guards TODO do only when needed
-+ g_hash_table_foreach_remove ( tmpl_guards, tmpl_drop_unused_guards, NULL );
-+ // always return false, this is oneshot idle call
-+ return FALSE;
-+}
-+
-+// schedule templates reevaluation
-+static void tmpl_schedule_rerun ( void )
-+{
-+ if ( tmpl_attached_id == 0 ) {
-+ GSource * source = g_idle_source_new ();
-+ g_source_set_callback ( source, reevaluate_templates, NULL, NULL );
-+ tmpl_attached_id = g_source_attach ( source, main_context );
-+ g_source_unref ( source );
-+ }
-+}
-+
-+// [cb] sets changed flag on template
-+static void template_set_changed ( gpointer data, gpointer udata )
-+{
-+ template_t * template = data;
-+ template -> changed = TRUE;
-+}
-+
-+// [guard] generic guard for variable
-+static gchar *tmpl_guard ( const gchar *key, const gchar *new_value )
-+{
-+ if ( g_strcmp0 ( new_value, settings_opt_get ( key ) ) ) {
-+ // mark dependent commands as modified
-+ tmpl_guard_t *guard = g_hash_table_lookup ( tmpl_guards, key );
-+ g_slist_foreach ( guard -> templates, template_set_changed, NULL );
-+ // schedule execution of modified commands
-+ tmpl_schedule_rerun ();
-+ }
-+ return g_strdup ( new_value );
-+}
-+
-+// public
-+gboolean template_add ( const gchar * name, tmpl_callback_t callback, gpointer udata )
-+{
-+ g_assert ( name != NULL && callback != NULL );
-+ // check for existing template
-+ template_t * template = g_hash_table_lookup ( tmpl_templates, name );
-+ if ( template != NULL )
-+ return FALSE;
-+ // create new
-+ template = g_slice_new ( template_t );
-+ template -> name = g_strdup ( name );
-+ template -> callback = callback;
-+ template -> userdata = udata;
-+ template -> in_use = TRUE;
-+ template -> guards = NULL;
-+ template -> changed = TRUE;
-+ template -> prev_expansion = NULL;
-+ // schedule reevaluation
-+ tmpl_schedule_rerun ();
-+ return TRUE;
-+}
-+
-+// public
-+void template_set_in_use ( const gchar * name, gboolean in_use )
-+{
-+ g_assert ( name != NULL );
-+ template_t * template = g_hash_table_lookup ( tmpl_templates, name );
-+ g_assert ( template != NULL );
-+ template -> in_use = in_use;
-+}
-+
-+// private
-+void templates_init ( void )
-+{
-+ // the key will be freed by destruction cb
-+ tmpl_guards = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, tmpl_free_guard );
-+ tmpl_templates = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, tmpl_free_template );
-+}
-+
-+// private
-+void templates_uninit ( void )
-+{
-+ if ( tmpl_attached_id != 0 )
-+ g_source_remove ( tmpl_attached_id );
-+ g_hash_table_destroy ( tmpl_templates );
-+ g_hash_table_destroy ( tmpl_guards );
-+}
-+
-+/* vim: se ts=4 sw=4: */
-diff -r 456d83274014 mcabber/mcabber/templates.h
---- /dev/null Thu Jan 01 00:00:00 1970 +0000
-+++ b/mcabber/mcabber/templates.h Fri Jul 20 17:30:20 2012 +0300
-@@ -0,0 +1,44 @@
-+
-+/* Copyright 2012 Myhailo Danylenko
-+ *
-+ * This file is part of mcabber
-+ *
-+ * mcabber 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.
-+ *
-+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+#include <glib.h>
-+
-+// Type for template callback
-+typedef void (*tmpl_callback_t) ( const gchar * expansion, gpointer udata );
-+
-+// success template_add ( option name, cb, cb udata )
-+// Adds given mcabber option to list of watched templates.
-+// If any option, used in that template (or template itself) will change,
-+// callback will be called with new expansion of template.
-+gboolean template_add ( const gchar * name, tmpl_callback_t callback, gpointer udata );
-+
-+// template_set_in_use ( option name, used flag )
-+// Marks template as (un)used.
-+// Note: Template will be actually removed only on next evaluation run,
-+// though call to this function schedules such run. This way, you can
-+// mark a bunch of templates as unused and then mark some of them as used.
-+void template_set_in_use ( const gchar * name, gboolean in_use );
-+
-+// XXX do we need this?
-+// void tmpl_schedule_rerun ( void );
-+
-+// private
-+void templates_init ( void );
-+void templates_uninit ( void );
-+
-+/* vim: se ts=4 sw=4: */
-diff -r 456d83274014 mcabber/mcabber/utils.c
---- a/mcabber/mcabber/utils.c Fri Jul 20 17:29:53 2012 +0300
-+++ b/mcabber/mcabber/utils.c Fri Jul 20 17:30:20 2012 +0300
-@@ -650,6 +650,42 @@
- g_free(arglst);
- }
-
-+// parse_list(arg, cb, udata)
-+// Calls cb for every element in space/semicolon/comma-separated list.
-+// Designed to work in-place, so, no escapes, quoting etc.
-+// Terminates parsing if callback returns false.
-+void parse_list(const char *arg, parse_list_cb_t cb, void *udata)
-+{
-+ const char *p, *start;
-+ enum {
-+ in_space,
-+ in_string,
-+ } state;
-+
-+ if (!arg)
-+ return;
-+
-+ state = in_space;
-+ while ( *p ) {
-+ if ( *p == ' ' || *p == ';' || *p == ',' ) {
-+ if ( state == in_string ) {
-+ if ( ! cb ( start, p, udata ) )
-+ return;
-+ state = in_space;
-+ }
-+ } else {
-+ if ( state == in_space ) {
-+ start = p;
-+ state = in_string;
-+ }
-+ }
-+ p ++;
-+ }
-+
-+ if ( state == in_string )
-+ cb ( start, p, udata );
-+}
-+
- // replace_nl_with_dots(bufstr)
- // Replace '\n' with "(...)" (or with a NUL if the string is too short)
- void replace_nl_with_dots(char *bufstr)
-diff -r 456d83274014 mcabber/mcabber/utils.h
---- a/mcabber/mcabber/utils.h Fri Jul 20 17:29:53 2012 +0300
-+++ b/mcabber/mcabber/utils.h Fri Jul 20 17:30:20 2012 +0300
-@@ -43,6 +43,11 @@
- char **split_arg(const char *arg, unsigned int n, int dontstriplast);
- void free_arg_lst(char **arglst);
-
-+/* fast in-place string split on space/semicolon/comma
-+ * stops processing if callback returns false value */
-+typedef int (*parse_list_cb_t)(const char *start, const char *end, void *udata);
-+void parse_list(const char *arg, parse_list_cb_t cb, void *udata);
-+
- void replace_nl_with_dots(char *bufstr);
- char *ut_expand_tabs(const char *text);
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,1346 @@
+# HG changeset patch
+# Parent 456d832740146c5c9a0428c9ca0a1a0c431481b0
+[work-in-progress] Use templates for statusbars
+
+diff -r 456d83274014 mcabber/CMakeLists.txt
+--- a/mcabber/CMakeLists.txt Fri Jul 20 17:29:53 2012 +0300
++++ b/mcabber/CMakeLists.txt Fri Jul 20 17:30:20 2012 +0300
+@@ -154,8 +154,8 @@
+
+ ## Define targets
+ set ( mcabber_SUBSYSTEMS
+- caps commands compl events hbuf help histolog hooks
+- modules nohtml otr pgp roster screen settings utf8 utils
++ caps commands compl events hbuf help histolog hooks modules
++ nohtml otr parser pgp roster screen settings templates utf8 utils
+ xmpp xmpp_helper xmpp_iq xmpp_iqrequest xmpp_muc xmpp_s10n )
+ if ( NOT MODULES_ENABLE )
+ list ( APPEND mcabber_SUBSYSTEMS extcmd fifo )
+diff -r 456d83274014 mcabber/mcabber/parser.c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/parser.c Fri Jul 20 17:30:20 2012 +0300
+@@ -0,0 +1,853 @@
++
++/* Copyright 2012 Myhailo Danylenko
++ *
++ * This file is part of mcabber
++ *
++ * mcabber 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.
++ *
++ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <glib.h>
++
++#include <ctype.h> // is*
++#include <stdlib.h> // strtol
++#include <string.h> // strchr
++
++#include "parser.h"
++
++#include "config.h"
++
++//
++// Private types
++//
++
++typedef enum {
++ in_string,
++ in_escape,
++ in_var,
++ in_plainvarstart,
++ in_plainvar,
++ in_prevarname,
++ in_varnamestart,
++ in_varname,
++ in_varchop,
++ in_varstrip,
++ in_varstripchop,
++ in_varslash,
++ in_varmatch,
++ in_varpresubst,
++ in_varsubst,
++ in_varcolon,
++ in_varminus,
++ in_varplus,
++ in_varpos,
++ in_postvarpos,
++ in_varlen,
++ in_varsubstring,
++ in_varend,
++ in_evalstart,
++ in_mathexpand,
++ in_dblparen_eol,
++ nostate,
++} tmpl_parser_state_t;
++
++const static char * const tmpl_statenames [ ] = {
++ "in string",
++ "in escape",
++ "in var",
++ "in plainvarstart",
++ "in plainvar",
++ "in prevarname",
++ "in varnamestart",
++ "in varname",
++ "in varchop",
++ "in varstrip",
++ "in varstripchop",
++ "in varslash",
++ "in varmatch",
++ "in varpresubst",
++ "in varsubst",
++ "in varcolon",
++ "in varminus",
++ "in varplus",
++ "in varpos",
++ "in postvarpos",
++ "in varlen",
++ "in varsubstring",
++ "in varend",
++ "in evalstart",
++ "in mathexpand",
++ "in dblparen eol",
++ "wrong state",
++};
++
++typedef enum {
++ no_eol = 0x0,
++ brace_eol = 0x1,
++ slash_eol = 0x2,
++ colon_eol = 0x4,
++ paren_eol = 0x8,
++ dblparen_eol = 0x10,
++ balance_parens = 0x20,
++} tmpl_parser_flags_t;
++
++typedef enum {
++ no_operation,
++ expand_var,
++ value_length,
++} tmpl_var_operation_t;
++
++typedef enum {
++ no_op,
++ noempty_op,
++ paren_op,
++ plus_op,
++ minus_op,
++ remainder_op,
++ division_op,
++ multiplication_op,
++} tmpl_math_op_t;
++
++typedef enum {
++ in_preargument,
++ in_hexoctzero,
++ in_number,
++ in_variable,
++ in_postparen,
++ in_op,
++} tmpl_math_state_t;
++
++//
++// Code
++//
++
++GQuark tmpl_gerror_quark ( void )
++{
++ return g_quark_from_static_string ( "tmpl_gerror_quark" );
++}
++
++// match tmpl_glob ( string, pattern, greedy, right-to-left )
++// Performs matching of given pattern against given string,
++// returns first substring in input, that matched. In pattern,
++// there are currently two tokens recognized: '*' and '?'.
++const gchar *tmpl_glob ( const char *str, gsize length, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len )
++{
++ *ret_len = 0;
++ if ( rtl ) { // rtl
++ const char *p = pat + patlen - 1;
++ const char *s = str + length - 1;
++ const char *matchend = NULL;
++ const char *patrollback = p;
++ const char *strrollback = s;
++ while ( p >= pat && s >= str ) {
++ if ( *p == '*' ) {
++ if ( matchend == NULL && patrollback != NULL )
++ matchend = strrollback;
++ patrollback = NULL;
++ strrollback = NULL;
++ p --;
++ } else if ( *p == '?' || *p == *s ) {
++ if ( patrollback == NULL ) {
++ patrollback = p;
++ strrollback = s;
++ }
++ p --;
++ s --;
++ } else if ( patrollback != NULL ) {
++ p = patrollback;
++ s = -- strrollback;
++ } else {
++ s --;
++ }
++ }
++ // now s, p or both have finished run
++ if ( s >= str && patrollback == NULL ) // s remains and final star
++ s = str - 1; // value at the end of loop
++ if ( p >= pat ) { // p remains
++ while ( *p == '*' && p >= pat ) // ignore empty stars
++ p --;
++ if ( p >= pat ) // symbols remain in pattern, no match
++ return NULL;
++ }
++ if ( matchend == NULL && patrollback != NULL ) // no stars
++ matchend = strrollback;
++ if ( matchend != NULL ) {
++ *ret_len = matchend - s;
++ return s + 1;
++ } else
++ return NULL;
++ } else { // ltr
++ const char * const pe = pat + patlen;
++ const char * const se = str + length;
++ const char *p = pat;
++ const char *s = str;
++ const char *matchstart = NULL;
++ const char *patrollback = p;
++ const char *strrollback = s;
++ while ( p < pe && s < se ) {
++ if ( *p == '*' ) {
++ if ( matchstart == NULL && patrollback != NULL )
++ matchstart = strrollback;
++ patrollback = NULL;
++ strrollback = NULL;
++ p ++;
++ } else if ( *p == '?' || *p == *s ) {
++ if ( patrollback == NULL ) {
++ patrollback = p;
++ strrollback = s;
++ }
++ p ++;
++ s ++;
++ } else if ( patrollback != NULL ) { // start/nomatch-recovery eat
++ p = patrollback;
++ s = ++ strrollback;
++ } else { // star-eat
++ s ++;
++ }
++ }
++ // now s, p or both have finished run
++ if ( s < se && patrollback == NULL ) // s remains and final star
++ s = se;
++ if ( p < pe ) { // p remains
++ while ( *p == '*' && p < pe ) // ignore empty stars
++ p ++;
++ if ( p < pe ) // symbols remain in pattern, no match
++ return NULL;
++ }
++ if ( matchstart == NULL && patrollback != NULL ) // no stars
++ matchstart = strrollback;
++ if ( matchstart != NULL )
++ *ret_len = s - matchstart;
++ return matchstart;
++ }
++}
++
++// match tmpl_greedy_glob ( string, pattern, right-to-left )
++// The same as above, but greedy.
++const gchar *tmpl_greedy_glob ( const char *str, gsize length, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len )
++{
++ const char * const pe = pat + patlen;
++ const char *p = pat;
++ while ( p < pe ) {
++ if ( *p == '*' ) {
++ gsize llen = 0;
++ const char *left = tmpl_glob ( str, length, pat, p - pat, FALSE, &llen );
++ if ( left != NULL ) {
++ gsize rlen = 0;
++ const char *right = tmpl_glob ( left + llen, str + length - ( left + llen ), p + 1, pe - p - 1, TRUE, &rlen );
++ if ( right != NULL ) {
++ *ret_len = right + rlen - left;
++ return left;
++ }
++ }
++ return NULL;
++ }
++ p ++;
++ }
++ return tmpl_glob ( str, length, pat, patlen, rtl, ret_len );
++}
++
++// - + * / % ( )
++static gssize tmpl_math_expand_internal ( const char *str, gsize length, tmpl_variable_callback_t get_val, gpointer udata, GError **error, tmpl_math_op_t bound, gsize *proc_len )
++{
++ g_assert ( error != NULL && *error == NULL );
++
++ gssize result = 0;
++ const char * p = str;
++ const char * const e = str + length;
++ tmpl_math_state_t state = in_preargument;
++ const char * varname = NULL; // variable name start
++ gsize base = 10; // number base
++ gboolean invert = FALSE; // number change sign
++ gboolean errstate = bound >= noempty_op; // indicates erroneous state
++
++ // process first argument
++ while ( p < e && *error == NULL ) {
++ if ( state == in_preargument ) { // initial space-skipping state
++ if ( isspace ( *p ) )
++ p ++;
++ else if ( *p == '-' ) {
++ invert = ! invert;
++ errstate = TRUE;
++ p ++;
++ } else if ( *p == '+' ) {
++ errstate = TRUE;
++ p ++;
++ } else if ( *p == '(' ) {
++ gsize processed = 0;
++ p ++;
++ result = tmpl_math_expand_internal ( p, e - p, get_val, udata, error, paren_op, &processed );
++ if ( *error == NULL )
++ state = in_postparen;
++ errstate = TRUE;
++ p += processed;
++ } else if ( *p == '0' ) {
++ result = 0;
++ errstate = FALSE;
++ state = in_hexoctzero;
++ p ++;
++ } else if ( isdigit ( *p ) ) {
++ result = 0;
++ base = 10;
++ state = in_number;
++ } else if ( isalpha ( *p ) || *p == '_' ) {
++ state = in_variable;
++ errstate = FALSE;
++ varname = p;
++ p ++;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGARG, "Wrong symbol in math argument" );
++ } else if ( state == in_hexoctzero ) {
++ if ( *p == 'x' ) {
++ errstate = TRUE;
++ base = 16;
++ p ++;
++ } else
++ base = 8;
++ state = in_number;
++ } else if ( state == in_number ) {
++ short d = -1;
++ if ( *p == '0' )
++ d = 0;
++ else if ( *p == '1' )
++ d = 1;
++ else if ( *p == '2' )
++ d = 2;
++ else if ( *p == '3' )
++ d = 3;
++ else if ( *p == '4' )
++ d = 4;
++ else if ( *p == '5' )
++ d = 5;
++ else if ( *p == '6' )
++ d = 6;
++ else if ( *p == '7' )
++ d = 7;
++ else if ( base <= 8 )
++ ;
++ else if ( *p == '8' )
++ d = 8;
++ else if ( *p == '9' )
++ d = 9;
++ else if ( base <= 10 )
++ ;
++ else if ( *p == 'a' || *p == 'A' )
++ d = 10;
++ else if ( *p == 'b' || *p == 'B' )
++ d = 11;
++ else if ( *p == 'c' || *p == 'C' )
++ d = 12;
++ else if ( *p == 'd' || *p == 'D' )
++ d = 13;
++ else if ( *p == 'e' || *p == 'E' )
++ d = 14;
++ else if ( *p == 'f' || *p == 'F' )
++ d = 15;
++ if ( d >= 0 ) {
++ errstate = FALSE;
++ result = result * base + d;
++ p ++;
++ } else if ( ! errstate )
++ state = in_op;
++ else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGNUMBER, "Too short math number" );
++ } else if ( state == in_variable ) {
++ if ( ! ( isalnum ( *p ) || *p == '_' ) ) { // XXX vars with '-' have to be expanded earlier
++ gsize vlen = 0;
++ const char * value = get_val ( varname, p - varname, udata, &vlen );
++ // TODO control recursion level
++ result = tmpl_math_expand_internal ( value, vlen, get_val, udata, error, no_op, NULL );
++ if ( *error == NULL )
++ state = in_op;
++ } else
++ p ++;
++ } else if ( state == in_postparen ) {
++ if ( *p == ')' ) {
++ errstate = FALSE;
++ p ++;
++ state = in_op;
++ } else
++ g_assert_not_reached ();
++ } else if ( state == in_op )
++ break;
++ else
++ g_assert_not_reached ();
++ }
++
++ if ( *error == NULL ) {
++ // in_preargument - empty argument, possibly +-+- - 0 / errstate
++ // in_hexoctzero - 0 - 0
++ // in_number - 0, 0x, 01, 0x1, 1 - failure at 0x (errstate), invert
++ // in_variable - varname - get var, invert
++ // in_postparen - eof - failure (errstate)
++ // in_op - ok - invert, continue parsing
++ if ( errstate )
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGSTATE, "Wrong state at the end of first math argument parsing" );
++ else {
++ if ( state == in_op ) {
++ if ( invert )
++ result = - result;
++
++ // process operator+second argument pairs
++ while ( p < e && *error == NULL ) {
++ tmpl_math_op_t op = no_op;
++ if ( isspace ( *p ) )
++ p ++;
++ else if ( *p == '+' ) // set op
++ op = plus_op;
++ else if ( *p == '-' )
++ op = minus_op;
++ else if ( *p == '/' )
++ op = division_op;
++ else if ( *p == '*' )
++ op = multiplication_op;
++ else if ( *p == '%' )
++ op = remainder_op;
++ else if ( *p == ')' ) {
++ if ( bound < paren_op )
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHUNBALPAREN, "Unbalanced parens in math expression" );
++ else
++ break;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EMATHWRONGOP, "Wrong math operator '%c'", *p );
++
++ if ( op != no_op ) {
++ // lower priority operator, returning result
++ if ( op < bound )
++ break;
++
++ p ++;
++ gsize processed = 0;
++ gssize arg = tmpl_math_expand_internal ( p, e - p, get_val, udata, error, op, &processed );
++ if ( *error == NULL ) {
++ if ( op == plus_op )
++ result += arg;
++ else if ( op == minus_op )
++ result -= arg;
++ else if ( op == division_op )
++ result /= arg;
++ else if ( op == multiplication_op )
++ result *= arg;
++ else if ( op == remainder_op )
++ result %= arg;
++ else
++ g_assert_not_reached ();
++ }
++ p += processed;
++ }
++ }
++ } else if ( state == in_variable ) {
++ gsize vlen = 0;
++ const char * value = get_val ( varname, p - varname, udata, &vlen );
++ result = tmpl_math_expand_internal ( value, vlen, get_val, udata, error, no_op, NULL );
++ if ( *error == NULL && invert )
++ result = - result;
++ } else if ( invert )
++ result = - result;
++ // XXX may add long conditional with g_assert here, but I'm lazy
++ }
++ }
++
++ // return result
++ if ( proc_len != NULL )
++ *proc_len = p - str;
++ return result;
++}
++
++// result tmpl_math_expand ( plainexpanded string, callback )
++// Performs mathematical expansion on given string.
++// Note, that it does not perform variable/quote/etc expansion,
++// if you want it, you should do that first.
++gssize tmpl_math_expand ( const char *str, gsize length, tmpl_variable_callback_t get_val, gpointer udata, GError **ret_err )
++{
++ g_return_val_if_fail ( ret_err == NULL || *ret_err == NULL, -1 ); // XXX
++
++ GError * error = NULL;
++ gssize result = tmpl_math_expand_internal ( str, length, get_val, udata, &error, no_op, NULL );
++ if ( error != NULL )
++ g_propagate_error ( ret_err, error );
++ return result;
++}
++
++// expansion tmpl_expand ( template, callback )
++// Parse template, substitute shell-like expressions:
++// * $var ${var}
++// * ${var##pat} ${var#pat} ${var%%pat} ${var%pat}
++// * ${var/pat} ${var/pat/repl} ${var//pat/repl} ${var/%pat/repl} ${var/#pat/repl}
++// * ${var:+tmpl} ${var:-tmpl}
++// * ${var:uint} ${var:uint:uint}
++// * ${#var} ${!var} ${!var[operation]}
++// * \n \t \e \$ \\ \X
++// Callback will be called to obtain variable values.
++// Variable name should be: /^[[:alnum:]][[:alnum:]_-]*$/
++// For pattern rules see tmpl_glob ().
++static gchar *tmpl_expand_internal ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **error, tmpl_parser_flags_t flags, gsize *proc_len )
++{
++ g_assert ( error != NULL && *error == NULL );
++
++ if ( template == NULL || len == 0 )
++ return NULL;
++
++ GString *result = g_string_new ( NULL );
++ const char *p = template;
++ const char * const e = template + len;
++ tmpl_parser_state_t state = in_string;
++ const char *a1s = p; // string, varname
++ const gchar *value = NULL; // variable value
++ gsize vlen = 0; // variable value length
++ tmpl_var_operation_t operation = no_operation;
++ gchar *pattern = NULL;
++ gsize plen = 0;
++ gboolean rtl = FALSE; // strip/chop
++ gboolean greedy = FALSE; // strip/chop
++ gboolean multiglob = FALSE; // replace
++ gsize parencount = 0;
++ gssize subsoffset = -1;
++ gssize subslength = -1;
++
++ while ( p < e && *error == NULL ) {
++ if ( state == in_string ) { // nothing special
++ if ( *p == '\\' ) { // escape next char
++ g_string_append_len ( result, a1s, p - a1s );
++ state = in_escape;
++ } else if ( *p == '$' ) { // start variable
++ g_string_append_len ( result, a1s, p - a1s );
++ state = in_var;
++ } else if ( flags & balance_parens && *p == '(' ) {
++ parencount ++;
++ } else if ( *p == ')' ) {
++ if ( flags & balance_parens && parencount > 0 ) {
++ parencount --;
++ } else if ( flags & dblparen_eol ) {
++ g_string_append_len ( result, a1s, p - a1s );
++ state = in_dblparen_eol;
++ } else if ( flags & balance_parens )
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Unbalanced parentheses" );
++ } else if ( ( flags & brace_eol && *p == '}' ) ||
++ ( flags & slash_eol && *p == '/' ) ||
++ ( flags & colon_eol && parencount == 0 && *p == ':' ) )
++ break;
++ p ++;
++ } else if ( state == in_escape ) { // escape (on escaped char)
++ if ( *p == 'n' )
++ g_string_append_c ( result, '\n' );
++ else if ( *p == 't' )
++ g_string_append_c ( result, '\t' );
++ else if ( *p == 'e' ) // for some experimentation with colors...
++ g_string_append_c ( result, '\033' );
++ else
++ g_string_append_c ( result, *p );
++ p ++;
++ state = in_string;
++ a1s = p;
++ } else if ( state == in_var ) { // some variable
++ if ( *p == '{' ) { // enclosed variable
++ state = in_prevarname;
++ p ++;
++ } else if ( *p == '(' ) {
++ state = in_evalstart;
++ p ++;
++ } else // unenclosed variable
++ state = in_plainvarstart;
++ } else if ( state == in_plainvarstart ) {
++ if ( isalpha ( *p ) || *p == '_' ) {
++ state = in_plainvar;
++ a1s = p;
++ p ++;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVARNAME, "Wrong symbol at the start of variable name" );
++ } else if ( state == in_plainvar ) { // unenclosed variable
++ if ( ! ( isalnum ( *p ) || *p == '-' || *p == '_' ) ) {
++ gsize vlen = 0;
++ const gchar *value = get_val ( a1s, p - a1s, udata, &vlen );
++ g_string_append_len ( result, value, vlen );
++ state = in_string;
++ a1s = p;
++ } else
++ p ++;
++ } else if ( state == in_prevarname ) { // allow ! and # at varname start
++ if ( *p == '!' ) {
++ operation = expand_var;
++ p++;
++ } else if ( *p == '#' ) {
++ operation = value_length;
++ p++;
++ } else
++ operation = no_operation;
++ state = in_varnamestart;
++ } else if ( state == in_varnamestart ) {
++ if ( isalpha ( *p ) || *p == '_' ) {
++ state = in_varname;
++ a1s = p;
++ p ++;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVARNAME, "Wrong symbol at start of variable name" );
++ } else if ( state == in_varname ) { // enclosed variable name
++ if ( ! ( isalnum ( *p ) || *p == '-' || *p == '_' ) ) {
++ value = get_val ( a1s, p - a1s, udata, &vlen );
++ if ( operation == value_length ) {
++ if ( *p == '}' ) {
++ g_string_append_printf ( result, "%lu", vlen );
++ state = in_varend;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVAREXP, "Wrong variable expansion expression" );
++ } else {
++ if ( operation == expand_var && value != NULL && vlen > 0 )
++ value = get_val ( value, vlen, udata, &vlen );
++ if ( *p == '}' ) { // end of variable
++ g_string_append_len ( result, value, vlen );
++ state = in_varend;
++ } else if ( *p == '#' ) { // strip expression
++ state = in_varstrip;
++ p ++;
++ } else if ( *p == '%' ) { // chop expression
++ state = in_varchop;
++ p ++;
++ } else if ( *p == '/' ) { // replace expression
++ state = in_varslash;
++ p ++;
++ } else if ( *p == ':' ) { // substring expression
++ state = in_varcolon;
++ p ++;
++ } else // wrong symbols
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGVAREXP, "Wrong variable expansion expression" );
++ }
++ } else
++ p ++;
++ } else if ( state == in_varstrip ) { // one of strip expressions
++ if ( *p == '#' ) {
++ greedy = TRUE;
++ p ++;
++ }
++ rtl = FALSE;
++ state = in_varstripchop;
++ } else if ( state == in_varchop ) { // one of chop expressions
++ if ( *p == '%' ) {
++ greedy = TRUE;
++ p ++;
++ }
++ rtl = TRUE;
++ state = in_varstripchop;
++ } else if ( state == in_varstripchop ) { // pattern expressions
++ gsize elen = 0;
++ gsize processed = 0;
++ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol, &processed );
++ if ( *error == NULL ) {
++ gsize mlen = 0;
++ const char *match = NULL;
++ if ( greedy )
++ match = tmpl_greedy_glob ( value, vlen, expansion, elen, rtl, &mlen );
++ else
++ match = tmpl_glob ( value, vlen, expansion, elen, rtl, &mlen );
++ if ( mlen > 0 ) {
++ if ( rtl ) { // % and %%
++ if ( match + mlen == value + vlen )
++ vlen -= mlen;
++ } else if ( match == value ) { // # and ##
++ value = match + mlen;
++ vlen -= mlen;
++ }
++ }
++ g_string_append_len ( result, value, vlen );
++ }
++ g_free ( expansion );
++ p += processed;
++ state = in_varend;
++ } else if ( state == in_varslash ) { // replace expression
++ multiglob = FALSE;
++ rtl = FALSE;
++ if ( *p == '#' )
++ p ++;
++ else if ( *p == '%' ) {
++ rtl = TRUE;
++ p ++;
++ } else if ( *p == '/' ) {
++ multiglob = TRUE;
++ p ++;
++ }
++ state = in_varmatch;
++ } else if ( state == in_varmatch ) { // match part of replace expression
++ gsize processed = 0;
++ pattern = tmpl_expand_internal ( p, e - p, get_val, udata, &plen, error, brace_eol | slash_eol, &processed );
++ p += processed;
++ state = in_varpresubst;
++ } else if ( state == in_varpresubst ) { // skip slash
++ if ( *p == '/' )
++ p ++;
++ state = in_varsubst;
++ } else if ( state == in_varsubst ) { // replace expression
++ gsize processed = 0;
++ gsize slen = 0;
++ gchar *subst = tmpl_expand_internal ( p, e - p, get_val, udata, &slen, error, brace_eol, &processed );
++ if ( *error == NULL ) {
++ const char * start = value;
++ const char * const ve = value + vlen;
++ while ( start < ve ) {
++ gsize mlen = 0;
++ const char *match = tmpl_glob ( start, ve - start, pattern, plen, rtl, &mlen );
++ if ( mlen > 0 ) {
++ g_string_append_len ( result, start, match - start );
++ g_string_append_len ( result, subst, slen );
++ start = match + mlen;
++ } else
++ break;
++ if ( ! multiglob )
++ break;
++ }
++ g_string_append_len ( result, start, ve - start );
++ }
++ g_free ( subst );
++ g_free ( pattern );
++ pattern = NULL;
++ p += processed;
++ state = in_varend;
++ } else if ( state == in_varcolon ) { // substring or substitution expression
++ if ( *p == '-' ) {
++ state = in_varminus;
++ p ++;
++ } else if ( *p == '+' ) {
++ state = in_varplus;
++ p ++;
++ } else {
++ subsoffset = -1;
++ subslength = -1;
++ state = in_varpos;
++ }
++ } else if ( state == in_varminus || state == in_varplus ) { // zero and non-zero substitution
++ gsize elen = 0;
++ gsize processed = 0;
++ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol, &processed );
++ if ( *error == NULL ) {
++ if ( ( vlen == 0 && state == in_varminus ) || ( vlen > 0 && state == in_varplus ) )
++ g_string_append_len ( result, expansion, elen );
++ else if ( vlen > 0 && state == in_varminus )
++ g_string_append_len ( result, value, vlen );
++ }
++ g_free ( expansion );
++ p += processed;
++ state = in_varend;
++ } else if ( state == in_varpos ) { // substring expression
++ gsize elen = 0;
++ gsize processed = 0;
++ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, colon_eol | brace_eol | balance_parens, &processed );
++ if ( *error == NULL ) {
++ subsoffset = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
++ if ( subsoffset < 0 )
++ subsoffset = 0;
++ }
++ g_free ( expansion );
++ p += processed;
++ state = in_postvarpos;
++ } else if ( state == in_postvarpos ) {
++ if ( *p == ':' ) {
++ state = in_varlen;
++ p ++;
++ } else if ( *p == '}' )
++ state = in_varsubstring;
++ else
++ g_assert_not_reached ();
++ } else if ( state == in_varlen ) {
++ gsize elen = 0;
++ gsize processed = 0;
++ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, brace_eol | balance_parens, &processed );
++ if ( *error == NULL ) {
++ subslength = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
++ if ( subslength < 0 )
++ subslength = 0;
++ }
++ g_free ( expansion );
++ p += processed;
++ state = in_varsubstring;
++ } else if ( state == in_varsubstring ) {
++ g_assert ( subsoffset >= 0 );
++ if ( subslength == -1 )
++ subslength = vlen;
++ if ( subsoffset < vlen ) {
++ if ( subsoffset + subslength > vlen )
++ g_string_append_len ( result, value + subsoffset, vlen - subsoffset );
++ else
++ g_string_append_len ( result, value + subsoffset, subslength );
++ }
++ state = in_varend;
++ } else if ( state == in_varend ) { // end of enclosed variable
++ if ( *p == '}' ) {
++ p ++;
++ state = in_string;
++ a1s = p;
++ } else
++ g_assert_not_reached (); // this state must be used only on '}' or EOL
++ } else if ( state == in_evalstart ) {
++ if ( *p == '(' ) {
++ p ++;
++ state = in_mathexpand;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Command evaluation is not supported" );
++ } else if ( state == in_mathexpand ) {
++ gsize elen = 0;
++ gsize processed = 0;
++ gchar *expansion = tmpl_expand_internal ( p, e - p, get_val, udata, &elen, error, dblparen_eol | balance_parens, &processed );
++ if ( *error == NULL ) {
++ gssize value = tmpl_math_expand_internal ( expansion, elen, get_val, udata, error, no_op, NULL );
++ if ( *error == NULL )
++ g_string_append_printf ( result, "%li", value );
++ }
++ g_free ( expansion );
++ p += processed;
++ state = in_string;
++ a1s = p;
++ } else if ( state == in_dblparen_eol ) {
++ if ( *p == ')' ) {
++ p ++;
++ break;
++ } else
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EUNBALPAREN, "Unbalanced parentheses" );
++ } else
++ g_assert_not_reached ();
++ }
++
++ if ( *error == NULL ) {
++ if ( state == in_plainvar ) { // plain variable at the end of template
++ gsize vlen = 0;
++ const gchar *value = get_val ( a1s, p - a1s, udata, &vlen );
++ g_string_append_len ( result, value, vlen );
++ } else if ( state == in_string ) // end of string
++ g_string_append_len ( result, a1s, p - a1s );
++ else if ( state != in_dblparen_eol )
++ g_set_error ( error, TMPL_GERROR_QUARK, TMPL_EWRONGENDSTATE, "Parser stopped in incorrect state '%s'", state < nostate ? tmpl_statenames [ state ] : "wrong state" );
++ }
++
++ if ( pattern != NULL )
++ g_free ( pattern );
++
++ if ( proc_len != NULL )
++ *proc_len = p - template;
++ if ( ret_len != NULL )
++ *ret_len = result -> len;
++ return g_string_free ( result, FALSE );
++}
++
++//
++// public wrappers
++//
++
++gchar *tmpl_expand ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **ret_err )
++{
++ g_return_val_if_fail ( ret_err == NULL || *ret_err == NULL, NULL );
++
++ GError * error = NULL;
++ gchar * result = tmpl_expand_internal ( template, len, get_val, udata, ret_len, &error, no_eol, NULL );
++ if ( error != NULL )
++ g_propagate_error ( ret_err, error );
++ return result;
++}
++
++/* vim: se ts=4 sw=4: */
+diff -r 456d83274014 mcabber/mcabber/parser.h
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/parser.h Fri Jul 20 17:30:20 2012 +0300
+@@ -0,0 +1,77 @@
++
++#ifndef MCABBER_PARSER_H
++#define MCABBER_PARSER_H
++
++/* Copyright 2012 Myhailo Danylenko
++ *
++ * This file is part of mcabber
++ *
++ * mcabber 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.
++ *
++ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <glib.h>
++
++#define TMPL_GERROR_QUARK tmpl_gerror_quark ()
++
++#define TMPL_EWRONGVAREXP ( 0x01 )
++#define TMPL_EWRONGVARNAME ( 0x03 )
++#define TMPL_EUNBALPAREN ( 0x04 )
++#define TMPL_EWRONGENDSTATE ( 0x02 )
++
++#define TMPL_EMATHWRONGARG ( 0x05 )
++#define TMPL_EMATHWRONGNUMBER ( 0x06 )
++#define TMPL_EMATHUNBALPAREN ( 0x07 )
++#define TMPL_EMATHWRONGOP ( 0x08 )
++#define TMPL_EMATHWRONGSTATE ( 0x09 )
++
++typedef const char *(*tmpl_variable_callback_t) ( const gchar *name, gsize len, gpointer udata, gsize *ret_len );
++
++GQuark tmpl_gerror_quark ( void );
++
++// match tmpl_glob ( string, pattern, right-to-left )
++// Performs matching of given pattern against given string,
++// returns substring, that matched. In pattern, there are currently
++// two tokens recognized: '*' and '?'. Two flags determine, if
++// '*' should be greedy and which end of string must match the
++// pattern (i.e. have anchor).
++const char *tmpl_glob ( const char *str, gsize strlen, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len );
++
++// match tmpl_greedy_glob ( string, pattern, rigt-to-left )
++// The same, as above, but greedy.
++const char *tmpl_greedy_glob ( const char *str, gsize strlen, const char *pat, gsize patlen, gboolean rtl, gsize *ret_len );
++
++// result tmpl_math_expand ( string, callback )
++// Performs mathematical expansion of given string.
++// Note, that $var expressions are not recognized, you have to
++// supply already expanded string here.
++// Supported operators:
++// ( ) + - * / %
++gssize tmpl_math_expand ( const char *str, gsize len, tmpl_variable_callback_t get_val, gpointer udata, GError **error );
++
++// expansion tmpl_expand ( template, callback )
++// Parse template, substitute shell-like expressions:
++// * $var ${var}
++// * ${var##pat} ${var#pat} ${var%%pat} ${var%pat}
++// * ${var/pat} ${var/pat/repl} ${var//pat/repl} ${var/%pat/repl} ${var/#pat/repl}
++// * ${var:+tmpl} ${var:-tmpl}
++// * ${var:mathexp} ${var:mathexp:mathexp}
++// * ${#var} ${!var} ${!var[operation]}
++// * $(( mathexp ))
++// * \n \t \e \$ \\ \X
++// Callback will be called to obtain variable values.
++// Variable name should be: /^[[:alnum:]][[:alnum:]_-]*$/
++// For pattern rules see mms_glob ().
++gchar *tmpl_expand ( const char *template, gsize len, tmpl_variable_callback_t get_val, gpointer udata, gsize *ret_len, GError **error );
++
++# endif
++
+diff -r 456d83274014 mcabber/mcabber/templates.c
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/templates.c Fri Jul 20 17:30:20 2012 +0300
+@@ -0,0 +1,277 @@
++
++/* Copyright 2012 Myhailo Danylenko
++ *
++ * This file is part of mcabber
++ *
++ * mcabber 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.
++ *
++ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <glib.h>
++
++#include <string.h> // strlen
++
++#include "logprint.h"
++#include "settings.h"
++#include "main.h" // main_context
++#include "templates.h"
++#include "parser.h"
++
++//
++// globals
++//
++
++typedef void (*tmpl_callback_t) ( const gchar * expansion, gpointer udata );
++
++typedef struct {
++ gchar * name;
++ tmpl_callback_t callback;
++ gpointer userdata;
++ gboolean in_use;
++ GSList * guards;
++ gboolean changed;
++ gchar * prev_expansion;
++} template_t;
++
++typedef struct {
++ gchar * name;
++ GSList * templates;
++} tmpl_guard_t;
++
++static GHashTable * tmpl_templates = NULL;
++static GHashTable * tmpl_guards = NULL;
++static guint tmpl_attached_id = 0;
++
++//
++// predeclarations
++//
++
++static gchar *tmpl_guard ( const gchar *key, const gchar *new_value );
++
++//
++// code
++//
++
++// [cb] drops template from guard's 'templates' list
++static void tmpl_unguard ( gpointer data, gpointer udata )
++{
++ tmpl_guard_t *guard = data;
++ template_t *template = udata;
++ guard -> templates = g_slist_remove ( guard -> templates, template );
++}
++
++// [destructor cb] releases guard hash table entry
++static void tmpl_free_guard ( gpointer data )
++{
++ tmpl_guard_t *guard = data;
++ settings_del_guard ( guard -> name );
++ g_slist_free ( guard -> templates );
++ g_free ( guard -> name );
++ g_slice_free ( tmpl_guard_t, guard );
++}
++
++// [destructor cb] releases taken guards and frees command
++static void tmpl_free_template ( gpointer data )
++{
++ template_t *template = data;
++ g_slist_foreach ( template -> guards, tmpl_unguard, template );
++ g_slist_free ( template -> guards );
++ g_free ( template -> name );
++ g_free ( template -> prev_expansion );
++ g_slice_free ( template_t, template );
++}
++
++// install guard (name must be glib-allocated string)
++static void tmpl_install_guard ( gchar *name, template_t *template, settings_guard_t callback )
++{
++ tmpl_guard_t *guard = g_hash_table_lookup ( tmpl_guards, name );
++ if ( guard == NULL ) {
++ if ( ! settings_set_guard ( name, callback ) ) {
++ scr_log_print ( LPRINT_LOGNORM, "Error: Unable to install guard for variable '%s' for template '%s'.", name, template -> name );
++ g_free ( name );
++ } else {
++ guard = g_slice_new ( tmpl_guard_t );
++ guard -> name = name;
++ guard -> templates = NULL;
++ g_hash_table_replace ( tmpl_guards, guard -> name, guard ); // to be sure
++ }
++ } else
++ g_free ( name );
++ if ( ! g_slist_find ( template -> guards, guard ) ) {
++ template -> guards = g_slist_prepend ( template -> guards, guard );
++ guard -> templates = g_slist_prepend ( guard -> templates, template );
++ }
++}
++
++// [parser cb] provides mcabber option values & reinstalls guards
++static const char *tmpl_get_var ( const gchar *name, gsize len, gpointer udata, gsize *ret_len )
++{
++ const char *result = NULL;
++ if ( name != NULL && len > 0 ) {
++ template_t * template = udata;
++ gchar * var = g_strndup ( name, len );
++ result = settings_opt_get ( var );
++ // consumes var
++ tmpl_install_guard ( var, template, tmpl_guard );
++ }
++ if ( ret_len != NULL ) {
++ if ( result != NULL )
++ *ret_len = strlen ( result );
++ else
++ *ret_len = 0;
++ }
++ return result;
++}
++
++// [cb] mark unused guards for removal
++static gboolean tmpl_drop_unused_guards ( gpointer key, gpointer value, gpointer udata )
++{
++ tmpl_guard_t * guard = value;
++ if ( guard -> templates == NULL )
++ return TRUE;
++ return FALSE;
++}
++
++// [cb] mark deleted templates for removal, reevaluate changed
++static gboolean reevaluate_template ( gpointer key, gpointer value, gpointer udata )
++{
++ template_t * template = value;
++
++ if ( ! template -> in_use )
++ return TRUE;
++
++ if ( template -> changed ) {
++ const gchar * expression = settings_opt_get ( template -> name );
++ gchar * expansion = NULL;
++ // release guards (but do not free them)
++ g_slist_foreach ( template -> guards, tmpl_unguard, template );
++ g_slist_free ( template -> guards );
++ template -> guards = NULL;
++ // re-install guards & get updated expansion
++ if ( expression != NULL ) {
++ GError *error = NULL;
++ expansion = tmpl_expand ( expression, strlen ( expression ), tmpl_get_var, template, NULL, &error );
++ if ( error != NULL ) {
++ scr_log_print ( LPRINT_LOGNORM, "Error: Expansion error on template '%s': %s.\nExpansion stopped at: '%s'", template -> name, error -> message, expansion );
++ g_error_free ( error );
++ g_free ( expansion );
++ expansion = NULL;
++ }
++ }
++ // re-install guard on template itself
++ tmpl_install_guard ( g_strdup ( template -> name ), template, tmpl_guard );
++ template -> changed = FALSE;
++ // pass result to callback
++ if ( g_strcmp0 ( expansion, template -> prev_expansion ) ) {
++ g_free ( template -> prev_expansion );
++ template -> prev_expansion = expansion;
++ template -> callback ( expansion, template -> userdata );
++ } else
++ g_free ( expansion );
++ }
++
++ return FALSE;
++}
++
++// [idle cb] update commands/guards & call cbs when necessary
++static gboolean reevaluate_templates ( gpointer data )
++{
++ // allow reschedule in a process of reevaluation
++ tmpl_attached_id = 0;
++ // drop removed & reevaluate changed templates
++ g_hash_table_foreach_remove ( tmpl_templates, reevaluate_template, NULL );
++ // free unused guards TODO do only when needed
++ g_hash_table_foreach_remove ( tmpl_guards, tmpl_drop_unused_guards, NULL );
++ // always return false, this is oneshot idle call
++ return FALSE;
++}
++
++// schedule templates reevaluation
++static void tmpl_schedule_rerun ( void )
++{
++ if ( tmpl_attached_id == 0 ) {
++ GSource * source = g_idle_source_new ();
++ g_source_set_callback ( source, reevaluate_templates, NULL, NULL );
++ tmpl_attached_id = g_source_attach ( source, main_context );
++ g_source_unref ( source );
++ }
++}
++
++// [cb] sets changed flag on template
++static void template_set_changed ( gpointer data, gpointer udata )
++{
++ template_t * template = data;
++ template -> changed = TRUE;
++}
++
++// [guard] generic guard for variable
++static gchar *tmpl_guard ( const gchar *key, const gchar *new_value )
++{
++ if ( g_strcmp0 ( new_value, settings_opt_get ( key ) ) ) {
++ // mark dependent commands as modified
++ tmpl_guard_t *guard = g_hash_table_lookup ( tmpl_guards, key );
++ g_slist_foreach ( guard -> templates, template_set_changed, NULL );
++ // schedule execution of modified commands
++ tmpl_schedule_rerun ();
++ }
++ return g_strdup ( new_value );
++}
++
++// public
++gboolean template_add ( const gchar * name, tmpl_callback_t callback, gpointer udata )
++{
++ g_assert ( name != NULL && callback != NULL );
++ // check for existing template
++ template_t * template = g_hash_table_lookup ( tmpl_templates, name );
++ if ( template != NULL )
++ return FALSE;
++ // create new
++ template = g_slice_new ( template_t );
++ template -> name = g_strdup ( name );
++ template -> callback = callback;
++ template -> userdata = udata;
++ template -> in_use = TRUE;
++ template -> guards = NULL;
++ template -> changed = TRUE;
++ template -> prev_expansion = NULL;
++ // schedule reevaluation
++ tmpl_schedule_rerun ();
++ return TRUE;
++}
++
++// public
++void template_set_in_use ( const gchar * name, gboolean in_use )
++{
++ g_assert ( name != NULL );
++ template_t * template = g_hash_table_lookup ( tmpl_templates, name );
++ g_assert ( template != NULL );
++ template -> in_use = in_use;
++}
++
++// private
++void templates_init ( void )
++{
++ // the key will be freed by destruction cb
++ tmpl_guards = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, tmpl_free_guard );
++ tmpl_templates = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, tmpl_free_template );
++}
++
++// private
++void templates_uninit ( void )
++{
++ if ( tmpl_attached_id != 0 )
++ g_source_remove ( tmpl_attached_id );
++ g_hash_table_destroy ( tmpl_templates );
++ g_hash_table_destroy ( tmpl_guards );
++}
++
++/* vim: se ts=4 sw=4: */
+diff -r 456d83274014 mcabber/mcabber/templates.h
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/mcabber/mcabber/templates.h Fri Jul 20 17:30:20 2012 +0300
+@@ -0,0 +1,44 @@
++
++/* Copyright 2012 Myhailo Danylenko
++ *
++ * This file is part of mcabber
++ *
++ * mcabber 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.
++ *
++ * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <glib.h>
++
++// Type for template callback
++typedef void (*tmpl_callback_t) ( const gchar * expansion, gpointer udata );
++
++// success template_add ( option name, cb, cb udata )
++// Adds given mcabber option to list of watched templates.
++// If any option, used in that template (or template itself) will change,
++// callback will be called with new expansion of template.
++gboolean template_add ( const gchar * name, tmpl_callback_t callback, gpointer udata );
++
++// template_set_in_use ( option name, used flag )
++// Marks template as (un)used.
++// Note: Template will be actually removed only on next evaluation run,
++// though call to this function schedules such run. This way, you can
++// mark a bunch of templates as unused and then mark some of them as used.
++void template_set_in_use ( const gchar * name, gboolean in_use );
++
++// XXX do we need this?
++// void tmpl_schedule_rerun ( void );
++
++// private
++void templates_init ( void );
++void templates_uninit ( void );
++
++/* vim: se ts=4 sw=4: */
+diff -r 456d83274014 mcabber/mcabber/utils.c
+--- a/mcabber/mcabber/utils.c Fri Jul 20 17:29:53 2012 +0300
++++ b/mcabber/mcabber/utils.c Fri Jul 20 17:30:20 2012 +0300
+@@ -650,6 +650,42 @@
+ g_free(arglst);
+ }
+
++// parse_list(arg, cb, udata)
++// Calls cb for every element in space/semicolon/comma-separated list.
++// Designed to work in-place, so, no escapes, quoting etc.
++// Terminates parsing if callback returns false.
++void parse_list(const char *arg, parse_list_cb_t cb, void *udata)
++{
++ const char *p, *start;
++ enum {
++ in_space,
++ in_string,
++ } state;
++
++ if (!arg)
++ return;
++
++ state = in_space;
++ while ( *p ) {
++ if ( *p == ' ' || *p == ';' || *p == ',' ) {
++ if ( state == in_string ) {
++ if ( ! cb ( start, p, udata ) )
++ return;
++ state = in_space;
++ }
++ } else {
++ if ( state == in_space ) {
++ start = p;
++ state = in_string;
++ }
++ }
++ p ++;
++ }
++
++ if ( state == in_string )
++ cb ( start, p, udata );
++}
++
+ // replace_nl_with_dots(bufstr)
+ // Replace '\n' with "(...)" (or with a NUL if the string is too short)
+ void replace_nl_with_dots(char *bufstr)
+diff -r 456d83274014 mcabber/mcabber/utils.h
+--- a/mcabber/mcabber/utils.h Fri Jul 20 17:29:53 2012 +0300
++++ b/mcabber/mcabber/utils.h Fri Jul 20 17:30:20 2012 +0300
+@@ -43,6 +43,11 @@
+ char **split_arg(const char *arg, unsigned int n, int dontstriplast);
+ void free_arg_lst(char **arglst);
+
++/* fast in-place string split on space/semicolon/comma
++ * stops processing if callback returns false value */
++typedef int (*parse_list_cb_t)(const char *start, const char *end, void *udata);
++void parse_list(const char *arg, parse_list_cb_t cb, void *udata);
++
+ void replace_nl_with_dots(char *bufstr);
+ char *ut_expand_tabs(const char *text);
+
--- a/update-pc Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-# HG changeset patch
-# Parent ec1520e78aae04d3516b09c694ca6f3c2d4967de
-Make linking with mcabber deps optional
-
-diff -r ec1520e78aae -r 9d2c1bf277e5 mcabber/mcabber.pc.in
---- a/mcabber/mcabber.pc.in Thu Jul 19 08:01:36 2012 +0300
-+++ b/mcabber/mcabber.pc.in Thu Jul 19 08:01:53 2012 +0300
-@@ -5,7 +5,8 @@
-
- Name: MCabber
- Description: Modular XMPP client
--Requires: glib-2.0 gmodule-2.0 loudmouth-1.0
-+URL: http://mcabber.com
-+Requires.private: glib-2.0 gmodule-2.0 loudmouth-1.0
- Version: @VERSION@
- Libs:
- Cflags: -I${includedir} @LIBOTR_CFLAGS@ @GPGME_CFLAGS@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/update-pc.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,17 @@
+# HG changeset patch
+# Parent ec1520e78aae04d3516b09c694ca6f3c2d4967de
+Make linking with mcabber deps optional
+
+diff -r ec1520e78aae -r 9d2c1bf277e5 mcabber/mcabber.pc.in
+--- a/mcabber/mcabber.pc.in Thu Jul 19 08:01:36 2012 +0300
++++ b/mcabber/mcabber.pc.in Thu Jul 19 08:01:53 2012 +0300
+@@ -5,7 +5,8 @@
+
+ Name: MCabber
+ Description: Modular XMPP client
+-Requires: glib-2.0 gmodule-2.0 loudmouth-1.0
++URL: http://mcabber.com
++Requires.private: glib-2.0 gmodule-2.0 loudmouth-1.0
+ Version: @VERSION@
+ Libs:
+ Cflags: -I${includedir} @LIBOTR_CFLAGS@ @GPGME_CFLAGS@
--- a/use-gslice Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,434 +0,0 @@
-# HG changeset patch
-# Parent 70b1f1918050fa1126a21f4772d5d644799af32b
-[work-in-progress] Use glib slices to allocate constant-size blocks
-
-diff -r 70b1f1918050 mcabber/mcabber/caps.c
---- a/mcabber/mcabber/caps.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/caps.c Fri Jul 20 17:51:21 2012 +0300
-@@ -52,7 +52,7 @@
- g_hash_table_destroy(c->identities);
- g_hash_table_destroy(c->features);
- g_hash_table_destroy(c->forms);
-- g_free(c);
-+ g_slice_free(caps, c);
- }
-
- void identity_destroy(gpointer data)
-@@ -61,14 +61,14 @@
- g_free(i->category);
- g_free(i->type);
- g_free(i->name);
-- g_free(i);
-+ g_slice_free(identity, i);
- }
-
- void form_destroy(gpointer data)
- {
- dataform *f = data;
- g_hash_table_destroy(f->fields);
-- g_free(f);
-+ g_slice_free(dataform, f);
- }
-
- void field_destroy(gpointer data)
-@@ -97,7 +97,7 @@
- {
- if (!hash)
- return;
-- caps *c = g_new0(caps, 1);
-+ caps *c = g_slice_new(caps);
- c->features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- c->identities = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, identity_destroy);
- c->forms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, form_destroy);
-@@ -158,7 +158,7 @@
-
- c = g_hash_table_lookup(caps_cache, hash);
- if (c) {
-- identity *i = g_new0(identity, 1);
-+ identity *i = g_slice_new(identity);
-
- i->category = g_strdup(category);
- i->name = g_strdup(name);
-@@ -182,7 +182,7 @@
- return;
- c = g_hash_table_lookup(caps_cache, hash);
- if (c) {
-- dataform *d = g_new0(dataform, 1);
-+ dataform *d = g_slice_new(dataform);
- char *f = g_strdup(formtype);
-
- d->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, field_destroy);
-diff -r 70b1f1918050 mcabber/mcabber/commands.c
---- a/mcabber/mcabber/commands.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/commands.c Fri Jul 20 17:51:21 2012 +0300
-@@ -114,7 +114,7 @@
- gpointer userdata = command->userdata;
- Commands = g_slist_delete_link(Commands, sl_cmd);
- compl_del_category_word(COMPL_CMD, command->name);
-- g_free(command);
-+ g_slice_free(cmd, command);
- return userdata;
- }
- return NULL;
-@@ -126,7 +126,7 @@
- gpointer cmd_add(const char *name, const char *help, guint flags_row1,
- guint flags_row2, void (*f)(char*), gpointer userdata)
- {
-- cmd *n_cmd = g_new0(cmd, 1);
-+ cmd *n_cmd = g_slice_new0(cmd);
- strncpy(n_cmd->name, name, 32-1);
- n_cmd->help = help;
- n_cmd->completion_flags[0] = flags_row1;
-@@ -1579,7 +1579,7 @@
- return NULL;
- }
-
-- msgbuf = g_new0(char, HBB_BLOCKSIZE);
-+ msgbuf = g_slice_alloc0(HBB_BLOCKSIZE);
- len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd);
- fclose(fd);
-
-@@ -1603,7 +1603,7 @@
- if (*p || (size_t)(p-msgbuf) != len) { // We're not at the End Of Line...
- scr_LogPrint(LPRINT_LOGNORM, "Message file contains "
- "invalid characters (%s)", filename);
-- g_free(msgbuf);
-+ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
- return NULL;
- }
-
-@@ -1617,7 +1617,7 @@
- // It could be empty, once the trailing newlines are gone
- if (p == msgbuf && *p == '\n') {
- scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
-- g_free(msgbuf);
-+ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
- return NULL;
- }
-
-@@ -1626,7 +1626,7 @@
- if (!msgbuf_utf8 && msgbuf)
- scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)",
- filename);
-- g_free(msgbuf);
-+ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
- return msgbuf_utf8;
- }
-
-@@ -1898,7 +1898,7 @@
- esub = buddy_getsubscription(bud);
- on_srv = buddy_getonserverflag(bud);
-
-- buffer = g_new(char, 4096);
-+ buffer = g_slice_alloc(4096);
-
- if (bjid) {
- GSList *resources, *p_res;
-@@ -1997,7 +1997,7 @@
- type == ROSTER_TYPE_GROUP ? "group" :
- (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown"));
- }
-- g_free(buffer);
-+ g_slice_free1(4096, buffer);
-
- // Tell the user if this item has an annotation.
- if (type == ROSTER_TYPE_USER ||
-@@ -2045,7 +2045,7 @@
-
- bjid = buddy_getjid(bud);
-
-- buffer = g_new(char, 4096);
-+ buffer = g_slice_alloc(4096);
- strncpy(buffer, "Room members:", 127);
- scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
-
-@@ -2100,7 +2100,7 @@
- g_free(p_res->data);
- }
- g_slist_free(resources);
-- g_free(buffer);
-+ g_slice_free1(4096, buffer);
- }
-
- static void move_group_member(gpointer bud, void *groupnamedata)
-@@ -3091,7 +3091,7 @@
- affil = buddy_getaffil(bud, nick);
- realjid = buddy_getrjid(bud, nick);
-
-- buffer = g_new(char, 4096);
-+ buffer = g_slice_alloc(4096);
-
- snprintf(buffer, 4095, "Whois [%s]", nick);
- scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag, 0);
-@@ -3121,7 +3121,7 @@
-
- scr_WriteIncomingMessage(bjid, "End of WHOIS", 0, msg_flag, 0);
-
-- g_free(buffer);
-+ g_slice_free1(4096, buffer);
- g_free(nick);
- if (paramlst)
- free_arg_lst(paramlst);
-diff -r 70b1f1918050 mcabber/mcabber/events.c
---- a/mcabber/mcabber/events.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/events.c Fri Jul 20 17:51:21 2012 +0300
-@@ -83,7 +83,7 @@
- return NULL;
- }
-
-- event = g_new(evs_t, 1);
-+ event = g_slice_new(evs_t);
-
- event->id = stridn;
- event->description = g_strdup(desc);
-@@ -134,7 +134,7 @@
- evs_list = g_slist_remove(evs_list, event);
- g_free(event->id);
- g_free(event->description);
-- g_free(event);
-+ g_slice_free(evs_t, event);
-
- return 0; // Ok, deleted
- }
-@@ -213,7 +213,7 @@
- evs_list = g_slist_remove(evs_list, event);
- g_free(event->id);
- g_free(event->description);
-- g_free(event);
-+ g_slice_free(evs_t, event);
- }
- g_slist_free(evs_list);
- evs_list = NULL;
-diff -r 70b1f1918050 mcabber/mcabber/hbuf.c
---- a/mcabber/mcabber/hbuf.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/hbuf.c Fri Jul 20 17:51:21 2012 +0300
-@@ -29,7 +29,6 @@
- #include "utf8.h"
- #include "screen.h"
-
--
- /* This is a private structure type */
-
- typedef struct {
-@@ -59,7 +58,7 @@
- {
- GList *curr_elt = first_hbuf_elt;
-
-- // Let's add non-persistent blocs if necessary
-+ // Let's add non-persistent blocks if necessary
- // - If there are '\n' in the string
- // - If length > width (and width != 0)
- while (curr_elt) {
-@@ -95,17 +94,21 @@
- end = hbuf_b_curr->ptr_end;
- hbuf_b_curr->ptr_end = br;
- // Create another block
-- hbuf_b_curr = g_new0(hbuf_block, 1);
-+ hbuf_b_curr = g_slice_new(hbuf_block);
- // The block must be persistent after a CR
- if (cr) {
- hbuf_b_curr->ptr = hbuf_b_prev->ptr_end + 1; // == cr+1
- hbuf_b_curr->flags = HBB_FLAG_PERSISTENT;
- } else {
- hbuf_b_curr->ptr = hbuf_b_prev->ptr_end; // == br
-- hbuf_b_curr->flags = 0;
-+ hbuf_b_curr->flags = 0;
- }
-- hbuf_b_curr->ptr_end = end;
-+ hbuf_b_curr->ptr_end = end;
- hbuf_b_curr->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
-+ hbuf_b_curr->prefix.timestamp = 0;
-+ hbuf_b_curr->prefix.flags = 0;
-+ hbuf_b_curr->prefix.mucnicklen = 0;
-+ hbuf_b_curr->prefix.xep184 = NULL;
- // This is OK because insert_before(NULL) == append():
- *p_hbuf = g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
- }
-@@ -138,7 +141,7 @@
- textlen = strlen(text);
- hbb_blocksize = MAX(textlen+1, HBB_BLOCKSIZE);
-
-- hbuf_block_elt = g_new0(hbuf_block, 1);
-+ hbuf_block_elt = g_slice_new(hbuf_block);
- hbuf_block_elt->prefix.timestamp = timestamp;
- hbuf_block_elt->prefix.flags = prefix_flags;
- hbuf_block_elt->prefix.mucnicklen = mucnicklen;
-@@ -146,7 +149,7 @@
- if (!*p_hbuf) {
- hbuf_block_elt->ptr = g_new(char, hbb_blocksize);
- if (!hbuf_block_elt->ptr) {
-- g_free(hbuf_block_elt);
-+ g_slice_free(hbuf_block, hbuf_block_elt);
- return;
- }
- hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
-@@ -208,7 +211,7 @@
- g_free(hbuf_b_elt->ptr);
- }
- }
-- g_free(hbuf_b_elt);
-+ g_slice_free(hbuf_block, hbuf_b_elt);
- hbuf_head = *p_hbuf = g_list_delete_link(hbuf_head, hbuf_elt);
- }
- n--;
-@@ -245,7 +248,7 @@
- if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
- g_free(hbuf_b_elt->ptr);
- }
-- g_free(hbuf_b_elt);
-+ g_slice_free(hbuf_block, hbuf_b_elt);
- }
-
- g_list_free(first_elt);
-@@ -275,7 +278,7 @@
- // Is next line not-persistent?
- if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
- hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
-- g_free(hbuf_b_next);
-+ g_slice_free(hbuf_block, hbuf_b_next);
- curr_elt = g_list_delete_link(curr_elt, next_elt);
- } else
- curr_elt = next_elt;
-@@ -309,8 +312,8 @@
- // hbuf_get_lines(hbuf, n)
- // Returns an array of n hbb_line pointers
- // (The first line will be the line currently pointed by hbuf)
--// Note: The caller should free the array, the hbb_line pointers and the
--// text pointers after use.
-+// Note: The caller should g_free the array, g_slice_free hbb_line pointers
-+// and g_free text pointers after use.
- hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n)
- {
- unsigned int i;
-@@ -349,7 +352,7 @@
-
- blk = (hbuf_block*)(hbuf->data);
- maxlen = blk->ptr_end - blk->ptr;
-- *array_elt = (hbb_line*)g_new(hbb_line, 1);
-+ *array_elt = (hbb_line*)g_slice_new(hbb_line);
- (*array_elt)->timestamp = blk->prefix.timestamp;
- (*array_elt)->flags = blk->prefix.flags;
- (*array_elt)->mucnicklen = blk->prefix.mucnicklen;
-diff -r 70b1f1918050 mcabber/mcabber/hooks.c
---- a/mcabber/mcabber/hooks.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/hooks.c Fri Jul 20 17:51:21 2012 +0300
-@@ -99,7 +99,7 @@
- gint priority, gpointer userdata)
- {
- GSList **hqueue = NULL;
-- hook_list_data_t *h = g_new(hook_list_data_t, 1);
-+ hook_list_data_t *h = g_slice_new(hook_list_data_t);
-
- h->handler = handler;
- h->priority = priority;
-@@ -148,7 +148,7 @@
- el = g_slist_find_custom(*hqueue, &hid,
- (GCompareFunc)_hk_queue_search_cb);
- if (el) {
-- g_free(el->data);
-+ g_slice_free(hook_list_data_t, el->data);
- *hqueue = g_slist_delete_link(*hqueue, el);
- // Remove hook hash table entry if the hook queue is empty
- if (!*hqueue)
-diff -r 70b1f1918050 mcabber/mcabber/modules.c
---- a/mcabber/mcabber/modules.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/modules.c Fri Jul 20 17:51:21 2012 +0300
-@@ -176,7 +176,7 @@
- }
-
- { // Register module
-- loaded_module_t *module = g_new(loaded_module_t, 1);
-+ loaded_module_t *module = g_slice_new(loaded_module_t);
-
- module->refcount = 1;
- module->locked = manual;
-@@ -267,7 +267,7 @@
- // Output this here, as arg may point to module->name
- scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", module->name);
- g_free(module->name);
-- g_free(module);
-+ g_slice_free(loaded_module_t, module);
-
- return NULL;
- }
-diff -r 70b1f1918050 mcabber/mcabber/screen.c
---- a/mcabber/mcabber/screen.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/screen.c Fri Jul 20 17:51:21 2012 +0300
-@@ -1305,7 +1305,7 @@
- wattrset(win_entry->win, get_color(COLOR_GENERAL));
-
- g_free(line->text);
-- g_free(line);
-+ g_slice_free(hbb_line, line);
- } else {
- wclrtobot(win_entry->win);
- break;
-diff -r 70b1f1918050 mcabber/mcabber/settings.c
---- a/mcabber/mcabber/settings.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/settings.c Fri Jul 20 17:51:21 2012 +0300
-@@ -453,7 +453,7 @@
- // If value is 0, we do not need to create a structure (that's
- // the default value).
- if (value) {
-- pgpdata = g_new0(T_pgpopt, 1);
-+ pgpdata = g_slice_new0(T_pgpopt);
- pgpdata->pgp_disabled = value;
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
-@@ -493,7 +493,7 @@
- // If value is 0, we do not need to create a structure (that's
- // the default value).
- if (value) {
-- pgpdata = g_new0(T_pgpopt, 1);
-+ pgpdata = g_slice_new0(T_pgpopt);
- pgpdata->pgp_force = value;
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
-@@ -533,7 +533,7 @@
- // If keyid is NULL, we do not need to create a structure (that's
- // the default value).
- if (keyid) {
-- pgpdata = g_new0(T_pgpopt, 1);
-+ pgpdata = g_slice_new0(T_pgpopt);
- pgpdata->pgp_keyid = g_strdup(keyid);
- g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
- }
-diff -r 70b1f1918050 mcabber/mcabber/xmpp_iq.c
---- a/mcabber/mcabber/xmpp_iq.c Fri Jul 20 17:41:15 2012 +0300
-+++ b/mcabber/mcabber/xmpp_iq.c Fri Jul 20 17:51:21 2012 +0300
-@@ -751,7 +751,7 @@
- lm_message_get_from(m));
- }
-
-- buf = g_new0(char, 512);
-+ buf = g_slice_alloc(512);
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "query", NULL);
-@@ -778,7 +778,7 @@
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
-- g_free(buf);
-+ g_slice_free1(512, buf);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
-@@ -801,7 +801,7 @@
- lm_message_get_from(m));
- }
-
-- buf = g_new0(char, 512);
-+ buf = g_slice_alloc(512);
-
- r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
- query = lm_message_node_add_child(r->node, "time", NULL);
-@@ -838,7 +838,7 @@
-
- lm_connection_send(c, r, NULL);
- lm_message_unref(r);
-- g_free(buf);
-+ g_slice_free1(512, buf);
- return LM_HANDLER_RESULT_REMOVE_MESSAGE;
- }
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/use-gslice.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,434 @@
+# HG changeset patch
+# Parent 70b1f1918050fa1126a21f4772d5d644799af32b
+[work-in-progress] Use glib slices to allocate constant-size blocks
+
+diff -r 70b1f1918050 mcabber/mcabber/caps.c
+--- a/mcabber/mcabber/caps.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/caps.c Fri Jul 20 17:51:21 2012 +0300
+@@ -52,7 +52,7 @@
+ g_hash_table_destroy(c->identities);
+ g_hash_table_destroy(c->features);
+ g_hash_table_destroy(c->forms);
+- g_free(c);
++ g_slice_free(caps, c);
+ }
+
+ void identity_destroy(gpointer data)
+@@ -61,14 +61,14 @@
+ g_free(i->category);
+ g_free(i->type);
+ g_free(i->name);
+- g_free(i);
++ g_slice_free(identity, i);
+ }
+
+ void form_destroy(gpointer data)
+ {
+ dataform *f = data;
+ g_hash_table_destroy(f->fields);
+- g_free(f);
++ g_slice_free(dataform, f);
+ }
+
+ void field_destroy(gpointer data)
+@@ -97,7 +97,7 @@
+ {
+ if (!hash)
+ return;
+- caps *c = g_new0(caps, 1);
++ caps *c = g_slice_new(caps);
+ c->features = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ c->identities = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, identity_destroy);
+ c->forms = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, form_destroy);
+@@ -158,7 +158,7 @@
+
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (c) {
+- identity *i = g_new0(identity, 1);
++ identity *i = g_slice_new(identity);
+
+ i->category = g_strdup(category);
+ i->name = g_strdup(name);
+@@ -182,7 +182,7 @@
+ return;
+ c = g_hash_table_lookup(caps_cache, hash);
+ if (c) {
+- dataform *d = g_new0(dataform, 1);
++ dataform *d = g_slice_new(dataform);
+ char *f = g_strdup(formtype);
+
+ d->fields = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, field_destroy);
+diff -r 70b1f1918050 mcabber/mcabber/commands.c
+--- a/mcabber/mcabber/commands.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/commands.c Fri Jul 20 17:51:21 2012 +0300
+@@ -114,7 +114,7 @@
+ gpointer userdata = command->userdata;
+ Commands = g_slist_delete_link(Commands, sl_cmd);
+ compl_del_category_word(COMPL_CMD, command->name);
+- g_free(command);
++ g_slice_free(cmd, command);
+ return userdata;
+ }
+ return NULL;
+@@ -126,7 +126,7 @@
+ gpointer cmd_add(const char *name, const char *help, guint flags_row1,
+ guint flags_row2, void (*f)(char*), gpointer userdata)
+ {
+- cmd *n_cmd = g_new0(cmd, 1);
++ cmd *n_cmd = g_slice_new0(cmd);
+ strncpy(n_cmd->name, name, 32-1);
+ n_cmd->help = help;
+ n_cmd->completion_flags[0] = flags_row1;
+@@ -1579,7 +1579,7 @@
+ return NULL;
+ }
+
+- msgbuf = g_new0(char, HBB_BLOCKSIZE);
++ msgbuf = g_slice_alloc0(HBB_BLOCKSIZE);
+ len = fread(msgbuf, 1, HBB_BLOCKSIZE-1, fd);
+ fclose(fd);
+
+@@ -1603,7 +1603,7 @@
+ if (*p || (size_t)(p-msgbuf) != len) { // We're not at the End Of Line...
+ scr_LogPrint(LPRINT_LOGNORM, "Message file contains "
+ "invalid characters (%s)", filename);
+- g_free(msgbuf);
++ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
+ return NULL;
+ }
+
+@@ -1617,7 +1617,7 @@
+ // It could be empty, once the trailing newlines are gone
+ if (p == msgbuf && *p == '\n') {
+ scr_LogPrint(LPRINT_LOGNORM, "Message file is empty (%s)", filename);
+- g_free(msgbuf);
++ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
+ return NULL;
+ }
+
+@@ -1626,7 +1626,7 @@
+ if (!msgbuf_utf8 && msgbuf)
+ scr_LogPrint(LPRINT_LOGNORM, "Message file charset conversion error (%s)",
+ filename);
+- g_free(msgbuf);
++ g_slice_free1(HBB_BLOCKSIZE, msgbuf);
+ return msgbuf_utf8;
+ }
+
+@@ -1898,7 +1898,7 @@
+ esub = buddy_getsubscription(bud);
+ on_srv = buddy_getonserverflag(bud);
+
+- buffer = g_new(char, 4096);
++ buffer = g_slice_alloc(4096);
+
+ if (bjid) {
+ GSList *resources, *p_res;
+@@ -1997,7 +1997,7 @@
+ type == ROSTER_TYPE_GROUP ? "group" :
+ (type == ROSTER_TYPE_SPECIAL ? "special" : "unknown"));
+ }
+- g_free(buffer);
++ g_slice_free1(4096, buffer);
+
+ // Tell the user if this item has an annotation.
+ if (type == ROSTER_TYPE_USER ||
+@@ -2045,7 +2045,7 @@
+
+ bjid = buddy_getjid(bud);
+
+- buffer = g_new(char, 4096);
++ buffer = g_slice_alloc(4096);
+ strncpy(buffer, "Room members:", 127);
+ scr_WriteIncomingMessage(bjid, buffer, 0, HBB_PREFIX_INFO, 0);
+
+@@ -2100,7 +2100,7 @@
+ g_free(p_res->data);
+ }
+ g_slist_free(resources);
+- g_free(buffer);
++ g_slice_free1(4096, buffer);
+ }
+
+ static void move_group_member(gpointer bud, void *groupnamedata)
+@@ -3091,7 +3091,7 @@
+ affil = buddy_getaffil(bud, nick);
+ realjid = buddy_getrjid(bud, nick);
+
+- buffer = g_new(char, 4096);
++ buffer = g_slice_alloc(4096);
+
+ snprintf(buffer, 4095, "Whois [%s]", nick);
+ scr_WriteIncomingMessage(bjid, buffer, 0, msg_flag, 0);
+@@ -3121,7 +3121,7 @@
+
+ scr_WriteIncomingMessage(bjid, "End of WHOIS", 0, msg_flag, 0);
+
+- g_free(buffer);
++ g_slice_free1(4096, buffer);
+ g_free(nick);
+ if (paramlst)
+ free_arg_lst(paramlst);
+diff -r 70b1f1918050 mcabber/mcabber/events.c
+--- a/mcabber/mcabber/events.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/events.c Fri Jul 20 17:51:21 2012 +0300
+@@ -83,7 +83,7 @@
+ return NULL;
+ }
+
+- event = g_new(evs_t, 1);
++ event = g_slice_new(evs_t);
+
+ event->id = stridn;
+ event->description = g_strdup(desc);
+@@ -134,7 +134,7 @@
+ evs_list = g_slist_remove(evs_list, event);
+ g_free(event->id);
+ g_free(event->description);
+- g_free(event);
++ g_slice_free(evs_t, event);
+
+ return 0; // Ok, deleted
+ }
+@@ -213,7 +213,7 @@
+ evs_list = g_slist_remove(evs_list, event);
+ g_free(event->id);
+ g_free(event->description);
+- g_free(event);
++ g_slice_free(evs_t, event);
+ }
+ g_slist_free(evs_list);
+ evs_list = NULL;
+diff -r 70b1f1918050 mcabber/mcabber/hbuf.c
+--- a/mcabber/mcabber/hbuf.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/hbuf.c Fri Jul 20 17:51:21 2012 +0300
+@@ -29,7 +29,6 @@
+ #include "utf8.h"
+ #include "screen.h"
+
+-
+ /* This is a private structure type */
+
+ typedef struct {
+@@ -59,7 +58,7 @@
+ {
+ GList *curr_elt = first_hbuf_elt;
+
+- // Let's add non-persistent blocs if necessary
++ // Let's add non-persistent blocks if necessary
+ // - If there are '\n' in the string
+ // - If length > width (and width != 0)
+ while (curr_elt) {
+@@ -95,17 +94,21 @@
+ end = hbuf_b_curr->ptr_end;
+ hbuf_b_curr->ptr_end = br;
+ // Create another block
+- hbuf_b_curr = g_new0(hbuf_block, 1);
++ hbuf_b_curr = g_slice_new(hbuf_block);
+ // The block must be persistent after a CR
+ if (cr) {
+ hbuf_b_curr->ptr = hbuf_b_prev->ptr_end + 1; // == cr+1
+ hbuf_b_curr->flags = HBB_FLAG_PERSISTENT;
+ } else {
+ hbuf_b_curr->ptr = hbuf_b_prev->ptr_end; // == br
+- hbuf_b_curr->flags = 0;
++ hbuf_b_curr->flags = 0;
+ }
+- hbuf_b_curr->ptr_end = end;
++ hbuf_b_curr->ptr_end = end;
+ hbuf_b_curr->ptr_end_alloc = hbuf_b_prev->ptr_end_alloc;
++ hbuf_b_curr->prefix.timestamp = 0;
++ hbuf_b_curr->prefix.flags = 0;
++ hbuf_b_curr->prefix.mucnicklen = 0;
++ hbuf_b_curr->prefix.xep184 = NULL;
+ // This is OK because insert_before(NULL) == append():
+ *p_hbuf = g_list_insert_before(*p_hbuf, curr_elt->next, hbuf_b_curr);
+ }
+@@ -138,7 +141,7 @@
+ textlen = strlen(text);
+ hbb_blocksize = MAX(textlen+1, HBB_BLOCKSIZE);
+
+- hbuf_block_elt = g_new0(hbuf_block, 1);
++ hbuf_block_elt = g_slice_new(hbuf_block);
+ hbuf_block_elt->prefix.timestamp = timestamp;
+ hbuf_block_elt->prefix.flags = prefix_flags;
+ hbuf_block_elt->prefix.mucnicklen = mucnicklen;
+@@ -146,7 +149,7 @@
+ if (!*p_hbuf) {
+ hbuf_block_elt->ptr = g_new(char, hbb_blocksize);
+ if (!hbuf_block_elt->ptr) {
+- g_free(hbuf_block_elt);
++ g_slice_free(hbuf_block, hbuf_block_elt);
+ return;
+ }
+ hbuf_block_elt->flags = HBB_FLAG_ALLOC | HBB_FLAG_PERSISTENT;
+@@ -208,7 +211,7 @@
+ g_free(hbuf_b_elt->ptr);
+ }
+ }
+- g_free(hbuf_b_elt);
++ g_slice_free(hbuf_block, hbuf_b_elt);
+ hbuf_head = *p_hbuf = g_list_delete_link(hbuf_head, hbuf_elt);
+ }
+ n--;
+@@ -245,7 +248,7 @@
+ if (hbuf_b_elt->flags & HBB_FLAG_ALLOC) {
+ g_free(hbuf_b_elt->ptr);
+ }
+- g_free(hbuf_b_elt);
++ g_slice_free(hbuf_block, hbuf_b_elt);
+ }
+
+ g_list_free(first_elt);
+@@ -275,7 +278,7 @@
+ // Is next line not-persistent?
+ if (!(hbuf_b_next->flags & HBB_FLAG_PERSISTENT)) {
+ hbuf_b_curr->ptr_end = hbuf_b_next->ptr_end;
+- g_free(hbuf_b_next);
++ g_slice_free(hbuf_block, hbuf_b_next);
+ curr_elt = g_list_delete_link(curr_elt, next_elt);
+ } else
+ curr_elt = next_elt;
+@@ -309,8 +312,8 @@
+ // hbuf_get_lines(hbuf, n)
+ // Returns an array of n hbb_line pointers
+ // (The first line will be the line currently pointed by hbuf)
+-// Note: The caller should free the array, the hbb_line pointers and the
+-// text pointers after use.
++// Note: The caller should g_free the array, g_slice_free hbb_line pointers
++// and g_free text pointers after use.
+ hbb_line **hbuf_get_lines(GList *hbuf, unsigned int n)
+ {
+ unsigned int i;
+@@ -349,7 +352,7 @@
+
+ blk = (hbuf_block*)(hbuf->data);
+ maxlen = blk->ptr_end - blk->ptr;
+- *array_elt = (hbb_line*)g_new(hbb_line, 1);
++ *array_elt = (hbb_line*)g_slice_new(hbb_line);
+ (*array_elt)->timestamp = blk->prefix.timestamp;
+ (*array_elt)->flags = blk->prefix.flags;
+ (*array_elt)->mucnicklen = blk->prefix.mucnicklen;
+diff -r 70b1f1918050 mcabber/mcabber/hooks.c
+--- a/mcabber/mcabber/hooks.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/hooks.c Fri Jul 20 17:51:21 2012 +0300
+@@ -99,7 +99,7 @@
+ gint priority, gpointer userdata)
+ {
+ GSList **hqueue = NULL;
+- hook_list_data_t *h = g_new(hook_list_data_t, 1);
++ hook_list_data_t *h = g_slice_new(hook_list_data_t);
+
+ h->handler = handler;
+ h->priority = priority;
+@@ -148,7 +148,7 @@
+ el = g_slist_find_custom(*hqueue, &hid,
+ (GCompareFunc)_hk_queue_search_cb);
+ if (el) {
+- g_free(el->data);
++ g_slice_free(hook_list_data_t, el->data);
+ *hqueue = g_slist_delete_link(*hqueue, el);
+ // Remove hook hash table entry if the hook queue is empty
+ if (!*hqueue)
+diff -r 70b1f1918050 mcabber/mcabber/modules.c
+--- a/mcabber/mcabber/modules.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/modules.c Fri Jul 20 17:51:21 2012 +0300
+@@ -176,7 +176,7 @@
+ }
+
+ { // Register module
+- loaded_module_t *module = g_new(loaded_module_t, 1);
++ loaded_module_t *module = g_slice_new(loaded_module_t);
+
+ module->refcount = 1;
+ module->locked = manual;
+@@ -267,7 +267,7 @@
+ // Output this here, as arg may point to module->name
+ scr_LogPrint(LPRINT_LOGNORM, "Unloaded module %s.", module->name);
+ g_free(module->name);
+- g_free(module);
++ g_slice_free(loaded_module_t, module);
+
+ return NULL;
+ }
+diff -r 70b1f1918050 mcabber/mcabber/screen.c
+--- a/mcabber/mcabber/screen.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/screen.c Fri Jul 20 17:51:21 2012 +0300
+@@ -1305,7 +1305,7 @@
+ wattrset(win_entry->win, get_color(COLOR_GENERAL));
+
+ g_free(line->text);
+- g_free(line);
++ g_slice_free(hbb_line, line);
+ } else {
+ wclrtobot(win_entry->win);
+ break;
+diff -r 70b1f1918050 mcabber/mcabber/settings.c
+--- a/mcabber/mcabber/settings.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/settings.c Fri Jul 20 17:51:21 2012 +0300
+@@ -453,7 +453,7 @@
+ // If value is 0, we do not need to create a structure (that's
+ // the default value).
+ if (value) {
+- pgpdata = g_new0(T_pgpopt, 1);
++ pgpdata = g_slice_new0(T_pgpopt);
+ pgpdata->pgp_disabled = value;
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+@@ -493,7 +493,7 @@
+ // If value is 0, we do not need to create a structure (that's
+ // the default value).
+ if (value) {
+- pgpdata = g_new0(T_pgpopt, 1);
++ pgpdata = g_slice_new0(T_pgpopt);
+ pgpdata->pgp_force = value;
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+@@ -533,7 +533,7 @@
+ // If keyid is NULL, we do not need to create a structure (that's
+ // the default value).
+ if (keyid) {
+- pgpdata = g_new0(T_pgpopt, 1);
++ pgpdata = g_slice_new0(T_pgpopt);
+ pgpdata->pgp_keyid = g_strdup(keyid);
+ g_hash_table_insert(pgpopt, g_strdup(bjid), pgpdata);
+ }
+diff -r 70b1f1918050 mcabber/mcabber/xmpp_iq.c
+--- a/mcabber/mcabber/xmpp_iq.c Fri Jul 20 17:41:15 2012 +0300
++++ b/mcabber/mcabber/xmpp_iq.c Fri Jul 20 17:51:21 2012 +0300
+@@ -751,7 +751,7 @@
+ lm_message_get_from(m));
+ }
+
+- buf = g_new0(char, 512);
++ buf = g_slice_alloc(512);
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "query", NULL);
+@@ -778,7 +778,7 @@
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+- g_free(buf);
++ g_slice_free1(512, buf);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
+@@ -801,7 +801,7 @@
+ lm_message_get_from(m));
+ }
+
+- buf = g_new0(char, 512);
++ buf = g_slice_alloc(512);
+
+ r = lm_message_new_iq_from_query(m, LM_MESSAGE_SUB_TYPE_RESULT);
+ query = lm_message_node_add_child(r->node, "time", NULL);
+@@ -838,7 +838,7 @@
+
+ lm_connection_send(c, r, NULL);
+ lm_message_unref(r);
+- g_free(buf);
++ g_slice_free1(512, buf);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+ }
+
--- a/warning-fixes Sat Jul 28 19:37:32 2012 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-# HG changeset patch
-# Parent b5ea7efbf1f97502016888c5c7f463bae703c13e
-Fix some compiler warnings
-
-diff -r b5ea7efbf1f9 mcabber/mcabber/commands.c
---- a/mcabber/mcabber/commands.c Thu Jul 19 08:03:37 2012 +0300
-+++ b/mcabber/mcabber/commands.c Thu Jul 19 08:04:52 2012 +0300
-@@ -1949,13 +1949,11 @@
- enum imstatus rstatus;
- const char *rst_msg;
- time_t rst_time;
-- struct pgp_data *rpgp;
-
- rprio = buddy_getresourceprio(bud, p_res->data);
- rstatus = buddy_getstatus(bud, p_res->data);
- rst_msg = buddy_getstatusmsg(bud, p_res->data);
- rst_time = buddy_getstatustime(bud, p_res->data);
-- rpgp = buddy_resource_pgp(bud, p_res->data);
-
- snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus],
- rprio, (char*)p_res->data);
-@@ -1974,6 +1972,8 @@
- 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
- }
- #ifdef HAVE_GPGME
-+ struct pgp_data *rpgp = buddy_resource_pgp(bud, p_res->data);
-+
- if (rpgp && rpgp->sign_keyid) {
- snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid);
- scr_WriteIncomingMessage(bjid, buffer,
-diff -r b5ea7efbf1f9 mcabber/mcabber/xmpp_helper.c
---- a/mcabber/mcabber/xmpp_helper.c Thu Jul 19 08:03:37 2012 +0300
-+++ b/mcabber/mcabber/xmpp_helper.c Thu Jul 19 08:04:52 2012 +0300
-@@ -22,6 +22,7 @@
-
- #include <string.h>
- #include <stdlib.h>
-+#include <stdio.h> // snprintf
-
- #include "xmpp_helper.h"
- #include "settings.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/warning-fixes.diff Sat Jul 28 19:42:13 2012 +0300
@@ -0,0 +1,41 @@
+# HG changeset patch
+# Parent b5ea7efbf1f97502016888c5c7f463bae703c13e
+Fix some compiler warnings
+
+diff -r b5ea7efbf1f9 mcabber/mcabber/commands.c
+--- a/mcabber/mcabber/commands.c Thu Jul 19 08:03:37 2012 +0300
++++ b/mcabber/mcabber/commands.c Thu Jul 19 08:04:52 2012 +0300
+@@ -1949,13 +1949,11 @@
+ enum imstatus rstatus;
+ const char *rst_msg;
+ time_t rst_time;
+- struct pgp_data *rpgp;
+
+ rprio = buddy_getresourceprio(bud, p_res->data);
+ rstatus = buddy_getstatus(bud, p_res->data);
+ rst_msg = buddy_getstatusmsg(bud, p_res->data);
+ rst_time = buddy_getstatustime(bud, p_res->data);
+- rpgp = buddy_resource_pgp(bud, p_res->data);
+
+ snprintf(buffer, 4095, "Resource: [%c] (%d) %s", imstatus2char[rstatus],
+ rprio, (char*)p_res->data);
+@@ -1974,6 +1972,8 @@
+ 0, HBB_PREFIX_INFO | HBB_PREFIX_CONT, 0);
+ }
+ #ifdef HAVE_GPGME
++ struct pgp_data *rpgp = buddy_resource_pgp(bud, p_res->data);
++
+ if (rpgp && rpgp->sign_keyid) {
+ snprintf(buffer, 4095, "PGP key id: %s", rpgp->sign_keyid);
+ scr_WriteIncomingMessage(bjid, buffer,
+diff -r b5ea7efbf1f9 mcabber/mcabber/xmpp_helper.c
+--- a/mcabber/mcabber/xmpp_helper.c Thu Jul 19 08:03:37 2012 +0300
++++ b/mcabber/mcabber/xmpp_helper.c Thu Jul 19 08:04:52 2012 +0300
+@@ -22,6 +22,7 @@
+
+ #include <string.h>
+ #include <stdlib.h>
++#include <stdio.h> // snprintf
+
+ #include "xmpp_helper.h"
+ #include "settings.h"