forked from external/yambar
wip: verify configuration
We now verify the configuration (currently down to module level, but not including particles) that are present are of the expected type.
This commit is contained in:
parent
45e5f11fea
commit
aa38063e37
7 changed files with 856 additions and 27 deletions
|
@ -27,6 +27,7 @@ pkg_check_modules(ALSA REQUIRED alsa) # Module/als
|
|||
add_executable(f00bar
|
||||
bar.c bar.h
|
||||
config.c config.h
|
||||
config-verify.c config-verify.h
|
||||
decoration.h
|
||||
font.c font.h
|
||||
log.c log.h
|
||||
|
|
762
config-verify.c
Normal file
762
config-verify.c
Normal file
|
@ -0,0 +1,762 @@
|
|||
#include "config-verify.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define LOG_MODULE "config:verify"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "tllist.h"
|
||||
|
||||
typedef tll(const char *) keychain_t;
|
||||
|
||||
static keychain_t *
|
||||
chain_push(keychain_t *chain, const char *key)
|
||||
{
|
||||
tll_push_back(*chain, key);
|
||||
return chain;
|
||||
}
|
||||
|
||||
static void
|
||||
chain_pop(keychain_t *chain)
|
||||
{
|
||||
tll_pop_back(*chain);
|
||||
}
|
||||
|
||||
static const char *
|
||||
err_prefix(const keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
static char msg[4096];
|
||||
int idx = 0;
|
||||
|
||||
tll_foreach(*chain, key)
|
||||
idx += snprintf(&msg[idx], sizeof(msg) - idx, "%s.", key->item);
|
||||
|
||||
/* Remove trailing "." */
|
||||
msg[idx - 1] = '\0';
|
||||
return msg;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_string(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
const char *s = yml_value_as_string(node);
|
||||
if (s == NULL) {
|
||||
LOG_ERR("%s: value must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_int(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
if (yml_value_is_int(node))
|
||||
return true;
|
||||
|
||||
LOG_ERR("%s: value is not an integer: '%s'",
|
||||
err_prefix(chain, node), yml_value_as_string(node));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_enum(keychain_t *chain, const struct yml_node *node,
|
||||
const char *values[], size_t count)
|
||||
{
|
||||
const char *s = yml_value_as_string(node);
|
||||
if (s == NULL) {
|
||||
LOG_ERR("%s: value must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; s != NULL && i < count; i++) {
|
||||
if (strcmp(s, values[i]) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_ERR("%s: value must be one of:", err_prefix(chain, node));
|
||||
for (size_t i = 0; i < count; i++)
|
||||
LOG_ERR(" %s", values[i]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_color(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
const char *s = yml_value_as_string(node);
|
||||
if (s == NULL) {
|
||||
LOG_ERR("%s: value must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int r, g, b, a;
|
||||
int v = sscanf(s, "%02x%02x%02x%02x", &r, &g, &b, &a);
|
||||
|
||||
if (strlen(s) != 8 || v != 4) {
|
||||
LOG_ERR("%s: value must be a color ('rrggbbaa', e.g ff00ffff)",
|
||||
err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_font(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
if (!yml_is_dict(node)) {
|
||||
LOG_ERR("%s: must be a dictionary", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *sub_key = yml_value_as_string(it.key);
|
||||
if (sub_key == NULL) {
|
||||
LOG_ERR("%s: font: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(sub_key, "size") == 0 ||
|
||||
strcmp(sub_key, "y_offset") == 0)
|
||||
{
|
||||
if (!verify_int(chain_push(chain, sub_key), it.value))
|
||||
return false;
|
||||
} else if (strcmp(sub_key, "family") == 0) {
|
||||
if (!verify_string(chain_push(chain, sub_key), it.value))
|
||||
return false;
|
||||
} else {
|
||||
LOG_ERR("%s: font: invalid key: %s", err_prefix(chain, node), sub_key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_border(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
if (!yml_is_dict(node)) {
|
||||
LOG_ERR("bar: border: must be a dictionary");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "width") == 0) {
|
||||
if (!verify_int(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
} else if (strcmp(key, "color") == 0) {
|
||||
if (!verify_color(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
} else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_particle(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_alsa(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "card") == 0 ||
|
||||
strcmp(key, "mixer") == 0)
|
||||
{
|
||||
if (!verify_string(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_backlight(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "name") == 0) {
|
||||
if (!verify_string(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_battery(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "name") == 0) {
|
||||
if (!verify_string(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "poll_interval") == 0) {
|
||||
if (!verify_int(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_clock(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_i3(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "spacing") == 0 ||
|
||||
strcmp(key, "left_spacing") == 0 ||
|
||||
strcmp(key, "right_spacing") == 0)
|
||||
{
|
||||
if (!verify_int(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_label(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_mpd(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "host") == 0) {
|
||||
if (!verify_string(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "port") == 0) {
|
||||
if (!verify_int(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_network(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "name") == 0) {
|
||||
if (!verify_string(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_removables(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "spacing") == 0 ||
|
||||
strcmp(key, "left_spacing") == 0 ||
|
||||
strcmp(key, "right_spacing") == 0)
|
||||
{
|
||||
if (!verify_int(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_xkb(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_xwindow(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
for (struct yml_dict_iter it = yml_dict_iter(node);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("%s: key must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(key, "content") == 0) {
|
||||
if (!verify_particle(chain_push(chain, key), it.value))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "anchors") == 0) {
|
||||
/* Skip */
|
||||
chain_push(chain, key);
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(chain, node), key);
|
||||
return false;
|
||||
}
|
||||
|
||||
chain_pop(chain);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
if (!yml_is_dict(node) || yml_dict_length(node) != 1) {
|
||||
LOG_ERR("%s: module must be a dictionary with a single key; "
|
||||
"the name of the module", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct yml_dict_iter m = yml_dict_iter(node);
|
||||
const struct yml_node *module = m.key;
|
||||
const struct yml_node *values = m.value;
|
||||
|
||||
const char *mod_name = yml_value_as_string(module);
|
||||
if (mod_name == NULL) {
|
||||
LOG_ERR("%s: module name must be a string", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
bool (*verify_fun)(keychain_t *chain, const struct yml_node *node);
|
||||
} modules[] = {
|
||||
{"alsa", &verify_module_alsa},
|
||||
{"backlight", &verify_module_backlight},
|
||||
{"battery", &verify_module_battery},
|
||||
{"clock", &verify_module_clock},
|
||||
{"i3", &verify_module_i3},
|
||||
{"label", &verify_module_label},
|
||||
{"mpd", &verify_module_mpd},
|
||||
{"network", &verify_module_network},
|
||||
{"removables", &verify_module_removables},
|
||||
{"xkb", &verify_module_xkb},
|
||||
{"xwindow", &verify_module_xwindow},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(modules) / sizeof(modules[0]); i++) {
|
||||
if (strcmp(mod_name, modules[i].name) == 0) {
|
||||
if (!modules[i].verify_fun(chain_push(chain, mod_name), values))
|
||||
return false;
|
||||
chain_pop(chain);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERR("%s: invalid module name: %s", err_prefix(chain, node), mod_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
verify_module_list(keychain_t *chain, const struct yml_node *node)
|
||||
{
|
||||
if (!yml_is_list(node)) {
|
||||
LOG_ERR("%s: must be a list of modules", err_prefix(chain, node));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (struct yml_list_iter it = yml_list_iter(node);
|
||||
it.node != NULL;
|
||||
yml_list_next(&it))
|
||||
{
|
||||
if (!verify_module(chain, it.node))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
config_verify_bar(const struct yml_node *bar)
|
||||
{
|
||||
if (!yml_is_dict(bar)) {
|
||||
LOG_ERR("bar is not a dictionary");
|
||||
return false;
|
||||
}
|
||||
|
||||
keychain_t chain = tll_init();
|
||||
chain_push(&chain, "bar");
|
||||
|
||||
bool ret = false;
|
||||
|
||||
for (struct yml_dict_iter it = yml_dict_iter(bar);
|
||||
it.key != NULL;
|
||||
yml_dict_next(&it))
|
||||
{
|
||||
if (!yml_is_scalar(it.key)) {
|
||||
LOG_ERR("bar: key is not a scalar");
|
||||
goto err;
|
||||
}
|
||||
|
||||
const char *key = yml_value_as_string(it.key);
|
||||
if (key == NULL) {
|
||||
LOG_ERR("bar: key must be a string");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (strcmp(key, "height") == 0 ||
|
||||
strcmp(key, "spacing") == 0 ||
|
||||
strcmp(key, "left_spacing") == 0 ||
|
||||
strcmp(key, "right_spacing") == 0 ||
|
||||
strcmp(key, "margin") == 0 ||
|
||||
strcmp(key, "left_margin") == 0 ||
|
||||
strcmp(key, "right_margin") == 0)
|
||||
{
|
||||
if (!verify_int(chain_push(&chain, key), it.value))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "location") == 0) {
|
||||
if (!verify_enum(chain_push(&chain, key), it.value,
|
||||
(const char *[]){"top", "bottom"}, 2))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "background") == 0) {
|
||||
if (!verify_color(chain_push(&chain, key), it.value))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "border") == 0) {
|
||||
if (!verify_border(chain_push(&chain, key), it.value))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "font") == 0) {
|
||||
if (!verify_font(chain_push(&chain, key), it.value))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else if (strcmp(key, "left") == 0 ||
|
||||
strcmp(key, "center") == 0 ||
|
||||
strcmp(key, "right") == 0)
|
||||
{
|
||||
if (!verify_module_list(chain_push(&chain, key), it.value))
|
||||
goto err;
|
||||
}
|
||||
|
||||
else {
|
||||
LOG_ERR("%s: invalid key: %s", err_prefix(&chain, bar), key);
|
||||
goto err;
|
||||
}
|
||||
|
||||
LOG_DBG("%s: verified", key);
|
||||
chain_pop(&chain);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
err:
|
||||
tll_free(chain);
|
||||
return ret;
|
||||
}
|
5
config-verify.h
Normal file
5
config-verify.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include "yml.h"
|
||||
|
||||
bool config_verify_bar(const struct yml_node *bar);
|
36
config.c
36
config.c
|
@ -33,6 +33,8 @@
|
|||
#include "modules/xkb.h"
|
||||
#include "modules/xwindow.h"
|
||||
|
||||
#include "config-verify.h"
|
||||
|
||||
static uint8_t
|
||||
hex_nibble(char hex)
|
||||
{
|
||||
|
@ -590,6 +592,9 @@ module_alsa_from_config(const struct yml_node *node,
|
|||
struct bar *
|
||||
conf_to_bar(const struct yml_node *bar)
|
||||
{
|
||||
if (!config_verify_bar(bar))
|
||||
return NULL;
|
||||
|
||||
struct bar_config conf = {0};
|
||||
|
||||
/* Create a default font */
|
||||
|
@ -676,33 +681,36 @@ conf_to_bar(const struct yml_node *bar)
|
|||
it.node != NULL;
|
||||
yml_list_next(&it), idx++)
|
||||
{
|
||||
const struct yml_node *n = yml_get_value(it.node, "module");
|
||||
assert(yml_is_dict(it.node));
|
||||
assert(yml_dict_length(it.node) == 1);
|
||||
|
||||
assert(n != NULL);
|
||||
const char *mod_name = yml_value_as_string(n);
|
||||
struct yml_dict_iter m = yml_dict_iter(it.node);
|
||||
|
||||
const char *mod_name = yml_value_as_string(m.key);
|
||||
assert(mod_name != NULL);
|
||||
|
||||
if (strcmp(mod_name, "label") == 0)
|
||||
mods[idx] = module_label_from_config(it.node, font);
|
||||
mods[idx] = module_label_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "clock") == 0)
|
||||
mods[idx] = module_clock_from_config(it.node, font);
|
||||
mods[idx] = module_clock_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "xwindow") == 0)
|
||||
mods[idx] = module_xwindow_from_config(it.node, font);
|
||||
mods[idx] = module_xwindow_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "i3") == 0)
|
||||
mods[idx] = module_i3_from_config(it.node, font);
|
||||
mods[idx] = module_i3_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "battery") == 0)
|
||||
mods[idx] = module_battery_from_config(it.node, font);
|
||||
mods[idx] = module_battery_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "xkb") == 0)
|
||||
mods[idx] = module_xkb_from_config(it.node, font);
|
||||
mods[idx] = module_xkb_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "backlight") == 0)
|
||||
mods[idx] = module_backlight_from_config(it.node, font);
|
||||
mods[idx] = module_backlight_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "mpd") == 0)
|
||||
mods[idx] = module_mpd_from_config(it.node, font);
|
||||
mods[idx] = module_mpd_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "network") == 0)
|
||||
mods[idx] = module_network_from_config(it.node, font);
|
||||
mods[idx] = module_network_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "removables") == 0)
|
||||
mods[idx] = module_removables_from_config(it.node, font);
|
||||
mods[idx] = module_removables_from_config(m.value, font);
|
||||
else if (strcmp(mod_name, "alsa") == 0)
|
||||
mods[idx] = module_alsa_from_config(it.node, font);
|
||||
mods[idx] = module_alsa_from_config(m.value, font);
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
|
17
main.c
17
main.c
|
@ -82,7 +82,12 @@ main(int argc, const char *const *argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
free(config_path);
|
||||
const struct yml_node *bar_conf = yml_get_value(conf, "bar");
|
||||
if (bar_conf == NULL) {
|
||||
LOG_ERR("%s: missing required top level key 'bar'", config_path);
|
||||
free(config_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xcb_init();
|
||||
|
||||
|
@ -92,10 +97,18 @@ main(int argc, const char *const *argv)
|
|||
int abort_fd = eventfd(0, EFD_CLOEXEC);
|
||||
if (abort_fd == -1) {
|
||||
LOG_ERRNO("failed to create eventfd (for abort signalling)");
|
||||
free(config_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct bar *bar = conf_to_bar(yml_get_value(conf, "bar"));
|
||||
struct bar *bar = conf_to_bar(bar_conf);
|
||||
if (bar == NULL) {
|
||||
LOG_ERR("%s: failed to load configuration", config_path);
|
||||
free(config_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(config_path);
|
||||
|
||||
struct bar_run_context bar_ctx = {
|
||||
.bar = bar,
|
||||
|
|
59
yml.c
59
yml.c
|
@ -682,42 +682,79 @@ yml_dict_length(const struct yml_node *dict)
|
|||
const char *
|
||||
yml_value_as_string(const struct yml_node *value)
|
||||
{
|
||||
assert(yml_is_scalar(value));
|
||||
if (!yml_is_scalar(value))
|
||||
return NULL;
|
||||
return value->scalar.value;
|
||||
}
|
||||
|
||||
static bool
|
||||
_as_int(const struct yml_node *value, long *ret)
|
||||
{
|
||||
const char *s = yml_value_as_string(value);
|
||||
if (s == NULL)
|
||||
return false;
|
||||
|
||||
int cnt;
|
||||
int res = sscanf(s, "%ld%n", ret, &cnt);
|
||||
return res == 1 && strlen(s) == (size_t)cnt;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_value_is_int(const struct yml_node *value)
|
||||
{
|
||||
long dummy;
|
||||
return _as_int(value, &dummy);
|
||||
}
|
||||
|
||||
long
|
||||
yml_value_as_int(const struct yml_node *value)
|
||||
{
|
||||
assert(yml_is_scalar(value));
|
||||
|
||||
long ival;
|
||||
int res = sscanf(yml_value_as_string(value), "%ld", &ival);
|
||||
return res != 1 ? -1 : ival;
|
||||
long ret = -1;
|
||||
_as_int(value, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_value_as_bool(const struct yml_node *value)
|
||||
static bool
|
||||
_as_bool(const struct yml_node *value, bool *ret)
|
||||
{
|
||||
if (!yml_is_scalar(value))
|
||||
return false;
|
||||
|
||||
const char *v = yml_value_as_string(value);
|
||||
if (strcasecmp(v, "y") == 0 ||
|
||||
strcasecmp(v, "yes") == 0 ||
|
||||
strcasecmp(v, "true") == 0 ||
|
||||
strcasecmp(v, "on") == 0)
|
||||
{
|
||||
*ret = true;
|
||||
return true;
|
||||
} else if (strcasecmp(v, "n") == 0 ||
|
||||
strcasecmp(v, "no") == 0 ||
|
||||
strcasecmp(v, "false") == 0 ||
|
||||
strcasecmp(v, "off") == 0)
|
||||
{
|
||||
return false;
|
||||
} else
|
||||
assert(false);
|
||||
*ret = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
yml_value_is_bool(const struct yml_node *value)
|
||||
{
|
||||
bool dummy;
|
||||
return _as_bool(value, &dummy);
|
||||
}
|
||||
|
||||
bool
|
||||
yml_value_as_bool(const struct yml_node *value)
|
||||
{
|
||||
bool ret;
|
||||
_as_bool(value, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
_print_node(const struct yml_node *n, int indent)
|
||||
{
|
||||
|
|
3
yml.h
3
yml.h
|
@ -32,6 +32,9 @@ struct yml_dict_iter yml_dict_iter(const struct yml_node *dict);
|
|||
void yml_dict_next(struct yml_dict_iter *iter);
|
||||
size_t yml_dict_length(const struct yml_node *dict);
|
||||
|
||||
bool yml_value_is_int(const struct yml_node *value);
|
||||
bool yml_value_is_bool(const struct yml_node *value);
|
||||
|
||||
const char *yml_value_as_string(const struct yml_node *value);
|
||||
long yml_value_as_int(const struct yml_node *value);
|
||||
bool yml_value_as_bool(const struct yml_node *value);
|
||||
|
|
Loading…
Add table
Reference in a new issue