| // <system_error> implementation file | |
| // Copyright (C) 2007-2025 Free Software Foundation, Inc. | |
| // | |
| // This file is part of the GNU ISO C++ Library. This library is free | |
| // software; you can redistribute it and/or modify it under the | |
| // terms of the GNU General Public License as published by the | |
| // Free Software Foundation; either version 3, or (at your option) | |
| // any later version. | |
| // This library is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // GNU General Public License for more details. | |
| // Under Section 7 of GPL version 3, you are granted additional | |
| // permissions described in the GCC Runtime Library Exception, version | |
| // 3.1, as published by the Free Software Foundation. | |
| // You should have received a copy of the GNU General Public License and | |
| // a copy of the GCC Runtime Library Exception along with this program; | |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| // <http://www.gnu.org/licenses/>. | |
| #define _GLIBCXX_USE_CXX11_ABI 1 | |
| #define __sso_string __sso_stringxxx | |
| #include <cstring> | |
| #include <system_error> | |
| #include <bits/functexcept.h> | |
| #include <limits> | |
| #include <errno.h> | |
| #undef __sso_string | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| #define WIN32_LEAN_AND_MEAN | |
| #include <memory> | |
| #include <windows.h> | |
| #endif | |
| #if __has_cpp_attribute(clang::require_constant_initialization) | |
| # define __constinit [[clang::require_constant_initialization]] | |
| #endif | |
| namespace | |
| { | |
| using std::string; | |
| #if _GLIBCXX_HAVE_STRERROR_R | |
| // Handle the result of POSIX strerror_r. | |
| inline std::size_t | |
| use_strerror_result(int res, char* buf, std::size_t bufsz, | |
| std::size_t& nextbufsz) | |
| { | |
| if (res == 0) // Success. | |
| return std::strlen(buf); | |
| if (res == ERANGE) // Buffer too small to hold result string. | |
| { | |
| nextbufsz = 2 * bufsz; | |
| return 0; | |
| } | |
| // else res == EINVAL, unknown error. | |
| if (*buf == '\0') // No result string written to buffer. | |
| { | |
| const char msg[] = "Unknown error"; | |
| std::memcpy(buf, msg, sizeof(msg) - 1); | |
| return sizeof(msg) - 1; | |
| } | |
| else // An "unknown error" string was already written to the buffer. | |
| return std::strlen(buf); | |
| } | |
| // Handle the result of GNU strerror_r. | |
| inline std::size_t | |
| use_strerror_result(char* res, char* buf, std::size_t bufsz, | |
| std::size_t& nextbufsz) | |
| { | |
| if (res == buf) // Result string written to the buffer. | |
| return std::strlen(res); | |
| // Static result string returned, must be copied to the buffer. | |
| std::size_t len = std::strlen(res); | |
| if (len <= bufsz) | |
| { | |
| std::strcpy(buf, res); | |
| return len; | |
| } | |
| // Reallocate and try again: | |
| nextbufsz = len; | |
| return 0; | |
| } | |
| string strerror_string(int err) | |
| { | |
| // Estimate maximum length of strerror strings (including "Unknown error"). | |
| // Any excess capacity here can be used by std::system_error constructors | |
| // when concatenating strings. | |
| std::size_t len = 60; | |
| string s; | |
| do | |
| s.__resize_and_overwrite(len, [err, &len](char* p, std::size_t n) { | |
| *p = '\0'; | |
| return use_strerror_result(strerror_r(err, p, n), p, n, len); | |
| }); | |
| while (s.empty()); | |
| return s; | |
| } | |
| #else | |
| string strerror_string(int err) | |
| { | |
| auto str = strerror(err); // XXX Not thread-safe. | |
| if (str) [[__likely__]] | |
| return str; | |
| // strerror should not return NULL, but some implementations do. | |
| return "Unknown error"; | |
| } | |
| #endif | |
| template<typename T> | |
| struct constant_init | |
| { | |
| union { | |
| T obj; | |
| }; | |
| constexpr constant_init() : obj() { } | |
| ~constant_init() { /* do nothing, union member is not destroyed */ } | |
| }; | |
| struct generic_error_category final : public std::error_category | |
| { | |
| const char* | |
| name() const noexcept final | |
| { return "generic"; } | |
| _GLIBCXX_DEFAULT_ABI_TAG | |
| string | |
| message(int i) const final | |
| { return strerror_string(i); } | |
| // Override this to avoid a virtual call to default_error_condition(i). | |
| bool | |
| equivalent(int i, const std::error_condition& cond) const noexcept final | |
| { return i == cond.value() && *this == cond.category(); } | |
| }; | |
| __constinit constant_init<generic_error_category> generic_category_instance{}; | |
| struct system_error_category final : public std::error_category | |
| { | |
| const char* | |
| name() const noexcept final | |
| { return "system"; } | |
| _GLIBCXX_DEFAULT_ABI_TAG | |
| string | |
| message(int i) const final | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| char* buf = nullptr; | |
| auto len | |
| = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | |
| | FORMAT_MESSAGE_ALLOCATE_BUFFER, | |
| nullptr, | |
| i, | |
| LANG_USER_DEFAULT, | |
| reinterpret_cast<LPTSTR>(&buf), | |
| 0, | |
| nullptr); | |
| if (len > 0) | |
| { | |
| struct deleter { | |
| void operator()(void* p) const { ::LocalFree(p); } | |
| }; | |
| std::unique_ptr<char[], deleter> guard(buf); | |
| if (len > 3 && !__builtin_memcmp(buf + len - 3, ".\r\n", 3)) [[likely]] | |
| len -= 3; | |
| return string(buf, len); | |
| } | |
| return string("Unknown error code"); | |
| #else | |
| return strerror_string(i); | |
| #endif | |
| } | |
| std::error_condition | |
| default_error_condition(int ev) const noexcept final | |
| { | |
| // Use generic category for all known POSIX errno values (including zero) | |
| // and system category otherwise. | |
| switch (ev) | |
| { | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| case 0: | |
| return {0, generic_category_instance.obj}; | |
| // Convert Windows error code into a corresponding POSIX errno value. | |
| #define X(w, e) case ERROR_##w: return {e, generic_category_instance.obj}; | |
| // This list is based on Cygwin's winsup/cygwin/errno.cc | |
| X (ACCESS_DENIED, EACCES); | |
| X (ACTIVE_CONNECTIONS, EAGAIN); | |
| X (ALREADY_EXISTS, EEXIST); | |
| X (BAD_DEVICE, ENODEV); | |
| X (BAD_EXE_FORMAT, ENOEXEC); | |
| X (BAD_NETPATH, ENOENT); | |
| X (BAD_NET_NAME, ENOENT); | |
| X (BAD_NET_RESP, ENOSYS); | |
| X (BAD_PATHNAME, ENOENT); | |
| X (BAD_PIPE, EINVAL); | |
| X (BAD_UNIT, ENODEV); | |
| X (BAD_USERNAME, EINVAL); | |
| X (BEGINNING_OF_MEDIA, EIO); | |
| X (BROKEN_PIPE, EPIPE); | |
| X (BUSY, EBUSY); | |
| X (BUS_RESET, EIO); | |
| X (CALL_NOT_IMPLEMENTED, ENOSYS); | |
| X (CANCELLED, EINTR); | |
| X (CANNOT_MAKE, EPERM); | |
| X (CHILD_NOT_COMPLETE, EBUSY); | |
| X (COMMITMENT_LIMIT, EAGAIN); | |
| X (CONNECTION_REFUSED, ECONNREFUSED); | |
| X (CRC, EIO); | |
| X (DEVICE_DOOR_OPEN, EIO); | |
| X (DEVICE_IN_USE, EAGAIN); | |
| X (DEVICE_REQUIRES_CLEANING, EIO); | |
| X (DEV_NOT_EXIST, ENOENT); | |
| X (DIRECTORY, ENOTDIR); | |
| X (DIR_NOT_EMPTY, ENOTEMPTY); | |
| X (DISK_CORRUPT, EIO); | |
| #ifdef ENOSPC | |
| X (DISK_FULL, ENOSPC); | |
| #endif | |
| X (DS_GENERIC_ERROR, EIO); | |
| #ifdef ENOSPC | |
| X (END_OF_MEDIA, ENOSPC); | |
| #endif | |
| X (EOM_OVERFLOW, EIO); | |
| X (EXE_MACHINE_TYPE_MISMATCH, ENOEXEC); | |
| X (EXE_MARKED_INVALID, ENOEXEC); | |
| X (FILEMARK_DETECTED, EIO); | |
| X (FILENAME_EXCED_RANGE, ENAMETOOLONG); | |
| X (FILE_CORRUPT, EEXIST); | |
| X (FILE_EXISTS, EEXIST); | |
| X (FILE_INVALID, ENXIO); | |
| X (FILE_NOT_FOUND, ENOENT); | |
| #ifdef ENOSPC | |
| X (HANDLE_DISK_FULL, ENOSPC); | |
| #endif | |
| X (INVALID_ADDRESS, EINVAL); | |
| X (INVALID_AT_INTERRUPT_TIME, EINTR); | |
| X (INVALID_BLOCK_LENGTH, EIO); | |
| X (INVALID_DATA, EINVAL); | |
| X (INVALID_DRIVE, ENODEV); | |
| X (INVALID_EA_NAME, EINVAL); | |
| X (INVALID_EXE_SIGNATURE, ENOEXEC); | |
| X (INVALID_HANDLE, EBADF); | |
| X (INVALID_NAME, ENOENT); | |
| X (INVALID_PARAMETER, EINVAL); | |
| X (INVALID_SIGNAL_NUMBER, EINVAL); | |
| X (IOPL_NOT_ENABLED, ENOEXEC); | |
| X (IO_DEVICE, EIO); | |
| X (IO_INCOMPLETE, EAGAIN); | |
| X (IO_PENDING, EAGAIN); | |
| X (LOCK_VIOLATION, EBUSY); | |
| X (MAX_THRDS_REACHED, EAGAIN); | |
| X (META_EXPANSION_TOO_LONG, EINVAL); | |
| X (MOD_NOT_FOUND, ENOENT); | |
| X (MORE_DATA, EMSGSIZE); | |
| X (NEGATIVE_SEEK, EINVAL); | |
| X (NETNAME_DELETED, ENOENT); | |
| X (NOACCESS, EFAULT); | |
| X (NONE_MAPPED, EINVAL); | |
| X (NONPAGED_SYSTEM_RESOURCES, EAGAIN); | |
| X (NOT_ENOUGH_MEMORY, ENOMEM); | |
| X (NOT_ENOUGH_QUOTA, EIO); | |
| #ifdef EPERM | |
| X (NOT_OWNER, EPERM); | |
| #else | |
| X (NOT_OWNER, EACCES); | |
| #endif | |
| X (NOT_SAME_DEVICE, EXDEV); | |
| X (NOT_SUPPORTED, ENOSYS); | |
| X (NO_DATA, EPIPE); | |
| X (NO_DATA_DETECTED, EIO); | |
| X (NO_MORE_SEARCH_HANDLES, ENFILE); | |
| X (NO_PROC_SLOTS, EAGAIN); | |
| X (NO_SIGNAL_SENT, EIO); | |
| X (NO_SYSTEM_RESOURCES, EFBIG); | |
| X (NO_TOKEN, EINVAL); | |
| X (OPEN_FAILED, EIO); | |
| X (OPEN_FILES, EAGAIN); | |
| X (OUTOFMEMORY, ENOMEM); | |
| X (PAGED_SYSTEM_RESOURCES, EAGAIN); | |
| X (PAGEFILE_QUOTA, EAGAIN); | |
| X (PATH_NOT_FOUND, ENOENT); | |
| X (PIPE_BUSY, EBUSY); | |
| X (PIPE_CONNECTED, EBUSY); | |
| X (POSSIBLE_DEADLOCK, EDEADLK); | |
| X (PRIVILEGE_NOT_HELD, EPERM); | |
| X (PROCESS_ABORTED, EFAULT); | |
| X (PROC_NOT_FOUND, ESRCH); | |
| X (SECTOR_NOT_FOUND, EINVAL); | |
| X (SEEK, EINVAL); | |
| X (SERVICE_REQUEST_TIMEOUT, EBUSY); | |
| X (SETMARK_DETECTED, EIO); | |
| X (SHARING_BUFFER_EXCEEDED, ENOLCK); | |
| X (SHARING_VIOLATION, EBUSY); | |
| X (SIGNAL_PENDING, EBUSY); | |
| X (SIGNAL_REFUSED, EIO); | |
| X (THREAD_1_INACTIVE, EINVAL); | |
| X (TIMEOUT, EBUSY); | |
| X (TOO_MANY_LINKS, EMLINK); | |
| X (TOO_MANY_OPEN_FILES, EMFILE); | |
| X (UNEXP_NET_ERR, EIO); | |
| X (WORKING_SET_QUOTA, EAGAIN); | |
| X (WRITE_PROTECT, EROFS); | |
| #undef X | |
| #elif defined __AVR__ | |
| // avr-libc only defines a few distinct error numbers. Most <errno.h> | |
| // constants are not usable in #if directives and have the same value. | |
| case EDOM: | |
| case ERANGE: | |
| case ENOSYS: | |
| case EINTR: | |
| case 0: | |
| return std::error_condition(ev, generic_category_instance.obj); | |
| #else | |
| // List of errno macros from [cerrno.syn]. | |
| // C11 only defines EDOM, EILSEQ and ERANGE, the rest are from POSIX. | |
| // They expand to integer constant expressions with type int, | |
| // and distinct positive values, suitable for use in #if directives. | |
| // POSIX adds more macros (but they're not defined on all targets, | |
| // see config/os/.../error_constants.h), and POSIX allows | |
| // EAGAIN == EWOULDBLOCK and ENOTSUP == EOPNOTSUPP. | |
| #ifdef E2BIG | |
| case E2BIG: | |
| #endif | |
| #ifdef EACCES | |
| case EACCES: | |
| #endif | |
| #ifdef EADDRINUSE | |
| case EADDRINUSE: | |
| #endif | |
| #ifdef EADDRNOTAVAIL | |
| case EADDRNOTAVAIL: | |
| #endif | |
| #ifdef EAFNOSUPPORT | |
| case EAFNOSUPPORT: | |
| #endif | |
| #ifdef EAGAIN | |
| case EAGAIN: | |
| #endif | |
| #ifdef EALREADY | |
| case EALREADY: | |
| #endif | |
| #ifdef EBADF | |
| case EBADF: | |
| #endif | |
| #ifdef EBADMSG | |
| case EBADMSG: | |
| #endif | |
| #ifdef EBUSY | |
| case EBUSY: | |
| #endif | |
| #ifdef ECANCELED | |
| case ECANCELED: | |
| #endif | |
| #ifdef ECHILD | |
| case ECHILD: | |
| #endif | |
| #ifdef ECONNABORTED | |
| case ECONNABORTED: | |
| #endif | |
| #ifdef ECONNREFUSED | |
| case ECONNREFUSED: | |
| #endif | |
| #ifdef ECONNRESET | |
| case ECONNRESET: | |
| #endif | |
| #ifdef EDEADLK | |
| case EDEADLK: | |
| #endif | |
| #ifdef EDESTADDRREQ | |
| case EDESTADDRREQ: | |
| #endif | |
| case EDOM: | |
| #ifdef EEXIST | |
| case EEXIST: | |
| #endif | |
| #ifdef EFAULT | |
| case EFAULT: | |
| #endif | |
| #ifdef EFBIG | |
| case EFBIG: | |
| #endif | |
| #ifdef EHOSTUNREACH | |
| case EHOSTUNREACH: | |
| #endif | |
| #ifdef EIDRM | |
| case EIDRM: | |
| #endif | |
| case EILSEQ: | |
| #ifdef EINPROGRESS | |
| case EINPROGRESS: | |
| #endif | |
| #ifdef EINTR | |
| case EINTR: | |
| #endif | |
| #ifdef EINVAL | |
| case EINVAL: | |
| #endif | |
| #ifdef EIO | |
| case EIO: | |
| #endif | |
| #ifdef EISCONN | |
| case EISCONN: | |
| #endif | |
| #ifdef EISDIR | |
| case EISDIR: | |
| #endif | |
| #ifdef ELOOP | |
| case ELOOP: | |
| #endif | |
| #ifdef EMFILE | |
| case EMFILE: | |
| #endif | |
| #ifdef EMLINK | |
| case EMLINK: | |
| #endif | |
| #ifdef EMSGSIZE | |
| case EMSGSIZE: | |
| #endif | |
| #ifdef ENAMETOOLONG | |
| case ENAMETOOLONG: | |
| #endif | |
| #ifdef ENETDOWN | |
| case ENETDOWN: | |
| #endif | |
| #ifdef ENETRESET | |
| case ENETRESET: | |
| #endif | |
| #ifdef ENETUNREACH | |
| case ENETUNREACH: | |
| #endif | |
| #ifdef ENFILE | |
| case ENFILE: | |
| #endif | |
| #ifdef ENOBUFS | |
| case ENOBUFS: | |
| #endif | |
| #ifdef ENODATA | |
| case ENODATA: | |
| #endif | |
| #ifdef ENODEV | |
| case ENODEV: | |
| #endif | |
| #ifdef ENOENT | |
| case ENOENT: | |
| #endif | |
| #ifdef ENOEXEC | |
| case ENOEXEC: | |
| #endif | |
| #ifdef ENOLCK | |
| case ENOLCK: | |
| #endif | |
| #ifdef ENOLINK | |
| case ENOLINK: | |
| #endif | |
| #ifdef ENOMEM | |
| case ENOMEM: | |
| #endif | |
| #ifdef ENOMSG | |
| case ENOMSG: | |
| #endif | |
| #ifdef ENOPROTOOPT | |
| case ENOPROTOOPT: | |
| #endif | |
| #ifdef ENOSPC | |
| case ENOSPC: | |
| #endif | |
| #ifdef ENOSR | |
| case ENOSR: | |
| #endif | |
| #ifdef ENOSTR | |
| case ENOSTR: | |
| #endif | |
| #ifdef ENOSYS | |
| case ENOSYS: | |
| #endif | |
| #ifdef ENOTCONN | |
| case ENOTCONN: | |
| #endif | |
| #ifdef ENOTDIR | |
| case ENOTDIR: | |
| #endif | |
| #if defined ENOTEMPTY && (!defined EEXIST || ENOTEMPTY != EEXIST) | |
| // AIX sometimes uses the same value for EEXIST and ENOTEMPTY | |
| case ENOTEMPTY: | |
| #endif | |
| #ifdef ENOTRECOVERABLE | |
| case ENOTRECOVERABLE: | |
| #endif | |
| #ifdef ENOTSOCK | |
| case ENOTSOCK: | |
| #endif | |
| #if defined ENOTSUP && (!defined ENOSYS || ENOTSUP != ENOSYS) | |
| // zTPF uses the same value for ENOSYS and ENOTSUP | |
| case ENOTSUP: | |
| #endif | |
| #ifdef ENOTTY | |
| case ENOTTY: | |
| #endif | |
| #ifdef ENXIO | |
| case ENXIO: | |
| #endif | |
| #if defined EOPNOTSUPP && (!defined ENOTSUP || EOPNOTSUPP != ENOTSUP) | |
| case EOPNOTSUPP: | |
| #endif | |
| #ifdef EOVERFLOW | |
| case EOVERFLOW: | |
| #endif | |
| #ifdef EOWNERDEAD | |
| case EOWNERDEAD: | |
| #endif | |
| #ifdef EPERM | |
| case EPERM: | |
| #endif | |
| #ifdef EPIPE | |
| case EPIPE: | |
| #endif | |
| #ifdef EPROTO | |
| case EPROTO: | |
| #endif | |
| #ifdef EPROTONOSUPPORT | |
| case EPROTONOSUPPORT: | |
| #endif | |
| #ifdef EPROTOTYPE | |
| case EPROTOTYPE: | |
| #endif | |
| case ERANGE: | |
| #ifdef EROFS | |
| case EROFS: | |
| #endif | |
| #ifdef ESPIPE | |
| case ESPIPE: | |
| #endif | |
| #ifdef ESRCH | |
| case ESRCH: | |
| #endif | |
| #ifdef ETIME | |
| case ETIME: | |
| #endif | |
| #ifdef ETIMEDOUT | |
| case ETIMEDOUT: | |
| #endif | |
| #ifdef ETXTBSY | |
| case ETXTBSY: | |
| #endif | |
| #if defined EWOULDBLOCK && (!defined EAGAIN || EWOULDBLOCK != EAGAIN) | |
| case EWOULDBLOCK: | |
| #endif | |
| #ifdef EXDEV | |
| case EXDEV: | |
| #endif | |
| case 0: | |
| return std::error_condition(ev, generic_category_instance.obj); | |
| /* Additional system-dependent mappings from non-standard error codes | |
| * to one of the POSIX values above would go here, e.g. | |
| case EBLAH: | |
| return std::error_condition(EINVAL, std::generic_category()); | |
| */ | |
| #endif | |
| default: | |
| return std::error_condition(ev, *this); | |
| } | |
| } | |
| // Override this to avoid a virtual call to default_error_condition(i). | |
| bool | |
| equivalent(int i, const std::error_condition& cond) const noexcept final | |
| { return system_error_category::default_error_condition(i) == cond; } | |
| }; | |
| __constinit constant_init<system_error_category> system_category_instance{}; | |
| } | |
| namespace std _GLIBCXX_VISIBILITY(default) | |
| { | |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | |
| void | |
| __throw_system_error(int __i __attribute__((unused))) | |
| { | |
| _GLIBCXX_THROW_OR_ABORT(system_error(__i, generic_category_instance.obj)); | |
| } | |
| error_category::~error_category() = default; | |
| _GLIBCXX_BEGIN_INLINE_ABI_NAMESPACE(_V2) | |
| const error_category& | |
| system_category() noexcept { return system_category_instance.obj; } | |
| const error_category& | |
| generic_category() noexcept { return generic_category_instance.obj; } | |
| _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2) | |
| system_error::~system_error() = default; | |
| error_condition | |
| error_category::default_error_condition(int __i) const noexcept | |
| { return error_condition(__i, *this); } | |
| bool | |
| error_category::equivalent(int __i, | |
| const error_condition& __cond) const noexcept | |
| { return default_error_condition(__i) == __cond; } | |
| bool | |
| error_category::equivalent(const error_code& __code, int __i) const noexcept | |
| { return *this == __code.category() && __code.value() == __i; } | |
| error_condition | |
| error_code::default_error_condition() const noexcept | |
| { return category().default_error_condition(value()); } | |
| #if _GLIBCXX_USE_CXX11_ABI | |
| // Return error_category::message() as a COW string | |
| __cow_string | |
| error_category::_M_message(int i) const | |
| { | |
| string msg = this->message(i); | |
| return {msg.c_str(), msg.length()}; | |
| } | |
| #endif | |
| _GLIBCXX_END_NAMESPACE_VERSION | |
| } // namespace |