|
25 | 25 | #include "bson-string.h"
|
26 | 26 | #include "bson-types.h"
|
27 | 27 |
|
| 28 | +// See `bson_strerror_r()` definition below. |
| 29 | +#if !defined(_WIN32) && !defined(__APPLE__) |
| 30 | +#include <locale.h> // uselocale() |
| 31 | +#endif |
| 32 | + |
28 | 33 |
|
29 | 34 | /*
|
30 | 35 | *--------------------------------------------------------------------------
|
@@ -98,25 +103,71 @@ bson_set_error (bson_error_t *error, /* OUT */
|
98 | 103 | */
|
99 | 104 |
|
100 | 105 | char *
|
101 |
| -bson_strerror_r (int err_code, /* IN */ |
102 |
| - char *buf, /* IN */ |
103 |
| - size_t buflen) /* IN */ |
| 106 | +bson_strerror_r (int err_code, /* IN */ |
| 107 | + char *buf BSON_MAYBE_UNUSED, /* IN */ |
| 108 | + size_t buflen BSON_MAYBE_UNUSED) /* IN */ |
104 | 109 | {
|
105 | 110 | static const char *unknown_msg = "Unknown error";
|
106 | 111 | char *ret = NULL;
|
107 | 112 |
|
108 | 113 | #if defined(_WIN32)
|
| 114 | + // Windows does not provide `strerror_l` or `strerror_r`, but it does |
| 115 | + // unconditionally provide `strerror_s`. |
109 | 116 | if (strerror_s (buf, buflen, err_code) != 0) {
|
110 | 117 | ret = buf;
|
111 | 118 | }
|
112 |
| -#elif defined(BSON_HAVE_XSI_STRERROR_R) |
113 |
| - if (strerror_r (err_code, buf, buflen) == 0) { |
114 |
| - ret = buf; |
| 119 | +#elif defined(__APPLE__) |
| 120 | + // Apple does not provide `strerror_l`, but it does unconditionally provide |
| 121 | + // the XSI-compliant `strerror_r`, but only when compiling with Apple Clang. |
| 122 | + // GNU extensions may still be a problem if we are being compiled with GCC on |
| 123 | + // Apple. Avoid the compatibility headaches with GNU extensions and the musl |
| 124 | + // library by assuming the implementation will not cause UB when reading the |
| 125 | + // error message string even when `strerror_r` fails, as encouraged (but not |
| 126 | + // required) by the POSIX spec (see: |
| 127 | + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html#tag_16_574_08). |
| 128 | + (void) strerror_r (err_code, buf, buflen); |
| 129 | +#elif defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700 |
| 130 | + // The behavior (of `strerror_l`) is undefined if the locale argument to |
| 131 | + // `strerror_l()` is the special locale object LC_GLOBAL_LOCALE or is not a |
| 132 | + // valid locale object handle. |
| 133 | + locale_t locale = uselocale ((locale_t) 0); |
| 134 | + // No need to test for error (it can only be [EINVAL]). |
| 135 | + if (locale == LC_GLOBAL_LOCALE) { |
| 136 | + // Only use our own locale if a thread-local locale was not already set. |
| 137 | + // This is just to satisfy `strerror_l`. We do NOT want to unconditionally |
| 138 | + // set a thread-local locale. |
| 139 | + locale = newlocale (LC_MESSAGES_MASK, "C", (locale_t) 0); |
| 140 | + } |
| 141 | + BSON_ASSERT (locale != LC_GLOBAL_LOCALE); |
| 142 | + |
| 143 | + // Avoid `strerror_r` compatibility headaches with GNU extensions and the |
| 144 | + // musl library by using `strerror_l` instead. Furthermore, `strerror_r` is |
| 145 | + // scheduled to be marked as obsolete in favor of `strerror_l` in the |
| 146 | + // upcoming POSIX Issue 8 (see: |
| 147 | + // https://www.austingroupbugs.net/view.php?id=655). |
| 148 | + // |
| 149 | + // POSIX Spec: since strerror_l() is required to return a string for some |
| 150 | + // errors, an application wishing to check for all error situations should |
| 151 | + // set errno to 0, then call strerror_l(), then check errno. |
| 152 | + if (locale != (locale_t) 0) { |
| 153 | + errno = 0; |
| 154 | + ret = strerror_l (err_code, locale); |
| 155 | + |
| 156 | + if (errno != 0) { |
| 157 | + ret = NULL; |
| 158 | + } |
| 159 | + |
| 160 | + freelocale (locale); |
| 161 | + } else { |
| 162 | + // Could not obtain a valid `locale_t` object to satisfy `strerror_l`. |
| 163 | + // Fallback to `bson_strncpy` below. |
115 | 164 | }
|
116 | 165 | #elif defined(_GNU_SOURCE)
|
| 166 | + // Unlikely, but continue supporting use of GNU extension in cases where the |
| 167 | + // C Driver is being built without _XOPEN_SOURCE=700. |
117 | 168 | ret = strerror_r (err_code, buf, buflen);
|
118 | 169 | #else
|
119 |
| - #error "Unable to find a supported strerror_r candidate" |
| 170 | +#error "Unable to find a supported strerror_r candidate" |
120 | 171 | #endif
|
121 | 172 |
|
122 | 173 | if (!ret) {
|
|
0 commit comments