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