Mercurial > hg > octave-lojdl > gnulib-hg
view lib/strerror_r.c @ 14769:8e815ef27998
strerror_r: guarantee unchanged errno
POSIX guarantees that strerror doesn't change errno on success,
and since we implement strerror by using strerror_r, it makes
sense to make the same guarantee for strerror_r (rather, going
one step further to say that sterror_r does not corrupt errno,
since it returns an error value explicitly).
See also http://austingroupbugs.net/view.php?id=447
* lib/strerror_r.c (strerror_r): Guarantee unchanged errno.
* lib/strerror-impl.h (strerror): Set errno to match strerror_r
failure.
* tests/test-strerror_r.c (main): Enhance test.
Signed-off-by: Eric Blake <eblake@redhat.com>
author | Eric Blake <eblake@redhat.com> |
---|---|
date | Thu, 19 May 2011 13:35:39 -0600 |
parents | a12deda870d9 |
children | edc214f40f31 |
line wrap: on
line source
/* strerror_r.c --- POSIX compatible system error routine Copyright (C) 2010-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Bruno Haible <bruno@clisp.org>, 2010. */ #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 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__) /* 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> # define USE_SYSTEM_STRERROR_R 1 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ /* Use the system's 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) /* 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 { #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 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) { int saved_errno = errno; size_t len = strlen (msg); int ret = ERANGE; if (len < buflen) { memcpy (buf, msg, len + 1); ret = 0; } errno = saved_errno; return ret; } } #endif { int ret; int saved_errno = errno; #if 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; } #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)) { 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 1.7.8 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) { /* 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 /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */ if (ret < 0) ret = errno; /* 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"); } } #else /* USE_SYSTEM_STRERROR */ /* Try to do what strerror (errnum) does, but without clobbering the buffer used by strerror(). */ # if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) /* NetBSD, HP-UX, native Win32 */ /* 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>. */ 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); 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; # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */ /* 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); 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; } } else 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; } }