/***
*dbgrpt.c - Debug CRT Reporting Functions
*
*       Copyright (c) 1988-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*
*******************************************************************************/

#ifdef _DEBUG

#include <internal.h>
#include <mtdll.h>
#include <malloc.h>
#include <mbstring.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <dbgint.h>
#include <signal.h>
#include <string.h>
#include <awint.h>

#ifdef _WIN32

#include <windows.h>

#define _CrtInterlockedIncrement InterlockedIncrement
#define _CrtInterlockedDecrement InterlockedDecrement

#else  /* _WIN32 */

#define FALSE 0
#define TRUE  1
#define BYTE char

#include <macos\msvcmac.h>
#include <macos\Desk.h>
#include <macos\Dialogs.h>
#include <macos\Events.h>
#include <macos\Fonts.h>
#include <macos\Memory.h>
#include <macos\LowMem.h>
#include <macos\Menus.h>
#include <macos\OSUtils.h>
#include <macos\QuickDra.h>
#include <macos\Resource.h>
#include <macos\Types.h>
#include <macos\Windows.h>
#include <macos\TextUtil.h>
#include <macos\SegLoad.h>
#include <macos\files.h>
#include <macos\gestalt.h>

__inline long __cdecl _CrtInterlockedIncrement(long * plVar) { return ++(*plVar); }
__inline long __cdecl _CrtInterlockedDecrement(long * plVar) { return --(*plVar); }

static void __cdecl _CrtOutputDebugString(char * sz);

#endif  /* _WIN32 */

/*---------------------------------------------------------------------------
 *
 * Debug Reporting
 *
 --------------------------------------------------------------------------*/

static int CrtMessageWindow(
        int,
        const char *,
        const char *,
        const char *,
        const char *
        );


_CRT_REPORT_HOOK _pfnReportHook;

_CRTIMP long _crtAssertBusy = -1;

int _CrtDbgMode[_CRT_ERRCNT] = {
#ifdef _WIN32
        _CRTDBG_MODE_DEBUG,
        _CRTDBG_MODE_WNDW,
        _CRTDBG_MODE_WNDW
#else  /* _WIN32 */
        _CRTDBG_MODE_DEBUG,
        _CRTDBG_MODE_DEBUG,
        _CRTDBG_MODE_DEBUG
#endif  /* _WIN32 */
        };

_HFILE _CrtDbgFile[_CRT_ERRCNT] = { _CRTDBG_INVALID_HFILE,
                                    _CRTDBG_INVALID_HFILE,
                                    _CRTDBG_INVALID_HFILE
                                  };


static const char * _CrtDbgModeMsg[_CRT_ERRCNT] = { "Warning", "Error", "Assertion Failed" };

/***
*void _CrtDebugBreak - call OS-specific debug function
*
*Purpose:
*       call OS-specific debug function
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

#undef _CrtDbgBreak

_CRTIMP void _cdecl _CrtDbgBreak(
        void
        )
{
#ifdef _WIN32
        DebugBreak();
#else  /* _WIN32 */
        Debugger();
#endif  /* _WIN32 */
}

/***
*int _CrtSetReportMode - set the reporting mode for a given report type
*
*Purpose:
*       set the reporting mode for a given report type
*
*Entry:
*       int nRptType    - the report type
*       int fMode       - new mode for given report type
*
*Exit:
*       previous mode for given report type
*
*Exceptions:
*
*******************************************************************************/
_CRTIMP int __cdecl _CrtSetReportMode(
        int nRptType,
        int fMode
        )
{
        int oldMode;

        if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
            return -1;

        if (fMode == _CRTDBG_REPORT_MODE)
            return _CrtDbgMode[nRptType];

        /* verify flags values */
        if (fMode & ~(_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW))
            return -1;

        oldMode = _CrtDbgMode[nRptType];

        _CrtDbgMode[nRptType] = fMode;

        return oldMode;
}

