xref: /freebsd-src/contrib/llvm-project/libcxx/src/system_error.cpp (revision 1db9f3b21e39176dd5b67cf8ac378633b172463e)
1349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
981ad6265SDimitry Andric #include <__assert>
105f757f3fSDimitry Andric #include <__config>
1106c3fb27SDimitry Andric #include <__verbose_abort>
1281ad6265SDimitry Andric #include <cerrno>
1381ad6265SDimitry Andric #include <cstdio>
1481ad6265SDimitry Andric #include <cstdlib>
1581ad6265SDimitry Andric #include <cstring>
1681ad6265SDimitry Andric #include <string.h>
17cb14a3feSDimitry Andric #include <string>
1881ad6265SDimitry Andric #include <system_error>
190b57cec5SDimitry Andric 
200b57cec5SDimitry Andric #include "include/config_elast.h"
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric #if defined(__ANDROID__)
230b57cec5SDimitry Andric #  include <android/api-level.h>
240b57cec5SDimitry Andric #endif
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric namespace {
295f757f3fSDimitry Andric #if !defined(_LIBCPP_HAS_NO_THREADS)
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric //  GLIBC also uses 1024 as the maximum buffer size internally.
320b57cec5SDimitry Andric constexpr size_t strerror_buff_size = 1024;
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric string do_strerror_r(int ev);
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric #  if defined(_LIBCPP_MSVCRT_LIKE)
do_strerror_r(int ev)370b57cec5SDimitry Andric string do_strerror_r(int ev) {
380b57cec5SDimitry Andric   char buffer[strerror_buff_size];
390b57cec5SDimitry Andric   if (::strerror_s(buffer, strerror_buff_size, ev) == 0)
400b57cec5SDimitry Andric     return string(buffer);
410b57cec5SDimitry Andric   std::snprintf(buffer, strerror_buff_size, "unknown error %d", ev);
420b57cec5SDimitry Andric   return string(buffer);
430b57cec5SDimitry Andric }
440b57cec5SDimitry Andric #  else
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric // Only one of the two following functions will be used, depending on
470b57cec5SDimitry Andric // the return type of strerror_r:
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric // For the GNU variant, a char* return value:
handle_strerror_r_return(char * strerror_return,char * buffer)50cb14a3feSDimitry Andric __attribute__((unused)) const char* handle_strerror_r_return(char* strerror_return, char* buffer) {
510b57cec5SDimitry Andric   // GNU always returns a string pointer in its return value. The
520b57cec5SDimitry Andric   // string might point to either the input buffer, or a static
530b57cec5SDimitry Andric   // buffer, but we don't care which.
540b57cec5SDimitry Andric   return strerror_return;
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric // For the POSIX variant: an int return value.
handle_strerror_r_return(int strerror_return,char * buffer)58cb14a3feSDimitry Andric __attribute__((unused)) const char* handle_strerror_r_return(int strerror_return, char* buffer) {
590b57cec5SDimitry Andric   // The POSIX variant either:
600b57cec5SDimitry Andric   // - fills in the provided buffer and returns 0
610b57cec5SDimitry Andric   // - returns a positive error value, or
620b57cec5SDimitry Andric   // - returns -1 and fills in errno with an error value.
630b57cec5SDimitry Andric   if (strerror_return == 0)
640b57cec5SDimitry Andric     return buffer;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   // Only handle EINVAL. Other errors abort.
670b57cec5SDimitry Andric   int new_errno = strerror_return == -1 ? errno : strerror_return;
680b57cec5SDimitry Andric   if (new_errno == EINVAL)
690b57cec5SDimitry Andric     return "";
700b57cec5SDimitry Andric 
71*1db9f3b2SDimitry Andric   _LIBCPP_ASSERT_INTERNAL(new_errno == ERANGE, "unexpected error from ::strerror_r");
720b57cec5SDimitry Andric   // FIXME maybe? 'strerror_buff_size' is likely to exceed the
730b57cec5SDimitry Andric   // maximum error size so ERANGE shouldn't be returned.
740b57cec5SDimitry Andric   std::abort();
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric // This function handles both GNU and POSIX variants, dispatching to
780b57cec5SDimitry Andric // one of the two above functions.
do_strerror_r(int ev)790b57cec5SDimitry Andric string do_strerror_r(int ev) {
800b57cec5SDimitry Andric   char buffer[strerror_buff_size];
810b57cec5SDimitry Andric   // Preserve errno around the call. (The C++ standard requires that
820b57cec5SDimitry Andric   // system_error functions not modify errno).
830b57cec5SDimitry Andric   const int old_errno       = errno;
84cb14a3feSDimitry Andric   const char* error_message = handle_strerror_r_return(::strerror_r(ev, buffer, strerror_buff_size), buffer);
850b57cec5SDimitry Andric   // If we didn't get any message, print one now.
860b57cec5SDimitry Andric   if (!error_message[0]) {
870b57cec5SDimitry Andric     std::snprintf(buffer, strerror_buff_size, "Unknown error %d", ev);
880b57cec5SDimitry Andric     error_message = buffer;
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric   errno = old_errno;
910b57cec5SDimitry Andric   return string(error_message);
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric #  endif
945f757f3fSDimitry Andric 
955f757f3fSDimitry Andric #endif // !defined(_LIBCPP_HAS_NO_THREADS)
965f757f3fSDimitry Andric 
make_error_str(const error_code & ec,string what_arg)975f757f3fSDimitry Andric string make_error_str(const error_code& ec, string what_arg) {
985f757f3fSDimitry Andric   if (ec) {
995f757f3fSDimitry Andric     if (!what_arg.empty()) {
1005f757f3fSDimitry Andric       what_arg += ": ";
1015f757f3fSDimitry Andric     }
1025f757f3fSDimitry Andric     what_arg += ec.message();
1035f757f3fSDimitry Andric   }
1045f757f3fSDimitry Andric   return what_arg;
1055f757f3fSDimitry Andric }
1065f757f3fSDimitry Andric 
make_error_str(const error_code & ec)1075f757f3fSDimitry Andric string make_error_str(const error_code& ec) {
1085f757f3fSDimitry Andric   if (ec) {
1095f757f3fSDimitry Andric     return ec.message();
1105f757f3fSDimitry Andric   }
1115f757f3fSDimitry Andric   return string();
1125f757f3fSDimitry Andric }
1130b57cec5SDimitry Andric } // end namespace
1140b57cec5SDimitry Andric 
message(int ev) const115cb14a3feSDimitry Andric string __do_message::message(int ev) const {
1160b57cec5SDimitry Andric #if defined(_LIBCPP_HAS_NO_THREADS)
1170b57cec5SDimitry Andric   return string(::strerror(ev));
1180b57cec5SDimitry Andric #else
1190b57cec5SDimitry Andric   return do_strerror_r(ev);
1200b57cec5SDimitry Andric #endif
1210b57cec5SDimitry Andric }
1220b57cec5SDimitry Andric 
123cb14a3feSDimitry Andric class _LIBCPP_HIDDEN __generic_error_category : public __do_message {
1240b57cec5SDimitry Andric public:
125fe6060f1SDimitry Andric   virtual const char* name() const noexcept;
1260b57cec5SDimitry Andric   virtual string message(int ev) const;
1270b57cec5SDimitry Andric };
1280b57cec5SDimitry Andric 
name() const129cb14a3feSDimitry Andric const char* __generic_error_category::name() const noexcept { return "generic"; }
1300b57cec5SDimitry Andric 
message(int ev) const131cb14a3feSDimitry Andric string __generic_error_category::message(int ev) const {
1320b57cec5SDimitry Andric #ifdef _LIBCPP_ELAST
1330b57cec5SDimitry Andric   if (ev > _LIBCPP_ELAST)
1340b57cec5SDimitry Andric     return string("unspecified generic_category error");
1350b57cec5SDimitry Andric #endif // _LIBCPP_ELAST
1360b57cec5SDimitry Andric   return __do_message::message(ev);
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric 
generic_category()139cb14a3feSDimitry Andric const error_category& generic_category() noexcept {
1405f757f3fSDimitry Andric   union AvoidDestroyingGenericCategory {
1415f757f3fSDimitry Andric     __generic_error_category generic_error_category;
1425f757f3fSDimitry Andric     constexpr explicit AvoidDestroyingGenericCategory() : generic_error_category() {}
1435f757f3fSDimitry Andric     ~AvoidDestroyingGenericCategory() {}
1445f757f3fSDimitry Andric   };
1455f757f3fSDimitry Andric   constinit static AvoidDestroyingGenericCategory helper;
1465f757f3fSDimitry Andric   return helper.generic_error_category;
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
149cb14a3feSDimitry Andric class _LIBCPP_HIDDEN __system_error_category : public __do_message {
1500b57cec5SDimitry Andric public:
151fe6060f1SDimitry Andric   virtual const char* name() const noexcept;
1520b57cec5SDimitry Andric   virtual string message(int ev) const;
153fe6060f1SDimitry Andric   virtual error_condition default_error_condition(int ev) const noexcept;
1540b57cec5SDimitry Andric };
1550b57cec5SDimitry Andric 
name() const156cb14a3feSDimitry Andric const char* __system_error_category::name() const noexcept { return "system"; }
1570b57cec5SDimitry Andric 
message(int ev) const158cb14a3feSDimitry Andric string __system_error_category::message(int ev) const {
1590b57cec5SDimitry Andric #ifdef _LIBCPP_ELAST
1600b57cec5SDimitry Andric   if (ev > _LIBCPP_ELAST)
1610b57cec5SDimitry Andric     return string("unspecified system_category error");
1620b57cec5SDimitry Andric #endif // _LIBCPP_ELAST
1630b57cec5SDimitry Andric   return __do_message::message(ev);
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric 
default_error_condition(int ev) const166cb14a3feSDimitry Andric error_condition __system_error_category::default_error_condition(int ev) const noexcept {
1670b57cec5SDimitry Andric #ifdef _LIBCPP_ELAST
1680b57cec5SDimitry Andric   if (ev > _LIBCPP_ELAST)
1690b57cec5SDimitry Andric     return error_condition(ev, system_category());
1700b57cec5SDimitry Andric #endif // _LIBCPP_ELAST
1710b57cec5SDimitry Andric   return error_condition(ev, generic_category());
1720b57cec5SDimitry Andric }
1730b57cec5SDimitry Andric 
system_category()174cb14a3feSDimitry Andric const error_category& system_category() noexcept {
1755f757f3fSDimitry Andric   union AvoidDestroyingSystemCategory {
1765f757f3fSDimitry Andric     __system_error_category system_error_category;
1775f757f3fSDimitry Andric     constexpr explicit AvoidDestroyingSystemCategory() : system_error_category() {}
1785f757f3fSDimitry Andric     ~AvoidDestroyingSystemCategory() {}
1795f757f3fSDimitry Andric   };
1805f757f3fSDimitry Andric   constinit static AvoidDestroyingSystemCategory helper;
1815f757f3fSDimitry Andric   return helper.system_error_category;
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric // error_condition
1850b57cec5SDimitry Andric 
message() const186cb14a3feSDimitry Andric string error_condition::message() const { return __cat_->message(__val_); }
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric // error_code
1890b57cec5SDimitry Andric 
message() const190cb14a3feSDimitry Andric string error_code::message() const { return __cat_->message(__val_); }
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric // system_error
1930b57cec5SDimitry Andric 
system_error(error_code ec,const string & what_arg)1940b57cec5SDimitry Andric system_error::system_error(error_code ec, const string& what_arg)
195cb14a3feSDimitry Andric     : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
1960b57cec5SDimitry Andric 
system_error(error_code ec,const char * what_arg)1970b57cec5SDimitry Andric system_error::system_error(error_code ec, const char* what_arg)
198cb14a3feSDimitry Andric     : runtime_error(make_error_str(ec, what_arg)), __ec_(ec) {}
1990b57cec5SDimitry Andric 
system_error(error_code ec)200cb14a3feSDimitry Andric system_error::system_error(error_code ec) : runtime_error(make_error_str(ec)), __ec_(ec) {}
2010b57cec5SDimitry Andric 
system_error(int ev,const error_category & ecat,const string & what_arg)2020b57cec5SDimitry Andric system_error::system_error(int ev, const error_category& ecat, const string& what_arg)
203cb14a3feSDimitry Andric     : runtime_error(make_error_str(error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
2040b57cec5SDimitry Andric 
system_error(int ev,const error_category & ecat,const char * what_arg)2050b57cec5SDimitry Andric system_error::system_error(int ev, const error_category& ecat, const char* what_arg)
206cb14a3feSDimitry Andric     : runtime_error(make_error_str(error_code(ev, ecat), what_arg)), __ec_(error_code(ev, ecat)) {}
2070b57cec5SDimitry Andric 
system_error(int ev,const error_category & ecat)2080b57cec5SDimitry Andric system_error::system_error(int ev, const error_category& ecat)
209cb14a3feSDimitry Andric     : runtime_error(make_error_str(error_code(ev, ecat))), __ec_(error_code(ev, ecat)) {}
2100b57cec5SDimitry Andric 
~system_error()211cb14a3feSDimitry Andric system_error::~system_error() noexcept {}
2120b57cec5SDimitry Andric 
__throw_system_error(int ev,const char * what_arg)213cb14a3feSDimitry Andric void __throw_system_error(int ev, const char* what_arg) {
21406c3fb27SDimitry Andric #ifndef _LIBCPP_HAS_NO_EXCEPTIONS
2155f757f3fSDimitry Andric   std::__throw_system_error(error_code(ev, system_category()), what_arg);
2160b57cec5SDimitry Andric #else
2175f757f3fSDimitry Andric   // The above could also handle the no-exception case, but for size, avoid referencing system_category() unnecessarily.
218cb14a3feSDimitry Andric   _LIBCPP_VERBOSE_ABORT(
219cb14a3feSDimitry Andric       "system_error was thrown in -fno-exceptions mode with error %i and message \"%s\"", ev, what_arg);
2200b57cec5SDimitry Andric #endif
2210b57cec5SDimitry Andric }
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric _LIBCPP_END_NAMESPACE_STD
224