www.pudn.com > helpview.zip > Macroeng.c, change:1997-10-15,size:73655b


/* 
  
Copyright 1997 Willows Software, Inc.  
 
This library is free software; you can redistribute it and/or 
modify it under the terms of the GNU Library General Public License as 
published by the Free Software Foundation; either version 2 of the 
License, or (at your option) any later version. 
 
This library 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 
Library General Public License for more details. 
 
You should have received a copy of the GNU Library General Public 
License along with this library; see the file COPYING.LIB.  If 
not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
Cambridge, MA 02139, USA. 
 
*/ 
 
/************************************************************************* 
* 
* MACROENG.C 
* Macro engine support routines.      
* 
* Author: Paul E. Kissel 
* 
**************************************************************************/ 
 
 
/*********************************** 
** 
**  System Includes 
** 
***********************************/ 
#include <stdlib.h> 
 
 
/*********************************** 
** 
**  App. Includes 
** 
***********************************/ 
#include "macroeng.h" 
#include "winmem.h" 
#include "string.h" 
#include "msgbox.h" 
#include "twhlprc.h" 
#include "globals.h" 
 
 
/*********************************** 
** 
**  Private Defines 
** 
***********************************/ 
 
/* How many default macros are there. */ 
#define NUMDEFAULTMACROS ( sizeof DefaultMacros / sizeof DefaultMacros[0] ) 
 
/*  
** Information for each instance of a macro engine. 
*/ 
typedef struct tagMACROENGINE 
{ 
  HGLOBAL hRegTableList;     /* Registered tables (Linked list of REGTABLEREC structures). */ 
  HGLOBAL hLoadTableStack;   /* Loaded tables stack (Linked list of REGTABLEREC structures). */ 
  HGLOBAL hExecutionStack;   /* Macro execution stack. */ 
}  
MACROENGINE; 
typedef MACROENGINE * MACROENGINEPTR; 
 
 
/*  
** Record in the list of registered tables.  
*/ 
typedef HGLOBAL HREGTABLEREC; 
typedef struct tagREGTABLEREC 
{ 
  /* Linked list data. */ 
  HREGTABLEREC hPrev; 
  HREGTABLEREC hNext; 
   
  /* Record data. */ 
  char    szTableName[ MAX_TABLE_NAME ];  /* Name of the table. */ 
  char    szAppData[ MAX_APP_DATA ];      /* String data that the app. assoc. with the table. */ 
  WORD    wNumMacros;                     /* Number of macros in the table. */ 
  HGLOBAL hAtomList;                      /* Handle to the first ATOM in the list. */ 
  HGLOBAL hMacroTable;                    /* Handle to the first REGTABLEENTRY in the table. */ 
}  
REGTABLEREC; 
typedef REGTABLEREC * REGTABLERECPTR; 
 
/*  
** A reg. table entry contains information 
** for one macro that is in a registered 
** macro table. 
*/ 
typedef struct tagREGTABLEENTRY 
{ 
  MACROFUNCPTR MacroFuncPtr; 
  HGLOBAL      hMacroData; 
}  
REGTABLEENTRY; 
typedef REGTABLEENTRY * REGTABLEENTRYPTR; 
 
 
/*  
** Record in the loaded tables stack. 
*/ 
typedef HGLOBAL HLOADEDTABLEREC; 
typedef struct tagLOADEDTABLEREC 
{ 
  /* Linked list data. */ 
  HLOADEDTABLEREC hPrev; 
  HLOADEDTABLEREC hNext; 
   
  /* Record data. */ 
  HGLOBAL hRegTableRec;    /* Copy of a handle to the registered table. */ 
}  
LOADEDTABLEREC; 
typedef LOADEDTABLEREC * LOADEDTABLERECPTR; 
 
 
/*  
** Record in the execution stack.  
*/ 
typedef HGLOBAL HEXECREC; 
typedef struct tagEXECREC 
{ 
  /* Linked list data. */ 
  HEXECREC hPrev; 
  HEXECREC hNext; 
   
  /* Record data. */ 
  WORD      wPosition;          /* Current position in the macro string being executed. */ 
  HGLOBAL   hMacroString;       /* Macro string being executed. */ 
  short int nOpenParen;         /* Number of parenthesis still left open. */ 
}  
EXECREC; 
typedef EXECREC * EXECRECPTR; 
 
 
/* Enumeration callback function for . */ 
typedef BOOL ( CALLBACK * ENUMREGTABLERECPROC )( HREGTABLEREC hRegTableRec, LPARAM lParam ); 
typedef BOOL ( CALLBACK * ENUMLOADEDTABLERECPROC )( HLOADEDTABLEREC hLoadedTableRec, LPARAM lParam ); 
 
/* 
** Data sent to FindRegTable() enumeration call-back function. 
*/ 
typedef struct tagFINDREGTABLEDATA 
{ 
  char * szTableName; 
  HREGTABLEREC hRegTableRec; 
} 
FINDREGTABLEDATA; 
typedef FINDREGTABLEDATA * FINDREGTABLEDATAPTR; 
 
/* Token used in macro command expressions. */ 
#define CMDSEP_CHAR_TOKEN ';' 
 
 
/* 
** Data for GetCommandInfo() function and the enumeration functions that 
** it uses. 
*/ 
typedef struct tagGETCOMMANDINFODATA 
{ 
  BOOL bFound;                      /* Did we find data for the command. */ 
  char * szMacroCommand;      /* Command. */ 
  MACROFUNCPTR MacroFuncPtr;        /* Command's function. */ 
  HGLOBAL      hMacroData;          /* Command's data. */ 
} 
GETCOMMANDINFODATA; 
typedef GETCOMMANDINFODATA * GETCOMMANDINFODATAPTR; 
 
typedef char * STRPTR; 
typedef STRPTR * LPSTRPTR; 
 
typedef WORD * WORDPTR; 
typedef WORDPTR * LPWORDPTR; 
 
typedef DWORD * DWORDPTR; 
typedef DWORDPTR * LPDWORDPTR; 
 
typedef short int * INTPTR; 
typedef INTPTR * LPINTPTR; 
 
typedef long int * LONGPTR; 
typedef INTPTR * LPLONGPTR; 
 
 
 
/*********************************** 
** 
**  Private Function Prototypes 
** 
***********************************/ 
 
static BOOL CreateRegMacroTable 
(  
  HWND hErrorWnd, 
  HGLOBAL * hRegTableListPtr, 
  char * szTableName,  
  WORD wNumMacros, 
  MACROTABLEENTRYPTR MacroTableEntryPtr, 
  char * szAppData 
); 
static HREGTABLEREC DestroyRegMacroTable 
(  
  HGLOBAL * hRegTableListPtr, 
  char * szTableName  
); 
static void DestroyRegTableList 
(  
  HGLOBAL * hRegTableListPtr 
); 
static void FreeRegTableRec( HREGTABLEREC hRegTableRec ); 
static BOOL CALLBACK FindRegTable( HREGTABLEREC hRegTableRec, LPARAM lParam ); 
static void EnumRegTableRec( HGLOBAL hRegTableList, ENUMREGTABLERECPROC EnumRegTableRecProc, LPARAM lParam ); 
static BOOL FindRegTableMacro 
(  
  HREGTABLEREC hRegTableRec,  
  char * szMacroCommand,  
  MACROFUNCPTR * MacroFuncDblPtr,  
  HGLOBAL * hMacroDataPtr  
); 
 
 
static BOOL UnloadAllOneTable 
(  
  HGLOBAL * hLoadTableStackPtr,  
  HREGTABLEREC hRegTableRec  
); 
static void DestroyLoadTableStack 
(  
  HGLOBAL * hLoadTableStackPtr  
); 
static void EnumLoadTableStack( HGLOBAL hLoadTableStack, ENUMLOADEDTABLERECPROC EnumLoadedTableRecProc, LPARAM lParam ); 
 
 
static BOOL PushMacroString 
(  
  HWND hErrorWnd, 
  HGLOBAL * hExecutionStackPtr, 
  char * MacroStringPtr 
); 
static BOOL PopMacroString 
(  
  HGLOBAL * hExecutionStackPtr 
); 
static BOOL GetNextMacroCommand 
(  
  HWND hErrorWnd, 
  MACROENGINEPTR MacroEnginePtr,  
  MACROFUNCPTR * MacroFuncDblPtr, 
  HGLOBAL * hMacroDataPtr, 
  BOOL bShowError 
); 
static void DestroyExecutionStack 
(  
  HGLOBAL * hExecutionStackPtr  
); 
static BOOL GetCommandInfo 
(  
  char * szMacroCommand, 
  HGLOBAL hLoadTableStack,  
  MACROFUNCPTR * MacroFuncDblPtr,  
  HGLOBAL * hMacroDataPtr  
); 
static BOOL CALLBACK GetCommandInfoEx( HLOADEDTABLEREC hLoadedTableRec, LPARAM lParam ); 
 
 
static BOOL CALLBACK MacroLoadMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
); 
static BOOL CALLBACK MacroUnloadMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
); 
static BOOL CALLBACK MacroExecuteMacroFile 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
); 
 
static BOOL HexConvert( char * ArgPtr, long unsigned int * nHexConvert ); 
 
 
/*********************************** 
** 
**  Global Static Data 
** 
***********************************/ 
 
/* Default macros. */ 
static MACROTABLEENTRY DefaultMacros[] = 
{ 
  "LoadMacroTable",       MacroLoadMacroTable,       NULL,   
  "UnloadMacroTable",     MacroUnloadMacroTable,     NULL,   
  "ExecuteMacroFile",     MacroExecuteMacroFile,     NULL   
}; 
 
 
 
/********************************************************************************************* 
** 
**                                   Shared Functions 
** 
**********************************************************************************************/ 
 
 
/************************************ ENGINE FUNCTIONS *****************************/ 
 
