os.cc 11 KB

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