/*******************************************************************************
 * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution, and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * Machine and OS dependend definitions.
 * This module implements host OS abstraction layer that helps make
 * agent code portable between Linux, Windows, VxWorks and potentially other OSes.
 */

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include "mdep.h"
#include "myalloc.h"

pthread_attr_t pthread_create_attr;

#ifdef WIN32

#include <fcntl.h>
#include <shlobj.h>

#define ERR_SOCKET (-1)
#define ERR_WIN32  (-2)

/*********************************************************************
    Support of pthreads on Windows is implemented according to
    reccomendations from the paper:
    
    Strategies for Implementing POSIX Condition Variables on Win32
    C++ Report, SIGS, Vol. 10, No. 5, June, 1998

    Douglas C. Schmidt and Irfan Pyarali
    Department of Computer Science
    Washington University, St. Louis, Missouri
**********************************************************************/

void pthread_mutex_init(pthread_mutex_t * mutex, void * attr) {
    assert(attr == NULL);
    *mutex = CreateMutex(NULL, FALSE, NULL);
    if (*mutex == NULL) {
        fprintf(stderr, "Can't create mutex: error %d\n", GetLastError());
        exit(1);
    }
}

void pthread_mutex_lock(pthread_mutex_t * mutex) {
    assert(mutex != NULL);
    assert(*mutex != NULL);
    WaitForSingleObject(*mutex, INFINITE);
}

void pthread_mutex_unlock(pthread_mutex_t * mutex) {
    assert(mutex != NULL);
    assert(*mutex != NULL);
    ReleaseMutex(*mutex);
}

void pthread_cond_init(pthread_cond_t * cond, void * attr) {
    assert(attr == NULL);
    cond->waiters_count = 0;
    cond->was_broadcast = 0;
    cond->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
    InitializeCriticalSection(&cond->waiters_count_lock);
    cond->waiters_done = CreateEvent(NULL, FALSE, FALSE, NULL);
}

int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) {
    DWORD res = 0;
    int last_waiter = 0;

    EnterCriticalSection(&cond->waiters_count_lock);
    cond->waiters_count++;
    LeaveCriticalSection(&cond->waiters_count_lock);

    // This call atomically releases the mutex and waits on the
    // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
    // are called by another thread.
    res = SignalObjectAndWait(*mutex, cond->sema, INFINITE, FALSE);

    // Reacquire lock to avoid race conditions.
    EnterCriticalSection(&cond->waiters_count_lock);

    // We're no longer waiting...
    cond->waiters_count--;

    // Check to see if we're the last waiter after <pthread_cond_broadcast>.
    last_waiter = cond->was_broadcast && cond->waiters_count == 0;

    LeaveCriticalSection(&cond->waiters_count_lock);

    // If we're the last waiter thread during this particular broadcast
    // then let all the other threads proceed.
    if (last_waiter) {
        // This call atomically signals the <waiters_done_> event and waits until
        // it can acquire the <mutex>.  This is required to ensure fairness. 
        SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
    }
    else {
        // Always regain the external mutex since that's the guarantee we
        // give to our callers. 
        WaitForSingleObject(*mutex, INFINITE);
    }
    assert(res == WAIT_OBJECT_0);
    return 0;
}

int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * timeout) {
    DWORD res = 0;
    int last_waiter = 0;

    EnterCriticalSection(&cond->waiters_count_lock);
    cond->waiters_count++;
    LeaveCriticalSection(&cond->waiters_count_lock);

    // This call atomically releases the mutex and waits on the
    // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
    // are called by another thread.
    res = SignalObjectAndWait(*mutex, cond->sema, timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000, FALSE);

    // Reacquire lock to avoid race conditions.
    EnterCriticalSection(&cond->waiters_count_lock);

    // We're no longer waiting...
    cond->waiters_count--;

    // Check to see if we're the last waiter after <pthread_cond_broadcast>.
    last_waiter = cond->was_broadcast && cond->waiters_count == 0;

    LeaveCriticalSection(&cond->waiters_count_lock);

    // If we're the last waiter thread during this particular broadcast
    // then let all the other threads proceed.
    if (last_waiter) {
        // This call atomically signals the <waiters_done> event and waits until
        // it can acquire the <mutex>.  This is required to ensure fairness. 
        SignalObjectAndWait(cond->waiters_done, *mutex, INFINITE, FALSE);
    }
    else {
        // Always regain the external mutex since that's the guarantee we
        // give to our callers. 
        WaitForSingleObject(*mutex, INFINITE);
    }

    if (res == WAIT_TIMEOUT) return errno = ETIMEDOUT;
    assert(res == WAIT_OBJECT_0);
    return 0;
}

