RavEngine
Loading...
Searching...
No Matches
os.cc
1// Formatting library for C++ - optional OS-specific functionality
2//
3// Copyright (c) 2012 - 2016, Victor Zverovich
4// All rights reserved.
5//
6// For the license information refer to format.h.
7
8// Disable bogus MSVC warnings.
9#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
10# define _CRT_SECURE_NO_WARNINGS
11#endif
12
13#include "fmt/os.h"
14
15#include <climits>
16
17#if FMT_USE_FCNTL
18# include <sys/stat.h>
19# include <sys/types.h>
20
21# ifdef _WRS_KERNEL // VxWorks7 kernel
22# include <ioLib.h> // getpagesize
23# endif
24
25# ifndef _WIN32
26# include <unistd.h>
27# else
28# ifndef WIN32_LEAN_AND_MEAN
29# define WIN32_LEAN_AND_MEAN
30# endif
31# include <io.h>
32
33# ifndef S_IRUSR
34# define S_IRUSR _S_IREAD
35# endif
36# ifndef S_IWUSR
37# define S_IWUSR _S_IWRITE
38# endif
39# ifndef S_IRGRP
40# define S_IRGRP 0
41# endif
42# ifndef S_IWGRP
43# define S_IWGRP 0
44# endif
45# ifndef S_IROTH
46# define S_IROTH 0
47# endif
48# ifndef S_IWOTH
49# define S_IWOTH 0
50# endif
51# endif // _WIN32
52#endif // FMT_USE_FCNTL
53
54#ifdef _WIN32
55# include <windows.h>
56#endif
57
58namespace {
59#ifdef _WIN32
60// Return type of read and write functions.
61using rwresult = int;
62
63// On Windows the count argument to read and write is unsigned, so convert
64// it from size_t preventing integer overflow.
65inline unsigned convert_rwcount(std::size_t count) {
66 return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
67}
68#elif FMT_USE_FCNTL
69// Return type of read and write functions.
70using rwresult = ssize_t;
71
72inline std::size_t convert_rwcount(std::size_t count) { return count; }
73#endif
74} // namespace
75
76FMT_BEGIN_NAMESPACE
77
78#ifdef _WIN32
79namespace detail {
80
81class system_message {
82 system_message(const system_message&) = delete;
83 void operator=(const system_message&) = delete;
84
85 unsigned long result_;
86 wchar_t* message_;
87
88 static bool is_whitespace(wchar_t c) noexcept {
89 return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
90 }
91
92 public:
93 explicit system_message(unsigned long error_code)
94 : result_(0), message_(nullptr) {
95 result_ = FormatMessageW(
96 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
97 FORMAT_MESSAGE_IGNORE_INSERTS,
98 nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
99 reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
100 if (result_ != 0) {
101 while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
102 --result_;
103 }
104 }
105 }
106 ~system_message() { LocalFree(message_); }
107 explicit operator bool() const noexcept { return result_ != 0; }
108 operator basic_string_view<wchar_t>() const noexcept {
109 return basic_string_view<wchar_t>(message_, result_);
110 }
111};
112
113class utf8_system_category final : public std::error_category {
114 public:
115 const char* name() const noexcept override { return "system"; }
116 std::string message(int error_code) const override {
117 auto&& msg = system_message(error_code);
118 if (msg) {
119 auto utf8_message = to_utf8<wchar_t>();
120 if (utf8_message.convert(msg)) {
121 return utf8_message.str();
122 }
123 }
124 return "unknown error";
125 }
126};
127
128} // namespace detail
129
130FMT_API const std::error_category& system_category() noexcept {
131 static const detail::utf8_system_category category;
132 return category;
133}
134
135std::system_error vwindows_error(int err_code, string_view format_str,
136 format_args args) {
137 auto ec = std::error_code(err_code, system_category());
138 return std::system_error(ec, vformat(format_str, args));
139}
140
141void detail::format_windows_error(detail::buffer<char>& out, int error_code,
142 const char* message) noexcept {
143 FMT_TRY {
144 auto&& msg = system_message(error_code);
145 if (msg) {
146 auto utf8_message = to_utf8<wchar_t>();
147 if (utf8_message.convert(msg)) {
148 fmt::format_to(appender(out), FMT_STRING("{}: {}"), message,
149 string_view(utf8_message));
150 return;
151 }
152 }
153 }
154 FMT_CATCH(...) {}
155 format_error_code(out, error_code, message);
156}
157
158void report_windows_error(int error_code, const char* message) noexcept {
159 report_error(detail::format_windows_error, error_code, message);
160}
161#endif // _WIN32
162
163buffered_file::~buffered_file() noexcept {
164 if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
165 report_system_error(errno, "cannot close file");
166}
167
168buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
169 FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
170 nullptr);
171 if (!file_)
172 FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"),
173 filename.c_str()));
174}
175
176void buffered_file::close() {
177 if (!file_) return;
178 int result = FMT_SYSTEM(fclose(file_));
179 file_ = nullptr;
180 if (result != 0)
181 FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
182}
183
184int buffered_file::descriptor() const {
185#ifdef FMT_HAS_SYSTEM
186 // fileno is a macro on OpenBSD.
187# ifdef fileno
188# undef fileno
189# endif
190 int fd = FMT_POSIX_CALL(fileno(file_));
191#elif defined(_WIN32)
192 int fd = _fileno(file_);
193#else
194 int fd = fileno(file_);
195#endif
196 if (fd == -1)
197 FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
198 return fd;
199}
200
201#if FMT_USE_FCNTL
202# ifdef _WIN32
203using mode_t = int;
204# endif
205
206constexpr mode_t default_open_mode =
207 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
208
209file::file(cstring_view path, int oflag) {
210# if defined(_WIN32) && !defined(__MINGW32__)
211 fd_ = -1;
212 auto converted = detail::utf8_to_utf16(string_view(path.c_str()));
213 *this = file::open_windows_file(converted.c_str(), oflag);
214# else
215 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode)));
216 if (fd_ == -1)
217 FMT_THROW(
218 system_error(errno, FMT_STRING("cannot open file {}"), path.c_str()));
219# endif
220}
221
222file::~file() noexcept {
223 // Don't retry close in case of EINTR!
224 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
225 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
226 report_system_error(errno, "cannot close file");
227}
228
229void file::close() {
230 if (fd_ == -1) return;
231 // Don't retry close in case of EINTR!
232 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
233 int result = FMT_POSIX_CALL(close(fd_));
234 fd_ = -1;
235 if (result != 0)
236 FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
237}
238
239long long file::size() const {
240# ifdef _WIN32
241 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
242 // is less than 0x0500 as is the case with some default MinGW builds.
243 // Both functions support large file sizes.
244 DWORD size_upper = 0;
245 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
246 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
247 if (size_lower == INVALID_FILE_SIZE) {
248 DWORD error = GetLastError();
249 if (error != NO_ERROR)
250 FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
251 }
252 unsigned long long long_size = size_upper;
253 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
254# else
255 using Stat = struct stat;
256 Stat file_stat = Stat();
257 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
258 FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes")));
259 static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
260 "return type of file::size is not large enough");
261 return file_stat.st_size;
262# endif
263}
264
265std::size_t file::read(void* buffer, std::size_t count) {
266 rwresult result = 0;
267 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
268 if (result < 0)
269 FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
270 return detail::to_unsigned(result);
271}
272
273std::size_t file::write(const void* buffer, std::size_t count) {
274 rwresult result = 0;
275 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
276 if (result < 0)
277 FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
278 return detail::to_unsigned(result);
279}
280
281file file::dup(int fd) {
282 // Don't retry as dup doesn't return EINTR.
283 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
284 int new_fd = FMT_POSIX_CALL(dup(fd));
285 if (new_fd == -1)
286 FMT_THROW(system_error(
287 errno, FMT_STRING("cannot duplicate file descriptor {}"), fd));
288 return file(new_fd);
289}
290
291void file::dup2(int fd) {
292 int result = 0;
293 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
294 if (result == -1) {
295 FMT_THROW(system_error(
296 errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_,
297 fd));
298 }
299}
300
301void file::dup2(int fd, std::error_code& ec) noexcept {
302 int result = 0;
303 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
304 if (result == -1) ec = std::error_code(errno, std::generic_category());
305}
306
307buffered_file file::fdopen(const char* mode) {
308// Don't retry as fdopen doesn't return EINTR.
309# if defined(__MINGW32__) && defined(_POSIX_)
310 FILE* f = ::fdopen(fd_, mode);
311# else
312 FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
313# endif
314 if (!f) {
315 FMT_THROW(system_error(
316 errno, FMT_STRING("cannot associate stream with file descriptor")));
317 }
318 buffered_file bf(f);
319 fd_ = -1;
320 return bf;
321}
322
323# if defined(_WIN32) && !defined(__MINGW32__)
324file file::open_windows_file(wcstring_view path, int oflag) {
325 int fd = -1;
326 auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
327 if (fd == -1) {
328 FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"),
329 detail::to_utf8<wchar_t>(path.c_str()).c_str()));
330 }
331 return file(fd);
332}
333# endif
334
335pipe::pipe() {
336 int fds[2] = {};
337# ifdef _WIN32
338 // Make the default pipe capacity same as on Linux 2.6.11+.
339 enum { DEFAULT_CAPACITY = 65536 };
340 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
341# else
342 // Don't retry as the pipe function doesn't return EINTR.
343 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
344 int result = FMT_POSIX_CALL(pipe(fds));
345# endif
346 if (result != 0)
347 FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
348 // The following assignments don't throw.
349 read_end = file(fds[0]);
350 write_end = file(fds[1]);
351}
352
353# if !defined(__MSDOS__)
354long getpagesize() {
355# ifdef _WIN32
356 SYSTEM_INFO si;
357 GetSystemInfo(&si);
358 return si.dwPageSize;
359# else
360# ifdef _WRS_KERNEL
361 long size = FMT_POSIX_CALL(getpagesize());
362# else
363 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
364# endif
365
366 if (size < 0)
367 FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
368 return size;
369# endif
370}
371# endif
372
373namespace detail {
374
375void file_buffer::grow(buffer<char>& buf, size_t) {
376 if (buf.size() == buf.capacity()) static_cast<file_buffer&>(buf).flush();
377}
378
379file_buffer::file_buffer(cstring_view path, const ostream_params& params)
380 : buffer<char>(grow), file_(path, params.oflag) {
381 set(new char[params.buffer_size], params.buffer_size);
382}
383
384file_buffer::file_buffer(file_buffer&& other) noexcept
385 : buffer<char>(grow, other.data(), other.size(), other.capacity()),
386 file_(std::move(other.file_)) {
387 other.clear();
388 other.set(nullptr, 0);
389}
390
391file_buffer::~file_buffer() {
392 flush();
393 delete[] data();
394}
395} // namespace detail
396
397ostream::~ostream() = default;
398#endif // FMT_USE_FCNTL
399FMT_END_NAMESPACE
Definition os.h:106
auto c_str() const -> const Char *
Definition os.h:122
Definition base.h:1827
Definition base.h:484
Definition os.h:191
Definition base.h:838
Definition format.h:1381
Definition format.h:1366