/*
 * $Id: dbgentry.c 12982 2009-11-22 23:46:57Z vszakats $
 */

/*
 * xHarbour Project source code:
 * Debugger entry routine
 *
 * Copyright 2005 Phil Krylov <phil a t newstar.rinet.ru>
 * www - http://www.xharbour.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, xHarbour license gives permission for
 * additional uses of the text contained in its release of xHarbour.
 *
 * The exception is that, if you link the xHarbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the xHarbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released with this xHarbour
 * explicit exception.  If you add/copy code from other sources,
 * as the General Public License permits, the above exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for xHarbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbapidbg.h"
#include "hbapiitm.h"
#include "hbapicls.h"
#include "hbapirdd.h"
#include "hbstack.h"
#include "hbvm.h"

#include "hbdebug.ch"
#include "hbmacro.ch"

/* dummy function declaration */
static BOOL hb_clsSetScope( BOOL fScope ) { return fScope; }

#define HB_DBGINFO_DISABLE    ( ( HB_DEBUGINFO * ) ( HB_PTRDIFF ) 0x01 )

#if defined( HB_OS_UNIX )
#define FILENAME_EQUAL(s1, s2) ( !strcmp( (s1), (s2) ) )
#else
#define FILENAME_EQUAL(s1, s2) ( !hb_stricmp( (s1), (s2) ) )
#endif

#define ARRAY_ADD( type, array, length ) \
   ( ( ++length == 1 ) ? ( array = ( type * ) hb_xgrab( sizeof( type ) ) ) : \
      ( ( array = ( type * ) hb_xrealloc( array, sizeof( type ) * length ) ) + \
          length - 1 ) )

#define ARRAY_DEL( type, array, length, index ) \
   do { \
      if ( !--length ) \
         hb_xfree( array ); \
      else if( index < length ) \
         memmove( array + index, array + index + 1, sizeof( type ) * ( length - index ) ); \
   } while( 0 )

typedef struct
{
   char *szModule;
   int nLine;
   char *szFunction;
} HB_BREAKPOINT;

typedef struct
{
   int nIndex;
   PHB_ITEM xValue;
} HB_TRACEPOINT;

typedef struct
{
   char *szName;
   char cType;
   union {
      int num;
      PHB_ITEM ptr;
   } frame;
   int nIndex;
} HB_VARINFO;

typedef struct
{
   char *szExpr;
   PHB_ITEM pBlock;
   int nVars;
   char **aVars;
   HB_VARINFO *aScopes;
} HB_WATCHPOINT;

typedef struct
{
   char *szModule;
   char *szFunction;
   int nLine;
   int nProcLevel;
   int nLocals;
   HB_VARINFO *aLocals;
   int nStatics;
   HB_VARINFO *aStatics;
} HB_CALLSTACKINFO;

typedef struct
{
   char *szModule;
   int nStatics;
   HB_VARINFO *aStatics;
   int nGlobals;
   HB_VARINFO *aGlobals;
   int nExternGlobals;
   HB_VARINFO *aExternGlobals;
} HB_MODULEINFO;

typedef struct
{
   BOOL bQuit;
   BOOL bGo;
   BOOL bInside;
   int nBreakPoints;
   HB_BREAKPOINT *aBreak;
   int nTracePoints;
   HB_TRACEPOINT *aTrace;
   int nWatchPoints;
   HB_WATCHPOINT *aWatch;
   BOOL bTraceOver;
   int nTraceLevel;
   BOOL bNextRoutine;
   BOOL bCodeBlock;
   BOOL bToCursor;
   int nToCursorLine;
   char *szToCursorModule;
   int nProcLevel;
   int nCallStackLen;
   HB_CALLSTACKINFO *aCallStack;
   int nModules;
   HB_MODULEINFO *aModules;
   BOOL bCBTrace;
   BOOL ( *pFunInvoke )( void );
   BOOL bInitGlobals;
   BOOL bInitStatics;
   BOOL bInitLines;
   PHB_ITEM pStopLines;
} HB_DEBUGINFO;


static PHB_ITEM hb_dbgActivateBreakArray( HB_DEBUGINFO *info );
static PHB_ITEM hb_dbgActivateModuleArray( HB_DEBUGINFO *info );
static PHB_ITEM hb_dbgActivateVarArray( int nVars, HB_VARINFO *aVars );
static void     hb_dbgAddLocal( HB_DEBUGINFO *info, const char *szName, int nIndex, int nFrame );
static void     hb_dbgAddModule( HB_DEBUGINFO *info, const char *szName );
static void     hb_dbgAddStack( HB_DEBUGINFO *info, const char *szName, int nProcLevel );
static void     hb_dbgAddStatic( HB_DEBUGINFO *info, const char *szName, int nIndex, PHB_ITEM pFrame );
static void     hb_dbgAddVar( int *nVars, HB_VARINFO **aVars, const char *szName, char cType, int nIndex, int nFrame, PHB_ITEM pFrame );
static void     hb_dbgAddStopLines( HB_DEBUGINFO *info, PHB_ITEM pItem );
static void     hb_dbgEndProc( HB_DEBUGINFO *info );
static PHB_ITEM hb_dbgEval( HB_DEBUGINFO *info, HB_WATCHPOINT *watch );
static PHB_ITEM hb_dbgEvalMacro( const char *szExpr, PHB_ITEM pItem );
static PHB_ITEM hb_dbgEvalMakeBlock( HB_WATCHPOINT *watch );
static PHB_ITEM hb_dbgEvalResolve( HB_DEBUGINFO *info, HB_WATCHPOINT *watch );
static BOOL     hb_dbgIsAltD( void );
static BOOL     hb_dbgIsBreakPoint( HB_DEBUGINFO *info, const char *szModule, int nLine );
static BOOL     hb_dbgEqual( PHB_ITEM pItem1, PHB_ITEM pItem2 );
static void     hb_dbgQuit( HB_DEBUGINFO *info );
static PHB_ITEM hb_dbgVarGet( HB_VARINFO *scope );
static void     hb_dbgVarSet( HB_VARINFO *scope, PHB_ITEM xNewValue );

