diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/util.c | 97 |
1 files changed, 96 insertions, 1 deletions
@@ -5,10 +5,54 @@ #include <sys/stat.h> #include <errno.h> #include <string.h> +#include <stdlib.h> +#include <unistd.h> + +int vl_mkdir_parents_open(int fd, char *path, mode_t nmode) +{ + int last = 0; + int at = fd; + int temp; + char *ch = path; + char *compstart = path; + int ret = -1; + + do { + if (*ch == '/' || (*ch == '\0' && (last = 1))) { + if (ch == path) continue; /* false alarm: we're trying to create / */ + + *ch = '\0'; + + if (mkdirat(at, compstart, nmode) < 0 && errno != EEXIST) { + vl_debug("%s: failed to create directory %s: %s", __func__, path, strerror(errno)); + goto cleanup; + } + + temp = openat(at, compstart, O_RDONLY); + if (temp < 0) { + vl_debug("%s: failed to open new directory %s: %s", __func__, path, strerror(errno)); + goto cleanup; + } + + close(at); + temp = at; + + compstart = ch + 1; + if (!last) *ch = '/'; /* make an effort to unclobber path */ + } + } while (*(ch++)); + + ret = 0; + +cleanup: + if (at != fd) close(at); + return ret; +} /* will clobber path */ -int vl_mkdir_parents(int fd, char *path, mode_t mode) +int vl_mkdir_parents(int fd, char *path, mode_t nmode) { +#if 0 int last = 0; char *ch = path; @@ -25,6 +69,57 @@ int vl_mkdir_parents(int fd, char *path, mode_t mode) if (!last) *ch = '/'; } } while (*(ch++)); +#endif /* old implementation */ + + fd = vl_mkdir_parents_open(fd, path, nmode); + if (fd >= 0) { + close(fd); + return 0; + } + + return -1; +} + +/* def: home-relative default */ +/* https://specifications.freedesktop.org/basedir/latest/ */ +static int open_xdg_dir(vl_arena *scratch, const char *xdg_env, const char *def) +{ + const char *xdg_dir = getenv(xdg_env); + int dfd; + + if (xdg_dir && *xdg_dir == '/') { + char *xdg_dir_mod = vl_arena_strdup(scratch, xdg_dir); + + /* must be an absolute path */ + if ((dfd = vl_mkdir_parents_open(AT_FDCWD, xdg_dir_mod, 0700)) < 0) { + vl_warn("get_xdg_dir: Failed to create %s: %s", xdg_dir, strerror(errno)); + return -1; + } + + return dfd; + } + + /* xdg dir is missing or not absolute (as required) */ + const char *home = getenv("HOME"); + /* TODO: could get passwd entry for home directory */ + + if (!home || *home != '/') { + vl_warn("Missing $HOME (or it is not an absolute path)! Cannot decide launcher data directory."); + return -1; + } + + char *path = vl_arena_sprintf(scratch, "%s/%s", home, def); + + if ((dfd = vl_mkdir_parents_open(AT_FDCWD, path, 0700)) < 0) { + vl_warn("%s: failed to create/open xdg dir %s: %s", __func__, path, strerror(errno)); + return -1; + } return 0; } + +int vl_get_data_dir(vl_arena *alloc, const char *application, char **opath) +{ + /* TODO: not just linux */ + int datadir = open_xdg_dir(alloc, "XDG_DATA_HOME", ".local/share"); +} |
