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