/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_tools.hxx"

#define _TOOLS_DEBUG_CXX

#if defined (UNX) || defined (GCC)
#include <unistd.h>
#else
#include <direct.h>
#endif

#include <time.h>
#include <cstdarg>  // combinations
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#ifdef OS2
#define INCL_DOSSEMAPHORES
#define INCL_DOSMISC
#define INCL_WINDIALOGS
#define INCL_WINSHELLDATA
#include <svpm.h>
#endif

#if defined ( WNT )
#include <windows.h>
#endif

#include <tools/debug.hxx>
#include <rtl/string.h>
#include <sal/macros.h>

#include <vector>

#include <osl/diagnose.h>

// =======================================================================

#ifdef DBG_UTIL

// --- DbgErrors ---

static sal_Char const DbgError_ProfEnd1[]   = "DBG_PROF...() without DBG_PROFSTART(): ";
static sal_Char const DbgError_Xtor1[]      = "DBG_DTOR() or DBG_CHKTHIS() without DBG_CTOR(): ";

static sal_Char const DbgError_CtorDtor1[]  = "this == NULL in class ";
static sal_Char const DbgError_CtorDtor2[]  = "invalid this-Pointer %p in class ";
static sal_Char const DbgError_CtorDtor3[]  = "Error-Msg from Object %p in class ";

static sal_Char const DbgTrace_EnterCtor[]  = "Enter Ctor from class ";
static sal_Char const DbgTrace_LeaveCtor[]  = "Leave Ctor from class ";
static sal_Char const DbgTrace_EnterDtor[]  = "Enter Dtor from class ";
static sal_Char const DbgTrace_LeaveDtor[]  = "Leave Dtor from class ";
static sal_Char const DbgTrace_EnterMeth[]  = "Enter method from class ";
static sal_Char const DbgTrace_LeaveMeth[]  = "Leave method from class ";

// --- PointerList ---

#define PBLOCKCOUNT     1024

struct PBlock
{
    void*       aData[PBLOCKCOUNT];
    sal_uInt16      nCount;
    PBlock*     pPrev;
    PBlock*     pNext;
};

class PointerList
{
private:
    PBlock*     pFirst;
    PBlock*     pLast;
    sal_uIntPtr       nCount;

public:
                PointerList() { pFirst = NULL; pLast = NULL; nCount = 0; }
                ~PointerList();

    void        Add( const void* p );
    sal_Bool        Remove( const void* p );

    const void* Get( sal_uIntPtr nPos ) const;
    sal_Bool        IsIn( const void* p ) const;
    sal_uIntPtr       Count() const { return nCount; }
};

// --- Datentypen ---

#define DBG_MAXNAME     28

struct ProfType
{
    sal_uIntPtr                   nCount;
    sal_uIntPtr                   nTime;
    sal_uIntPtr                   nMinTime;
    sal_uIntPtr                   nMaxTime;
    sal_uIntPtr                   nStart;
    sal_uIntPtr                   nContinueTime;
    sal_uIntPtr                   nContinueStart;
    sal_Char                aName[DBG_MAXNAME+1];
};

struct XtorType
{
    sal_uIntPtr                   nCtorCalls;
    sal_uIntPtr                   nDtorCalls;
    sal_uIntPtr                   nMaxCount;
    sal_uIntPtr                   nStatics;
    sal_Char                aName[DBG_MAXNAME+1];
    sal_Bool                    bTest;
    PointerList             aThisList;
};

struct DebugData
{
    DbgData                 aDbgData;
    sal_uInt16                  bInit;
    DbgPrintLine            pDbgPrintMsgBox;
    DbgPrintLine            pDbgPrintWindow;
    DbgPrintLine            pDbgPrintTestTool;
    DbgPrintLine			pDbgAbort;
    ::std::vector< DbgPrintLine >
                            aDbgPrintUserChannels;
    PointerList*            pProfList;
    PointerList*            pXtorList;
    DbgTestSolarMutexProc   pDbgTestSolarMutex;
    pfunc_osl_printDetailedDebugMessage
                            pOldDebugMessageFunc;
    bool                    bOslIsHooked;

    DebugData()
        :bInit( sal_False )
        ,pDbgPrintMsgBox( NULL )
        ,pDbgPrintWindow( NULL )
        ,pDbgPrintTestTool( NULL )
        ,pDbgAbort( NULL )
        ,pProfList( NULL )
        ,pXtorList( NULL )
        ,pDbgTestSolarMutex( NULL )
        ,pOldDebugMessageFunc( NULL )
        ,bOslIsHooked( false )
    {
        aDbgData.nTestFlags = DBG_TEST_RESOURCE | DBG_TEST_MEM_INIT;
        aDbgData.bOverwrite = sal_True;
        aDbgData.nTraceOut = DBG_OUT_NULL;
        aDbgData.nWarningOut = DBG_OUT_NULL;
#ifdef UNX
        aDbgData.nErrorOut = DBG_OUT_SHELL;
#else
        aDbgData.nErrorOut = DBG_OUT_MSGBOX;
#endif
        aDbgData.bMemInit = 0x77;
        aDbgData.bMemBound = 0x55;
        aDbgData.bMemFree = 0x33;
        aDbgData.bHookOSLAssert = sal_True;
        aDbgData.aDebugName[0] = 0;
        aDbgData.aInclFilter[0] = 0;
        aDbgData.aExclFilter[0] = 0;
        aDbgData.aInclClassFilter[0] = 0;
        aDbgData.aExclClassFilter[0] = 0;
        aDbgData.aDbgWinState[0] = 0;
    }
};

#define DBG_TEST_XTOR_EXTRA (DBG_TEST_XTOR_THIS |  DBG_TEST_XTOR_FUNC |               \
                             DBG_TEST_XTOR_EXIT |  DBG_TEST_XTOR_REPORT )

// ------------------------------
// - statische Verwaltungsdaten -
// ------------------------------

static DebugData aDebugData;

static sal_Char aCurPath[260];

static int bDbgImplInMain = sal_False;

// =======================================================================

#if defined( WNT )
static CRITICAL_SECTION aImplCritDbgSection;
#elif defined( OS2 )
static HMTX             hImplCritDbgSection = 0;
#endif
static sal_Bool             bImplCritDbgSectionInit = sal_False;

// -----------------------------------------------------------------------

void ImplDbgInitLock()
{
#if defined( WNT )
    InitializeCriticalSection( &aImplCritDbgSection );
#elif defined( OS2 )
    DosCreateMutexSem( NULL, &hImplCritDbgSection, 0, sal_False );
#endif
    bImplCritDbgSectionInit = sal_True;
}

// -----------------------------------------------------------------------

void ImplDbgDeInitLock()
{
#if defined( WNT )
    DeleteCriticalSection( &aImplCritDbgSection );
#elif defined( OS2 )
    DosCloseMutexSem( hImplCritDbgSection );
#endif
    bImplCritDbgSectionInit = sal_False;
}

// -----------------------------------------------------------------------

void ImplDbgLock()
{
    if ( !bImplCritDbgSectionInit )
        return;

#if defined( WNT )
    EnterCriticalSection( &aImplCritDbgSection );
#elif defined( OS2 )
    DosRequestMutexSem( hImplCritDbgSection, SEM_INDEFINITE_WAIT );
#endif
}

// -----------------------------------------------------------------------

void ImplDbgUnlock()
{
    if ( !bImplCritDbgSectionInit )
        return;

#if defined( WNT )
    LeaveCriticalSection( &aImplCritDbgSection );
#elif defined( OS2 )
    DosReleaseMutexSem( hImplCritDbgSection );
#endif
}

// =======================================================================

#if (defined WNT || defined OS2)
//#define SV_MEMMGR //
#endif
#ifdef SV_MEMMGR
void DbgImpCheckMemory( void* p = NULL );
void DbgImpCheckMemoryDeInit();
void DbgImpMemoryInfo( sal_Char* pBuf );
#endif

#define FILE_LINEEND    "\n"

// =======================================================================

