]> git.neil.brown.name Git - edlib.git/commitdiff
Use XDG standard to find config (and data) files.
authorNeilBrown <neil@brown.name>
Sun, 2 Jul 2023 01:31:42 +0000 (11:31 +1000)
committerNeilBrown <neil@brown.name>
Sun, 2 Jul 2023 01:31:42 +0000 (11:31 +1000)
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 <neil@brown.name>
Makefile
core-editor.c
edlib.c
lib-config.c

index 8c718cf9f512133d2adaf8a1f2dea84184d56ad3..60258e011113fa57d94e2abe22aa4313346ba46b 100644 (file)
--- 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
index 4b1ca01b11ac9df59b7eb4737ab385691eeb7390..825971d3730c20a9f38c043e2766238ff880c06d 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <fcntl.h>
 #include <stdarg.h>
 #include <dlfcn.h>
 
@@ -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 c7c210070719467a06acd7bf487650856e3ab582..2a60580e4077738931deb2806050badad8df4276 100644 (file)
--- 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);
 
index f49fd4484a372c8f6979a574e2de91bc086296fe..8aecc6ef4250d7c940029e2220c10b782cca2769 100644 (file)
@@ -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;
 }