void pthread_cond_signal(pthread_cond_t *cond) {
    int have_waiters = 0;
    
    EnterCriticalSection(&cond->waiters_count_lock);
    have_waiters = cond->waiters_count > 0;
    LeaveCriticalSection(&cond->waiters_count_lock);

    // If there aren't any waiters, then this is a no-op.  
    if (have_waiters) ReleaseSemaphore(cond->sema, 1, 0);
}

void pthread_cond_broadcast(pthread_cond_t *cond) {
    int have_waiters = 0;

    // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
    // consistent relative to each other.
    EnterCriticalSection(&cond->waiters_count_lock);

    if (cond->waiters_count > 0) {
        // We are broadcasting, even if there is just one waiter...
        // Record that we are broadcasting, which helps optimize
        // <pthread_cond_wait> for the non-broadcast case.
        cond->was_broadcast = 1;
        have_waiters = 1;
    }

    if (have_waiters) {
        // Wake up all the waiters atomically.
        ReleaseSemaphore(cond->sema, cond->waiters_count, 0);

        LeaveCriticalSection(&cond->waiters_count_lock);

        // Wait for all the awakened threads to acquire the counting
        // semaphore. 
        WaitForSingleObject(cond->waiters_done, INFINITE);
        // This assignment is okay, even without the <waiters_count_lock_> held 
        // because no other waiter threads can wake up to access it.
        cond->was_broadcast = 0;
    }
    else {
        LeaveCriticalSection(&cond->waiters_count_lock);
    }
}

typedef struct ThreadArgs ThreadArgs;

struct ThreadArgs {
    void * (*start)(void *);
    void * args;
};

static void start_thread(void * x) {
    ThreadArgs a = *(ThreadArgs *)x;

    loc_free(x);
    ExitThread((DWORD)a.start(a.args));
}

int pthread_create(pthread_t * thread, pthread_attr_t * attr,
                   void * (*start)(void *), void * args) {
    unsigned long r;
    ThreadArgs * a;

    a = (ThreadArgs *)loc_alloc(sizeof(ThreadArgs));
    a->start = start;
    a->args = args;
    r = _beginthread(start_thread, 0, a);
    if (r == (unsigned long)-1) {
        int error = errno;
        loc_free(a);
        errno = error;
        return error;
    }
    *thread = (HANDLE)r;
    return 0;
}

int pthread_join(pthread_t thread, void **value_ptr) {
    if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) {
        return EINVAL;
    }
    if (!GetExitCodeThread(thread, (LPDWORD)value_ptr)) {
        return EINVAL;
    }
    CloseHandle(thread);
    return 0;
}

pthread_t pthread_self(void) {
    return GetCurrentThread();
}

static __int64 file_time_to_unix_time (const FILETIME * ft) {
    __int64 res = (__int64)ft->dwHighDateTime << 32;

    res |= ft->dwLowDateTime;
    res /= 10;                  /* from 100 nano-sec periods to usec */
    res -= 11644473600000000u;  /* from Win epoch to Unix epoch */
    return res;
}

int clock_gettime(clockid_t clock_id, struct timespec * tp) {
    FILETIME ft;
    __int64 tim;

    assert(clock_id == CLOCK_REALTIME);
    if (!tp) {
        errno = EINVAL;
        return -1;
    }
    GetSystemTimeAsFileTime(&ft);
    tim = file_time_to_unix_time(&ft);
    tp->tv_sec  = (long)(tim / 1000000L);
    tp->tv_nsec = (long)(tim % 1000000L) * 1000;
    return 0;
}