/*********************************** 
** 
**  Start macro engine.  
** 
**  Returns: 
** 
**     Success - Handle to macro engine. 
**     Failure - NULL. 
** 
***********************************/ 
HMACROENGINE __declspec(dllexport) StartMacroEngine( HWND hErrorWnd ) 
{ 
  HMACROENGINE   hMacroEngine; 
  MACROENGINEPTR MacroEnginePtr; 
   
   
  /* Alloc. instance data. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hMacroEngine, sizeof(MACROENGINE) ) ) 
  { 
    /* Failure. */ 
    return( NULL ); 
  } 
 
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /* Initialize structure. */ 
  MacroEnginePtr->hRegTableList   = NULL; 
  MacroEnginePtr->hLoadTableStack = NULL; 
  MacroEnginePtr->hExecutionStack = NULL; 
   
  /* Register default macro table. */ 
  if( !RegisterMacroTable( hErrorWnd, hMacroEngine, RESERVED_TABLE_NAME,  
                           NUMDEFAULTMACROS, DefaultMacros, NULL ) ) 
  { 
    /* Free engine data. */ 
    GlobalUnlock( hMacroEngine ); 
    MyGlobalFree( hMacroEngine ); 
   
    /* Failure. */ 
    return( NULL ); 
  } 
   
  /* Load default macro table. */ 
  if( ! LoadMacroTable( hErrorWnd, hMacroEngine, RESERVED_TABLE_NAME ) ) 
  { 
    /* Unregister default macro table. */ 
    UnregisterMacroTable( hMacroEngine, RESERVED_TABLE_NAME ); 
     
    /* Free engine data. */ 
    GlobalUnlock( hMacroEngine ); 
    MyGlobalFree( hMacroEngine ); 
   
    /* Failure. */ 
    return( NULL ); 
  } 
 
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success. */ 
  return( hMacroEngine ); 
} 
 
 
 
/*********************************** 
** 
**  Stop macro system and frees 
**  resource associated with the  
**  instance of the engine.  
** 
***********************************/ 
void __declspec(dllexport) StopMacroEngine( HMACROENGINE * hMacroEnginePtr ) 
{ 
  MACROENGINEPTR MacroEnginePtr; 
   
   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( *hMacroEnginePtr ); 
   
  /* Destroy macro table registration list. */ 
  DestroyRegTableList( &(MacroEnginePtr)->hRegTableList ); 
 
  /* Destroy loaded table stack. */ 
  DestroyLoadTableStack( &(MacroEnginePtr)->hLoadTableStack ); 
 
  /* Destroy macro execution stack. */ 
  DestroyExecutionStack( &(MacroEnginePtr)->hExecutionStack ); 
 
  /* Free engine data. */ 
  GlobalUnlock( *hMacroEnginePtr ); 
  MyGlobalFree( *hMacroEnginePtr ); 
  *hMacroEnginePtr = NULL; 
} 
 
 
/******************************** TABLE REGISTRATION FUNCTIONS *****************************/ 
   