static sal_Bool ImplActivateDebugger( const sal_Char* pMsg )
{
#if defined( WNT )
    static sal_Char aImplDbgOutBuf[DBG_BUF_MAXLEN];
    strcpy( aImplDbgOutBuf, pMsg );
    strcat( aImplDbgOutBuf, "\r\n" );
    OutputDebugString( aImplDbgOutBuf );
    DebugBreak();
    return sal_True;
#else
    (void) pMsg; // avoid warning about unused parameter
    return sal_False;
#endif
}

// -----------------------------------------------------------------------

static sal_Bool ImplCoreDump()
{
#if defined( WNT )
    DebugBreak();
#else
    long* pTemp = 0;
    *pTemp = 0xCCCC;
#endif
    return sal_True;
}

// =======================================================================

static sal_uIntPtr ImplGetPerfTime()
{
#if defined( WNT )
    return (sal_uIntPtr)GetTickCount();
#elif defined( OS2 )
    sal_uIntPtr nClock;
    DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &nClock, sizeof( nClock ) );
    return (sal_uIntPtr)nClock;
#else
    static sal_uIntPtr    nImplTicksPerSecond = 0;
    static double   dImplTicksPerSecond;
    sal_uIntPtr           nTicks = (sal_uIntPtr)clock();

    if ( !nImplTicksPerSecond )
    {
        nImplTicksPerSecond = CLOCKS_PER_SEC;
        dImplTicksPerSecond = nImplTicksPerSecond;
    }

    double fTicks = nTicks;
    fTicks *= 1000;
    fTicks /= dImplTicksPerSecond;
    return (sal_uIntPtr)fTicks;
#endif
}

// -----------------------------------------------------------------------

typedef FILE*       FILETYPE;
#define FileOpen    fopen
#define FileRead    fread
#define FileWrite   fwrite
#define FilePrintF  fprintf
#define FileClose   fclose

// =======================================================================

namespace
{
    enum ConfigSection
    {
        eOutput,
        eMemory,
        eGUI,
        eObjects,
        eTest,

        eUnknown
    };

    void lcl_lineFeed( FILETYPE _pFile )
    {
        FilePrintF( _pFile, "%s", FILE_LINEEND );
    }

    const sal_Char* lcl_getSectionName( ConfigSection _eSection )
    {
        const sal_Char* pSectionName = NULL;
        switch ( _eSection )
        {
            case eOutput    : pSectionName = "output";  break;
            case eMemory    : pSectionName = "memory";  break;
            case eGUI       : pSectionName = "gui";     break;
            case eObjects   : pSectionName = "objects"; break;
            case eTest      : pSectionName = "test";    break;
            case eUnknown:
                OSL_ASSERT(false);
                break;
        }
        return pSectionName;
    }

    ConfigSection lcl_getSectionFromName( const sal_Char* _pSectionName, size_t _nSectionNameLength )
    {
        if ( strncmp( _pSectionName, "output",  _nSectionNameLength < 6 ? _nSectionNameLength : 6 ) == 0 )
            return eOutput;
        if ( strncmp( _pSectionName, "memory",  _nSectionNameLength < 6 ? _nSectionNameLength : 6 ) == 0 )
            return eMemory;
        if ( strncmp( _pSectionName, "gui",     _nSectionNameLength < 3 ? _nSectionNameLength : 3 ) == 0 )
            return eGUI;
        if ( strncmp( _pSectionName, "objects", _nSectionNameLength < 7 ? _nSectionNameLength : 7 ) == 0 )
            return eObjects;
        if ( strncmp( _pSectionName, "test",    _nSectionNameLength < 4 ? _nSectionNameLength : 4 ) == 0 )
            return eTest;
        return eUnknown;
    }

    void lcl_startSection( FILETYPE _pFile, ConfigSection _eSection )
    {
        FilePrintF( _pFile, "[%s]%s", lcl_getSectionName( _eSection ), FILE_LINEEND );
    }

    void lcl_writeConfigString( FILETYPE _pFile, const sal_Char* _pKeyName, const sal_Char* _pValue )
    {
        FilePrintF( _pFile, "%s=%s%s", _pKeyName, _pValue, FILE_LINEEND );
    }

    void lcl_writeConfigBoolean( FILETYPE _pFile, const sal_Char* _pKeyName, bool _bValue )
    {
        lcl_writeConfigString( _pFile, _pKeyName, _bValue ? "1" : "0" );
    }

    void lcl_writeConfigFlag( FILETYPE _pFile, const sal_Char* _pKeyName, sal_uIntPtr _nAllFlags, sal_uIntPtr _nCheckFlag )
    {
        lcl_writeConfigBoolean( _pFile, _pKeyName, ( _nAllFlags & _nCheckFlag ) != 0 );
    }

    void lcl_writeConfigOutChannel( FILETYPE _pFile, const sal_Char* _pKeyName, sal_uIntPtr _nValue )
    {
        const sal_Char* names[ DBG_OUT_COUNT ] =
        {
            "dev/null", "file", "window", "shell", "messagebox", "testtool", "debugger", "abort"
        };
        lcl_writeConfigString( _pFile, _pKeyName, names[ _nValue ] );
    }
    void lcl_writeHexByte( FILETYPE _pFile, const sal_Char* _pKeyName, sal_uInt8 _nValue )
    {
        sal_Char buf[RTL_STR_MAX_VALUEOFINT32];
        rtl_String* stringData = NULL;
        rtl_string_newFromStr_WithLength( &stringData, buf, rtl_str_valueOfInt32( buf, _nValue, 16 ) );

        lcl_writeConfigString( _pFile, _pKeyName, stringData->buffer );

        rtl_string_release( stringData );
    }
    bool lcl_isConfigSection( const sal_Char* _pLine, size_t _nLineLen )
    {
        if ( _nLineLen < 2 )
            // not even enough space for '[' and ']'
            return false;
        if ( ( _pLine[0] == '[' ) && ( _pLine[ _nLineLen - 1 ] == ']' ) )
            return true;
        return false;
    }
    bool lcl_isConfigKey( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName )
    {
        size_t nKeyLength = strlen( _pKeyName );
        if ( nKeyLength + 1 >= _nLineLen )
            // not even long enough for the key name plus "=" plus a one-character value
            return false;
        if ( ( strncmp( _pLine, _pKeyName, nKeyLength ) == 0 ) && ( _pLine[ nKeyLength ] == '=' ) )
            return true;
        return false;
    }
    sal_Int32 lcl_tryReadConfigString( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_Char* _pValue, size_t _nValueLen )
    {
        if ( !lcl_isConfigKey( _pLine, _nLineLen, _pKeyName ) )
            return 0;
        size_t nValuePos = strlen( _pKeyName ) + 1;
        size_t nValueLen = _nLineLen - nValuePos;
        const sal_Char* pValue = _pLine + nValuePos;
        strncpy( _pValue, pValue, ( _nValueLen > nValueLen ) ? nValueLen : _nValueLen );
        _pValue[ ( _nValueLen > nValueLen ) ? nValueLen : _nValueLen - 1 ] = 0;
        return strlen( _pValue );
    }
    void lcl_tryReadConfigBoolean( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_uIntPtr* _out_pnValue )
    {
        sal_Char aBuf[2];
        size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) );
        if ( nValueLen )
            *_out_pnValue = strcmp( aBuf, "1" ) == 0 ? sal_True : sal_False;
    }
    void lcl_matchOutputChannel( sal_Char const * i_buffer, sal_uIntPtr* o_value )
    {
        if ( i_buffer == NULL )
            return;
        const sal_Char* names[ DBG_OUT_COUNT ] =
        {
            "dev/null", "file", "window", "shell", "messagebox", "testtool", "debugger", "abort"
        };
        for ( size_t name = 0; name < SAL_N_ELEMENTS( names ); ++name )
        {
            if ( strcmp( i_buffer, names[ name ] ) == 0 )
            {
                *o_value = name;
                return;
            }
        }
    }
    void lcl_tryReadOutputChannel( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_uIntPtr* _out_pnValue )
    {
        sal_Char aBuf[20];
        size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) );
        if ( nValueLen )
            lcl_matchOutputChannel( aBuf, _out_pnValue );
    }
    void lcl_tryReadConfigFlag( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_uIntPtr* _out_pnAllFlags, sal_uIntPtr _nCheckFlag )
    {
        sal_Char aBuf[2];
        size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) );
        if ( nValueLen )
        {
            if ( strcmp( aBuf, "1" ) == 0 )
                *_out_pnAllFlags |= _nCheckFlag;
            else
                *_out_pnAllFlags &= ~_nCheckFlag;
        }
    }
    void lcl_tryReadHexByte( const sal_Char* _pLine, size_t _nLineLen, const sal_Char* _pKeyName, sal_uInt8* _out_pnValue )
    {
        sal_Char aBuf[3];
        size_t nValueLen = lcl_tryReadConfigString( _pLine, _nLineLen, _pKeyName, aBuf, sizeof( aBuf ) );
        if ( nValueLen )
            *_out_pnValue = (sal_uInt8)rtl_str_toInt32( aBuf, 16 );
    }
}

