changeset 14758:0ed8de9d6bbe

strerror_r: Avoid clobbering the strerror buffer when possible. * lib/strerror_r.c (strerror_r): Merge the three implementations. Handle gnulib defined errno values here. When strerror() returns NULL or an empty string, return EINVAL. * lib/strerror.c (strerror): Always call strerror_r. Don't handle gnulib defined errno values here. * modules/strerror (Depends-on): Add verify, strerror_r-posix.
author Bruno Haible <bruno@clisp.org>
date Thu, 19 May 2011 05:24:33 +0200
parents 9a4e1861a46a
children 0f9cae3e684f
files ChangeLog lib/strerror.c lib/strerror_r.c modules/strerror
diffstat 4 files changed, 452 insertions(+), 401 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2011-05-18  Bruno Haible  <bruno@clisp.org>
+
+	strerror_r: Avoid clobbering the strerror buffer when possible.
+	* lib/strerror_r.c (strerror_r): Merge the three implementations.
+	Handle gnulib defined errno values here. When strerror() returns NULL
+	or an empty string, return EINVAL.
+	* lib/strerror.c (strerror): Always call strerror_r. Don't handle
+	gnulib defined errno values here.
+	* modules/strerror (Depends-on): Add verify, strerror_r-posix.
+
 2011-05-18  Eric Blake  <eblake@redhat.com>
 
 	fnmatch: avoid compiler warning
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -17,339 +17,40 @@
 
 #include <config.h>
 
+/* Specification.  */
 #include <string.h>
 
 #if REPLACE_STRERROR
 
 # include <errno.h>
 # include <stdio.h>
-
-# if GNULIB_defined_ESOCK /* native Windows platforms */
-#  if HAVE_WINSOCK2_H
-#   include <winsock2.h>
-#  endif
-# endif
+# include <stdlib.h>
 
 # include "intprops.h"
+# include "verify.h"
 
 /* Use the system functions, not the gnulib overrides in this file.  */
 # undef sprintf
 
-# undef strerror
-# if ! HAVE_DECL_STRERROR
-#  define strerror(n) NULL
-# endif
-
 char *
