The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
paths.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "desktop/paths.hpp"
18 
19 #include "game_config.hpp"
20 #include "filesystem.hpp"
21 #include "gettext.hpp"
22 #include "log.hpp"
23 #include "preferences/general.hpp"
26 
27 #if !defined(_WIN32) && !defined(__APPLE__)
28 #include <boost/filesystem.hpp>
29 #endif
30 
31 #ifndef _WIN32
32 
33 // For username stuff on Unix:
34 #include <pwd.h>
35 #include <sys/types.h>
36 
37 #else // _WIN32
38 
39 #ifndef UNICODE
40 #define UNICODE
41 #endif
42 
43 #define WIN32_LEAN_AND_MEAN
44 
45 #include <windows.h>
46 #include <shlobj.h>
47 
48 #endif
49 
50 static lg::log_domain log_desktop("desktop");
51 #define ERR_DU LOG_STREAM(err, log_desktop)
52 #define LOG_DU LOG_STREAM(info, log_desktop)
53 #define DBG_DU LOG_STREAM(debug, log_desktop)
54 
55 namespace desktop
56 {
57 
58 namespace
59 {
60 
61 void enumerate_storage_devices(std::vector<path_info>& res)
62 {
63 #ifdef _WIN32
64 
65  const DWORD drive_table = GetLogicalDrives();
66 
67  for(unsigned n = 0; n < 26; ++n) {
68  if((drive_table >> n) & 1) {
69  std::string u8drive = "A:";
70  u8drive[0] += n;
71 
72  LOG_DU << "enumerate_win32_drives(): " << u8drive << " is reported to be present\n";
73 
74  wchar_t drive[] = L"A:\\";
75  drive[0] += n;
76 
77  const DWORD label_bufsize = MAX_PATH + 1;
78  wchar_t label[label_bufsize] { 0 };
79 
80  if(GetVolumeInformation(drive, label, label_bufsize, nullptr, nullptr, nullptr, nullptr, 0) == 0) {
81  // Probably an empty removable drive, just ignore it and carry on.
82  const DWORD err = GetLastError();
83  LOG_DU << "enumerate_win32_drives(): GetVolumeInformation() failed (" << err << ")\n";
84  continue;
85  }
86 
87  // Trailing slash so that we don't get compatibility per-drive working dirs
88  // involved in path resolution.
89  res.push_back({u8drive, unicode_cast<std::string>(std::wstring{label}), u8drive + '\\'});
90  }
91  }
92 
93 #elif defined(__APPLE__)
94 
95  // Probably as unreliable as /media|/mnt on other platforms, not worth
96  // examining in detail.
97  res.push_back({{ N_("filesystem_path_system^Volumes"), GETTEXT_DOMAIN }, "", "/Volumes"});
98 
99 #else
100 
101  namespace bsys = boost::system;
102  namespace bfs = boost::filesystem;
103 
104  // These are either used as mount points themselves, or host mount points. The
105  // reasoning here is that if any or all of them are non-empty, they are
106  // probably used for _something_ that might be of interest to the user (if not
107  // directly and actively controlled by the user themselves).
108  static const std::vector<std::string> candidates { "/media", "/mnt" };
109 
110  for(const auto& mnt : candidates) {
111  bsys::error_code e;
112  try {
113  if(bfs::is_directory(mnt, e) && !bfs::is_empty(mnt, e) && !e) {
114  DBG_DU << "enumerate_mount_parents(): " << mnt << " appears to be a non-empty dir\n";
115  res.push_back({mnt, "", mnt});
116  }
117  }
118  catch(...) {
119  //bool is_empty(const path& p, system::error_code& ec) might throw.
120  //For example if you have no permission on that directory. Don't list the file in that case.
121  DBG_DU << "caught exception in enumerate_storage_devices\n";
122  }
123  }
124 
125 #endif
126 }
127 
128 bool have_path(const std::vector<path_info>& pathset, const std::string& path)
129 {
130  for(const auto& pinfo : pathset) {
131  if(pinfo.path == path) {
132  return true;
133  }
134  }
135 
136  return false;
137 }
138 
139 inline std::string pretty_path(const std::string& path)
140 {
141  return filesystem::normalize_path(path, true, true);
142 }
143 
144 inline config get_bookmarks_config()
145 {
146  const config& cfg = preferences::get_child("dir_bookmarks");
147  return cfg ? cfg : config{};
148 }
149 
150 inline void commit_bookmarks_config(config& cfg)
151 {
152  preferences::set_child("dir_bookmarks", cfg);
153 }
154 
155 } // unnamed namespace
156 
158 {
159 #ifndef _WIN32
160 
161  // TODO: The filesystem API uses $HOME for this purpose, which may be
162  // overridden or missing. Not sure which one really makes more sense
163  // for us here.
164  const passwd* const pwd = getpwuid(geteuid());
165 
166  if(!pwd || !pwd->pw_dir || !*pwd->pw_dir) {
167  return "";
168  }
169 
170  return pwd->pw_dir;
171 
172 #else // _WIN32
173 
174  wchar_t profile_path[MAX_PATH];
175  HRESULT res = SHGetFolderPath(nullptr, CSIDL_PROFILE, nullptr, SHGFP_TYPE_CURRENT, profile_path);
176  return res != S_OK ? "" : unicode_cast<std::string>(std::wstring{profile_path});
177 
178 #endif // _WIN32
179 }
180 
182 {
183  return label.empty() ? name : label + " (" + name + ")";
184 }
185 
186 std::ostream& operator<<(std::ostream& os, const path_info& pinf)
187 {
188  return os << pinf.name << " [" << pinf.label << "] - " << pinf.path;
189 }
190 
191 std::vector<path_info> game_paths(unsigned path_types)
192 {
193  static const std::string& game_bin_dir = pretty_path(filesystem::get_exe_dir());
194  static const std::string& game_data_dir = pretty_path(game_config::path);
195  static const std::string& game_user_data_dir = pretty_path(filesystem::get_user_data_dir());
196  static const std::string& game_user_pref_dir = pretty_path(filesystem::get_user_config_dir());
197 
198  std::vector<path_info> res;
199 
200  if(path_types & GAME_BIN_DIR && !have_path(res, game_bin_dir)) {
201  res.push_back({{ N_("filesystem_path_game^Game executables"), GETTEXT_DOMAIN }, "", game_bin_dir});
202  }
203 
204  if(path_types & GAME_CORE_DATA_DIR && !have_path(res, game_data_dir)) {
205  res.push_back({{ N_("filesystem_path_game^Game data"), GETTEXT_DOMAIN }, "", game_data_dir});
206  }
207 
208  if(path_types & GAME_USER_DATA_DIR && !have_path(res, game_user_data_dir)) {
209  res.push_back({{ N_("filesystem_path_game^User data"), GETTEXT_DOMAIN }, "", game_user_data_dir});
210  }
211 
212  if(path_types & GAME_USER_PREFS_DIR && !have_path(res, game_user_pref_dir)) {
213  res.push_back({{ N_("filesystem_path_game^User preferences"), GETTEXT_DOMAIN }, "", game_user_pref_dir});
214  }
215 
216  return res;
217 }
218 
219 std::vector<path_info> system_paths(unsigned path_types)
220 {
221  static const std::string& home_dir = user_profile_dir();
222 
223  std::vector<path_info> res;
224 
225  if(path_types & SYSTEM_USER_PROFILE && !home_dir.empty()) {
226  res.push_back({{ N_("filesystem_path_system^Home"), GETTEXT_DOMAIN }, "", home_dir});
227  }
228 
229  if(path_types & SYSTEM_ALL_DRIVES) {
230  enumerate_storage_devices(res);
231  }
232 
233 #ifndef _WIN32
234  if(path_types & SYSTEM_ROOTFS) {
235  res.push_back({{ N_("filesystem_path_system^Root"), GETTEXT_DOMAIN }, "", "/"});
236  }
237 #endif
238 
239  return res;
240 }
241 
242 unsigned add_user_bookmark(const std::string& label, const std::string& path)
243 {
244  config cfg = get_bookmarks_config();
245 
246  config& bookmark_cfg = cfg.add_child("bookmark");
247  bookmark_cfg["label"] = label;
248  bookmark_cfg["path"] = path;
249 
250  commit_bookmarks_config(cfg);
251 
252  return cfg.child_count("bookmark");
253 }
254 
256 {
257  config cfg = get_bookmarks_config();
258  const unsigned prev_size = cfg.child_count("bookmark");
259 
260  if(index < prev_size) {
261  cfg.remove_child("bookmark", index);
262  }
263 
264  commit_bookmarks_config(cfg);
265 }
266 
267 std::vector<bookmark_info> user_bookmarks()
268 {
269  const config& cfg = get_bookmarks_config();
270  std::vector<bookmark_info> res;
271 
272  if(cfg.has_child("bookmark")) {
273  for(const config& bookmark_cfg : cfg.child_range("bookmark")) {
274  res.push_back({ bookmark_cfg["label"], bookmark_cfg["path"] });
275  }
276  }
277 
278  return res;
279 }
280 
281 } // namespace desktop
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
void remove_user_bookmark(unsigned index)
Definition: paths.cpp:255
Paths for each storage media found (Windows), /media and/or /mnt (X11, if non-empty).
Definition: paths.hpp:64
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
child_itors child_range(config_key_type key)
Definition: config.cpp:343
#define DBG_DU
Definition: paths.cpp:53
std::vector< bookmark_info > user_bookmarks()
Definition: paths.cpp:267
Path to the root of the filesystem hierarchy (ignored on Windows).
Definition: paths.hpp:66
std::string normalize_path(const std::string &path, bool normalize_separators=false, bool resolve_dot_entries=false)
Returns the absolute path of a file.
std::vector< path_info > game_paths(unsigned path_types)
Returns a list of game-related paths.
Definition: paths.cpp:191
unsigned child_count(config_key_type key) const
Definition: config.cpp:371
static lg::log_domain log_desktop("desktop")
std::string get_user_data_dir()
std::vector< path_info > system_paths(unsigned path_types)
Returns a list of system-defined paths.
Definition: paths.cpp:219
t_string name
Path name or drive letter/mount point path; may be a translatable string if it's a game resources pat...
Definition: paths.hpp:40
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::string path
Definition: game_config.cpp:56
Desktop paths, storage media and bookmark functions.
#define GETTEXT_DOMAIN
Definition: paths.cpp:15
std::string label
System-defined label, if the path is a drive or mount point.
Definition: paths.hpp:42
std::string display_name() const
Formats this path for UI display.
Definition: paths.cpp:181
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:393
User preferences dir.
Definition: paths.hpp:58
void set_child(const std::string &key, const config &val)
Definition: general.cpp:212
std::string get_exe_dir()
logger & err()
Definition: log.cpp:79
std::string path
Real path.
Definition: paths.hpp:44
Path to the user's profile dir (e.g.
Definition: paths.hpp:65
Game executable dir.
Definition: paths.hpp:56
unsigned add_user_bookmark(const std::string &label, const std::string &path)
Definition: paths.cpp:242
Declarations for File-IO.
#define N_(String)
Definition: gettext.hpp:97
const config & get_child(const std::string &key)
Definition: general.cpp:217
config & add_child(config_key_type key)
Definition: config.cpp:456
std::string user_profile_dir()
Returns the path to the user profile dir (e.g.
Definition: paths.cpp:157
#define LOG_DU
Definition: paths.cpp:52
std::string get_user_config_dir()
Game data dir.
Definition: paths.hpp:57
Standard logging facilities (interface).
std::ostream & operator<<(std::ostream &os, const path_info &pinf)
Definition: paths.cpp:186
#define e
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
static map_location::DIRECTION n
void remove_child(config_key_type key, unsigned index)
Definition: config.cpp:624
User data dir.
Definition: paths.hpp:59