summaryrefslogtreecommitdiff
path: root/src/xdg.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
committerJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
commitc029d90d1975e124d237605f1edb2be16bd05b5d (patch)
tree9df87ffb365354bdb74a969440b32c8304bdbcb7 /src/xdg.cc
Initial commit
Diffstat (limited to 'src/xdg.cc')
-rw-r--r--src/xdg.cc135
1 files changed, 135 insertions, 0 deletions
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 <map>
+#include <memory>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+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<std::string>* 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<std::string>* ret) {
+ auto env = getenv(env_name);
+ if (!env || !*env) {
+ dirs(fallback, ret);
+ } else {
+ dirs(env, ret);
+ }
+}
+
+void remove_dupes(std::vector<std::string>* lst) {
+ std::multimap<size_t, std::string*> 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<std::string> XDG::config_dirs() {
+ std::vector<std::string> 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<std::string> XDG::data_dirs() {
+ std::vector<std::string> 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<char[]>(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);
+}