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, XWAYLAND_MODE_IMMEDIATE,
}; };
struct sway_config_file {
const char *data;
const char *path;
};
/** /**
* The configuration struct. The result of loading a config file. * The configuration struct. The result of loading a config file.
*/ */
@ -550,10 +555,9 @@ struct sway_config {
int gaps_inner; int gaps_inner;
struct side_gaps gaps_outer; struct side_gaps gaps_outer;
list_t *config_chain; list_t *config_chain; /* list of struct sway_config_file* */
bool user_config_path; bool user_config_path;
const char *current_config_path; struct sway_config_file *current_config;
const char *current_config;
int current_config_line_number; int current_config_line_number;
char *current_config_line; char *current_config_line;

View file

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

View file

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

View file

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