/***
*int _CrtSetReportFile - set the reporting file for a given report type
*
*Purpose:
*       set the reporting file for a given report type
*
*Entry:
*       int nRptType    - the report type
*       _HFILE hFile    - new file for given report type
*
*Exit:
*       previous file for given report type
*
*Exceptions:
*
*******************************************************************************/
_CRTIMP _HFILE __cdecl _CrtSetReportFile(
        int nRptType,
        _HFILE hFile
        )
{
        _HFILE oldFile;

        if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
            return _CRTDBG_HFILE_ERROR;

        if (hFile == _CRTDBG_REPORT_FILE)
            return _CrtDbgFile[nRptType];

        oldFile = _CrtDbgFile[nRptType];

#ifdef _WIN32
        if (_CRTDBG_FILE_STDOUT == hFile)
            _CrtDbgFile[nRptType] = GetStdHandle(STD_OUTPUT_HANDLE);

        else if (_CRTDBG_FILE_STDERR == hFile)
            _CrtDbgFile[nRptType] = GetStdHandle(STD_ERROR_HANDLE);
#else  /* _WIN32 */
        if (_CRTDBG_FILE_STDOUT == hFile || _CRTDBG_FILE_STDERR == hFile)
            return _CRTDBG_HFILE_ERROR;
#endif  /* _WIN32 */
        else
            _CrtDbgFile[nRptType] = hFile;

        return oldFile;
}


/***
*_CRT_REPORT_HOOK _CrtSetReportHook() - set client report hook
*
*Purpose:
*       set client report hook
*
*Entry:
*       _CRT_REPORT_HOOK pfnNewHook - new report hook
*
*Exit:
*       return previous hook
*
*Exceptions:
*
*******************************************************************************/
_CRTIMP _CRT_REPORT_HOOK __cdecl _CrtSetReportHook(
        _CRT_REPORT_HOOK pfnNewHook
        )
{
        _CRT_REPORT_HOOK pfnOldHook = _pfnReportHook;
        _pfnReportHook = pfnNewHook;
        return pfnOldHook;
}


#define MAXLINELEN 64
#define MAX_MSG 4096
#define TOOLONGMSG "_CrtDbgReport: String too long or IO Error"


