/* $Id: ncbi_strerror.c,v 1.11 2015/12/17 18:59:24 fukanchi Exp $
 * ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Authors:  Pavel Ivanov, Anton Lavrentiev, Denis Vakatov
 *
 * File Description:
 *   errno->text conversion helper
 */


#ifdef NCBI_INCLUDE_STRERROR_C


#  ifdef _FREETDS_LIBRARY_SOURCE

#    include "../impl/ncbi_ftds_ver.h"
#    define s_StrError        s_StrErrorInternal
#    define UTIL_TcharToUtf8  NCBI_FTDS_VERSION_NAME(UTIL_TcharToUtf8_ftds)
#    define UTIL_ReleaseBufferOnHeap  \
                      NCBI_FTDS_VERSION_NAME(UTIL_ReleaseBufferOnHeap_ftds)

#  endif /*_FREETDS_LIBRARY_SOURCE*/


#  if defined(NCBI_OS_MSWIN)  &&  defined(_UNICODE)

static const char* s_WinStrdup(const char* str)
{
    size_t n = strlen(str);
    char*  s = (char*) LocalAlloc(LMEM_FIXED, ++n * sizeof(*s));
    return s ? (const char*) memcpy(s, str, n) : 0;
}

#    define   MSWIN_STRDUP(s)         s_WinStrdup(s)

#    ifndef   UTIL_ReleaseBuffer
#      define UTIL_ReleaseBuffer(x)   UTIL_ReleaseBufferOnHeap(x)
#    endif

#  else /*NCBI_OS_MSWIN && _UNICODE*/

#    define   MSWIN_STRDUP(s)         (s)

#    ifndef   UTIL_TcharToUtf8
#      define UTIL_TcharToUtf8(x)     (x)
#    endif

#    ifndef   UTIL_ReleaseBuffer
#      define UTIL_ReleaseBuffer(x)   /*void*/
#    endif

#  endif /*NCBI_OS_MSWIN && _UNICODE*/


#  ifdef NCBI_OS_MSWIN

#    ifdef _UNICODE

extern const char* UTIL_TcharToUtf8(const TCHAR* str)
{
    char* s = NULL;
    if (str) {
        /* Note "-1" means to consume all input including the trailing NUL */
        int n = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
        if (n > 0) {
            s = (char*) LocalAlloc(LMEM_FIXED, n * sizeof(*s));
            if (s)
                WideCharToMultiByte(CP_UTF8, 0, str, -1, s,    n, NULL, NULL);
        }
    }
    return s;
}

#    endif /*_UNICODE*/

extern void UTIL_ReleaseBufferOnHeap(const void* ptr)
{
    if (ptr)
        LocalFree((HLOCAL) ptr);
}

#  endif /*NCBI_OS_MSWIN*/