// =======================================================================

PointerList::~PointerList()
{
    PBlock* pBlock = pFirst;
    while ( pBlock )
    {
        PBlock* pNextBlock = pBlock->pNext;
        delete pBlock;
        pBlock = pNextBlock;
    }
}

// -----------------------------------------------------------------------

void PointerList::Add( const void* p )
{
    if ( !pFirst )
    {
        pFirst = new PBlock;
        memset( pFirst->aData, 0, PBLOCKCOUNT * sizeof( void* ) );
        pFirst->nCount = 0;
        pFirst->pPrev  = NULL;
        pFirst->pNext  = NULL;
        pLast = pFirst;
    }

    PBlock* pBlock = pFirst;
    while ( pBlock && (pBlock->nCount == PBLOCKCOUNT) )
        pBlock = pBlock->pNext;

    if ( !pBlock )
    {
        pBlock = new PBlock;
        memset( pBlock->aData, 0, PBLOCKCOUNT * sizeof( void* ) );
        pBlock->nCount = 0;
        pBlock->pPrev  = pLast;
        pBlock->pNext  = NULL;
        pLast->pNext   = pBlock;
        pLast          = pBlock;
    }

    sal_uInt16 i = 0;
    while ( pBlock->aData[i] )
        i++;

    pBlock->aData[i] = (void*)p;
    pBlock->nCount++;
    nCount++;
}

// -----------------------------------------------------------------------

sal_Bool PointerList::Remove( const void* p )
{
    if ( !p )
       return sal_False;

    PBlock* pBlock = pFirst;
    while ( pBlock )
    {
        sal_uInt16 i = 0;
        while ( i < PBLOCKCOUNT )
        {
            if ( ((sal_uIntPtr)p) == ((sal_uIntPtr)pBlock->aData[i]) )
            {
                pBlock->aData[i] = NULL;
                pBlock->nCount--;
                nCount--;

                if ( !pBlock->nCount )
                {
                    if ( pBlock->pPrev )
                        pBlock->pPrev->pNext = pBlock->pNext;
                    if ( pBlock->pNext )
                        pBlock->pNext->pPrev = pBlock->pPrev;
                    if ( pBlock == pFirst )
                        pFirst = pBlock->pNext;
                    if ( pBlock == pLast )
                        pLast = pBlock->pPrev;
                    delete pBlock;
                }

                return sal_True;
            }
            i++;
        }

        pBlock = pBlock->pNext;
    }

    return sal_False;
}

// -----------------------------------------------------------------------

const void* PointerList::Get( sal_uIntPtr nPos ) const
{
    if ( nCount <= nPos )
        return NULL;

    PBlock* pBlock = pFirst;
    sal_uIntPtr   nStart = 0;
    while ( pBlock )
    {
        sal_uInt16 i = 0;
        while ( i < PBLOCKCOUNT )
        {
            if ( pBlock->aData[i] )
            {
                nStart++;
                if ( (nStart-1) == nPos )
                    return pBlock->aData[i];
            }

            i++;
        }

        pBlock = pBlock->pNext;
    }

    return NULL;
}

// -----------------------------------------------------------------------

sal_Bool PointerList::IsIn( const void* p ) const
{
    if ( !p )
       return sal_False;

    PBlock* pBlock = pFirst;
    while ( pBlock )
    {
        sal_uInt16 i = 0;
        while ( i < PBLOCKCOUNT )
        {
            if ( ((sal_uIntPtr)p) == ((sal_uIntPtr)pBlock->aData[i]) )
                return sal_True;
            i++;
        }

        pBlock = pBlock->pNext;
    }

    return sal_False;
}


// =======================================================================

static void DbgGetDbgFileName( sal_Char* pStr, sal_Int32 nMaxLen )
{
#if defined( UNX )
    const sal_Char* pName = getenv("DBGSV_INIT");
    if ( !pName )
        pName = ".dbgsv.init";
    strncpy( pStr, pName, nMaxLen );
#elif defined( WNT )
    const sal_Char* pName = getenv("DBGSV_INIT");
    if ( pName )
        strncpy( pStr, pName, nMaxLen );
    else
        GetProfileStringA( "sv", "dbgsv", "dbgsv.ini", pStr, nMaxLen );
#elif defined( OS2 )
    PrfQueryProfileString( HINI_PROFILE, (PSZ)"SV", (PSZ)"DBGSV",
                           "dbgsv.ini", (PSZ)pStr, nMaxLen );
#else
    strncpy( pStr, "dbgsv.ini", nMaxLen );
#endif
    pStr[ nMaxLen - 1 ] = 0;
}

// -----------------------------------------------------------------------

static void DbgGetLogFileName( sal_Char* pStr )
{
#if defined( UNX )
    const sal_Char* pName = getenv("DBGSV_LOG");
    if ( !pName )
        pName = "dbgsv.log";
    strcpy( pStr, pName );
#elif defined( WNT )
    const sal_Char* pName = getenv("DBGSV_LOG");
    if ( pName )
        strcpy( pStr, pName );
    else
        GetProfileStringA( "sv", "dbgsvlog", "dbgsv.log", pStr, 200 );
#elif defined( OS2 )
    PrfQueryProfileString( HINI_PROFILE, (PSZ)"SV", (PSZ)"DBGSVLOG",
                           "dbgsv.log", (PSZ)pStr, 200 );
#else
    strcpy( pStr, "dbgsv.log" );
#endif
}

// -----------------------------------------------------------------------

static void DbgDebugBeep()
{
#if defined( WNT )
    MessageBeep( MB_ICONHAND );
#elif defined( OS2 )
    WinAlarm( HWND_DESKTOP, WA_ERROR );
#endif
}

// -----------------------------------------------------------------------

