1 /*
2  A C++ interface to POSIX functions.
3 
4  Copyright (c) 2014 - 2015, Victor Zverovich
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions are met:
9 
10  1. Redistributions of source code must retain the above copyright notice, this
11     list of conditions and the following disclaimer.
12  2. Redistributions in binary form must reproduce the above copyright notice,
13     this list of conditions and the following disclaimer in the documentation
14     and/or other materials provided with the distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef FMT_POSIX_H_
29 #define FMT_POSIX_H_
30 
31 #ifdef __MINGW32__
32 // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
33 # undef __STRICT_ANSI__
34 #endif
35 
36 #include <errno.h>
37 #include <fcntl.h>  // for O_RDONLY
38 #include <stdio.h>
39 
40 #include <cstddef>
41 
42 #include "format.h"
43 
44 #ifndef FMT_POSIX
45 # if defined(_WIN32) && !defined(__MINGW32__)
46 // Fix warnings about deprecated symbols.
47 #  define FMT_POSIX(call) _##call
48 # else
49 #  define FMT_POSIX(call) call
50 # endif
51 #endif
52 
53 // Calls to system functions are wrapped in FMT_SYSTEM for testability.
54 #ifdef FMT_SYSTEM
55 # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
56 #else
57 # define FMT_SYSTEM(call) call
58 # ifdef _WIN32
59 // Fix warnings about deprecated symbols.
60 #  define FMT_POSIX_CALL(call) ::_##call
61 # else
62 #  define FMT_POSIX_CALL(call) ::call
63 # endif
64 #endif
65 
66 #if FMT_GCC_VERSION >= 407
67 # define FMT_UNUSED __attribute__((unused))
68 #else
69 # define FMT_UNUSED
70 #endif
71 
72 #ifndef FMT_USE_STATIC_ASSERT
73 # define FMT_USE_STATIC_ASSERT 0
74 #endif
75 
76 #if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
77   (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
78 # define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
79 #else
80 # define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
81 # define FMT_STATIC_ASSERT(cond, message) \
82   typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
83 #endif
84 
85 // Retries the expression while it evaluates to error_result and errno
86 // equals to EINTR.
87 #ifndef _WIN32
88 # define FMT_RETRY_VAL(result, expression, error_result) \
89   do { \
90     result = (expression); \
91   } while (result == error_result && errno == EINTR)
92 #else
93 # define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
94 #endif
95 
96 #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
97 
98 namespace clmdep_fmt {
99 
100 // An error code.
101 class ErrorCode {
102  private:
103   int value_;
104 
105  public:
value_(value)106   explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
107 
get()108   int get() const FMT_NOEXCEPT { return value_; }
109 };
110 
111 // A buffered file.
112 class BufferedFile {
113  private:
114   FILE *file_;
115 
116   friend class File;
117 
BufferedFile(FILE * f)118   explicit BufferedFile(FILE *f) : file_(f) {}
119 
120  public:
121   // Constructs a BufferedFile object which doesn't represent any file.
BufferedFile()122   BufferedFile() FMT_NOEXCEPT : file_(0) {}
123 
124   // Destroys the object closing the file it represents if any.
125   ~BufferedFile() FMT_NOEXCEPT;
126 
127 #if !FMT_USE_RVALUE_REFERENCES
128   // Emulate a move constructor and a move assignment operator if rvalue
129   // references are not supported.
130 
131  private:
132   // A proxy object to emulate a move constructor.
133   // It is private to make it impossible call operator Proxy directly.
134   struct Proxy {
135     FILE *file;
136   };
137 
138 public:
139   // A "move constructor" for moving from a temporary.
BufferedFile(Proxy p)140   BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
141 
142   // A "move constructor" for for moving from an lvalue.
BufferedFile(BufferedFile & f)143   BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
144     f.file_ = 0;
145   }
146 
147   // A "move assignment operator" for moving from a temporary.
148   BufferedFile &operator=(Proxy p) {
149     close();
150     file_ = p.file;
151     return *this;
152   }
153 
154   // A "move assignment operator" for moving from an lvalue.
155   BufferedFile &operator=(BufferedFile &other) {
156     close();
157     file_ = other.file_;
158     other.file_ = 0;
159     return *this;
160   }
161 
162   // Returns a proxy object for moving from a temporary:
163   //   BufferedFile file = BufferedFile(...);
Proxy()164   operator Proxy() FMT_NOEXCEPT {
165     Proxy p = {file_};
166     file_ = 0;
167     return p;
168   }
169 
170 #else
171  private:
172   FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
173 
174  public:
BufferedFile(BufferedFile && other)175   BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
176     other.file_ = 0;
177   }
178 
179   BufferedFile& operator=(BufferedFile &&other) {
180     close();
181     file_ = other.file_;
182     other.file_ = 0;
183     return *this;
184   }
185 #endif
186 
187   // Opens a file.
188   BufferedFile(CStringRef filename, CStringRef mode);
189 
190   // Closes the file.
191   void close();
192 
193   // Returns the pointer to a FILE object representing this file.
get()194   FILE *get() const FMT_NOEXCEPT { return file_; }
195 
196   // We place parentheses around fileno to workaround a bug in some versions
197   // of MinGW that define fileno as a macro.
198   int (fileno)() const;
199 
print(CStringRef format_str,const ArgList & args)200   void print(CStringRef format_str, const ArgList &args) {
201     clmdep_fmt::print(file_, format_str, args);
202   }
203   FMT_VARIADIC(void, print, CStringRef)
204 };
205 
206 // A file. Closed file is represented by a File object with descriptor -1.
207 // Methods that are not declared with FMT_NOEXCEPT may throw
208 // clmdep_fmt::SystemError in case of failure. Note that some errors such as
209 // closing the file multiple times will cause a crash on Windows rather
210 // than an exception. You can get standard behavior by overriding the
211 // invalid parameter handler with _set_invalid_parameter_handler.
212 class File {
213  private:
214   int fd_;  // File descriptor.
215 
216   // Constructs a File object with a given descriptor.
File(int fd)217   explicit File(int fd) : fd_(fd) {}
218 
219  public:
220   // Possible values for the oflag argument to the constructor.
221   enum {
222     RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
223     WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
224     RDWR   = FMT_POSIX(O_RDWR)    // Open for reading and writing.
225   };
226 
227   // Constructs a File object which doesn't represent any file.
File()228   File() FMT_NOEXCEPT : fd_(-1) {}
229 
230   // Opens a file and constructs a File object representing this file.
231   File(CStringRef path, int oflag);
232 
233 #if !FMT_USE_RVALUE_REFERENCES
234   // Emulate a move constructor and a move assignment operator if rvalue
235   // references are not supported.
236 
237  private:
238   // A proxy object to emulate a move constructor.
239   // It is private to make it impossible call operator Proxy directly.
240   struct Proxy {
241     int fd;
242   };
243 
244  public:
245   // A "move constructor" for moving from a temporary.
File(Proxy p)246   File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
247 
248   // A "move constructor" for for moving from an lvalue.
File(File & other)249   File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
250     other.fd_ = -1;
251   }
252 
253   // A "move assignment operator" for moving from a temporary.
254   File &operator=(Proxy p) {
255     close();
256     fd_ = p.fd;
257     return *this;
258   }
259 
260   // A "move assignment operator" for moving from an lvalue.
261   File &operator=(File &other) {
262     close();
263     fd_ = other.fd_;
264     other.fd_ = -1;
265     return *this;
266   }
267 
268   // Returns a proxy object for moving from a temporary:
269   //   File file = File(...);
Proxy()270   operator Proxy() FMT_NOEXCEPT {
271     Proxy p = {fd_};
272     fd_ = -1;
273     return p;
274   }
275 
276 #else
277  private:
278   FMT_DISALLOW_COPY_AND_ASSIGN(File);
279 
280  public:
File(File && other)281   File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
282     other.fd_ = -1;
283   }
284 
285   File& operator=(File &&other) {
286     close();
287     fd_ = other.fd_;
288     other.fd_ = -1;
289     return *this;
290   }
291 #endif
292 
293   // Destroys the object closing the file it represents if any.
294   ~File() FMT_NOEXCEPT;
295 
296   // Returns the file descriptor.
descriptor()297   int descriptor() const FMT_NOEXCEPT { return fd_; }
298 
299   // Closes the file.
300   void close();
301 
302   // Returns the file size.
303   LongLong size() const;
304 
305   // Attempts to read count bytes from the file into the specified buffer.
306   std::size_t read(void *buffer, std::size_t count);
307 
308   // Attempts to write count bytes from the specified buffer to the file.
309   std::size_t write(const void *buffer, std::size_t count);
310 
311   // Duplicates a file descriptor with the dup function and returns
312   // the duplicate as a file object.
313   static File dup(int fd);
314 
315   // Makes fd be the copy of this file descriptor, closing fd first if
316   // necessary.
317   void dup2(int fd);
318 
319   // Makes fd be the copy of this file descriptor, closing fd first if
320   // necessary.
321   void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
322 
323   // Creates a pipe setting up read_end and write_end file objects for reading
324   // and writing respectively.
325   static void pipe(File &read_end, File &write_end);
326 
327   // Creates a BufferedFile object associated with this file and detaches
328   // this File object from the file.
329   BufferedFile fdopen(const char *mode);
330 };
331 
332 // Returns the memory page size.
333 long getpagesize();
334 }  // namespace clmdep_fmt
335 
336 #if !FMT_USE_RVALUE_REFERENCES
337 namespace std {
338 // For compatibility with C++98.
move(clmdep_fmt::BufferedFile & f)339 inline clmdep_fmt::BufferedFile &move(clmdep_fmt::BufferedFile &f) { return f; }
move(clmdep_fmt::File & f)340 inline clmdep_fmt::File &move(clmdep_fmt::File &f) { return f; }
341 }
342 #endif
343 
344 #endif  // FMT_POSIX_H_
345