/ externals / fmt / src / os.cc
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  
 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