static DebugData* GetDebugData()
{
    if ( !aDebugData.bInit )
    {
        aDebugData.bInit = sal_True;

        // Default Debug-Namen setzen
        DbgGetLogFileName( aDebugData.aDbgData.aDebugName );

        // DEBUG.INI-File
        sal_Char aBuf[ 4096 ];
        DbgGetDbgFileName( aBuf, sizeof( aBuf ) );
        FILETYPE pIniFile = FileOpen( aBuf, "r" );
        if ( pIniFile != NULL )
        {
            ConfigSection eCurrentSection = eUnknown;

            // no sophisticated algorithm here, assume that the whole file fits into aBuf ...
            sal_uIntPtr nReallyRead = FileRead( aBuf, 1, sizeof( aBuf ) / sizeof( sal_Char ) - 1, pIniFile );
            aBuf[ nReallyRead ] = 0;
            const sal_Char* pLine = aBuf;
            while ( const sal_Char* pNextLine = strstr( pLine, FILE_LINEEND ) )
            {
                size_t nLineLength = pNextLine - pLine;

                if ( lcl_isConfigSection( pLine, nLineLength ) )
                    eCurrentSection = lcl_getSectionFromName( pLine + 1, nLineLength - 2 );

                // elements of the [output] section
                if ( eCurrentSection == eOutput )
                {
                    lcl_tryReadConfigString( pLine, nLineLength, "log_file", aDebugData.aDbgData.aDebugName, sizeof( aDebugData.aDbgData.aDebugName ) );
                    lcl_tryReadConfigBoolean( pLine, nLineLength, "overwrite", &aDebugData.aDbgData.bOverwrite );
                    lcl_tryReadConfigString( pLine, nLineLength, "include", aDebugData.aDbgData.aInclFilter, sizeof( aDebugData.aDbgData.aInclFilter ) );
                    lcl_tryReadConfigString( pLine, nLineLength, "exclude", aDebugData.aDbgData.aExclFilter, sizeof( aDebugData.aDbgData.aExclFilter ) );
                    lcl_tryReadConfigString( pLine, nLineLength, "include_class", aDebugData.aDbgData.aInclClassFilter, sizeof( aDebugData.aDbgData.aInclClassFilter ) );
                    lcl_tryReadConfigString( pLine, nLineLength, "exclude_class", aDebugData.aDbgData.aExclClassFilter, sizeof( aDebugData.aDbgData.aExclClassFilter ) );
                    lcl_tryReadOutputChannel( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTraceOut );
                    lcl_tryReadOutputChannel( pLine, nLineLength, "warning", &aDebugData.aDbgData.nWarningOut );
                    lcl_tryReadOutputChannel( pLine, nLineLength, "error", &aDebugData.aDbgData.nErrorOut );
                    lcl_tryReadConfigBoolean( pLine, nLineLength, "oslhook", &aDebugData.aDbgData.bHookOSLAssert );
                }

                // elements of the [memory] section
                if ( eCurrentSection == eMemory )
                {
                    lcl_tryReadConfigFlag( pLine, nLineLength, "initialize", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_INIT );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "overwrite", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_OVERWRITE );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "overwrite_free", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_OVERWRITEFREE );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "pointer", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_POINTER );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_REPORT );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_TRACE );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "new_and_delete", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_NEWDEL );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "object_test", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_XTOR );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "sys_alloc", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_SYSALLOC );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "leak_report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_MEM_LEAKREPORT );

                    lcl_tryReadHexByte( pLine, nLineLength, "init_byte", &aDebugData.aDbgData.bMemInit );
                    lcl_tryReadHexByte( pLine, nLineLength, "bound_byte", &aDebugData.aDbgData.bMemBound );
                    lcl_tryReadHexByte( pLine, nLineLength, "free_byte", &aDebugData.aDbgData.bMemFree );
                }

                // elements of the [gui] section
                if ( eCurrentSection == eGUI )
                {
                    lcl_tryReadConfigString( pLine, nLineLength, "debug_window_state", aDebugData.aDbgData.aDbgWinState, sizeof( aDebugData.aDbgData.aDbgWinState ) );
                }

                // elements of the [objects] section
                if ( eCurrentSection == eObjects )
                {
                    lcl_tryReadConfigFlag( pLine, nLineLength, "check_this", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_THIS );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "check_function", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_FUNC );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "check_exit", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_EXIT );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "generate_report", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_REPORT );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "trace", &aDebugData.aDbgData.nTestFlags, DBG_TEST_XTOR_TRACE );
                }

                // elements of the [test] section
                if ( eCurrentSection == eTest )
                {
                    lcl_tryReadConfigFlag( pLine, nLineLength, "profiling", &aDebugData.aDbgData.nTestFlags, DBG_TEST_PROFILING );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "resources", &aDebugData.aDbgData.nTestFlags, DBG_TEST_RESOURCE );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "dialog", &aDebugData.aDbgData.nTestFlags, DBG_TEST_DIALOG );
                    lcl_tryReadConfigFlag( pLine, nLineLength, "bold_app_font", &aDebugData.aDbgData.nTestFlags, DBG_TEST_BOLDAPPFONT );
                }

                pLine = pNextLine + strlen( FILE_LINEEND );
            }

            FileClose( pIniFile );
        }
        else
        {
            lcl_matchOutputChannel( getenv( "DBGSV_TRACE_OUT" ), &aDebugData.aDbgData.nTraceOut );
            lcl_matchOutputChannel( getenv( "DBGSV_WARNING_OUT" ), &aDebugData.aDbgData.nWarningOut );
            lcl_matchOutputChannel( getenv( "DBGSV_ERROR_OUT" ), &aDebugData.aDbgData.nErrorOut );
            
        }

        getcwd( aCurPath, sizeof( aCurPath ) );

        // Daten initialisieren
        if ( aDebugData.aDbgData.nTestFlags & DBG_TEST_XTOR )
            aDebugData.pXtorList = new PointerList;
        if ( aDebugData.aDbgData.nTestFlags & DBG_TEST_PROFILING )
            aDebugData.pProfList = new PointerList;
    }

    return &aDebugData;
}

// -----------------------------------------------------------------------

inline DebugData* ImplGetDebugData()
{
    if ( !aDebugData.bInit )
        return GetDebugData();
    else
        return &aDebugData;
}

// -----------------------------------------------------------------------

static FILETYPE ImplDbgInitFile()
{
    static sal_Bool bFileInit = sal_False;

    sal_Char aBuf[4096];
    getcwd( aBuf, sizeof( aBuf ) );
    chdir( aCurPath );

    DebugData*  pData = GetDebugData();
    FILETYPE    pDebugFile;

    if ( !bFileInit )
    {
        bFileInit = sal_True;

        if ( pData->aDbgData.bOverwrite )
            pDebugFile = FileOpen( pData->aDbgData.aDebugName, "w" );
        else
            pDebugFile = FileOpen( pData->aDbgData.aDebugName, "a" );

        if ( pDebugFile )
        {
            time_t  nTime = time( 0 );
            tm*     pTime;
#ifdef UNX
            tm      aTime;
            pTime = localtime_r( &nTime, &aTime );
#else
            pTime = localtime( &nTime );
#endif

            // Header ausgeben
            FilePrintF( pDebugFile, "******************************************************************************%s", FILE_LINEEND );
            FilePrintF( pDebugFile, "%s%s", pData->aDbgData.aDebugName, FILE_LINEEND );
            if ( pTime )
                FilePrintF( pDebugFile, "%s%s", asctime( pTime ), FILE_LINEEND );
        }
    }
    else
        pDebugFile = FileOpen( pData->aDbgData.aDebugName, "a" );

    chdir( aBuf );

    return pDebugFile;
}

// -----------------------------------------------------------------------

static void ImplDbgPrintFile( const sal_Char* pLine )
{
    FILETYPE pDebugFile = ImplDbgInitFile();

    if ( pDebugFile )
    {
        FilePrintF( pDebugFile, "%s%s", pLine, FILE_LINEEND );
        FileClose( pDebugFile );
    }
}

// -----------------------------------------------------------------------

static int ImplStrSearch( const sal_Char* pSearchStr, int nSearchLen,
                          const sal_Char* pStr, int nLen )
{
    int nPos = 0;
    while ( nPos+nSearchLen <= nLen )
    {
        if ( strncmp( pStr+nPos, pSearchStr, nSearchLen ) == 0 )
            return 1;
        nPos++;
    }

    return 0;
}

// -----------------------------------------------------------------------

static int ImplDbgFilter( const sal_Char* pFilter, const sal_Char* pMsg,
                          int bEmpty )
{
    int nStrLen = strlen( pFilter );
    if ( !nStrLen )
        return bEmpty;

    int nMsgLen = strlen( pMsg );
    const sal_Char* pTok = pFilter;
    int         nTok = 0;
    while ( pTok[nTok] )
    {
        if ( pTok[nTok] == ';' )
        {
            if ( nTok && ImplStrSearch( pTok, nTok, pMsg, nMsgLen ) )
                return sal_True;

            pTok += nTok+1;
            nTok = 0;
        }

        nTok++;
    }

    if ( nTok && ImplStrSearch( pTok, nTok, pMsg, nMsgLen ) )
        return sal_True;
    else
        return sal_False;
}

// -----------------------------------------------------------------------

extern "C"
void SAL_CALL dbg_printOslDebugMessage( const sal_Char * pszFileName, sal_Int32 nLine, const sal_Char * pszMessage )
{
    DbgOut( pszMessage ? pszMessage : "assertion failed!", DBG_OUT_ERROR, pszFileName, (sal_uInt16)nLine );
}

// -----------------------------------------------------------------------

