summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binding.h3
-rw-r--r--src/buffer.c13
-rw-r--r--src/buffer.h2
-rw-r--r--src/command.c86
-rw-r--r--src/command.h11
-rw-r--r--src/hash.h11
-rw-r--r--src/main.c8
-rw-r--r--src/minibuffer.c48
-rw-r--r--src/minibuffer.h19
-rw-r--r--src/settings.c168
-rw-r--r--src/settings.h133
11 files changed, 438 insertions, 64 deletions
diff --git a/src/binding.h b/src/binding.h
index db4867b..f2a531d 100644
--- a/src/binding.h
+++ b/src/binding.h
@@ -1,3 +1,4 @@
+#include "hash.h"
#include "keyboard.h"
/**
@@ -36,7 +37,7 @@ enum binding_type {
#define BINDING_INNER(mod_, c_, command_) \
(struct binding) { \
.key = {.mod = mod_, .key = c_}, .type = BindingType_Command, \
- .command = hash_command_name(command_) \
+ .command = hash_name(command_) \
}
#define ANONYMOUS_BINDING_INNER(mod_, c_, command_) \
diff --git a/src/buffer.c b/src/buffer.c
index 60ef0f4..2decdea 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4,6 +4,7 @@
#include "errno.h"
#include "minibuffer.h"
#include "reactor.h"
+#include "settings.h"
#include "utf8.h"
#include <fcntl.h>
@@ -149,6 +150,12 @@ void buffer_clear(struct buffer *buffer) {
buffer->dot.col = buffer->dot.line = 0;
}
+void buffer_static_init() {
+ settings_register_setting(
+ "editor.tab-width",
+ (struct setting_value){.type = Setting_Number, .number_value = 4});
+}
+
void buffer_static_teardown() {
for (uint32_t i = 0; i < KILL_RING_SZ; ++i) {
if (g_kill_ring.buffer[i].allocated) {
@@ -585,8 +592,10 @@ void buffer_newline(struct buffer *buffer) {
}
void buffer_indent(struct buffer *buffer) {
- // TODO: config
- buffer_add_text(buffer, (uint8_t *)" ", 4);
+ struct setting *setting = settings_get("editor.tab-width");
+ buffer_add_text(
+ buffer, (uint8_t *)" ",
+ setting->value.number_value > 16 ? 16 : setting->value.number_value);
}
uint32_t buffer_add_update_hook(struct buffer *buffer, update_hook_cb hook,
diff --git a/src/buffer.h b/src/buffer.h
index 57f7d4e..9a312c0 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -140,6 +140,8 @@ struct buffer {
struct buffer buffer_create(char *name, bool modeline);
void buffer_destroy(struct buffer *buffer);
+
+void buffer_static_init();
void buffer_static_teardown();
uint32_t buffer_keymaps(struct buffer *buffer, struct keymap **keymaps_out);
diff --git a/src/command.c b/src/command.c
index be021fe..55b5bb3 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1,5 +1,7 @@
+#include "command.h"
#include "buffer.h"
#include "buffers.h"
+#include "hash.h"
#include "minibuffer.h"
#include <errno.h>
@@ -26,16 +28,6 @@ void command_registry_destroy(struct commands *commands) {
commands->capacity = 0;
}
-uint32_t hash_command_name(const char *name) {
- unsigned long hash = 5381;
- int c;
-
- while ((c = *name++))
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
-
- return hash;
-}
-
uint32_t register_command(struct commands *commands, struct command command) {
if (commands->ncommands == commands->capacity) {
commands->capacity *= 2;
@@ -43,7 +35,7 @@ uint32_t register_command(struct commands *commands, struct command command) {
commands->commands, sizeof(struct hashed_command) * commands->capacity);
}
- uint32_t hash = hash_command_name(command.name);
+ uint32_t hash = hash_name(command.name);
commands->commands[commands->ncommands] =
(struct hashed_command){.command = command, .hash = hash};
@@ -60,7 +52,7 @@ void register_commands(struct commands *command_list, struct command *commands,
struct command *lookup_command(struct commands *command_list,
const char *name) {
- uint32_t needle = hash_command_name(name);
+ uint32_t needle = hash_name(name);
return lookup_command_by_hash(command_list, needle);
}
@@ -86,53 +78,68 @@ int32_t execute_command(struct command *command, struct commands *commands,
.userdata = command->userdata,
.commands = commands,
.self = command,
+ .saved_argv = {0},
+ .saved_argc = 0,
},
argc, argv);
}
+void command_ctx_push_arg(struct command_ctx *ctx, const char *argv) {
+ if (ctx->saved_argc < 64) {
+ ctx->saved_argv[ctx->saved_argc] = strdup(argv);
+ ++ctx->saved_argc;
+ }
+}
+
+void command_ctx_free(struct command_ctx *ctx) {
+ for (uint32_t i = 0; i < ctx->saved_argc; ++i) {
+ free((char *)ctx->saved_argv[i]);
+ }
+
+ ctx->saved_argc = 0;
+}
+
int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) {
const char *pth = NULL;
- if (argc == 1) {
- pth = argv[0];
- struct stat sb;
- if (stat(pth, &sb) < 0 && errno != ENOENT) {
- minibuffer_echo("stat on %s failed: %s", pth, strerror(errno));
- return 1;
- }
-
- if (S_ISDIR(sb.st_mode) && errno != ENOENT) {
- minibuffer_echo("TODO: implement dired!");
- return 1;
- }
+ if (argc == 0) {
+ return minibuffer_prompt(ctx, "find file: ");
+ }
- window_set_buffer(ctx.active_window,
- buffers_add(ctx.buffers, buffer_from_file((char *)pth)));
- minibuffer_echo_timeout(4, "buffer \"%s\" loaded",
- ctx.active_window->buffer->name);
+ pth = argv[0];
+ struct stat sb;
+ if (stat(pth, &sb) < 0 && errno != ENOENT) {
+ minibuffer_echo("stat on %s failed: %s", pth, strerror(errno));
+ return 1;
+ }
- } else {
- minibuffer_prompt(ctx, "find file: ");
+ if (S_ISDIR(sb.st_mode) && errno != ENOENT) {
+ minibuffer_echo("TODO: implement dired!");
+ return 1;
}
+ window_set_buffer(ctx.active_window,
+ buffers_add(ctx.buffers, buffer_from_file((char *)pth)));
+ minibuffer_echo_timeout(4, "buffer \"%s\" loaded",
+ ctx.active_window->buffer->name);
+
return 0;
}
int32_t write_file(struct command_ctx ctx, int argc, const char *argv[]) {
const char *pth = NULL;
- if (argc == 1) {
- pth = argv[0];
- buffer_write_to(ctx.active_window->buffer, pth);
- } else {
- minibuffer_prompt(ctx, "write to file: ");
+ if (argc == 0) {
+ return minibuffer_prompt(ctx, "write to file: ");
}
+ pth = argv[0];
+ buffer_write_to(ctx.active_window->buffer, pth);
+
return 0;
}
int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]) {
if (argc == 0) {
- minibuffer_prompt(ctx, "execute: ");
- return 0;
+ return minibuffer_prompt(ctx, "execute: ");
}
struct command *cmd = lookup_command(ctx.commands, argv[0]);
@@ -174,12 +181,11 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
if (argc == 0) {
ctx.self = &do_switch_buffer_cmd;
if (ctx.active_window->prev_buffer != NULL) {
- minibuffer_prompt(
+ return minibuffer_prompt(
ctx, "buffer (default %s): ", ctx.active_window->prev_buffer->name);
} else {
- minibuffer_prompt(ctx, "buffer: ");
+ return minibuffer_prompt(ctx, "buffer: ");
}
- return 0;
}
return execute_command(&do_switch_buffer_cmd, ctx.commands, ctx.active_window,
diff --git a/src/command.h b/src/command.h
index 35467e7..b151eb8 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,3 +1,6 @@
+#ifndef _COMMAND_H
+#define _COMMAND_H
+
/** @file command.h
* Commands and command registries
*/
@@ -40,6 +43,10 @@ struct command_ctx {
* User data set up by the command currently being executed.
*/
void *userdata;
+
+ const char *saved_argv[64];
+
+ int saved_argc;
};
/** A command function callback which holds the implementation of a command */
@@ -165,6 +172,9 @@ struct command *lookup_command(struct commands *commands, const char *name);
struct command *lookup_command_by_hash(struct commands *commands,
uint32_t hash);
+void command_ctx_push_arg(struct command_ctx *ctx, const char *argv);
+void command_ctx_free(struct command_ctx *ctx);
+
/**
* @defgroup common-commands Implementation of common commands
* @{
@@ -191,3 +201,4 @@ int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]);
int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]);
/**@}*/
+#endif
diff --git a/src/hash.h b/src/hash.h
new file mode 100644
index 0000000..0fd689b
--- /dev/null
+++ b/src/hash.h
@@ -0,0 +1,11 @@
+#include <stdint.h>
+
+static uint32_t hash_name(const char *s) {
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *s++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
diff --git a/src/main.c b/src/main.c
index 6a84dd4..31305d9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,7 @@
#include "display.h"
#include "minibuffer.h"
#include "reactor.h"
+#include "settings.h"
struct frame_allocator frame_allocator;
@@ -84,6 +85,9 @@ int main(int argc, char *argv[]) {
signal(SIGTERM, terminate);
+ settings_init(64);
+ buffer_static_init();
+
frame_allocator = frame_allocator_create(16 * 1024 * 1024);
// create reactor
@@ -103,6 +107,8 @@ int main(int argc, char *argv[]) {
sizeof(GLOBAL_COMMANDS) / sizeof(GLOBAL_COMMANDS[0]));
register_commands(&commands, BUFFER_COMMANDS,
sizeof(BUFFER_COMMANDS) / sizeof(BUFFER_COMMANDS[0]));
+ register_commands(&commands, SETTINGS_COMMANDS,
+ sizeof(SETTINGS_COMMANDS) / sizeof(SETTINGS_COMMANDS[0]));
// keymaps
struct keymap *current_keymap = NULL;
@@ -307,6 +313,7 @@ int main(int argc, char *argv[]) {
frame_allocator_clear(&frame_allocator);
}
+ minibuffer_destroy();
buffer_destroy(&minibuffer);
buffers_destroy(&buflist);
display_clear(display);
@@ -317,6 +324,7 @@ int main(int argc, char *argv[]) {
reactor_destroy(reactor);
frame_allocator_destroy(&frame_allocator);
buffer_static_teardown();
+ settings_destroy();
return 0;
}
diff --git a/src/minibuffer.c b/src/minibuffer.c
index 63cc5a8..3fa311c 100644
--- a/src/minibuffer.c
+++ b/src/minibuffer.c
@@ -1,6 +1,7 @@
#include "minibuffer.h"
#include "binding.h"
#include "buffer.h"
+#include "command.h"
#include "display.h"
#include <stdarg.h>
@@ -11,9 +12,11 @@
static struct minibuffer {
struct buffer *buffer;
struct timespec expires;
+
char prompt[128];
struct command_ctx prompt_command_ctx;
bool prompt_active;
+
struct keymap keymap;
} g_minibuffer = {0};
@@ -26,17 +29,25 @@ void draw_prompt(struct command_list *commands, void *userdata) {
int32_t execute(struct command_ctx ctx, int argc, const char *argv[]) {
if (g_minibuffer.prompt_active) {
+ struct command_ctx *c = &g_minibuffer.prompt_command_ctx;
+
struct text_chunk line = buffer_get_line(g_minibuffer.buffer, 0);
char *l = (char *)malloc(line.nbytes + 1);
memcpy(l, line.text, line.nbytes);
l[line.nbytes] = '\0';
+ // propagate any saved arguments
+ char *argv[64];
+ for (uint32_t i = 0; i < c->saved_argc; ++i) {
+ argv[i] = (char *)c->saved_argv[i];
+ }
+ argv[c->saved_argc] = l;
+ uint32_t argc = c->saved_argc + (line.nbytes > 0 ? 1 : 0);
+
// split on ' '
- const char *argv[128] = {l};
- argc = line.nbytes > 0 ? 1 : 0;
for (uint32_t bytei = 0; bytei < line.nbytes; ++bytei) {
uint8_t byte = line.text[bytei];
- if (byte == ' ') {
+ if (byte == ' ' && argc < 64) {
l[bytei] = '\0';
argv[argc] = l + bytei + 1;
++argc;
@@ -44,11 +55,11 @@ int32_t execute(struct command_ctx ctx, int argc, const char *argv[]) {
}
minibuffer_abort_prompt();
- struct command_ctx *ctx = &g_minibuffer.prompt_command_ctx;
- uint32_t res = execute_command(ctx->self, ctx->commands, ctx->active_window,
- ctx->buffers, argc, argv);
+ int32_t res = execute_command(c->self, c->commands, c->active_window,
+ c->buffers, argc, (const char **)argv);
free(l);
+
return res;
} else {
return 0;
@@ -61,16 +72,6 @@ struct command execute_minibuffer_command = {
.userdata = NULL,
};
-int32_t complete(struct command_ctx ctx, int argc, const char *argv[]) {
- return 0;
-}
-
-struct command complete_minibuffer_command = {
- .fn = complete,
- .name = "minibuffer-complete",
- .userdata = NULL,
-};
-
struct update_hook_result update(struct buffer *buffer,
struct command_list *commands, uint32_t width,
uint32_t height, uint64_t frame_time,
@@ -101,7 +102,6 @@ void minibuffer_init(struct buffer *buffer) {
struct binding bindings[] = {
ANONYMOUS_BINDING(Ctrl, 'M', &execute_minibuffer_command),
- ANONYMOUS_BINDING(Ctrl, 'I', &complete_minibuffer_command),
};
keymap_bind_keys(&g_minibuffer.keymap, bindings,
sizeof(bindings) / sizeof(bindings[0]));
@@ -128,6 +128,10 @@ void echo(uint32_t timeout, const char *fmt, va_list args) {
g_minibuffer.expires.tv_sec += timeout;
}
+void minibuffer_destroy() {
+ command_ctx_free(&g_minibuffer.prompt_command_ctx);
+}
+
void minibuffer_echo(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -142,20 +146,24 @@ void minibuffer_echo_timeout(uint32_t timeout, const char *fmt, ...) {
va_end(args);
}
-void minibuffer_prompt(struct command_ctx command_ctx, const char *fmt, ...) {
+int32_t minibuffer_prompt(struct command_ctx command_ctx, const char *fmt,
+ ...) {
if (g_minibuffer.buffer == NULL) {
- return;
+ return 1;
}
minibuffer_clear();
-
g_minibuffer.prompt_active = true;
+
+ command_ctx_free(&g_minibuffer.prompt_command_ctx);
g_minibuffer.prompt_command_ctx = command_ctx;
va_list args;
va_start(args, fmt);
vsnprintf(g_minibuffer.prompt, sizeof(g_minibuffer.prompt), fmt, args);
va_end(args);
+
+ return 0;
}
void minibuffer_abort_prompt() {
diff --git a/src/minibuffer.h b/src/minibuffer.h
index 71885ec..6845b07 100644
--- a/src/minibuffer.h
+++ b/src/minibuffer.h
@@ -16,6 +16,13 @@ struct command_ctx;
void minibuffer_init(struct buffer *buffer);
/**
+ * Destroy the minibuffer
+ *
+ * Note that this does not release the buffer used.
+ */
+void minibuffer_destroy();
+
+/**
* Echo a message to the minibuffer.
*
* @param fmt Format string for the message.
@@ -42,8 +49,10 @@ void minibuffer_echo_timeout(uint32_t timeout, const char *fmt, ...);
* command (or other command) when the user confirms the input.
* @param fmt Format string for the prompt.
* @param ... Format arguments.
+ * @returns a return code suitable to return from a command to signal more input
+ * is needed.
*/
-void minibuffer_prompt(struct command_ctx command_ctx, const char *fmt, ...);
+int32_t minibuffer_prompt(struct command_ctx command_ctx, const char *fmt, ...);
/**
* Abort the current minibuffer prompt.
@@ -53,6 +62,14 @@ void minibuffer_prompt(struct command_ctx command_ctx, const char *fmt, ...);
void minibuffer_abort_prompt();
/**
+ * Minibuffer prompt args
+ */
+struct minibuffer_prompt_args {
+ int argc;
+ const char **argv;
+};
+
+/**
* Clear the current text in the minibuffer.
*/
void minibuffer_clear();
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..08e31d4
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,168 @@
+#include "settings.h"
+#include "command.h"
+#include "hash.h"
+#include "minibuffer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct settings g_settings = {0};
+
+void settings_resize(uint32_t new_capacity) {
+ if (new_capacity > g_settings.capacity) {
+ g_settings.settings =
+ realloc(g_settings.settings, sizeof(struct setting) * new_capacity);
+ }
+}
+
+void settings_init(uint32_t initial_capacity) {
+ settings_resize(initial_capacity);
+ g_settings.capacity = initial_capacity;
+ g_settings.nsettings = 0;
+}
+
+void settings_destroy() {
+ for (uint32_t i = 0; i < g_settings.nsettings; ++i) {
+ struct setting *setting = &g_settings.settings[i];
+ if (setting->value.type == Setting_String) {
+ free(setting->value.string_value);
+ }
+ }
+
+ free(g_settings.settings);
+ g_settings.settings = NULL;
+ g_settings.capacity = 0;
+ g_settings.nsettings = 0;
+}
+
+void settings_register_setting(const char *path,
+ struct setting_value default_value) {
+ if (g_settings.nsettings + 1 == g_settings.capacity) {
+ g_settings.capacity *= 2;
+ settings_resize(g_settings.capacity);
+ }
+
+ struct setting *s = &g_settings.settings[g_settings.nsettings];
+ s->value = default_value;
+ s->hash = hash_name(path);
+ strncpy(s->path, path, 128);
+ s->path[127] = '\0';
+
+ ++g_settings.nsettings;
+}
+
+struct setting *settings_get(const char *path) {
+ uint32_t needle = hash_name(path);
+
+ for (uint32_t i = 0; i < g_settings.nsettings; ++i) {
+ struct setting *setting = &g_settings.settings[i];
+ if (setting->hash == needle) {
+ return setting;
+ }
+ }
+
+ return NULL;
+}
+
+void settings_get_prefix(const char *prefix, struct setting **settings_out[],
+ uint32_t *nsettings_out) {
+
+ uint32_t capacity = 16;
+ struct setting **res = malloc(sizeof(struct setting *) * capacity);
+ uint32_t nsettings = 0;
+ for (uint32_t i = 0; i < g_settings.nsettings; ++i) {
+ struct setting *setting = &g_settings.settings[i];
+ if (strncmp(prefix, setting->path, strlen(prefix)) == 0) {
+ if (nsettings + 1 == capacity) {
+ capacity *= 2;
+ res = realloc(res, sizeof(struct setting *) * capacity);
+ }
+
+ res[nsettings] = setting;
+ ++nsettings;
+ }
+ }
+
+ *nsettings_out = nsettings;
+ *settings_out = res;
+}
+
+void settings_set(const char *path, struct setting_value value) {
+ struct setting *setting = settings_get(path);
+ if (setting != NULL && setting->value.type == value.type) {
+ setting->value = value;
+ }
+}
+
+void setting_to_string(struct setting *setting, char *buf, size_t n) {
+ switch (setting->value.type) {
+ case Setting_Bool:
+ snprintf(buf, n, "%s", setting->value.bool_value ? "true" : false);
+ break;
+ case Setting_Number:
+ snprintf(buf, n, "%ld", setting->value.number_value);
+ break;
+ case Setting_String:
+ snprintf(buf, n, "%s", setting->value.string_value);
+ break;
+ }
+}
+
+int32_t settings_get_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
+ if (argc == 0) {
+ return minibuffer_prompt(ctx, "setting: ");
+ }
+
+ struct setting *setting = settings_get(argv[0]);
+ if (setting == NULL) {
+ minibuffer_echo_timeout(4, "no such setting \"%s\"", argv[0]);
+ return 1;
+ } else {
+ char buf[128];
+ setting_to_string(setting, buf, 128);
+ minibuffer_echo("%s = %s", argv[0], buf);
+ }
+
+ return 0;
+}
+
+int32_t settings_set_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
+ if (argc == 0) {
+ return minibuffer_prompt(ctx, "setting: ");
+ } else if (argc == 1) {
+ // validate setting here as well
+ struct setting *setting = settings_get(argv[0]);
+ if (setting == NULL) {
+ minibuffer_echo_timeout(4, "no such setting \"%s\"", argv[0]);
+ return 1;
+ }
+
+ command_ctx_push_arg(&ctx, argv[0]);
+ return minibuffer_prompt(ctx, "value: ");
+ }
+
+ struct setting *setting = settings_get(argv[0]);
+ if (setting == NULL) {
+ minibuffer_echo_timeout(4, "no such setting \"%s\"", argv[0]);
+ return 1;
+ } else {
+ const char *value = argv[1];
+ struct setting_value new_value = {.type = setting->value.type};
+ switch (setting->value.type) {
+ case Setting_Bool:
+ new_value.bool_value = strncmp("true", value, 4) == 0;
+ break;
+ case Setting_Number:
+ new_value.number_value = atol(value);
+ break;
+ case Setting_String:
+ new_value.string_value = strdup(value);
+ break;
+ }
+
+ setting->value = new_value;
+ }
+
+ return 0;
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 0000000..8d6f1f2
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,133 @@
+#include "command.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * The type of setting value.
+ */
+enum setting_type {
+ /** String setting. */
+ Setting_String = 0,
+
+ /** Number setting (a signed 64 bit integer). */
+ Setting_Number,
+
+ /** Boolean setting. */
+ Setting_Bool,
+};
+
+/**
+ * Value for a setting.
+ */
+struct setting_value {
+ /** Type of setting. */
+ enum setting_type type;
+
+ union {
+ /** String setting. */
+ char *string_value;
+
+ /** Real number setting. */
+ int64_t number_value;
+
+ /** Boolean setting value. */
+ bool bool_value;
+ };
+};
+
+/**
+ * A single setting.
+ *
+ * A setting has a "path", denoted by a string
+ * containing a number (0-) of dots.
+ * Example: editor.tab-width.
+ */
+struct setting {
+
+ /** Path of the setting. */
+ char path[128];
+
+ /** Hashed path that can be used for equality checks. */
+ uint32_t hash;
+
+ /** Value of the setting. */
+ struct setting_value value;
+};
+
+/**
+ * A collection of settings.
+ */
+struct settings {
+ /** Settings */
+ struct setting *settings;
+
+ /** Number of settings currently in collection. */
+ uint32_t nsettings;
+
+ /** Current capacity of collection. */
+ uint32_t capacity;
+};
+
+/**
+ * Initialize the global collection of settings.
+ *
+ * @param initial_capacity Initial capacity of the settings collection.
+ * @returns Nothing, the settings collection is a global instance.
+ */
+void settings_init(uint32_t initial_capacity);
+
+/**
+ * Destroy the global collection of settings.
+ */
+void settings_destroy();
+
+/**
+ * Register a new setting.
+ *
+ * @param path The path of the new setting on
+ * the form <category>.<sub-category>.<setting-name>.
+ * @param default_value The default value for the setting.
+ * All settings are required to declare a default value.
+ */
+void settings_register_setting(const char *path,
+ struct setting_value default_value);
+
+/**
+ * Retrieve a single setting by path.
+ *
+ * @param path The exact path of the setting on
+ * the form <category>.<sub-category>.<setting-name>.
+ * @returns A pointer to the setting if found, NULL otherwise.
+ */
+struct setting *settings_get(const char *path);
+
+/**
+ * Retrieve a collection of settings by prefix
+ *
+ * @param prefix Path prefix for the settings to retrieve.
+ * @param settings_out Pointer to an array that will be modified to point to the
+ * result.
+ * @param nsettings_out Pointer to an integer that will be set to the number of
+ * settings pointers in the result.
+ */
+void settings_get_prefix(const char *prefix, struct setting **settings_out[],
+ uint32_t *nsettings_out);
+
+/**
+ * Set a value for a setting.
+ *
+ * @param path The exact path of the setting on
+ * the form <category>.<sub-category>.<setting-name>.
+ * @param value The new value of the setting. The type has to match the declared
+ * type for the setting. If not, the new value is ignored.
+ */
+void settings_set(const char *path, struct setting_value value);
+
+int32_t settings_get_cmd(struct command_ctx ctx, int argc, const char *argv[]);
+int32_t settings_set_cmd(struct command_ctx ctx, int argc, const char *argv[]);
+
+static struct command SETTINGS_COMMANDS[] = {
+ {.name = "set", .fn = settings_set_cmd},
+ {.name = "get", .fn = settings_get_cmd},
+};