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