Blame gnulib-tests/strerror_r.c

Packit 33f14e
/* strerror_r.c --- POSIX compatible system error routine
Packit 33f14e
Packit 33f14e
   Copyright (C) 2010-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Written by Bruno Haible <bruno@clisp.org>, 2010.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
/* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD.  */
Packit 33f14e
#define _NETBSD_SOURCE 1
Packit 33f14e
Packit 33f14e
/* Specification.  */
Packit 33f14e
#include <string.h>
Packit 33f14e
Packit 33f14e
#include <errno.h>
Packit 33f14e
#include <stdio.h>
Packit 33f14e
#include <stdlib.h>
Packit 33f14e
#if !HAVE_SNPRINTF
Packit 33f14e
# include <stdarg.h>
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include "strerror-override.h"
Packit 33f14e
Packit 33f14e
#if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
Packit 33f14e
Packit 33f14e
# define USE_XPG_STRERROR_R 1
Packit 33f14e
extern
Packit 33f14e
#ifdef __cplusplus
Packit 33f14e
"C"
Packit 33f14e
#endif
Packit 33f14e
int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
Packit 33f14e
Packit 33f14e
#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
Packit 33f14e
Packit 33f14e
/* The system's strerror_r function is OK, except that its third argument
Packit 33f14e
   is 'int', not 'size_t', or its return type is wrong.  */
Packit 33f14e
Packit 33f14e
# include <limits.h>
Packit 33f14e
Packit 33f14e
# define USE_SYSTEM_STRERROR_R 1
Packit 33f14e
Packit 33f14e
#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
Packit 33f14e
Packit 33f14e
/* Use the system's strerror().  Exclude glibc and cygwin because the
Packit 33f14e
   system strerror_r has the wrong return type, and cygwin 1.7.9
Packit 33f14e
   strerror_r clobbers strerror.  */
Packit 33f14e
# undef strerror
Packit 33f14e
Packit 33f14e
# define USE_SYSTEM_STRERROR 1
Packit 33f14e
Packit 33f14e
# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
Packit 33f14e
Packit 33f14e
/* No locking needed.  */
Packit 33f14e
Packit 33f14e
/* Get catgets internationalization functions.  */
Packit 33f14e
#  if HAVE_CATGETS
Packit 33f14e
#   include <nl_types.h>
Packit 33f14e
#  endif
Packit 33f14e
Packit 33f14e
#ifdef __cplusplus
Packit 33f14e
extern "C" {
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
Packit 33f14e
   Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI).  */
Packit 33f14e
#  if defined __hpux || defined __sgi
Packit 33f14e
extern int sys_nerr;
Packit 33f14e
extern char *sys_errlist[];
Packit 33f14e
#  endif
Packit 33f14e
Packit 33f14e
/* Get sys_nerr on Solaris.  */
Packit 33f14e
#  if defined __sun && !defined _LP64
Packit 33f14e
extern int sys_nerr;
Packit 33f14e
#  endif
Packit 33f14e
Packit 33f14e
#ifdef __cplusplus
Packit 33f14e
}
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
# else
Packit 33f14e
Packit 33f14e
#  include "glthread/lock.h"
Packit 33f14e
Packit 33f14e
/* This lock protects the buffer returned by strerror().  We assume that
Packit 33f14e
   no other uses of strerror() exist in the program.  */
Packit 33f14e
gl_lock_define_initialized(static, strerror_lock)
Packit 33f14e
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* On MSVC, there is no snprintf() function, just a _snprintf().
Packit 33f14e
   It is of lower quality, but sufficient for the simple use here.
Packit 33f14e
   We only have to make sure to NUL terminate the result (_snprintf
Packit 33f14e
   does not NUL terminate, like strncpy).  */
