degesch: add a read-only /set command
This commit is contained in:
parent
e000a6b495
commit
4928f9ed62
176
degesch.c
176
degesch.c
@ -3547,6 +3547,106 @@ log_outcoming_notice (struct server *s,
|
||||
send_autosplit_message ((s), (struct send_autosplit_args) \
|
||||
{ "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 -----------------------------------------------------
|
||||
|
||||
static bool handle_command_help (struct app_context *, char *);
|
||||
@ -3673,6 +3773,80 @@ handle_command_buffer (struct app_context *ctx, char *arguments)
|
||||
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
|
||||
handle_command_msg (struct app_context *ctx, char *arguments)
|
||||
{
|
||||
@ -3901,6 +4075,8 @@ g_command_handlers[] =
|
||||
"[<message>]" },
|
||||
{ "buffer", handle_command_buffer, "Manage buffers",
|
||||
"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",
|
||||
"<target> <message>" },
|
||||
|
Loading…
Reference in New Issue
Block a user