static void hb_dbgActivate( HB_DEBUGINFO *info )
{
   PHB_DYNS pDynSym = hb_dynsymFind( "__DBGENTRY" );

   if( pDynSym && hb_dynsymIsFunction( pDynSym ) )
   {
      int i;
      PHB_ITEM aCallStack = hb_itemArrayNew( info->nCallStackLen );
      PHB_ITEM aModules;
      PHB_ITEM aBreak;

      for ( i = 0; i < info->nCallStackLen; i++ )
      {
         HB_CALLSTACKINFO *pEntry = &info->aCallStack[ i ];
         PHB_ITEM aEntry = hb_itemArrayNew( 6 );
         PHB_ITEM pItem;

         hb_arraySetC( aEntry, 1, pEntry->szModule );
         hb_arraySetC( aEntry, 2, pEntry->szFunction );
         hb_arraySetNL( aEntry, 3, pEntry->nLine );
         hb_arraySetNL( aEntry, 4, pEntry->nProcLevel );

         pItem = hb_dbgActivateVarArray( pEntry->nLocals, pEntry->aLocals );
         hb_arraySet( aEntry, 5, pItem );
         hb_itemRelease( pItem );

         pItem = hb_dbgActivateVarArray( pEntry->nStatics, pEntry->aStatics );
         hb_arraySet( aEntry, 6, pItem );
         hb_itemRelease( pItem );

         hb_arraySet( aCallStack, info->nCallStackLen - i, aEntry );
         hb_itemRelease( aEntry );
      }

      aModules = hb_dbgActivateModuleArray( info );
      aBreak = hb_dbgActivateBreakArray( info );

      hb_vmPushDynSym( pDynSym );
      hb_vmPushNil();
      hb_vmPushLong( HB_DBG_ACTIVATE );
      hb_vmPushPointer( info );
      hb_vmPushLong( info->nProcLevel );
      hb_vmPush( aCallStack );
      hb_vmPush( aModules );
      hb_vmPush( aBreak );

      hb_itemRelease( aCallStack );
      hb_itemRelease( aModules );
      hb_itemRelease( aBreak );

      hb_vmDo( 6 );
   }
}


static PHB_ITEM hb_dbgActivateBreakArray( HB_DEBUGINFO *info )
{
   int i;
   PHB_ITEM pArray = hb_itemArrayNew( info->nBreakPoints );

   for ( i = 0; i < info->nBreakPoints; i++ )
   {
      PHB_ITEM pBreak = hb_itemArrayNew( 3 );

      if ( !info->aBreak[ i ].szFunction )
      {
         hb_arraySetNI( pBreak, 1, info->aBreak[ i ].nLine );
         hb_arraySetC( pBreak, 2, info->aBreak[ i ].szModule );
      }
      else
      {
         hb_arraySetC( pBreak, 3, info->aBreak[ i ].szFunction );
      }

      hb_arraySet( pArray, i + 1, pBreak );
      hb_itemRelease( pBreak );
   }
   return pArray;
}


static PHB_ITEM hb_dbgActivateModuleArray( HB_DEBUGINFO *info )
{
   int i;
   PHB_ITEM pArray = hb_itemArrayNew( info->nModules );

   for ( i = 0; i < info->nModules; i++ )
   {
      PHB_ITEM pModule = hb_itemArrayNew( 4 );
      PHB_ITEM item;

      hb_arraySetC( pModule, 1, info->aModules[ i ].szModule );

      item = hb_dbgActivateVarArray( info->aModules[ i ].nStatics,
                                     info->aModules[ i ].aStatics );
      hb_arraySet( pModule, 2, item );
      hb_itemRelease( item );

      item = hb_dbgActivateVarArray( info->aModules[ i ].nGlobals,
                                     info->aModules[ i ].aGlobals );
      hb_arraySet( pModule, 3, item );
      hb_itemRelease( item );

      item = hb_dbgActivateVarArray( info->aModules[ i ].nExternGlobals,
                                     info->aModules[ i ].aExternGlobals );
      hb_arraySet( pModule, 4, item );
      hb_itemRelease( item );

      hb_arraySet( pArray, i + 1, pModule );
      hb_itemRelease( pModule );
   }
   return pArray;
}


static PHB_ITEM hb_dbgActivateVarArray( int nVars, HB_VARINFO *aVars )
{
   int i;
   PHB_ITEM pArray = hb_itemArrayNew( nVars );

   for ( i = 0; i < nVars; i++ )
   {
      PHB_ITEM aVar = hb_itemArrayNew( 4 );

      hb_arraySetC( aVar, 1, aVars[ i ].szName );
      hb_arraySetNL( aVar, 2, aVars[ i ].nIndex );
      hb_arraySetCL( aVar, 3, &aVars[ i ].cType, 1 );
      if( aVars[ i ].cType == 'S' )
         hb_arraySet( aVar, 4, aVars[ i ].frame.ptr );
      else
         hb_arraySetNL( aVar, 4, aVars[ i ].frame.num );

      hb_arraySet( pArray, i + 1, aVar );
      hb_itemRelease( aVar );
   }
   return pArray;
}


