/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS
 * FILE:            lib/nt/entry_point.c
 * PURPOSE:         Native NT Runtime Library
 * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
 */

/* INCLUDES ******************************************************************/

/* PSDK/NDK Headers */
#define WIN32_NO_STATUS
#define NTOS_MODE_USER
// #include <ndk/psfuncs.h>
#include <ndk/rtlfuncs.h>

NTSTATUS
__cdecl
_main(
    int argc,
    char *argv[],
    char *envp[],
    ULONG DebugFlag
);

#define NDEBUG
#include <debug.h>

/* FUNCTIONS ****************************************************************/

static
VOID FASTCALL EnvironmentStringToUnicodeString (PWCHAR wsIn, PUNICODE_STRING usOut)
{
    if (wsIn)
    {
        PWCHAR CurrentChar = wsIn;

        while (*CurrentChar)
        {
            while (*CurrentChar++);
        }
        /* Double NULL-termination at end */
        CurrentChar++;

        usOut->Buffer = wsIn;
        /* FIXME: the last (double) nullterm should perhaps not be included in Length
        * but only in MaximumLength. -Gunnar */
        usOut->MaximumLength = usOut->Length =  (CurrentChar-wsIn) * sizeof(WCHAR);
    }
    else
    {
        usOut->Buffer = NULL;
        usOut->Length =  usOut->MaximumLength = 0;
    }
}

VOID
NTAPI
NtProcessStartup(PPEB Peb)
{
    NTSTATUS Status;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
    PUNICODE_STRING CmdLineString;
    ANSI_STRING AnsiCmdLine;
    UNICODE_STRING UnicodeEnvironment;
    ANSI_STRING AnsiEnvironment;
    PCHAR NullPointer = NULL;
    INT argc = 0;
    PCHAR *argv;
    PCHAR *envp;
    PCHAR *ArgumentList;
    PCHAR Source, Destination;
    ULONG Length;
    ASSERT(Peb);

#ifdef _M_ARM // Huge achievement
    DPRINT1("%s(%08lx) called\n", __FUNCTION__, Peb);
    while (TRUE);
#endif

    /* Normalize and get the Process Parameters */
    ProcessParameters = RtlNormalizeProcessParams(Peb->ProcessParameters);
    ASSERT(ProcessParameters);

    /* Allocate memory for the argument list, enough for 512 tokens */
    // FIXME: what if 512 is not enough????
    ArgumentList = RtlAllocateHeap(RtlGetProcessHeap(), 0, 512 * sizeof(PCHAR));
    if (!ArgumentList)
    {
        DPRINT1("ERR: no mem!");
        Status = STATUS_NO_MEMORY;
        goto fail;
    }

    /* Use a null pointer as default */
    argv = &NullPointer;
    envp = &NullPointer;

    /* Set the first pointer to NULL, and set the argument array to the buffer */
    *ArgumentList = NULL;
    argv = ArgumentList;

    /* Get the pointer to the Command Line */
    CmdLineString = &ProcessParameters->CommandLine;

    /* If we don't have a command line, use the image path instead */
    if (!CmdLineString->Buffer || !CmdLineString->Length)
    {
        CmdLineString = &ProcessParameters->ImagePathName;
    }

    /* Convert it to an ANSI string */
    Status = RtlUnicodeStringToAnsiString(&AnsiCmdLine, CmdLineString, TRUE);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("ERR: no mem(guess)\n");
        goto fail;
    }

    /* Save parameters for parsing */
    Source = AnsiCmdLine.Buffer;
    Length = AnsiCmdLine.Length;

    /* Ensure it's valid */
    if (Source)
    {
        /* Allocate a buffer for the destination */
        Destination = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length + sizeof(WCHAR));
        if (!Destination)
        {
            DPRINT1("ERR: no mem!");
            Status = STATUS_NO_MEMORY;
            goto fail;
        }

        /* Start parsing */
        while (*Source)
        {
            /* Skip the white space */
            while (*Source && *Source <= ' ') Source++;

            /* Copy until the next white space is reached */
            if (*Source)
            {
                /* Save one token pointer */
                *ArgumentList++ = Destination;

                /* Increase one token count */
                argc++;

                /* Copy token until white space */
                while (*Source > ' ') *Destination++ = *Source++;

                /* Null terminate it */
                *Destination++ = '\0';
            }
        }
    }

    /* Null terminate the token pointer list */
    *ArgumentList++ = NULL;

    /* Now handle the environment, point the envp at our current list location. */
    envp = ArgumentList;

    if (ProcessParameters->Environment)
    {
        EnvironmentStringToUnicodeString(ProcessParameters->Environment, &UnicodeEnvironment);
        Status = RtlUnicodeStringToAnsiString (&AnsiEnvironment, &UnicodeEnvironment, TRUE);
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("ERR: no mem(guess)\n");
            goto fail;
        }

        ASSERT(AnsiEnvironment.Buffer);

        Source = AnsiEnvironment.Buffer;
        while (*Source)
        {
            /* Save a pointer to this token */
            *ArgumentList++ = Source;

            /* Keep looking for another variable */
            while (*Source++);
        }

        /* Null terminate the list again */
        *ArgumentList++ = NULL;
    }
    /* Breakpoint if we were requested to do so */
    if (ProcessParameters->DebugFlags) DbgBreakPoint();

    /* Call the Main Function */
    Status = _main(argc, argv, envp, ProcessParameters->DebugFlags);

fail:
    /* We're done here */
    NtTerminateProcess(NtCurrentProcess(), Status);
}

/* EOF */