1 /*
2 Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 /**
16 * @file
17 * Declarations for File-IO.
18 */
19
20 #pragma once
21
22 #include <algorithm>
23 #include <ctime>
24 #include <functional>
25 #include <iosfwd>
26 #include <string>
27 #include <vector>
28 #include <memory>
29
30 #include "exceptions.hpp"
31 #include "serialization/string_utils.hpp"
32
33 class config;
34
35 struct SDL_RWops;
36
37 namespace filesystem {
38
39 using scoped_istream = std::unique_ptr<std::istream>;
40 using scoped_ostream = std::unique_ptr<std::ostream>;
41
42 typedef std::unique_ptr<SDL_RWops, void(*)(SDL_RWops*)> rwops_ptr;
43
44 rwops_ptr make_read_RWops(const std::string &path);
45 rwops_ptr make_write_RWops(const std::string &path);
46
47 /** An exception object used when an IO error occurs */
48 struct io_exception : public game::error {
io_exceptionfilesystem::io_exception49 io_exception() : game::error("") {}
io_exceptionfilesystem::io_exception50 io_exception(const std::string& msg) : game::error(msg) {}
51 };
52
53 struct file_tree_checksum;
54
55 enum file_name_option { ENTIRE_FILE_PATH, FILE_NAME_ONLY };
56 enum file_filter_option { NO_FILTER, SKIP_MEDIA_DIR, SKIP_PBL_FILES };
57 enum file_reorder_option { DONT_REORDER, DO_REORDER };
58
59 // A list of file and directory blacklist patterns
60 class blacklist_pattern_list
61 {
62 public:
blacklist_pattern_list()63 blacklist_pattern_list()
64 : file_patterns_(), directory_patterns_()
65 {}
blacklist_pattern_list(const std::vector<std::string> & file_patterns,const std::vector<std::string> & directory_patterns)66 blacklist_pattern_list(const std::vector<std::string>& file_patterns, const std::vector<std::string>& directory_patterns)
67 : file_patterns_(file_patterns), directory_patterns_(directory_patterns)
68 {}
69
match_file(const std::string & name) const70 bool match_file(const std::string& name) const
71 {
72 return std::any_of(file_patterns_.begin(), file_patterns_.end(),
73 std::bind(&utils::wildcard_string_match, std::ref(name), std::placeholders::_1));
74 }
75
match_dir(const std::string & name) const76 bool match_dir(const std::string& name) const
77 {
78 return std::any_of(directory_patterns_.begin(), directory_patterns_.end(),
79 std::bind(&utils::wildcard_string_match, std::ref(name), std::placeholders::_1));
80 }
81
add_file_pattern(const std::string & pattern)82 void add_file_pattern(const std::string& pattern)
83 {
84 file_patterns_.push_back(pattern);
85 }
86
add_directory_pattern(const std::string & pattern)87 void add_directory_pattern(const std::string& pattern)
88 {
89 directory_patterns_.push_back(pattern);
90 }
91
92 void remove_blacklisted_files_and_dirs(std::vector<std::string>& files, std::vector<std::string>& directories) const;
93
94 private:
95 std::vector<std::string> file_patterns_;
96 std::vector<std::string> directory_patterns_;
97 };
98
99 static const blacklist_pattern_list default_blacklist{
100 {
101 /* Blacklist dot-files/dirs, which are hidden files in UNIX platforms */
102 ".+",
103 "#*#",
104 "*~",
105 "*-bak",
106 "*.swp",
107 "*.pbl",
108 "*.ign",
109 "_info.cfg",
110 "*.exe",
111 "*.bat",
112 "*.cmd",
113 "*.com",
114 "*.scr",
115 "*.sh",
116 "*.js",
117 "*.vbs",
118 "*.o",
119 "*.ini",
120 /* Remove junk created by certain file manager ;) */
121 "Thumbs.db",
122 /* Eclipse plugin */
123 "*.wesnoth",
124 "*.project",
125 },
126 {
127 ".+",
128 /* macOS metadata-like cruft (http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files/) */
129 "__MACOSX",
130 }
131 };
132
133 /** Some tasks to run on startup. */
134 void init();
135
136 /**
137 * Populates 'files' with all the files and
138 * 'dirs' with all the directories in dir.
139 * If files or dirs are nullptr they will not be used.
140 *
141 * mode: determines whether the entire path or just the filename is retrieved.
142 * filter: determines if we skip images and sounds directories
143 * reorder: triggers the special handling of _main.cfg and _final.cfg
144 * checksum: can be used to store checksum info
145 */
146 void get_files_in_dir(const std::string &dir,
147 std::vector<std::string>* files,
148 std::vector<std::string>* dirs=nullptr,
149 file_name_option mode = FILE_NAME_ONLY,
150 file_filter_option filter = NO_FILTER,
151 file_reorder_option reorder = DONT_REORDER,
152 file_tree_checksum* checksum = nullptr);
153
154 std::string get_dir(const std::string &dir);
155
156 // The location of various important files:
157 std::string get_prefs_file();
158 std::string get_credentials_file();
159 std::string get_default_prefs_file();
160 std::string get_save_index_file();
161 std::string get_saves_dir();
162 std::string get_intl_dir();
163 std::string get_screenshot_dir();
164 std::string get_addons_dir();
165
166 /**
167 * Get the next free filename using "name + number (3 digits) + extension"
168 * maximum 1000 files then start always giving 999
169 */
170 std::string get_next_filename(const std::string& name, const std::string& extension);
171 void set_user_config_dir(const std::string& path);
172 void set_user_data_dir(std::string path);
173
174 std::string get_user_config_dir();
175 std::string get_user_data_dir();
176 std::string get_cache_dir();
177
178 std::string get_cwd();
179 std::string get_exe_dir();
180
181 bool make_directory(const std::string& dirname);
182 bool delete_directory(const std::string& dirname, const bool keep_pbl = false);
183 bool delete_file(const std::string &filename);
184
185 bool looks_like_pbl(const std::string& file);
186
187 // Basic disk I/O:
188
189 /** Basic disk I/O - read file. */
190 std::string read_file(const std::string &fname);
191 filesystem::scoped_istream istream_file(const std::string& fname, bool treat_failure_as_error = true);
192 filesystem::scoped_ostream ostream_file(const std::string& fname, bool create_directory = true);
193 /** Throws io_exception if an error occurs. */
194 void write_file(const std::string& fname, const std::string& data);
195
196 std::string read_map(const std::string& name);
197
198 /**
199 * Creates a directory if it does not exist already.
200 *
201 * @param dirname Path to directory. All parents should exist.
202 * @returns True if the directory exists or could be
203 * successfully created; false otherwise.
204 */
205 bool create_directory_if_missing(const std::string& dirname);
206 /**
207 * Creates a recursive directory tree if it does not exist already
208 * @param dirname Full path of target directory. Non existing parents
209 * will be created
210 * @return True if the directory exists or could be
211 * successfully created; false otherwise.
212 */
213 bool create_directory_if_missing_recursive(const std::string& dirname);
214
215 /** Returns true if the given file is a directory. */
216 bool is_directory(const std::string& fname);
217
218 /** Returns true if a file or directory with such name already exists. */
219 bool file_exists(const std::string& name);
220
221 /** Get the modification time of a file. */
222 time_t file_modified_time(const std::string& fname);
223
224 /** Returns true if the file ends with '.gz'. */
225 bool is_gzip_file(const std::string& filename);
226
227 /** Returns true if the file ends with '.bz2'. */
228 bool is_bzip2_file(const std::string& filename);
229
is_compressed_file(const std::string & filename)230 inline bool is_compressed_file(const std::string& filename) {
231 return is_gzip_file(filename) || is_bzip2_file(filename);
232 }
233
234 struct file_tree_checksum
235 {
236 file_tree_checksum();
237 explicit file_tree_checksum(const config& cfg);
238 void write(config& cfg) const;
resetfilesystem::file_tree_checksum239 void reset() {nfiles = 0;modified = 0;sum_size=0;}
240 // @todo make variables private!
241 size_t nfiles, sum_size;
242 time_t modified;
243 bool operator==(const file_tree_checksum &rhs) const;
operator !=filesystem::file_tree_checksum244 bool operator!=(const file_tree_checksum &rhs) const
245 { return !operator==(rhs); }
246 };
247
248 /** Get the time at which the data/ tree was last modified at. */
249 const file_tree_checksum& data_tree_checksum(bool reset = false);
250
251 /** Returns the size of a file, or -1 if the file doesn't exist. */
252 int file_size(const std::string& fname);
253
254 /** Returns the sum of the sizes of the files contained in a directory. */
255 int dir_size(const std::string& path);
256
257 bool ends_with(const std::string& str, const std::string& suffix);
258
259 /**
260 * Returns the base filename of a file, with directory name stripped.
261 * Equivalent to a portable basename() function.
262 *
263 * If @a remove_extension is true, the filename extension will be stripped
264 * from the returned value.
265 */
266 std::string base_name(const std::string& file, const bool remove_extension = false);
267
268 /**
269 * Returns the directory name of a file, with filename stripped.
270 * Equivalent to a portable dirname()
271 */
272 std::string directory_name(const std::string& file);
273
274 /**
275 * Finds the nearest parent in existence for a file or directory.
276 *
277 * @note The file's own existence is not checked.
278 *
279 * @returns An absolute path to the closest parent of the given path, or an
280 * empty string if none could be found. While on POSIX platforms this
281 * cannot happen (unless the original path was already empty), on
282 * Windows it might be the case that the original path refers to a
283 * drive letter or network share that does not exist.
284 */
285 std::string nearest_extant_parent(const std::string& file);
286
287 /**
288 * Returns the absolute path of a file.
289 *
290 * @param path Original path.
291 * @param normalize_separators Whether to substitute path separators with the
292 * platform's preferred format.
293 * @param resolve_dot_entries Whether to resolve . and .. directory entries.
294 * This requires @a path to refer to a valid
295 * existing object.
296 *
297 * @returns An absolute path -- that is, a path that is independent of the
298 * current working directory for the process. If resolve_dot_entries
299 * is set to true, the returned path has . and .. components resolved;
300 * however, if resolution fails because a component does not exist, an
301 * empty string is returned instead.
302 */
303 std::string normalize_path(const std::string& path,
304 bool normalize_separators = false,
305 bool resolve_dot_entries = false);
306
307 /**
308 * Sanitizes a path to remove references to the user's name.
309 */
310 std::string sanitize_path(const std::string& path);
311
312 /**
313 * Returns whether the path is the root of the file hierarchy.
314 *
315 * @note This function is unreliable for paths that do not exist -- it will
316 * always return @a false for those.
317 */
318 bool is_root(const std::string& path);
319
320 /**
321 * Returns the name of the root device if included in the given path.
322 *
323 * This only properly makes sense on Windows with paths containing a drive
324 * letter or UNC at the start -- otherwise, it will return the empty string. To
325 * ensure that a suitable root name can be found you might want to use
326 * normalize_path() first with @a resolve_dot_entries set to true.
327 */
328 std::string root_name(const std::string& path);
329
330 /**
331 * Returns whether the path seems to be relative.
332 */
333 bool is_relative(const std::string& path);
334
335 /**
336 * Returns whether @a c is a path separator.
337 *
338 * @note / is always a path separator. Additionally, on Windows \\ is a
339 * path separator as well.
340 */
341 bool is_path_sep(char c);
342
343 /**
344 * Returns the standard path separator for the current platform.
345 */
346 char path_separator();
347
348 /**
349 * The paths manager is responsible for recording the various paths
350 * that binary files may be located at.
351 * It should be passed a config object which holds binary path information.
352 * This is in the format
353 *@verbatim
354 * [binary_path]
355 * path=<path>
356 * [/binary_path]
357 * Binaries will be searched for in [wesnoth-path]/data/<path>/images/
358 *@endverbatim
359 */
360 struct binary_paths_manager
361 {
362 binary_paths_manager();
363 binary_paths_manager(const config& cfg);
364 ~binary_paths_manager();
365
366 void set_paths(const config& cfg);
367
368 private:
369 binary_paths_manager(const binary_paths_manager& o);
370 binary_paths_manager& operator=(const binary_paths_manager& o);
371
372 void cleanup();
373
374 std::vector<std::string> paths_;
375 };
376
377 void clear_binary_paths_cache();
378
379 /**
380 * Returns a vector with all possible paths to a given type of binary,
381 * e.g. 'images', 'sounds', etc,
382 */
383 const std::vector<std::string>& get_binary_paths(const std::string& type);
384
385 /**
386 * Returns a complete path to the actual file of a given @a type
387 * or an empty string if the file isn't present.
388 */
389 std::string get_binary_file_location(const std::string& type, const std::string& filename);
390
391 /**
392 * Returns a complete path to the actual directory of a given @a type
393 * or an empty string if the directory isn't present.
394 */
395 std::string get_binary_dir_location(const std::string &type, const std::string &filename);
396
397 /**
398 * Returns a complete path to the actual WML file or directory
399 * or an empty string if the file isn't present.
400 */
401 std::string get_wml_location(const std::string &filename,
402 const std::string ¤t_dir = std::string());
403
404 /**
405 * Returns a short path to @a filename, skipping the (user) data directory.
406 */
407 std::string get_short_wml_path(const std::string &filename);
408
409 /**
410 * Returns an image path to @a filename for binary path-independent use in saved games.
411 *
412 * Example:
413 * units/konrad-fighter.png ->
414 * data/campaigns/Heir_To_The_Throne/images/units/konrad-fighter.png
415 */
416 std::string get_independent_image_path(const std::string &filename);
417
418 /**
419 * Returns the appropriate invocation for a Wesnoth-related binary, assuming
420 * that it is located in the same directory as the running wesnoth binary.
421 * This is just a string-transformation based on argv[0], so the returned
422 * program is not guaranteed to actually exist. '-debug' variants are handled
423 * correctly.
424 */
425 std::string get_program_invocation(const std::string &program_name);
426
427
428 }
429