/***
*int _CrtDbgReport() - primary reporting function
*
*Purpose:
*       Display a message window with the following format.
*
*       ================= Microsft Visual C++ Debug Library ================
*
*       {Warning! | Error! | Assertion Failed!}
*
*       Program: c:\test\mytest\foo.exe
*       [Module: c:\test\mytest\bar.dll]
*       [File: c:\test\mytest\bar.c]
*       [Line: 69]
*
*       {<warning or error message> | Expression: <expression>}
*
*       [For information on how your program can cause an assertion
*        failure, see the Visual C++ documentation on asserts]
*
*       (Press Retry to debug the application)
*
*       ===================================================================
*
*Entry:
*       int             nRptType    - report type
*       const char *    szFile      - file name
*       int             nLine       - line number
*       const char *    szModule    - module name
*       const char *    szFormat    - format string
*       ...                         - var args
*
*Exit:
*       if (MessageBox)
*       {
*           Abort -> aborts
*           Retry -> return TRUE
*           Ignore-> return FALSE
*       }
*       else
*           return FALSE
*
*Exceptions:
*
*******************************************************************************/
_CRTIMP int __cdecl _CrtDbgReport(
        int nRptType,
        const char * szFile,
        int nLine,
        const char * szModule,
        const char * szFormat,
        ...
        )
{
        int retval;
        va_list arglist;
        char szLineMessage[MAX_MSG] = {0};
        char szOutMessage[MAX_MSG] = {0};
        char szUserMessage[MAX_MSG] = {0};
        #define ASSERTINTRO1 "Assertion failed: "
        #define ASSERTINTRO2 "Assertion failed!"

        va_start(arglist, szFormat);

        if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
            return -1;

        /*
         * handle the (hopefully rare) case of
         *
         * 1) ASSERT while already dealing with an ASSERT
         *      or
         * 2) two threads asserting at the same time
         */
        if (_CRT_ASSERT == nRptType && _CrtInterlockedIncrement(&_crtAssertBusy) > 0)
        {
            /* use only 'safe' functions -- must not assert in here! */
#ifdef _WIN32
            static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL;

            if (NULL == pfnwsprintfA)
            {
                HANDLE hlib = LoadLibrary("user32.dll");

                if (NULL == hlib || NULL == (pfnwsprintfA =
                            (int (APIENTRY *)(LPSTR, LPCSTR, ...))
                            GetProcAddress(hlib, "wsprintfA")))
                    return -1;
            }

            (*pfnwsprintfA)( szOutMessage,
                "Second Chance Assertion Failed: File %s, Line %d\n",
                szFile, nLine);

            OutputDebugString(szOutMessage);
#else  /* _WIN32 */
            strcpy(szOutMessage, "Second Chance Assertion Failed: File ");
            strcat(szOutMessage, szFile);
            strcat(szOutMessage, ", Line ");
            numtostring(nLine, &szOutMessage[strlen(szOutMessage)]);
            strcat(szOutMessage, "\n");
            _CrtOutputDebugString(szOutMessage);
#endif  /* _WIN32 */

            _CrtInterlockedDecrement(&_crtAssertBusy);

            _CrtDbgBreak();
            return -1;
        }

        if (szFormat && _vsnprintf(szUserMessage,
                       MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)),
                       szFormat,
                       arglist) < 0)
            strcpy(szUserMessage, TOOLONGMSG);

        if (_CRT_ASSERT == nRptType)
            strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2);

        strcat(szLineMessage, szUserMessage);

        if (_CRT_ASSERT == nRptType)
        {
            if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
                strcat(szLineMessage, "\r");
            strcat(szLineMessage, "\n");
        }

        if (szFile)
        {
            if (_snprintf(szOutMessage, MAX_MSG, "%s(%d) : %s",
                szFile, nLine, szLineMessage) < 0)
            strcpy(szOutMessage, TOOLONGMSG);
        }
        else
            strcpy(szOutMessage, szLineMessage);

        /* user hook may handle report */
        if (_pfnReportHook && (*_pfnReportHook)(nRptType, szOutMessage, &retval))
        {
            if (_CRT_ASSERT == nRptType)
                _CrtInterlockedDecrement(&_crtAssertBusy);
            return retval;
        }

        if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
        {
            if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE)
            {
#ifdef _WIN32
                DWORD written;
                WriteFile(_CrtDbgFile[nRptType], szOutMessage, strlen(szOutMessage), &written, NULL);
#else  /* _WIN32 */
                long written = strlen(szOutMessage);

                FSWrite((short)_CrtDbgFile[nRptType], &written, szOutMessage);
#endif  /* _WIN32 */
            }
        }

        if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG)
        {
#ifdef _WIN32
            OutputDebugString(szOutMessage);
#else  /* _WIN32 */
            _CrtOutputDebugString(szOutMessage);
#endif  /* _WIN32 */
        }

        if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW)
        {
            char szLine[20];

            retval = CrtMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage);
            if (_CRT_ASSERT == nRptType)
                _CrtInterlockedDecrement(&_crtAssertBusy);
            return retval;
        }

        if (_CRT_ASSERT == nRptType)
            _CrtInterlockedDecrement(&_crtAssertBusy);
        /* ignore */
        return FALSE;
}


/***
*static int CrtMessageWindow() - report to a message window
*
*Purpose:
*       put report into message window, allow user to choose action to take
*
*Entry:
*       int             nRptType      - report type
*       const char *    szFile        - file name
*       const char *    szLine        - line number
*       const char *    szModule      - module name
*       const char *    szUserMessage - user message
*
*Exit:
*       if (MessageBox)
*       {
*           Abort -> aborts
*           Retry -> return TRUE
*           Ignore-> return FALSE
*       }
*       else
*           return FALSE
*
*Exceptions:
*
*******************************************************************************/
#ifdef _WIN32

