Commit message: Disallow inclusion of .pbl files from WML (bug #23504)

Note that this will also cause Lua wesnoth.have_file() to return false
on .pbl files.
CVE: CVE-2015-5069
CWE: CWE-200
Vulnerability description: The (1) filesystem::get_wml_location function in filesystem.cpp and (2) is_legal_file function in filesystem_boost.cpp in Battle for Wesnoth before 1.12.3 and 1.13.x before 1.13.1 allow remote attackers to obtain sensitive information via vectors related to inclusion of .pbl files from WML.

Project: wesnoth
File: filesystem_boost.cpp

std::string directory_name(const std::string& file)
{
	return path(file).parent_path().string();
}

Project: wesnoth
File: filesystem_boost.cpp

static void set_user_config_path(path newconfig)
{
	user_config_dir = newconfig;
	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';
	}
}

Project: wesnoth
File: filesystem.cpp

void get_files_in_dir(const std::string &directory,
					  std::vector<std::string>* files,
					  std::vector<std::string>* dirs,
					  file_name_option mode,
					  file_filter_option filter,
					  file_reorder_option reorder,
					  file_tree_checksum* checksum)
{
	// If we have a path to find directories in,
	// then convert relative pathnames to be rooted
	// on the wesnoth path
	if(!directory.empty() && directory[0] != '/' && !game_config::path.empty()){
		std::string dir = game_config::path + "/" + directory;
		if(is_directory(dir)) {
			get_files_in_dir(dir,files,dirs,mode,filter,reorder,checksum);
			return;
		}
	}

	struct stat st;

	if (reorder == DO_REORDER) {
		LOG_FS << "searching for _main.cfg in directory " << directory << '\n';
		std::string maincfg;
		if (directory.empty() || directory[directory.size()-1] == '/')
			maincfg = directory + maincfg_filename;
		else
			maincfg = (directory + "/") + maincfg_filename;

		if (::stat(maincfg.c_str(), &st) != -1) {
			LOG_FS << "_main.cfg found : " << maincfg << '\n';
			if (files != NULL) {
				if (mode == ENTIRE_FILE_PATH)
					files->push_back(maincfg);
				else
					files->push_back(maincfg_filename);
			}
			return;
		}
	}

	DIR* dir = opendir(directory.c_str());

	if(dir == NULL) {
		// Probably not a directory, let the caller deal with it.
		return;
	}

	struct dirent* entry;
	while((entry = readdir(dir)) != NULL) {
		if(entry->d_name[0] == '.')
			continue;
#ifdef __APPLE__
		// HFS Mac OS X decomposes filenames using combining unicode characters.
		// Try to get the precomposed form.
		char macname[MAXNAMLEN+1];
		CFStringRef cstr = CFStringCreateWithCString(NULL,
							 entry->d_name,
							 kCFStringEncodingUTF8);
		CFMutableStringRef mut_str = CFStringCreateMutableCopy(NULL,
							 0, cstr);
		CFStringNormalize(mut_str, kCFStringNormalizationFormC);
		CFStringGetCString(mut_str,
				macname,sizeof(macname)-1,
				kCFStringEncodingUTF8);
		CFRelease(cstr);
		CFRelease(mut_str);
		const std::string basename = macname;
#else
		// generic Unix
		const std::string basename = entry->d_name;
#endif /* !APPLE */

		std::string fullname;
		if (directory.empty() || directory[directory.size()-1] == '/')
			fullname = directory + basename;
		else
			fullname = directory + "/" + basename;

		if (::stat(fullname.c_str(), &st) != -1) {
			if (S_ISREG(st.st_mode)) {
				if(filter == SKIP_PBL_FILES && looks_like_pbl(basename)) {
					continue;
				}
				if (files != NULL) {
					if (mode == ENTIRE_FILE_PATH)
						files->push_back(fullname);
					else
						files->push_back(basename);
				}
				if (checksum != NULL) {
					if(st.st_mtime > checksum->modified) {
						checksum->modified = st.st_mtime;
					}
					checksum->sum_size += st.st_size;
					checksum->nfiles++;
				}
			} else if (S_ISDIR(st.st_mode)) {
				if (filter == SKIP_MEDIA_DIR
						&& (basename == "images"|| basename == "sounds"))
					continue;

				if (reorder == DO_REORDER &&
						::stat((fullname+"/"+maincfg_filename).c_str(), &st)!=-1 &&
						S_ISREG(st.st_mode)) {
					LOG_FS << "_main.cfg found : ";
					if (files != NULL) {
						if (mode == ENTIRE_FILE_PATH) {
							files->push_back(fullname + "/" + maincfg_filename);
							LOG_FS << fullname << "/" << maincfg_filename << '\n';
						} else {
							files->push_back(basename + "/" + maincfg_filename);
							LOG_FS << basename << "/" << maincfg_filename << '\n';
					}
					} else {
					// Show what I consider strange
						LOG_FS << fullname << "/" << maincfg_filename << " not used now but skip the directory " << std::endl;
					}
				} else if (dirs != NULL) {
					if (mode == ENTIRE_FILE_PATH)
						dirs->push_back(fullname);
					else
						dirs->push_back(basename);
				}
			}
		}
	}

	closedir(dir);

	if(files != NULL)
		std::sort(files->begin(),files->end());

	if (dirs != NULL)
		std::sort(dirs->begin(),dirs->end());

	if (files != NULL && 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)) {
				files->push_back((*files)[i]);
				files->erase(files->begin()+i);
				break;
			}
		}
		// move initialcfg_filename, if present, to the beginning of the vector
		unsigned int foundit = 0;
		for (unsigned int i = 0; i < files->size(); i++)
			if (ends_with((*files)[i], "/" + initialcfg_filename)) {
				foundit = i;
				break;
			}
		// If _initial.cfg needs to be moved (it was found, but not at index 0).
		if (foundit > 0) {
			std::string initialcfg = (*files)[foundit];
			for (unsigned int i = foundit; i > 0; --i)
				(*files)[i] = (*files)[i-1];
			(*files)[0] = initialcfg;
		}
	}
}

