duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

os.cc (11533B)


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