Ejemplo n.º 1
0
Archivo: ignore.c Proyecto: ahf/irssi
static void ignore_set_config(IGNORE_REC *rec)
{
	CONFIG_NODE *node;
	char *levelstr;

	if (rec->level == 0)
		return;

	node = iconfig_node_traverse("(ignores", TRUE);
	node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);

	if (rec->mask != NULL) iconfig_node_set_str(node, "mask", rec->mask);
	if (rec->level) {
		levelstr = bits2level(rec->level);
		iconfig_node_set_str(node, "level", levelstr);
		g_free(levelstr);
	}
	iconfig_node_set_str(node, "pattern", rec->pattern);
	if (rec->exception) iconfig_node_set_bool(node, "exception", TRUE);
	if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
	if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
	if (rec->replies) iconfig_node_set_bool(node, "replies", TRUE);
	if (rec->unignore_time != 0)
		iconfig_node_set_int(node, "unignore_time", rec->unignore_time);
	iconfig_node_set_str(node, "servertag", rec->servertag);

	if (rec->channels != NULL && *rec->channels != NULL) {
		node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
		iconfig_node_add_list(node, rec->channels);
	}
}
Ejemplo n.º 2
0
static void hilight_add_config(HILIGHT_REC *rec)
{
	CONFIG_NODE *node;

	g_return_if_fail(rec != NULL);

	node = iconfig_node_traverse("(hilights", TRUE);
	node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);

	iconfig_node_set_str(node, "text", rec->text);
	if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
	if (rec->color) iconfig_node_set_str(node, "color", rec->color);
	if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
	if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
	iconfig_node_set_bool(node, "nick", rec->nick);
	iconfig_node_set_bool(node, "word", rec->word);
	if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
	if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
	if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
	if (rec->case_sensitive) iconfig_node_set_bool(node, "matchcase", TRUE);
	if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);

	if (rec->channels != NULL && *rec->channels != NULL) {
		node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
		iconfig_node_add_list(node, rec->channels);
	}
}
Ejemplo n.º 3
0
static CONFIG_NODE *statusbar_copy_config(CONFIG_REC *config, CONFIG_NODE *source,
                                          CONFIG_NODE *parent)
{
	GSList *tmp;

	g_return_val_if_fail(parent != NULL, NULL);

	parent = iconfig_sbar_items_section(parent, TRUE);

	/* since items list in config file overrides defaults,
	   we'll need to copy the whole list. */
	for (tmp = config_node_first(source->value); tmp != NULL; tmp = config_node_next(tmp)) {
		int priority, right_alignment;
		CONFIG_NODE *node, *snode;

		snode = tmp->data;

		priority = config_node_get_int(snode, "priority", 0);
		right_alignment =
		    g_strcmp0(config_node_get_str(snode, "alignment", ""), "right") == 0;

		/* create new item */
		node = iconfig_node_section(parent, snode->key, NODE_TYPE_BLOCK);

		if (priority != 0)
			iconfig_node_set_int(node, "priority", priority);
		if (right_alignment)
			iconfig_node_set_str(node, "alignment", "right");
	}

	return parent;
}
Ejemplo n.º 4
0
/* SYNTAX: STATUSBAR RESET <statusbar> */
static void cmd_statusbar_reset(const char *data, void *server, void *witem)
{
	CONFIG_NODE *node, *parent;
	char *name;
	void *free_arg;

	if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_STRIP_TRAILING_WS, &name))
		return;

	if (*name == '\0') {
		cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
	}

	node = sbar_node(name, FALSE);
	if (node == NULL && !sbar_node_isdefault(name)) {
		printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_STATUSBAR_NOT_FOUND, name);
		cmd_params_free(free_arg);
		return;
	}

	parent = iconfig_node_traverse("statusbar", FALSE);
	if (parent != NULL) {
		parent = iconfig_node_section(parent, active_statusbar_group->name, -1);
	}

	if (parent != NULL && node != NULL) {
		iconfig_node_set_str(parent, node->key, NULL);
	}

	read_statusbar_config();
	cmd_params_free(free_arg);
}
Ejemplo n.º 5
0
static void sig_layout_save_item(WINDOW_REC *window, WI_ITEM_REC *item,
				 CONFIG_NODE *node)
{
	CONFIG_NODE *subnode;
        CHAT_PROTOCOL_REC *proto;
	const char *type;
	WINDOW_BIND_REC *rec;

	type = module_find_id_str("WINDOW ITEM TYPE", item->type);
	if (type == NULL)
		return;

	subnode = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);

	iconfig_node_set_str(subnode, "type", type);
	proto = item->chat_type == 0 ? NULL :
		chat_protocol_find_id(item->chat_type);
	if (proto != NULL)
		iconfig_node_set_str(subnode, "chat_type", proto->name);
	iconfig_node_set_str(subnode, "name", item->visible_name);

	if (item->server != NULL) {
		iconfig_node_set_str(subnode, "tag", item->server->tag);
		if (IS_CHANNEL(item)) {
			rec = window_bind_add(window, item->server->tag, item->visible_name);
			if (rec != NULL)
				rec->sticky = TRUE;
		}
	} else if (IS_QUERY(item)) {
		iconfig_node_set_str(subnode, "tag", QUERY(item)->server_tag);
	}
}
Ejemplo n.º 6
0
static void sig_layout_restore(void)
{
	WINDOW_REC *window;
	CONFIG_NODE *node;
	GSList *tmp;

	node = iconfig_node_traverse("windows", FALSE);
	if (node == NULL) return;

	tmp = config_node_first(node->value);
	for (; tmp != NULL; tmp = config_node_next(tmp)) {
		CONFIG_NODE *node = tmp->data;

		if (node->key == NULL) continue;
		window = window_find_refnum(atoi(node->key));
		if (window == NULL)
			window = window_create(NULL, TRUE);

		window_set_refnum(window, atoi(node->key));
                window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE);
                window->immortal = config_node_get_bool(node, "immortal", FALSE);
		window_set_name(window, config_node_get_str(node, "name", NULL));
		window_set_history(window, config_node_get_str(node, "history_name", NULL));
		window_set_level(window, level2bits(config_node_get_str(node, "level", ""), NULL));

		window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL));
		window->theme_name = g_strdup(config_node_get_str(node, "theme", NULL));
		if (window->theme_name != NULL)
			window->theme = theme_load(window->theme_name);

		window_add_items(window, iconfig_node_section(node, "items", -1));
		signal_emit("layout restore window", 2, window, node);
	}
}
Ejemplo n.º 7
0
static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
{
	GSList *tmp;

	node = iconfig_node_section(node, "items", NODE_TYPE_LIST);
	for (tmp = window->items; tmp != NULL; tmp = tmp->next)
		signal_emit("layout save item", 3, window, tmp->data, node);
}
Ejemplo n.º 8
0
static void read_hilight_config(void)
{
	CONFIG_NODE *node;
	HILIGHT_REC *rec;
	GSList *tmp;
	char *text, *color;

	hilights_destroy_all();

	node = iconfig_node_traverse("hilights", FALSE);
	if (node == NULL) {
                reset_cache();
		return;
	}

	tmp = config_node_first(node->value);
	for (; tmp != NULL; tmp = config_node_next(tmp)) {
		node = tmp->data;

		if (node->type != NODE_TYPE_BLOCK)
			continue;

		text = config_node_get_str(node, "text", NULL);
		if (text == NULL || *text == '\0')
			continue;

		rec = g_new0(HILIGHT_REC, 1);
		hilights = g_slist_append(hilights, rec);

		rec->text = g_strdup(text);

		color = config_node_get_str(node, "color", NULL);
		rec->color = color == NULL || *color == '\0' ? NULL :
			g_strdup(color);

		color = config_node_get_str(node, "act_color", NULL);
		rec->act_color = color == NULL || *color == '\0' ? NULL :
			g_strdup(color);

		rec->level = config_node_get_int(node, "level", 0);
		rec->priority = config_node_get_int(node, "priority", 0);
		rec->nick = config_node_get_bool(node, "nick", TRUE);
		rec->word = config_node_get_bool(node, "word", TRUE);
		rec->case_sensitive = config_node_get_bool(node, "matchcase", FALSE);

		rec->nickmask = config_node_get_bool(node, "mask", FALSE);
		rec->fullword = config_node_get_bool(node, "fullword", FALSE);
		rec->regexp = config_node_get_bool(node, "regexp", FALSE);
		rec->servertag = config_node_get_str(node, "servertag", NULL);
		hilight_init_rec(rec);

		node = iconfig_node_section(node, "channels", -1);
		if (node != NULL) rec->channels = config_node_get_list(node);
	}

	reset_cache();
}
Ejemplo n.º 9
0
/* verify that all settings in config file for `module' are actually found
   from /SET list */
