You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
11 KiB

  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. // Disable bogus MSVC warnings.
  8. #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
  9. # define _CRT_SECURE_NO_WARNINGS
  10. #endif
  11. #include "fmt/os.h"
  12. #include <climits>
  13. #if FMT_USE_FCNTL
  14. # include <sys/stat.h>
  15. # include <sys/types.h>
  16. # ifndef _WIN32
  17. # include <unistd.h>
  18. # else
  19. # ifndef WIN32_LEAN_AND_MEAN
  20. # define WIN32_LEAN_AND_MEAN
  21. # endif
  22. # include <io.h>
  23. # ifndef S_IRUSR
  24. # define S_IRUSR _S_IREAD
  25. # endif
  26. # ifndef S_IWUSR
  27. # define S_IWUSR _S_IWRITE
  28. # endif
  29. # ifndef S_IRGRP
  30. # define S_IRGRP 0
  31. # endif
  32. # ifndef S_IWGRP
  33. # define S_IWGRP 0
  34. # endif
  35. # ifndef S_IROTH
  36. # define S_IROTH 0
  37. # endif
  38. # ifndef S_IWOTH
  39. # define S_IWOTH 0
  40. # endif
  41. # endif // _WIN32
  42. #endif // FMT_USE_FCNTL
  43. #ifdef _WIN32
  44. # include <windows.h>
  45. #endif
  46. namespace {
  47. #ifdef _WIN32
  48. // Return type of read and write functions.
  49. using rwresult = int;
  50. // On Windows the count argument to read and write is unsigned, so convert
  51. // it from size_t preventing integer overflow.
  52. inline unsigned convert_rwcount(std::size_t count) {
  53. return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
  54. }
  55. #elif FMT_USE_FCNTL
  56. // Return type of read and write functions.
  57. using rwresult = ssize_t;
  58. inline std::size_t convert_rwcount(std::size_t count) { return count; }
  59. #endif
  60. } // namespace
  61. FMT_BEGIN_NAMESPACE
  62. #ifdef _WIN32
  63. detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
  64. if (int error_code = convert(s)) {
  65. FMT_THROW(windows_error(error_code,
  66. "cannot convert string from UTF-16 to UTF-8"));
  67. }
  68. }
  69. int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
  70. if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
  71. int s_size = static_cast<int>(s.size());
  72. if (s_size == 0) {
  73. // WideCharToMultiByte does not support zero length, handle separately.
  74. buffer_.resize(1);
  75. buffer_[0] = 0;
  76. return 0;
  77. }
  78. int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
  79. nullptr, nullptr);
  80. if (length == 0) return GetLastError();
  81. buffer_.resize(length + 1);
  82. length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
  83. length, nullptr, nullptr);
  84. if (length == 0) return GetLastError();
  85. buffer_[length] = 0;
  86. return 0;
  87. }
  88. namespace detail {
  89. class system_message {
  90. system_message(const system_message&) = delete;
  91. void operator=(const system_message&) = delete;
  92. unsigned long result_;
  93. wchar_t* message_;
  94. static bool is_whitespace(wchar_t c) noexcept {
  95. return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
  96. }
  97. public:
  98. explicit system_message(unsigned long error_code)
  99. : result_(0), message_(nullptr) {
  100. result_ = FormatMessageW(
  101. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
  102. FORMAT_MESSAGE_IGNORE_INSERTS,
  103. nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  104. reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
  105. if (result_ != 0) {
  106. while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
  107. --result_;
  108. }
  109. }
  110. }
  111. ~system_message() { LocalFree(message_); }
  112. explicit operator bool() const noexcept { return result_ != 0; }
  113. operator basic_string_view<wchar_t>() const noexcept {
  114. return basic_string_view<wchar_t>(message_, result_);
  115. }
  116. };
  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. system_message msg(error_code);
  122. if (msg) {
  123. utf16_to_utf8 utf8_message;
  124. if (utf8_message.convert(msg) == ERROR_SUCCESS) {
  125. return utf8_message.str();
  126. }
  127. }
  128. return "unknown error";
  129. }
  130. };
  131. } // namespace detail
  132. FMT_API const std::error_category& system_category() noexcept {
  133. static const detail::utf8_system_category category;
  134. return category;
  135. }
  136. std::system_error vwindows_error(int err_code, string_view format_str,
  137. format_args args) {
  138. auto ec = std::error_code(err_code, system_category());
  139. return std::system_error(ec, vformat(format_str, args));
  140. }
  141. void detail::format_windows_error(detail::buffer<char>& out, int error_code,
  142. const char* message) noexcept {
  143. FMT_TRY {
  144. system_message msg(error_code);
  145. if (msg) {
  146. utf16_to_utf8 utf8_message;
  147. if (utf8_message.convert(msg) == ERROR_SUCCESS) {
  148. fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
  149. return;
  150. }
  151. }
  152. }
  153. FMT_CATCH(...) {}
  154. format_error_code(out, error_code, message);
  155. }
  156. void report_windows_error(int error_code, const char* message) noexcept {
  157. report_error(detail::format_windows_error, error_code, message);
  158. }
  159. #endif // _WIN32
  160. buffered_file::~buffered_file() noexcept {
  161. if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
  162. report_system_error(errno, "cannot close file");
  163. }
  164. buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
  165. FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
  166. nullptr);
  167. if (!file_)
  168. FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
  169. }
  170. void buffered_file::close() {
  171. if (!file_) return;
  172. int result = FMT_SYSTEM(fclose(file_));
  173. file_ = nullptr;
  174. if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
  175. }
  176. int buffered_file::descriptor() const {
  177. int fd = FMT_POSIX_CALL(fileno(file_));
  178. if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
  179. return fd;
  180. }
  181. #if FMT_USE_FCNTL
  182. file::file(cstring_view path, int oflag) {
  183. # ifdef _WIN32
  184. using mode_t = int;
  185. # endif
  186. constexpr mode_t mode =
  187. S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
  188. # if defined(_WIN32) && !defined(__MINGW32__)
  189. fd_ = -1;
  190. FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
  191. # else
  192. FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
  193. # endif
  194. if (fd_ == -1)
  195. FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
  196. }
  197. file::~file() noexcept {
  198. // Don't retry close in case of EINTR!
  199. // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
  200. if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
  201. report_system_error(errno, "cannot close file");
  202. }
  203. void file::close() {
  204. if (fd_ == -1) return;
  205. // Don't retry close in case of EINTR!
  206. // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
  207. int result = FMT_POSIX_CALL(close(fd_));
  208. fd_ = -1;
  209. if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
  210. }
  211. long long file::size() const {
  212. # ifdef _WIN32
  213. // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
  214. // is less than 0x0500 as is the case with some default MinGW builds.
  215. // Both functions support large file sizes.
  216. DWORD size_upper = 0;
  217. HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
  218. DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
  219. if (size_lower == INVALID_FILE_SIZE) {
  220. DWORD error = GetLastError();
  221. if (error != NO_ERROR)
  222. FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
  223. }
  224. unsigned long long long_size = size_upper;
  225. return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
  226. # else
  227. using Stat = struct stat;
  228. Stat file_stat = Stat();
  229. if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
  230. FMT_THROW(system_error(errno, "cannot get file attributes"));
  231. static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
  232. "return type of file::size is not large enough");
  233. return file_stat.st_size;
  234. # endif
  235. }
  236. std::size_t file::read(void* buffer, std::size_t count) {
  237. rwresult result = 0;
  238. FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
  239. if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
  240. return detail::to_unsigned(result);
  241. }
  242. std::size_t file::write(const void* buffer, std::size_t count) {
  243. rwresult result = 0;
  244. FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
  245. if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
  246. return detail::to_unsigned(result);
  247. }
  248. file file::dup(int fd) {
  249. // Don't retry as dup doesn't return EINTR.
  250. // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
  251. int new_fd = FMT_POSIX_CALL(dup(fd));
  252. if (new_fd == -1)
  253. FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
  254. return file(new_fd);
  255. }
  256. void file::dup2(int fd) {
  257. int result = 0;
  258. FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
  259. if (result == -1) {
  260. FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
  261. fd_, fd));
  262. }
  263. }
  264. void file::dup2(int fd, std::error_code& ec) noexcept {
  265. int result = 0;
  266. FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
  267. if (result == -1) ec = std::error_code(errno, std::generic_category());
  268. }
  269. void file::pipe(file& read_end, file& write_end) {
  270. // Close the descriptors first to make sure that assignments don't throw
  271. // and there are no leaks.
  272. read_end.close();
  273. write_end.close();
  274. int fds[2] = {};
  275. # ifdef _WIN32
  276. // Make the default pipe capacity same as on Linux 2.6.11+.
  277. enum { DEFAULT_CAPACITY = 65536 };
  278. int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
  279. # else
  280. // Don't retry as the pipe function doesn't return EINTR.
  281. // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
  282. int result = FMT_POSIX_CALL(pipe(fds));
  283. # endif
  284. if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
  285. // The following assignments don't throw because read_fd and write_fd
  286. // are closed.
  287. read_end = file(fds[0]);
  288. write_end = file(fds[1]);
  289. }
  290. buffered_file file::fdopen(const char* mode) {
  291. // Don't retry as fdopen doesn't return EINTR.
  292. # if defined(__MINGW32__) && defined(_POSIX_)
  293. FILE* f = ::fdopen(fd_, mode);
  294. # else
  295. FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
  296. # endif
  297. if (!f)
  298. FMT_THROW(
  299. system_error(errno, "cannot associate stream with file descriptor"));
  300. buffered_file bf(f);
  301. fd_ = -1;
  302. return bf;
  303. }
  304. long getpagesize() {
  305. # ifdef _WIN32
  306. SYSTEM_INFO si;
  307. GetSystemInfo(&si);
  308. return si.dwPageSize;
  309. # else
  310. long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
  311. if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
  312. return size;
  313. # endif
  314. }
  315. FMT_API void ostream::grow(size_t) {
  316. if (this->size() == this->capacity()) flush();
  317. }
  318. #endif // FMT_USE_FCNTL
  319. FMT_END_NAMESPACE