void usleep(useconds_t useconds) {
    Sleep(useconds / 1000);
}

void perror(const char * msg) {
    int error = errno;
    if (error == ERR_SOCKET) {
        fprintf(stderr, "%s: socket error 0x%08x\n", msg, WSAGetLastError());
    }
    else if (error == ERR_WIN32) {
        fprintf(stderr, "%s: Win32 error 0x%08x\n", msg, GetLastError());
    }
    else {
        fprintf(stderr, "%s: %s\n", msg, strerror(error));
    }
}

#undef bind
int wsa_bind(int socket, const struct sockaddr * addr, int addr_size) {
    int res = 0;
    SetLastError(0);
    WSASetLastError(0);
    res = bind(socket, addr, addr_size);
    if (res != 0) {
        errno = ERR_SOCKET;
        return -1;
    }
    return 0;
}

#undef socket
int wsa_socket(int af, int type, int protocol) {
    int res = 0;
    SetLastError(0);
    WSASetLastError(0);
    res = socket(af, type, protocol);
    if (res < 0) {
        errno = ERR_SOCKET;
        return -1;
    }
    return res;
}

#ifndef __GNUC__
int inet_aton(const char *cp, struct in_addr *inp) {
    return ( ( inp->s_addr = inet_addr(cp) ) != INADDR_NONE );
}
#endif

int truncate(const char * path, int64 size) {
    int res = 0;
    int f = _open(path, _O_RDWR | _O_BINARY);
    if (f < 0) return -1;
    res = ftruncate(f, size);
    _close(f);
    return res;
}

int ftruncate(int fd, int64 size) {
    int64 cur, pos;
    BOOL ret = FALSE;
    HANDLE handle = (HANDLE)_get_osfhandle(fd);

    if (handle == INVALID_HANDLE_VALUE) {
        errno = EBADF;
        return -1;
    }
    /* save the current file pointer */
    cur = _lseeki64(fd, 0, SEEK_CUR);
    if (cur >= 0) {
        pos = _lseeki64(fd, size, SEEK_SET);
        if (pos >= 0) {
            ret = SetEndOfFile(handle);
            if (!ret) errno = EBADF;
        }
        /* restore the file pointer */
        _lseeki64(fd, cur, SEEK_SET);
    }
    return ret ? 0 : -1;
}

DIR * opendir(const char *path) {
    DIR * d = (DIR *)loc_alloc(sizeof(DIR));
    if (!d) { errno = ENOMEM; return 0; }
    strcpy(d->path, path);
    strcat(d->path, "/*.*");
    d->hdl = -1;
    return d;
}

struct dirent * readdir(DIR *d) {
    static struct dirent de;
    if (d->hdl < 0) {
        d->hdl = _findfirsti64(d->path, &d->blk);
        if (d->hdl < 0) {
            if (errno == ENOENT) errno = 0;
            return 0;
        }
    }
    else {
        int r = _findnexti64(d->hdl, &d->blk);
        if (r < 0) {
            if (errno == ENOENT) errno = 0;
            return 0;
        }
    }
    strcpy(de.d_name, d->blk.name);
    de.d_size = d->blk.size;
    de.d_atime = d->blk.time_access;
    de.d_ctime = d->blk.time_create;
    de.d_wtime = d->blk.time_write;
    return &de;
}

int closedir(DIR * d) {
    int r = 0;
    if (!d) {
        errno = EBADF;
        return -1;
    }
    if (d->hdl >= 0) r = _findclose(d->hdl);
    loc_free(d);
    return r;
}

int getuid(void) {
    /* Windows user is always a superuser :) */
    return 0;
}

int geteuid(void) {
    return 0;
}

int getgid(void) {
    return 0;
}

int getegid(void) {
    return 0;
}

char * get_os_name(void) {
    static char str[256];
    OSVERSIONINFOEX info;
    memset(&info, 0, sizeof(info));
    info.dwOSVersionInfoSize = sizeof(info);
    GetVersionEx((OSVERSIONINFO *)&info);
    switch (info.dwMajorVersion) {
    case 4:
        return "Windows NT";
    case 5:
        switch (info.dwMinorVersion) {
        case 0: return "Windows 2000";
        case 1: return "Windows XP";
        case 2: return "Windows Server 2003";
        }
        break;
    case 6:
        return "Windows Vista";
    }
    snprintf(str, sizeof(str), "Windows %d.%d", info.dwMajorVersion, info.dwMajorVersion);
    return str;
}