static int CrtMessageWindow(
        int nRptType,
        const char * szFile,
        const char * szLine,
        const char * szModule,
        const char * szUserMessage
        )
{
        int nCode;
        char *szShortProgName;
        char *szShortModuleName;
        char szExeName[MAX_PATH];
        char szOutMessage[MAX_MSG];

        _ASSERTE(szUserMessage != NULL);

        /* Shorten program name */
        if (!GetModuleFileName(NULL, szExeName, MAX_PATH))
            strcpy(szExeName, "<program name unknown>");

        szShortProgName = szExeName;

        if (strlen(szShortProgName) > MAXLINELEN)
        {
            szShortProgName += strlen(szShortProgName) - MAXLINELEN;
            strncpy(szShortProgName, "...", 3);
        }

        /* Shorten module name */
        szShortModuleName = (char *) szModule;

        if (szShortModuleName && strlen(szShortModuleName) > MAXLINELEN)
        {
            szShortModuleName += strlen(szShortModuleName) - MAXLINELEN;
            strncpy(szShortModuleName, "...", 3);
        }

        if (_snprintf(szOutMessage, MAX_MSG,
                "Debug %s!\n\nProgram: %s%s%s%s%s%s%s%s%s%s%s"
                "\n\n(Press Retry to debug the application)",
                _CrtDbgModeMsg[nRptType],
                szShortProgName,
                szShortModuleName ? "\nModule: " : "",
                szShortModuleName ? szShortModuleName : "",
                szFile ? "\nFile: " : "",
                szFile ? szFile : "",
                szLine ? "\nLine: " : "",
                szLine ? szLine : "",
                szUserMessage[0] ? "\n\n" : "",
                szUserMessage[0] && _CRT_ASSERT == nRptType ? "Expression: " : "",
                szUserMessage[0] ? szUserMessage : "",
                _CRT_ASSERT == nRptType ?
                "\n\nFor information on how your program can cause an assertion"
                "\nfailure, see the Visual C++ documentation on asserts."
                : "") < 0)
            strcpy(szOutMessage, TOOLONGMSG);

        /* Report the warning/error */
        nCode = __crtMessageBoxA(szOutMessage,
                             "Microsoft Visual C++ Debug Library",
                             MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND);

        /* Abort: abort the program */
        if (IDABORT == nCode)
        {
            /* raise abort signal */
            raise(SIGABRT);

            /* We usually won't get here, but it's possible that
               SIGABRT was ignored.  So exit the program anyway. */

            _exit(3);
        }

        /* Retry: return 1 to call the debugger */
        if (IDRETRY == nCode)
            return 1;

        /* Ignore: continue execution */
        return 0;
}

#elif defined (_M_MPPC)

/* Definitions */
#define  NIL       0

/* Portable Types */
typedef unsigned char     Char;
typedef unsigned long     Word;

QDGlobals qd;

static void BlinkButton(int count,Rect *r)
{
        long n;

        while (count--) {
            InvertRoundRect(r,10,10);
            Delay(4,&n);
        }
}

static int ButtonClicked(Rect *r)
{
        int state = 0;
        Point pt;

        do {
            GetMouse(&pt);
            if (PtInRect(pt,r)) {
                if (0 == state) {
                    state = 1;
                    BlinkButton(1,r);
                }
            }
            else if (1 == state) {
                state = 0;
                BlinkButton(1,r);
            }
        } while (Button());
        if (state)
            BlinkButton(3,r);
        return state;
}

