Implement include command
The include command (`include <path>`) makes it possible to include sub config files from the main config file (or from within other sub config files). The include command uses the following rules for including config files: * the `path` can be either a full path or a path that is relative to the parent config. Shell expansion is supported, so it's possible to do `include ~/.config/sway.d/*`. * The same config file can only be included once (to prevent include cycles). If a config is included multiple times it will just be ignored after it has been included once. * Including a sub config file is the same as inserting the content of that file into the parent config, thus rules about overwriting bindsyms etc. works the same as for a single config. Implement #542
This commit is contained in:
parent
3da269b78a
commit
71a5350b68
|
@ -184,15 +184,27 @@ struct sway_config {
|
||||||
bool smart_gaps;
|
bool smart_gaps;
|
||||||
int gaps_inner;
|
int gaps_inner;
|
||||||
int gaps_outer;
|
int gaps_outer;
|
||||||
|
|
||||||
|
list_t *config_chain;
|
||||||
|
const char *current_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the config from the given path.
|
* Loads the main config from the given path. is_active should be true when
|
||||||
|
* reloading the config.
|
||||||
*/
|
*/
|
||||||
bool load_config(const char *file);
|
bool load_main_config(const char *path, bool is_active);
|
||||||
/** Reads the config from the given FILE.
|
|
||||||
|
/**
|
||||||
|
* Loads an included config. Can only be used after load_main_config.
|
||||||
*/
|
*/
|
||||||
bool read_config(FILE *file, bool is_active);
|
bool load_include_configs(const char *path, struct sway_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the config from the given FILE.
|
||||||
|
*/
|
||||||
|
bool read_config(FILE *file, struct sway_config *config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free config struct
|
* Free config struct
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -55,6 +55,7 @@ static sway_cmd cmd_font;
|
||||||
static sway_cmd cmd_for_window;
|
static sway_cmd cmd_for_window;
|
||||||
static sway_cmd cmd_fullscreen;
|
static sway_cmd cmd_fullscreen;
|
||||||
static sway_cmd cmd_gaps;
|
static sway_cmd cmd_gaps;
|
||||||
|
static sway_cmd cmd_include;
|
||||||
static sway_cmd cmd_input;
|
static sway_cmd cmd_input;
|
||||||
static sway_cmd cmd_kill;
|
static sway_cmd cmd_kill;
|
||||||
static sway_cmd cmd_layout;
|
static sway_cmd cmd_layout;
|
||||||
|
@ -1141,6 +1142,19 @@ static struct cmd_results *input_cmd_tap(int argc, char **argv) {
|
||||||
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct cmd_results *cmd_include(int argc, char **argv) {
|
||||||
|
struct cmd_results *error = NULL;
|
||||||
|
if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!load_include_configs(argv[0], config)) {
|
||||||
|
return cmd_results_new(CMD_INVALID, "include", "Failed to include sub configuration file: %s", argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static struct cmd_results *cmd_input(int argc, char **argv) {
|
static struct cmd_results *cmd_input(int argc, char **argv) {
|
||||||
struct cmd_results *error = NULL;
|
struct cmd_results *error = NULL;
|
||||||
if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
|
if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
|
||||||
|
@ -1541,7 +1555,7 @@ static struct cmd_results *cmd_reload(int argc, char **argv) {
|
||||||
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
|
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
if (!load_config(NULL)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
|
if (!load_main_config(NULL, true)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
|
||||||
|
|
||||||
load_swaybars();
|
load_swaybars();
|
||||||
|
|
||||||
|
@ -2053,6 +2067,7 @@ static struct cmd_handler handlers[] = {
|
||||||
{ "for_window", cmd_for_window },
|
{ "for_window", cmd_for_window },
|
||||||
{ "fullscreen", cmd_fullscreen },
|
{ "fullscreen", cmd_fullscreen },
|
||||||
{ "gaps", cmd_gaps },
|
{ "gaps", cmd_gaps },
|
||||||
|
{ "include", cmd_include },
|
||||||
{ "input", cmd_input },
|
{ "input", cmd_input },
|
||||||
{ "kill", cmd_kill },
|
{ "kill", cmd_kill },
|
||||||
{ "layout", cmd_layout },
|
{ "layout", cmd_layout },
|
||||||
|
|
151
sway/config.c
151
sway/config.c
|
@ -2,6 +2,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
@ -126,6 +127,7 @@ void free_config(struct sway_config *config) {
|
||||||
list_free(config->output_configs);
|
list_free(config->output_configs);
|
||||||
|
|
||||||
list_free(config->active_bar_modifiers);
|
list_free(config->active_bar_modifiers);
|
||||||
|
free_flat_list(config->config_chain);
|
||||||
free(config->font);
|
free(config->font);
|
||||||
free(config);
|
free(config);
|
||||||
}
|
}
|
||||||
|
@ -175,6 +177,9 @@ static void config_defaults(struct sway_config *config) {
|
||||||
config->gaps_outer = 0;
|
config->gaps_outer = 0;
|
||||||
|
|
||||||
config->active_bar_modifiers = create_list();
|
config->active_bar_modifiers = create_list();
|
||||||
|
|
||||||
|
config->config_chain = create_list();
|
||||||
|
config->current_config = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compare_modifiers(const void *left, const void *right) {
|
static int compare_modifiers(const void *left, const void *right) {
|
||||||
|
@ -237,16 +242,7 @@ static char *get_config_path(void) {
|
||||||
return NULL; // Not reached
|
return NULL; // Not reached
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_config(const char *file) {
|
static bool load_config(const char *path, struct sway_config *config) {
|
||||||
input_init();
|
|
||||||
|
|
||||||
char *path;
|
|
||||||
if (file != NULL) {
|
|
||||||
path = strdup(file);
|
|
||||||
} else {
|
|
||||||
path = get_config_path();
|
|
||||||
}
|
|
||||||
|
|
||||||
sway_log(L_INFO, "Loading config from %s", path);
|
sway_log(L_INFO, "Loading config from %s", path);
|
||||||
|
|
||||||
if (path == NULL) {
|
if (path == NULL) {
|
||||||
|
@ -257,20 +253,12 @@ bool load_config(const char *file) {
|
||||||
FILE *f = fopen(path, "r");
|
FILE *f = fopen(path, "r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
sway_log(L_ERROR, "Unable to open %s for reading", path);
|
sway_log(L_ERROR, "Unable to open %s for reading", path);
|
||||||
free(path);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
free(path);
|
|
||||||
|
|
||||||
bool config_load_success;
|
bool config_load_success = read_config(f, config);
|
||||||
if (config) {
|
|
||||||
config_load_success = read_config(f, true);
|
|
||||||
} else {
|
|
||||||
config_load_success = read_config(f, false);
|
|
||||||
}
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
update_active_bar_modifiers();
|
|
||||||
|
|
||||||
if (!config_load_success) {
|
if (!config_load_success) {
|
||||||
sway_log(L_ERROR, "Error(s) loading config!");
|
sway_log(L_ERROR, "Error(s) loading config!");
|
||||||
|
@ -279,17 +267,129 @@ bool load_config(const char *file) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_config(FILE *file, bool is_active) {
|
bool load_main_config(const char *file, bool is_active) {
|
||||||
|
input_init();
|
||||||
|
|
||||||
|
char *path;
|
||||||
|
if (file != NULL) {
|
||||||
|
path = strdup(file);
|
||||||
|
} else {
|
||||||
|
path = get_config_path();
|
||||||
|
}
|
||||||
|
|
||||||
struct sway_config *old_config = config;
|
struct sway_config *old_config = config;
|
||||||
config = calloc(1, sizeof(struct sway_config));
|
config = calloc(1, sizeof(struct sway_config));
|
||||||
|
|
||||||
config_defaults(config);
|
config_defaults(config);
|
||||||
config->reading = true;
|
|
||||||
if (is_active) {
|
if (is_active) {
|
||||||
sway_log(L_DEBUG, "Performing configuration file reload");
|
sway_log(L_DEBUG, "Performing configuration file reload");
|
||||||
config->reloading = true;
|
config->reloading = true;
|
||||||
config->active = true;
|
config->active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config->current_config = path;
|
||||||
|
list_add(config->config_chain, path);
|
||||||
|
|
||||||
|
config->reading = true;
|
||||||
|
bool success = load_config(path, config);
|
||||||
|
|
||||||
|
if (is_active) {
|
||||||
|
config->reloading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_config) {
|
||||||
|
free_config(old_config);
|
||||||
|
}
|
||||||
|
config->reading = false;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
update_active_bar_modifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) {
|
||||||
|
// save parent config
|
||||||
|
const char *parent_config = config->current_config;
|
||||||
|
|
||||||
|
char *full_path = strdup(path);
|
||||||
|
int len = strlen(path);
|
||||||
|
if (len >= 1 && path[0] != '/') {
|
||||||
|
len = len + strlen(parent_dir) + 2;
|
||||||
|
full_path = malloc(len * sizeof(char));
|
||||||
|
snprintf(full_path, len, "%s/%s", parent_dir, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *real_path = realpath(full_path, NULL);
|
||||||
|
free(full_path);
|
||||||
|
|
||||||
|
// check if config has already been included
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < config->config_chain->length; ++j) {
|
||||||
|
char *old_path = config->config_chain->items[j];
|
||||||
|
if (strcmp(real_path, old_path) == 0) {
|
||||||
|
sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path);
|
||||||
|
free(real_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config->current_config = real_path;
|
||||||
|
list_add(config->config_chain, real_path);
|
||||||
|
int index = config->config_chain->length - 1;
|
||||||
|
|
||||||
|
if (!load_config(real_path, config)) {
|
||||||
|
free(real_path);
|
||||||
|
config->current_config = parent_config;
|
||||||
|
list_del(config->config_chain, index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore current_config
|
||||||
|
config->current_config = parent_config;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load_include_configs(const char *path, struct sway_config *config) {
|
||||||
|
char *wd = getcwd(NULL, 0);
|
||||||
|
char *parent_path = strdup(config->current_config);
|
||||||
|
const char *parent_dir = dirname(parent_path);
|
||||||
|
|
||||||
|
if (chdir(parent_dir) < 0) {
|
||||||
|
free(parent_path);
|
||||||
|
free(wd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wordexp_t p;
|
||||||
|
|
||||||
|
if (wordexp(path, &p, 0) < 0) {
|
||||||
|
free(parent_path);
|
||||||
|
free(wd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char **w = p.we_wordv;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < p.we_wordc; ++i) {
|
||||||
|
load_include_config(w[i], parent_dir, config);
|
||||||
|
}
|
||||||
|
free(parent_path);
|
||||||
|
wordfree(&p);
|
||||||
|
|
||||||
|
// restore wd
|
||||||
|
if (chdir(wd) < 0) {
|
||||||
|
free(wd);
|
||||||
|
sway_log(L_ERROR, "failed to restore working directory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(wd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read_config(FILE *file, struct sway_config *config) {
|
||||||
bool success = true;
|
bool success = true;
|
||||||
enum cmd_status block = CMD_BLOCK_END;
|
enum cmd_status block = CMD_BLOCK_END;
|
||||||
|
|
||||||
|
@ -386,15 +486,6 @@ bool read_config(FILE *file, bool is_active) {
|
||||||
free(res);
|
free(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_active) {
|
|
||||||
config->reloading = false;
|
|
||||||
arrange_windows(&root_container, -1, -1);
|
|
||||||
}
|
|
||||||
if (old_config) {
|
|
||||||
free_config(old_config);
|
|
||||||
}
|
|
||||||
|
|
||||||
config->reading = false;
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,11 +206,11 @@ int main(int argc, char **argv) {
|
||||||
init_layout();
|
init_layout();
|
||||||
|
|
||||||
if (validate) {
|
if (validate) {
|
||||||
bool valid = load_config(config_path);
|
bool valid = load_main_config(config_path, false);
|
||||||
return valid ? 0 : 1;
|
return valid ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!load_config(config_path)) {
|
if (!load_main_config(config_path, false)) {
|
||||||
sway_terminate(EXIT_FAILURE);
|
sway_terminate(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue