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