1 // A C++ interface to POSIX functions.
2 //
3 // Copyright (c) 2012 - 2016, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7 
8 #ifndef FMT_POSIX_H_
9 #define FMT_POSIX_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 <errno.h>
17 #include <fcntl.h>  // for O_RDONLY
18 #include <locale.h> // for locale_t
19 #include <stdio.h>
20 #include <stdlib.h> // for strtod_l
21 
22 #include <cstddef>
23 
24 #if defined __APPLE__ || defined(__FreeBSD__)
25 #include <xlocale.h> // for LC_NUMERIC_MASK on OS X
26 #endif
27 
28 #include "format.h"
29 
30 #ifndef FMT_POSIX
31 #if defined(_WIN32) && !defined(__MINGW32__)
32 // Fix warnings about deprecated symbols.
33 #define FMT_POSIX(call) _##call
34 #else
35 #define FMT_POSIX(call) call
36 #endif
37 #endif
38 
39 // Calls to system functions are wrapped in FMT_SYSTEM for testability.
40 #ifdef FMT_SYSTEM
41 #define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
42 #else
43 #define FMT_SYSTEM(call) call
44 #ifdef _WIN32
45 // Fix warnings about deprecated symbols.
46 #define FMT_POSIX_CALL(call) ::_##call
47 #else
48 #define FMT_POSIX_CALL(call) ::call
49 #endif
50 #endif
51 
52 // Retries the expression while it evaluates to error_result and errno
53 // equals to EINTR.
54 #ifndef _WIN32
55 #define FMT_RETRY_VAL(result, expression, error_result)                                                                                    \
56     do                                                                                                                                     \
57     {                                                                                                                                      \
58         result = (expression);                                                                                                             \
59     } while (result == error_result && errno == EINTR)
60 #else
61 #define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
62 #endif
63 
64 #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
65 
66 FMT_BEGIN_NAMESPACE
67 
68 /**
69   \rst
70   A reference to a null-terminated string. It can be constructed from a C
71   string or ``std::string``.
72 
73   You can use one of the following typedefs for common character types:
74 
75   +---------------+-----------------------------+
76   | Type          | Definition                  |
77   +===============+=============================+
78   | cstring_view  | basic_cstring_view<char>    |
79   +---------------+-----------------------------+
80   | wcstring_view | basic_cstring_view<wchar_t> |
81   +---------------+-----------------------------+
82 
83   This class is most useful as a parameter type to allow passing
84   different types of strings to a function, for example::
85 
86     template <typename... Args>
87     std::string format(cstring_view format_str, const Args & ... args);
88 
89     format("{}", 42);
90     format(std::string("{}"), 42);
91   \endrst
92  */
93 template<typename Char>
94 class basic_cstring_view
95 {
96 private:
97     const Char *data_;
98 
99 public:
100     /** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s)101     basic_cstring_view(const Char *s)
102         : data_(s)
103     {
104     }
105 
106     /**
107       \rst
108       Constructs a string reference from an ``std::string`` object.
109       \endrst
110      */
basic_cstring_view(const std::basic_string<Char> &s)111     basic_cstring_view(const std::basic_string<Char> &s)
112         : data_(s.c_str())
113     {
114     }
115 
116     /** Returns the pointer to a C string. */
c_str() const117     const Char *c_str() const
118     {
119         return data_;
120     }
121 };
122 
123 typedef basic_cstring_view<char> cstring_view;
124 typedef basic_cstring_view<wchar_t> wcstring_view;
125 
126 // An error code.
127 class error_code
128 {
129 private:
130     int value_;
131 
132 public:
value_(value)133     explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
134 
135     int get() const FMT_NOEXCEPT
136     {
137         return value_;
138     }
139 };
140 
141 // A buffered file.
142 class buffered_file
143 {
144 private:
145     FILE *file_;
146 
147     friend class file;
148 
buffered_file(FILE *f)149     explicit buffered_file(FILE *f)
150         : file_(f)
151     {
152     }
153 
154 public:
155     // Constructs a buffered_file object which doesn't represent any file.
file_(FMT_NULL)156     buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
157 
158     // Destroys the object closing the file it represents if any.
159     FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
160 
161 #if !FMT_USE_RVALUE_REFERENCES
162     // Emulate a move constructor and a move assignment operator if rvalue
163     // references are not supported.
164 
165 private:
166     // A proxy object to emulate a move constructor.
167     // It is private to make it impossible call operator Proxy directly.
168     struct Proxy
169     {
170         FILE *file;
171     };
172 
173 public:
174     // A "move constructor" for moving from a temporary.
file_(p.file)175     buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
176 
177     // A "move constructor" for moving from an lvalue.
file_(f.file_)178     buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_)
179     {
180         f.file_ = FMT_NULL;
181     }
182 
183     // A "move assignment operator" for moving from a temporary.
operator =(Proxy p)184     buffered_file &operator=(Proxy p)
185     {
186         close();
187         file_ = p.file;
188         return *this;
189     }
190 
191     // A "move assignment operator" for moving from an lvalue.
operator =(buffered_file &other)192     buffered_file &operator=(buffered_file &other)
193     {
194         close();
195         file_ = other.file_;
196         other.file_ = FMT_NULL;
197         return *this;
198     }
199 
200     // Returns a proxy object for moving from a temporary:
201     //   buffered_file file = buffered_file(...);
202     operator Proxy() FMT_NOEXCEPT
203     {
204         Proxy p = {file_};
205         file_ = FMT_NULL;
206         return p;
207     }
208 
209 #else
210 private:
211     FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
212 
213 public:
file_(other.file_)214     buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
215     {
216         other.file_ = FMT_NULL;
217     }
218 
operator =(buffered_file &&other)219     buffered_file &operator=(buffered_file &&other)
220     {
221         close();
222         file_ = other.file_;
223         other.file_ = FMT_NULL;
224         return *this;
225     }
226 #endif
227 
228     // Opens a file.
229     FMT_API buffered_file(cstring_view filename, cstring_view mode);
230 
231     // Closes the file.
232     FMT_API void close();
233 
234     // Returns the pointer to a FILE object representing this file.
235     FILE *get() const FMT_NOEXCEPT
236     {
237         return file_;
238     }
239 
240     // We place parentheses around fileno to workaround a bug in some versions
241     // of MinGW that define fileno as a macro.
242     FMT_API int(fileno)() const;
243 
vprint(string_view format_str, format_args args)244     void vprint(string_view format_str, format_args args)
245     {
246         fmt::vprint(file_, format_str, args);
247     }
248 
249     template<typename... Args>
print(string_view format_str, const Args &... args)250     inline void print(string_view format_str, const Args &... args)
251     {
252         vprint(format_str, make_format_args(args...));
253     }
254 };
255 
256 // A file. Closed file is represented by a file object with descriptor -1.
257 // Methods that are not declared with FMT_NOEXCEPT may throw
258 // fmt::system_error in case of failure. Note that some errors such as
259 // closing the file multiple times will cause a crash on Windows rather
260 // than an exception. You can get standard behavior by overriding the
261 // invalid parameter handler with _set_invalid_parameter_handler.
262 class file
263 {
264 private:
265     int fd_; // File descriptor.
266 
267     // Constructs a file object with a given descriptor.
file(int fd)268     explicit file(int fd)
269         : fd_(fd)
270     {
271     }
272 
273 public:
274     // Possible values for the oflag argument to the constructor.
275     enum
276     {
277         RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
278         WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
279         RDWR = FMT_POSIX(O_RDWR)      // Open for reading and writing.
280     };
281 
282     // Constructs a file object which doesn't represent any file.
283     file() FMT_NOEXCEPT : fd_(-1) {}
284 
285     // Opens a file and constructs a file object representing this file.
286     FMT_API file(cstring_view path, int oflag);
287 
288 #if !FMT_USE_RVALUE_REFERENCES
289     // Emulate a move constructor and a move assignment operator if rvalue
290     // references are not supported.
291 
292 private:
293     // A proxy object to emulate a move constructor.
294     // It is private to make it impossible call operator Proxy directly.
295     struct Proxy
296     {
297         int fd;
298     };
299 
300 public:
301     // A "move constructor" for moving from a temporary.
fd_(p.fd)302     file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
303 
304     // A "move constructor" for moving from an lvalue.
fd_(other.fd_)305     file(file &other) FMT_NOEXCEPT : fd_(other.fd_)
306     {
307         other.fd_ = -1;
308     }
309 
310     // A "move assignment operator" for moving from a temporary.
operator =(Proxy p)311     file &operator=(Proxy p)
312     {
313         close();
314         fd_ = p.fd;
315         return *this;
316     }
317 
318     // A "move assignment operator" for moving from an lvalue.
operator =(file &other)319     file &operator=(file &other)
320     {
321         close();
322         fd_ = other.fd_;
323         other.fd_ = -1;
324         return *this;
325     }
326 
327     // Returns a proxy object for moving from a temporary:
328     //   file f = file(...);
329     operator Proxy() FMT_NOEXCEPT
330     {
331         Proxy p = {fd_};
332         fd_ = -1;
333         return p;
334     }
335 
336 #else
337 private:
338     FMT_DISALLOW_COPY_AND_ASSIGN(file);
339 
340 public:
fd_(other.fd_)341     file(file &&other) FMT_NOEXCEPT : fd_(other.fd_)
342     {
343         other.fd_ = -1;
344     }
345 
operator =(file &&other)346     file &operator=(file &&other)
347     {
348         close();
349         fd_ = other.fd_;
350         other.fd_ = -1;
351         return *this;
352     }
353 #endif
354 
355     // Destroys the object closing the file it represents if any.
356     FMT_API ~file() FMT_DTOR_NOEXCEPT;
357 
358     // Returns the file descriptor.
359     int descriptor() const FMT_NOEXCEPT
360     {
361         return fd_;
362     }
363 
364     // Closes the file.
365     FMT_API void close();
366 
367     // Returns the file size. The size has signed type for consistency with
368     // stat::st_size.
369     FMT_API long long size() const;
370 
371     // Attempts to read count bytes from the file into the specified buffer.
372     FMT_API std::size_t read(void *buffer, std::size_t count);
373 
374     // Attempts to write count bytes from the specified buffer to the file.
375     FMT_API std::size_t write(const void *buffer, std::size_t count);
376 
377     // Duplicates a file descriptor with the dup function and returns
378     // the duplicate as a file object.
379     FMT_API static file dup(int fd);
380 
381     // Makes fd be the copy of this file descriptor, closing fd first if
382     // necessary.
383     FMT_API void dup2(int fd);
384 
385     // Makes fd be the copy of this file descriptor, closing fd first if
386     // necessary.
387     FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
388 
389     // Creates a pipe setting up read_end and write_end file objects for reading
390     // and writing respectively.
391     FMT_API static void pipe(file &read_end, file &write_end);
392 
393     // Creates a buffered_file object associated with this file and detaches
394     // this file object from the file.
395     FMT_API buffered_file fdopen(const char *mode);
396 };
397 
398 // Returns the memory page size.
399 long getpagesize();
400 
401 #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
402 #define FMT_LOCALE
403 #endif
404 
405 #ifdef FMT_LOCALE
406 // A "C" numeric locale.
407 class Locale
408 {
409 private:
410 #ifdef _MSC_VER
411     typedef _locale_t locale_t;
412 
413     enum
414     {
415         LC_NUMERIC_MASK = LC_NUMERIC
416     };
417 
newlocale(int category_mask, const char *locale, locale_t)418     static locale_t newlocale(int category_mask, const char *locale, locale_t)
419     {
420         return _create_locale(category_mask, locale);
421     }
422 
freelocale(locale_t locale)423     static void freelocale(locale_t locale)
424     {
425         _free_locale(locale);
426     }
427 
strtod_l(const char *nptr, char **endptr, _locale_t locale)428     static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
429     {
430         return _strtod_l(nptr, endptr, locale);
431     }
432 #endif
433 
434     locale_t locale_;
435 
436     FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
437 
438 public:
439     typedef locale_t Type;
440 
Locale()441     Locale()
442         : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
443     {
444         if (!locale_)
445             FMT_THROW(system_error(errno, "cannot create locale"));
446     }
~Locale()447     ~Locale()
448     {
449         freelocale(locale_);
450     }
451 
get() const452     Type get() const
453     {
454         return locale_;
455     }
456 
457     // Converts string to floating-point number and advances str past the end
458     // of the parsed input.
strtod(const char *&str) const459     double strtod(const char *&str) const
460     {
461         char *end = FMT_NULL;
462         double result = strtod_l(str, &end, locale_);
463         str = end;
464         return result;
465     }
466 };
467 #endif // FMT_LOCALE
468 FMT_END_NAMESPACE
469 
470 #if !FMT_USE_RVALUE_REFERENCES
471 namespace std {
472 // For compatibility with C++98.
move(fmt::buffered_file &f)473 inline fmt::buffered_file &move(fmt::buffered_file &f)
474 {
475     return f;
476 }
move(fmt::file &f)477 inline fmt::file &move(fmt::file &f)
478 {
479     return f;
480 }
481 } // namespace std
482 #endif
483 
484 #endif // FMT_POSIX_H_
485