diff --git a/src/filesystem_boost.cpp b/src/filesystem_boost.cpp index 96573205f633..e8a5e6207fba 100644 --- a/src/filesystem_boost.cpp +++ b/src/filesystem_boost.cpp @@ -18,16 +18,20 @@ */ #include "filesystem.hpp" + +#include "config.hpp" +#include "game_config.hpp" +#include "log.hpp" #include "serialization/unicode.hpp" +#include "serialization/unicode_cast.hpp" +#include "version.hpp" +#include #include #include -#include #include #include -#include -#include -#include +#include #ifdef _WIN32 #include "log_windows.hpp" @@ -45,11 +49,8 @@ #endif /* !_WIN32 */ -#include "config.hpp" -#include "game_config.hpp" -#include "log.hpp" -#include "serialization/unicode_cast.hpp" -#include "version.hpp" +#include +#include static lg::log_domain log_filesystem("filesystem"); #define DBG_FS LOG_STREAM(debug, log_filesystem) @@ -61,274 +62,292 @@ namespace bfs = boost::filesystem; using boost::filesystem::path; using boost::system::error_code; -namespace { - // These are the filenames that get special processing - const std::string maincfg_filename = "_main.cfg"; - const std::string finalcfg_filename = "_final.cfg"; - const std::string initialcfg_filename = "_initial.cfg"; -} -namespace { - //only used by windows but put outside the ifdef to let it check by ci build. - class customcodecvt : public std::codecvt +namespace +{ +// These are the filenames that get special processing +const std::string maincfg_filename = "_main.cfg"; +const std::string finalcfg_filename = "_final.cfg"; +const std::string initialcfg_filename = "_initial.cfg"; + +// only used by windows but put outside the ifdef to let it check by ci build. +class customcodecvt : public std::codecvt +{ +private: + // private static helper things + template + struct customcodecvt_do_conversion_writer { - private: - //private static helper things - template - struct customcodecvt_do_conversion_writer + customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end) + : to_next(_to_next) + , to_end(_to_end) { - customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end) : - to_next(_to_next), - to_end(_to_end) - {} - char_t_to*& to_next; - char_t_to* to_end; - - bool can_push(std::size_t count) const - { - return static_cast(to_end - to_next) > count; - } + } - void push(char_t_to val) - { - assert(to_next != to_end); - *to_next++ = val; - } - }; + char_t_to*& to_next; + char_t_to* to_end; - template - static void customcodecvt_do_conversion( std::mbstate_t& /*state*/, + bool can_push(std::size_t count) const + { + return static_cast(to_end - to_next) > count; + } + + void push(char_t_to val) + { + assert(to_next != to_end); + *to_next++ = val; + } + }; + + template + static void customcodecvt_do_conversion(std::mbstate_t& /*state*/, const char_t_from* from, const char_t_from* from_end, const char_t_from*& from_next, char_t_to* to, char_t_to* to_end, - char_t_to*& to_next ) - { - typedef typename ucs4_convert_impl::convert_impl::type impl_type_from; - typedef typename ucs4_convert_impl::convert_impl::type impl_type_to; + char_t_to*& to_next) + { + typedef typename ucs4_convert_impl::convert_impl::type impl_type_from; + typedef typename ucs4_convert_impl::convert_impl::type impl_type_to; - from_next = from; - to_next = to; - customcodecvt_do_conversion_writer writer(to_next, to_end); - while(from_next != from_end) - { - impl_type_to::write(writer, impl_type_from::read(from_next, from_end)); - } + from_next = from; + to_next = to; + customcodecvt_do_conversion_writer writer(to_next, to_end); + + while(from_next != from_end) { + impl_type_to::write(writer, impl_type_from::read(from_next, from_end)); } + } - public: +public: + // Not used by boost filesystem + int do_encoding() const noexcept + { + return 0; + } - //Not used by boost filesystem - int do_encoding() const noexcept { return 0; } - //Not used by boost filesystem - bool do_always_noconv() const noexcept { return false; } - int do_length( std::mbstate_t& /*state*/, - const char* /*from*/, - const char* /*from_end*/, - std::size_t /*max*/ ) const - { - //Not used by boost filesystem - throw "Not supported"; - } + // Not used by boost filesystem + bool do_always_noconv() const noexcept + { + return false; + } - std::codecvt_base::result unshift( std::mbstate_t& /*state*/, - char* /*to*/, - char* /*to_end*/, - char*& /*to_next*/) const - { - //Not used by boost filesystem - throw "Not supported"; - } + int do_length(std::mbstate_t& /*state*/, const char* /*from*/, const char* /*from_end*/, std::size_t /*max*/) const + { + // Not used by boost filesystem + throw "Not supported"; + } - //there are still some methods which could be implemented but aren't because boost filesystem won't use them. - std::codecvt_base::result do_in( std::mbstate_t& state, + std::codecvt_base::result unshift( + std::mbstate_t& /*state*/, char* /*to*/, char* /*to_end*/, char*& /*to_next*/) const + { + // Not used by boost filesystem + throw "Not supported"; + } + + // there are still some methods which could be implemented but aren't because boost filesystem won't use them. + std::codecvt_base::result do_in(std::mbstate_t& state, const char* from, const char* from_end, const char*& from_next, wchar_t* to, wchar_t* to_end, - wchar_t*& to_next ) const - { - try - { - customcodecvt_do_conversion(state, from, from_end, from_next, to, to_end, to_next); - } - catch(...) - { - ERR_FS << "Invalid UTF-8 string'" << std::string(from, from_end) << "' " << std::endl; - return std::codecvt_base::error; - } - return std::codecvt_base::ok; + wchar_t*& to_next) const + { + try { + customcodecvt_do_conversion(state, from, from_end, from_next, to, to_end, to_next); + } catch(...) { + ERR_FS << "Invalid UTF-8 string'" << std::string(from, from_end) << "' " << std::endl; + return std::codecvt_base::error; } - std::codecvt_base::result do_out( std::mbstate_t& state, + return std::codecvt_base::ok; + } + + std::codecvt_base::result do_out(std::mbstate_t& state, const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, char* to, char* to_end, - char*& to_next ) const - { - try - { - customcodecvt_do_conversion(state, from, from_end, from_next, to, to_end, to_next); - } - catch(...) - { - ERR_FS << "Invalid UTF-16 string" << std::endl; - return std::codecvt_base::error; - } - return std::codecvt_base::ok; + char*& to_next) const + { + try { + customcodecvt_do_conversion(state, from, from_end, from_next, to, to_end, to_next); + } catch(...) { + ERR_FS << "Invalid UTF-16 string" << std::endl; + return std::codecvt_base::error; } - }; + + return std::codecvt_base::ok; + } +}; #ifdef _WIN32 - class static_runner { - public: - static_runner() { - // Boost uses the current locale to generate a UTF-8 one - std::locale utf8_loc = boost::locale::generator().generate(""); - // use a custom locale because we want to use out log.hpp functions in case of an invalid string. - utf8_loc = std::locale(utf8_loc, new customcodecvt()); - boost::filesystem::path::imbue(utf8_loc); - } - }; +class static_runner +{ +public: + static_runner() + { + // Boost uses the current locale to generate a UTF-8 one + std::locale utf8_loc = boost::locale::generator().generate(""); - static static_runner static_bfs_path_imbuer; + // use a custom locale because we want to use out log.hpp functions in case of an invalid string. + utf8_loc = std::locale(utf8_loc, new customcodecvt()); - typedef DWORD(WINAPI *GetFinalPathNameByHandleWPtr)(HANDLE, LPWSTR, DWORD, DWORD); - static GetFinalPathNameByHandleWPtr dyn_GetFinalPathNameByHandle; + boost::filesystem::path::imbue(utf8_loc); + } +}; - bool is_filename_case_correct(const std::string& fname, const boost::iostreams::file_descriptor_source& fd) - { - if(dyn_GetFinalPathNameByHandle == nullptr) { - // Windows XP. Just assume that the case is correct. - return true; - } +static static_runner static_bfs_path_imbuer; - wchar_t real_path[MAX_PATH]; - dyn_GetFinalPathNameByHandle(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE); - std::string real_name = filesystem::base_name(unicode_cast(std::wstring(real_path))); - return real_name == filesystem::base_name(fname); - } +typedef DWORD(WINAPI* GetFinalPathNameByHandleWPtr)(HANDLE, LPWSTR, DWORD, DWORD); +static GetFinalPathNameByHandleWPtr dyn_GetFinalPathNameByHandle; -#else - bool is_filename_case_correct(const std::string& /*fname*/, const boost::iostreams::file_descriptor_source& /*fd*/) - { +bool is_filename_case_correct(const std::string& fname, const boost::iostreams::file_descriptor_source& fd) +{ + if(dyn_GetFinalPathNameByHandle == nullptr) { + // Windows XP. Just assume that the case is correct. return true; } -#endif -} + wchar_t real_path[MAX_PATH]; + dyn_GetFinalPathNameByHandle(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE); -namespace filesystem { + std::string real_name = filesystem::base_name(unicode_cast(std::wstring(real_path))); + return real_name == filesystem::base_name(fname); +} +#else +bool is_filename_case_correct(const std::string& /*fname*/, const boost::iostreams::file_descriptor_source& /*fd*/) +{ + return true; +} +#endif +} // namespace + +namespace filesystem +{ void init() { #ifdef _WIN32 HMODULE kernel32 = GetModuleHandle(TEXT("Kernel32.dll")); // Note that this returns a null pointer on Windows XP! - dyn_GetFinalPathNameByHandle = reinterpret_cast( - GetProcAddress(kernel32, "GetFinalPathNameByHandleW")); + dyn_GetFinalPathNameByHandle + = reinterpret_cast(GetProcAddress(kernel32, "GetFinalPathNameByHandleW")); #endif } -static void push_if_exists(std::vector *vec, const path &file, bool full) { - if (vec != nullptr) { - if (full) +static void push_if_exists(std::vector* vec, const path& file, bool full) +{ + if(vec != nullptr) { + if(full) { vec->push_back(file.generic_string()); - else + } else { vec->push_back(file.filename().generic_string()); + } } } -static inline bool error_except_not_found(const error_code &ec) +static inline bool error_except_not_found(const error_code& ec) { - return (ec - && ec.value() != boost::system::errc::no_such_file_or_directory + return (ec && ec.value() != boost::system::errc::no_such_file_or_directory #ifdef _WIN32 && ec.value() != boost::system::windows_error::path_not_found #endif /*_WIN32*/ - ); + ); } -static bool is_directory_internal(const path &fpath) +static bool is_directory_internal(const path& fpath) { error_code ec; bool is_dir = bfs::is_directory(fpath, ec); - if (error_except_not_found(ec)) { + if(error_except_not_found(ec)) { LOG_FS << "Failed to check if " << fpath.string() << " is a directory: " << ec.message() << '\n'; } + return is_dir; } -static bool file_exists(const path &fpath) +static bool file_exists(const path& fpath) { error_code ec; bool exists = bfs::exists(fpath, ec); - if (error_except_not_found(ec)) { + if(error_except_not_found(ec)) { ERR_FS << "Failed to check existence of file " << fpath.string() << ": " << ec.message() << '\n'; } + return exists; } -static path get_dir(const path &dirpath) + +static path get_dir(const path& dirpath) { bool is_dir = is_directory_internal(dirpath); - if (!is_dir) { + if(!is_dir) { error_code ec; bfs::create_directory(dirpath, ec); - if (ec) { + + if(ec) { ERR_FS << "Failed to create directory " << dirpath.string() << ": " << ec.message() << '\n'; } + // This is probably redundant is_dir = is_directory_internal(dirpath); } - if (!is_dir) { + + if(!is_dir) { ERR_FS << "Could not open or create directory " << dirpath.string() << '\n'; return std::string(); } return dirpath; } -static bool create_directory_if_missing(const path &dirpath) + +static bool create_directory_if_missing(const path& dirpath) { error_code ec; bfs::file_status fs = bfs::status(dirpath, ec); - if (error_except_not_found(ec)) { + + if(error_except_not_found(ec)) { ERR_FS << "Failed to retrieve file status for " << dirpath.string() << ": " << ec.message() << '\n'; return false; - } else if (bfs::is_directory(fs)) { + } else if(bfs::is_directory(fs)) { DBG_FS << "directory " << dirpath.string() << " exists, not creating\n"; return true; - } else if (bfs::exists(fs)) { + } else if(bfs::exists(fs)) { ERR_FS << "cannot create directory " << dirpath.string() << "; file exists\n"; return false; } bool created = bfs::create_directory(dirpath, ec); - if (ec) { + if(ec) { ERR_FS << "Failed to create directory " << dirpath.string() << ": " << ec.message() << '\n'; } + return created; } + static bool create_directory_if_missing_recursive(const path& dirpath) { DBG_FS << "creating recursive directory: " << dirpath.string() << '\n'; - if (dirpath.empty()) + if(dirpath.empty()) { return false; + } + error_code ec; bfs::file_status fs = bfs::status(dirpath); - if (error_except_not_found(ec)) { + + if(error_except_not_found(ec)) { ERR_FS << "Failed to retrieve file status for " << dirpath.string() << ": " << ec.message() << '\n'; return false; - } else if (bfs::is_directory(fs)) { + } else if(bfs::is_directory(fs)) { return true; - } else if (bfs::exists(fs)) { + } else if(bfs::exists(fs)) { return false; } - if (!dirpath.has_parent_path() || create_directory_if_missing_recursive(dirpath.parent_path())) { + if(!dirpath.has_parent_path() || create_directory_if_missing_recursive(dirpath.parent_path())) { return create_directory_if_missing(dirpath); } else { ERR_FS << "Could not create parents to " << dirpath.string() << '\n'; @@ -336,16 +355,18 @@ static bool create_directory_if_missing_recursive(const path& dirpath) } } -void get_files_in_dir(const std::string &dir, - std::vector* files, - std::vector* dirs, - file_name_option mode, - file_filter_option filter, - file_reorder_option reorder, - file_tree_checksum* checksum) { +void get_files_in_dir(const std::string& dir, + std::vector* files, + std::vector* dirs, + file_name_option mode, + file_filter_option filter, + file_reorder_option reorder, + file_tree_checksum* checksum) +{ if(path(dir).is_relative() && !game_config::path.empty()) { path absolute_dir(game_config::path); absolute_dir /= dir; + if(is_directory_internal(absolute_dir)) { get_files_in_dir(absolute_dir.string(), files, dirs, mode, filter, reorder, checksum); return; @@ -354,11 +375,11 @@ void get_files_in_dir(const std::string &dir, const path dirpath(dir); - if (reorder == DO_REORDER) { + if(reorder == DO_REORDER) { LOG_FS << "searching for _main.cfg in directory " << dir << '\n'; const path maincfg = dirpath / maincfg_filename; - if (file_exists(maincfg)) { + if(file_exists(maincfg)) { LOG_FS << "_main.cfg found : " << maincfg << '\n'; push_if_exists(files, maincfg, mode == ENTIRE_FILE_PATH); return; @@ -368,57 +389,67 @@ void get_files_in_dir(const std::string &dir, error_code ec; bfs::directory_iterator di(dirpath, ec); bfs::directory_iterator end; - if (ec) { - // Probably not a directory, let the caller deal with it. + + // Probably not a directory, let the caller deal with it. + if(ec) { return; } + for(; di != end; ++di) { bfs::file_status st = di->status(ec); - if (ec) { + if(ec) { LOG_FS << "Failed to get file status of " << di->path().string() << ": " << ec.message() << '\n'; continue; } - if (st.type() == bfs::regular_file) { + + if(st.type() == bfs::regular_file) { { std::string basename = di->path().filename().string(); - if (filter == SKIP_PBL_FILES && looks_like_pbl(basename)) + if(filter == SKIP_PBL_FILES && looks_like_pbl(basename)) continue; - if(!basename.empty() && basename[0] == '.' ) + if(!basename.empty() && basename[0] == '.') continue; } + push_if_exists(files, di->path(), mode == ENTIRE_FILE_PATH); - if (checksum != nullptr) { + if(checksum != nullptr) { std::time_t mtime = bfs::last_write_time(di->path(), ec); - if (ec) { - LOG_FS << "Failed to read modification time of " << di->path().string() << ": " << ec.message() << '\n'; - } else if (mtime > checksum->modified) { + if(ec) { + LOG_FS << "Failed to read modification time of " << di->path().string() << ": " << ec.message() + << '\n'; + } else if(mtime > checksum->modified) { checksum->modified = mtime; } uintmax_t size = bfs::file_size(di->path(), ec); - if (ec) { + if(ec) { LOG_FS << "Failed to read filesize of " << di->path().string() << ": " << ec.message() << '\n'; } else { checksum->sum_size += size; } + checksum->nfiles++; } - } else if (st.type() == bfs::directory_file) { + } else if(st.type() == bfs::directory_file) { std::string basename = di->path().filename().string(); - if(!basename.empty() && basename[0] == '.' ) + if(!basename.empty() && basename[0] == '.') { continue; - if (filter == SKIP_MEDIA_DIR - && (basename == "images" || basename == "sounds")) + } + + if(filter == SKIP_MEDIA_DIR && (basename == "images" || basename == "sounds")) { continue; + } const path inner_main(di->path() / maincfg_filename); bfs::file_status main_st = bfs::status(inner_main, ec); - if (error_except_not_found(ec)) { + + if(error_except_not_found(ec)) { LOG_FS << "Failed to get file status of " << inner_main.string() << ": " << ec.message() << '\n'; - } else if (reorder == DO_REORDER && main_st.type() == bfs::regular_file) { - LOG_FS << "_main.cfg found : " << (mode == ENTIRE_FILE_PATH ? inner_main.string() : inner_main.filename().string()) << '\n'; + } else if(reorder == DO_REORDER && main_st.type() == bfs::regular_file) { + LOG_FS << "_main.cfg found : " + << (mode == ENTIRE_FILE_PATH ? inner_main.string() : inner_main.filename().string()) << '\n'; push_if_exists(files, inner_main, mode == ENTIRE_FILE_PATH); } else { push_if_exists(dirs, di->path(), mode == ENTIRE_FILE_PATH); @@ -426,38 +457,41 @@ void get_files_in_dir(const std::string &dir, } } - if (files != nullptr) - std::sort(files->begin(),files->end()); + if(files != nullptr) { + std::sort(files->begin(), files->end()); + } - if (dirs != nullptr) - std::sort(dirs->begin(),dirs->end()); + if(dirs != nullptr) { + std::sort(dirs->begin(), dirs->end()); + } - if (files != nullptr && reorder == DO_REORDER) { + if(files != nullptr && reorder == DO_REORDER) { // move finalcfg_filename, if present, to the end of the vector - for (unsigned int i = 0; i < files->size(); i++) { - if (ends_with((*files)[i], "/" + finalcfg_filename)) { + for(unsigned int i = 0; i < files->size(); i++) { + if(ends_with((*files)[i], "/" + finalcfg_filename)) { files->push_back((*files)[i]); - files->erase(files->begin()+i); + files->erase(files->begin() + i); break; } } + // move initialcfg_filename, if present, to the beginning of the vector int foundit = -1; - for (unsigned int i = 0; i < files->size(); i++) - if (ends_with((*files)[i], "/" + initialcfg_filename)) { + for(unsigned int i = 0; i < files->size(); i++) + if(ends_with((*files)[i], "/" + initialcfg_filename)) { foundit = i; break; } - if (foundit > 0) { + if(foundit > 0) { std::string initialcfg = (*files)[foundit]; - for (unsigned int i = foundit; i > 0; i--) - (*files)[i] = (*files)[i-1]; + for(unsigned int i = foundit; i > 0; i--) + (*files)[i] = (*files)[i - 1]; (*files)[0] = initialcfg; } } } -std::string get_dir(const std::string &dir) +std::string get_dir(const std::string& dir) { return get_dir(path(dir)).string(); } @@ -475,9 +509,11 @@ std::string get_next_filename(const std::string& name, const std::string& extens filename.fill('0'); filename.setf(std::ios_base::right); filename << counter << extension; + counter++; next_filename = filename.str(); } while(file_exists(next_filename) && counter < 1000); + return next_filename; } @@ -492,8 +528,7 @@ static const std::string& get_version_path_suffix() if(suffix.empty()) { std::ostringstream s; - s << game_config::wesnoth_version.major_version() << '.' - << game_config::wesnoth_version.minor_version(); + s << game_config::wesnoth_version.major_version() << '.' << game_config::wesnoth_version.minor_version(); suffix = s.str(); } @@ -502,7 +537,7 @@ static const std::string& get_version_path_suffix() static void setup_user_data_dir() { - if (!create_directory_if_missing_recursive(user_data_dir)) { + if(!create_directory_if_missing_recursive(user_data_dir)) { ERR_FS << "could not open or create user data directory at " << user_data_dir.string() << '\n'; return; } @@ -541,12 +576,14 @@ static bool is_path_relative_to_cwd(const std::string& str) void set_user_data_dir(std::string newprefdir) { #ifdef PREFERENCES_DIR - if (newprefdir.empty()) newprefdir = PREFERENCES_DIR; + if(newprefdir.empty()) { + newprefdir = PREFERENCES_DIR; + } #endif #ifdef _WIN32 if(newprefdir.size() > 2 && newprefdir[1] == ':') { - //allow absolute path override + // allow absolute path override user_data_dir = newprefdir; } else if(is_path_relative_to_cwd(newprefdir)) { // Custom directory relative to workdir (for portable installs, etc.) @@ -563,8 +600,7 @@ void set_user_data_dir(std::string newprefdir) // // Crummy fallback path full of pain and suffering. // - ERR_FS << "Could not determine path to user's Documents folder! (" - << std::hex << "0x" << res << std::dec << ") " + ERR_FS << "Could not determine path to user's Documents folder! (" << std::hex << "0x" << res << std::dec << ") " << "User config/data directories may be unavailable for " << "this session. Please report this as a bug.\n"; user_data_dir = path(get_cwd()) / newprefdir; @@ -583,39 +619,47 @@ void set_user_data_dir(std::string newprefdir) std::string backupprefdir = ".wesnoth" + get_version_path_suffix(); #ifdef _X11 - const char *home_str = getenv("HOME"); + const char* home_str = getenv("HOME"); - if (newprefdir.empty()) { - char const *xdg_data = getenv("XDG_DATA_HOME"); - if (!xdg_data || xdg_data[0] == '\0') { - if (!home_str) { + if(newprefdir.empty()) { + char const* xdg_data = getenv("XDG_DATA_HOME"); + if(!xdg_data || xdg_data[0] == '\0') { + if(!home_str) { newprefdir = backupprefdir; goto other; } + user_data_dir = home_str; user_data_dir /= ".local/share"; - } else user_data_dir = xdg_data; + } else { + user_data_dir = xdg_data; + } + user_data_dir /= "wesnoth"; user_data_dir /= get_version_path_suffix(); } else { - other: + other: path home = home_str ? home_str : "."; - if (newprefdir[0] == '/') + if(newprefdir[0] == '/') { user_data_dir = newprefdir; - else + } else { user_data_dir = home / newprefdir; + } } #else - if (newprefdir.empty()) newprefdir = backupprefdir; + if(newprefdir.empty()) { + newprefdir = backupprefdir; + } const char* home_str = getenv("HOME"); path home = home_str ? home_str : "."; - if (newprefdir[0] == '/') + if(newprefdir[0] == '/') { user_data_dir = newprefdir; - else + } else { user_data_dir = home / newprefdir; + } #endif #endif /*_WIN32*/ @@ -626,7 +670,7 @@ void set_user_data_dir(std::string newprefdir) static void set_user_config_path(path newconfig) { user_config_dir = newconfig; - if (!create_directory_if_missing_recursive(user_config_dir)) { + if(!create_directory_if_missing_recursive(user_config_dir)) { ERR_FS << "could not open or create user config directory at " << user_config_dir.string() << '\n'; } } @@ -636,62 +680,75 @@ void set_user_config_dir(const std::string& newconfigdir) set_user_config_path(newconfigdir); } -static const path &get_user_data_path() +static const path& get_user_data_path() { - if (user_data_dir.empty()) - { + if(user_data_dir.empty()) { set_user_data_dir(std::string()); } + return user_data_dir; } + std::string get_user_config_dir() { - if (user_config_dir.empty()) - { + if(user_config_dir.empty()) { #if defined(_X11) && !defined(PREFERENCES_DIR) - char const *xdg_config = getenv("XDG_CONFIG_HOME"); - if (!xdg_config || xdg_config[0] == '\0') { + char const* xdg_config = getenv("XDG_CONFIG_HOME"); + + if(!xdg_config || xdg_config[0] == '\0') { xdg_config = getenv("HOME"); - if (!xdg_config) { + if(!xdg_config) { user_config_dir = get_user_data_path(); return user_config_dir.string(); } + user_config_dir = xdg_config; user_config_dir /= ".config"; - } else user_config_dir = xdg_config; + } else { + user_config_dir = xdg_config; + } + user_config_dir /= "wesnoth"; set_user_config_path(user_config_dir); #else user_config_dir = get_user_data_path(); #endif } + return user_config_dir.string(); } + std::string get_user_data_dir() { return get_user_data_path().string(); } + std::string get_cache_dir() { - if (cache_dir.empty()) - { + if(cache_dir.empty()) { #if defined(_X11) && !defined(PREFERENCES_DIR) - char const *xdg_cache = getenv("XDG_CACHE_HOME"); - if (!xdg_cache || xdg_cache[0] == '\0') { + char const* xdg_cache = getenv("XDG_CACHE_HOME"); + + if(!xdg_cache || xdg_cache[0] == '\0') { xdg_cache = getenv("HOME"); - if (!xdg_cache) { + if(!xdg_cache) { cache_dir = get_dir(get_user_data_path() / "cache"); return cache_dir.string(); } + cache_dir = xdg_cache; cache_dir /= ".cache"; - } else cache_dir = xdg_cache; + } else { + cache_dir = xdg_cache; + } + cache_dir /= "wesnoth"; create_directory_if_missing_recursive(cache_dir); #else cache_dir = get_dir(get_user_data_path() / "cache"); #endif } + return cache_dir.string(); } @@ -699,37 +756,41 @@ std::string get_cwd() { error_code ec; path cwd = bfs::current_path(ec); - if (ec) { + + if(ec) { ERR_FS << "Failed to get current directory: " << ec.message() << '\n'; return ""; } + return cwd.generic_string(); } std::string get_exe_dir() { #ifdef _WIN32 - wchar_t process_path[MAX_PATH]; - SetLastError(ERROR_SUCCESS); - GetModuleFileNameW(nullptr, process_path, MAX_PATH); - if (GetLastError() != ERROR_SUCCESS) { - return get_cwd(); - } - - path exe(process_path); - return exe.parent_path().string(); + wchar_t process_path[MAX_PATH]; + SetLastError(ERROR_SUCCESS); + + GetModuleFileNameW(nullptr, process_path, MAX_PATH); + + if(GetLastError() != ERROR_SUCCESS) { + return get_cwd(); + } + + path exe(process_path); + return exe.parent_path().string(); #else - if (bfs::exists("/proc/")) { - path self_exe("/proc/self/exe"); - error_code ec; - path exe = bfs::read_symlink(self_exe, ec); - if (ec) { - return std::string(); - } - - return exe.parent_path().string(); - } else { - return get_cwd(); - } + if(bfs::exists("/proc/")) { + path self_exe("/proc/self/exe"); + error_code ec; + path exe = bfs::read_symlink(self_exe, ec); + if(ec) { + return std::string(); + } + + return exe.parent_path().string(); + } else { + return get_cwd(); + } #endif } @@ -737,11 +798,13 @@ bool make_directory(const std::string& dirname) { error_code ec; bool created = bfs::create_directory(path(dirname), ec); - if (ec) { + if(ec) { ERR_FS << "Failed to create directory " << dirname << ": " << ec.message() << '\n'; } + return created; } + bool delete_directory(const std::string& dirname, const bool keep_pbl) { bool ret = true; @@ -754,7 +817,7 @@ bool delete_directory(const std::string& dirname, const bool keep_pbl) if(!files.empty()) { for(std::vector::const_iterator i = files.begin(); i != files.end(); ++i) { bfs::remove(path(*i), ec); - if (ec) { + if(ec) { LOG_FS << "remove(" << (*i) << "): " << ec.message() << '\n'; ret = false; } @@ -763,34 +826,36 @@ bool delete_directory(const std::string& dirname, const bool keep_pbl) if(!dirs.empty()) { for(std::vector::const_iterator j = dirs.begin(); j != dirs.end(); ++j) { - //TODO: this does not preserve any other PBL files + // TODO: this does not preserve any other PBL files // filesystem.cpp does this too, so this might be intentional if(!delete_directory(*j)) ret = false; } } - if (ret) { + if(ret) { bfs::remove(path(dirname), ec); - if (ec) { + if(ec) { LOG_FS << "remove(" << dirname << "): " << ec.message() << '\n'; ret = false; } } + return ret; } -bool delete_file(const std::string &filename) +bool delete_file(const std::string& filename) { error_code ec; bool ret = bfs::remove(path(filename), ec); - if (ec) { + if(ec) { ERR_FS << "Could not delete file " << filename << ": " << ec.message() << '\n'; } + return ret; } -std::string read_file(const std::string &fname) +std::string read_file(const std::string& fname) { scoped_istream is = istream_file(fname); std::stringstream ss; @@ -798,38 +863,39 @@ std::string read_file(const std::string &fname) return ss.str(); } -filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error) +filesystem::scoped_istream istream_file(const std::string& fname, bool treat_failure_as_error) { LOG_FS << "Streaming " << fname << " for reading.\n"; - if (fname.empty()) { + + if(fname.empty()) { ERR_FS << "Trying to open file with empty name.\n"; filesystem::scoped_istream s(new bfs::ifstream()); s->clear(std::ios_base::failbit); return s; } - //mingw doesn't support std::basic_ifstream::basic_ifstream(const wchar_t* fname) - //that why boost::filesystem::fstream.hpp doesn't work with mingw. - try - { + // mingw doesn't support std::basic_ifstream::basic_ifstream(const wchar_t* fname) + // that why boost::filesystem::fstream.hpp doesn't work with mingw. + try { boost::iostreams::file_descriptor_source fd(bfs::path(fname), std::ios_base::binary); - //TODO: has this still use ? - if (!fd.is_open() && treat_failure_as_error) { + + // TODO: has this still use ? + if(!fd.is_open() && treat_failure_as_error) { ERR_FS << "Could not open '" << fname << "' for reading.\n"; - } else if (!is_filename_case_correct(fname, fd)) { + } else if(!is_filename_case_correct(fname, fd)) { ERR_FS << "Not opening '" << fname << "' due to case mismatch.\n"; filesystem::scoped_istream s(new bfs::ifstream()); s->clear(std::ios_base::failbit); return s; } - return filesystem::scoped_istream(new boost::iostreams::stream(fd, 4096, 0)); - } - catch(const std::exception&) - { - if(treat_failure_as_error) - { + + return filesystem::scoped_istream( + new boost::iostreams::stream(fd, 4096, 0)); + } catch(const std::exception&) { + if(treat_failure_as_error) { ERR_FS << "Could not open '" << fname << "' for reading.\n"; } + filesystem::scoped_istream s(new bfs::ifstream()); s->clear(std::ios_base::failbit); return s; @@ -840,25 +906,25 @@ filesystem::scoped_ostream ostream_file(const std::string& fname, bool create_di { LOG_FS << "streaming " << fname << " for writing.\n"; #if 1 - try - { + try { boost::iostreams::file_descriptor_sink fd(bfs::path(fname), std::ios_base::binary); - return filesystem::scoped_ostream(new boost::iostreams::stream(fd, 4096, 0)); - } - catch(BOOST_IOSTREAMS_FAILURE& e) - { - // If this operation failed because the parent directory didn't exist, create the parent directory and retry. + return filesystem::scoped_ostream( + new boost::iostreams::stream(fd, 4096, 0)); + } catch(BOOST_IOSTREAMS_FAILURE& e) { + // If this operation failed because the parent directory didn't exist, create the parent directory and + // retry. error_code ec_unused; - if(create_directory && bfs::create_directories(bfs::path(fname).parent_path(), ec_unused)) - { + if(create_directory && bfs::create_directories(bfs::path(fname).parent_path(), ec_unused)) { return ostream_file(fname, false); } + throw filesystem::io_exception(e.what()); } #else return new bfs::ofstream(path(fname), std::ios_base::binary); #endif } + // Throws io_exception if an error occurs void write_file(const std::string& fname, const std::string& data) { @@ -869,12 +935,13 @@ void write_file(const std::string& fname, const std::string& data) char buf[block_size]; for(std::size_t i = 0; i < data.size(); i += block_size) { - const std::size_t bytes = std::min(block_size,data.size() - i); - std::copy(data.begin() + i, data.begin() + i + bytes,buf); + const std::size_t bytes = std::min(block_size, data.size() - i); + std::copy(data.begin() + i, data.begin() + i + bytes, buf); os->write(buf, bytes); - if (os->bad()) + if(os->bad()) { throw io_exception("Error writing to file: '" + fname + "'"); + } } } @@ -882,6 +949,7 @@ bool create_directory_if_missing(const std::string& dirname) { return create_directory_if_missing(path(dirname)); } + bool create_directory_if_missing_recursive(const std::string& dirname) { return create_directory_if_missing_recursive(path(dirname)); @@ -901,9 +969,10 @@ time_t file_modified_time(const std::string& fname) { error_code ec; std::time_t mtime = bfs::last_write_time(path(fname), ec); - if (ec) { + if(ec) { LOG_FS << "Failed to read modification time of " << fname << ": " << ec.message() << '\n'; } + return mtime; } @@ -921,13 +990,14 @@ int file_size(const std::string& fname) { error_code ec; uintmax_t size = bfs::file_size(path(fname), ec); - if (ec) { + if(ec) { LOG_FS << "Failed to read filesize of " << fname << ": " << ec.message() << '\n'; return -1; - } else if (size > INT_MAX) + } else if(size > INT_MAX) { return INT_MAX; - else + } else { return size; + } } int dir_size(const std::string& pname) @@ -935,19 +1005,20 @@ int dir_size(const std::string& pname) bfs::path p(pname); uintmax_t size_sum = 0; error_code ec; - for ( bfs::recursive_directory_iterator i(p), end; i != end && !ec; ++i ) { + for(bfs::recursive_directory_iterator i(p), end; i != end && !ec; ++i) { if(bfs::is_regular_file(i->path())) { size_sum += bfs::file_size(i->path(), ec); } } - if (ec) { + + if(ec) { LOG_FS << "Failed to read directorysize of " << pname << ": " << ec.message() << '\n'; return -1; - } - else if (size_sum > INT_MAX) + } else if(size_sum > INT_MAX) { return INT_MAX; - else + } else { return size_sum; + } } std::string base_name(const std::string& file, const bool remove_extension) @@ -976,7 +1047,7 @@ std::string nearest_extant_parent(const std::string& file) do { p = p.parent_path(); bfs::path q = canonical(p, ec); - if (!ec) { + if(!ec) { p = q; } } while(ec && !is_root(p.string())); @@ -1046,9 +1117,7 @@ std::string normalize_path(const std::string& fpath, bool normalize_separators, } error_code ec; - bfs::path p = resolve_dot_entries - ? bfs::canonical(fpath, ec) - : bfs::absolute(fpath); + bfs::path p = resolve_dot_entries ? bfs::canonical(fpath, ec) : bfs::absolute(fpath); if(ec) { return ""; @@ -1073,14 +1142,14 @@ std::string normalize_path(const std::string& fpath, bool normalize_separators, * Binaries will be searched for in [wesnoth-path]/data//images/ *@endverbatim */ -namespace { - +namespace +{ std::set binary_paths; -typedef std::map> paths_map; +typedef std::map> paths_map; paths_map binary_paths_cache; -} +} // namespace static void init_binary_paths() { @@ -1089,10 +1158,13 @@ static void init_binary_paths() } } -binary_paths_manager::binary_paths_manager() : paths_() -{} +binary_paths_manager::binary_paths_manager() + : paths_() +{ +} -binary_paths_manager::binary_paths_manager(const config& cfg) : paths_() +binary_paths_manager::binary_paths_manager(const config& cfg) + : paths_() { set_paths(cfg); } @@ -1107,14 +1179,15 @@ void binary_paths_manager::set_paths(const config& cfg) cleanup(); init_binary_paths(); - for (const config &bp : cfg.child_range("binary_path")) - { + for(const config& bp : cfg.child_range("binary_path")) { std::string path = bp["path"].str(); - if (path.find("..") != std::string::npos) { + if(path.find("..") != std::string::npos) { ERR_FS << "Invalid binary path '" << path << "'\n"; continue; } - if (!path.empty() && path[path.size()-1] != '/') path += "/"; + + if(!path.empty() && path[path.size() - 1] != '/') + path += "/"; if(binary_paths.count(path) == 0) { binary_paths.insert(path); paths_.push_back(path); @@ -1131,13 +1204,12 @@ void binary_paths_manager::cleanup() } } - void clear_binary_paths_cache() { binary_paths_cache.clear(); } -static bool is_legal_file(const std::string &filename_str) +static bool is_legal_file(const std::string& filename_str) { DBG_FS << "Looking for '" << filename_str << "'.\n"; @@ -1152,7 +1224,8 @@ static bool is_legal_file(const std::string &filename_str) } if(filename_str.find('\\') != std::string::npos) { - ERR_FS << "Illegal path '" << filename_str << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl; + ERR_FS << "Illegal path '" << filename_str + << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl; return false; } @@ -1163,7 +1236,8 @@ static bool is_legal_file(const std::string &filename_str) return false; } - if(std::any_of(filepath.begin(), filepath.end(), [](const path& dirname) { return default_blacklist.match_dir(dirname.string()); })) { + if(std::any_of(filepath.begin(), filepath.end(), + [](const path& dirname) { return default_blacklist.match_dir(dirname.string()); })) { ERR_FS << "Illegal path '" << filename_str << "' (blacklisted directory name)." << std::endl; return false; } @@ -1182,7 +1256,7 @@ const std::vector& get_binary_paths(const std::string& type) return itor->second; } - if (type.find("..") != std::string::npos) { + if(type.find("..") != std::string::npos) { // Not an assertion, as language.cpp is passing user data as type. ERR_FS << "Invalid WML type '" << type << "' for binary paths\n"; static std::vector dummy; @@ -1193,8 +1267,7 @@ const std::vector& get_binary_paths(const std::string& type) init_binary_paths(); - for(const std::string &path : binary_paths) - { + for(const std::string& path : binary_paths) { res.push_back(get_user_data_dir() + "/" + path + type + "/"); if(!game_config::path.empty()) { @@ -1205,8 +1278,9 @@ const std::vector& get_binary_paths(const std::string& type) // not found in "/type" directory, try main directory res.push_back(get_user_data_dir() + "/"); - if(!game_config::path.empty()) - res.push_back(game_config::path+"/"); + if(!game_config::path.empty()) { + res.push_back(game_config::path + "/"); + } return res; } @@ -1226,15 +1300,17 @@ std::string get_binary_file_location(const std::string& type, const std::string& } } - if (!is_legal_file(filename)) + if(!is_legal_file(filename)) { return std::string(); + } - for(const std::string &bp : get_binary_paths(type)) - { + for(const std::string& bp : get_binary_paths(type)) { path bpath(bp); bpath /= filename; + DBG_FS << " checking '" << bp << "'\n"; - if (file_exists(bpath)) { + + if(file_exists(bpath)) { DBG_FS << " found at '" << bpath.string() << "'\n"; return bpath.string(); } @@ -1244,17 +1320,17 @@ std::string get_binary_file_location(const std::string& type, const std::string& return std::string(); } -std::string get_binary_dir_location(const std::string &type, const std::string &filename) +std::string get_binary_dir_location(const std::string& type, const std::string& filename) { - if (!is_legal_file(filename)) + if(!is_legal_file(filename)) { return std::string(); + } - for (const std::string &bp : get_binary_paths(type)) - { + for(const std::string& bp : get_binary_paths(type)) { path bpath(bp); bpath /= filename; DBG_FS << " checking '" << bp << "'\n"; - if (is_directory_internal(bpath)) { + if(is_directory_internal(bpath)) { DBG_FS << " found at '" << bpath.string() << "'\n"; return bpath.string(); } @@ -1264,102 +1340,110 @@ std::string get_binary_dir_location(const std::string &type, const std::string & return std::string(); } -std::string get_wml_location(const std::string &filename, const std::string ¤t_dir) +std::string get_wml_location(const std::string& filename, const std::string& current_dir) { - if (!is_legal_file(filename)) + if(!is_legal_file(filename)) { return std::string(); + } assert(game_config::path.empty() == false); path fpath(filename); path result; - if (filename[0] == '~') - { + if(filename[0] == '~') { result /= get_user_data_path() / "data" / filename.substr(1); DBG_FS << " trying '" << result.string() << "'\n"; - } else if (*fpath.begin() == ".") { - if (!current_dir.empty()) { + } else if(*fpath.begin() == ".") { + if(!current_dir.empty()) { result /= path(current_dir); } else { result /= path(game_config::path) / "data"; } result /= filename; - } else if (!game_config::path.empty()) { + } else if(!game_config::path.empty()) { result /= path(game_config::path) / "data" / filename; } - if (result.empty() || !file_exists(result)) { + + if(result.empty() || !file_exists(result)) { DBG_FS << " not found\n"; result.clear(); - } else + } else { DBG_FS << " found: '" << result.string() << "'\n"; + } return result.string(); } -static path subtract_path(const path &full, const path &prefix) +static path subtract_path(const path& full, const path& prefix) { - path::iterator fi = full.begin() - , fe = full.end() - , pi = prefix.begin() - , pe = prefix.end(); - while (fi != fe && pi != pe && *fi == *pi) { + path::iterator fi = full.begin(), fe = full.end(), pi = prefix.begin(), pe = prefix.end(); + while(fi != fe && pi != pe && *fi == *pi) { ++fi; ++pi; } + path rest; - if (pi == pe) - while (fi != fe) { + if(pi == pe) { + while(fi != fe) { rest /= *fi; ++fi; } + } + return rest; } -std::string get_short_wml_path(const std::string &filename) +std::string get_short_wml_path(const std::string& filename) { path full_path(filename); path partial = subtract_path(full_path, get_user_data_path() / "data"); - if (!partial.empty()) + if(!partial.empty()) { return "~" + partial.generic_string(); + } partial = subtract_path(full_path, path(game_config::path) / "data"); - if (!partial.empty()) + if(!partial.empty()) { return partial.generic_string(); + } return filename; } -std::string get_independent_image_path(const std::string &filename) +std::string get_independent_image_path(const std::string& filename) { path full_path(get_binary_file_location("images", filename)); - if (full_path.empty()) + if(full_path.empty()) { return full_path.generic_string(); + } path partial = subtract_path(full_path, get_user_data_path()); - if (!partial.empty()) + if(!partial.empty()) { return partial.generic_string(); + } partial = subtract_path(full_path, game_config::path); - if (!partial.empty()) + if(!partial.empty()) { return partial.generic_string(); + } return full_path.generic_string(); } -std::string get_program_invocation(const std::string &program_name) +std::string get_program_invocation(const std::string& program_name) { const std::string real_program_name(program_name #ifdef DEBUG - + "-debug" + + "-debug" #endif #ifdef _WIN32 - + ".exe" + + ".exe" #endif ); + return (path(game_config::wesnoth_program_dir) / real_program_name).string(); } @@ -1379,4 +1463,4 @@ std::string sanitize_path(const std::string& path) return canonicalized; } -} +} // namespace filesystem