Packit 33f14e
#if !HAVE_SNPRINTF
Packit 33f14e
static int
Packit 33f14e
local_snprintf (char *buf, size_t buflen, const char *format, ...)
Packit 33f14e
{
Packit 33f14e
  va_list args;
Packit 33f14e
  int result;
Packit 33f14e
Packit 33f14e
  va_start (args, format);
Packit 33f14e
  result = _vsnprintf (buf, buflen, format, args);
Packit 33f14e
  va_end (args);
Packit 33f14e
  if (buflen > 0 && (result < 0 || result >= buflen))
Packit 33f14e
    buf[buflen - 1] = '\0';
Packit 33f14e
  return result;
Packit 33f14e
}
Packit 33f14e
# define snprintf local_snprintf
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* Copy as much of MSG into BUF as possible, without corrupting errno.
Packit 33f14e
   Return 0 if MSG fit in BUFLEN, otherwise return ERANGE.  */
Packit 33f14e
static int
Packit 33f14e
safe_copy (char *buf, size_t buflen, const char *msg)
Packit 33f14e
{
Packit 33f14e
  size_t len = strlen (msg);
Packit 33f14e
  int ret;
Packit 33f14e
Packit 33f14e
  if (len < buflen)
Packit 33f14e
    {
Packit 33f14e
      /* Although POSIX allows memcpy() to corrupt errno, we don't
Packit 33f14e
         know of any implementation where this is a real problem.  */
Packit 33f14e
      memcpy (buf, msg, len + 1);
Packit 33f14e
      ret = 0;
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      memcpy (buf, msg, buflen - 1);
Packit 33f14e
      buf[buflen - 1] = '\0';
Packit 33f14e
      ret = ERANGE;
Packit 33f14e
    }
Packit 33f14e
  return ret;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
strerror_r (int errnum, char *buf, size_t buflen)
Packit 33f14e
#undef strerror_r
Packit 33f14e
{
Packit 33f14e
  /* Filter this out now, so that rest of this replacement knows that
Packit 33f14e
     there is room for a non-empty message and trailing NUL.  */
Packit 33f14e
  if (buflen <= 1)
Packit 33f14e
    {
Packit 33f14e
      if (buflen)
Packit 33f14e
        *buf = '\0';
Packit 33f14e
      return ERANGE;
Packit 33f14e
    }
Packit 33f14e
  *buf = '\0';
Packit 33f14e
Packit 33f14e
  /* Check for gnulib overrides.  */
Packit 33f14e
  {
Packit 33f14e
    char const *msg = strerror_override (errnum);
Packit 33f14e
Packit 33f14e
    if (msg)
Packit 33f14e
      return safe_copy (buf, buflen, msg);
Packit 33f14e
  }
Packit 33f14e
Packit 33f14e
  {
Packit 33f14e
    int ret;
Packit 33f14e
    int saved_errno = errno;
Packit 33f14e
Packit 33f14e
#if USE_XPG_STRERROR_R
Packit 33f14e
Packit 33f14e
    {
Packit 33f14e
      ret = __xpg_strerror_r (errnum, buf, buflen);
Packit 33f14e
      if (ret < 0)
Packit 33f14e
        ret = errno;
Packit 33f14e
      if (!*buf)
Packit 33f14e
        {
Packit 33f14e
          /* glibc 2.13 would not touch buf on err, so we have to fall
Packit 33f14e
             back to GNU strerror_r which always returns a thread-safe
Packit 33f14e
             untruncated string to (partially) copy into our buf.  */
Packit 33f14e
          safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
#elif USE_SYSTEM_STRERROR_R
Packit 33f14e
Packit 33f14e
    if (buflen > INT_MAX)
Packit 33f14e
      buflen = INT_MAX;
Packit 33f14e
Packit 33f14e
# ifdef __hpux
Packit 33f14e
    /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
Packit 33f14e
       also fails to change buf on EINVAL.  */
Packit 33f14e
    {
Packit 33f14e
      char stackbuf[80];
Packit 33f14e
Packit 33f14e
      if (buflen < sizeof stackbuf)
Packit 33f14e
        {
Packit 33f14e
          ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
Packit 33f14e
          if (ret == 0)
Packit 33f14e
            ret = safe_copy (buf, buflen, stackbuf);
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        ret = strerror_r (errnum, buf, buflen);
Packit 33f14e
    }
Packit 33f14e
# else
Packit 33f14e
    ret = strerror_r (errnum, buf, buflen);
Packit 33f14e
Packit 33f14e
    /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
Packit 33f14e
    if (ret < 0)
Packit 33f14e
      ret = errno;
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
# ifdef _AIX
Packit 33f14e
    /* AIX returns 0 rather than ERANGE when truncating strings; try
Packit 33f14e
       again until we are sure we got the entire string.  */
Packit 33f14e
    if (!ret && strlen (buf) == buflen - 1)
Packit 33f14e
      {
Packit 33f14e
        char stackbuf[STACKBUF_LEN];
Packit 33f14e
        size_t len;
Packit 33f14e
        strerror_r (errnum, stackbuf, sizeof stackbuf);
Packit 33f14e
        len = strlen (stackbuf);
Packit 33f14e
        /* STACKBUF_LEN should have been large enough.  */
Packit 33f14e
        if (len + 1 == sizeof stackbuf)
Packit 33f14e
          abort ();
Packit 33f14e
        if (buflen <= len)
Packit 33f14e
          ret = ERANGE;
Packit 33f14e
      }
Packit 33f14e
# else
Packit 33f14e
    /* Solaris 10 does not populate buf on ERANGE.  OpenBSD 4.7
Packit 33f14e
       truncates early on ERANGE rather than return a partial integer.
Packit 33f14e
       We prefer the maximal string.  We set buf[0] earlier, and we
Packit 33f14e
       know of no implementation that modifies buf to be an
Packit 33f14e
       unterminated string, so this strlen should be portable in
Packit 33f14e
       practice (rather than pulling in a safer strnlen).  */
Packit 33f14e
    if (ret == ERANGE && strlen (buf) < buflen - 1)
Packit 33f14e
      {
Packit 33f14e
        char stackbuf[STACKBUF_LEN];
Packit 33f14e
Packit 33f14e
        /* STACKBUF_LEN should have been large enough.  */
Packit 33f14e
        if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
Packit 33f14e
          abort ();
Packit 33f14e
        safe_copy (buf, buflen, stackbuf);
Packit 33f14e
      }
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
#else /* USE_SYSTEM_STRERROR */
Packit 33f14e
Packit 33f14e
    /* Try to do what strerror (errnum) does, but without clobbering the
Packit 33f14e
       buffer used by strerror().  */
Packit 33f14e
Packit 33f14e
# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */
Packit 33f14e
Packit 33f14e
    /* NetBSD:         sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
Packit 33f14e
                       and <errno.h> above.
Packit 33f14e
       HP-UX:          sys_nerr, sys_errlist are declared explicitly above.
Packit 33f14e
       native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
Packit 33f14e
       Cygwin:         sys_nerr, sys_errlist are declared in <errno.h>.  */
Packit 33f14e
    if (errnum >= 0 && errnum < sys_nerr)
Packit 33f14e
      {
Packit 33f14e
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
Packit 33f14e
#   if defined __NetBSD__
Packit 33f14e
        nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
Packit 33f14e
        const char *errmsg =
Packit 33f14e
          (catd != (nl_catd)-1
Packit 33f14e
           ? catgets (catd, 1, errnum, sys_errlist[errnum])
Packit 33f14e
           : sys_errlist[errnum]);
Packit 33f14e
#   endif
Packit 33f14e
#   if defined __hpux
Packit 33f14e
        nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
Packit 33f14e
        const char *errmsg =
Packit 33f14e
          (catd != (nl_catd)-1
Packit 33f14e
           ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
Packit 33f14e
           : sys_errlist[errnum]);
Packit 33f14e
#   endif
Packit 33f14e
#  else
Packit 33f14e
        const char *errmsg = sys_errlist[errnum];
Packit 33f14e
#  endif
Packit 33f14e
        if (errmsg == NULL || *errmsg == '\0')
Packit 33f14e
          ret = EINVAL;
Packit 33f14e
        else
Packit 33f14e
          ret = safe_copy (buf, buflen, errmsg);
Packit 33f14e
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
Packit 33f14e
        if (catd != (nl_catd)-1)
Packit 33f14e
          catclose (catd);
Packit 33f14e
#  endif
Packit 33f14e
      }
Packit 33f14e
    else
Packit 33f14e
      ret = EINVAL;
Packit 33f14e
Packit 33f14e
# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
Packit 33f14e
Packit 33f14e
    /* For a valid error number, the system's strerror() function returns
Packit 33f14e
       a pointer to a not copied string, not to a buffer.  */
Packit 33f14e
    if (errnum >= 0 && errnum < sys_nerr)
Packit 33f14e
      {
Packit 33f14e
        char *errmsg = strerror (errnum);
Packit 33f14e
Packit 33f14e
        if (errmsg == NULL || *errmsg == '\0')
Packit 33f14e
          ret = EINVAL;
Packit 33f14e
        else
Packit 33f14e
          ret = safe_copy (buf, buflen, errmsg);
Packit 33f14e
      }
Packit 33f14e
    else
Packit 33f14e
      ret = EINVAL;
Packit 33f14e
Packit 33f14e
# else
Packit 33f14e
Packit 33f14e
    gl_lock_lock (strerror_lock);
Packit 33f14e
Packit 33f14e
    {
Packit 33f14e
      char *errmsg = strerror (errnum);
Packit 33f14e
Packit 33f14e
      /* For invalid error numbers, strerror() on
Packit 33f14e
           - IRIX 6.5 returns NULL,
Packit 33f14e
           - HP-UX 11 returns an empty string.  */
Packit 33f14e
      if (errmsg == NULL || *errmsg == '\0')
Packit 33f14e
        ret = EINVAL;
Packit 33f14e
      else
Packit 33f14e
        ret = safe_copy (buf, buflen, errmsg);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
    gl_lock_unlock (strerror_lock);
Packit 33f14e
Packit 33f14e
# endif
Packit 33f14e
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
Packit 33f14e
    /* MSVC 14 defines names for many error codes in the range 100..140,
Packit 33f14e
       but _sys_errlist contains strings only for the error codes
Packit 33f14e
       < _sys_nerr = 43.  */
Packit 33f14e
    if (ret == EINVAL)
Packit 33f14e
      {
Packit 33f14e
        const char *errmsg;
Packit 33f14e
Packit 33f14e
        switch (errnum)
Packit 33f14e
          {
Packit 33f14e
          case 100 /* EADDRINUSE */:
Packit 33f14e
            errmsg = "Address already in use";
Packit 33f14e
            break;
Packit 33f14e
          case 101 /* EADDRNOTAVAIL */:
Packit 33f14e
            errmsg = "Cannot assign requested address";
Packit 33f14e
            break;
Packit 33f14e
          case 102 /* EAFNOSUPPORT */:
Packit 33f14e
            errmsg = "Address family not supported by protocol";
Packit 33f14e
            break;
Packit 33f14e
          case 103 /* EALREADY */:
Packit 33f14e
            errmsg = "Operation already in progress";
Packit 33f14e
            break;
Packit 33f14e
          case 105 /* ECANCELED */:
Packit 33f14e
            errmsg = "Operation canceled";
Packit 33f14e
            break;
Packit 33f14e
          case 106 /* ECONNABORTED */:
Packit 33f14e
            errmsg = "Software caused connection abort";
Packit 33f14e
            break;
Packit 33f14e
          case 107 /* ECONNREFUSED */:
Packit 33f14e
            errmsg = "Connection refused";
Packit 33f14e
            break;
Packit 33f14e
          case 108 /* ECONNRESET */:
Packit 33f14e
            errmsg = "Connection reset by peer";
Packit 33f14e
            break;
Packit 33f14e
          case 109 /* EDESTADDRREQ */:
Packit 33f14e
            errmsg = "Destination address required";
Packit 33f14e
            break;
Packit 33f14e
          case 110 /* EHOSTUNREACH */:
Packit 33f14e
            errmsg = "No route to host";
Packit 33f14e
            break;
Packit 33f14e
          case 112 /* EINPROGRESS */:
Packit 33f14e
            errmsg = "Operation now in progress";
Packit 33f14e
            break;
Packit 33f14e
          case 113 /* EISCONN */:
Packit 33f14e
            errmsg = "Transport endpoint is already connected";
Packit 33f14e
            break;
Packit 33f14e
          case 114 /* ELOOP */:
Packit 33f14e
            errmsg = "Too many levels of symbolic links";
Packit 33f14e
            break;
Packit 33f14e
          case 115 /* EMSGSIZE */:
Packit 33f14e
            errmsg = "Message too long";
Packit 33f14e
            break;
Packit 33f14e
          case 116 /* ENETDOWN */:
Packit 33f14e
            errmsg = "Network is down";
Packit 33f14e
            break;
Packit 33f14e
          case 117 /* ENETRESET */:
Packit 33f14e
            errmsg = "Network dropped connection on reset";
Packit 33f14e
            break;
Packit 33f14e
          case 118 /* ENETUNREACH */:
Packit 33f14e
            errmsg = "Network is unreachable";
Packit 33f14e
            break;
Packit 33f14e
          case 119 /* ENOBUFS */:
Packit 33f14e
            errmsg = "No buffer space available";
Packit 33f14e
            break;
Packit 33f14e
          case 123 /* ENOPROTOOPT */:
Packit 33f14e
            errmsg = "Protocol not available";
Packit 33f14e
            break;
Packit 33f14e
          case 126 /* ENOTCONN */:
Packit 33f14e
            errmsg = "Transport endpoint is not connected";
Packit 33f14e
            break;
Packit 33f14e
          case 128 /* ENOTSOCK */:
Packit 33f14e
            errmsg = "Socket operation on non-socket";
Packit 33f14e
            break;
Packit 33f14e
          case 129 /* ENOTSUP */:
Packit 33f14e
            errmsg = "Not supported";
Packit 33f14e
            break;
Packit 33f14e
          case 130 /* EOPNOTSUPP */:
Packit 33f14e
            errmsg = "Operation not supported";
Packit 33f14e
            break;
Packit 33f14e
          case 132 /* EOVERFLOW */:
Packit 33f14e
            errmsg = "Value too large for defined data type";
Packit 33f14e
            break;
Packit 33f14e
          case 133 /* EOWNERDEAD */:
Packit 33f14e
            errmsg = "Owner died";
Packit 33f14e
            break;
Packit 33f14e
          case 134 /* EPROTO */:
Packit 33f14e
            errmsg = "Protocol error";
Packit 33f14e
            break;
Packit 33f14e
          case 135 /* EPROTONOSUPPORT */:
Packit 33f14e
            errmsg = "Protocol not supported";
Packit 33f14e
            break;
Packit 33f14e
          case 136 /* EPROTOTYPE */:
Packit 33f14e
            errmsg = "Protocol wrong type for socket";
Packit 33f14e
            break;
Packit 33f14e
          case 138 /* ETIMEDOUT */:
Packit 33f14e
            errmsg = "Connection timed out";
Packit 33f14e
            break;
Packit 33f14e
          case 140 /* EWOULDBLOCK */:
Packit 33f14e
            errmsg = "Operation would block";
Packit 33f14e
            break;
Packit 33f14e
          default:
Packit 33f14e
            errmsg = NULL;
Packit 33f14e
            break;
Packit 33f14e
          }
Packit 33f14e
        if (errmsg != NULL)
Packit 33f14e
          ret = safe_copy (buf, buflen, errmsg);
Packit 33f14e
      }
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
    if (ret == EINVAL && !*buf)
Packit 33f14e
      snprintf (buf, buflen, "Unknown error %d", errnum);
Packit 33f14e
Packit 33f14e
    errno = saved_errno;
Packit 33f14e
    return ret;
Packit 33f14e
  }
Packit 33f14e
}