/* strerror.c --- POSIX compatible system error routine

   Copyright (C) 2007-2010 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/>.  */

#include <config.h>

#include <string.h>

#if REPLACE_STRERROR

# include <errno.h>
# include <stdio.h>

# if GNULIB_defined_ESOCK /* native Windows platforms */
#  if HAVE_WINSOCK2_H
#   include <winsock2.h>
#  endif
# endif

# include "intprops.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)
{
  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

# 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_ECANCELED
    case ECANCELED:
      msg = "Operation canceled";
      break;
# endif
    }

  if (msg)
    return (char *) msg;

  {
    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;
  }
}

#endif
