Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/foreign/zstr/strict_fstream.hpp
169678 views
1
#pragma once
2
3
#include <cassert>
4
#include <fstream>
5
#include <cstring>
6
#include <string>
7
#include <vector>
8
9
/**
10
* This namespace defines wrappers for std::ifstream, std::ofstream, and
11
* std::fstream objects. The wrappers perform the following steps:
12
* - check the open modes make sense
13
* - check that the call to open() is successful
14
* - (for input streams) check that the opened file is peek-able
15
* - turn on the badbit in the exception mask
16
*/
17
namespace strict_fstream
18
{
19
20
// Help people out a bit, it seems like this is a common recommenation since
21
// musl breaks all over the place.
22
#if defined(__NEED_size_t) && !defined(__MUSL__)
23
#warning "It seems to be recommended to patch in a define for __MUSL__ if you use musl globally: https://www.openwall.com/lists/musl/2013/02/10/5"
24
#define __MUSL__
25
#endif
26
27
// Workaround for broken musl implementation
28
// Since musl insists that they are perfectly compatible, ironically enough,
29
// they don't officially have a __musl__ or similar. But __NEED_size_t is defined in their
30
// relevant header (and not in working implementations), so we can use that.
31
#ifdef __MUSL__
32
#warning "Working around broken strerror_r() implementation in musl, remove when musl is fixed"
33
#endif
34
35
// Non-gnu variants of strerror_* don't necessarily null-terminate if
36
// truncating, so we have to do things manually.
37
inline std::string trim_to_null(const std::vector<char> &buff)
38
{
39
std::string ret(buff.begin(), buff.end());
40
41
const std::string::size_type pos = ret.find('\0');
42
if (pos == std::string::npos) {
43
ret += " [...]"; // it has been truncated
44
} else {
45
ret.resize(pos);
46
}
47
return ret;
48
}
49
50
/// Overload of error-reporting function, to enable use with VS and non-GNU
51
/// POSIX libc's
52
/// Ref:
53
/// - http://stackoverflow.com/a/901316/717706
54
static std::string strerror()
55
{
56
// Can't use std::string since we're pre-C++17
57
std::vector<char> buff(256, '\0');
58
59
#ifdef _WIN32
60
// Since strerror_s might set errno itself, we need to store it.
61
const int err_num = errno;
62
if (strerror_s(buff.data(), buff.size(), err_num) != 0) {
63
return trim_to_null(buff);
64
} else {
65
return "Unknown error (" + std::to_string(err_num) + ")";
66
}
67
#elif ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || defined(__APPLE__) || defined(__FreeBSD__)) && ! _GNU_SOURCE) || defined(__MUSL__) || defined(__ANDROID_API__)
68
// XSI-compliant strerror_r()
69
const int err_num = errno; // See above
70
if (strerror_r(err_num, buff.data(), buff.size()) == 0) {
71
return trim_to_null(buff);
72
} else {
73
return "Unknown error (" + std::to_string(err_num) + ")";
74
}
75
#else
76
// GNU-specific strerror_r()
77
char * p = strerror_r(errno, &buff[0], buff.size());
78
return std::string(p, std::strlen(p));
79
#endif
80
}
81
82
/// Exception class thrown by failed operations.
83
class Exception
84
: public std::exception
85
{
86
public:
87
Exception(const std::string& msg) : _msg(msg) {}
88
const char * what() const noexcept { return _msg.c_str(); }
89
private:
90
std::string _msg;
91
}; // class Exception
92
93
namespace detail
94
{
95
96
struct static_method_holder
97
{
98
static std::string mode_to_string(std::ios_base::openmode mode)
99
{
100
static const int n_modes = 6;
101
static const std::ios_base::openmode mode_val_v[n_modes] =
102
{
103
std::ios_base::in,
104
std::ios_base::out,
105
std::ios_base::app,
106
std::ios_base::ate,
107
std::ios_base::trunc,
108
std::ios_base::binary
109
};
110
111
static const char * mode_name_v[n_modes] =
112
{
113
"in",
114
"out",
115
"app",
116
"ate",
117
"trunc",
118
"binary"
119
};
120
std::string res;
121
for (int i = 0; i < n_modes; ++i)
122
{
123
if (mode & mode_val_v[i])
124
{
125
res += (! res.empty()? "|" : "");
126
res += mode_name_v[i];
127
}
128
}
129
if (res.empty()) res = "none";
130
return res;
131
}
132
static void check_mode(const std::string& filename, std::ios_base::openmode mode)
133
{
134
if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out))
135
{
136
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out");
137
}
138
else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out))
139
{
140
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out");
141
}
142
else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app))
143
{
144
throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app");
145
}
146
}
147
static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode)
148
{
149
if (s_p->fail())
150
{
151
throw Exception(std::string("strict_fstream: open('")
152
+ filename + "'," + mode_to_string(mode) + "): open failed: "
153
+ strerror());
154
}
155
}
156
static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode)
157
{
158
bool peek_failed = true;
159
try
160
{
161
is_p->peek();
162
peek_failed = is_p->fail();
163
}
164
catch (const std::ios_base::failure &) {}
165
if (peek_failed)
166
{
167
throw Exception(std::string("strict_fstream: open('")
168
+ filename + "'," + mode_to_string(mode) + "): peek failed: "
169
+ strerror());
170
}
171
is_p->clear();
172
}
173
}; // struct static_method_holder
174
175
} // namespace detail
176
177
class ifstream
178
: public std::ifstream
179
{
180
public:
181
ifstream() = default;
182
ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
183
{
184
open(filename, mode);
185
}
186
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
187
{
188
mode |= std::ios_base::in;
189
exceptions(std::ios_base::badbit);
190
detail::static_method_holder::check_mode(filename, mode);
191
std::ifstream::open(filename, mode);
192
detail::static_method_holder::check_open(this, filename, mode);
193
detail::static_method_holder::check_peek(this, filename, mode);
194
}
195
}; // class ifstream
196
197
class ofstream
198
: public std::ofstream
199
{
200
public:
201
ofstream() = default;
202
ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
203
{
204
open(filename, mode);
205
}
206
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
207
{
208
mode |= std::ios_base::out;
209
exceptions(std::ios_base::badbit);
210
detail::static_method_holder::check_mode(filename, mode);
211
std::ofstream::open(filename, mode);
212
detail::static_method_holder::check_open(this, filename, mode);
213
}
214
}; // class ofstream
215
216
class fstream
217
: public std::fstream
218
{
219
public:
220
fstream() = default;
221
fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
222
{
223
open(filename, mode);
224
}
225
void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
226
{
227
if (! (mode & std::ios_base::out)) mode |= std::ios_base::in;
228
exceptions(std::ios_base::badbit);
229
detail::static_method_holder::check_mode(filename, mode);
230
std::fstream::open(filename, mode);
231
detail::static_method_holder::check_open(this, filename, mode);
232
detail::static_method_holder::check_peek(this, filename, mode);
233
}
234
}; // class fstream
235
236
} // namespace strict_fstream
237
238