#include "log.h" #include "util.h" #include #include #include #include #include #include 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 nmode) { #if 0 int last = 0; char *ch = path; do { if (*ch == '/' || (*ch == '\0' && (last = 1))) { /* last is set to 1 iff *ch == '\0' */ *ch = '\0'; if (mkdirat(fd, path, mode) < 0 && errno != EEXIST) { vl_debug("Failed to create directory %s: %s", path, strerror(errno)); return -1; } 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"); }