void settings_check_module(const char *module)
{
        SETTINGS_REC *set;
	CONFIG_NODE *node, *parent;
        GString *errors;
	GSList *tmp, *next;
        int count;

        g_return_if_fail(module != NULL);

	node = iconfig_node_traverse("settings", FALSE);
	node = node == NULL ? NULL : iconfig_node_section(node, module, -1);
	if (node == NULL) return;

        errors = g_string_new(NULL);
	g_string_printf(errors, "Unknown settings in configuration "
			 "file for module %s:", module);

        count = 0;
	parent = node;
	tmp = config_node_first(node->value);
	for (; tmp != NULL; tmp = next) {
		node = tmp->data;
		next = config_node_next(tmp);
		if (node->key == NULL) continue;

		set = g_hash_table_lookup(settings, node->key);
		if (backwards_compatibility(module, node, parent))
			continue;

		if (set == NULL || g_strcmp0(set->module, module) != 0) {
			g_string_append_printf(errors, " %s", node->key);
                        count++;
		}
	}
	if (count > 0) {
		if (gslist_find_icase_string(last_invalid_modules,
					     module) == NULL) {
                        /* mark this module having invalid settings */
			last_invalid_modules =
				g_slist_append(last_invalid_modules,
					       g_strdup(module));
		}
		if (fe_initialized)
                        signal_emit("settings errors", 1, errors->str);
		else {
			if (last_errors == NULL)
				last_errors = g_string_new(NULL);
			else
				g_string_append_c(last_errors, '\n');
                        g_string_append(last_errors, errors->str);
		}
	}
        g_string_free(errors, TRUE);
}
Ejemplo n.º 10
0
int settings_get_int(const char *key)
{
	SETTINGS_REC *rec;
	CONFIG_NODE *node;

	rec = settings_get(key, SETTING_TYPE_INT);
	if (rec == NULL) return 0;

	node = iconfig_node_traverse("settings", FALSE);
	node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);

	return node == NULL ? rec->default_value.v_int :
		config_node_get_int(node, key, rec->default_value.v_int);
}
Ejemplo n.º 11
0
static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node)
{
	STATUSBAR_CONFIG_REC *bar;
        GSList *tmp;
        const char *visible_str;

	g_return_if_fail(is_node_list(node));
	g_return_if_fail(node->key != NULL);

	bar = statusbar_config_find(group, node->key);
	if (config_node_get_bool(node, "disabled", FALSE)) {
		/* disabled, destroy it if it already exists */
		if (bar != NULL)
			statusbar_config_destroy(group, bar);
                return;
	}

	if (bar == NULL) {
		bar = statusbar_config_create(group, node->key);
		bar->type = STATUSBAR_TYPE_ROOT;
		bar->placement = STATUSBAR_BOTTOM;
		bar->position = 0;
	}

        visible_str = config_node_get_str(node, "visible", "");
	if (g_ascii_strcasecmp(visible_str, "active") == 0)
                bar->visible = STATUSBAR_VISIBLE_ACTIVE;
	else if (g_ascii_strcasecmp(visible_str, "inactive") == 0)
		bar->visible = STATUSBAR_VISIBLE_INACTIVE;
	else
		bar->visible = STATUSBAR_VISIBLE_ALWAYS;

	if (g_ascii_strcasecmp(config_node_get_str(node, "type", ""), "window") == 0)
                bar->type = STATUSBAR_TYPE_WINDOW;
	if (g_ascii_strcasecmp(config_node_get_str(node, "placement", ""), "top") == 0)
                bar->placement = STATUSBAR_TOP;
	bar->position = config_node_get_int(node, "position", 0);

	node = iconfig_node_section(node, "items", -1);
	if (node != NULL) {
                /* we're overriding the items - destroy the old */
                while (bar->items != NULL)
			statusbar_config_item_destroy(bar, bar->items->data);

		tmp = config_node_first(node->value);
		for (; tmp != NULL; tmp = config_node_next(tmp))
			statusbar_read_item(bar, tmp->data);
	}
}
Ejemplo n.º 12
0
static const char *
settings_get_str_type(const char *key, SettingType type)
{
	SETTINGS_REC *rec;
	CONFIG_NODE *node;

	rec = settings_get(key, type);
	if (rec == NULL) return NULL;

	node = iconfig_node_traverse("settings", FALSE);
	node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);

	return node == NULL ? rec->default_value.v_string :
		config_node_get_str(node, key, rec->default_value.v_string);
}
Ejemplo n.º 13
0
static CONFIG_NODE *settings_get_node(const char *key)
{
	SETTINGS_REC *rec;
        CONFIG_NODE *node;

	g_return_val_if_fail(key != NULL, NULL);

	rec = g_hash_table_lookup(settings, key);
	if (rec == NULL) {
		g_warning("Changing unknown setting '%s'", key);
		return NULL;
	}

	node = iconfig_node_traverse("settings", TRUE);
	return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK);
}
Ejemplo n.º 14
0
static const char *completion_find(const char *key, int automatic)
{
	CONFIG_NODE *node;

	node = iconfig_node_traverse("completions", FALSE);
	if (node == NULL || node->type != NODE_TYPE_BLOCK)
		return NULL;

	node = iconfig_node_section(node, key, -1);
	if (node == NULL)
		return NULL;

	if (automatic && !config_node_get_bool(node, "auto", FALSE))
		return NULL;

	return config_node_get_str(node, "value", NULL);
}
Ejemplo n.º 15
0
Archivo: keyboard.c Proyecto: ahf/irssi
static void keyconfig_save(const char *id, const char *key, const char *data)
{
	CONFIG_NODE *node;

	g_return_if_fail(id != NULL);
	g_return_if_fail(key != NULL);

	node = key_config_find(key);
	if (node == NULL) {
		node = iconfig_node_traverse("(keyboard", TRUE);
		node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
	}

	iconfig_node_set_str(node, "key", key);
	iconfig_node_set_str(node, "id", id);
	iconfig_node_set_str(node, "data", data);
}
Ejemplo n.º 16
0
static void read_statusbar_config_from_node(CONFIG_NODE *node)
{
	CONFIG_NODE *items, *group;
	GSList *tmp;
	int i;

	items = iconfig_node_section(node, "items", -1);
	if (items != NULL)
		statusbar_read_items(items);

	for (tmp = config_node_first(node->value), i = 0; tmp != NULL; tmp = config_node_next(tmp), i++) {
		group = tmp->data;
		if (group != items) {
			skip_corrupt_config(node, group, i, "");
			statusbar_read_group(group);
		}
	}
}
Ejemplo n.º 17
0
static void server_setup_save(SERVER_SETUP_REC *rec)
{
	CONFIG_NODE *parent_node, *node;
	GSList *config_node;

	parent_node = iconfig_node_traverse("(servers", TRUE);

	/* Try to find this channel in the configuration */
	config_node = g_slist_find_custom(parent_node->value, rec,
					  (GCompareFunc)compare_server_setup);
	if (config_node != NULL)
		/* Let's update this server record */
		node = config_node->data;
	else
		/* Create a brand-new server record */
		node = iconfig_node_section(parent_node, NULL, NODE_TYPE_BLOCK);

        iconfig_node_clear(node);
	iconfig_node_set_str(node, "address", rec->address);
	iconfig_node_set_str(node, "chatnet", rec->chatnet);

	iconfig_node_set_int(node, "port", rec->port);
	iconfig_node_set_str(node, "password", rec->password);
	iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
	iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
	iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
	iconfig_node_set_str(node, "ssl_pass", rec->ssl_pass);
	iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
	iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
	iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
	iconfig_node_set_str(node, "ssl_ciphers", rec->ssl_ciphers);
	iconfig_node_set_str(node, "own_host", rec->own_host);

	iconfig_node_set_str(node, "family",
			     rec->family == AF_INET6 ? "inet6" :
			     rec->family == AF_INET ? "inet" : NULL);

	if (rec->autoconnect)
		iconfig_node_set_bool(node, "autoconnect", TRUE);
	if (rec->no_proxy)
		iconfig_node_set_bool(node, "no_proxy", TRUE);

	signal_emit("server setup saved", 2, rec, node);
}
Ejemplo n.º 18
0
Archivo: ignore.c Proyecto: ahf/irssi
static void read_ignores(void)
{
	IGNORE_REC *rec;
	CONFIG_NODE *node;
	GSList *tmp;

	while (ignores != NULL)
                ignore_destroy(ignores->data, FALSE);

	node = iconfig_node_traverse("ignores", FALSE);
	if (node == NULL) {
		nickmatch_rebuild(nickmatch);
		return;
	}

	tmp = config_node_first(node->value);
	for (; tmp != NULL; tmp = config_node_next(tmp)) {
		node = tmp->data;

		if (node->type != NODE_TYPE_BLOCK)
			continue;

		rec = g_new0(IGNORE_REC, 1);
		ignores = g_slist_append(ignores, rec);

		rec->mask = g_strdup(config_node_get_str(node, "mask", NULL));
		rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL));
		rec->level = level2bits(config_node_get_str(node, "level", ""), NULL);
                rec->exception = config_node_get_bool(node, "exception", FALSE);
		rec->regexp = config_node_get_bool(node, "regexp", FALSE);
		rec->fullword = config_node_get_bool(node, "fullword", FALSE);
		rec->replies = config_node_get_bool(node, "replies", FALSE);
		rec->unignore_time = config_node_get_int(node, "unignore_time", 0);
		rec->servertag = g_strdup(config_node_get_str(node, "servertag", 0));

		node = iconfig_node_section(node, "channels", -1);
		if (node != NULL) rec->channels = config_node_get_list(node);

		ignore_init_rec(rec);
	}

	nickmatch_rebuild(nickmatch);
}
Ejemplo n.º 19
0
static void settings_clean_invalid_module(const char *module)
{
        CONFIG_NODE *node;
        SETTINGS_REC *set;
	GSList *tmp, *next;

	node = iconfig_node_traverse("settings", FALSE);
	if (node == NULL) return;

	node = iconfig_node_section(node, module, -1);
	if (node == NULL) return;

	for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
		CONFIG_NODE *subnode = tmp->data;
                next = config_node_next(tmp);

		set = g_hash_table_lookup(settings, subnode->key);
		if (set == NULL || g_strcmp0(set->module, module) != 0)
                        iconfig_node_remove(node, subnode);
	}
}
Ejemplo n.º 20
0
int settings_get_choice(const char *key)
{
	SETTINGS_REC *rec;
	CONFIG_NODE *node;
	char *str;
	int index;

	rec = settings_get(key, SETTING_TYPE_CHOICE);
	if (rec == NULL) return -1;

	node = iconfig_node_traverse("settings", FALSE);
	node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);

	str = node == NULL ? rec->default_value.v_string :
		config_node_get_str(node, key, rec->default_value.v_string);

	if (str == NULL || (index = strarray_find(rec->choices, str)) < 0)
		return rec->default_value.v_int;

	return index;
}
Ejemplo n.º 21
0
static int backwards_compatibility(const char *module, CONFIG_NODE *node,
				   CONFIG_NODE *parent)
{
	const char *new_key, *new_module;
	CONFIG_NODE *new_node;
	char *new_value;

	new_value = NULL; new_key = NULL; new_module = NULL;

	/* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
	if (g_strcmp0(module, "fe-text") == 0) {
		if (g_ascii_strcasecmp(node->key, "term_type") == 0 ||
		    /* kludge for cvs-version where term_charset was in fe-text */
		    g_ascii_strcasecmp(node->key, "term_charset") == 0) {
			new_module = "fe-common/core";
			new_key = "term_charset";
			new_value = !is_valid_charset(node->value) ? NULL :
				g_strdup(node->value);
			new_node = iconfig_node_traverse("settings", FALSE);
			new_node = new_node == NULL ? NULL :
				iconfig_node_section(new_node, new_module, -1);

			config_node_set_str(mainconfig, new_node,
					    new_key, new_value);
			/* remove old */
			config_node_set_str(mainconfig, parent,
					    node->key, NULL);
			g_free(new_value);
			config_changed = TRUE;
			return new_key != NULL;
		} else if (g_ascii_strcasecmp(node->key, "actlist_moves") == 0 &&
			   node->value != NULL && g_ascii_strcasecmp(node->value, "yes") == 0) {
			config_node_set_str(mainconfig, parent, "actlist_sort", "recent");
			config_node_set_str(mainconfig, parent, node->key, NULL);
			config_changed = TRUE;
			return TRUE;
		}
	}
	return new_key != NULL;
}
Ejemplo n.º 22
0
static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
{
	char refnum[MAX_INT_STRLEN];

        ltoa(refnum, window->refnum);
	node = iconfig_node_section(node, refnum, NODE_TYPE_BLOCK);

	if (window->sticky_refnum)
		iconfig_node_set_bool(node, "sticky_refnum", TRUE);

	if (window->immortal)
		iconfig_node_set_bool(node, "immortal", TRUE);

	if (window->name != NULL)
		iconfig_node_set_str(node, "name", window->name);

	if (window->history_name != NULL)
		iconfig_node_set_str(node, "history_name", window->history_name);

	if (window->servertag != NULL)
		iconfig_node_set_str(node, "servertag", window->servertag);
	if (window->level != 0) {
                char *level = bits2level(window->level);
		iconfig_node_set_str(node, "level", level);
		g_free(level);
	}
	if (window->theme_name != NULL)
		iconfig_node_set_str(node, "theme", window->theme_name);

	while (window->bound_items != NULL)
		window_bind_destroy(window, window->bound_items->data);
	if (window->items != NULL)
		window_save_items(window, node);

	signal_emit("layout save window", 2, window, node);
}
Ejemplo n.º 23
0
/* SYNTAX: COMPLETION [-auto] [-delete] <key> <value> */
static void cmd_completion(const char *data)
{
	GHashTable *optlist;
	CONFIG_NODE *node;
	GSList *tmp;
	char *key, *value;
	void *free_arg;
	int len;

	if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
			    PARAM_FLAG_GETREST,
			    "completion", &optlist, &key, &value))
		return;

	node = iconfig_node_traverse("completions", *value != '\0');
	if (node != NULL && node->type != NODE_TYPE_BLOCK) {
		/* FIXME: remove after 0.8.5 */
		iconfig_node_remove(mainconfig->mainnode, node);
		node = iconfig_node_traverse("completions", *value != '\0');
	}

	if (node == NULL || (node->value == NULL && *value == '\0')) {
		printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
			    TXT_NO_COMPLETIONS);
		cmd_params_free(free_arg);
		return;
	}

	if (g_hash_table_lookup(optlist, "delete") != NULL && *key != '\0') {
		printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
			    TXT_COMPLETION_REMOVED, key);

		iconfig_set_str("completions", key, NULL);
		signal_emit("completion removed", 1, key);
	} else if (*key != '\0' && *value != '\0') {
		int automatic = g_hash_table_lookup(optlist, "auto") != NULL;

		node = iconfig_node_section(node, key, NODE_TYPE_BLOCK);
		iconfig_node_set_str(node, "value", value);
		if (automatic)
			iconfig_node_set_bool(node, "auto", TRUE);
		else
			iconfig_node_set_str(node, "auto", NULL);

		printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
			    TXT_COMPLETION_LINE,
			    key, value, automatic ? "yes" : "no");

		signal_emit("completion added", 1, key);
	} else {
		printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
			    TXT_COMPLETION_HEADER);

		len = strlen(key);
		for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
			node = tmp->data;

			if (len == 0 ||
			    g_ascii_strncasecmp(node->key, key, len) == 0) {
				printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
					    TXT_COMPLETION_LINE, node->key,
					    config_node_get_str(node, "value", ""),
					    config_node_get_bool(node, "auto", FALSE) ? "yes" : "no");
			}
		}

		printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
			    TXT_COMPLETION_FOOTER);
	}

	cmd_params_free(free_arg);
}