void hb_dbgEntry( int nMode, int nLine, const char *szName, int nIndex, PHB_ITEM pFrame )
{
   int i;
   ULONG nProcLevel;
   char szProcName[ HB_SYMBOL_NAME_LEN + HB_SYMBOL_NAME_LEN + 5 ];
   HB_DEBUGINFO ** infoPtr = ( HB_DEBUGINFO ** ) hb_stackDebugInfo();
   HB_DEBUGINFO *info = *infoPtr;

   if( info == HB_DBGINFO_DISABLE )
      return;
   else if ( nMode != HB_DBG_VMQUIT )
   {
      if( !info )
      {
         info = *infoPtr = ( HB_DEBUGINFO * ) hb_xgrab( sizeof( HB_DEBUGINFO ) );
         memset( info, 0, sizeof( HB_DEBUGINFO ) );
         info->bCBTrace = TRUE;
      }
      else if ( info->bInside || info->bQuit )
         return;
   }

   switch ( nMode )
   {
      case HB_DBG_MODULENAME:
         HB_TRACE( HB_TR_DEBUG, ( "MODULENAME %s", szName ) );

         if ( szName[ strlen( szName ) - 1 ] == ':' )
            return;

         hb_procinfo( 0, szProcName, NULL, NULL );
         if ( !strncmp( szProcName, "(_INITSTATICS", 13 ) )
            info->bInitStatics = TRUE;
         else if ( !strncmp( szProcName, "(_INITGLOBALS", 13 ) )
            info->bInitGlobals = TRUE;
         else if ( !strncmp( szProcName, "(_INITLINES", 11 ) )
            info->bInitLines = TRUE;

         if ( info->bInitStatics || info->bInitGlobals )
            hb_dbgAddModule( info, szName );
         else if ( !strncmp( szProcName, "(b)", 3 ) )
            info->bCodeBlock = TRUE;
         else if ( info->bNextRoutine )
            info->bNextRoutine = FALSE;

         hb_dbgAddStack( info, szName, hb_dbg_ProcLevel() );
         for ( i = 0; i < info->nBreakPoints; i++ )
         {
            if ( info->aBreak[ i ].szFunction
                 && !strcmp( info->aBreak[ i ].szFunction, szProcName ) )
            {
               hb_dbg_InvokeDebug( TRUE );
               break;
            }
         }
         return;

      case HB_DBG_LOCALNAME:
         HB_TRACE( HB_TR_DEBUG, ( "LOCALNAME %s index %d", szName, nIndex ) );

         hb_dbgAddLocal( info, szName, nIndex, hb_dbg_ProcLevel() );
         return;

      case HB_DBG_STATICNAME:
         HB_TRACE( HB_TR_DEBUG, ( "STATICNAME %s index %d frame %p", szName, nIndex, pFrame ) );

         hb_dbgAddStatic( info, szName, nIndex, pFrame );
         return;

      case HB_DBG_SHOWLINE:
      {
         HB_CALLSTACKINFO *pTop = &info->aCallStack[ info->nCallStackLen - 1 ];
         BOOL bOldClsScope;

         HB_TRACE( HB_TR_DEBUG, ( "SHOWLINE %d", nLine ) );

         nProcLevel = hb_dbg_ProcLevel();

         /* Check if we've hit a tracepoint */
         bOldClsScope = hb_clsSetScope( FALSE );
         for ( i = 0; i < info->nTracePoints; i++ )
         {
            HB_TRACEPOINT *tp = &info->aTrace[ i ];
            PHB_ITEM xValue;

            xValue = hb_dbgEval( info, &info->aWatch[ tp->nIndex ] );
            if ( !xValue )
               xValue = hb_itemNew( NULL );

            if ( HB_ITEM_TYPE( xValue ) != HB_ITEM_TYPE( tp->xValue ) ||
                 !hb_dbgEqual( xValue, tp->xValue ) )
            {
               hb_itemCopy( tp->xValue, xValue );
               hb_itemRelease( xValue );

               pTop->nLine = nLine;
               info->nProcLevel = nProcLevel - ( hb_dbgIsAltD() ? 2 : 0 );
               info->bTraceOver = FALSE;
               info->bCodeBlock = FALSE;
               info->bGo = FALSE;
               if ( info->bToCursor )
               {
                  info->bToCursor = FALSE;
                  hb_xfree( info->szToCursorModule );
               }
               info->bNextRoutine = FALSE;

               hb_dbgActivate( info );
               return;
            }
            hb_itemRelease( xValue );
         }
         hb_clsSetScope( bOldClsScope );

         if ( hb_dbgIsBreakPoint( info, pTop->szModule, nLine )
              || hb_dbg_InvokeDebug( FALSE )
              || ( info->pFunInvoke && info->pFunInvoke() ) )
         {
            info->bTraceOver = FALSE;
            if ( info->bToCursor )
            {
               info->bToCursor = FALSE;
               hb_xfree( info->szToCursorModule );
            }
            info->bNextRoutine = FALSE;
            info->bGo = FALSE;
         }

         /* Check if we must skip every level above info->nTraceLevel */
         if ( info->bTraceOver )
         {
            if ( info->nTraceLevel < info->nCallStackLen )
               return;
            info->bTraceOver = FALSE;
         }

         /* Check if we're skipping to a specific line of source */
         if ( info->bToCursor )
         {
            if ( nLine == info->nToCursorLine
                 && FILENAME_EQUAL( pTop->szModule, info->szToCursorModule ) )
            {
               hb_xfree( info->szToCursorModule );
               info->bToCursor = FALSE;
            }
            else
            {
               return;
            }
         }

         /* Check if'we skipping to the end of current routine */
         if ( info->bNextRoutine )
            return;

         if ( info->bCodeBlock )
         {
            info->bCodeBlock = FALSE;
            if ( !info->bCBTrace )
               return;
         }

         pTop->nLine = nLine;
         if ( !info->bGo )
         {
            info->nProcLevel = nProcLevel - ( hb_dbgIsAltD() ? 2 : 0 );
            hb_dbgActivate( info );
         }
         return;
      }

      case HB_DBG_ENDPROC:
         if ( info->bQuit )
            return;

         HB_TRACE( HB_TR_DEBUG, ( "ENDPROC %d", nLine ) );

         if ( info->bInitLines )
         {
            hb_dbgAddStopLines( info, hb_stackReturnItem() );
         }
         info->bCodeBlock = FALSE;
         info->bInitStatics = FALSE;
         info->bInitGlobals = FALSE;
         info->bInitLines = FALSE;
         hb_dbgEndProc( info );
         return;

      case HB_DBG_VMQUIT:
         if( info )
         {
            hb_dbgQuit( info );
            hb_xfree( info );
            *infoPtr = HB_DBGINFO_DISABLE;
         }
         return;
   }
}


static const char * hb_dbgStripModuleName( const char * szName )
{
   const char * ptr;

   if( ( ptr = strrchr( szName, '/' ) ) != NULL )
   {
      szName = ptr + 1;
   }
   if( ( ptr = strrchr( szName, '\\' ) ) != NULL )
   {
      szName = ptr + 1;
   }

   return szName;
}


void hb_dbgAddBreak( void *handle, const char *szModule, int nLine, const char *szFunction )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   HB_BREAKPOINT *pBreak;

   pBreak = ARRAY_ADD( HB_BREAKPOINT, info->aBreak, info->nBreakPoints );
   pBreak->szModule = hb_strdup( szModule );
   pBreak->nLine = nLine;
   if ( szFunction )
   {
      pBreak->szFunction = hb_strdup( szFunction );
   }
   else
   {
      pBreak->szFunction = NULL;
   }
}


static void hb_dbgAddLocal( HB_DEBUGINFO *info, const char *szName, int nIndex, int nFrame )
{
   if ( info->bInitGlobals )
   {
      HB_MODULEINFO *module = &info->aModules[ info->nModules - 1 ];

      hb_dbgAddVar( &module->nGlobals, &module->aGlobals, szName, 'G', nIndex, hb_dbg_vmVarGCount(), NULL );
   }
   else
   {
      HB_CALLSTACKINFO *top = &info->aCallStack[ info->nCallStackLen - 1 ];

      hb_dbgAddVar( &top->nLocals, &top->aLocals, szName, 'L', nIndex, nFrame, NULL );
   }
}


static void hb_dbgAddModule( HB_DEBUGINFO *info, const char *szName )
{
   char * szModuleName;
   const char * szFuncName;
   int iLen;

   szName = hb_dbgStripModuleName( szName );
   szFuncName = strrchr( szName, ':' );
   iLen = szFuncName ? ( int ) ( szFuncName - szName ) : ( int ) strlen( szName );
   szModuleName = hb_strndup( szName, iLen );

   if( !info->nModules || strcmp( info->aModules[ info->nModules - 1 ].szModule, szModuleName ) )
   {
      HB_MODULEINFO *pModule;

      pModule = ARRAY_ADD( HB_MODULEINFO, info->aModules, info->nModules );
      pModule->szModule = szModuleName;
      pModule->nStatics = 0;
      pModule->nGlobals = 0;
      pModule->nExternGlobals = 0;
   }
   else
   {
      hb_xfree( szModuleName );
   }
}