/*********************************** 
** 
**  Registers a table of macros.  The 
**  table can then be loaded and unloaded.  
** 
***********************************/ 
BOOL __declspec(dllexport) RegisterMacroTable 
(  
  HWND hErrorWnd, 
  HMACROENGINE hMacroEngine, 
  char * szTableName,  
  WORD wNumMacros, 
  MACROTABLEENTRYPTR MacroTableEntryPtr, 
  char * szAppData 
) 
{ 
  MACROENGINEPTR MacroEnginePtr; 
  BOOL bReturn; 
     
                                   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /*   
  ** Create and insert a new macro table into the registered macro 
  ** table list. 
  */ 
  bReturn = CreateRegMacroTable( hErrorWnd, &(MacroEnginePtr)->hRegTableList,  
                                 szTableName, wNumMacros, MacroTableEntryPtr, 
                                 szAppData ); 
 
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success/Failure. */ 
  return( bReturn ); 
} 
 
 
/*********************************** 
** 
**  Unregisters a table of macros.  The 
**  table can not be loaded and unloaded. 
** 
**  All loaded instances of the table are 
**  unloaded.  
** 
***********************************/ 
BOOL __declspec(dllexport) UnregisterMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  char * szTableName 
) 
{ 
  MACROENGINEPTR MacroEnginePtr; 
  HREGTABLEREC   hDestroyTableRec; 
 
                                   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /*   
  ** Destroy information associated with the registered macro 
  ** table list. 
  */ 
  hDestroyTableRec = DestroyRegMacroTable( &(MacroEnginePtr)->hRegTableList, szTableName ); 
 
  /* If we found something to delete, update the loaded table stack. */ 
  if( hDestroyTableRec != NULL ) 
  { 
    /* Remove all instances of the table from the loaded table stack. */ 
    UnloadAllOneTable( &(MacroEnginePtr)->hLoadTableStack, hDestroyTableRec ); 
  } 
   
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success/Failure. */ 
  return( hDestroyTableRec != NULL ); 
} 
 
 
/*********************************** 
** 
**  Asks the call-back function if 
**  the table should be unregistered 
**  and if it returns TRUE then 
**  the table is unregistered. 
** 
***********************************/ 
void UnregMacroTablesQuery 
(  
  HMACROENGINE hMacroEngine, 
  UNREGTABLEPROC UnregTableProc,  
  LPARAM lParam  
) 
{ 
  MACROENGINEPTR  MacroEnginePtr; 
   
  HREGTABLEREC hCurr; 
  HREGTABLEREC hNext; 
 
  REGTABLERECPTR CurrTableRecPtr, TempTableRecPtr; 
   
  BOOL bUnregister; 
   
   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /* Get handle of first record. */ 
  hCurr = MacroEnginePtr->hRegTableList; 
   
  /* Not empty list. */                                   
  while( hCurr != NULL ) 
  { 
    /* Lock the reg. table record. */ 
    CurrTableRecPtr = (REGTABLERECPTR) GlobalLock( hCurr ); 
     
    /* Get next. */ 
    hNext = CurrTableRecPtr->hNext; 
 
    /* Do unregister the reserved table. */ 
    if( strcmp( RESERVED_TABLE_NAME, CurrTableRecPtr->szTableName ) == 0 ) 
    { 
      /* Unlock the reg. table record. */ 
      GlobalUnlock( hCurr ); 
 
      /* New current record. */ 
      hCurr = hNext; 
 
      continue; 
    } 
     
    /* Call the call-back function. */ 
    bUnregister = (*UnregTableProc)( CurrTableRecPtr->szTableName,  
                                     CurrTableRecPtr->szAppData, lParam ); 
 
    /* Do not unregister table. */ 
    if( bUnregister ) 
    { 
      /* Remove all instances of the table from the loaded table stack. */ 
      UnloadAllOneTable( &(MacroEnginePtr)->hLoadTableStack, hCurr ); 
 
      /* Adjust prev. record to point to next. */ 
      if( CurrTableRecPtr->hPrev != NULL ) 
      { 
        /* Lock the prev. reg. table record. */ 
        TempTableRecPtr = (REGTABLERECPTR) GlobalLock( CurrTableRecPtr->hPrev ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hNext = CurrTableRecPtr->hNext; 
 
        /* Unlock the prev. reg. table record. */ 
        GlobalUnlock( CurrTableRecPtr->hPrev ); 
      } 
     
      /* Adjust next record to point to prev. */ 
      if( CurrTableRecPtr->hNext != NULL ) 
      { 
        /* Lock the next reg. table record. */ 
        TempTableRecPtr = (REGTABLERECPTR) GlobalLock( CurrTableRecPtr->hNext ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hPrev = CurrTableRecPtr->hPrev; 
 
        /* Unlock the next reg. table record. */ 
        GlobalUnlock( CurrTableRecPtr->hNext ); 
      } 
     
      /* If we have a new top. */ 
      if( hCurr == MacroEnginePtr->hRegTableList ) 
      { 
        MacroEnginePtr->hRegTableList = CurrTableRecPtr->hNext; 
      } 
       
      /* Unlock the current reg. table record. */ 
      GlobalUnlock( hCurr ); 
 
      /* Free the record and its data. */ 
      FreeRegTableRec( hCurr ); 
    } 
    else 
    { 
      /* Unlock the reg. table record. */ 
      GlobalUnlock( hCurr ); 
    } 
     
    /* New current record. */ 
    hCurr = hNext; 
  }  
 
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
} 
 
 
 
/*********************************** 
** 
**  Create and insert a new macro 
**  table into the registered macro 
**  table list. 
** 
***********************************/ 
static BOOL CreateRegMacroTable 
(  
  HWND hErrorWnd, 
  HGLOBAL * hRegTableListPtr, 
  char * szTableName,  
  WORD wNumMacros, 
  MACROTABLEENTRYPTR MacroTableEntryPtr, 
  char * szAppData 
) 
{ 
  REGTABLERECPTR RegTableRecPtr; 
   
  WORD               wCounter; 
   
  HREGTABLEREC   hNewRegTableRec; 
  REGTABLERECPTR NewRegTableRecPtr; 
   
  ATOM * CurrAtomPtr; 
   
  REGTABLEENTRYPTR RegTableEntryPtr; 
   
  FINDREGTABLEDATA FindRegTableData; 
   
   
  /* Not empty list. */                                   
  if( *hRegTableListPtr != NULL ) 
  { 
    /* Set search criteria. */ 
    FindRegTableData.hRegTableRec = NULL; 
    FindRegTableData.szTableName  = szTableName; 
     
    /* Verify that no other table with that name exists. */ 
    EnumRegTableRec( *hRegTableListPtr, FindRegTable, (LPARAM) (FINDREGTABLEDATAPTR) &FindRegTableData ); 
   
    /* Duplicate found? */ 
    if( FindRegTableData.hRegTableRec != NULL ) 
    { 
      /* Failure. */ 
      return( FALSE ); 
    } 
  } 
 
 
  /* Allocate space for the reg. macro table record. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hNewRegTableRec, sizeof(REGTABLEREC) ) ) 
  { 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Lock the new record. */ 
  NewRegTableRecPtr = (REGTABLERECPTR) GlobalLock( hNewRegTableRec ); 
   
  /* Initialize the record's data. */ 
  strcpy( NewRegTableRecPtr->szTableName, szTableName ); 
   
  /* If there is app. data. */ 
  if( szAppData != NULL ) 
  { 
    strcpy( NewRegTableRecPtr->szAppData, szAppData ); 
  } 
  else 
  { 
    NewRegTableRecPtr->szAppData[0] = '\0'; 
  } 
   
  NewRegTableRecPtr->wNumMacros = wNumMacros; 
  NewRegTableRecPtr->hAtomList = NULL; 
  NewRegTableRecPtr->hMacroTable = NULL; 
   
  /* Allocate the atom list. */ 
  if( ! GlobalAllocMem( hErrorWnd, &(NewRegTableRecPtr)->hAtomList, wNumMacros * sizeof(ATOM) ) ) 
  { 
    /* Free the new record. */ 
    GlobalUnlock( hNewRegTableRec ); 
    MyGlobalFree( hNewRegTableRec ); 
 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Lock the atom list. */ 
  CurrAtomPtr = (ATOM *) GlobalLock( NewRegTableRecPtr->hAtomList ); 
 
 
  /* Allocate the macro table. */ 
  if( ! GlobalAllocMem( hErrorWnd, &(NewRegTableRecPtr)->hMacroTable, wNumMacros * sizeof(REGTABLEENTRY) ) ) 
  { 
    /* Free the atom table. */ 
    GlobalUnlock( NewRegTableRecPtr->hAtomList ); 
    MyGlobalFree( NewRegTableRecPtr->hAtomList ); 
 
    /* Free the new record. */ 
    GlobalUnlock( hNewRegTableRec ); 
    MyGlobalFree( hNewRegTableRec ); 
 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Lock the first entry in the macro table. */ 
  RegTableEntryPtr = (REGTABLEENTRYPTR) GlobalLock( NewRegTableRecPtr->hMacroTable ); 
   
   
  /* For each macro table entry. */ 
  for( wCounter = 0; wCounter < wNumMacros; wCounter++ ) 
  { 
    /* Make atom for the macro's name. */ 
    *CurrAtomPtr = AddAtom( MacroTableEntryPtr->MacroNamePtr ); 
     
    /* Save function name and data. */ 
    RegTableEntryPtr->MacroFuncPtr = MacroTableEntryPtr->MacroFuncPtr; 
    RegTableEntryPtr->hMacroData   = MacroTableEntryPtr->hMacroData; 
 
    /* Move to next macro table entry. */ 
    CurrAtomPtr++; 
    RegTableEntryPtr++; 
    MacroTableEntryPtr++; 
  } 
   
  /* Unlock the atom list. */ 
  GlobalUnlock( NewRegTableRecPtr->hAtomList ); 
 
  /* Unlock the macro table. */ 
  GlobalUnlock( NewRegTableRecPtr->hMacroTable ); 
 
  /* Unlock the new record. */ 
  GlobalUnlock( hNewRegTableRec ); 
   
  /* Not empty list. */                                   
  if( *hRegTableListPtr != NULL ) 
  { 
    /* Lock the reg. table list. */ 
    RegTableRecPtr = (REGTABLERECPTR) GlobalLock( *hRegTableListPtr ); 
   
    /* Set previous record. */ 
    RegTableRecPtr->hPrev = hNewRegTableRec; 
 
    /* Unlock the reg. table list. */ 
    GlobalUnlock( *hRegTableListPtr ); 
  } 
   
  /* Lock the new reg. table record. */ 
  RegTableRecPtr = (REGTABLERECPTR) GlobalLock( hNewRegTableRec ); 
 
  /* Set previous record. */ 
  RegTableRecPtr->hPrev = NULL; 
  RegTableRecPtr->hNext = *hRegTableListPtr; 
   
  /* Unlock the new reg. table record. */ 
  GlobalUnlock( hNewRegTableRec ); 
 
  /* New top. */ 
  *hRegTableListPtr = hNewRegTableRec; 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/*********************************** 
** 
**  Destroy information associated  
**  with a table in the registered  
**  macro table list. 
** 
***********************************/ 
static HREGTABLEREC DestroyRegMacroTable 
(  
  HGLOBAL * hRegTableListPtr, 
  char * szTableName  
) 
{ 
  HREGTABLEREC   hCurrTableRec; 
  HREGTABLEREC   hNextTableRec; 
  REGTABLERECPTR CurrTableRecPtr; 
  REGTABLERECPTR TempTableRecPtr; 
 
 
  HREGTABLEREC   hDestroyTableRec; 
     
   
  /* Empty list. */                                   
  if( *hRegTableListPtr == NULL ) return NULL; 
 
  /* Nothing destroyed yet. */ 
  hDestroyTableRec = NULL; 
  
  /* Get handle to first record. */ 
  hCurrTableRec = *hRegTableListPtr; 
   
  while( hCurrTableRec != NULL ) 
  { 
    /* Lock the current reg. table record. */ 
    CurrTableRecPtr = (REGTABLERECPTR) GlobalLock( hCurrTableRec ); 
   
    /* Found name? */ 
    if( strcmp( CurrTableRecPtr->szTableName, szTableName ) == 0 ) 
    { 
      /* Table destroyed. */ 
      hDestroyTableRec = hCurrTableRec; 
       
      /* Adjust prev. record to point to next. */ 
      if( CurrTableRecPtr->hPrev != NULL ) 
      { 
        /* Lock the prev. reg. table record. */ 
        TempTableRecPtr = (REGTABLERECPTR) GlobalLock( CurrTableRecPtr->hPrev ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hNext = CurrTableRecPtr->hNext; 
 
        /* Unlock the prev. reg. table record. */ 
        GlobalUnlock( CurrTableRecPtr->hPrev ); 
      } 
     
      /* Adjust next record to point to prev. */ 
      if( CurrTableRecPtr->hNext != NULL ) 
      { 
        /* Lock the next reg. table record. */ 
        TempTableRecPtr = (REGTABLERECPTR) GlobalLock( CurrTableRecPtr->hNext ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hPrev = CurrTableRecPtr->hPrev; 
 
        /* Unlock the next reg. table record. */ 
        GlobalUnlock( CurrTableRecPtr->hNext ); 
      } 
     
      /* If we have a new top. */ 
      if( hCurrTableRec == *hRegTableListPtr ) 
      { 
        *hRegTableListPtr = CurrTableRecPtr->hNext; 
      } 
       
      /* Unlock the current reg. table record. */ 
      GlobalUnlock( hCurrTableRec ); 
 
      /* Free the record and its data. */ 
      FreeRegTableRec( hCurrTableRec ); 
 
      /* Stop looking. */ 
      hCurrTableRec = NULL; 
    } 
      
    /* Keep looking. */ 
    else 
    { 
      /* Get next record's handle. */ 
      hNextTableRec = CurrTableRecPtr->hNext; 
     
      /* Unlock the current reg. table record. */ 
      GlobalUnlock( hCurrTableRec ); 
 
      /* New current. */ 
      hCurrTableRec = hNextTableRec; 
    } 
  }  
   
  /* Return handle of destroyed record. */ 
  return( hDestroyTableRec ); 
} 
 
 
/*********************************** 
** 
**  Free all registered macro table 
**  list records. 
** 
***********************************/ 
static void DestroyRegTableList 
(  
  HGLOBAL * hRegTableListPtr 
) 
{ 
  HREGTABLEREC   hCurrTableRec; 
  HREGTABLEREC   hNextTableRec; 
  REGTABLERECPTR CurrTableRecPtr; 
 
   
  /* Get handle to first record. */ 
  hCurrTableRec = *hRegTableListPtr; 
   
  while( hCurrTableRec != NULL ) 
  { 
    /* Lock the current reg. table record. */ 
    CurrTableRecPtr = (REGTABLERECPTR) GlobalLock( hCurrTableRec ); 
   
    /* Get next record's handle. */ 
    hNextTableRec = CurrTableRecPtr->hNext; 
 
    /* Unlock the current reg. table record. */ 
    GlobalUnlock( hCurrTableRec ); 
 
    /* Free the record and its data. */ 
    FreeRegTableRec( hCurrTableRec ); 
 
    /* New current. */ 
    hCurrTableRec = hNextTableRec; 
  } 
 
  /* Empty list. */ 
  *hRegTableListPtr = NULL; 
} 
 
 
/*********************************** 
** 
**  Delete a registration table list's 
**  record and its contents. 
** 
***********************************/ 
static void FreeRegTableRec( HREGTABLEREC hRegTableRec ) 
{ 
  REGTABLERECPTR RegTableRecPtr; 
 
  ATOM * CurrAtomPtr; 
   
  REGTABLEENTRYPTR RegTableEntryPtr; 
   
  WORD wCounter; 
 
 
  /* Lock the reg. table record. */ 
  RegTableRecPtr = (REGTABLERECPTR) GlobalLock( hRegTableRec ); 
       
  /* Lock the first entry in the macro table. */ 
  RegTableEntryPtr = (REGTABLEENTRYPTR) GlobalLock( RegTableRecPtr->hMacroTable ); 
       
  /* Lock first entry in the atom list. */ 
  CurrAtomPtr = (ATOM *) GlobalLock( RegTableRecPtr->hAtomList ); 
 
  /* For each macro table entry. */ 
  for( wCounter = 0; wCounter < RegTableRecPtr->wNumMacros; wCounter++ ) 
  { 
    /* Delete the atom. */ 
    DeleteAtom( *CurrAtomPtr ); 
         
    /* Free the macro's associated data if there is any. */ 
    if( RegTableEntryPtr->hMacroData != 0 ) 
    { 
      MyGlobalFree( RegTableEntryPtr->hMacroData ); 
    } 
     
    /* Move to next macro table entry. */ 
    CurrAtomPtr++; 
    RegTableEntryPtr++; 
  } 
 
  /* Free the the macro table. */ 
  GlobalUnlock( RegTableRecPtr->hMacroTable ); 
  MyGlobalFree( RegTableRecPtr->hMacroTable ); 
 
  /* Free the the ATOM list. */ 
  GlobalUnlock( RegTableRecPtr->hAtomList ); 
  MyGlobalFree( RegTableRecPtr->hAtomList ); 
 
  /* Free the reg. table record. */ 
  GlobalUnlock( hRegTableRec ); 
  MyGlobalFree( hRegTableRec); 
} 
 
 
/*********************************** 
** 
**  Search for a registered macro 
**  table list with the specified 
**  name. 
** 
***********************************/ 
static BOOL CALLBACK FindRegTable( HREGTABLEREC hRegTableRec, LPARAM lParam ) 
{ 
  REGTABLERECPTR RegTableRecPtr; 
  FINDREGTABLEDATAPTR FindRegTableDataPtr; 
   
  /* Get search criteria. */ 
  FindRegTableDataPtr = (FINDREGTABLEDATAPTR) lParam; 
 
  /* Lock the reg. table record. */ 
  RegTableRecPtr = (REGTABLERECPTR) GlobalLock( hRegTableRec ); 
     
  /* Found name? */ 
  if( strcmp( RegTableRecPtr->szTableName, FindRegTableDataPtr->szTableName ) == 0 ) 
  { 
    FindRegTableDataPtr->hRegTableRec = hRegTableRec; 
  } 
   
  /* Unlock the reg. table record. */ 
  GlobalUnlock( hRegTableRec ); 
   
  /* If we didn't found it keep enumerating. */ 
  return( FindRegTableDataPtr->hRegTableRec == NULL ); 
}   
 
      
      
/*********************************** 
** 
**  Pass each record in the  
**  hRegTableList to the call-back 
**  function. 
** 
***********************************/ 
static void EnumRegTableRec 
(  
  HGLOBAL hRegTableList,  
  ENUMREGTABLERECPROC EnumRegTableRecProc,  
  LPARAM lParam  
) 
{ 
   
  HREGTABLEREC hCurr; 
  HREGTABLEREC hNext; 
 
  REGTABLERECPTR RegTableRecPtr; 
   
  BOOL bContinue; 
   
   
  /* Get handle of first record. */ 
  hCurr = hRegTableList; 
   
  /* Not empty list. */                                   
  while( hCurr != NULL ) 
  { 
    /* Lock the reg. table record. */ 
    RegTableRecPtr = (REGTABLERECPTR) GlobalLock( hCurr ); 
     
    /* Get next. */ 
    hNext = RegTableRecPtr->hNext; 
 
    /* Unlock the reg. table record. */ 
    GlobalUnlock( hCurr ); 
 
    /* Call call-back function. */ 
    bContinue = (*EnumRegTableRecProc)( hCurr, lParam ); 
 
    /* If keep enumerating. */ 
    if( bContinue ) 
    { 
      /* New current record. */ 
      hCurr = hNext; 
    } 
     
    /* If not keep enumerating. */ 
    else  
    {  
      /* Stop. */ 
      hCurr = NULL; 
    } 
  }  
} 
 
 
 
/*********************************** 
** 
**  Find a macro's data in a 
**  registered table record. 
** 
***********************************/ 
static BOOL FindRegTableMacro 
(  
  HREGTABLEREC hRegTableRec,  
  char * szMacroCommand,  
  MACROFUNCPTR * MacroFuncDblPtr,  
  HGLOBAL * hMacroDataPtr  
) 
{ 
  REGTABLERECPTR RegTableRecPtr; 
  REGTABLEENTRYPTR RegTableEntryPtr; 
   
  ATOM SearchAtom; 
  ATOM * CurrAtomPtr; 
 
  WORD wCounter; 
  BOOL bFound;  
   
   
   
  /* Create an atom from the target macro's name. */ 
  SearchAtom = FindAtom( szMacroCommand ); 
  if( SearchAtom == 0 ) 
  { 
    /* If we didn't find the atom. */ 
    return( FALSE ); 
  } 
   
  /* Lock the record. */ 
  RegTableRecPtr = (REGTABLERECPTR) GlobalLock( hRegTableRec ); 
   
  /* Lock the atom list. */ 
  CurrAtomPtr = (ATOM *) GlobalLock( RegTableRecPtr->hAtomList ); 
 
  /* Haven't found atom. */ 
  bFound = FALSE; 
   
  /* For each atom in the table entry. */ 
  for( wCounter = 0; wCounter < RegTableRecPtr->wNumMacros; wCounter++ ) 
  { 
    /* Found the atom for the command. */ 
    if( *CurrAtomPtr == SearchAtom ) 
    { 
      bFound = TRUE; 
      break; 
    } 
 
    /* Move to next atom. */ 
    CurrAtomPtr++; 
  } 
   
  /* Unlock the atom list. */ 
  GlobalUnlock( RegTableRecPtr->hAtomList ); 
 
  /* If we didn't find the atom. */ 
  if( bFound == FALSE ) 
  { 
    /* Unlock the record. */ 
    GlobalLock( hRegTableRec ); 
   
    return( FALSE ); 
  } 
   
  /* Lock the first entry in the macro table. */ 
  RegTableEntryPtr = (REGTABLEENTRYPTR) GlobalLock( RegTableRecPtr->hMacroTable ); 
   
  /* Go to correct entry. */ 
  RegTableEntryPtr = RegTableEntryPtr + wCounter; 
   
  /* Get macro command's data. */ 
  *MacroFuncDblPtr = RegTableEntryPtr->MacroFuncPtr;  
  *hMacroDataPtr   = RegTableEntryPtr->hMacroData; 
 
  /* Unlock the macro table. */ 
  GlobalUnlock( RegTableRecPtr->hMacroTable ); 
 
  /* Unlock the record. */ 
  GlobalLock( hRegTableRec ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
 
/********************************** LOADED TABLES STACK FUNCTIONS *****************************/ 
 
/*********************************** 
** 
**  Adds the table to the top of the 
**  macro table stack. 
** 
**  It will be the first table that is 
**  considered when executing a macro. 
** 
***********************************/ 
BOOL __declspec(dllexport) LoadMacroTable 
(  
  HWND hErrorWnd, 
  HMACROENGINE hMacroEngine, 
  char * szTableName  
) 
{ 
  MACROENGINEPTR MacroEnginePtr; 
  FINDREGTABLEDATA FindRegTableData; 
   
  HLOADEDTABLEREC hNewLoadTableRec; 
  LOADEDTABLERECPTR LoadTableRecPtr; 
   
                                   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /* Set search criteria. */ 
  FindRegTableData.hRegTableRec = NULL; 
  FindRegTableData.szTableName  = szTableName; 
     
  /* Find the registered table's handle. */ 
  EnumRegTableRec( MacroEnginePtr->hRegTableList, FindRegTable, (LPARAM) (FINDREGTABLEDATAPTR) &FindRegTableData ); 
   
  /* Not found? */ 
  if( FindRegTableData.hRegTableRec == NULL ) 
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* Allocate space for the loaded table record. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hNewLoadTableRec, sizeof(LOADEDTABLEREC) ) ) 
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Not empty stack. */                                   
  if( MacroEnginePtr->hLoadTableStack != NULL ) 
  { 
    /* Lock the top of stack. */ 
    LoadTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( MacroEnginePtr->hLoadTableStack ); 
   
    /* Set previous record. */ 
    LoadTableRecPtr->hPrev = hNewLoadTableRec; 
 
    /* Unlock the top of stack. */ 
    GlobalUnlock( MacroEnginePtr->hLoadTableStack ); 
  } 
   
  /* Lock the new record. */ 
  LoadTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hNewLoadTableRec ); 
   
  /* Set new record's data. */ 
  LoadTableRecPtr->hPrev = NULL; 
  LoadTableRecPtr->hNext = MacroEnginePtr->hLoadTableStack; 
  LoadTableRecPtr->hRegTableRec = FindRegTableData.hRegTableRec; 
   
  /* Unlock the new reg. table record. */ 
  GlobalUnlock( hNewLoadTableRec ); 
 
  /* New top. */ 
  MacroEnginePtr->hLoadTableStack = hNewLoadTableRec; 
   
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/*********************************** 
** 
**  Remove the first instance of the 
**  table from the macro table list. 
** 
**  Other instances of the table will 
**  not be removed. 
** 
**  If no instance of the table exists, 
**  no action is performed. 
** 
***********************************/ 
BOOL __declspec(dllexport) UnloadMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  char * szTableName  
) 
{ 
  MACROENGINEPTR MacroEnginePtr; 
  FINDREGTABLEDATA FindRegTableData; 
   
  HLOADEDTABLEREC hCurrLoadTableRec; 
  HLOADEDTABLEREC hNextLoadTableRec; 
   
  LOADEDTABLERECPTR CurrLoadTableRecPtr; 
  LOADEDTABLERECPTR TempTableRecPtr; 
   
  BOOL bUnloaded; 
   
                                   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /* Empty stack? */ 
  if( MacroEnginePtr->hLoadTableStack == NULL )  
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
   
    /* Failure. */ 
    return FALSE; 
  } 
   
  /* Set search criteria. */ 
  FindRegTableData.hRegTableRec = NULL; 
  FindRegTableData.szTableName  = szTableName; 
     
  /* Find the registered table's handle. */ 
  EnumRegTableRec( MacroEnginePtr->hRegTableList, FindRegTable, (LPARAM) (FINDREGTABLEDATAPTR) &FindRegTableData ); 
   
  /* Not found? */ 
  if( FindRegTableData.hRegTableRec == NULL ) 
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
   
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* No unloaded yet. */ 
  bUnloaded = FALSE; 
 
  /* Get first record. */ 
  hCurrLoadTableRec = MacroEnginePtr->hLoadTableStack; 
    
  /* For each record. */ 
  while( hCurrLoadTableRec != NULL ) 
  { 
    /* Lock the current record. */ 
    CurrLoadTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hCurrLoadTableRec ); 
   
    /* Found name? */ 
    if( CurrLoadTableRecPtr->hRegTableRec == FindRegTableData.hRegTableRec ) 
    { 
      /* Something getting unloaded. */ 
      bUnloaded = TRUE; 
       
      /* Adjust prev. record to point to next. */ 
      if( CurrLoadTableRecPtr->hPrev != NULL ) 
      { 
        /* Lock the prev. record. */ 
        TempTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( CurrLoadTableRecPtr->hPrev ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hNext = CurrLoadTableRecPtr->hNext; 
 
        /* Unlock the prev. reg. table record. */ 
        GlobalUnlock( CurrLoadTableRecPtr->hPrev ); 
      } 
     
      /* Adjust next record to point to prev. */ 
      if( CurrLoadTableRecPtr->hNext != NULL ) 
      { 
        /* Lock the next reg. table record. */ 
        TempTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( CurrLoadTableRecPtr->hNext ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hPrev = CurrLoadTableRecPtr->hPrev; 
 
        /* Unlock the next reg. table record. */ 
        GlobalUnlock( CurrLoadTableRecPtr->hNext ); 
      } 
     
      /* If we have a new top. */ 
      if( hCurrLoadTableRec == MacroEnginePtr->hLoadTableStack ) 
      { 
        MacroEnginePtr->hLoadTableStack = CurrLoadTableRecPtr->hNext; 
      } 
       
      /* Free the current record. */ 
      GlobalUnlock( hCurrLoadTableRec ); 
      MyGlobalFree( hCurrLoadTableRec); 
 
      /* Stop looking. */ 
      hCurrLoadTableRec = NULL; 
    } 
      
    /* Keep looking. */ 
    else 
    { 
      /* Get next record's handle. */ 
      hNextLoadTableRec = CurrLoadTableRecPtr->hNext; 
     
      /* Unlock the current record. */ 
      GlobalUnlock( hCurrLoadTableRec ); 
 
      /* New current record. */ 
      hCurrLoadTableRec = hNextLoadTableRec; 
    } 
  }  
 
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success/Failure. */ 
  return( bUnloaded ); 
} 
 
 
 
/*********************************** 
** 
**  Remove all instances of a  
**  table from the loaded table stack. 
** 
***********************************/ 
static BOOL UnloadAllOneTable 
(  
  HGLOBAL * hLoadTableStackPtr,  
  HREGTABLEREC hRegTableRec  
) 
{ 
  HLOADEDTABLEREC hCurrLoadTableRec; 
  HLOADEDTABLEREC hNextLoadTableRec; 
   
  LOADEDTABLERECPTR CurrLoadTableRecPtr; 
  LOADEDTABLERECPTR TempTableRecPtr; 
   
  BOOL bUnloaded; 
   
                                   
  /* Empty stack? */ 
  if( *hLoadTableStackPtr == NULL )  
  { 
    /* Failure. */ 
    return FALSE; 
  } 
   
  /* No unloaded yet. */ 
  bUnloaded = FALSE; 
 
  /* Get first record. */ 
  hCurrLoadTableRec = *hLoadTableStackPtr; 
    
  /* For each record. */ 
  while( hCurrLoadTableRec != NULL ) 
  { 
    /* Lock the current record. */ 
    CurrLoadTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hCurrLoadTableRec ); 
   
    /* Found name? */ 
    if( CurrLoadTableRecPtr->hRegTableRec == hRegTableRec ) 
    { 
      /* Something getting unloaded. */ 
      bUnloaded = TRUE; 
       
      /* Adjust prev. record to point to next. */ 
      if( CurrLoadTableRecPtr->hPrev != NULL ) 
      { 
        /* Lock the prev. record. */ 
        TempTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( CurrLoadTableRecPtr->hPrev ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hNext = CurrLoadTableRecPtr->hNext; 
 
        /* Unlock the prev. reg. table record. */ 
        GlobalUnlock( CurrLoadTableRecPtr->hPrev ); 
      } 
     
      /* Adjust next record to point to prev. */ 
      if( CurrLoadTableRecPtr->hNext != NULL ) 
      { 
        /* Lock the next reg. table record. */ 
        TempTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( CurrLoadTableRecPtr->hNext ); 
         
        /* Prev.'s next points top current's next. */ 
        TempTableRecPtr->hPrev = CurrLoadTableRecPtr->hPrev; 
 
        /* Unlock the next reg. table record. */ 
        GlobalUnlock( CurrLoadTableRecPtr->hNext ); 
      } 
     
      /* If we have a new top. */ 
      if( hCurrLoadTableRec == *hLoadTableStackPtr ) 
      { 
        *hLoadTableStackPtr = CurrLoadTableRecPtr->hNext; 
      } 
       
      /* Get next record's handle. */ 
      hNextLoadTableRec = CurrLoadTableRecPtr->hNext; 
     
      /* Free the current record. */ 
      GlobalUnlock( hCurrLoadTableRec ); 
      MyGlobalFree( hCurrLoadTableRec); 
 
      /* New current record. */ 
      hCurrLoadTableRec = hNextLoadTableRec; 
    } 
      
    /* Keep looking. */ 
    else 
    { 
      /* Get next record's handle. */ 
      hNextLoadTableRec = CurrLoadTableRecPtr->hNext; 
     
      /* Unlock the current record. */ 
      GlobalUnlock( hCurrLoadTableRec ); 
 
      /* New current record. */ 
      hCurrLoadTableRec = hNextLoadTableRec; 
    } 
  }  
 
  /* Success/Failure. */ 
  return( bUnloaded ); 
} 
 
   
 
/*********************************** 
** 
**  Remove all tables from the  
**  loaded table stack. 
** 
***********************************/ 
static void DestroyLoadTableStack 
(  
  HGLOBAL * hLoadTableStackPtr  
) 
{ 
  HLOADEDTABLEREC hCurrLoadTableRec; 
  HLOADEDTABLEREC hNextLoadTableRec; 
   
  LOADEDTABLERECPTR CurrLoadTableRecPtr; 
   
  /* Get first record. */ 
  hCurrLoadTableRec = *hLoadTableStackPtr; 
    
  /* For each record. */ 
  while( hCurrLoadTableRec != NULL ) 
  { 
    /* Lock the current record. */ 
    CurrLoadTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hCurrLoadTableRec ); 
       
    /* Get next record's handle. */ 
    hNextLoadTableRec = CurrLoadTableRecPtr->hNext; 
     
    /* Free the current record. */ 
    GlobalUnlock( hCurrLoadTableRec ); 
    MyGlobalFree( hCurrLoadTableRec); 
 
    /* New current record. */ 
    hCurrLoadTableRec = hNextLoadTableRec; 
  } 
   
  /* Empty stack. */ 
  *hLoadTableStackPtr = NULL; 
} 
 
 
 
/*********************************** 
** 
**  Pass each record in the  
**  hLoadedTableStack to the call-back 
**  function. 
** 
***********************************/ 
static void EnumLoadTableStack 
(  
  HGLOBAL hLoadTableStack,  
  ENUMLOADEDTABLERECPROC EnumLoadedTableRecProc,  
  LPARAM lParam  
) 
{ 
   
  HLOADEDTABLEREC hCurr; 
  HLOADEDTABLEREC hNext; 
 
  LOADEDTABLERECPTR LoadedTableRecPtr; 
   
  BOOL bContinue; 
   
   
  /* Get handle of first record. */ 
  hCurr = hLoadTableStack; 
   
  /* Not empty list. */                                   
  while( hCurr != NULL ) 
  { 
    /* Lock the reg. table record. */ 
    LoadedTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hCurr ); 
     
    /* Get next. */ 
    hNext = LoadedTableRecPtr->hNext; 
 
    /* Unlock the reg. table record. */ 
    GlobalUnlock( hCurr ); 
 
    /* Call call-back function. */ 
    bContinue = (*EnumLoadedTableRecProc)( hCurr, lParam ); 
 
    /* If keep enumerating. */ 
    if( bContinue ) 
    { 
      /* New current record. */ 
      hCurr = hNext; 
    } 
     
    /* If not keep enumerating. */ 
    else  
    {  
      /* Stop. */ 
      hCurr = NULL; 
    } 
  }  
} 
 
 
 
/********************************** EXECUTION STACK FUNCTIONS *****************************/ 
 
/*********************************** 
** 
**  Execute a macro string.  
** 
**  Returns: 
** 
**      TRUE  - No error. 
**      FALSE - Error occured. 
** 
***********************************/ 
BOOL __declspec(dllexport) ExecuteMacro 
(  
  HWND hErrorWnd, 
  HMACROENGINE hMacroEngine, 
  char * MacroStringPtr, 
  MACRORETVAL * MacroRetValPtr, 
  void * UserDefinedDataPtr, 
  BOOL bShowError 
) 
{ 
  BOOL           bReturn; 
  MACROENGINEPTR MacroEnginePtr; 
   
  MACROFUNCPTR   MacroFuncPtr; 
  HGLOBAL        hMacroData; 
 
                                   
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /*  
  ** Push the new macro string onto the top of  
  ** the execution stack.  
  */ 
  if( ! PushMacroString( hErrorWnd, &(MacroEnginePtr)->hExecutionStack, MacroStringPtr ) ) 
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure */ 
    return( FALSE ); 
  } 
 
 
  /* Get first command - We should always have at least one command. */ 
  bReturn = GetNextMacroCommand( hErrorWnd, MacroEnginePtr, &MacroFuncPtr, &hMacroData, bShowError ); 
 
  /* No macro command found or no matching function for the command. */ 
  if( bReturn == FALSE || MacroFuncPtr == NULL ) 
  { 
    /* Pop unprocessed macro string off top of execution stack. */ 
    PopMacroString( &(MacroEnginePtr)->hExecutionStack ); 
  
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure */ 
    return( FALSE ); 
  } 
 
  /* Call the function associated with the macro command. */ 
  bReturn = (*MacroFuncPtr)( hMacroEngine, UserDefinedDataPtr, hMacroData, MacroRetValPtr );  
     
  /* Function failed. */ 
  if( bReturn == FALSE ) 
  { 
    /* Pop unprocessed macro string off top of execution stack. */ 
    PopMacroString( &(MacroEnginePtr)->hExecutionStack ); 
  
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure */ 
    return( FALSE ); 
  } 
 
 
  /* Process any other commands in the line. */ 
  while( TRUE ) 
  { 
    /* Get next command. */ 
    bReturn = GetNextMacroCommand( hErrorWnd, MacroEnginePtr, &MacroFuncPtr, &hMacroData, bShowError ); 
   
    /* Error. */ 
    if( bReturn == FALSE ) 
    { 
      /* Pop unprocessed macro string off top of execution stack. */ 
      PopMacroString( &(MacroEnginePtr)->hExecutionStack ); 
    
      /* Unlock engine data. */ 
      GlobalUnlock( hMacroEngine ); 
       
      /* Failure */ 
      return( FALSE ); 
    } 
 
   
    /* No error, but no more commands. */ 
    else if( bReturn == TRUE && MacroFuncPtr == NULL ) 
    { 
      /* Stop. */ 
      break; 
    } 
 
   
    /* Call the function associated with the macro command. */ 
    else  
    { 
      bReturn = (*MacroFuncPtr)( hMacroEngine, UserDefinedDataPtr, hMacroData, MacroRetValPtr );  
    } 
  } 
 
  /*  
  ** Pop the processed macro string from the top of the  
  ** execution stack.  
  */ 
  if( ! PopMacroString( &(MacroEnginePtr)->hExecutionStack ) ) 
  { 
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
     
    /* Failure */ 
    return( FALSE ); 
  } 
 
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
/*********************************** 
** 
** Push a macro string onto the 
** the top of the execution stack.  
** 
***********************************/ 
static BOOL PushMacroString 
(  
  HWND hErrorWnd, 
  HGLOBAL * hExecutionStackPtr, 
  char * MacroStringPtr 
) 
{ 
  HGLOBAL      hMacroString; 
  char * DstMacroStringPtr; 
  WORD         wStringSize; 
   
  HEXECREC   hNewExecRec; 
  EXECRECPTR ExecRecPtr; 
   
                                   
  /* Allocate space for the loaded table record. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hNewExecRec, sizeof(EXECREC) ) ) 
  { 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /*  
  ** Size of macro string.  
  ** 
  ** One extra char. added for teminating null char. 
  ** One extra char. added for ';' added as the first char. 
  */ 
  wStringSize = strlen( MacroStringPtr ) + 2; 
   
  /* Allocate space for the loaded table record. */ 
  if( ! GlobalAllocMem( hErrorWnd, &hMacroString, wStringSize ) ) 
  { 
    /* Free new record. */ 
    MyGlobalFree( hNewExecRec ); 
     
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* Copy macro string into buffer. */ 
  DstMacroStringPtr = (char *) GlobalLock( hMacroString ); 
  *DstMacroStringPtr = CMDSEP_CHAR_TOKEN; 
  strcat( DstMacroStringPtr, MacroStringPtr ); 
  GlobalUnlock( hMacroString ); 
 
  /* Not empty stack. */                                   
  if( *hExecutionStackPtr != NULL ) 
  { 
    /* Lock the top of stack. */ 
    ExecRecPtr = (EXECRECPTR) GlobalLock( *hExecutionStackPtr ); 
   
    /* Set previous record. */ 
    ExecRecPtr->hPrev = hNewExecRec; 
 
    /* Unlock the top of stack. */ 
    GlobalUnlock( *hExecutionStackPtr ); 
  } 
   
  /* Lock the new record. */ 
  ExecRecPtr = (EXECRECPTR) GlobalLock( hNewExecRec ); 
   
  /* Set new record's data. */ 
  ExecRecPtr->hPrev          = NULL; 
  ExecRecPtr->hNext          = *hExecutionStackPtr; 
  ExecRecPtr->wPosition      = 0; 
  ExecRecPtr->hMacroString   = hMacroString; 
  ExecRecPtr->nOpenParen     = 0; 
 
  /* Unlock the new reg. table record. */ 
  GlobalUnlock( hNewExecRec ); 
 
  /* New top. */ 
  *hExecutionStackPtr = hNewExecRec; 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
  
/*********************************** 
** 
** Pop a macro string from the 
** the top of the execution stack.  
** 
***********************************/ 
static BOOL PopMacroString 
(  
  HGLOBAL * hExecutionStackPtr 
) 
{ 
  HEXECREC   hOldExecRec; 
  EXECRECPTR OldExecRecPtr; 
  EXECRECPTR NextExecRecPtr; 
   
  /* Not empty stack. */                                   
  if( *hExecutionStackPtr == NULL ) return( FALSE ); 
 
  /* Save handle of record being freed. */ 
  hOldExecRec = *hExecutionStackPtr; 
   
  /* Lock the old record. */ 
  OldExecRecPtr = (EXECRECPTR) GlobalLock( hOldExecRec ); 
   
  /* Next record is new top. */ 
  *hExecutionStackPtr = OldExecRecPtr->hNext; 
   
  /* If there is a next record. */ 
  if( OldExecRecPtr->hNext != NULL ) 
  { 
    /* Lock the next record. */ 
    NextExecRecPtr = (EXECRECPTR) GlobalLock( OldExecRecPtr->hNext ); 
   
    /* Set prev. to nothing. */ 
    NextExecRecPtr->hPrev = NULL; 
 
    /* Unlock the next record. */ 
    GlobalUnlock( OldExecRecPtr->hNext ); 
  } 
 
  /* Free the old record. */ 
  MyGlobalFree( OldExecRecPtr->hMacroString ); 
  GlobalUnlock( hOldExecRec ); 
  MyGlobalFree( hOldExecRec ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
/*********************************** 
** 
**  Removes all records 
**  from the execution stack. 
** 
***********************************/ 
static void DestroyExecutionStack 
(  
  HGLOBAL * hExecutionStackPtr  
) 
{ 
  HEXECREC hCurrExecRec; 
  HEXECREC hNextExecRec; 
  EXECRECPTR CurrExecRecPtr; 
 
   
  /* Get first record. */ 
  hCurrExecRec = *hExecutionStackPtr; 
    
  /* For each record. */ 
  while( hCurrExecRec != NULL ) 
  { 
    /* Lock the current record. */ 
    CurrExecRecPtr = (EXECRECPTR) GlobalLock( hCurrExecRec ); 
       
    /* Free the current record's macro string. */ 
    MyGlobalFree( CurrExecRecPtr->hMacroString ); 
 
    /* Get next record's handle. */ 
    hNextExecRec = CurrExecRecPtr->hNext; 
     
    /* Free the current record. */ 
    GlobalUnlock( hCurrExecRec ); 
    MyGlobalFree( hCurrExecRec); 
 
    /* New current record. */ 
    hCurrExecRec = hNextExecRec; 
  } 
   
  /* Empty stack. */ 
  *hExecutionStackPtr = NULL; 
} 
 
 
 
/********************************* MACRO PARSING FUNCTIONS ***************************/ 
 
/*********************************** 
** 
**  Get next command from the current 
**  position in the macro string and 
**  find its associated call-back 
**  function and private data. 
**   
**  Returns: 
**       
**    TRUE  - If no more commands and no error occurred.  
**  
**    FALSE - Error occurred such as parsing error or could not 
**            find the function associated with the next macro command. 
** 
***********************************/ 
static BOOL GetNextMacroCommand 
(  
  HWND hErrorWnd, 
  MACROENGINEPTR MacroEnginePtr,  
  MACROFUNCPTR * MacroFuncDblPtr, 
  HGLOBAL * hMacroDataPtr, 
  BOOL bShowError 
) 
{ 
  EXECRECPTR ExecRecPtr;      /* Pointer to the macro string currently being executed. */ 
 
  char * CurrPosPtr;    /* Pointer to the current position in the macro string. */ 
 
  char * CmdEndPtr;     /* Pointer to the position in the macro string after a command. */ 
  WORD wCmdLen;               /* Length of the command. */ 
 
  BOOL bFoundNextCmnd; 
 
  BOOL bReturn; 
   
   
  /* Lock the execution stack's top record. */ 
  ExecRecPtr = (EXECRECPTR) GlobalLock( MacroEnginePtr->hExecutionStack ); 
       
  /* Lock the macro string. */ 
  CurrPosPtr = (char *) GlobalLock( ExecRecPtr->hMacroString ); 
 
  /* Move to current position in macro string. */ 
  CurrPosPtr = CurrPosPtr + ExecRecPtr->wPosition; 
   
  /* Haven't found a command yet. */ 
  bFoundNextCmnd = FALSE; 
   
  /* Skip leading white space. */ 
  while( *CurrPosPtr == ' ' )  
  {  
    ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
    CurrPosPtr++; 
  } 
                               
  /* Go to next command. */ 
  while( *CurrPosPtr != '\0' && bFoundNextCmnd == FALSE ) 
  { 
    switch( *CurrPosPtr ) 
    { 
      /* Found an open parenthesis. */ 
      case '(': 
      { 
        /* Increment position. */ 
        ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
        CurrPosPtr++;  
         
        /* Increment open parenthesis counter. */ 
        ExecRecPtr->nOpenParen = ExecRecPtr->nOpenParen + 1; 
       
        break; 
      } 
     
 
      /* Found a closing parenthesis. */ 
      case ')': 
      { 
        /* Increment position. */ 
        ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
        CurrPosPtr++;  
         
        /* Decrement open parenthesis counter. */ 
        ExecRecPtr->nOpenParen = ExecRecPtr->nOpenParen - 1; 
       
        break; 
      } 
     
 
      /* Found character that separates commands. */ 
      case CMDSEP_CHAR_TOKEN: 
      { 
        /* Thru all parenthesis groups? */ 
        if( ExecRecPtr->nOpenParen == 0 ) 
        { 
          /* At end of current command. */ 
          bFoundNextCmnd = TRUE; 
        } 
           
        /* Increment position. */ 
        ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
        CurrPosPtr++;  
         
        break; 
      } 
 
 
      /* Found an unimportant character. */ 
      default: 
      { 
        /* Increment position. */ 
        ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
        CurrPosPtr++;  
       
        break; 
      } 
 
    } 
  } 
 
  /* No more commands? */ 
  if( bFoundNextCmnd == FALSE || *CurrPosPtr == '\0' ) 
  { 
    /* Unlock the macro string. */ 
    GlobalUnlock( ExecRecPtr->hMacroString ); 
   
    /* No error. */ 
    *MacroFuncDblPtr = NULL; 
    return( TRUE ); 
  } 
   
  /* Find end of command string. */   
  CmdEndPtr = strchr( CurrPosPtr, '(' ); 
 
  /* Parsing error. */   
  if( CmdEndPtr == NULL ) 
  { 
    /* Unlock the macro string. */ 
    GlobalUnlock( ExecRecPtr->hMacroString ); 
   
    /* If we can show the error. */ 
    if( bShowError ) 
    { 
      /* Parsing error. */  
      MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
    } 
     
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* Terminate end of the command found. */ 
  *CmdEndPtr = '\0'; 
   
  /* Set the next read position. */ 
  wCmdLen = strlen( CurrPosPtr ) + 1; 
  ExecRecPtr->wPosition = ExecRecPtr->wPosition + wCmdLen; 
   
  /* Trim beginning blanks from command. */ 
  while( *CurrPosPtr == ' ' )  
  {  
    CurrPosPtr = CurrPosPtr + 1; 
  } 
 
  /* Trim ending blanks from command. */ 
  CmdEndPtr = CmdEndPtr - 1; 
  while( *CmdEndPtr == ' ' )  
  {  
    CmdEndPtr = CmdEndPtr - 1; 
  } 
  CmdEndPtr = CmdEndPtr + 1; 
  *CmdEndPtr = '\0'; 
 
  /* Have one open parenthesis. */ 
  ExecRecPtr->nOpenParen = 1; 
 
  /* Get the call-back function associated with the command. */ 
  bReturn = GetCommandInfo( CurrPosPtr, MacroEnginePtr->hLoadTableStack, MacroFuncDblPtr,  
                            hMacroDataPtr ); 
   
  /* Failed to find command's data. */ 
  if( bReturn == FALSE ) 
  { 
    /* Unlock the macro string. */ 
    GlobalUnlock( ExecRecPtr->hMacroString ); 
   
    /* If we can show the error. */ 
    if( bShowError ) 
    { 
      /* Parsing error. */  
      MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
    } 
     
    /* Failure. */ 
    return( FALSE ); 
  } 
 
  /* Unlock the macro string. */ 
  GlobalUnlock( ExecRecPtr->hMacroString ); 
 
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
/*********************************** 
** 
**  Find a command in one of the 
**  loaded tables and get the 
**  data asssociated with it. 
** 
***********************************/ 
static BOOL GetCommandInfo 
(  
  char * szMacroCommand, 
  HGLOBAL hLoadTableStack,  
  MACROFUNCPTR * MacroFuncDblPtr,  
  HGLOBAL * hMacroDataPtr  
) 
{ 
  GETCOMMANDINFODATA Data; 
   
  /* Init. search data. */ 
  Data.bFound         = FALSE; 
  Data.szMacroCommand = szMacroCommand; 
   
  /* Use GetCommandInfoEx() to look through the loaded macro tables. */ 
  EnumLoadTableStack( hLoadTableStack, GetCommandInfoEx, (LPARAM) (GETCOMMANDINFODATAPTR) &Data ); 
 
  /* If we found the command's info. */ 
  if( Data.bFound == TRUE ) 
  { 
    *MacroFuncDblPtr = Data.MacroFuncPtr; 
    *hMacroDataPtr   = Data.hMacroData; 
  } 
   
  /* Success or Failure */ 
  return( Data.bFound ); 
} 
 
 
/*********************************** 
** 
**  Search for a registered macro 
**  table list with the specified 
**  name. 
** 
***********************************/ 
static BOOL CALLBACK GetCommandInfoEx( HLOADEDTABLEREC hLoadedTableRec, LPARAM lParam ) 
{ 
  LOADEDTABLERECPTR LoadedTableRecPtr; 
  GETCOMMANDINFODATAPTR DataPtr; 
   
  /* Get search criteria. */ 
  DataPtr = (GETCOMMANDINFODATAPTR) lParam; 
 
  /* Lock the loaded table record. */ 
  LoadedTableRecPtr = (LOADEDTABLERECPTR) GlobalLock( hLoadedTableRec ); 
     
  DataPtr->bFound = FindRegTableMacro( LoadedTableRecPtr->hRegTableRec, DataPtr->szMacroCommand, 
                                       &(DataPtr)->MacroFuncPtr, &(DataPtr)->hMacroData ); 
  /* Unlock the loaded table record. */ 
  GlobalUnlock( hLoadedTableRec ); 
   
  /* If we didn't found it keep enumerating. */ 
  return( ! DataPtr->bFound ); 
}   
 
 
/*********************************** 
** 
**  Get the next argument from  
**  the top macro string.  
** 
***********************************/ 
BOOL __declspec(dllexport) GetNextMacroArg  
( 
  HWND hErrorWnd,                /* Parent window for error message box. */ 
  HMACROENGINE hMacroEngine,     /* Handle to engine. */ 
  PTRVOIDPTR PtrDataPtr,         /* Place in which to save data. */ 
  MACROARGTYPE MacroArgType,     /* Type of data that it is. */ 
  WORD wBufferSize,              /* Size of string buffer (including null-char) if  
                                    string argument is being retrieved. */ 
  WORD * wDataSizePtr,     /* Size, in bytes, of the data found. If it is a string, the 
                                    string's teriminating NULL character is included 
                                    in the number. */ 
  VARHANDLERFUNCPTR VarHandlerProc,  /* Call back procedure to handle variable processing. */ 
  LPARAM VarHandlerProcData,      /* Application specific data for variable processing. */ 
  void * UserDefinedDataPtr  /* Application specific macro processing data. */ 
) 
{ 
  MACROENGINEPTR MacroEnginePtr; /* Pointer to the macro engine's instance data. */ 
 
  EXECRECPTR ExecRecPtr;      /* Pointer to the macro string currently being executed. */ 
 
  char * ArgStartPtr;   /* Pointer to the current position in the macro string. */ 
  char * CurrPosPtr;    /* Pointer to the current position in the macro string. */ 
 
  WORD wCmdLen;               /* Length of the command. */ 
 
  BOOL bFoundArgEnd; 
 
  short int nEmbeddedOpenParen; 
   
  /* In case of hex conversion. */ 
  unsigned long int nHexConvert; 
   
  /* Pointers to the data saving area. */ 
  LPSTRPTR LPStrPtr; 
  STRPTR StrPtr; 
 
  LPWORDPTR LPWordPtr; 
  WORDPTR WordPtr; 
 
  LPDWORDPTR LPDWordPtr; 
  DWORDPTR DWordPtr; 
 
  LPINTPTR LPIntPtr;  
  INTPTR IntPtr;  
   
  LPLONGPTR LPLongPtr;  
  LONGPTR LongPtr;  
 
  WORD wArgStrLen; 
   
  BOOL bIsVariable; 
  BOOL bVarProcessingErr; 
   
  MACRORETVAL MacroRetVal; 
 
    
  /* Lock engine data. */ 
  MacroEnginePtr = (MACROENGINEPTR) GlobalLock( hMacroEngine ); 
   
  /* Lock the execution stack's top record. */ 
  ExecRecPtr = (EXECRECPTR) GlobalLock( MacroEnginePtr->hExecutionStack ); 
       
  /* Lock the macro string. */ 
  CurrPosPtr = (char *) GlobalLock( ExecRecPtr->hMacroString ); 
 
  /* Move to current position in macro string. */ 
  CurrPosPtr = CurrPosPtr + ExecRecPtr->wPosition; 
   
  /* Haven't found the end of argument yet. */ 
  bFoundArgEnd = FALSE; 
   
  /* Skip leading white space. */ 
  while( *CurrPosPtr == ' ' )  
  {  
    ExecRecPtr->wPosition = ExecRecPtr->wPosition + 1; 
    CurrPosPtr++; 
  } 
   
  /* Save starting position of argument. */ 
  ArgStartPtr = CurrPosPtr; 
   
  /* No open parenthesis. */                             
  nEmbeddedOpenParen = 0; 
 
  /* Go to end of argument. */ 
  while( *CurrPosPtr != '\0' && bFoundArgEnd == FALSE ) 
  { 
    switch( *CurrPosPtr ) 
    { 
      /* Found an open parenthesis. */ 
      case '(': 
      { 
        /* Increment position. */ 
        CurrPosPtr++;  
         
        /* Increment open parenthesis counters. */ 
        ExecRecPtr->nOpenParen = ExecRecPtr->nOpenParen + 1; 
        nEmbeddedOpenParen = nEmbeddedOpenParen + 1; 
        break; 
      } 
     
 
      /* Found a closing parenthesis - may be end of argument. */ 
      case ')': 
      { 
         /* Decrement open parenthesis counter. */ 
        ExecRecPtr->nOpenParen = ExecRecPtr->nOpenParen - 1; 
        nEmbeddedOpenParen = nEmbeddedOpenParen - 1; 
       
       /* Thru all parenthesis groups at this level? */ 
        if( ExecRecPtr->nOpenParen == 0 ) 
        { 
          /* At end of current argument. */ 
          bFoundArgEnd = TRUE; 
          break; 
        } 
           
        /* Increment position. */ 
        CurrPosPtr++;  
 
        break; 
      } 
     
 
      /* Found character that separate arguments. */ 
      case ',': 
      { 
        /* Thru all embedded parenthesis in the current argument? */ 
        if( nEmbeddedOpenParen == 0 ) 
        { 
          /* At end of current argument. */ 
          bFoundArgEnd = TRUE; 
          break; 
        } 
           
        /* Increment position. */ 
        CurrPosPtr++;  
 
        break; 
      } 
 
 
      /* Found an unimportant character. */ 
      default: 
      { 
        /* Increment position. */ 
        CurrPosPtr++;  
       
        break; 
      } 
 
    } 
  } 
 
  /* Did not find end of argument? */ 
  if( bFoundArgEnd == FALSE || *CurrPosPtr == '\0' ) 
  { 
    /* Unlock the macro string. */ 
    GlobalUnlock( ExecRecPtr->hMacroString ); 
   
    /* Unlock the execution stack. */ 
    GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
   
    /* Unlock engine data. */ 
    GlobalUnlock( hMacroEngine ); 
   
    /* Parsing error. */  
    MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Terminate end of the argument found. */ 
  *CurrPosPtr = '\0'; 
   
  /* Set the next read position. */ 
  wCmdLen = strlen( ArgStartPtr ) + 1; 
  ExecRecPtr->wPosition = ExecRecPtr->wPosition + wCmdLen; 
   
   
  /* Trim ending blanks from argument. */ 
  CurrPosPtr = CurrPosPtr - 1; 
  while( *CurrPosPtr == ' ' )  
  {  
    CurrPosPtr = CurrPosPtr - 1; 
  } 
  CurrPosPtr = CurrPosPtr + 1; 
  *CurrPosPtr = '\0'; 
 
  /*  
  ** If first char is not a quote or tilde char. 
  ** process argument first as a variable.  
  **   
  */ 
  if( *ArgStartPtr != '\"' && *ArgStartPtr != '`' ) 
  { 
    bIsVariable = (*VarHandlerProc)( hErrorWnd, ArgStartPtr, VarHandlerProcData, PtrDataPtr, wBufferSize, wDataSizePtr, &bVarProcessingErr );  
    if( bIsVariable ) 
    { 
      /* Unlock the macro string. */ 
      GlobalUnlock( ExecRecPtr->hMacroString ); 
         
      /* Unlock the execution stack. */ 
      GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
         
      /* Unlock engine data. */ 
      GlobalUnlock( hMacroEngine ); 
     
      /* If processing error happened. */ 
      if( bVarProcessingErr ) 
      { 
        /* Failure. */ 
        return( FALSE ); 
      } 
   
      /* Success. */ 
      return( TRUE ); 
    } 
  } 
     
  /*  
  ** If first char is not a quote or tilde char. 
  ** Process the argument first as a Macro call  
  ** and use its return value.  
  **   
  */ 
  if( *ArgStartPtr != '\"' && *ArgStartPtr != '`' ) 
  { 
    /* If the return value is a string. */ 
    LPStrPtr = (LPSTRPTR) PtrDataPtr; 
    MacroRetVal.StringPtr = *LPStrPtr; 
 
    if( ExecuteMacro( hErrorWnd, hMacroEngine, ArgStartPtr, &MacroRetVal, UserDefinedDataPtr, FALSE ) ) 
    { 
      /* Convert the argument and save it to the buffer. */ 
      switch( MacroArgType ) 
      { 
        /* Argument is an signed long int. */ 
        case MACRO_SIGNEDLONGINT: 
        { 
          /* Cast void data pointer. */ 
          LPLongPtr = (LPLONGPTR) PtrDataPtr; 
          LongPtr = (LONGPTR) *LPLongPtr; 
     
          /* Save the return value. */ 
          *LongPtr = ( long int ) MacroRetVal.dwValue; 
     
          /* Save size of argument in bytes. */ 
          *wDataSizePtr = sizeof(long int); 
           
          break; 
        } 
     
     
        /* Argument is an signed short int. */ 
        case MACRO_SIGNEDSHORTINT: 
        { 
          /* Cast void data pointer. */ 
          LPIntPtr = (LPINTPTR) PtrDataPtr; 
          IntPtr = (INTPTR) *LPIntPtr; 
     
          /* Save the return vaule. */ 
          *IntPtr = (short int) MacroRetVal.dwValue; 
     
          /* Save size of argument in bytes. */ 
          *wDataSizePtr = sizeof(short int); 
           
          break; 
        } 
     
     
        /* Argument is an unsigned long int. */ 
        case MACRO_UNSIGNEDLONGINT: 
        { 
          /* Cast void data pointer. */ 
          LPDWordPtr = (LPDWORDPTR) PtrDataPtr; 
          DWordPtr = (DWORDPTR) *LPDWordPtr; 
     
          /* Save the return value. */ 
          *DWordPtr = (DWORD) MacroRetVal.dwValue; 
     
          /* Save size of argument in bytes. */ 
          *wDataSizePtr = sizeof(DWORD); 
           
          break; 
        } 
     
     
        /* Argument is an unsigned short int. */ 
        case MACRO_UNSIGNEDSHORTINT: 
        { 
          /* Cast void data pointer. */ 
          LPWordPtr = (LPWORDPTR) PtrDataPtr; 
          WordPtr = (WORDPTR) *LPWordPtr; 
     
          /* Save the return value. */ 
          *WordPtr = (WORD) MacroRetVal.dwValue; 
     
          /* Save size of argument in bytes. */ 
          *wDataSizePtr = sizeof(WORD); 
           
          break; 
        } 
     
     
        /* Argument is a string. */ 
        case MACRO_NEARSTRING: 
        case MACRO_FARSTRING: 
        { 
          /* Save size of argument in bytes. */ 
          *wDataSizePtr = strlen(MacroRetVal.StringPtr) + sizeof(char);  
           
          break; 
        } 
      } 
     
      /* Called the macro and got return value. */ 
      return( TRUE ); 
    } 
  } 
   
   
  /* Convert the argument and save it to the buffer. */ 
  switch( MacroArgType ) 
  { 
    /* Argument is an signed long int. */ 
    case MACRO_SIGNEDLONGINT: 
    { 
      /* Cast void data pointer. */ 
      LPLongPtr = (LPLONGPTR) PtrDataPtr; 
      LongPtr = (LONGPTR) *LPLongPtr; 
 
      /* If its hex, convert it first. */ 
      if( HexConvert( ArgStartPtr, &nHexConvert ) ) 
      { 
        /* Save to data pointer. */ 
        *LongPtr = (long int) nHexConvert; 
      } 
       
      else 
      { 
        /* Convert the string. */ 
        *LongPtr = atol( ArgStartPtr ); 
      } 
 
      /* Save size of argument in bytes. */ 
      *wDataSizePtr = sizeof(long int); 
       
      break; 
    } 
 
 
    /* Argument is an signed short int. */ 
    case MACRO_SIGNEDSHORTINT: 
    { 
      /* Cast void data pointer. */ 
      LPIntPtr = (LPINTPTR) PtrDataPtr; 
      IntPtr = (INTPTR) *LPIntPtr; 
 
      /* If its hex, convert it first. */ 
      if( HexConvert( ArgStartPtr, &nHexConvert ) ) 
      { 
        /* Save to data pointer. */ 
        *IntPtr = (short int) nHexConvert; 
      } 
       
      else 
      { 
        /* Convert the string. */ 
        *IntPtr = atoi( ArgStartPtr ); 
      } 
 
      /* Save size of argument in bytes. */ 
      *wDataSizePtr = sizeof(short int); 
       
      break; 
    } 
 
 
    /* Argument is an unsigned long int. */ 
    case MACRO_UNSIGNEDLONGINT: 
    { 
      /* Cast void data pointer. */ 
      LPDWordPtr = (LPDWORDPTR) PtrDataPtr; 
      DWordPtr = (DWORDPTR) *LPDWordPtr; 
 
      /* If its hex, convert it first. */ 
      if( HexConvert( ArgStartPtr, &nHexConvert ) ) 
      { 
        /* Save to data pointer. */ 
        *DWordPtr = (DWORD) nHexConvert; 
      } 
       
      else 
      { 
        /* Convert the string. */ 
        *DWordPtr = (DWORD) atol( ArgStartPtr ); 
      } 
 
      /* Save size of argument in bytes. */ 
      *wDataSizePtr = sizeof(DWORD); 
       
      break; 
    } 
 
 
    /* Argument is an unsigned short int. */ 
    case MACRO_UNSIGNEDSHORTINT: 
    { 
      /* Cast void data pointer. */ 
      LPWordPtr = (LPWORDPTR) PtrDataPtr; 
      WordPtr = (WORDPTR) *LPWordPtr; 
 
      /* If its hex, convert it first. */ 
      if( HexConvert( ArgStartPtr, &nHexConvert ) ) 
      { 
        /* Save to data pointer. */ 
        *WordPtr = (WORD) nHexConvert; 
      } 
       
      else 
      { 
        /* Convert the string. */ 
        *WordPtr = (WORD) atoi( ArgStartPtr ); 
      } 
 
      /* Save size of argument in bytes. */ 
      *wDataSizePtr = sizeof(WORD); 
       
      break; 
    } 
 
 
    /* Argument is a quoted string - quotes will be removed. */ 
    case MACRO_NEARSTRING: 
    case MACRO_FARSTRING: 
    { 
      /* First char should be a quote or tilde char. */ 
      if( *ArgStartPtr != '\"' && *ArgStartPtr != '`') 
      { 
        /* Unlock the macro string. */ 
        GlobalUnlock( ExecRecPtr->hMacroString ); 
       
        /* Unlock the execution stack. */ 
        GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
       
        /* Unlock engine data. */ 
        GlobalUnlock( hMacroEngine ); 
   
        /* Parsing error. */  
        MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
     
        /* Failure. */ 
        return( FALSE ); 
      } 
      else 
      { 
        /* Skip first quote char or tilde char. */ 
        ArgStartPtr =  ArgStartPtr + 1; 
      } 
       
      /* Point to last char in the string. */ 
      CurrPosPtr = CurrPosPtr - 1; 
 
      /* Last char should be a quote or apostrophe char. */ 
      if( *CurrPosPtr != '\"' && *CurrPosPtr != '\'') 
      { 
        /* Unlock the macro string. */ 
        GlobalUnlock( ExecRecPtr->hMacroString ); 
       
        /* Unlock the execution stack. */ 
        GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
       
        /* Unlock engine data. */ 
        GlobalUnlock( hMacroEngine ); 
   
        /* Parsing error. */  
        MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
     
        /* Failure. */ 
        return( FALSE ); 
      } 
      else 
      { 
        /* Trim last quote char. */ 
        *CurrPosPtr = '\0'; 
      } 
 
      /* Verify that the string buffer is long enough. */ 
      wArgStrLen = strlen((char *) ArgStartPtr) + sizeof(char);  
      if( wArgStrLen > wBufferSize ) 
      { 
        /* Unlock the macro string. */ 
        GlobalUnlock( ExecRecPtr->hMacroString ); 
       
        /* Unlock the execution stack. */ 
        GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
       
        /* Unlock engine data. */ 
        GlobalUnlock( hMacroEngine ); 
   
        /* Failure. */ 
        return( FALSE ); 
      } 
       
      /* Copy argument into string buffer. */ 
      LPStrPtr = (LPSTRPTR) PtrDataPtr; 
      StrPtr = (STRPTR) *LPStrPtr; 
      strcpy( StrPtr, ArgStartPtr ); 
 
      /* Save size of argument in bytes. */ 
      *wDataSizePtr = wArgStrLen; 
       
      break; 
    } 
   
    /* Unknown parameter type. */ 
    default: 
    { 
      /* Parsing error. */  
      MsgBox( GetLibInst(), hErrorWnd, IDS_ERRORTITLE, IDS_MACROPARSINGERROR, MB_ICONHAND | MB_OK ); 
     
      /* Unlock the macro string. */ 
      GlobalUnlock( ExecRecPtr->hMacroString ); 
       
      /* Unlock the execution stack. */ 
      GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
       
      /* Unlock engine data. */ 
      GlobalUnlock( hMacroEngine ); 
   
      /* Failure. */ 
      return( FALSE ); 
    } 
  } 
 
  /* Unlock the macro string. */ 
  GlobalUnlock( ExecRecPtr->hMacroString ); 
   
  /* Unlock the execution stack. */ 
  GlobalUnlock( MacroEnginePtr->hExecutionStack ); 
   
  /* Unlock engine data. */ 
  GlobalUnlock( hMacroEngine ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/*********************************** 
** 
**  Checks to see if the argument 
**  is in hex format and if it 
**  is translates the number to 
**  an unsigned long int. 
** 
**  Returns: 
** 
**      TRUE:  Number was hex and converted. 
**      FALSE: Number was not in hex. 
** 
***********************************/ 
static BOOL HexConvert( char * ArgPtr, long unsigned int * nHexConvert ) 
{ 
  if( *ArgPtr == '0' && ( *(ArgPtr + 1 ) == 'x' ||  *(ArgPtr + 1 ) == 'X' ) ) 
  { 
    /* If its hex, convert it first. */ 
    *nHexConvert = strtoul( ArgPtr, NULL, 0 ); 
   
    /* Was in hex. */ 
    return( TRUE ); 
  } 
 
  /* Was not in hex. */ 
  return( FALSE ); 
}       
 
 
 
/**************************** DEFAULT MACROS IMPLEMENTATION FUNCTIONS ***********************/ 
 
 
/*********************************** 
** 
**  Macro that adds the specified 
**  table to the top of the 
**  macro table list. 
** 
**  It will be the first table that is 
**  considered when executing a macro. 
** 
***********************************/ 
static BOOL CALLBACK MacroLoadMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
) 
{ 
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/*********************************** 
** 
**  Macro that removes the first instance of  
**  the table from the macro table list. 
** 
**  Other instances of the table will 
**  not be removed. 
** 
**  If no instance of the table exists, 
**  no action is performed. 
** 
***********************************/ 
static BOOL CALLBACK MacroUnloadMacroTable 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
) 
{ 
  /* Success. */ 
  return( TRUE ); 
} 
 
 
/*********************************** 
** 
**  Remove the first instance of the 
**  table from the macro table list. 
** 
**  Other instances of the table will 
**  not be removed. 
** 
**  If no instance of the table exists, 
**  no action is performed. 
** 
***********************************/ 
static BOOL CALLBACK MacroExecuteMacroFile 
(  
  HMACROENGINE hMacroEngine, 
  void * UserDefinedDataPtr, 
  HGLOBAL hMacroData, 
  MACRORETVAL * MacroRetValPtr 
) 
{ 
  /* Success. */ 
  return( TRUE ); 
}