26 #include <boost/filesystem.hpp>
27 #include <boost/filesystem/fstream.hpp>
28 #include <boost/system/windows_error.hpp>
29 #include <boost/iostreams/device/file_descriptor.hpp>
30 #include <boost/iostreams/stream.hpp>
33 using boost::uintmax_t;
38 #include <boost/locale.hpp>
51 #define DBG_FS LOG_STREAM(debug, log_filesystem)
52 #define LOG_FS LOG_STREAM(info, log_filesystem)
53 #define WRN_FS LOG_STREAM(warn, log_filesystem)
54 #define ERR_FS LOG_STREAM(err, log_filesystem)
56 namespace bfs = boost::filesystem;
58 using boost::system::error_code;
64 const std::string initialcfg_filename =
"_initial.cfg";
68 class customcodecvt :
public std::codecvt<wchar_t , char , std::mbstate_t>
72 template<
typename char_t_to>
73 struct customcodecvt_do_conversion_writer
75 customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end) :
82 bool can_push(
size_t count)
84 return static_cast<size_t>(to_end - to_next) > count;
87 void push(char_t_to
val)
89 assert(to_next != to_end);
94 template<
typename char_t_from ,
typename char_t_to>
95 static void customcodecvt_do_conversion( std::mbstate_t& ,
96 const char_t_from* from,
97 const char_t_from* from_end,
98 const char_t_from*& from_next,
101 char_t_to*& to_next )
108 customcodecvt_do_conversion_writer<char_t_to>
writer(to_next, to_end);
109 while(from_next != from_end)
118 int do_encoding()
const throw() {
return 0; }
120 bool do_always_noconv()
const throw() {
return false; }
121 int do_length( std::mbstate_t& ,
127 throw "Not supported";
136 throw "Not supported";
142 const char* from_end,
143 const char*& from_next,
146 wchar_t*& to_next )
const
150 customcodecvt_do_conversion<char, wchar_t>(state, from, from_end, from_next, to, to_end, to_next);
154 ERR_FS <<
"Invalid UTF-8 string'" <<
std::string(from, from_end) <<
"' " << std::endl;
157 return std::codecvt_base::ok;
162 const wchar_t* from_end,
163 const wchar_t*& from_next,
166 char*& to_next )
const
170 customcodecvt_do_conversion<wchar_t, char>(state, from, from_end, from_next, to, to_end, to_next);
174 ERR_FS <<
"Invalid UTF-16 string" << std::endl;
177 return std::codecvt_base::ok;
182 class static_runner {
188 utf8_loc = std::locale(utf8_loc,
new customcodecvt());
189 boost::filesystem::path::imbue(utf8_loc);
193 static static_runner static_bfs_path_imbuer;
201 if (vec !=
nullptr) {
203 vec->push_back(file.generic_string());
205 vec->push_back(file.filename().generic_string());
212 && ec.value() != boost::system::errc::no_such_file_or_directory
214 && ec.value() != boost::system::windows_error::path_not_found
224 LOG_FS <<
"Failed to check if " << fpath.string() <<
" is a directory: " << ec.message() <<
'\n';
234 ERR_FS <<
"Failed to check existence of file " << fpath.string() <<
": " << ec.message() <<
'\n';
243 bfs::create_directory(dirpath, ec);
245 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message() <<
'\n';
251 ERR_FS <<
"Could not open or create directory " << dirpath.string() <<
'\n';
260 bfs::file_status fs = bfs::status(dirpath, ec);
262 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message() <<
'\n';
265 DBG_FS <<
"directory " << dirpath.string() <<
" exists, not creating\n";
268 ERR_FS <<
"cannot create directory " << dirpath.string() <<
"; file exists\n";
272 bool created = bfs::create_directory(dirpath, ec);
274 ERR_FS <<
"Failed to create directory " << dirpath.string() <<
": " << ec.message() <<
'\n';
280 DBG_FS <<
"creating recursive directory: " << dirpath.string() <<
'\n';
285 bfs::file_status fs = bfs::status(dirpath);
287 ERR_FS <<
"Failed to retrieve file status for " << dirpath.string() <<
": " << ec.message() <<
'\n';
298 ERR_FS <<
"Could not create parents to " << dirpath.string() <<
'\n';
304 std::vector<std::string>* files,
305 std::vector<std::string>* dirs,
319 const path dirpath(dir);
322 LOG_FS <<
"searching for _main.cfg in directory " << dir <<
'\n';
323 const path maincfg = dirpath / maincfg_filename;
326 LOG_FS <<
"_main.cfg found : " << maincfg <<
'\n';
333 bfs::directory_iterator di(dirpath, ec);
334 bfs::directory_iterator
end;
339 for(; di !=
end; ++di) {
340 bfs::file_status st = di->status(ec);
342 LOG_FS <<
"Failed to get file status of " << di->path().string() <<
": " << ec.message() <<
'\n';
345 if (st.type() == bfs::regular_file) {
347 std::string basename = di->path().filename().string();
350 if(!basename.empty() && basename[0] ==
'.' )
355 if (checksum !=
nullptr) {
356 std::time_t mtime = bfs::last_write_time(di->path(), ec);
358 LOG_FS <<
"Failed to read modification time of " << di->path().string() <<
": " << ec.message() <<
'\n';
359 }
else if (mtime > checksum->
modified) {
365 LOG_FS <<
"Failed to read filesize of " << di->path().string() <<
": " << ec.message() <<
'\n';
371 }
else if (st.type() == bfs::directory_file) {
372 std::string basename = di->path().filename().string();
374 if(!basename.empty() && basename[0] ==
'.' )
377 && (basename ==
"images" || basename ==
"sounds"))
380 const path inner_main(di->path() / maincfg_filename);
381 bfs::file_status main_st = bfs::status(inner_main, ec);
383 LOG_FS <<
"Failed to get file status of " << inner_main.string() <<
": " << ec.message() <<
'\n';
384 }
else if (reorder ==
DO_REORDER && main_st.type() == bfs::regular_file) {
385 LOG_FS <<
"_main.cfg found : " << (mode ==
ENTIRE_FILE_PATH ? inner_main.string() : inner_main.filename().string()) <<
'\n';
393 if (files !=
nullptr)
399 if (files !=
nullptr && reorder ==
DO_REORDER) {
401 for (
unsigned int i = 0;
i < files->size();
i++) {
402 if (
ends_with((*files)[
i],
"/" + finalcfg_filename)) {
403 files->push_back((*files)[i]);
404 files->erase(files->begin()+
i);
410 for (
unsigned int i = 0;
i < files->size();
i++)
411 if (
ends_with((*files)[
i],
"/" + initialcfg_filename)) {
417 for (
unsigned int i = foundit; i > 0; i--)
418 (*files)[
i] = (*files)[i-1];
419 (*files)[0] = initialcfg;
435 std::stringstream filename;
441 filename << counter << extension;
443 next_filename = filename.str();
444 }
while(
file_exists(next_filename) && counter < 1000);
445 return next_filename;
458 std::ostringstream
s;
470 ERR_FS <<
"could not open or create user data directory at " << user_data_dir.string() <<
'\n';
493 static bool is_path_relative_to_cwd(
const std::string& str)
501 return *
p.begin() ==
"." || *
p.begin() ==
"..";
507 #ifdef PREFERENCES_DIR
508 if (newprefdir.empty()) newprefdir = PREFERENCES_DIR;
512 if(newprefdir.size() > 2 && newprefdir[1] ==
':') {
514 user_data_dir = newprefdir;
515 }
else if(is_path_relative_to_cwd(newprefdir)) {
517 user_data_dir =
get_cwd() +
"/" + newprefdir;
519 if(newprefdir.empty()) {
523 wchar_t docs_path[MAX_PATH];
525 HRESULT
res = SHGetFolderPathW(
nullptr,
526 CSIDL_PERSONAL | CSIDL_FLAG_CREATE,
nullptr,
533 ERR_FS <<
"Could not determine path to user's Documents folder! ("
534 << std::hex <<
"0x" << res << std::dec <<
") "
535 <<
"User config/data directories may be unavailable for "
536 <<
"this session. Please report this as a bug.\n";
539 path games_path =
path(docs_path) /
"My Games";
542 user_data_dir = games_path / newprefdir;
551 const char *home_str = getenv(
"HOME");
553 if (newprefdir.empty()) {
554 char const *xdg_data = getenv(
"XDG_DATA_HOME");
555 if (!xdg_data || xdg_data[0] ==
'\0') {
557 newprefdir = backupprefdir;
560 user_data_dir = home_str;
561 user_data_dir /=
".local/share";
562 }
else user_data_dir = xdg_data;
563 user_data_dir /=
"wesnoth";
567 path home = home_str ? home_str :
".";
569 if (newprefdir[0] ==
'/')
570 user_data_dir = newprefdir;
572 user_data_dir = home / newprefdir;
575 if (newprefdir.empty()) newprefdir = backupprefdir;
577 const char* home_str = getenv(
"HOME");
578 path home = home_str ? home_str :
".";
580 if (newprefdir[0] ==
'/')
581 user_data_dir = newprefdir;
583 user_data_dir = home / newprefdir;
592 user_config_dir = newconfig;
594 ERR_FS <<
"could not open or create user config directory at " << user_config_dir.string() <<
'\n';
605 if (user_data_dir.empty())
613 if (user_config_dir.empty())
615 #if defined(_X11) && !defined(PREFERENCES_DIR)
616 char const *xdg_config = getenv(
"XDG_CONFIG_HOME");
617 if (!xdg_config || xdg_config[0] ==
'\0') {
618 xdg_config = getenv(
"HOME");
621 return user_config_dir.string();
623 user_config_dir = xdg_config;
624 user_config_dir /=
".config";
625 }
else user_config_dir = xdg_config;
626 user_config_dir /=
"wesnoth";
632 return user_config_dir.string();
640 if (cache_dir.empty())
642 #if defined(_X11) && !defined(PREFERENCES_DIR)
643 char const *xdg_cache = getenv(
"XDG_CACHE_HOME");
644 if (!xdg_cache || xdg_cache[0] ==
'\0') {
645 xdg_cache = getenv(
"HOME");
648 return cache_dir.string();
650 cache_dir = xdg_cache;
651 cache_dir /=
".cache";
652 }
else cache_dir = xdg_cache;
653 cache_dir /=
"wesnoth";
659 return cache_dir.string();
665 path cwd = bfs::current_path(ec);
667 ERR_FS <<
"Failed to get current directory: " << ec.message() <<
'\n';
670 return cwd.generic_string();
675 wchar_t process_path[MAX_PATH];
676 SetLastError(ERROR_SUCCESS);
677 GetModuleFileNameW(
nullptr, process_path, MAX_PATH);
678 if (GetLastError() != ERROR_SUCCESS) {
682 path exe(process_path);
683 return exe.parent_path().string();
686 path self_exe(
"/proc/self/exe");
688 path exe = bfs::read_symlink(self_exe, ec);
693 return exe.parent_path().string();
703 bool created = bfs::create_directory(
path(dirname), ec);
705 ERR_FS <<
"Failed to create directory " << dirname <<
": " << ec.message() <<
'\n';
712 std::vector<std::string> files;
713 std::vector<std::string> dirs;
719 for(std::vector<std::string>::const_iterator
i = files.begin();
i != files.end(); ++
i) {
722 LOG_FS <<
"remove(" << (*i) <<
"): " << ec.message() <<
'\n';
729 for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
740 LOG_FS <<
"remove(" << dirname <<
"): " << ec.message() <<
'\n';
752 ERR_FS <<
"Could not delete file " << filename <<
": " << ec.message() <<
'\n';
760 std::stringstream ss;
765 #if BOOST_VERSION < 104800
769 template<
typename stringtype>
778 return path_.native();
789 LOG_FS <<
"Streaming " << fname <<
" for reading.\n";
791 ERR_FS <<
"Trying to open file with empty name.\n";
792 bfs::ifstream *
s =
new bfs::ifstream();
793 s->clear(std::ios_base::failbit);
803 if (!fd.is_open() && treat_failure_as_error) {
804 ERR_FS <<
"Could not open '" << fname <<
"' for reading.\n";
806 return new boost::iostreams::stream<boost::iostreams::file_descriptor_source>(fd, 4096, 0);
808 catch(
const std::exception ex)
810 if(treat_failure_as_error)
812 ERR_FS <<
"Could not open '" << fname <<
"' for reading.\n";
814 bfs::ifstream *
s =
new bfs::ifstream();
815 s->clear(std::ios_base::failbit);
822 LOG_FS <<
"streaming " << fname <<
" for writing.\n";
827 return new boost::iostreams::stream<boost::iostreams::file_descriptor_sink>(fd, 4096, 0);
829 catch(BOOST_IOSTREAMS_FAILURE&
e)
832 error_code ec_unused;
833 if(create_directory && bfs::create_directories(
bfs::path(fname).parent_path(), ec_unused))
847 os->exceptions(std::ios_base::goodbit);
849 const size_t block_size = 4096;
850 char buf[block_size];
852 for(
size_t i = 0;
i < data.size();
i += block_size) {
853 const size_t bytes = std::min<size_t>(block_size,data.size() -
i);
854 std::copy(data.begin() +
i, data.begin() +
i + bytes,
buf);
856 os->write(buf, bytes);
858 throw io_exception(
"Error writing to file: '" + fname +
"'");
884 std::time_t mtime = bfs::last_write_time(
path(fname), ec);
886 LOG_FS <<
"Failed to read modification time of " << fname <<
": " << ec.message() <<
'\n';
893 return path(filename).extension() ==
".gz";
898 return path(filename).extension() ==
".bz2";
906 LOG_FS <<
"Failed to read filesize of " << fname <<
": " << ec.message() <<
'\n';
908 }
else if (size > INT_MAX)
917 uintmax_t size_sum = 0;
919 for ( bfs::recursive_directory_iterator
i(p),
end;
i !=
end && !ec; ++
i ) {
920 if(bfs::is_regular_file(
i->path())) {
925 LOG_FS <<
"Failed to read directorysize of " << pname <<
": " << ec.message() <<
'\n';
928 else if (size_sum > INT_MAX)
936 return path(file).filename().string();
941 return path(file).parent_path().string();
945 static const path sep =
path(
"/").make_preferred();
947 return sep ==
path(s).make_preferred();
955 return bfs::absolute(fpath).string();
972 std::set<std::string> binary_paths;
974 typedef std::map<std::string,std::vector<std::string> > paths_map;
975 paths_map binary_paths_cache;
981 if(binary_paths.empty()) {
982 binary_paths.insert(
"");
1007 if (path.find(
"..") != std::string::npos) {
1008 ERR_FS <<
"Invalid binary path '" << path <<
"'\n";
1011 if (!path.empty() && path[path.size()-1] !=
'/') path +=
"/";
1012 if(binary_paths.count(path) == 0) {
1013 binary_paths.insert(path);
1021 binary_paths_cache.clear();
1023 for(std::vector<std::string>::const_iterator
i =
paths_.begin();
i !=
paths_.end(); ++
i) {
1024 binary_paths.erase(*
i);
1031 binary_paths_cache.clear();
1036 DBG_FS <<
"Looking for '" << filename <<
"'.\n";
1038 if (filename.empty()) {
1039 LOG_FS <<
" invalid filename\n";
1043 if (filename.find(
"..") != std::string::npos) {
1044 ERR_FS <<
"Illegal path '" << filename <<
"' (\"..\" not allowed).\n";
1049 ERR_FS <<
"Illegal path '" << filename <<
"' (.pbl files are not allowed)." << std::endl;
1062 const paths_map::const_iterator
itor = binary_paths_cache.find(type);
1063 if(itor != binary_paths_cache.end()) {
1064 return itor->second;
1067 if (type.find(
"..") != std::string::npos) {
1069 ERR_FS <<
"Invalid WML type '" << type <<
"' for binary paths\n";
1070 static std::vector<std::string> dummy;
1074 std::vector<std::string>&
res = binary_paths_cache[
type];
1105 for(std::string::size_type
pos = filename.rfind(
"../");
pos != std::string::npos;)
1115 DBG_FS <<
" checking '" << bp <<
"'\n";
1117 DBG_FS <<
" found at '" << bpath.string() <<
"'\n";
1118 return bpath.string();
1122 DBG_FS <<
" not found\n";
1135 DBG_FS <<
" checking '" << bp <<
"'\n";
1137 DBG_FS <<
" found at '" << bpath.string() <<
"'\n";
1138 return bpath.string();
1142 DBG_FS <<
" not found\n";
1153 path fpath(filename);
1156 if (filename[0] ==
'~')
1159 DBG_FS <<
" trying '" << result.string() <<
"'\n";
1160 }
else if (*fpath.begin() ==
".") {
1161 if (!current_dir.empty()) {
1162 result /=
path(current_dir);
1172 DBG_FS <<
" not found\n";
1175 DBG_FS <<
" found: '" << result.string() <<
"'\n";
1177 return result.string();
1184 , pi = prefix.begin()
1185 , pe = prefix.end();
1186 while (fi != fe && pi != pe && *fi == *pi) {
1201 path full_path(filename);
1204 if (!partial.empty())
1205 return "~" + partial.string();
1208 if (!partial.empty())
1209 return partial.string();
1218 if (full_path.empty())
1219 return full_path.string();
1222 if (!partial.empty())
1223 return partial.string();
1226 if (!partial.empty())
1227 return partial.string();
1229 return full_path.string();
child_itors child_range(const std::string &key)
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...
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)
void set_paths(const config &cfg)
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...
static bool is_directory_internal(const path &fpath)
bool delete_file(const std::string &filename)
bool looks_like_pbl(const std::string &file)
rng * generator
This generator is automatically synced during synced context.
GLuint GLuint GLsizei GLenum type
external_string_type external_file_string() const
static l_noret error(LoadState *S, const char *why)
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.
static path subtract_path(const path &full, const path &prefix)
GLuint const GLfloat * val
static path user_data_dir
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't prese...
iostream_path(const stringtype &s)
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definitions for the interface to Wesnoth Markup Language (WML).
void clear_binary_paths_cache()
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
static bool is_legal_file(const std::string &filename)
GLsizei const char ** path
bool create_directory_if_missing_recursive(const std::string &dirname)
Creates a recursive directory tree if it does not exist already.
bool create_directory_if_missing(const std::string &dirname)
Creates a directory if it does not exist already.
std::string get_user_data_dir()
const GLuint GLenum const GLvoid * binary
std::string normalize_path(const std::string &path)
Returns the absolute path of a file.
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
std::string base_name(const std::string &file)
Returns the base filename of a file, with directory name stripped.
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
static void set_user_config_path(path newconfig)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
static const path & get_user_data_path()
GLenum GLuint GLsizei const char * buf
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. ...
Templates and utility-routines for strings and numbers.
std::string get_dir(const std::string &dir)
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
GLuint GLuint GLsizei count
bool is_path_sep(char c)
Returns whether c is a path separator.
static path user_config_dir
static void push_if_exists(std::vector< std::string > *vec, const path &file, bool full)
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
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()
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 'files' with all the files and 'dirs' with all the directories in dir.
Log file control routines for Windows.
std::map< std::string, tfilter >::iterator itor
int dir_size(const std::string &path)
Returns the sum of the sizes of the files contained in a directory.
void set_user_config_dir(std::string path)
std::string get_wml_location(const std::string &filename, const std::string ¤t_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::vector< std::string > paths_
An exception object used when an IO error occurs.
unsigned int minor_version() const
Retrieves the minor version number (x2 in "x1.x2.x3").
bool make_directory(const std::string &dirname)
unsigned int major_version() const
Retrieves the major version number (x1 in "x1.x2.x3").
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
static int writer(lua_State *L, const void *b, size_t size, void *B)
static int sort(lua_State *L)
const version_info wesnoth_version(VERSION)
bool delete_directory(const std::string &dirname, const bool keep_pbl=false)
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
GLuint const GLchar * name
std::string get_user_config_dir()
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
static bool error_except_not_found(const error_code &ec)
Standard logging facilities (interface).
const std::string remove
remove directive
static lg::log_domain log_filesystem("filesystem")
A config object defines a single node in a WML file, with access to child nodes.
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.
void write(std::ostream &out, configr_of const &cfg, unsigned int level)
GLsizei const GLcharARB ** string
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
bfs::path::string_type external_string_type
std::string wesnoth_program_dir