diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2025-10-07 19:58:28 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2025-10-19 00:13:47 +0200 |
| commit | 4f6e76493fb74f5385d5a14dce3a01c9901802ed (patch) | |
| tree | a38722ec832fd44ad34257730e075e8b07825bd0 /src/paths.cc | |
| parent | c87f9627efc8b612eb9b000acfcc6731cad15765 (diff) | |
paths: New module
Path utilities (doh)
Diffstat (limited to 'src/paths.cc')
| -rw-r--r-- | src/paths.cc | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/paths.cc b/src/paths.cc new file mode 100644 index 0000000..091be6e --- /dev/null +++ b/src/paths.cc @@ -0,0 +1,100 @@ +#include "paths.hh" + +#include "str.hh" + +#include <algorithm> +#include <cerrno> +#include <cstddef> +#include <cstdlib> +#include <iterator> +#include <memory> +#include <pwd.h> +#include <string_view> +#include <unistd.h> +#include <unordered_set> +#include <vector> + +namespace paths { + +namespace { + +std::vector<std::filesystem::path> xdg_read_dirs( + const char* userdir_env_name, std::string_view userdir_home_default, + const char* dirs_env_name, + std::vector<std::filesystem::path> const& dirs_default_value) { + std::vector<std::filesystem::path> ret; + std::unordered_set<std::filesystem::path> tmp; + auto* env_userdir = getenv(userdir_env_name); + if (env_userdir != nullptr && env_userdir[0] != '\0') { + ret.emplace_back(env_userdir); + } else { + ret.emplace_back(home() / userdir_home_default); + } + tmp.insert(ret.back()); + auto* env_dirs = getenv(dirs_env_name); + if (env_dirs != nullptr && env_dirs[0] != '\0') { + for (auto dir : str::split(env_dirs, ':')) { + if (tmp.emplace(dir).second) { + ret.emplace_back(dir); + } + } + } else { + std::ranges::copy_if( + dirs_default_value, std::back_inserter(ret), + [&tmp](auto const& dir) { return tmp.emplace(dir).second; }); + } + return ret; +} + +} // namespace + +std::filesystem::path home() { + { + auto* str = getenv("HOME"); + if (str != nullptr && str[0] != '\0') + return str; + } + + { + auto maybe_size = sysconf(_SC_GETPW_R_SIZE_MAX); + size_t size = maybe_size > 0 ? static_cast<size_t>(maybe_size) : 1024; + auto buffer = std::make_unique<char[]>(size); + struct passwd pwd; + struct passwd* ret; + int err; + while (true) { + err = getpwuid_r(geteuid(), &pwd, buffer.get(), size, &ret); + if (err == 0) + break; + if (err != ERANGE) + break; + auto new_size = size * 2; + if (new_size < size) + break; + buffer = std::make_unique<char[]>(new_size); + size = new_size; + } + if (err == 0 && ret) { + if (ret->pw_dir != nullptr && ret->pw_dir[0] != '\0') { + return ret->pw_dir; + } + } + } + + return "/"; +} + +std::vector<std::filesystem::path> config_dirs() { + static const std::vector<std::filesystem::path> fallback{"/etc/xdg"}; + return xdg_read_dirs("XDG_CONFIG_HOME", ".config", "XDG_CONFIG_DIRS", + fallback); +} + +std::vector<std::filesystem::path> data_dirs() { + static const std::vector<std::filesystem::path> fallback{"/usr/local/share/", + "/usr/share/"}; + return xdg_read_dirs("XDG_DATA_HOME", ".local/share", "XDG_DATA_DIRS", + fallback); +} + +} // namespace paths |