static int CrtMessageWindow(
        int nRptType,
        const char * szFile,
        const char * szLine,
        const char * szModule,
        const char * szUserMessage
        )
{
        #define LEFTMARGIN  15
        #define ALINE       14

        char szOutMessage[MAX_MSG];
        Rect abort,retry,ignore,r;
        GrafPtr oldPort;
        WindowPtr window;
        EventRecord event;
        long total, contig;
        short currHeight = 20;
        int numLines = 13 +
            4 * (_CRT_ASSERT == nRptType) +
            2 * (szUserMessage[0] != '\0') +
            (szModule != NULL) +
            2 * (szFile != NULL);

        /* center a rectangle on the screen, near the top of the screen */
        r.top = 50;
        r.bottom = r.top + numLines * ALINE;
        r.left = qd.screenBits.bounds.right/2 - 170;
        r.right = qd.screenBits.bounds.right/2 + 170;

        /*
         * NewWindow will hang in insufficient memory conditions. Must
         * confirm available memory before use.
         */

        contig = 0;
        PurgeSpace(&total, &contig);
        if (contig < 1024)
            return -1;

        /* create the window and setup the port */
        if ((window = NewWindow(NIL,&r,"\p",TRUE,dBoxProc,(WindowPtr)-1,TRUE,0))
            == NULL)
            return -1;

        GetPort(&oldPort);
        SetPort(window);
        InitCursor();

        TextFont(systemFont);

        MoveTo(LEFTMARGIN,currHeight);
        TextFace(underline);
        DrawString("\pMicrosoft Visual C++ Debug Library");
        TextFace(0);
        currHeight += 2 * ALINE;

        _snprintf(szOutMessage, MAX_MSG, "Debug %s!", _CrtDbgModeMsg[nRptType]);
        _c2pstr(szOutMessage);
        MoveTo(LEFTMARGIN,currHeight);
        TextFace(bold);
        DrawString(szOutMessage);
        TextFace(0);
        currHeight += 2 * ALINE;

        MoveTo(LEFTMARGIN,currHeight);
        DrawString("\pProgram: ");
        DrawString(LMGetCurApName());
        currHeight += ALINE;

        if (szModule) {
            _snprintf(szOutMessage, MAX_MSG, "Module: %s", szModule);
            _c2pstr(szOutMessage);
            MoveTo(LEFTMARGIN,currHeight);
            DrawString(szOutMessage);
            currHeight += ALINE;
        }

        if (szFile) {
            _snprintf(szOutMessage, MAX_MSG, "File: %s", szFile);
            _c2pstr(szOutMessage);
            MoveTo(LEFTMARGIN,currHeight);
            DrawString(szOutMessage);
            currHeight += ALINE;
        }

        if (szLine) {
            _snprintf(szOutMessage, MAX_MSG, "Line: %s", szLine);
            _c2pstr(szOutMessage);
            MoveTo(LEFTMARGIN,currHeight);
            DrawString(szOutMessage);
            currHeight += ALINE;
        }
        currHeight += ALINE;

        if (szUserMessage[0])
        {
            _snprintf(szOutMessage, MAX_MSG, "%s%s",
                (_CRT_ASSERT == nRptType) ? "Expression: " : "",
                szUserMessage);

            r.top = currHeight-10;
            r.bottom = currHeight - 5;
            r.left = LEFTMARGIN - 2;
            r.right = window->portRect.right - LEFTMARGIN - 2;
            TextFace(condense);
            TextBox(szOutMessage, strlen(szOutMessage), &r, teJustLeft);
            TextFace(0);
            currHeight += 2*ALINE;
        }

        currHeight += ALINE;

        if (_CRT_ASSERT == nRptType)
        {
            MoveTo(LEFTMARGIN,currHeight);
            DrawString("\pFor more information on how your program");
            currHeight += ALINE;

            MoveTo(LEFTMARGIN,currHeight);
            DrawString("\pcan cause an assertion failure, see the");
            currHeight += ALINE;

            MoveTo(LEFTMARGIN,currHeight);
            DrawString("\pVisual C++ documentation on asserts.");
            currHeight += 2*ALINE;

            MoveTo(LEFTMARGIN,currHeight);
            DrawString("\p(Press Retry to debug the application)");
            currHeight += 2*ALINE;
        }
        else {
            MoveTo(LEFTMARGIN,currHeight);
            DrawString("\p(Press Retry to debug the application)");
            currHeight += 2*ALINE;
        }

        /* draw abort button */
        abort.top = currHeight;
        abort.bottom = abort.top + 20;
        abort.left = LEFTMARGIN;
        abort.right = abort.left + 75;
        FrameRoundRect(&abort,10,10);
        MoveTo((short)(abort.left+20),(short)(abort.bottom-6));
        DrawString("\pAbort");

        /* draw retry button */
        retry.top = currHeight;
        retry.bottom = retry.top + 20;
        retry.left = LEFTMARGIN + 110;
        retry.right = retry.left + 75;
        FrameRoundRect(&retry,10,10);
        MoveTo((short)(retry.left+20),(short)(retry.bottom-6));
        DrawString("\pRetry");

        /* draw ignore button */
        ignore.top = currHeight;
        ignore.bottom = ignore.top + 20;
        ignore.left = LEFTMARGIN + 110 * 2;
        ignore.right = ignore.left + 75;
        FrameRoundRect(&ignore,10,10);
        MoveTo((short)(ignore.left+17),(short)(ignore.bottom-6));
        DrawString("\pIgnore");

        /* event loop */
        while (TRUE) {
            if (GetNextEvent(mDownMask+keyDownMask,&event)) {
                if (event.what == keyDown) { /* keyboard shortcuts */
                    char c = (char)event.message;

                    /* abort */
                    if (c == 'a') {
                        BlinkButton(3,&abort);
                        ExitToShell();
                    }

                    /* retry */
                    else if (c == 'r') {
                        BlinkButton(3,&retry);
                        Debugger();
                        break;
                    }

                    /* ignore */
                    else if (c == 'i') {
                        BlinkButton(3,&ignore);
                        break;
                    }
                }
                if (event.what == mouseDown) {
                    GlobalToLocal(&event.where);
                    if (PtInRect(event.where,&abort) && ButtonClicked(&abort))
                        ExitToShell();
                    else if (PtInRect(event.where,&retry) && ButtonClicked(&retry)) {
                        Debugger();
                        break;
                    }
                    else if (PtInRect(event.where,&ignore) && ButtonClicked(&ignore))
                        break;
                }
            }
        }
        DisposeWindow(window);
        SetPort(oldPort);

        /* ignore */
        return 0;
}

