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) \
|
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>" },
|
||||||
|
|
Loading…
Reference in New Issue