#include <config.h>
+/* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD. */
+#define _NETBSD_SOURCE 1
+
/* Specification. */
#include <string.h>
#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 (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
+
+# define USE_XPG_STRERROR_R 1
+
+#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
/* The system's strerror_r function is OK, except that its third argument
- is 'int', not 'size_t'. */
+ is 'int', not 'size_t', or its return type is wrong. */
# include <limits.h>
+# define USE_SYSTEM_STRERROR_R 1
+
+#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
+
+/* Use the system's strerror(). Exclude glibc and cygwin because the
+ system strerror_r has the wrong return type, and cygwin 1.7.9
+ strerror_r clobbers strerror. */
+# undef strerror
+
+# define USE_SYSTEM_STRERROR 1
+
+# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
+
+/* No locking needed. */
+
+/* Get catgets internationalization functions. */
+# if HAVE_CATGETS
+# include <nl_types.h>
+# endif
+
+/* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
+ Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI). */
+# if defined __hpux || defined __sgi
+extern int sys_nerr;
+extern char *sys_errlist[];
+# endif
+
+/* Get sys_nerr on Solaris. */
+# if defined __sun && !defined _LP64
+extern int sys_nerr;
+# endif
+
+/* Get sys_nerr, sys_errlist on native Windows. */
+# include <stdlib.h>
+
+# else
+
+# include "glthread/lock.h"
+
+/* 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
+
+#endif
+
+
int
strerror_r (int errnum, char *buf, size_t buflen)
-# undef strerror_r
+#undef strerror_r
{
- int ret;
-
- if (buflen > INT_MAX)
- buflen = INT_MAX;
+ /* Filter this out now, so that rest of this replacement knows that
+ there is room for a non-empty message and trailing NUL. */
+ if (buflen <= 1)
+ {
+ if (buflen)
+ *buf = 0;
+ return ERANGE;
+ }
-# ifdef __hpux
- /* On HP-UX 11.31, strerror_r always fails when buflen < 80. */
+#if GNULIB_defined_ETXTBSY \
+ || GNULIB_defined_ESOCK \
+ || GNULIB_defined_ENOMSG \
+ || GNULIB_defined_EIDRM \
+ || GNULIB_defined_ENOLINK \
+ || GNULIB_defined_EPROTO \
+ || GNULIB_defined_EMULTIHOP \
+ || GNULIB_defined_EBADMSG \
+ || GNULIB_defined_EOVERFLOW \
+ || GNULIB_defined_ENOTSUP \
+ || GNULIB_defined_ESTALE \
+ || GNULIB_defined_EDQUOT \
+ || GNULIB_defined_ECANCELED
{
- char stackbuf[80];
+ 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 (buflen < sizeof (stackbuf))
+# 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)
{
- ret = strerror_r (errnum, stackbuf, sizeof (stackbuf));
- if (ret == 0)
- {
- size_t len = strlen (stackbuf);
+ int saved_errno = errno;
+ size_t len = strlen (msg);
+ int ret = ERANGE;
- if (len < buflen)
- memcpy (buf, stackbuf, len + 1);
- else
- ret = ERANGE;
+ if (len < buflen)
+ {
+ memcpy (buf, msg, len + 1);
+ ret = 0;
}
+ errno = saved_errno;
+ return ret;
}
- else
- ret = strerror_r (errnum, buf, buflen);
}
-# else
- ret = strerror_r (errnum, buf, buflen);
-# endif
+#endif
+
+ {
+ int ret;
+ int saved_errno = errno;
+
+#if USE_XPG_STRERROR_R
-# 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)
+ extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
+
+ ret = __xpg_strerror_r (errnum, buf, buflen);
+ if (ret < 0)
+ ret = errno;
+ }
+
+#elif 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))
{
- /* errnum was out of range. */
- return EINVAL;
+ 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
- {
- /* buf was too small. */
- return ERANGE;
- }
+ ret = strerror_r (errnum, buf, buflen);
}
+# else
+ ret = strerror_r (errnum, buf, buflen);
# endif
- /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */
- return (ret < 0 ? errno : ret);
-}
+ /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */
+ if (ret < 0)
+ ret = errno;
-#elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */ && !EXTEND_STRERROR_R
+ /* FreeBSD rejects 0; see http://austingroupbugs.net/view.php?id=382. */
+ if (errnum == 0 && ret == EINVAL)
+ {
+ if (buflen <= strlen ("Success"))
+ {
+ ret = ERANGE;
+ if (buflen)
+ buf[0] = 0;
+ }
+ else
+ {
+ ret = 0;
+ strcpy (buf, "Success");
+ }
+ }
-int
-strerror_r (int errnum, char *buf, size_t buflen)
-{
- extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
+#else /* USE_SYSTEM_STRERROR */
- int ret = __xpg_strerror_r (errnum, buf, buflen);
- return (ret < 0 ? errno : ret);
-}
+ /* Try to do what strerror (errnum) does, but without clobbering the
+ buffer used by strerror(). */
-#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) || EXTEND_STRERROR_R */
+# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Win32, Cygwin */
-# include "glthread/lock.h"
+ /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
+ and <errno.h> above.
+ HP-UX: sys_nerr, sys_errlist are declared explicitly above.
+ native Win32: sys_nerr, sys_errlist are declared in <stdlib.h>.
+ Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */
+ if (errnum >= 0 && errnum < sys_nerr)
+ {
+# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
+# if defined __NetBSD__
+ nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
+ const char *errmsg =
+ (catd != (nl_catd)-1
+ ? catgets (catd, 1, errnum, sys_errlist[errnum])
+ : sys_errlist[errnum]);
+# endif
+# if defined __hpux
+ nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
+ const char *errmsg =
+ (catd != (nl_catd)-1
+ ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
+ : sys_errlist[errnum]);
+# endif
+# else
+ const char *errmsg = sys_errlist[errnum];
+# endif
+ if (errmsg == NULL || *errmsg == '\0')
+ ret = EINVAL;
+ else
+ {
+ size_t len = strlen (errmsg);
-/* Use strerror(), with locking. */
+ if (len < buflen)
+ {
+ memcpy (buf, errmsg, len + 1);
+ ret = 0;
+ }
+ else
+ ret = ERANGE;
+ }
+# if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
+ if (catd != (nl_catd)-1)
+ catclose (catd);
+# endif
+ }
+ else
+ ret = EINVAL;
-/* 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)
+# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
-int
-strerror_r (int errnum, char *buf, size_t buflen)
-{
- gl_lock_lock (strerror_lock);
+ /* For a valid error number, the system's strerror() function returns
+ a pointer to a not copied string, not to a buffer. */
+ if (errnum >= 0 && errnum < sys_nerr)
+ {
+ char *errmsg = strerror (errnum);
- {
- char *errmsg = strerror (errnum);
- size_t len = strlen (errmsg);
- int ret;
+ if (errmsg == NULL || *errmsg == '\0')
+ ret = EINVAL;
+ else
+ {
+ size_t len = strlen (errmsg);
- if (len < buflen)
- {
- memcpy (buf, errmsg, len + 1);
- ret = 0;
+ if (len < buflen)
+ {
+ memcpy (buf, errmsg, len + 1);
+ ret = 0;
+ }
+ else
+ ret = ERANGE;
+ }
}
else
- ret = ERANGE;
+ ret = EINVAL;
+
+# else
+
+ 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
+
+#endif
+
+ errno = saved_errno;
return ret;
}
}
-
-#endif