From: Bruno Haible Date: Thu, 19 May 2011 03:24:33 +0000 (+0200) Subject: strerror_r: Avoid clobbering the strerror buffer when possible. X-Git-Tag: v0.1~2768 X-Git-Url: http://erislabs.net/gitweb/?a=commitdiff_plain;h=d3455c00b721eb7440419f2d10d50d35c5e7525a;p=gnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 99e2c3e70..04b29046d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2011-05-18 Bruno Haible + + 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 fnmatch: avoid compiler warning diff --git a/lib/strerror.c b/lib/strerror.c index 5dae1111a..6137552ef 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -17,339 +17,40 @@ #include +/* Specification. */ #include #if REPLACE_STRERROR # include # include - -# if GNULIB_defined_ESOCK /* native Windows platforms */ -# if HAVE_WINSOCK2_H -# include -# endif -# endif +# include # 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 - -# 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 + static char buf[256]; -# if GNULIB_defined_EMULTIHOP - case EMULTIHOP: - msg = "Multihop attempted"; - break; -# endif + int ret = strerror_r (n, buf, sizeof (buf)); -# if GNULIB_defined_EBADMSG - case EBADMSG: - msg = "Bad message"; - break; -# endif + if (ret == 0) + return buf; -# 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; } } diff --git a/lib/strerror_r.c b/lib/strerror_r.c index 1fa52d99d..3bba26cd9 100644 --- a/lib/strerror_r.c +++ b/lib/strerror_r.c @@ -24,120 +24,458 @@ #include -#if HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) && !EXTEND_STRERROR_R +# if GNULIB_defined_ESOCK /* native Windows platforms */ +# if HAVE_WINSOCK2_H +# include +# 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 +# define USE_SYSTEM_STRERROR_R 1 + +#elif (__GLIBC__ >= 2 || defined __UCLIBC__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4 */ + +# define USE_XPG_STRERROR_R 1 + +#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 +#undef strerror_r { - int ret; +#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 (buflen > INT_MAX) - buflen = INT_MAX; +# 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 -# ifdef __hpux - /* On HP-UX 11.31, strerror_r always fails when buflen < 80. */ - { - char stackbuf[80]; +# if GNULIB_defined_ENOMSG + case ENOMSG: + msg = "No message of desired type"; + break; +# endif - if (buflen < sizeof (stackbuf)) +# 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); + size_t len = strlen (msg); - if (len < buflen) - memcpy (buf, stackbuf, len + 1); - else - ret = ERANGE; + if (len < buflen) + { + memcpy (buf, msg, len + 1); + return 0; } + else + return 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 +#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) + { + int ret; + +#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. */ { - /* Retry with a larger buffer. */ - char largerbuf[10]; - ret = strerror_r (errnum, largerbuf, sizeof (largerbuf)); - if (ret < 0 && errno == EINVAL) + 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); } +# 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 - /* 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 +# 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. */ + ret = EINVAL; + } + else + { + /* buf was too small. */ + ret = ERANGE; + } + } +# endif -int -strerror_r (int errnum, char *buf, size_t buflen) -{ - extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen); + /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */ + if (ret < 0) + ret = errno; - int ret = __xpg_strerror_r (errnum, buf, buflen); - return (ret < 0 ? errno : ret); -} +#elif USE_XPG_STRERROR_R -#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) || EXTEND_STRERROR_R */ + { + extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen); -# include "glthread/lock.h" + ret = __xpg_strerror_r (errnum, buf, buflen); + if (ret < 0) + ret = errno; + } -/* Use strerror(), with locking. */ +#else /* USE_SYSTEM_STRERROR */ -/* 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) + gl_lock_lock (strerror_lock); -int -strerror_r (int errnum, char *buf, size_t buflen) -{ - gl_lock_lock (strerror_lock); + { + char *errmsg = strerror (errnum); - { - char *errmsg = strerror (errnum); - size_t len = strlen (errmsg); - int ret; + /* 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; + if (len < buflen) + { + memcpy (buf, errmsg, len + 1); + ret = 0; + } + else + ret = ERANGE; + } + } gl_lock_unlock (strerror_lock); +#endif + return ret; } } - -#endif diff --git a/modules/strerror b/modules/strerror index cd14d1257..6e4c9c546 100644 --- a/modules/strerror +++ b/modules/strerror @@ -7,8 +7,10 @@ m4/strerror.m4 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