static void DebugInit()
{
    bDbgImplInMain = sal_True;
    ImplDbgInitLock();

    DebugData* pData = GetDebugData();
    if( pData->aDbgData.bHookOSLAssert && ! pData->bOslIsHooked )
    {
        pData->pOldDebugMessageFunc = osl_setDetailedDebugMessageFunc( &dbg_printOslDebugMessage );
        pData->bOslIsHooked = true;
    }
}

// -----------------------------------------------------------------------

static void DebugDeInit()
{
    DebugData*  pData = GetDebugData();
    sal_uIntPtr       i;
    sal_uIntPtr       nCount;
    sal_uIntPtr       nOldOut;

    if( pData->bOslIsHooked )
    {
        osl_setDetailedDebugMessageFunc( pData->pOldDebugMessageFunc );
        pData->bOslIsHooked = sal_False;
    }

    // Statistik-Ausgaben immer in File
    nOldOut = pData->aDbgData.nTraceOut;
    pData->aDbgData.nTraceOut = DBG_OUT_FILE;

    // Xtor-Liste ausgeben
    if ( pData->pXtorList && pData->pXtorList->Count() &&
         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_REPORT) )
    {
        DbgOutf( "------------------------------------------------------------------------------" );
        DbgOutf( "Object Report" );
        DbgOutf( "------------------------------------------------------------------------------" );
        DbgOutf( "%-27s : %-9s : %-9s : %-7s : %-3s : %-6s :",
                 "XTor-List", "Ctor", "Dtor", "MaxInst", "St.", "Diff." );
        DbgOutf( "----------------------------:-----------:-----------:---------:----:---------:" );
        for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ )
        {
            XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i );
            if ( pXtorData->bTest )
            {
                // Static-Objekte dazurechnen
                pXtorData->nDtorCalls += pXtorData->nStatics;
                if ( pXtorData->nStatics && (pXtorData->nDtorCalls > pXtorData->nCtorCalls) )
                    pXtorData->nDtorCalls = pXtorData->nCtorCalls;
                DbgOutf( "%-27s : %9lu : %9lu : %7lu : %3lu : %4lu %-1s :",
                         pXtorData->aName, pXtorData->nCtorCalls, pXtorData->nDtorCalls,
                         pXtorData->nMaxCount, pXtorData->nStatics,
                         pXtorData->nCtorCalls - pXtorData->nDtorCalls,
                         (pXtorData->nCtorCalls - pXtorData->nDtorCalls) ? "!" : " " );
            }
        }
        DbgOutf( "==============================================================================" );
    }

    // Aufraeumen
    if ( pData->pXtorList )
    {
        for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ )
        {
            XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i );
            delete pXtorData;
        }
        delete pData->pXtorList;
        pData->pXtorList = NULL;
    }

    // Alles auf sal_False setzen, damit globale Variablen nicht das
    // System zum Abstuerzen bringt. Dabei muessen aber die
    // Memory-Flags erhalten bleiben, da sonst new/delete in globalen
    // Variablen abstuerzen, da die Pointeranpassung dann nicht mehr richtig
    // funktioniert
    pData->aDbgData.nTraceOut   = nOldOut;
    pData->aDbgData.nTestFlags &= (DBG_TEST_MEM | DBG_TEST_PROFILING);
    pData->aDbgPrintUserChannels.clear();
    pData->pDbgPrintTestTool    = NULL;
    pData->pDbgPrintWindow      = NULL;
    pData->pOldDebugMessageFunc = NULL;
    ImplDbgDeInitLock();
}

// -----------------------------------------------------------------------

static void DebugGlobalDeInit()
{
    DebugData*  pData = GetDebugData();
    sal_uIntPtr       i;
    sal_uIntPtr       nCount;
    sal_uIntPtr       nOldOut;

    // Statistik-Ausgaben immer in File
    nOldOut = pData->aDbgData.nTraceOut;
    pData->aDbgData.nTraceOut = DBG_OUT_FILE;

    // Profileliste ausgeben
    if ( pData->pProfList && pData->pProfList->Count() )
    {
        DbgOutf( "------------------------------------------------------------------------------" );
        DbgOutf( "Profiling Report" );
        DbgOutf( "------------------------------------------------------------------------------" );
        DbgOutf( "%-25s : %-9s : %-6s : %-6s : %-6s : %-9s :",
                 "Prof-List (ms)", "Time", "Min", "Max", "Ave", "Count" );
        DbgOutf( "--------------------------:-----------:--------:--------:--------:-----------:" );
        for( i = 0, nCount = pData->pProfList->Count(); i < nCount; i++ )
        {
            ProfType* pProfData = (ProfType*)pData->pProfList->Get( i );
            sal_uIntPtr nAve = pProfData->nTime / pProfData->nCount;
            DbgOutf( "%-25s : %9lu : %6lu : %6lu : %6lu : %9lu :",
                     pProfData->aName, pProfData->nTime,
                     pProfData->nMinTime, pProfData->nMaxTime, nAve,
                     pProfData->nCount );
        }
        DbgOutf( "==============================================================================" );
    }

    // Aufraeumen
    if ( pData->pProfList )
    {
        for( i = 0, nCount = pData->pProfList->Count(); i < nCount; i++ )
        {
            ProfType* pProfData = (ProfType*)pData->pProfList->Get( i );
            delete pProfData;
        }
        delete pData->pProfList;
        pData->pProfList = NULL;
    }

#ifdef SV_MEMMGR
    DbgImpCheckMemoryDeInit();
#endif

    // Profiling-Flags ausschalten
    pData->aDbgData.nTraceOut   = nOldOut;
    pData->aDbgData.nTestFlags &= ~DBG_TEST_PROFILING;
}

// -----------------------------------------------------------------------

void ImpDbgOutfBuf( sal_Char* pBuf, const sal_Char* pFStr, ... )
{
    va_list pList;

    va_start( pList, pFStr );
    sal_Char aBuf[DBG_BUF_MAXLEN];
    vsprintf( aBuf, pFStr, pList );
    va_end( pList );

    strcat( pBuf, aBuf );
    strcat( pBuf, "\n" );
}

// -----------------------------------------------------------------------

static void DebugXTorInfo( sal_Char* pBuf )
{
    DebugData*  pData = GetDebugData();
    sal_uIntPtr       i;
    sal_uIntPtr       nCount;

    // Xtor-Liste ausgeben
    if ( pData->pXtorList && pData->pXtorList->Count() &&
         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_REPORT) )
    {
        ImpDbgOutfBuf( pBuf, "------------------------------------------------------------------------------" );
        ImpDbgOutfBuf( pBuf, "Object Report" );
        ImpDbgOutfBuf( pBuf, "------------------------------------------------------------------------------" );
        ImpDbgOutfBuf( pBuf, "%-27s : %-9s : %-9s : %-7s : %-3s : %-6s :",
                       "XTor-List", "Ctor", "Dtor", "MaxInst", "St.", "Diff." );
        ImpDbgOutfBuf( pBuf, "----------------------------:-----------:-----------:---------:----:---------:" );
        for( i = 0, nCount = pData->pXtorList->Count(); i < nCount; i++ )
        {
            XtorType* pXtorData = (XtorType*)pData->pXtorList->Get( i );
            if ( pXtorData->bTest )
            {
                ImpDbgOutfBuf( pBuf, "%-27s : %9lu : %9lu : %7lu : %3lu : %6lu :",
                               pXtorData->aName, pXtorData->nCtorCalls, pXtorData->nDtorCalls,
                               pXtorData->nMaxCount, pXtorData->nStatics,
                               pXtorData->nCtorCalls - pXtorData->nDtorCalls );
            }
        }
        ImpDbgOutfBuf( pBuf, "==============================================================================" );
        ImpDbgOutfBuf( pBuf, "" );
    }
}

// -----------------------------------------------------------------------
sal_Bool ImplDbgFilterMessage( const sal_Char* pMsg )
{
    DebugData*  pData = GetDebugData();
    if ( !ImplDbgFilter( pData->aDbgData.aInclFilter, pMsg, sal_True ) )
        return sal_True;
    if ( ImplDbgFilter( pData->aDbgData.aExclFilter, pMsg, sal_False ) )
        return sal_True;
    return sal_False;
}

