ipc: return all included configs in GET_CONFIG

The corresponding i3 feature[1] was added in i3 4.20.
`variable_replaced_contents` is not implemented, because Sway does
variable substitutions differently and it's much harder to obtain a copy
of the config data with substitutions.

Fixes: #5559

[1]: https://github.com/i3/i3/pull/4528
This commit is contained in:
Aleksei Bavshin 2023-12-24 20:53:51 -08:00
parent 646019cad9
commit 694165b551
No known key found for this signature in database
GPG key ID: 4F071603387A382A
6 changed files with 83 additions and 67 deletions

View file

@ -481,6 +481,11 @@ enum xwayland_mode {
XWAYLAND_MODE_IMMEDIATE,
};
struct sway_config_file {
const char *data;
const char *path;
};
/**
* The configuration struct. The result of loading a config file.
*/
@ -550,10 +555,9 @@ struct sway_config {
int gaps_inner;
struct side_gaps gaps_outer;
list_t *config_chain;
list_t *config_chain; /* list of struct sway_config_file* */
bool user_config_path;
const char *current_config_path;
const char *current_config;
struct sway_config_file *current_config;
int current_config_line_number;
char *current_config_line;

View file

@ -92,7 +92,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
if (config->reading && *src != '/') {
// src file is inside configuration dir
char *conf = strdup(config->current_config_path);
char *conf = strdup(config->current_config->path);
if (!conf) {
sway_log(SWAY_ERROR, "Failed to duplicate string");
free(src);

View file

@ -22,7 +22,7 @@ static void do_reload(void *data) {
const char *path = NULL;
if (config->user_config_path) {
path = config->current_config_path;
path = config->current_config->path;
}
if (!load_main_config(path, true, false)) {
@ -59,7 +59,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
const char *path = NULL;
if (config->user_config_path) {
path = config->current_config_path;
path = config->current_config->path;
}
if (!load_main_config(path, true, true)) {

View file

@ -99,6 +99,15 @@ static void free_mode(struct sway_mode *mode) {
free(mode);
}
static void free_config_file(struct sway_config_file *config_file) {
if (!config_file) {
return;
}
free((char *)config_file->data);
free((char *)config_file->path);
free(config_file);
}
void free_config(struct sway_config *config) {
if (!config) {
return;
@ -165,9 +174,14 @@ void free_config(struct sway_config *config) {
}
list_free(config->criteria);
}
if (config->config_chain) {
for (int i = 0; i < config->config_chain->length; ++i) {
free_config_file(config->config_chain->items[i]);
}
list_free(config->config_chain);
}
list_free(config->no_focus);
list_free(config->active_bar_modifiers);
list_free_items_and_destroy(config->config_chain);
free(config->floating_scroll_up_cmd);
free(config->floating_scroll_down_cmd);
free(config->floating_scroll_left_cmd);
@ -175,8 +189,6 @@ void free_config(struct sway_config *config) {
free(config->font);
free(config->swaybg_command);
free(config->swaynag_command);
free((char *)config->current_config_path);
free((char *)config->current_config);
keysym_translation_state_destroy(config->keysym_translation_state);
free(config);
}
@ -295,7 +307,6 @@ static void config_defaults(struct sway_config *config) {
if (!(config->swaybg_command = strdup("swaybg"))) goto cleanup;
if (!(config->config_chain = create_list())) goto cleanup;
config->current_config_path = NULL;
config->current_config = NULL;
// borders
@ -411,24 +422,24 @@ static char *get_config_path(void) {
return path;
}
static bool load_config(const char *path, struct sway_config *config,
struct swaynag_instance *swaynag) {
if (path == NULL) {
static bool load_config(struct sway_config_file *cf,
struct sway_config *config, struct swaynag_instance *swaynag) {
if (cf->path == NULL) {
sway_log(SWAY_ERROR, "Unable to find a config file!");
return false;
}
sway_log(SWAY_INFO, "Loading config from %s", path);
sway_log(SWAY_INFO, "Loading config from %s", cf->path);
struct stat sb;
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
sway_log(SWAY_ERROR, "%s is a directory not a config file", path);
if (stat(cf->path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
sway_log(SWAY_ERROR, "%s is a directory not a config file", cf->path);
return false;
}
FILE *f = fopen(path, "r");
FILE *f = fopen(cf->path, "r");
if (!f) {
sway_log(SWAY_ERROR, "Unable to open %s for reading", path);
sway_log(SWAY_ERROR, "Unable to open %s for reading", cf->path);
return false;
}
@ -498,13 +509,15 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
}
}
config->current_config = malloc(sizeof(struct sway_config_file));
config->current_config->path = real_path;
config->user_config_path = file ? true : false;
config->current_config_path = path;
list_add(config->config_chain, real_path);
list_add(config->config_chain, config->current_config);
config->reading = true;
bool success = load_config(path, config, &config->swaynag_config_errors);
bool success = load_config(config->current_config, config,
&config->swaynag_config_errors);
if (validating) {
free_config(config);
@ -552,7 +565,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
static bool load_include_config(const char *path, const char *parent_dir,
struct sway_config *config, struct swaynag_instance *swaynag) {
// save parent config
const char *parent_config = config->current_config_path;
struct sway_config_file *parent_config = config->current_config;
char *full_path;
int len = strlen(path);
@ -580,8 +593,8 @@ static bool load_include_config(const char *path, const char *parent_dir,
// 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) {
struct sway_config_file *old = config->config_chain->items[j];
if (strcmp(real_path, old->path) == 0) {
sway_log(SWAY_DEBUG,
"%s already included once, won't be included again.",
real_path);
@ -590,26 +603,27 @@ static bool load_include_config(const char *path, const char *parent_dir,
}
}
config->current_config_path = real_path;
list_add(config->config_chain, real_path);
config->current_config = calloc(1, sizeof(struct sway_config_file));
config->current_config->path = real_path;
list_add(config->config_chain, config->current_config);
int index = config->config_chain->length - 1;
if (!load_config(real_path, config, swaynag)) {
free(real_path);
config->current_config_path = parent_config;
if (!load_config(config->current_config, config, swaynag)) {
free_config_file(config->current_config);
config->current_config = parent_config;
list_del(config->config_chain, index);
return false;
}
// restore current_config_path
config->current_config_path = parent_config;
// restore current_config
config->current_config = parent_config;
return true;
}
void load_include_configs(const char *path, struct sway_config *config,
struct swaynag_instance *swaynag) {
char *wd = getcwd(NULL, 0);
char *parent_path = strdup(config->current_config_path);
char *parent_path = strdup(config->current_config->path);
const char *parent_dir = dirname(parent_path);
if (chdir(parent_dir) < 0) {
@ -744,27 +758,24 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
bool read_config(FILE *file, struct sway_config *config,
struct swaynag_instance *swaynag) {
bool reading_main_config = false;
char *this_config = NULL;
size_t config_size = 0;
if (config->current_config == NULL) {
reading_main_config = true;
int fd = fileno(file);
struct stat sb;
int ret_seek = fseek(file, 0, SEEK_END);
long ret_tell = ftell(file);
if (ret_seek == -1 || ret_tell == -1) {
sway_log(SWAY_ERROR, "Unable to get size of config file");
return false;
}
config_size = ret_tell;
rewind(file);
config->current_config = this_config = calloc(1, config_size + 1);
if (this_config == NULL) {
sway_log(SWAY_ERROR, "Unable to allocate buffer for config contents");
return false;
}
if (fd == -1 || fstat(fd, &sb) == -1) {
sway_log(SWAY_ERROR, "Unable to get size of config file");
return false;
}
config->current_config->data = this_config = calloc(1, sb.st_size + 1);
if (this_config == NULL) {
sway_log(SWAY_ERROR, "Unable to allocate buffer for config contents");
return false;
}
if (fread(this_config, 1, sb.st_size, file) != (size_t)sb.st_size) {
sway_log(SWAY_ERROR, "Config file read error");
return false;
}
rewind(file);
bool success = true;
int line_number = 0;
@ -772,20 +783,8 @@ bool read_config(FILE *file, struct sway_config *config,
size_t line_size = 0;
ssize_t nread;
list_t *stack = create_list();
size_t read = 0;
int nlines = 0;
while ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) {
if (reading_main_config) {
if (read + nread > config_size) {
sway_log(SWAY_ERROR, "Config file changed during reading");
success = false;
break;
}
strcpy(&this_config[read], line);
read += nread;
}
if (line[nread - 1] == '\n') {
line[nread - 1] = '\0';
}
@ -825,11 +824,11 @@ bool read_config(FILE *file, struct sway_config *config,
case CMD_FAILURE:
case CMD_INVALID:
sway_log(SWAY_ERROR, "Error on line %i '%s': %s (%s)", line_number,
line, res->error, config->current_config_path);
line, res->error, config->current_config->path);
if (!config->validating) {
swaynag_log(config->swaynag_command, swaynag,
"Error on line %i (%s) '%s': %s", line_number,
config->current_config_path, line, res->error);
config->current_config->path, line, res->error);
}
success = false;
break;
@ -893,7 +892,7 @@ void config_add_swaynag_warning(char *fmt, ...) {
swaynag_log(config->swaynag_command, &config->swaynag_config_errors,
"Warning on line %i (%s) '%s': %s",
config->current_config_line_number, config->current_config_path,
config->current_config_line_number, config->current_config->path,
config->current_config_line, str);
free(str);

View file

@ -231,7 +231,7 @@ json_object *ipc_json_get_version(void) {
json_object_object_add(version, "major", json_object_new_int(major));
json_object_object_add(version, "minor", json_object_new_int(minor));
json_object_object_add(version, "patch", json_object_new_int(patch));
json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path));
json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config->path));
return version;
}

View file

@ -901,7 +901,20 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
case IPC_GET_CONFIG:
{
json_object *json = json_object_new_object();
json_object_object_add(json, "config", json_object_new_string(config->current_config));
json_object_object_add(json, "config",
json_object_new_string(config->current_config->data));
json_object *includes =
json_object_new_array_ext(config->config_chain->length);
for (int i = 0; i < config->config_chain->length; ++i) {
struct sway_config_file *cf = config->config_chain->items[i];
json_object *include = json_object_new_object();
json_object_object_add(include, "path",
json_object_new_string(cf->path));
json_object_object_add(include, "raw_contents",
json_object_new_string(cf->data));
json_object_array_add(includes, include);
}
json_object_object_add(json, "included_configs", includes);
const char *json_string = json_object_to_json_string(json);
ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string));