Project: wesnoth
File: filesystem.cpp

std::string get_user_config_dir()
{
	if (user_config_dir.empty())
	{
#if defined(_X11) && !defined(PREFERENCES_DIR)
		char const *xdg_config = getenv("XDG_CONFIG_HOME");
		std::string path;
		if (!xdg_config || xdg_config[0] == '\0') {
			xdg_config = getenv("HOME");
			if (!xdg_config) {
				user_config_dir = get_user_data_dir();
				return user_config_dir;
			}
			path = xdg_config;
			path += "/.config";
		} else path = xdg_config;
		path += "/wesnoth";
		set_user_config_dir(path);
#else
		user_config_dir = get_user_data_dir();
#endif
	}
	return user_config_dir;
}

Project: wesnoth
File: filesystem_boost.cpp

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)) {
		LOG_FS << "Failed to check if " << fpath.string() << " is a directory: " << ec.message() << '\n';
	}
	return is_dir;
}

Project: wesnoth
File: filesystem_boost.cpp

	iostream_path(const stringtype& s)
		: path_(s)
	{

	}

Project: wesnoth
File: filesystem.cpp

bool is_bzip2_file(const std::string& filename)
{
	return (filename.length() > 4
		&& filename.substr(filename.length() - 4) == ".bz2");
}

Project: wesnoth
File: filesystem_boost.cpp

static void push_if_exists(std::vector<std::string> *vec, const path &file, bool full) {
	if (vec != NULL) {
		if (full)
			vec->push_back(file.generic_string());
		else
			vec->push_back(file.filename().generic_string());
	}
}

Project: wesnoth
File: filesystem_boost.cpp

std::ostream *ostream_file(std::string const &fname)
{
	LOG_FS << "streaming " << fname << " for writing.\n";
#if 1
	try
	{
		boost::iostreams::file_descriptor_sink fd(iostream_path(fname), std::ios_base::binary);
		return new boost::iostreams::stream<boost::iostreams::file_descriptor_sink>(fd, 4096, 0);
	}
	catch(BOOST_IOSTREAMS_FAILURE& e)
	{
		throw filesystem::io_exception(e.what());
	}
#else
	return new bfs::ofstream(path(fname), std::ios_base::binary);
#endif
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_binary_dir_location(const std::string &type, const std::string &filename)
{
	if (!is_legal_file(filename))
		return std::string();

	BOOST_FOREACH(const std::string &bp, get_binary_paths(type))
	{
		path bpath(bp);
		bpath /= filename;
		DBG_FS << "  checking '" << bp << "'\n";
		if (is_directory_internal(bpath)) {
			DBG_FS << "  found at '" << bpath.string() << "'\n";
			return bpath.string();
		}
	}

	DBG_FS << "  not found\n";
	return std::string();
}

Project: wesnoth
File: filesystem_boost.cpp

std::string base_name(const std::string& file)
{
	return path(file).filename().string();
}

Project: wesnoth
File: filesystem_boost.cpp

		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<char, wchar_t>(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;
		}

Project: wesnoth
File: filesystem_boost.cpp

		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 becasue 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);
		}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_user_data_dir()
{
	return get_user_data_path().string();
}

Project: wesnoth
File: filesystem.cpp

std::string get_user_data_dir()
{
	// ensure setup gets called only once per session
	// FIXME: this is okay and optimized, but how should we react
	// if the user deletes a dir while we are running?
	if (user_data_dir.empty())
	{
		set_user_data_dir(std::string());
	}
	return user_data_dir;
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_wml_location(const std::string &filename, const std::string &current_dir)
{
	if (!is_legal_file(filename))
		return std::string();

	assert(game_config::path.empty() == false);

	path fpath(filename);
	path result;

	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()) {
			result /= path(current_dir);
		} else {
			result /= path(game_config::path) / "data";
		}

		result /= filename;
	} else if (!game_config::path.empty()) {
		result /= path(game_config::path) / "data" / filename;
	}
	if (result.empty() || !file_exists(result)) {
		DBG_FS << "  not found\n";
		result.clear();
	} else
		DBG_FS << "  found: '" << result.string() << "'\n";

	return result.string();
}

Project: wesnoth
File: filesystem.cpp

std::istream *istream_file(const std::string &fname, bool treat_failure_as_error)
{
	LOG_FS << "Streaming " << fname << " for reading." << std::endl;
	if (fname.empty())
	{
		ERR_FS << "Trying to open file with empty name." << std::endl;
		std::ifstream *s = new std::ifstream();
		s->clear(std::ios_base::failbit);
		return s;
	}

	std::ifstream *s = new std::ifstream(fname.c_str(),std::ios_base::binary);
	if (s->is_open()) {
		return s;
	}

	if (treat_failure_as_error) {
		ERR_FS << "Could not open '" << fname << "' for reading." << std::endl;
	} else {
		LOG_FS << "Could not open '" << fname << "' for reading." << std::endl;
	}
	return s;
}

Project: wesnoth
File: filesystem.cpp

