// -*- 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); }