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