
#define _COMPAT_C 1

#include <config.h>
#include "diffie/mostincludes.h"
#include "diffie/compat.h"
#include "mad.h"

#ifdef WIN32

static char *mingw32_pathdup (const char *p)
{
    char *q, *r;
    r = q = malloc (strlen (p) + 2);
    memset (r, 0, strlen (p) + 2);
    for (;;) {
	if (!*p) {
	    *q = '\0';
	    break;
	}
	if (*p != PATH_SEP) {
	    *q++ = *p++;
	} else {
	    while (*p == PATH_SEP) {
		*q = PATH_SEP;
		if (!strncmp (p, PATH_SEP_STR "." PATH_SEP_STR, 3) || !strcmp (p, PATH_SEP_STR "."))
		    p++;
		else if (!strncmp (p, PATH_SEP_STR ".." PATH_SEP_STR, 4) || !strcmp (p, PATH_SEP_STR "..")) {
		    char *s;
		    p += 2;
		    *q = ' ';
		    s = q;
		    q = strrchr (r, PATH_SEP);
		    if (!q) {
			q = s;
			strcpy (q, PATH_SEP_STR "..");
			q += 3;
		    }
		}
		p++;
	    }
	    q++;
	}
    }
/* get rid of trailing / */
    if (r[0] && r[1])
	if (*--q == PATH_SEP)
	    *q = '\0';
    return r;
}

struct passwd *getpwuid (uid_t uid)
{
    static struct passwd p =
    {"root", "root", 0, 0, "root", PATH_SEP_STR, "command.com"};
    return &p;
}

struct passwd *getpwnam (const char *name)
{
    return getpwuid (0);
}

struct group *getgrgid (gid_t gid)
{
    static struct group g =
    {
	"root", "root", 0, 0
    };
    char *gr_mem[2] =
    {"root", 0};
    g.gr_mem = (char **) gr_mem;
    return &g;
}

struct group *getgrnam (const char *name)
{
    return getgrgid (0);
}

int fork (void)
{
    errno = EPERM;
    return -1;
}

int execve (char *argv_0, char **argv, char *envp[])
{
    errno = EPERM;
    return -1;
}

int waitpid (pid_t pid, int *result, int flags)
{
    errno = EPERM;
    return -1;
}

int wait (int *result)
{
    errno = EPERM;
    return -1;
}

int gettimeofday (void *a, void *b)
{
    clock_t t;
    struct {
	long tv_sec;
	long tv_usec;
    } *s = a;
    t = clock ();
    s->tv_sec = t / CLOCKS_PER_SEC;
    s->tv_usec = ((long) t % (long) CLOCKS_PER_SEC) * (1000000 / CLOCKS_PER_SEC);
    return 0;
}

int mknod (char *pathname,...)
{
    errno = EPERM;
    return -1;
}

int readlink (char *path, char *buf, size_t bufsiz)
{
    errno = EPERM;
    return -1;
}

int symlink (char *oldpath, char *newpath)
{
/* FIXME */
    errno = EPERM;
    return -1;
}

int chown (char *path, uid_t owner, gid_t group)
{
    return 0;
}

int link (char *f1, char *f2)
{
/* FIXME */
    errno = EPERM;
    return -1;
}

char *getpass (char *prompt)
{
    static char s[80];
    SECURITY_ATTRIBUTES security;
    HANDLE h;
    int p;
    DWORD mode = 0;
    printf ("%s ", prompt);
    fflush (stdout);

    memset (&security, 0, sizeof (security));
    security.nLength = sizeof (SECURITY_ATTRIBUTES);
    security.bInheritHandle = TRUE;
    h = CreateFileA ("CONIN$", GENERIC_READ | GENERIC_WRITE,
		     FILE_SHARE_READ | FILE_SHARE_WRITE, &security,
		     OPEN_EXISTING, 0, 0);
    GetConsoleMode (h, &mode);
    SetConsoleMode (h, mode & ~ENABLE_ECHO_INPUT);

    p = 0;
    while (read (0, s + p, 1) == 1) {
	if (s[p] == '\n' || s[p] == '\r')
	    break;
	if (p > 78)
	    break;
	p++;
    }
    s[p] = '\0';

    SetConsoleMode (h, mode);
    CloseHandle (h);

    printf ("\n");
    return s;
}