std::string get_dir(const std::string& dir_path)
{
	DIR* dir = opendir(dir_path.c_str());
	if(dir == NULL) {
		const int res = mkdir(dir_path.c_str(),AccessMode);
		if(res == 0) {
			dir = opendir(dir_path.c_str());
		} else {
			ERR_FS << "could not open or create directory: " << dir_path << std::endl;
		}
	}

	if(dir == NULL)
		return "";

	closedir(dir);

	return dir_path;
}

Project: wesnoth
File: filesystem_boost.cpp

			void push(char_t_to val)
			{
				assert(to_next != to_end);
				*to_next++  = val;
			}

Project: wesnoth
File: filesystem.cpp

std::string get_cache_dir()
{
	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') {
			xdg_cache = getenv("HOME");
			if (!xdg_cache) {
				cache_dir = get_dir(get_user_data_dir() + "/cache");
				return cache_dir;
			}
			cache_dir = xdg_cache;
			cache_dir += "/.cache";
		} else cache_dir = xdg_cache;
		cache_dir += "/wesnoth";
		create_directory_if_missing_recursive(cache_dir);
#else
		cache_dir = get_dir(get_user_data_dir() + "/cache");
#endif
	}
	return cache_dir;
}

Project: wesnoth
File: filesystem.cpp

void set_user_data_dir(std::string path)
{
#ifdef _WIN32
	if(path.empty()) {
		user_data_dir = get_cwd() + "/userdata";
	} else if (path.size() > 2 && path[1] == ':') {
		//allow absolute path override
		user_data_dir = path;
	} else {
		typedef BOOL (WINAPI *SHGSFPAddress)(HWND, LPSTR, int, BOOL);
		SHGSFPAddress SHGetSpecialFolderPathA;
		HMODULE module = LoadLibraryA("shell32");
		SHGetSpecialFolderPathA = reinterpret_cast<SHGSFPAddress>(GetProcAddress(module, "SHGetSpecialFolderPathA"));
		if(SHGetSpecialFolderPathA) {
			LOG_FS << "Using SHGetSpecialFolderPath to find My Documents" << std::endl;
			char my_documents_path[MAX_PATH];
			if(SHGetSpecialFolderPathA(NULL, my_documents_path, 5, 1)) {
				std::string mygames_path = std::string(my_documents_path) + "/" + "My Games";
				boost::algorithm::replace_all(mygames_path, std::string("\\"), std::string("/"));
				create_directory_if_missing(mygames_path);
				user_data_dir = mygames_path + "/" + path;
			} else {
				WRN_FS << "SHGetSpecialFolderPath failed" << std::endl;
				user_data_dir = get_cwd() + "/" + path;
			}
		} else {
			LOG_FS << "Failed to load SHGetSpecialFolderPath function" << std::endl;
			user_data_dir = get_cwd() + "/" + path;
		}
	}

#else /*_WIN32*/

#ifdef PREFERENCES_DIR
	if (path.empty()) path = PREFERENCES_DIR;
#endif

	std::string path2 = ".wesnoth" + get_version_path_suffix();

#ifdef _X11
	const char *home_str = getenv("HOME");

	if (path.empty()) {
		char const *xdg_data = getenv("XDG_DATA_HOME");
		if (!xdg_data || xdg_data[0] == '\0') {
			if (!home_str) {
				path = path2;
				goto other;
			}
			user_data_dir = home_str;
			user_data_dir += "/.local/share";
		} else user_data_dir = xdg_data;
		user_data_dir += "/wesnoth/";
		user_data_dir += get_version_path_suffix();
		create_directory_if_missing_recursive(user_data_dir);
	} else {
		other:
		std::string home = home_str ? home_str : ".";

		if (path[0] == '/')
			user_data_dir = path;
		else
			user_data_dir = home + "/" + path;
	}
#else
	if (path.empty()) path = path2;

	const char* home_str = getenv("HOME");
	std::string home = home_str ? home_str : ".";

	if (path[0] == '/')
		user_data_dir = path;
	else
		user_data_dir = home + std::string("/") + path;
#endif

#endif /*_WIN32*/
	setup_user_data_dir();
}

Project: wesnoth
File: filesystem_boost.cpp

void set_user_data_dir(std::string newprefdir)
{
#ifdef _WIN32
	if(newprefdir.empty()) {
		user_data_dir = path(get_cwd()) / "userdata";
	} else if (newprefdir.size() > 2 && newprefdir[1] == ':') {
		//allow absolute path override
		user_data_dir = newprefdir;
	} else {
		typedef BOOL (WINAPI *SHGSFPAddress)(HWND, LPWSTR, int, BOOL);
		SHGSFPAddress SHGetSpecialFolderPathW;
		HMODULE module = LoadLibraryA("shell32");
		SHGetSpecialFolderPathW = reinterpret_cast<SHGSFPAddress>(GetProcAddress(module, "SHGetSpecialFolderPathW"));
		if(SHGetSpecialFolderPathW) {
			LOG_FS << "Using SHGetSpecialFolderPath to find My Documents\n";
			wchar_t my_documents_path[MAX_PATH];
			if(SHGetSpecialFolderPathW(NULL, my_documents_path, 5, 1)) {
				path mygames_path = path(my_documents_path) / "My Games";
				create_directory_if_missing(mygames_path);
				user_data_dir = mygames_path / newprefdir;
			} else {
				WRN_FS << "SHGetSpecialFolderPath failed\n";
				user_data_dir = path(get_cwd()) / newprefdir;
			}
		} else {
			LOG_FS << "Failed to load SHGetSpecialFolderPath function\n";
			user_data_dir = path(get_cwd()) / newprefdir;
		}
	}

#else /*_WIN32*/

#ifdef PREFERENCES_DIR
	if (newprefdir.empty()) newprefdir = PREFERENCES_DIR;
#endif

	std::string backupprefdir = ".wesnoth" + get_version_path_suffix();

#ifdef _X11
	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) {
				newprefdir = backupprefdir;
				goto other;
			}
			user_data_dir = home_str;
			user_data_dir /= ".local/share";
		} else user_data_dir = xdg_data;
		user_data_dir /= "wesnoth";
		user_data_dir /= get_version_path_suffix();
	} else {
		other:
		path home = home_str ? home_str : ".";

		if (newprefdir[0] == '/')
			user_data_dir = newprefdir;
		else
			user_data_dir = home / newprefdir;
	}
