summaryrefslogtreecommitdiff
path: root/src/xdg.cc
blob: 69b2a532bae0ab33adc339014004dfb01757397f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "common.hh"

#include "xdg.hh"

#include <filesystem>
#include <pwd.h>
#include <stdlib.h>
#include <unistd.h>

namespace xdg {

namespace {

bool valid(char const* path) {
  return path && path[0] == '/';
}

bool valid(std::string const& path, size_t pos, size_t len) {
  return len > 0 && path[pos] == '/';
}

std::filesystem::path get_home() {
  auto* env = getenv("HOME");
  if (valid(env))
    return env;
  auto* pwd = getpwuid(getuid());
  if (pwd && valid(pwd->pw_dir))
    return pwd->pw_dir;
  return "/";
}

void get_paths(Type type, std::string* dirs, std::filesystem::path* home) {
  char const* env;
  switch (type) {
  case Type::CONFIG:
    if (dirs) {
      env = getenv("XDG_CONFIG_DIRS");
      if (valid(env)) {
        dirs->assign(env);
      } else {
        dirs->assign("/etc/xdg");
      }
    }
    if (home) {
      env = getenv("XDG_CONFIG_HOME");
      if (valid(env)) {
        *home = env;
      } else {
        *home = get_home() / ".config";
      }
    }
    break;
  case Type::DATA:
    if (dirs) {
      env = getenv("XDG_DATA_DIRS");
      if (valid(env)) {
        dirs->assign(env);
      } else {
        dirs->assign("usr/local/share/:/usr/share/");
      }
    }
    if (home) {
      env = getenv("XDG_DATA_HOME");
      if (valid(env)) {
        *home = env;
      } else {
        *home = get_home() / ".local/share";
      }
    }
    break;
  case Type::CACHE:
    if (dirs)
      dirs->clear();
    if (home) {
      env = getenv("XDG_CACHE_HOME");
      if (valid(env)) {
        *home = env;
      } else {
        *home = get_home() / ".cache";
      }
    }
    break;
  }
}

}  // namespace

void paths_to_read(Type type, std::string_view name,
                   std::vector<std::filesystem::path>& out) {
  std::string dirs;
  std::filesystem::path home;
  get_paths(type, &dirs, &home);
  out.clear();
  out.push_back(home / name);
  size_t last = 0;
  while (true) {
    size_t next = dirs.find(':', last);
    if (next == std::string::npos) {
      if (valid(dirs, last, dirs.size() - last))
        out.push_back(std::filesystem::path(dirs.substr(last)) / name);
      break;
    }
    if (valid(dirs, last, next - last))
      out.push_back(
          std::filesystem::path(dirs.substr(last, next - last)) / name);
    last = next + 1;
  }
}

std::filesystem::path path_to_write(Type type, std::string_view name) {
  std::filesystem::path home;
  get_paths(type, nullptr, &home);
  return home / name;
}

}  // namespace xdg