From c029d90d1975e124d237605f1edb2be16bd05b5d Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Tue, 28 Feb 2017 21:50:44 +0100 Subject: Initial commit --- src/xdg.cc | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/xdg.cc (limited to 'src/xdg.cc') diff --git a/src/xdg.cc b/src/xdg.cc new file mode 100644 index 0000000..d230e07 --- /dev/null +++ b/src/xdg.cc @@ -0,0 +1,135 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" + +#include "paths.hh" +#include "xdg.hh" + +#include +#include +#include +#include +#include + +namespace { + +std::string home_based_dir(char const* env_name, char const* fallback) { + auto env = getenv(env_name); + if (!env || !*env) { + return Paths::join(XDG::home(), fallback); + } + return Paths::cleanup(env); +} + +void dirs(char const* dirs, std::vector* ret) { + auto start = dirs; + auto i = dirs; + while (true) { + if (*i == ':' || !*i) { + if (i > start) { + auto tmp = Paths::cleanup(std::string(start, i - start)); + if (tmp[0] == '/') { + ret->push_back(tmp); + } + } + if (!*i) break; + start = ++i; + } else { + ++i; + } + } +} + +void dirs(char const* env_name, char const* fallback, + std::vector* ret) { + auto env = getenv(env_name); + if (!env || !*env) { + dirs(fallback, ret); + } else { + dirs(env, ret); + } +} + +void remove_dupes(std::vector* lst) { + std::multimap mem; + bool modifying = false; + auto out = lst->begin(); + for (auto it = lst->begin(); it != lst->end(); ++it) { + auto len = it->size(); + auto pair = mem.equal_range(len); + bool remove = false; + for (auto j = pair.first; j != pair.second; ++j) { + if (*(j->second) == *it) { + remove = true; + break; + } + } + if (remove) { + if (!modifying) { + modifying = true; + out = it; + } + } else { + if (modifying) { + *(out++) = *it; + } + mem.insert(pair.first, std::make_pair(len, &(*it))); + } + } + if (modifying) { + lst->resize(out - lst->begin()); + } +} + +} // namespace + +// static +std::string XDG::config_home() { + return home_based_dir("XDG_CONFIG_HOME", ".config"); +} + +// static +std::vector XDG::config_dirs() { + std::vector ret; + ret.push_back(config_home()); + dirs("XDG_CONFIG_DIRS", SYSCONFDIR "/xdg", &ret); + remove_dupes(&ret); + return ret; +} + +// static +std::string XDG::data_home() { + return home_based_dir("XDG_DATA_HOME", ".local/share"); +} + +// static +std::vector XDG::data_dirs() { + std::vector ret; + ret.push_back(data_home()); + dirs("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/", &ret); + remove_dupes(&ret); + return ret; +} + +// static +std::string XDG::cache_home() { + return home_based_dir("XDG_CACHE_HOME", ".cache"); +} + +// static +std::string XDG::home() { + auto env = getenv("HOME"); + if (!env || !*env) { + auto size = sysconf(_SC_GETPW_R_SIZE_MAX); + if (size == -1) size = 16384; + auto buf = std::unique_ptr(new char[size]); + struct passwd pwd; + struct passwd* result; + auto ret = getpwuid_r(getuid(), &pwd, buf.get(), size, &result); + if (result && ret == 0) { + return Paths::cleanup(result->pw_dir); + } + return "."; + } + return Paths::cleanup(env); +} -- cgit v1.2.3-70-g09d2