/*
 * Placed into the Public Domain.
 * written by Walter Bright
 * www.digitalmars.com
 */

import object;
import std.c.stdio;
import std.c.string;
import std.c.stdlib;
import std.string;

extern (C) void _STI_monitor_staticctor();
extern (C) void _STD_monitor_staticdtor();
extern (C) void _STI_critical_init();
extern (C) void _STD_critical_term();
extern (C) void gc_init();
extern (C) void gc_term();
extern (C) void _minit();
extern (C) void _moduleCtor();
extern (C) void _moduleDtor();
extern (C) void _moduleUnitTests();

extern (C) bool no_catch_exceptions;

version (OSX)
{
    // The bottom of the stack
    extern (C) void* __osx_stack_end = cast(void*)0xC0000000;
}

version (FreeBSD)
{
    // The bottom of the stack
    extern (C) void* __libc_stack_end;
}

version (Solaris)
{
    // The bottom of the stack
    extern (C) void* __libc_stack_end;
}

/***********************************
 * The D main() function supplied by the user's program
 */
int main(char[][] args);

/***********************************
 * Substitutes for the C main() function.
 * It's purpose is to wrap the call to the D main()
 * function and catch any unhandled exceptions.
 */

extern (C) int main(size_t argc, char **argv)
{
    char[] *am;
    char[][] args;
    int result;
    int myesp;
    int myebx;

    version (OSX)
    {   /* OSX does not provide a way to get at the top of the
         * stack, except for the magic value 0xC0000000.
         * But as far as the gc is concerned, argv is at the top
         * of the main thread's stack, so save the address of that.
         */
        __osx_stack_end = cast(void*)&argv;
    }

    version (FreeBSD)
    {   /* FreeBSD does not provide a way to get at the top of the
         * stack.
         * But as far as the gc is concerned, argv is at the top
         * of the main thread's stack, so save the address of that.
         */
        __libc_stack_end = cast(void*)&argv;
    }

    version (Solaris)
    {   /* As far as the gc is concerned, argv is at the top
         * of the main thread's stack, so save the address of that.
         */
        __libc_stack_end = cast(void*)&argv;
    }

    version (Posix)
    {
        _STI_monitor_staticctor();
        _STI_critical_init();
        gc_init();
        am = cast(char[] *) malloc(argc * (char[]).sizeof);
        // BUG: alloca() conflicts with try-catch-finally stack unwinding
        //am = (char[] *) alloca(argc * (char[]).sizeof);
    }
    version (Win32)
    {
        gc_init();
        _minit();
        am = cast(char[] *) alloca(argc * (char[]).sizeof);
    }

    if (no_catch_exceptions)
    {
        _moduleCtor();
        _moduleUnitTests();

        for (size_t i = 0; i < argc; i++)
        {
            auto len = strlen(argv[i]);
            am[i] = argv[i][0 .. len];
        }

        args = am[0 .. argc];

        result = main(args);
        _moduleDtor();
        gc_term();
    }
    else
    {
        try
        {
            _moduleCtor();
            _moduleUnitTests();

            for (size_t i = 0; i < argc; i++)
            {
                auto len = strlen(argv[i]);
                am[i] = argv[i][0 .. len];
            }

            args = am[0 .. argc];

            result = main(args);
            _moduleDtor();
            gc_term();
        }
        catch (Object o)
        {
            version (none)
            {
                printf("Error: ");
                o.print();
            }
            else
            {   auto s = o.toString();
                fprintf(stderr, "Error: %.*s\n", s.length, s.ptr);
            }
            exit(EXIT_FAILURE);
        }
    }

    version (Posix)
    {
        free(am);
        _STD_critical_term();
        _STD_monitor_staticdtor();
    }
    return result;
}