-rpl_strerror (int n)
+strerror (int n)
 {
-  char const *msg = NULL;
-  /* These error messages are taken from glibc/sysdeps/gnu/errlist.c.  */
-  switch (n)
-    {
-# if GNULIB_defined_ETXTBSY
-    case ETXTBSY:
-      msg = "Text file busy";
-      break;
-# endif
+  static char buf[256];
+
+  int ret = strerror_r (n, buf, sizeof (buf));
 
-# if GNULIB_defined_ESOCK /* native Windows platforms */
-    /* EWOULDBLOCK is the same as EAGAIN.  */
-    case EINPROGRESS:
-      msg = "Operation now in progress";
-      break;
-    case EALREADY:
-      msg = "Operation already in progress";
-      break;
-    case ENOTSOCK:
-      msg = "Socket operation on non-socket";
-      break;
-    case EDESTADDRREQ:
-      msg = "Destination address required";
-      break;
-    case EMSGSIZE:
-      msg = "Message too long";
-      break;
-    case EPROTOTYPE:
-      msg = "Protocol wrong type for socket";
-      break;
-    case ENOPROTOOPT:
-      msg = "Protocol not available";
-      break;
-    case EPROTONOSUPPORT:
-      msg = "Protocol not supported";
-      break;
-    case ESOCKTNOSUPPORT:
-      msg = "Socket type not supported";
-      break;
-    case EOPNOTSUPP:
-      msg = "Operation not supported";
-      break;
-    case EPFNOSUPPORT:
-      msg = "Protocol family not supported";
-      break;
-    case EAFNOSUPPORT:
-      msg = "Address family not supported by protocol";
-      break;
-    case EADDRINUSE:
-      msg = "Address already in use";
-      break;
-    case EADDRNOTAVAIL:
-      msg = "Cannot assign requested address";
-      break;
-    case ENETDOWN:
-      msg = "Network is down";
-      break;
-    case ENETUNREACH:
-      msg = "Network is unreachable";
-      break;
-    case ENETRESET:
-      msg = "Network dropped connection on reset";
-      break;
-    case ECONNABORTED:
-      msg = "Software caused connection abort";
-      break;
-    case ECONNRESET:
-      msg = "Connection reset by peer";
-      break;
-    case ENOBUFS:
-      msg = "No buffer space available";
-      break;
-    case EISCONN:
-      msg = "Transport endpoint is already connected";
-      break;
-    case ENOTCONN:
-      msg = "Transport endpoint is not connected";
-      break;
-    case ESHUTDOWN:
-      msg = "Cannot send after transport endpoint shutdown";
-      break;
-    case ETOOMANYREFS:
-      msg = "Too many references: cannot splice";
-      break;
-    case ETIMEDOUT:
-      msg = "Connection timed out";
-      break;
-    case ECONNREFUSED:
-      msg = "Connection refused";
-      break;
-    case ELOOP:
-      msg = "Too many levels of symbolic links";
-      break;
-    case EHOSTDOWN:
-      msg = "Host is down";
-      break;
-    case EHOSTUNREACH:
-      msg = "No route to host";
-      break;
-    case EPROCLIM:
-      msg = "Too many processes";
-      break;
-    case EUSERS:
-      msg = "Too many users";
-      break;
-    case EDQUOT:
-      msg = "Disk quota exceeded";
-      break;
-    case ESTALE:
-      msg = "Stale NFS file handle";
-      break;
-    case EREMOTE:
-      msg = "Object is remote";
-      break;
-#  if HAVE_WINSOCK2_H
-    /* WSA_INVALID_HANDLE maps to EBADF */
-    /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */
-    /* WSA_INVALID_PARAMETER maps to EINVAL */
-    case WSA_OPERATION_ABORTED:
-      msg = "Overlapped operation aborted";
-      break;
-    case WSA_IO_INCOMPLETE:
-      msg = "Overlapped I/O event object not in signaled state";
-      break;
-    case WSA_IO_PENDING:
-      msg = "Overlapped operations will complete later";
-      break;
-    /* WSAEINTR maps to EINTR */
-    /* WSAEBADF maps to EBADF */
-    /* WSAEACCES maps to EACCES */
-    /* WSAEFAULT maps to EFAULT */
-    /* WSAEINVAL maps to EINVAL */
-    /* WSAEMFILE maps to EMFILE */
-    /* WSAEWOULDBLOCK maps to EWOULDBLOCK */
-    /* WSAEINPROGRESS is EINPROGRESS */
-    /* WSAEALREADY is EALREADY */
-    /* WSAENOTSOCK is ENOTSOCK */
-    /* WSAEDESTADDRREQ is EDESTADDRREQ */
-    /* WSAEMSGSIZE is EMSGSIZE */
-    /* WSAEPROTOTYPE is EPROTOTYPE */
-    /* WSAENOPROTOOPT is ENOPROTOOPT */
-    /* WSAEPROTONOSUPPORT is EPROTONOSUPPORT */
-    /* WSAESOCKTNOSUPPORT is ESOCKTNOSUPPORT */
-    /* WSAEOPNOTSUPP is EOPNOTSUPP */
-    /* WSAEPFNOSUPPORT is EPFNOSUPPORT */
-    /* WSAEAFNOSUPPORT is EAFNOSUPPORT */
-    /* WSAEADDRINUSE is EADDRINUSE */
-    /* WSAEADDRNOTAVAIL is EADDRNOTAVAIL */
-    /* WSAENETDOWN is ENETDOWN */
-    /* WSAENETUNREACH is ENETUNREACH */
-    /* WSAENETRESET is ENETRESET */
-    /* WSAECONNABORTED is ECONNABORTED */
-    /* WSAECONNRESET is ECONNRESET */
-    /* WSAENOBUFS is ENOBUFS */
-    /* WSAEISCONN is EISCONN */
-    /* WSAENOTCONN is ENOTCONN */
-    /* WSAESHUTDOWN is ESHUTDOWN */
-    /* WSAETOOMANYREFS is ETOOMANYREFS */
-    /* WSAETIMEDOUT is ETIMEDOUT */
-    /* WSAECONNREFUSED is ECONNREFUSED */
-    /* WSAELOOP is ELOOP */
-    /* WSAENAMETOOLONG maps to ENAMETOOLONG */
-    /* WSAEHOSTDOWN is EHOSTDOWN */
-    /* WSAEHOSTUNREACH is EHOSTUNREACH */
-    /* WSAENOTEMPTY maps to ENOTEMPTY */
-    /* WSAEPROCLIM is EPROCLIM */
-    /* WSAEUSERS is EUSERS */
-    /* WSAEDQUOT is EDQUOT */
-    /* WSAESTALE is ESTALE */
-    /* WSAEREMOTE is EREMOTE */
-    case WSASYSNOTREADY:
-      msg = "Network subsystem is unavailable";
-      break;
-    case WSAVERNOTSUPPORTED:
-      msg = "Winsock.dll version out of range";
-      break;
-    case WSANOTINITIALISED:
-      msg = "Successful WSAStartup not yet performed";
-      break;
-    case WSAEDISCON:
-      msg = "Graceful shutdown in progress";
-      break;
-    case WSAENOMORE: case WSA_E_NO_MORE:
-      msg = "No more results";
-      break;
-    case WSAECANCELLED: case WSA_E_CANCELLED:
-      msg = "Call was canceled";
-      break;
-    case WSAEINVALIDPROCTABLE:
-      msg = "Procedure call table is invalid";
-      break;
-    case WSAEINVALIDPROVIDER:
-      msg = "Service provider is invalid";
-      break;
-    case WSAEPROVIDERFAILEDINIT:
-      msg = "Service provider failed to initialize";
-      break;
-    case WSASYSCALLFAILURE:
-      msg = "System call failure";
-      break;
-    case WSASERVICE_NOT_FOUND:
-      msg = "Service not found";
-      break;
-    case WSATYPE_NOT_FOUND:
-      msg = "Class type not found";
-      break;
-    case WSAEREFUSED:
-      msg = "Database query was refused";
-      break;
-    case WSAHOST_NOT_FOUND:
-      msg = "Host not found";
-      break;
-    case WSATRY_AGAIN:
-      msg = "Nonauthoritative host not found";
-      break;
-    case WSANO_RECOVERY:
-      msg = "Nonrecoverable error";
-      break;
-    case WSANO_DATA:
-      msg = "Valid name, no data record of requested type";
-      break;
-    /* WSA_QOS_* omitted */
-#  endif
-# endif
+  if (ret == 0)
+    return buf;
 
-# if GNULIB_defined_ENOMSG
-    case ENOMSG:
-      msg = "No message of desired type";
-      break;
-# endif
-
-# if GNULIB_defined_EIDRM
-    case EIDRM:
-      msg = "Identifier removed";
-      break;
-# endif
-
-# if GNULIB_defined_ENOLINK
-    case ENOLINK:
-      msg = "Link has been severed";
-      break;
-# endif
-
-# if GNULIB_defined_EPROTO
-    case EPROTO:
-      msg = "Protocol error";
-      break;
-# endif
-
-# if GNULIB_defined_EMULTIHOP
-    case EMULTIHOP:
-      msg = "Multihop attempted";
-      break;
-# endif
-
-# if GNULIB_defined_EBADMSG
-    case EBADMSG:
-      msg = "Bad message";
-      break;
-# endif
-
-# if GNULIB_defined_EOVERFLOW
-    case EOVERFLOW:
-      msg = "Value too large for defined data type";
-      break;
-# endif
-
-# if GNULIB_defined_ENOTSUP
-    case ENOTSUP:
-      msg = "Not supported";
-      break;
-# endif
-
-# if GNULIB_defined_ESTALE
-    case ESTALE:
-      msg = "Stale NFS file handle";
-      break;
-# endif
-
-# if GNULIB_defined_EDQUOT
-    case EDQUOT:
-      msg = "Disk quota exceeded";
-      break;
-# endif
-
-# if GNULIB_defined_ECANCELED
-    case ECANCELED:
-      msg = "Operation canceled";
-      break;
-# endif
-    }
-
-  if (msg)
-    return (char *) msg;
+  if (ret == ERANGE)
+    /* If this happens, increase the size of buf.  */
+    abort ();
 
   {
-    char *result = strerror (n);
-
-    if (result == NULL || result[0] == '\0')
-      {
-        static char const fmt[] = "Unknown error (%d)";
-        static char msg_buf[sizeof fmt + INT_STRLEN_BOUND (n)];
-        sprintf (msg_buf, fmt, n);
-        return msg_buf;
-      }
-
-    return result;
+    static char const fmt[] = "Unknown error (%d)";
+    verify (sizeof (buf) >= sizeof (fmt) + INT_STRLEN_BOUND (n));
+    sprintf (buf, fmt, n);
+    return buf;
   }
 }
 