char * get_user_home(void) {
    static char buf[MAX_PATH];
    if (buf[0] != 0) return buf;
    if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, buf))) return buf;
    return NULL;
}

void ini_mdep(void) {
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD( 1, 1 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 ) {
        fprintf(stderr, "Couldn't access winsock.dll.\n");
        exit(1);
    }
    /* Confirm that the Windows Sockets DLL supports 1.1.*/
    /* Note that if the DLL supports versions greater */
    /* than 1.1 in addition to 1.1, it will still return */
    /* 1.1 in wVersion since that is the version we */
    /* requested.     */
    if (LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1) {
        fprintf(stderr, "Unacceptable version of winsock.dll.\n");
        WSACleanup();
        exit(1);
    }
}

#elif defined(_WRS_KERNEL)

void usleep(useconds_t useconds) {
    struct timespec tv;
    tv.tv_sec = useconds / 1000000;
    tv.tv_nsec = (useconds % 1000000) * 1000;
    nanosleep(&tv, NULL);
}

int truncate(char * path, int64 size) {
    int f = open(path, O_RDWR, 0);
    if (f < 0) return -1;
    if (ftruncate(f, size) < 0) {
        int err = errno;
        close(f);
        errno = err;
        return -1;
    }
    return close(f);
}

int getuid(void) {
    return 0;
}

int geteuid(void) {
    return 0;
}

int getgid(void) {
    return 0;
}

int getegid(void) {
    return 0;
}

char * get_os_name(void) {
    static char str[256];
    snprintf(str, sizeof(str), "VxWorks %s", kernelVersion());
    return str;
}

char * get_user_home(void) {
    return "/";
}

void ini_mdep(void) {
    pthread_attr_init(&pthread_create_attr);
    pthread_attr_setstacksize(&pthread_create_attr, 0x4000);
    pthread_attr_setname(&pthread_create_attr, "tTcf");
}

#else

#include <pwd.h>
#include <sys/utsname.h>
#ifndef __CYGWIN__
#include <asm/unistd.h>
#endif

char * get_os_name(void) {
    static char str[256];
    struct utsname info;
    memset(&info, 0, sizeof(info));
    uname(&info);
    assert(strlen(info.sysname) + strlen(info.release) < sizeof(str));
    snprintf(str, sizeof(str), "%s %s", info.sysname, info.release);
    return str;
}

char * get_user_home(void) {
    static char buf[PATH_MAX];
    if (buf[0] == 0) {
        struct passwd * pwd = getpwuid(getuid());
        if (pwd == NULL) return NULL;
        strcpy(buf, pwd->pw_dir);
    }
    return buf;
}

#ifndef __CYGWIN__
int tkill(pid_t pid, int signal) {
    return syscall(__NR_tkill, pid, signal);
}
#endif

