• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
convert_rwcount(std::size_t count)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 
is_whitespace(wchar_t c)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:
system_message(unsigned long error_code)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   }
~system_message()106   ~system_message() { LocalFree(message_); }
operator bool() const107   explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const108   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:
name() const115   const char* name() const noexcept override { return "system"; }
message(int error_code) const116   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 
system_category()130 FMT_API const std::error_category& system_category() noexcept {
131   static const detail::utf8_system_category category;
132   return category;
133 }
134 
vwindows_error(int err_code,string_view format_str,format_args args)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 
format_windows_error(detail::buffer<char> & out,int error_code,const char * message)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 
report_windows_error(int error_code,const char * message)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 
~buffered_file()163 buffered_file::~buffered_file() noexcept {
164   if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
165     report_system_error(errno, "cannot close file");
166 }
167 
buffered_file(cstring_view filename,cstring_view mode)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 
close()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 
descriptor() const184 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 
file(cstring_view path,int oflag)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 
~file()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 
close()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 
size() const236 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 
read(void * buffer,std::size_t count)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 
write(const void * buffer,std::size_t count)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 
dup(int fd)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 
dup2(int fd)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 
dup2(int fd,std::error_code & ec)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 
pipe(file & read_end,file & write_end)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 
fdopen(const char * mode)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__)
open_windows_file(wcstring_view path,int oflag)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__)
getpagesize()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 
grow(size_t)377 void file_buffer::grow(size_t) {
378   if (this->size() == this->capacity()) flush();
379 }
380 
file_buffer(cstring_view path,const detail::ostream_params & params)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 
file_buffer(file_buffer && other)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 
~file_buffer()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
403