--- a/lib/strerror_r.c
+++ b/lib/strerror_r.c
@@ -24,120 +24,458 @@
 
 #include <errno.h>
 
-#if HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) && !EXTEND_STRERROR_R
+# if GNULIB_defined_ESOCK /* native Windows platforms */
+#  if HAVE_WINSOCK2_H
+#   include <winsock2.h>
+#  endif
+# endif
+
+
+#if HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__)
 
 /* The system's strerror_r function is OK, except that its third argument
    is 'int', not 'size_t', or its return type is wrong.  */
 
 # include <limits.h>
 
-int
-strerror_r (int errnum, char *buf, size_t buflen)
-# undef strerror_r
-{
-  int ret;
-
-  if (buflen > INT_MAX)
-    buflen = INT_MAX;
+# define USE_SYSTEM_STRERROR_R 1
 
-# ifdef __hpux
-  /* On HP-UX 11.31, strerror_r always fails when buflen < 80.  */
-  {
-    char stackbuf[80];
-
-    if (buflen < sizeof (stackbuf))
-      {
-        ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
-        if (ret == 0)
-          {
-            size_t len = strlen (stackbuf);
+#elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */
 
-            if (len < buflen)
-              memcpy (buf, stackbuf, len + 1);
-            else
-              ret = ERANGE;
-          }
-      }
-    else
-      ret = strerror_r (errnum, buf, buflen);
-  }
-# elif defined __CYGWIN__
-  /* Cygwin only provides the glibc interface, is thread-safe, and
-     always succeeds (although it may truncate). */
-  strerror_r (errnum, buf, buflen);
-  ret = 0;
-# else
-  ret = strerror_r (errnum, buf, buflen);
-# endif
+# define USE_XPG_STRERROR_R 1
 
-# ifdef _AIX
-  /* On AIX 6.1, strerror_r returns -1 and sets errno to EINVAL
-     if buflen <= 1.  */
-  if (ret < 0 && errno == EINVAL && buflen <= 1)
-    {
-      /* Retry with a larger buffer.  */
-      char largerbuf[10];
-      ret = strerror_r (errnum, largerbuf, sizeof (largerbuf));
-      if (ret < 0 && errno == EINVAL)
-        {
-          /* errnum was out of range.  */
-          return EINVAL;
-        }
-      else
-        {
-          /* buf was too small.  */
-          return ERANGE;
-        }
-    }
-# endif
-
-  /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
-  return (ret < 0 ? errno : ret);
-}
-
-#elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */ && !EXTEND_STRERROR_R
-
-int
-strerror_r (int errnum, char *buf, size_t buflen)
-{
-  extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
-
-  int ret = __xpg_strerror_r (errnum, buf, buflen);
-  return (ret < 0 ? errno : ret);
-}
-
-#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) || EXTEND_STRERROR_R */
+#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
 
 # include "glthread/lock.h"
 
 /* Use strerror(), with locking.  */
