Commit | Line | Data |
---|---|---|
05aa7e19 JG |
1 | // Formatting library for C++ - optional OS-specific functionality |
2 | // | |
3 | // Copyright (c) 2012 - present, Victor Zverovich | |
4 | // All rights reserved. | |
5 | // | |
6 | // For the license information refer to format.h. | |
7 | ||
8 | #ifndef FMT_OS_H_ | |
9 | #define FMT_OS_H_ | |
10 | ||
bd9231e4 | 11 | #include "format.h" |
05aa7e19 | 12 | |
bd9231e4 JG |
13 | #ifndef FMT_MODULE |
14 | # include <cerrno> | |
15 | # include <cstddef> | |
16 | # include <cstdio> | |
17 | # include <system_error> // std::system_error | |
05aa7e19 | 18 | |
bd9231e4 JG |
19 | # if FMT_HAS_INCLUDE(<xlocale.h>) |
20 | # include <xlocale.h> // LC_NUMERIC_MASK on macOS | |
21 | # endif | |
22 | #endif // FMT_MODULE | |
05aa7e19 JG |
23 | |
24 | #ifndef FMT_USE_FCNTL | |
25 | // UWP doesn't provide _pipe. | |
26 | # if FMT_HAS_INCLUDE("winapifamily.h") | |
27 | # include <winapifamily.h> | |
28 | # endif | |
29 | # if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ | |
30 | defined(__linux__)) && \ | |
31 | (!defined(WINAPI_FAMILY) || \ | |
32 | (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) | |
33 | # include <fcntl.h> // for O_RDONLY | |
34 | # define FMT_USE_FCNTL 1 | |
35 | # else | |
36 | # define FMT_USE_FCNTL 0 | |
37 | # endif | |
38 | #endif | |
39 | ||
40 | #ifndef FMT_POSIX | |
41 | # if defined(_WIN32) && !defined(__MINGW32__) | |
42 | // Fix warnings about deprecated symbols. | |
43 | # define FMT_POSIX(call) _##call | |
44 | # else | |
45 | # define FMT_POSIX(call) call | |
46 | # endif | |
47 | #endif | |
48 | ||
49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. | |
50 | #ifdef FMT_SYSTEM | |
bd9231e4 | 51 | # define FMT_HAS_SYSTEM |
05aa7e19 JG |
52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) |
53 | #else | |
54 | # define FMT_SYSTEM(call) ::call | |
55 | # ifdef _WIN32 | |
56 | // Fix warnings about deprecated symbols. | |
57 | # define FMT_POSIX_CALL(call) ::_##call | |
58 | # else | |
59 | # define FMT_POSIX_CALL(call) ::call | |
60 | # endif | |
61 | #endif | |
62 | ||
63 | // Retries the expression while it evaluates to error_result and errno | |
64 | // equals to EINTR. | |
65 | #ifndef _WIN32 | |
66 | # define FMT_RETRY_VAL(result, expression, error_result) \ | |
67 | do { \ | |
68 | (result) = (expression); \ | |
69 | } while ((result) == (error_result) && errno == EINTR) | |
70 | #else | |
71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) | |
72 | #endif | |
73 | ||
74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) | |
75 | ||
76 | FMT_BEGIN_NAMESPACE | |
bd9231e4 | 77 | FMT_BEGIN_EXPORT |
05aa7e19 JG |
78 | |
79 | /** | |
bd9231e4 JG |
80 | * A reference to a null-terminated string. It can be constructed from a C |
81 | * string or `std::string`. | |
82 | * | |
83 | * You can use one of the following type aliases for common character types: | |
84 | * | |
85 | * +---------------+-----------------------------+ | |
86 | * | Type | Definition | | |
87 | * +===============+=============================+ | |
88 | * | cstring_view | basic_cstring_view<char> | | |
89 | * +---------------+-----------------------------+ | |
90 | * | wcstring_view | basic_cstring_view<wchar_t> | | |
91 | * +---------------+-----------------------------+ | |
92 | * | |
93 | * This class is most useful as a parameter type for functions that wrap C APIs. | |
05aa7e19 JG |
94 | */ |
95 | template <typename Char> class basic_cstring_view { | |
96 | private: | |
97 | const Char* data_; | |
98 | ||
99 | public: | |
bd9231e4 | 100 | /// Constructs a string reference object from a C string. |
05aa7e19 JG |
101 | basic_cstring_view(const Char* s) : data_(s) {} |
102 | ||
bd9231e4 | 103 | /// Constructs a string reference from an `std::string` object. |
05aa7e19 JG |
104 | basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} |
105 | ||
bd9231e4 JG |
106 | /// Returns the pointer to a C string. |
107 | auto c_str() const -> const Char* { return data_; } | |
05aa7e19 JG |
108 | }; |
109 | ||
110 | using cstring_view = basic_cstring_view<char>; | |
111 | using wcstring_view = basic_cstring_view<wchar_t>; | |
112 | ||
05aa7e19 | 113 | #ifdef _WIN32 |
8b75cd77 | 114 | FMT_API const std::error_category& system_category() noexcept; |
05aa7e19 | 115 | |
bd9231e4 | 116 | namespace detail { |
05aa7e19 | 117 | FMT_API void format_windows_error(buffer<char>& out, int error_code, |
8b75cd77 | 118 | const char* message) noexcept; |
bd9231e4 | 119 | } |
05aa7e19 JG |
120 | |
121 | FMT_API std::system_error vwindows_error(int error_code, string_view format_str, | |
122 | format_args args); | |
123 | ||
124 | /** | |
bd9231e4 JG |
125 | * Constructs a `std::system_error` object with the description of the form |
126 | * | |
127 | * <message>: <system-message> | |
128 | * | |
129 | * where `<message>` is the formatted message and `<system-message>` is the | |
130 | * system message corresponding to the error code. | |
131 | * `error_code` is a Windows error code as given by `GetLastError`. | |
132 | * If `error_code` is not a valid error code such as -1, the system message | |
133 | * will look like "error -1". | |
134 | * | |
135 | * **Example**: | |
136 | * | |
137 | * // This throws a system_error with the description | |
138 | * // cannot open file 'madeup': The system cannot find the file | |
139 | * specified. | |
140 | * // or similar (system message may vary). | |
141 | * const char *filename = "madeup"; | |
142 | * LPOFSTRUCT of = LPOFSTRUCT(); | |
143 | * HFILE file = OpenFile(filename, &of, OF_READ); | |
144 | * if (file == HFILE_ERROR) { | |
145 | * throw fmt::windows_error(GetLastError(), | |
146 | * "cannot open file '{}'", filename); | |
147 | * } | |
148 | */ | |
05aa7e19 JG |
149 | template <typename... Args> |
150 | std::system_error windows_error(int error_code, string_view message, | |
151 | const Args&... args) { | |
152 | return vwindows_error(error_code, message, fmt::make_format_args(args...)); | |
153 | } | |
154 | ||
155 | // Reports a Windows error without throwing an exception. | |
156 | // Can be used to report errors from destructors. | |
8b75cd77 | 157 | FMT_API void report_windows_error(int error_code, const char* message) noexcept; |
05aa7e19 | 158 | #else |
bd9231e4 | 159 | inline auto system_category() noexcept -> const std::error_category& { |
05aa7e19 JG |
160 | return std::system_category(); |
161 | } | |
162 | #endif // _WIN32 | |
163 | ||
164 | // std::system is not available on some platforms such as iOS (#2248). | |
165 | #ifdef __OSX__ | |
166 | template <typename S, typename... Args, typename Char = char_t<S>> | |
167 | void say(const S& format_str, Args&&... args) { | |
168 | std::system(format("say \"{}\"", format(format_str, args...)).c_str()); | |
169 | } | |
170 | #endif | |
171 | ||
172 | // A buffered file. | |
173 | class buffered_file { | |
174 | private: | |
175 | FILE* file_; | |
176 | ||
177 | friend class file; | |
178 | ||
179 | explicit buffered_file(FILE* f) : file_(f) {} | |
180 | ||
181 | public: | |
182 | buffered_file(const buffered_file&) = delete; | |
183 | void operator=(const buffered_file&) = delete; | |
184 | ||
185 | // Constructs a buffered_file object which doesn't represent any file. | |
8b75cd77 | 186 | buffered_file() noexcept : file_(nullptr) {} |
05aa7e19 JG |
187 | |
188 | // Destroys the object closing the file it represents if any. | |
8b75cd77 | 189 | FMT_API ~buffered_file() noexcept; |
05aa7e19 JG |
190 | |
191 | public: | |
8b75cd77 | 192 | buffered_file(buffered_file&& other) noexcept : file_(other.file_) { |
05aa7e19 JG |
193 | other.file_ = nullptr; |
194 | } | |
195 | ||
bd9231e4 | 196 | auto operator=(buffered_file&& other) -> buffered_file& { |
05aa7e19 JG |
197 | close(); |
198 | file_ = other.file_; | |
199 | other.file_ = nullptr; | |
200 | return *this; | |
201 | } | |
202 | ||
203 | // Opens a file. | |
204 | FMT_API buffered_file(cstring_view filename, cstring_view mode); | |
205 | ||
206 | // Closes the file. | |
207 | FMT_API void close(); | |
208 | ||
209 | // Returns the pointer to a FILE object representing this file. | |
bd9231e4 | 210 | auto get() const noexcept -> FILE* { return file_; } |
05aa7e19 | 211 | |
bd9231e4 | 212 | FMT_API auto descriptor() const -> int; |
05aa7e19 | 213 | |
bd9231e4 JG |
214 | template <typename... T> |
215 | inline void print(string_view fmt, const T&... args) { | |
216 | const auto& vargs = fmt::make_format_args(args...); | |
217 | detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs) | |
218 | : fmt::vprint(file_, fmt, vargs); | |
05aa7e19 JG |
219 | } |
220 | }; | |
221 | ||
222 | #if FMT_USE_FCNTL | |
bd9231e4 | 223 | |
05aa7e19 | 224 | // A file. Closed file is represented by a file object with descriptor -1. |
8b75cd77 | 225 | // Methods that are not declared with noexcept may throw |
05aa7e19 JG |
226 | // fmt::system_error in case of failure. Note that some errors such as |
227 | // closing the file multiple times will cause a crash on Windows rather | |
228 | // than an exception. You can get standard behavior by overriding the | |
229 | // invalid parameter handler with _set_invalid_parameter_handler. | |
8b75cd77 | 230 | class FMT_API file { |
05aa7e19 JG |
231 | private: |
232 | int fd_; // File descriptor. | |
233 | ||
234 | // Constructs a file object with a given descriptor. | |
235 | explicit file(int fd) : fd_(fd) {} | |
236 | ||
bd9231e4 JG |
237 | friend struct pipe; |
238 | ||
05aa7e19 JG |
239 | public: |
240 | // Possible values for the oflag argument to the constructor. | |
241 | enum { | |
242 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. | |
243 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. | |
244 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. | |
245 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. | |
246 | APPEND = FMT_POSIX(O_APPEND), // Open in append mode. | |
247 | TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. | |
248 | }; | |
249 | ||
250 | // Constructs a file object which doesn't represent any file. | |
8b75cd77 | 251 | file() noexcept : fd_(-1) {} |
05aa7e19 JG |
252 | |
253 | // Opens a file and constructs a file object representing this file. | |
8b75cd77 | 254 | file(cstring_view path, int oflag); |
05aa7e19 JG |
255 | |
256 | public: | |
257 | file(const file&) = delete; | |
258 | void operator=(const file&) = delete; | |
259 | ||
8b75cd77 | 260 | file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } |
05aa7e19 JG |
261 | |
262 | // Move assignment is not noexcept because close may throw. | |
bd9231e4 | 263 | auto operator=(file&& other) -> file& { |
05aa7e19 JG |
264 | close(); |
265 | fd_ = other.fd_; | |
266 | other.fd_ = -1; | |
267 | return *this; | |
268 | } | |
269 | ||
270 | // Destroys the object closing the file it represents if any. | |
8b75cd77 | 271 | ~file() noexcept; |
05aa7e19 JG |
272 | |
273 | // Returns the file descriptor. | |
bd9231e4 | 274 | auto descriptor() const noexcept -> int { return fd_; } |
05aa7e19 JG |
275 | |
276 | // Closes the file. | |
8b75cd77 | 277 | void close(); |
05aa7e19 JG |
278 | |
279 | // Returns the file size. The size has signed type for consistency with | |
280 | // stat::st_size. | |
bd9231e4 | 281 | auto size() const -> long long; |
05aa7e19 JG |
282 | |
283 | // Attempts to read count bytes from the file into the specified buffer. | |
bd9231e4 | 284 | auto read(void* buffer, size_t count) -> size_t; |
05aa7e19 JG |
285 | |
286 | // Attempts to write count bytes from the specified buffer to the file. | |
bd9231e4 | 287 | auto write(const void* buffer, size_t count) -> size_t; |
05aa7e19 JG |
288 | |
289 | // Duplicates a file descriptor with the dup function and returns | |
290 | // the duplicate as a file object. | |
bd9231e4 | 291 | static auto dup(int fd) -> file; |
05aa7e19 JG |
292 | |
293 | // Makes fd be the copy of this file descriptor, closing fd first if | |
294 | // necessary. | |
8b75cd77 | 295 | void dup2(int fd); |
05aa7e19 JG |
296 | |
297 | // Makes fd be the copy of this file descriptor, closing fd first if | |
298 | // necessary. | |
8b75cd77 | 299 | void dup2(int fd, std::error_code& ec) noexcept; |
05aa7e19 | 300 | |
05aa7e19 JG |
301 | // Creates a buffered_file object associated with this file and detaches |
302 | // this file object from the file. | |
bd9231e4 JG |
303 | auto fdopen(const char* mode) -> buffered_file; |
304 | ||
305 | # if defined(_WIN32) && !defined(__MINGW32__) | |
306 | // Opens a file and constructs a file object representing this file by | |
307 | // wcstring_view filename. Windows only. | |
308 | static file open_windows_file(wcstring_view path, int oflag); | |
309 | # endif | |
310 | }; | |
311 | ||
312 | struct FMT_API pipe { | |
313 | file read_end; | |
314 | file write_end; | |
315 | ||
316 | // Creates a pipe setting up read_end and write_end file objects for reading | |
317 | // and writing respectively. | |
318 | pipe(); | |
05aa7e19 JG |
319 | }; |
320 | ||
321 | // Returns the memory page size. | |
bd9231e4 | 322 | auto getpagesize() -> long; |
05aa7e19 | 323 | |
bd9231e4 | 324 | namespace detail { |
05aa7e19 JG |
325 | |
326 | struct buffer_size { | |
327 | buffer_size() = default; | |
328 | size_t value = 0; | |
bd9231e4 | 329 | auto operator=(size_t val) const -> buffer_size { |
05aa7e19 JG |
330 | auto bs = buffer_size(); |
331 | bs.value = val; | |
332 | return bs; | |
333 | } | |
334 | }; | |
335 | ||
336 | struct ostream_params { | |
337 | int oflag = file::WRONLY | file::CREATE | file::TRUNC; | |
338 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; | |
339 | ||
340 | ostream_params() {} | |
341 | ||
342 | template <typename... T> | |
343 | ostream_params(T... params, int new_oflag) : ostream_params(params...) { | |
344 | oflag = new_oflag; | |
345 | } | |
346 | ||
347 | template <typename... T> | |
348 | ostream_params(T... params, detail::buffer_size bs) | |
349 | : ostream_params(params...) { | |
350 | this->buffer_size = bs.value; | |
351 | } | |
352 | ||
353 | // Intel has a bug that results in failure to deduce a constructor | |
354 | // for empty parameter packs. | |
355 | # if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 | |
356 | ostream_params(int new_oflag) : oflag(new_oflag) {} | |
357 | ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} | |
358 | # endif | |
359 | }; | |
360 | ||
bd9231e4 | 361 | class file_buffer final : public buffer<char> { |
05aa7e19 JG |
362 | private: |
363 | file file_; | |
364 | ||
bd9231e4 | 365 | FMT_API static void grow(buffer<char>& buf, size_t); |
05aa7e19 JG |
366 | |
367 | public: | |
bd9231e4 JG |
368 | FMT_API file_buffer(cstring_view path, const ostream_params& params); |
369 | FMT_API file_buffer(file_buffer&& other) noexcept; | |
370 | FMT_API ~file_buffer(); | |
05aa7e19 JG |
371 | |
372 | void flush() { | |
373 | if (size() == 0) return; | |
bd9231e4 | 374 | file_.write(data(), size() * sizeof(data()[0])); |
05aa7e19 JG |
375 | clear(); |
376 | } | |
377 | ||
05aa7e19 JG |
378 | void close() { |
379 | flush(); | |
380 | file_.close(); | |
381 | } | |
bd9231e4 | 382 | }; |
05aa7e19 | 383 | |
bd9231e4 JG |
384 | } // namespace detail |
385 | ||
386 | constexpr auto buffer_size = detail::buffer_size(); | |
387 | ||
388 | /// A fast output stream for writing from a single thread. Writing from | |
389 | /// multiple threads without external synchronization may result in a data race. | |
390 | class FMT_API ostream { | |
391 | private: | |
392 | FMT_MSC_WARNING(suppress : 4251) | |
393 | detail::file_buffer buffer_; | |
394 | ||
395 | ostream(cstring_view path, const detail::ostream_params& params) | |
396 | : buffer_(path, params) {} | |
397 | ||
398 | public: | |
399 | ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {} | |
400 | ||
401 | ~ostream(); | |
402 | ||
403 | void flush() { buffer_.flush(); } | |
404 | ||
405 | template <typename... T> | |
406 | friend auto output_file(cstring_view path, T... params) -> ostream; | |
407 | ||
408 | void close() { buffer_.close(); } | |
409 | ||
410 | /// Formats `args` according to specifications in `fmt` and writes the | |
411 | /// output to the file. | |
05aa7e19 | 412 | template <typename... T> void print(format_string<T...> fmt, T&&... args) { |
bd9231e4 | 413 | vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...)); |
05aa7e19 JG |
414 | } |
415 | }; | |
416 | ||
417 | /** | |
bd9231e4 JG |
418 | * Opens a file for writing. Supported parameters passed in `params`: |
419 | * | |
420 | * - `<integer>`: Flags passed to [open]( | |
421 | * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) | |
422 | * (`file::WRONLY | file::CREATE | file::TRUNC` by default) | |
423 | * - `buffer_size=<integer>`: Output buffer size | |
424 | * | |
425 | * **Example**: | |
426 | * | |
427 | * auto out = fmt::output_file("guide.txt"); | |
428 | * out.print("Don't {}", "Panic"); | |
05aa7e19 JG |
429 | */ |
430 | template <typename... T> | |
bd9231e4 | 431 | inline auto output_file(cstring_view path, T... params) -> ostream { |
05aa7e19 JG |
432 | return {path, detail::ostream_params(params...)}; |
433 | } | |
434 | #endif // FMT_USE_FCNTL | |
435 | ||
bd9231e4 | 436 | FMT_END_EXPORT |
05aa7e19 JG |
437 | FMT_END_NAMESPACE |
438 | ||
439 | #endif // FMT_OS_H_ |