|
13 | 13 |
|
14 | 14 | #include <boost/filesystem.hpp>
|
15 | 15 | #include <boost/filesystem/fstream.hpp>
|
| 16 | +#include <tinyformat.h> |
16 | 17 |
|
17 | 18 | /** Filesystem operations and types */
|
18 |
| -namespace fs = boost::filesystem; |
| 19 | +namespace fs { |
| 20 | + |
| 21 | +using namespace boost::filesystem; |
| 22 | + |
| 23 | +/** |
| 24 | + * Path class wrapper to prepare application code for transition from |
| 25 | + * boost::filesystem library to std::filesystem implementation. The main |
| 26 | + * purpose of the class is to define fs::path::u8string() and fs::u8path() |
| 27 | + * functions not present in boost. It also blocks calls to the |
| 28 | + * fs::path(std::string) implicit constructor and the fs::path::string() |
| 29 | + * method, which worked well in the boost::filesystem implementation, but have |
| 30 | + * unsafe and unpredictable behavior on Windows in the std::filesystem |
| 31 | + * implementation (see implementation note in \ref PathToString for details). |
| 32 | + */ |
| 33 | +class path : public boost::filesystem::path |
| 34 | +{ |
| 35 | +public: |
| 36 | + using boost::filesystem::path::path; |
| 37 | + |
| 38 | + // Allow path objects arguments for compatibility. |
| 39 | + path(boost::filesystem::path path) : boost::filesystem::path::path(std::move(path)) {} |
| 40 | + path& operator=(boost::filesystem::path path) { boost::filesystem::path::operator=(std::move(path)); return *this; } |
| 41 | + path& operator/=(boost::filesystem::path path) { boost::filesystem::path::operator/=(std::move(path)); return *this; } |
| 42 | + |
| 43 | + // Allow literal string arguments, which are safe as long as the literals are ASCII. |
| 44 | + path(const char* c) : boost::filesystem::path(c) {} |
| 45 | + path& operator=(const char* c) { boost::filesystem::path::operator=(c); return *this; } |
| 46 | + path& operator/=(const char* c) { boost::filesystem::path::operator/=(c); return *this; } |
| 47 | + path& append(const char* c) { boost::filesystem::path::append(c); return *this; } |
| 48 | + |
| 49 | + // Disallow std::string arguments to avoid locale-dependent decoding on windows. |
| 50 | + path(std::string) = delete; |
| 51 | + path& operator=(std::string) = delete; |
| 52 | + path& operator/=(std::string) = delete; |
| 53 | + path& append(std::string) = delete; |
| 54 | + |
| 55 | + // Disallow std::string conversion method to avoid locale-dependent encoding on windows. |
| 56 | + std::string string() const = delete; |
| 57 | + |
| 58 | + // Define UTF-8 string conversion method not present in boost::filesystem but present in std::filesystem. |
| 59 | + std::string u8string() const { return boost::filesystem::path::string(); } |
| 60 | +}; |
| 61 | + |
| 62 | +// Define UTF-8 string conversion function not present in boost::filesystem but present in std::filesystem. |
| 63 | +static inline path u8path(const std::string& string) |
| 64 | +{ |
| 65 | + return boost::filesystem::path(string); |
| 66 | +} |
| 67 | + |
| 68 | +// Disallow implicit std::string conversion for system_complete to avoid |
| 69 | +// locale-dependent encoding on windows. |
| 70 | +static inline path system_complete(const path& p) |
| 71 | +{ |
| 72 | + return boost::filesystem::system_complete(p); |
| 73 | +} |
| 74 | + |
| 75 | +// Disallow implicit std::string conversion for exists to avoid |
| 76 | +// locale-dependent encoding on windows. |
| 77 | +static inline bool exists(const path& p) |
| 78 | +{ |
| 79 | + return boost::filesystem::exists(p); |
| 80 | +} |
| 81 | + |
| 82 | +// Allow explicit quoted stream I/O. |
| 83 | +static inline auto quoted(const std::string& s) |
| 84 | +{ |
| 85 | + return boost::io::quoted(s, '&'); |
| 86 | +} |
| 87 | + |
| 88 | +// Allow safe path append operations. |
| 89 | +static inline path operator+(path p1, path p2) |
| 90 | +{ |
| 91 | + p1 += std::move(p2); |
| 92 | + return p1; |
| 93 | +} |
| 94 | + |
| 95 | +/** |
| 96 | + * Convert path object to byte string. On POSIX, paths natively are byte |
| 97 | + * strings so this is trivial. On Windows, paths natively are Unicode, so an |
| 98 | + * encoding step is necessary. |
| 99 | + * |
| 100 | + * The inverse of \ref PathToString is \ref PathFromString. The strings |
| 101 | + * returned and parsed by these functions can be used to call POSIX APIs, and |
| 102 | + * for roundtrip conversion, logging, and debugging. But they are not |
| 103 | + * guaranteed to be valid UTF-8, and are generally meant to be used internally, |
| 104 | + * not externally. When communicating with external programs and libraries that |
| 105 | + * require UTF-8, fs::path::u8string() and fs::u8path() methods can be used. |
| 106 | + * For other applications, if support for non UTF-8 paths is required, or if |
| 107 | + * higher-level JSON or XML or URI or C-style escapes are preferred, it may be |
| 108 | + * also be appropriate to use different path encoding functions. |
| 109 | + * |
| 110 | + * Implementation note: On Windows, the std::filesystem::path(string) |
| 111 | + * constructor and std::filesystem::path::string() method are not safe to use |
| 112 | + * here, because these methods encode the path using C++'s narrow multibyte |
| 113 | + * encoding, which on Windows corresponds to the current "code page", which is |
| 114 | + * unpredictable and typically not able to represent all valid paths. So |
| 115 | + * std::filesystem::path::u8string() and std::filesystem::u8path() functions |
| 116 | + * are used instead on Windows. On POSIX, u8string/u8path functions are not |
| 117 | + * safe to use because paths are not always valid UTF-8, so plain string |
| 118 | + * methods which do not transform the path there are used. |
| 119 | + */ |
| 120 | +static inline std::string PathToString(const path& path) |
| 121 | +{ |
| 122 | +#ifdef WIN32 |
| 123 | + return path.u8string(); |
| 124 | +#else |
| 125 | + static_assert(std::is_same<path::string_type, std::string>::value, "PathToString not implemented on this platform"); |
| 126 | + return path.boost::filesystem::path::string(); |
| 127 | +#endif |
| 128 | +} |
| 129 | + |
| 130 | +/** |
| 131 | + * Convert byte string to path object. Inverse of \ref PathToString. |
| 132 | + */ |
| 133 | +static inline path PathFromString(const std::string& string) |
| 134 | +{ |
| 135 | +#ifdef WIN32 |
| 136 | + return u8path(string); |
| 137 | +#else |
| 138 | + return boost::filesystem::path(string); |
| 139 | +#endif |
| 140 | +} |
| 141 | +} // namespace fs |
19 | 142 |
|
20 | 143 | /** Bridge operations to C stdio */
|
21 | 144 | namespace fsbridge {
|
@@ -103,4 +226,11 @@ namespace fsbridge {
|
103 | 226 | #endif // WIN32 && __GLIBCXX__
|
104 | 227 | };
|
105 | 228 |
|
| 229 | +// Disallow path operator<< formatting in tinyformat to avoid locale-dependent |
| 230 | +// encoding on windows. |
| 231 | +namespace tinyformat { |
| 232 | +template<> inline void formatValue(std::ostream&, const char*, const char*, int, const boost::filesystem::path&) = delete; |
| 233 | +template<> inline void formatValue(std::ostream&, const char*, const char*, int, const fs::path&) = delete; |
| 234 | +} // namespace tinyformat |
| 235 | + |
106 | 236 | #endif // BITCOIN_FS_H
|
0 commit comments