#undef fprintf
int mingwin32_fprintf (FILE * stream, const char *format, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j)
{
    if (stream == stdout || stream == stderr) {
	return printf (format, a, b, c, d, e, f, g, h, i, j);
    }
    return fprintf (stream, format, a, b, c, d, e, f, g, h, i, j);
}

#undef open
#undef creat
int mingw32_open (const char *szFileName, int flags, int mode)
{
    int fd;
    char *p;
    p = mingw32_pathdup (szFileName);
    fd = open (p, flags | O_BINARY, (mode & S_IWUSR) ? 0x80 : 0);
    free (p);
    return fd;
}

/* this is a one to one mapping */
static int mode_unix_to_mingw32 (unsigned long mode)
{
    int a = 0;
    if (S_ISDIR (mode))
	a |= FILE_ATTRIBUTE_DIRECTORY;
    if (mode >> 16)
	mode >>= 16;
    if ((mode & 0010) && !(mode & 0100)) {	/* magic bits */
	if (mode & 0400)
	    a |= FILE_ATTRIBUTE_NORMAL;
	if (!(mode & 0200))
	    a |= FILE_ATTRIBUTE_READONLY;
	if (!(mode & 0040))
	    a |= FILE_ATTRIBUTE_SYSTEM;
	if (mode & 0020)
	    a |= FILE_ATTRIBUTE_ARCHIVE;
	if (!(mode & 0004))
	    a |= FILE_ATTRIBUTE_HIDDEN;
	if (mode & 0001)
	    a |= FILE_ATTRIBUTE_COMPRESSED;
    } else {
	if (!(a & FILE_ATTRIBUTE_DIRECTORY))
	    a |= (mode & 0222) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
    }
    return a;
}

mode_t mode_mingw32_to_unix (int a)
{
    mode_t mode;
    mode = 0;
    if (a & FILE_ATTRIBUTE_DIRECTORY)
	mode |= S_IFDIR;
    else
	mode |= S_IFREG;
    a &= ~FILE_ATTRIBUTE_DIRECTORY;
    a &= FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM \
	|FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_COMPRESSED;
    if (a & ~FILE_ATTRIBUTE_READONLY) {
	mode |= 0410;		/* magic bits */
	if (a & FILE_ATTRIBUTE_NORMAL)
	    mode = (mode & ~0777) | 0700;
	else {
	    if (!(a & FILE_ATTRIBUTE_READONLY))
		mode |= 0200;
	    if (!(a & FILE_ATTRIBUTE_SYSTEM))
		mode |= 0040;
	    if (a & FILE_ATTRIBUTE_ARCHIVE)
		mode |= 0020;
	    if (!(a & FILE_ATTRIBUTE_HIDDEN))
		mode |= 0004;
	    if (a & FILE_ATTRIBUTE_COMPRESSED)
		mode |= 0001;
	}
    } else {
	mode |= 0500 | ((a & FILE_ATTRIBUTE_READONLY) ? 0 : 0200);
    }
    mode = (unsigned long) mode | (((unsigned long) mode) << 16);
    return mode;
}

#undef stat
int mingw32_stat (const char *file_name, struct stat *buf)
{
    int a;
    char *p;
    p = mingw32_pathdup (file_name);
    a = stat (p, buf);
    if (a) {
	free (p);
	return a;
    }
    a = GetFileAttributes (p);
    free (p);
    if (a == -1)
	return 1;
    buf->st_mode = mode_mingw32_to_unix (a);
    return 0;
}

#undef fstat
int mingw32_fstat (int filedes, struct stat *buf)
{
    int r;
    r = fstat (filedes, buf);
    buf->st_mode = (unsigned long) buf->_st_mode | ((unsigned long) (buf->_st_mode & (S_IRWXU | S_IFREG | S_IFDIR)) << 16);
    return r;
}

#undef chmod
int mingw32_chmod (char *path, mode_t mode)
{
    int a;
    char *p;
    p = mingw32_pathdup (path);
    a = mode_unix_to_mingw32 (mode);
    a = !SetFileAttributes (p, a);
    free (p);
    return a;
}

#undef mkdir
int mingw32_mkdir (char *filename, mode_t mode)
{
    char *p;
    int r;
    p = mingw32_pathdup (filename);
    r = mkdir (p);
    r |= !SetFileAttributes (p, (mode_unix_to_mingw32 (mode) & ~FILE_ATTRIBUTE_NORMAL) | FILE_ATTRIBUTE_DIRECTORY);
    free (p);
    return r;
}

#endif


