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

    All rights reserved
*/

#include <rsxnt.h>

#define CHARLOWER(arg) (char)(int)CharLower((char *)(int)(arg))

static void set_tz();

char _rsxnt_copyright[] =
    "RSXNT Version 1.40\n"
    "1995-1997 (c) Rainer Schnitker\n"
    "All rights reserved.";

/* ------------------------------------------------------------------------ *\
    Globals:
\* ------------------------------------------------------------------------ */

VERSION_INFO    _rsxnt_win32_version = EMPTY;
struct file_operations _rsxnt_socket_fop = {0};

/* ------------------------------------------------------------------------ *\
    vars for forked process

    System Lib:
        We must save this area (overritten by copy_mem)

    hEvent, hProcess & pFather are copied from cmdline
\* ------------------------------------------------------------------------ */

static struct fork_vars
{
    HANDLE hEvent, hProcess;
    EMXPROCESS *pFather;
    EMXPROCESS copy;
    DWORD esp_top;
} fv;

static int x2i(char *s)
{
    int i, res=0;

    for (i = 0; i <= 7; i++) {
        char c = s[i];
        if (c >= 'a')
            c -= ('a' - 10);
        else if (c >= 'A')
            c -= ('A' - 10);
        else
            c -= '0';
        res <<= 4;
        res |= c;
    }
    return res;
}

static void copy_mem(void *dst, void *src, unsigned size)
{
    DWORD n;

    if (ReadProcessMemory(fv.hProcess, src, dst, size, &n) == FALSE) {

        MessageBox(NULL, "init new forked process fails",
               "RSXNT", MB_OK|MB_ICONERROR|MB_APPLMODAL|MB_SETFOREGROUND);

        for (;;)
            ExitProcess(0);
    }
}

/* return first non-digit */
static char * asc2int(char *s, int *retv)
{
    char *str = s;
    *retv = 0;

    while (*str != 0) {
        if ((*str < '0') || (*str > '9'))
            break;
        *retv *= 10;
        *retv += *str - '0';
        str++;
    }
    return (str);
}

static char *scan_for_option(EMXPROCESS *p, unsigned char *s)
{
    int temp;
    char *t;

    switch (*s) {

    case 'c':                   /* disable core dumps */
        p->rsxnt_opt.nocore = 1;
        break;

    case 'e':                   /* redirect standard error */
        p->rsxnt_opt.redir = 1;
        break;

    case 'h':                   /* max handles */
        ++s;
        if (*s < '0' || *s > '9')
            return NULL;
        t = asc2int(s, &temp);
        if (temp > N_FILES)
            temp = N_FILES;
        p->rsxnt_opt.handles = temp;
        return t;

    case 'q':                   /* quote all arguments to child */
        p->rsxnt_opt.quote = 1;
        break;

    case 's':                   /* heap size */
        ++s;
        if (*s < '0' || *s > '9')
            return NULL;
        t = asc2int(s, &temp);
        p->rsxnt_opt.heapsize = temp;
        return t;
        break;

    case 'r':                   /* default disk-drive */
        if (IsCharAlpha(*++s) == FALSE)
            return NULL;
        p->rsxnt_opt.defdisk = CHARLOWER(*s);
        break;

    case 't':                   /* truncate filenames to 8.3 */
        p->rsxnt_opt.truncate = 1;
        break;

    case 'V':
        p->rsxnt_opt.ansi = 1;
        break;

    default:
        return NULL;

    } /* switch (*s) */

    return s;
}

static void scan_options(EMXPROCESS *p, unsigned char *options)
{
    unsigned char *s = options;

    if (s != NULL) {
        for (; *s != '\0'; ++s) {
            while (*s == ' ')
                ++s;
            if (*s == '-') {
                s = scan_for_option(p, ++s);
                if (s == NULL) {
                    MessageBox(NULL,
                                "Warning: The Environment RSXNTOPT is bad",
                                "rsxnt startup",
                                MB_OK|MB_ICONERROR|MB_APPLMODAL|MB_SETFOREGROUND);
                    return;
                }
            } else
                break;
        }
    }
}

/* ------------------------------------------------------------------------ *\
    called from emx process crt0.s __init()
    - get cmdline
    - test forked process
\* ------------------------------------------------------------------------ */

