/*
    Copyright (C) 1995-1997
	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld
	email: rainer@mathematik.uni-bielefeld.de

    All rights reserved
*/

#include <rsxnt.h>
#include <string.h>

static void x2s(int v, char *s)
{
    static char hex[] = "0123456789abcdef";
    int i;

    for (i = 0; i <= 7; i++) {
	s[7 - i] = hex[v & 15];
	v >>= 4;
    }
    s[8] = 0;
}

static DWORD WINAPI exit_thread(LPVOID lpThreadParameter)
{
    EMXPROCESS *p = _rsxnt_get_process_ptr();
    int i = (int) lpThreadParameter;

    WaitForSingleObject(p->childs[i].hProcess, INFINITE);

    _rsxnt_send_signal(p, SIGCLD);
    return _rsxnt_do_signal(SIGCLD);
}

int _rsxnt_do_fork(SYSCALL_FRAME *f)
{
    static SECURITY_ATTRIBUTES defsec =
	{ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
    const char magic_fork[] = "!magic_fork!";
    STARTUPINFO si;
    PROCESS_INFORMATION procinfo;
    DWORD fdwCreate;
    char modulename[512];
    EMXPROCESS *p = _rsxnt_get_process_ptr();
    DWORD len,ret;
    HANDLE hEvent, hProcess;
    HANDLE hWait[2];

    if (_rsxnt_win32_version == WIN32S)
	return _rsxnt_errno(ENOSYS);

    /* for rsxio32: if no console detach process */
    if (p->bConsoleApp != FALSE && !GetConsoleTitle(modulename, 512))
	fdwCreate = DETACHED_PROCESS;
    else
	fdwCreate = 0;

    memset(&si, 0, sizeof (STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    if (p->file[0].f_handle != (HANDLE)-2) {	/* dummy GUI handles */
	si.dwFlags = STARTF_USESTDHANDLES;
	si.hStdInput = p->file[0].f_handle;
	si.hStdOutput = p->file[1].f_handle;
	si.hStdError = p->file[2].f_handle;
    }

    len = GetModuleFileName(NULL, modulename, 260);
    modulename[len] = ' ';
    lstrcpy(modulename + len + 1, magic_fork);

    if ((hEvent = CreateEvent(&defsec, TRUE, FALSE, NULL))
	    == INVALID_HANDLE_VALUE)
	return _rsxnt_errno(EIO);

    hProcess = GetCurrentProcess();
    if (DuplicateHandle(hProcess, hProcess, hProcess, &hProcess,
            PROCESS_ALL_ACCESS, TRUE, 0) == FALSE)
	return _rsxnt_errno(EIO);

    len = lstrlen(modulename);
    modulename[len] = ' ';
    x2s((int)hEvent, modulename + len + 1);
    len = lstrlen(modulename);
    modulename[len] = ' ';
    x2s((int)hProcess, modulename + len + 1);
    len = lstrlen(modulename);
    modulename[len] = ' ';
    x2s((int)p, modulename + len + 1);

    /* show stack to child */
    p->regs.esp = (DWORD) f;

    if (CreateProcess(NULL, modulename, NULL, NULL,
	    TRUE, fdwCreate, NULL, NULL, &si, &procinfo) == FALSE) {
	MessageBox(NULL, modulename, "RSXNT: fork error",
		  MB_OK|MB_ICONERROR|MB_APPLMODAL|MB_SETFOREGROUND);
	return _rsxnt_errno(EIO);
    }

    hWait[0] = hEvent;
    hWait[1] = procinfo.hProcess;

    ret = WaitForMultipleObjects(2, hWait, FALSE, INFINITE);
    CloseHandle (hEvent);
    if (ret == WAIT_FAILED || ret == WAIT_OBJECT_0 + 1)
    {
        CloseHandle (procinfo.hProcess);
        CloseHandle (procinfo.hThread);
	return _rsxnt_errno(EIO);
    }
    else {
	int i;
	DWORD tid;
	HANDLE h;

	for (i = 0; i < MAX_CHILD; i++) {
	    if (!p->childs[i].hProcess)
		break;
	}
	if (i == MAX_CHILD)	/* kill last child entry */
	    --i;

	p->childs[i].hProcess = procinfo.hProcess;
        p->childs[i].pid = p->childs[i].dwProcessId = procinfo.dwProcessId;
	p->childs[i].hThread = procinfo.hThread;
	p->childs[i].dwThreadId = procinfo.dwThreadId;

        if (p->childs[i].pid < 0) /* Win95 hack */
            p->childs[i].pid = - p->childs[i].pid;

	h = CreateThread(NULL, 0, exit_thread, (LPVOID) i, 0, &tid);
	if (h)
	  CloseHandle (h);

	return p->childs[i].pid;
    }
}
