//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       assert.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    4-03-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include "debuglib.h"

typedef ULONG (NTAPI * DBGPROMPT)(PCH, PCH, ULONG);

#define DSYSASSERT_FAILED   0x00000001
#define DSYSASSERT_ERROR    0x00000002
#define DSYSASSERT_WARN     0x00000004

DWORD           __AssertInfoLevel = DSYSASSERT_FAILED;
DebugModule     __AssertModule = {NULL, &__AssertInfoLevel, 0, 7,
                                    NULL, 0, 0, "Assert",
                                    {"FAILED", "Error", "Warning", "",
                                     "", "", "", "",
                                     "", "", "", "", "", "", "", "",
                                     "", "", "", "", "", "", "", "",
                                     "", "", "", "", "", "", "", "" }
                                    };

DebugModule *   __pAssertModule = &__AssertModule;

VOID
__AssertDebugOut(
    ULONG Mask,
    CHAR * Format,
    ... )
{
    va_list ArgList;
    va_start(ArgList, Format);
    _DebugOut( __pAssertModule, Mask, Format, ArgList);
}

BOOL
DbgpStartDebuggerOnMyself(BOOL UseKernelDebugger)
{
    WCHAR   cch[80];
    STARTUPINFO StartupInfo;
    PROCESS_INFORMATION ProcessInformation;
    SECURITY_ATTRIBUTES sa;
    HANDLE  hEvent;

    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = &DbgpPartySd;
    sa.bInheritHandle = TRUE;

    hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);

    swprintf(cch, TEXT("ntsd %s -p %ld -e %ld -g"), (UseKernelDebugger ? "-d" : ""),
                        GetCurrentProcessId(), hEvent);


    ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));

    StartupInfo.cb = sizeof(STARTUPINFO);
    if (!UseKernelDebugger)
    {
        StartupInfo.lpDesktop = TEXT("WinSta0\\Default");
    }

    if (CreateProcess(  NULL,
                        cch,
                        NULL,
                        NULL,
                        TRUE,
                        HIGH_PRIORITY_CLASS,
                        NULL,
                        NULL,
                        &StartupInfo,
                        &ProcessInformation) )
    {
        CloseHandle(ProcessInformation.hProcess);
        CloseHandle(ProcessInformation.hThread);
        WaitForSingleObject(hEvent, 60000);
        CloseHandle(hEvent);
        return(TRUE);
    }
    else
    {
        __AssertDebugOut( DSYSASSERT_ERROR, "Could not start debugger '%ws', %d\n", cch, GetLastError());
        return(FALSE);
    }

}


VOID
_DsysAssertEx(
    PVOID FailedAssertion,
    PVOID FileName,
    ULONG LineNumber,
    PCHAR Message,
    ULONG ContinueCode
    )
{
    CHAR        Response[2];
    HMODULE     hNtDll;
    DBGPROMPT   DbgPromptFn;


    if (DbgpHeader)
    {
        __pAssertModule->pHeader = DbgpHeader;
        if (DbgpHeader->fDebug & DEBUG_DISABLE_ASRT)
        {
            __AssertDebugOut( DSYSASSERT_WARN, "Assertion at %s:%d disabled\n",
                            FileName, LineNumber);
            return;
        }
    }

    if (Message)
        __AssertDebugOut( DSYSASSERT_FAILED, "%s: %s (%s:%d)\n",
                    Message, FailedAssertion, FileName, LineNumber);
     else
        __AssertDebugOut( DSYSASSERT_FAILED, "%s (%s:%d)\n",
                    FailedAssertion, FileName, LineNumber);

    switch (ContinueCode)
    {

        case DSYSDBG_ASSERT_BREAK:
            __AssertDebugOut( DSYSASSERT_FAILED, "\tBreakpoint\n");
            DebugBreak();
            break;

        case DSYSDBG_ASSERT_CONTINUE:
            __AssertDebugOut( DSYSASSERT_WARN, "\tContinuing\n");
            return;

        case DSYSDBG_ASSERT_SUSPEND:
            __AssertDebugOut( DSYSASSERT_WARN, "\tSuspending Thread %d\n", GetCurrentThreadId());
            SuspendThread(GetCurrentThread());
            return;

        case DSYSDBG_ASSERT_KILL:
            __AssertDebugOut( DSYSASSERT_WARN, "\tKill Thread (exit %x)\n", NtCurrentTeb()->LastStatusValue);
            TerminateThread(GetCurrentThread(), NtCurrentTeb()->LastStatusValue);
            return;

        case DSYSDBG_ASSERT_DEBUGGER:
            if (IsDebuggerPresent())
            {
                DebugBreak();
            }
            else
            {
                if (DbgpHeader)
                {
                    if ((DbgpHeader->fDebug & DEBUG_PROMPTS) == 0)
                    {
                        DbgpStartDebuggerOnMyself(DbgpHeader->fDebug & DEBUG_USE_KDEBUG);
                        DebugBreak();
                        break;
                    }
                }

                hNtDll = LoadLibrary(TEXT("ntdll.dll"));
                if (hNtDll)
                {
                    DbgPromptFn = (DBGPROMPT) GetProcAddress(hNtDll, "DbgPrompt");
                }
                else
                {
                    DbgPromptFn = NULL;
                }

                while (TRUE)
                {

                    if (DbgPromptFn)
                    {
                        DbgPromptFn( "Start Debugger, Break, Ignore (dbi)?",
                                    Response, sizeof(Response));

                        switch (Response[0])
                        {
                            case 'i':
                            case 'I':
                                return;

                            case 'd':
                            case 'D':
                                DbgpStartDebuggerOnMyself(DbgpHeader ? (DbgpHeader->fDebug & DEBUG_USE_KDEBUG) : TRUE );

                            case 'b':
                            case 'B':
                                DebugBreak();
                                return;
                        }
                    }
                    else
                    {
                        DbgpStartDebuggerOnMyself(TRUE);
                        DebugBreak();
                        return;
                    }
                }


            }
            break;

        default:
            __AssertDebugOut( DSYSASSERT_ERROR, "Unknown continue code for assert: %d\n",
                        ContinueCode);
            return;
    }

}