static void hb_dbgAddStack( HB_DEBUGINFO *info, const char *szName, int nProcLevel )
{
   char szBuff[ HB_SYMBOL_NAME_LEN + HB_SYMBOL_NAME_LEN + 5 ];
   HB_CALLSTACKINFO *top;
   const char *szFunction = strrchr( szName, ':' );

   if ( szFunction )
   {
      szFunction++;
   }
   top = ARRAY_ADD( HB_CALLSTACKINFO, info->aCallStack, info->nCallStackLen );
   if ( info->bCodeBlock )
   {
      memcpy( szBuff, "(b)", 3 );
      hb_strncpy( szBuff + 3, szFunction, sizeof( szBuff ) - 4 );
      top->szFunction = hb_strdup( szBuff );
   }
   else
   {
      if ( szFunction )
      {
         top->szFunction = hb_strdup( szFunction );
      }
      else
      {
         /* We're in an (_INITSTATICSnnnnn) pseudo-function */
         hb_procinfo( 0, szBuff, NULL, NULL );
         top->szFunction = hb_strdup( szBuff );
      }
   }
   szName = hb_dbgStripModuleName( szName );
   if ( szFunction )
   {
      top->szModule = hb_strndup( szName, szFunction - szName - 1 );
   }
   else
   {
      top->szModule = hb_strdup( szName );
   }
   top->nProcLevel = nProcLevel;
   top->nLine = 0;
   top->nLocals = 0;
   top->nStatics = 0;
}


static void hb_dbgAddStatic( HB_DEBUGINFO *info, const char *szName, int nIndex, PHB_ITEM pFrame )
{
   if ( info->bInitGlobals )
   {
      HB_MODULEINFO *module = &info->aModules[ info->nModules - 1 ];

      hb_dbgAddVar( &module->nExternGlobals, &module->aExternGlobals, szName,
                    'G', nIndex, hb_dbg_vmVarGCount(), NULL );
   }
   else if ( info->bInitStatics )
   {
      HB_MODULEINFO *module = &info->aModules[ info->nModules - 1 ];

      hb_dbgAddVar( &module->nStatics, &module->aStatics, szName, 'S', nIndex, 0, pFrame );
   }
   else
   {
      HB_CALLSTACKINFO *top = &info->aCallStack[ info->nCallStackLen - 1 ];

      hb_dbgAddVar( &top->nStatics, &top->aStatics, szName, 'S', nIndex, 0, pFrame );
   }
}


static void hb_dbgAddStopLines( HB_DEBUGINFO *info, PHB_ITEM pItem )
{
   int i, nLinesLen;

   if ( !info->pStopLines )
   {
      info->pStopLines = hb_itemNew( pItem );
   }
   else
   {
      int j;
      int nItemLen = hb_itemSize( pItem );

      nLinesLen = hb_itemSize( info->pStopLines );

      for ( i = 1; i <= nItemLen; i++ )
      {
         PHB_ITEM pEntry = hb_arrayGetItemPtr( pItem, i );
         const char *szModule = hb_arrayGetCPtr( pEntry, 1 );
         BOOL bFound = FALSE;

         szModule = hb_dbgStripModuleName( szModule );
         for ( j = 1; j <= nLinesLen; j++ )
         {
            PHB_ITEM pLines = hb_arrayGetItemPtr( info->pStopLines, j );

            if ( !strcmp( hb_arrayGetCPtr( pLines, 1 ), szModule ) )
            {
               /* Merge stopline info */
               int nOrigMin = hb_arrayGetNL( pLines, 2 );
               int nNewMin = hb_arrayGetNL( pEntry, 2 );
               int nOrigLen = hb_arrayGetCLen( pLines, 3 );
               int nNewLen = hb_arrayGetCLen( pEntry, 3 );
               int nMin = HB_MIN( nNewMin, nOrigMin );
               int nMax = HB_MAX( nNewMin + nNewLen * 8, nOrigMin + nOrigLen * 8 );
               const char *pOrigBuffer = hb_arrayGetCPtr( pLines, 3 );
               const char *pNewBuffer = hb_arrayGetCPtr( pEntry, 3 );
               int nLen = ( nMax + 1 - nMin + 7 ) / 8 + 1;
               int k;
               char *pBuffer = (char *) hb_xgrab( nLen );

               hb_xmemset( pBuffer, 0, nLen );

               memmove( &( pBuffer[ ( nNewMin - nMin ) / 8 ] ), pNewBuffer, nNewLen );
               for ( k = 0; k < nOrigLen; k++ )
               {
                  pBuffer[ nOrigMin / 8 + k - nMin / 8 ] |= pOrigBuffer[ k ];
               }
               hb_arraySetNL( pLines, 2, nMin );
               if( !hb_arraySetCLPtr( pLines, 3, pBuffer, nLen - 1 ) )
                  hb_xfree( pBuffer );
               bFound = TRUE;
               break;
            }
         }
         if ( !bFound )
         {
            hb_arrayAddForward( info->pStopLines, pEntry );
         }
      }
   }
   nLinesLen = hb_itemSize( info->pStopLines );
   for ( i = 1; i <= nLinesLen; i++ )
   {
      PHB_ITEM pEntry = hb_arrayGetItemPtr( info->pStopLines, i );
      const char *szModule = hb_arrayGetCPtr( pEntry, 1 );

      if( szModule )
      {
         const char *szName = hb_dbgStripModuleName( szModule );

         if( szName != szModule )
         {
            hb_arraySetCLPtr( pEntry, 1, hb_strdup( szName ), strlen( szName ) );
         }
      }
   }
}


static void hb_dbgAddVar( int *nVars, HB_VARINFO **aVars, const char *szName, char cType, int nIndex, int nFrame, PHB_ITEM pFrame )
{
   HB_VARINFO *var;

   var = ARRAY_ADD( HB_VARINFO, *aVars, *nVars );
   /* TODO/TOFIX: value should be duplicated here and then released */
   var->szName = ( char * ) szName;
   var->cType = cType;
   var->nIndex = nIndex;
   if( cType == 'S' )
      var->frame.ptr = pFrame;
   else
      var->frame.num = nFrame;
}


void hb_dbgAddWatch( void *handle, const char *szExpr, BOOL bTrace )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   HB_WATCHPOINT *pWatch;

   pWatch = ARRAY_ADD( HB_WATCHPOINT, info->aWatch, info->nWatchPoints );
   pWatch->szExpr = hb_strdup( szExpr );
   pWatch->pBlock = NULL;
   pWatch->nVars = 0;

   if ( bTrace )
   {
     HB_TRACEPOINT *pTrace = ARRAY_ADD( HB_TRACEPOINT, info->aTrace, info->nTracePoints );

     pTrace->nIndex = info->nWatchPoints - 1;
     pTrace->xValue = hb_dbgEval( info, pWatch );
   }
}


