Skip to content

Commit 86eb3b3

Browse files
committed
utils: Add fsbridge fstream function wrapper
1 parent cc7258b commit 86eb3b3

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

src/fs.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,106 @@ std::string get_filesystem_error_message(const fs::filesystem_error& e)
113113
#endif
114114
}
115115

116+
#ifdef WIN32
117+
#ifdef __GLIBCXX__
118+
119+
// reference: https://github.com/gcc-mirror/gcc/blob/gcc-7_3_0-release/libstdc%2B%2B-v3/include/std/fstream#L270
120+
121+
static std::string openmodeToStr(std::ios_base::openmode mode)
122+
{
123+
switch (mode & ~std::ios_base::ate) {
124+
case std::ios_base::out:
125+
case std::ios_base::out | std::ios_base::trunc:
126+
return "w";
127+
case std::ios_base::out | std::ios_base::app:
128+
case std::ios_base::app:
129+
return "a";
130+
case std::ios_base::in:
131+
return "r";
132+
case std::ios_base::in | std::ios_base::out:
133+
return "r+";
134+
case std::ios_base::in | std::ios_base::out | std::ios_base::trunc:
135+
return "w+";
136+
case std::ios_base::in | std::ios_base::out | std::ios_base::app:
137+
case std::ios_base::in | std::ios_base::app:
138+
return "a+";
139+
case std::ios_base::out | std::ios_base::binary:
140+
case std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
141+
return "wb";
142+
case std::ios_base::out | std::ios_base::app | std::ios_base::binary:
143+
case std::ios_base::app | std::ios_base::binary:
144+
return "ab";
145+
case std::ios_base::in | std::ios_base::binary:
146+
return "rb";
147+
case std::ios_base::in | std::ios_base::out | std::ios_base::binary:
148+
return "r+b";
149+
case std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::binary:
150+
return "w+b";
151+
case std::ios_base::in | std::ios_base::out | std::ios_base::app | std::ios_base::binary:
152+
case std::ios_base::in | std::ios_base::app | std::ios_base::binary:
153+
return "a+b";
154+
default:
155+
return std::string();
156+
}
157+
}
158+
159+
void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
160+
{
161+
close();
162+
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
163+
if (m_file == nullptr) {
164+
return;
165+
}
166+
m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
167+
rdbuf(&m_filebuf);
168+
if (mode & std::ios_base::ate) {
169+
seekg(0, std::ios_base::end);
170+
}
171+
}
172+
173+
void ifstream::close()
174+
{
175+
if (m_file != nullptr) {
176+
m_filebuf.close();
177+
fclose(m_file);
178+
}
179+
m_file = nullptr;
180+
}
181+
182+
void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
183+
{
184+
close();
185+
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
186+
if (m_file == nullptr) {
187+
return;
188+
}
189+
m_filebuf = __gnu_cxx::stdio_filebuf<char>(m_file, mode);
190+
rdbuf(&m_filebuf);
191+
if (mode & std::ios_base::ate) {
192+
seekp(0, std::ios_base::end);
193+
}
194+
}
195+
196+
void ofstream::close()
197+
{
198+
if (m_file != nullptr) {
199+
m_filebuf.close();
200+
fclose(m_file);
201+
}
202+
m_file = nullptr;
203+
}
204+
#else // __GLIBCXX__
205+
206+
static_assert(sizeof(*fs::path().BOOST_FILESYSTEM_C_STR) == sizeof(wchar_t),
207+
"Warning: This build is using boost::filesystem ofstream and ifstream "
208+
"implementations which will fail to open paths containing multibyte "
209+
"characters. You should delete this static_assert to ignore this warning, "
210+
"or switch to a different C++ standard library like the Microsoft C++ "
211+
"Standard Library (where boost uses non-standard extensions to construct "
212+
"stream objects with wide filenames), or the GNU libstdc++ library (where "
213+
"a more complicated workaround has been implemented above).");
214+
215+
#endif // __GLIBCXX__
216+
#endif // WIN32
217+
116218
} // fsbridge

src/fs.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
#include <stdio.h>
99
#include <string>
10+
#if defined WIN32 && defined __GLIBCXX__
11+
#include <ext/stdio_filebuf.h>
12+
#endif
1013

1114
#include <boost/filesystem.hpp>
1215
#include <boost/filesystem/fstream.hpp>
@@ -39,6 +42,54 @@ namespace fsbridge {
3942
};
4043

4144
std::string get_filesystem_error_message(const fs::filesystem_error& e);
45+
46+
// GNU libstdc++ specific workaround for opening UTF-8 paths on Windows.
47+
//
48+
// On Windows, it is only possible to reliably access multibyte file paths through
49+
// `wchar_t` APIs, not `char` APIs. But because the C++ standard doesn't
50+
// require ifstream/ofstream `wchar_t` constructors, and the GNU library doesn't
51+
// provide them (in contrast to the Microsoft C++ library, see
52+
// https://stackoverflow.com/questions/821873/how-to-open-an-stdfstream-ofstream-or-ifstream-with-a-unicode-filename/822032#822032),
53+
// Boost is forced to fall back to `char` constructors which may not work properly.
54+
//
55+
// Work around this issue by creating stream objects with `_wfopen` in
56+
// combination with `__gnu_cxx::stdio_filebuf`. This workaround can be removed
57+
// with an upgrade to C++17, where streams can be constructed directly from
58+
// `std::filesystem::path` objects.
59+
60+
#if defined WIN32 && defined __GLIBCXX__
61+
class ifstream : public std::istream
62+
{
63+
public:
64+
ifstream() = default;
65+
explicit ifstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in) { open(p, mode); }
66+
~ifstream() { close(); }
67+
void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::in);
68+
bool is_open() { return m_filebuf.is_open(); }
69+
void close();
70+
71+
private:
72+
__gnu_cxx::stdio_filebuf<char> m_filebuf;
73+
FILE* m_file = nullptr;
74+
};
75+
class ofstream : public std::ostream
76+
{
77+
public:
78+
ofstream() = default;
79+
explicit ofstream(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out) { open(p, mode); }
80+
~ofstream() { close(); }
81+
void open(const fs::path& p, std::ios_base::openmode mode = std::ios_base::out);
82+
bool is_open() { return m_filebuf.is_open(); }
83+
void close();
84+
85+
private:
86+
__gnu_cxx::stdio_filebuf<char> m_filebuf;
87+
FILE* m_file = nullptr;
88+
};
89+
#else // !(WIN32 && __GLIBCXX__)
90+
typedef fs::ifstream ifstream;
91+
typedef fs::ofstream ofstream;
92+
#endif // WIN32 && __GLIBCXX__
4293
};
4394

4495
#endif // BITCOIN_FS_H

0 commit comments

Comments
 (0)