]> git.mxchange.org Git - simgear.git/commitdiff
Thread-safe alternative to strerror()
authorFlorent Rougon <f.rougon@free.fr>
Sun, 8 May 2016 19:45:01 +0000 (21:45 +0200)
committerRoland Haeder <roland@mxchange.org>
Sat, 13 Aug 2016 08:21:16 +0000 (10:21 +0200)
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.
    <https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.predefined>);
  - 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
simgear/misc/strutils.hxx
simgear/misc/strutils_test.cxx

index 86418b4c1daf665a78734e16644c1ca6fb419368..9ab39a64578a192a4f04387a7a21c0564bff129d 100644 (file)
 #include <cstring>
 #include <sstream>
 #include <algorithm>
+#include <string.h>             // strerror_r() and strerror_s()
+#include <errno.h>
 
 #include "strutils.hxx"
 
 #include <simgear/debug/logstream.hxx>
 #include <simgear/package/md5.h>
+#include <simgear/compiler.h>   // 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
index 2b02909ede30e8afbe912c72b954b6e40a7cb62e..6a2f35b1ace408f08723e0960f53e99d6f6147e5 100644 (file)
@@ -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
 
index aaa753a44832994ff6ecac453f5d59b80e20e6fa..0ab87224c0c9b82b0c3b372da1a2cc2c14ce4b4b 100644 (file)
@@ -2,6 +2,9 @@
 #define BOOST_TEST_MODULE misc
 #include <BoostTestTargetConfig.h>
 
+#include <errno.h>
+#include <stdlib.h>             // _set_errno() on Windows
+#include <fstream>              // std::ifstream
 #include <simgear/compiler.h>
 #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);
+}