// -----------------------------------------------------------------------

void* DbgFunc( sal_uInt16 nAction, void* pParam )
{
    DebugData* pDebugData = ImplGetDebugData();

    if ( nAction == DBG_FUNC_GETDATA )
        return (void*)&(pDebugData->aDbgData);
    else if ( nAction == DBG_FUNC_GETPRINTMSGBOX )
        return (void*)(long)(pDebugData->pDbgPrintMsgBox);
    else if ( nAction == DBG_FUNC_FILTERMESSAGE )
        if ( ImplDbgFilterMessage( (const sal_Char*) pParam ) )
            return (void*) -1;
        else
            return (void*) 0;   // aka NULL
    else

    {
        switch ( nAction )
        {
            case DBG_FUNC_DEBUGSTART:
                DebugInit();
                break;

            case DBG_FUNC_DEBUGEND:
                DebugDeInit();
                break;

            case DBG_FUNC_GLOBALDEBUGEND:
                DebugGlobalDeInit();
                break;

            case DBG_FUNC_SETPRINTMSGBOX:
                pDebugData->pDbgPrintMsgBox = (DbgPrintLine)(long)pParam;
                break;

            case DBG_FUNC_SETPRINTWINDOW:
                pDebugData->pDbgPrintWindow = (DbgPrintLine)(long)pParam;
                break;

            case DBG_FUNC_SETPRINTTESTTOOL:
                pDebugData->pDbgPrintTestTool = (DbgPrintLine)(long)pParam;
                break;

            case DBG_FUNC_SET_ABORT:
                pDebugData->pDbgAbort = (DbgPrintLine)(long)pParam;
                break;

            case DBG_FUNC_SAVEDATA:
                {
                const DbgData* pData = static_cast< const DbgData* >( pParam );
                
                sal_Char aBuf[ 4096 ];
                DbgGetDbgFileName( aBuf, sizeof( aBuf ) );
                FILETYPE pIniFile = FileOpen( aBuf, "w" );
                if ( pIniFile == NULL )
                    break;

                lcl_startSection( pIniFile, eOutput );
                lcl_writeConfigString( pIniFile, "log_file", pData->aDebugName );
                lcl_writeConfigBoolean( pIniFile, "overwrite", pData->bOverwrite );
                lcl_writeConfigString( pIniFile, "include", pData->aInclFilter );
                lcl_writeConfigString( pIniFile, "exclude", pData->aExclFilter );
                lcl_writeConfigString( pIniFile, "include_class", pData->aInclClassFilter );
                lcl_writeConfigString( pIniFile, "exclude_class", pData->aExclClassFilter );
                lcl_writeConfigOutChannel( pIniFile, "trace", pData->nTraceOut );
                lcl_writeConfigOutChannel( pIniFile, "warning", pData->nWarningOut );
                lcl_writeConfigOutChannel( pIniFile, "error", pData->nErrorOut );
                lcl_writeConfigBoolean( pIniFile, "oslhook", pData->bHookOSLAssert );

                lcl_lineFeed( pIniFile );
                lcl_startSection( pIniFile, eMemory );
                lcl_writeConfigFlag( pIniFile, "initialize", pData->nTestFlags, DBG_TEST_MEM_INIT );
                lcl_writeConfigFlag( pIniFile, "overwrite", pData->nTestFlags, DBG_TEST_MEM_OVERWRITE );
                lcl_writeConfigFlag( pIniFile, "overwrite_free", pData->nTestFlags, DBG_TEST_MEM_OVERWRITEFREE );
                lcl_writeConfigFlag( pIniFile, "pointer", pData->nTestFlags, DBG_TEST_MEM_POINTER );
                lcl_writeConfigFlag( pIniFile, "report", pData->nTestFlags, DBG_TEST_MEM_REPORT );
                lcl_writeConfigFlag( pIniFile, "trace", pData->nTestFlags, DBG_TEST_MEM_TRACE );
                lcl_writeConfigFlag( pIniFile, "new_and_delete", pData->nTestFlags, DBG_TEST_MEM_NEWDEL );
                lcl_writeConfigFlag( pIniFile, "object_test", pData->nTestFlags, DBG_TEST_MEM_XTOR );
                lcl_writeConfigFlag( pIniFile, "sys_alloc", pData->nTestFlags, DBG_TEST_MEM_SYSALLOC );
                lcl_writeConfigFlag( pIniFile, "leak_report", pData->nTestFlags, DBG_TEST_MEM_LEAKREPORT );

                lcl_lineFeed( pIniFile );
                lcl_writeHexByte( pIniFile, "init_byte", pData->bMemInit );
                lcl_writeHexByte( pIniFile, "bound_byte", pData->bMemBound );
                lcl_writeHexByte( pIniFile, "free_byte", pData->bMemFree );

                lcl_lineFeed( pIniFile );
                lcl_startSection( pIniFile, eGUI );
                lcl_writeConfigString( pIniFile, "debug_window_state", pData->aDbgWinState );

                lcl_lineFeed( pIniFile );
                lcl_startSection( pIniFile, eObjects );
                lcl_writeConfigFlag( pIniFile, "check_this", pData->nTestFlags, DBG_TEST_XTOR_THIS );
                lcl_writeConfigFlag( pIniFile, "check_function", pData->nTestFlags, DBG_TEST_XTOR_FUNC );
                lcl_writeConfigFlag( pIniFile, "check_exit", pData->nTestFlags, DBG_TEST_XTOR_EXIT );
                lcl_writeConfigFlag( pIniFile, "generate_report", pData->nTestFlags, DBG_TEST_XTOR_REPORT );
                lcl_writeConfigFlag( pIniFile, "trace", pData->nTestFlags, DBG_TEST_XTOR_TRACE );

                lcl_lineFeed( pIniFile );
                lcl_startSection( pIniFile, eTest );
                lcl_writeConfigFlag( pIniFile, "profiling", pData->nTestFlags, DBG_TEST_PROFILING );
                lcl_writeConfigFlag( pIniFile, "resources", pData->nTestFlags, DBG_TEST_RESOURCE );
                lcl_writeConfigFlag( pIniFile, "dialog", pData->nTestFlags, DBG_TEST_DIALOG );
                lcl_writeConfigFlag( pIniFile, "bold_app_font", pData->nTestFlags, DBG_TEST_BOLDAPPFONT );

                FileClose( pIniFile );
                }
                break;

            case DBG_FUNC_MEMTEST:
#ifdef SV_MEMMGR
                DbgImpCheckMemory( pParam );
#endif
                break;

            case DBG_FUNC_XTORINFO:
                DebugXTorInfo( (sal_Char*)pParam );
                break;

            case DBG_FUNC_MEMINFO:
#ifdef SV_MEMMGR
                DbgImpMemoryInfo( (sal_Char*)pParam );
#endif
                break;

            case DBG_FUNC_COREDUMP:
                ImplCoreDump();
                break;

            case DBG_FUNC_ALLERROROUT:
                return (void*)(sal_uIntPtr)sal_True;

            case DBG_FUNC_SETTESTSOLARMUTEX:
                pDebugData->pDbgTestSolarMutex = (DbgTestSolarMutexProc)(long)pParam;
                break;

            case DBG_FUNC_TESTSOLARMUTEX:
                if ( pDebugData->pDbgTestSolarMutex )
                    pDebugData->pDbgTestSolarMutex();
                break;

            case DBG_FUNC_PRINTFILE:
                ImplDbgPrintFile( (const sal_Char*)pParam );
                break;
            case DBG_FUNC_UPDATEOSLHOOK:
            {
                const DbgData* pData = static_cast< const DbgData* >( pParam );
                pDebugData->aDbgData.bHookOSLAssert = pData->bHookOSLAssert;
                if( pDebugData->bOslIsHooked && ! pData->bHookOSLAssert )
                {
                    osl_setDetailedDebugMessageFunc( pDebugData->pOldDebugMessageFunc );
                    pDebugData->bOslIsHooked = sal_False;
                }
                else if( ! pDebugData->bOslIsHooked && pData->bHookOSLAssert )
                {
                    pDebugData->pOldDebugMessageFunc = osl_setDetailedDebugMessageFunc( &dbg_printOslDebugMessage );
                    pDebugData->bOslIsHooked = sal_True;
                }
            }
            break;
       }

        return NULL;
    }
}

