Configuration and TOML


MontaukOS provides a small header-only C++ API for reading and writing TOML configuration files from userspace. Include <montauk/config.h> for file-backed configuration and <montauk/toml.h> for the in-memory TOML document model.

Configuration locations

System configuration is stored in 0:/config. The API accepts a name without the .toml extension:

0:/config/desktop.toml
0:/config/network.toml

Per-user configuration is stored below the user directory:

0:/users/<username>/config/<name>.toml

Loading and saving

#include <montauk/config.h>

auto doc = montauk::config::load("desktop");
const char* theme = doc.get_string("appearance.theme", "light");

montauk::config::set_string(&doc, "appearance.theme", "dark");
int result = montauk::config::save("desktop", &doc);

doc.destroy();

load() returns an initialized empty document if the file does not exist. save() creates the configuration directory and returns 0 on success or a negative value on error. Saving rewrites the whole file.

System configuration API

toml::Doc config::load(const char* name);
int       config::save(const char* name, toml::Doc* doc);
int       config::remove(const char* name);

Per-user configuration API

toml::Doc config::load_user(const char* username, const char* name);
int       config::save_user(const char* username,
                            const char* name,
                            toml::Doc* doc);

For example:

auto doc = montauk::config::load_user("alice", "desktop");
bool clock24 = doc.get_bool("display.clock_24h", false);

montauk::config::set_bool(&doc, "display.clock_24h", true);
montauk::config::save_user("alice", "desktop", &doc);
doc.destroy();

Reading values

Keys may use dotted paths corresponding to TOML tables:

[server]
host = "pool.ntp.org"
port = 123
enabled = true
const char* host = doc.get_string("server.host", "localhost");
int64_t port = doc.get_int("server.port", 80);
bool enabled = doc.get_bool("server.enabled", false);

Typed getters return their default when the key is absent or has another type. The available value types are:

toml::Type::String
toml::Type::Int
toml::Type::Bool
toml::Type::Array
toml::Type::Table

Arrays and tables are returned as toml::Value*. Their children are available through value->array.items and value->array.count:

auto* names = doc.get_array("server.names");
if (names) {
    for (int i = 0; i < names->array.count; ++i) {
        auto* item = names->array.items[i];
        if (item->type == montauk::toml::Type::String)
            montauk::print(item->str);
    }
}

Modifying documents

void config::set_string(toml::Doc* doc,
                        const char* key, const char* value);
void config::set_int(toml::Doc* doc,
                     const char* key, int64_t value);
void config::set_bool(toml::Doc* doc,
                      const char* key, bool value);
bool config::unset(toml::Doc* doc, const char* key);

The setters overwrite an existing value or append a new one. The unset() return value is true when a matching key was removed.

Parsing and serialization

Use toml::parse() when TOML is already available in memory:

const char* text =
    "[server]\n"
    "port = 8080\n"
    "enabled = true\n";

auto doc = montauk::toml::parse(text);
int64_t port = doc.get_int("server.port");
doc.destroy();

A document can be serialized to newly allocated TOML text:

char* text = montauk::config::serialize(&doc);
// Use text...
montauk::mfree(text);

Serialization produces normalized TOML and does not preserve comments or the original formatting.

Memory ownership

toml::Doc owns its parsed values and strings. Every document returned by load(), load_user(), or toml::parse() must eventually be released with doc.destroy().

When constructing a document manually, initialize it before using the mutation helpers:

montauk::toml::Doc doc;
doc.init();
montauk::config::set_bool(&doc, "enabled", true);
montauk::config::save("example", &doc);
doc.destroy();

Supported TOML features

The userspace parser supports strings, literal and multiline strings, integers (including hexadecimal, octal, and binary forms), booleans, arrays, tables, inline tables, dotted keys, and comments.

There are no typed float or datetime accessors. Callers should also treat configuration names and usernames as safe path components, since they are used to construct filesystem paths.


Back to Application Programming Manual