#define WLMD_STR        1
#define LMGetMacJmp()   (*(ProcPtr*) 0x120)

enum
{
        uppGestaltDebugProcInfo =   kCStackBased
            | STACK_ROUTINE_PARAMETER (1, SIZE_CODE (sizeof (int)))
            | STACK_ROUTINE_PARAMETER (2, SIZE_CODE (sizeof (StringPtr)))
            | STACK_ROUTINE_PARAMETER (3, SIZE_CODE (sizeof (char*)))
            | STACK_ROUTINE_PARAMETER (4, SIZE_CODE (sizeof (char*)))
};


static UniversalProcPtr pfnGestaltDebugger;


/***
*void _CleanStr - clean string
*
*Purpose:
*       Cleans a string of characters that aren't useful. Always removes '\r'. If
*       the string is going to MacsBug, replaces semicolons with periods, and
*       removes trailing newlines, since MacsBug always advances to the next line
*       after a _DebugStr.
*
*Entry:
*       StringPtr   st          - the string to clean
*       int         fMacsBug    - whether the string is going to MacsBug
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static void __cdecl _CleanStr(StringPtr st, int fMacsBug)
{
        int     i;

        /* change semi-colons to periods (semi-colons hose MacsBug) */
        if (fMacsBug)
        {
            for (i = 0; i < StrLength(st); i++)
            {
                /* skip MB chars */
                if (_ismbblead(st[i+1]))
                {
                    i++;
                    continue;
                }

                if (st[i + 1] == ';')
                    st[i + 1] = '.';
            }
        }

        for (i = StrLength(st); i >= 1; i--)
        {
            /* if trail byte, stop processing */
            if (_ismbstrail(&st[1], &st[i]))
                break;

            /* remove trailing newlines */
            if (st[i] == '\n')
            {
                if (fMacsBug)
                    st[0]--;
            }
            /* remove trailing carriage returns */
            else if (st[i] == '\r')
            {
                st[0]--;
            }
            /* otherwise, stop processing */
            else
            {
                break;
            }
        }
}


