about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2016-05-23 22:34:45 +0100
committerJames Booth <boothj5@gmail.com>2016-05-23 22:34:45 +0100
commit0edd4309257a8372761f44cbd576b027d1b9766c (patch)
treea34f200941578e4a6d351aacec4965f1c5207149
parent3aa788772ea74724ca49ab8488168144bb287daa (diff)
parentd0117cdabe5803867596c1256a68a22245abf166 (diff)
downloadprofani-tty-0edd4309257a8372761f44cbd576b027d1b9766c.tar.gz
Merge branch 'cmd-tidy'
-rw-r--r--Makefile.am10
-rw-r--r--src/command/cmd_ac.c2918
-rw-r--r--src/command/cmd_ac.h62
-rw-r--r--src/command/cmd_defs.c2367
-rw-r--r--src/command/cmd_defs.h (renamed from src/command/command.h)33
-rw-r--r--src/command/cmd_funcs.c (renamed from src/command/commands.c)310
-rw-r--r--src/command/cmd_funcs.h (renamed from src/command/commands.h)96
-rw-r--r--src/command/command.c5279
-rw-r--r--src/config/scripts.c2
-rw-r--r--src/main.c2
-rw-r--r--src/plugins/api.c2
-rw-r--r--src/plugins/callbacks.c8
-rw-r--r--src/plugins/callbacks.h2
-rw-r--r--src/plugins/plugins.h2
-rw-r--r--src/profanity.c2
-rw-r--r--src/ui/console.c2
-rw-r--r--src/ui/core.c9
-rw-r--r--src/ui/inputwin.c6
-rw-r--r--src/ui/ui.h2
-rw-r--r--tests/unittests/test_cmd_account.c2
-rw-r--r--tests/unittests/test_cmd_alias.c7
-rw-r--r--tests/unittests/test_cmd_bookmark.c2
-rw-r--r--tests/unittests/test_cmd_connect.c2
-rw-r--r--tests/unittests/test_cmd_disconnect.c2
-rw-r--r--tests/unittests/test_cmd_join.c2
-rw-r--r--tests/unittests/test_cmd_otr.c4
-rw-r--r--tests/unittests/test_cmd_pgp.c2
-rw-r--r--tests/unittests/test_cmd_rooms.c2
-rw-r--r--tests/unittests/test_cmd_roster.c2
-rw-r--r--tests/unittests/test_cmd_statuses.c2
-rw-r--r--tests/unittests/test_cmd_sub.c2
31 files changed, 5646 insertions, 5499 deletions
diff --git a/Makefile.am b/Makefile.am
index 52ae369f..a2c023c5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,8 +30,9 @@ core_sources = \
 	src/ui/privwin.c \
 	src/ui/mucconfwin.c \
 	src/ui/xmlwin.c \
-	src/command/command.h src/command/command.c \
-	src/command/commands.h src/command/commands.c \
+	src/command/cmd_defs.h src/command/cmd_defs.c \
+	src/command/cmd_funcs.h src/command/cmd_funcs.c \
+	src/command/cmd_ac.h src/command/cmd_ac.c \
 	src/tools/parser.c \
 	src/tools/parser.h \
 	src/tools/http_upload.c \
@@ -67,8 +68,9 @@ unittest_sources = \
 	src/ui/ui.h \
 	src/otr/otr.h \
 	src/pgp/gpg.h \
-	src/command/command.h src/command/command.c \
-	src/command/commands.h src/command/commands.c \
+	src/command/cmd_defs.h src/command/cmd_defs.c \
+	src/command/cmd_funcs.h src/command/cmd_funcs.c \
+	src/command/cmd_ac.h src/command/cmd_ac.c \
 	src/tools/parser.c \
 	src/tools/parser.h \
 	src/tools/p_sha1.h src/tools/p_sha1.c \
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
new file mode 100644
index 00000000..4ecbdae3
--- /dev/null
+++ b/src/command/cmd_ac.c
@@ -0,0 +1,2918 @@
+/*
+ * cmd_ac.c
+ *
+ * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#define _GNU_SOURCE 1
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <libgen.h>
+#include <dirent.h>
+
+#include "common.h"
+#include "tools/parser.h"
+#include "ui/win_types.h"
+#include "command/cmd_funcs.h"
+#include "config/preferences.h"
+#include "config/scripts.h"
+#include "muc.h"
+#include "xmpp/xmpp.h"
+#include "roster_list.h"
+#include "window_list.h"
+#include "plugins/plugins.h"
+#include "command/cmd_ac.h"
+
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
+
+static char* _sub_autocomplete(ProfWin *window, const char *const input);
+static char* _notify_autocomplete(ProfWin *window, const char *const input);
+static char* _theme_autocomplete(ProfWin *window, const char *const input);
+static char* _autoaway_autocomplete(ProfWin *window, const char *const input);
+static char* _autoconnect_autocomplete(ProfWin *window, const char *const input);
+static char* _account_autocomplete(ProfWin *window, const char *const input);
+static char* _who_autocomplete(ProfWin *window, const char *const input);
+static char* _roster_autocomplete(ProfWin *window, const char *const input);
+static char* _group_autocomplete(ProfWin *window, const char *const input);
+static char* _bookmark_autocomplete(ProfWin *window, const char *const input);
+static char* _otr_autocomplete(ProfWin *window, const char *const input);
+static char* _pgp_autocomplete(ProfWin *window, const char *const input);
+static char* _connect_autocomplete(ProfWin *window, const char *const input);
+static char* _statuses_autocomplete(ProfWin *window, const char *const input);
+static char* _alias_autocomplete(ProfWin *window, const char *const input);
+static char* _join_autocomplete(ProfWin *window, const char *const input);
+static char* _log_autocomplete(ProfWin *window, const char *const input);
+static char* _form_autocomplete(ProfWin *window, const char *const input);
+static char* _form_field_autocomplete(ProfWin *window, const char *const input);
+static char* _occupants_autocomplete(ProfWin *window, const char *const input);
+static char* _kick_autocomplete(ProfWin *window, const char *const input);
+static char* _ban_autocomplete(ProfWin *window, const char *const input);
+static char* _affiliation_autocomplete(ProfWin *window, const char *const input);
+static char* _role_autocomplete(ProfWin *window, const char *const input);
+static char* _resource_autocomplete(ProfWin *window, const char *const input);
+static char* _titlebar_autocomplete(ProfWin *window, const char *const input);
+static char* _inpblock_autocomplete(ProfWin *window, const char *const input);
+static char* _time_autocomplete(ProfWin *window, const char *const input);
+static char* _receipts_autocomplete(ProfWin *window, const char *const input);
+static char* _help_autocomplete(ProfWin *window, const char *const input);
+static char* _wins_autocomplete(ProfWin *window, const char *const input);
+static char* _tls_autocomplete(ProfWin *window, const char *const input);
+static char* _script_autocomplete(ProfWin *window, const char *const input);
+static char* _subject_autocomplete(ProfWin *window, const char *const input);
+static char* _console_autocomplete(ProfWin *window, const char *const input);
+static char* _win_autocomplete(ProfWin *window, const char *const input);
+static char* _close_autocomplete(ProfWin *window, const char *const input);
+static char* _plugins_autocomplete(ProfWin *window, const char *const input);
+static char* _sendfile_autocomplete(ProfWin *window, const char *const input);
+static char* _blocked_autocomplete(ProfWin *window, const char *const input);
+static char *_tray_autocomplete(ProfWin *window, const char *const input);
+
+static char* _script_autocomplete_func(const char *const prefix);
+
+static char* _cmd_ac_complete_params(ProfWin *window, const char *const input);
+
+static Autocomplete commands_ac;
+static Autocomplete who_room_ac;
+static Autocomplete who_roster_ac;
+static Autocomplete help_ac;
+static Autocomplete help_commands_ac;
+static Autocomplete notify_ac;
+static Autocomplete notify_chat_ac;
+static Autocomplete notify_room_ac;
+static Autocomplete notify_typing_ac;
+static Autocomplete notify_mention_ac;
+static Autocomplete notify_trigger_ac;
+static Autocomplete prefs_ac;
+static Autocomplete sub_ac;
+static Autocomplete log_ac;
+static Autocomplete autoaway_ac;
+static Autocomplete autoaway_mode_ac;
+static Autocomplete autoaway_presence_ac;
+static Autocomplete autoconnect_ac;
+static Autocomplete titlebar_ac;
+static Autocomplete theme_ac;
+static Autocomplete theme_load_ac;
+static Autocomplete account_ac;
+static Autocomplete account_set_ac;
+static Autocomplete account_clear_ac;
+static Autocomplete account_default_ac;
+static Autocomplete account_status_ac;
+static Autocomplete disco_ac;
+static Autocomplete wins_ac;
+static Autocomplete roster_ac;
+static Autocomplete roster_show_ac;
+static Autocomplete roster_by_ac;
+static Autocomplete roster_count_ac;
+static Autocomplete roster_order_ac;
+static Autocomplete roster_header_ac;
+static Autocomplete roster_contact_ac;
+static Autocomplete roster_resource_ac;
+static Autocomplete roster_presence_ac;
+static Autocomplete roster_char_ac;
+static Autocomplete roster_remove_all_ac;
+static Autocomplete roster_room_ac;
+static Autocomplete roster_room_position_ac;
+static Autocomplete roster_room_by_ac;
+static Autocomplete roster_room_order_ac;
+static Autocomplete roster_unread_ac;
+static Autocomplete roster_private_ac;
+static Autocomplete group_ac;
+static Autocomplete bookmark_ac;
+static Autocomplete bookmark_property_ac;
+static Autocomplete otr_ac;
+static Autocomplete otr_log_ac;
+static Autocomplete otr_policy_ac;
+static Autocomplete connect_property_ac;
+static Autocomplete tls_property_ac;
+static Autocomplete statuses_ac;
+static Autocomplete statuses_setting_ac;
+static Autocomplete alias_ac;
+static Autocomplete aliases_ac;
+static Autocomplete join_property_ac;
+static Autocomplete room_ac;
+static Autocomplete affiliation_ac;
+static Autocomplete role_ac;
+static Autocomplete privilege_cmd_ac;
+static Autocomplete subject_ac;
+static Autocomplete form_ac;
+static Autocomplete form_field_multi_ac;
+static Autocomplete occupants_ac;
+static Autocomplete occupants_default_ac;
+static Autocomplete occupants_show_ac;
+static Autocomplete time_ac;
+static Autocomplete time_format_ac;
+static Autocomplete resource_ac;
+static Autocomplete inpblock_ac;
+static Autocomplete receipts_ac;
+static Autocomplete pgp_ac;
+static Autocomplete pgp_log_ac;
+static Autocomplete tls_ac;
+static Autocomplete tls_certpath_ac;
+static Autocomplete script_ac;
+static Autocomplete script_show_ac;
+static Autocomplete console_ac;
+static Autocomplete console_msg_ac;
+static Autocomplete autoping_ac;
+static Autocomplete plugins_ac;
+static Autocomplete plugins_load_ac;
+static Autocomplete sendfile_ac;
+static Autocomplete blocked_ac;
+static Autocomplete tray_ac;
+
+void
+cmd_ac_init(void)
+{
+    commands_ac = autocomplete_new();
+    aliases_ac = autocomplete_new();
+
+    help_ac = autocomplete_new();
+    autocomplete_add(help_ac, "commands");
+    autocomplete_add(help_ac, "navigation");
+
+    help_commands_ac = autocomplete_new();
+    autocomplete_add(help_commands_ac, "chat");
+    autocomplete_add(help_commands_ac, "groupchat");
+    autocomplete_add(help_commands_ac, "roster");
+    autocomplete_add(help_commands_ac, "presence");
+    autocomplete_add(help_commands_ac, "discovery");
+    autocomplete_add(help_commands_ac, "connection");
+    autocomplete_add(help_commands_ac, "ui");
+    autocomplete_add(help_commands_ac, "plugins");
+
+    prefs_ac = autocomplete_new();
+    autocomplete_add(prefs_ac, "ui");
+    autocomplete_add(prefs_ac, "desktop");
+    autocomplete_add(prefs_ac, "chat");
+    autocomplete_add(prefs_ac, "log");
+    autocomplete_add(prefs_ac, "conn");
+    autocomplete_add(prefs_ac, "presence");
+    autocomplete_add(prefs_ac, "otr");
+    autocomplete_add(prefs_ac, "pgp");
+
+    notify_ac = autocomplete_new();
+    autocomplete_add(notify_ac, "chat");
+    autocomplete_add(notify_ac, "room");
+    autocomplete_add(notify_ac, "typing");
+    autocomplete_add(notify_ac, "remind");
+    autocomplete_add(notify_ac, "invite");
+    autocomplete_add(notify_ac, "sub");
+    autocomplete_add(notify_ac, "on");
+    autocomplete_add(notify_ac, "off");
+    autocomplete_add(notify_ac, "mention");
+    autocomplete_add(notify_ac, "trigger");
+    autocomplete_add(notify_ac, "reset");
+
+    notify_chat_ac = autocomplete_new();
+    autocomplete_add(notify_chat_ac, "on");
+    autocomplete_add(notify_chat_ac, "off");
+    autocomplete_add(notify_chat_ac, "current");
+    autocomplete_add(notify_chat_ac, "text");
+
+    notify_room_ac = autocomplete_new();
+    autocomplete_add(notify_room_ac, "on");
+    autocomplete_add(notify_room_ac, "off");
+    autocomplete_add(notify_room_ac, "mention");
+    autocomplete_add(notify_room_ac, "current");
+    autocomplete_add(notify_room_ac, "text");
+    autocomplete_add(notify_room_ac, "trigger");
+
+    notify_typing_ac = autocomplete_new();
+    autocomplete_add(notify_typing_ac, "on");
+    autocomplete_add(notify_typing_ac, "off");
+    autocomplete_add(notify_typing_ac, "current");
+
+    notify_mention_ac = autocomplete_new();
+    autocomplete_add(notify_mention_ac, "on");
+    autocomplete_add(notify_mention_ac, "off");
+    autocomplete_add(notify_mention_ac, "case_sensitive");
+    autocomplete_add(notify_mention_ac, "case_insensitive");
+    autocomplete_add(notify_mention_ac, "word_whole");
+    autocomplete_add(notify_mention_ac, "word_part");
+
+    notify_trigger_ac = autocomplete_new();
+    autocomplete_add(notify_trigger_ac, "add");
+    autocomplete_add(notify_trigger_ac, "remove");
+    autocomplete_add(notify_trigger_ac, "list");
+    autocomplete_add(notify_trigger_ac, "on");
+    autocomplete_add(notify_trigger_ac, "off");
+
+    sub_ac = autocomplete_new();
+    autocomplete_add(sub_ac, "request");
+    autocomplete_add(sub_ac, "allow");
+    autocomplete_add(sub_ac, "deny");
+    autocomplete_add(sub_ac, "show");
+    autocomplete_add(sub_ac, "sent");
+    autocomplete_add(sub_ac, "received");
+
+    titlebar_ac = autocomplete_new();
+    autocomplete_add(titlebar_ac, "show");
+    autocomplete_add(titlebar_ac, "goodbye");
+
+    log_ac = autocomplete_new();
+    autocomplete_add(log_ac, "maxsize");
+    autocomplete_add(log_ac, "rotate");
+    autocomplete_add(log_ac, "shared");
+    autocomplete_add(log_ac, "where");
+
+    autoaway_ac = autocomplete_new();
+    autocomplete_add(autoaway_ac, "mode");
+    autocomplete_add(autoaway_ac, "time");
+    autocomplete_add(autoaway_ac, "message");
+    autocomplete_add(autoaway_ac, "check");
+
+    autoaway_mode_ac = autocomplete_new();
+    autocomplete_add(autoaway_mode_ac, "away");
+    autocomplete_add(autoaway_mode_ac, "idle");
+    autocomplete_add(autoaway_mode_ac, "off");
+
+    autoaway_presence_ac = autocomplete_new();
+    autocomplete_add(autoaway_presence_ac, "away");
+    autocomplete_add(autoaway_presence_ac, "xa");
+
+    autoconnect_ac = autocomplete_new();
+    autocomplete_add(autoconnect_ac, "set");
+    autocomplete_add(autoconnect_ac, "off");
+
+    theme_ac = autocomplete_new();
+    autocomplete_add(theme_ac, "load");
+    autocomplete_add(theme_ac, "list");
+    autocomplete_add(theme_ac, "colours");
+    autocomplete_add(theme_ac, "properties");
+
+    disco_ac = autocomplete_new();
+    autocomplete_add(disco_ac, "info");
+    autocomplete_add(disco_ac, "items");
+
+    account_ac = autocomplete_new();
+    autocomplete_add(account_ac, "list");
+    autocomplete_add(account_ac, "show");
+    autocomplete_add(account_ac, "add");
+    autocomplete_add(account_ac, "remove");
+    autocomplete_add(account_ac, "enable");
+    autocomplete_add(account_ac, "disable");
+    autocomplete_add(account_ac, "default");
+    autocomplete_add(account_ac, "rename");
+    autocomplete_add(account_ac, "set");
+    autocomplete_add(account_ac, "clear");
+
+    account_set_ac = autocomplete_new();
+    autocomplete_add(account_set_ac, "jid");
+    autocomplete_add(account_set_ac, "server");
+    autocomplete_add(account_set_ac, "port");
+    autocomplete_add(account_set_ac, "status");
+    autocomplete_add(account_set_ac, "online");
+    autocomplete_add(account_set_ac, "chat");
+    autocomplete_add(account_set_ac, "away");
+    autocomplete_add(account_set_ac, "xa");
+    autocomplete_add(account_set_ac, "dnd");
+    autocomplete_add(account_set_ac, "resource");
+    autocomplete_add(account_set_ac, "password");
+    autocomplete_add(account_set_ac, "eval_password");
+    autocomplete_add(account_set_ac, "muc");
+    autocomplete_add(account_set_ac, "nick");
+    autocomplete_add(account_set_ac, "otr");
+    autocomplete_add(account_set_ac, "pgpkeyid");
+    autocomplete_add(account_set_ac, "startscript");
+    autocomplete_add(account_set_ac, "tls");
+    autocomplete_add(account_set_ac, "theme");
+
+    account_clear_ac = autocomplete_new();
+    autocomplete_add(account_clear_ac, "password");
+    autocomplete_add(account_clear_ac, "eval_password");
+    autocomplete_add(account_clear_ac, "server");
+    autocomplete_add(account_clear_ac, "port");
+    autocomplete_add(account_clear_ac, "otr");
+    autocomplete_add(account_clear_ac, "pgpkeyid");
+    autocomplete_add(account_clear_ac, "startscript");
+    autocomplete_add(account_clear_ac, "theme");
+
+    account_default_ac = autocomplete_new();
+    autocomplete_add(account_default_ac, "set");
+    autocomplete_add(account_default_ac, "off");
+
+    account_status_ac = autocomplete_new();
+    autocomplete_add(account_status_ac, "online");
+    autocomplete_add(account_status_ac, "chat");
+    autocomplete_add(account_status_ac, "away");
+    autocomplete_add(account_status_ac, "xa");
+    autocomplete_add(account_status_ac, "dnd");
+    autocomplete_add(account_status_ac, "last");
+
+    wins_ac = autocomplete_new();
+    autocomplete_add(wins_ac, "unread");
+    autocomplete_add(wins_ac, "prune");
+    autocomplete_add(wins_ac, "tidy");
+    autocomplete_add(wins_ac, "autotidy");
+    autocomplete_add(wins_ac, "swap");
+
+    roster_ac = autocomplete_new();
+    autocomplete_add(roster_ac, "add");
+    autocomplete_add(roster_ac, "online");
+    autocomplete_add(roster_ac, "nick");
+    autocomplete_add(roster_ac, "clearnick");
+    autocomplete_add(roster_ac, "remove");
+    autocomplete_add(roster_ac, "remove_all");
+    autocomplete_add(roster_ac, "show");
+    autocomplete_add(roster_ac, "hide");
+    autocomplete_add(roster_ac, "by");
+    autocomplete_add(roster_ac, "count");
+    autocomplete_add(roster_ac, "order");
+    autocomplete_add(roster_ac, "unread");
+    autocomplete_add(roster_ac, "room");
+    autocomplete_add(roster_ac, "size");
+    autocomplete_add(roster_ac, "wrap");
+    autocomplete_add(roster_ac, "header");
+    autocomplete_add(roster_ac, "contact");
+    autocomplete_add(roster_ac, "resource");
+    autocomplete_add(roster_ac, "presence");
+    autocomplete_add(roster_ac, "private");
+
+    roster_private_ac = autocomplete_new();
+    autocomplete_add(roster_private_ac, "room");
+    autocomplete_add(roster_private_ac, "group");
+    autocomplete_add(roster_private_ac, "off");
+    autocomplete_add(roster_private_ac, "char");
+
+    roster_header_ac = autocomplete_new();
+    autocomplete_add(roster_header_ac, "char");
+
+    roster_contact_ac = autocomplete_new();
+    autocomplete_add(roster_contact_ac, "char");
+    autocomplete_add(roster_contact_ac, "indent");
+
+    roster_resource_ac = autocomplete_new();
+    autocomplete_add(roster_resource_ac, "char");
+    autocomplete_add(roster_resource_ac, "indent");
+    autocomplete_add(roster_resource_ac, "join");
+
+    roster_presence_ac = autocomplete_new();
+    autocomplete_add(roster_presence_ac, "indent");
+
+    roster_char_ac = autocomplete_new();
+    autocomplete_add(roster_char_ac, "none");
+
+    roster_show_ac = autocomplete_new();
+    autocomplete_add(roster_show_ac, "offline");
+    autocomplete_add(roster_show_ac, "resource");
+    autocomplete_add(roster_show_ac, "presence");
+    autocomplete_add(roster_show_ac, "status");
+    autocomplete_add(roster_show_ac, "empty");
+    autocomplete_add(roster_show_ac, "priority");
+    autocomplete_add(roster_show_ac, "contacts");
+    autocomplete_add(roster_show_ac, "unsubscribed");
+    autocomplete_add(roster_show_ac, "rooms");
+
+    roster_by_ac = autocomplete_new();
+    autocomplete_add(roster_by_ac, "group");
+    autocomplete_add(roster_by_ac, "presence");
+    autocomplete_add(roster_by_ac, "none");
+
+    roster_count_ac = autocomplete_new();
+    autocomplete_add(roster_count_ac, "unread");
+    autocomplete_add(roster_count_ac, "items");
+    autocomplete_add(roster_count_ac, "off");
+    autocomplete_add(roster_count_ac, "zero");
+
+    roster_order_ac = autocomplete_new();
+    autocomplete_add(roster_order_ac, "name");
+    autocomplete_add(roster_order_ac, "presence");
+
+    roster_unread_ac = autocomplete_new();
+    autocomplete_add(roster_unread_ac, "before");
+    autocomplete_add(roster_unread_ac, "after");
+    autocomplete_add(roster_unread_ac, "off");
+
+    roster_room_ac = autocomplete_new();
+    autocomplete_add(roster_room_ac, "char");
+    autocomplete_add(roster_room_ac, "position");
+    autocomplete_add(roster_room_ac, "by");
+    autocomplete_add(roster_room_ac, "order");
+    autocomplete_add(roster_room_ac, "unread");
+    autocomplete_add(roster_room_ac, "private");
+
+    roster_room_by_ac = autocomplete_new();
+    autocomplete_add(roster_room_by_ac, "service");
+    autocomplete_add(roster_room_by_ac, "none");
+
+    roster_room_order_ac = autocomplete_new();
+    autocomplete_add(roster_room_order_ac, "name");
+    autocomplete_add(roster_room_order_ac, "unread");
+
+    roster_room_position_ac = autocomplete_new();
+    autocomplete_add(roster_room_position_ac, "first");
+    autocomplete_add(roster_room_position_ac, "last");
+
+    roster_remove_all_ac = autocomplete_new();
+    autocomplete_add(roster_remove_all_ac, "contacts");
+
+    group_ac = autocomplete_new();
+    autocomplete_add(group_ac, "show");
+    autocomplete_add(group_ac, "add");
+    autocomplete_add(group_ac, "remove");
+
+    theme_load_ac = NULL;
+    plugins_load_ac = NULL;
+
+    who_roster_ac = autocomplete_new();
+    autocomplete_add(who_roster_ac, "chat");
+    autocomplete_add(who_roster_ac, "online");
+    autocomplete_add(who_roster_ac, "away");
+    autocomplete_add(who_roster_ac, "xa");
+    autocomplete_add(who_roster_ac, "dnd");
+    autocomplete_add(who_roster_ac, "offline");
+    autocomplete_add(who_roster_ac, "available");
+    autocomplete_add(who_roster_ac, "unavailable");
+    autocomplete_add(who_roster_ac, "any");
+
+    who_room_ac = autocomplete_new();
+    autocomplete_add(who_room_ac, "chat");
+    autocomplete_add(who_room_ac, "online");
+    autocomplete_add(who_room_ac, "away");
+    autocomplete_add(who_room_ac, "xa");
+    autocomplete_add(who_room_ac, "dnd");
+    autocomplete_add(who_room_ac, "available");
+    autocomplete_add(who_room_ac, "unavailable");
+    autocomplete_add(who_room_ac, "moderator");
+    autocomplete_add(who_room_ac, "participant");
+    autocomplete_add(who_room_ac, "visitor");
+    autocomplete_add(who_room_ac, "owner");
+    autocomplete_add(who_room_ac, "admin");
+    autocomplete_add(who_room_ac, "member");
+
+    bookmark_ac = autocomplete_new();
+    autocomplete_add(bookmark_ac, "list");
+    autocomplete_add(bookmark_ac, "add");
+    autocomplete_add(bookmark_ac, "update");
+    autocomplete_add(bookmark_ac, "remove");
+    autocomplete_add(bookmark_ac, "join");
+
+    bookmark_property_ac = autocomplete_new();
+    autocomplete_add(bookmark_property_ac, "nick");
+    autocomplete_add(bookmark_property_ac, "password");
+    autocomplete_add(bookmark_property_ac, "autojoin");
+
+    otr_ac = autocomplete_new();
+    autocomplete_add(otr_ac, "gen");
+    autocomplete_add(otr_ac, "start");
+    autocomplete_add(otr_ac, "end");
+    autocomplete_add(otr_ac, "myfp");
+    autocomplete_add(otr_ac, "theirfp");
+    autocomplete_add(otr_ac, "trust");
+    autocomplete_add(otr_ac, "untrust");
+    autocomplete_add(otr_ac, "secret");
+    autocomplete_add(otr_ac, "log");
+    autocomplete_add(otr_ac, "libver");
+    autocomplete_add(otr_ac, "policy");
+    autocomplete_add(otr_ac, "question");
+    autocomplete_add(otr_ac, "answer");
+    autocomplete_add(otr_ac, "char");
+
+    otr_log_ac = autocomplete_new();
+    autocomplete_add(otr_log_ac, "on");
+    autocomplete_add(otr_log_ac, "off");
+    autocomplete_add(otr_log_ac, "redact");
+
+    otr_policy_ac = autocomplete_new();
+    autocomplete_add(otr_policy_ac, "manual");
+    autocomplete_add(otr_policy_ac, "opportunistic");
+    autocomplete_add(otr_policy_ac, "always");
+
+    connect_property_ac = autocomplete_new();
+    autocomplete_add(connect_property_ac, "server");
+    autocomplete_add(connect_property_ac, "port");
+    autocomplete_add(connect_property_ac, "tls");
+
+    tls_property_ac = autocomplete_new();
+    autocomplete_add(tls_property_ac, "force");
+    autocomplete_add(tls_property_ac, "allow");
+    autocomplete_add(tls_property_ac, "disable");
+
+    join_property_ac = autocomplete_new();
+    autocomplete_add(join_property_ac, "nick");
+    autocomplete_add(join_property_ac, "password");
+
+    statuses_ac = autocomplete_new();
+    autocomplete_add(statuses_ac, "console");
+    autocomplete_add(statuses_ac, "chat");
+    autocomplete_add(statuses_ac, "muc");
+
+    statuses_setting_ac = autocomplete_new();
+    autocomplete_add(statuses_setting_ac, "all");
+    autocomplete_add(statuses_setting_ac, "online");
+    autocomplete_add(statuses_setting_ac, "none");
+
+    alias_ac = autocomplete_new();
+    autocomplete_add(alias_ac, "add");
+    autocomplete_add(alias_ac, "remove");
+    autocomplete_add(alias_ac, "list");
+
+    room_ac = autocomplete_new();
+    autocomplete_add(room_ac, "accept");
+    autocomplete_add(room_ac, "destroy");
+    autocomplete_add(room_ac, "config");
+
+    affiliation_ac = autocomplete_new();
+    autocomplete_add(affiliation_ac, "owner");
+    autocomplete_add(affiliation_ac, "admin");
+    autocomplete_add(affiliation_ac, "member");
+    autocomplete_add(affiliation_ac, "none");
+    autocomplete_add(affiliation_ac, "outcast");
+
+    role_ac = autocomplete_new();
+    autocomplete_add(role_ac, "moderator");
+    autocomplete_add(role_ac, "participant");
+    autocomplete_add(role_ac, "visitor");
+    autocomplete_add(role_ac, "none");
+
+    privilege_cmd_ac = autocomplete_new();
+    autocomplete_add(privilege_cmd_ac, "list");
+    autocomplete_add(privilege_cmd_ac, "set");
+
+    subject_ac = autocomplete_new();
+    autocomplete_add(subject_ac, "set");
+    autocomplete_add(subject_ac, "edit");
+    autocomplete_add(subject_ac, "prepend");
+    autocomplete_add(subject_ac, "append");
+    autocomplete_add(subject_ac, "clear");
+
+    form_ac = autocomplete_new();
+    autocomplete_add(form_ac, "submit");
+    autocomplete_add(form_ac, "cancel");
+    autocomplete_add(form_ac, "show");
+    autocomplete_add(form_ac, "help");
+
+    form_field_multi_ac = autocomplete_new();
+    autocomplete_add(form_field_multi_ac, "add");
+    autocomplete_add(form_field_multi_ac, "remove");
+
+    occupants_ac = autocomplete_new();
+    autocomplete_add(occupants_ac, "show");
+    autocomplete_add(occupants_ac, "hide");
+    autocomplete_add(occupants_ac, "default");
+    autocomplete_add(occupants_ac, "size");
+
+    occupants_default_ac = autocomplete_new();
+    autocomplete_add(occupants_default_ac, "show");
+    autocomplete_add(occupants_default_ac, "hide");
+
+    occupants_show_ac = autocomplete_new();
+    autocomplete_add(occupants_show_ac, "jid");
+
+    time_ac = autocomplete_new();
+    autocomplete_add(time_ac, "console");
+    autocomplete_add(time_ac, "chat");
+    autocomplete_add(time_ac, "muc");
+    autocomplete_add(time_ac, "mucconfig");
+    autocomplete_add(time_ac, "private");
+    autocomplete_add(time_ac, "xml");
+    autocomplete_add(time_ac, "statusbar");
+    autocomplete_add(time_ac, "lastactivity");
+
+    time_format_ac = autocomplete_new();
+    autocomplete_add(time_format_ac, "set");
+    autocomplete_add(time_format_ac, "off");
+
+    resource_ac = autocomplete_new();
+    autocomplete_add(resource_ac, "set");
+    autocomplete_add(resource_ac, "off");
+    autocomplete_add(resource_ac, "title");
+    autocomplete_add(resource_ac, "message");
+
+    inpblock_ac = autocomplete_new();
+    autocomplete_add(inpblock_ac, "timeout");
+    autocomplete_add(inpblock_ac, "dynamic");
+
+    receipts_ac = autocomplete_new();
+    autocomplete_add(receipts_ac, "send");
+    autocomplete_add(receipts_ac, "request");
+
+    pgp_ac = autocomplete_new();
+    autocomplete_add(pgp_ac, "keys");
+    autocomplete_add(pgp_ac, "contacts");
+    autocomplete_add(pgp_ac, "setkey");
+    autocomplete_add(pgp_ac, "libver");
+    autocomplete_add(pgp_ac, "start");
+    autocomplete_add(pgp_ac, "end");
+    autocomplete_add(pgp_ac, "log");
+    autocomplete_add(pgp_ac, "char");
+
+    pgp_log_ac = autocomplete_new();
+    autocomplete_add(pgp_log_ac, "on");
+    autocomplete_add(pgp_log_ac, "off");
+    autocomplete_add(pgp_log_ac, "redact");
+
+    tls_ac = autocomplete_new();
+    autocomplete_add(tls_ac, "allow");
+    autocomplete_add(tls_ac, "always");
+    autocomplete_add(tls_ac, "deny");
+    autocomplete_add(tls_ac, "cert");
+    autocomplete_add(tls_ac, "trust");
+    autocomplete_add(tls_ac, "trusted");
+    autocomplete_add(tls_ac, "revoke");
+    autocomplete_add(tls_ac, "certpath");
+    autocomplete_add(tls_ac, "show");
+
+    tls_certpath_ac = autocomplete_new();
+    autocomplete_add(tls_certpath_ac, "set");
+    autocomplete_add(tls_certpath_ac, "clear");
+
+    script_ac = autocomplete_new();
+    autocomplete_add(script_ac, "run");
+    autocomplete_add(script_ac, "list");
+    autocomplete_add(script_ac, "show");
+
+    script_show_ac = NULL;
+
+    console_ac = autocomplete_new();
+    autocomplete_add(console_ac, "chat");
+    autocomplete_add(console_ac, "muc");
+    autocomplete_add(console_ac, "private");
+
+    console_msg_ac = autocomplete_new();
+    autocomplete_add(console_msg_ac, "all");
+    autocomplete_add(console_msg_ac, "first");
+    autocomplete_add(console_msg_ac, "none");
+
+    autoping_ac = autocomplete_new();
+    autocomplete_add(autoping_ac, "set");
+    autocomplete_add(autoping_ac, "timeout");
+
+    plugins_ac = autocomplete_new();
+    autocomplete_add(plugins_ac, "load");
+
+    sendfile_ac = autocomplete_new();
+
+    blocked_ac = autocomplete_new();
+    autocomplete_add(blocked_ac, "add");
+    autocomplete_add(blocked_ac, "remove");
+
+    tray_ac = autocomplete_new();
+    autocomplete_add(tray_ac, "on");
+    autocomplete_add(tray_ac, "off");
+    autocomplete_add(tray_ac, "read");
+    autocomplete_add(tray_ac, "timer");
+}
+
+void
+cmd_ac_add(const char *const value)
+{
+    if (commands_ac) {
+        autocomplete_add(commands_ac, value);
+    }
+}
+
+void
+cmd_ac_add_help(const char *const value)
+{
+    if (help_ac) {
+        autocomplete_add(help_ac, value);
+    }
+}
+
+void
+cmd_ac_add_cmd(Command *command)
+{
+    autocomplete_add(commands_ac, command->cmd);
+    autocomplete_add(help_ac, command->cmd+1);
+}
+
+void
+cmd_ac_add_alias(ProfAlias *alias)
+{
+    GString *ac_alias = g_string_new("/");
+    g_string_append(ac_alias, alias->name);
+    autocomplete_add(commands_ac, ac_alias->str);
+    autocomplete_add(aliases_ac, alias->name);
+    g_string_free(ac_alias, TRUE);
+}
+
+void
+cmd_ac_add_alias_value(char *value)
+{
+    if (aliases_ac) {
+        autocomplete_add(aliases_ac, value);
+    }
+}
+
+void
+cmd_ac_remove_alias_value(char *value)
+{
+    if (aliases_ac) {
+        autocomplete_remove(aliases_ac, value);
+    }
+}
+
+void
+cmd_ac_remove(const char *const value)
+{
+    if (commands_ac) {
+        autocomplete_remove(commands_ac, value);
+    }
+}
+
+gboolean
+cmd_ac_exists(char *cmd)
+{
+    if (commands_ac == NULL) {
+        return FALSE;
+    } else {
+        return autocomplete_contains(commands_ac, cmd);
+    }
+}
+
+void
+cmd_ac_add_form_fields(DataForm *form)
+{
+    if (form == NULL) {
+        return;
+    }
+
+    GSList *fields = autocomplete_create_list(form->tag_ac);
+    GSList *curr_field = fields;
+    while (curr_field) {
+        GString *field_str = g_string_new("/");
+        g_string_append(field_str, curr_field->data);
+        cmd_ac_add(field_str->str);
+        g_string_free(field_str, TRUE);
+        curr_field = g_slist_next(curr_field);
+    }
+    g_slist_free_full(fields, free);
+}
+
+void
+cmd_ac_remove_form_fields(DataForm *form)
+{
+    if (form == NULL) {
+        return;
+    }
+
+    GSList *fields = autocomplete_create_list(form->tag_ac);
+    GSList *curr_field = fields;
+    while (curr_field) {
+        GString *field_str = g_string_new("/");
+        g_string_append(field_str, curr_field->data);
+        cmd_ac_remove(field_str->str);
+        g_string_free(field_str, TRUE);
+        curr_field = g_slist_next(curr_field);
+    }
+    g_slist_free_full(fields, free);
+}
+
+char*
+cmd_ac_complete(ProfWin *window, const char *const input)
+{
+    // autocomplete command
+    if ((strncmp(input, "/", 1) == 0) && (!str_contains(input, strlen(input), ' '))) {
+        char *found = NULL;
+        found = autocomplete_complete(commands_ac, input, TRUE);
+        if (found) {
+            return found;
+        }
+
+    // autocomplete parameters
+    } else {
+        char *found = _cmd_ac_complete_params(window, input);
+        if (found) {
+            return found;
+        }
+    }
+
+    return NULL;
+}
+
+void
+cmd_ac_reset(ProfWin *window)
+{
+    jabber_conn_status_t conn_status = connection_get_status();
+    if (conn_status == JABBER_CONNECTED) {
+        roster_reset_search_attempts();
+
+        if (window->type == WIN_CHAT) {
+            ProfChatWin *chatwin = (ProfChatWin*)window;
+            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+            PContact contact = roster_get_contact(chatwin->barejid);
+            if (contact) {
+                p_contact_resource_ac_reset(contact);
+            }
+        }
+    }
+
+    muc_invites_reset_ac();
+    accounts_reset_all_search();
+    accounts_reset_enabled_search();
+    tlscerts_reset_ac();
+    prefs_reset_boolean_choice();
+    presence_reset_sub_request_search();
+#ifdef HAVE_LIBGPGME
+    p_gpg_autocomplete_key_reset();
+#endif
+    autocomplete_reset(help_ac);
+    autocomplete_reset(help_commands_ac);
+    autocomplete_reset(notify_ac);
+    autocomplete_reset(notify_chat_ac);
+    autocomplete_reset(notify_room_ac);
+    autocomplete_reset(notify_typing_ac);
+    autocomplete_reset(notify_mention_ac);
+    autocomplete_reset(notify_trigger_ac);
+    autocomplete_reset(sub_ac);
+    autocomplete_reset(sendfile_ac);
+
+    autocomplete_reset(who_room_ac);
+    autocomplete_reset(who_roster_ac);
+    autocomplete_reset(prefs_ac);
+    autocomplete_reset(log_ac);
+    autocomplete_reset(commands_ac);
+    autocomplete_reset(autoaway_ac);
+    autocomplete_reset(autoaway_mode_ac);
+    autocomplete_reset(autoaway_presence_ac);
+    autocomplete_reset(autoconnect_ac);
+    autocomplete_reset(theme_ac);
+    if (theme_load_ac) {
+        autocomplete_free(theme_load_ac);
+        theme_load_ac = NULL;
+    }
+    if (plugins_load_ac) {
+        autocomplete_free(plugins_load_ac);
+        plugins_load_ac = NULL;
+    }
+    autocomplete_reset(account_ac);
+    autocomplete_reset(account_set_ac);
+    autocomplete_reset(account_clear_ac);
+    autocomplete_reset(account_default_ac);
+    autocomplete_reset(account_status_ac);
+    autocomplete_reset(disco_ac);
+    autocomplete_reset(wins_ac);
+    autocomplete_reset(roster_ac);
+    autocomplete_reset(roster_header_ac);
+    autocomplete_reset(roster_contact_ac);
+    autocomplete_reset(roster_resource_ac);
+    autocomplete_reset(roster_presence_ac);
+    autocomplete_reset(roster_char_ac);
+    autocomplete_reset(roster_show_ac);
+    autocomplete_reset(roster_by_ac);
+    autocomplete_reset(roster_count_ac);
+    autocomplete_reset(roster_order_ac);
+    autocomplete_reset(roster_room_ac);
+    autocomplete_reset(roster_room_by_ac);
+    autocomplete_reset(roster_unread_ac);
+    autocomplete_reset(roster_room_position_ac);
+    autocomplete_reset(roster_room_order_ac);
+    autocomplete_reset(roster_remove_all_ac);
+    autocomplete_reset(roster_private_ac);
+    autocomplete_reset(group_ac);
+    autocomplete_reset(titlebar_ac);
+    autocomplete_reset(bookmark_ac);
+    autocomplete_reset(bookmark_property_ac);
+    autocomplete_reset(otr_ac);
+    autocomplete_reset(otr_log_ac);
+    autocomplete_reset(otr_policy_ac);
+    autocomplete_reset(connect_property_ac);
+    autocomplete_reset(tls_property_ac);
+    autocomplete_reset(statuses_ac);
+    autocomplete_reset(statuses_setting_ac);
+    autocomplete_reset(alias_ac);
+    autocomplete_reset(aliases_ac);
+    autocomplete_reset(join_property_ac);
+    autocomplete_reset(room_ac);
+    autocomplete_reset(affiliation_ac);
+    autocomplete_reset(role_ac);
+    autocomplete_reset(privilege_cmd_ac);
+    autocomplete_reset(subject_ac);
+    autocomplete_reset(form_ac);
+    autocomplete_reset(form_field_multi_ac);
+    autocomplete_reset(occupants_ac);
+    autocomplete_reset(occupants_default_ac);
+    autocomplete_reset(occupants_show_ac);
+    autocomplete_reset(time_ac);
+    autocomplete_reset(time_format_ac);
+    autocomplete_reset(resource_ac);
+    autocomplete_reset(inpblock_ac);
+    autocomplete_reset(receipts_ac);
+    autocomplete_reset(pgp_ac);
+    autocomplete_reset(pgp_log_ac);
+    autocomplete_reset(tls_ac);
+    autocomplete_reset(tls_certpath_ac);
+    autocomplete_reset(console_ac);
+    autocomplete_reset(console_msg_ac);
+    autocomplete_reset(autoping_ac);
+    autocomplete_reset(plugins_ac);
+    autocomplete_reset(blocked_ac);
+    autocomplete_reset(tray_ac);
+    autocomplete_reset(script_ac);
+    if (script_show_ac) {
+        autocomplete_free(script_show_ac);
+        script_show_ac = NULL;
+    }
+
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        muc_autocomplete_reset(mucwin->roomjid);
+        muc_jid_autocomplete_reset(mucwin->roomjid);
+    }
+
+    if (window->type == WIN_MUC_CONFIG) {
+        ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+        assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
+        if (confwin->form) {
+            form_reset_autocompleters(confwin->form);
+        }
+    }
+
+    bookmark_autocomplete_reset();
+    blocked_ac_reset();
+    prefs_reset_room_trigger_ac();
+    win_reset_search_attempts();
+    win_close_reset_search_attempts();
+    plugins_reset_autocomplete();
+}
+
+void
+cmd_ac_uninit(void)
+{
+    autocomplete_free(commands_ac);
+    autocomplete_free(who_room_ac);
+    autocomplete_free(who_roster_ac);
+    autocomplete_free(help_ac);
+    autocomplete_free(help_commands_ac);
+    autocomplete_free(notify_ac);
+    autocomplete_free(notify_chat_ac);
+    autocomplete_free(notify_room_ac);
+    autocomplete_free(notify_typing_ac);
+    autocomplete_free(notify_mention_ac);
+    autocomplete_free(notify_trigger_ac);
+    autocomplete_free(sub_ac);
+    autocomplete_free(titlebar_ac);
+    autocomplete_free(log_ac);
+    autocomplete_free(prefs_ac);
+    autocomplete_free(autoaway_ac);
+    autocomplete_free(autoaway_mode_ac);
+    autocomplete_free(autoaway_presence_ac);
+    autocomplete_free(autoconnect_ac);
+    autocomplete_free(theme_ac);
+    autocomplete_free(theme_load_ac);
+    autocomplete_free(account_ac);
+    autocomplete_free(account_set_ac);
+    autocomplete_free(account_clear_ac);
+    autocomplete_free(account_default_ac);
+    autocomplete_free(account_status_ac);
+    autocomplete_free(disco_ac);
+    autocomplete_free(wins_ac);
+    autocomplete_free(roster_ac);
+    autocomplete_free(roster_header_ac);
+    autocomplete_free(roster_contact_ac);
+    autocomplete_free(roster_resource_ac);
+    autocomplete_free(roster_presence_ac);
+    autocomplete_free(roster_char_ac);
+    autocomplete_free(roster_show_ac);
+    autocomplete_free(roster_by_ac);
+    autocomplete_free(roster_count_ac);
+    autocomplete_free(roster_order_ac);
+    autocomplete_free(roster_room_ac);
+    autocomplete_free(roster_room_by_ac);
+    autocomplete_free(roster_unread_ac);
+    autocomplete_free(roster_room_position_ac);
+    autocomplete_free(roster_room_order_ac);
+    autocomplete_free(roster_remove_all_ac);
+    autocomplete_free(roster_private_ac);
+    autocomplete_free(group_ac);
+    autocomplete_free(bookmark_ac);
+    autocomplete_free(bookmark_property_ac);
+    autocomplete_free(otr_ac);
+    autocomplete_free(otr_log_ac);
+    autocomplete_free(otr_policy_ac);
+    autocomplete_free(connect_property_ac);
+    autocomplete_free(tls_property_ac);
+    autocomplete_free(statuses_ac);
+    autocomplete_free(statuses_setting_ac);
+    autocomplete_free(alias_ac);
+    autocomplete_free(aliases_ac);
+    autocomplete_free(join_property_ac);
+    autocomplete_free(room_ac);
+    autocomplete_free(affiliation_ac);
+    autocomplete_free(role_ac);
+    autocomplete_free(privilege_cmd_ac);
+    autocomplete_free(subject_ac);
+    autocomplete_free(form_ac);
+    autocomplete_free(form_field_multi_ac);
+    autocomplete_free(occupants_ac);
+    autocomplete_free(occupants_default_ac);
+    autocomplete_free(occupants_show_ac);
+    autocomplete_free(time_ac);
+    autocomplete_free(time_format_ac);
+    autocomplete_free(resource_ac);
+    autocomplete_free(inpblock_ac);
+    autocomplete_free(receipts_ac);
+    autocomplete_free(pgp_ac);
+    autocomplete_free(pgp_log_ac);
+    autocomplete_free(tls_ac);
+    autocomplete_free(tls_certpath_ac);
+    autocomplete_free(script_ac);
+    autocomplete_free(script_show_ac);
+    autocomplete_free(console_ac);
+    autocomplete_free(console_msg_ac);
+    autocomplete_free(autoping_ac);
+    autocomplete_free(plugins_ac);
+    autocomplete_free(plugins_load_ac);
+    autocomplete_free(sendfile_ac);
+    autocomplete_free(blocked_ac);
+    autocomplete_free(tray_ac);
+}
+
+static char*
+_cmd_ac_complete_params(ProfWin *window, const char *const input)
+{
+    int i;
+    char *result = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+
+    // autocomplete boolean settings
+    gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype", "/flash", "/splash", "/chlog", "/grlog",
+        "/history", "/vercheck", "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn",
+        "/lastactivity" };
+
+    for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
+        result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice);
+        if (result) {
+            return result;
+        }
+    }
+
+    // autocomplete nickname in chat rooms
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
+        if (nick_ac) {
+            gchar *nick_choices[] = { "/msg", "/info", "/caps", "/status", "/software" } ;
+
+            // Remove quote character before and after names when doing autocomplete
+            char *unquoted = strip_arg_quotes(input);
+            for (i = 0; i < ARRAY_SIZE(nick_choices); i++) {
+                result = autocomplete_param_with_ac(unquoted, nick_choices[i], nick_ac, TRUE);
+                if (result) {
+                    free(unquoted);
+                    return result;
+                }
+            }
+            free(unquoted);
+        }
+
+    // otherwise autocomplete using roster
+    } else if (conn_status == JABBER_CONNECTED) {
+        gchar *contact_choices[] = { "/msg", "/info", "/status" };
+        // Remove quote character before and after names when doing autocomplete
+        char *unquoted = strip_arg_quotes(input);
+        for (i = 0; i < ARRAY_SIZE(contact_choices); i++) {
+            result = autocomplete_param_with_func(unquoted, contact_choices[i], roster_contact_autocomplete);
+            if (result) {
+                free(unquoted);
+                return result;
+            }
+        }
+        free(unquoted);
+
+        gchar *resource_choices[] = { "/caps", "/software", "/ping" };
+        for (i = 0; i < ARRAY_SIZE(resource_choices); i++) {
+            result = autocomplete_param_with_func(input, resource_choices[i], roster_fulljid_autocomplete);
+            if (result) {
+                return result;
+            }
+        }
+    }
+
+    if (conn_status == JABBER_CONNECTED) {
+        result = autocomplete_param_with_func(input, "/invite", roster_contact_autocomplete);
+        if (result) {
+            return result;
+        }
+    }
+
+    gchar *invite_choices[] = { "/decline", "/join" };
+    for (i = 0; i < ARRAY_SIZE(invite_choices); i++) {
+        result = autocomplete_param_with_func(input, invite_choices[i], muc_invites_find);
+        if (result) {
+            return result;
+        }
+    }
+
+    gchar *cmds[] = { "/prefs", "/disco", "/room", "/autoping" };
+    Autocomplete completers[] = { prefs_ac, disco_ac, room_ac, autoping_ac };
+
+    for (i = 0; i < ARRAY_SIZE(cmds); i++) {
+        result = autocomplete_param_with_ac(input, cmds[i], completers[i], TRUE);
+        if (result) {
+            return result;
+        }
+    }
+
+    GHashTable *ac_funcs = g_hash_table_new(g_str_hash, g_str_equal);
+    g_hash_table_insert(ac_funcs, "/help",          _help_autocomplete);
+    g_hash_table_insert(ac_funcs, "/who",           _who_autocomplete);
+    g_hash_table_insert(ac_funcs, "/sub",           _sub_autocomplete);
+    g_hash_table_insert(ac_funcs, "/notify",        _notify_autocomplete);
+    g_hash_table_insert(ac_funcs, "/autoaway",      _autoaway_autocomplete);
+    g_hash_table_insert(ac_funcs, "/theme",         _theme_autocomplete);
+    g_hash_table_insert(ac_funcs, "/log",           _log_autocomplete);
+    g_hash_table_insert(ac_funcs, "/account",       _account_autocomplete);
+    g_hash_table_insert(ac_funcs, "/roster",        _roster_autocomplete);
+    g_hash_table_insert(ac_funcs, "/group",         _group_autocomplete);
+    g_hash_table_insert(ac_funcs, "/bookmark",      _bookmark_autocomplete);
+    g_hash_table_insert(ac_funcs, "/autoconnect",   _autoconnect_autocomplete);
+    g_hash_table_insert(ac_funcs, "/otr",           _otr_autocomplete);
+    g_hash_table_insert(ac_funcs, "/pgp",           _pgp_autocomplete);
+    g_hash_table_insert(ac_funcs, "/connect",       _connect_autocomplete);
+    g_hash_table_insert(ac_funcs, "/statuses",      _statuses_autocomplete);
+    g_hash_table_insert(ac_funcs, "/alias",         _alias_autocomplete);
+    g_hash_table_insert(ac_funcs, "/join",          _join_autocomplete);
+    g_hash_table_insert(ac_funcs, "/form",          _form_autocomplete);
+    g_hash_table_insert(ac_funcs, "/occupants",     _occupants_autocomplete);
+    g_hash_table_insert(ac_funcs, "/kick",          _kick_autocomplete);
+    g_hash_table_insert(ac_funcs, "/ban",           _ban_autocomplete);
+    g_hash_table_insert(ac_funcs, "/affiliation",   _affiliation_autocomplete);
+    g_hash_table_insert(ac_funcs, "/role",          _role_autocomplete);
+    g_hash_table_insert(ac_funcs, "/resource",      _resource_autocomplete);
+    g_hash_table_insert(ac_funcs, "/titlebar",      _titlebar_autocomplete);
+    g_hash_table_insert(ac_funcs, "/inpblock",      _inpblock_autocomplete);
+    g_hash_table_insert(ac_funcs, "/time",          _time_autocomplete);
+    g_hash_table_insert(ac_funcs, "/receipts",      _receipts_autocomplete);
+    g_hash_table_insert(ac_funcs, "/wins",          _wins_autocomplete);
+    g_hash_table_insert(ac_funcs, "/tls",           _tls_autocomplete);
+    g_hash_table_insert(ac_funcs, "/script",        _script_autocomplete);
+    g_hash_table_insert(ac_funcs, "/subject",       _subject_autocomplete);
+    g_hash_table_insert(ac_funcs, "/console",       _console_autocomplete);
+    g_hash_table_insert(ac_funcs, "/win",           _win_autocomplete);
+    g_hash_table_insert(ac_funcs, "/close",         _close_autocomplete);
+    g_hash_table_insert(ac_funcs, "/plugins",       _plugins_autocomplete);
+    g_hash_table_insert(ac_funcs, "/sendfile",      _sendfile_autocomplete);
+    g_hash_table_insert(ac_funcs, "/blocked",       _blocked_autocomplete);
+    g_hash_table_insert(ac_funcs, "/tray",          _tray_autocomplete);
+
+    int len = strlen(input);
+    char parsed[len+1];
+    i = 0;
+    while (i < len) {
+        if (input[i] == ' ') {
+            break;
+        } else {
+            parsed[i] = input[i];
+        }
+        i++;
+    }
+    parsed[i] = '\0';
+
+    char * (*ac_func)(ProfWin*, const char * const) = g_hash_table_lookup(ac_funcs, parsed);
+    if (ac_func) {
+        result = ac_func(window, input);
+        if (result) {
+            g_hash_table_destroy(ac_funcs);
+            return result;
+        }
+    }
+    g_hash_table_destroy(ac_funcs);
+
+    result = plugins_autocomplete(input);
+    if (result) {
+        return result;
+    }
+
+    if (g_str_has_prefix(input, "/field")) {
+        result = _form_field_autocomplete(window, input);
+        if (result) {
+            return result;
+        }
+    }
+
+    return NULL;
+}
+
+static char*
+_sub_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    result = autocomplete_param_with_func(input, "/sub allow", presence_sub_request_find);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_func(input, "/sub deny", presence_sub_request_find);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/sub", sub_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_tray_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    result = autocomplete_param_with_func(input, "/tray read", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/tray", tray_ac, FALSE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_who_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        result = autocomplete_param_with_ac(input, "/who", who_room_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    } else {
+        jabber_conn_status_t conn_status = connection_get_status();
+        if (conn_status == JABBER_CONNECTED) {
+            int i = 0;
+            gchar *group_commands[] = { "/who any", "/who online", "/who offline",
+                "/who chat", "/who away", "/who xa", "/who dnd", "/who available",
+                "/who unavailable" };
+
+            for (i = 0; i < ARRAY_SIZE(group_commands); i++) {
+                result = autocomplete_param_with_func(input, group_commands[i], roster_group_autocomplete);
+                if (result) {
+                    return result;
+                }
+            }
+        }
+
+        result = autocomplete_param_with_ac(input, "/who", who_roster_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    }
+
+    return result;
+}
+
+static char*
+_roster_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    result = autocomplete_param_with_ac(input, "/roster room private char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room private", roster_header_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster header char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster contact char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster private char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster resource char", roster_char_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_func(input, "/roster resource join", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room position", roster_room_position_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room by", roster_room_by_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room order", roster_room_order_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room unread", roster_unread_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_func(input, "/roster count zero", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    jabber_conn_status_t conn_status = connection_get_status();
+    if (conn_status == JABBER_CONNECTED) {
+        result = autocomplete_param_with_func(input, "/roster nick", roster_barejid_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_with_func(input, "/roster clearnick", roster_barejid_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_with_func(input, "/roster remove", roster_barejid_autocomplete);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/roster remove_all", roster_remove_all_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster show", roster_show_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster hide", roster_show_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster by", roster_by_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster count", roster_count_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster order", roster_order_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster unread", roster_unread_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster room", roster_room_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_func(input, "/roster wrap", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster header", roster_header_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster contact", roster_contact_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster resource", roster_resource_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster presence", roster_presence_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster private", roster_private_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/roster", roster_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_group_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+
+    if (conn_status == JABBER_CONNECTED) {
+        result = autocomplete_param_with_func(input, "/group show", roster_group_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_no_with_func(input, "/group add", 4, roster_contact_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_no_with_func(input, "/group remove", 4, roster_contact_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_with_func(input, "/group add", roster_group_autocomplete);
+        if (result) {
+            return result;
+        }
+        result = autocomplete_param_with_func(input, "/group remove", roster_group_autocomplete);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/group", group_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_blocked_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/blocked remove", blocked_ac_find);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/blocked", blocked_ac, FALSE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+
+static char*
+_bookmark_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    gboolean result;
+    gchar **args = parse_args(input, 3, 8, &result);
+    gboolean handle_options = result && (g_strv_length(args) > 2);
+
+    if (handle_options && ((strcmp(args[0], "add") == 0) || (strcmp(args[0], "update") == 0)) ) {
+        GString *beginning = g_string_new("/bookmark");
+        gboolean autojoin = FALSE;
+        int num_args = g_strv_length(args);
+
+        g_string_append(beginning, " ");
+        g_string_append(beginning, args[0]);
+        g_string_append(beginning, " ");
+        g_string_append(beginning, args[1]);
+        if (num_args == 4 && g_strcmp0(args[2], "autojoin") == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            autojoin = TRUE;
+        }
+
+        if (num_args > 4) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[3]);
+            if (num_args == 6 && g_strcmp0(args[4], "autojoin") == 0) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[4]);
+                autojoin = TRUE;
+            }
+        }
+
+        if (num_args > 6) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[4]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[5]);
+            if (num_args == 8 && g_strcmp0(args[6], "autojoin") == 0) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[6]);
+                autojoin = TRUE;
+            }
+        }
+
+        if (autojoin) {
+            found = autocomplete_param_with_func(input, beginning->str, prefs_autocomplete_boolean_choice);
+        } else {
+            found = autocomplete_param_with_ac(input, beginning->str, bookmark_property_ac, TRUE);
+        }
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+
+    g_strfreev(args);
+
+    found = autocomplete_param_with_func(input, "/bookmark remove", bookmark_find);
+    if (found) {
+        return found;
+    }
+    found = autocomplete_param_with_func(input, "/bookmark join", bookmark_find);
+    if (found) {
+        return found;
+    }
+    found = autocomplete_param_with_func(input, "/bookmark update", bookmark_find);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/bookmark", bookmark_ac, TRUE);
+    return found;
+}
+
+static char*
+_notify_autocomplete(ProfWin *window, const char *const input)
+{
+    int i = 0;
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/notify room trigger remove", prefs_autocomplete_room_trigger);
+    if (result) {
+        return result;
+    }
+
+    gchar *boolean_choices1[] = { "/notify room current", "/notify chat current", "/notify typing current",
+        "/notify room text", "/notify chat text" };
+    for (i = 0; i < ARRAY_SIZE(boolean_choices1); i++) {
+        result = autocomplete_param_with_func(input, boolean_choices1[i], prefs_autocomplete_boolean_choice);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify room mention", notify_mention_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify room trigger", notify_trigger_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify room", notify_room_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify chat", notify_chat_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify typing", notify_typing_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    gchar *boolean_choices2[] = { "/notify invite", "/notify sub", "/notify mention", "/notify trigger"};
+    for (i = 0; i < ARRAY_SIZE(boolean_choices2); i++) {
+        result = autocomplete_param_with_func(input, boolean_choices2[i], prefs_autocomplete_boolean_choice);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/notify", notify_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_autoaway_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/autoaway mode", autoaway_mode_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/autoaway time", autoaway_presence_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/autoaway message", autoaway_presence_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_func(input, "/autoaway check", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/autoaway", autoaway_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_log_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/log rotate",
+        prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_func(input, "/log shared",
+        prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/log", log_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_autoconnect_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/autoconnect set", accounts_find_enabled);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/autoconnect", autoconnect_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_otr_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+
+    if (conn_status == JABBER_CONNECTED) {
+        found = autocomplete_param_with_func(input, "/otr start", roster_contact_autocomplete);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/otr log", otr_log_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    // /otr policy always user@server.com
+    if (conn_status == JABBER_CONNECTED) {
+        gboolean result;
+        gchar **args = parse_args(input, 3, 3, &result);
+        if (result && (strcmp(args[0], "policy") == 0)) {
+            GString *beginning = g_string_new("/otr ");
+            g_string_append(beginning, args[0]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+
+            found = autocomplete_param_with_func(input, beginning->str, roster_contact_autocomplete);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        }
+        g_strfreev(args);
+    }
+
+    found = autocomplete_param_with_ac(input, "/otr policy", otr_policy_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_pgp_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+
+    if (conn_status == JABBER_CONNECTED) {
+        found = autocomplete_param_with_func(input, "/pgp start", roster_contact_autocomplete);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp log", pgp_log_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+#ifdef HAVE_LIBGPGME
+    gboolean result;
+    gchar **args = parse_args(input, 2, 3, &result);
+    if ((strncmp(input, "/pgp", 4) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/pgp ");
+        g_string_append(beginning, args[0]);
+        if (args[1]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+        }
+        found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key);
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+    g_strfreev(args);
+#endif
+
+    if (conn_status == JABBER_CONNECTED) {
+        found = autocomplete_param_with_func(input, "/pgp setkey", roster_barejid_autocomplete);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/pgp", pgp_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_plugins_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    if ((strncmp(input, "/plugins load ", 14) == 0) && (strlen(input) > 14)) {
+        if (plugins_load_ac == NULL) {
+            plugins_load_ac = autocomplete_new();
+            GSList *plugins = plugins_unloaded_list();
+            GSList *curr = plugins;
+            while (curr) {
+                autocomplete_add(plugins_load_ac, curr->data);
+                curr = g_slist_next(curr);
+            }
+            g_slist_free_full(plugins, g_free);
+        }
+        result = autocomplete_param_with_ac(input, "/plugins load", plugins_load_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    }
+    result = autocomplete_param_with_ac(input, "/plugins", plugins_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_theme_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    if ((strncmp(input, "/theme load ", 12) == 0) && (strlen(input) > 12)) {
+        if (theme_load_ac == NULL) {
+            theme_load_ac = autocomplete_new();
+            GSList *themes = theme_list();
+            GSList *curr = themes;
+            while (curr) {
+                autocomplete_add(theme_load_ac, curr->data);
+                curr = g_slist_next(curr);
+            }
+            g_slist_free_full(themes, g_free);
+            autocomplete_add(theme_load_ac, "default");
+        }
+        result = autocomplete_param_with_ac(input, "/theme load", theme_load_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    }
+    result = autocomplete_param_with_ac(input, "/theme", theme_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_script_autocomplete_func(const char *const prefix)
+{
+    if (script_show_ac == NULL) {
+        script_show_ac = autocomplete_new();
+        GSList *scripts = scripts_list();
+        GSList *curr = scripts;
+        while (curr) {
+            autocomplete_add(script_show_ac, curr->data);
+            curr = g_slist_next(curr);
+        }
+        g_slist_free_full(scripts, g_free);
+    }
+
+    return autocomplete_complete(script_show_ac, prefix, FALSE);
+}
+
+
+static char*
+_script_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+    if ((strncmp(input, "/script show ", 13) == 0) && (strlen(input) > 13)) {
+        result = autocomplete_param_with_func(input, "/script show", _script_autocomplete_func);
+        if (result) {
+            return result;
+        }
+    }
+
+    if ((strncmp(input, "/script run ", 12) == 0) && (strlen(input) > 12)) {
+        result = autocomplete_param_with_func(input, "/script run", _script_autocomplete_func);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/script", script_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_resource_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    jabber_conn_status_t conn_status = connection_get_status();
+    if (conn_status == JABBER_CONNECTED && window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        PContact contact = roster_get_contact(chatwin->barejid);
+        if (contact) {
+            Autocomplete ac = p_contact_resource_ac(contact);
+            found = autocomplete_param_with_ac(input, "/resource set", ac, FALSE);
+            if (found) {
+                return found;
+            }
+        }
+    }
+
+    found = autocomplete_param_with_func(input, "/resource title", prefs_autocomplete_boolean_choice);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_func(input, "/resource message", prefs_autocomplete_boolean_choice);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/resource", resource_ac, FALSE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_titlebar_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/titlebar show", prefs_autocomplete_boolean_choice);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_func(input, "/titlebar goodbye", prefs_autocomplete_boolean_choice);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/titlebar", titlebar_ac, FALSE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_inpblock_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/inpblock dynamic", prefs_autocomplete_boolean_choice);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/inpblock", inpblock_ac, FALSE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_form_autocomplete(ProfWin *window, const char *const input)
+{
+    if (window->type != WIN_MUC_CONFIG) {
+        return NULL;
+    }
+
+    char *found = NULL;
+
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+    DataForm *form = confwin->form;
+    if (form) {
+        found = autocomplete_param_with_ac(input, "/form help", form->tag_ac, TRUE);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/form", form_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_form_field_autocomplete(ProfWin *window, const char *const input)
+{
+    if (window->type != WIN_MUC_CONFIG) {
+        return NULL;
+    }
+
+    char *found = NULL;
+
+    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+    DataForm *form = confwin->form;
+    if (form == NULL) {
+        return NULL;
+    }
+
+    gchar **split = g_strsplit(input, " ", 0);
+
+    if (g_strv_length(split) == 3) {
+        char *field_tag = split[0]+1;
+        if (form_tag_exists(form, field_tag)) {
+            form_field_type_t field_type = form_get_field_type(form, field_tag);
+            Autocomplete value_ac = form_get_value_ac(form, field_tag);;
+            GString *beginning = g_string_new(split[0]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, split[1]);
+
+            if (((g_strcmp0(split[1], "add") == 0) || (g_strcmp0(split[1], "remove") == 0))
+                    && field_type == FIELD_LIST_MULTI) {
+                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
+
+            } else if ((g_strcmp0(split[1], "remove") == 0) && field_type == FIELD_TEXT_MULTI) {
+                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
+
+            } else if ((g_strcmp0(split[1], "remove") == 0) && field_type == FIELD_JID_MULTI) {
+                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
+            }
+
+            g_string_free(beginning, TRUE);
+        }
+
+    } else if (g_strv_length(split) == 2) {
+        char *field_tag = split[0]+1;
+        if (form_tag_exists(form, field_tag)) {
+            form_field_type_t field_type = form_get_field_type(form, field_tag);
+            Autocomplete value_ac = form_get_value_ac(form, field_tag);;
+
+            switch (field_type)
+            {
+                case FIELD_BOOLEAN:
+                    found = autocomplete_param_with_func(input, split[0], prefs_autocomplete_boolean_choice);
+                    break;
+                case FIELD_LIST_SINGLE:
+                    found = autocomplete_param_with_ac(input, split[0], value_ac, TRUE);
+                    break;
+                case FIELD_LIST_MULTI:
+                case FIELD_JID_MULTI:
+                case FIELD_TEXT_MULTI:
+                    found = autocomplete_param_with_ac(input, split[0], form_field_multi_ac, TRUE);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    g_strfreev(split);
+
+    return found;
+}
+
+static char*
+_occupants_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_ac(input, "/occupants default show", occupants_show_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/occupants default hide", occupants_show_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/occupants default", occupants_default_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/occupants show", occupants_show_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/occupants hide", occupants_show_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/occupants", occupants_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_time_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_ac(input, "/time statusbar", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time lastactivity", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time console", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time chat", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time muc", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time mucconfig", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time private", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time xml", time_format_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    found = autocomplete_param_with_ac(input, "/time", time_ac, TRUE);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_kick_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
+
+        if (nick_ac) {
+            result = autocomplete_param_with_ac(input, "/kick", nick_ac, TRUE);
+            if (result) {
+                return result;
+            }
+        }
+    }
+
+    return result;
+}
+
+static char*
+_ban_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
+
+        if (jid_ac) {
+            result = autocomplete_param_with_ac(input, "/ban", jid_ac, TRUE);
+            if (result) {
+                return result;
+            }
+        }
+    }
+
+    return result;
+}
+
+static char*
+_affiliation_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        gboolean parse_result;
+        Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
+
+        gchar **args = parse_args(input, 3, 3, &parse_result);
+
+        if ((strncmp(input, "/affiliation", 12) == 0) && (parse_result == TRUE)) {
+            GString *beginning = g_string_new("/affiliation ");
+            g_string_append(beginning, args[0]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+
+            result = autocomplete_param_with_ac(input, beginning->str, jid_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (result) {
+                g_strfreev(args);
+                return result;
+            }
+        }
+
+        g_strfreev(args);
+    }
+
+    result = autocomplete_param_with_ac(input, "/affiliation set", affiliation_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/affiliation list", affiliation_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/affiliation", privilege_cmd_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return result;
+}
+
+static char*
+_role_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        gboolean parse_result;
+        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
+
+        gchar **args = parse_args(input, 3, 3, &parse_result);
+
+        if ((strncmp(input, "/role", 5) == 0) && (parse_result == TRUE)) {
+            GString *beginning = g_string_new("/role ");
+            g_string_append(beginning, args[0]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+
+            result = autocomplete_param_with_ac(input, beginning->str, nick_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (result) {
+                g_strfreev(args);
+                return result;
+            }
+        }
+
+        g_strfreev(args);
+    }
+
+    result = autocomplete_param_with_ac(input, "/role set", role_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/role list", role_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/role", privilege_cmd_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return result;
+}
+
+static char*
+_statuses_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/statuses console", statuses_setting_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/statuses chat", statuses_setting_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/statuses muc", statuses_setting_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/statuses", statuses_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_wins_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/wins autotidy", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/wins", wins_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_tls_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/tls revoke", tlscerts_complete);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_func(input, "/tls cert", tlscerts_complete);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/tls certpath", tls_certpath_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_func(input, "/tls show", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/tls", tls_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return result;
+}
+
+static char*
+_receipts_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_func(input, "/receipts send", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_func(input, "/receipts request", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/receipts", receipts_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_alias_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/alias remove", aliases_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/alias", alias_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_connect_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+    gboolean result = FALSE;
+
+    gchar **args = parse_args(input, 2, 6, &result);
+
+    if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/connect ");
+        g_string_append(beginning, args[0]);
+        if (args[1] && args[2]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            if (args[3] && args[4]) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[3]);
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[4]);
+            }
+        }
+        found = autocomplete_param_with_ac(input, beginning->str, connect_property_ac, TRUE);
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+
+    g_strfreev(args);
+
+    result = FALSE;
+    args = parse_args(input, 2, 7, &result);
+
+    if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/connect ");
+        g_string_append(beginning, args[0]);
+        int curr = 0;
+        if (args[1]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+            curr = 1;
+            if (args[2] && args[3]) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[2]);
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[3]);
+                curr = 3;
+                if (args[4] && args[5]) {
+                    g_string_append(beginning, " ");
+                    g_string_append(beginning, args[4]);
+                    g_string_append(beginning, " ");
+                    g_string_append(beginning, args[5]);
+                    curr = 5;
+                }
+            }
+        }
+        if (curr != 0 && (g_strcmp0(args[curr], "tls") == 0)) {
+            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else {
+            g_string_free(beginning, TRUE);
+        }
+    }
+
+    g_strfreev(args);
+
+    found = autocomplete_param_with_func(input, "/connect", accounts_find_enabled);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_help_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/help commands", help_commands_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/help", help_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_join_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+    gboolean result = FALSE;
+
+    found = autocomplete_param_with_func(input, "/join", bookmark_find);
+    if (found) {
+        return found;
+    }
+
+    gchar **args = parse_args(input, 2, 4, &result);
+
+    if ((strncmp(input, "/join", 5) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/join ");
+        g_string_append(beginning, args[0]);
+        if (args[1] && args[2]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+        }
+        found = autocomplete_param_with_ac(input, beginning->str, join_property_ac, TRUE);
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+
+    g_strfreev(args);
+
+    return NULL;
+}
+
+static char*
+_console_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    result = autocomplete_param_with_ac(input, "/console chat", console_msg_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/console muc", console_msg_ac, TRUE);
+    if (result) {
+        return result;
+    }
+    result = autocomplete_param_with_ac(input, "/console private", console_msg_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/console", console_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_win_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/win", win_autocomplete);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_close_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+
+    found = autocomplete_param_with_func(input, "/close", win_close_autocomplete);
+    if (found) {
+        return found;
+    }
+
+    return NULL;
+}
+
+static char*
+_sendfile_autocomplete(ProfWin *window, const char *const input)
+{
+    static char* last_directory = NULL;
+
+    unsigned int output_off = 0;
+
+    char *result = NULL;
+    char *tmp;
+
+    // strip command
+    char *inpcp = (char*)input + 9;
+    while (*inpcp == ' ') {
+        inpcp++;
+    }
+
+    inpcp = strdup(inpcp);
+
+    // strip quotes
+    if (*inpcp == '"') {
+        tmp = strchr(inpcp+1, '"');
+        if (tmp) {
+            *tmp = '\0';
+        }
+        tmp = strdup(inpcp+1);
+        free(inpcp);
+        inpcp = tmp;
+    }
+
+    // expand ~ to $HOME
+    if (inpcp[0] == '~' && inpcp[1] == '/') {
+        if (asprintf(&tmp, "%s/%sfoo", getenv("HOME"), inpcp+2) == -1) {
+            return NULL;
+        }
+        output_off = strlen(getenv("HOME"))+1;
+    } else {
+        if (asprintf(&tmp, "%sfoo", inpcp) == -1) {
+            return NULL;
+        }
+    }
+    free(inpcp);
+    inpcp = tmp;
+
+    char* inpcp2 = strdup(inpcp);
+    char* foofile = strdup(basename(inpcp2));
+    char* directory = strdup(dirname(inpcp));
+    free(inpcp);
+    free(inpcp2);
+
+    if (!last_directory || strcmp(last_directory, directory) != 0) {
+        free(last_directory);
+        last_directory = directory;
+        autocomplete_reset(sendfile_ac);
+
+        struct dirent *dir;
+
+        DIR *d = opendir(directory);
+        if (d) {
+            while ((dir = readdir(d)) != NULL) {
+                if (strcmp(dir->d_name, ".") == 0) {
+                    continue;
+                } else if (strcmp(dir->d_name, "..") == 0) {
+                    continue;
+                } else if (*(dir->d_name) == '.' && *foofile != '.') {
+                    // only show hidden files on explicit request
+                    continue;
+                }
+                char * acstring;
+                if (output_off) {
+                    if (asprintf(&tmp, "%s/%s", directory, dir->d_name) == -1) {
+                        return NULL;
+                    }
+                    if (asprintf(&acstring, "~/%s", tmp+output_off) == -1) {
+                        return NULL;
+                    }
+                    free(tmp);
+                } else if (strcmp(directory, "/") == 0) {
+                    if (asprintf(&acstring, "/%s", dir->d_name) == -1) {
+                        return NULL;
+                    }
+                } else {
+                    if (asprintf(&acstring, "%s/%s", directory, dir->d_name) == -1) {
+                        return NULL;
+                    }
+                }
+                autocomplete_add(sendfile_ac, acstring);
+                free(acstring);
+            }
+            closedir(d);
+        }
+    } else {
+        free(foofile);
+        free(directory);
+    }
+
+    result = autocomplete_param_with_ac(input, "/sendfile", sendfile_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+
+static char*
+_subject_autocomplete(ProfWin *window, const char *const input)
+{
+    char *result = NULL;
+
+    if (window->type == WIN_MUC) {
+        if ((g_strcmp0(input, "/subject e") == 0)
+                || (g_strcmp0(input, "/subject ed") == 0)
+                || (g_strcmp0(input, "/subject edi") == 0)
+                || (g_strcmp0(input, "/subject edit") == 0)
+                || (g_strcmp0(input, "/subject edit ") == 0)
+                || (g_strcmp0(input, "/subject edit \"") == 0)) {
+            ProfMucWin *mucwin = (ProfMucWin*)window;
+            assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+
+            char *subject = muc_subject(mucwin->roomjid);
+            if (subject) {
+                GString *result_str = g_string_new("/subject edit \"");
+                g_string_append(result_str, subject);
+                g_string_append(result_str, "\"");
+
+                result = result_str->str;
+                g_string_free(result_str, FALSE);
+            }
+        }
+    }
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/subject", subject_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char*
+_account_autocomplete(ProfWin *window, const char *const input)
+{
+    char *found = NULL;
+    gboolean result = FALSE;
+
+    gchar **args = parse_args(input, 3, 4, &result);
+
+    if ((strncmp(input, "/account set", 12) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/account set ");
+        g_string_append(beginning, args[1]);
+        if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "otr")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_ac(input, beginning->str, otr_policy_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "status")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_ac(input, beginning->str, account_status_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "tls")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "startscript")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_func(input, beginning->str, _script_autocomplete_func);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "theme")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            if (theme_load_ac == NULL) {
+                theme_load_ac = autocomplete_new();
+                GSList *themes = theme_list();
+                GSList *curr = themes;
+                while (curr) {
+                    autocomplete_add(theme_load_ac, curr->data);
+                    curr = g_slist_next(curr);
+                }
+                g_slist_free_full(themes, g_free);
+                autocomplete_add(theme_load_ac, "default");
+            }
+            found = autocomplete_param_with_ac(input, beginning->str, theme_load_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                return found;
+            }
+#ifdef HAVE_LIBGPGME
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "pgpkeyid")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+#endif
+        } else {
+            found = autocomplete_param_with_ac(input, beginning->str, account_set_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        }
+    }
+
+    if ((strncmp(input, "/account clear", 14) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/account clear ");
+        g_string_append(beginning, args[1]);
+        found = autocomplete_param_with_ac(input, beginning->str, account_clear_ac, TRUE);
+        g_string_free(beginning, TRUE);
+        if (found) {
+            g_strfreev(args);
+            return found;
+        }
+    }
+
+    g_strfreev(args);
+
+    found = autocomplete_param_with_ac(input, "/account default", account_default_ac, TRUE);
+    if(found){
+        return found;
+    }
+
+    int i = 0;
+    gchar *account_choice[] = { "/account set", "/account show", "/account enable",
+        "/account disable", "/account rename", "/account clear", "/account remove",
+        "/account default set" };
+
+    for (i = 0; i < ARRAY_SIZE(account_choice); i++) {
+        found = autocomplete_param_with_func(input, account_choice[i], accounts_find_all);
+        if (found) {
+            return found;
+        }
+    }
+
+    found = autocomplete_param_with_ac(input, "/account", account_ac, TRUE);
+    return found;
+}
diff --git a/src/command/cmd_ac.h b/src/command/cmd_ac.h
new file mode 100644
index 00000000..b294fcd5
--- /dev/null
+++ b/src/command/cmd_ac.h
@@ -0,0 +1,62 @@
+/*
+ * cmd_ac.h
+ *
+ * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef CMD_AC_H
+#define CMD_AC_H
+
+#include "config/preferences.h"
+#include "command/cmd_funcs.h"
+
+void cmd_ac_init(void);
+void cmd_ac_uninit(void);
+
+void cmd_ac_add(const char *const value);
+void cmd_ac_add_help(const char *const value);
+void cmd_ac_add_cmd(Command *command);
+void cmd_ac_add_alias(ProfAlias *alias);
+void cmd_ac_add_alias_value(char *value);
+
+void cmd_ac_remove(const char *const value);
+void cmd_ac_remove_alias_value(char *value);
+
+gboolean cmd_ac_exists(char *cmd);
+
+void cmd_ac_add_form_fields(DataForm *form);
+void cmd_ac_remove_form_fields(DataForm *form);
+
+char* cmd_ac_complete(ProfWin *window, const char *const input);
+
+void cmd_ac_reset(ProfWin *window);
+
+#endif
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
new file mode 100644
index 00000000..9669f4b6
--- /dev/null
+++ b/src/command/cmd_defs.c
@@ -0,0 +1,2367 @@
+/*
+ * cmd_defs.c
+ *
+ * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#define _GNU_SOURCE 1
+
+#include "config.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <glib.h>
+
+#include "chat_session.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_funcs.h"
+#include "command/cmd_ac.h"
+#include "common.h"
+#include "config/accounts.h"
+#include "config/preferences.h"
+#include "config/theme.h"
+#include "config/tlscerts.h"
+#include "config/scripts.h"
+#include "contact.h"
+#include "roster_list.h"
+#include "jid.h"
+#include "log.h"
+#include "muc.h"
+#include "plugins/plugins.h"
+#include "profanity.h"
+#include "tools/autocomplete.h"
+#include "tools/parser.h"
+#include "tools/tinyurl.h"
+#include "xmpp/xmpp.h"
+#include "ui/ui.h"
+#include "window_list.h"
+
+#ifdef HAVE_LIBOTR
+#include "otr/otr.h"
+#endif
+
+#ifdef HAVE_LIBGPGME
+#include "pgp/gpg.h"
+#endif
+
+#define CMD_TAG_CHAT        "chat"
+#define CMD_TAG_GROUPCHAT   "groupchat"
+#define CMD_TAG_ROSTER      "roster"
+#define CMD_TAG_PRESENCE    "presence"
+#define CMD_TAG_CONNECTION  "connection"
+#define CMD_TAG_DISCOVERY   "discovery"
+#define CMD_TAG_UI          "ui"
+#define CMD_TAG_PLUGINS     "plugins"
+
+#define CMD_MAINFUNC(func)  func,
+#define CMD_NOMAINFUNC      NULL,
+#define CMD_SUBFUNCS(...)   { __VA_ARGS__, { NULL, NULL } },
+#define CMD_NOSUBFUNCS      { { NULL, NULL } },
+
+#define CMD_NOTAGS          { { NULL },
+#define CMD_TAGS(...)       { { __VA_ARGS__, NULL },
+#define CMD_SYN(...)        { __VA_ARGS__, NULL },
+#define CMD_DESC(desc)      desc,
+#define CMD_NOARGS          { { NULL, NULL } },
+#define CMD_ARGS(...)       { __VA_ARGS__, { NULL, NULL } },
+#define CMD_NOEXAMPLES      { NULL } }
+#define CMD_EXAMPLES(...)   { __VA_ARGS__, NULL } }
+
+GHashTable *commands = NULL;
+
+static gboolean _cmd_has_tag(Command *pcmd, const char *const tag);
+
+/*
+ * Command list
+ */
+static struct cmd_t command_defs[] =
+{
+    { "/help",
+        parse_args, 0, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_help)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/help [<area>|<command>]")
+        CMD_DESC(
+            "Help on using Profanity. Passing no arguments list help areas. "
+            "For command help, optional arguments are shown using square brackets e.g. [argument], "
+            "arguments representing variables rather than a literal name are surrounded by angle brackets "
+            "e.g. <argument>. "
+            "Arguments that may be one of a number of values are separated by a pipe "
+            "e.g. val1|val2|val3.")
+        CMD_ARGS(
+            { "<area>",    "Summary help for commands in a certain area of functionality." },
+            { "<command>", "Full help for a specific command, for example '/help connect'." })
+        CMD_EXAMPLES(
+            "/help commands",
+            "/help presence",
+            "/help who")
+    },
+
+    { "/about",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_about)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/about")
+        CMD_DESC(
+            "Show version and license information.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/connect",
+        parse_args, 0, 7, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_connect)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/connect [<account>]",
+            "/connect <account> [server <server>] [port <port>] [tls force|allow|disable]")
+        CMD_DESC(
+            "Login to a chat service. "
+            "If no account is specified, the default is used if one is configured. "
+            "A local account is created with the JID as it's name if it doesn't already exist.")
+        CMD_ARGS(
+            { "<account>",         "The local account you wish to connect with, or a JID if connecting for the first time." },
+            { "server <server>",   "Supply a server if it is different to the domain part of your JID." },
+            { "port <port>",       "The port to use if different to the default (5222, or 5223 for SSL)." },
+            { "tls force",         "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
+            { "tls allow",         "Use TLS for the connection if it is available." },
+            { "tls disable",       "Disable TLS for the connection." })
+        CMD_EXAMPLES(
+            "/connect",
+            "/connect myuser@gmail.com",
+            "/connect myuser@mycompany.com server talk.google.com",
+            "/connect bob@someplace port 5678",
+            "/connect me@localhost.test.org server 127.0.0.1 tls disable",
+            "/connect me@chatty server chatty.com port 5443")
+        },
+
+    { "/tls",
+        parse_args, 1, 3, NULL,
+        CMD_SUBFUNCS(
+            { "certpath",   cmd_tls_certpath },
+            { "trust",      cmd_tls_trust },
+            { "trusted",    cmd_tls_trusted },
+            { "revoke",     cmd_tls_revoke },
+            { "show",       cmd_tls_show },
+            { "cert",       cmd_tls_cert })
+        CMD_NOMAINFUNC
+        CMD_TAGS(
+            CMD_TAG_CONNECTION,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/tls allow",
+            "/tls always",
+            "/tls deny",
+            "/tls cert [<fingerprint>]",
+            "/tls trust",
+            "/tls trusted",
+            "/tls revoke <fingerprint>",
+            "/tls certpath",
+            "/tls certpath set <path>",
+            "/tls certpath clear",
+            "/tls show on|off")
+        CMD_DESC(
+            "Handle TLS certificates. ")
+        CMD_ARGS(
+            { "allow",                "Allow connection to continue with TLS certificate." },
+            { "always",               "Always allow connections with TLS certificate." },
+            { "deny",                 "Abort connection." },
+            { "cert",                 "Show the current TLS certificate." },
+            { "cert <fingerprint>",   "Show details of trusted certificate." },
+            { "trust",                "Add the current TLS certificate to manually trusted certificates." },
+            { "trusted",              "List summary of manually trusted certificates (with '/tls always' or '/tls trust')." },
+            { "revoke <fingerprint>", "Remove a manually trusted certificate." },
+            { "certpath",             "Show the trusted certificate path." },
+            { "certpath set <path>",  "Specify filesystem path containing trusted certificates." },
+            { "certpath clear",       "Clear the trusted certificate path." },
+            { "show on|off",          "Show or hide the TLS indicator in the titlebar." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/disconnect",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_disconnect)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/disconnect")
+        CMD_DESC(
+            "Disconnect from the current chat service.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/msg",
+        parse_args_with_freetext, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_msg)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/msg <contact> [<message>]",
+            "/msg <nick> [<message>]")
+        CMD_DESC(
+            "Send a one to one chat message, or a private message to a chat room occupant. "
+            "If the message is omitted, a new chat window will be opened without sending a message. "
+            "Use quotes if the nickname includes spaces.")
+        CMD_ARGS(
+            { "<contact>",             "Open chat window with contact, by JID or nickname." },
+            { "<contact> [<message>]", "Send message to contact, by JID or nickname." },
+            { "<nick>",                "Open private chat window with chat room occupant." },
+            { "<nick> [<message>]",    "Send a private message to a chat room occupant." })
+        CMD_EXAMPLES(
+            "/msg myfriend@server.com Hey, here's a message!",
+            "/msg otherfriend@server.com",
+            "/msg Bob Here is a private message",
+            "/msg \"My Friend\" Hi, how are you?")
+    },
+
+    { "/roster",
+        parse_args_with_freetext, 0, 4, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_roster)
+        CMD_TAGS(
+            CMD_TAG_ROSTER,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/roster",
+            "/roster online",
+            "/roster show [offline|resource|presence|status|empty|priority|contacts|rooms]",
+            "/roster hide [offline|resource|presence|status|empty|priority|contacts|rooms]",
+            "/roster by group|presence|none",
+            "/roster count unread|items|off",
+            "/roster count zero on|off",
+            "/roster order name|presence",
+            "/roster unread before|after|off",
+            "/roster room char <char>|none",
+            "/roster room private char <char>|none",
+            "/roster room position first|last",
+            "/roster room by service|none",
+            "/roster room order name|unread",
+            "/roster room unread before|after|off",
+            "/roster private room|group|off",
+            "/roster private char <char>|none",
+            "/roster header char <char>|none",
+            "/roster presence indent <indent>",
+            "/roster contact char <char>|none",
+            "/roster contact indent <indent>",
+            "/roster resource char <char>|none",
+            "/roster resource indent <indent>",
+            "/roster resource join on|off",
+            "/roster size <percent>",
+            "/roster wrap on|off",
+            "/roster add <jid> [<nick>]",
+            "/roster remove <jid>",
+            "/roster remove_all contacts",
+            "/roster nick <jid> <nick>",
+            "/roster clearnick <jid>")
+        CMD_DESC(
+            "Manage your roster, and roster display settings. "
+            "Passing no arguments lists all contacts in your roster.")
+        CMD_ARGS(
+            { "online",                     "Show all online contacts in console." },
+            { "show",                       "Show the roster panel." },
+            { "show offline",               "Show offline contacts in roster panel." },
+            { "show resource",              "Show contact's connected resources in roster panel." },
+            { "show presence",              "Show contact's presence in roster panel." },
+            { "show status",                "Show contact's status message in roster panel." },
+            { "show empty",                 "Show empty groups in roster panel." },
+            { "show priority",              "Show resource priority in roster panel." },
+            { "show contacts",              "Show contacts in roster panel." },
+            { "show rooms",                 "Show chat rooms in roster panel." },
+            { "hide",                       "Hide the roster panel." },
+            { "hide offline",               "Hide offline contacts in roster panel." },
+            { "hide resource",              "Hide contact's connected resources in roster panel." },
+            { "hide presence",              "Hide contact's presence in roster panel." },
+            { "hide status",                "Hide contact's status message in roster panel." },
+            { "hide empty",                 "Hide empty groups in roster panel." },
+            { "hide priority",              "Hide resource priority in roster panel." },
+            { "hide contacts",              "Hide contacts in roster panel." },
+            { "hide rooms",                 "Hide chat rooms in roster panel." },
+            { "by group",                   "Group contacts in roster panel by roster group." },
+            { "by presence",                "Group contacts in roster panel by presence." },
+            { "by none",                    "No grouping in roster panel." },
+            { "count unread",               "Show unread message count with roster headers." },
+            { "count items",                "Show item count with roster headers." },
+            { "count off",                  "Do not show any count with roster headers." },
+            { "count zero on",              "Show roster header count when 0." },
+            { "count zero off",             "Hide roster header count when 0." },
+            { "order name",                 "Order roster contacts by name only." },
+            { "order presence",             "Order roster contacts by presence, and then by name." },
+            { "unread before",              "Show unread message count before contact." },
+            { "unread after",               "Show unread message count after contact." },
+            { "unread off",                 "Do not show unread message count for contacts." },
+            { "room char <char>",           "Prefix rooms with specified character." },
+            { "room char none",             "Remove room character prefix." },
+            { "room private char <char>",   "Prefix private room chat with specified character when displayed with room." },
+            { "room private char none",     "Remove private room chat character prefix when displayed with room." },
+            { "room position first",        "Show rooms first in roster." },
+            { "room position last",         "Show rooms last in roster." },
+            { "room by service",            "Group rooms by chat service." },
+            { "room by none",               "Do not group rooms." },
+            { "room order name",            "Order rooms by name." },
+            { "room order unread",          "Order rooms by unread messages, and then by name." },
+            { "room unread before",         "Show unread message count before room." },
+            { "room unread after",          "Show unread message count after room." },
+            { "room unread off",            "Do not show unread message count for rooms." },
+            { "private room",               "Show room private chats with the room." },
+            { "private group",              "Show room private chats as a separate roster group." },
+            { "private off",                "Do not show room private chats." },
+            { "private char <char>",        "Prefix private room chats with specified character when displayed in separate group." },
+            { "private char none",          "Remove private room chat character prefix." },
+            { "header char <char>",         "Prefix roster headers with specified character." },
+            { "header char none",           "Remove roster header character prefix." },
+            { "contact char <char>",        "Prefix roster contacts with specified character." },
+            { "contact char none",          "Remove roster contact character prefix." },
+            { "contact indent <indent>",    "Indent contact line by <indent> spaces (0 to 10)." },
+            { "resource char <char>",       "Prefix roster resources with specified character." },
+            { "resource char none",         "Remove roster resource character prefix." },
+            { "resource indent <indent>",   "Indent resource line by <indent> spaces (0 to 10)." },
+            { "resource join on|off",       "Join resource with previous line when only one available resource." },
+            { "presence indent <indent>",   "Indent presence line by <indent> spaces (-1 to 10), a value of -1 will show presence on the previous line." },
+            { "size <precent>",             "Percentage of the screen taken up by the roster (1-99)." },
+            { "wrap on|off",                "Enable or disable line wrapping in roster panel." },
+            { "add <jid> [<nick>]",         "Add a new item to the roster." },
+            { "remove <jid>",               "Removes an item from the roster." },
+            { "remove_all contacts",        "Remove all items from roster." },
+            { "nick <jid> <nick>",          "Change a contacts nickname." },
+            { "clearnick <jid>",            "Removes the current nickname." })
+        CMD_EXAMPLES(
+            "/roster",
+            "/roster add someone@contacts.org",
+            "/roster add someone@contacts.org Buddy",
+            "/roster remove someone@contacts.org",
+            "/roster nick myfriend@chat.org My Friend",
+            "/roster clearnick kai@server.com",
+            "/roster size 15")
+    },
+
+    { "/blocked",
+        parse_args, 0, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_blocked)
+        CMD_TAGS(
+            CMD_TAG_ROSTER,
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/blocked",
+            "/blocked add [<jid>]",
+            "/blocked remove <jid>")
+        CMD_DESC(
+            "Manage blocked users, calling with no arguments shows the current list of blocked users.")
+        CMD_ARGS(
+            { "add [<jid>]",    "Block the specified Jabber ID, if in a chat window, and not jid specified, the current recipient will be blocked." },
+            { "remove <jid>",   "Remove the specified Jabber ID from the blocked list." })
+        CMD_EXAMPLES(
+            "/blocked add spammer@spam.org")
+    },
+
+    { "/group",
+        parse_args_with_freetext, 0, 3, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_group)
+        CMD_TAGS(
+            CMD_TAG_ROSTER,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/group",
+            "/group show <group>",
+            "/group add <group> <contat>",
+            "/group remove <group> <contact>")
+        CMD_DESC(
+            "View, add to, and remove from roster groups. "
+            "Passing no argument will list all roster groups.")
+        CMD_ARGS(
+            { "show <group>",             "List all roster items a group." },
+            { "add <group> <contact>",    "Add a contact to a group." },
+            { "remove <group> <contact>", "Remove a contact from a group." })
+        CMD_EXAMPLES(
+            "/group",
+            "/group show friends",
+            "/group add friends newfriend@server.org",
+            "/group add family Brother",
+            "/group remove colleagues boss@work.com")
+    },
+
+    { "/info",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_info)
+        CMD_TAGS(
+            CMD_TAG_ROSTER,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/info",
+            "/info <contact>|<nick>")
+        CMD_DESC(
+            "Show information about a contact, room, or room member. "
+            "Passing no argument in a chat window will use the current recipient. "
+            "Passing no argument in a chat room will display information about the room.")
+        CMD_ARGS(
+            { "<contact>", "The contact you wish to view information about." },
+            { "<nick>",    "When in a chat room, the occupant you wish to view information about." })
+        CMD_EXAMPLES(
+            "/info mybuddy@chat.server.org",
+            "/info kai")
+    },
+
+    { "/caps",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_caps)
+        CMD_TAGS(
+            CMD_TAG_DISCOVERY,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/caps",
+            "/caps <fulljid>|<nick>")
+        CMD_DESC(
+            "Find out a contacts, or room members client software capabilities. "
+            "If in private chat initiated from a chat room, no parameter is required.")
+        CMD_ARGS(
+            { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see capabilities." },
+            { "<nick>",    "If in a chat room, nickname for which you wish to see capabilities." })
+        CMD_EXAMPLES(
+            "/caps mybuddy@chat.server.org/laptop",
+            "/caps mybuddy@chat.server.org/phone",
+            "/caps bruce")
+    },
+
+    { "/software",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_software)
+        CMD_TAGS(
+            CMD_TAG_DISCOVERY,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/software",
+            "/software <fulljid>|<nick>")
+        CMD_DESC(
+            "Find out a contact, or room members software version information. "
+            "If in private chat initiated from a chat room, no parameter is required. "
+            "If the contact's software does not support software version requests, nothing will be displayed.")
+        CMD_ARGS(
+            { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see software information." },
+            { "<nick>",    "If in a chat room, nickname for which you wish to see software information." })
+        CMD_EXAMPLES(
+            "/software mybuddy@chat.server.org/laptop",
+            "/software mybuddy@chat.server.org/phone",
+            "/software bruce")
+    },
+
+    { "/status",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_status)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/status",
+            "/status <contact>|<nick>")
+        CMD_DESC(
+            "Find out a contact, or room members presence information. "
+            "If in a chat window the parameter is not required, the current recipient will be used.")
+        CMD_ARGS(
+            { "<contact>", "The contact who's presence you which to see." },
+            { "<nick>",    "If in a chat room, the occupant who's presence you wish to see." })
+        CMD_EXAMPLES(
+            "/status buddy@server.com",
+            "/status jon")
+    },
+
+    { "/resource",
+        parse_args, 1, 2, &cons_resource_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_resource)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/resource set <resource>",
+            "/resource off",
+            "/resource title on|off",
+            "/resource message on|off")
+        CMD_DESC(
+            "Override chat session resource, and manage resource display settings.")
+        CMD_ARGS(
+            { "set <resource>", "Set the resource to which messages will be sent." },
+            { "off",            "Let the server choose which resource to route messages to." },
+            { "title on|off",   "Show or hide the current resource in the titlebar." },
+            { "message on|off", "Show or hide the resource when showing an incoming message." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/join",
+        parse_args, 0, 5, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_join)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/join",
+            "/join <room> [nick <nick>] [password <password>]")
+        CMD_DESC(
+            "Join a chat room at the conference server. "
+            "If no room is supplied, a generated name will be used with the format private-chat-[UUID]. "
+            "If the domain part is not included in the room name, the account preference 'muc.service' will be used. "
+            "If no nickname is specified the account preference 'muc.nick' will be used which by default is the localpart of your JID. "
+            "If the room doesn't exist, and the server allows it, a new one will be created.")
+        CMD_ARGS(
+            { "<room>",              "The chat room to join." },
+            { "nick <nick>",         "Nickname to use in the room." },
+            { "password <password>", "Password if the room requires one." })
+        CMD_EXAMPLES(
+            "/join",
+            "/join jdev@conference.jabber.org",
+            "/join jdev@conference.jabber.org nick mynick",
+            "/join private@conference.jabber.org nick mynick password mypassword",
+            "/join jdev")
+    },
+
+    { "/leave",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_leave)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/leave")
+        CMD_DESC(
+            "Leave the current chat room.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/invite",
+        parse_args_with_freetext, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_invite)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/invite <contact> [<message>]")
+        CMD_DESC(
+            "Send an invite to a contact for the current chat room.")
+        CMD_ARGS(
+            { "<contact>", "The contact you wish to invite." },
+            { "<message>", "An optional message to send with the invite." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/invites",
+        parse_args_with_freetext, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_invites)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/invites")
+        CMD_DESC(
+            "Show all rooms that you have been invited to, and not accepted or declined.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/decline",
+        parse_args_with_freetext, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_decline)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/decline <room>")
+        CMD_DESC(
+            "Decline a chat room invitation.")
+        CMD_ARGS(
+            { "<room>", "The room for the invite you wish to decline." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/room",
+        parse_args, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_room)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/room accept|destroy|config")
+        CMD_DESC(
+            "Chat room configuration.")
+        CMD_ARGS(
+            { "accept",  "Accept default room configuration." },
+            { "destroy", "Reject default room configuration, and destroy the room." },
+            { "config",  "Edit room configuration." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/kick",
+        parse_args_with_freetext, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_kick)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/kick <nick> [<reason>]")
+        CMD_DESC(
+            "Kick occupant from chat room.")
+        CMD_ARGS(
+            { "<nick>",   "Nickname of the occupant to kick from the room." },
+            { "<reason>", "Optional reason for kicking the occupant." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/ban",
+        parse_args_with_freetext, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_ban)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/ban <jid> [<reason>]")
+        CMD_DESC(
+            "Ban user from chat room.")
+        CMD_ARGS(
+            { "<jid>",    "Bare JID of the user to ban from the room." },
+            { "<reason>", "Optional reason for banning the user." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/subject",
+        parse_args_with_freetext, 0, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_subject)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/subject set <subject>",
+            "/subject edit <subject>",
+            "/subject prepend <text>",
+            "/subject append <text>",
+            "/subject clear")
+        CMD_DESC(
+            "Set, modify, or clear room subject.")
+        CMD_ARGS(
+            { "set <subject>",  "Set the room subject." },
+            { "edit <subject>", "Edit the current room subject, tab autocompletion will display the subject to edit." },
+            { "prepend <text>", "Prepend text to the current room subject, use double quotes if a trailing space is needed." },
+            { "append <text>",  "Append text to the current room subject, use double quotes if a preceding space is needed." },
+            { "clear",          "Clear the room subject." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/affiliation",
+        parse_args_with_freetext, 1, 4, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_affiliation)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/affiliation set <affiliation> <jid> [<reason>]",
+            "/affiliation list [<affiliation>]")
+        CMD_DESC(
+            "Manage room affiliations. "
+            "Affiliation may be one of owner, admin, member, outcast or none.")
+        CMD_ARGS(
+            { "set <affiliation> <jid> [<reason>]", "Set the affiliation of user with jid, with an optional reason." },
+            { "list [<affiliation>]",               "List all users with the specified affiliation, or all if none specified." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/role",
+        parse_args_with_freetext, 1, 4, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_role)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/role set <role> <nick> [<reason>]",
+            "/role list [<role>]")
+        CMD_DESC(
+            "Manage room roles. "
+            "Role may be one of moderator, participant, visitor or none.")
+        CMD_ARGS(
+            { "set <role> <nick> [<reason>]", "Set the role of occupant with nick, with an optional reason." },
+            { "list [<role>]",                "List all occupants with the specified role, or all if none specified." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/occupants",
+        parse_args, 1, 3, cons_occupants_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_occupants)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/occupants show|hide [jid]",
+            "/occupants default show|hide [jid]",
+            "/occupants size [<percent>]")
+        CMD_DESC(
+            "Show or hide room occupants, and occupants panel display settings.")
+        CMD_ARGS(
+            { "show",                  "Show the occupants panel in current room." },
+            { "hide",                  "Hide the occupants panel in current room." },
+            { "show jid",              "Show jid in the occupants panel in current room." },
+            { "hide jid",              "Hide jid in the occupants panel in current room." },
+            { "default show|hide",     "Whether occupants are shown by default in new rooms." },
+            { "default show|hide jid", "Whether occupants jids are shown by default in new rooms." },
+            { "size <percent>",        "Percentage of the screen taken by the occupants list in rooms (1-99)." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/form",
+        parse_args, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_form)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/form show",
+            "/form submit",
+            "/form cancel",
+            "/form help [<tag>]")
+        CMD_DESC(
+            "Form configuration.")
+        CMD_ARGS(
+            { "show",         "Show the current form." },
+            { "submit",       "Submit the current form." },
+            { "cancel",       "Cancel changes to the current form." },
+            { "help [<tag>]", "Display help for form, or a specific field." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/rooms",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_rooms)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/rooms [<service>]")
+        CMD_DESC(
+            "List the chat rooms available at the specified conference service. "
+            "If no argument is supplied, the account preference 'muc.service' is used, 'conference.<domain-part>' by default.")
+        CMD_ARGS(
+            { "<service>", "The conference service to query." })
+        CMD_EXAMPLES(
+            "/rooms conference.jabber.org")
+    },
+
+    { "/bookmark",
+        parse_args, 0, 8, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_bookmark)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/bookmark",
+            "/bookmark list",
+            "/bookmark add <room> [nick <nick>] [password <password>] [autojoin on|off]",
+            "/bookmark update <room> [nick <nick>] [password <password>] [autojoin on|off]",
+            "/bookmark remove <room>",
+            "/bookmark join <room>")
+        CMD_DESC(
+            "Manage bookmarks and join bookmarked rooms. "
+            "In a chat room, no arguments will bookmark the current room, setting autojoin to \"on\".")
+        CMD_ARGS(
+            { "list", "List all bookmarks." },
+            { "add <room>", "Add a bookmark." },
+            { "remove <room>", "Remove a bookmark." },
+            { "update <room>", "Update the properties associated with a bookmark." },
+            { "nick <nick>", "Nickname used in the chat room." },
+            { "password <password>", "Password if required, may be stored in plaintext on your server." },
+            { "autojoin on|off", "Whether to join the room automatically on login." },
+            { "join <room>", "Join room using the properties associated with the bookmark." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/disco",
+        parse_args, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_disco)
+        CMD_TAGS(
+            CMD_TAG_DISCOVERY)
+        CMD_SYN(
+            "/disco info [<jid>]",
+            "/disco items [<jid>]")
+        CMD_DESC(
+            "Find out information about an entities supported services. "
+            "Calling with no arguments will query the server you are currently connected to.")
+        CMD_ARGS(
+            { "info [<jid>]", "List protocols and features supported by an entity." },
+            { "items [<jid>]", "List items associated with an entity." })
+        CMD_EXAMPLES(
+            "/disco info",
+            "/disco items myserver.org",
+            "/disco items conference.jabber.org",
+            "/disco info myfriend@server.com/laptop")
+    },
+
+    { "/sendfile",
+        parse_args_with_freetext, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_sendfile)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/sendfile <file>")
+        CMD_DESC(
+            "Send a file using XEP-0363 HTTP file transfer.")
+        CMD_ARGS(
+            { "<file>", "Path to the file." })
+        CMD_EXAMPLES(
+            "/sendfile /etc/hosts",
+            "/sendfile ~/images/sweet_cat.jpg")
+    },
+
+    { "/lastactivity",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_lastactivity)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/lastactivity on|off",
+            "/lastactivity [<jid>]")
+        CMD_DESC(
+            "Enable/disable sending last activity, and send last activity requests.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable sending of last activity." },
+            { "<jid>",  "The JID of the entity to query, omitting the JID will query your server." })
+        CMD_EXAMPLES(
+            "/lastactivity",
+            "/lastactivity off",
+            "/lastactivity alice@securechat.org",
+            "/lastactivity alice@securechat.org/laptop",
+            "/lastactivity someserver.com")
+    },
+
+    { "/nick",
+        parse_args_with_freetext, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_nick)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/nick <nickname>")
+        CMD_DESC(
+            "Change your nickname in the current chat room.")
+        CMD_ARGS(
+            { "<nickname>", "Your new nickname." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/win",
+        parse_args, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_win)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/win console",
+            "/win <num>",
+            "/win <barejid>",
+            "/win <nick>",
+            "/win <roomjid>",
+            "/win <roomoccupantjid>",
+            "/win xmlconsole")
+        CMD_DESC(
+            "Move to the specified window.")
+        CMD_ARGS(
+            { "console",            "Go to the Console window." },
+            { "<num>",              "Go to specified window number." },
+            { "<barejid>",          "Go to chat window with contact by JID if open." },
+            { "<nick>",             "Go to chat window with contact by nickname if open." },
+            { "<roomjid>",          "Go to chat room window with roomjid if open." },
+            { "<roomoccupantjid>",  "Go to private chat roomoccupantjid if open." },
+            { "xmlconsole",         "Go to the XML Console window if open." })
+        CMD_EXAMPLES(
+            "/win console",
+            "/win 4",
+            "/win friend@chat.org",
+            "/win Eddie",
+            "/win bigroom@conference.chat.org",
+            "/win bigroom@conference.chat.org/bruce")
+    },
+
+    { "/wins",
+        parse_args, 0, 3, NULL,
+        CMD_SUBFUNCS(
+            { "unread",     cmd_wins_unread },
+            { "tidy",       cmd_wins_tidy },
+            { "prune",      cmd_wins_prune },
+            { "swap",       cmd_wins_swap },
+            { "autotidy",   cmd_wins_autotidy })
+        CMD_MAINFUNC(cmd_wins)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/wins",
+            "/wins unread",
+            "/wins tidy",
+            "/wins autotidy on|off",
+            "/wins prune",
+            "/wins swap <source> <target>")
+        CMD_DESC(
+            "Manage windows. "
+            "Passing no argument will list all currently active windows and information about their usage.")
+        CMD_ARGS(
+            { "unread",                 "List windows with unread messages." },
+            { "tidy",                   "Move windows so there are no gaps." },
+            { "autotidy on|off",        "Automatically remove gaps when closing windows." },
+            { "prune",                  "Close all windows with no unread messages, and then tidy so there are no gaps." },
+            { "swap <source> <target>", "Swap windows, target may be an empty position." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/sub",
+        parse_args, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_sub)
+        CMD_TAGS(
+            CMD_TAG_ROSTER)
+        CMD_SYN(
+            "/sub request [<jid>]",
+            "/sub allow [<jid>]",
+            "/sub deny [<jid>]",
+            "/sub show [<jid>]",
+            "/sub sent",
+            "/sub received")
+        CMD_DESC(
+            "Manage subscriptions to contact presence. "
+            "If jid is omitted, the contact of the current window is used.")
+        CMD_ARGS(
+            { "request [<jid>]", "Send a subscription request to the user." },
+            { "allow [<jid>]",   "Approve a contact's subscription request." },
+            { "deny [<jid>]",    "Remove subscription for a contact, or deny a request." },
+            { "show [<jid>]",    "Show subscription status for a contact." },
+            { "sent",            "Show all sent subscription requests pending a response." },
+            { "received",        "Show all received subscription requests awaiting your response." })
+        CMD_EXAMPLES(
+            "/sub request myfriend@jabber.org",
+            "/sub allow myfriend@jabber.org",
+            "/sub request",
+            "/sub sent")
+    },
+
+    { "/tiny",
+        parse_args, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_tiny)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/tiny <url>")
+        CMD_DESC(
+            "Send url as tinyurl in current chat.")
+        CMD_ARGS(
+            { "<url>", "The url to make tiny." })
+        CMD_EXAMPLES(
+            "Example: /tiny http://www.profanity.im")
+    },
+
+    { "/who",
+        parse_args, 0, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_who)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT,
+            CMD_TAG_ROSTER)
+        CMD_SYN(
+            "/who",
+            "/who online|offline|away|dnd|xa|chat|available|unavailable|any [<group>]",
+            "/who moderator|participant|visitor",
+            "/who owner|admin|member")
+        CMD_DESC(
+            "Show contacts or room occupants with chosen status, role or affiliation")
+        CMD_ARGS(
+            { "offline|away|dnd|xa|chat", "Show contacts or room occupants with specified presence." },
+            { "online", "Contacts that are online, chat, away, xa, dnd." },
+            { "available", "Contacts that are available for chat - online, chat." },
+            { "unavailable", "Contacts that are not available for chat - offline, away, xa, dnd." },
+            { "any", "Contacts with any status (same as calling with no argument)." },
+            { "<group>", "Filter the results by the specified roster group, not applicable in chat rooms." },
+            { "moderator|participant|visitor", "Room occupants with the specified role." },
+            { "owner|admin|member", "Room occupants with the specified affiliation." })
+        CMD_EXAMPLES(
+            "/who",
+            "/who xa",
+            "/who online friends",
+            "/who any family",
+            "/who participant",
+            "/who admin")
+    },
+
+    { "/close",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_close)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/close",
+            "/close <num>",
+            "/close <barejid>",
+            "/close <nick>",
+            "/close <roomjid>",
+            "/close <roomoccupantjid>",
+            "/close xmlconsole",
+            "/close all|read")
+        CMD_DESC(
+            "Close windows. "
+            "Passing no argument closes the current window.")
+        CMD_ARGS(
+            { "<num>",              "Close specified window number." },
+            { "<barejid>",          "Close chat window with contact by JID if open." },
+            { "<nick>",             "Close chat window with contact by nickname if open." },
+            { "<roomjid>",          "Close chat room window with roomjid if open." },
+            { "<roomoccupantjid>",  "Close private chat roomoccupantjid if open." },
+            { "xmlconsole",         "Close the XML Console window if open." },
+            { "all",                "Close all windows." },
+            { "read",               "Close all windows that have no unread messages." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/clear",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_clear)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/clear")
+        CMD_DESC(
+            "Clear the current window.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/quit",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_quit)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/quit")
+        CMD_DESC(
+            "Logout of any current session, and quit Profanity.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/privileges",
+        parse_args, 1, 1, &cons_privileges_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_privileges)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/privileges on|off")
+        CMD_DESC(
+            "Group occupants panel by role, and show role information in chat rooms.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable privilege information." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/charset",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_charset)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/charset")
+        CMD_DESC(
+            "Display information about the current character set supported by the terminal. ")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/beep",
+        parse_args, 1, 1, &cons_beep_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_beep)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/beep on|off")
+        CMD_DESC(
+            "Switch the terminal bell on or off. "
+            "The bell will sound when incoming messages are received. "
+            "If the terminal does not support sounds, it may attempt to flash the screen instead.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable terminal bell." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/console",
+        parse_args, 2, 2, &cons_console_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_console)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/console chat all|first|none",
+            "/console muc all|first|none",
+            "/console private all|first|none")
+        CMD_DESC(
+            "Configure what is displayed in the console window when messages are received. "
+            "The default is set to 'all' for all types of messages.")
+        CMD_ARGS(
+            { "chat all",       "Indicate all new chat messages in the console." },
+            { "chat first",     "Indicate only the first new message per chat in the console." },
+            { "chat none",      "Do not show any new chat messages in the console window." },
+            { "muc all",        "Indicate all new chat room messages in the console." },
+            { "muc first",      "Indicate only the first new message in each room in the console." },
+            { "muc none",       "Do not show any new chat room messages in the console window." },
+            { "private all",    "Indicate all new private room messages in the console." },
+            { "private first",  "Indicate only the first private room message in the console." },
+            { "private none",   "Do not show any new private room messages in the console window." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/encwarn",
+        parse_args, 1, 1, &cons_encwarn_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_encwarn)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/encwarn on|off")
+        CMD_DESC(
+            "Titlebar encryption warning.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable the unencrypted warning message in the titlebar." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/presence",
+        parse_args, 1, 1, &cons_presence_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_presence)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/presence on|off")
+        CMD_DESC(
+            "Show the contacts presence in the titlebar.")
+        CMD_ARGS(
+            { "on|off", "Switch display of the contacts presence in the titlebar on or off." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/wrap",
+        parse_args, 1, 1, &cons_wrap_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_wrap)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/wrap on|off")
+        CMD_DESC(
+            "Word wrapping.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable word wrapping in the main window." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/time",
+        parse_args, 1, 3, &cons_time_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_time)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/time console|chat|muc|mucconfig|private|xml set <format>",
+            "/time console|chat|muc|mucconfig|private|xml off",
+            "/time statusbar set <format>",
+            "/time statusbar off",
+            "/time lastactivity set <format>")
+        CMD_DESC(
+            "Configure time display preferences. "
+            "Time formats are strings supported by g_date_time_format. "
+            "See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format for more details. "
+            "Setting the format to an unsupported string, will display the string. "
+            "If the format contains spaces, it must be surrounded with double quotes.")
+        CMD_ARGS(
+            { "console set <format>",      "Set time format for console window." },
+            { "console off",               "Do not show time in console window." },
+            { "chat set <format>",         "Set time format for chat windows." },
+            { "chat off",                  "Do not show time in chat windows." },
+            { "muc set <format>",          "Set time format for chat room windows." },
+            { "muc off",                   "Do not show time in chat room windows." },
+            { "mucconfig set <format>",    "Set time format for chat room config windows." },
+            { "mucconfig off",             "Do not show time in chat room config windows." },
+            { "private set <format>",      "Set time format for private chat windows." },
+            { "private off",               "Do not show time in private chat windows." },
+            { "xml set <format>",          "Set time format for XML console window." },
+            { "xml off",                   "Do not show time in XML console window." },
+            { "statusbar set <format>",    "Change time format in statusbar." },
+            { "statusbar off",             "Do not show time in status bar." },
+            { "lastactivity set <format>", "Change time format for last activity." })
+        CMD_EXAMPLES(
+            "/time console set %H:%M:%S",
+            "/time chat set \"%d-%m-%y %H:%M:%S\"",
+            "/time xml off",
+            "/time statusbar set %H:%M",
+            "/time lastactivity set \"%d-%m-%y %H:%M:%S\"")
+    },
+
+    { "/inpblock",
+        parse_args, 2, 2, &cons_inpblock_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_inpblock)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/inpblock timeout <millis>",
+            "/inpblock dynamic on|off")
+        CMD_DESC(
+            "How long to wait for keyboard input before checking for new messages or checking for state changes such as 'idle'.")
+        CMD_ARGS(
+            { "timeout <millis>", "Time to wait (1-1000) in milliseconds before reading input from the terminal buffer, default: 1000." },
+            { "dynamic on|off", "Start with 0 millis and dynamically increase up to timeout when no activity, default: on." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/notify",
+        parse_args_with_freetext, 0, 4, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_notify)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/notify chat on|off",
+            "/notify chat current on|off",
+            "/notify chat text on|off",
+            "/notify room on|off",
+            "/notify room mention on|off",
+            "/notify room mention case_sensitive|case_insensitive",
+            "/notify room mention word_whole|word_part",
+            "/notify room current on|off",
+            "/notify room text on|off",
+            "/notify room trigger add <text>",
+            "/notify room trigger remove <text>",
+            "/notify room trigger list",
+            "/notify room trigger on|off",
+            "/notify on|off",
+            "/notify mention on|off",
+            "/notify trigger on|off",
+            "/notify reset",
+            "/notify remind <seconds>",
+            "/notify typing on|off",
+            "/notify typing current on|off",
+            "/notify invite on|off",
+            "/notify sub on|off")
+        CMD_DESC(
+            "Settings for various kinds of desktop notifications.")
+        CMD_ARGS(
+            { "chat on|off",                    "Notifications for regular chat messages." },
+            { "chat current on|off",            "Whether to show regular chat message notifications when the window is focussed." },
+            { "chat text on|off",               "Show message text in regular message notifications." },
+            { "room on|off",                    "Notifications for all chat room messages, 'mention' only notifies when your nick is mentioned." },
+            { "room mention on|off",            "Notifications for all chat room messages when your nick is mentioned." },
+            { "room mention case_sensitive",    "Set room mention notifications as case sensitive." },
+            { "room mention case_insensitive",  "Set room mention notifications as case insensitive." },
+            { "room mention word_whole",        "Set room mention notifications only on whole word match, i.e. when nickname is not part of a larger word." },
+            { "room mention word_part",         "Set room mention notifications on partial word match, i.e. nickname may be part of a larger word." },
+            { "room current on|off",            "Whether to show all chat room messages notifications when the window is focussed." },
+            { "room text on|off",               "Show message text in chat room message notifications." },
+            { "room trigger add <text>",        "Notify when specified text included in all chat room messages." },
+            { "room trigger remove <text>",     "Remove chat room notification trigger." },
+            { "room trigger list",              "List all chat room triggers." },
+            { "room trigger on|off",            "Enable or disable all chat room notification triggers." },
+            { "on|off",                         "Override the global message setting for the current chat room." },
+            { "mention on|off",                 "Override the global 'mention' setting for the current chat room." },
+            { "trigger on|off",                 "Override the global 'trigger' setting for the current chat room." },
+            { "reset",                          "Reset to global notification settings for the current chat room." },
+            { "remind <seconds>",               "Notification reminder period for unread messages, use 0 to disable." },
+            { "typing on|off",                  "Notifications when contacts are typing." },
+            { "typing current on|off",          "Whether typing notifications are triggered for the current window." },
+            { "invite on|off",                  "Notifications for chat room invites." },
+            { "sub on|off",                     "Notifications for subscription requests." })
+        CMD_EXAMPLES(
+            "/notify chat on",
+            "/notify chat text on",
+            "/notify room mention on",
+            "/notify room trigger add beer",
+            "/notify room trigger on",
+            "/notify room current off",
+            "/notify room text off",
+            "/notify remind 60",
+            "/notify typing on",
+            "/notify invite on")
+    },
+
+    { "/flash",
+        parse_args, 1, 1, &cons_flash_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_flash)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/flash on|off")
+        CMD_DESC(
+            "Make the terminal flash when incoming messages are received in another window. "
+            "If the terminal doesn't support flashing, it may attempt to beep.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable terminal flash." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/tray",
+        parse_args, 1, 2, &cons_tray_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_tray)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/tray on|off",
+            "/tray read on|off",
+            "/tray timer <seconds>")
+        CMD_DESC(
+            "Display an icon in the tray that will indicate new messages.")
+        CMD_ARGS(
+            { "on|off",             "Show tray icon." },
+            { "read on|off",        "Show tray icon when no unread messages." },
+            { "timer <seconds>",    "Set tray icon timer, seconds must be between 1-10" })
+        CMD_NOEXAMPLES
+    },
+
+    { "/intype",
+        parse_args, 1, 1, &cons_intype_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_intype)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/intype on|off")
+        CMD_DESC(
+            "Show when a contact is typing in the console, and in active message window.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable contact typing messages." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/splash",
+        parse_args, 1, 1, &cons_splash_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_splash)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/splash on|off")
+        CMD_DESC(
+            "Switch on or off the ascii logo on start up and when the /about command is called.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable splash logo." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/autoconnect",
+        parse_args, 1, 2, &cons_autoconnect_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_autoconnect)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/autoconnect set <account>",
+            "/autoconnect off")
+        CMD_DESC(
+            "Enable or disable autoconnect on start up. "
+            "The setting can be overridden by the -a (--account) command line option.")
+        CMD_ARGS(
+            { "set <account>", "Connect with account on start up." },
+            { "off",           "Disable autoconnect." })
+        CMD_EXAMPLES(
+            "/autoconnect set jc@stuntteam.org",
+            "/autoconnect off")
+    },
+
+    { "/vercheck",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_vercheck)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/vercheck on|off")
+        CMD_DESC(
+            "Check for new versions when Profanity starts, and when the /about command is run.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable the version check." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/titlebar",
+        parse_args, 2, 2, &cons_titlebar_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_titlebar)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/titlebar show on|off",
+            "/titlebar goodbye on|off")
+        CMD_DESC(
+            "Allow Profanity to modify the window title bar.")
+        CMD_ARGS(
+            { "show on|off",    "Show current logged in user, and unread messages as the window title." },
+            { "goodbye on|off", "Show a message in the title when exiting profanity." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/alias",
+        parse_args_with_freetext, 1, 3, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_alias)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/alias list",
+            "/alias add <name> <value>",
+            "/alias remove <name>")
+        CMD_DESC(
+            "Add, remove or list command aliases.")
+        CMD_ARGS(
+            { "list",               "List all aliases." },
+            { "add <name> <value>", "Add a new command alias." },
+            { "remove <name>",      "Remove a command alias." })
+        CMD_EXAMPLES(
+            "/alias add friends /who online friends",
+            "/alias add /q /quit",
+            "/alias a /away \"I'm in a meeting.\"",
+            "/alias remove q",
+            "/alias list")
+    },
+
+    { "/chlog",
+        parse_args, 1, 1, &cons_chlog_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_chlog)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/chlog on|off")
+        CMD_DESC(
+            "Switch chat logging on or off. "
+            "This setting will be enabled if /history is set to on. "
+            "When disabling this option, /history will also be disabled. "
+            "See the /grlog setting for enabling logging of chat room (groupchat) messages.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable chat logging." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/grlog",
+        parse_args, 1, 1, &cons_grlog_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_grlog)
+        CMD_TAGS(
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/grlog on|off")
+        CMD_DESC(
+            "Switch chat room logging on or off. "
+            "See the /chlog setting for enabling logging of one to one chat.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable chat room logging." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/states",
+        parse_args, 1, 1, &cons_states_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_states)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/states on|off")
+        CMD_DESC(
+            "Send chat state notifications to recipient during chat sessions, such as typing, paused, active, gone.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable sending of chat state notifications." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/pgp",
+        parse_args, 1, 3, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_pgp)
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/pgp libver",
+            "/pgp keys",
+            "/pgp contacts",
+            "/pgp setkey <contact> <keyid>",
+            "/pgp start [<contact>]",
+            "/pgp end",
+            "/pgp log on|off|redact",
+            "/pgp char <char>")
+        CMD_DESC(
+            "Open PGP commands to manage keys, and perform PGP encryption during chat sessions. "
+            "See the /account command to set your own PGP key.")
+        CMD_ARGS(
+            { "libver",                   "Show which version of the libgpgme library is being used." },
+            { "keys",                     "List all keys known to the system." },
+            { "contacts",                 "Show contacts with assigned public keys." },
+            { "setkey <contact> <keyid>", "Manually associate a contact with a public key." },
+            { "start [<contact>]",        "Start PGP encrypted chat, current contact will be used if not specified." },
+            { "end",                      "End PGP encrypted chat with the current recipient." },
+            { "log on|off",               "Enable or disable plaintext logging of PGP encrypted messages." },
+            { "log redact",               "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." },
+            { "char <char>",              "Set the character to be displayed next to PGP encrypted messages." })
+        CMD_EXAMPLES(
+            "/pgp log off",
+            "/pgp setkey buddy@buddychat.org BA19CACE5A9592C5",
+            "/pgp start buddy@buddychat.org",
+            "/pgp end",
+            "/pgp char P")
+    },
+
+    { "/otr",
+        parse_args, 1, 3, NULL,
+        CMD_SUBFUNCS(
+            { "char",       cmd_otr_char },
+            { "log",        cmd_otr_log },
+            { "libver",     cmd_otr_libver },
+            { "policy",     cmd_otr_policy },
+            { "gen",        cmd_otr_gen },
+            { "myfp",       cmd_otr_myfp },
+            { "theirfp",    cmd_otr_theirfp },
+            { "start",      cmd_otr_start },
+            { "end",        cmd_otr_end },
+            { "trust",      cmd_otr_trust },
+            { "untrust",    cmd_otr_untrust },
+            { "secret",     cmd_otr_secret },
+            { "question",   cmd_otr_question },
+            { "answer",     cmd_otr_answer })
+        CMD_NOMAINFUNC
+        CMD_TAGS(
+            CMD_TAG_CHAT,
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/otr libver",
+            "/otr gen",
+            "/otr myfp|theirfp",
+            "/otr start [<contact>]",
+            "/otr end",
+            "/otr trust|untrust",
+            "/otr secret <secret>",
+            "/otr question <question> <answer>",
+            "/otr answer <answer>",
+            "/otr policy manual|opportunistic|always [<contact>]",
+            "/otr log on|off|redact",
+            "/otr char <char>")
+        CMD_DESC(
+            "Off The Record (OTR) commands to manage keys, and perform OTR encryption during chat sessions.")
+        CMD_ARGS(
+            { "libver",                         "Show which version of the libotr library is being used." },
+            { "gen",                            "Generate your private key." },
+            { "myfp",                           "Show your fingerprint." },
+            { "theirfp",                        "Show contacts fingerprint." },
+            { "start [<contact>]",              "Start an OTR session with contact, or current recipient if omitted." },
+            { "end",                            "End the current OTR session," },
+            { "trust|untrust",                  "Indicate whether or not you trust the contact's fingerprint." },
+            { "secret <secret>",                "Verify a contact's identity using a shared secret." },
+            { "question <question> <answer>",   "Verify a contact's identity using a question and expected answer." },
+            { "answer <answer>",                "Respond to a question answer verification request with your answer." },
+            { "policy manual",                  "Set the global OTR policy to manual, OTR sessions must be started manually." },
+            { "policy manual <contact>",        "Set the OTR policy to manual for a specific contact." },
+            { "policy opportunistic",           "Set the global OTR policy to opportunistic, an OTR session will be attempted upon starting a conversation." },
+            { "policy opportunistic <contact>", "Set the OTR policy to opportunistic for a specific contact." },
+            { "policy always",                  "Set the global OTR policy to always, an error will be displayed if an OTR session cannot be initiated upon starting a conversation." },
+            { "policy always <contact>",        "Set the OTR policy to always for a specific contact." },
+            { "log on|off",                     "Enable or disable plaintext logging of OTR encrypted messages." },
+            { "log redact",                     "Log OTR encrypted messages, but replace the contents with [redacted]. This is the default." },
+            { "char <char>",                    "Set the character to be displayed next to OTR encrypted messages." })
+        CMD_EXAMPLES(
+            "/otr log off",
+            "/otr policy manual",
+            "/otr policy opportunistic mrfriend@workchat.org",
+            "/otr gen",
+            "/otr start buddy@buddychat.org",
+            "/otr myfp",
+            "/otr theirfp",
+            "/otr question \"What is the name of my rabbit?\" fiffi",
+            "/otr end",
+            "/otr char *")
+    },
+
+    { "/outtype",
+        parse_args, 1, 1, &cons_outtype_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_outtype)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/outtype on|off")
+        CMD_DESC(
+            "Send typing notifications, chat states (/states) will be enabled if this setting is enabled.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable sending typing notifications." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/gone",
+        parse_args, 1, 1, &cons_gone_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_gone)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/gone <minutes>")
+        CMD_DESC(
+            "Send a 'gone' state to the recipient after the specified number of minutes. "
+            "Chat states (/states) will be enabled if this setting is set.")
+        CMD_ARGS(
+            { "<minutes>", "Number of minutes of inactivity before sending the 'gone' state, a value of 0 will disable sending this state." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/history",
+        parse_args, 1, 1, &cons_history_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_history)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/history on|off")
+        CMD_DESC(
+            "Switch chat history on or off, /chlog will automatically be enabled when this setting is on. "
+            "When history is enabled, previous messages are shown in chat windows.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable showing chat history." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/log",
+        parse_args, 1, 2, &cons_log_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_log)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/log where",
+            "/log rotate on|off",
+            "/log maxsize <bytes>",
+            "/log shared on|off")
+        CMD_DESC(
+            "Manage profanity log settings.")
+        CMD_ARGS(
+            { "where",           "Show the current log file location." },
+            { "rotate on|off",   "Rotate log, default on." },
+            { "maxsize <bytes>", "With rotate enabled, specifies the max log size, defaults to 1048580 (1MB)." },
+            { "shared on|off",   "Share logs between all instances, default: on. When off, the process id will be included in the log." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/carbons",
+        parse_args, 1, 1, &cons_carbons_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_carbons)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/carbons on|off")
+        CMD_DESC(
+            "Enable or disable message carbons. "
+            "Message carbons ensure that both sides of all conversations are shared with all the user's clients that implement this protocol.")
+        CMD_ARGS(
+            { "on|off", "Enable or disable message carbons." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/receipts",
+        parse_args, 2, 2, &cons_receipts_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_receipts)
+        CMD_TAGS(
+            CMD_TAG_CHAT)
+        CMD_SYN(
+            "/receipts request on|off",
+            "/receipts send on|off")
+        CMD_DESC(
+            "Enable or disable message delivery receipts. The interface will indicate when a message has been received.")
+        CMD_ARGS(
+            { "request on|off", "Whether or not to request a receipt upon sending a message." },
+            { "send on|off",    "Whether or not to send a receipt if one has been requested with a received message." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/reconnect",
+        parse_args, 1, 1, &cons_reconnect_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_reconnect)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/reconnect <seconds>")
+        CMD_DESC(
+            "Set the reconnect attempt interval for when the connection is lost.")
+        CMD_ARGS(
+            { "<seconds>", "Number of seconds before attempting to reconnect, a value of 0 disables reconnect." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/autoping",
+        parse_args, 2, 2, &cons_autoping_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_autoping)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/autoping set <seconds>",
+            "/autoping timeout <seconds>")
+        CMD_DESC(
+            "Set the interval between sending ping requests to the server to ensure the connection is kept alive.")
+        CMD_ARGS(
+            { "set <seconds>",      "Number of seconds between sending pings, a value of 0 disables autoping." },
+            { "timeout <seconds>",  "Seconds to wait for autoping responses, after which the connection is considered broken." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/ping",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_ping)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION)
+        CMD_SYN(
+            "/ping [<jid>]")
+        CMD_DESC(
+            "Sends an IQ ping stanza to the specified JID. "
+            "If no JID is supplied, your chat server will be pinged.")
+        CMD_ARGS(
+            { "<jid>", "The Jabber ID to send the ping request to." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/autoaway",
+        parse_args_with_freetext, 2, 3, &cons_autoaway_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_autoaway)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/autoaway mode idle|away|off",
+            "/autoaway time away|xa <minutes>",
+            "/autoaway message away|xa <message>|off",
+            "/autoaway check on|off")
+        CMD_DESC(
+            "Manage autoway settings for idle time.")
+        CMD_ARGS(
+            { "mode idle",              "Sends idle time, status remains online." },
+            { "mode away",              "Sends away and xa presence as well as idle time." },
+            { "mode off",               "Disabled (default)." },
+            { "time away <minutes>",    "Number of minutes before the away presence is sent, default: 15." },
+            { "time xa <minutes>",      "Number of minutes before the xa presence is sent, default: 0 (disabled)." },
+            { "message away <message>", "Optional message to send with the away presence, default: off (disabled)." },
+            { "message xa <message>",   "Optional message to send with the xa presence, default: off (disabled)." },
+            { "message away off",       "Send no message with away presence." },
+            { "message xa off",         "Send no message with xa presence." },
+            { "check on|off",           "When enabled, checks for activity and sends online presence, default: on." })
+        CMD_EXAMPLES(
+            "/autoaway mode away",
+            "/autoaway time away 30",
+            "/autoaway message away Away from computer for a while",
+            "/autoaway time xa 120",
+            "/autoaway message xa Away from computer for a very long time",
+            "/autoaway check off")
+    },
+
+    { "/priority",
+        parse_args, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_priority)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/priority <priority>")
+        CMD_DESC(
+            "Set priority for the current account. "
+            "See the /account command for specific priority settings per presence status.")
+        CMD_ARGS(
+            { "<priority>", "Number between -128 and 127, default: 0." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/account",
+        parse_args, 0, 4, NULL,
+        CMD_SUBFUNCS(
+            { "list",       cmd_account_list },
+            { "show",       cmd_account_show },
+            { "add",        cmd_account_add },
+            { "remove",     cmd_account_remove },
+            { "enable",     cmd_account_enable },
+            { "disable",    cmd_account_disable },
+            { "rename",     cmd_account_rename },
+            { "default",    cmd_account_default },
+            { "set",        cmd_account_set },
+            { "clear",      cmd_account_clear })
+        CMD_MAINFUNC(cmd_account)
+        CMD_TAGS(
+            CMD_TAG_CONNECTION
+            CMD_TAG_PRESENCE,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/account",
+            "/account list",
+            "/account show <account>",
+            "/account enable|disable <account>",
+            "/account default set <account>",
+            "/account default off",
+            "/account add <account>",
+            "/account remove <account>",
+            "/account rename <account> <newaccount>",
+            "/account set <account> jid <jid>",
+            "/account set <account> server <server>",
+            "/account set <account> port <port>",
+            "/account set <account> status <presence>",
+            "/account set <account> status last",
+            "/account set <account> <presence> <priority>",
+            "/account set <account> resource <resource>",
+            "/account set <account> password <password>",
+            "/account set <account> eval_password <command>",
+            "/account set <account> muc <service>",
+            "/account set <account> nick <nick>",
+            "/account set <account> otr <policy>",
+            "/account set <account> pgpkeyid <pgpkeyid>",
+            "/account set <account> startscript <script>",
+            "/account set <account> tls force|allow|disable",
+            "/account set <account> theme <theme>",
+            "/account clear <account> password",
+            "/account clear <account> eval_password",
+            "/account clear <account> server",
+            "/account clear <account> port",
+            "/account clear <account> otr",
+            "/account clear <account> pgpkeyid",
+            "/account clear <account> startscript")
+        CMD_DESC(
+            "Commands for creating and managing accounts. "
+            "Calling with no arguments will display information for the current account.")
+        CMD_ARGS(
+            { "list",                                   "List all accounts." },
+            { "enable <account>",                       "Enable the account, it will be used for autocompletion." },
+            { "show <account>",                         "Show details for the specified account." },
+            { "disable <account>",                      "Disable the account." },
+            { "default set <account>",                  "Set the default account, used when no argument passed to the /connect command." },
+            { "default off",                            "Clear the default account setting." },
+            { "add <account>",                          "Create a new account." },
+            { "remove <account>",                       "Remove an account." },
+            { "rename <account> <newaccount>",          "Rename 'account' to 'newaccount'." },
+            { "set <account> jid <jid>",                "Set the Jabber ID for the account, account name will be used if not set." },
+            { "set <account> server <server>",          "The chat server, if different to the domainpart of the JID." },
+            { "set <account> port <port>",              "The port used for connecting if not the default (5222, or 5223 for SSL)." },
+            { "set <account> status <presence>",        "The presence status to use on login." },
+            { "set <account> status last",              "Use your last status before logging out, when logging in." },
+            { "set <account> <presence> <priority>",    "Set the priority (-128..127) to use for the specified presence." },
+            { "set <account> resource <resource>",      "The resource to be used for this account." },
+            { "set <account> password <password>",      "Password for the account, note this is currently stored in plaintext if set." },
+            { "set <account> eval_password <command>",  "Shell command evaluated to retrieve password for the account. Can be used to retrieve password from keyring." },
+            { "set <account> muc <service>",            "The default MUC chat service to use, defaults to 'conference.<domainpart>' where the domain part is from the account JID." },
+            { "set <account> nick <nick>",              "The default nickname to use when joining chat rooms." },
+            { "set <account> otr <policy>",             "Override global OTR policy for this account, see /otr." },
+            { "set <account> pgpkeyid <pgpkeyid>",      "Set the ID of the PGP key for this account, see /pgp." },
+            { "set <account> startscript <script>",     "Set the script to execute after connecting." },
+            { "set <account> tls force",                "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
+            { "set <account> tls allow",                "Use TLS for the connection if it is available." },
+            { "set <account> tls disable",              "Disable TLS for the connection." },
+            { "set <account> <theme>",                  "Set the UI theme for the account." },
+            { "clear <account> server",                 "Remove the server setting for this account." },
+            { "clear <account> port",                   "Remove the port setting for this account." },
+            { "clear <account> password",               "Remove the password setting for this account." },
+            { "clear <account> eval_password",          "Remove the eval_password setting for this account." },
+            { "clear <account> otr",                    "Remove the OTR policy setting for this account." },
+            { "clear <account> pgpkeyid",               "Remove pgpkeyid associated with this account." },
+            { "clear <account> startscript",            "Remove startscript associated with this account." },
+            { "clear <account> theme",                  "Clear the theme setting for the account, the global theme will be used." })
+        CMD_EXAMPLES(
+            "/account add me",
+            "/account set me jid me@chatty",
+            "/account set me server talk.chat.com",
+            "/account set me port 5111",
+            "/account set me muc chatservice.mycompany.com",
+            "/account set me nick dennis",
+            "/account set me status dnd",
+            "/account set me dnd -1",
+            "/account rename me gtalk")
+    },
+
+    { "/plugins",
+        parse_args, 0, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_plugins)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/plugins",
+            "/plugins load <plugin>")
+        CMD_DESC(
+            "Manage plugins. Passing no arguments lists currently loaded plugins.")
+        CMD_ARGS(
+            { "load <plugin>",       "Load a plugin." })
+        CMD_EXAMPLES(
+            "/plugin load browser.py")
+    },
+
+    { "/prefs",
+        parse_args, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_prefs)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/prefs [ui|desktop|chat|log|conn|presence|otr|pgp]")
+        CMD_DESC(
+            "Show preferences for different areas of functionality. "
+            "Passing no arguments shows all preferences.")
+        CMD_ARGS(
+            { "ui",       "User interface preferences." },
+            { "desktop",  "Desktop notification preferences." },
+            { "chat",     "Chat state preferences." },
+            { "log",      "Logging preferences." },
+            { "conn",     "Connection handling preferences." },
+            { "presence", "Chat presence preferences." },
+            { "otr",      "Off The Record preferences." },
+            { "pgp",      "OpenPGP preferences." })
+        CMD_NOEXAMPLES
+    },
+
+    { "/theme",
+        parse_args, 1, 2, &cons_theme_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_theme)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/theme list",
+            "/theme load <theme>",
+            "/theme colours",
+            "/theme properties")
+        CMD_DESC(
+            "Load a theme, includes colours and UI options.")
+        CMD_ARGS(
+            { "list",           "List all available themes." },
+            { "load <theme>",   "Load the specified theme. 'default' will reset to the default theme." },
+            { "colours",        "Show colour values as rendered by the terminal." },
+            { "properties",     "Show colour settings for current theme." })
+        CMD_EXAMPLES(
+            "/theme list",
+            "/theme load forest")
+    },
+
+    { "/statuses",
+        parse_args, 2, 2, &cons_statuses_setting,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_statuses)
+        CMD_TAGS(
+            CMD_TAG_UI,
+            CMD_TAG_CHAT,
+            CMD_TAG_GROUPCHAT)
+        CMD_SYN(
+            "/statuses console|chat|muc all|online|none")
+        CMD_DESC(
+            "Configure which presence changes are displayed in various windows. "
+            "The default is 'all' for all windows.")
+        CMD_ARGS(
+            { "console", "Configure what is displayed in the console window." },
+            { "chat",    "Configure what is displayed in chat windows." },
+            { "muc",     "Configure what is displayed in chat room windows." },
+            { "all",     "Show all presence changes." },
+            { "online",  "Show only online/offline changes." },
+            { "none",    "Don't show any presence changes." })
+        CMD_EXAMPLES(
+            "/statuses console none",
+            "/statuses chat online",
+            "/statuses muc all")
+    },
+
+    { "/xmlconsole",
+        parse_args, 0, 0, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_xmlconsole)
+        CMD_TAGS(
+            CMD_TAG_UI)
+        CMD_SYN(
+            "/xmlconsole")
+        CMD_DESC(
+            "Open the XML console to view incoming and outgoing XMPP traffic.")
+        CMD_NOARGS
+        CMD_NOEXAMPLES
+    },
+
+    { "/away",
+        parse_args_with_freetext, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_away)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/away [<message>]")
+        CMD_DESC(
+            "Set your status to 'away'.")
+        CMD_ARGS(
+            { "<message>",  "Optional message to use with the status." })
+        CMD_EXAMPLES(
+            "/away",
+            "/away Gone for lunch")
+    },
+
+    { "/chat",
+        parse_args_with_freetext, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_chat)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/chat [<message>]")
+        CMD_DESC(
+            "Set your status to 'chat' (available for chat).")
+        CMD_ARGS(
+            { "<message>",  "Optional message to use with the status." })
+        CMD_EXAMPLES(
+            "/chat",
+            "/chat Please talk to me!")
+    },
+
+    { "/dnd",
+        parse_args_with_freetext, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_dnd)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/dnd [<message>]")
+        CMD_DESC(
+            "Set your status to 'dnd' (do not disturb).")
+        CMD_ARGS(
+            { "<message>",  "Optional message to use with the status." })
+        CMD_EXAMPLES(
+            "/dnd",
+            "/dnd I'm in the zone")
+    },
+
+    { "/online",
+        parse_args_with_freetext, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_online)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/online [<message>]")
+        CMD_DESC(
+            "Set your status to 'online'.")
+        CMD_ARGS(
+            { "<message>",  "Optional message to use with the status." })
+        CMD_EXAMPLES(
+            "/online",
+            "/online Up the Irons!")
+    },
+
+    { "/xa",
+        parse_args_with_freetext, 0, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_xa)
+        CMD_TAGS(
+            CMD_TAG_PRESENCE)
+        CMD_SYN(
+            "/xa [<message>]")
+        CMD_DESC(
+            "Set your status to 'xa' (extended away).")
+        CMD_ARGS(
+            { "<message>",  "Optional message to use with the status." })
+        CMD_EXAMPLES(
+            "/xa",
+            "/xa This meeting is going to be a long one")
+    },
+
+    { "/script",
+        parse_args, 1, 2, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_script)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/script run <script>",
+            "/script list",
+            "/script show <script>")
+        CMD_DESC(
+            "Run command scripts. "
+            "Scripts are stored in $XDG_DATA_HOME/profanity/scripts/ which is usually $HOME/.local/share/profanity/scripts/.")
+        CMD_ARGS(
+            { "script run <script>",    "Execute a script." },
+            { "script list",            "List all scripts TODO." },
+            { "script show <script>",   "Show the commands in script TODO." })
+        CMD_EXAMPLES(
+            "/script list",
+            "/script run myscript",
+            "/script show somescript")
+    },
+
+    { "/export",
+        parse_args, 1, 1, NULL,
+        CMD_NOSUBFUNCS
+        CMD_MAINFUNC(cmd_export)
+        CMD_NOTAGS
+        CMD_SYN(
+            "/export <filepath>")
+        CMD_DESC(
+            "Exports contacts to a csv file.")
+        CMD_ARGS(
+            { "<filepath>", "Path to the output file." })
+        CMD_EXAMPLES(
+            "/export /path/to/output.csv",
+            "/export ~/contacts.csv")
+    },
+};
+
+
+/*
+ * Initialise command autocompleter and history
+ */
+void
+cmd_init(void)
+{
+    log_info("Initialising commands");
+
+    cmd_ac_init();
+
+    // load command defs into hash table
+    commands = g_hash_table_new(g_str_hash, g_str_equal);
+    unsigned int i;
+    for (i = 0; i < ARRAY_SIZE(command_defs); i++) {
+        Command *pcmd = command_defs+i;
+
+        // add to hash
+        g_hash_table_insert(commands, pcmd->cmd, pcmd);
+
+        // add to commands and help autocompleters
+        cmd_ac_add_cmd(pcmd);
+    }
+
+    // load aliases
+    GList *aliases = prefs_get_aliases();
+    GList *curr = aliases;
+    while (curr) {
+        ProfAlias *alias = curr->data;
+        cmd_ac_add_alias(alias);
+        curr = g_list_next(curr);
+    }
+    prefs_free_aliases(aliases);
+}
+
+void
+cmd_uninit(void)
+{
+    cmd_ac_uninit();
+}
+
+gboolean
+cmd_valid_tag(const char *const str)
+{
+    return ((g_strcmp0(str, CMD_TAG_CHAT) == 0) ||
+        (g_strcmp0(str, CMD_TAG_GROUPCHAT) == 0) ||
+        (g_strcmp0(str, CMD_TAG_PRESENCE) == 0) ||
+        (g_strcmp0(str, CMD_TAG_ROSTER) == 0) ||
+        (g_strcmp0(str, CMD_TAG_DISCOVERY) == 0) ||
+        (g_strcmp0(str, CMD_TAG_CONNECTION) == 0) ||
+        (g_strcmp0(str, CMD_TAG_UI) == 0) ||
+        (g_strcmp0(str, CMD_TAG_PLUGINS) == 0));
+}
+
+Command*
+cmd_get(const char *const command)
+{
+    if (commands) {
+        return g_hash_table_lookup(commands, command);
+    } else {
+        return NULL;
+    }
+}
+
+GList*
+cmd_get_ordered(const char *const tag)
+{
+    GList *ordered_commands = NULL;
+
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+
+    g_hash_table_iter_init(&iter, commands);
+    while (g_hash_table_iter_next(&iter, &key, &value)) {
+        Command *pcmd = (Command *)value;
+        if (tag) {
+            if (_cmd_has_tag(pcmd, tag)) {
+                ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
+            }
+        } else {
+            ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
+        }
+    }
+
+    return ordered_commands;
+}
+
+static gboolean
+_cmd_has_tag(Command *pcmd, const char *const tag)
+{
+    int i = 0;
+    for (i = 0; pcmd->help.tags[i] != NULL; i++) {
+        if (g_strcmp0(tag, pcmd->help.tags[i]) == 0) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+static int
+_cmp_command(Command *cmd1, Command *cmd2)
+{
+    return g_strcmp0(cmd1->cmd, cmd2->cmd);
+}
+
+void
+command_docgen(void)
+{
+    GList *cmds = NULL;
+    unsigned int i;
+    for (i = 0; i < ARRAY_SIZE(command_defs); i++) {
+        Command *pcmd = command_defs+i;
+        cmds = g_list_insert_sorted(cmds, pcmd, (GCompareFunc)_cmp_command);
+    }
+
+    FILE *toc_fragment = fopen("toc_fragment.html", "w");
+    FILE *main_fragment = fopen("main_fragment.html", "w");
+
+    fputs("<ul><li><ul><li>\n", toc_fragment);
+    fputs("<hr>\n", main_fragment);
+
+    GList *curr = cmds;
+    while (curr) {
+        Command *pcmd = curr->data;
+
+        fprintf(toc_fragment, "<a href=\"#%s\">%s</a>,\n", &pcmd->cmd[1], pcmd->cmd);
+        fprintf(main_fragment, "<a name=\"%s\"></a>\n", &pcmd->cmd[1]);
+        fprintf(main_fragment, "<h4>%s</h4>\n", pcmd->cmd);
+
+        fputs("<p><b>Synopsis</b></p>\n", main_fragment);
+        fputs("<p><pre><code>", main_fragment);
+        int i = 0;
+        while (pcmd->help.synopsis[i]) {
+            char *str1 = str_replace(pcmd->help.synopsis[i], "<", "&lt;");
+            char *str2 = str_replace(str1, ">", "&gt;");
+            fprintf(main_fragment, "%s\n", str2);
+            i++;
+        }
+        fputs("</code></pre></p>\n", main_fragment);
+
+        fputs("<p><b>Description</b></p>\n", main_fragment);
+        fputs("<p>", main_fragment);
+        fprintf(main_fragment, "%s\n", pcmd->help.desc);
+        fputs("</p>\n", main_fragment);
+
+        if (pcmd->help.args[0][0] != NULL) {
+            fputs("<p><b>Arguments</b></p>\n", main_fragment);
+            fputs("<table>", main_fragment);
+            for (i = 0; pcmd->help.args[i][0] != NULL; i++) {
+                fputs("<tr>", main_fragment);
+                fputs("<td>", main_fragment);
+                fputs("<code>", main_fragment);
+                char *str1 = str_replace(pcmd->help.args[i][0], "<", "&lt;");
+                char *str2 = str_replace(str1, ">", "&gt;");
+                fprintf(main_fragment, "%s", str2);
+                fputs("</code>", main_fragment);
+                fputs("</td>", main_fragment);
+                fputs("<td>", main_fragment);
+                fprintf(main_fragment, "%s", pcmd->help.args[i][1]);
+                fputs("</td>", main_fragment);
+                fputs("</tr>", main_fragment);
+            }
+            fputs("</table>\n", main_fragment);
+        }
+
+        if (pcmd->help.examples[0] != NULL) {
+            fputs("<p><b>Examples</b></p>\n", main_fragment);
+            fputs("<p><pre><code>", main_fragment);
+            int i = 0;
+            while (pcmd->help.examples[i]) {
+                fprintf(main_fragment, "%s\n", pcmd->help.examples[i]);
+                i++;
+            }
+            fputs("</code></pre></p>\n", main_fragment);
+        }
+
+        fputs("<a href=\"#top\"><h5>back to top</h5></a><br><hr>\n", main_fragment);
+        fputs("\n", main_fragment);
+
+        curr = g_list_next(curr);
+    }
+
+    fputs("</ul></ul>\n", toc_fragment);
+
+    fclose(toc_fragment);
+    fclose(main_fragment);
+    printf("\nProcessed %d commands.\n\n", g_list_length(cmds));
+    g_list_free(cmds);
+}
diff --git a/src/command/command.h b/src/command/cmd_defs.h
index 34beee86..85dee14f 100644
--- a/src/command/command.h
+++ b/src/command/cmd_defs.h
@@ -1,5 +1,5 @@
 /*
- * command.h
+ * cmd_defs.h
  *
  * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
  *
@@ -32,43 +32,20 @@
  *
  */
 
-#ifndef COMMAND_H
-#define COMMAND_H
+#ifndef CMD_DEFS_H
+#define CMD_DEFS_H
 
 #include <glib.h>
 
 #include "ui/ui.h"
 
-GHashTable *commands;
-
 void cmd_init(void);
 void cmd_uninit(void);
 
-char* cmd_autocomplete(ProfWin *window, const char *const input);
-void cmd_reset_autocomplete(ProfWin *window);
-void cmd_help_autocomplete_add(const char *const value);
-void cmd_autocomplete_add(const char *const value);
-void cmd_autocomplete_remove(const char *const value);
-void cmd_autocomplete_add_form_fields(DataForm *form);
-void cmd_autocomplete_remove_form_fields(DataForm *form);
-void cmd_alias_add(char *value);
-void cmd_alias_remove(char *value);
+Command* cmd_get(const char *const command);
+GList* cmd_get_ordered(const char *const tag);
 
 gboolean cmd_valid_tag(const char *const str);
-gboolean cmd_has_tag(Command *pcmd, const char *const tag);
-
-gboolean cmd_process_input(ProfWin *window, char *inp);
-void cmd_execute_connect(ProfWin *window, const char *const account);
-
-gboolean cmd_exists(char *cmd);
-
-GSList* cmd_get_basic_help(void);
-GSList* cmd_get_settings_help(void);
-GSList* cmd_get_presence_help(void);
-
-void cmd_history_append(char *inp);
-char* cmd_history_previous(char *inp);
-char* cmd_history_next(char *inp);
 
 void command_docgen(void);
 
diff --git a/src/command/commands.c b/src/command/cmd_funcs.c
index 177c56bb..03a7e8e5 100644
--- a/src/command/commands.c
+++ b/src/command/cmd_funcs.c
@@ -1,5 +1,5 @@
 /*
- * commands.c
+ * cmd_funcs.c
  *
  * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
  *
@@ -50,8 +50,9 @@
 #include <ctype.h>
 
 #include "chat_session.h"
-#include "command/commands.h"
-#include "command/command.h"
+#include "command/cmd_funcs.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_ac.h"
 #include "common.h"
 #include "config/accounts.h"
 #include "config/account.h"
@@ -90,96 +91,59 @@ static void _cmd_set_boolean_preference(gchar *arg, const char *const command,
     const char *const display, preference_t pref);
 static void _who_room(ProfWin *window, const char *const command, gchar **args);
 static void _who_roster(ProfWin *window, const char *const command, gchar **args);
+static gboolean _cmd_execute(ProfWin *window, const char *const command, const char *const inp);
+static gboolean _cmd_execute_default(ProfWin *window, const char *inp);
+static gboolean _cmd_execute_alias(ProfWin *window, const char *const inp, gboolean *ran);
 
-extern GHashTable *commands;
-
+/*
+ * Take a line of input and process it, return TRUE if profanity is to
+ * continue, FALSE otherwise
+ */
 gboolean
-cmd_execute_default(ProfWin *window, const char *inp)
+cmd_process_input(ProfWin *window, char *inp)
 {
-    // handle escaped commands - treat as normal message
-    if (g_str_has_prefix(inp, "//")) {
-        inp++;
-
-    // handle unknown commands
-    } else if ((inp[0] == '/') && (!g_str_has_prefix(inp, "/me "))) {
-        cons_show("Unknown command: %s", inp);
-        cons_alert();
-        return TRUE;
-    }
+    log_debug("Input received: %s", inp);
+    gboolean result = FALSE;
+    g_strchomp(inp);
 
-    // handle non commands in non chat or plugin windows
-    if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE && window->type != WIN_PLUGIN && window->type != WIN_XML) {
-        cons_show("Unknown command: %s", inp);
-        return TRUE;
-    }
-
-    // handle plugin window
-    if (window->type == WIN_PLUGIN) {
-        ProfPluginWin *pluginwin = (ProfPluginWin*)window;
-        plugins_win_process_line(pluginwin->tag, inp);
-        return TRUE;
-    }
+    // just carry on if no input
+    if (strlen(inp) == 0) {
+        result = TRUE;
 
-    jabber_conn_status_t status = connection_get_status();
-    if (status != JABBER_CONNECTED) {
-        ui_current_print_line("You are not currently connected.");
-        return TRUE;
-    }
+    // handle command if input starts with a '/'
+    } else if (inp[0] == '/') {
+        char *inp_cpy = strdup(inp);
+        char *command = strtok(inp_cpy, " ");
+        char *question_mark = strchr(command, '?');
+        if (question_mark) {
+            *question_mark = '\0';
+            char *fakeinp;
+            if (asprintf(&fakeinp, "/help %s", command+1)) {
+                result = _cmd_execute(window, "/help", fakeinp);
+                free(fakeinp);
+            }
+        } else {
+            result = _cmd_execute(window, command, inp);
+        }
+        free(inp_cpy);
 
-    switch (window->type) {
-    case WIN_CHAT:
-    {
-        ProfChatWin *chatwin = (ProfChatWin*)window;
-        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
-        cl_ev_send_msg(chatwin, inp, NULL);
-        break;
-    }
-    case WIN_PRIVATE:
-    {
-        ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
-        assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
-        cl_ev_send_priv_msg(privatewin, inp, NULL);
-        break;
-    }
-    case WIN_MUC:
-    {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        cl_ev_send_muc_msg(mucwin, inp, NULL);
-        break;
-    }
-    case WIN_XML:
-    {
-        connection_send_stanza(inp);
-        break;
-    }
-    default:
-        break;
+    // call a default handler if input didn't start with '/'
+    } else {
+        result = _cmd_execute_default(window, inp);
     }
 
-    return TRUE;
+    return result;
 }
 
-gboolean
-cmd_execute_alias(ProfWin *window, const char *const inp, gboolean *ran)
-{
-    if (inp[0] != '/') {
-        ran = FALSE;
-        return TRUE;
-    }
+// Command execution
 
-    char *alias = strdup(inp+1);
-    char *value = prefs_get_alias(alias);
-    free(alias);
-    if (value) {
-        *ran = TRUE;
-        gboolean result = cmd_process_input(window, value);
-        prefs_free_string(value);
-        return result;
-    }
-
-    *ran = FALSE;
-    return TRUE;
+void
+cmd_execute_connect(ProfWin *window, const char *const account)
+{
+    GString *command = g_string_new("/connect ");
+    g_string_append(command, account);
+    cmd_process_input(window, command->str);
+    g_string_free(command, TRUE);
 }
 
 gboolean
@@ -1522,21 +1486,7 @@ _cmd_help_cmd_list(const char *const tag)
         }
         g_list_free(plugins_cmds);
     } else {
-        GHashTableIter iter;
-        gpointer key;
-        gpointer value;
-
-        g_hash_table_iter_init(&iter, commands);
-        while (g_hash_table_iter_next(&iter, &key, &value)) {
-            Command *pcmd = (Command *)value;
-            if (tag) {
-                if (cmd_has_tag(pcmd, tag)) {
-                    ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
-                }
-            } else {
-                ordered_commands = g_list_insert_sorted(ordered_commands, pcmd->cmd, (GCompareFunc)g_strcmp0);
-            }
-        }
+        ordered_commands = cmd_get_ordered(tag);
 
         // add plugins if showing all commands
         if (!tag) {
@@ -1607,7 +1557,7 @@ cmd_help(ProfWin *window, const char *const command, gchar **args)
         char cmd_with_slash[1 + strlen(cmd) + 1];
         sprintf(cmd_with_slash, "/%s", cmd);
 
-        Command *command = g_hash_table_lookup(commands, cmd_with_slash);
+        Command *command = cmd_get(cmd_with_slash);
         if (command) {
             cons_show_help(cmd_with_slash, &command->help);
         } else {
@@ -3860,7 +3810,7 @@ cmd_form(ProfWin *window, const char *const command, gchar **args)
             mucconfwin_form_help(confwin);
 
             const gchar **help_text = NULL;
-            Command *command = g_hash_table_lookup(commands, "/form");
+            Command *command = cmd_get("/form");
 
             if (command) {
                 help_text = command->help.synopsis;
@@ -3882,7 +3832,7 @@ cmd_form(ProfWin *window, const char *const command, gchar **args)
 
     if ((g_strcmp0(args[0], "submit") == 0) || (g_strcmp0(args[0], "cancel") == 0)) {
         if (confwin->form) {
-            cmd_autocomplete_remove_form_fields(confwin->form);
+            cmd_ac_remove_form_fields(confwin->form);
         }
 
         int num = wins_get_num(window);
@@ -4627,14 +4577,14 @@ cmd_alias(ProfWin *window, const char *const command, gchar **args)
                 cons_bad_cmd_usage(command);
                 g_string_free(ac_value, TRUE);
                 return TRUE;
-            } else if (cmd_exists(ac_value->str)) {
+            } else if (cmd_ac_exists(ac_value->str)) {
                 cons_show("Command or alias '%s' already exists.", ac_value->str);
                 g_string_free(ac_value, TRUE);
                 return TRUE;
             } else {
                 prefs_add_alias(alias_p, value);
-                cmd_autocomplete_add(ac_value->str);
-                cmd_alias_add(alias_p);
+                cmd_ac_add(ac_value->str);
+                cmd_ac_add_alias_value(alias_p);
                 cons_show("Command alias added %s -> %s", ac_value->str, value);
                 g_string_free(ac_value, TRUE);
                 return TRUE;
@@ -4655,8 +4605,8 @@ cmd_alias(ProfWin *window, const char *const command, gchar **args)
             } else {
                 GString *ac_value = g_string_new("/");
                 g_string_append(ac_value, alias);
-                cmd_autocomplete_remove(ac_value->str);
-                cmd_alias_remove(alias);
+                cmd_ac_remove(ac_value->str);
+                cmd_ac_remove_alias_value(alias);
                 g_string_free(ac_value, TRUE);
                 cons_show("Command alias removed -> /%s", alias);
             }
@@ -6800,6 +6750,156 @@ cmd_encwarn(ProfWin *window, const char *const command, gchar **args)
     return TRUE;
 }
 
+static gboolean
+_cmd_execute(ProfWin *window, const char *const command, const char *const inp)
+{
+    if (g_str_has_prefix(command, "/field") && window->type == WIN_MUC_CONFIG) {
+        gboolean result = FALSE;
+        gchar **args = parse_args_with_freetext(inp, 1, 2, &result);
+        if (!result) {
+            ui_current_print_formatted_line('!', 0, "Invalid command, see /form help");
+            result = TRUE;
+        } else {
+            gchar **tokens = g_strsplit(inp, " ", 2);
+            char *field = tokens[0] + 1;
+            result = cmd_form_field(window, field, args);
+            g_strfreev(tokens);
+        }
+
+        g_strfreev(args);
+        return result;
+    }
+
+    Command *cmd = cmd_get(command);
+    gboolean result = FALSE;
+
+    if (cmd) {
+        gchar **args = cmd->parser(inp, cmd->min_args, cmd->max_args, &result);
+        if (result == FALSE) {
+            ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
+            return TRUE;
+        }
+        if (args[0] && cmd->sub_funcs[0][0]) {
+            int i = 0;
+            while (cmd->sub_funcs[i][0]) {
+                if (g_strcmp0(args[0], (char*)cmd->sub_funcs[i][0]) == 0) {
+                    gboolean (*func)(ProfWin *window, const char *const command, gchar **args) = cmd->sub_funcs[i][1];
+                    gboolean result = func(window, command, args);
+                    g_strfreev(args);
+                    return result;
+                }
+                i++;
+            }
+        }
+        if (!cmd->func) {
+            ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
+            return TRUE;
+        }
+        gboolean result = cmd->func(window, command, args);
+        g_strfreev(args);
+        return result;
+    } else if (plugins_run_command(inp)) {
+        return TRUE;
+    } else {
+        gboolean ran_alias = FALSE;
+        gboolean alias_result = _cmd_execute_alias(window, inp, &ran_alias);
+        if (!ran_alias) {
+            return _cmd_execute_default(window, inp);
+        } else {
+            return alias_result;
+        }
+    }
+}
+
+static gboolean
+_cmd_execute_default(ProfWin *window, const char *inp)
+{
+    // handle escaped commands - treat as normal message
+    if (g_str_has_prefix(inp, "//")) {
+        inp++;
+
+    // handle unknown commands
+    } else if ((inp[0] == '/') && (!g_str_has_prefix(inp, "/me "))) {
+        cons_show("Unknown command: %s", inp);
+        cons_alert();
+        return TRUE;
+    }
+
+    // handle non commands in non chat or plugin windows
+    if (window->type != WIN_CHAT && window->type != WIN_MUC && window->type != WIN_PRIVATE && window->type != WIN_PLUGIN && window->type != WIN_XML) {
+        cons_show("Unknown command: %s", inp);
+        return TRUE;
+    }
+
+    // handle plugin window
+    if (window->type == WIN_PLUGIN) {
+        ProfPluginWin *pluginwin = (ProfPluginWin*)window;
+        plugins_win_process_line(pluginwin->tag, inp);
+        return TRUE;
+    }
+
+    jabber_conn_status_t status = connection_get_status();
+    if (status != JABBER_CONNECTED) {
+        ui_current_print_line("You are not currently connected.");
+        return TRUE;
+    }
+
+    switch (window->type) {
+    case WIN_CHAT:
+    {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
+        cl_ev_send_msg(chatwin, inp, NULL);
+        break;
+    }
+    case WIN_PRIVATE:
+    {
+        ProfPrivateWin *privatewin = (ProfPrivateWin*)window;
+        assert(privatewin->memcheck == PROFPRIVATEWIN_MEMCHECK);
+        cl_ev_send_priv_msg(privatewin, inp, NULL);
+        break;
+    }
+    case WIN_MUC:
+    {
+        ProfMucWin *mucwin = (ProfMucWin*)window;
+        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
+        cl_ev_send_muc_msg(mucwin, inp, NULL);
+        break;
+    }
+    case WIN_XML:
+    {
+        connection_send_stanza(inp);
+        break;
+    }
+    default:
+        break;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+_cmd_execute_alias(ProfWin *window, const char *const inp, gboolean *ran)
+{
+    if (inp[0] != '/') {
+        ran = FALSE;
+        return TRUE;
+    }
+
+    char *alias = strdup(inp+1);
+    char *value = prefs_get_alias(alias);
+    free(alias);
+    if (value) {
+        *ran = TRUE;
+        gboolean result = cmd_process_input(window, value);
+        prefs_free_string(value);
+        return result;
+    }
+
+    *ran = FALSE;
+    return TRUE;
+}
+
 // helper function for status change commands
 static void
 _update_presence(const resource_presence_t resource_presence,
diff --git a/src/command/commands.h b/src/command/cmd_funcs.h
index 24828989..0856f5be 100644
--- a/src/command/commands.h
+++ b/src/command/cmd_funcs.h
@@ -1,5 +1,5 @@
 /*
- * commands.h
+ * cmd_funcs.h
  *
  * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
  *
@@ -32,8 +32,8 @@
  *
  */
 
-#ifndef COMMANDS_H
-#define COMMANDS_H
+#ifndef CMD_FUNCS_H
+#define CMD_FUNCS_H
 
 #include "ui/win_types.h"
 
@@ -69,22 +69,11 @@ typedef struct cmd_t {
     CommandHelp help;
 } Command;
 
-gboolean cmd_execute_alias(ProfWin *window, const char *const inp, gboolean *ran);
-gboolean cmd_execute_default(ProfWin *window, const char *inp);
-gboolean cmd_about(ProfWin *window, const char *const command, gchar **args);
 
-gboolean cmd_account(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_list(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_show(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_add(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_remove(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_enable(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_disable(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_rename(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_default(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_set(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_account_clear(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_process_input(ProfWin *window, char *inp);
+void cmd_execute_connect(ProfWin *window, const char *const account);
 
+gboolean cmd_about(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_autoaway(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_autoconnect(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_autoping(ProfWin *window, const char *const command, gchar **args);
@@ -96,14 +85,6 @@ gboolean cmd_chlog(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_clear(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_close(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_connect(ProfWin *window, const char *const command, gchar **args);
-
-gboolean cmd_tls_certpath(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_tls_trust(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_tls_trusted(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_tls_revoke(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_tls_show(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_tls_cert(ProfWin *window, const char *const command, gchar **args);
-
 gboolean cmd_decline(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_disco(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_sendfile(ProfWin *window, const char *const command, gchar **args);
@@ -130,22 +111,6 @@ gboolean cmd_msg(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_nick(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_notify(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_online(ProfWin *window, const char *const command, gchar **args);
-
-gboolean cmd_otr_char(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_log(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_libver(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_policy(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_gen(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_myfp(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_theirfp(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_start(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_end(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_trust(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_untrust(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_secret(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_question(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_otr_answer(ProfWin *window, const char *const command, gchar **args);
-
 gboolean cmd_pgp(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_outtype(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_prefs(ProfWin *window, const char *const command, gchar **args);
@@ -168,14 +133,6 @@ gboolean cmd_titlebar(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_vercheck(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_who(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_win(ProfWin *window, const char *const command, gchar **args);
-
-gboolean cmd_wins(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_wins_unread(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_wins_tidy(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_wins_prune(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args);
-gboolean cmd_wins_autotidy(ProfWin *window, const char *const command, gchar **args);
-
 gboolean cmd_xa(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_alias(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_xmlconsole(ProfWin *window, const char *const command, gchar **args);
@@ -201,6 +158,47 @@ gboolean cmd_console(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_plugins(ProfWin *window, const char *const command, gchar **args);
 gboolean cmd_blocked(ProfWin *window, const char *const command, gchar **args);
 
+gboolean cmd_account(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_list(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_show(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_add(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_remove(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_enable(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_disable(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_rename(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_default(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_set(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_account_clear(ProfWin *window, const char *const command, gchar **args);
+
+gboolean cmd_tls_certpath(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_tls_trust(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_tls_trusted(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_tls_revoke(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_tls_show(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_tls_cert(ProfWin *window, const char *const command, gchar **args);
+
+gboolean cmd_otr_char(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_log(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_libver(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_policy(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_gen(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_myfp(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_theirfp(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_start(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_end(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_trust(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_untrust(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_secret(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_question(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_otr_answer(ProfWin *window, const char *const command, gchar **args);
+
+gboolean cmd_wins(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_wins_unread(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_wins_tidy(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_wins_prune(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args);
+gboolean cmd_wins_autotidy(ProfWin *window, const char *const command, gchar **args);
+
 gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args);
 
 #endif
diff --git a/src/command/command.c b/src/command/command.c
deleted file mode 100644
index 7495506f..00000000
--- a/src/command/command.c
+++ /dev/null
@@ -1,5279 +0,0 @@
-/*
- * command.c
- *
- * Copyright (C) 2012 - 2016 James Booth <boothj5@gmail.com>
- *
- * This file is part of Profanity.
- *
- * Profanity 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 3 of the License, or
- * (at your option) any later version.
- *
- * Profanity 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 Profanity.  If not, see <http://www.gnu.org/licenses/>.
- *
- * In addition, as a special exception, the copyright holders give permission to
- * link the code of portions of this program with the OpenSSL library under
- * certain conditions as described in each individual source file, and
- * distribute linked combinations including the two.
- *
- * You must obey the GNU General Public License in all respects for all of the
- * code used other than OpenSSL. If you modify file(s) with this exception, you
- * may extend this exception to your version of the file(s), but you are not
- * obligated to do so. If you do not wish to do so, delete this exception
- * statement from your version. If you delete this exception statement from all
- * source files in the program, then also delete it here.
- *
- */
-
-#define _GNU_SOURCE 1
-
-#include "config.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <libgen.h>
-
-#include <dirent.h>
-#include <sys/types.h>
-
-#include <glib.h>
-
-#include "chat_session.h"
-#include "command/command.h"
-#include "command/commands.h"
-#include "common.h"
-#include "config/accounts.h"
-#include "config/preferences.h"
-#include "config/theme.h"
-#include "config/tlscerts.h"
-#include "config/scripts.h"
-#include "contact.h"
-#include "roster_list.h"
-#include "jid.h"
-#include "log.h"
-#include "muc.h"
-#include "plugins/plugins.h"
-#ifdef HAVE_LIBOTR
-#include "otr/otr.h"
-#endif
-#ifdef HAVE_LIBGPGME
-#include "pgp/gpg.h"
-#endif
-#include "profanity.h"
-#include "tools/autocomplete.h"
-#include "tools/parser.h"
-#include "tools/tinyurl.h"
-#include "xmpp/xmpp.h"
-#include "ui/ui.h"
-#include "window_list.h"
-
-static gboolean _cmd_execute(ProfWin *window, const char *const command, const char *const inp);
-
-static char* _cmd_complete_parameters(ProfWin *window, const char *const input);
-
-static char* _script_autocomplete_func(const char *const prefix);
-
-static char* _sub_autocomplete(ProfWin *window, const char *const input);
-static char* _notify_autocomplete(ProfWin *window, const char *const input);
-static char* _theme_autocomplete(ProfWin *window, const char *const input);
-static char* _autoaway_autocomplete(ProfWin *window, const char *const input);
-static char* _autoconnect_autocomplete(ProfWin *window, const char *const input);
-static char* _account_autocomplete(ProfWin *window, const char *const input);
-static char* _who_autocomplete(ProfWin *window, const char *const input);
-static char* _roster_autocomplete(ProfWin *window, const char *const input);
-static char* _group_autocomplete(ProfWin *window, const char *const input);
-static char* _bookmark_autocomplete(ProfWin *window, const char *const input);
-static char* _otr_autocomplete(ProfWin *window, const char *const input);
-static char* _pgp_autocomplete(ProfWin *window, const char *const input);
-static char* _connect_autocomplete(ProfWin *window, const char *const input);
-static char* _statuses_autocomplete(ProfWin *window, const char *const input);
-static char* _alias_autocomplete(ProfWin *window, const char *const input);
-static char* _join_autocomplete(ProfWin *window, const char *const input);
-static char* _log_autocomplete(ProfWin *window, const char *const input);
-static char* _form_autocomplete(ProfWin *window, const char *const input);
-static char* _form_field_autocomplete(ProfWin *window, const char *const input);
-static char* _occupants_autocomplete(ProfWin *window, const char *const input);
-static char* _kick_autocomplete(ProfWin *window, const char *const input);
-static char* _ban_autocomplete(ProfWin *window, const char *const input);
-static char* _affiliation_autocomplete(ProfWin *window, const char *const input);
-static char* _role_autocomplete(ProfWin *window, const char *const input);
-static char* _resource_autocomplete(ProfWin *window, const char *const input);
-static char* _titlebar_autocomplete(ProfWin *window, const char *const input);
-static char* _inpblock_autocomplete(ProfWin *window, const char *const input);
-static char* _time_autocomplete(ProfWin *window, const char *const input);
-static char* _receipts_autocomplete(ProfWin *window, const char *const input);
-static char* _help_autocomplete(ProfWin *window, const char *const input);
-static char* _wins_autocomplete(ProfWin *window, const char *const input);
-static char* _tls_autocomplete(ProfWin *window, const char *const input);
-static char* _script_autocomplete(ProfWin *window, const char *const input);
-static char* _subject_autocomplete(ProfWin *window, const char *const input);
-static char* _console_autocomplete(ProfWin *window, const char *const input);
-static char* _win_autocomplete(ProfWin *window, const char *const input);
-static char* _close_autocomplete(ProfWin *window, const char *const input);
-static char* _plugins_autocomplete(ProfWin *window, const char *const input);
-static char* _sendfile_autocomplete(ProfWin *window, const char *const input);
-static char* _blocked_autocomplete(ProfWin *window, const char *const input);
-static char *_tray_autocomplete(ProfWin *window, const char *const input);
-
-GHashTable *commands = NULL;
-
-#define CMD_TAG_CHAT        "chat"
-#define CMD_TAG_GROUPCHAT   "groupchat"
-#define CMD_TAG_ROSTER      "roster"
-#define CMD_TAG_PRESENCE    "presence"
-#define CMD_TAG_CONNECTION  "connection"
-#define CMD_TAG_DISCOVERY   "discovery"
-#define CMD_TAG_UI          "ui"
-#define CMD_TAG_PLUGINS     "plugins"
-
-#define CMD_MAINFUNC(func)  func,
-#define CMD_NOMAINFUNC      NULL,
-#define CMD_SUBFUNCS(...)   { __VA_ARGS__, { NULL, NULL } },
-#define CMD_NOSUBFUNCS      { { NULL, NULL } },
-
-#define CMD_NOTAGS          { { NULL },
-#define CMD_TAGS(...)       { { __VA_ARGS__, NULL },
-#define CMD_SYN(...)        { __VA_ARGS__, NULL },
-#define CMD_DESC(desc)      desc,
-#define CMD_NOARGS          { { NULL, NULL } },
-#define CMD_ARGS(...)       { __VA_ARGS__, { NULL, NULL } },
-#define CMD_NOEXAMPLES      { NULL } }
-#define CMD_EXAMPLES(...)   { __VA_ARGS__, NULL } }
-
-/*
- * Command list
- */
-static struct cmd_t command_defs[] =
-{
-    { "/help",
-        parse_args, 0, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_help)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/help [<area>|<command>]")
-        CMD_DESC(
-            "Help on using Profanity. Passing no arguments list help areas. "
-            "For command help, optional arguments are shown using square brackets e.g. [argument], "
-            "arguments representing variables rather than a literal name are surrounded by angle brackets "
-            "e.g. <argument>. "
-            "Arguments that may be one of a number of values are separated by a pipe "
-            "e.g. val1|val2|val3.")
-        CMD_ARGS(
-            { "<area>",    "Summary help for commands in a certain area of functionality." },
-            { "<command>", "Full help for a specific command, for example '/help connect'." })
-        CMD_EXAMPLES(
-            "/help commands",
-            "/help presence",
-            "/help who")
-    },
-
-    { "/about",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_about)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/about")
-        CMD_DESC(
-            "Show version and license information.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/connect",
-        parse_args, 0, 7, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_connect)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/connect [<account>]",
-            "/connect <account> [server <server>] [port <port>] [tls force|allow|disable]")
-        CMD_DESC(
-            "Login to a chat service. "
-            "If no account is specified, the default is used if one is configured. "
-            "A local account is created with the JID as it's name if it doesn't already exist.")
-        CMD_ARGS(
-            { "<account>",         "The local account you wish to connect with, or a JID if connecting for the first time." },
-            { "server <server>",   "Supply a server if it is different to the domain part of your JID." },
-            { "port <port>",       "The port to use if different to the default (5222, or 5223 for SSL)." },
-            { "tls force",         "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
-            { "tls allow",         "Use TLS for the connection if it is available." },
-            { "tls disable",       "Disable TLS for the connection." })
-        CMD_EXAMPLES(
-            "/connect",
-            "/connect myuser@gmail.com",
-            "/connect myuser@mycompany.com server talk.google.com",
-            "/connect bob@someplace port 5678",
-            "/connect me@localhost.test.org server 127.0.0.1 tls disable",
-            "/connect me@chatty server chatty.com port 5443")
-        },
-
-    { "/tls",
-        parse_args, 1, 3, NULL,
-        CMD_SUBFUNCS(
-            { "certpath",   cmd_tls_certpath },
-            { "trust",      cmd_tls_trust },
-            { "trusted",    cmd_tls_trusted },
-            { "revoke",     cmd_tls_revoke },
-            { "show",       cmd_tls_show },
-            { "cert",       cmd_tls_cert })
-        CMD_NOMAINFUNC
-        CMD_TAGS(
-            CMD_TAG_CONNECTION,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/tls allow",
-            "/tls always",
-            "/tls deny",
-            "/tls cert [<fingerprint>]",
-            "/tls trust",
-            "/tls trusted",
-            "/tls revoke <fingerprint>",
-            "/tls certpath",
-            "/tls certpath set <path>",
-            "/tls certpath clear",
-            "/tls show on|off")
-        CMD_DESC(
-            "Handle TLS certificates. ")
-        CMD_ARGS(
-            { "allow",                "Allow connection to continue with TLS certificate." },
-            { "always",               "Always allow connections with TLS certificate." },
-            { "deny",                 "Abort connection." },
-            { "cert",                 "Show the current TLS certificate." },
-            { "cert <fingerprint>",   "Show details of trusted certificate." },
-            { "trust",                "Add the current TLS certificate to manually trusted certificates." },
-            { "trusted",              "List summary of manually trusted certificates (with '/tls always' or '/tls trust')." },
-            { "revoke <fingerprint>", "Remove a manually trusted certificate." },
-            { "certpath",             "Show the trusted certificate path." },
-            { "certpath set <path>",  "Specify filesystem path containing trusted certificates." },
-            { "certpath clear",       "Clear the trusted certificate path." },
-            { "show on|off",          "Show or hide the TLS indicator in the titlebar." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/disconnect",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_disconnect)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/disconnect")
-        CMD_DESC(
-            "Disconnect from the current chat service.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/msg",
-        parse_args_with_freetext, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_msg)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/msg <contact> [<message>]",
-            "/msg <nick> [<message>]")
-        CMD_DESC(
-            "Send a one to one chat message, or a private message to a chat room occupant. "
-            "If the message is omitted, a new chat window will be opened without sending a message. "
-            "Use quotes if the nickname includes spaces.")
-        CMD_ARGS(
-            { "<contact>",             "Open chat window with contact, by JID or nickname." },
-            { "<contact> [<message>]", "Send message to contact, by JID or nickname." },
-            { "<nick>",                "Open private chat window with chat room occupant." },
-            { "<nick> [<message>]",    "Send a private message to a chat room occupant." })
-        CMD_EXAMPLES(
-            "/msg myfriend@server.com Hey, here's a message!",
-            "/msg otherfriend@server.com",
-            "/msg Bob Here is a private message",
-            "/msg \"My Friend\" Hi, how are you?")
-    },
-
-    { "/roster",
-        parse_args_with_freetext, 0, 4, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_roster)
-        CMD_TAGS(
-            CMD_TAG_ROSTER,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/roster",
-            "/roster online",
-            "/roster show [offline|resource|presence|status|empty|priority|contacts|rooms]",
-            "/roster hide [offline|resource|presence|status|empty|priority|contacts|rooms]",
-            "/roster by group|presence|none",
-            "/roster count unread|items|off",
-            "/roster count zero on|off",
-            "/roster order name|presence",
-            "/roster unread before|after|off",
-            "/roster room char <char>|none",
-            "/roster room private char <char>|none",
-            "/roster room position first|last",
-            "/roster room by service|none",
-            "/roster room order name|unread",
-            "/roster room unread before|after|off",
-            "/roster private room|group|off",
-            "/roster private char <char>|none",
-            "/roster header char <char>|none",
-            "/roster presence indent <indent>",
-            "/roster contact char <char>|none",
-            "/roster contact indent <indent>",
-            "/roster resource char <char>|none",
-            "/roster resource indent <indent>",
-            "/roster resource join on|off",
-            "/roster size <percent>",
-            "/roster wrap on|off",
-            "/roster add <jid> [<nick>]",
-            "/roster remove <jid>",
-            "/roster remove_all contacts",
-            "/roster nick <jid> <nick>",
-            "/roster clearnick <jid>")
-        CMD_DESC(
-            "Manage your roster, and roster display settings. "
-            "Passing no arguments lists all contacts in your roster.")
-        CMD_ARGS(
-            { "online",                     "Show all online contacts in console." },
-            { "show",                       "Show the roster panel." },
-            { "show offline",               "Show offline contacts in roster panel." },
-            { "show resource",              "Show contact's connected resources in roster panel." },
-            { "show presence",              "Show contact's presence in roster panel." },
-            { "show status",                "Show contact's status message in roster panel." },
-            { "show empty",                 "Show empty groups in roster panel." },
-            { "show priority",              "Show resource priority in roster panel." },
-            { "show contacts",              "Show contacts in roster panel." },
-            { "show rooms",                 "Show chat rooms in roster panel." },
-            { "hide",                       "Hide the roster panel." },
-            { "hide offline",               "Hide offline contacts in roster panel." },
-            { "hide resource",              "Hide contact's connected resources in roster panel." },
-            { "hide presence",              "Hide contact's presence in roster panel." },
-            { "hide status",                "Hide contact's status message in roster panel." },
-            { "hide empty",                 "Hide empty groups in roster panel." },
-            { "hide priority",              "Hide resource priority in roster panel." },
-            { "hide contacts",              "Hide contacts in roster panel." },
-            { "hide rooms",                 "Hide chat rooms in roster panel." },
-            { "by group",                   "Group contacts in roster panel by roster group." },
-            { "by presence",                "Group contacts in roster panel by presence." },
-            { "by none",                    "No grouping in roster panel." },
-            { "count unread",               "Show unread message count with roster headers." },
-            { "count items",                "Show item count with roster headers." },
-            { "count off",                  "Do not show any count with roster headers." },
-            { "count zero on",              "Show roster header count when 0." },
-            { "count zero off",             "Hide roster header count when 0." },
-            { "order name",                 "Order roster contacts by name only." },
-            { "order presence",             "Order roster contacts by presence, and then by name." },
-            { "unread before",              "Show unread message count before contact." },
-            { "unread after",               "Show unread message count after contact." },
-            { "unread off",                 "Do not show unread message count for contacts." },
-            { "room char <char>",           "Prefix rooms with specified character." },
-            { "room char none",             "Remove room character prefix." },
-            { "room private char <char>",   "Prefix private room chat with specified character when displayed with room." },
-            { "room private char none",     "Remove private room chat character prefix when displayed with room." },
-            { "room position first",        "Show rooms first in roster." },
-            { "room position last",         "Show rooms last in roster." },
-            { "room by service",            "Group rooms by chat service." },
-            { "room by none",               "Do not group rooms." },
-            { "room order name",            "Order rooms by name." },
-            { "room order unread",          "Order rooms by unread messages, and then by name." },
-            { "room unread before",         "Show unread message count before room." },
-            { "room unread after",          "Show unread message count after room." },
-            { "room unread off",            "Do not show unread message count for rooms." },
-            { "private room",               "Show room private chats with the room." },
-            { "private group",              "Show room private chats as a separate roster group." },
-            { "private off",                "Do not show room private chats." },
-            { "private char <char>",        "Prefix private room chats with specified character when displayed in separate group." },
-            { "private char none",          "Remove private room chat character prefix." },
-            { "header char <char>",         "Prefix roster headers with specified character." },
-            { "header char none",           "Remove roster header character prefix." },
-            { "contact char <char>",        "Prefix roster contacts with specified character." },
-            { "contact char none",          "Remove roster contact character prefix." },
-            { "contact indent <indent>",    "Indent contact line by <indent> spaces (0 to 10)." },
-            { "resource char <char>",       "Prefix roster resources with specified character." },
-            { "resource char none",         "Remove roster resource character prefix." },
-            { "resource indent <indent>",   "Indent resource line by <indent> spaces (0 to 10)." },
-            { "resource join on|off",       "Join resource with previous line when only one available resource." },
-            { "presence indent <indent>",   "Indent presence line by <indent> spaces (-1 to 10), a value of -1 will show presence on the previous line." },
-            { "size <precent>",             "Percentage of the screen taken up by the roster (1-99)." },
-            { "wrap on|off",                "Enable or disable line wrapping in roster panel." },
-            { "add <jid> [<nick>]",         "Add a new item to the roster." },
-            { "remove <jid>",               "Removes an item from the roster." },
-            { "remove_all contacts",        "Remove all items from roster." },
-            { "nick <jid> <nick>",          "Change a contacts nickname." },
-            { "clearnick <jid>",            "Removes the current nickname." })
-        CMD_EXAMPLES(
-            "/roster",
-            "/roster add someone@contacts.org",
-            "/roster add someone@contacts.org Buddy",
-            "/roster remove someone@contacts.org",
-            "/roster nick myfriend@chat.org My Friend",
-            "/roster clearnick kai@server.com",
-            "/roster size 15")
-    },
-
-    { "/blocked",
-        parse_args, 0, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_blocked)
-        CMD_TAGS(
-            CMD_TAG_ROSTER,
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/blocked",
-            "/blocked add [<jid>]",
-            "/blocked remove <jid>")
-        CMD_DESC(
-            "Manage blocked users, calling with no arguments shows the current list of blocked users.")
-        CMD_ARGS(
-            { "add [<jid>]",    "Block the specified Jabber ID, if in a chat window, and not jid specified, the current recipient will be blocked." },
-            { "remove <jid>",   "Remove the specified Jabber ID from the blocked list." })
-        CMD_EXAMPLES(
-            "/blocked add spammer@spam.org")
-    },
-
-    { "/group",
-        parse_args_with_freetext, 0, 3, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_group)
-        CMD_TAGS(
-            CMD_TAG_ROSTER,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/group",
-            "/group show <group>",
-            "/group add <group> <contat>",
-            "/group remove <group> <contact>")
-        CMD_DESC(
-            "View, add to, and remove from roster groups. "
-            "Passing no argument will list all roster groups.")
-        CMD_ARGS(
-            { "show <group>",             "List all roster items a group." },
-            { "add <group> <contact>",    "Add a contact to a group." },
-            { "remove <group> <contact>", "Remove a contact from a group." })
-        CMD_EXAMPLES(
-            "/group",
-            "/group show friends",
-            "/group add friends newfriend@server.org",
-            "/group add family Brother",
-            "/group remove colleagues boss@work.com")
-    },
-
-    { "/info",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_info)
-        CMD_TAGS(
-            CMD_TAG_ROSTER,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/info",
-            "/info <contact>|<nick>")
-        CMD_DESC(
-            "Show information about a contact, room, or room member. "
-            "Passing no argument in a chat window will use the current recipient. "
-            "Passing no argument in a chat room will display information about the room.")
-        CMD_ARGS(
-            { "<contact>", "The contact you wish to view information about." },
-            { "<nick>",    "When in a chat room, the occupant you wish to view information about." })
-        CMD_EXAMPLES(
-            "/info mybuddy@chat.server.org",
-            "/info kai")
-    },
-
-    { "/caps",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_caps)
-        CMD_TAGS(
-            CMD_TAG_DISCOVERY,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/caps",
-            "/caps <fulljid>|<nick>")
-        CMD_DESC(
-            "Find out a contacts, or room members client software capabilities. "
-            "If in private chat initiated from a chat room, no parameter is required.")
-        CMD_ARGS(
-            { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see capabilities." },
-            { "<nick>",    "If in a chat room, nickname for which you wish to see capabilities." })
-        CMD_EXAMPLES(
-            "/caps mybuddy@chat.server.org/laptop",
-            "/caps mybuddy@chat.server.org/phone",
-            "/caps bruce")
-    },
-
-    { "/software",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_software)
-        CMD_TAGS(
-            CMD_TAG_DISCOVERY,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/software",
-            "/software <fulljid>|<nick>")
-        CMD_DESC(
-            "Find out a contact, or room members software version information. "
-            "If in private chat initiated from a chat room, no parameter is required. "
-            "If the contact's software does not support software version requests, nothing will be displayed.")
-        CMD_ARGS(
-            { "<fulljid>", "If in the console or a chat window, the full JID for which you wish to see software information." },
-            { "<nick>",    "If in a chat room, nickname for which you wish to see software information." })
-        CMD_EXAMPLES(
-            "/software mybuddy@chat.server.org/laptop",
-            "/software mybuddy@chat.server.org/phone",
-            "/software bruce")
-    },
-
-    { "/status",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_status)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/status",
-            "/status <contact>|<nick>")
-        CMD_DESC(
-            "Find out a contact, or room members presence information. "
-            "If in a chat window the parameter is not required, the current recipient will be used.")
-        CMD_ARGS(
-            { "<contact>", "The contact who's presence you which to see." },
-            { "<nick>",    "If in a chat room, the occupant who's presence you wish to see." })
-        CMD_EXAMPLES(
-            "/status buddy@server.com",
-            "/status jon")
-    },
-
-    { "/resource",
-        parse_args, 1, 2, &cons_resource_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_resource)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/resource set <resource>",
-            "/resource off",
-            "/resource title on|off",
-            "/resource message on|off")
-        CMD_DESC(
-            "Override chat session resource, and manage resource display settings.")
-        CMD_ARGS(
-            { "set <resource>", "Set the resource to which messages will be sent." },
-            { "off",            "Let the server choose which resource to route messages to." },
-            { "title on|off",   "Show or hide the current resource in the titlebar." },
-            { "message on|off", "Show or hide the resource when showing an incoming message." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/join",
-        parse_args, 0, 5, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_join)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/join",
-            "/join <room> [nick <nick>] [password <password>]")
-        CMD_DESC(
-            "Join a chat room at the conference server. "
-            "If no room is supplied, a generated name will be used with the format private-chat-[UUID]. "
-            "If the domain part is not included in the room name, the account preference 'muc.service' will be used. "
-            "If no nickname is specified the account preference 'muc.nick' will be used which by default is the localpart of your JID. "
-            "If the room doesn't exist, and the server allows it, a new one will be created.")
-        CMD_ARGS(
-            { "<room>",              "The chat room to join." },
-            { "nick <nick>",         "Nickname to use in the room." },
-            { "password <password>", "Password if the room requires one." })
-        CMD_EXAMPLES(
-            "/join",
-            "/join jdev@conference.jabber.org",
-            "/join jdev@conference.jabber.org nick mynick",
-            "/join private@conference.jabber.org nick mynick password mypassword",
-            "/join jdev")
-    },
-
-    { "/leave",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_leave)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/leave")
-        CMD_DESC(
-            "Leave the current chat room.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/invite",
-        parse_args_with_freetext, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_invite)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/invite <contact> [<message>]")
-        CMD_DESC(
-            "Send an invite to a contact for the current chat room.")
-        CMD_ARGS(
-            { "<contact>", "The contact you wish to invite." },
-            { "<message>", "An optional message to send with the invite." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/invites",
-        parse_args_with_freetext, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_invites)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/invites")
-        CMD_DESC(
-            "Show all rooms that you have been invited to, and not accepted or declined.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/decline",
-        parse_args_with_freetext, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_decline)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/decline <room>")
-        CMD_DESC(
-            "Decline a chat room invitation.")
-        CMD_ARGS(
-            { "<room>", "The room for the invite you wish to decline." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/room",
-        parse_args, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_room)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/room accept|destroy|config")
-        CMD_DESC(
-            "Chat room configuration.")
-        CMD_ARGS(
-            { "accept",  "Accept default room configuration." },
-            { "destroy", "Reject default room configuration, and destroy the room." },
-            { "config",  "Edit room configuration." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/kick",
-        parse_args_with_freetext, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_kick)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/kick <nick> [<reason>]")
-        CMD_DESC(
-            "Kick occupant from chat room.")
-        CMD_ARGS(
-            { "<nick>",   "Nickname of the occupant to kick from the room." },
-            { "<reason>", "Optional reason for kicking the occupant." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/ban",
-        parse_args_with_freetext, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_ban)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/ban <jid> [<reason>]")
-        CMD_DESC(
-            "Ban user from chat room.")
-        CMD_ARGS(
-            { "<jid>",    "Bare JID of the user to ban from the room." },
-            { "<reason>", "Optional reason for banning the user." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/subject",
-        parse_args_with_freetext, 0, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_subject)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/subject set <subject>",
-            "/subject edit <subject>",
-            "/subject prepend <text>",
-            "/subject append <text>",
-            "/subject clear")
-        CMD_DESC(
-            "Set, modify, or clear room subject.")
-        CMD_ARGS(
-            { "set <subject>",  "Set the room subject." },
-            { "edit <subject>", "Edit the current room subject, tab autocompletion will display the subject to edit." },
-            { "prepend <text>", "Prepend text to the current room subject, use double quotes if a trailing space is needed." },
-            { "append <text>",  "Append text to the current room subject, use double quotes if a preceding space is needed." },
-            { "clear",          "Clear the room subject." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/affiliation",
-        parse_args_with_freetext, 1, 4, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_affiliation)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/affiliation set <affiliation> <jid> [<reason>]",
-            "/affiliation list [<affiliation>]")
-        CMD_DESC(
-            "Manage room affiliations. "
-            "Affiliation may be one of owner, admin, member, outcast or none.")
-        CMD_ARGS(
-            { "set <affiliation> <jid> [<reason>]", "Set the affiliation of user with jid, with an optional reason." },
-            { "list [<affiliation>]",               "List all users with the specified affiliation, or all if none specified." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/role",
-        parse_args_with_freetext, 1, 4, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_role)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/role set <role> <nick> [<reason>]",
-            "/role list [<role>]")
-        CMD_DESC(
-            "Manage room roles. "
-            "Role may be one of moderator, participant, visitor or none.")
-        CMD_ARGS(
-            { "set <role> <nick> [<reason>]", "Set the role of occupant with nick, with an optional reason." },
-            { "list [<role>]",                "List all occupants with the specified role, or all if none specified." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/occupants",
-        parse_args, 1, 3, cons_occupants_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_occupants)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/occupants show|hide [jid]",
-            "/occupants default show|hide [jid]",
-            "/occupants size [<percent>]")
-        CMD_DESC(
-            "Show or hide room occupants, and occupants panel display settings.")
-        CMD_ARGS(
-            { "show",                  "Show the occupants panel in current room." },
-            { "hide",                  "Hide the occupants panel in current room." },
-            { "show jid",              "Show jid in the occupants panel in current room." },
-            { "hide jid",              "Hide jid in the occupants panel in current room." },
-            { "default show|hide",     "Whether occupants are shown by default in new rooms." },
-            { "default show|hide jid", "Whether occupants jids are shown by default in new rooms." },
-            { "size <percent>",        "Percentage of the screen taken by the occupants list in rooms (1-99)." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/form",
-        parse_args, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_form)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/form show",
-            "/form submit",
-            "/form cancel",
-            "/form help [<tag>]")
-        CMD_DESC(
-            "Form configuration.")
-        CMD_ARGS(
-            { "show",         "Show the current form." },
-            { "submit",       "Submit the current form." },
-            { "cancel",       "Cancel changes to the current form." },
-            { "help [<tag>]", "Display help for form, or a specific field." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/rooms",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_rooms)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/rooms [<service>]")
-        CMD_DESC(
-            "List the chat rooms available at the specified conference service. "
-            "If no argument is supplied, the account preference 'muc.service' is used, 'conference.<domain-part>' by default.")
-        CMD_ARGS(
-            { "<service>", "The conference service to query." })
-        CMD_EXAMPLES(
-            "/rooms conference.jabber.org")
-    },
-
-    { "/bookmark",
-        parse_args, 0, 8, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_bookmark)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/bookmark",
-            "/bookmark list",
-            "/bookmark add <room> [nick <nick>] [password <password>] [autojoin on|off]",
-            "/bookmark update <room> [nick <nick>] [password <password>] [autojoin on|off]",
-            "/bookmark remove <room>",
-            "/bookmark join <room>")
-        CMD_DESC(
-            "Manage bookmarks and join bookmarked rooms. "
-            "In a chat room, no arguments will bookmark the current room, setting autojoin to \"on\".")
-        CMD_ARGS(
-            { "list", "List all bookmarks." },
-            { "add <room>", "Add a bookmark." },
-            { "remove <room>", "Remove a bookmark." },
-            { "update <room>", "Update the properties associated with a bookmark." },
-            { "nick <nick>", "Nickname used in the chat room." },
-            { "password <password>", "Password if required, may be stored in plaintext on your server." },
-            { "autojoin on|off", "Whether to join the room automatically on login." },
-            { "join <room>", "Join room using the properties associated with the bookmark." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/disco",
-        parse_args, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_disco)
-        CMD_TAGS(
-            CMD_TAG_DISCOVERY)
-        CMD_SYN(
-            "/disco info [<jid>]",
-            "/disco items [<jid>]")
-        CMD_DESC(
-            "Find out information about an entities supported services. "
-            "Calling with no arguments will query the server you are currently connected to.")
-        CMD_ARGS(
-            { "info [<jid>]", "List protocols and features supported by an entity." },
-            { "items [<jid>]", "List items associated with an entity." })
-        CMD_EXAMPLES(
-            "/disco info",
-            "/disco items myserver.org",
-            "/disco items conference.jabber.org",
-            "/disco info myfriend@server.com/laptop")
-    },
-
-    { "/sendfile",
-        parse_args_with_freetext, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_sendfile)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/sendfile <file>")
-        CMD_DESC(
-            "Send a file using XEP-0363 HTTP file transfer.")
-        CMD_ARGS(
-            { "<file>", "Path to the file." })
-        CMD_EXAMPLES(
-            "/sendfile /etc/hosts",
-            "/sendfile ~/images/sweet_cat.jpg")
-    },
-
-    { "/lastactivity",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_lastactivity)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/lastactivity on|off",
-            "/lastactivity [<jid>]")
-        CMD_DESC(
-            "Enable/disable sending last activity, and send last activity requests.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable sending of last activity." },
-            { "<jid>",  "The JID of the entity to query, omitting the JID will query your server." })
-        CMD_EXAMPLES(
-            "/lastactivity",
-            "/lastactivity off",
-            "/lastactivity alice@securechat.org",
-            "/lastactivity alice@securechat.org/laptop",
-            "/lastactivity someserver.com")
-    },
-
-    { "/nick",
-        parse_args_with_freetext, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_nick)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/nick <nickname>")
-        CMD_DESC(
-            "Change your nickname in the current chat room.")
-        CMD_ARGS(
-            { "<nickname>", "Your new nickname." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/win",
-        parse_args, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_win)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/win console",
-            "/win <num>",
-            "/win <barejid>",
-            "/win <nick>",
-            "/win <roomjid>",
-            "/win <roomoccupantjid>",
-            "/win xmlconsole")
-        CMD_DESC(
-            "Move to the specified window.")
-        CMD_ARGS(
-            { "console",            "Go to the Console window." },
-            { "<num>",              "Go to specified window number." },
-            { "<barejid>",          "Go to chat window with contact by JID if open." },
-            { "<nick>",             "Go to chat window with contact by nickname if open." },
-            { "<roomjid>",          "Go to chat room window with roomjid if open." },
-            { "<roomoccupantjid>",  "Go to private chat roomoccupantjid if open." },
-            { "xmlconsole",         "Go to the XML Console window if open." })
-        CMD_EXAMPLES(
-            "/win console",
-            "/win 4",
-            "/win friend@chat.org",
-            "/win Eddie",
-            "/win bigroom@conference.chat.org",
-            "/win bigroom@conference.chat.org/bruce")
-    },
-
-    { "/wins",
-        parse_args, 0, 3, NULL,
-        CMD_SUBFUNCS(
-            { "unread",     cmd_wins_unread },
-            { "tidy",       cmd_wins_tidy },
-            { "prune",      cmd_wins_prune },
-            { "swap",       cmd_wins_swap },
-            { "autotidy",   cmd_wins_autotidy })
-        CMD_MAINFUNC(cmd_wins)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/wins",
-            "/wins unread",
-            "/wins tidy",
-            "/wins autotidy on|off",
-            "/wins prune",
-            "/wins swap <source> <target>")
-        CMD_DESC(
-            "Manage windows. "
-            "Passing no argument will list all currently active windows and information about their usage.")
-        CMD_ARGS(
-            { "unread",                 "List windows with unread messages." },
-            { "tidy",                   "Move windows so there are no gaps." },
-            { "autotidy on|off",        "Automatically remove gaps when closing windows." },
-            { "prune",                  "Close all windows with no unread messages, and then tidy so there are no gaps." },
-            { "swap <source> <target>", "Swap windows, target may be an empty position." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/sub",
-        parse_args, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_sub)
-        CMD_TAGS(
-            CMD_TAG_ROSTER)
-        CMD_SYN(
-            "/sub request [<jid>]",
-            "/sub allow [<jid>]",
-            "/sub deny [<jid>]",
-            "/sub show [<jid>]",
-            "/sub sent",
-            "/sub received")
-        CMD_DESC(
-            "Manage subscriptions to contact presence. "
-            "If jid is omitted, the contact of the current window is used.")
-        CMD_ARGS(
-            { "request [<jid>]", "Send a subscription request to the user." },
-            { "allow [<jid>]",   "Approve a contact's subscription request." },
-            { "deny [<jid>]",    "Remove subscription for a contact, or deny a request." },
-            { "show [<jid>]",    "Show subscription status for a contact." },
-            { "sent",            "Show all sent subscription requests pending a response." },
-            { "received",        "Show all received subscription requests awaiting your response." })
-        CMD_EXAMPLES(
-            "/sub request myfriend@jabber.org",
-            "/sub allow myfriend@jabber.org",
-            "/sub request",
-            "/sub sent")
-    },
-
-    { "/tiny",
-        parse_args, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_tiny)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/tiny <url>")
-        CMD_DESC(
-            "Send url as tinyurl in current chat.")
-        CMD_ARGS(
-            { "<url>", "The url to make tiny." })
-        CMD_EXAMPLES(
-            "Example: /tiny http://www.profanity.im")
-    },
-
-    { "/who",
-        parse_args, 0, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_who)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT,
-            CMD_TAG_ROSTER)
-        CMD_SYN(
-            "/who",
-            "/who online|offline|away|dnd|xa|chat|available|unavailable|any [<group>]",
-            "/who moderator|participant|visitor",
-            "/who owner|admin|member")
-        CMD_DESC(
-            "Show contacts or room occupants with chosen status, role or affiliation")
-        CMD_ARGS(
-            { "offline|away|dnd|xa|chat", "Show contacts or room occupants with specified presence." },
-            { "online", "Contacts that are online, chat, away, xa, dnd." },
-            { "available", "Contacts that are available for chat - online, chat." },
-            { "unavailable", "Contacts that are not available for chat - offline, away, xa, dnd." },
-            { "any", "Contacts with any status (same as calling with no argument)." },
-            { "<group>", "Filter the results by the specified roster group, not applicable in chat rooms." },
-            { "moderator|participant|visitor", "Room occupants with the specified role." },
-            { "owner|admin|member", "Room occupants with the specified affiliation." })
-        CMD_EXAMPLES(
-            "/who",
-            "/who xa",
-            "/who online friends",
-            "/who any family",
-            "/who participant",
-            "/who admin")
-    },
-
-    { "/close",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_close)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/close",
-            "/close <num>",
-            "/close <barejid>",
-            "/close <nick>",
-            "/close <roomjid>",
-            "/close <roomoccupantjid>",
-            "/close xmlconsole",
-            "/close all|read")
-        CMD_DESC(
-            "Close windows. "
-            "Passing no argument closes the current window.")
-        CMD_ARGS(
-            { "<num>",              "Close specified window number." },
-            { "<barejid>",          "Close chat window with contact by JID if open." },
-            { "<nick>",             "Close chat window with contact by nickname if open." },
-            { "<roomjid>",          "Close chat room window with roomjid if open." },
-            { "<roomoccupantjid>",  "Close private chat roomoccupantjid if open." },
-            { "xmlconsole",         "Close the XML Console window if open." },
-            { "all",                "Close all windows." },
-            { "read",               "Close all windows that have no unread messages." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/clear",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_clear)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/clear")
-        CMD_DESC(
-            "Clear the current window.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/quit",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_quit)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/quit")
-        CMD_DESC(
-            "Logout of any current session, and quit Profanity.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/privileges",
-        parse_args, 1, 1, &cons_privileges_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_privileges)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/privileges on|off")
-        CMD_DESC(
-            "Group occupants panel by role, and show role information in chat rooms.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable privilege information." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/charset",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_charset)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/charset")
-        CMD_DESC(
-            "Display information about the current character set supported by the terminal. ")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/beep",
-        parse_args, 1, 1, &cons_beep_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_beep)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/beep on|off")
-        CMD_DESC(
-            "Switch the terminal bell on or off. "
-            "The bell will sound when incoming messages are received. "
-            "If the terminal does not support sounds, it may attempt to flash the screen instead.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable terminal bell." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/console",
-        parse_args, 2, 2, &cons_console_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_console)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/console chat all|first|none",
-            "/console muc all|first|none",
-            "/console private all|first|none")
-        CMD_DESC(
-            "Configure what is displayed in the console window when messages are received. "
-            "The default is set to 'all' for all types of messages.")
-        CMD_ARGS(
-            { "chat all",       "Indicate all new chat messages in the console." },
-            { "chat first",     "Indicate only the first new message per chat in the console." },
-            { "chat none",      "Do not show any new chat messages in the console window." },
-            { "muc all",        "Indicate all new chat room messages in the console." },
-            { "muc first",      "Indicate only the first new message in each room in the console." },
-            { "muc none",       "Do not show any new chat room messages in the console window." },
-            { "private all",    "Indicate all new private room messages in the console." },
-            { "private first",  "Indicate only the first private room message in the console." },
-            { "private none",   "Do not show any new private room messages in the console window." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/encwarn",
-        parse_args, 1, 1, &cons_encwarn_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_encwarn)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/encwarn on|off")
-        CMD_DESC(
-            "Titlebar encryption warning.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable the unencrypted warning message in the titlebar." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/presence",
-        parse_args, 1, 1, &cons_presence_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_presence)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/presence on|off")
-        CMD_DESC(
-            "Show the contacts presence in the titlebar.")
-        CMD_ARGS(
-            { "on|off", "Switch display of the contacts presence in the titlebar on or off." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/wrap",
-        parse_args, 1, 1, &cons_wrap_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_wrap)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/wrap on|off")
-        CMD_DESC(
-            "Word wrapping.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable word wrapping in the main window." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/time",
-        parse_args, 1, 3, &cons_time_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_time)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/time console|chat|muc|mucconfig|private|xml set <format>",
-            "/time console|chat|muc|mucconfig|private|xml off",
-            "/time statusbar set <format>",
-            "/time statusbar off",
-            "/time lastactivity set <format>")
-        CMD_DESC(
-            "Configure time display preferences. "
-            "Time formats are strings supported by g_date_time_format. "
-            "See https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format for more details. "
-            "Setting the format to an unsupported string, will display the string. "
-            "If the format contains spaces, it must be surrounded with double quotes.")
-        CMD_ARGS(
-            { "console set <format>",      "Set time format for console window." },
-            { "console off",               "Do not show time in console window." },
-            { "chat set <format>",         "Set time format for chat windows." },
-            { "chat off",                  "Do not show time in chat windows." },
-            { "muc set <format>",          "Set time format for chat room windows." },
-            { "muc off",                   "Do not show time in chat room windows." },
-            { "mucconfig set <format>",    "Set time format for chat room config windows." },
-            { "mucconfig off",             "Do not show time in chat room config windows." },
-            { "private set <format>",      "Set time format for private chat windows." },
-            { "private off",               "Do not show time in private chat windows." },
-            { "xml set <format>",          "Set time format for XML console window." },
-            { "xml off",                   "Do not show time in XML console window." },
-            { "statusbar set <format>",    "Change time format in statusbar." },
-            { "statusbar off",             "Do not show time in status bar." },
-            { "lastactivity set <format>", "Change time format for last activity." })
-        CMD_EXAMPLES(
-            "/time console set %H:%M:%S",
-            "/time chat set \"%d-%m-%y %H:%M:%S\"",
-            "/time xml off",
-            "/time statusbar set %H:%M",
-            "/time lastactivity set \"%d-%m-%y %H:%M:%S\"")
-    },
-
-    { "/inpblock",
-        parse_args, 2, 2, &cons_inpblock_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_inpblock)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/inpblock timeout <millis>",
-            "/inpblock dynamic on|off")
-        CMD_DESC(
-            "How long to wait for keyboard input before checking for new messages or checking for state changes such as 'idle'.")
-        CMD_ARGS(
-            { "timeout <millis>", "Time to wait (1-1000) in milliseconds before reading input from the terminal buffer, default: 1000." },
-            { "dynamic on|off", "Start with 0 millis and dynamically increase up to timeout when no activity, default: on." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/notify",
-        parse_args_with_freetext, 0, 4, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_notify)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/notify chat on|off",
-            "/notify chat current on|off",
-            "/notify chat text on|off",
-            "/notify room on|off",
-            "/notify room mention on|off",
-            "/notify room mention case_sensitive|case_insensitive",
-            "/notify room mention word_whole|word_part",
-            "/notify room current on|off",
-            "/notify room text on|off",
-            "/notify room trigger add <text>",
-            "/notify room trigger remove <text>",
-            "/notify room trigger list",
-            "/notify room trigger on|off",
-            "/notify on|off",
-            "/notify mention on|off",
-            "/notify trigger on|off",
-            "/notify reset",
-            "/notify remind <seconds>",
-            "/notify typing on|off",
-            "/notify typing current on|off",
-            "/notify invite on|off",
-            "/notify sub on|off")
-        CMD_DESC(
-            "Settings for various kinds of desktop notifications.")
-        CMD_ARGS(
-            { "chat on|off",                    "Notifications for regular chat messages." },
-            { "chat current on|off",            "Whether to show regular chat message notifications when the window is focussed." },
-            { "chat text on|off",               "Show message text in regular message notifications." },
-            { "room on|off",                    "Notifications for all chat room messages, 'mention' only notifies when your nick is mentioned." },
-            { "room mention on|off",            "Notifications for all chat room messages when your nick is mentioned." },
-            { "room mention case_sensitive",    "Set room mention notifications as case sensitive." },
-            { "room mention case_insensitive",  "Set room mention notifications as case insensitive." },
-            { "room mention word_whole",        "Set room mention notifications only on whole word match, i.e. when nickname is not part of a larger word." },
-            { "room mention word_part",         "Set room mention notifications on partial word match, i.e. nickname may be part of a larger word." },
-            { "room current on|off",            "Whether to show all chat room messages notifications when the window is focussed." },
-            { "room text on|off",               "Show message text in chat room message notifications." },
-            { "room trigger add <text>",        "Notify when specified text included in all chat room messages." },
-            { "room trigger remove <text>",     "Remove chat room notification trigger." },
-            { "room trigger list",              "List all chat room triggers." },
-            { "room trigger on|off",            "Enable or disable all chat room notification triggers." },
-            { "on|off",                         "Override the global message setting for the current chat room." },
-            { "mention on|off",                 "Override the global 'mention' setting for the current chat room." },
-            { "trigger on|off",                 "Override the global 'trigger' setting for the current chat room." },
-            { "reset",                          "Reset to global notification settings for the current chat room." },
-            { "remind <seconds>",               "Notification reminder period for unread messages, use 0 to disable." },
-            { "typing on|off",                  "Notifications when contacts are typing." },
-            { "typing current on|off",          "Whether typing notifications are triggered for the current window." },
-            { "invite on|off",                  "Notifications for chat room invites." },
-            { "sub on|off",                     "Notifications for subscription requests." })
-        CMD_EXAMPLES(
-            "/notify chat on",
-            "/notify chat text on",
-            "/notify room mention on",
-            "/notify room trigger add beer",
-            "/notify room trigger on",
-            "/notify room current off",
-            "/notify room text off",
-            "/notify remind 60",
-            "/notify typing on",
-            "/notify invite on")
-    },
-
-    { "/flash",
-        parse_args, 1, 1, &cons_flash_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_flash)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/flash on|off")
-        CMD_DESC(
-            "Make the terminal flash when incoming messages are received in another window. "
-            "If the terminal doesn't support flashing, it may attempt to beep.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable terminal flash." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/tray",
-        parse_args, 1, 2, &cons_tray_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_tray)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/tray on|off",
-            "/tray read on|off",
-            "/tray timer <seconds>")
-        CMD_DESC(
-            "Display an icon in the tray that will indicate new messages.")
-        CMD_ARGS(
-            { "on|off",             "Show tray icon." },
-            { "read on|off",        "Show tray icon when no unread messages." },
-            { "timer <seconds>",    "Set tray icon timer, seconds must be between 1-10" })
-        CMD_NOEXAMPLES
-    },
-
-    { "/intype",
-        parse_args, 1, 1, &cons_intype_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_intype)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/intype on|off")
-        CMD_DESC(
-            "Show when a contact is typing in the console, and in active message window.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable contact typing messages." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/splash",
-        parse_args, 1, 1, &cons_splash_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_splash)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/splash on|off")
-        CMD_DESC(
-            "Switch on or off the ascii logo on start up and when the /about command is called.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable splash logo." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/autoconnect",
-        parse_args, 1, 2, &cons_autoconnect_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_autoconnect)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/autoconnect set <account>",
-            "/autoconnect off")
-        CMD_DESC(
-            "Enable or disable autoconnect on start up. "
-            "The setting can be overridden by the -a (--account) command line option.")
-        CMD_ARGS(
-            { "set <account>", "Connect with account on start up." },
-            { "off",           "Disable autoconnect." })
-        CMD_EXAMPLES(
-            "/autoconnect set jc@stuntteam.org",
-            "/autoconnect off")
-    },
-
-    { "/vercheck",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_vercheck)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/vercheck on|off")
-        CMD_DESC(
-            "Check for new versions when Profanity starts, and when the /about command is run.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable the version check." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/titlebar",
-        parse_args, 2, 2, &cons_titlebar_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_titlebar)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/titlebar show on|off",
-            "/titlebar goodbye on|off")
-        CMD_DESC(
-            "Allow Profanity to modify the window title bar.")
-        CMD_ARGS(
-            { "show on|off",    "Show current logged in user, and unread messages as the window title." },
-            { "goodbye on|off", "Show a message in the title when exiting profanity." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/alias",
-        parse_args_with_freetext, 1, 3, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_alias)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/alias list",
-            "/alias add <name> <value>",
-            "/alias remove <name>")
-        CMD_DESC(
-            "Add, remove or list command aliases.")
-        CMD_ARGS(
-            { "list",               "List all aliases." },
-            { "add <name> <value>", "Add a new command alias." },
-            { "remove <name>",      "Remove a command alias." })
-        CMD_EXAMPLES(
-            "/alias add friends /who online friends",
-            "/alias add /q /quit",
-            "/alias a /away \"I'm in a meeting.\"",
-            "/alias remove q",
-            "/alias list")
-    },
-
-    { "/chlog",
-        parse_args, 1, 1, &cons_chlog_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_chlog)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/chlog on|off")
-        CMD_DESC(
-            "Switch chat logging on or off. "
-            "This setting will be enabled if /history is set to on. "
-            "When disabling this option, /history will also be disabled. "
-            "See the /grlog setting for enabling logging of chat room (groupchat) messages.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable chat logging." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/grlog",
-        parse_args, 1, 1, &cons_grlog_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_grlog)
-        CMD_TAGS(
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/grlog on|off")
-        CMD_DESC(
-            "Switch chat room logging on or off. "
-            "See the /chlog setting for enabling logging of one to one chat.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable chat room logging." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/states",
-        parse_args, 1, 1, &cons_states_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_states)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/states on|off")
-        CMD_DESC(
-            "Send chat state notifications to recipient during chat sessions, such as typing, paused, active, gone.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable sending of chat state notifications." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/pgp",
-        parse_args, 1, 3, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_pgp)
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/pgp libver",
-            "/pgp keys",
-            "/pgp contacts",
-            "/pgp setkey <contact> <keyid>",
-            "/pgp start [<contact>]",
-            "/pgp end",
-            "/pgp log on|off|redact",
-            "/pgp char <char>")
-        CMD_DESC(
-            "Open PGP commands to manage keys, and perform PGP encryption during chat sessions. "
-            "See the /account command to set your own PGP key.")
-        CMD_ARGS(
-            { "libver",                   "Show which version of the libgpgme library is being used." },
-            { "keys",                     "List all keys known to the system." },
-            { "contacts",                 "Show contacts with assigned public keys." },
-            { "setkey <contact> <keyid>", "Manually associate a contact with a public key." },
-            { "start [<contact>]",        "Start PGP encrypted chat, current contact will be used if not specified." },
-            { "end",                      "End PGP encrypted chat with the current recipient." },
-            { "log on|off",               "Enable or disable plaintext logging of PGP encrypted messages." },
-            { "log redact",               "Log PGP encrypted messages, but replace the contents with [redacted]. This is the default." },
-            { "char <char>",              "Set the character to be displayed next to PGP encrypted messages." })
-        CMD_EXAMPLES(
-            "/pgp log off",
-            "/pgp setkey buddy@buddychat.org BA19CACE5A9592C5",
-            "/pgp start buddy@buddychat.org",
-            "/pgp end",
-            "/pgp char P")
-    },
-
-    { "/otr",
-        parse_args, 1, 3, NULL,
-        CMD_SUBFUNCS(
-            { "char",       cmd_otr_char },
-            { "log",        cmd_otr_log },
-            { "libver",     cmd_otr_libver },
-            { "policy",     cmd_otr_policy },
-            { "gen",        cmd_otr_gen },
-            { "myfp",       cmd_otr_myfp },
-            { "theirfp",    cmd_otr_theirfp },
-            { "start",      cmd_otr_start },
-            { "end",        cmd_otr_end },
-            { "trust",      cmd_otr_trust },
-            { "untrust",    cmd_otr_untrust },
-            { "secret",     cmd_otr_secret },
-            { "question",   cmd_otr_question },
-            { "answer",     cmd_otr_answer })
-        CMD_NOMAINFUNC
-        CMD_TAGS(
-            CMD_TAG_CHAT,
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/otr libver",
-            "/otr gen",
-            "/otr myfp|theirfp",
-            "/otr start [<contact>]",
-            "/otr end",
-            "/otr trust|untrust",
-            "/otr secret <secret>",
-            "/otr question <question> <answer>",
-            "/otr answer <answer>",
-            "/otr policy manual|opportunistic|always [<contact>]",
-            "/otr log on|off|redact",
-            "/otr char <char>")
-        CMD_DESC(
-            "Off The Record (OTR) commands to manage keys, and perform OTR encryption during chat sessions.")
-        CMD_ARGS(
-            { "libver",                         "Show which version of the libotr library is being used." },
-            { "gen",                            "Generate your private key." },
-            { "myfp",                           "Show your fingerprint." },
-            { "theirfp",                        "Show contacts fingerprint." },
-            { "start [<contact>]",              "Start an OTR session with contact, or current recipient if omitted." },
-            { "end",                            "End the current OTR session," },
-            { "trust|untrust",                  "Indicate whether or not you trust the contact's fingerprint." },
-            { "secret <secret>",                "Verify a contact's identity using a shared secret." },
-            { "question <question> <answer>",   "Verify a contact's identity using a question and expected answer." },
-            { "answer <answer>",                "Respond to a question answer verification request with your answer." },
-            { "policy manual",                  "Set the global OTR policy to manual, OTR sessions must be started manually." },
-            { "policy manual <contact>",        "Set the OTR policy to manual for a specific contact." },
-            { "policy opportunistic",           "Set the global OTR policy to opportunistic, an OTR session will be attempted upon starting a conversation." },
-            { "policy opportunistic <contact>", "Set the OTR policy to opportunistic for a specific contact." },
-            { "policy always",                  "Set the global OTR policy to always, an error will be displayed if an OTR session cannot be initiated upon starting a conversation." },
-            { "policy always <contact>",        "Set the OTR policy to always for a specific contact." },
-            { "log on|off",                     "Enable or disable plaintext logging of OTR encrypted messages." },
-            { "log redact",                     "Log OTR encrypted messages, but replace the contents with [redacted]. This is the default." },
-            { "char <char>",                    "Set the character to be displayed next to OTR encrypted messages." })
-        CMD_EXAMPLES(
-            "/otr log off",
-            "/otr policy manual",
-            "/otr policy opportunistic mrfriend@workchat.org",
-            "/otr gen",
-            "/otr start buddy@buddychat.org",
-            "/otr myfp",
-            "/otr theirfp",
-            "/otr question \"What is the name of my rabbit?\" fiffi",
-            "/otr end",
-            "/otr char *")
-    },
-
-    { "/outtype",
-        parse_args, 1, 1, &cons_outtype_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_outtype)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/outtype on|off")
-        CMD_DESC(
-            "Send typing notifications, chat states (/states) will be enabled if this setting is enabled.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable sending typing notifications." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/gone",
-        parse_args, 1, 1, &cons_gone_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_gone)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/gone <minutes>")
-        CMD_DESC(
-            "Send a 'gone' state to the recipient after the specified number of minutes. "
-            "Chat states (/states) will be enabled if this setting is set.")
-        CMD_ARGS(
-            { "<minutes>", "Number of minutes of inactivity before sending the 'gone' state, a value of 0 will disable sending this state." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/history",
-        parse_args, 1, 1, &cons_history_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_history)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/history on|off")
-        CMD_DESC(
-            "Switch chat history on or off, /chlog will automatically be enabled when this setting is on. "
-            "When history is enabled, previous messages are shown in chat windows.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable showing chat history." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/log",
-        parse_args, 1, 2, &cons_log_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_log)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/log where",
-            "/log rotate on|off",
-            "/log maxsize <bytes>",
-            "/log shared on|off")
-        CMD_DESC(
-            "Manage profanity log settings.")
-        CMD_ARGS(
-            { "where",           "Show the current log file location." },
-            { "rotate on|off",   "Rotate log, default on." },
-            { "maxsize <bytes>", "With rotate enabled, specifies the max log size, defaults to 1048580 (1MB)." },
-            { "shared on|off",   "Share logs between all instances, default: on. When off, the process id will be included in the log." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/carbons",
-        parse_args, 1, 1, &cons_carbons_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_carbons)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/carbons on|off")
-        CMD_DESC(
-            "Enable or disable message carbons. "
-            "Message carbons ensure that both sides of all conversations are shared with all the user's clients that implement this protocol.")
-        CMD_ARGS(
-            { "on|off", "Enable or disable message carbons." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/receipts",
-        parse_args, 2, 2, &cons_receipts_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_receipts)
-        CMD_TAGS(
-            CMD_TAG_CHAT)
-        CMD_SYN(
-            "/receipts request on|off",
-            "/receipts send on|off")
-        CMD_DESC(
-            "Enable or disable message delivery receipts. The interface will indicate when a message has been received.")
-        CMD_ARGS(
-            { "request on|off", "Whether or not to request a receipt upon sending a message." },
-            { "send on|off",    "Whether or not to send a receipt if one has been requested with a received message." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/reconnect",
-        parse_args, 1, 1, &cons_reconnect_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_reconnect)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/reconnect <seconds>")
-        CMD_DESC(
-            "Set the reconnect attempt interval for when the connection is lost.")
-        CMD_ARGS(
-            { "<seconds>", "Number of seconds before attempting to reconnect, a value of 0 disables reconnect." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/autoping",
-        parse_args, 2, 2, &cons_autoping_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_autoping)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/autoping set <seconds>",
-            "/autoping timeout <seconds>")
-        CMD_DESC(
-            "Set the interval between sending ping requests to the server to ensure the connection is kept alive.")
-        CMD_ARGS(
-            { "set <seconds>",      "Number of seconds between sending pings, a value of 0 disables autoping." },
-            { "timeout <seconds>",  "Seconds to wait for autoping responses, after which the connection is considered broken." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/ping",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_ping)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION)
-        CMD_SYN(
-            "/ping [<jid>]")
-        CMD_DESC(
-            "Sends an IQ ping stanza to the specified JID. "
-            "If no JID is supplied, your chat server will be pinged.")
-        CMD_ARGS(
-            { "<jid>", "The Jabber ID to send the ping request to." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/autoaway",
-        parse_args_with_freetext, 2, 3, &cons_autoaway_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_autoaway)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/autoaway mode idle|away|off",
-            "/autoaway time away|xa <minutes>",
-            "/autoaway message away|xa <message>|off",
-            "/autoaway check on|off")
-        CMD_DESC(
-            "Manage autoway settings for idle time.")
-        CMD_ARGS(
-            { "mode idle",              "Sends idle time, status remains online." },
-            { "mode away",              "Sends away and xa presence as well as idle time." },
-            { "mode off",               "Disabled (default)." },
-            { "time away <minutes>",    "Number of minutes before the away presence is sent, default: 15." },
-            { "time xa <minutes>",      "Number of minutes before the xa presence is sent, default: 0 (disabled)." },
-            { "message away <message>", "Optional message to send with the away presence, default: off (disabled)." },
-            { "message xa <message>",   "Optional message to send with the xa presence, default: off (disabled)." },
-            { "message away off",       "Send no message with away presence." },
-            { "message xa off",         "Send no message with xa presence." },
-            { "check on|off",           "When enabled, checks for activity and sends online presence, default: on." })
-        CMD_EXAMPLES(
-            "/autoaway mode away",
-            "/autoaway time away 30",
-            "/autoaway message away Away from computer for a while",
-            "/autoaway time xa 120",
-            "/autoaway message xa Away from computer for a very long time",
-            "/autoaway check off")
-    },
-
-    { "/priority",
-        parse_args, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_priority)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/priority <priority>")
-        CMD_DESC(
-            "Set priority for the current account. "
-            "See the /account command for specific priority settings per presence status.")
-        CMD_ARGS(
-            { "<priority>", "Number between -128 and 127, default: 0." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/account",
-        parse_args, 0, 4, NULL,
-        CMD_SUBFUNCS(
-            { "list",       cmd_account_list },
-            { "show",       cmd_account_show },
-            { "add",        cmd_account_add },
-            { "remove",     cmd_account_remove },
-            { "enable",     cmd_account_enable },
-            { "disable",    cmd_account_disable },
-            { "rename",     cmd_account_rename },
-            { "default",    cmd_account_default },
-            { "set",        cmd_account_set },
-            { "clear",      cmd_account_clear })
-        CMD_MAINFUNC(cmd_account)
-        CMD_TAGS(
-            CMD_TAG_CONNECTION
-            CMD_TAG_PRESENCE,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/account",
-            "/account list",
-            "/account show <account>",
-            "/account enable|disable <account>",
-            "/account default set <account>",
-            "/account default off",
-            "/account add <account>",
-            "/account remove <account>",
-            "/account rename <account> <newaccount>",
-            "/account set <account> jid <jid>",
-            "/account set <account> server <server>",
-            "/account set <account> port <port>",
-            "/account set <account> status <presence>",
-            "/account set <account> status last",
-            "/account set <account> <presence> <priority>",
-            "/account set <account> resource <resource>",
-            "/account set <account> password <password>",
-            "/account set <account> eval_password <command>",
-            "/account set <account> muc <service>",
-            "/account set <account> nick <nick>",
-            "/account set <account> otr <policy>",
-            "/account set <account> pgpkeyid <pgpkeyid>",
-            "/account set <account> startscript <script>",
-            "/account set <account> tls force|allow|disable",
-            "/account set <account> theme <theme>",
-            "/account clear <account> password",
-            "/account clear <account> eval_password",
-            "/account clear <account> server",
-            "/account clear <account> port",
-            "/account clear <account> otr",
-            "/account clear <account> pgpkeyid",
-            "/account clear <account> startscript")
-        CMD_DESC(
-            "Commands for creating and managing accounts. "
-            "Calling with no arguments will display information for the current account.")
-        CMD_ARGS(
-            { "list",                                   "List all accounts." },
-            { "enable <account>",                       "Enable the account, it will be used for autocompletion." },
-            { "show <account>",                         "Show details for the specified account." },
-            { "disable <account>",                      "Disable the account." },
-            { "default set <account>",                  "Set the default account, used when no argument passed to the /connect command." },
-            { "default off",                            "Clear the default account setting." },
-            { "add <account>",                          "Create a new account." },
-            { "remove <account>",                       "Remove an account." },
-            { "rename <account> <newaccount>",          "Rename 'account' to 'newaccount'." },
-            { "set <account> jid <jid>",                "Set the Jabber ID for the account, account name will be used if not set." },
-            { "set <account> server <server>",          "The chat server, if different to the domainpart of the JID." },
-            { "set <account> port <port>",              "The port used for connecting if not the default (5222, or 5223 for SSL)." },
-            { "set <account> status <presence>",        "The presence status to use on login." },
-            { "set <account> status last",              "Use your last status before logging out, when logging in." },
-            { "set <account> <presence> <priority>",    "Set the priority (-128..127) to use for the specified presence." },
-            { "set <account> resource <resource>",      "The resource to be used for this account." },
-            { "set <account> password <password>",      "Password for the account, note this is currently stored in plaintext if set." },
-            { "set <account> eval_password <command>",  "Shell command evaluated to retrieve password for the account. Can be used to retrieve password from keyring." },
-            { "set <account> muc <service>",            "The default MUC chat service to use, defaults to 'conference.<domainpart>' where the domain part is from the account JID." },
-            { "set <account> nick <nick>",              "The default nickname to use when joining chat rooms." },
-            { "set <account> otr <policy>",             "Override global OTR policy for this account, see /otr." },
-            { "set <account> pgpkeyid <pgpkeyid>",      "Set the ID of the PGP key for this account, see /pgp." },
-            { "set <account> startscript <script>",     "Set the script to execute after connecting." },
-            { "set <account> tls force",                "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
-            { "set <account> tls allow",                "Use TLS for the connection if it is available." },
-            { "set <account> tls disable",              "Disable TLS for the connection." },
-            { "set <account> <theme>",                  "Set the UI theme for the account." },
-            { "clear <account> server",                 "Remove the server setting for this account." },
-            { "clear <account> port",                   "Remove the port setting for this account." },
-            { "clear <account> password",               "Remove the password setting for this account." },
-            { "clear <account> eval_password",          "Remove the eval_password setting for this account." },
-            { "clear <account> otr",                    "Remove the OTR policy setting for this account." },
-            { "clear <account> pgpkeyid",               "Remove pgpkeyid associated with this account." },
-            { "clear <account> startscript",            "Remove startscript associated with this account." },
-            { "clear <account> theme",                  "Clear the theme setting for the account, the global theme will be used." })
-        CMD_EXAMPLES(
-            "/account add me",
-            "/account set me jid me@chatty",
-            "/account set me server talk.chat.com",
-            "/account set me port 5111",
-            "/account set me muc chatservice.mycompany.com",
-            "/account set me nick dennis",
-            "/account set me status dnd",
-            "/account set me dnd -1",
-            "/account rename me gtalk")
-    },
-
-    { "/plugins",
-        parse_args, 0, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_plugins)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/plugins",
-            "/plugins load <plugin>")
-        CMD_DESC(
-            "Manage plugins. Passing no arguments lists currently loaded plugins.")
-        CMD_ARGS(
-            { "load <plugin>",       "Load a plugin." })
-        CMD_EXAMPLES(
-            "/plugin load browser.py")
-    },
-
-    { "/prefs",
-        parse_args, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_prefs)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/prefs [ui|desktop|chat|log|conn|presence|otr|pgp]")
-        CMD_DESC(
-            "Show preferences for different areas of functionality. "
-            "Passing no arguments shows all preferences.")
-        CMD_ARGS(
-            { "ui",       "User interface preferences." },
-            { "desktop",  "Desktop notification preferences." },
-            { "chat",     "Chat state preferences." },
-            { "log",      "Logging preferences." },
-            { "conn",     "Connection handling preferences." },
-            { "presence", "Chat presence preferences." },
-            { "otr",      "Off The Record preferences." },
-            { "pgp",      "OpenPGP preferences." })
-        CMD_NOEXAMPLES
-    },
-
-    { "/theme",
-        parse_args, 1, 2, &cons_theme_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_theme)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/theme list",
-            "/theme load <theme>",
-            "/theme colours",
-            "/theme properties")
-        CMD_DESC(
-            "Load a theme, includes colours and UI options.")
-        CMD_ARGS(
-            { "list",           "List all available themes." },
-            { "load <theme>",   "Load the specified theme. 'default' will reset to the default theme." },
-            { "colours",        "Show colour values as rendered by the terminal." },
-            { "properties",     "Show colour settings for current theme." })
-        CMD_EXAMPLES(
-            "/theme list",
-            "/theme load forest")
-    },
-
-    { "/statuses",
-        parse_args, 2, 2, &cons_statuses_setting,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_statuses)
-        CMD_TAGS(
-            CMD_TAG_UI,
-            CMD_TAG_CHAT,
-            CMD_TAG_GROUPCHAT)
-        CMD_SYN(
-            "/statuses console|chat|muc all|online|none")
-        CMD_DESC(
-            "Configure which presence changes are displayed in various windows. "
-            "The default is 'all' for all windows.")
-        CMD_ARGS(
-            { "console", "Configure what is displayed in the console window." },
-            { "chat",    "Configure what is displayed in chat windows." },
-            { "muc",     "Configure what is displayed in chat room windows." },
-            { "all",     "Show all presence changes." },
-            { "online",  "Show only online/offline changes." },
-            { "none",    "Don't show any presence changes." })
-        CMD_EXAMPLES(
-            "/statuses console none",
-            "/statuses chat online",
-            "/statuses muc all")
-    },
-
-    { "/xmlconsole",
-        parse_args, 0, 0, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_xmlconsole)
-        CMD_TAGS(
-            CMD_TAG_UI)
-        CMD_SYN(
-            "/xmlconsole")
-        CMD_DESC(
-            "Open the XML console to view incoming and outgoing XMPP traffic.")
-        CMD_NOARGS
-        CMD_NOEXAMPLES
-    },
-
-    { "/away",
-        parse_args_with_freetext, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_away)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/away [<message>]")
-        CMD_DESC(
-            "Set your status to 'away'.")
-        CMD_ARGS(
-            { "<message>",  "Optional message to use with the status." })
-        CMD_EXAMPLES(
-            "/away",
-            "/away Gone for lunch")
-    },
-
-    { "/chat",
-        parse_args_with_freetext, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_chat)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/chat [<message>]")
-        CMD_DESC(
-            "Set your status to 'chat' (available for chat).")
-        CMD_ARGS(
-            { "<message>",  "Optional message to use with the status." })
-        CMD_EXAMPLES(
-            "/chat",
-            "/chat Please talk to me!")
-    },
-
-    { "/dnd",
-        parse_args_with_freetext, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_dnd)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/dnd [<message>]")
-        CMD_DESC(
-            "Set your status to 'dnd' (do not disturb).")
-        CMD_ARGS(
-            { "<message>",  "Optional message to use with the status." })
-        CMD_EXAMPLES(
-            "/dnd",
-            "/dnd I'm in the zone")
-    },
-
-    { "/online",
-        parse_args_with_freetext, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_online)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/online [<message>]")
-        CMD_DESC(
-            "Set your status to 'online'.")
-        CMD_ARGS(
-            { "<message>",  "Optional message to use with the status." })
-        CMD_EXAMPLES(
-            "/online",
-            "/online Up the Irons!")
-    },
-
-    { "/xa",
-        parse_args_with_freetext, 0, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_xa)
-        CMD_TAGS(
-            CMD_TAG_PRESENCE)
-        CMD_SYN(
-            "/xa [<message>]")
-        CMD_DESC(
-            "Set your status to 'xa' (extended away).")
-        CMD_ARGS(
-            { "<message>",  "Optional message to use with the status." })
-        CMD_EXAMPLES(
-            "/xa",
-            "/xa This meeting is going to be a long one")
-    },
-
-    { "/script",
-        parse_args, 1, 2, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_script)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/script run <script>",
-            "/script list",
-            "/script show <script>")
-        CMD_DESC(
-            "Run command scripts. "
-            "Scripts are stored in $XDG_DATA_HOME/profanity/scripts/ which is usually $HOME/.local/share/profanity/scripts/.")
-        CMD_ARGS(
-            { "script run <script>",    "Execute a script." },
-            { "script list",            "List all scripts TODO." },
-            { "script show <script>",   "Show the commands in script TODO." })
-        CMD_EXAMPLES(
-            "/script list",
-            "/script run myscript",
-            "/script show somescript")
-    },
-
-    { "/export",
-        parse_args, 1, 1, NULL,
-        CMD_NOSUBFUNCS
-        CMD_MAINFUNC(cmd_export)
-        CMD_NOTAGS
-        CMD_SYN(
-            "/export <filepath>")
-        CMD_DESC(
-            "Exports contacts to a csv file.")
-        CMD_ARGS(
-            { "<filepath>", "Path to the output file." })
-        CMD_EXAMPLES(
-            "/export /path/to/output.csv",
-            "/export ~/contacts.csv")
-    },
-};
-
-static Autocomplete commands_ac;
-static Autocomplete who_room_ac;
-static Autocomplete who_roster_ac;
-static Autocomplete help_ac;
-static Autocomplete help_commands_ac;
-static Autocomplete notify_ac;
-static Autocomplete notify_chat_ac;
-static Autocomplete notify_room_ac;
-static Autocomplete notify_typing_ac;
-static Autocomplete notify_mention_ac;
-static Autocomplete notify_trigger_ac;
-static Autocomplete prefs_ac;
-static Autocomplete sub_ac;
-static Autocomplete log_ac;
-static Autocomplete autoaway_ac;
-static Autocomplete autoaway_mode_ac;
-static Autocomplete autoaway_presence_ac;
-static Autocomplete autoconnect_ac;
-static Autocomplete titlebar_ac;
-static Autocomplete theme_ac;
-static Autocomplete theme_load_ac;
-static Autocomplete account_ac;
-static Autocomplete account_set_ac;
-static Autocomplete account_clear_ac;
-static Autocomplete account_default_ac;
-static Autocomplete account_status_ac;
-static Autocomplete disco_ac;
-static Autocomplete wins_ac;
-static Autocomplete roster_ac;
-static Autocomplete roster_show_ac;
-static Autocomplete roster_by_ac;
-static Autocomplete roster_count_ac;
-static Autocomplete roster_order_ac;
-static Autocomplete roster_header_ac;
-static Autocomplete roster_contact_ac;
-static Autocomplete roster_resource_ac;
-static Autocomplete roster_presence_ac;
-static Autocomplete roster_char_ac;
-static Autocomplete roster_remove_all_ac;
-static Autocomplete roster_room_ac;
-static Autocomplete roster_room_position_ac;
-static Autocomplete roster_room_by_ac;
-static Autocomplete roster_room_order_ac;
-static Autocomplete roster_unread_ac;
-static Autocomplete roster_private_ac;
-static Autocomplete group_ac;
-static Autocomplete bookmark_ac;
-static Autocomplete bookmark_property_ac;
-static Autocomplete otr_ac;
-static Autocomplete otr_log_ac;
-static Autocomplete otr_policy_ac;
-static Autocomplete connect_property_ac;
-static Autocomplete tls_property_ac;
-static Autocomplete statuses_ac;
-static Autocomplete statuses_setting_ac;
-static Autocomplete alias_ac;
-static Autocomplete aliases_ac;
-static Autocomplete join_property_ac;
-static Autocomplete room_ac;
-static Autocomplete affiliation_ac;
-static Autocomplete role_ac;
-static Autocomplete privilege_cmd_ac;
-static Autocomplete subject_ac;
-static Autocomplete form_ac;
-static Autocomplete form_field_multi_ac;
-static Autocomplete occupants_ac;
-static Autocomplete occupants_default_ac;
-static Autocomplete occupants_show_ac;
-static Autocomplete time_ac;
-static Autocomplete time_format_ac;
-static Autocomplete resource_ac;
-static Autocomplete inpblock_ac;
-static Autocomplete receipts_ac;
-static Autocomplete pgp_ac;
-static Autocomplete pgp_log_ac;
-static Autocomplete tls_ac;
-static Autocomplete tls_certpath_ac;
-static Autocomplete script_ac;
-static Autocomplete script_show_ac;
-static Autocomplete console_ac;
-static Autocomplete console_msg_ac;
-static Autocomplete autoping_ac;
-static Autocomplete plugins_ac;
-static Autocomplete plugins_load_ac;
-static Autocomplete sendfile_ac;
-static Autocomplete blocked_ac;
-static Autocomplete tray_ac;
-
-/*
- * Initialise command autocompleter and history
- */
-void
-cmd_init(void)
-{
-    log_info("Initialising commands");
-
-    commands_ac = autocomplete_new();
-    aliases_ac = autocomplete_new();
-
-    help_ac = autocomplete_new();
-    autocomplete_add(help_ac, "commands");
-    autocomplete_add(help_ac, "navigation");
-
-    // load command defs into hash table
-    commands = g_hash_table_new(g_str_hash, g_str_equal);
-    unsigned int i;
-    for (i = 0; i < ARRAY_SIZE(command_defs); i++) {
-        Command *pcmd = command_defs+i;
-
-        // add to hash
-        g_hash_table_insert(commands, pcmd->cmd, pcmd);
-
-        // add to commands and help autocompleters
-        autocomplete_add(commands_ac, pcmd->cmd);
-        autocomplete_add(help_ac, pcmd->cmd+1);
-    }
-
-    // load aliases
-    GList *aliases = prefs_get_aliases();
-    GList *curr = aliases;
-    while (curr) {
-        ProfAlias *alias = curr->data;
-        GString *ac_alias = g_string_new("/");
-        g_string_append(ac_alias, alias->name);
-        autocomplete_add(commands_ac, ac_alias->str);
-        autocomplete_add(aliases_ac, alias->name);
-        g_string_free(ac_alias, TRUE);
-        curr = g_list_next(curr);
-    }
-    prefs_free_aliases(aliases);
-
-    help_commands_ac = autocomplete_new();
-    autocomplete_add(help_commands_ac, "chat");
-    autocomplete_add(help_commands_ac, "groupchat");
-    autocomplete_add(help_commands_ac, "roster");
-    autocomplete_add(help_commands_ac, "presence");
-    autocomplete_add(help_commands_ac, "discovery");
-    autocomplete_add(help_commands_ac, "connection");
-    autocomplete_add(help_commands_ac, "ui");
-    autocomplete_add(help_commands_ac, "plugins");
-
-    prefs_ac = autocomplete_new();
-    autocomplete_add(prefs_ac, "ui");
-    autocomplete_add(prefs_ac, "desktop");
-    autocomplete_add(prefs_ac, "chat");
-    autocomplete_add(prefs_ac, "log");
-    autocomplete_add(prefs_ac, "conn");
-    autocomplete_add(prefs_ac, "presence");
-    autocomplete_add(prefs_ac, "otr");
-    autocomplete_add(prefs_ac, "pgp");
-
-    notify_ac = autocomplete_new();
-    autocomplete_add(notify_ac, "chat");
-    autocomplete_add(notify_ac, "room");
-    autocomplete_add(notify_ac, "typing");
-    autocomplete_add(notify_ac, "remind");
-    autocomplete_add(notify_ac, "invite");
-    autocomplete_add(notify_ac, "sub");
-    autocomplete_add(notify_ac, "on");
-    autocomplete_add(notify_ac, "off");
-    autocomplete_add(notify_ac, "mention");
-    autocomplete_add(notify_ac, "trigger");
-    autocomplete_add(notify_ac, "reset");
-
-    notify_chat_ac = autocomplete_new();
-    autocomplete_add(notify_chat_ac, "on");
-    autocomplete_add(notify_chat_ac, "off");
-    autocomplete_add(notify_chat_ac, "current");
-    autocomplete_add(notify_chat_ac, "text");
-
-    notify_room_ac = autocomplete_new();
-    autocomplete_add(notify_room_ac, "on");
-    autocomplete_add(notify_room_ac, "off");
-    autocomplete_add(notify_room_ac, "mention");
-    autocomplete_add(notify_room_ac, "current");
-    autocomplete_add(notify_room_ac, "text");
-    autocomplete_add(notify_room_ac, "trigger");
-
-    notify_typing_ac = autocomplete_new();
-    autocomplete_add(notify_typing_ac, "on");
-    autocomplete_add(notify_typing_ac, "off");
-    autocomplete_add(notify_typing_ac, "current");
-
-    notify_mention_ac = autocomplete_new();
-    autocomplete_add(notify_mention_ac, "on");
-    autocomplete_add(notify_mention_ac, "off");
-    autocomplete_add(notify_mention_ac, "case_sensitive");
-    autocomplete_add(notify_mention_ac, "case_insensitive");
-    autocomplete_add(notify_mention_ac, "word_whole");
-    autocomplete_add(notify_mention_ac, "word_part");
-
-    notify_trigger_ac = autocomplete_new();
-    autocomplete_add(notify_trigger_ac, "add");
-    autocomplete_add(notify_trigger_ac, "remove");
-    autocomplete_add(notify_trigger_ac, "list");
-    autocomplete_add(notify_trigger_ac, "on");
-    autocomplete_add(notify_trigger_ac, "off");
-
-    sub_ac = autocomplete_new();
-    autocomplete_add(sub_ac, "request");
-    autocomplete_add(sub_ac, "allow");
-    autocomplete_add(sub_ac, "deny");
-    autocomplete_add(sub_ac, "show");
-    autocomplete_add(sub_ac, "sent");
-    autocomplete_add(sub_ac, "received");
-
-    titlebar_ac = autocomplete_new();
-    autocomplete_add(titlebar_ac, "show");
-    autocomplete_add(titlebar_ac, "goodbye");
-
-    log_ac = autocomplete_new();
-    autocomplete_add(log_ac, "maxsize");
-    autocomplete_add(log_ac, "rotate");
-    autocomplete_add(log_ac, "shared");
-    autocomplete_add(log_ac, "where");
-
-    autoaway_ac = autocomplete_new();
-    autocomplete_add(autoaway_ac, "mode");
-    autocomplete_add(autoaway_ac, "time");
-    autocomplete_add(autoaway_ac, "message");
-    autocomplete_add(autoaway_ac, "check");
-
-    autoaway_mode_ac = autocomplete_new();
-    autocomplete_add(autoaway_mode_ac, "away");
-    autocomplete_add(autoaway_mode_ac, "idle");
-    autocomplete_add(autoaway_mode_ac, "off");
-
-    autoaway_presence_ac = autocomplete_new();
-    autocomplete_add(autoaway_presence_ac, "away");
-    autocomplete_add(autoaway_presence_ac, "xa");
-
-    autoconnect_ac = autocomplete_new();
-    autocomplete_add(autoconnect_ac, "set");
-    autocomplete_add(autoconnect_ac, "off");
-
-    theme_ac = autocomplete_new();
-    autocomplete_add(theme_ac, "load");
-    autocomplete_add(theme_ac, "list");
-    autocomplete_add(theme_ac, "colours");
-    autocomplete_add(theme_ac, "properties");
-
-    disco_ac = autocomplete_new();
-    autocomplete_add(disco_ac, "info");
-    autocomplete_add(disco_ac, "items");
-
-    account_ac = autocomplete_new();
-    autocomplete_add(account_ac, "list");
-    autocomplete_add(account_ac, "show");
-    autocomplete_add(account_ac, "add");
-    autocomplete_add(account_ac, "remove");
-    autocomplete_add(account_ac, "enable");
-    autocomplete_add(account_ac, "disable");
-    autocomplete_add(account_ac, "default");
-    autocomplete_add(account_ac, "rename");
-    autocomplete_add(account_ac, "set");
-    autocomplete_add(account_ac, "clear");
-
-    account_set_ac = autocomplete_new();
-    autocomplete_add(account_set_ac, "jid");
-    autocomplete_add(account_set_ac, "server");
-    autocomplete_add(account_set_ac, "port");
-    autocomplete_add(account_set_ac, "status");
-    autocomplete_add(account_set_ac, "online");
-    autocomplete_add(account_set_ac, "chat");
-    autocomplete_add(account_set_ac, "away");
-    autocomplete_add(account_set_ac, "xa");
-    autocomplete_add(account_set_ac, "dnd");
-    autocomplete_add(account_set_ac, "resource");
-    autocomplete_add(account_set_ac, "password");
-    autocomplete_add(account_set_ac, "eval_password");
-    autocomplete_add(account_set_ac, "muc");
-    autocomplete_add(account_set_ac, "nick");
-    autocomplete_add(account_set_ac, "otr");
-    autocomplete_add(account_set_ac, "pgpkeyid");
-    autocomplete_add(account_set_ac, "startscript");
-    autocomplete_add(account_set_ac, "tls");
-    autocomplete_add(account_set_ac, "theme");
-
-    account_clear_ac = autocomplete_new();
-    autocomplete_add(account_clear_ac, "password");
-    autocomplete_add(account_clear_ac, "eval_password");
-    autocomplete_add(account_clear_ac, "server");
-    autocomplete_add(account_clear_ac, "port");
-    autocomplete_add(account_clear_ac, "otr");
-    autocomplete_add(account_clear_ac, "pgpkeyid");
-    autocomplete_add(account_clear_ac, "startscript");
-    autocomplete_add(account_clear_ac, "theme");
-
-    account_default_ac = autocomplete_new();
-    autocomplete_add(account_default_ac, "set");
-    autocomplete_add(account_default_ac, "off");
-
-    account_status_ac = autocomplete_new();
-    autocomplete_add(account_status_ac, "online");
-    autocomplete_add(account_status_ac, "chat");
-    autocomplete_add(account_status_ac, "away");
-    autocomplete_add(account_status_ac, "xa");
-    autocomplete_add(account_status_ac, "dnd");
-    autocomplete_add(account_status_ac, "last");
-
-    wins_ac = autocomplete_new();
-    autocomplete_add(wins_ac, "unread");
-    autocomplete_add(wins_ac, "prune");
-    autocomplete_add(wins_ac, "tidy");
-    autocomplete_add(wins_ac, "autotidy");
-    autocomplete_add(wins_ac, "swap");
-
-    roster_ac = autocomplete_new();
-    autocomplete_add(roster_ac, "add");
-    autocomplete_add(roster_ac, "online");
-    autocomplete_add(roster_ac, "nick");
-    autocomplete_add(roster_ac, "clearnick");
-    autocomplete_add(roster_ac, "remove");
-    autocomplete_add(roster_ac, "remove_all");
-    autocomplete_add(roster_ac, "show");
-    autocomplete_add(roster_ac, "hide");
-    autocomplete_add(roster_ac, "by");
-    autocomplete_add(roster_ac, "count");
-    autocomplete_add(roster_ac, "order");
-    autocomplete_add(roster_ac, "unread");
-    autocomplete_add(roster_ac, "room");
-    autocomplete_add(roster_ac, "size");
-    autocomplete_add(roster_ac, "wrap");
-    autocomplete_add(roster_ac, "header");
-    autocomplete_add(roster_ac, "contact");
-    autocomplete_add(roster_ac, "resource");
-    autocomplete_add(roster_ac, "presence");
-    autocomplete_add(roster_ac, "private");
-
-    roster_private_ac = autocomplete_new();
-    autocomplete_add(roster_private_ac, "room");
-    autocomplete_add(roster_private_ac, "group");
-    autocomplete_add(roster_private_ac, "off");
-    autocomplete_add(roster_private_ac, "char");
-
-    roster_header_ac = autocomplete_new();
-    autocomplete_add(roster_header_ac, "char");
-
-    roster_contact_ac = autocomplete_new();
-    autocomplete_add(roster_contact_ac, "char");
-    autocomplete_add(roster_contact_ac, "indent");
-
-    roster_resource_ac = autocomplete_new();
-    autocomplete_add(roster_resource_ac, "char");
-    autocomplete_add(roster_resource_ac, "indent");
-    autocomplete_add(roster_resource_ac, "join");
-
-    roster_presence_ac = autocomplete_new();
-    autocomplete_add(roster_presence_ac, "indent");
-
-    roster_char_ac = autocomplete_new();
-    autocomplete_add(roster_char_ac, "none");
-
-    roster_show_ac = autocomplete_new();
-    autocomplete_add(roster_show_ac, "offline");
-    autocomplete_add(roster_show_ac, "resource");
-    autocomplete_add(roster_show_ac, "presence");
-    autocomplete_add(roster_show_ac, "status");
-    autocomplete_add(roster_show_ac, "empty");
-    autocomplete_add(roster_show_ac, "priority");
-    autocomplete_add(roster_show_ac, "contacts");
-    autocomplete_add(roster_show_ac, "unsubscribed");
-    autocomplete_add(roster_show_ac, "rooms");
-
-    roster_by_ac = autocomplete_new();
-    autocomplete_add(roster_by_ac, "group");
-    autocomplete_add(roster_by_ac, "presence");
-    autocomplete_add(roster_by_ac, "none");
-
-    roster_count_ac = autocomplete_new();
-    autocomplete_add(roster_count_ac, "unread");
-    autocomplete_add(roster_count_ac, "items");
-    autocomplete_add(roster_count_ac, "off");
-    autocomplete_add(roster_count_ac, "zero");
-
-    roster_order_ac = autocomplete_new();
-    autocomplete_add(roster_order_ac, "name");
-    autocomplete_add(roster_order_ac, "presence");
-
-    roster_unread_ac = autocomplete_new();
-    autocomplete_add(roster_unread_ac, "before");
-    autocomplete_add(roster_unread_ac, "after");
-    autocomplete_add(roster_unread_ac, "off");
-
-    roster_room_ac = autocomplete_new();
-    autocomplete_add(roster_room_ac, "char");
-    autocomplete_add(roster_room_ac, "position");
-    autocomplete_add(roster_room_ac, "by");
-    autocomplete_add(roster_room_ac, "order");
-    autocomplete_add(roster_room_ac, "unread");
-    autocomplete_add(roster_room_ac, "private");
-
-    roster_room_by_ac = autocomplete_new();
-    autocomplete_add(roster_room_by_ac, "service");
-    autocomplete_add(roster_room_by_ac, "none");
-
-    roster_room_order_ac = autocomplete_new();
-    autocomplete_add(roster_room_order_ac, "name");
-    autocomplete_add(roster_room_order_ac, "unread");
-
-    roster_room_position_ac = autocomplete_new();
-    autocomplete_add(roster_room_position_ac, "first");
-    autocomplete_add(roster_room_position_ac, "last");
-
-    roster_remove_all_ac = autocomplete_new();
-    autocomplete_add(roster_remove_all_ac, "contacts");
-
-    group_ac = autocomplete_new();
-    autocomplete_add(group_ac, "show");
-    autocomplete_add(group_ac, "add");
-    autocomplete_add(group_ac, "remove");
-
-    theme_load_ac = NULL;
-    plugins_load_ac = NULL;
-
-    who_roster_ac = autocomplete_new();
-    autocomplete_add(who_roster_ac, "chat");
-    autocomplete_add(who_roster_ac, "online");
-    autocomplete_add(who_roster_ac, "away");
-    autocomplete_add(who_roster_ac, "xa");
-    autocomplete_add(who_roster_ac, "dnd");
-    autocomplete_add(who_roster_ac, "offline");
-    autocomplete_add(who_roster_ac, "available");
-    autocomplete_add(who_roster_ac, "unavailable");
-    autocomplete_add(who_roster_ac, "any");
-
-    who_room_ac = autocomplete_new();
-    autocomplete_add(who_room_ac, "chat");
-    autocomplete_add(who_room_ac, "online");
-    autocomplete_add(who_room_ac, "away");
-    autocomplete_add(who_room_ac, "xa");
-    autocomplete_add(who_room_ac, "dnd");
-    autocomplete_add(who_room_ac, "available");
-    autocomplete_add(who_room_ac, "unavailable");
-    autocomplete_add(who_room_ac, "moderator");
-    autocomplete_add(who_room_ac, "participant");
-    autocomplete_add(who_room_ac, "visitor");
-    autocomplete_add(who_room_ac, "owner");
-    autocomplete_add(who_room_ac, "admin");
-    autocomplete_add(who_room_ac, "member");
-
-    bookmark_ac = autocomplete_new();
-    autocomplete_add(bookmark_ac, "list");
-    autocomplete_add(bookmark_ac, "add");
-    autocomplete_add(bookmark_ac, "update");
-    autocomplete_add(bookmark_ac, "remove");
-    autocomplete_add(bookmark_ac, "join");
-
-    bookmark_property_ac = autocomplete_new();
-    autocomplete_add(bookmark_property_ac, "nick");
-    autocomplete_add(bookmark_property_ac, "password");
-    autocomplete_add(bookmark_property_ac, "autojoin");
-
-    otr_ac = autocomplete_new();
-    autocomplete_add(otr_ac, "gen");
-    autocomplete_add(otr_ac, "start");
-    autocomplete_add(otr_ac, "end");
-    autocomplete_add(otr_ac, "myfp");
-    autocomplete_add(otr_ac, "theirfp");
-    autocomplete_add(otr_ac, "trust");
-    autocomplete_add(otr_ac, "untrust");
-    autocomplete_add(otr_ac, "secret");
-    autocomplete_add(otr_ac, "log");
-    autocomplete_add(otr_ac, "libver");
-    autocomplete_add(otr_ac, "policy");
-    autocomplete_add(otr_ac, "question");
-    autocomplete_add(otr_ac, "answer");
-    autocomplete_add(otr_ac, "char");
-
-    otr_log_ac = autocomplete_new();
-    autocomplete_add(otr_log_ac, "on");
-    autocomplete_add(otr_log_ac, "off");
-    autocomplete_add(otr_log_ac, "redact");
-
-    otr_policy_ac = autocomplete_new();
-    autocomplete_add(otr_policy_ac, "manual");
-    autocomplete_add(otr_policy_ac, "opportunistic");
-    autocomplete_add(otr_policy_ac, "always");
-
-    connect_property_ac = autocomplete_new();
-    autocomplete_add(connect_property_ac, "server");
-    autocomplete_add(connect_property_ac, "port");
-    autocomplete_add(connect_property_ac, "tls");
-
-    tls_property_ac = autocomplete_new();
-    autocomplete_add(tls_property_ac, "force");
-    autocomplete_add(tls_property_ac, "allow");
-    autocomplete_add(tls_property_ac, "disable");
-
-    join_property_ac = autocomplete_new();
-    autocomplete_add(join_property_ac, "nick");
-    autocomplete_add(join_property_ac, "password");
-
-    statuses_ac = autocomplete_new();
-    autocomplete_add(statuses_ac, "console");
-    autocomplete_add(statuses_ac, "chat");
-    autocomplete_add(statuses_ac, "muc");
-
-    statuses_setting_ac = autocomplete_new();
-    autocomplete_add(statuses_setting_ac, "all");
-    autocomplete_add(statuses_setting_ac, "online");
-    autocomplete_add(statuses_setting_ac, "none");
-
-    alias_ac = autocomplete_new();
-    autocomplete_add(alias_ac, "add");
-    autocomplete_add(alias_ac, "remove");
-    autocomplete_add(alias_ac, "list");
-
-    room_ac = autocomplete_new();
-    autocomplete_add(room_ac, "accept");
-    autocomplete_add(room_ac, "destroy");
-    autocomplete_add(room_ac, "config");
-
-    affiliation_ac = autocomplete_new();
-    autocomplete_add(affiliation_ac, "owner");
-    autocomplete_add(affiliation_ac, "admin");
-    autocomplete_add(affiliation_ac, "member");
-    autocomplete_add(affiliation_ac, "none");
-    autocomplete_add(affiliation_ac, "outcast");
-
-    role_ac = autocomplete_new();
-    autocomplete_add(role_ac, "moderator");
-    autocomplete_add(role_ac, "participant");
-    autocomplete_add(role_ac, "visitor");
-    autocomplete_add(role_ac, "none");
-
-    privilege_cmd_ac = autocomplete_new();
-    autocomplete_add(privilege_cmd_ac, "list");
-    autocomplete_add(privilege_cmd_ac, "set");
-
-    subject_ac = autocomplete_new();
-    autocomplete_add(subject_ac, "set");
-    autocomplete_add(subject_ac, "edit");
-    autocomplete_add(subject_ac, "prepend");
-    autocomplete_add(subject_ac, "append");
-    autocomplete_add(subject_ac, "clear");
-
-    form_ac = autocomplete_new();
-    autocomplete_add(form_ac, "submit");
-    autocomplete_add(form_ac, "cancel");
-    autocomplete_add(form_ac, "show");
-    autocomplete_add(form_ac, "help");
-
-    form_field_multi_ac = autocomplete_new();
-    autocomplete_add(form_field_multi_ac, "add");
-    autocomplete_add(form_field_multi_ac, "remove");
-
-    occupants_ac = autocomplete_new();
-    autocomplete_add(occupants_ac, "show");
-    autocomplete_add(occupants_ac, "hide");
-    autocomplete_add(occupants_ac, "default");
-    autocomplete_add(occupants_ac, "size");
-
-    occupants_default_ac = autocomplete_new();
-    autocomplete_add(occupants_default_ac, "show");
-    autocomplete_add(occupants_default_ac, "hide");
-
-    occupants_show_ac = autocomplete_new();
-    autocomplete_add(occupants_show_ac, "jid");
-
-    time_ac = autocomplete_new();
-    autocomplete_add(time_ac, "console");
-    autocomplete_add(time_ac, "chat");
-    autocomplete_add(time_ac, "muc");
-    autocomplete_add(time_ac, "mucconfig");
-    autocomplete_add(time_ac, "private");
-    autocomplete_add(time_ac, "xml");
-    autocomplete_add(time_ac, "statusbar");
-    autocomplete_add(time_ac, "lastactivity");
-
-    time_format_ac = autocomplete_new();
-    autocomplete_add(time_format_ac, "set");
-    autocomplete_add(time_format_ac, "off");
-
-    resource_ac = autocomplete_new();
-    autocomplete_add(resource_ac, "set");
-    autocomplete_add(resource_ac, "off");
-    autocomplete_add(resource_ac, "title");
-    autocomplete_add(resource_ac, "message");
-
-    inpblock_ac = autocomplete_new();
-    autocomplete_add(inpblock_ac, "timeout");
-    autocomplete_add(inpblock_ac, "dynamic");
-
-    receipts_ac = autocomplete_new();
-    autocomplete_add(receipts_ac, "send");
-    autocomplete_add(receipts_ac, "request");
-
-    pgp_ac = autocomplete_new();
-    autocomplete_add(pgp_ac, "keys");
-    autocomplete_add(pgp_ac, "contacts");
-    autocomplete_add(pgp_ac, "setkey");
-    autocomplete_add(pgp_ac, "libver");
-    autocomplete_add(pgp_ac, "start");
-    autocomplete_add(pgp_ac, "end");
-    autocomplete_add(pgp_ac, "log");
-    autocomplete_add(pgp_ac, "char");
-
-    pgp_log_ac = autocomplete_new();
-    autocomplete_add(pgp_log_ac, "on");
-    autocomplete_add(pgp_log_ac, "off");
-    autocomplete_add(pgp_log_ac, "redact");
-
-    tls_ac = autocomplete_new();
-    autocomplete_add(tls_ac, "allow");
-    autocomplete_add(tls_ac, "always");
-    autocomplete_add(tls_ac, "deny");
-    autocomplete_add(tls_ac, "cert");
-    autocomplete_add(tls_ac, "trust");
-    autocomplete_add(tls_ac, "trusted");
-    autocomplete_add(tls_ac, "revoke");
-    autocomplete_add(tls_ac, "certpath");
-    autocomplete_add(tls_ac, "show");
-
-    tls_certpath_ac = autocomplete_new();
-    autocomplete_add(tls_certpath_ac, "set");
-    autocomplete_add(tls_certpath_ac, "clear");
-
-    script_ac = autocomplete_new();
-    autocomplete_add(script_ac, "run");
-    autocomplete_add(script_ac, "list");
-    autocomplete_add(script_ac, "show");
-
-    script_show_ac = NULL;
-
-    console_ac = autocomplete_new();
-    autocomplete_add(console_ac, "chat");
-    autocomplete_add(console_ac, "muc");
-    autocomplete_add(console_ac, "private");
-
-    console_msg_ac = autocomplete_new();
-    autocomplete_add(console_msg_ac, "all");
-    autocomplete_add(console_msg_ac, "first");
-    autocomplete_add(console_msg_ac, "none");
-
-    autoping_ac = autocomplete_new();
-    autocomplete_add(autoping_ac, "set");
-    autocomplete_add(autoping_ac, "timeout");
-
-    plugins_ac = autocomplete_new();
-    autocomplete_add(plugins_ac, "load");
-
-    sendfile_ac = autocomplete_new();
-
-    blocked_ac = autocomplete_new();
-    autocomplete_add(blocked_ac, "add");
-    autocomplete_add(blocked_ac, "remove");
-
-    tray_ac = autocomplete_new();
-    autocomplete_add(tray_ac, "on");
-    autocomplete_add(tray_ac, "off");
-    autocomplete_add(tray_ac, "read");
-    autocomplete_add(tray_ac, "timer");
-}
-
-void
-cmd_uninit(void)
-{
-    autocomplete_free(commands_ac);
-    autocomplete_free(who_room_ac);
-    autocomplete_free(who_roster_ac);
-    autocomplete_free(help_ac);
-    autocomplete_free(help_commands_ac);
-    autocomplete_free(notify_ac);
-    autocomplete_free(notify_chat_ac);
-    autocomplete_free(notify_room_ac);
-    autocomplete_free(notify_typing_ac);
-    autocomplete_free(notify_mention_ac);
-    autocomplete_free(notify_trigger_ac);
-    autocomplete_free(sub_ac);
-    autocomplete_free(titlebar_ac);
-    autocomplete_free(log_ac);
-    autocomplete_free(prefs_ac);
-    autocomplete_free(autoaway_ac);
-    autocomplete_free(autoaway_mode_ac);
-    autocomplete_free(autoaway_presence_ac);
-    autocomplete_free(autoconnect_ac);
-    autocomplete_free(theme_ac);
-    autocomplete_free(theme_load_ac);
-    autocomplete_free(account_ac);
-    autocomplete_free(account_set_ac);
-    autocomplete_free(account_clear_ac);
-    autocomplete_free(account_default_ac);
-    autocomplete_free(account_status_ac);
-    autocomplete_free(disco_ac);
-    autocomplete_free(wins_ac);
-    autocomplete_free(roster_ac);
-    autocomplete_free(roster_header_ac);
-    autocomplete_free(roster_contact_ac);
-    autocomplete_free(roster_resource_ac);
-    autocomplete_free(roster_presence_ac);
-    autocomplete_free(roster_char_ac);
-    autocomplete_free(roster_show_ac);
-    autocomplete_free(roster_by_ac);
-    autocomplete_free(roster_count_ac);
-    autocomplete_free(roster_order_ac);
-    autocomplete_free(roster_room_ac);
-    autocomplete_free(roster_room_by_ac);
-    autocomplete_free(roster_unread_ac);
-    autocomplete_free(roster_room_position_ac);
-    autocomplete_free(roster_room_order_ac);
-    autocomplete_free(roster_remove_all_ac);
-    autocomplete_free(roster_private_ac);
-    autocomplete_free(group_ac);
-    autocomplete_free(bookmark_ac);
-    autocomplete_free(bookmark_property_ac);
-    autocomplete_free(otr_ac);
-    autocomplete_free(otr_log_ac);
-    autocomplete_free(otr_policy_ac);
-    autocomplete_free(connect_property_ac);
-    autocomplete_free(tls_property_ac);
-    autocomplete_free(statuses_ac);
-    autocomplete_free(statuses_setting_ac);
-    autocomplete_free(alias_ac);
-    autocomplete_free(aliases_ac);
-    autocomplete_free(join_property_ac);
-    autocomplete_free(room_ac);
-    autocomplete_free(affiliation_ac);
-    autocomplete_free(role_ac);
-    autocomplete_free(privilege_cmd_ac);
-    autocomplete_free(subject_ac);
-    autocomplete_free(form_ac);
-    autocomplete_free(form_field_multi_ac);
-    autocomplete_free(occupants_ac);
-    autocomplete_free(occupants_default_ac);
-    autocomplete_free(occupants_show_ac);
-    autocomplete_free(time_ac);
-    autocomplete_free(time_format_ac);
-    autocomplete_free(resource_ac);
-    autocomplete_free(inpblock_ac);
-    autocomplete_free(receipts_ac);
-    autocomplete_free(pgp_ac);
-    autocomplete_free(pgp_log_ac);
-    autocomplete_free(tls_ac);
-    autocomplete_free(tls_certpath_ac);
-    autocomplete_free(script_ac);
-    autocomplete_free(script_show_ac);
-    autocomplete_free(console_ac);
-    autocomplete_free(console_msg_ac);
-    autocomplete_free(autoping_ac);
-    autocomplete_free(plugins_ac);
-    autocomplete_free(plugins_load_ac);
-    autocomplete_free(sendfile_ac);
-    autocomplete_free(blocked_ac);
-    autocomplete_free(tray_ac);
-}
-
-gboolean
-cmd_exists(char *cmd)
-{
-    if (commands_ac == NULL) {
-        return FALSE;
-    } else {
-        return autocomplete_contains(commands_ac, cmd);
-    }
-}
-
-void
-cmd_autocomplete_add(const char *const value)
-{
-    if (commands_ac) {
-        autocomplete_add(commands_ac, value);
-    }
-}
-
-void
-cmd_help_autocomplete_add(const char *const value)
-{
-    if (help_ac) {
-        autocomplete_add(help_ac, value);
-    }
-}
-
-void
-cmd_autocomplete_add_form_fields(DataForm *form)
-{
-    if (form == NULL) {
-        return;
-    }
-
-    GSList *fields = autocomplete_create_list(form->tag_ac);
-    GSList *curr_field = fields;
-    while (curr_field) {
-        GString *field_str = g_string_new("/");
-        g_string_append(field_str, curr_field->data);
-        cmd_autocomplete_add(field_str->str);
-        g_string_free(field_str, TRUE);
-        curr_field = g_slist_next(curr_field);
-    }
-    g_slist_free_full(fields, free);
-}
-
-void
-cmd_autocomplete_remove_form_fields(DataForm *form)
-{
-    if (form == NULL) {
-        return;
-    }
-
-    GSList *fields = autocomplete_create_list(form->tag_ac);
-    GSList *curr_field = fields;
-    while (curr_field) {
-        GString *field_str = g_string_new("/");
-        g_string_append(field_str, curr_field->data);
-        cmd_autocomplete_remove(field_str->str);
-        g_string_free(field_str, TRUE);
-        curr_field = g_slist_next(curr_field);
-    }
-    g_slist_free_full(fields, free);
-}
-
-void
-cmd_autocomplete_remove(const char *const value)
-{
-    if (commands_ac) {
-        autocomplete_remove(commands_ac, value);
-    }
-}
-
-void
-cmd_alias_add(char *value)
-{
-    if (aliases_ac) {
-        autocomplete_add(aliases_ac, value);
-    }
-}
-
-void
-cmd_alias_remove(char *value)
-{
-    if (aliases_ac) {
-        autocomplete_remove(aliases_ac, value);
-    }
-}
-
-// Command autocompletion functions
-char*
-cmd_autocomplete(ProfWin *window, const char *const input)
-{
-    // autocomplete command
-    if ((strncmp(input, "/", 1) == 0) && (!str_contains(input, strlen(input), ' '))) {
-        char *found = NULL;
-        found = autocomplete_complete(commands_ac, input, TRUE);
-        if (found) {
-            return found;
-        }
-
-    // autocomplete parameters
-    } else {
-        char *found = _cmd_complete_parameters(window, input);
-        if (found) {
-            return found;
-        }
-    }
-
-    return NULL;
-}
-
-void
-cmd_reset_autocomplete(ProfWin *window)
-{
-    jabber_conn_status_t conn_status = connection_get_status();
-    if (conn_status == JABBER_CONNECTED) {
-        roster_reset_search_attempts();
-
-        if (window->type == WIN_CHAT) {
-            ProfChatWin *chatwin = (ProfChatWin*)window;
-            assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
-            PContact contact = roster_get_contact(chatwin->barejid);
-            if (contact) {
-                p_contact_resource_ac_reset(contact);
-            }
-        }
-    }
-
-    muc_invites_reset_ac();
-    accounts_reset_all_search();
-    accounts_reset_enabled_search();
-    tlscerts_reset_ac();
-    prefs_reset_boolean_choice();
-    presence_reset_sub_request_search();
-#ifdef HAVE_LIBGPGME
-    p_gpg_autocomplete_key_reset();
-#endif
-    autocomplete_reset(help_ac);
-    autocomplete_reset(help_commands_ac);
-    autocomplete_reset(notify_ac);
-    autocomplete_reset(notify_chat_ac);
-    autocomplete_reset(notify_room_ac);
-    autocomplete_reset(notify_typing_ac);
-    autocomplete_reset(notify_mention_ac);
-    autocomplete_reset(notify_trigger_ac);
-    autocomplete_reset(sub_ac);
-    autocomplete_reset(sendfile_ac);
-
-    autocomplete_reset(who_room_ac);
-    autocomplete_reset(who_roster_ac);
-    autocomplete_reset(prefs_ac);
-    autocomplete_reset(log_ac);
-    autocomplete_reset(commands_ac);
-    autocomplete_reset(autoaway_ac);
-    autocomplete_reset(autoaway_mode_ac);
-    autocomplete_reset(autoaway_presence_ac);
-    autocomplete_reset(autoconnect_ac);
-    autocomplete_reset(theme_ac);
-    if (theme_load_ac) {
-        autocomplete_free(theme_load_ac);
-        theme_load_ac = NULL;
-    }
-    if (plugins_load_ac) {
-        autocomplete_free(plugins_load_ac);
-        plugins_load_ac = NULL;
-    }
-    autocomplete_reset(account_ac);
-    autocomplete_reset(account_set_ac);
-    autocomplete_reset(account_clear_ac);
-    autocomplete_reset(account_default_ac);
-    autocomplete_reset(account_status_ac);
-    autocomplete_reset(disco_ac);
-    autocomplete_reset(wins_ac);
-    autocomplete_reset(roster_ac);
-    autocomplete_reset(roster_header_ac);
-    autocomplete_reset(roster_contact_ac);
-    autocomplete_reset(roster_resource_ac);
-    autocomplete_reset(roster_presence_ac);
-    autocomplete_reset(roster_char_ac);
-    autocomplete_reset(roster_show_ac);
-    autocomplete_reset(roster_by_ac);
-    autocomplete_reset(roster_count_ac);
-    autocomplete_reset(roster_order_ac);
-    autocomplete_reset(roster_room_ac);
-    autocomplete_reset(roster_room_by_ac);
-    autocomplete_reset(roster_unread_ac);
-    autocomplete_reset(roster_room_position_ac);
-    autocomplete_reset(roster_room_order_ac);
-    autocomplete_reset(roster_remove_all_ac);
-    autocomplete_reset(roster_private_ac);
-    autocomplete_reset(group_ac);
-    autocomplete_reset(titlebar_ac);
-    autocomplete_reset(bookmark_ac);
-    autocomplete_reset(bookmark_property_ac);
-    autocomplete_reset(otr_ac);
-    autocomplete_reset(otr_log_ac);
-    autocomplete_reset(otr_policy_ac);
-    autocomplete_reset(connect_property_ac);
-    autocomplete_reset(tls_property_ac);
-    autocomplete_reset(statuses_ac);
-    autocomplete_reset(statuses_setting_ac);
-    autocomplete_reset(alias_ac);
-    autocomplete_reset(aliases_ac);
-    autocomplete_reset(join_property_ac);
-    autocomplete_reset(room_ac);
-    autocomplete_reset(affiliation_ac);
-    autocomplete_reset(role_ac);
-    autocomplete_reset(privilege_cmd_ac);
-    autocomplete_reset(subject_ac);
-    autocomplete_reset(form_ac);
-    autocomplete_reset(form_field_multi_ac);
-    autocomplete_reset(occupants_ac);
-    autocomplete_reset(occupants_default_ac);
-    autocomplete_reset(occupants_show_ac);
-    autocomplete_reset(time_ac);
-    autocomplete_reset(time_format_ac);
-    autocomplete_reset(resource_ac);
-    autocomplete_reset(inpblock_ac);
-    autocomplete_reset(receipts_ac);
-    autocomplete_reset(pgp_ac);
-    autocomplete_reset(pgp_log_ac);
-    autocomplete_reset(tls_ac);
-    autocomplete_reset(tls_certpath_ac);
-    autocomplete_reset(console_ac);
-    autocomplete_reset(console_msg_ac);
-    autocomplete_reset(autoping_ac);
-    autocomplete_reset(plugins_ac);
-    autocomplete_reset(blocked_ac);
-    autocomplete_reset(tray_ac);
-    autocomplete_reset(script_ac);
-    if (script_show_ac) {
-        autocomplete_free(script_show_ac);
-        script_show_ac = NULL;
-    }
-
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        muc_autocomplete_reset(mucwin->roomjid);
-        muc_jid_autocomplete_reset(mucwin->roomjid);
-    }
-
-    if (window->type == WIN_MUC_CONFIG) {
-        ProfMucConfWin *confwin = (ProfMucConfWin*)window;
-        assert(confwin->memcheck == PROFCONFWIN_MEMCHECK);
-        if (confwin->form) {
-            form_reset_autocompleters(confwin->form);
-        }
-    }
-
-    bookmark_autocomplete_reset();
-    blocked_ac_reset();
-    prefs_reset_room_trigger_ac();
-    win_reset_search_attempts();
-    win_close_reset_search_attempts();
-    plugins_reset_autocomplete();
-}
-
-gboolean
-cmd_valid_tag(const char *const str)
-{
-    return ((g_strcmp0(str, CMD_TAG_CHAT) == 0) ||
-        (g_strcmp0(str, CMD_TAG_GROUPCHAT) == 0) ||
-        (g_strcmp0(str, CMD_TAG_PRESENCE) == 0) ||
-        (g_strcmp0(str, CMD_TAG_ROSTER) == 0) ||
-        (g_strcmp0(str, CMD_TAG_DISCOVERY) == 0) ||
-        (g_strcmp0(str, CMD_TAG_CONNECTION) == 0) ||
-        (g_strcmp0(str, CMD_TAG_UI) == 0) ||
-        (g_strcmp0(str, CMD_TAG_PLUGINS) == 0));
-}
-
-gboolean
-cmd_has_tag(Command *pcmd, const char *const tag)
-{
-    int i = 0;
-    for (i = 0; pcmd->help.tags[i] != NULL; i++) {
-        if (g_strcmp0(tag, pcmd->help.tags[i]) == 0) {
-            return TRUE;
-        }
-    }
-
-    return FALSE;
-}
-
-/*
- * Take a line of input and process it, return TRUE if profanity is to
- * continue, FALSE otherwise
- */
-gboolean
-cmd_process_input(ProfWin *window, char *inp)
-{
-    log_debug("Input received: %s", inp);
-    gboolean result = FALSE;
-    g_strchomp(inp);
-
-    // just carry on if no input
-    if (strlen(inp) == 0) {
-        result = TRUE;
-
-    // handle command if input starts with a '/'
-    } else if (inp[0] == '/') {
-        char *inp_cpy = strdup(inp);
-        char *command = strtok(inp_cpy, " ");
-        char *question_mark = strchr(command, '?');
-        if (question_mark) {
-            *question_mark = '\0';
-            char *fakeinp;
-            if (asprintf(&fakeinp, "/help %s", command+1)) {
-                result = _cmd_execute(window, "/help", fakeinp);
-                free(fakeinp);
-            }
-        } else {
-            result = _cmd_execute(window, command, inp);
-        }
-        free(inp_cpy);
-
-    // call a default handler if input didn't start with '/'
-    } else {
-        result = cmd_execute_default(window, inp);
-    }
-
-    return result;
-}
-
-// Command execution
-
-void
-cmd_execute_connect(ProfWin *window, const char *const account)
-{
-    GString *command = g_string_new("/connect ");
-    g_string_append(command, account);
-    cmd_process_input(window, command->str);
-    g_string_free(command, TRUE);
-}
-
-static gboolean
-_cmd_execute(ProfWin *window, const char *const command, const char *const inp)
-{
-    if (g_str_has_prefix(command, "/field") && window->type == WIN_MUC_CONFIG) {
-        gboolean result = FALSE;
-        gchar **args = parse_args_with_freetext(inp, 1, 2, &result);
-        if (!result) {
-            ui_current_print_formatted_line('!', 0, "Invalid command, see /form help");
-            result = TRUE;
-        } else {
-            gchar **tokens = g_strsplit(inp, " ", 2);
-            char *field = tokens[0] + 1;
-            result = cmd_form_field(window, field, args);
-            g_strfreev(tokens);
-        }
-
-        g_strfreev(args);
-        return result;
-    }
-
-    Command *cmd = g_hash_table_lookup(commands, command);
-    gboolean result = FALSE;
-
-    if (cmd) {
-        gchar **args = cmd->parser(inp, cmd->min_args, cmd->max_args, &result);
-        if (result == FALSE) {
-            ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
-            return TRUE;
-        }
-        if (args[0] && cmd->sub_funcs[0][0]) {
-            int i = 0;
-            while (cmd->sub_funcs[i][0]) {
-                if (g_strcmp0(args[0], (char*)cmd->sub_funcs[i][0]) == 0) {
-                    gboolean (*func)(ProfWin *window, const char *const command, gchar **args) = cmd->sub_funcs[i][1];
-                    gboolean result = func(window, command, args);
-                    g_strfreev(args);
-                    return result;
-                }
-                i++;
-            }
-        }
-        if (!cmd->func) {
-            ui_invalid_command_usage(cmd->cmd, cmd->setting_func);
-            return TRUE;
-        }
-        gboolean result = cmd->func(window, command, args);
-        g_strfreev(args);
-        return result;
-    } else if (plugins_run_command(inp)) {
-        return TRUE;
-    } else {
-        gboolean ran_alias = FALSE;
-        gboolean alias_result = cmd_execute_alias(window, inp, &ran_alias);
-        if (!ran_alias) {
-            return cmd_execute_default(window, inp);
-        } else {
-            return alias_result;
-        }
-    }
-}
-
-static char*
-_cmd_complete_parameters(ProfWin *window, const char *const input)
-{
-    int i;
-    char *result = NULL;
-
-    jabber_conn_status_t conn_status = connection_get_status();
-
-    // autocomplete boolean settings
-    gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype", "/flash", "/splash", "/chlog", "/grlog",
-        "/history", "/vercheck", "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn",
-        "/lastactivity" };
-
-    for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
-        result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice);
-        if (result) {
-            return result;
-        }
-    }
-
-    // autocomplete nickname in chat rooms
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
-        if (nick_ac) {
-            gchar *nick_choices[] = { "/msg", "/info", "/caps", "/status", "/software" } ;
-
-            // Remove quote character before and after names when doing autocomplete
-            char *unquoted = strip_arg_quotes(input);
-            for (i = 0; i < ARRAY_SIZE(nick_choices); i++) {
-                result = autocomplete_param_with_ac(unquoted, nick_choices[i], nick_ac, TRUE);
-                if (result) {
-                    free(unquoted);
-                    return result;
-                }
-            }
-            free(unquoted);
-        }
-
-    // otherwise autocomplete using roster
-    } else if (conn_status == JABBER_CONNECTED) {
-        gchar *contact_choices[] = { "/msg", "/info", "/status" };
-        // Remove quote character before and after names when doing autocomplete
-        char *unquoted = strip_arg_quotes(input);
-        for (i = 0; i < ARRAY_SIZE(contact_choices); i++) {
-            result = autocomplete_param_with_func(unquoted, contact_choices[i], roster_contact_autocomplete);
-            if (result) {
-                free(unquoted);
-                return result;
-            }
-        }
-        free(unquoted);
-
-        gchar *resource_choices[] = { "/caps", "/software", "/ping" };
-        for (i = 0; i < ARRAY_SIZE(resource_choices); i++) {
-            result = autocomplete_param_with_func(input, resource_choices[i], roster_fulljid_autocomplete);
-            if (result) {
-                return result;
-            }
-        }
-    }
-
-    if (conn_status == JABBER_CONNECTED) {
-        result = autocomplete_param_with_func(input, "/invite", roster_contact_autocomplete);
-        if (result) {
-            return result;
-        }
-    }
-
-    gchar *invite_choices[] = { "/decline", "/join" };
-    for (i = 0; i < ARRAY_SIZE(invite_choices); i++) {
-        result = autocomplete_param_with_func(input, invite_choices[i], muc_invites_find);
-        if (result) {
-            return result;
-        }
-    }
-
-    gchar *cmds[] = { "/prefs", "/disco", "/room", "/autoping" };
-    Autocomplete completers[] = { prefs_ac, disco_ac, room_ac, autoping_ac };
-
-    for (i = 0; i < ARRAY_SIZE(cmds); i++) {
-        result = autocomplete_param_with_ac(input, cmds[i], completers[i], TRUE);
-        if (result) {
-            return result;
-        }
-    }
-
-    GHashTable *ac_funcs = g_hash_table_new(g_str_hash, g_str_equal);
-    g_hash_table_insert(ac_funcs, "/help",          _help_autocomplete);
-    g_hash_table_insert(ac_funcs, "/who",           _who_autocomplete);
-    g_hash_table_insert(ac_funcs, "/sub",           _sub_autocomplete);
-    g_hash_table_insert(ac_funcs, "/notify",        _notify_autocomplete);
-    g_hash_table_insert(ac_funcs, "/autoaway",      _autoaway_autocomplete);
-    g_hash_table_insert(ac_funcs, "/theme",         _theme_autocomplete);
-    g_hash_table_insert(ac_funcs, "/log",           _log_autocomplete);
-    g_hash_table_insert(ac_funcs, "/account",       _account_autocomplete);
-    g_hash_table_insert(ac_funcs, "/roster",        _roster_autocomplete);
-    g_hash_table_insert(ac_funcs, "/group",         _group_autocomplete);
-    g_hash_table_insert(ac_funcs, "/bookmark",      _bookmark_autocomplete);
-    g_hash_table_insert(ac_funcs, "/autoconnect",   _autoconnect_autocomplete);
-    g_hash_table_insert(ac_funcs, "/otr",           _otr_autocomplete);
-    g_hash_table_insert(ac_funcs, "/pgp",           _pgp_autocomplete);
-    g_hash_table_insert(ac_funcs, "/connect",       _connect_autocomplete);
-    g_hash_table_insert(ac_funcs, "/statuses",      _statuses_autocomplete);
-    g_hash_table_insert(ac_funcs, "/alias",         _alias_autocomplete);
-    g_hash_table_insert(ac_funcs, "/join",          _join_autocomplete);
-    g_hash_table_insert(ac_funcs, "/form",          _form_autocomplete);
-    g_hash_table_insert(ac_funcs, "/occupants",     _occupants_autocomplete);
-    g_hash_table_insert(ac_funcs, "/kick",          _kick_autocomplete);
-    g_hash_table_insert(ac_funcs, "/ban",           _ban_autocomplete);
-    g_hash_table_insert(ac_funcs, "/affiliation",   _affiliation_autocomplete);
-    g_hash_table_insert(ac_funcs, "/role",          _role_autocomplete);
-    g_hash_table_insert(ac_funcs, "/resource",      _resource_autocomplete);
-    g_hash_table_insert(ac_funcs, "/titlebar",      _titlebar_autocomplete);
-    g_hash_table_insert(ac_funcs, "/inpblock",      _inpblock_autocomplete);
-    g_hash_table_insert(ac_funcs, "/time",          _time_autocomplete);
-    g_hash_table_insert(ac_funcs, "/receipts",      _receipts_autocomplete);
-    g_hash_table_insert(ac_funcs, "/wins",          _wins_autocomplete);
-    g_hash_table_insert(ac_funcs, "/tls",           _tls_autocomplete);
-    g_hash_table_insert(ac_funcs, "/script",        _script_autocomplete);
-    g_hash_table_insert(ac_funcs, "/subject",       _subject_autocomplete);
-    g_hash_table_insert(ac_funcs, "/console",       _console_autocomplete);
-    g_hash_table_insert(ac_funcs, "/win",           _win_autocomplete);
-    g_hash_table_insert(ac_funcs, "/close",         _close_autocomplete);
-    g_hash_table_insert(ac_funcs, "/plugins",       _plugins_autocomplete);
-    g_hash_table_insert(ac_funcs, "/sendfile",      _sendfile_autocomplete);
-    g_hash_table_insert(ac_funcs, "/blocked",       _blocked_autocomplete);
-    g_hash_table_insert(ac_funcs, "/tray",          _tray_autocomplete);
-
-    int len = strlen(input);
-    char parsed[len+1];
-    i = 0;
-    while (i < len) {
-        if (input[i] == ' ') {
-            break;
-        } else {
-            parsed[i] = input[i];
-        }
-        i++;
-    }
-    parsed[i] = '\0';
-
-    char * (*ac_func)(ProfWin*, const char * const) = g_hash_table_lookup(ac_funcs, parsed);
-    if (ac_func) {
-        result = ac_func(window, input);
-        if (result) {
-            g_hash_table_destroy(ac_funcs);
-            return result;
-        }
-    }
-    g_hash_table_destroy(ac_funcs);
-
-    result = plugins_autocomplete(input);
-    if (result) {
-        return result;
-    }
-
-    if (g_str_has_prefix(input, "/field")) {
-        result = _form_field_autocomplete(window, input);
-        if (result) {
-            return result;
-        }
-    }
-
-    return NULL;
-}
-
-static char*
-_sub_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    result = autocomplete_param_with_func(input, "/sub allow", presence_sub_request_find);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_func(input, "/sub deny", presence_sub_request_find);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/sub", sub_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_tray_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    result = autocomplete_param_with_func(input, "/tray read", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/tray", tray_ac, FALSE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_who_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        result = autocomplete_param_with_ac(input, "/who", who_room_ac, TRUE);
-        if (result) {
-            return result;
-        }
-    } else {
-        jabber_conn_status_t conn_status = connection_get_status();
-        if (conn_status == JABBER_CONNECTED) {
-            int i = 0;
-            gchar *group_commands[] = { "/who any", "/who online", "/who offline",
-                "/who chat", "/who away", "/who xa", "/who dnd", "/who available",
-                "/who unavailable" };
-
-            for (i = 0; i < ARRAY_SIZE(group_commands); i++) {
-                result = autocomplete_param_with_func(input, group_commands[i], roster_group_autocomplete);
-                if (result) {
-                    return result;
-                }
-            }
-        }
-
-        result = autocomplete_param_with_ac(input, "/who", who_roster_ac, TRUE);
-        if (result) {
-            return result;
-        }
-    }
-
-    return result;
-}
-
-static char*
-_roster_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    result = autocomplete_param_with_ac(input, "/roster room private char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room private", roster_header_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster header char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster contact char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster private char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster resource char", roster_char_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_func(input, "/roster resource join", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room position", roster_room_position_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room by", roster_room_by_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room order", roster_room_order_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room unread", roster_unread_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_func(input, "/roster count zero", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    jabber_conn_status_t conn_status = connection_get_status();
-    if (conn_status == JABBER_CONNECTED) {
-        result = autocomplete_param_with_func(input, "/roster nick", roster_barejid_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_with_func(input, "/roster clearnick", roster_barejid_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_with_func(input, "/roster remove", roster_barejid_autocomplete);
-        if (result) {
-            return result;
-        }
-    }
-
-    result = autocomplete_param_with_ac(input, "/roster remove_all", roster_remove_all_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster show", roster_show_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster hide", roster_show_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster by", roster_by_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster count", roster_count_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster order", roster_order_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster unread", roster_unread_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster room", roster_room_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_func(input, "/roster wrap", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster header", roster_header_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster contact", roster_contact_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster resource", roster_resource_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster presence", roster_presence_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster private", roster_private_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/roster", roster_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_group_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    jabber_conn_status_t conn_status = connection_get_status();
-
-    if (conn_status == JABBER_CONNECTED) {
-        result = autocomplete_param_with_func(input, "/group show", roster_group_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_no_with_func(input, "/group add", 4, roster_contact_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_no_with_func(input, "/group remove", 4, roster_contact_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_with_func(input, "/group add", roster_group_autocomplete);
-        if (result) {
-            return result;
-        }
-        result = autocomplete_param_with_func(input, "/group remove", roster_group_autocomplete);
-        if (result) {
-            return result;
-        }
-    }
-
-    result = autocomplete_param_with_ac(input, "/group", group_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_blocked_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/blocked remove", blocked_ac_find);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/blocked", blocked_ac, FALSE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-
-static char*
-_bookmark_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    gboolean result;
-    gchar **args = parse_args(input, 3, 8, &result);
-    gboolean handle_options = result && (g_strv_length(args) > 2);
-
-    if (handle_options && ((strcmp(args[0], "add") == 0) || (strcmp(args[0], "update") == 0)) ) {
-        GString *beginning = g_string_new("/bookmark");
-        gboolean autojoin = FALSE;
-        int num_args = g_strv_length(args);
-
-        g_string_append(beginning, " ");
-        g_string_append(beginning, args[0]);
-        g_string_append(beginning, " ");
-        g_string_append(beginning, args[1]);
-        if (num_args == 4 && g_strcmp0(args[2], "autojoin") == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            autojoin = TRUE;
-        }
-
-        if (num_args > 4) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[3]);
-            if (num_args == 6 && g_strcmp0(args[4], "autojoin") == 0) {
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[4]);
-                autojoin = TRUE;
-            }
-        }
-
-        if (num_args > 6) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[4]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[5]);
-            if (num_args == 8 && g_strcmp0(args[6], "autojoin") == 0) {
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[6]);
-                autojoin = TRUE;
-            }
-        }
-
-        if (autojoin) {
-            found = autocomplete_param_with_func(input, beginning->str, prefs_autocomplete_boolean_choice);
-        } else {
-            found = autocomplete_param_with_ac(input, beginning->str, bookmark_property_ac, TRUE);
-        }
-        g_string_free(beginning, TRUE);
-        if (found) {
-            g_strfreev(args);
-            return found;
-        }
-    }
-
-    g_strfreev(args);
-
-    found = autocomplete_param_with_func(input, "/bookmark remove", bookmark_find);
-    if (found) {
-        return found;
-    }
-    found = autocomplete_param_with_func(input, "/bookmark join", bookmark_find);
-    if (found) {
-        return found;
-    }
-    found = autocomplete_param_with_func(input, "/bookmark update", bookmark_find);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/bookmark", bookmark_ac, TRUE);
-    return found;
-}
-
-static char*
-_notify_autocomplete(ProfWin *window, const char *const input)
-{
-    int i = 0;
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/notify room trigger remove", prefs_autocomplete_room_trigger);
-    if (result) {
-        return result;
-    }
-
-    gchar *boolean_choices1[] = { "/notify room current", "/notify chat current", "/notify typing current",
-        "/notify room text", "/notify chat text" };
-    for (i = 0; i < ARRAY_SIZE(boolean_choices1); i++) {
-        result = autocomplete_param_with_func(input, boolean_choices1[i], prefs_autocomplete_boolean_choice);
-        if (result) {
-            return result;
-        }
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify room mention", notify_mention_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify room trigger", notify_trigger_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify room", notify_room_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify chat", notify_chat_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify typing", notify_typing_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    gchar *boolean_choices2[] = { "/notify invite", "/notify sub", "/notify mention", "/notify trigger"};
-    for (i = 0; i < ARRAY_SIZE(boolean_choices2); i++) {
-        result = autocomplete_param_with_func(input, boolean_choices2[i], prefs_autocomplete_boolean_choice);
-        if (result) {
-            return result;
-        }
-    }
-
-    result = autocomplete_param_with_ac(input, "/notify", notify_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_autoaway_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/autoaway mode", autoaway_mode_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/autoaway time", autoaway_presence_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/autoaway message", autoaway_presence_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_func(input, "/autoaway check", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/autoaway", autoaway_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_log_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/log rotate",
-        prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_func(input, "/log shared",
-        prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/log", log_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_autoconnect_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/autoconnect set", accounts_find_enabled);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/autoconnect", autoconnect_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_otr_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    jabber_conn_status_t conn_status = connection_get_status();
-
-    if (conn_status == JABBER_CONNECTED) {
-        found = autocomplete_param_with_func(input, "/otr start", roster_contact_autocomplete);
-        if (found) {
-            return found;
-        }
-    }
-
-    found = autocomplete_param_with_ac(input, "/otr log", otr_log_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    // /otr policy always user@server.com
-    if (conn_status == JABBER_CONNECTED) {
-        gboolean result;
-        gchar **args = parse_args(input, 3, 3, &result);
-        if (result && (strcmp(args[0], "policy") == 0)) {
-            GString *beginning = g_string_new("/otr ");
-            g_string_append(beginning, args[0]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-
-            found = autocomplete_param_with_func(input, beginning->str, roster_contact_autocomplete);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        }
-        g_strfreev(args);
-    }
-
-    found = autocomplete_param_with_ac(input, "/otr policy", otr_policy_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/otr", otr_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_pgp_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    jabber_conn_status_t conn_status = connection_get_status();
-
-    if (conn_status == JABBER_CONNECTED) {
-        found = autocomplete_param_with_func(input, "/pgp start", roster_contact_autocomplete);
-        if (found) {
-            return found;
-        }
-    }
-
-    found = autocomplete_param_with_ac(input, "/pgp log", pgp_log_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-#ifdef HAVE_LIBGPGME
-    gboolean result;
-    gchar **args = parse_args(input, 2, 3, &result);
-    if ((strncmp(input, "/pgp", 4) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/pgp ");
-        g_string_append(beginning, args[0]);
-        if (args[1]) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-        }
-        found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key);
-        g_string_free(beginning, TRUE);
-        if (found) {
-            g_strfreev(args);
-            return found;
-        }
-    }
-    g_strfreev(args);
-#endif
-
-    if (conn_status == JABBER_CONNECTED) {
-        found = autocomplete_param_with_func(input, "/pgp setkey", roster_barejid_autocomplete);
-        if (found) {
-            return found;
-        }
-    }
-
-    found = autocomplete_param_with_ac(input, "/pgp", pgp_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_plugins_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    if ((strncmp(input, "/plugins load ", 14) == 0) && (strlen(input) > 14)) {
-        if (plugins_load_ac == NULL) {
-            plugins_load_ac = autocomplete_new();
-            GSList *plugins = plugins_unloaded_list();
-            GSList *curr = plugins;
-            while (curr) {
-                autocomplete_add(plugins_load_ac, curr->data);
-                curr = g_slist_next(curr);
-            }
-            g_slist_free_full(plugins, g_free);
-        }
-        result = autocomplete_param_with_ac(input, "/plugins load", plugins_load_ac, TRUE);
-        if (result) {
-            return result;
-        }
-    }
-    result = autocomplete_param_with_ac(input, "/plugins", plugins_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_theme_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    if ((strncmp(input, "/theme load ", 12) == 0) && (strlen(input) > 12)) {
-        if (theme_load_ac == NULL) {
-            theme_load_ac = autocomplete_new();
-            GSList *themes = theme_list();
-            GSList *curr = themes;
-            while (curr) {
-                autocomplete_add(theme_load_ac, curr->data);
-                curr = g_slist_next(curr);
-            }
-            g_slist_free_full(themes, g_free);
-            autocomplete_add(theme_load_ac, "default");
-        }
-        result = autocomplete_param_with_ac(input, "/theme load", theme_load_ac, TRUE);
-        if (result) {
-            return result;
-        }
-    }
-    result = autocomplete_param_with_ac(input, "/theme", theme_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_script_autocomplete_func(const char *const prefix)
-{
-    if (script_show_ac == NULL) {
-        script_show_ac = autocomplete_new();
-        GSList *scripts = scripts_list();
-        GSList *curr = scripts;
-        while (curr) {
-            autocomplete_add(script_show_ac, curr->data);
-            curr = g_slist_next(curr);
-        }
-        g_slist_free_full(scripts, g_free);
-    }
-
-    return autocomplete_complete(script_show_ac, prefix, FALSE);
-}
-
-
-static char*
-_script_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-    if ((strncmp(input, "/script show ", 13) == 0) && (strlen(input) > 13)) {
-        result = autocomplete_param_with_func(input, "/script show", _script_autocomplete_func);
-        if (result) {
-            return result;
-        }
-    }
-
-    if ((strncmp(input, "/script run ", 12) == 0) && (strlen(input) > 12)) {
-        result = autocomplete_param_with_func(input, "/script run", _script_autocomplete_func);
-        if (result) {
-            return result;
-        }
-    }
-
-    result = autocomplete_param_with_ac(input, "/script", script_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_resource_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    jabber_conn_status_t conn_status = connection_get_status();
-    if (conn_status == JABBER_CONNECTED && window->type == WIN_CHAT) {
-        ProfChatWin *chatwin = (ProfChatWin*)window;
-        assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
-        PContact contact = roster_get_contact(chatwin->barejid);
-        if (contact) {
-            Autocomplete ac = p_contact_resource_ac(contact);
-            found = autocomplete_param_with_ac(input, "/resource set", ac, FALSE);
-            if (found) {
-                return found;
-            }
-        }
-    }
-
-    found = autocomplete_param_with_func(input, "/resource title", prefs_autocomplete_boolean_choice);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_func(input, "/resource message", prefs_autocomplete_boolean_choice);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/resource", resource_ac, FALSE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_titlebar_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_func(input, "/titlebar show", prefs_autocomplete_boolean_choice);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_func(input, "/titlebar goodbye", prefs_autocomplete_boolean_choice);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/titlebar", titlebar_ac, FALSE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_inpblock_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_func(input, "/inpblock dynamic", prefs_autocomplete_boolean_choice);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/inpblock", inpblock_ac, FALSE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_form_autocomplete(ProfWin *window, const char *const input)
-{
-    if (window->type != WIN_MUC_CONFIG) {
-        return NULL;
-    }
-
-    char *found = NULL;
-
-    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
-    DataForm *form = confwin->form;
-    if (form) {
-        found = autocomplete_param_with_ac(input, "/form help", form->tag_ac, TRUE);
-        if (found) {
-            return found;
-        }
-    }
-
-    found = autocomplete_param_with_ac(input, "/form", form_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_form_field_autocomplete(ProfWin *window, const char *const input)
-{
-    if (window->type != WIN_MUC_CONFIG) {
-        return NULL;
-    }
-
-    char *found = NULL;
-
-    ProfMucConfWin *confwin = (ProfMucConfWin*)window;
-    DataForm *form = confwin->form;
-    if (form == NULL) {
-        return NULL;
-    }
-
-    gchar **split = g_strsplit(input, " ", 0);
-
-    if (g_strv_length(split) == 3) {
-        char *field_tag = split[0]+1;
-        if (form_tag_exists(form, field_tag)) {
-            form_field_type_t field_type = form_get_field_type(form, field_tag);
-            Autocomplete value_ac = form_get_value_ac(form, field_tag);;
-            GString *beginning = g_string_new(split[0]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, split[1]);
-
-            if (((g_strcmp0(split[1], "add") == 0) || (g_strcmp0(split[1], "remove") == 0))
-                    && field_type == FIELD_LIST_MULTI) {
-                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
-
-            } else if ((g_strcmp0(split[1], "remove") == 0) && field_type == FIELD_TEXT_MULTI) {
-                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
-
-            } else if ((g_strcmp0(split[1], "remove") == 0) && field_type == FIELD_JID_MULTI) {
-                found = autocomplete_param_with_ac(input, beginning->str, value_ac, TRUE);
-            }
-
-            g_string_free(beginning, TRUE);
-        }
-
-    } else if (g_strv_length(split) == 2) {
-        char *field_tag = split[0]+1;
-        if (form_tag_exists(form, field_tag)) {
-            form_field_type_t field_type = form_get_field_type(form, field_tag);
-            Autocomplete value_ac = form_get_value_ac(form, field_tag);;
-
-            switch (field_type)
-            {
-                case FIELD_BOOLEAN:
-                    found = autocomplete_param_with_func(input, split[0], prefs_autocomplete_boolean_choice);
-                    break;
-                case FIELD_LIST_SINGLE:
-                    found = autocomplete_param_with_ac(input, split[0], value_ac, TRUE);
-                    break;
-                case FIELD_LIST_MULTI:
-                case FIELD_JID_MULTI:
-                case FIELD_TEXT_MULTI:
-                    found = autocomplete_param_with_ac(input, split[0], form_field_multi_ac, TRUE);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    g_strfreev(split);
-
-    return found;
-}
-
-static char*
-_occupants_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_ac(input, "/occupants default show", occupants_show_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/occupants default hide", occupants_show_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/occupants default", occupants_default_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/occupants show", occupants_show_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/occupants hide", occupants_show_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/occupants", occupants_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_time_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_ac(input, "/time statusbar", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time lastactivity", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time console", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time chat", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time muc", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time mucconfig", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time private", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time xml", time_format_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    found = autocomplete_param_with_ac(input, "/time", time_ac, TRUE);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_kick_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
-
-        if (nick_ac) {
-            result = autocomplete_param_with_ac(input, "/kick", nick_ac, TRUE);
-            if (result) {
-                return result;
-            }
-        }
-    }
-
-    return result;
-}
-
-static char*
-_ban_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
-
-        if (jid_ac) {
-            result = autocomplete_param_with_ac(input, "/ban", jid_ac, TRUE);
-            if (result) {
-                return result;
-            }
-        }
-    }
-
-    return result;
-}
-
-static char*
-_affiliation_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        gboolean parse_result;
-        Autocomplete jid_ac = muc_roster_jid_ac(mucwin->roomjid);
-
-        gchar **args = parse_args(input, 3, 3, &parse_result);
-
-        if ((strncmp(input, "/affiliation", 12) == 0) && (parse_result == TRUE)) {
-            GString *beginning = g_string_new("/affiliation ");
-            g_string_append(beginning, args[0]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-
-            result = autocomplete_param_with_ac(input, beginning->str, jid_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (result) {
-                g_strfreev(args);
-                return result;
-            }
-        }
-
-        g_strfreev(args);
-    }
-
-    result = autocomplete_param_with_ac(input, "/affiliation set", affiliation_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/affiliation list", affiliation_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/affiliation", privilege_cmd_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return result;
-}
-
-static char*
-_role_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        ProfMucWin *mucwin = (ProfMucWin*)window;
-        assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-        gboolean parse_result;
-        Autocomplete nick_ac = muc_roster_ac(mucwin->roomjid);
-
-        gchar **args = parse_args(input, 3, 3, &parse_result);
-
-        if ((strncmp(input, "/role", 5) == 0) && (parse_result == TRUE)) {
-            GString *beginning = g_string_new("/role ");
-            g_string_append(beginning, args[0]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-
-            result = autocomplete_param_with_ac(input, beginning->str, nick_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (result) {
-                g_strfreev(args);
-                return result;
-            }
-        }
-
-        g_strfreev(args);
-    }
-
-    result = autocomplete_param_with_ac(input, "/role set", role_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/role list", role_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/role", privilege_cmd_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return result;
-}
-
-static char*
-_statuses_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/statuses console", statuses_setting_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/statuses chat", statuses_setting_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/statuses muc", statuses_setting_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/statuses", statuses_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_wins_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/wins autotidy", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/wins", wins_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_tls_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/tls revoke", tlscerts_complete);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_func(input, "/tls cert", tlscerts_complete);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/tls certpath", tls_certpath_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_func(input, "/tls show", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/tls", tls_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return result;
-}
-
-static char*
-_receipts_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_func(input, "/receipts send", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_func(input, "/receipts request", prefs_autocomplete_boolean_choice);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/receipts", receipts_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_alias_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/alias remove", aliases_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/alias", alias_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_connect_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-    gboolean result = FALSE;
-
-    gchar **args = parse_args(input, 2, 6, &result);
-
-    if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/connect ");
-        g_string_append(beginning, args[0]);
-        if (args[1] && args[2]) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            if (args[3] && args[4]) {
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[3]);
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[4]);
-            }
-        }
-        found = autocomplete_param_with_ac(input, beginning->str, connect_property_ac, TRUE);
-        g_string_free(beginning, TRUE);
-        if (found) {
-            g_strfreev(args);
-            return found;
-        }
-    }
-
-    g_strfreev(args);
-
-    result = FALSE;
-    args = parse_args(input, 2, 7, &result);
-
-    if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/connect ");
-        g_string_append(beginning, args[0]);
-        int curr = 0;
-        if (args[1]) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-            curr = 1;
-            if (args[2] && args[3]) {
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[2]);
-                g_string_append(beginning, " ");
-                g_string_append(beginning, args[3]);
-                curr = 3;
-                if (args[4] && args[5]) {
-                    g_string_append(beginning, " ");
-                    g_string_append(beginning, args[4]);
-                    g_string_append(beginning, " ");
-                    g_string_append(beginning, args[5]);
-                    curr = 5;
-                }
-            }
-        }
-        if (curr != 0 && (g_strcmp0(args[curr], "tls") == 0)) {
-            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        } else {
-            g_string_free(beginning, TRUE);
-        }
-    }
-
-    g_strfreev(args);
-
-    found = autocomplete_param_with_func(input, "/connect", accounts_find_enabled);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_help_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/help commands", help_commands_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/help", help_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_join_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-    gboolean result = FALSE;
-
-    found = autocomplete_param_with_func(input, "/join", bookmark_find);
-    if (found) {
-        return found;
-    }
-
-    gchar **args = parse_args(input, 2, 4, &result);
-
-    if ((strncmp(input, "/join", 5) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/join ");
-        g_string_append(beginning, args[0]);
-        if (args[1] && args[2]) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[1]);
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-        }
-        found = autocomplete_param_with_ac(input, beginning->str, join_property_ac, TRUE);
-        g_string_free(beginning, TRUE);
-        if (found) {
-            g_strfreev(args);
-            return found;
-        }
-    }
-
-    g_strfreev(args);
-
-    return NULL;
-}
-
-static char*
-_console_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/console chat", console_msg_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/console muc", console_msg_ac, TRUE);
-    if (result) {
-        return result;
-    }
-    result = autocomplete_param_with_ac(input, "/console private", console_msg_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/console", console_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_win_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_func(input, "/win", win_autocomplete);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_close_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-
-    found = autocomplete_param_with_func(input, "/close", win_close_autocomplete);
-    if (found) {
-        return found;
-    }
-
-    return NULL;
-}
-
-static char*
-_sendfile_autocomplete(ProfWin *window, const char *const input)
-{
-    static char* last_directory = NULL;
-
-    unsigned int output_off = 0;
-
-    char *result = NULL;
-    char *tmp;
-
-    // strip command
-    char *inpcp = (char*)input + 9;
-    while (*inpcp == ' ') {
-        inpcp++;
-    }
-
-    inpcp = strdup(inpcp);
-
-    // strip quotes
-    if (*inpcp == '"') {
-        tmp = strchr(inpcp+1, '"');
-        if (tmp) {
-            *tmp = '\0';
-        }
-        tmp = strdup(inpcp+1);
-        free(inpcp);
-        inpcp = tmp;
-    }
-
-    // expand ~ to $HOME
-    if (inpcp[0] == '~' && inpcp[1] == '/') {
-        if (asprintf(&tmp, "%s/%sfoo", getenv("HOME"), inpcp+2) == -1) {
-            return NULL;
-        }
-        output_off = strlen(getenv("HOME"))+1;
-    } else {
-        if (asprintf(&tmp, "%sfoo", inpcp) == -1) {
-            return NULL;
-        }
-    }
-    free(inpcp);
-    inpcp = tmp;
-
-    char* inpcp2 = strdup(inpcp);
-    char* foofile = strdup(basename(inpcp2));
-    char* directory = strdup(dirname(inpcp));
-    free(inpcp);
-    free(inpcp2);
-
-    if (!last_directory || strcmp(last_directory, directory) != 0) {
-        free(last_directory);
-        last_directory = directory;
-        autocomplete_reset(sendfile_ac);
-
-        struct dirent *dir;
-
-        DIR *d = opendir(directory);
-        if (d) {
-            while ((dir = readdir(d)) != NULL) {
-                if (strcmp(dir->d_name, ".") == 0) {
-                    continue;
-                } else if (strcmp(dir->d_name, "..") == 0) {
-                    continue;
-                } else if (*(dir->d_name) == '.' && *foofile != '.') {
-                    // only show hidden files on explicit request
-                    continue;
-                }
-                char * acstring;
-                if (output_off) {
-                    if (asprintf(&tmp, "%s/%s", directory, dir->d_name) == -1) {
-                        return NULL;
-                    }
-                    if (asprintf(&acstring, "~/%s", tmp+output_off) == -1) {
-                        return NULL;
-                    }
-                    free(tmp);
-                } else if (strcmp(directory, "/") == 0) {
-                    if (asprintf(&acstring, "/%s", dir->d_name) == -1) {
-                        return NULL;
-                    }
-                } else {
-                    if (asprintf(&acstring, "%s/%s", directory, dir->d_name) == -1) {
-                        return NULL;
-                    }
-                }
-                autocomplete_add(sendfile_ac, acstring);
-                free(acstring);
-            }
-            closedir(d);
-        }
-    } else {
-        free(foofile);
-        free(directory);
-    }
-
-    result = autocomplete_param_with_ac(input, "/sendfile", sendfile_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-
-static char*
-_subject_autocomplete(ProfWin *window, const char *const input)
-{
-    char *result = NULL;
-
-    if (window->type == WIN_MUC) {
-        if ((g_strcmp0(input, "/subject e") == 0)
-                || (g_strcmp0(input, "/subject ed") == 0)
-                || (g_strcmp0(input, "/subject edi") == 0)
-                || (g_strcmp0(input, "/subject edit") == 0)
-                || (g_strcmp0(input, "/subject edit ") == 0)
-                || (g_strcmp0(input, "/subject edit \"") == 0)) {
-            ProfMucWin *mucwin = (ProfMucWin*)window;
-            assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
-
-            char *subject = muc_subject(mucwin->roomjid);
-            if (subject) {
-                GString *result_str = g_string_new("/subject edit \"");
-                g_string_append(result_str, subject);
-                g_string_append(result_str, "\"");
-
-                result = result_str->str;
-                g_string_free(result_str, FALSE);
-            }
-        }
-    }
-    if (result) {
-        return result;
-    }
-
-    result = autocomplete_param_with_ac(input, "/subject", subject_ac, TRUE);
-    if (result) {
-        return result;
-    }
-
-    return NULL;
-}
-
-static char*
-_account_autocomplete(ProfWin *window, const char *const input)
-{
-    char *found = NULL;
-    gboolean result = FALSE;
-
-    gchar **args = parse_args(input, 3, 4, &result);
-
-    if ((strncmp(input, "/account set", 12) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/account set ");
-        g_string_append(beginning, args[1]);
-        if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "otr")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            found = autocomplete_param_with_ac(input, beginning->str, otr_policy_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "status")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            found = autocomplete_param_with_ac(input, beginning->str, account_status_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "tls")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "startscript")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            found = autocomplete_param_with_func(input, beginning->str, _script_autocomplete_func);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "theme")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            if (theme_load_ac == NULL) {
-                theme_load_ac = autocomplete_new();
-                GSList *themes = theme_list();
-                GSList *curr = themes;
-                while (curr) {
-                    autocomplete_add(theme_load_ac, curr->data);
-                    curr = g_slist_next(curr);
-                }
-                g_slist_free_full(themes, g_free);
-                autocomplete_add(theme_load_ac, "default");
-            }
-            found = autocomplete_param_with_ac(input, beginning->str, theme_load_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                return found;
-            }
-#ifdef HAVE_LIBGPGME
-        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "pgpkeyid")) == 0) {
-            g_string_append(beginning, " ");
-            g_string_append(beginning, args[2]);
-            found = autocomplete_param_with_func(input, beginning->str, p_gpg_autocomplete_key);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-#endif
-        } else {
-            found = autocomplete_param_with_ac(input, beginning->str, account_set_ac, TRUE);
-            g_string_free(beginning, TRUE);
-            if (found) {
-                g_strfreev(args);
-                return found;
-            }
-        }
-    }
-
-    if ((strncmp(input, "/account clear", 14) == 0) && (result == TRUE)) {
-        GString *beginning = g_string_new("/account clear ");
-        g_string_append(beginning, args[1]);
-        found = autocomplete_param_with_ac(input, beginning->str, account_clear_ac, TRUE);
-        g_string_free(beginning, TRUE);
-        if (found) {
-            g_strfreev(args);
-            return found;
-        }
-    }
-
-    g_strfreev(args);
-
-    found = autocomplete_param_with_ac(input, "/account default", account_default_ac, TRUE);
-    if(found){
-        return found;
-    }
-
-    int i = 0;
-    gchar *account_choice[] = { "/account set", "/account show", "/account enable",
-        "/account disable", "/account rename", "/account clear", "/account remove",
-        "/account default set" };
-
-    for (i = 0; i < ARRAY_SIZE(account_choice); i++) {
-        found = autocomplete_param_with_func(input, account_choice[i], accounts_find_all);
-        if (found) {
-            return found;
-        }
-    }
-
-    found = autocomplete_param_with_ac(input, "/account", account_ac, TRUE);
-    return found;
-}
-
-static int
-_cmp_command(Command *cmd1, Command *cmd2)
-{
-    return g_strcmp0(cmd1->cmd, cmd2->cmd);
-}
-
-void
-command_docgen(void)
-{
-    GList *cmds = NULL;
-    unsigned int i;
-    for (i = 0; i < ARRAY_SIZE(command_defs); i++) {
-        Command *pcmd = command_defs+i;
-        cmds = g_list_insert_sorted(cmds, pcmd, (GCompareFunc)_cmp_command);
-    }
-
-    FILE *toc_fragment = fopen("toc_fragment.html", "w");
-    FILE *main_fragment = fopen("main_fragment.html", "w");
-
-    fputs("<ul><li><ul><li>\n", toc_fragment);
-    fputs("<hr>\n", main_fragment);
-
-    GList *curr = cmds;
-    while (curr) {
-        Command *pcmd = curr->data;
-
-        fprintf(toc_fragment, "<a href=\"#%s\">%s</a>,\n", &pcmd->cmd[1], pcmd->cmd);
-        fprintf(main_fragment, "<a name=\"%s\"></a>\n", &pcmd->cmd[1]);
-        fprintf(main_fragment, "<h4>%s</h4>\n", pcmd->cmd);
-
-        fputs("<p><b>Synopsis</b></p>\n", main_fragment);
-        fputs("<p><pre><code>", main_fragment);
-        int i = 0;
-        while (pcmd->help.synopsis[i]) {
-            char *str1 = str_replace(pcmd->help.synopsis[i], "<", "&lt;");
-            char *str2 = str_replace(str1, ">", "&gt;");
-            fprintf(main_fragment, "%s\n", str2);
-            i++;
-        }
-        fputs("</code></pre></p>\n", main_fragment);
-
-        fputs("<p><b>Description</b></p>\n", main_fragment);
-        fputs("<p>", main_fragment);
-        fprintf(main_fragment, "%s\n", pcmd->help.desc);
-        fputs("</p>\n", main_fragment);
-
-        if (pcmd->help.args[0][0] != NULL) {
-            fputs("<p><b>Arguments</b></p>\n", main_fragment);
-            fputs("<table>", main_fragment);
-            for (i = 0; pcmd->help.args[i][0] != NULL; i++) {
-                fputs("<tr>", main_fragment);
-                fputs("<td>", main_fragment);
-                fputs("<code>", main_fragment);
-                char *str1 = str_replace(pcmd->help.args[i][0], "<", "&lt;");
-                char *str2 = str_replace(str1, ">", "&gt;");
-                fprintf(main_fragment, "%s", str2);
-                fputs("</code>", main_fragment);
-                fputs("</td>", main_fragment);
-                fputs("<td>", main_fragment);
-                fprintf(main_fragment, "%s", pcmd->help.args[i][1]);
-                fputs("</td>", main_fragment);
-                fputs("</tr>", main_fragment);
-            }
-            fputs("</table>\n", main_fragment);
-        }
-
-        if (pcmd->help.examples[0] != NULL) {
-            fputs("<p><b>Examples</b></p>\n", main_fragment);
-            fputs("<p><pre><code>", main_fragment);
-            int i = 0;
-            while (pcmd->help.examples[i]) {
-                fprintf(main_fragment, "%s\n", pcmd->help.examples[i]);
-                i++;
-            }
-            fputs("</code></pre></p>\n", main_fragment);
-        }
-
-        fputs("<a href=\"#top\"><h5>back to top</h5></a><br><hr>\n", main_fragment);
-        fputs("\n", main_fragment);
-
-        curr = g_list_next(curr);
-    }
-
-    fputs("</ul></ul>\n", toc_fragment);
-
-    fclose(toc_fragment);
-    fclose(main_fragment);
-    printf("\nProcessed %d commands.\n\n", g_list_length(cmds));
-    g_list_free(cmds);
-}
diff --git a/src/config/scripts.c b/src/config/scripts.c
index 0f8800d8..945aaec2 100644
--- a/src/config/scripts.c
+++ b/src/config/scripts.c
@@ -43,7 +43,7 @@
 #include "common.h"
 #include "log.h"
 #include "window_list.h"
-#include "command/command.h"
+#include "command/cmd_defs.h"
 #include "ui/ui.h"
 #include "xmpp/xmpp.h"
 
diff --git a/src/main.c b/src/main.c
index 7f87c165..b15293d0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,7 +43,7 @@
 
 #include "profanity.h"
 #include "common.h"
-#include "command/command.h"
+#include "command/cmd_defs.h"
 
 static gboolean version = FALSE;
 static char *log = "INFO";
diff --git a/src/plugins/api.c b/src/plugins/api.c
index 0a4b04af..326ec61d 100644
--- a/src/plugins/api.c
+++ b/src/plugins/api.c
@@ -49,7 +49,7 @@
 #include "profanity.h"
 #include "ui/ui.h"
 #include "config/theme.h"
-#include "command/command.h"
+#include "command/cmd_defs.h"
 #include "window_list.h"
 #include "common.h"
 
diff --git a/src/plugins/callbacks.c b/src/plugins/callbacks.c
index eaca342a..2572f7a4 100644
--- a/src/plugins/callbacks.c
+++ b/src/plugins/callbacks.c
@@ -35,7 +35,8 @@
 #include <string.h>
 #include <stdlib.h>
 
-#include "command/command.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_ac.h"
 #include "plugins/callbacks.h"
 #include "plugins/plugins.h"
 #include "tools/autocomplete.h"
@@ -72,9 +73,8 @@ void
 callbacks_add_command(PluginCommand *command)
 {
     p_commands = g_slist_append(p_commands, command);
-    cmd_autocomplete_add(command->command_name);
-    cmd_help_autocomplete_add(&command->command_name[1]);
-
+    cmd_ac_add(command->command_name);
+    cmd_ac_add_help(&command->command_name[1]);
 }
 
 void
diff --git a/src/plugins/callbacks.h b/src/plugins/callbacks.h
index dc6b78bb..9b175d0a 100644
--- a/src/plugins/callbacks.h
+++ b/src/plugins/callbacks.h
@@ -37,7 +37,7 @@
 
 #include <glib.h>
 
-#include "command/command.h"
+#include "command/cmd_defs.h"
 
 typedef struct p_command {
     const char *command_name;
diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h
index b7eb4c80..b03248ab 100644
--- a/src/plugins/plugins.h
+++ b/src/plugins/plugins.h
@@ -35,7 +35,7 @@
 #ifndef PLUGINS_H
 #define PLUGINS_H
 
-#include "command/command.h"
+#include "command/cmd_defs.h"
 
 typedef enum {
     LANG_PYTHON,
diff --git a/src/profanity.c b/src/profanity.c
index 56f242ef..c9e19fa8 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -55,7 +55,7 @@
 #include "config/preferences.h"
 #include "config/theme.h"
 #include "config/scripts.h"
-#include "command/command.h"
+#include "command/cmd_defs.h"
 #include "common.h"
 #include "contact.h"
 #include "roster_list.h"
diff --git a/src/ui/console.c b/src/ui/console.c
index cae0d637..a5ff3a76 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -43,7 +43,7 @@
 #include <ncurses.h>
 #endif
 
-#include "command/command.h"
+#include "command/cmd_defs.h"
 #include "common.h"
 #include "log.h"
 #include "muc.h"
diff --git a/src/ui/core.c b/src/ui/core.c
index 0b761274..3a89008f 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -55,7 +55,8 @@
 #endif
 
 #include "chat_session.h"
-#include "command/command.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_ac.h"
 #include "common.h"
 #include "config/preferences.h"
 #include "config/theme.h"
@@ -651,12 +652,12 @@ ui_focus_win(ProfWin *window)
     ProfWin *old_current = wins_get_current();
     if (old_current->type == WIN_MUC_CONFIG) {
         ProfMucConfWin *confwin = (ProfMucConfWin*)old_current;
-        cmd_autocomplete_remove_form_fields(confwin->form);
+        cmd_ac_remove_form_fields(confwin->form);
     }
 
     if (window->type == WIN_MUC_CONFIG) {
         ProfMucConfWin *confwin = (ProfMucConfWin*)window;
-        cmd_autocomplete_add_form_fields(confwin->form);
+        cmd_ac_add_form_fields(confwin->form);
     }
 
     int i = wins_get_num(window);
@@ -679,7 +680,7 @@ ui_close_win(int index)
     if (window && window->type == WIN_MUC_CONFIG) {
         ProfMucConfWin *confwin = (ProfMucConfWin*)window;
         if (confwin->form) {
-            cmd_autocomplete_remove_form_fields(confwin->form);
+            cmd_ac_remove_form_fields(confwin->form);
         }
     }
 
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index 2c7eae34..8f896dbe 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -53,7 +53,7 @@
 #include <ncurses.h>
 #endif
 
-#include "command/command.h"
+#include "command/cmd_ac.h"
 #include "common.h"
 #include "config/accounts.h"
 #include "config/preferences.h"
@@ -452,7 +452,7 @@ _inp_rl_getc(FILE *stream)
     int ch = rl_getc(stream);
     if (_inp_printable(ch)) {
         ProfWin *window = wins_get_current();
-        cmd_reset_autocomplete(window);
+        cmd_ac_reset(window);
     }
     return ch;
 }
@@ -482,7 +482,7 @@ _inp_rl_tab_handler(int count, int key)
         }
     } else if (strncmp(rl_line_buffer, "/", 1) == 0) {
         ProfWin *window = wins_get_current();
-        char *result = cmd_autocomplete(window, rl_line_buffer);
+        char *result = cmd_ac_complete(window, rl_line_buffer);
         if (result) {
             rl_replace_line(result, 1);
             rl_point = rl_end;
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 18a5864f..2213ccc1 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -37,7 +37,7 @@
 
 #include "config.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 #include "ui/win_types.h"
 #include "muc.h"
 #include "config/tlscerts.h"
diff --git a/tests/unittests/test_cmd_account.c b/tests/unittests/test_cmd_account.c
index 74781177..0b723bde 100644
--- a/tests/unittests/test_cmd_account.c
+++ b/tests/unittests/test_cmd_account.c
@@ -13,7 +13,7 @@
 
 #include "config/accounts.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #define CMD_ACCOUNT "/account"
 
diff --git a/tests/unittests/test_cmd_alias.c b/tests/unittests/test_cmd_alias.c
index fd8f824f..d1cdaf01 100644
--- a/tests/unittests/test_cmd_alias.c
+++ b/tests/unittests/test_cmd_alias.c
@@ -13,8 +13,9 @@
 
 #include "config/preferences.h"
 
-#include "command/command.h"
-#include "command/commands.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_funcs.h"
+#include "command/cmd_ac.h"
 
 #define CMD_ALIAS "/alias"
 
@@ -77,7 +78,7 @@ void cmd_alias_add_shows_message_when_exists(void **state)
 
     cmd_init();
     prefs_add_alias("hc", "/help commands");
-    cmd_autocomplete_add("/hc");
+    cmd_ac_add("/hc");
 
     expect_cons_show("Command or alias '/hc' already exists.");
 
diff --git a/tests/unittests/test_cmd_bookmark.c b/tests/unittests/test_cmd_bookmark.c
index 8b8b4871..5b17c6e0 100644
--- a/tests/unittests/test_cmd_bookmark.c
+++ b/tests/unittests/test_cmd_bookmark.c
@@ -14,7 +14,7 @@
 #include "muc.h"
 #include "common.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #include "xmpp/bookmark.h"
 
diff --git a/tests/unittests/test_cmd_connect.c b/tests/unittests/test_cmd_connect.c
index e4f01271..b2511183 100644
--- a/tests/unittests/test_cmd_connect.c
+++ b/tests/unittests/test_cmd_connect.c
@@ -11,7 +11,7 @@
 #include "ui/ui.h"
 #include "ui/stub_ui.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 #include "config/accounts.h"
 
 #define CMD_CONNECT "/connect"
diff --git a/tests/unittests/test_cmd_disconnect.c b/tests/unittests/test_cmd_disconnect.c
index ba73adf5..9bf78a7a 100644
--- a/tests/unittests/test_cmd_disconnect.c
+++ b/tests/unittests/test_cmd_disconnect.c
@@ -6,7 +6,7 @@
 #include <string.h>
 
 #include "chat_session.h"
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 #include "xmpp/xmpp.h"
 #include "roster_list.h"
 
diff --git a/tests/unittests/test_cmd_join.c b/tests/unittests/test_cmd_join.c
index 8b71c5e5..709b4145 100644
--- a/tests/unittests/test_cmd_join.c
+++ b/tests/unittests/test_cmd_join.c
@@ -13,7 +13,7 @@
 
 #include "config/accounts.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 #include "muc.h"
 
 #define CMD_JOIN "/join"
diff --git a/tests/unittests/test_cmd_otr.c b/tests/unittests/test_cmd_otr.c
index 44703943..ab03e7d4 100644
--- a/tests/unittests/test_cmd_otr.c
+++ b/tests/unittests/test_cmd_otr.c
@@ -15,8 +15,8 @@
 
 #include "config/preferences.h"
 
-#include "command/command.h"
-#include "command/commands.h"
+#include "command/cmd_defs.h"
+#include "command/cmd_funcs.h"
 #include "window_list.h"
 #include "xmpp/xmpp.h"
 
diff --git a/tests/unittests/test_cmd_pgp.c b/tests/unittests/test_cmd_pgp.c
index ec010cce..b2a1aa7e 100644
--- a/tests/unittests/test_cmd_pgp.c
+++ b/tests/unittests/test_cmd_pgp.c
@@ -8,7 +8,7 @@
 
 #include "config.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 #include "xmpp/xmpp.h"
 
 #include "ui/stub_ui.h"
diff --git a/tests/unittests/test_cmd_rooms.c b/tests/unittests/test_cmd_rooms.c
index 85a528b5..2cb6be6b 100644
--- a/tests/unittests/test_cmd_rooms.c
+++ b/tests/unittests/test_cmd_rooms.c
@@ -12,7 +12,7 @@
 #include "ui/stub_ui.h"
 
 #include "config/accounts.h"
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #define CMD_ROOMS "/rooms"
 
diff --git a/tests/unittests/test_cmd_roster.c b/tests/unittests/test_cmd_roster.c
index 8b194de4..6628147d 100644
--- a/tests/unittests/test_cmd_roster.c
+++ b/tests/unittests/test_cmd_roster.c
@@ -11,7 +11,7 @@
 
 #include "xmpp/xmpp.h"
 #include "roster_list.h"
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #define CMD_ROSTER "/roster"
 
diff --git a/tests/unittests/test_cmd_statuses.c b/tests/unittests/test_cmd_statuses.c
index bd5337c3..f4d8c067 100644
--- a/tests/unittests/test_cmd_statuses.c
+++ b/tests/unittests/test_cmd_statuses.c
@@ -11,7 +11,7 @@
 #include "ui/ui.h"
 #include "ui/stub_ui.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #define CMD_STATUSES "/statuses"
 
diff --git a/tests/unittests/test_cmd_sub.c b/tests/unittests/test_cmd_sub.c
index a89e82bf..4afe8752 100644
--- a/tests/unittests/test_cmd_sub.c
+++ b/tests/unittests/test_cmd_sub.c
@@ -11,7 +11,7 @@
 #include "ui/ui.h"
 #include "ui/stub_ui.h"
 
-#include "command/commands.h"
+#include "command/cmd_funcs.h"
 
 #define CMD_SUB "/sub"