2019-11-07 02:49:52 +08:00
/* strerror_r.c --- POSIX compatible system error routine
2020-02-23 09:31:54 +08:00
Copyright ( C ) 2010 - 2020 Free Software Foundation , Inc .
2019-11-07 02:49:52 +08:00
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
2019-11-17 13:37:06 +08:00
along with this program . If not , see < https : //www.gnu.org/licenses/>. */
2019-11-07 02:49:52 +08:00
/* 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>
# include <stdio.h>
# include <stdlib.h>
2019-11-17 13:37:06 +08:00
# if !HAVE_SNPRINTF
# include <stdarg.h>
# endif
2019-11-07 02:49:52 +08:00
# include "strerror-override.h"
# 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
extern
# ifdef __cplusplus
" C "
# endif
int __xpg_strerror_r ( int errnum , char * buf , size_t buflen ) ;
2019-11-17 13:37:06 +08:00
# elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
2019-11-07 02:49:52 +08:00
/* 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__ || 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
2019-11-17 13:37:06 +08:00
# if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
2019-11-07 02:49:52 +08:00
/* No locking needed. */
/* Get catgets internationalization functions. */
# if HAVE_CATGETS
# include <nl_types.h>
# endif
# ifdef __cplusplus
extern " C " {
# 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
# ifdef __cplusplus
}
# endif
# 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
/* On MSVC, there is no snprintf() function, just a _snprintf().
It is of lower quality , but sufficient for the simple use here .
We only have to make sure to NUL terminate the result ( _snprintf
does not NUL terminate , like strncpy ) . */
# if !HAVE_SNPRINTF
static int
local_snprintf ( char * buf , size_t buflen , const char * format , . . . )
{
va_list args ;
int result ;
va_start ( args , format ) ;
result = _vsnprintf ( buf , buflen , format , args ) ;
va_end ( args ) ;
if ( buflen > 0 & & ( result < 0 | | result > = buflen ) )
buf [ buflen - 1 ] = ' \0 ' ;
return result ;
}
# define snprintf local_snprintf
# endif
/* Copy as much of MSG into BUF as possible, without corrupting errno.
Return 0 if MSG fit in BUFLEN , otherwise return ERANGE . */
static int
safe_copy ( char * buf , size_t buflen , const char * msg )
{
size_t len = strlen ( msg ) ;
2019-11-17 13:37:06 +08:00
size_t moved = len < buflen ? len : buflen - 1 ;
2019-11-07 02:49:52 +08:00
2019-11-17 13:37:06 +08:00
/* Although POSIX lets memmove corrupt errno, we don't
know of any implementation where this is a real problem . */
memmove ( buf , msg , moved ) ;
buf [ moved ] = ' \0 ' ;
return len < buflen ? 0 : ERANGE ;
2019-11-07 02:49:52 +08:00
}
int
strerror_r ( int errnum , char * buf , size_t buflen )
# undef strerror_r
{
/* 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 ;
}
* buf = ' \0 ' ;
/* Check for gnulib overrides. */
{
char const * msg = strerror_override ( errnum ) ;
if ( msg )
return safe_copy ( buf , buflen , msg ) ;
}
{
int ret ;
int saved_errno = errno ;
# if USE_XPG_STRERROR_R
{
ret = __xpg_strerror_r ( errnum , buf , buflen ) ;
if ( ret < 0 )
ret = errno ;
if ( ! * buf )
{
/* glibc 2.13 would not touch buf on err, so we have to fall
back to GNU strerror_r which always returns a thread - safe
untruncated string to ( partially ) copy into our buf . */
safe_copy ( buf , buflen , strerror_r ( errnum , buf , buflen ) ) ;
}
}
# 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; it
also fails to change buf on EINVAL . */
{
char stackbuf [ 80 ] ;
if ( buflen < sizeof stackbuf )
{
ret = strerror_r ( errnum , stackbuf , sizeof stackbuf ) ;
if ( ret = = 0 )
ret = safe_copy ( buf , buflen , stackbuf ) ;
}
else
ret = strerror_r ( errnum , buf , buflen ) ;
}
# else
ret = strerror_r ( errnum , buf , buflen ) ;
2019-11-17 13:37:06 +08:00
/* Some old implementations may return (-1, EINVAL) instead of EINVAL.
But on Haiku , valid error numbers are negative . */
# if !defined __HAIKU__
2019-11-07 02:49:52 +08:00
if ( ret < 0 )
ret = errno ;
2019-11-17 13:37:06 +08:00
# endif
2019-11-07 02:49:52 +08:00
# endif
2019-11-17 13:37:06 +08:00
# if defined _AIX || defined __HAIKU__
/* AIX and Haiku return 0 rather than ERANGE when truncating strings; try
2019-11-07 02:49:52 +08:00
again until we are sure we got the entire string . */
if ( ! ret & & strlen ( buf ) = = buflen - 1 )
{
char stackbuf [ STACKBUF_LEN ] ;
size_t len ;
strerror_r ( errnum , stackbuf , sizeof stackbuf ) ;
len = strlen ( stackbuf ) ;
/* STACKBUF_LEN should have been large enough. */
if ( len + 1 = = sizeof stackbuf )
abort ( ) ;
if ( buflen < = len )
ret = ERANGE ;
}
# else
/* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7
truncates early on ERANGE rather than return a partial integer .
We prefer the maximal string . We set buf [ 0 ] earlier , and we
know of no implementation that modifies buf to be an
unterminated string , so this strlen should be portable in
practice ( rather than pulling in a safer strnlen ) . */
if ( ret = = ERANGE & & strlen ( buf ) < buflen - 1 )
{
char stackbuf [ STACKBUF_LEN ] ;
/* STACKBUF_LEN should have been large enough. */
if ( strerror_r ( errnum , stackbuf , sizeof stackbuf ) = = ERANGE )
abort ( ) ;
safe_copy ( buf , buflen , stackbuf ) ;
}
# endif
# else /* USE_SYSTEM_STRERROR */
/* Try to do what strerror (errnum) does, but without clobbering the
buffer used by strerror ( ) . */
2019-11-17 13:37:06 +08:00
# if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */
2019-11-07 02:49:52 +08:00
/* 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 Windows : 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
ret = safe_copy ( buf , buflen , errmsg ) ;
# 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
ret = safe_copy ( buf , buflen , errmsg ) ;
}
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
ret = safe_copy ( buf , buflen , errmsg ) ;
}
gl_lock_unlock ( strerror_lock ) ;
# endif
2019-11-17 13:37:06 +08:00
# endif
# if defined _WIN32 && !defined __CYGWIN__
/* MSVC 14 defines names for many error codes in the range 100..140,
but _sys_errlist contains strings only for the error codes
< _sys_nerr = 43. */
if ( ret = = EINVAL )
{
const char * errmsg ;
switch ( errnum )
{
case 100 /* EADDRINUSE */ :
errmsg = " Address already in use " ;
break ;
case 101 /* EADDRNOTAVAIL */ :
errmsg = " Cannot assign requested address " ;
break ;
case 102 /* EAFNOSUPPORT */ :
errmsg = " Address family not supported by protocol " ;
break ;
case 103 /* EALREADY */ :
errmsg = " Operation already in progress " ;
break ;
case 105 /* ECANCELED */ :
errmsg = " Operation canceled " ;
break ;
case 106 /* ECONNABORTED */ :
errmsg = " Software caused connection abort " ;
break ;
case 107 /* ECONNREFUSED */ :
errmsg = " Connection refused " ;
break ;
case 108 /* ECONNRESET */ :
errmsg = " Connection reset by peer " ;
break ;
case 109 /* EDESTADDRREQ */ :
errmsg = " Destination address required " ;
break ;
case 110 /* EHOSTUNREACH */ :
errmsg = " No route to host " ;
break ;
case 112 /* EINPROGRESS */ :
errmsg = " Operation now in progress " ;
break ;
case 113 /* EISCONN */ :
errmsg = " Transport endpoint is already connected " ;
break ;
case 114 /* ELOOP */ :
errmsg = " Too many levels of symbolic links " ;
break ;
case 115 /* EMSGSIZE */ :
errmsg = " Message too long " ;
break ;
case 116 /* ENETDOWN */ :
errmsg = " Network is down " ;
break ;
case 117 /* ENETRESET */ :
errmsg = " Network dropped connection on reset " ;
break ;
case 118 /* ENETUNREACH */ :
errmsg = " Network is unreachable " ;
break ;
case 119 /* ENOBUFS */ :
errmsg = " No buffer space available " ;
break ;
case 123 /* ENOPROTOOPT */ :
errmsg = " Protocol not available " ;
break ;
case 126 /* ENOTCONN */ :
errmsg = " Transport endpoint is not connected " ;
break ;
case 128 /* ENOTSOCK */ :
errmsg = " Socket operation on non-socket " ;
break ;
case 129 /* ENOTSUP */ :
errmsg = " Not supported " ;
break ;
case 130 /* EOPNOTSUPP */ :
errmsg = " Operation not supported " ;
break ;
case 132 /* EOVERFLOW */ :
errmsg = " Value too large for defined data type " ;
break ;
case 133 /* EOWNERDEAD */ :
errmsg = " Owner died " ;
break ;
case 134 /* EPROTO */ :
errmsg = " Protocol error " ;
break ;
case 135 /* EPROTONOSUPPORT */ :
errmsg = " Protocol not supported " ;
break ;
case 136 /* EPROTOTYPE */ :
errmsg = " Protocol wrong type for socket " ;
break ;
case 138 /* ETIMEDOUT */ :
errmsg = " Connection timed out " ;
break ;
case 140 /* EWOULDBLOCK */ :
errmsg = " Operation would block " ;
break ;
default :
errmsg = NULL ;
break ;
}
if ( errmsg ! = NULL )
ret = safe_copy ( buf , buflen , errmsg ) ;
}
2019-11-07 02:49:52 +08:00
# endif
if ( ret = = EINVAL & & ! * buf )
2019-11-17 13:37:06 +08:00
{
# if defined __HAIKU__
/* For consistency with perror(). */
snprintf ( buf , buflen , " Unknown Application Error (%d) " , errnum ) ;
# else
snprintf ( buf , buflen , " Unknown error %d " , errnum ) ;
# endif
}
2019-11-07 02:49:52 +08:00
errno = saved_errno ;
return ret ;
}
}