|
1 |
| -#ifndef __STRICT_FSTREAM_HPP |
2 |
| -#define __STRICT_FSTREAM_HPP |
| 1 | +#pragma once |
3 | 2 |
|
4 | 3 | #include <cassert>
|
5 | 4 | #include <fstream>
|
6 | 5 | #include <cstring>
|
7 | 6 | #include <string>
|
| 7 | +#include <vector> |
8 | 8 |
|
9 | 9 | /**
|
10 | 10 | * This namespace defines wrappers for std::ifstream, std::ofstream, and
|
|
16 | 16 | */
|
17 | 17 | namespace strict_fstream
|
18 | 18 | {
|
19 |
| -/// Overload of error-reporting function, to enable use with VS (1) |
20 |
| -/// and POSIX signature found in MUSL on Alpine (2) |
21 |
| -/// Ref 1: http://stackoverflow.com/a/901316/717706 |
22 |
| -/// Ref 2: http://stackoverflow.com/a/41956165 |
23 |
| -static inline char* strerror_r_compat(int result, char* buffer, int err) |
24 |
| -{ |
25 |
| - if (result) |
26 |
| - { |
27 |
| - sprintf(buffer, "Unknown error: %d", err); |
28 |
| - } |
29 |
| - return buffer; |
30 |
| -} |
31 | 19 |
|
32 |
| -static inline char* strerror_r_compat(char* result, char*, int) |
| 20 | +// Help people out a bit, it seems like this is a common recommenation since |
| 21 | +// musl breaks all over the place. |
| 22 | +#if defined(__NEED_size_t) && !defined(__MUSL__) |
| 23 | +#warning "It seems to be recommended to patch in a define for __MUSL__ if you use musl globally: https://www.openwall.com/lists/musl/2013/02/10/5" |
| 24 | +#define __MUSL__ |
| 25 | +#endif |
| 26 | + |
| 27 | +// Workaround for broken musl implementation |
| 28 | +// Since musl insists that they are perfectly compatible, ironically enough, |
| 29 | +// they don't officially have a __musl__ or similar. But __NEED_size_t is defined in their |
| 30 | +// relevant header (and not in working implementations), so we can use that. |
| 31 | +#ifdef __MUSL__ |
| 32 | +#warning "Working around broken strerror_r() implementation in musl, remove when musl is fixed" |
| 33 | +#endif |
| 34 | + |
| 35 | +// Non-gnu variants of strerror_* don't necessarily null-terminate if |
| 36 | +// truncating, so we have to do things manually. |
| 37 | +inline std::string trim_to_null(const std::vector<char> &buff) |
33 | 38 | {
|
34 |
| - return result; |
| 39 | + std::string ret(buff.begin(), buff.end()); |
| 40 | + |
| 41 | + const std::string::size_type pos = ret.find('\0'); |
| 42 | + if (pos == std::string::npos) { |
| 43 | + ret += " [...]"; // it has been truncated |
| 44 | + } else { |
| 45 | + ret.resize(pos); |
| 46 | + } |
| 47 | + return ret; |
35 | 48 | }
|
36 | 49 |
|
| 50 | +/// Overload of error-reporting function, to enable use with VS and non-GNU |
| 51 | +/// POSIX libc's |
| 52 | +/// Ref: |
| 53 | +/// - http://stackoverflow.com/a/901316/717706 |
37 | 54 | static std::string strerror()
|
38 | 55 | {
|
39 |
| - std::string buff(80, '\0'); |
| 56 | + // Can't use std::string since we're pre-C++17 |
| 57 | + std::vector<char> buff(256, '\0'); |
| 58 | + |
40 | 59 | #ifdef _WIN32
|
41 |
| - if (strerror_s(&buff[0], buff.size(), errno) != 0) |
42 |
| - { |
43 |
| - buff = "Unknown error"; |
| 60 | + // Since strerror_s might set errno itself, we need to store it. |
| 61 | + const int err_num = errno; |
| 62 | + if (strerror_s(buff.data(), buff.size(), err_num) != 0) { |
| 63 | + return trim_to_null(buff); |
| 64 | + } else { |
| 65 | + return "Unknown error (" + std::to_string(err_num) + ")"; |
| 66 | + } |
| 67 | +#elif ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || defined(__APPLE__)) && ! _GNU_SOURCE) || defined(__MUSL__) |
| 68 | +// XSI-compliant strerror_r() |
| 69 | + const int err_num = errno; // See above |
| 70 | + if (strerror_r(err_num, buff.data(), buff.size()) == 0) { |
| 71 | + return trim_to_null(buff); |
| 72 | + } else { |
| 73 | + return "Unknown error (" + std::to_string(err_num) + ")"; |
44 | 74 | }
|
45 | 75 | #else
|
46 |
| - auto p = strerror_r_compat(strerror_r(errno, &buff[0], buff.size()), &buff[0], errno); |
47 |
| - std::string tmp(p, std::strlen(p)); |
48 |
| - std::swap(buff, tmp); |
| 76 | +// GNU-specific strerror_r() |
| 77 | + char * p = strerror_r(errno, &buff[0], buff.size()); |
| 78 | + return std::string(p, std::strlen(p)); |
49 | 79 | #endif
|
50 |
| - if(buff.find('\0') != std::string::npos) |
51 |
| - { |
52 |
| - buff.resize(buff.find('\0')); |
53 |
| - } |
54 |
| - return buff; |
55 | 80 | }
|
56 | 81 |
|
57 | 82 | /// Exception class thrown by failed operations.
|
@@ -136,7 +161,7 @@ struct static_method_holder
|
136 | 161 | is_p->peek();
|
137 | 162 | peek_failed = is_p->fail();
|
138 | 163 | }
|
139 |
| - catch (const std::ios_base::failure &e) {} |
| 164 | + catch (const std::ios_base::failure &) {} |
140 | 165 | if (peek_failed)
|
141 | 166 | {
|
142 | 167 | throw Exception(std::string("strict_fstream: open('")
|
@@ -210,4 +235,3 @@ class fstream
|
210 | 235 |
|
211 | 236 | } // namespace strict_fstream
|
212 | 237 |
|
213 |
| -#endif |
|
0 commit comments