#define is_space(c) ((c) == ' ' || ((c) >= 0x09 && (c) <= 0x0d))

int __cdecl init_process_test_fork(struct layout *layout)
{
    const char magic_fork[] = "!magic_fork!";
    char *cmd;

    EMXPROCESS *p = _rsxnt_get_process_ptr();
    if (!p->pid) { /* inited only from DLL LibMain */
        p->hProcess = GetCurrentProcess();
        p->pid = p->dwProcessId = GetCurrentProcessId();
        if (p->pid < 0)
            p->pid = - p->pid;
    }

    p->syscall_state = SYSCALL_STATE_INIT;
    p->data_start = layout;     /* pointer to __data */

    if (layout->flags & 1)      /* DLL is calling */
        return 0;

    /* test fork process */
    cmd = GetCommandLine();
    while (!is_space(*cmd) && *cmd != 0)
        cmd++;
    while(is_space(*cmd))
        cmd++;

    if (memcmp(cmd, magic_fork, sizeof(magic_fork) - 1) == 0) {
        DWORD esp;

        fv.esp_top = (DWORD) &layout;

        /* get cmdline parameter */
        cmd += sizeof(magic_fork);
        fv.hEvent = (HANDLE) x2i(cmd);
        cmd += 9;
        fv.hProcess = (HANDLE) x2i(cmd);
        cmd += 9;
        fv.pFather = (EMXPROCESS *) x2i(cmd);

        copy_mem(&fv.copy, fv.pFather, sizeof(EMXPROCESS));

        /* commit stack */
        for (esp = fv.esp_top; esp >= fv.copy.regs.esp; esp -= 4096)
            if (*(char *) esp == 1)
                * (char *) esp = 0;

        return (int) fv.copy.regs.esp;
    }
    return 0;
}

/* ------------------------------------------------------------------------ *\
    called from emx process crt0.s __init()

    - global init
\* ------------------------------------------------------------------------ */

void __cdecl init_rte(struct layout *layout)
{
    static unsigned char rsxnt_env[80];
    static int inited = 0;
    EMXPROCESS *p = _rsxnt_get_process_ptr();

    if (inited)
        return;
    else
        ++inited;

    if (GetEnvironmentVariable("RSXNTOPT", rsxnt_env, sizeof(rsxnt_env) - 1) > 0)
        scan_options(p, rsxnt_env);
    scan_options(p, layout->options);

    if (_rsxnt_init_heap(p) == FALSE) {
        MessageBox( NULL, "No virtual memory",
                    "rsxnt startup",
                    MB_OK|MB_ICONERROR|MB_APPLMODAL|MB_SETFOREGROUND);
        for (;;)
            ExitProcess(0);
    }
    set_tz();
    _rsxnt_init_files(p);
    _rsxnt_init_signals(p);
    _rsxnt_init_termio(p);

    p->time_tic = GetTickCount();
}

/* ------------------------------------------------------------------------ *\
    called after init_process_test_fork()
    if this process forked from father

    - stack is copied from father
    - Syscall frame is a copy from father fork() function
\* ------------------------------------------------------------------------ */

void __cdecl init_process_fork(SYSCALL_FRAME *f)
{
    EMXPROCESS *p = _rsxnt_get_process_ptr();
    struct layout *layout = (struct layout *) p->data_start;

#ifdef EMX_SYS_LIB
    struct fork_vars fork_copy;
    memcpy (&fork_copy, &fv, sizeof(struct fork_vars));
#endif

    copy_mem(f, f, fv.esp_top - (DWORD)f);   /* stack */
    _rsxnt_init_heap(p);
    _rsxnt_grow_heap(p, (DWORD) fv.copy.heap_pagebrk - (DWORD) fv.copy.heap_start);

    copy_mem(fv.copy.heap_start, fv.copy.heap_start,
            (DWORD) fv.copy.heap_pagebrk - (DWORD) fv.copy.heap_start);

    copy_mem((void *)layout->data_base, (void *)layout->data_base,
            (DWORD) layout->data_end - (DWORD) layout->data_base);

    copy_mem((void *)layout->bss_base, (void *)layout->bss_base,
            (DWORD) layout->bss_end - (DWORD) layout->bss_base);

#ifdef EMX_SYS_LIB
    memcpy (&fv, &fork_copy, sizeof(struct fork_vars));
#endif
    p->hProcess = GetCurrentProcess();
    p->pid = p->dwProcessId = GetCurrentProcessId();
    if (p->pid < 0)
        p->pid = - p->pid;

    /* wake up father */
    SetEvent(fv.hEvent);
    CloseHandle(fv.hProcess);
    CloseHandle(fv.hEvent);

    _rsxnt_init_files(p);
    _rsxnt_init_signals(p);
    _rsxnt_init_termio(p);

    memcpy(p->sigaction, fv.copy.sigaction, sizeof(struct sigaction) * NSIG);
    p->sig_raised = fv.copy.sig_raised;
    p->sig_blocked = fv.copy.sig_blocked;
    p->umask_bits = fv.copy.umask_bits;

    p->time_tic = GetTickCount();

    f->e.eax = 0;   /* fork return 0 */
    f->e.ecx = 0;
}

