Mercurial > minori
comparison dep/fmt/src/os.cc @ 329:4aeffed717ef
dep/fmt: add dependency
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Mon, 17 Jun 2024 04:54:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
328:71396ecb6f7e | 329:4aeffed717ef |
---|---|
1 // Formatting library for C++ - optional OS-specific functionality | |
2 // | |
3 // Copyright (c) 2012 - 2016, Victor Zverovich | |
4 // All rights reserved. | |
5 // | |
6 // For the license information refer to format.h. | |
7 | |
8 // Disable bogus MSVC warnings. | |
9 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) | |
10 # define _CRT_SECURE_NO_WARNINGS | |
11 #endif | |
12 | |
13 #include "fmt/os.h" | |
14 | |
15 #include <climits> | |
16 | |
17 #if FMT_USE_FCNTL | |
18 # include <sys/stat.h> | |
19 # include <sys/types.h> | |
20 | |
21 # ifdef _WRS_KERNEL // VxWorks7 kernel | |
22 # include <ioLib.h> // getpagesize | |
23 # endif | |
24 | |
25 # ifndef _WIN32 | |
26 # include <unistd.h> | |
27 # else | |
28 # ifndef WIN32_LEAN_AND_MEAN | |
29 # define WIN32_LEAN_AND_MEAN | |
30 # endif | |
31 # include <io.h> | |
32 | |
33 # ifndef S_IRUSR | |
34 # define S_IRUSR _S_IREAD | |
35 # endif | |
36 # ifndef S_IWUSR | |
37 # define S_IWUSR _S_IWRITE | |
38 # endif | |
39 # ifndef S_IRGRP | |
40 # define S_IRGRP 0 | |
41 # endif | |
42 # ifndef S_IWGRP | |
43 # define S_IWGRP 0 | |
44 # endif | |
45 # ifndef S_IROTH | |
46 # define S_IROTH 0 | |
47 # endif | |
48 # ifndef S_IWOTH | |
49 # define S_IWOTH 0 | |
50 # endif | |
51 # endif // _WIN32 | |
52 #endif // FMT_USE_FCNTL | |
53 | |
54 #ifdef _WIN32 | |
55 # include <windows.h> | |
56 #endif | |
57 | |
58 namespace { | |
59 #ifdef _WIN32 | |
60 // Return type of read and write functions. | |
61 using rwresult = int; | |
62 | |
63 // On Windows the count argument to read and write is unsigned, so convert | |
64 // it from size_t preventing integer overflow. | |
65 inline unsigned convert_rwcount(std::size_t count) { | |
66 return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; | |
67 } | |
68 #elif FMT_USE_FCNTL | |
69 // Return type of read and write functions. | |
70 using rwresult = ssize_t; | |
71 | |
72 inline std::size_t convert_rwcount(std::size_t count) { return count; } | |
73 #endif | |
74 } // namespace | |
75 | |
76 FMT_BEGIN_NAMESPACE | |
77 | |
78 #ifdef _WIN32 | |
79 namespace detail { | |
80 | |
81 class system_message { | |
82 system_message(const system_message&) = delete; | |
83 void operator=(const system_message&) = delete; | |
84 | |
85 unsigned long result_; | |
86 wchar_t* message_; | |
87 | |
88 static bool is_whitespace(wchar_t c) noexcept { | |
89 return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; | |
90 } | |
91 | |
92 public: | |
93 explicit system_message(unsigned long error_code) | |
94 : result_(0), message_(nullptr) { | |
95 result_ = FormatMessageW( | |
96 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | |
97 FORMAT_MESSAGE_IGNORE_INSERTS, | |
98 nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
99 reinterpret_cast<wchar_t*>(&message_), 0, nullptr); | |
100 if (result_ != 0) { | |
101 while (result_ != 0 && is_whitespace(message_[result_ - 1])) { | |
102 --result_; | |
103 } | |
104 } | |
105 } | |
106 ~system_message() { LocalFree(message_); } | |
107 explicit operator bool() const noexcept { return result_ != 0; } | |
108 operator basic_string_view<wchar_t>() const noexcept { | |
109 return basic_string_view<wchar_t>(message_, result_); | |
110 } | |
111 }; | |
112 | |
113 class utf8_system_category final : public std::error_category { | |
114 public: | |
115 const char* name() const noexcept override { return "system"; } | |
116 std::string message(int error_code) const override { | |
117 auto&& msg = system_message(error_code); | |
118 if (msg) { | |
119 auto utf8_message = to_utf8<wchar_t>(); | |
120 if (utf8_message.convert(msg)) { | |
121 return utf8_message.str(); | |
122 } | |
123 } | |
124 return "unknown error"; | |
125 } | |
126 }; | |
127 | |
128 } // namespace detail | |
129 | |
130 FMT_API const std::error_category& system_category() noexcept { | |
131 static const detail::utf8_system_category category; | |
132 return category; | |
133 } | |
134 | |
135 std::system_error vwindows_error(int err_code, string_view format_str, | |
136 format_args args) { | |
137 auto ec = std::error_code(err_code, system_category()); | |
138 return std::system_error(ec, vformat(format_str, args)); | |
139 } | |
140 | |
141 void detail::format_windows_error(detail::buffer<char>& out, int error_code, | |
142 const char* message) noexcept { | |
143 FMT_TRY { | |
144 auto&& msg = system_message(error_code); | |
145 if (msg) { | |
146 auto utf8_message = to_utf8<wchar_t>(); | |
147 if (utf8_message.convert(msg)) { | |
148 fmt::format_to(appender(out), FMT_STRING("{}: {}"), message, | |
149 string_view(utf8_message)); | |
150 return; | |
151 } | |
152 } | |
153 } | |
154 FMT_CATCH(...) {} | |
155 format_error_code(out, error_code, message); | |
156 } | |
157 | |
158 void report_windows_error(int error_code, const char* message) noexcept { | |
159 report_error(detail::format_windows_error, error_code, message); | |
160 } | |
161 #endif // _WIN32 | |
162 | |
163 buffered_file::~buffered_file() noexcept { | |
164 if (file_ && FMT_SYSTEM(fclose(file_)) != 0) | |
165 report_system_error(errno, "cannot close file"); | |
166 } | |
167 | |
168 buffered_file::buffered_file(cstring_view filename, cstring_view mode) { | |
169 FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), | |
170 nullptr); | |
171 if (!file_) | |
172 FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"), | |
173 filename.c_str())); | |
174 } | |
175 | |
176 void buffered_file::close() { | |
177 if (!file_) return; | |
178 int result = FMT_SYSTEM(fclose(file_)); | |
179 file_ = nullptr; | |
180 if (result != 0) | |
181 FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); | |
182 } | |
183 | |
184 int buffered_file::descriptor() const { | |
185 #if !defined(fileno) | |
186 int fd = FMT_POSIX_CALL(fileno(file_)); | |
187 #elif defined(FMT_HAS_SYSTEM) | |
188 // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. | |
189 # define FMT_DISABLE_MACRO | |
190 int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_)); | |
191 #else | |
192 int fd = fileno(file_); | |
193 #endif | |
194 if (fd == -1) | |
195 FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor"))); | |
196 return fd; | |
197 } | |
198 | |
199 #if FMT_USE_FCNTL | |
200 # ifdef _WIN32 | |
201 using mode_t = int; | |
202 # endif | |
203 constexpr mode_t default_open_mode = | |
204 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | |
205 | |
206 file::file(cstring_view path, int oflag) { | |
207 # if defined(_WIN32) && !defined(__MINGW32__) | |
208 fd_ = -1; | |
209 auto converted = detail::utf8_to_utf16(string_view(path.c_str())); | |
210 *this = file::open_windows_file(converted.c_str(), oflag); | |
211 # else | |
212 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode))); | |
213 if (fd_ == -1) | |
214 FMT_THROW( | |
215 system_error(errno, FMT_STRING("cannot open file {}"), path.c_str())); | |
216 # endif | |
217 } | |
218 | |
219 file::~file() noexcept { | |
220 // Don't retry close in case of EINTR! | |
221 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |
222 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) | |
223 report_system_error(errno, "cannot close file"); | |
224 } | |
225 | |
226 void file::close() { | |
227 if (fd_ == -1) return; | |
228 // Don't retry close in case of EINTR! | |
229 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html | |
230 int result = FMT_POSIX_CALL(close(fd_)); | |
231 fd_ = -1; | |
232 if (result != 0) | |
233 FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); | |
234 } | |
235 | |
236 long long file::size() const { | |
237 # ifdef _WIN32 | |
238 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT | |
239 // is less than 0x0500 as is the case with some default MinGW builds. | |
240 // Both functions support large file sizes. | |
241 DWORD size_upper = 0; | |
242 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); | |
243 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); | |
244 if (size_lower == INVALID_FILE_SIZE) { | |
245 DWORD error = GetLastError(); | |
246 if (error != NO_ERROR) | |
247 FMT_THROW(windows_error(GetLastError(), "cannot get file size")); | |
248 } | |
249 unsigned long long long_size = size_upper; | |
250 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; | |
251 # else | |
252 using Stat = struct stat; | |
253 Stat file_stat = Stat(); | |
254 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) | |
255 FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes"))); | |
256 static_assert(sizeof(long long) >= sizeof(file_stat.st_size), | |
257 "return type of file::size is not large enough"); | |
258 return file_stat.st_size; | |
259 # endif | |
260 } | |
261 | |
262 std::size_t file::read(void* buffer, std::size_t count) { | |
263 rwresult result = 0; | |
264 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); | |
265 if (result < 0) | |
266 FMT_THROW(system_error(errno, FMT_STRING("cannot read from file"))); | |
267 return detail::to_unsigned(result); | |
268 } | |
269 | |
270 std::size_t file::write(const void* buffer, std::size_t count) { | |
271 rwresult result = 0; | |
272 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); | |
273 if (result < 0) | |
274 FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | |
275 return detail::to_unsigned(result); | |
276 } | |
277 | |
278 file file::dup(int fd) { | |
279 // Don't retry as dup doesn't return EINTR. | |
280 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html | |
281 int new_fd = FMT_POSIX_CALL(dup(fd)); | |
282 if (new_fd == -1) | |
283 FMT_THROW(system_error( | |
284 errno, FMT_STRING("cannot duplicate file descriptor {}"), fd)); | |
285 return file(new_fd); | |
286 } | |
287 | |
288 void file::dup2(int fd) { | |
289 int result = 0; | |
290 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |
291 if (result == -1) { | |
292 FMT_THROW(system_error( | |
293 errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_, | |
294 fd)); | |
295 } | |
296 } | |
297 | |
298 void file::dup2(int fd, std::error_code& ec) noexcept { | |
299 int result = 0; | |
300 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); | |
301 if (result == -1) ec = std::error_code(errno, std::generic_category()); | |
302 } | |
303 | |
304 void file::pipe(file& read_end, file& write_end) { | |
305 // Close the descriptors first to make sure that assignments don't throw | |
306 // and there are no leaks. | |
307 read_end.close(); | |
308 write_end.close(); | |
309 int fds[2] = {}; | |
310 # ifdef _WIN32 | |
311 // Make the default pipe capacity same as on Linux 2.6.11+. | |
312 enum { DEFAULT_CAPACITY = 65536 }; | |
313 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | |
314 # else | |
315 // Don't retry as the pipe function doesn't return EINTR. | |
316 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | |
317 int result = FMT_POSIX_CALL(pipe(fds)); | |
318 # endif | |
319 if (result != 0) | |
320 FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); | |
321 // The following assignments don't throw because read_fd and write_fd | |
322 // are closed. | |
323 read_end = file(fds[0]); | |
324 write_end = file(fds[1]); | |
325 } | |
326 | |
327 buffered_file file::fdopen(const char* mode) { | |
328 // Don't retry as fdopen doesn't return EINTR. | |
329 # if defined(__MINGW32__) && defined(_POSIX_) | |
330 FILE* f = ::fdopen(fd_, mode); | |
331 # else | |
332 FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); | |
333 # endif | |
334 if (!f) { | |
335 FMT_THROW(system_error( | |
336 errno, FMT_STRING("cannot associate stream with file descriptor"))); | |
337 } | |
338 buffered_file bf(f); | |
339 fd_ = -1; | |
340 return bf; | |
341 } | |
342 | |
343 # if defined(_WIN32) && !defined(__MINGW32__) | |
344 file file::open_windows_file(wcstring_view path, int oflag) { | |
345 int fd = -1; | |
346 auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode); | |
347 if (fd == -1) { | |
348 FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"), | |
349 detail::to_utf8<wchar_t>(path.c_str()).c_str())); | |
350 } | |
351 return file(fd); | |
352 } | |
353 # endif | |
354 | |
355 # if !defined(__MSDOS__) | |
356 long getpagesize() { | |
357 # ifdef _WIN32 | |
358 SYSTEM_INFO si; | |
359 GetSystemInfo(&si); | |
360 return si.dwPageSize; | |
361 # else | |
362 # ifdef _WRS_KERNEL | |
363 long size = FMT_POSIX_CALL(getpagesize()); | |
364 # else | |
365 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); | |
366 # endif | |
367 | |
368 if (size < 0) | |
369 FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size"))); | |
370 return size; | |
371 # endif | |
372 } | |
373 # endif | |
374 | |
375 namespace detail { | |
376 | |
377 void file_buffer::grow(size_t) { | |
378 if (this->size() == this->capacity()) flush(); | |
379 } | |
380 | |
381 file_buffer::file_buffer(cstring_view path, | |
382 const detail::ostream_params& params) | |
383 : file_(path, params.oflag) { | |
384 set(new char[params.buffer_size], params.buffer_size); | |
385 } | |
386 | |
387 file_buffer::file_buffer(file_buffer&& other) | |
388 : detail::buffer<char>(other.data(), other.size(), other.capacity()), | |
389 file_(std::move(other.file_)) { | |
390 other.clear(); | |
391 other.set(nullptr, 0); | |
392 } | |
393 | |
394 file_buffer::~file_buffer() { | |
395 flush(); | |
396 delete[] data(); | |
397 } | |
398 } // namespace detail | |
399 | |
400 ostream::~ostream() = default; | |
401 #endif // FMT_USE_FCNTL | |
402 FMT_END_NAMESPACE |