The Battle for Wesnoth  1.19.0-dev
filesystem.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Declarations for File-IO.
19  */
20 
21 #pragma once
22 
23 #include <ctime>
24 #include <fstream>
25 #include <iosfwd>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include "exceptions.hpp"
31 #include "game_version.hpp"
32 
33 namespace game_config {
34 extern std::string path;
35 extern std::string default_preferences_path;
36 extern bool check_migration;
37 
38 /** observer team name used for observer team chat */
39 extern const std::string observer_team_name;
40 
41 extern int cache_compression_level;
42 }
43 
44 class config;
45 class game_config_view;
46 struct SDL_RWops;
47 
48 namespace filesystem {
49 
50 using scoped_istream = std::unique_ptr<std::istream>;
51 using scoped_ostream = std::unique_ptr<std::ostream>;
52 
54 {
55  void operator()(SDL_RWops*) const noexcept;
56 };
57 
58 using rwops_ptr = std::unique_ptr<SDL_RWops, sdl_rwops_deleter>;
59 
60 rwops_ptr make_read_RWops(const std::string &path);
61 rwops_ptr make_write_RWops(const std::string &path);
62 
63 /** An exception object used when an IO error occurs */
64 struct io_exception : public game::error {
65  io_exception() : game::error("") {}
66  io_exception(const std::string& msg) : game::error(msg) {}
67 };
68 
69 struct file_tree_checksum;
70 
74 
75 // A list of file and directory blacklist patterns
77 {
78 public:
81  {}
82  blacklist_pattern_list(const std::vector<std::string>& file_patterns, const std::vector<std::string>& directory_patterns)
83  : file_patterns_(file_patterns), directory_patterns_(directory_patterns)
84  {}
85 
86  bool match_file(const std::string& name) const;
87 
88  bool match_dir(const std::string& name) const;
89 
90  void add_file_pattern(const std::string& pattern)
91  {
92  file_patterns_.push_back(pattern);
93  }
94 
95  void add_directory_pattern(const std::string& pattern)
96  {
97  directory_patterns_.push_back(pattern);
98  }
99 
100  void remove_blacklisted_files_and_dirs(std::vector<std::string>& files, std::vector<std::string>& directories) const;
101 
102 private:
103  std::vector<std::string> file_patterns_;
104  std::vector<std::string> directory_patterns_;
105 };
106 
108 
109 /**
110  * Get a list of all files and/or directories in a given directory.
111  *
112  * @param dir The directory to examine.
113  * @param[out] files The files in @a dir. Won't be used if nullptr.
114  * @param[out] dirs The directories in @a dir. Won't be used if nullptr.
115  * @param mode Determines whether the entire path or just the filename is retrieved.
116  * @param filter Determines if we skip images and sounds directories.
117  * @param reorder Triggers the special handling of _main.cfg and _final.cfg.
118  * @param[out] checksum Can be used to store checksum info.
119  */
120 void get_files_in_dir(const std::string &dir,
121  std::vector<std::string>* files,
122  std::vector<std::string>* dirs=nullptr,
126  file_tree_checksum* checksum = nullptr);
127 
128 std::string get_dir(const std::string &dir);
129 
130 // The location of various important files:
131 std::string get_prefs_file();
132 std::string get_credentials_file();
133 std::string get_default_prefs_file();
134 std::string get_save_index_file();
135 std::string get_saves_dir();
136 std::string get_wml_persist_dir();
137 std::string get_intl_dir();
138 std::string get_screenshot_dir();
139 std::string get_addons_data_dir();
140 std::string get_addons_dir();
141 std::string get_current_editor_dir(const std::string& addon_id);
142 const std::string get_version_path_suffix(const version_info& version);
143 const std::string& get_version_path_suffix();
144 
145 /**
146  * Get the next free filename using "name + number (3 digits) + extension"
147  * maximum 1000 files then start always giving 999
148  */
149 std::string get_next_filename(const std::string& name, const std::string& extension);
150 void set_user_config_dir(const std::string& path);
151 void set_user_data_dir(std::string path);
152 void set_cache_dir(const std::string& path);
153 
154 std::string get_user_config_dir();
155 std::string get_user_data_dir();
156 std::string get_logs_dir();
157 std::string get_cache_dir();
158 std::string get_legacy_editor_dir();
159 std::string get_core_images_dir();
160 
161 bool rename_dir(const std::string& old_dir, const std::string& new_dir);
162 
164 {
165  /**
166  * Here the version is given as a string instead of a version_info, because the
167  * logic of how many components are significant ("1.16" rather than
168  * "1.16.0") is encapsulated in find_other_version_saves_dirs().
169  */
170  std::string version;
171  std::string path;
172 
173  // constructor because emplace_back() doesn't use aggregate initialization
174  other_version_dir(const std::string& v, const std::string& p)
175  : version(v)
176  , path(p)
177  {
178  }
179 };
180 
181 /**
182  * Searches for directories containing saves created by other versions of Wesnoth.
183  *
184  * The directories returned will exist, but might not contain any saves. Changes to
185  * the filesystem (by running other versions or by deleting old directories) may
186  * change the results returned by the function.
187  */
188 std::vector<other_version_dir> find_other_version_saves_dirs();
189 
190 std::string get_cwd();
191 bool set_cwd(const std::string& dir);
192 
193 std::string get_exe_dir();
194 
195 bool make_directory(const std::string& dirname);
196 bool delete_directory(const std::string& dirname, const bool keep_pbl = false);
197 bool delete_file(const std::string& filename);
198 
199 bool looks_like_pbl(const std::string& file);
200 
201 // Basic disk I/O:
202 
203 /** Basic disk I/O - read file. */
204 std::string read_file(const std::string& fname);
205 std::vector<uint8_t> read_file_binary(const std::string& fname);
206 std::string read_file_as_data_uri(const std::string& fname);
207 
208 filesystem::scoped_istream istream_file(const std::string& fname, bool treat_failure_as_error = true);
209 filesystem::scoped_ostream ostream_file(const std::string& fname, std::ios_base::openmode mode = std::ios_base::binary, bool create_directory = true);
210 /** Throws io_exception if an error occurs. */
211 void write_file(const std::string& fname, const std::string& data, std::ios_base::openmode mode = std::ios_base::binary);
212 /**
213  * Read a file and then writes it back out.
214  *
215  * @param src The source file.
216  * @param dest The destination of the copied file.
217  */
218 void copy_file(const std::string& src, const std::string& dest);
219 
220 std::string read_map(const std::string& name);
221 std::string read_scenario(const std::string& name);
222 
223 /**
224  * Creates a directory if it does not exist already.
225  *
226  * @param dirname Path to directory. All parents should exist.
227  * @returns True if the directory exists or could be
228  * successfully created; false otherwise.
229  */
230 bool create_directory_if_missing(const std::string& dirname);
231 /**
232  * Creates a recursive directory tree if it does not exist already
233  * @param dirname Full path of target directory. Non existing parents
234  * will be created
235  * @return True if the directory exists or could be
236  * successfully created; false otherwise.
237  */
238 bool create_directory_if_missing_recursive(const std::string& dirname);
239 
240 /** Returns true if the given file is a directory. */
241 bool is_directory(const std::string& fname);
242 
243 /** Returns true if a file or directory with such name already exists. */
244 bool file_exists(const std::string& name);
245 
246 /** Get the modification time of a file. */
247 std::time_t file_modified_time(const std::string& fname);
248 
249 /** Returns true if the file ends with '.gz'. */
250 bool is_gzip_file(const std::string& filename);
251 
252 /** Returns true if the file ends with '.bz2'. */
253 bool is_bzip2_file(const std::string& filename);
254 
255 inline bool is_compressed_file(const std::string& filename) {
256  return is_gzip_file(filename) || is_bzip2_file(filename);
257 }
258 
259 /**
260  * Returns whether the given filename is a legal name for a user-created file.
261  *
262  * This is meant to be used for any files created by Wesnoth where user input
263  * is required, including save files and add-on files for uploading to the
264  * add-ons server.
265  *
266  * @param name File name to verify.
267  * @param allow_whitespace Whether whitespace should be allowed.
268  */
269 bool is_legal_user_file_name(const std::string& name, bool allow_whitespace = true);
270 
272 {
274  explicit file_tree_checksum(const config& cfg);
275  void write(config& cfg) const;
276  void reset() {nfiles = 0;modified = 0;sum_size=0;}
277  // @todo make variables private!
278  std::size_t nfiles, sum_size;
279  std::time_t modified;
280  bool operator==(const file_tree_checksum &rhs) const;
281  bool operator!=(const file_tree_checksum &rhs) const
282  { return !operator==(rhs); }
283 };
284 
285 /** Get the time at which the data/ tree was last modified at. */
286 const file_tree_checksum& data_tree_checksum(bool reset = false);
287 
288 /** Returns the size of a file, or -1 if the file doesn't exist. */
289 int file_size(const std::string& fname);
290 
291 /** Returns the sum of the sizes of the files contained in a directory. */
292 int dir_size(const std::string& path);
293 
294 bool ends_with(const std::string& str, const std::string& suffix);
295 
296 /**
297  * Returns the base filename of a file, with directory name stripped.
298  * Equivalent to a portable basename() function.
299  *
300  * If @a remove_extension is true, the filename extension will be stripped
301  * from the returned value.
302  */
303 std::string base_name(const std::string& file, const bool remove_extension = false);
304 
305 /**
306  * Returns the directory name of a file, with filename stripped.
307  * Equivalent to a portable dirname()
308  */
309 std::string directory_name(const std::string& file);
310 
311 /**
312  * Finds the nearest parent in existence for a file or directory.
313  *
314  * @note The file's own existence is not checked.
315  *
316  * @returns An absolute path to the closest parent of the given path, or an
317  * empty string if none could be found. While on POSIX platforms this
318  * cannot happen (unless the original path was already empty), on
319  * Windows it might be the case that the original path refers to a
320  * drive letter or network share that does not exist.
321  */
322 std::string nearest_extant_parent(const std::string& file);
323 
324 /**
325  * Returns the absolute path of a file.
326  *
327  * @param path Original path.
328  * @param normalize_separators Whether to substitute path separators with the
329  * platform's preferred format.
330  * @param resolve_dot_entries Whether to resolve . and .. directory entries.
331  * This requires @a path to refer to a valid
332  * existing object.
333  *
334  * @returns An absolute path -- that is, a path that is independent of the
335  * current working directory for the process. If resolve_dot_entries
336  * is set to true, the returned path has . and .. components resolved;
337  * however, if resolution fails because a component does not exist, an
338  * empty string is returned instead.
339  */
340 std::string normalize_path(const std::string& path,
341  bool normalize_separators = false,
342  bool resolve_dot_entries = false);
343 
344 /**
345  * Sanitizes a path to remove references to the user's name.
346  */
347 std::string sanitize_path(const std::string& path);
348 
349 /**
350  * Returns whether the path is the root of the file hierarchy.
351  *
352  * @note This function is unreliable for paths that do not exist -- it will
353  * always return @a false for those.
354  */
355 bool is_root(const std::string& path);
356 
357 /**
358  * Returns the name of the root device if included in the given path.
359  *
360  * This only properly makes sense on Windows with paths containing a drive
361  * letter or UNC at the start -- otherwise, it will return the empty string. To
362  * ensure that a suitable root name can be found you might want to use
363  * normalize_path() first with @a resolve_dot_entries set to true.
364  */
365 std::string root_name(const std::string& path);
366 
367 /**
368  * Returns whether the path seems to be relative.
369  */
370 bool is_relative(const std::string& path);
371 
372 /**
373  * Returns whether @a c is a path separator.
374  *
375  * @note / is always a path separator. Additionally, on Windows \\ is a
376  * path separator as well.
377  */
378 bool is_path_sep(char c);
379 
380 /**
381  * Returns the standard path separator for the current platform.
382  */
383 char path_separator();
384 
385 /**
386  * The paths manager is responsible for recording the various paths
387  * that binary files may be located at.
388  * It should be passed a config object which holds binary path information.
389  * This is in the format
390  *@verbatim
391  * [binary_path]
392  * path=<path>
393  * [/binary_path]
394  * Binaries will be searched for in [wesnoth-path]/data/<path>/images/
395  *@endverbatim
396  */
398 {
402 
403  void set_paths(const game_config_view& cfg);
404 
405 private:
408 
409  void cleanup();
410 
411  std::vector<std::string> paths_;
412 };
413 
415 
416 /**
417  * Returns a vector with all possible paths to a given type of binary,
418  * e.g. 'images', 'sounds', etc,
419  */
420 const std::vector<std::string>& get_binary_paths(const std::string& type);
421 
422 /**
423  * Returns a complete path to the actual file of a given @a type
424  * or an empty string if the file isn't present.
425  */
426 std::string get_binary_file_location(const std::string& type, const std::string& filename);
427 
428 /**
429  * Returns a complete path to the actual directory of a given @a type
430  * or an empty string if the directory isn't present.
431  */
432 std::string get_binary_dir_location(const std::string &type, const std::string &filename);
433 
434 /**
435  * Returns a complete path to the actual WML file or directory
436  * or an empty string if the file isn't present.
437  */
438 std::string get_wml_location(const std::string &filename,
439  const std::string &current_dir = std::string());
440 
441 /**
442  * Returns a short path to @a filename, skipping the (user) data directory.
443  */
444 std::string get_short_wml_path(const std::string &filename);
445 
446 /**
447  * Returns an asset path to @a filename for binary path-independent use in saved games.
448  *
449  * Example:
450  * images, units/konrad-fighter.png ->
451  * data/campaigns/Heir_To_The_Throne/images/units/konrad-fighter.png
452  */
453 std::string get_independent_binary_file_path(const std::string& type, const std::string &filename);
454 
455 /**
456  * Returns the appropriate invocation for a Wesnoth-related binary, assuming
457  * that it is located in the same directory as the running wesnoth binary.
458  * This is just a string-transformation based on argv[0], so the returned
459  * program is not guaranteed to actually exist. '-debug' variants are handled
460  * correctly.
461  */
462 std::string get_program_invocation(const std::string &program_name);
463 
464 /**
465  * Returns the localized version of the given filename, if it exists.
466  */
467 std::string get_localized_path(const std::string& file, const std::string& suff = "");
468 
469 /**
470  * Returns the add-on ID from a path.
471  * aka the directory directly following the "add-ons" folder, or an empty string if none is found.
472  */
473 std::string get_addon_id_from_path(const std::string& location);
474 
475 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
std::vector< std::string > file_patterns_
Definition: filesystem.hpp:103
bool match_file(const std::string &name) const
void remove_blacklisted_files_and_dirs(std::vector< std::string > &files, std::vector< std::string > &directories) const
void add_file_pattern(const std::string &pattern)
Definition: filesystem.hpp:90
std::vector< std::string > directory_patterns_
Definition: filesystem.hpp:104
blacklist_pattern_list(const std::vector< std::string > &file_patterns, const std::vector< std::string > &directory_patterns)
Definition: filesystem.hpp:82
void add_directory_pattern(const std::string &pattern)
Definition: filesystem.hpp:95
bool match_dir(const std::string &name) const
A class grating read only view to a vector of config objects, viewed as one config with all children ...
Represents version numbers.
Interfaces for manipulating version numbers of engine, add-ons, etc.
std::string get_legacy_editor_dir()
std::string get_cache_dir()
Definition: filesystem.cpp:880
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
bool is_bzip2_file(const std::string &filename)
Returns true if the file ends with '.bz2'.
int dir_size(const std::string &pname)
Returns the sum of the sizes of the files contained in a directory.
bool is_relative(const std::string &path)
Returns whether the path seems to be relative.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_config_dir()
Definition: filesystem.cpp:841
std::string get_localized_path(const std::string &file, const std::string &suff)
Returns the localized version of the given filename, if it exists.
bool is_root(const std::string &path)
Returns whether the path is the root of the file hierarchy.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
Definition: filesystem.cpp:404
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:329
std::string get_user_data_dir()
Definition: filesystem.cpp:870
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
std::string get_wml_persist_dir()
bool rename_dir(const std::string &old_dir, const std::string &new_dir)
Definition: filesystem.cpp:794
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
bool delete_file(const std::string &filename)
bool is_gzip_file(const std::string &filename)
Returns true if the file ends with '.gz'.
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:318
std::unique_ptr< SDL_RWops, sdl_rwops_deleter > rwops_ptr
Definition: filesystem.hpp:58
std::string get_exe_dir()
Definition: filesystem.cpp:990
bool is_legal_user_file_name(const std::string &name, bool allow_whitespace=true)
Returns whether the given filename is a legal name for a user-created file.
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
bool delete_directory(const std::string &dirname, const bool keep_pbl)
std::string get_saves_dir()
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
rwops_ptr make_read_RWops(const std::string &path)
const file_tree_checksum & data_tree_checksum(bool reset=false)
Get the time at which the data/ tree was last modified at.
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...
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...
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
bool ends_with(const std::string &str, const std::string &suffix)
std::string get_independent_binary_file_path(const std::string &type, const std::string &filename)
Returns an asset path to filename for binary path-independent use in saved games.
std::string get_save_index_file()
void set_user_config_dir(const std::string &newconfigdir)
Definition: filesystem.cpp:814
bool is_compressed_file(const std::string &filename)
Definition: filesystem.hpp:255
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...
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static bool create_directory_if_missing_recursive(const bfs::path &dirpath)
Definition: filesystem.cpp:376
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
std::string get_prefs_file()
std::string read_file_as_data_uri(const std::string &fname)
void set_cache_dir(const std::string &newcachedir)
Definition: filesystem.cpp:827
std::string read_scenario(const std::string &name)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:50
void clear_binary_paths_cache()
std::string get_screenshot_dir()
std::string get_credentials_file()
std::string get_addon_id_from_path(const std::string &location)
Returns the add-on ID from a path.
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string root_name(const std::string &path)
Returns the name of the root device if included in the given path.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::string get_logs_dir()
Definition: filesystem.cpp:875
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:51
static bool create_directory_if_missing(const bfs::path &dirpath)
Definition: filesystem.cpp:352
std::string nearest_extant_parent(const std::string &file)
Finds the nearest parent in existence for a file or directory.
char path_separator()
Returns the standard path separator for the current platform.
bool looks_like_pbl(const std::string &file)
std::string get_addons_data_dir()
bool make_directory(const std::string &dirname)
std::string get_default_prefs_file()
const blacklist_pattern_list default_blacklist
Definition: filesystem.cpp:257
std::string get_addons_dir()
bool set_cwd(const std::string &dir)
Definition: filesystem.cpp:975
std::vector< other_version_dir > find_other_version_saves_dirs()
Searches for directories containing saves created by other versions of Wesnoth.
Definition: filesystem.cpp:909
rwops_ptr make_write_RWops(const std::string &path)
std::string get_next_filename(const std::string &name, const std::string &extension)
Get the next free filename using "name + number (3 digits) + extension" maximum 1000 files then start...
Definition: filesystem.cpp:545
std::string get_intl_dir()
std::string get_core_images_dir()
std::vector< uint8_t > read_file_binary(const std::string &fname)
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user's name.
std::string get_current_editor_dir(const std::string &addon_id)
const std::string get_version_path_suffix(const version_info &version)
Definition: filesystem.cpp:568
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.
bool is_path_sep(char c)
Returns whether c is a path separator.
std::string read_map(const std::string &name)
std::string get_cwd()
Definition: filesystem.cpp:962
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void set_user_data_dir(std::string newprefdir)
Definition: filesystem.cpp:663
Game configuration data as global variables.
Definition: build_info.cpp:60
std::string path
Definition: filesystem.cpp:83
std::string default_preferences_path
Definition: filesystem.cpp:89
const std::string observer_team_name
observer team name used for observer team chat
Definition: filesystem.cpp:93
int cache_compression_level
Definition: filesystem.cpp:95
bool check_migration
Definition: filesystem.cpp:91
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::string_view data
Definition: picture.cpp:194
The paths manager is responsible for recording the various paths that binary files may be located at.
Definition: filesystem.hpp:398
std::vector< std::string > paths_
Definition: filesystem.hpp:411
binary_paths_manager(const binary_paths_manager &o)
binary_paths_manager & operator=(const binary_paths_manager &o)
void set_paths(const game_config_view &cfg)
bool operator==(const file_tree_checksum &rhs) const
bool operator!=(const file_tree_checksum &rhs) const
Definition: filesystem.hpp:281
An exception object used when an IO error occurs.
Definition: filesystem.hpp:64
io_exception(const std::string &msg)
Definition: filesystem.hpp:66
std::string version
Here the version is given as a string instead of a version_info, because the logic of how many compon...
Definition: filesystem.hpp:170
other_version_dir(const std::string &v, const std::string &p)
Definition: filesystem.hpp:174
void operator()(SDL_RWops *) const noexcept
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
mock_char c
mock_party p