/* 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 . */
/* Written by Bruno Haible , 2010. */
#include
/* Specification. */
#include
#include
#if HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) && !EXTEND_STRERROR_R
/* 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
int
strerror_r (int errnum, char *buf, size_t buflen)
# undef strerror_r
{
int ret;
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)
{
/* 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 */
# include "glthread/lock.h"
/* Use strerror(), with locking. */
/* 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)
int
strerror_r (int errnum, char *buf, size_t buflen)
{
gl_lock_lock (strerror_lock);
{
char *errmsg = strerror (errnum);
size_t len = strlen (errmsg);
int ret;
if (len < buflen)
{
memcpy (buf, errmsg, len + 1);
ret = 0;
}
else
ret = ERANGE;
gl_lock_unlock (strerror_lock);
return ret;
}
}
#endif