mirror of
https://github.com/emersion/kanshi
synced 2024-11-26 05:55:27 +01:00
Add 'include' directive to read additional configs
This commit is contained in:
parent
7095bedd25
commit
9731ff9c79
20
kanshi.5.scd
20
kanshi.5.scd
@ -6,12 +6,14 @@ kanshi - configuration file
|
|||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
A kanshi configuration file is a list of profiles. Each profile has an
|
A kanshi configuration file is a list of profiles. Each profile has an optional
|
||||||
optional name and contains directives delimited by brackets (*{* and *}*).
|
name and contains profile directives delimited by brackets (*{* and *}*).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
include /etc/kanshi/config.d/*
|
||||||
|
|
||||||
profile {
|
profile {
|
||||||
output LVDS-1 disable
|
output LVDS-1 disable
|
||||||
output "Some Company ASDF 4242" mode 1600x900 position 0,0
|
output "Some Company ASDF 4242" mode 1600x900 position 0,0
|
||||||
@ -24,8 +26,18 @@ profile nomad {
|
|||||||
|
|
||||||
# DIRECTIVES
|
# DIRECTIVES
|
||||||
|
|
||||||
Directives are followed by space-separated arguments. Arguments can be quoted
|
*profile* [<name>] { <profile directives...> }
|
||||||
(with *"*) if they contain spaces.
|
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...>
|
*output* <criteria> <output-command...>
|
||||||
An output directive adds an output to the profile. The criteria can either
|
An output directive adds an output to the profile. The criteria can either
|
||||||
|
77
parser.c
77
parser.c
@ -6,6 +6,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <wordexp.h>
|
||||||
|
|
||||||
#include <wayland-client.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) {
|
static bool parse_config_file(const char *path, struct kanshi_config *config);
|
||||||
struct kanshi_config *config = calloc(1, sizeof(*config));
|
|
||||||
wl_list_init(&config->profiles);
|
|
||||||
|
|
||||||
|
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) {
|
while (1) {
|
||||||
int ch = parser_peek_char(parser);
|
int ch = parser_peek_char(parser);
|
||||||
if (ch < 0) {
|
if (ch < 0) {
|
||||||
return NULL;
|
return false;
|
||||||
} else if (ch == 0) {
|
} else if (ch == 0) {
|
||||||
return config;
|
return true;
|
||||||
} else if (ch == '#') {
|
} else if (ch == '#') {
|
||||||
parser_ignore_line(parser);
|
parser_ignore_line(parser);
|
||||||
continue;
|
continue;
|
||||||
@ -500,34 +532,38 @@ static struct kanshi_config *_parse_config(struct kanshi_parser *parser) {
|
|||||||
// Legacy profile syntax without a profile directive
|
// Legacy profile syntax without a profile directive
|
||||||
struct kanshi_profile *profile = parse_profile(parser);
|
struct kanshi_profile *profile = parse_profile(parser);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
wl_list_insert(config->profiles.prev, &profile->link);
|
wl_list_insert(config->profiles.prev, &profile->link);
|
||||||
} else {
|
} else {
|
||||||
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *directive = parser->tok_str;
|
const char *directive = parser->tok_str;
|
||||||
if (strcmp(parser->tok_str, "profile") == 0) {
|
if (strcmp(parser->tok_str, "profile") == 0) {
|
||||||
struct kanshi_profile *profile = parse_profile(parser);
|
struct kanshi_profile *profile = parse_profile(parser);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
wl_list_insert(config->profiles.prev, &profile->link);
|
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 {
|
} else {
|
||||||
fprintf(stderr, "unknown directive '%s'\n", directive);
|
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");
|
FILE *f = fopen(path, "r");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
fprintf(stderr, "failed to open file\n");
|
fprintf(stderr, "failed to open file\n");
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct kanshi_parser parser = {
|
struct kanshi_parser parser = {
|
||||||
@ -536,11 +572,26 @@ struct kanshi_config *parse_config(const char *path) {
|
|||||||
.line = 1,
|
.line = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kanshi_config *config = _parse_config(&parser);
|
bool res = _parse_config(&parser, config);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if (config == NULL) {
|
if (!res) {
|
||||||
fprintf(stderr, "failed to parse config file: "
|
fprintf(stderr, "failed to parse config file: "
|
||||||
"error on line %d, column %d\n", parser.line, parser.col);
|
"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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user