void ini_mdep(void) {
    pthread_attr_init(&pthread_create_attr);
    pthread_attr_setstacksize(&pthread_create_attr, 0x8000);
#if defined(__CYGWIN__)
    /* Force initialization of WinSock library */
    closesocket(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
#endif
}

#endif


/** canonicalize_file_name ****************************************************/

#if defined(WIN32)

char * canonicalize_file_name(const char * path) {
    char buf[MAX_PATH];
    char * basename;
    int i = 0;
    DWORD len = GetFullPathName(path, sizeof(buf), buf, &basename);
    if (len == 0) {
        errno = ENOENT;
        return NULL;
    }
    if (len > MAX_PATH - 1) {
        errno = ENAMETOOLONG;
        return NULL;
    }
    while (buf[i] != 0) {
        if (buf[i] == '\\') buf[i] = '/';
        i++;
    }
    return strdup(buf);
}

#elif defined(_WRS_KERNEL) || defined(__CYGWIN__)

char * canonicalize_file_name(const char * path) {
    char buf[PATH_MAX];
    int i = 0, j = 0;
    if (path[0] == '.' && (path[1] == '/' || path[1] == '\\' || path[1] == 0)) {
        getcwd(buf, sizeof(buf));
        j = strlen(buf);
        if (j == 1 && buf[0] == '/') j = 0;
        i = 1;
    }
    else if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\' || path[2] == 0)) {
        getcwd(buf, sizeof(buf));
        j = strlen(buf);
        while (j > 0 && buf[j - 1] != '/') j--;
        if (j > 0 && buf[j - 1] == '/') j--;
        i = 2;
    }
    while (path[i] && j < PATH_MAX - 1) {
        char ch = path[i];
        if (ch == '\\') ch = '/';
        if (ch == '/') {
            if (path[i + 1] == '/' || path[i + 1] == '\\') {
                i++;
                continue;
            }
            if (path[i + 1] == '.') {
                if (path[i + 2] == 0) {
                    break;
                }
                if (path[i + 2] == '/' || path[i + 2] == '\\') {
                    i += 2;
                    continue;
                }
                if ((j == 0 || buf[0] == '/') && path[i + 2] == '.') {
                    if (path[i + 3] == '/' || path[i + 3] == '\\' || path[i + 3] == 0) {
                        while (j > 0 && buf[j - 1] != '/') j--;
                        if (j > 0 && buf[j - 1] == '/') j--;
                        i += 3;
                        continue;
                    }
                }
            }
        }
        buf[j++] = ch;
        i++;
    }
    if (j == 0 && path[0] != 0) buf[j++] = '/';
    buf[j] = 0;
    return strdup(buf);
}

#endif


/** getaddrinfo ***************************************************************/

#if defined(_WRS_KERNEL) && defined(USE_VXWORKS_GETADDRINFO)

// TODO: VxWorks 6.6 getaddrinfo returns error when port is empty string, should return port 0
// TODO: VxWorks 6.6 source (as shipped at 2007 fall release) does not include ipcom header files.
extern void ipcom_freeaddrinfo();
extern int ipcom_getaddrinfo();

static struct ai_errlist {
    const char * str;
    int code;
} ai_errlist[] = {
    { "Success", 0 },
    /*
    { "Invalid value for ai_flags", IP_EAI_BADFLAGS },
    { "Non-recoverable failure in name resolution", IP_EAI_FAIL },
    { "ai_family not supported", IP_EAI_FAMILY },
    { "Memory allocation failure", IP_EAI_MEMORY },
    { "hostname nor servname provided, or not known", IP_EAI_NONAME },
    { "servname not supported for ai_socktype",     IP_EAI_SERVICE },
    { "ai_socktype not supported", IP_EAI_SOCKTYPE },
    { "System error returned in errno", IP_EAI_SYSTEM },
     */
    /* backward compatibility with userland code prior to 2553bis-02 */
    { "Address family for hostname not supported", 1 },
    { "No address associated with hostname", 7 },
    { NULL, -1 },
};

void loc_freeaddrinfo(struct addrinfo * ai) {
    ipcom_freeaddrinfo(ai);
}

int loc_getaddrinfo(const char * nodename, const char * servname,
       const struct addrinfo * hints, struct addrinfo ** res) {
    return ipcom_getaddrinfo(nodename, servname, hints, res);
}

const char * loc_gai_strerror(int ecode) {
    struct ai_errlist * p;
    static char buf[32];
    for (p = ai_errlist; p->str; p++) {
        if (p->code == ecode) return p->str;
    }
    snprintf(buf, sizeof(buf), "Error code %d", ecode);
    return buf;
}

#elif defined(_WRS_KERNEL)

union sockaddr_union {
    struct sockaddr sa;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
};

extern int ipcom_getsockaddrbyaddr();

void loc_freeaddrinfo(struct addrinfo * ai) {
    while (ai != NULL) {
        struct addrinfo * next = ai->ai_next;
        if (ai->ai_canonname != NULL) loc_free(ai->ai_canonname);
        if (ai->ai_addr != NULL) loc_free(ai->ai_addr);
        loc_free(ai);
        ai = next;
    }
}