// -----------------------------------------------------------------------

DbgChannelId DbgRegisterUserChannel( DbgPrintLine pProc )
{
    DebugData* pData = ImplGetDebugData();
    pData->aDbgPrintUserChannels.push_back( pProc );
    return (DbgChannelId)( pData->aDbgPrintUserChannels.size() - 1 + DBG_OUT_USER_CHANNEL_0 );
}

// -----------------------------------------------------------------------

void DbgProf( sal_uInt16 nAction, DbgDataType* pDbgData )
{
    // Ueberhaupt Profiling-Test an
    DebugData* pData = ImplGetDebugData();

    if ( !(pData->aDbgData.nTestFlags & DBG_TEST_PROFILING) )
        return;

    sal_Char    aBuf[DBG_BUF_MAXLEN];
    ProfType*   pProfData = (ProfType*)pDbgData->pData;
    sal_uIntPtr       nTime;
    if ( (nAction != DBG_PROF_START) && !pProfData )
    {
        strcpy( aBuf, DbgError_ProfEnd1 );
        strcat( aBuf, pDbgData->pName );
        DbgError( aBuf );
        return;
    }

    switch ( nAction )
    {
        case DBG_PROF_START:
            if ( !pDbgData->pData )
            {
                pDbgData->pData = (void*)new ProfType;
                pProfData = (ProfType*)pDbgData->pData;
                strncpy( pProfData->aName, pDbgData->pName, DBG_MAXNAME );
                pProfData->aName[DBG_MAXNAME] = '\0';
                pProfData->nCount           = 0;
                pProfData->nTime            = 0;
                pProfData->nMinTime         = 0xFFFFFFFF;
                pProfData->nMaxTime         = 0;
                pProfData->nStart           = 0xFFFFFFFF;
                pProfData->nContinueTime    = 0;
                pProfData->nContinueStart   = 0xFFFFFFFF;
                pData->pProfList->Add( (void*)pProfData );
            }

            if ( pProfData->nStart == 0xFFFFFFFF )
            {
                pProfData->nStart = ImplGetPerfTime();
                pProfData->nCount++;
            }
            break;

        case DBG_PROF_STOP:
            nTime = ImplGetPerfTime();

            if ( pProfData->nStart == 0xFFFFFFFF )
            {
                DbgError( DbgError_ProfEnd1 );
                return;
            }

            if ( pProfData->nContinueStart != 0xFFFFFFFF )
            {
                pProfData->nContinueTime += ImplGetPerfTime() - pProfData->nContinueStart;
                pProfData->nContinueStart = 0xFFFFFFFF;
            }

            nTime -= pProfData->nStart;
            nTime -= pProfData->nContinueTime;

            if ( nTime < pProfData->nMinTime )
                pProfData->nMinTime = nTime;

            if ( nTime > pProfData->nMaxTime )
                pProfData->nMaxTime = nTime;

            pProfData->nTime += nTime;

            pProfData->nStart         = 0xFFFFFFFF;
            pProfData->nContinueTime  = 0;
            pProfData->nContinueStart = 0xFFFFFFFF;
            break;

        case DBG_PROF_CONTINUE:
            if ( pProfData->nContinueStart != 0xFFFFFFFF )
            {
                pProfData->nContinueTime += ImplGetPerfTime() - pProfData->nContinueStart;
                pProfData->nContinueStart = 0xFFFFFFFF;
            }
            break;

        case DBG_PROF_PAUSE:
            if ( pProfData->nContinueStart == 0xFFFFFFFF )
                pProfData->nContinueStart = ImplGetPerfTime();
            break;
    }
}

// -----------------------------------------------------------------------

void DbgXtor( DbgDataType* pDbgData, sal_uInt16 nAction, const void* pThis,
              DbgUsr fDbgUsr )
{
    DebugData* pData = ImplGetDebugData();

    // Verbindung zu Debug-Memory-Manager testen
#ifdef SV_MEMMGR
    if ( pData->aDbgData.nTestFlags & DBG_TEST_MEM_XTOR )
        DbgImpCheckMemory();
#endif

    // Schnell-Test
    if ( !(pData->aDbgData.nTestFlags & DBG_TEST_XTOR) )
        return;

    XtorType* pXtorData = (XtorType*)pDbgData->pData;
    if ( !pXtorData )
    {
        pDbgData->pData = (void*)new XtorType;
        pXtorData = (XtorType*)pDbgData->pData;
        strncpy( pXtorData->aName, pDbgData->pName, DBG_MAXNAME );
        pXtorData->aName[DBG_MAXNAME] = '\0';
        pXtorData->nCtorCalls   = 0;
        pXtorData->nDtorCalls   = 0;
        pXtorData->nMaxCount    = 0;
        pXtorData->nStatics     = 0;
        pXtorData->bTest        = sal_True;
        pData->pXtorList->Add( (void*)pXtorData );

        if ( !ImplDbgFilter( pData->aDbgData.aInclClassFilter, pXtorData->aName, sal_True ) )
            pXtorData->bTest = sal_False;
        if ( ImplDbgFilter( pData->aDbgData.aExclClassFilter, pXtorData->aName, sal_False ) )
            pXtorData->bTest = sal_False;
    }
    if ( !pXtorData->bTest )
        return;

    sal_Char    aBuf[DBG_BUF_MAXLEN];
    sal_uInt16      nAct = nAction & ~DBG_XTOR_DTOROBJ;

    // Trace (Enter)
    if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_TRACE) &&
         !(nAction & DBG_XTOR_DTOROBJ) )
    {
        if ( nAct != DBG_XTOR_CHKOBJ )
        {
            if ( nAct == DBG_XTOR_CTOR )
                strcpy( aBuf, DbgTrace_EnterCtor );
            else if ( nAct == DBG_XTOR_DTOR )
                strcpy( aBuf, DbgTrace_EnterDtor );
            else
                strcpy( aBuf, DbgTrace_EnterMeth );
            strcat( aBuf, pDbgData->pName );
            DbgTrace( aBuf );
        }
    }

    // Sind noch Xtor-Tests als Trace an
    if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXTRA )
    {
        // DBG_CTOR-Aufruf vor allen anderen DBG_XTOR-Aufrufen
        if ( ((nAction & ~DBG_XTOR_DTOROBJ) != DBG_XTOR_CTOR) && !pDbgData->pData )
        {
            strcpy( aBuf, DbgError_Xtor1 );
            strcat( aBuf, pDbgData->pName );
            DbgError( aBuf );
            return;
        }

        // Testen, ob This-Pointer gueltig
        if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS )
        {
            if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) ||
                 !(nAction & DBG_XTOR_DTOROBJ) )
            {
                // This-Pointer == NULL
                if ( !pThis )
                {
                    strcpy( aBuf, DbgError_CtorDtor1 );
                    strcat( aBuf, pDbgData->pName );
                    DbgError( aBuf );
                    return;
                }

                if ( (nAction & ~DBG_XTOR_DTOROBJ) != DBG_XTOR_CTOR )
                {
                    if ( !pXtorData->aThisList.IsIn( pThis ) )
                    {
                        sprintf( aBuf, DbgError_CtorDtor2, pThis );
                        strcat( aBuf, pDbgData->pName );
                        DbgError( aBuf );
                    }
                }
            }
        }

        // Function-Test durchfuehren und Verwaltungsdaten updaten
        const sal_Char* pMsg = NULL;
        switch ( nAction & ~DBG_XTOR_DTOROBJ )
        {
            case DBG_XTOR_CTOR:
                if ( nAction & DBG_XTOR_DTOROBJ )
                {
                    if ( fDbgUsr &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) )
                        pMsg = fDbgUsr( pThis );
                }
                else
                {
                    pXtorData->nCtorCalls++;
                    if ( !bDbgImplInMain )
                        pXtorData->nStatics++;
                    if ( (pXtorData->nCtorCalls-pXtorData->nDtorCalls) > pXtorData->nMaxCount )
                        pXtorData->nMaxCount = pXtorData->nCtorCalls - pXtorData->nDtorCalls;

                    if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS )
                        pXtorData->aThisList.Add( pThis );
                }
                break;

            case DBG_XTOR_DTOR:
                if ( nAction & DBG_XTOR_DTOROBJ )
                {
                    pXtorData->nDtorCalls++;
                    if ( pData->aDbgData.nTestFlags & DBG_TEST_XTOR_THIS )
                        pXtorData->aThisList.Remove( pThis );
                }
                else
                {
                    if ( fDbgUsr &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) )
                        pMsg = fDbgUsr( pThis );
                }
                break;

            case DBG_XTOR_CHKTHIS:
            case DBG_XTOR_CHKOBJ:
                if ( nAction & DBG_XTOR_DTOROBJ )
                {
                    if ( fDbgUsr &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_EXIT) &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) )
                        pMsg = fDbgUsr( pThis );
                }
                else
                {
                    if ( fDbgUsr &&
                         (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_FUNC) )
                        pMsg = fDbgUsr( pThis );
                }
                break;
        }

        // Gegebenenfalls Fehlermeldung ausgeben
        if ( pMsg )
        {
            sprintf( aBuf, DbgError_CtorDtor3, pThis );
            strcat( aBuf, pDbgData->pName );
            strcat( aBuf, ": \n" );
            strcat( aBuf, pMsg );
            DbgError( aBuf );
        }
    }

    // Trace (Leave)
    if ( (pData->aDbgData.nTestFlags & DBG_TEST_XTOR_TRACE) &&
         (nAction & DBG_XTOR_DTOROBJ) )
    {
        if ( nAct != DBG_XTOR_CHKOBJ )
        {
            if ( nAct == DBG_XTOR_CTOR )
                strcpy( aBuf, DbgTrace_LeaveCtor );
            else if ( nAct == DBG_XTOR_DTOR )
                strcpy( aBuf, DbgTrace_LeaveDtor );
            else
                strcpy( aBuf, DbgTrace_LeaveMeth );
            strcat( aBuf, pDbgData->pName );
            DbgTrace( aBuf );
        }
    }
}