#else
	if (newprefdir.empty()) newprefdir = backupprefdir;

	const char* home_str = getenv("HOME");
	path home = home_str ? home_str : ".";

	if (newprefdir[0] == '/')
		user_data_dir = newprefdir;
	else
		user_data_dir = home / newprefdir;
#endif

#endif /*_WIN32*/
	setup_user_data_dir();
}

Project: wesnoth
File: filesystem_boost.cpp

void write_file(const std::string& fname, const std::string& data)
{
	scoped_ostream os = ostream_file(fname);
	os->exceptions(std::ios_base::goodbit);

	const size_t block_size = 4096;
	char buf[block_size];

	for(size_t i = 0; i < data.size(); i += block_size) {
		const size_t bytes = std::min<size_t>(block_size,data.size() - i);
		std::copy(data.begin() + i, data.begin() + i + bytes,buf);

		os->write(buf, bytes);
		if (os->bad())
			throw io_exception("Error writing to file: '" + fname + "'");
	}
}

Project: wesnoth
File: filesystem_boost.cpp

static path get_dir(const path &dirpath)
{
	bool is_dir = is_directory_internal(dirpath);
	if (!is_dir) {
		error_code ec;
		bfs::create_directory(dirpath, 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) {
		ERR_FS << "Could not open or create directory " << dirpath.string() << '\n';
		return std::string();
	}

	return dirpath;
}

Project: wesnoth
File: filesystem.cpp

std::ostream *ostream_file(std::string const &fname)
{
	LOG_FS << "streaming " << fname << " for writing." << std::endl;
	return new std::ofstream(fname.c_str(), std::ios_base::binary);
}

Project: wesnoth
File: filesystem.cpp

std::string get_exe_dir()
{
#ifndef _WIN32
	char buf[1024];
	size_t path_size = readlink("/proc/self/exe", buf, sizeof(buf)-1);
	if(path_size == static_cast<size_t>(-1))
		return std::string();
	buf[path_size] = 0;
	return std::string(dirname(buf));
#else
	return get_cwd();
#endif
}

Project: wesnoth
File: filesystem.cpp

time_t file_modified_time(const std::string& fname)
{
	struct stat buf;
	if(::stat(fname.c_str(),&buf) == -1)
		return 0;

	return buf.st_mtime;
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_user_config_dir()
{
	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') {
			xdg_config = getenv("HOME");
			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;
		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();
}

Project: wesnoth
File: filesystem.cpp

int dir_size(const std::string& path)
{
	std::vector<std::string> files, dirs;
	get_files_in_dir(path, &files, &dirs, ENTIRE_FILE_PATH);

	int res = 0;

	BOOST_FOREACH(const std::string& file_path, files)
	{
		res += file_size(file_path);
	}

	BOOST_FOREACH(const std::string& dir_path, dirs)
	{
		// FIXME: this could result in infinite recursion with symlinks!!
		res += dir_size(dir_path);
	}

	return res;
}

Project: wesnoth
File: filesystem_boost.cpp

void set_user_config_dir(std::string newconfigdir)
{
	set_user_config_path(newconfigdir);
}

Project: wesnoth
File: filesystem_boost.cpp

		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<wchar_t, char>(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;
		}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_dir(const std::string &dir)
{
	return get_dir(path(dir)).string();
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_cache_dir()
{
	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') {
			xdg_cache = getenv("HOME");
			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;
		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();
}

Project: wesnoth
File: filesystem_boost.cpp

		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<char_t_from>::type impl_type_from;
			typedef typename ucs4_convert_impl::convert_impl<char_t_to>::type impl_type_to;

			from_next = from;
			to_next = to;
			customcodecvt_do_conversion_writer<char_t_to> writer(to_next, to_end);
			while(from_next != from_end)
			{
				impl_type_to::write(writer, impl_type_from::read(from_next, from_end));
			}
		}

Project: wesnoth
File: filesystem.cpp

void binary_paths_manager::set_paths(const config& cfg)
{
	cleanup();
	init_binary_paths();

	BOOST_FOREACH(const config &bp, cfg.child_range("binary_path"))
	{
		std::string path = bp["path"].str();
		if (path.find("..") != std::string::npos) {
			ERR_FS << "Invalid binary path '" << path << "'" << std::endl;
			continue;
		}
		if (!path.empty() && path[path.size()-1] != '/') path += "/";
		if(binary_paths.count(path) == 0) {
			binary_paths.insert(path);
			paths_.push_back(path);
		}
	}
}

Project: wesnoth
File: filesystem_boost.cpp

static bool create_directory_if_missing_recursive(const path& dirpath)
{
	DBG_FS << "creating recursive directory: " << dirpath.string() << '\n';

	if (dirpath.empty())
		return false;
	error_code ec;
	bfs::file_status fs = bfs::status(dirpath);
	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)) {
		return true;
	} else if (bfs::exists(fs)) {
		return false;
	}

	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';
		return false;
	}
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_independent_image_path(const std::string &filename)
{
	path full_path(get_binary_file_location("images", filename));

	if (full_path.empty())
		return full_path.string();

	path partial = subtract_path(full_path, get_user_data_path());
	if (!partial.empty())
		return partial.string();

	partial = subtract_path(full_path, game_config::path);
	if (!partial.empty())
		return partial.string();

	return full_path.string();
}

