/*****************************************************************************
 *
 * LIBRARY:      unix_socks.c  -- Routines related to using UNIX domain
 *               sockets for IPC mechanisms (such as XDRIVER).
 *
 * AUTHOR(S):    Eric G. Miller
 *
 * PURPOSE:      Historically GRASS has used FIFO for interprocess communic-
 *               ations for display functions.  Unfortunately, FIFO's are
 *               not available on all target platforms.  An attempt has been
 *               made to use IPC message passing, but the semantics are
 *               variable and it also isn't available on all target platforms.
 *               UNIX sockets, or local or domain sockets, are much more
 *               widely available and consistent.  NOTE: This implementation
 *               of UNIX sockets provides zero security checking so should
 *               not be used from untrusted clients.
 *
 * COPYRIGHT:    (C) 2000 by the GRASS Development Team
 *
 *               This program is free software under the GNU General Public
 *               License (>=v2). Read the file COPYING that comes with GRASS
 *               for details.
 *
 *****************************************************************************/

#ifndef __MINGW32__ /* TODO */
#ifdef __MINGW32__

#include <grass/gis.h>
#include <windows.h>
#include <io.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/stat.h>

#define PIPE_TIMEOUT 5000
#define BUFSIZE      2048

/* ---------------------------------------------------------------------
 * _get_make_pipe_path(), builds and tests the path for the socket
 * directory.  Returns NULL on any failure, otherwise it returns the
 * directory path. The path will be like "/tmp/grass-$USER".
 * ---------------------------------------------------------------------*/
static char *_get_make_pipe_path(void)
{
    char *path, *user;
    const char *prefix = "c:/grass-";
    char *whoami = "mingw-anon-user";
    int len, status;
    struct _stat theStat;

    user = G_whoami(); /* Don't G_free () return value ever! */
    if (user == NULL) {
        user = whoami;
    }
    len = strlen(prefix) + strlen(user) + 1;
    path = G_malloc(len);
    sprintf(path, "%s%s", prefix, user);

    if ((status = G_lstat(path, &theStat)) != 0) {
        status = G_mkdir(path);
    }
    else {
        if (!S_ISDIR(theStat.st_mode)) {
            status = -1; /* not a directory ?? */
        }
        else {
            status = chmod(path, S_IRWXU); /* fails if we don't own it */
        }
    }

    if (status) { /* something's wrong if non-zero */
        G_free(path);
        path = NULL;
    }

    return path;
}

/* ----------------------------------------------------------------------
 * G_pipe_get_fname(), builds the full path for a UNIX socket.  Caller
 * should G_free () the return value when it is no longer needed.  Returns
 * NULL on failure.
 * ---------------------------------------------------------------------*/
char *G_pipe_get_fname(char *name)
{
    char *path, *dirpath;
    int len;

    if (name == NULL)
        return NULL;

    dirpath = _get_make_pipe_path();

    if (dirpath == NULL)
        return NULL;

    len = strlen(dirpath) + strlen(name) + 2;
    path = G_malloc(len);
    sprintf(path, "%s/%s", dirpath, name);
    G_free(dirpath);

    return path;
}

/* -------------------------------------------------------------------
 * G_pipe_exists(char *): Returns 1 if path is to a UNIX socket that
 * already exists, 0 otherwise.
 * -------------------------------------------------------------------*/

int G_pipe_exists(char *name)
{
    int rv = 0;
    HANDLE hFile = hFile =
        CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
                   FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        if (name == NULL || (FILE_TYPE_PIPE != GetFileType(hFile))) {
            rv = 0;
        }
        else {
            rv = 1;
            CloseFile(hFile);
        }
    }
    return (rv);
}

/* -----------------------------------------------------------------
 * G_pipe_bind (char *): Takes the full pathname for a UNIX socket
 * and returns the file descriptor to the socket after a successful
 * call to bind().  On error, it returns -1.  Check "errno" if you
 * want to find out why this failed (clear it before the call).
 * ----------------------------------------------------------------*/

HANDLE G_pipe_bind(char *name)
{
    HANDLE hPipe;

    if (name == NULL) {
        return -1;
    }
    if (G_pipe_exists(name)) {
        /*errno = EADDRINUSE; */
        return -1;
    }

    hPipe = CreateNamedPipe(name,                       // pipe name
                            PIPE_ACCESS_DUPLEX,         // read/write access
                            PIPE_TYPE_MESSAGE |         // message type pipe
                                PIPE_READMODE_MESSAGE | // message-read mode
                                PIPE_WAIT,              // blocking mode
                            PIPE_UNLIMITED_INSTANCES,   // max. instances
                            BUFSIZE,                    // output buffer size
                            BUFSIZE,                    // input buffer size
                            PIPE_TIMEOUT,               // client time-out
                            NULL);                      // no security attribute

    if (hPipe == INVALID_HANDLE_VALUE) {
        return (-1);
    }
    return (hPipe);
}

/* ---------------------------------------------------------------------
 * G_pipe_listen(int, unsigned int): Wrapper around the listen()
 * function.
 * --------------------------------------------------------------------*/

int G_pipe_listen(HANDLE hPipe, unsigned int queue_len)
{
    return (0);
}

/* -----------------------------------------------------------------------
 * G_pipe_accept (int sockfd):
 * Wrapper around the accept() function. No client info is returned, but
 * that's not generally useful for local sockets anyway.  Function returns
 * the file descriptor or an error code generated by accept().  Note,
 * this call will usually block until a connection arrives.  You can use
 * select() for a time out on the call.
 * ---------------------------------------------------------------------*/

HANDLE G_pipe_accept(HANDLE hPipe)
{
    BOOL fConnected;
    HANDLE rv = hPipe;

    fConnected = ConnectNamedPipe(hPipe, NULL)
                     ? TRUE
                     : (GetLastError() == ERROR_PIPE_CONNECTED);
    if (fConnected) {
        rv = NULL;
    }
    return (rv);
}

/* ----------------------------------------------------------------------
 * G_pipe_connect (char *name):  Tries to connect to the unix socket
 * specified by "name".  Returns the file descriptor if successful, or
 * -1 if unsuccessful.  Global errno is set by connect() if return is -1
 * (though you should zero errno first, since this function doesn't set
 * it for a couple conditions).
 * --------------------------------------------------------------------*/

HANDLE G_pipe_connect(char *name)
{
    HANDLE hPipe = -1;

    if (!G_pipe_exists(name)) {
        return hPipe;
    }

    while (1) {
        hPipe = CreateFile(name,          // pipe name
                           GENERIC_READ | // read and write access
                               GENERIC_WRITE,
                           0,             // no sharing
                           NULL,          // no security attributes
                           OPEN_EXISTING, // opens existing pipe
                           0,             // default attributes
                           NULL);         // no template file

        if (hPipe != INVALID_HANDLE_VALUE) {
            break;
        }
        if (GetLastError() != ERROR_PIPE_BUSY) {
            return (-1);
        }
        /* Wait for 5 seconds */
        if (!WaitNamedPipe(name, PIPE_TIMEOUT)) {
            return (-1);
        }
    }
    return (hPipe);
}

#endif                              /* __MINGW32__ */
extern int dummy_for_iso_compilers; /* suppress -Wempty-translation-unit */
#endif                              /* __MINGW32__ */