static const char* s_StrErrorInternal(int error)
{
    static const struct {
        int         errnum;
        const char* errstr;
    } errmap[] = {
#  ifdef NCBI_OS_MSWIN
        {WSAEINTR,              "Interrupted system call"},
        {WSAEBADF,              "Bad file number"},
        {WSAEACCES,             "Access denied"},
        {WSAEFAULT,             "Segmentation fault"},
        {WSAEINVAL,             "Invalid agrument"},
        {WSAEMFILE,             "Too many open files"},
        /*
         * Windows Sockets definitions of regular Berkeley error constants
         */
        {WSAEWOULDBLOCK,        "Resource temporarily unavailable"},
        {WSAEINPROGRESS,        "Operation now in progress"},
        {WSAEALREADY,           "Operation already in progress"},
        {WSAENOTSOCK,           "Not a socket"},
        {WSAEDESTADDRREQ,       "Destination address required"},
        {WSAEMSGSIZE,           "Invalid message size"},
        {WSAEPROTOTYPE,         "Wrong protocol type"},
        {WSAENOPROTOOPT,        "Bad protocol option"},
        {WSAEPROTONOSUPPORT,    "Protocol not supported"},
        {WSAESOCKTNOSUPPORT,    "Socket type not supported"},
        {WSAEOPNOTSUPP,         "Operation not supported"},
        {WSAEPFNOSUPPORT,       "Protocol family not supported"},
        {WSAEAFNOSUPPORT,       "Address family not supported"},
        {WSAEADDRINUSE,         "Address already in use"},
        {WSAEADDRNOTAVAIL,      "Cannot assign requested address"},
        {WSAENETDOWN,           "Network is down"},
        {WSAENETUNREACH,        "Network is unreachable"},
        {WSAENETRESET,          "Connection dropped on network reset"},
        {WSAECONNABORTED,       "Software caused connection abort"},
        {WSAECONNRESET,         "Connection reset by peer"},
        {WSAENOBUFS,            "No buffer space available"},
        {WSAEISCONN,            "Socket is already connected"},
        {WSAENOTCONN,           "Socket is not connected"},
        {WSAESHUTDOWN,          "Cannot send after socket shutdown"},
        {WSAETOOMANYREFS,       "Too many references"},
        {WSAETIMEDOUT,          "Operation timed out"},
        {WSAECONNREFUSED,       "Connection refused"},
        {WSAELOOP,              "Infinite loop"},
        {WSAENAMETOOLONG,       "Name too long"},
        {WSAEHOSTDOWN,          "Host is down"},
        {WSAEHOSTUNREACH,       "Host unreachable"},
        {WSAENOTEMPTY,          "Not empty"},
        {WSAEPROCLIM,           "Too many processes"},
        {WSAEUSERS,             "Too many users"},
        {WSAEDQUOT,             "Quota exceeded"},
        {WSAESTALE,             "Stale descriptor"},
        {WSAEREMOTE,            "Remote error"},
        /*
         * Extended Windows Sockets error constant definitions
         */
        {WSASYSNOTREADY,        "Network subsystem is unavailable"},
        {WSAVERNOTSUPPORTED,    "Winsock.dll version out of range"},
        {WSANOTINITIALISED,     "Not yet initialized"},
        {WSAEDISCON,            "Graceful shutdown in progress"},
#    ifdef WSAENOMORE
        /*NB: replaced with WSA_E_NO_MORE*/
        {WSAENOMORE,            "No more data available"},
#    endif /*WSAENOMORE*/
#    ifdef WSA_E_NO_MORE
        {WSA_E_NO_MORE,         "No more data available"},
#    endif /*WSA_E_NO_MORE*/
#    ifdef WSAECANCELLED
        /*NB: replaced with WSA_E_CANCELLED*/
        {WSAECANCELLED,         "Call has been cancelled"},
#    endif /*WSAECANCELLED*/
#    ifdef WSA_E_CANCELLED
        {WSA_E_CANCELLED,       "Call has been cancelled"},
#    endif /*WSA_E_CANCELLED*/
        {WSAEINVALIDPROCTABLE,  "Invalid procedure table"},
        {WSAEINVALIDPROVIDER,   "Invalid provider version number"},
        {WSAEPROVIDERFAILEDINIT,"Cannot init provider"},
        {WSASYSCALLFAILURE,     "System call failed"},
        {WSASERVICE_NOT_FOUND,  "Service not found"},
        {WSATYPE_NOT_FOUND,     "Class type not found"},
        {WSAEREFUSED,           "Query refused"},
        /*
         * WinSock 2 extension
         */
#    ifdef WSA_IO_PENDING
        {WSA_IO_PENDING,        "Operation has been queued"},
#    endif /*WSA_IO_PENDING*/
#    ifdef WSA_IO_INCOMPLETE
        {WSA_IO_INCOMPLETE,     "Operation still in progress"},
#    endif /*WSA_IO_INCOMPLETE*/
#    ifdef WSA_INVALID_HANDLE
        {WSA_INVALID_HANDLE,    "Invalid handle"},
#    endif /*WSA_INVALID_HANDLE*/
#    ifdef WSA_INVALID_PARAMETER
        {WSA_INVALID_PARAMETER, "Invalid parameter"},
#    endif /*WSA_INVALID_PARAMETER*/
#    ifdef WSA_NOT_ENOUGH_MEMORY
        {WSA_NOT_ENOUGH_MEMORY, "Out of memory"},
#    endif /*WSA_NOT_ENOUGH_MEMORY*/
#    ifdef WSA_OPERATION_ABORTED
        {WSA_OPERATION_ABORTED, "Operation aborted"},
#    endif /*WSA_OPERATION_ABORTED*/
#  endif /*NCBI_OS_MSWIN*/
#  ifdef NCBI_OS_MSWIN
#    define EAI_BASE  0
#  else
#    define EAI_BASE  100000
#  endif /*NCBI_OS_MSWIN*/
#  ifdef EAI_ADDRFAMILY
        {EAI_ADDRFAMILY + EAI_BASE,
                                "Address family not supported"},
#  endif /*EAI_ADDRFAMILY*/
#  ifdef EAI_AGAIN
        {EAI_AGAIN + EAI_BASE,
                                "Temporary failure in name resolution"},
#  endif /*EAI_AGAIN*/
#  ifdef EAI_BADFLAGS
        {EAI_BADFLAGS + EAI_BASE,
                                "Invalid value for lookup flags"},
#  endif /*EAI_BADFLAGS*/
#  ifdef EAI_FAIL
        {EAI_FAIL + EAI_BASE,
                                "Non-recoverable failure in name resolution"},
#  endif /*EAI_FAIL*/
#  ifdef EAI_FAMILY
        {EAI_FAMILY + EAI_BASE,
                                "Address family not supported"},
#  endif /*EAI_FAMILY*/
#  ifdef EAI_MEMORY
        {EAI_MEMORY + EAI_BASE,
                                "Memory allocation failure"},
#  endif /*EAI_MEMORY*/
#  ifdef EAI_NODATA
        {EAI_NODATA + EAI_BASE,
                                "No address associated with nodename"},
#  endif /*EAI_NODATA*/
#  ifdef EAI_NONAME
        {EAI_NONAME + EAI_BASE,
                                "Host/service name not known"},
#  endif /*EAI_NONAME*/
#  ifdef EAI_OVERFLOW
        {EAI_OVERFLOW + EAI_BASE,
                                "Buffer overflow"},
#  endif /*EAI_OVERFLOW*/
#  ifdef EAI_SERVICE
        {EAI_SERVICE + EAI_BASE,
                                "Service name not supported for socket type"},
#  endif /*EAI_SERVICE*/
#  ifdef EAI_SOCKTYPE
        {EAI_SOCKTYPE + EAI_BASE,
                                "Socket type not supported"},
#  endif /*EAI_SOCKTYPE*/
        /* GNU extensions */
#  ifdef EAI_ALLDONE
        {EAI_ALLDONE + EAI_BASE,
                                "All requests done"},
#  endif /*EAI_ALLDONE*/
#  ifdef EAI_CANCELED
        {EAI_CANCELED + EAI_BASE,
                                "Request canceled"},
#  endif /*EAI_BADFLAGS*/
#  ifdef EAI_INPROGRESS
        {EAI_INPROGRESS + EAI_BASE,
                                "Processing request in progress"},
#  endif /*EAI_INPROGRESS*/
#  ifdef EAI_INTR
        {EAI_INTR + EAI_BASE,
                                "Interrupted by a signal"},
#  endif /*EAI_INTR*/
#  ifdef EAI_NOTCANCELED
        {EAI_NOTCANCELED + EAI_BASE,
                                "Request not canceled"},
#  endif /*EAI_NOTCANCELED*/
#  ifdef NCBI_OS_MSWIN
#    define DNS_BASE  0
#  else
#    define DNS_BASE  200000
#  endif /*NCBI_OS_MSWIN*/
#  ifdef HOST_NOT_FOUND
        {HOST_NOT_FOUND + DNS_BASE,
                                "Host not found"},
#  endif /*HOST_NOT_FOUND*/
#  ifdef TRY_AGAIN
        {TRY_AGAIN + DNS_BASE,
                                "DNS server failure"},
#  endif /*TRY_AGAIN*/
#  ifdef NO_RECOVERY
        {NO_RECOVERY + DNS_BASE,
                                "Unrecoverable DNS error"},
#  endif /*NO_RECOVERY*/
#  ifdef NO_ADDRESS
        {NO_ADDRESS + DNS_BASE,
                                "No address record found in DNS"},
#  endif /*NO_ADDRESS*/
#  ifdef NO_DATA
        {NO_DATA + DNS_BASE,
                                "No DNS data of requested type"},
#  endif /*NO_DATA*/

        /* Last dummy entry - must present */
        {0, 0}
    };
#if defined(NCBI_OS_LINUX)  ||  defined(NCBI_OS_CYGWIN)
    /* To work correctly, descending order of offsets is required here */
    static const struct {
        int           erroff;
        const char* (*errfun)(int errnum);
    } errsup[] = {
#  ifdef __GLIBC__
        {DNS_BASE,              hstrerror},
#  endif /*__GLIBC__*/
#  if defined(HAVE_GETADDRINFO)  ||  defined(HAVE_GETNAMEINFO)
        {EAI_BASE,              gai_strerror},
#  endif /*HAVE_GETADDRINFO || HAVE_GETNAMEINFO*/
        /* Last dummy entry - must present */
        {0, 0}
    };
#endif /*NCBI_OS_LINUX || NCBI_OS_CYGWIN*/
    size_t i;

    if (!error)
        return 0;

#if defined(NCBI_OS_LINUX)  ||  defined(NCBI_OS_CYGWIN)
    for (i = 0;  i < sizeof(errsup) / sizeof(errsup[0]) - 1/*dummy*/;  ++i) {
        if (errsup[i].erroff < error) {
            const char* errstr = errsup[i].errfun(error - errsup[i].erroff);
            if (errstr  &&  *errstr)
                return errstr;
        }
    }
#endif /*NCBI_OS_LINUX || NCBI_OS_CYGWIN*/

    for (i = 0;  i < sizeof(errmap) / sizeof(errmap[0]) - 1/*dummy*/;  ++i) {
        if (errmap[i].errnum == error)
            return MSWIN_STRDUP(errmap[i].errstr);
    }

#  if defined(NCBI_OS_MSWIN)  &&  defined(_UNICODE)
    return UTIL_TcharToUtf8(_wcserror(error));
#  else
    return strerror(error);
#  endif /*NCBI_OS_MSWIN && _UNICODE*/
}


#endif /*NCBI_INCLUDE_STRERROR_C*/