Project: wesnoth
File: filesystem.cpp

std::string base_name(const std::string& file)
// Analogous to POSIX basename(3), but for C++ string-object pathnames
{
#ifdef _WIN32
	static const std::string dir_separators = "\\/:";
#else
	static const std::string dir_separators = "/";
#endif

	std::string::size_type pos = file.find_last_of(dir_separators);

	if(pos == std::string::npos)
		return file;
	if(pos >= file.size()-1)
		return "";

	return file.substr(pos+1);
}

Project: wesnoth
File: filesystem.cpp

bool delete_file(const std::string& path)
{
	bool ret = true;
	if(remove(path.c_str()) != 0 && errno != ENOENT) {
		ERR_FS << "remove(" << path << "): " << strerror(errno) << "\n";
		ret = false;
	}
	return ret;
}

Project: wesnoth
File: filesystem_boost.cpp

bool delete_directory(const std::string& dirname, const bool keep_pbl)
{
	bool ret = true;
	std::vector<std::string> files;
	std::vector<std::string> dirs;
	error_code ec;

	get_files_in_dir(dirname, &files, &dirs, ENTIRE_FILE_PATH, keep_pbl ? SKIP_PBL_FILES : NO_FILTER);

	if(!files.empty()) {
		for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
			bfs::remove(path(*i), ec);
			if (ec) {
				LOG_FS << "remove(" << (*i) << "): " << ec.message() << '\n';
				ret = false;
			}
		}
	}

	if(!dirs.empty()) {
		for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
			//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) {
		bfs::remove(path(dirname), ec);
		if (ec) {
			LOG_FS << "remove(" << dirname << "): " << ec.message() << '\n';
			ret = false;
		}
	}
	return ret;
}

Project: wesnoth
File: filesystem_boost.cpp

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 ) {
		if(bfs::is_regular_file(i->path())) {
			size_sum += bfs::file_size(i->path(), ec);
		}
	}
	if (ec) {
		LOG_FS << "Failed to read directorysize of " << pname << ": " << ec.message() << '\n';
		return -1;
	}
	else if (size_sum > INT_MAX)
		return INT_MAX;
	else
		return size_sum;
}

Project: wesnoth
File: filesystem_boost.cpp

bool is_bzip2_file(const std::string& filename)
{
	return path(filename).extension() == ".bz2";
}

Project: wesnoth
File: filesystem_boost.cpp

static bool file_exists(const path &fpath)
{
	error_code ec;
	bool exists = bfs::exists(fpath, ec);
	if (error_except_not_found(ec)) {
		ERR_FS << "Failed to check existence of file " << fpath.string() << ": " << ec.message() << '\n';
	}
	return exists;
}

Project: wesnoth
File: filesystem_boost.cpp

bool is_gzip_file(const std::string& filename)
{
	return path(filename).extension() == ".gz";
}

Project: wesnoth
File: filesystem.cpp

static void setup_user_data_dir()
{
#ifdef _WIN32
	_mkdir(user_data_dir.c_str());
	_mkdir((user_data_dir + "/editor").c_str());
	_mkdir((user_data_dir + "/editor/maps").c_str());
	_mkdir((user_data_dir + "/editor/scenarios").c_str());
	_mkdir((user_data_dir + "/data").c_str());
	_mkdir((user_data_dir + "/data/add-ons").c_str());
	_mkdir((user_data_dir + "/saves").c_str());
	_mkdir((user_data_dir + "/persist").c_str());
#else
	const std::string& dir_path = user_data_dir;

	const bool res = create_directory_if_missing(dir_path);
	// probe read permissions (if we could make the directory)
	DIR* const dir = res ? opendir(dir_path.c_str()) : NULL;
	if(dir == NULL) {
		ERR_FS << "could not open or create preferences directory at " << dir_path << std::endl;
		return;
	}
	closedir(dir);

	// Create user data and add-on directories
	create_directory_if_missing(dir_path + "/editor");
	create_directory_if_missing(dir_path + "/editor/maps");
	create_directory_if_missing(dir_path + "/editor/scenarios");
	create_directory_if_missing(dir_path + "/data");
	create_directory_if_missing(dir_path + "/data/add-ons");
	create_directory_if_missing(dir_path + "/saves");
	create_directory_if_missing(dir_path + "/persist");
#endif
}

Project: wesnoth
File: filesystem_boost.cpp

		int do_encoding() const throw() { return 0; }

Project: wesnoth
File: filesystem_boost.cpp

bool create_directory_if_missing_recursive(const std::string& dirname)
{
	return create_directory_if_missing_recursive(path(dirname));
}

Project: wesnoth
File: filesystem_boost.cpp

bool is_directory(const std::string& fname)
{
	return is_directory_internal(path(fname));
}

Project: wesnoth
File: filesystem_boost.cpp

bool file_exists(const std::string& name)
{
	return file_exists(path(name));
}

Project: wesnoth
File: filesystem_boost.cpp