static void hb_dbgClearWatch( HB_WATCHPOINT *pWatch )
{
   hb_xfree( pWatch->szExpr );
   if ( pWatch->pBlock )
   {
      hb_itemRelease( pWatch->pBlock );
   }
   if ( pWatch->nVars )
   {
      int i;

      for (i = 0; i < pWatch->nVars; i++)
      {
         hb_xfree( pWatch->aVars[ i ] );
      }
      hb_xfree( pWatch->aVars );
   }
}


void hb_dbgDelBreak( void *handle, int nBreak )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   HB_BREAKPOINT *pBreak = &info->aBreak[ nBreak ];

   hb_xfree( pBreak->szModule );
   if ( pBreak->szFunction )
   {
      hb_xfree( pBreak->szFunction );
   }
   ARRAY_DEL( HB_BREAKPOINT, info->aBreak, info->nBreakPoints, nBreak );
}


void hb_dbgDelWatch( void *handle, int nWatch )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   HB_WATCHPOINT *pWatch = &info->aWatch[ nWatch ];
   int i;

   hb_dbgClearWatch( pWatch );
   ARRAY_DEL( HB_WATCHPOINT, info->aWatch, info->nWatchPoints, nWatch );

   for ( i = 0; i < info->nTracePoints; i++ )
   {
      HB_TRACEPOINT *pTrace = &info->aTrace[ i ];

      if ( pTrace->nIndex == nWatch )
      {
         if ( pTrace->xValue )
         {
            hb_itemRelease( pTrace->xValue );
         }
         ARRAY_DEL( HB_TRACEPOINT, info->aTrace, info->nTracePoints, i );
         i--;
      }
      else if ( pTrace->nIndex > nWatch )
      {
         pTrace->nIndex--;
      }
   }
}


static void hb_dbgEndProc( HB_DEBUGINFO *info )
{
   HB_CALLSTACKINFO *top;

   if ( !info->nCallStackLen )
      return;

   top = &info->aCallStack[ --info->nCallStackLen ];
   hb_xfree( top->szFunction );
   hb_xfree( top->szModule );
   if ( top->nLocals )
   {
      hb_xfree( top->aLocals );
   }
   if ( top->nStatics )
   {
      hb_xfree( top->aStatics );
   }
   if ( !info->nCallStackLen )
   {
      hb_xfree( info->aCallStack );
      info->aCallStack = NULL;
   }
}


static BOOL hb_dbgEqual( PHB_ITEM pItem1, PHB_ITEM pItem2 )
{
   if ( HB_ITEM_TYPE( pItem1 ) != HB_ITEM_TYPE( pItem2 ) )
      return FALSE;
   if ( HB_IS_NIL( pItem1 ) )
      return HB_IS_NIL( pItem2 );
   if ( HB_IS_LOGICAL( pItem1 ) )
      return ( hb_itemGetL( pItem1 ) == hb_itemGetL( pItem2 ) );
   if ( HB_IS_POINTER( pItem1 ) )
      return ( hb_itemGetPtr( pItem1 ) == hb_itemGetPtr( pItem2 ) );
   if ( HB_IS_STRING( pItem1 ) )
      return !hb_itemStrCmp( pItem1, pItem2, TRUE );
   if ( HB_IS_NUMINT( pItem1 ) )
      return ( hb_itemGetNInt( pItem1 ) == hb_itemGetNInt( pItem2 ) );
   if ( HB_IS_NUMERIC( pItem1 ) )
      return ( hb_itemGetND( pItem1 ) == hb_itemGetND( pItem2 ) );
   if ( HB_IS_ARRAY( pItem1 ) )
      return ( hb_arrayId( pItem1 ) == hb_arrayId( pItem2 ) );
   if ( HB_IS_HASH( pItem1 ) )
      return ( hb_hashId( pItem1 ) == hb_hashId( pItem2 ) );
   return FALSE;
}


static PHB_ITEM hb_dbgEval( HB_DEBUGINFO *info, HB_WATCHPOINT *watch )
{
   PHB_ITEM xResult = NULL;

   HB_TRACE( HB_TR_DEBUG, ( "expr %s", watch->szExpr ) );

   /* Check if we have a cached pBlock */
   if ( !watch->pBlock )
   {
      watch->pBlock = hb_dbgEvalMakeBlock( watch );
   }

   if ( watch->pBlock )
   {
      PHB_ITEM aVars = hb_dbgEvalResolve( info, watch );
      PHB_ITEM aNewVars = hb_itemArrayNew( watch->nVars );
      int i;

      hb_arrayCopy( aVars, aNewVars, NULL, NULL, NULL );

      info->bInside = TRUE;
      xResult = hb_itemDo( watch->pBlock, 1, aNewVars );
      info->bInside = FALSE;

      for ( i = 0; i < watch->nVars; i++ )
      {
         PHB_ITEM xOldValue = hb_itemArrayGet( aVars, i + 1 );
         PHB_ITEM xNewValue = hb_itemArrayGet( aNewVars, i + 1 );

         if ( !hb_dbgEqual( xOldValue, xNewValue ) )
         {
            hb_dbgVarSet( &watch->aScopes[ i ], xNewValue );
         }
         hb_itemRelease( xOldValue );
         hb_itemRelease( xNewValue );
      }

      hb_itemRelease( aVars );
      hb_itemRelease( aNewVars );
      for ( i = 0; i < watch->nVars; i++ )
      {
         if ( watch->aScopes[ i ].cType == 'M' )
         {
            hb_xfree( watch->aScopes[ i ].szName );
         }
      }
      if ( watch->nVars )
      {
         hb_xfree( watch->aScopes );
      }
   }
   return xResult;
}


static PHB_ITEM hb_dbgEvalMacro( const char *szExpr, PHB_ITEM pItem )
{
   PHB_ITEM pStr;
   const char *type;

   pStr = hb_itemPutC( NULL, szExpr );
   type = hb_macroGetType( pStr );
   hb_itemRelease( pStr );
   if ( ( !strcmp( type, "U" ) || !strcmp( type, "UE" ) ) )
   {
      return NULL;
   }
   hb_vmPushString( szExpr, strlen( szExpr ) );
   hb_macroGetValue( hb_stackItemFromTop( -1 ), 0, HB_SM_RT_MACRO );
   hb_itemMove( pItem, hb_stackItemFromTop( -1 ) );
   hb_stackPop();
   return pItem;
}


