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 #if defined(__MINGW32__) || defined(__CYGWIN__)
12 // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
13 # undef __STRICT_ANSI__
14 #endif
15
16 #include <cerrno>
17 #include <clocale> // for locale_t
18 #include <cstddef>
19 #include <cstdio>
20 #include <cstdlib> // for strtod_l
21
22 #if defined __APPLE__ || defined(__FreeBSD__)
23 # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
24 #endif
25
26 #include "format.h"
27
28 // UWP doesn't provide _pipe.
29 #if FMT_HAS_INCLUDE("winapifamily.h")
30 # include <winapifamily.h>
31 #endif
32 #if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
33 defined(__linux__)) && \
34 (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
35 # include <fcntl.h> // for O_RDONLY
36 # define FMT_USE_FCNTL 1
37 #else
38 # define FMT_USE_FCNTL 0
39 #endif
40
41 #ifndef FMT_POSIX
42 # if defined(_WIN32) && !defined(__MINGW32__)
43 // Fix warnings about deprecated symbols.
44 # define FMT_POSIX(call) _##call
45 # else
46 # define FMT_POSIX(call) call
47 # endif
48 #endif
49
50 // Calls to system functions are wrapped in FMT_SYSTEM for testability.
51 #ifdef FMT_SYSTEM
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
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. */
basic_cstring_view(const Char * s)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 */
basic_cstring_view(const std::basic_string<Char> & s)116 basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
117
118 /** Returns the pointer to a C string. */
c_str()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 // An error code.
126 class error_code {
127 private:
128 int value_;
129
130 public:
value_(value)131 explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
132
get()133 int get() const FMT_NOEXCEPT { return value_; }
134 };
135
136 #ifdef _WIN32
137 namespace detail {
138 // A converter from UTF-16 to UTF-8.
139 // It is only provided for Windows since other systems support UTF-8 natively.
140 class utf16_to_utf8 {
141 private:
142 memory_buffer buffer_;
143
144 public:
utf16_to_utf8()145 utf16_to_utf8() {}
146 FMT_API explicit utf16_to_utf8(wstring_view s);
string_view()147 operator string_view() const { return string_view(&buffer_[0], size()); }
size()148 size_t size() const { return buffer_.size() - 1; }
c_str()149 const char* c_str() const { return &buffer_[0]; }
str()150 std::string str() const { return std::string(&buffer_[0], size()); }
151
152 // Performs conversion returning a system error code instead of
153 // throwing exception on conversion error. This method may still throw
154 // in case of memory allocation error.
155 FMT_API int convert(wstring_view s);
156 };
157
158 FMT_API void format_windows_error(buffer<char>& out, int error_code,
159 string_view message) FMT_NOEXCEPT;
160 } // namespace detail
161
162 /** A Windows error. */
163 class windows_error : public system_error {
164 private:
165 FMT_API void init(int error_code, string_view format_str, format_args args);
166
167 public:
168 /**
169 \rst
170 Constructs a :class:`fmt::windows_error` object with the description
171 of the form
172
173 .. parsed-literal::
174 *<message>*: *<system-message>*
175
176 where *<message>* is the formatted message and *<system-message>* is the
177 system message corresponding to the error code.
178 *error_code* is a Windows error code as given by ``GetLastError``.
179 If *error_code* is not a valid error code such as -1, the system message
180 will look like "error -1".
181
182 **Example**::
183
184 // This throws a windows_error with the description
185 // cannot open file 'madeup': The system cannot find the file specified.
186 // or similar (system message may vary).
187 const char *filename = "madeup";
188 LPOFSTRUCT of = LPOFSTRUCT();
189 HFILE file = OpenFile(filename, &of, OF_READ);
190 if (file == HFILE_ERROR) {
191 throw fmt::windows_error(GetLastError(),
192 "cannot open file '{}'", filename);
193 }
194 \endrst
195 */
196 template <typename... Args>
windows_error(int error_code,string_view message,const Args &...args)197 windows_error(int error_code, string_view message, const Args&... args) {
198 init(error_code, message, make_format_args(args...));
199 }
200 };
201
202 // Reports a Windows error without throwing an exception.
203 // Can be used to report errors from destructors.
204 FMT_API void report_windows_error(int error_code,
205 string_view message) FMT_NOEXCEPT;
206 #endif // _WIN32
207
208 // A buffered file.
209 class buffered_file {
210 private:
211 FILE* file_;
212
213 friend class file;
214
buffered_file(FILE * f)215 explicit buffered_file(FILE* f) : file_(f) {}
216
217 public:
218 buffered_file(const buffered_file&) = delete;
219 void operator=(const buffered_file&) = delete;
220
221 // Constructs a buffered_file object which doesn't represent any file.
buffered_file()222 buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
223
224 // Destroys the object closing the file it represents if any.
225 FMT_API ~buffered_file() FMT_NOEXCEPT;
226
227 public:
buffered_file(buffered_file && other)228 buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
229 other.file_ = nullptr;
230 }
231
232 buffered_file& operator=(buffered_file&& other) {
233 close();
234 file_ = other.file_;
235 other.file_ = nullptr;
236 return *this;
237 }
238
239 // Opens a file.
240 FMT_API buffered_file(cstring_view filename, cstring_view mode);
241
242 // Closes the file.
243 FMT_API void close();
244
245 // Returns the pointer to a FILE object representing this file.
get()246 FILE* get() const FMT_NOEXCEPT { return file_; }
247
248 // We place parentheses around fileno to workaround a bug in some versions
249 // of MinGW that define fileno as a macro.
250 FMT_API int(fileno)() const;
251
vprint(string_view format_str,format_args args)252 void vprint(string_view format_str, format_args args) {
253 fmt::vprint(file_, format_str, args);
254 }
255
256 template <typename... Args>
print(string_view format_str,const Args &...args)257 inline void print(string_view format_str, const Args&... args) {
258 vprint(format_str, make_format_args(args...));
259 }
260 };
261
262 #if FMT_USE_FCNTL
263 // A file. Closed file is represented by a file object with descriptor -1.
264 // Methods that are not declared with FMT_NOEXCEPT may throw
265 // fmt::system_error in case of failure. Note that some errors such as
266 // closing the file multiple times will cause a crash on Windows rather
267 // than an exception. You can get standard behavior by overriding the
268 // invalid parameter handler with _set_invalid_parameter_handler.
269 class file {
270 private:
271 int fd_; // File descriptor.
272
273 // Constructs a file object with a given descriptor.
file(int fd)274 explicit file(int fd) : fd_(fd) {}
275
276 public:
277 // Possible values for the oflag argument to the constructor.
278 enum {
279 RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
280 WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
281 RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
282 CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
283 APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
284 };
285
286 // Constructs a file object which doesn't represent any file.
file()287 file() FMT_NOEXCEPT : fd_(-1) {}
288
289 // Opens a file and constructs a file object representing this file.
290 FMT_API file(cstring_view path, int oflag);
291
292 public:
293 file(const file&) = delete;
294 void operator=(const file&) = delete;
295
file(file && other)296 file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
297
298 file& operator=(file&& other) FMT_NOEXCEPT {
299 close();
300 fd_ = other.fd_;
301 other.fd_ = -1;
302 return *this;
303 }
304
305 // Destroys the object closing the file it represents if any.
306 FMT_API ~file() FMT_NOEXCEPT;
307
308 // Returns the file descriptor.
descriptor()309 int descriptor() const FMT_NOEXCEPT { return fd_; }
310
311 // Closes the file.
312 FMT_API void close();
313
314 // Returns the file size. The size has signed type for consistency with
315 // stat::st_size.
316 FMT_API long long size() const;
317
318 // Attempts to read count bytes from the file into the specified buffer.
319 FMT_API size_t read(void* buffer, size_t count);
320
321 // Attempts to write count bytes from the specified buffer to the file.
322 FMT_API size_t write(const void* buffer, size_t count);
323
324 // Duplicates a file descriptor with the dup function and returns
325 // the duplicate as a file object.
326 FMT_API static file dup(int fd);
327
328 // Makes fd be the copy of this file descriptor, closing fd first if
329 // necessary.
330 FMT_API void dup2(int fd);
331
332 // Makes fd be the copy of this file descriptor, closing fd first if
333 // necessary.
334 FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
335
336 // Creates a pipe setting up read_end and write_end file objects for reading
337 // and writing respectively.
338 FMT_API static void pipe(file& read_end, file& write_end);
339
340 // Creates a buffered_file object associated with this file and detaches
341 // this file object from the file.
342 FMT_API buffered_file fdopen(const char* mode);
343 };
344
345 // Returns the memory page size.
346 long getpagesize();
347
348 namespace detail {
349
350 struct buffer_size {
351 size_t value = 0;
352 buffer_size operator=(size_t val) const {
353 auto bs = buffer_size();
354 bs.value = val;
355 return bs;
356 }
357 };
358
359 struct ostream_params {
360 int oflag = file::WRONLY | file::CREATE;
361 size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
362
ostream_paramsostream_params363 ostream_params() {}
364
365 template <typename... T>
ostream_paramsostream_params366 ostream_params(T... params, int oflag) : ostream_params(params...) {
367 this->oflag = oflag;
368 }
369
370 template <typename... T>
ostream_paramsostream_params371 ostream_params(T... params, detail::buffer_size bs)
372 : ostream_params(params...) {
373 this->buffer_size = bs.value;
374 }
375 };
376 } // namespace detail
377
378 static constexpr detail::buffer_size buffer_size;
379
380 // A fast output stream which is not thread-safe.
381 class ostream final : private detail::buffer<char> {
382 private:
383 file file_;
384
flush()385 void flush() {
386 if (size() == 0) return;
387 file_.write(data(), size());
388 clear();
389 }
390
grow(size_t)391 FMT_API void grow(size_t) override final;
392
393 ostream(cstring_view path, const detail::ostream_params& params)
394 : file_(path, params.oflag) {
395 set(new char[params.buffer_size], params.buffer_size);
396 }
397
398 public:
ostream(ostream && other)399 ostream(ostream&& other)
400 : detail::buffer<char>(other.data(), other.size(), other.capacity()),
401 file_(std::move(other.file_)) {
402 other.set(nullptr, 0);
403 }
~ostream()404 ~ostream() {
405 flush();
406 delete[] data();
407 }
408
409 template <typename... T>
410 friend ostream output_file(cstring_view path, T... params);
411
close()412 void close() {
413 flush();
414 file_.close();
415 }
416
417 template <typename S, typename... Args>
print(const S & format_str,const Args &...args)418 void print(const S& format_str, const Args&... args) {
419 format_to(detail::buffer_appender<char>(*this), format_str, args...);
420 }
421 };
422
423 /**
424 Opens a file for writing. Supported parameters passed in `params`:
425 * ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
426 * ``buffer_size=<integer>``: Output buffer size
427 */
428 template <typename... T>
output_file(cstring_view path,T...params)429 inline ostream output_file(cstring_view path, T... params) {
430 return {path, detail::ostream_params(params...)};
431 }
432 #endif // FMT_USE_FCNTL
433
434 #ifdef FMT_LOCALE
435 // A "C" numeric locale.
436 class locale {
437 private:
438 # ifdef _WIN32
439 using locale_t = _locale_t;
440
freelocale(locale_t loc)441 static void freelocale(locale_t loc) { _free_locale(loc); }
442
strtod_l(const char * nptr,char ** endptr,_locale_t loc)443 static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
444 return _strtod_l(nptr, endptr, loc);
445 }
446 # endif
447
448 locale_t locale_;
449
450 public:
451 using type = locale_t;
452 locale(const locale&) = delete;
453 void operator=(const locale&) = delete;
454
locale()455 locale() {
456 # ifndef _WIN32
457 locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
458 # else
459 locale_ = _create_locale(LC_NUMERIC, "C");
460 # endif
461 if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
462 }
~locale()463 ~locale() { freelocale(locale_); }
464
get()465 type get() const { return locale_; }
466
467 // Converts string to floating-point number and advances str past the end
468 // of the parsed input.
strtod(const char * & str)469 double strtod(const char*& str) const {
470 char* end = nullptr;
471 double result = strtod_l(str, &end, locale_);
472 str = end;
473 return result;
474 }
475 };
476 using Locale FMT_DEPRECATED_ALIAS = locale;
477 #endif // FMT_LOCALE
478 FMT_END_NAMESPACE
479
480 #endif // FMT_OS_H_
481