From: NeilBrown Date: Sun, 2 Jul 2023 01:31:42 +0000 (+1000) Subject: Use XDG standard to find config (and data) files. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=e88415d034e4dfd63896088c7541d4261a515638;p=edlib.git Use XDG standard to find config (and data) files. Provide an interface to search for config and data files in appropriate XDG locations. Enhance this by looking in the directory that contains the main shared library - after HOME but before system locations. Use this for config files. This removes the need to use ARGV[0] to find the initial ini file. Signed-off-by: NeilBrown --- diff --git a/Makefile b/Makefile index 8c718cf9..60258e01 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,7 @@ O/.exists: lib/.exists: @mkdir -p lib @ln -s ../python lib/python + @ln -s .. lib/edlib @touch $@ .PHONY: lib diff --git a/core-editor.c b/core-editor.c index 4b1ca01b..825971d3 100644 --- a/core-editor.c +++ b/core-editor.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,12 @@ struct ed_info { struct mark *mark_free_list; struct map *map safe; struct lookup_cmd cmd; + /* These two paths contain nul-terminated strings, + * with a double-nul at the end. + */ + char *data_path; + char *config_path; + char *here; bool testing; struct store { struct store *next; @@ -349,7 +356,11 @@ DEF_EXTERN_CMD(edlib_noop) DEF_CMD(editor_close) { + struct ed_info *ei = ci->home->data; stat_free(); + free(ei->here); ei->here = NULL; + free(ei->data_path); ei->data_path = NULL; + free(ei->config_path); ei->config_path = NULL; return Efallthrough; } @@ -465,6 +476,173 @@ void editor_delayed_mark_free(struct mark *m safe) ei->mark_free_list = m; } +static char *set_here(struct pane *p safe) +{ + struct ed_info *ei = p->data; + Dl_info info; + + if (ei->here) + ; + else if (dladdr(&set_here, &info) == 0) + ei->here = strdup(""); + else { + char *sl; + ei->here = strdup(info.dli_fname ?: ""); + sl = strrchr(ei->here, '/'); + if (sl) + *sl = 0; + } + return ei->here; +} + +static char *set_data_path(struct pane *p safe) +{ + struct ed_info *ei = p->data; + char *dh, *dd, *here; + struct buf b; + + if (ei->data_path) + return ei->data_path; + + buf_init(&b); + dh = getenv("XDG_DATA_HOME"); + if (!dh) { + char *h = getenv("HOME"); + if (h) + dh = strconcat(p, h, "/.local/share"); + } + if (dh && *dh == '/') { + buf_concat(&b, dh); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + + here = set_here(p); + if (here && *here == '/') { + buf_concat(&b, here); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + + dd = getenv("XDG_DATA_DIRS"); + if (!dd) + dd = "/usr/local/share:/usr/share"; + while (*dd) { + char *c = strchrnul(dd, ':'); + if (*dd == '/') { + buf_concat_len(&b, dd, c-dd); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + if (*c) + c++; + dd = c; + } + if (b.len) + ei->data_path = buf_final(&b); + else + free(buf_final(&b)); + return ei->data_path; +} + +static char *set_config_path(struct pane *p safe) +{ + struct ed_info *ei = p->data; + char *ch, *cd, *here; + struct buf b; + + if (ei->config_path) + return ei->config_path; + + buf_init(&b); + ch = getenv("XDG_CONFIG_HOME"); + if (!ch) { + char *h = getenv("HOME"); + if (h) + ch = strconcat(p, h, "/.config"); + } + if (ch && *ch == '/') { + buf_concat(&b, ch); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + + here = set_here(p); + if (here && *here == '/') { + buf_concat(&b, here); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + + cd = getenv("XDG_CONFIG_DIRS"); + if (!cd) + cd = "/etc/xdg"; + while (*cd) { + char *c = strchrnul(cd, ':'); + if (*cd == '/') { + buf_concat_len(&b, cd, c-cd); + buf_concat(&b, "/edlib/"); + buf_append_byte(&b, 0); + } + if (*c) + c++; + cd = c; + } + if (b.len) + ei->config_path = buf_final(&b); + else + free(buf_final(&b)); + return ei->config_path; +} + +DEF_CMD(global_find_file) +{ + /* + * ->str is a file basename. + * ->str2 is one of "data", "config" + * We find a file with basename in a known location following + * the XDG Base Directory Specificaton. + * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + * but also look in the directory containing this library $HERE + * For "data" we look in a directory "edlib" under: + * - $XDG_DATA_HOME, or $HOME/.local/share + * - $HERE + * - $XDG_DATA_DIRS, or /usr/local/share:/usr/share + * + * For config we lookin a "edlib" under: + * - $XDG_CONFIG_HOME, or $HOME/.config + * - $HERE + * - $XDG_CONFIG_DIRS, or /etc/xdg + */ + char *path = NULL; + + if (ci->str == NULL || ci->str2 == NULL) + return -Enoarg; + if (strcmp(ci->str2, "data") == 0) + path = set_data_path(ci->home); + else if (strcmp(ci->str2, "config") == 0) + path = set_config_path(ci->home); + + if (!path) + return Einval; + for (; path && *path; path += strlen(path)+1) { + char *p = strconcat(NULL, path, ci->str); + int fd; + if (!p) + continue; + fd = open(p, O_RDONLY); + if (fd < 0) { + free(p); + continue; + } + close(fd); + comm_call(ci->comm2, "cb", ci->focus, 0, NULL, p); + free(p); + return 1; + } + return Efalse; +} + struct pane *editor_new(void) { struct pane *ed; @@ -481,6 +659,7 @@ struct pane *editor_new(void) key_add(ed_map, "global-get-command", &global_get_command); key_add(ed_map, "global-load-module", &editor_load_module); key_add(ed_map, "global-config-dir", &global_config_dir); + key_add(ed_map, "xdg-find-edlib-file", &global_find_file); key_add_prefix(ed_map, "event:", &editor_auto_event); key_add_prefix(ed_map, "global-multicall-", &editor_multicall); key_add_prefix(ed_map, "editor:request:", diff --git a/edlib.c b/edlib.c index c7c21007..2a60580e 100644 --- a/edlib.c +++ b/edlib.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) setlocale(LC_CTYPE, "enUS.UTF-8"); call("global-load-module", ed, 0, NULL, "lib-config"); - call("config-load", ed, 0, NULL, "edlib.ini", 0, NULL, argv[0]); + call("config-load", ed, 0, NULL, "edlib.ini"); call("attach-doc-docs", ed); diff --git a/lib-config.c b/lib-config.c index f49fd448..8aecc6ef 100644 --- a/lib-config.c +++ b/lib-config.c @@ -19,7 +19,7 @@ #include "core.h" #include "parse-ini.h" -static void load_config(const char *path safe, void *data, const char *base); +static void load_config(const char *path safe, void *data); static bool __glob_match(const char *patn safe, const char *path safe) { @@ -179,7 +179,7 @@ static void handle(void *data, char *section safe, char *name safe, char *value if (strcmp(section, "") == 0 || strcmp(section,"include") == 0) { if (strcmp(name, "include") == 0) { - load_config(value, data, path); + load_config(value, data); return; } return; @@ -214,48 +214,22 @@ static void handle(void *data, char *section safe, char *name safe, char *value } } -static void load_config(const char *path safe, void *data, const char *base) +static void load_config(const char *path safe, void *data) { - char *sl, *p, *h; + char *p; + struct config_data *cd = data; + if (*path == '/') { parse_ini(path, handle, data); return; } /* - * Relative paths can be loaded from: - * dirname(base) - * /usr/share/edlib/ - * $HOME/.config/edlib/ + * Relative paths can be loaded using xdg-find-edlib-file data */ - if (base && (sl = strrchr(base, '/')) != NULL) { - sl += 1; - p = malloc((sl - base) + strlen(path) + 1); - memcpy(p, base, sl - base); - strcpy(p + (sl - base), path); - if (access(p, F_OK) == 0) { - parse_ini(p, handle, data); - free(p); - return; - } - free(p); - } - p = strconcat(NULL, "/usr/share/edlib/", path); - if (access(p, F_OK) == 0) { - parse_ini(p, handle, data); - free(p); - return; - } - free(p); - - h = getenv("HOME"); - if (h == NULL || *h != '/') - return; - p = strconcat(NULL, h, "/.config/edlib/", path); - if (access(p, F_OK) == 0) { + p = call_ret(str, "xdg-find-edlib-file", cd->root, 0, NULL, + path, 0, NULL, "config"); + if (p && access(p, F_OK) == 0) parse_ini(p, handle, data); - free(p); - return; - } free(p); } @@ -302,7 +276,7 @@ DEF_CMD(config_load) cd = container_of(ci->comm, struct config_data, c); } if (ci->str) - load_config(ci->str, cd, ci->str2); + load_config(ci->str, cd); return 1; }