+# undef strerror
+
+# define USE_SYSTEM_STRERROR 1
 
 /* This lock protects the buffer returned by strerror().  We assume that
    no other uses of strerror() exist in the program.  */
 gl_lock_define_initialized(static, strerror_lock)
 
+#endif
+
+
 int
 strerror_r (int errnum, char *buf, size_t buflen)
+#undef strerror_r
 {
-  gl_lock_lock (strerror_lock);
+#if EXTEND_STRERROR_R
+  {
+    char const *msg = NULL;
+    /* These error messages are taken from glibc/sysdeps/gnu/errlist.c.  */
+    switch (errnum)
+      {
+# if GNULIB_defined_ETXTBSY
+      case ETXTBSY:
+        msg = "Text file busy";
+        break;
+# endif
+
+# if GNULIB_defined_ESOCK /* native Windows platforms */
+      /* EWOULDBLOCK is the same as EAGAIN.  */
+      case EINPROGRESS:
+        msg = "Operation now in progress";
+        break;
+      case EALREADY:
+        msg = "Operation already in progress";
+        break;
+      case ENOTSOCK:
+        msg = "Socket operation on non-socket";
+        break;
+      case EDESTADDRREQ:
+        msg = "Destination address required";
+        break;
+      case EMSGSIZE:
+        msg = "Message too long";
+        break;
+      case EPROTOTYPE:
+        msg = "Protocol wrong type for socket";
+        break;
+      case ENOPROTOOPT:
+        msg = "Protocol not available";
+        break;
+      case EPROTONOSUPPORT:
+        msg = "Protocol not supported";
+        break;
+      case ESOCKTNOSUPPORT:
+        msg = "Socket type not supported";
+        break;
+      case EOPNOTSUPP:
+        msg = "Operation not supported";
+        break;
+      case EPFNOSUPPORT:
+        msg = "Protocol family not supported";
+        break;
+      case EAFNOSUPPORT:
+        msg = "Address family not supported by protocol";
+        break;
+      case EADDRINUSE:
+        msg = "Address already in use";
+        break;
+      case EADDRNOTAVAIL:
+        msg = "Cannot assign requested address";
+        break;
+      case ENETDOWN:
+        msg = "Network is down";
+        break;
+      case ENETUNREACH:
+        msg = "Network is unreachable";
+        break;
+      case ENETRESET:
+        msg = "Network dropped connection on reset";
+        break;
+      case ECONNABORTED:
+        msg = "Software caused connection abort";
+        break;
+      case ECONNRESET:
+        msg = "Connection reset by peer";
+        break;
+      case ENOBUFS:
+        msg = "No buffer space available";
+        break;
+      case EISCONN:
+        msg = "Transport endpoint is already connected";
+        break;
+      case ENOTCONN:
+        msg = "Transport endpoint is not connected";
+        break;
+      case ESHUTDOWN:
+        msg = "Cannot send after transport endpoint shutdown";
+        break;
+      case ETOOMANYREFS:
+        msg = "Too many references: cannot splice";
+        break;
+      case ETIMEDOUT:
+        msg = "Connection timed out";
+        break;
+      case ECONNREFUSED:
+        msg = "Connection refused";
+        break;
+      case ELOOP:
+        msg = "Too many levels of symbolic links";
+        break;
+      case EHOSTDOWN:
+        msg = "Host is down";
+        break;
+      case EHOSTUNREACH:
+        msg = "No route to host";
+        break;
+      case EPROCLIM:
+        msg = "Too many processes";
+        break;
+      case EUSERS:
+        msg = "Too many users";
+        break;
+      case EDQUOT:
+        msg = "Disk quota exceeded";
+        break;
+      case ESTALE:
+        msg = "Stale NFS file handle";
+        break;
+      case EREMOTE:
+        msg = "Object is remote";
+        break;
+#  if HAVE_WINSOCK2_H
+      /* WSA_INVALID_HANDLE maps to EBADF */
+      /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */
+      /* WSA_INVALID_PARAMETER maps to EINVAL */
+      case WSA_OPERATION_ABORTED:
+        msg = "Overlapped operation aborted";
+        break;
+      case WSA_IO_INCOMPLETE:
+        msg = "Overlapped I/O event object not in signaled state";
+        break;
+      case WSA_IO_PENDING:
+        msg = "Overlapped operations will complete later";
+        break;
+      /* WSAEINTR maps to EINTR */
+      /* WSAEBADF maps to EBADF */
+      /* WSAEACCES maps to EACCES */
+      /* WSAEFAULT maps to EFAULT */
+      /* WSAEINVAL maps to EINVAL */
+      /* WSAEMFILE maps to EMFILE */
+      /* WSAEWOULDBLOCK maps to EWOULDBLOCK */
+      /* WSAEINPROGRESS is EINPROGRESS */
+      /* WSAEALREADY is EALREADY */
+      /* WSAENOTSOCK is ENOTSOCK */
+      /* WSAEDESTADDRREQ is EDESTADDRREQ */
+      /* WSAEMSGSIZE is EMSGSIZE */
+      /* WSAEPROTOTYPE is EPROTOTYPE */
+      /* WSAENOPROTOOPT is ENOPROTOOPT */
+      /* WSAEPROTONOSUPPORT is EPROTONOSUPPORT */
+      /* WSAESOCKTNOSUPPORT is ESOCKTNOSUPPORT */
+      /* WSAEOPNOTSUPP is EOPNOTSUPP */
+      /* WSAEPFNOSUPPORT is EPFNOSUPPORT */
+      /* WSAEAFNOSUPPORT is EAFNOSUPPORT */
+      /* WSAEADDRINUSE is EADDRINUSE */
+      /* WSAEADDRNOTAVAIL is EADDRNOTAVAIL */
+      /* WSAENETDOWN is ENETDOWN */
+      /* WSAENETUNREACH is ENETUNREACH */
+      /* WSAENETRESET is ENETRESET */
+      /* WSAECONNABORTED is ECONNABORTED */
+      /* WSAECONNRESET is ECONNRESET */
+      /* WSAENOBUFS is ENOBUFS */
+      /* WSAEISCONN is EISCONN */
+      /* WSAENOTCONN is ENOTCONN */
+      /* WSAESHUTDOWN is ESHUTDOWN */
+      /* WSAETOOMANYREFS is ETOOMANYREFS */
+      /* WSAETIMEDOUT is ETIMEDOUT */
+      /* WSAECONNREFUSED is ECONNREFUSED */
+      /* WSAELOOP is ELOOP */
+      /* WSAENAMETOOLONG maps to ENAMETOOLONG */
+      /* WSAEHOSTDOWN is EHOSTDOWN */
+      /* WSAEHOSTUNREACH is EHOSTUNREACH */
+      /* WSAENOTEMPTY maps to ENOTEMPTY */
+      /* WSAEPROCLIM is EPROCLIM */
+      /* WSAEUSERS is EUSERS */
+      /* WSAEDQUOT is EDQUOT */
+      /* WSAESTALE is ESTALE */
+      /* WSAEREMOTE is EREMOTE */
+      case WSASYSNOTREADY:
+        msg = "Network subsystem is unavailable";
+        break;
+      case WSAVERNOTSUPPORTED:
+        msg = "Winsock.dll version out of range";
+        break;
+      case WSANOTINITIALISED:
+        msg = "Successful WSAStartup not yet performed";
+        break;
+      case WSAEDISCON:
+        msg = "Graceful shutdown in progress";
+        break;
+      case WSAENOMORE: case WSA_E_NO_MORE:
+        msg = "No more results";
+        break;
+      case WSAECANCELLED: case WSA_E_CANCELLED:
+        msg = "Call was canceled";
+        break;
+      case WSAEINVALIDPROCTABLE:
+        msg = "Procedure call table is invalid";
+        break;
+      case WSAEINVALIDPROVIDER:
+        msg = "Service provider is invalid";
+        break;
+      case WSAEPROVIDERFAILEDINIT:
+        msg = "Service provider failed to initialize";
+        break;
+      case WSASYSCALLFAILURE:
+        msg = "System call failure";
+        break;
+      case WSASERVICE_NOT_FOUND:
+        msg = "Service not found";
+        break;
+      case WSATYPE_NOT_FOUND:
+        msg = "Class type not found";
+        break;
+      case WSAEREFUSED:
+        msg = "Database query was refused";
+        break;
+      case WSAHOST_NOT_FOUND:
+        msg = "Host not found";
+        break;
+      case WSATRY_AGAIN:
+        msg = "Nonauthoritative host not found";
+        break;
+      case WSANO_RECOVERY:
+        msg = "Nonrecoverable error";
+        break;
+      case WSANO_DATA:
+        msg = "Valid name, no data record of requested type";
+        break;
+      /* WSA_QOS_* omitted */
+#  endif
+# endif
+
+# if GNULIB_defined_ENOMSG
+      case ENOMSG:
+        msg = "No message of desired type";
+        break;
+# endif
+
+# if GNULIB_defined_EIDRM
+      case EIDRM:
+        msg = "Identifier removed";
+        break;
+# endif
+
+# if GNULIB_defined_ENOLINK
+      case ENOLINK:
+        msg = "Link has been severed";
+        break;
+# endif
+
+# if GNULIB_defined_EPROTO
+      case EPROTO:
+        msg = "Protocol error";
+        break;
+# endif
+
+# if GNULIB_defined_EMULTIHOP
+      case EMULTIHOP:
+        msg = "Multihop attempted";
+        break;
+# endif
+
+# if GNULIB_defined_EBADMSG
+      case EBADMSG:
+        msg = "Bad message";
+        break;
+# endif
+
+# if GNULIB_defined_EOVERFLOW
+      case EOVERFLOW:
+        msg = "Value too large for defined data type";
+        break;
+# endif
+
+# if GNULIB_defined_ENOTSUP
+      case ENOTSUP:
+        msg = "Not supported";
+        break;
+# endif
+
+# if GNULIB_defined_ESTALE
+      case ESTALE:
+        msg = "Stale NFS file handle";
+        break;
+# endif
+
+# if GNULIB_defined_EDQUOT
+      case EDQUOT:
+        msg = "Disk quota exceeded";
+        break;
+# endif
+
+# if GNULIB_defined_ECANCELED
+      case ECANCELED:
+        msg = "Operation canceled";
+        break;
+# endif
+      }
+
+    if (msg)
+      {
+        size_t len = strlen (msg);
+
+        if (len < buflen)
+          {
+            memcpy (buf, msg, len + 1);
+            return 0;
+          }
+        else
+          return ERANGE;
+      }
+  }
+#endif
 
   {
-    char *errmsg = strerror (errnum);
-    size_t len = strlen (errmsg);
     int ret;
 
-    if (len < buflen)
+#if USE_SYSTEM_STRERROR_R
+
+    if (buflen > INT_MAX)
+      buflen = INT_MAX;
+
+# ifdef __hpux
+    /* On HP-UX 11.31, strerror_r always fails when buflen < 80.  */
+    {
+      char stackbuf[80];
+
+      if (buflen < sizeof (stackbuf))
+        {
+          ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
+          if (ret == 0)
+            {
+              size_t len = strlen (stackbuf);
+
+              if (len < buflen)
+                memcpy (buf, stackbuf, len + 1);
+              else
+                ret = ERANGE;
+            }
+        }
+      else
+        ret = strerror_r (errnum, buf, buflen);
+    }
+# elif defined __CYGWIN__
+    /* Cygwin only provides the glibc interface, is thread-safe, and
+       always succeeds (although it may truncate). */
+    strerror_r (errnum, buf, buflen);
+    ret = 0;
+# else
+    ret = strerror_r (errnum, buf, buflen);
+# endif
+
+# ifdef _AIX
+    /* On AIX 6.1, strerror_r returns -1 and sets errno to EINVAL
+       if buflen <= 1.  */
+    if (ret < 0 && errno == EINVAL && buflen <= 1)
       {
-        memcpy (buf, errmsg, len + 1);
-        ret = 0;
+        /* Retry with a larger buffer.  */
+        char largerbuf[10];
+        ret = strerror_r (errnum, largerbuf, sizeof (largerbuf));
+        if (ret < 0 && errno == EINVAL)
+          {
+            /* errnum was out of range.  */
+            ret = EINVAL;
+          }
+        else
+          {
+            /* buf was too small.  */
+            ret = ERANGE;
+          }
       }
-    else
-      ret = ERANGE;
+# endif
+
+    /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
+    if (ret < 0)
+      ret = errno;
+
+#elif USE_XPG_STRERROR_R
+
+    {
+      extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
+
+      ret = __xpg_strerror_r (errnum, buf, buflen);
+      if (ret < 0)
+        ret = errno;
+    }
+
+#else /* USE_SYSTEM_STRERROR */
+
+    gl_lock_lock (strerror_lock);
+
+    {
+      char *errmsg = strerror (errnum);
+
+      /* For invalid error numbers, strerror() on
+           - IRIX 6.5 returns NULL,
+           - HP-UX 11 returns an empty string.  */
+      if (errmsg == NULL || *errmsg == '\0')
+        ret = EINVAL;
+      else
+        {
+          size_t len = strlen (errmsg);
+
+          if (len < buflen)
+            {
+              memcpy (buf, errmsg, len + 1);
+              ret = 0;
+            }
+          else
+            ret = ERANGE;
+        }
+    }
 
     gl_lock_unlock (strerror_lock);
 
+#endif
+
     return ret;
   }
 }
-
-#endif
--- a/modules/strerror
+++ b/modules/strerror
@@ -7,8 +7,10 @@
 
 Depends-on:
 string
-errno           [test $REPLACE_STRERROR = 1]
-intprops        [test $REPLACE_STRERROR = 1]
+errno            [test $REPLACE_STRERROR = 1]
+intprops         [test $REPLACE_STRERROR = 1]
+verify           [test $REPLACE_STRERROR = 1]
+strerror_r-posix [test $REPLACE_STRERROR = 1]
 
 configure.ac:
 gl_FUNC_STRERROR