Mercurial > minori
comparison dep/fmt/src/os.cc @ 329:4aeffed717ef
dep/fmt: add dependency
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Mon, 17 Jun 2024 04:54:44 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 328:71396ecb6f7e | 329:4aeffed717ef |
|---|---|
| 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 | |
| 58 namespace { | |
| 59 #ifdef _WIN32 | |
| 60 // Return type of read and write functions. | |
| 61 using 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. | |
| 65 inline 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. | |
| 70 using rwresult = ssize_t; | |
| 71 | |
| 72 inline std::size_t convert_rwcount(std::size_t count) { return count; } | |
| 73 #endif | |
| 74 } // namespace | |
| 75 | |
| 76 FMT_BEGIN_NAMESPACE | |
| 77 | |
| 78 #ifdef _WIN32 | |
| 79 namespace detail { | |
| 80 | |
| 81 class 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 | |
| 113 class 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 | |
| 130 FMT_API const std::error_category& system_category() noexcept { | |
| 131 static const detail::utf8_system_category category; | |
| 132 return category; | |
| 133 } | |
| 134 | |
| 135 std::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 | |
| 141 void 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 | |
| 158 void 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 | |
| 163 buffered_file::~buffered_file() noexcept { | |
| 164 if (file_ && FMT_SYSTEM(fclose(file_)) != 0) | |
| 165 report_system_error(errno, "cannot close file"); | |
| 166 } | |
| 167 | |
| 168 buffered_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 | |
| 176 void 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 | |
| 184 int buffered_file::descriptor() const { | |
| 185 #if !defined(fileno) | |
| 186 int fd = FMT_POSIX_CALL(fileno(file_)); | |
| 187 #elif defined(FMT_HAS_SYSTEM) | |
| 188 // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. | |
| 189 # define FMT_DISABLE_MACRO | |
| 190 int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_)); | |
| 191 #else | |
| 192 int fd = fileno(file_); | |
| 193 #endif | |
| 194 if (fd == -1) | |
| 195 FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor"))); | |
| 196 return fd; | |
| 197 } | |
| 198 | |
| 199 #if FMT_USE_FCNTL | |
| 200 # ifdef _WIN32 | |
| 201 using mode_t = int; | |
| 202 # endif | |
| 203 constexpr mode_t default_open_mode = | |
| 204 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | |
| 205 | |
| 206 file::file(cstring_view path, int oflag) { | |
| 207 # if defined(_WIN32) && !defined(__MINGW32__) | |
| 208 fd_ = -1; | |
| 209 auto converted = detail::utf8_to_utf16(string_view(path.c_str())); | |
| 210 *this = file::open_windows_file(converted.c_str(), oflag); | |
| 211 # else | |
| 212 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode))); | |
| 213 if (fd_ == -1) | |
| 214 FMT_THROW( | |
| 215 system_error(errno, FMT_STRING("cannot open file {}"), path.c_str())); | |
| 216 # endif | |
| 217 } | |
| 218 | |
| 219 file::~file() noexcept { | |
| 220 // Don't retry close in case of EINTR! | |
| 221 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |
| 222 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) | |
| 223 report_system_error(errno, "cannot close file"); | |
| 224 } | |
| 225 | |
| 226 void file::close() { | |
| 227 if (fd_ == -1) return; | |
| 228 // Don't retry close in case of EINTR! | |
| 229 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |
| 230 int result = FMT_POSIX_CALL(close(fd_)); | |
| 231 fd_ = -1; | |
| 232 if (result != 0) | |
| 233 FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); | |
| 234 } | |
| 235 | |
| 236 long long file::size() const { | |
| 237 # ifdef _WIN32 | |
| 238 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT | |
| 239 // is less than 0x0500 as is the case with some default MinGW builds. | |
| 240 // Both functions support large file sizes. | |
| 241 DWORD size_upper = 0; | |
| 242 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); | |
| 243 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); | |
| 244 if (size_lower == INVALID_FILE_SIZE) { | |
| 245 DWORD error = GetLastError(); | |
| 246 if (error != NO_ERROR) | |
| 247 FMT_THROW(windows_error(GetLastError(), "cannot get file size")); | |
| 248 } | |
| 249 unsigned long long long_size = size_upper; | |
| 250 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; | |
| 251 # else | |
| 252 using Stat = struct stat; | |
| 253 Stat file_stat = Stat(); | |
| 254 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) | |
| 255 FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes"))); | |
| 256 static_assert(sizeof(long long) >= sizeof(file_stat.st_size), | |
| 257 "return type of file::size is not large enough"); | |
| 258 return file_stat.st_size; | |
| 259 # endif | |
| 260 } | |
| 261 | |
| 262 std::size_t file::read(void* buffer, std::size_t count) { | |
| 263 rwresult result = 0; | |
| 264 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); | |
| 265 if (result < 0) | |
| 266 FMT_THROW(system_error(errno, FMT_STRING("cannot read from file"))); | |
| 267 return detail::to_unsigned(result); | |
| 268 } | |
| 269 | |
| 270 std::size_t file::write(const void* buffer, std::size_t count) { | |
| 271 rwresult result = 0; | |
| 272 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); | |
| 273 if (result < 0) | |
| 274 FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | |
| 275 return detail::to_unsigned(result); | |
| 276 } | |
| 277 | |
| 278 file file::dup(int fd) { | |
| 279 // Don't retry as dup doesn't return EINTR. | |
| 280 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html | |
| 281 int new_fd = FMT_POSIX_CALL(dup(fd)); | |
| 282 if (new_fd == -1) | |
| 283 FMT_THROW(system_error( | |
| 284 errno, FMT_STRING("cannot duplicate file descriptor {}"), fd)); | |
| 285 return file(new_fd); | |
| 286 } | |
| 287 | |
| 288 void file::dup2(int fd) { | |
| 289 int result = 0; | |
| 290 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |
| 291 if (result == -1) { | |
| 292 FMT_THROW(system_error( | |
| 293 errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_, | |
| 294 fd)); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void file::dup2(int fd, std::error_code& ec) noexcept { | |
| 299 int result = 0; | |
| 300 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |
| 301 if (result == -1) ec = std::error_code(errno, std::generic_category()); | |
| 302 } | |
| 303 | |
| 304 void file::pipe(file& read_end, file& write_end) { | |
| 305 // Close the descriptors first to make sure that assignments don't throw | |
| 306 // and there are no leaks. | |
| 307 read_end.close(); | |
| 308 write_end.close(); | |
| 309 int fds[2] = {}; | |
| 310 # ifdef _WIN32 | |
| 311 // Make the default pipe capacity same as on Linux 2.6.11+. | |
| 312 enum { DEFAULT_CAPACITY = 65536 }; | |
| 313 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | |
| 314 # else | |
| 315 // Don't retry as the pipe function doesn't return EINTR. | |
| 316 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | |
| 317 int result = FMT_POSIX_CALL(pipe(fds)); | |
| 318 # endif | |
| 319 if (result != 0) | |
| 320 FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); | |
| 321 // The following assignments don't throw because read_fd and write_fd | |
| 322 // are closed. | |
| 323 read_end = file(fds[0]); | |
| 324 write_end = file(fds[1]); | |
| 325 } | |
| 326 | |
| 327 buffered_file file::fdopen(const char* mode) { | |
| 328 // Don't retry as fdopen doesn't return EINTR. | |
| 329 # if defined(__MINGW32__) && defined(_POSIX_) | |
| 330 FILE* f = ::fdopen(fd_, mode); | |
| 331 # else | |
| 332 FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); | |
| 333 # endif | |
| 334 if (!f) { | |
| 335 FMT_THROW(system_error( | |
| 336 errno, FMT_STRING("cannot associate stream with file descriptor"))); | |
| 337 } | |
| 338 buffered_file bf(f); | |
| 339 fd_ = -1; | |
| 340 return bf; | |
| 341 } | |
| 342 | |
| 343 # if defined(_WIN32) && !defined(__MINGW32__) | |
| 344 file file::open_windows_file(wcstring_view path, int oflag) { | |
| 345 int fd = -1; | |
| 346 auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode); | |
| 347 if (fd == -1) { | |
| 348 FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"), | |
| 349 detail::to_utf8<wchar_t>(path.c_str()).c_str())); | |
| 350 } | |
| 351 return file(fd); | |
| 352 } | |
| 353 # endif | |
| 354 | |
| 355 # if !defined(__MSDOS__) | |
| 356 long getpagesize() { | |
| 357 # ifdef _WIN32 | |
| 358 SYSTEM_INFO si; | |
| 359 GetSystemInfo(&si); | |
| 360 return si.dwPageSize; | |
| 361 # else | |
| 362 # ifdef _WRS_KERNEL | |
| 363 long size = FMT_POSIX_CALL(getpagesize()); | |
| 364 # else | |
| 365 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); | |
| 366 # endif | |
| 367 | |
| 368 if (size < 0) | |
| 369 FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size"))); | |
| 370 return size; | |
| 371 # endif | |
| 372 } | |
| 373 # endif | |
| 374 | |
| 375 namespace detail { | |
| 376 | |
| 377 void file_buffer::grow(size_t) { | |
| 378 if (this->size() == this->capacity()) flush(); | |
| 379 } | |
| 380 | |
| 381 file_buffer::file_buffer(cstring_view path, | |
| 382 const detail::ostream_params& params) | |
| 383 : file_(path, params.oflag) { | |
| 384 set(new char[params.buffer_size], params.buffer_size); | |
| 385 } | |
| 386 | |
| 387 file_buffer::file_buffer(file_buffer&& other) | |
| 388 : detail::buffer<char>(other.data(), other.size(), other.capacity()), | |
| 389 file_(std::move(other.file_)) { | |
| 390 other.clear(); | |
| 391 other.set(nullptr, 0); | |
| 392 } | |
| 393 | |
| 394 file_buffer::~file_buffer() { | |
| 395 flush(); | |
| 396 delete[] data(); | |
| 397 } | |
| 398 } // namespace detail | |
| 399 | |
| 400 ostream::~ostream() = default; | |
| 401 #endif // FMT_USE_FCNTL | |
| 402 FMT_END_NAMESPACE |