bool make_directory(const std::string& dirname)
{
	error_code ec;
	bool created = bfs::create_directory(path(dirname), ec);
	if (ec) {
		ERR_FS << "Failed to create directory " << dirname << ": " << ec.message() << '\n';
	}
	return created;
}

Project: wesnoth
File: filesystem.cpp

bool create_directory_if_missing(const std::string& dirname)
{
	if(is_directory(dirname)) {
		DBG_FS << "directory " << dirname << " exists, not creating" << std::endl;
		return true;
	} else if(file_exists(dirname)) {
		ERR_FS << "cannot create directory " << dirname << "; file exists" << std::endl;
		return false;
	}
	DBG_FS << "creating missing directory " << dirname << '\n';
	return make_directory(dirname);
}

Project: wesnoth
File: filesystem_boost.cpp

		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";
		}

Project: wesnoth
File: filesystem_boost.cpp

			bool can_push(size_t count)
			{
				return static_cast<size_t>(to_end - to_next) > count;
			}

Project: wesnoth
File: filesystem_boost.cpp

	external_string_type external_file_string() const
	{
		return path_.native();
	}

Project: wesnoth
File: filesystem_boost.cpp

		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";
		}

Project: wesnoth
File: filesystem_boost.cpp

bool delete_file(const std::string &filename)
{
	error_code ec;
	bool ret = bfs::remove(path(filename), ec);
	if (ec) {
		ERR_FS << "Could not delete file " << filename << ": " << ec.message() << '\n';
	}
	return ret;
}

Project: wesnoth
File: filesystem.cpp

const std::vector<std::string>& get_binary_paths(const std::string& type)
{
	const paths_map::const_iterator itor = binary_paths_cache.find(type);
	if(itor != binary_paths_cache.end()) {
		return itor->second;
	}

	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" << std::endl;
		static std::vector<std::string> dummy;
		return dummy;
	}

	std::vector<std::string>& res = binary_paths_cache[type];

	init_binary_paths();

	BOOST_FOREACH(const std::string &path, binary_paths)
	{
		res.push_back(get_user_data_dir() + "/" + path + type + "/");

		if(!game_config::path.empty()) {
			res.push_back(game_config::path + "/" + path + 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+"/");

	return res;
}

Project: wesnoth
File: filesystem_boost.cpp

void get_files_in_dir(const std::string &dir,
                      std::vector<std::string>* files,
                      std::vector<std::string>* 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;
		}
	}

	const path dirpath(dir);

	if (reorder == DO_REORDER) {
		LOG_FS << "searching for _main.cfg in directory " << dir << '\n';
		const path maincfg = dirpath / maincfg_filename;

		if (file_exists(maincfg)) {
			LOG_FS << "_main.cfg found : " << maincfg << '\n';
			push_if_exists(files, maincfg, mode == ENTIRE_FILE_PATH);
			return;
		}
	}

	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.
		return;
	}
	for(; di != end; ++di) {
		bfs::file_status st = di->status(ec);
		if (ec) {
			LOG_FS << "Failed to get file status of " << di->path().string() << ": " << ec.message() << '\n';
			continue;
		}
		if (st.type() == bfs::regular_file) {
			{
				std::string basename = di->path().filename().string();
				if (filter == SKIP_PBL_FILES && looks_like_pbl(basename))
					continue;
				if(!basename.empty() && basename[0] == '.' )
					continue;
			}
			push_if_exists(files, di->path(), mode == ENTIRE_FILE_PATH);

			if (checksum != NULL) {
				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) {
					checksum->modified = mtime;
				}

				uintmax_t size = bfs::file_size(di->path(), 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) {
			std::string basename = di->path().filename().string();
			
			if(!basename.empty() && basename[0] == '.' )
				continue;
			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)) {
				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';
				push_if_exists(files, inner_main, mode == ENTIRE_FILE_PATH);
			} else {
				push_if_exists(dirs, di->path(), mode == ENTIRE_FILE_PATH);
			}
		}
	}

	if (files != NULL)
		std::sort(files->begin(),files->end());

	if (dirs != NULL)
		std::sort(dirs->begin(),dirs->end());

	if (files != NULL && 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)) {
				files->push_back((*files)[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)) {
				foundit = i;
				break;
			}
		if (foundit > 0) {
			std::string initialcfg = (*files)[foundit];
			for (unsigned int i = foundit; i > 0; i--)
				(*files)[i] = (*files)[i-1];
			(*files)[0] = initialcfg;
		}
	}
}

Project: wesnoth
File: filesystem.cpp

std::string get_independent_image_path(const std::string &filename)
{
	std::string full_path = get_binary_file_location("images", filename);

	if(!full_path.empty()) {
		std::string match = get_user_data_dir() + "/";
		if(full_path.find(match) == 0) {
			return full_path.substr(match.size());
		}
		match = game_config::path + "/";
		if(full_path.find(match) == 0) {
			return full_path.substr(match.size());
		}
	}

	return full_path;
}

Project: wesnoth
File: filesystem.cpp