#define IS_IDENT_START( c ) HB_ISFIRSTIDCHAR( ( UCHAR ) ( c ) )
#define IS_IDENT_CHAR( c )  HB_ISNEXTIDCHAR( ( UCHAR ) ( c ) )

static int hb_dbgEvalSubstituteVar( HB_WATCHPOINT *watch, char *szWord, int nStart, int nLen )
{
   int j;
   char *t;

   for ( j = 0; j < watch->nVars; j++ )
   {
      if ( !strcmp( szWord, watch->aVars[ j ] ) )
      {
         break;
      }
   }
   if ( j == watch->nVars )
   {
      *ARRAY_ADD( char *, watch->aVars, watch->nVars ) = szWord;
   }
   else
   {
      hb_xfree( szWord );
   }

   t = (char *) hb_xgrab( strlen( watch->szExpr ) - nLen + 9 + 1 );
   memmove( t, watch->szExpr, nStart );
   memmove( t + nStart, "__dbg[", 6 );
   t[ nStart + 6 ] = '0' + ( char ) ( ( j + 1 ) / 10 );
   t[ nStart + 7 ] = '0' + ( char ) ( ( j + 1 ) % 10 );
   t[ nStart + 8 ] = ']';
   hb_strncpy( t + nStart + 9, watch->szExpr + nStart + nLen, strlen( watch->szExpr ) - nLen - nStart );
   hb_xfree( watch->szExpr );
   watch->szExpr = t;
   return nStart + 9;
}


static PHB_ITEM hb_dbgEvalMakeBlock( HB_WATCHPOINT *watch )
{
   int i = 0;
   PHB_ITEM pBlock;
   BOOL bAfterId = FALSE;
   char *s;
   int buffsize;

   watch->nVars = 0;
   while ( watch->szExpr[ i ] )
   {
      char c = watch->szExpr[ i ];

      if ( IS_IDENT_START( c ) )
      {
         int nStart = i, nLen;
         int j = i;
         char *szWord;

         while ( c && IS_IDENT_CHAR( c ) )
         {
            j++;
            c = watch->szExpr[ j ];
         }
         nLen = j - i;
         szWord = hb_strndup( watch->szExpr + i, nLen );
         i = j;
         if ( c )
         {
            while ( watch->szExpr[ i ] && watch->szExpr[ i ] == ' ' )
            {
               i++;
            }
            if ( watch->szExpr[ i ] == '(' )
            {
               hb_xfree( szWord );
               continue;
            }
            if ( watch->szExpr[ i ] == '-' && watch->szExpr[ i + 1 ] == '>' )
            {
               i += 2;
               while ( ( c = watch->szExpr[ i ] ) != '\0' && IS_IDENT_CHAR( c ) )
               {
                  i++;
               }
               hb_xfree( szWord );
               continue;
            }
         }
         hb_strupr( szWord );
         i = hb_dbgEvalSubstituteVar( watch, szWord, nStart, nLen );
         bAfterId = TRUE;
         continue;
      }
      if ( c == '.' )
      {
         if ( watch->szExpr[ i + 1 ]
              && strchr( "TtFf", watch->szExpr[ i + 1 ] )
              && watch->szExpr[ i + 2 ] == '.' )
         {
            i += 3;
         }
         else if ( !hb_strnicmp( watch->szExpr + i + 1, "OR.", 3 ) )
         {
            i += 4;
         }
         else if ( !hb_strnicmp( watch->szExpr + i + 1, "AND.", 4 )
                   || !hb_strnicmp( watch->szExpr + i + 1, "NOT.", 4 ) )
         {
            i += 5;
         }
         else
         {
            i++;
         }
         bAfterId = FALSE;
         continue;
      }
      if ( c == ':'
           || ( c == '-' && watch->szExpr[ i + 1 ] == '>'
                && IS_IDENT_START( watch->szExpr[ i + 2 ] ) ) )
      {
         if ( c == ':' && watch->szExpr[ i + 1 ] == ':' )
         {
            i = hb_dbgEvalSubstituteVar( watch, hb_strdup( "SELF" ), i, 1 );
            bAfterId = TRUE;
            continue;
         }
         if ( c == '-' )
         {
            i++;
         }
         i++;
         while ( watch->szExpr[ i ] && IS_IDENT_CHAR( watch->szExpr[ i ] ) )
         {
            i++;
         }
         bAfterId = TRUE;
         continue;
      }
      if ( strchr( " !#$=<>(+-*/%^|,{&", c ) )
      {
         i++;
         bAfterId = FALSE;
         continue;
      }
      if ( c == '\'' || c == '\"' )
      {
         i++;
         while ( watch->szExpr[ i ] && watch->szExpr[ i ] != c )
         {
            i++;
         }
         if ( watch->szExpr[ i ] )
         {
            i++;
         }
         bAfterId = TRUE;
         continue;
      }
      if ( c == '[' )
      {
         i++;
         if ( bAfterId )
         {
            bAfterId = FALSE;
         }
         else
         {
            while ( watch->szExpr[ i ] && watch->szExpr[ i ] != ']' )
            {
               i++;
            }
            if ( watch->szExpr[ i ] )
            {
               i++;
            }
            bAfterId = TRUE;
         }
         continue;
      }
      i++;
   }

   buffsize = 8 + strlen( watch->szExpr ) + 1;

   s = ( char * ) hb_xgrab( buffsize + 1 );
   hb_strncpy( s, "{|__dbg|", buffsize );
   hb_strncat( s, watch->szExpr, buffsize );
   hb_strncat( s, "}", buffsize );
   pBlock = hb_itemNew( NULL );

   if( ! hb_dbgEvalMacro( s, pBlock ) )
   {
      hb_itemRelease( pBlock );
      pBlock = NULL;
   }
   hb_xfree( s );

   return pBlock;
}


