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