std::string normalize_path(const std::string &p1)
{
	if (p1.empty()) return p1;

	std::string p2;
#ifdef _WIN32
	if (p1.size() >= 2 && p1[1] == ':')
		// Windows relative paths with explicit drive name are not handled.
		p2 = p1;
	else
#endif
	if (!is_path_sep(p1[0]))
		p2 = get_cwd() + "/" + p1;
	else
		p2 = p1;

#ifdef _WIN32
	std::string drive;
	if (p2.size() >= 2 && p2[1] == ':') {
		drive = p2.substr(0, 2);
		p2.erase(0, 2);
	}
#endif

	std::vector<std::string> components(1);
	for (int i = 0, i_end = p2.size(); i <= i_end; ++i)
	{
		std::string &last = components[components.size() - 1];
		char c = p2.c_str()[i];
		if (is_path_sep(c) || c == 0)
		{
			if (last == ".")
				last.clear();
			else if (last == "..")
			{
				if (components.size() >= 2) {
					components.pop_back();
					components[components.size() - 1].clear();
				} else
					last.clear();
			}
			else if (!last.empty())
				components.push_back(std::string());
		}
		else
			last += c;
	}

	std::ostringstream p4;
	components.pop_back();

#ifdef _WIN32
	p4 << drive;
#endif

	BOOST_FOREACH(const std::string &s, components)
	{
		p4 << '/' << s;
	}

	DBG_FS << "Normalizing '" << p2 << "' to '" << p4.str() << "'" << std::endl;

	return p4.str();
}

Project: wesnoth
File: filesystem_boost.cpp

std::string normalize_path(const std::string &fpath)
{
	if (fpath.empty()) {
		return fpath;
	}

	return bfs::absolute(fpath).string();
}

Project: wesnoth
File: filesystem.cpp

bool is_path_sep(char c)
{
#ifdef _WIN32
	if (c == '/' || c == '\\') return true;
#else
	if (c == '/') return true;
#endif
	return false;
}

Project: wesnoth
File: filesystem_boost.cpp

bool create_directory_if_missing(const std::string& dirname)
{
	return create_directory_if_missing(path(dirname));
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_exe_dir()
{
#ifndef _WIN32
	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
}

Project: wesnoth
File: filesystem.cpp

bool delete_directory(const std::string& path, const bool keep_pbl)
{
	bool ret = true;
	std::vector<std::string> files;
	std::vector<std::string> dirs;

	get_files_in_dir(path, &files, &dirs, ENTIRE_FILE_PATH, keep_pbl ? SKIP_PBL_FILES : NO_FILTER);

	if(!files.empty()) {
		for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) {
			errno = 0;
			if(remove((*i).c_str()) != 0) {
				LOG_FS << "remove(" << (*i) << "): " << strerror(errno) << std::endl;
				ret = false;
			}
		}
	}

	if(!dirs.empty()) {
		for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) {
			if(!delete_directory(*j))
				ret = false;
		}
	}

	errno = 0;
#ifdef _WIN32
	// remove() doesn't delete directories on windows.
	int (*remove)(const char*);
	if(is_directory(path))
		remove = rmdir;
	else
		remove = ::remove;
#endif
	if(remove(path.c_str()) != 0) {
		LOG_FS << "remove(" << path << "): " << strerror(errno) << std::endl;
		ret = false;
	}
	return ret;
}

Project: wesnoth
File: filesystem.cpp

std::string get_binary_dir_location(const std::string &type, const std::string &filename)
{
	DBG_FS << "Looking for '" << filename << "'." << std::endl;

	if (filename.empty()) {
		LOG_FS << "  invalid filename (type: " << type <<")" << std::endl;
		return std::string();
	}

	if (filename.find("..") != std::string::npos) {
		ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed)." << std::endl;
		return std::string();
	}

	BOOST_FOREACH(const std::string &path, get_binary_paths(type))
	{
		const std::string file = path + filename;
		DBG_FS << "  checking '" << path << "'" << std::endl;
		if (is_directory(file)) {
			DBG_FS << "  found at '" << file << "'" << std::endl;
			return file;
		}
	}

	DBG_FS << "  not found" << std::endl;
	return std::string();
}

Project: wesnoth
File: filesystem_boost.cpp

static inline bool error_except_not_found(const error_code &ec)
{
	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*/
		);
}

Project: wesnoth
File: filesystem_boost.cpp

		bool do_always_noconv() const throw() { return false; }

Project: wesnoth
File: filesystem_boost.cpp

std::istream *istream_file(const std::string &fname, bool treat_failure_as_error)
{
	LOG_FS << "Streaming " << fname << " for reading.\n";
	if (fname.empty()) {
		ERR_FS << "Trying to open file with empty name.\n";
		bfs::ifstream *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 doesnt work with mingw.
	try
	{
		boost::iostreams::file_descriptor_source fd(iostream_path(fname), std::ios_base::binary);
		//TODO: has this still use ?
		if (!fd.is_open() && treat_failure_as_error) {
			ERR_FS << "Could not open '" << fname << "' for reading.\n";
		}
		return new boost::iostreams::stream<boost::iostreams::file_descriptor_source>(fd, 4096, 0);
	}
	catch(const std::exception ex)
	{
		if(treat_failure_as_error)
		{
			ERR_FS << "Could not open '" << fname << "' for reading.\n";
		}
		bfs::ifstream *s = new bfs::ifstream();
		s->clear(std::ios_base::failbit);
		return s;
	}
}

Project: wesnoth
File: filesystem_boost.cpp

bool is_path_sep(char c)
{
	static const path sep = path("/").make_preferred();
	const std::string s = std::string(1, c);
	return sep == path(s).make_preferred();
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_binary_file_location(const std::string& type, const std::string& filename)
{
	// We define ".." as "remove everything before" this is needed becasue 
	// on the one hand allowing ".." would be a security risk but 
	// especialy for terrains the c++ engine puts a hardcoded "terrain/" before filename
	// and there would be no way to "escape" from "terrain/" otherwise. This is not the 
	// best solution but we cannot remove it without another solution (subtypes maybe?).
	
	// using 'for' instead 'if' to allow putting delcaration and check into the brackets
	for(std::string::size_type pos = filename.rfind("../"); pos != std::string::npos;)
		return get_binary_file_location(type, filename.substr(pos + 3));

	if (!is_legal_file(filename))
		return std::string();

	BOOST_FOREACH(const std::string &bp, get_binary_paths(type))
	{
		path bpath(bp);
		bpath /= filename;
		DBG_FS << "  checking '" << bp << "'\n";
		if (file_exists(bpath)) {
			DBG_FS << "  found at '" << bpath.string() << "'\n";
			return bpath.string();
		}
	}

	DBG_FS << "  not found\n";
	return std::string();
}

Project: wesnoth
File: filesystem_boost.cpp

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())
		return "~" + partial.string();

	partial = subtract_path(full_path, path(game_config::path) / "data");
	if (!partial.empty())
		return partial.string();

	return filename;
}