// -----------------------------------------------------------------------

void DbgOut( const sal_Char* pMsg, sal_uInt16 nDbgOut, const sal_Char* pFile, sal_uInt16 nLine )
{
    static sal_Bool bIn = sal_False;
    if ( bIn )
        return;
    bIn = sal_True;

    DebugData*  pData = GetDebugData();
    sal_Char const *   pStr;
    sal_uIntPtr       nOut;
    int         nBufLen = 0;

    if ( nDbgOut == DBG_OUT_ERROR )
    {
        nOut = pData->aDbgData.nErrorOut;
        pStr = "Error: ";
        if ( pData->aDbgData.nErrorOut == DBG_OUT_FILE )
            DbgDebugBeep();
    }
    else if ( nDbgOut == DBG_OUT_WARNING )
    {
        nOut = pData->aDbgData.nWarningOut;
        pStr = "Warning: ";
    }
    else
    {
        nOut = pData->aDbgData.nTraceOut;
        pStr = NULL;
    }

    if ( nOut == DBG_OUT_NULL )
    {
        bIn = sal_False;
        return;
    }

    if ( ImplDbgFilterMessage( pMsg ) )
    {
        bIn = sal_False;
        return;
    }

    ImplDbgLock();

    sal_Char aBufOut[DBG_BUF_MAXLEN];
    if ( pStr )
    {
        strcpy( aBufOut, pStr );
        nBufLen = strlen( pStr );
    }
    else
        aBufOut[0] = '\0';

    int nMsgLen = strlen( pMsg );
    if ( nBufLen+nMsgLen > DBG_BUF_MAXLEN )
    {
        int nCopyLen = DBG_BUF_MAXLEN-nBufLen-3;
        strncpy( &(aBufOut[nBufLen]), pMsg, nCopyLen );
        strcpy( &(aBufOut[nBufLen+nCopyLen]), "..." );
    }
    else
        strcpy( &(aBufOut[nBufLen]), pMsg );

    if ( pFile && nLine && (nBufLen+nMsgLen < DBG_BUF_MAXLEN) )
    {
        if ( nOut == DBG_OUT_MSGBOX )
            strcat( aBufOut, "\n" );
        else
            strcat( aBufOut, " " );
        strcat( aBufOut, "From File " );
        strcat( aBufOut, pFile );
        strcat( aBufOut, " at Line " );

        // Line in String umwandeln und dranhaengen
        sal_Char    aLine[9];
        sal_Char*   pLine = &aLine[7];
        sal_uInt16      i;
        memset( aLine, 0, sizeof( aLine ) );
        do
        {
            i = nLine % 10;
            pLine--;
            *(pLine) = (sal_Char)i + 48;
            nLine /= 10;
        }
        while ( nLine );
        strcat( aBufOut, pLine );
    }

    if ( ( nOut >= DBG_OUT_USER_CHANNEL_0 ) && ( nOut - DBG_OUT_USER_CHANNEL_0 < pData->aDbgPrintUserChannels.size() ) )
    {
        DbgPrintLine pPrinter = pData->aDbgPrintUserChannels[ nOut - DBG_OUT_USER_CHANNEL_0 ];
        if ( pPrinter )
            pPrinter( aBufOut );
        else
            nOut = DBG_OUT_DEBUGGER;
    }

    if ( nOut == DBG_OUT_ABORT )
    {
        if ( pData->pDbgAbort != NULL )
            pData->pDbgAbort( aBufOut );
        abort();
    }

    if ( nOut == DBG_OUT_DEBUGGER )
    {
        if ( !ImplActivateDebugger( aBufOut ) )
            nOut = DBG_OUT_TESTTOOL;
    }

    if ( nOut == DBG_OUT_TESTTOOL )
    {
        if ( pData->pDbgPrintTestTool )
            pData->pDbgPrintTestTool( aBufOut );
        else
            nOut = DBG_OUT_MSGBOX;
    }

    if ( nOut == DBG_OUT_MSGBOX )
    {
        if ( pData->pDbgPrintMsgBox )
            pData->pDbgPrintMsgBox( aBufOut );
        else
            nOut = DBG_OUT_WINDOW;
    }

    if ( nOut == DBG_OUT_WINDOW )
    {
        if ( pData->pDbgPrintWindow )
            pData->pDbgPrintWindow( aBufOut );
        else
            nOut = DBG_OUT_FILE;
    }

    switch ( nOut )
    {
    case DBG_OUT_SHELL:
        DbgPrintShell( aBufOut );
        break;
    case DBG_OUT_FILE:
        ImplDbgPrintFile( aBufOut );
        break;
    }

    ImplDbgUnlock();

    bIn = sal_False;
}

void DbgPrintShell(char const * message) {
    fprintf(stderr, "%s\n", message);
#if defined WNT
    OutputDebugStringA(message);
#endif
}

// -----------------------------------------------------------------------

void DbgOutTypef( sal_uInt16 nDbgOut, const sal_Char* pFStr, ... )
{
    va_list pList;

    va_start( pList, pFStr );
    sal_Char aBuf[DBG_BUF_MAXLEN];
    vsprintf( aBuf, pFStr, pList );
    va_end( pList );

    DbgOut( aBuf, nDbgOut );
}

// -----------------------------------------------------------------------

void DbgOutf( const sal_Char* pFStr, ... )
{
    va_list pList;

    va_start( pList, pFStr );
    sal_Char aBuf[DBG_BUF_MAXLEN];
    vsprintf( aBuf, pFStr, pList );
    va_end( pList );

    DbgOut( aBuf );
}

// =======================================================================

#else

void* DbgFunc( sal_uInt16, void* ) { return NULL; }

void DbgProf( sal_uInt16, DbgDataType* ) {}
void DbgXtor( DbgDataType*, sal_uInt16, const void*, DbgUsr ) {}

void DbgOut( const sal_Char*, sal_uInt16, const sal_Char*, sal_uInt16 ) {}
void DbgOutTypef( sal_uInt16, const sal_Char*, ... ) {}
void DbgOutf( const sal_Char*, ... ) {}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