static PHB_ITEM hb_dbgEvalResolve( HB_DEBUGINFO *info, HB_WATCHPOINT *watch )
{
   int i;
   HB_CALLSTACKINFO *top = &info->aCallStack[ info->nCallStackLen - 1 ];
   PHB_ITEM aVars = hb_itemArrayNew( watch->nVars );
   HB_VARINFO *scopes;
   HB_MODULEINFO *module = NULL;
   int nProcLevel;

   if ( !watch->nVars )
   {
      return aVars;
   }

   scopes = (HB_VARINFO *) hb_xgrab( watch->nVars * sizeof( HB_VARINFO ) );
   nProcLevel = hb_dbg_ProcLevel();

   for ( i = 0; i < info->nModules; i++ )
   {
      if ( !strcmp( info->aModules[ i ].szModule, top->szModule ) )
      {
         module = &info->aModules[ i ];
         break;
      }
   }

   for ( i = 0; i < watch->nVars; i++ )
   {
      char *name = watch->aVars[ i ];
      HB_VARINFO *var;
      int j;
      PHB_ITEM pItem;

      for ( j = 0; j < top->nLocals; j++ )
      {
         var = &top->aLocals[ j ];
         if ( !strcmp( name, var->szName ) )
         {
            scopes[ i ].cType = 'L';
            scopes[ i ].frame.num = nProcLevel - var->frame.num;
            scopes[ i ].nIndex = var->nIndex;
            hb_itemArrayPut( aVars, i + 1, hb_dbgVarGet( &scopes[ i ] ) );
            break;
         }
      }
      if ( j < top->nLocals )
         continue;

      for ( j = 0; j < top->nStatics; j++ )
      {
         var = &top->aStatics[ j ];
         if ( !strcmp( name, var->szName ) )
         {
            scopes[ i ].cType = 'S';
            scopes[ i ].frame.ptr = var->frame.ptr;
            scopes[ i ].nIndex = var->nIndex;
            hb_itemArrayPut( aVars, i + 1, hb_dbgVarGet( &scopes[ i ] ) );
            break;
         }
      }
      if ( j < top->nStatics )
         continue;

      if ( module )
      {
         for ( j = 0; j < module->nStatics; j++ )
         {
            var = &module->aStatics[ j ];
            if ( !strcmp( name, var->szName ) )
            {
               scopes[ i ].cType = 'S';
               scopes[ i ].frame.ptr = var->frame.ptr;
               scopes[ i ].nIndex = var->nIndex;
               hb_itemArrayPut( aVars, i + 1, hb_dbgVarGet( &scopes[ i ] ) );
               break;
            }
         }
         if ( j < module->nStatics )
            continue;

         for ( j = 0; j < module->nGlobals; j++ )
         {
            var = &module->aGlobals[ j ];
            if ( !strcmp( name, var->szName ) )
            {
               scopes[ i ].cType = 'G';
               scopes[ i ].frame.num = var->frame.num;
               scopes[ i ].nIndex = var->nIndex;
               hb_itemArrayPut( aVars, i + 1, hb_dbgVarGet( &scopes[ i ] ) );
               break;
            }
         }
         if ( j < module->nGlobals )
            continue;

         for ( j = 0; j < module->nExternGlobals; j++ )
         {
            var = &module->aExternGlobals[ j ];
            if ( !strcmp( name, var->szName ) )
            {
               scopes[ i ].cType = 'G';
               scopes[ i ].frame.num = var->frame.num;
               scopes[ i ].nIndex = var->nIndex;
               hb_itemArrayPut( aVars, i + 1, hb_dbgVarGet( &scopes[ i ] ) );
               break;
            }
         }
         if ( j < module->nExternGlobals )
            continue;
      }

      scopes[ i ].cType = 'M';
      scopes[ i ].szName = hb_strdup( name );
      pItem = hb_dbgVarGet( &scopes[ i ] );
      if ( pItem )
      {
         hb_itemArrayPut( aVars, i + 1, pItem );
      }
      if ( scopes[ i ].cType == 'F' )
      {
         hb_itemRelease( pItem );
      }
   }
   watch->aScopes = scopes;
   return aVars;
}


PHB_ITEM hb_dbgGetExpressionValue( void *handle, const char *expression )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   PHB_ITEM result;
   HB_WATCHPOINT point;

   point.szExpr = hb_strdup( expression );
   point.pBlock = NULL;
   point.nVars = 0;

   result = hb_dbgEval( info, &point );

   hb_dbgClearWatch( &point );

   return result;
}


PHB_ITEM hb_dbgGetSourceFiles( void *handle )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   int nModules = hb_itemSize( info->pStopLines );
   PHB_ITEM ret = hb_itemArrayNew( nModules );
   int i;

   for ( i = 1; i <= nModules; i++ )
   {
      hb_arraySet( ret, i, hb_arrayGetItemPtr( hb_arrayGetItemPtr( info->pStopLines, i ), 1 ) );
   }
   return ret;
}


PHB_ITEM hb_dbgGetWatchValue( void *handle, int nWatch )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   return hb_dbgEval( info, &( info->aWatch[ nWatch ] ) );
}


static BOOL hb_dbgIsAltD( void )
{
   char szName[ HB_SYMBOL_NAME_LEN + HB_SYMBOL_NAME_LEN + 5];

   hb_procinfo( 1, szName, NULL, NULL );
   return !strcmp( szName, "ALTD" );
}


static BOOL hb_dbgIsBreakPoint( HB_DEBUGINFO *info, const char *szModule, int nLine )
{
   int i;

   for ( i = 0; i < info->nBreakPoints; i++ )
   {
      HB_BREAKPOINT *point = &info->aBreak[ i ];

      if ( point->nLine == nLine
           && FILENAME_EQUAL( szModule, point->szModule ) )
         return TRUE;
   }
   return FALSE;
}


BOOL hb_dbgIsValidStopLine( void *handle, const char *szModule, int nLine )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   int nModules = hb_itemSize( info->pStopLines );
   int i;

   for ( i = 1; i <= nModules; i++ )
   {
      PHB_ITEM pEntry = hb_arrayGetItemPtr( info->pStopLines, i );

      if ( FILENAME_EQUAL( hb_arrayGetCPtr( pEntry, 1 ), szModule ) )
      {
         int nMin = hb_arrayGetNL( pEntry, 2 );
         int nOfs = nLine - nMin;

         if ( nLine < nMin || (ULONG)( nOfs / 8 ) > hb_arrayGetCLen( pEntry, 3 ) )
         {
            return FALSE;
         }
         return ( hb_arrayGetCPtr( pEntry, 3 )[ nOfs / 8 ] & ( 1 << ( nOfs % 8 ) ) ) != 0;
      }
   }
   return FALSE;
}


static void hb_dbgQuit( HB_DEBUGINFO *info )
{
   while ( info->nWatchPoints )
   {
      hb_dbgDelWatch( info, info->nWatchPoints - 1 );
   }
   while ( info->nBreakPoints )
   {
      hb_dbgDelBreak( info, info->nBreakPoints - 1 );
   }
   while ( info->nCallStackLen )
   {
      hb_dbgEndProc( info );
   }
   if ( info->pStopLines )
   {
      hb_itemRelease( info->pStopLines );
   }
   while ( info->nModules )
   {
      int nModules = info->nModules - 1;
      HB_MODULEINFO *module = &info->aModules[ nModules ];
      if ( module->nStatics )
      {
         hb_xfree( module->aStatics );
      }
      if ( module->szModule )
      {
         hb_xfree( module->szModule );
      }
      ARRAY_DEL( HB_MODULEINFO, info->aModules, info->nModules, nModules );
   }
   if ( info->bToCursor )
   {
      info->bToCursor = FALSE;
      hb_xfree( info->szToCursorModule );
   }
}


