Add 'include' directive to read additional configs

This commit is contained in:
Kenny Levinsen 2020-09-25 12:31:39 +02:00 committed by Simon Ser
parent 7095bedd25
commit 9731ff9c79
2 changed files with 80 additions and 17 deletions

View File

@ -6,12 +6,14 @@ kanshi - configuration file
# DESCRIPTION
A kanshi configuration file is a list of profiles. Each profile has an
optional name and contains directives delimited by brackets (*{* and *}*).
A kanshi configuration file is a list of profiles. Each profile has an optional
name and contains profile directives delimited by brackets (*{* and *}*).
Example:
```
include /etc/kanshi/config.d/*
profile {
output LVDS-1 disable
output "Some Company ASDF 4242" mode 1600x900 position 0,0
@ -24,8 +26,18 @@ profile nomad {
# DIRECTIVES
Directives are followed by space-separated arguments. Arguments can be quoted
(with *"*) if they contain spaces.
*profile* [<name>] { <profile directives...> }
Defines a new profile using the specified bracket-delimited profile
directives. A name can be specified but is optional.
*include* <path>
Include as another file from _path_. Expands shell syntax (see *wordexp*(3)
for details).
# PROFILE DIRECTIVES
Profile directives are followed by space-separated arguments. Arguments can be
quoted (with *"*) if they contain spaces.
*output* <criteria> <output-command...>
An output directive adds an output to the profile. The criteria can either

View File

@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wordexp.h>
#include <wayland-client.h>
@ -478,16 +479,47 @@ static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
}
}
static struct kanshi_config *_parse_config(struct kanshi_parser *parser) {
struct kanshi_config *config = calloc(1, sizeof(*config));
wl_list_init(&config->profiles);
static bool parse_config_file(const char *path, struct kanshi_config *config);
static bool parse_include_command(struct kanshi_parser *parser, struct kanshi_config *config) {
// Skip the 'include' directive.
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
return false;
}
if (!parser_read_line(parser)) {
return false;
}
if (parser->tok_str_len <= 0) {
return true;
}
wordexp_t p;
if (wordexp(parser->tok_str, &p, WRDE_SHOWERR | WRDE_UNDEF) != 0) {
fprintf(stderr, "Could not expand include path: '%s'\n", parser->tok_str);
return false;
}
char **w = p.we_wordv;
for (size_t idx = 0; idx < p.we_wordc; idx++) {
if (!parse_config_file(w[idx], config)) {
fprintf(stderr, "Could not parse included config: '%s'\n", w[idx]);
wordfree(&p);
return false;
}
}
wordfree(&p);
return true;
}
static bool _parse_config(struct kanshi_parser *parser, struct kanshi_config *config) {
while (1) {
int ch = parser_peek_char(parser);
if (ch < 0) {
return NULL;
return false;
} else if (ch == 0) {
return config;
return true;
} else if (ch == '#') {
parser_ignore_line(parser);
continue;
@ -500,34 +532,38 @@ static struct kanshi_config *_parse_config(struct kanshi_parser *parser) {
// Legacy profile syntax without a profile directive
struct kanshi_profile *profile = parse_profile(parser);
if (!profile) {
return NULL;
return false;
}
wl_list_insert(config->profiles.prev, &profile->link);
} else {
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
return NULL;
return false;
}
const char *directive = parser->tok_str;
if (strcmp(parser->tok_str, "profile") == 0) {
struct kanshi_profile *profile = parse_profile(parser);
if (!profile) {
return NULL;
return false;
}
wl_list_insert(config->profiles.prev, &profile->link);
} else if (strcmp(parser->tok_str, "include") == 0) {
if (!parse_include_command(parser, config)) {
return false;
}
} else {
fprintf(stderr, "unknown directive '%s'\n", directive);
return NULL;
return false;
}
}
}
}
struct kanshi_config *parse_config(const char *path) {
static bool parse_config_file(const char *path, struct kanshi_config *config) {
FILE *f = fopen(path, "r");
if (f == NULL) {
fprintf(stderr, "failed to open file\n");
return NULL;
return false;
}
struct kanshi_parser parser = {
@ -536,11 +572,26 @@ struct kanshi_config *parse_config(const char *path) {
.line = 1,
};
struct kanshi_config *config = _parse_config(&parser);
bool res = _parse_config(&parser, config);
fclose(f);
if (config == NULL) {
if (!res) {
fprintf(stderr, "failed to parse config file: "
"error on line %d, column %d\n", parser.line, parser.col);
return false;
}
return true;
}
struct kanshi_config *parse_config(const char *path) {
struct kanshi_config *config = calloc(1, sizeof(*config));
if (config == NULL) {
return NULL;
}
wl_list_init(&config->profiles);
if (!parse_config_file(path, config)) {
free(config);
return NULL;
}