int loc_getaddrinfo(const char * nodename, const char * servname,
       const struct addrinfo * hints, struct addrinfo ** res) {
    int family = 0;
    int flags = 0;
    int socktype = 0;
    int protocol = 0;
    int err = 0;
    int port = 0;
    char * canonname = NULL;
    const char * host = NULL;
    struct addrinfo * ai = NULL;
    union sockaddr_union * sa = NULL;
    
    *res = NULL;
    
    if (hints != NULL) {
        flags = hints->ai_flags;
        family = hints->ai_family;
        socktype = hints->ai_socktype;
        protocol = hints->ai_protocol;
    }
    if (family == AF_UNSPEC) {
        struct addrinfo lhints;
        struct addrinfo ** tmp;
        int err_v6;

        if (hints == NULL) memset(&lhints, 0, sizeof(lhints));
        else memcpy(&lhints, hints, sizeof(lhints));
        lhints.ai_family = AF_INET6;
        err_v6 = loc_getaddrinfo(nodename, servname, &lhints, res);
        lhints.ai_family = AF_INET;
        while (*res != NULL) res = &(*res)->ai_next;
        err = loc_getaddrinfo(nodename, servname, &lhints, res);
        return err && err_v6 ? err : 0;
    }
    if (servname != NULL && servname[0] != 0) {
        char * p = NULL;
        port = strtol(servname, &p, 10);
        if (port < 0 || port > 0xffff || *p != '\0' || p == servname) {
            return 1;
        }
    }
    if (nodename != NULL && nodename[0] != 0) {
        host = nodename;
    }
    else if (flags & AI_PASSIVE) {
        host = family == AF_INET ? "0.0.0.0" : "::";
    }
    else {
        host = family == AF_INET ? "127.0.0.1" : "::1";
    }
    if (socktype == 0) {
        socktype = SOCK_STREAM;
    }
    if (protocol == 0) {
        protocol = socktype == SOCK_STREAM ? IPPROTO_TCP : IPPROTO_UDP;
    }
    
    sa = loc_alloc_zero(sizeof(*sa));
    err = ipcom_getsockaddrbyaddr(family, host, (struct sockaddr *)sa);
    if (err) {
        loc_free(sa);
        return err;
    }
    
    ai = loc_alloc_zero(sizeof(*ai));
    switch (family) {
    case AF_INET:
        assert(sa->sin.sin_family == AF_INET);
        sa->sin.sin_port = htons(port);
        ai->ai_addrlen = sizeof(struct sockaddr_in);
        break;
    case AF_INET6:
        assert(sa->sin6.sin6_family == AF_INET6);
        sa->sin6.sin6_port = htons(port);
        ai->ai_addrlen = sizeof(struct sockaddr_in6);
        break;
    default:
        loc_free(sa);
        loc_free(ai);
        return 2;
    }
    
    ai->ai_flags = 0;
    ai->ai_family = family;
    ai->ai_socktype = socktype;
    ai->ai_protocol = protocol;
    ai->ai_canonname = canonname;
    ai->ai_addr = (struct sockaddr *)sa;
    ai->ai_next = NULL;
    *res = ai;
    return 0;
}

const char * loc_gai_strerror(int ecode) {
    static char buf[32];
    if (ecode == 0) return "Success";
    snprintf(buf, sizeof(buf), "Error code %d", ecode);
    return buf;
}

#elif defined(__CYGWIN__)

extern void __stdcall freeaddrinfo(struct addrinfo *);
extern int __stdcall getaddrinfo(const char *, const char *,
                const struct addrinfo *, struct addrinfo **);

void loc_freeaddrinfo(struct addrinfo * ai) {
        freeaddrinfo(ai);
}

int loc_getaddrinfo(const char * nodename, const char * servname,
       const struct addrinfo * hints, struct addrinfo ** res) {
        return getaddrinfo(nodename, servname, hints, res);
}

const char * loc_gai_strerror(int ecode) {
    static char buf[32];
    if (ecode == 0) return "Success";
    snprintf(buf, sizeof(buf), "Error code %d", ecode);
    return buf;
}

#endif
