From 7be3f4c53b5d872641568a16269c9914e8518db7 Mon Sep 17 00:00:00 2001 From: Florent Rougon Date: Sun, 8 May 2016 21:45:01 +0200 Subject: [PATCH] Thread-safe alternative to strerror() Uses: - strerror_s() on Windows; - the GNU strerror_r() on non-Windows systems where _GNU_SOURCE is defined (which is currently the case when the GNU libstdc++ is used, even if one doesn't explicitely define _GNU_SOURCE, cf. ); - the XSI-compliant strerror_r() on other systems, as long as _POSIX_C_SOURCE >= 200112L (otherwise, the compilation will abort due to a #error preprocessor instruction). --- simgear/misc/strutils.cxx | 46 ++++++++++++++++++++++++++++++++++ simgear/misc/strutils.hxx | 8 ++++++ simgear/misc/strutils_test.cxx | 24 ++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 86418b4c..9ab39a64 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -24,11 +24,14 @@ #include #include #include +#include // strerror_r() and strerror_s() +#include #include "strutils.hxx" #include #include +#include // SG_WINDOWS using std::string; using std::vector; @@ -597,6 +600,49 @@ string sanitizePrintfFormat(const string& input) return input; } +std::string error_string(int errnum) +{ + char buf[512]; // somewhat arbitrary... + // This could be simplified with C11 (annex K, optional...), which offers: + // + // errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum ); + // size_t strerrorlen_s( errno_t errnum ); + +#if defined(SG_WINDOWS) + errno_t retcode; + // Always makes the string in 'buf' null-terminated + retcode = strerror_s(buf, sizeof(buf), errnum); +#elif defined(_GNU_SOURCE) + return std::string(strerror_r(errnum, buf, sizeof(buf))); +#elif _POSIX_C_SOURCE >= 200112L + int retcode; + // POSIX.1-2001 and POSIX.1-2008 + retcode = strerror_r(errnum, buf, sizeof(buf)); +#else +#error "Could not find a thread-safe alternative to strerror()." +#endif + +#if !defined(_GNU_SOURCE) + if (retcode) { + std::string msg = "unable to get error message for a given error number"; + // C++11 would make this shorter with std::to_string() + std::ostringstream ostr; + ostr << errnum; + +#if !defined(SG_WINDOWS) + if (retcode == ERANGE) { // more specific error message in this case + msg = std::string("buffer too small to hold the error message for " + "the specified error number"); + } +#endif + + throw sg_error(msg, ostr.str()); + } + + return std::string(buf); +#endif // !defined(_GNU_SOURCE) +} + } // end namespace strutils } // end namespace simgear diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index 2b02909e..6a2f35b1 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -216,6 +216,14 @@ namespace simgear { */ std::string sanitizePrintfFormat(const std::string& input); + /** + * Get the message corresponding to a given value of errno. + * + * Similar to strerror(), except it should be thread-safe and returns an + * std::string. + */ + std::string error_string(int errnum); + } // end namespace strutils } // end namespace simgear diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index aaa753a4..0ab87224 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -2,6 +2,9 @@ #define BOOST_TEST_MODULE misc #include +#include +#include // _set_errno() on Windows +#include // std::ifstream #include #include "strutils.hxx" @@ -78,3 +81,24 @@ BOOST_AUTO_TEST_CASE( md5_hex ) // md5 BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6"); } + +BOOST_AUTO_TEST_CASE( error_string ) +{ +#if defined(SG_WINDOWS) + _set_errno(0); +#else + errno = 0; +#endif + + std::ifstream f("/\\/non-existent/file/a8f7bz97-3ffe-4f5b-b8db-38ccurJL-"); + +#if defined(SG_WINDOWS) + errno_t saved_errno = errno; +#else + int saved_errno = errno; +#endif + + BOOST_CHECK(!f.is_open()); + BOOST_CHECK_NE(saved_errno, 0); + BOOST_CHECK_GT(strutils::error_string(saved_errno).size(), 0); +} -- 2.39.2