The Battle for Wesnoth  1.15.0-dev
filesystem_boost.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
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 /**
16  * @file
17  * File-IO
18  */
19 
20 #include "filesystem.hpp"
21 
22 #include "config.hpp"
23 #include "game_config.hpp"
24 #include "log.hpp"
27 #include "version.hpp"
28 
29 #include <boost/algorithm/string.hpp>
30 #include <boost/filesystem.hpp>
31 #include <boost/filesystem/fstream.hpp>
32 #include <boost/iostreams/device/file_descriptor.hpp>
33 #include <boost/iostreams/stream.hpp>
34 #include <boost/system/windows_error.hpp>
35 
36 #ifdef _WIN32
37 #include "log_windows.hpp"
38 
39 #include <boost/locale.hpp>
40 
41 #include <windows.h>
42 #include <shlobj.h>
43 #include <shlwapi.h>
44 
45 // Work around TDM-GCC not #defining this according to @newfrenchy83.
46 #ifndef VOLUME_NAME_NONE
47 #define VOLUME_NAME_NONE 0x4
48 #endif
49 
50 #endif /* !_WIN32 */
51 
52 #include <algorithm>
53 #include <set>
54 
55 static lg::log_domain log_filesystem("filesystem");
56 #define DBG_FS LOG_STREAM(debug, log_filesystem)
57 #define LOG_FS LOG_STREAM(info, log_filesystem)
58 #define WRN_FS LOG_STREAM(warn, log_filesystem)
59 #define ERR_FS LOG_STREAM(err, log_filesystem)
60 
61 namespace bfs = boost::filesystem;
62 using boost::system::error_code;
63 
64 namespace
65 {
66 // These are the filenames that get special processing
67 const std::string maincfg_filename = "_main.cfg";
68 const std::string finalcfg_filename = "_final.cfg";
69 const std::string initialcfg_filename = "_initial.cfg";
70 
71 // only used by windows but put outside the ifdef to let it check by ci build.
72 class customcodecvt : public std::codecvt<wchar_t /*intern*/, char /*extern*/, std::mbstate_t>
73 {
74 private:
75  // private static helper things
76  template<typename char_t_to>
77  struct customcodecvt_do_conversion_writer
78  {
79  customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end)
80  : to_next(_to_next)
81  , to_end(_to_end)
82  {
83  }
84 
85  char_t_to*& to_next;
86  char_t_to* to_end;
87 
88  bool can_push(std::size_t count) const
89  {
90  return static_cast<std::size_t>(to_end - to_next) > count;
91  }
92 
93  void push(char_t_to val)
94  {
95  assert(to_next != to_end);
96  *to_next++ = val;
97  }
98  };
99 
100  template<typename char_t_from, typename char_t_to>
101  static void customcodecvt_do_conversion(std::mbstate_t& /*state*/,
102  const char_t_from* from,
103  const char_t_from* from_end,
104  const char_t_from*& from_next,
105  char_t_to* to,
106  char_t_to* to_end,
107  char_t_to*& to_next)
108  {
109  typedef typename ucs4_convert_impl::convert_impl<char_t_from>::type impl_type_from;
110  typedef typename ucs4_convert_impl::convert_impl<char_t_to>::type impl_type_to;
111 
112  from_next = from;
113  to_next = to;
114  customcodecvt_do_conversion_writer<char_t_to> writer(to_next, to_end);
115 
116  while(from_next != from_end) {
117  impl_type_to::write(writer, impl_type_from::read(from_next, from_end));
118  }
119  }
120 
121 public:
122  // Not used by boost filesystem
123  int do_encoding() const noexcept
124  {
125  return 0;
126  }
127 
128  // Not used by boost filesystem
129  bool do_always_noconv() const noexcept
130  {
131  return false;
132  }
133 
134  int do_length(std::mbstate_t& /*state*/, const char* /*from*/, const char* /*from_end*/, std::size_t /*max*/) const
135  {
136  // Not used by boost filesystem
137  throw "Not supported";
138  }
139 
140  std::codecvt_base::result unshift(
141  std::mbstate_t& /*state*/, char* /*to*/, char* /*to_end*/, char*& /*to_next*/) const
142  {
143  // Not used by boost filesystem
144  throw "Not supported";
145  }
146 
147  // there are still some methods which could be implemented but aren't because boost filesystem won't use them.
148  std::codecvt_base::result do_in(std::mbstate_t& state,
149  const char* from,
150  const char* from_end,
151  const char*& from_next,
152  wchar_t* to,
153  wchar_t* to_end,
154  wchar_t*& to_next) const
155  {
156  try {
157  customcodecvt_do_conversion<char, wchar_t>(state, from, from_end, from_next, to, to_end, to_next);
158  } catch(...) {
159  ERR_FS << "Invalid UTF-8 string'" << std::string(from, from_end) << "' " << std::endl;
161  }
162 
163  return std::codecvt_base::ok;
164  }
165 
166  std::codecvt_base::result do_out(std::mbstate_t& state,
167  const wchar_t* from,
168  const wchar_t* from_end,
169  const wchar_t*& from_next,
170  char* to,
171  char* to_end,
172  char*& to_next) const
173  {
174  try {
175  customcodecvt_do_conversion<wchar_t, char>(state, from, from_end, from_next, to, to_end, to_next);
176  } catch(...) {
177  ERR_FS << "Invalid UTF-16 string" << std::endl;
179  }
180 
181  return std::codecvt_base::ok;
182  }
183 };
184 
185 #ifdef _WIN32
186 class static_runner
187 {
188 public:
189  static_runner()
190  {
191  // Boost uses the current locale to generate a UTF-8 one
192  std::locale utf8_loc = boost::locale::generator().generate("");
193 
194  // use a custom locale because we want to use out log.hpp functions in case of an invalid string.
195  utf8_loc = std::locale(utf8_loc, new customcodecvt());
196 
197  boost::filesystem::path::imbue(utf8_loc);
198  }
199 };
200 
201 static static_runner static_bfs_path_imbuer;
202 
203 typedef DWORD(WINAPI* GetFinalPathNameByHandleWPtr)(HANDLE, LPWSTR, DWORD, DWORD);
204 static GetFinalPathNameByHandleWPtr dyn_GetFinalPathNameByHandle;
205 
206 bool is_filename_case_correct(const std::string& fname, const boost::iostreams::file_descriptor_source& fd)
207 {
208  if(dyn_GetFinalPathNameByHandle == nullptr) {
209  // Windows XP. Just assume that the case is correct.
210  return true;
211  }
212 
213  wchar_t real_path[MAX_PATH];
214  dyn_GetFinalPathNameByHandle(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE);
215 
216  std::string real_name = filesystem::base_name(unicode_cast<std::string>(std::wstring(real_path)));
217  return real_name == filesystem::base_name(fname);
218 }
219 
220 #else
221 bool is_filename_case_correct(const std::string& /*fname*/, const boost::iostreams::file_descriptor_source& /*fd*/)
222 {
223  return true;
224 }
225 #endif
226 } // namespace
227 
228 namespace filesystem
229 {
230 void init()
231 {
232 #ifdef _WIN32
233  HMODULE kernel32 = GetModuleHandle(TEXT("Kernel32.dll"));
234  // Note that this returns a null pointer on Windows XP!
235  dyn_GetFinalPathNameByHandle
236  = reinterpret_cast<GetFinalPathNameByHandleWPtr>(GetProcAddress(kernel32, "GetFinalPathNameByHandleW"));
237 #endif
238 }
239 
240 static void push_if_exists(std::vector<std::string>* vec, const bfs::path& file, bool full)
241 {
242  if(vec != nullptr) {
243  if(full) {
244  vec->push_back(file.generic_string());
245  } else {
246  vec->push_back(file.filename().generic_string());
247  }
248  }
249 }
250 
251 static inline bool error_except_not_found(const error_code& ec)
252 {
253  return (ec && ec.value() != boost::system::errc::no_such_file_or_directory
254 #ifdef _WIN32
255  && ec.value() != boost::system::windows_error::path_not_found
256 #endif /*_WIN32*/
257  );
258 }
259 
260 static bool is_directory_internal(const bfs::path& fpath)
261 {
262  error_code ec;
263  bool is_dir = bfs::is_directory(fpath, ec);
264  if(error_except_not_found(ec)) {
265  LOG_FS << "Failed to check if " << fpath.string() << " is a directory: " << ec.message() << '\n';
266  }
267 
268  return is_dir;
269 }
270 
271 static bool file_exists(const bfs::path& fpath)
272 {
273  error_code ec;
274  bool exists = bfs::exists(fpath, ec);
275  if(error_except_not_found(ec)) {
276  ERR_FS << "Failed to check existence of file " << fpath.string() << ": " << ec.message() << '\n';
277  }
278 
279  return exists;
280 }
281 
282 static bfs::path get_dir(const bfs::path& dirpath)
283 {
284  bool is_dir = is_directory_internal(dirpath);
285  if(!is_dir) {
286  error_code ec;
287  bfs::create_directory(dirpath, ec);
288 
289  if(ec) {
290  ERR_FS << "Failed to create directory " << dirpath.string() << ": " << ec.message() << '\n';
291  }
292 
293  // This is probably redundant
294  is_dir = is_directory_internal(dirpath);
295  }
296 
297  if(!is_dir) {
298  ERR_FS << "Could not open or create directory " << dirpath.string() << '\n';
299  return std::string();
300  }
301 
302  return dirpath;
303 }
304 
305 static bool create_directory_if_missing(const bfs::path& dirpath)
306 {
307  error_code ec;
308  bfs::file_status fs = bfs::status(dirpath, ec);
309 
310  if(error_except_not_found(ec)) {
311  ERR_FS << "Failed to retrieve file status for " << dirpath.string() << ": " << ec.message() << '\n';
312  return false;
313  } else if(bfs::is_directory(fs)) {
314  DBG_FS << "directory " << dirpath.string() << " exists, not creating\n";
315  return true;
316  } else if(bfs::exists(fs)) {
317  ERR_FS << "cannot create directory " << dirpath.string() << "; file exists\n";
318  return false;
319  }
320 
321  bool created = bfs::create_directory(dirpath, ec);
322  if(ec) {
323  ERR_FS << "Failed to create directory " << dirpath.string() << ": " << ec.message() << '\n';
324  }
325 
326  return created;
327 }
328 
330 {
331  DBG_FS << "creating recursive directory: " << dirpath.string() << '\n';
332 
333  if(dirpath.empty()) {
334  return false;
335  }
336 
337  error_code ec;
338  bfs::file_status fs = bfs::status(dirpath);
339 
340  if(error_except_not_found(ec)) {
341  ERR_FS << "Failed to retrieve file status for " << dirpath.string() << ": " << ec.message() << '\n';
342  return false;
343  } else if(bfs::is_directory(fs)) {
344  return true;
345  } else if(bfs::exists(fs)) {
346  return false;
347  }
348 
349  if(!dirpath.has_parent_path() || create_directory_if_missing_recursive(dirpath.parent_path())) {
350  return create_directory_if_missing(dirpath);
351  } else {
352  ERR_FS << "Could not create parents to " << dirpath.string() << '\n';
353  return false;
354  }
355 }
356 
357 void get_files_in_dir(const std::string& dir,
358  std::vector<std::string>* files,
359  std::vector<std::string>* dirs,
360  file_name_option mode,
361  file_filter_option filter,
362  file_reorder_option reorder,
363  file_tree_checksum* checksum)
364 {
365  if(bfs::path(dir).is_relative() && !game_config::path.empty()) {
366  bfs::path absolute_dir(game_config::path);
367  absolute_dir /= dir;
368 
369  if(is_directory_internal(absolute_dir)) {
370  get_files_in_dir(absolute_dir.string(), files, dirs, mode, filter, reorder, checksum);
371  return;
372  }
373  }
374 
375  const bfs::path dirpath(dir);
376 
377  if(reorder == DO_REORDER) {
378  LOG_FS << "searching for _main.cfg in directory " << dir << '\n';
379  const bfs::path maincfg = dirpath / maincfg_filename;
380 
381  if(file_exists(maincfg)) {
382  LOG_FS << "_main.cfg found : " << maincfg << '\n';
383  push_if_exists(files, maincfg, mode == ENTIRE_FILE_PATH);
384  return;
385  }
386  }
387 
388  error_code ec;
389  bfs::directory_iterator di(dirpath, ec);
390  bfs::directory_iterator end;
391 
392  // Probably not a directory, let the caller deal with it.
393  if(ec) {
394  return;
395  }
396 
397  for(; di != end; ++di) {
398  bfs::file_status st = di->status(ec);
399  if(ec) {
400  LOG_FS << "Failed to get file status of " << di->path().string() << ": " << ec.message() << '\n';
401  continue;
402  }
403 
404  if(st.type() == bfs::regular_file) {
405  {
406  std::string basename = di->path().filename().string();
407  if(filter == SKIP_PBL_FILES && looks_like_pbl(basename))
408  continue;
409  if(!basename.empty() && basename[0] == '.')
410  continue;
411  }
412 
413  push_if_exists(files, di->path(), mode == ENTIRE_FILE_PATH);
414 
415  if(checksum != nullptr) {
416  std::time_t mtime = bfs::last_write_time(di->path(), ec);
417  if(ec) {
418  LOG_FS << "Failed to read modification time of " << di->path().string() << ": " << ec.message()
419  << '\n';
420  } else if(mtime > checksum->modified) {
421  checksum->modified = mtime;
422  }
423 
424  uintmax_t size = bfs::file_size(di->path(), ec);
425  if(ec) {
426  LOG_FS << "Failed to read filesize of " << di->path().string() << ": " << ec.message() << '\n';
427  } else {
428  checksum->sum_size += size;
429  }
430 
431  checksum->nfiles++;
432  }
433  } else if(st.type() == bfs::directory_file) {
434  std::string basename = di->path().filename().string();
435 
436  if(!basename.empty() && basename[0] == '.') {
437  continue;
438  }
439 
440  if(filter == SKIP_MEDIA_DIR && (basename == "images" || basename == "sounds")) {
441  continue;
442  }
443 
444  const bfs::path inner_main(di->path() / maincfg_filename);
445  bfs::file_status main_st = bfs::status(inner_main, ec);
446 
447  if(error_except_not_found(ec)) {
448  LOG_FS << "Failed to get file status of " << inner_main.string() << ": " << ec.message() << '\n';
449  } else if(reorder == DO_REORDER && main_st.type() == bfs::regular_file) {
450  LOG_FS << "_main.cfg found : "
451  << (mode == ENTIRE_FILE_PATH ? inner_main.string() : inner_main.filename().string()) << '\n';
452  push_if_exists(files, inner_main, mode == ENTIRE_FILE_PATH);
453  } else {
454  push_if_exists(dirs, di->path(), mode == ENTIRE_FILE_PATH);
455  }
456  }
457  }
458 
459  if(files != nullptr) {
460  std::sort(files->begin(), files->end());
461  }
462 
463  if(dirs != nullptr) {
464  std::sort(dirs->begin(), dirs->end());
465  }
466 
467  if(files != nullptr && reorder == DO_REORDER) {
468  // move finalcfg_filename, if present, to the end of the vector
469  for(unsigned int i = 0; i < files->size(); i++) {
470  if(ends_with((*files)[i], "/" + finalcfg_filename)) {
471  files->push_back((*files)[i]);
472  files->erase(files->begin() + i);
473  break;
474  }
475  }
476 
477  // move initialcfg_filename, if present, to the beginning of the vector
478  int foundit = -1;
479  for(unsigned int i = 0; i < files->size(); i++)
480  if(ends_with((*files)[i], "/" + initialcfg_filename)) {
481  foundit = i;
482  break;
483  }
484  if(foundit > 0) {
485  std::string initialcfg = (*files)[foundit];
486  for(unsigned int i = foundit; i > 0; i--)
487  (*files)[i] = (*files)[i - 1];
488  (*files)[0] = initialcfg;
489  }
490  }
491 }
492 
493 std::string get_dir(const std::string& dir)
494 {
495  return get_dir(bfs::path(dir)).string();
496 }
497 
498 std::string get_next_filename(const std::string& name, const std::string& extension)
499 {
500  std::string next_filename;
501  int counter = 0;
502 
503  do {
504  std::stringstream filename;
505 
506  filename << name;
507  filename.width(3);
508  filename.fill('0');
509  filename.setf(std::ios_base::right);
510  filename << counter << extension;
511 
512  counter++;
513  next_filename = filename.str();
514  } while(file_exists(next_filename) && counter < 1000);
515 
516  return next_filename;
517 }
518 
520 
521 static const std::string& get_version_path_suffix()
522 {
523  static std::string suffix;
524 
525  // We only really need to generate this once since
526  // the version number cannot change during runtime.
527 
528  if(suffix.empty()) {
529  std::ostringstream s;
531  suffix = s.str();
532  }
533 
534  return suffix;
535 }
536 
537 static void setup_user_data_dir()
538 {
539  if(!create_directory_if_missing_recursive(user_data_dir)) {
540  ERR_FS << "could not open or create user data directory at " << user_data_dir.string() << '\n';
541  return;
542  }
543  // TODO: this may not print the error message if the directory exists but we don't have the proper permissions
544 
545  // Create user data and add-on directories
546  create_directory_if_missing(user_data_dir / "editor");
547  create_directory_if_missing(user_data_dir / "editor" / "maps");
548  create_directory_if_missing(user_data_dir / "editor" / "scenarios");
549  create_directory_if_missing(user_data_dir / "data");
550  create_directory_if_missing(user_data_dir / "data" / "add-ons");
551  create_directory_if_missing(user_data_dir / "saves");
552  create_directory_if_missing(user_data_dir / "persist");
553 
554 #ifdef _WIN32
556 #endif
557 }
558 
559 #ifdef _WIN32
560 // As a convenience for portable installs on Windows, relative paths with . or
561 // .. as the first component are considered relative to the current workdir
562 // instead of Documents/My Games.
563 static bool is_path_relative_to_cwd(const std::string& str)
564 {
565  const bfs::path p(str);
566 
567  if(p.empty()) {
568  return false;
569  }
570 
571  return *p.begin() == "." || *p.begin() == "..";
572 }
573 #endif
574 
575 void set_user_data_dir(std::string newprefdir)
576 {
577 #ifdef PREFERENCES_DIR
578  if(newprefdir.empty()) {
579  newprefdir = PREFERENCES_DIR;
580  }
581 #endif
582 
583 #ifdef _WIN32
584  if(newprefdir.size() > 2 && newprefdir[1] == ':') {
585  // allow absolute path override
586  user_data_dir = newprefdir;
587  } else if(is_path_relative_to_cwd(newprefdir)) {
588  // Custom directory relative to workdir (for portable installs, etc.)
589  user_data_dir = get_cwd() + "/" + newprefdir;
590  } else {
591  if(newprefdir.empty()) {
592  newprefdir = "Wesnoth" + get_version_path_suffix();
593  }
594 
595  PWSTR docs_path = nullptr;
596  HRESULT res = SHGetKnownFolderPath(FOLDERID_Documents, KF_FLAG_CREATE, nullptr, &docs_path);
597 
598  if(res != S_OK) {
599  //
600  // Crummy fallback path full of pain and suffering.
601  //
602  ERR_FS << "Could not determine path to user's Documents folder! (" << std::hex << "0x" << res << std::dec << ") "
603  << "User config/data directories may be unavailable for "
604  << "this session. Please report this as a bug.\n";
605  user_data_dir = bfs::path(get_cwd()) / newprefdir;
606  } else {
607  bfs::path games_path = bfs::path(docs_path) / "My Games";
608  create_directory_if_missing(games_path);
609 
610  user_data_dir = games_path / newprefdir;
611  }
612 
613  CoTaskMemFree(docs_path);
614  }
615 
616 #else /*_WIN32*/
617 
618  std::string backupprefdir = ".wesnoth" + get_version_path_suffix();
619 
620 #ifdef _X11
621  const char* home_str = getenv("HOME");
622 
623  if(newprefdir.empty()) {
624  char const* xdg_data = getenv("XDG_DATA_HOME");
625  if(!xdg_data || xdg_data[0] == '\0') {
626  if(!home_str) {
627  newprefdir = backupprefdir;
628  goto other;
629  }
630 
631  user_data_dir = home_str;
632  user_data_dir /= ".local/share";
633  } else {
634  user_data_dir = xdg_data;
635  }
636 
637  user_data_dir /= "wesnoth";
638  user_data_dir /= get_version_path_suffix();
639  } else {
640  other:
641  bfs::path home = home_str ? home_str : ".";
642 
643  if(newprefdir[0] == '/') {
644  user_data_dir = newprefdir;
645  } else {
646  user_data_dir = home / newprefdir;
647  }
648  }
649 #else
650  if(newprefdir.empty()) {
651  newprefdir = backupprefdir;
652  }
653 
654  const char* home_str = getenv("HOME");
655  bfs::path home = home_str ? home_str : ".";
656 
657  if(newprefdir[0] == '/') {
658  user_data_dir = newprefdir;
659  } else {
660  user_data_dir = home / newprefdir;
661  }
662 #endif
663 
664 #endif /*_WIN32*/
666  user_data_dir = normalize_path(user_data_dir.string(), true, true);
667 }
668 
669 static void set_user_config_path(bfs::path newconfig)
670 {
671  user_config_dir = newconfig;
672  if(!create_directory_if_missing_recursive(user_config_dir)) {
673  ERR_FS << "could not open or create user config directory at " << user_config_dir.string() << '\n';
674  }
675 }
676 
677 void set_user_config_dir(const std::string& newconfigdir)
678 {
679  set_user_config_path(newconfigdir);
680 }
681 
683 {
684  if(user_data_dir.empty()) {
685  set_user_data_dir(std::string());
686  }
687 
688  return user_data_dir;
689 }
690 
691 std::string get_user_config_dir()
692 {
693  if(user_config_dir.empty()) {
694 #if defined(_X11) && !defined(PREFERENCES_DIR)
695  char const* xdg_config = getenv("XDG_CONFIG_HOME");
696 
697  if(!xdg_config || xdg_config[0] == '\0') {
698  xdg_config = getenv("HOME");
699  if(!xdg_config) {
700  user_config_dir = get_user_data_path();
701  return user_config_dir.string();
702  }
703 
704  user_config_dir = xdg_config;
705  user_config_dir /= ".config";
706  } else {
707  user_config_dir = xdg_config;
708  }
709 
710  user_config_dir /= "wesnoth";
711  set_user_config_path(user_config_dir);
712 #else
713  user_config_dir = get_user_data_path();
714 #endif
715  }
716 
717  return user_config_dir.string();
718 }
719 
720 std::string get_user_data_dir()
721 {
722  return get_user_data_path().string();
723 }
724 
725 std::string get_cache_dir()
726 {
727  if(cache_dir.empty()) {
728 #if defined(_X11) && !defined(PREFERENCES_DIR)
729  char const* xdg_cache = getenv("XDG_CACHE_HOME");
730 
731  if(!xdg_cache || xdg_cache[0] == '\0') {
732  xdg_cache = getenv("HOME");
733  if(!xdg_cache) {
734  cache_dir = get_dir(get_user_data_path() / "cache");
735  return cache_dir.string();
736  }
737 
738  cache_dir = xdg_cache;
739  cache_dir /= ".cache";
740  } else {
741  cache_dir = xdg_cache;
742  }
743 
744  cache_dir /= "wesnoth";
746 #else
747  cache_dir = get_dir(get_user_data_path() / "cache");
748 #endif
749  }
750 
751  return cache_dir.string();
752 }
753 
754 std::string get_cwd()
755 {
756  error_code ec;
757  bfs::path cwd = bfs::current_path(ec);
758 
759  if(ec) {
760  ERR_FS << "Failed to get current directory: " << ec.message() << '\n';
761  return "";
762  }
763 
764  return cwd.generic_string();
765 }
766 std::string get_exe_dir()
767 {
768 #ifdef _WIN32
769  wchar_t process_path[MAX_PATH];
770  SetLastError(ERROR_SUCCESS);
771 
772  GetModuleFileNameW(nullptr, process_path, MAX_PATH);
773 
774  if(GetLastError() != ERROR_SUCCESS) {
775  return get_cwd();
776  }
777 
778  bfs::path exe(process_path);
779  return exe.parent_path().string();
780 #else
781  if(bfs::exists("/proc/")) {
782  bfs::path self_exe("/proc/self/exe");
783  error_code ec;
784  bfs::path exe = bfs::read_symlink(self_exe, ec);
785  if(ec) {
786  return std::string();
787  }
788 
789  return exe.parent_path().string();
790  } else {
791  return get_cwd();
792  }
793 #endif
794 }
795 
796 bool make_directory(const std::string& dirname)
797 {
798  error_code ec;
799  bool created = bfs::create_directory(bfs::path(dirname), ec);
800  if(ec) {
801  ERR_FS << "Failed to create directory " << dirname << ": " << ec.message() << '\n';
802  }
803 
804  return created;
805 }
806 
807 bool delete_directory(const std::string& dirname, const bool keep_pbl)
808 {
809  bool ret = true;
810  std::vector<std::string> files;
811  std::vector<std::string> dirs;
812  error_code ec;
813 
814  get_files_in_dir(dirname, &files, &dirs, ENTIRE_FILE_PATH, keep_pbl ? SKIP_PBL_FILES : NO_FILTER);
815 
816  if(!files.empty()) {
817  for(const std::string& f : files) {
818  bfs::remove(bfs::path(f), ec);
819  if(ec) {
820  LOG_FS << "remove(" << f << "): " << ec.message() << '\n';
821  ret = false;
822  }
823  }
824  }
825 
826  if(!dirs.empty()) {
827  for(const std::string& d : dirs) {
828  // TODO: this does not preserve any other PBL files
829  // filesystem.cpp does this too, so this might be intentional
830  if(!delete_directory(d))
831  ret = false;
832  }
833  }
834 
835  if(ret) {
836  bfs::remove(bfs::path(dirname), ec);
837  if(ec) {
838  LOG_FS << "remove(" << dirname << "): " << ec.message() << '\n';
839  ret = false;
840  }
841  }
842 
843  return ret;
844 }
845 
846 bool delete_file(const std::string& filename)
847 {
848  error_code ec;
849  bool ret = bfs::remove(bfs::path(filename), ec);
850  if(ec) {
851  ERR_FS << "Could not delete file " << filename << ": " << ec.message() << '\n';
852  }
853 
854  return ret;
855 }
856 
857 std::string read_file(const std::string& fname)
858 {
859  scoped_istream is = istream_file(fname);
860  std::stringstream ss;
861  ss << is->rdbuf();
862  return ss.str();
863 }
864 
865 filesystem::scoped_istream istream_file(const std::string& fname, bool treat_failure_as_error)
866 {
867  LOG_FS << "Streaming " << fname << " for reading.\n";
868 
869  if(fname.empty()) {
870  ERR_FS << "Trying to open file with empty name.\n";
871  filesystem::scoped_istream s(new bfs::ifstream());
872  s->clear(std::ios_base::failbit);
873  return s;
874  }
875 
876  // mingw doesn't support std::basic_ifstream::basic_ifstream(const wchar_t* fname)
877  // that why boost::filesystem::fstream.hpp doesn't work with mingw.
878  try {
879  boost::iostreams::file_descriptor_source fd(bfs::path(fname), std::ios_base::binary);
880 
881  // TODO: has this still use ?
882  if(!fd.is_open() && treat_failure_as_error) {
883  ERR_FS << "Could not open '" << fname << "' for reading.\n";
884  } else if(!is_filename_case_correct(fname, fd)) {
885  ERR_FS << "Not opening '" << fname << "' due to case mismatch.\n";
886  filesystem::scoped_istream s(new bfs::ifstream());
887  s->clear(std::ios_base::failbit);
888  return s;
889  }
890 
891  return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_source>>(fd, 4096, 0);
892  } catch(const std::exception&) {
893  if(treat_failure_as_error) {
894  ERR_FS << "Could not open '" << fname << "' for reading.\n";
895  }
896 
897  filesystem::scoped_istream s(new bfs::ifstream());
898  s->clear(std::ios_base::failbit);
899  return s;
900  }
901 }
902 
903 filesystem::scoped_ostream ostream_file(const std::string& fname, bool create_directory)
904 {
905  LOG_FS << "streaming " << fname << " for writing.\n";
906 #if 1
907  try {
908  boost::iostreams::file_descriptor_sink fd(bfs::path(fname), std::ios_base::binary);
909  return std::make_unique<boost::iostreams::stream<boost::iostreams::file_descriptor_sink>>(fd, 4096, 0);
910  } catch(BOOST_IOSTREAMS_FAILURE& e) {
911  // If this operation failed because the parent directory didn't exist, create the parent directory and
912  // retry.
913  error_code ec_unused;
914  if(create_directory && bfs::create_directories(bfs::path(fname).parent_path(), ec_unused)) {
915  return ostream_file(fname, false);
916  }
917 
918  throw filesystem::io_exception(e.what());
919  }
920 #else
921  return new bfs::ofstream(bfs::path(fname), std::ios_base::binary);
922 #endif
923 }
924 
925 // Throws io_exception if an error occurs
926 void write_file(const std::string& fname, const std::string& data)
927 {
928  scoped_ostream os = ostream_file(fname);
929  os->exceptions(std::ios_base::goodbit);
930 
931  const std::size_t block_size = 4096;
932  char buf[block_size];
933 
934  for(std::size_t i = 0; i < data.size(); i += block_size) {
935  const std::size_t bytes = std::min<std::size_t>(block_size, data.size() - i);
936  std::copy(data.begin() + i, data.begin() + i + bytes, buf);
937 
938  os->write(buf, bytes);
939  if(os->bad()) {
940  throw io_exception("Error writing to file: '" + fname + "'");
941  }
942  }
943 }
944 
945 bool create_directory_if_missing(const std::string& dirname)
946 {
947  return create_directory_if_missing(bfs::path(dirname));
948 }
949 
950 bool create_directory_if_missing_recursive(const std::string& dirname)
951 {
953 }
954 
955 bool is_directory(const std::string& fname)
956 {
957  return is_directory_internal(bfs::path(fname));
958 }
959 
960 bool file_exists(const std::string& name)
961 {
962  return file_exists(bfs::path(name));
963 }
964 
965 time_t file_modified_time(const std::string& fname)
966 {
967  error_code ec;
968  std::time_t mtime = bfs::last_write_time(bfs::path(fname), ec);
969  if(ec) {
970  LOG_FS << "Failed to read modification time of " << fname << ": " << ec.message() << '\n';
971  }
972 
973  return mtime;
974 }
975 
976 bool is_gzip_file(const std::string& filename)
977 {
978  return bfs::path(filename).extension() == ".gz";
979 }
980 
981 bool is_bzip2_file(const std::string& filename)
982 {
983  return bfs::path(filename).extension() == ".bz2";
984 }
985 
986 int file_size(const std::string& fname)
987 {
988  error_code ec;
989  uintmax_t size = bfs::file_size(bfs::path(fname), ec);
990  if(ec) {
991  LOG_FS << "Failed to read filesize of " << fname << ": " << ec.message() << '\n';
992  return -1;
993  } else if(size > INT_MAX) {
994  return INT_MAX;
995  } else {
996  return size;
997  }
998 }
999 
1000 int dir_size(const std::string& pname)
1001 {
1002  bfs::path p(pname);
1003  uintmax_t size_sum = 0;
1004  error_code ec;
1005  for(bfs::recursive_directory_iterator i(p), end; i != end && !ec; ++i) {
1006  if(bfs::is_regular_file(i->path())) {
1007  size_sum += bfs::file_size(i->path(), ec);
1008  }
1009  }
1010 
1011  if(ec) {
1012  LOG_FS << "Failed to read directorysize of " << pname << ": " << ec.message() << '\n';
1013  return -1;
1014  } else if(size_sum > INT_MAX) {
1015  return INT_MAX;
1016  } else {
1017  return size_sum;
1018  }
1019 }
1020 
1021 std::string base_name(const std::string& file, const bool remove_extension)
1022 {
1023  if(!remove_extension) {
1024  return bfs::path(file).filename().string();
1025  } else {
1026  return bfs::path(file).stem().string();
1027  }
1028 }
1029 
1030 std::string directory_name(const std::string& file)
1031 {
1032  return bfs::path(file).parent_path().string();
1033 }
1034 
1035 std::string nearest_extant_parent(const std::string& file)
1036 {
1037  if(file.empty()) {
1038  return "";
1039  }
1040 
1041  bfs::path p{file};
1042  error_code ec;
1043 
1044  do {
1045  p = p.parent_path();
1046  bfs::path q = canonical(p, ec);
1047  if(!ec) {
1048  p = q;
1049  }
1050  } while(ec && !is_root(p.string()));
1051 
1052  return ec ? "" : p.string();
1053 }
1054 
1055 bool is_path_sep(char c)
1056 {
1057  static const bfs::path sep = bfs::path("/").make_preferred();
1058  const std::string s = std::string(1, c);
1059  return sep == bfs::path(s).make_preferred();
1060 }
1061 
1063 {
1064  return bfs::path::preferred_separator;
1065 }
1066 
1067 bool is_root(const std::string& path)
1068 {
1069 #ifndef _WIN32
1070  error_code ec;
1071  const bfs::path& p = bfs::canonical(path, ec);
1072  return ec ? false : !p.has_parent_path();
1073 #else
1074  //
1075  // Boost.Filesystem is completely unreliable when it comes to detecting
1076  // whether a path refers to a drive's root directory on Windows, so we are
1077  // forced to take an alternative approach here. Instead of hand-parsing
1078  // strings we'll just call a graphical shell service.
1079  //
1080  // There are several poorly-documented ways to refer to a drive in Windows by
1081  // escaping the filesystem namespace using \\.\, \\?\, and \??\. We're just
1082  // going to ignore those here, which may yield unexpected results in places
1083  // such as the file dialog. This function really shouldn't be used for
1084  // security validation anyway, and there are virtually infinite ways to name
1085  // a drive's root using the NT object namespace so it's pretty pointless to
1086  // try to catch those there.
1087  //
1088  // (And no, shlwapi.dll's PathIsRoot() doesn't recognize \\.\C:\, \\?\C:\, or
1089  // \??\C:\ as roots either.)
1090  //
1091  // More generally, do NOT use this code in security-sensitive applications.
1092  //
1093  // See also: <https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html>
1094  //
1095  const std::wstring& wpath = bfs::path{path}.make_preferred().wstring();
1096  return PathIsRootW(wpath.c_str()) == TRUE;
1097 #endif
1098 }
1099 
1100 std::string root_name(const std::string& path)
1101 {
1102  return bfs::path{path}.root_name().string();
1103 }
1104 
1105 bool is_relative(const std::string& path)
1106 {
1107  return bfs::path{path}.is_relative();
1108 }
1109 
1110 std::string normalize_path(const std::string& fpath, bool normalize_separators, bool resolve_dot_entries)
1111 {
1112  if(fpath.empty()) {
1113  return fpath;
1114  }
1115 
1116  error_code ec;
1117  bfs::path p = resolve_dot_entries ? bfs::canonical(fpath, ec) : bfs::absolute(fpath);
1118 
1119  if(ec) {
1120  return "";
1121  }
1122 
1123  if(normalize_separators) {
1124  return p.make_preferred().string();
1125  } else {
1126  return p.string();
1127  }
1128 }
1129 
1130 /**
1131  * The paths manager is responsible for recording the various paths
1132  * that binary files may be located at.
1133  * It should be passed a config object which holds binary path information.
1134  * This is in the format
1135  *@verbatim
1136  * [binary_path]
1137  * path=<path>
1138  * [/binary_path]
1139  * Binaries will be searched for in [wesnoth-path]/data/<path>/images/
1140  *@endverbatim
1141  */
1142 namespace
1143 {
1144 std::set<std::string> binary_paths;
1145 
1146 typedef std::map<std::string, std::vector<std::string>> paths_map;
1147 paths_map binary_paths_cache;
1148 
1149 } // namespace
1150 
1151 static void init_binary_paths()
1152 {
1153  if(binary_paths.empty()) {
1154  binary_paths.insert("");
1155  }
1156 }
1157 
1158 binary_paths_manager::binary_paths_manager()
1159  : paths_()
1160 {
1161 }
1162 
1164  : paths_()
1165 {
1166  set_paths(cfg);
1167 }
1168 
1170 {
1171  cleanup();
1172 }
1173 
1175 {
1176  cleanup();
1178 
1179  for(const config& bp : cfg.child_range("binary_path")) {
1180  std::string path = bp["path"].str();
1181  if(path.find("..") != std::string::npos) {
1182  ERR_FS << "Invalid binary path '" << path << "'\n";
1183  continue;
1184  }
1185 
1186  if(!path.empty() && path[path.size() - 1] != '/')
1187  path += "/";
1188  if(binary_paths.count(path) == 0) {
1189  binary_paths.insert(path);
1190  paths_.push_back(path);
1191  }
1192  }
1193 }
1194 
1196 {
1197  binary_paths_cache.clear();
1198 
1199  for(const std::string& p : paths_) {
1200  binary_paths.erase(p);
1201  }
1202 }
1203 
1205 {
1206  binary_paths_cache.clear();
1207 }
1208 
1209 static bool is_legal_file(const std::string& filename_str)
1210 {
1211  DBG_FS << "Looking for '" << filename_str << "'.\n";
1212 
1213  if(filename_str.empty()) {
1214  LOG_FS << " invalid filename\n";
1215  return false;
1216  }
1217 
1218  if(filename_str.find("..") != std::string::npos) {
1219  ERR_FS << "Illegal path '" << filename_str << "' (\"..\" not allowed).\n";
1220  return false;
1221  }
1222 
1223  if(filename_str.find('\\') != std::string::npos) {
1224  ERR_FS << "Illegal path '" << filename_str
1225  << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl;
1226  return false;
1227  }
1228 
1229  bfs::path filepath(filename_str);
1230 
1231  if(default_blacklist.match_file(filepath.filename().string())) {
1232  ERR_FS << "Illegal path '" << filename_str << "' (blacklisted filename)." << std::endl;
1233  return false;
1234  }
1235 
1236  if(std::any_of(filepath.begin(), filepath.end(),
1237  [](const bfs::path& dirname) { return default_blacklist.match_dir(dirname.string()); })) {
1238  ERR_FS << "Illegal path '" << filename_str << "' (blacklisted directory name)." << std::endl;
1239  return false;
1240  }
1241 
1242  return true;
1243 }
1244 
1245 /**
1246  * Returns a vector with all possible paths to a given type of binary,
1247  * e.g. 'images', 'sounds', etc,
1248  */
1249 const std::vector<std::string>& get_binary_paths(const std::string& type)
1250 {
1251  const paths_map::const_iterator itor = binary_paths_cache.find(type);
1252  if(itor != binary_paths_cache.end()) {
1253  return itor->second;
1254  }
1255 
1256  if(type.find("..") != std::string::npos) {
1257  // Not an assertion, as language.cpp is passing user data as type.
1258  ERR_FS << "Invalid WML type '" << type << "' for binary paths\n";
1259  static std::vector<std::string> dummy;
1260  return dummy;
1261  }
1262 
1263  std::vector<std::string>& res = binary_paths_cache[type];
1264 
1266 
1267  for(const std::string& path : binary_paths) {
1268  res.push_back(get_user_data_dir() + "/" + path + type + "/");
1269 
1270  if(!game_config::path.empty()) {
1271  res.push_back(game_config::path + "/" + path + type + "/");
1272  }
1273  }
1274 
1275  // not found in "/type" directory, try main directory
1276  res.push_back(get_user_data_dir() + "/");
1277 
1278  if(!game_config::path.empty()) {
1279  res.push_back(game_config::path + "/");
1280  }
1281 
1282  return res;
1283 }
1284 
1285 std::string get_binary_file_location(const std::string& type, const std::string& filename)
1286 {
1287  // We define ".." as "remove everything before" this is needed because
1288  // on the one hand allowing ".." would be a security risk but
1289  // especially for terrains the c++ engine puts a hardcoded "terrain/" before filename
1290  // and there would be no way to "escape" from "terrain/" otherwise. This is not the
1291  // best solution but we cannot remove it without another solution (subtypes maybe?).
1292 
1293  {
1294  std::string::size_type pos = filename.rfind("../");
1295  if(pos != std::string::npos) {
1296  return get_binary_file_location(type, filename.substr(pos + 3));
1297  }
1298  }
1299 
1300  if(!is_legal_file(filename)) {
1301  return std::string();
1302  }
1303 
1304  for(const std::string& bp : get_binary_paths(type)) {
1305  bfs::path bpath(bp);
1306  bpath /= filename;
1307 
1308  DBG_FS << " checking '" << bp << "'\n";
1309 
1310  if(file_exists(bpath)) {
1311  DBG_FS << " found at '" << bpath.string() << "'\n";
1312  return bpath.string();
1313  }
1314  }
1315 
1316  DBG_FS << " not found\n";
1317  return std::string();
1318 }
1319 
1320 std::string get_binary_dir_location(const std::string& type, const std::string& filename)
1321 {
1322  if(!is_legal_file(filename)) {
1323  return std::string();
1324  }
1325 
1326  for(const std::string& bp : get_binary_paths(type)) {
1327  bfs::path bpath(bp);
1328  bpath /= filename;
1329  DBG_FS << " checking '" << bp << "'\n";
1330  if(is_directory_internal(bpath)) {
1331  DBG_FS << " found at '" << bpath.string() << "'\n";
1332  return bpath.string();
1333  }
1334  }
1335 
1336  DBG_FS << " not found\n";
1337  return std::string();
1338 }
1339 
1340 std::string get_wml_location(const std::string& filename, const std::string& current_dir)
1341 {
1342  if(!is_legal_file(filename)) {
1343  return std::string();
1344  }
1345 
1346  assert(game_config::path.empty() == false);
1347 
1348  bfs::path fpath(filename);
1349  bfs::path result;
1350 
1351  if(filename[0] == '~') {
1352  result /= get_user_data_path() / "data" / filename.substr(1);
1353  DBG_FS << " trying '" << result.string() << "'\n";
1354  } else if(*fpath.begin() == ".") {
1355  if(!current_dir.empty()) {
1356  result /= bfs::path(current_dir);
1357  } else {
1358  result /= bfs::path(game_config::path) / "data";
1359  }
1360 
1361  result /= filename;
1362  } else if(!game_config::path.empty()) {
1363  result /= bfs::path(game_config::path) / "data" / filename;
1364  }
1365 
1366  if(result.empty() || !file_exists(result)) {
1367  DBG_FS << " not found\n";
1368  result.clear();
1369  } else {
1370  DBG_FS << " found: '" << result.string() << "'\n";
1371  }
1372 
1373  return result.string();
1374 }
1375 
1376 static bfs::path subtract_path(const bfs::path& full, const bfs::path& prefix)
1377 {
1378  bfs::path::iterator fi = full.begin(), fe = full.end(), pi = prefix.begin(), pe = prefix.end();
1379  while(fi != fe && pi != pe && *fi == *pi) {
1380  ++fi;
1381  ++pi;
1382  }
1383 
1384  bfs::path rest;
1385  if(pi == pe) {
1386  while(fi != fe) {
1387  rest /= *fi;
1388  ++fi;
1389  }
1390  }
1391 
1392  return rest;
1393 }
1394 
1395 std::string get_short_wml_path(const std::string& filename)
1396 {
1397  bfs::path full_path(filename);
1398 
1399  bfs::path partial = subtract_path(full_path, get_user_data_path() / "data");
1400  if(!partial.empty()) {
1401  return "~" + partial.generic_string();
1402  }
1403 
1404  partial = subtract_path(full_path, bfs::path(game_config::path) / "data");
1405  if(!partial.empty()) {
1406  return partial.generic_string();
1407  }
1408 
1409  return filename;
1410 }
1411 
1412 std::string get_independent_image_path(const std::string& filename)
1413 {
1414  bfs::path full_path(get_binary_file_location("images", filename));
1415 
1416  if(full_path.empty()) {
1417  return full_path.generic_string();
1418  }
1419 
1420  bfs::path partial = subtract_path(full_path, get_user_data_path());
1421  if(!partial.empty()) {
1422  return partial.generic_string();
1423  }
1424 
1425  partial = subtract_path(full_path, game_config::path);
1426  if(!partial.empty()) {
1427  return partial.generic_string();
1428  }
1429 
1430  return full_path.generic_string();
1431 }
1432 
1433 std::string get_program_invocation(const std::string& program_name)
1434 {
1435  const std::string real_program_name(program_name
1436 #ifdef DEBUG
1437  + "-debug"
1438 #endif
1439 #ifdef _WIN32
1440  + ".exe"
1441 #endif
1442  );
1443 
1444  return (bfs::path(game_config::wesnoth_program_dir) / real_program_name).string();
1445 }
1446 
1447 std::string sanitize_path(const std::string& path)
1448 {
1449 #ifdef _WIN32
1450  const char* user_name = getenv("USERNAME");
1451 #else
1452  const char* user_name = getenv("USER");
1453 #endif
1454 
1455  std::string canonicalized = filesystem::normalize_path(path, true, false);
1456  if(user_name != nullptr) {
1457  boost::replace_all(canonicalized, user_name, "USER");
1458  }
1459 
1460  return canonicalized;
1461 }
1462 
1463 } // namespace filesystem
std::string get_binary_dir_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual directory of a given type or an empty string if the directory i...
void remove()
Removes a tip.
Definition: tooltip.cpp:189
static void push_if_exists(std::vector< std::string > *vec, const bfs::path &file, bool full)
static const std::string & get_version_path_suffix()
std::string get_program_invocation(const std::string &program_name)
Returns the appropriate invocation for a Wesnoth-related binary, assuming that it is located in the s...
void set_user_data_dir(std::string path)
std::string get_next_filename(const std::string &name, const std::string &extension)
Get the next free filename using "name + number (3 digits) + extension" maximum 1000 files then start...
int dummy
Definition: lstrlib.cpp:1125
bool delete_file(const std::string &filename)
static bool create_directory_if_missing(const bfs::path &dirpath)
bool looks_like_pbl(const std::string &file)
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
static bool file_exists(const bfs::path &fpath)
bool ends_with(const std::string &str, const std::string &suffix)
void finish_log_file_setup()
Relocates the stdout+stderr log file to the user data directory.
child_itors child_range(config_key_type key)
Definition: config.cpp:366
#define DBG_FS
static void init_binary_paths()
std::string get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type or an empty string if the file isn&#39;t prese...
std::string normalize_path(const std::string &path, bool normalize_separators=false, bool resolve_dot_entries=false)
Returns the absolute path of a file.
#define d
Definitions for the interface to Wesnoth Markup Language (WML).
static bfs::path get_dir(const bfs::path &dirpath)
void clear_binary_paths_cache()
std::string get_cwd()
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: image.cpp:1184
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:749
static bool is_legal_file(const std::string &filename_str)
bool match_file(const std::string &name) const
Definition: filesystem.hpp:70
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
struct utils::detail::formula_initer init
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
Definition: version.cpp:151
static bfs::path user_config_dir
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:612
static bool is_directory_internal(const bfs::path &fpath)
std::string get_user_data_dir()
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
Definition: version.cpp:155
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
std::string path
Definition: game_config.cpp:39
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string get_independent_image_path(const std::string &filename)
Returns an image path to filename for binary path-independent use in saved games. ...
static bfs::path user_data_dir
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user&#39;s name.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
#define LOG_FS
lu_byte right
Definition: lparser.cpp:1027
bool is_path_sep(char c)
Returns whether c is a path separator.
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:40
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with &#39;.gz&#39;.
std::string get_cache_dir()
const std::vector< std::string > & get_binary_paths(const std::string &type)
Returns a vector with all possible paths to a given type of binary, e.g.
time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
std::string get_exe_dir()
filesystem::scoped_ostream ostream_file(const std::string &fname, bool create_directory=true)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates &#39;files&#39; with all the files and &#39;dirs&#39; with all the directories in dir.
Log file control routines for Windows.
std::size_t i
Definition: function.cpp:933
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
int dir_size(const std::string &path)
Returns the sum of the sizes of the files contained in a directory.
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
std::vector< std::string > paths_
Definition: filesystem.hpp:374
mock_party p
An exception object used when an IO error occurs.
Definition: filesystem.hpp:48
static map_location::DIRECTION s
bool make_directory(const std::string &dirname)
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
Declarations for File-IO.
static int writer(lua_State *L, const void *b, size_t size, void *B)
Definition: lstrlib.cpp:182
static int sort(lua_State *L)
Definition: ltablib.cpp:411
#define ERR_FS
const version_info wesnoth_version(VERSION)
Definition: version.hpp:210
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:57
bool delete_directory(const std::string &dirname, const bool keep_pbl=false)
char path_separator()
Returns the standard path separator for the current platform.
static bfs::path cache_dir
std::string base_name(const std::string &file, const bool remove_extension=false)
Returns the base filename of a file, with directory name stripped.
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn&#39;t exist.
std::string get_user_config_dir()
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with &#39;.bz2&#39;.
#define f
static bool error_except_not_found(const error_code &ec)
static void set_user_config_path(bfs::path newconfig)
static bfs::path subtract_path(const bfs::path &full, const bfs::path &prefix)
Standard logging facilities (interface).
void set_user_config_dir(const std::string &path)
#define e
static lg::log_domain log_filesystem("filesystem")
bool match_dir(const std::string &name) const
Definition: filesystem.hpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
static void setup_user_data_dir()
Interfaces for manipulating version numbers of engine, add-ons, etc.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
std::string path
File path.
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string wesnoth_program_dir
Definition: game_config.cpp:48
static bool create_directory_if_missing_recursive(const bfs::path &dirpath)
static const bfs::path & get_user_data_path()
static const blacklist_pattern_list default_blacklist
Definition: filesystem.hpp:99