1 #include "Directories.h"
2
3 #include "OptionsDB.h"
4 #include "i18n.h"
5 #include <GG/utf8/checked.h>
6 #include "../universe/Enums.h"
7
8 #include <boost/filesystem/convenience.hpp>
9 #include <boost/filesystem/operations.hpp>
10 #include <boost/date_time/posix_time/posix_time.hpp>
11
12 #include <cstdlib>
13
14 #if defined(__FreeBSD__) || defined(__DragonFly__)
15 #include <sys/sysctl.h>
16 #endif
17
18 namespace fs = boost::filesystem;
19
20 namespace {
21 bool g_initialized = false;
22 fs::path bin_dir = fs::initial_path();
23 }
24
25 #if defined(FREEORION_MACOSX)
26
27 #include <iostream>
28 #include <sys/param.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <mach-o/dyld.h>
31
32 /* sets up the directories in the following way:
33 localdir: ~/Library/FreeOrion
34 globaldir: FreeOrion.app/Contents/Resources
35 bindir: FreeOrion.app/Contents/Executables
36 configpath: ~/Library/FreeOrion/config.xml
37 pythonhome: FreeOrion.app/Contents/Frameworks/Python.framework/Versions/{PythonMajor}.{PythonMinor}
38 */
39 namespace {
40 fs::path s_user_dir;
41 fs::path s_root_data_dir;
42 fs::path s_bin_dir;
43 fs::path s_config_path;
44 fs::path s_python_home;
45 }
46
47 void InitBinDir(const std::string& argv0);
48
InitDirs(const std::string & argv0)49 void InitDirs(const std::string& argv0) {
50 if (g_initialized)
51 return;
52
53 // store working dir
54 fs::initial_path();
55 fs::path bundle_path;
56 fs::path app_path;
57
58 CFBundleRef bundle = CFBundleGetMainBundle();
59 char bundle_dir[MAXPATHLEN];
60
61 if (bundle) {
62 CFURLRef bundleurl = CFBundleCopyBundleURL(bundle);
63 CFURLGetFileSystemRepresentation(bundleurl, true, reinterpret_cast<UInt8*>(bundle_dir), MAXPATHLEN);
64 } else {
65 // executable is not the main binary in application bundle (i.e. Server or AI)
66 uint32_t size = sizeof(bundle_dir);
67 if (_NSGetExecutablePath(bundle_dir, &size) != 0) {
68 std::cerr << "_NSGetExecutablePath() failed: buffer too small; need size " << size << std::endl;
69 exit(-1);
70 }
71 }
72
73 bundle_path = fs::path(bundle_dir);
74
75 // search bundle_path for a directory named "FreeOrion.app", exiting if not found, else constructing a path to application bundle contents
76 auto appiter = std::find(bundle_path.begin(), bundle_path.end(), "FreeOrion.app");
77 if (appiter == bundle_path.end()) {
78 std::cerr << "Error: Application bundle must be named 'FreeOrion.app' and executables must not be called from outside of it." << std::endl;
79 exit(-1);
80 } else {
81 for (auto piter = bundle_path.begin(); piter != appiter; ++piter) {
82 app_path /= *piter;
83 }
84 app_path /= "FreeOrion.app/Contents";
85 }
86
87 s_root_data_dir = app_path / "Resources";
88 s_user_dir = fs::path(getenv("HOME")) / "Library" / "Application Support" / "FreeOrion";
89 s_bin_dir = app_path / "Executables";
90 s_config_path = s_user_dir / "config.xml";
91 s_python_home = app_path / "Frameworks" / "Python.framework" / "Versions" / FREEORION_PYTHON_VERSION;
92
93 fs::path p = s_user_dir;
94 if (!exists(p))
95 fs::create_directories(p);
96
97 p /= "save";
98 if (!exists(p))
99 fs::create_directories(p);
100
101 // Intentionally do not create the server save dir.
102 // The server save dir is publically accessible and should not be
103 // automatically created for the user.
104
105 g_initialized = true;
106 }
107
GetUserConfigDir()108 const fs::path GetUserConfigDir() {
109 if (!g_initialized)
110 InitDirs("");
111 return s_user_dir;
112 }
113
GetUserDataDir()114 const fs::path GetUserDataDir() {
115 if (!g_initialized)
116 InitDirs("");
117 return s_user_dir;
118 }
119
GetRootDataDir()120 const fs::path GetRootDataDir() {
121 if (!g_initialized)
122 InitDirs("");
123 return s_root_data_dir;
124 }
125
GetBinDir()126 const fs::path GetBinDir() {
127 if (!g_initialized)
128 InitDirs("");
129 return s_bin_dir;
130 }
131
GetPythonHome()132 const fs::path GetPythonHome() {
133 if (!g_initialized)
134 InitDirs("");
135 return s_python_home;
136 }
137
138 #elif defined(FREEORION_LINUX) || defined(FREEORION_FREEBSD) || defined(FREEORION_OPENBSD) || defined(FREEORION_DRAGONFLY)
139 #include "binreloc.h"
140 #include <unistd.h>
141 #include <boost/filesystem/fstream.hpp>
142
143 namespace {
144 /// Copy directory from to directory to only to a depth of safe_depth
copy_directory_safe(fs::path from,fs::path to,int safe_depth)145 void copy_directory_safe(fs::path from, fs::path to, int safe_depth) {
146 if (safe_depth < 0)
147 return;
148
149 fs::copy(from, to);
150 fs::directory_iterator it(from);
151 while (it != fs::directory_iterator()) {
152 const fs::path p = *it++;
153 if (fs::is_directory(p)) {
154 copy_directory_safe(p, to / p.filename(), safe_depth - 1);
155 } else {
156 fs::copy(p, to / p.filename());
157 }
158 }
159 }
160
161 /** If the old configuration directory exists, but neither
162 the XDG_CONFIG_DIR nor the XDG_DATA_DIR exist then
163 copy the config and data files and inform the user.
164
165 It also updates the data dir in the config.xml and persisten_config.xml files.
166 */
MigrateOldConfigDirsToXDGLocation()167 void MigrateOldConfigDirsToXDGLocation() {
168 const fs::path old_path = fs::path(getenv("HOME")) / ".freeorion";
169 const fs::path config_path = GetUserConfigDir();
170 const fs::path data_path = GetUserDataDir();
171
172 bool dont_migrate = !exists(old_path) || exists(config_path) || exists(data_path);
173 if (dont_migrate)
174 return;
175
176 std::stringstream msg;
177 msg << "Freeorion added support for the XDG Base Directory Specification." << std::endl << std::endl
178 << "Configuration files and data were migrated from:" << std::endl
179 << old_path << std::endl << std::endl
180 << "Configuration were files copied to:" << std::endl << config_path << std::endl << std::endl
181 << "Data Files were copied to:" << std::endl << data_path << std::endl << std::endl
182 << "If your save.path option in persistent_config.xml was ~/.config, then you need to update it."
183 << std::endl;
184
185 try {
186 fs::create_directories(config_path);
187 fs::create_directories(data_path);
188
189 const fs::path old_config_file = old_path / "config.xml";
190 const fs::path old_persistent_file = old_path / "persistent_config.xml";
191
192 if (exists(old_config_file))
193 fs::copy(old_config_file, config_path / old_config_file.filename());
194 if (exists(old_persistent_file))
195 fs::copy(old_persistent_file, config_path / old_persistent_file.filename());
196
197 fs::directory_iterator it(old_path);
198 while (it != fs::directory_iterator()) {
199 const fs::path p = *it++;
200 if (p == old_config_file || p == old_persistent_file)
201 continue;
202
203 if (fs::is_directory(p)) {
204 int arbitrary_safe_depth = 6;
205 copy_directory_safe(p, data_path / p.filename(), arbitrary_safe_depth);
206 } else {
207 fs::copy(p, data_path / p.filename());
208 }
209 }
210
211 //Start update of save.path in config file and complete it in CompleteXDGMigration()
212 fs::path sentinel = GetUserDataDir() / "MIGRATION_TO_XDG_IN_PROGRESS";
213 if (!exists(sentinel)) {
214 fs::ofstream touchfile(sentinel);
215 touchfile << " ";
216 }
217
218 fs::ofstream msg_file(old_path / "MIGRATION.README");
219 msg_file << msg.str() << std::endl
220 << "You can delete this file it is a one time message." << std::endl << std::endl;
221
222 } catch(fs::filesystem_error const & e) {
223 std::cerr << "Error: Unable to migrate files from old config dir" << std::endl
224 << old_path << std::endl
225 << " to new XDG specified config dir" << std::endl << config_path << std::endl
226 << " and data dir" << std::endl << data_path << std::endl
227 << " because " << e.what() << std::endl;
228 throw;
229 }
230
231 std::cout << msg.str();
232 }
233
234 }
235
236 void InitBinDir(const std::string& argv0);
237
InitDirs(const std::string & argv0)238 void InitDirs(const std::string& argv0) {
239 if (g_initialized)
240 return;
241
242 /* store working dir. some implimentations get the value of initial_path
243 * from the value of current_path the first time initial_path is called,
244 * so it is necessary to call initial_path as soon as possible after
245 * starting the program, so that current_path doesn't have a chance to
246 * change before initial_path is initialized. */
247 fs::initial_path();
248
249 br_init(nullptr);
250
251 MigrateOldConfigDirsToXDGLocation();
252
253 fs::path cp = GetUserConfigDir();
254 if (!exists(cp)) {
255 fs::create_directories(cp);
256 }
257
258 fs::path p = GetUserDataDir();
259 if (!exists(p)) {
260 fs::create_directories(p);
261 }
262
263 p /= "save";
264 if (!exists(p)) {
265 fs::create_directories(p);
266 }
267
268 // Intentionally do not create the server save dir.
269 // The server save dir is publically accessible and should not be
270 // automatically created for the user.
271
272 InitBinDir(argv0);
273
274 g_initialized = true;
275 }
276
GetUserConfigDir()277 const fs::path GetUserConfigDir() {
278 static fs::path p = getenv("XDG_CONFIG_HOME")
279 ? fs::path(getenv("XDG_CONFIG_HOME")) / "freeorion"
280 : fs::path(getenv("HOME")) / ".config" / "freeorion";
281 return p;
282 }
283
GetUserDataDir()284 const fs::path GetUserDataDir() {
285 static fs::path p = getenv("XDG_DATA_HOME")
286 ? fs::path(getenv("XDG_DATA_HOME")) / "freeorion"
287 : fs::path(getenv("HOME")) / ".local" / "share" / "freeorion";
288 return p;
289 }
290
GetRootDataDir()291 const fs::path GetRootDataDir() {
292 if (!g_initialized) InitDirs("");
293 char* dir_name = br_find_data_dir(SHAREPATH);
294 fs::path p(dir_name);
295 std::free(dir_name);
296 p /= "freeorion";
297 // if the path does not exist, we fall back to the working directory
298 if (!exists(p)) {
299 return fs::initial_path();
300 } else {
301 return p;
302 }
303 }
304
GetBinDir()305 const fs::path GetBinDir() {
306 if (!g_initialized) InitDirs("");
307 return bin_dir;
308 }
309
InitBinDir(const std::string & argv0)310 void InitBinDir(const std::string& argv0) {
311 bool problem = false;
312 try {
313 // get this executable's path by following link
314 char buf[2048] = {'\0'};
315
316 #if defined(__FreeBSD__) || defined(__DragonFly__)
317 int mib[4];
318 mib[0] = CTL_KERN;
319 mib[1] = KERN_PROC;
320 mib[2] = KERN_PROC_PATHNAME;
321 mib[3] = -1;
322 size_t buf_size = sizeof(buf);
323 sysctl(mib, 4, buf, &buf_size, 0, 0);
324 #elif defined(__OpenBSD__)
325 // OpenBSD does not have executable path's retrieval feature
326 std::string argpath(argv0);
327 boost::erase_all(argpath, "\"");
328 if (argpath[0] != '/')
329 problem = (nullptr == realpath(argpath.c_str(), buf));
330 else
331 strncpy(buf, argpath.c_str(), sizeof(buf));
332 #else
333 problem = (-1 == readlink("/proc/self/exe", buf, sizeof(buf) - 1));
334 #endif
335
336 if (!problem) {
337 buf[sizeof(buf) - 1] = '\0'; // to be safe, else initializing an std::string with a non-null-terminated string could read invalid data outside the buffer range
338 std::string path_text(buf);
339
340 fs::path binary_file = fs::system_complete(fs::path(path_text));
341 bin_dir = binary_file.branch_path();
342
343 // check that a "freeoriond" file (hopefully the freeorion server binary) exists in the found directory
344 fs::path p(bin_dir);
345 p /= "freeoriond";
346 if (!exists(p))
347 problem = true;
348 }
349
350 } catch (...) {
351 problem = true;
352 }
353
354 if (problem) {
355 // failed trying to parse the call path, so try hard-coded standard location...
356 char* dir_name = br_find_bin_dir(BINPATH);
357 fs::path p(dir_name);
358 std::free(dir_name);
359
360 // if the path does not exist, fall back to the working directory
361 if (!exists(p)) {
362 bin_dir = fs::initial_path();
363 } else {
364 bin_dir = p;
365 }
366 }
367 }
368
369 #elif defined(FREEORION_WIN32)
370
371 void InitBinDir(const std::string& argv0);
372
InitDirs(const std::string & argv0)373 void InitDirs(const std::string& argv0) {
374 if (g_initialized)
375 return;
376
377 fs::path local_dir = GetUserConfigDir();
378 if (!exists(local_dir))
379 fs::create_directories(local_dir);
380
381 fs::path p(GetSaveDir());
382 if (!exists(p))
383 fs::create_directories(p);
384
385 // Intentionally do not create the server save dir.
386 // The server save dir is publically accessible and should not be
387 // automatically created for the user.
388
389 InitBinDir(argv0);
390
391 g_initialized = true;
392 }
393
GetUserConfigDir()394 const fs::path GetUserConfigDir() {
395 static fs::path p = fs::path(std::wstring(_wgetenv(L"APPDATA"))) / "FreeOrion";
396 return p;
397 }
398
GetUserDataDir()399 const fs::path GetUserDataDir() {
400 static fs::path p = fs::path(std::wstring(_wgetenv(L"APPDATA"))) / "FreeOrion";
401 return p;
402 }
403
GetRootDataDir()404 const fs::path GetRootDataDir()
405 { return fs::initial_path(); }
406
GetBinDir()407 const fs::path GetBinDir() {
408 if (!g_initialized) InitDirs("");
409 return bin_dir;
410 }
411
GetPythonHome()412 const fs::path GetPythonHome()
413 { return GetBinDir(); }
414
InitBinDir(const std::string & argv0)415 void InitBinDir(const std::string& argv0) {
416 try {
417 fs::path binary_file = fs::system_complete(FilenameToPath(argv0));
418 bin_dir = binary_file.branch_path();
419 } catch (const fs::filesystem_error &) {
420 bin_dir = fs::initial_path();
421 }
422 }
423
424 #else
425 # error Neither FREEORION_LINUX, FREEORION_FREEBSD, FREEORION_OPENBSD, FREEORION_DRAGONFLY nor FREEORION_WIN32 set
426 #endif
427
CompleteXDGMigration()428 void CompleteXDGMigration() {
429 fs::path sentinel = GetUserDataDir() / "MIGRATION_TO_XDG_IN_PROGRESS";
430 if (exists(sentinel)) {
431 fs::remove(sentinel);
432 // Update data dir in config file
433 const std::string options_save_dir = GetOptionsDB().Get<std::string>("save.path");
434 const fs::path old_path = fs::path(getenv("HOME")) / ".freeorion";
435 if (fs::path(options_save_dir) == old_path)
436 GetOptionsDB().Set<std::string>("save.path", GetUserDataDir().string());
437 }
438 }
439
GetResourceDir()440 const fs::path GetResourceDir() {
441 // if resource dir option has been set, use specified location. otherwise,
442 // use default location
443 std::string options_resource_dir = GetOptionsDB().Get<std::string>("resource.path");
444 fs::path dir = FilenameToPath(options_resource_dir);
445 if (fs::exists(dir) && fs::is_directory(dir))
446 return dir;
447
448 dir = GetOptionsDB().GetDefault<std::string>("resource.path");
449 if (!fs::is_directory(dir) || !fs::exists(dir))
450 dir = FilenameToPath(GetOptionsDB().GetDefault<std::string>("resource.path"));
451
452 return dir;
453 }
454
GetConfigPath()455 const fs::path GetConfigPath() {
456 static const fs::path p = GetUserConfigDir() / "config.xml";
457 return p;
458 }
459
GetPersistentConfigPath()460 const fs::path GetPersistentConfigPath() {
461 static const fs::path p = GetUserConfigDir() / "persistent_config.xml";
462 return p;
463 }
464
GetSaveDir()465 const fs::path GetSaveDir() {
466 // if save dir option has been set, use specified location. otherwise,
467 // use default location
468 std::string options_save_dir = GetOptionsDB().Get<std::string>("save.path");
469 if (options_save_dir.empty())
470 options_save_dir = GetOptionsDB().GetDefault<std::string>("save.path");
471 return FilenameToPath(options_save_dir);
472 }
473
GetServerSaveDir()474 const fs::path GetServerSaveDir() {
475 // if server save dir option has been set, use specified location. otherwise,
476 // use default location
477 std::string options_save_dir = GetOptionsDB().Get<std::string>("save.server.path");
478 if (options_save_dir.empty())
479 options_save_dir = GetOptionsDB().GetDefault<std::string>("save.server.path");
480 return FilenameToPath(options_save_dir);
481 }
482
RelativePath(const fs::path & from,const fs::path & to)483 fs::path RelativePath(const fs::path& from, const fs::path& to) {
484 fs::path retval;
485 fs::path from_abs = fs::absolute(from);
486 fs::path to_abs = fs::absolute(to);
487 auto from_it = from_abs.begin();
488 auto end_from_it = from_abs.end();
489 auto to_it = to_abs.begin();
490 auto end_to_it = to_abs.end();
491 while (from_it != end_from_it && to_it != end_to_it && *from_it == *to_it) {
492 ++from_it;
493 ++to_it;
494 }
495 for (; from_it != end_from_it; ++from_it)
496 { retval /= ".."; }
497 for (; to_it != end_to_it; ++to_it)
498 { retval /= *to_it; }
499 return retval;
500 }
501
502 #if defined(FREEORION_WIN32)
503
PathToString(const fs::path & path)504 std::string PathToString(const fs::path& path) {
505 fs::path::string_type native_string = path.generic_wstring();
506 std::string retval;
507 utf8::utf16to8(native_string.begin(), native_string.end(), std::back_inserter(retval));
508 return retval;
509 }
510
FilenameToPath(const std::string & path_str)511 fs::path FilenameToPath(const std::string& path_str) {
512 // convert UTF-8 directory string to UTF-16
513 boost::filesystem::path::string_type directory_native;
514 utf8::utf8to16(path_str.begin(), path_str.end(), std::back_inserter(directory_native));
515 #if (BOOST_VERSION >= 106300)
516 return fs::path(directory_native).generic_path();
517 #else
518 return fs::path(directory_native);
519 #endif
520 }
521
522 #else // defined(FREEORION_WIN32)
523
PathToString(const fs::path & path)524 std::string PathToString(const fs::path& path)
525 { return path.string(); }
526
FilenameToPath(const std::string & path_str)527 fs::path FilenameToPath(const std::string& path_str)
528 { return fs::path(path_str); }
529
530 #endif // defined(FREEORION_WIN32)
531
FilenameTimestamp()532 std::string FilenameTimestamp() {
533 boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%Y%m%d_%H%M%S");
534 std::stringstream date_stream;
535
536 date_stream.imbue(std::locale(date_stream.getloc(), facet));// alternate locales: GetLocale("en_US.UTF-8") or GetLocale("ja_JA.UTF-8") or date_stream.getloc()
537 date_stream << boost::posix_time::microsec_clock::local_time();
538 std::string retval = date_stream.str();
539 TraceLogger() << "Filename initial timestamp: " << retval << " is valid utf8?: " << utf8::is_valid(retval.begin(), retval.end());
540
541 // replace spaces and colons with safer chars for filenames
542 std::replace(retval.begin(), retval.end(), ' ', '_');
543 std::replace(retval.begin(), retval.end(), ':', '-');
544
545 // filter non-filename-safe characters that are valid single-byte UTF-8 characters, in case the default locale for this system has weird chars in the time-date format
546 auto do_remove = [](char c) -> bool { return !std::isalnum(c) && c <= 127 && c != '_' && c != '-'; };
547 retval.erase(std::remove_if(retval.begin(), retval.end(), do_remove), retval.end());
548 TraceLogger() << "Filename filtered timestamp: " << retval << " is valid utf8?: " << utf8::is_valid(retval.begin(), retval.end());
549
550 return retval;
551 }
552
IsFOCScript(const boost::filesystem::path & path)553 bool IsFOCScript(const boost::filesystem::path& path)
554 { return fs::is_regular_file(path) && ".txt" == path.extension() && path.stem().extension() == ".focs"; }
555
ListDir(const fs::path & path,std::function<bool (const fs::path &)> predicate)556 std::vector<fs::path> ListDir(const fs::path& path, std::function<bool (const fs::path&)> predicate) {
557 std::vector<fs::path> retval;
558
559 if (!predicate)
560 predicate = static_cast<bool (*)(const fs::path&)>(fs::is_regular_file);
561
562 bool is_rel = path.is_relative();
563 if (!is_rel && (fs::is_empty(path) || !fs::is_directory(path))) {
564 DebugLogger() << "ListDir: File " << PathToString(path) << " was not included as it is empty or not a directoy";
565 } else {
566 const fs::path& default_path = is_rel ? GetResourceDir() / path : path;
567
568 for (fs::recursive_directory_iterator dir_it(default_path);
569 dir_it != fs::recursive_directory_iterator(); ++dir_it)
570 {
571 if (predicate(dir_it->path()))
572 retval.push_back(dir_it->path());
573 else
574 TraceLogger() << "ListDir: Discarding non-matching path: " << PathToString(dir_it->path());
575 }
576 }
577
578 if (retval.empty()) {
579 DebugLogger() << "ListDir: No paths found for " << path.string();
580 }
581
582 return retval;
583 }
584
IsValidUTF8(const std::string & in)585 bool IsValidUTF8(const std::string& in)
586 { return utf8::is_valid(in.begin(), in.end()); }
587
IsInDir(const fs::path & dir,const fs::path & test_dir)588 bool IsInDir(const fs::path& dir, const fs::path& test_dir) {
589 if (!fs::exists(dir) || !fs::is_directory(dir))
590 return false;
591
592 if (fs::exists(test_dir) && !fs::is_directory(test_dir))
593 return false;
594
595 // Resolve any symbolic links, dots or dot-dots
596 auto canon_dir = fs::canonical(dir);
597 // Don't resolve path if directory doesn't exist
598 // TODO: Change to fs::weakly_canonical after bump boost version above 1.60
599 auto canon_path = test_dir;
600 if (fs::exists(test_dir))
601 canon_path = fs::canonical(test_dir);
602
603 // Paths shorter than dir are not in dir
604 auto dir_length = std::distance(canon_dir.begin(), canon_dir.end());
605 auto path_length = std::distance(canon_path.begin(), canon_path.end());
606 if (path_length < dir_length)
607 return false;
608
609 // Check that the whole dir path matches the test path
610 // Extra portions of path are contained in dir
611 return std::equal(canon_dir.begin(), canon_dir.end(), canon_path.begin());
612 }
613
GetPath(PathType path_type)614 fs::path GetPath(PathType path_type) {
615 switch (path_type) {
616 case PATH_BINARY:
617 return GetBinDir();
618 case PATH_RESOURCE:
619 return GetResourceDir();
620 case PATH_DATA_ROOT:
621 return GetRootDataDir();
622 case PATH_DATA_USER:
623 return GetUserDataDir();
624 case PATH_CONFIG:
625 return GetUserConfigDir();
626 case PATH_SAVE:
627 return GetSaveDir();
628 case PATH_TEMP:
629 return fs::temp_directory_path();
630 case PATH_PYTHON:
631 #if defined(FREEORION_MACOSX) || defined(FREEORION_WIN32)
632 return GetPythonHome();
633 #endif
634 case PATH_INVALID:
635 default:
636 ErrorLogger() << "Invalid path type " << path_type;
637 return fs::temp_directory_path();
638 }
639 }
640
GetPath(const std::string & path_string)641 fs::path GetPath(const std::string& path_string) {
642 if (path_string.empty()) {
643 ErrorLogger() << "GetPath called with empty argument";
644 return fs::temp_directory_path();
645 }
646
647 PathType path_type;
648 try {
649 path_type = boost::lexical_cast<PathType>(path_string);
650 } catch (const boost::bad_lexical_cast& ec) {
651 // try partial match
652 std::string retval = path_string;
653 for (const auto& path_type_str : PathTypeStrings()) {
654 std::string path_type_string = PathToString(GetPath(path_type_str));
655 boost::replace_all(retval, path_type_str, path_type_string);
656 }
657 if (path_string != retval) {
658 return FilenameToPath(retval);
659 } else {
660 ErrorLogger() << "Invalid cast for PathType from string " << path_string;
661 return fs::temp_directory_path();
662 }
663 }
664 return GetPath(path_type);
665 }
666
IsExistingFile(const fs::path & path)667 bool IsExistingFile(const fs::path& path) {
668 try {
669 auto stat = fs::status(path);
670 return fs::exists(stat) && fs::is_regular_file(stat);
671 } catch(boost::filesystem::filesystem_error& ec) {
672 ErrorLogger() << "Filesystem error during stat of " << PathToString(path) << " : " << ec.what();
673 }
674
675 return false;
676 }
677