Project: wesnoth
File: filesystem_boost.cpp

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) {
		++fi;
		++pi;
	}
	path rest;
	if (pi == pe)
		while (fi != fe) {
			rest /= *fi;
			++fi;
		}
	return rest;
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_program_invocation(const std::string &program_name)
{
	const std::string real_program_name(program_name
#ifdef DEBUG
		+ "-debug"
#endif
#ifdef _WIN32
		+ ".exe"
#endif
	);
	return (path(game_config::wesnoth_program_dir) / real_program_name).string();
}

Project: wesnoth
File: filesystem_boost.cpp

int file_size(const std::string& fname)
{
	error_code ec;
	uintmax_t size = bfs::file_size(path(fname), ec);
	if (ec) {
		LOG_FS << "Failed to read filesize of " << fname << ": " << ec.message() << '\n';
		return -1;
	} else if (size > INT_MAX)
		return INT_MAX;
	else
		return size;
}

Project: wesnoth
File: filesystem_boost.cpp

static const path &get_user_data_path()
{
	// TODO:
	// This function is called frequently. The file_exists call may slow things down a lot.
	if (user_data_dir.empty() || !file_exists(user_data_dir))
	{
		set_user_data_dir(std::string());
	}
	return user_data_dir;
}

Project: wesnoth
File: filesystem_boost.cpp

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)) {
		ERR_FS << "Failed to retrieve file status for " << dirpath.string() << ": " << ec.message() << '\n';
		return false;
	} else if (bfs::is_directory(fs)) {
		DBG_FS << "directory " << dirpath.string() << " exists, not creating\n";
		return true;
	} 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) {
		ERR_FS << "Failed to create directory " << dirpath.string() << ": " << ec.message() << '\n';
	}
	return created;
}

Project: wesnoth
File: filesystem_boost.cpp

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) {
		LOG_FS << "Failed to read modification time of " << fname << ": " << ec.message() << '\n';
	}
	return mtime;
}

Project: wesnoth
File: filesystem.cpp

std::string get_binary_file_location(const std::string& type, const std::string& filename)
{
	DBG_FS << "Looking for '" << filename << "'." << std::endl;

	if (filename.empty()) {
		LOG_FS << "  invalid filename (type: " << type <<")" << std::endl;
		return std::string();
	}

	// Some parts of Wesnoth enjoy putting ".." inside filenames. This is
	// bad and should be fixed. But in the meantime, deal with them in a dumb way.
	std::string::size_type pos = filename.rfind("../");
	if (pos != std::string::npos) {
		std::string nf = filename.substr(pos + 3);
		LOG_FS << "Illegal path '" << filename << "' replaced by '" << nf << "'" << std::endl;
		return get_binary_file_location(type, nf);
	}

	if (filename.find("..") != std::string::npos) {
		ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed)." << std::endl;
		return std::string();
	}

	BOOST_FOREACH(const std::string &path, get_binary_paths(type))
	{
		const std::string file = path + filename;
		DBG_FS << "  checking '" << path << "'" << std::endl;
		if(file_exists(file)) {
			DBG_FS << "  found at '" << file << "'" << std::endl;
			return file;
		}
	}

	DBG_FS << "  not found" << std::endl;
	return std::string();
}

Project: wesnoth
File: filesystem_boost.cpp

std::string get_cwd()
{
	error_code ec;
	path cwd = bfs::current_path(ec);
	if (ec) {
		ERR_FS << "Failed to get current directory: " << ec.message() << '\n';
		return "";
	}
	return cwd.generic_string();
}

Project: wesnoth
File: filesystem_boost.cpp

			customcodecvt_do_conversion_writer(char_t_to*& _to_next, char_t_to* _to_end) :
				to_next(_to_next),
				to_end(_to_end)
			{}

Project: wesnoth
File: filesystem.cpp

void set_user_config_dir(std::string path)
{
	user_config_dir = path;
	create_directory_if_missing(user_config_dir);
}

Project: wesnoth
File: filesystem_boost.cpp

std::string read_file(const std::string &fname)
{
	scoped_istream is = istream_file(fname);
	std::stringstream ss;
	ss << is->rdbuf();
	return ss.str();
}

Project: wesnoth
File: filesystem_boost.cpp

static void setup_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;
	}
	// TODO: this may not print the error message if the directory exists but we don't have the proper permissions

	// Create user data and add-on directories
	create_directory_if_missing(user_data_dir / "editor");
	create_directory_if_missing(user_data_dir / "editor" / "maps");
	create_directory_if_missing(user_data_dir / "editor" / "scenarios");
	create_directory_if_missing(user_data_dir / "data");
	create_directory_if_missing(user_data_dir / "data" / "add-ons");
	create_directory_if_missing(user_data_dir / "saves");
	create_directory_if_missing(user_data_dir / "persist");
}