//
// check TZ environment
//

static size_t wchar2multi(char *ansi, const wchar_t *uni, size_t n)
{
    size_t len = 0;

    for (len = 0; len < n; ++len) {
        ansi[len] = (char) uni[len];
        if (! uni[len])
            break;
    }
    return len;
}

static char const * comma = ",";

static char * ltoa (long value, char *string, int radix)
{
    char *dst;
    char digits[32];
    unsigned long x;
    int i, n;

    dst = string;
    if (radix < 2 || radix > 36) {
        *dst = 0;
        return string;
    }
    if (radix == 10 && value < 0) {
        *dst++ = '-';
        x = -value;
    }
    else
      x = value;
    i = 0;
    do {
        n = x % radix;
        digits[i++] = (n < 10 ? (char)n+'0' : (char)n-10+'a');
        x /= radix;
    } while (x != 0);
    while (i > 0)
      *dst++ = digits[--i];
    *dst = 0;
    return string;
}

static void timecat(char *tzs, SYSTEMTIME *st)
{
    char buf[32];

    // SM: month
    lstrcat(tzs, comma);
    ltoa(st->wMonth, buf, 10);
    lstrcat(tzs, buf);

    if (!st->wYear) {  // specifies last sunday of month x
        // SW: no of week
        lstrcat(tzs, comma);
        if (st->wDay == 5)
          lstrcat(tzs, "-1");
        else {
            ltoa(st->wDay ,buf, 10);
            lstrcat(tzs, buf);
        }
        // SD: day of week
        lstrcat(tzs, comma);
        ltoa(st->wDayOfWeek ,buf, 10);
        lstrcat(tzs, buf);
    }
    else {
        // SW: 0
        lstrcat(tzs, ",0,");
        // SD: day of month
        ltoa(st->wDay ,buf, 10);
        lstrcat(tzs, buf);
    }

    // ST: hour
    lstrcat(tzs, comma);
    ltoa((st->wHour * 60 + st->wMinute) * 60 + st->wSecond, buf, 10);
    lstrcat(tzs, buf);
}

static void set_tz()
{
    TIME_ZONE_INFORMATION ti;
    DWORD ret;
    char buf[32];
    char tzs[64];

    if (GetEnvironmentVariable("TZ", buf, 32))
        return;

    ret = GetTimeZoneInformation(&ti);

    tzs[0] = 0;

    if (ret != TIME_ZONE_ID_UNKNOWN) {
        wchar2multi(buf, ti.StandardName, 32);
        tzs[0] = buf[1];
        tzs[1] = buf[2];
        tzs[2] = buf[3];
        ltoa((ti.Bias + ti.StandardBias)/60, tzs+3, 10);

        wchar2multi(buf, ti.DaylightName, 32);
        buf[4] = 0;
        lstrcat (tzs, buf+1);
        CharUpper (tzs);  /* NT only uses lower case */

        timecat (tzs, &ti.DaylightDate);
        timecat (tzs, &ti.StandardDate);

        lstrcat(tzs, comma);
        ltoa(ti.DaylightBias * -60, buf, 10);
        lstrcat(tzs, buf);
    }

    SetEnvironmentVariable("TZ", tzs);
}