void hb_dbgSetCBTrace( void *handle, BOOL bCBTrace )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bCBTrace = bCBTrace;
}


void hb_dbgSetGo( void *handle )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bGo = TRUE;
}


void hb_dbgSetInvoke( void *handle, BOOL ( *pFunInvoke )( void ) )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->pFunInvoke = pFunInvoke;
}


void hb_dbgSetNextRoutine( void *handle )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bNextRoutine = TRUE;
}


void hb_dbgSetQuit( void *handle )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bQuit = TRUE;
}


void hb_dbgSetToCursor( void *handle, const char *szModule, int nLine )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bToCursor = TRUE;
   info->szToCursorModule = hb_strdup( szModule );
   info->nToCursorLine = nLine;
}


void hb_dbgSetTrace( void *handle )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;

   info->bTraceOver = TRUE;
   info->nTraceLevel = info->nCallStackLen;
}


void hb_dbgSetWatch( void *handle, int nWatch, const char *szExpr, BOOL bTrace )
{
   HB_DEBUGINFO *info = (HB_DEBUGINFO *)handle;
   HB_WATCHPOINT *pWatch = &info->aWatch[ nWatch ];
   int i;

   hb_dbgClearWatch( pWatch );
   pWatch->szExpr = hb_strdup( szExpr );
   pWatch->pBlock = NULL;
   for ( i = 0; i < info->nTracePoints; i++ )
   {
      HB_TRACEPOINT *pTrace = &info->aTrace[ i ];

      if ( pTrace->nIndex == nWatch )
      {
         if ( pTrace->xValue )
         {
            hb_itemRelease( pTrace->xValue );
         }
         ARRAY_DEL( HB_TRACEPOINT, info->aTrace, info->nTracePoints, i );
         break;
      }
   }
   if ( bTrace )
   {
      HB_TRACEPOINT *pTrace = ARRAY_ADD( HB_TRACEPOINT, info->aTrace, info->nTracePoints );

      pTrace->nIndex = nWatch;
      pTrace->xValue = hb_dbgEval( info, pWatch );
   }
}


static PHB_ITEM hb_dbgVarGet( HB_VARINFO *scope )
{
   switch ( scope->cType )
   {
      case 'G':
         return hb_dbg_vmVarGGet( scope->frame.num, scope->nIndex );
      case 'L':
         return hb_dbg_vmVarLGet( scope->frame.num, scope->nIndex );
      case 'S':
         return hb_dbg_vmVarSGet( scope->frame.ptr, scope->nIndex );
      case 'M':
      {
         PHB_DYNS pDyn;

         pDyn = hb_dynsymFind( scope->szName );
         if ( pDyn != NULL )
         {
            PHB_ITEM pItem = hb_memvarGetValueBySym( pDyn );
            if( !pItem )
            {
               pItem = hb_itemNew( NULL );
               if ( hb_rddFieldGet( pItem, hb_dynsymSymbol( pDyn ) ) == HB_SUCCESS )
               {
                  scope->cType = 'F';
               }
               else
               {
                  hb_itemRelease( pItem );
                  pItem = NULL;
               }
            }
            return pItem;
         }
      }
   }
   return NULL;
}


static void hb_dbgVarSet( HB_VARINFO *scope, PHB_ITEM xNewValue )
{
   switch ( scope->cType )
   {
      case 'G':
      case 'L':
      case 'S':
         hb_itemCopy( hb_dbgVarGet( scope ), xNewValue );
         break;
      case 'M':
      {
         PHB_DYNS pDynSym = hb_dynsymFind( "__MVPUT" );

         if ( pDynSym && hb_dynsymIsFunction( pDynSym ) )
         {
            hb_vmPushDynSym( pDynSym );
            hb_vmPushNil();
            hb_vmPushString( scope->szName, strlen( scope->szName ) );
            hb_vmPush( xNewValue );
            hb_vmDo( 2 );
         }
         break;
      }
   }
}

/*
 * .prg functions
 */
HB_FUNC( __DBGSETENTRY )
{
   hb_dbg_SetEntry( hb_dbgEntry );
}

HB_FUNC( __DBGSETGO )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetGo( ptr );
}

HB_FUNC( __DBGSETTRACE )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetTrace( ptr );
}

HB_FUNC( __DBGSETCBTRACE )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetCBTrace( ptr, hb_parl( 2 ) );
}

HB_FUNC( __DBGSETNEXTROUTINE )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetNextRoutine( ptr );
}

HB_FUNC( __DBGSETQUIT )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetQuit( ptr );
}

HB_FUNC( __DBGSETTOCURSOR )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetToCursor( ptr, hb_parc( 2 ), hb_parni( 3 ) );
}

HB_FUNC( __DBGGETEXPRVALUE )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
   {
      PHB_ITEM pItem;

      if( HB_ISCHAR( 2 ) )
         pItem = hb_dbgGetExpressionValue( hb_parptr( 1 ), hb_parc( 2 ) );
      else
         pItem = hb_dbgGetWatchValue( hb_parptr( 1 ), hb_parni( 2 ) - 1 );

      if( pItem )
      {
         hb_storl( TRUE, 3 );
         hb_itemReturnRelease( pItem );
      }
      else
         hb_storl( FALSE, 3 );
   }
}

HB_FUNC( __DBGGETSOURCEFILES )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_itemReturnRelease( hb_dbgGetSourceFiles( ptr ) );
}

HB_FUNC( __DBGISVALIDSTOPLINE )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_retl( hb_dbgIsValidStopLine( ptr, hb_parc( 2 ), hb_parni( 3 ) ) );
}

HB_FUNC( __DBGADDBREAK )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgAddBreak( ptr, hb_parc( 2 ), hb_parni( 3 ), NULL );
}

HB_FUNC( __DBGDELBREAK )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgDelBreak( ptr, hb_parni( 2 ) );
}

HB_FUNC( __DBGADDWATCH )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgAddWatch( ptr, hb_parc( 2 ), hb_parl( 3 ) );
}

HB_FUNC( __DBGDELWATCH )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgDelWatch( ptr, hb_parni( 2 ) );
}

HB_FUNC( __DBGSETWATCH )
{
   void * ptr = hb_parptr( 1 );
   if( ptr )
      hb_dbgSetWatch( ptr, hb_parni( 2 ), hb_parc( 3 ), hb_parl( 4 ) );
}

HB_FUNC( __DBGSENDMSG )
{
   hb_dbg_objSendMessage( hb_parnl( 1 ), hb_param( 2, HB_IT_ANY ),
                          hb_param( 3, HB_IT_ANY ), 4 );
}
