degesch: add a read-only /set command

This commit is contained in:
Přemysl Eric Janouch 2015-05-03 16:47:16 +02:00
parent e000a6b495
commit 4928f9ed62
1 changed files with 176 additions and 0 deletions

176
degesch.c
View File

@ -3547,6 +3547,106 @@ log_outcoming_notice (struct server *s,
send_autosplit_message ((s), (struct send_autosplit_args) \ send_autosplit_message ((s), (struct send_autosplit_args) \
{ "NOTICE", (target), (message), log_outcoming_notice, "", "" }) { "NOTICE", (target), (message), log_outcoming_notice, "", "" })
// --- Configuration dumper ----------------------------------------------------
struct config_dump_level
{
struct config_dump_level *next; ///< Next print level
const char *name; ///< Name of the object
};
struct config_dump_data
{
struct config_dump_level *head; ///< The first level
struct config_dump_level **tail; ///< Where to place further levels
struct str_vector *output; ///< Where to place new entries
};
static void config_dump_item
(struct config_item_ *item, struct config_dump_data *data);
static void
config_dump_children
(struct config_item_ *object, struct config_dump_data *data)
{
hard_assert (object->type = CONFIG_ITEM_OBJECT);
struct config_dump_level level;
level.next = NULL;
struct config_dump_level **prev_tail = data->tail;
*data->tail = &level;
data->tail = &level.next;
struct str_map_iter iter;
str_map_iter_init (&iter, &object->value.object);
struct config_item_ *child;
while ((child = str_map_iter_next (&iter)))
{
level.name = iter.link->key;
config_dump_item (child, data);
}
data->tail = prev_tail;
*data->tail = NULL;
}
static void
config_dump_item (struct config_item_ *item, struct config_dump_data *data)
{
struct str line;
str_init (&line);
struct config_dump_level *iter = data->head;
if (iter)
{
str_append (&line, iter->name);
iter = iter->next;
}
for (; iter; iter = iter->next)
str_append_printf (&line, ".%s", iter->name);
// Empty objects will show as such
if (item->type == CONFIG_ITEM_OBJECT
&& item->value.object.len)
{
config_dump_children (item, data);
return;
}
// Don't bother writing out null values everywhere
struct config_schema *schema = item->schema;
bool has_default = schema && schema->default_;
if (item->type != CONFIG_ITEM_NULL || has_default)
{
str_append (&line, " = ");
struct str value;
str_init (&value);
config_item_write (item, false, &value);
str_append_str (&line, &value);
if (has_default && strcmp (schema->default_, value.str))
str_append_printf (&line, " (default: %s)", schema->default_);
else if (!has_default && item->type != CONFIG_ITEM_NULL)
str_append_printf (&line, " (default: %s)", "null");
str_free (&value);
}
str_vector_add_owned (data->output, str_steal (&line));
}
static void
config_dump (struct config_item_ *root, struct str_vector *output)
{
struct config_dump_data data;
data.head = NULL;
data.tail = &data.head;
data.output = output;
config_dump_item (root, &data);
}
// --- User input handling ----------------------------------------------------- // --- User input handling -----------------------------------------------------
static bool handle_command_help (struct app_context *, char *); static bool handle_command_help (struct app_context *, char *);
@ -3673,6 +3773,80 @@ handle_command_buffer (struct app_context *ctx, char *arguments)
return true; return true;
} }
static int
str_vector_sort_cb (const void *a, const void *b)
{
return strcmp (*(const char **) a, *(const char **) b);
}
static void
str_vector_sort (struct str_vector *self)
{
qsort (self->vector, self->len, sizeof *self->vector, str_vector_sort_cb);
}
static bool
handle_command_set (struct app_context *ctx, char *arguments)
{
struct str_vector all;
str_vector_init (&all);
config_dump (ctx->config.root, &all);
str_vector_sort (&all);
char *option = "*";
if (*arguments)
option = cut_word (&arguments);
// Filter out results by wildcard matching
for (size_t i = 0; i < all.len; i++)
{
char *key = xstrdup (all.vector[i]);
char *end = strchr (key, ' ');
if (end)
*end = '\0';
if (fnmatch (option, key, 0))
str_vector_remove (&all, i--);
free (key);
}
if (!*arguments)
{
buffer_send_status (ctx, ctx->global_buffer, "%s", "");
for (size_t i = 0; i < all.len; i++)
buffer_send_status (ctx, ctx->global_buffer, "%s", all.vector[i]);
str_vector_free (&all);
return true;
}
char *op = cut_word (&arguments);
bool add = false;
bool remove = false;
if (!strcmp (op, "+=")) add = true;
else if (!strcmp (op, "-=")) remove = true;
else if (strcmp (op, "=")) return false;
if (!arguments)
return false;
char *value = cut_word (&arguments);
struct error *e = NULL;
struct config_item_ *new_ =
config_item_parse (value, strlen (value), true, &e);
if (e)
{
buffer_send_error (ctx, ctx->global_buffer,
"Invalid value: %s", e->message);
error_free (e);
return true;
}
// TODO: try to set the value, or modify the string list
buffer_send_error (ctx, ctx->global_buffer, "Not implemented");
config_item_destroy (new_);
return true;
}
static bool static bool
handle_command_msg (struct app_context *ctx, char *arguments) handle_command_msg (struct app_context *ctx, char *arguments)
{ {
@ -3901,6 +4075,8 @@ g_command_handlers[] =
"[<message>]" }, "[<message>]" },
{ "buffer", handle_command_buffer, "Manage buffers", { "buffer", handle_command_buffer, "Manage buffers",
"list | clear | move | { close [<number> | <name>] } | <number>" }, "list | clear | move | { close [<number> | <name>] } | <number>" },
{ "set", handle_command_set, "Manage configuration",
"[<option>]" },
{ "msg", handle_command_msg, "Send message to a nick or channel", { "msg", handle_command_msg, "Send message to a nick or channel",
"<target> <message>" }, "<target> <message>" },