/***
*void _ConfirmVCDebugger
*
*Purpose:
*       Determines if a 'WLMD' Gestalt selector is registered.
*
*Entry:
*
*Exit:
*       Current VC++ Debug Output function or NULL.
*
*Exceptions:
*
*******************************************************************************/

static UniversalProcPtr __cdecl _ConfirmVCDebugger(void)
{
        /* The VC++ Debug Ouput facilities are registered as "WLMD" */
        if (Gestalt('WLMD', (long*) &pfnGestaltDebugger) != noErr)
            pfnGestaltDebugger = NULL;
        return pfnGestaltDebugger;
}


/***
*int _IsMSVCDebugger
*
*Purpose:
*       Determines if a 'MSVC' Gestalt selector is registered, or if there
*       is a low level debugger in the system
*
*       Only call a macsbug-like debugger if it's going to be around to
*       handle the DebugStr
*
*Entry:
*
*Exit:
*       TRUE:   If MSVC debugger registered
*       FALSE:  Otherwise
*
*Exceptions:
*
*******************************************************************************/

static int __cdecl _IsLowLevelDebugger(void)
{
#define mskBeingDebugged 1
#define mskPreviousDebugger 2

        unsigned long caps;

        /* See if VC++ Debugger is running */
        if(Gestalt('MSVC', (long*) &caps) == noErr)
        {
            if((caps & mskBeingDebugged) || (caps & mskPreviousDebugger))
                return 1;
            else
                return 0;
        }
        /* See if MacsBug is running */
        else if (LMGetMacJmp() != NULL)
            return 1;
        else
            return 0;

#undef mskBeingDebugged
#undef mskPreviousDebugger
}


/***
*void _CrtOutputDebugString - output Debug String
*
*Purpose:
*       Does the actual output of a string to the debugger.
*
*Entry:
*       char * sz -- message to output
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static void __cdecl _CrtOutputDebugString(char * sz)
{
        Str255      stDebug;
        long        cchTotal;
        short       cchCur;
        const char* pchCur;
        EventRecord er;

        _ConfirmVCDebugger();

        OSEventAvail(0, &er);

        cchTotal = strlen(sz);
        pchCur = sz;

        /* output string in edible portions */
        while (cchTotal > 0)
        {
            cchCur = (short) min(cchTotal, 253);
            _ASSERTE(cchCur > 0);

            /* put back orphaned lead byte */
            if (_ismbslead(pchCur, pchCur+cchCur-1))
                cchCur--;

            /* convert CString to PascalString */
            stDebug[0] = (unsigned char) cchCur;
            BlockMoveData(pchCur, &stDebug[1], cchCur);

            /* See if VC++ Debug Output facility is running */
            if (pfnGestaltDebugger != NULL)
            {
                _CleanStr(stDebug, 0);

                /* if the Control key is down, we break into MacsBug before
                   sending the string to the Gestalt debugger */
                if ((er.modifiers & controlKey) != 0)
                    Debugger();

                CallUniversalProc(pfnGestaltDebugger, uppGestaltDebugProcInfo, WLMD_STR, stDebug,
                        NULL, NULL);
            }
            /* See if VC++ Debugger or MacsBug is running */
            else if (_IsLowLevelDebugger())
            {
                short   cchSt;

                _CleanStr(stDebug, 1);

                /* _CleanStr may have changed the length of stDebug */
                cchSt = StrLength(stDebug);

                /* if the Control key is down, we always stop in the debugger */
                if ((er.modifiers & controlKey) == 0)
                {
                    stDebug[cchSt + 1] = ';';
                    stDebug[cchSt + 2] = 'g';
                    stDebug[0] += 2;
                }

                DebugStr(stDebug);
            }
            cchTotal -= cchCur;
            pchCur += cchCur;
        }
}

#endif  /* defined (_M_MPPC) */

#endif  /* _DEBUG */

