From 6dbd87016fc9f86c1681c3c438dbaeb7065b370b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 19:48:13 +0200 Subject: [PATCH 01/10] config: prefer loading configuration from $XDG_CONFIG_HOME/foot/foot.ini --- config.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 217 insertions(+), 43 deletions(-) diff --git a/config.c b/config.c index 8a7a3476..c1f59b6c 100644 --- a/config.c +++ b/config.c @@ -6,11 +6,13 @@ #include #include #include -#include -#include -#include #include #include +#include +#include + +#include +#include #include #include @@ -161,50 +163,210 @@ get_shell(void) return xstrdup(shell); } -static char * -get_config_path_user_config(void) +struct config_file { + char *path; /* Full, absolute, path */ + int fd; /* FD of file, O_RDONLY */ +}; + +struct path_component { + const char *component; + int fd; +}; +typedef tll(struct path_component) path_components_t; + +static void +path_component_add(path_components_t *components, const char *comp, int fd) { - struct passwd *passwd = getpwuid(getuid()); - if (passwd == NULL) { - LOG_ERRNO("failed to lookup user"); - return NULL; + assert(comp != NULL); + assert(fd >= 0); + + struct path_component pc = {.component = comp, .fd = fd}; + tll_push_back(*components, pc); +} + +static void +path_component_destroy(struct path_component *component) +{ + assert(component->fd >= 0); + close(component->fd); +} + +static void +path_components_destroy(path_components_t *components) +{ + tll_foreach(*components, it) { + path_component_destroy(&it->item); + tll_remove(*components, it); + } +} + +static struct config_file +path_components_to_config_file(const path_components_t *components) +{ + if (tll_length(*components) == 0) + goto err; + + size_t len = 0; + tll_foreach(*components, it) + len += strlen(it->item.component) + 1; + + char *path = malloc(len); + if (path == NULL) + goto err; + + size_t idx = 0; + tll_foreach(*components, it) { + strcpy(&path[idx], it->item.component); + idx += strlen(it->item.component); + path[idx++] = '/'; + } + path[idx - 1] = '\0'; /* Strip last ’/’ */ + + int fd_copy = dup(tll_back(*components).fd); + if (fd_copy < 0) { + free(path); + goto err; } - const char *home_dir = passwd->pw_dir; - LOG_DBG("user's home directory: %s", home_dir); + return (struct config_file){.path = path, .fd = fd_copy}; - char *path = xasprintf("%s/.config/footrc", home_dir); - return path; +err: + return (struct config_file){.path = NULL, .fd = -1}; } -static char * -get_config_path_xdg(void) +static const char * +get_user_home_dir(void) { - const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (xdg_config_home == NULL) + const struct passwd *passwd = getpwuid(getuid()); + if (passwd == NULL) return NULL; - - char *path = xasprintf("%s/footrc", xdg_config_home); - return path; + return passwd->pw_dir; } -static char * -get_config_path(void) +static bool +try_open_file(path_components_t *components, const char *name) { + int parent_fd = tll_back(*components).fd; + struct stat st; + if (fstatat(parent_fd, name, &st, 0) == 0 && S_ISREG(st.st_mode)) { + int fd = openat(parent_fd, name, O_RDONLY); + if (fd >= 0) { + path_component_add(components, name, fd); + return true; + } + } - char *config = get_config_path_xdg(); - if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode)) - return config; - free(config); + return false; +} - /* 'Default' XDG_CONFIG_HOME */ - config = get_config_path_user_config(); - if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode)) - return config; - free(config); +static struct config_file +open_config(struct config *conf) +{ + struct config_file ret = {.path = NULL, .fd = -1}; + bool log_deprecation = false; - return NULL; + path_components_t components = tll_init(); + + const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char *user_home_dir = get_user_home_dir(); + char *xdg_config_dirs_copy = NULL; + + /* Use XDG_CONFIG_HOME, or ~/.config */ + if (xdg_config_home != NULL) { + int fd = open(xdg_config_home, O_RDONLY); + if (fd >= 0) + path_component_add(&components, xdg_config_home, fd); + } else if (user_home_dir != NULL) { + int home_fd = open(user_home_dir, O_RDONLY); + if (home_fd >= 0) { + int config_fd = openat(home_fd, ".config", O_RDONLY); + if (config_fd >= 0) { + path_component_add(&components, user_home_dir, home_fd); + path_component_add(&components, ".config", config_fd); + } else + close(home_fd); + } + } + + /* First look for foot/foot.ini */ + if (tll_length(components) > 0) { + int foot_fd = openat(tll_back(components).fd, "foot", O_RDONLY); + if (foot_fd >= 0) { + path_component_add(&components, "foot", foot_fd); + + if (try_open_file(&components, "foot.ini")) + goto done; + + struct path_component pc = tll_pop_back(components); + path_component_destroy(&pc); + } + } + + /* Next try footrc */ + if (tll_length(components) > 0 && try_open_file(&components, "footrc")) { + log_deprecation = true; + goto done; + } + + /* Finally, try foot/foot.ini in all XDG_CONFIG_DIRS */ + const char *xdg_config_dirs = getenv("XDG_CONFIG_DIRS"); + xdg_config_dirs_copy = xdg_config_dirs != NULL + ? strdup(xdg_config_dirs) : NULL; + + if (xdg_config_dirs_copy != NULL) { + for (char *save = NULL, + *xdg_dir = strtok_r(xdg_config_dirs_copy, ":", &save); + xdg_dir != NULL; + xdg_dir = strtok_r(NULL, ":", &save)) + { + path_components_destroy(&components); + + int xdg_fd = open(xdg_dir, O_RDONLY); + if (xdg_fd < 0) + continue; + + int foot_fd = openat(xdg_fd, "foot", O_RDONLY); + if (foot_fd < 0) { + close(xdg_fd); + continue; + } + + assert(tll_length(components) == 0); + path_component_add(&components, xdg_dir, xdg_fd); + path_component_add(&components, "foot", foot_fd); + + if (try_open_file(&components, "foot.ini")) + goto done; + } + } + +out: + path_components_destroy(&components); + free(xdg_config_dirs_copy); + return ret; + +done: + assert(tll_length(components) > 0); + ret = path_components_to_config_file(&components); + + if (log_deprecation && ret.path != NULL) { + LOG_WARN("deprecated: configuration in $XDG_CONFIG_HOME/footrc, " + "use $XDG_CONFIG_HOME/foot/foot.ini instead"); + + char *text = xstrdup( + "configuration in \033[31m$XDG_CONFIG_HOME/footrc\033[39m or " + "\033[31m~/.config/footrc\033[39m, " + "use \033[32m$XDG_CONFIG_HOME/foot/foot.ini\033[39m or " + "\033[32m~/.config/foot/foot.ini\033[39m instead"); + + struct user_notification deprecation = { + .kind = USER_NOTIFICATION_DEPRECATED, + .text = text, + }; + tll_push_back(conf->notifications, deprecation); + } + goto out; } static bool @@ -1644,24 +1806,33 @@ config_load(struct config *conf, const char *conf_path, bool errors_are_fatal) add_default_search_bindings(conf); add_default_mouse_bindings(conf); - char *default_path = NULL; - if (conf_path == NULL) { - if ((default_path = get_config_path()) == NULL) { - /* Default conf */ - LOG_AND_NOTIFY_WARN("no configuration found, using defaults"); + struct config_file conf_file = {.path = NULL, .fd = -1}; + if (conf_path != NULL) { + int fd = open(conf_path, O_RDONLY); + if (fd < 0) { + LOG_AND_NOTIFY_ERRNO("%s: failed to open", conf_path); ret = !errors_are_fatal; goto out; } - conf_path = default_path; + conf_file.path = xstrdup(conf_path); + conf_file.fd = fd; + } else { + conf_file = open_config(conf); + if (conf_file.fd < 0) { + LOG_AND_NOTIFY_ERR("no configuration found, using defaults"); + ret = !errors_are_fatal; + goto out; + } } - assert(conf_path != NULL); - LOG_INFO("loading configuration from %s", conf_path); + assert(conf_file.path != NULL); + assert(conf_file.fd >= 0); + LOG_INFO("loading configuration from %s", conf_file.path); - FILE *f = fopen(conf_path, "r"); + FILE *f = fdopen(conf_file.fd, "r"); if (f == NULL) { - LOG_AND_NOTIFY_ERR("%s: failed to open", conf_path); + LOG_AND_NOTIFY_ERRNO("%s: failed to open", conf_file.path); ret = !errors_are_fatal; goto out; } @@ -1677,7 +1848,10 @@ out: if (ret && tll_length(conf->fonts) == 0) tll_push_back(conf->fonts, config_font_parse("monospace")); - free(default_path); + free(conf_file.path); + if (conf_file.fd < 0) + close(conf_file.fd); + return ret; } From 865127d836e6bea2459ab4eaa18d25f082ebed9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 19:48:48 +0200 Subject: [PATCH 02/10] footrc: move to foot.ini --- footrc => foot.ini | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename footrc => foot.ini (100%) diff --git a/footrc b/foot.ini similarity index 100% rename from footrc rename to foot.ini From f7c29408804bc276ae01914947a84fd85e92e5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 19:50:07 +0200 Subject: [PATCH 03/10] readme: update references to the configuration file --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 41c6854a..6dcd8fe6 100644 --- a/README.md +++ b/README.md @@ -54,11 +54,12 @@ See [INSTALL.md](INSTALL.md). ## Configuration **foot** can be configured by creating a file -`$XDG_CONFIG_HOME/footrc` (defaulting to `~/.config/footrc`). A -template for that can usually be found in `/usr/share/foot/footrc` or -[here](https://codeberg.org/dnkl/foot/src/branch/master/footrc). +`$XDG_CONFIG_HOME/foot/foot.ini` (defaulting to +`~/.config/foot/foot.ini`). A template for that can usually be found +in `/usr/share/foot/foot.ini;9~` or +[here](https://codeberg.org/dnkl/foot/src/branch/master/foot.ini). -Further information can be found in foot's man page `footrc(5)`. +Further information can be found in foot's man page `foot.ini(5)`. ## Troubleshooting @@ -104,8 +105,8 @@ fonts, _then_ fontconfig's list is used. ## Shortcuts -These are the default shortcuts. See `man 5 foot` and the example -`footrc` to see how these can be changed. +These are the default shortcuts. See `man foot.ini` and the example +`foot.ini` to see how these can be changed. ### Keyboard From 5c4af880fc62ee509015c28eb0b4c395b956081d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 20:02:30 +0200 Subject: [PATCH 04/10] doc: rename footrc.5 to foot.ini.5, and update references --- doc/foot.1.scd | 9 +++++---- doc/{footrc.5.scd => foot.ini.5.scd} | 7 +++++-- doc/meson.build | 11 ++++++----- meson.build | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) rename doc/{footrc.5.scd => foot.ini.5.scd} (99%) diff --git a/doc/foot.1.scd b/doc/foot.1.scd index eb2213d6..19e1b0ee 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -23,7 +23,8 @@ the foot command line # OPTIONS *-c*,*--config*=_PATH_ - Path to configuration file. Default: *$XDG_CONFIG_HOME/footrc*. + Path to configuration file. Default: + *$XDG_CONFIG_HOME/foot/foot.ini*. *--check-config* Verify configuration and then exit with 0 if ok, otherwise exit @@ -131,7 +132,7 @@ The following keyboard shortcuts are available. ## NORMAL MODE Note that these are just the defaults; they can be changed in -*footrc*, see *footrc*(5). +*foot.ini*, see *foot.ini*(5). *shift*+*page up*/*page down* Scroll up/down in history @@ -253,7 +254,7 @@ Finally, pressing *alt* will prefix the transmitted byte with ESC. # CONFIGURATION -See *footrc*(5) +See *foot.ini*(5) # BUGS @@ -270,4 +271,4 @@ The report should contain the following: # SEE ALSO -*footrc*(5), *footclient*(1) +*foot.ini*(5), *footclient*(1) diff --git a/doc/footrc.5.scd b/doc/foot.ini.5.scd similarity index 99% rename from doc/footrc.5.scd rename to doc/foot.ini.5.scd index 1c974d41..cede6e26 100644 --- a/doc/footrc.5.scd +++ b/doc/foot.ini.5.scd @@ -1,7 +1,7 @@ -footrc(5) +foot.ini(5) # NAME -footrc - configuration file +foot.ini - configuration file # DESCRIPTION @@ -12,8 +12,11 @@ with a _[section]_). foot will search for a configuration file in the following locations, in this order: +- _XDG_CONFIG_HOME/foot/foot.ini_ +- _~/.config/foot/foot.ini_ - _XDG_CONFIG_HOME/footrc_ - _~/.config/footrc_ +- _XDG_CONFIG_DIRS/foot/foot.init_ # SECTION: default diff --git a/doc/meson.build b/doc/meson.build index 39697496..18cff552 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -3,16 +3,17 @@ sh = find_program('sh', native: true) scdoc = dependency('scdoc', native: true) scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) -foreach man_src : ['foot.1.scd', 'footrc.5.scd', 'footclient.1.scd'] - parts = man_src.split('.') - name = parts[-3] - section = parts[-2] +foreach man_src : [{'name': 'foot', 'section' : 1}, + {'name': 'foot.ini', 'section': 5}, + {'name': 'footclient', 'section': 1}] + name = man_src['name'] + section = man_src['section'] out = '@0@.@1@'.format(name, section) custom_target( out, output: out, - input: man_src, + input: '@0@.@01@.scd'.format(name, section), command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.path())], capture: true, install: true, diff --git a/meson.build b/meson.build index d5b306a6..468626d8 100644 --- a/meson.build +++ b/meson.build @@ -163,7 +163,7 @@ install_data( install_data( 'foot.desktop', 'foot-server.desktop', install_dir: join_paths(get_option('datadir'), 'applications')) -install_data('footrc', install_dir: join_paths(get_option('datadir'), 'foot')) +install_data('foot.ini', install_dir: join_paths(get_option('datadir'), 'foot')) subdir('completions') subdir('doc') From 3309d8d779dd22d19970e11a9f648f8ddb9358ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 20:02:43 +0200 Subject: [PATCH 05/10] changelog: new configuration file location --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba356441..537e7d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ ### Deprecated * **scrollback** option in `footrc`. Use **scrollback.lines** instead. +* `$XDG_CONFIG_HOME/footrc`/`~/.config/footrc`. Use + `$XDG_CONFIG_HOME/foot/foot.ini`/`~/.config/foot/foot.ini` instead. ### Removed @@ -64,6 +66,13 @@ * Trailing empty cells are no longer highlighted in mouse selections. * Scrollback position indicator is now based on the number of _used_ scrollback lines, instead of the _total_ number of scrollback lines. +* Foot now searches for its configuration in + `$XDG_CONFIG_HOME/foot/foot.ini` (typically + `~/.config/foot/foot.ini`), before looking for + `$XDG_CONFIG_HOME/footrc`. +* Foot now searches for its configuration in + `$XDG_DATA_DIRS/foot/foot.ini`, if no configuration is found in + `$XDG_CONFIG_HOME/foot/foot.ini` or in `$XDG_CONFIG_HOME/footrc`. ### Fixed From df35b09142d280db703fd62ad5e96fa6e86a3f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 20:03:30 +0200 Subject: [PATCH 06/10] main: usage: footrc -> foot.ini --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 3f5372bb..b53aaf17 100644 --- a/main.c +++ b/main.c @@ -46,7 +46,7 @@ print_usage(const char *prog_name) "Usage: %s [OPTIONS...] command [ARGS...]\n" "\n" "Options:\n" - " -c,--config=PATH load configuration from PATH ($XDG_CONFIG_HOME/footrc)\n" + " -c,--config=PATH load configuration from PATH ($XDG_CONFIG_HOME/foot/foot.ini)\n" " --check-config verify configuration, exit with 0 if ok, otherwise exit with 1\n" " -f,--font=FONT comma separated list of fonts in fontconfig format (monospace)\n" " -t,--term=TERM value to set the environment variable TERM to (foot)\n" From fdb1f64cae72250cbf53c3cfe75bdffa3cde4b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 21:08:24 +0200 Subject: [PATCH 07/10] =?UTF-8?q?readme:=20remove=20stray=20=E2=80=99;9~?= =?UTF-8?q?=E2=80=99=20sequence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6dcd8fe6..773d6a7f 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ See [INSTALL.md](INSTALL.md). **foot** can be configured by creating a file `$XDG_CONFIG_HOME/foot/foot.ini` (defaulting to `~/.config/foot/foot.ini`). A template for that can usually be found -in `/usr/share/foot/foot.ini;9~` or +in `/usr/share/foot/foot.ini` or [here](https://codeberg.org/dnkl/foot/src/branch/master/foot.ini). Further information can be found in foot's man page `foot.ini(5)`. From a24109a5e35e6d6cbeec64ec954328af91ebb889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 21:09:54 +0200 Subject: [PATCH 08/10] doc: foot.ini: highlight configuration file paths with bold, not italic/underline --- doc/foot.ini.5.scd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index cede6e26..9f8c6cbb 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -12,11 +12,11 @@ with a _[section]_). foot will search for a configuration file in the following locations, in this order: -- _XDG_CONFIG_HOME/foot/foot.ini_ -- _~/.config/foot/foot.ini_ -- _XDG_CONFIG_HOME/footrc_ -- _~/.config/footrc_ -- _XDG_CONFIG_DIRS/foot/foot.init_ +- *XDG_CONFIG_HOME/foot/foot.ini* +- *~/.config/foot/foot.ini* +- *XDG_CONFIG_HOME/footrc* +- *~/.config/footrc* +- *XDG_CONFIG_DIRS/foot/foot.init* # SECTION: default From 6f114f639adea4bfba801d08f5e2ee206d10d0e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 21:11:03 +0200 Subject: [PATCH 09/10] config: close FD if it is *valid*, not when it is *invalid* --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index c1f59b6c..83a0ef4d 100644 --- a/config.c +++ b/config.c @@ -1849,7 +1849,7 @@ out: tll_push_back(conf->fonts, config_font_parse("monospace")); free(conf_file.path); - if (conf_file.fd < 0) + if (conf_file.fd >= 0) close(conf_file.fd); return ret; From b957f2faa7a8c0624f25dcd6190b6e2bea8ca7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 27 Aug 2020 21:22:27 +0200 Subject: [PATCH 10/10] doc: meson: @01@ -> @1@ --- doc/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/meson.build b/doc/meson.build index 18cff552..1f017db4 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -13,7 +13,7 @@ foreach man_src : [{'name': 'foot', 'section' : 1}, custom_target( out, output: out, - input: '@0@.@01@.scd'.format(name, section), + input: '@0@.@1@.scd'.format(name, section), command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.path())], capture: true, install: true,