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


/* 
  
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. 
 
*/ 
 
/************************************************************************* 
* 
* FILEBUFF.C 
* Help file opening, topic buffering, help file buffering routines.      
* 
* Author: Paul E. Kissel 
* 
**************************************************************************/ 
 
/*********************************** 
** 
**  System Includes 
** 
***********************************/ 
#include <string.h> 
#include <io.h> 
 
/*********************************** 
** 
**  App. Includes 
** 
***********************************/ 
#include "filebuff.h" 
#include "twhlprc.h" 
#include "msgbox.h" 
#include "globals.h" 
#include "wnddata.h" 
#include "winmem.h" 
#include "elements.h" 
#include "openfile.h" 
 
 
/*********************************** 
** 
**  Private Defines 
** 
***********************************/ 
 
/* Maximum number of files in the file buffer. */ 
#define MAXFILEBUFF  4 
 
/* Maximum number of topics buffered per file. */ 
#define MAXTOPICBUFF 15 
 
 
/*********************************** 
** 
**  Private functions 
** 
***********************************/ 
 
static void ClearFileBufferRec( FPHLPFILEBUFFREC fpRec ); 
static RETVAL GetFileBufferRec( HGLOBAL hHelpFileBuffer, char * szHelpFile, FPHLPFILEBUFFREC fpSaveRec ); 
static void CopyFileBufferRec( FPHLPFILEBUFFREC fpDest, FPHLPFILEBUFFREC fpSrc ); 
static short int FreeOldestFileBufferRec( HGLOBAL hHelpFileBuffer, char * szKeepFileName ); 
static BOOL InsertFileBufferRec( HGLOBAL hHelpFileBuffer, FPHLPFILEBUFFREC fpRecData ); 
 
static RETVAL AllocTopicBuffer( HWND hErrorWnd, HGLOBAL * hTopicBuffer ); 
static void FreeTopicBuffer( HGLOBAL hTopicBuffer ); 
 
void ClearTopicBufferRec( FPTOPICBUFFREC fpRec ); 
static void CopyTopicBuffRec( FPTOPICBUFFREC fpDest, FPTOPICBUFFREC fpSrc ); 
static RETVAL FillTopicBuffRec( HWND hErrorWnd, DWORD dwTopicCharOffset, FPHLPFILEBUFFREC fpHelpBuffRec, FPTOPICBUFFREC fpTopicBuffRec ); 
static RETVAL InsertTopicBufferRec( HGLOBAL hTopicBuffer, FPTOPICBUFFREC fpRecData ); 
static RETVAL GrabTopicBufferRec( HGLOBAL hTopicBuffer, DWORD dwTopicCharOffset,  
                                                 FPTOPICBUFFREC fpSaveRec ); 
static void ReleaseTopicBufferRec( HGLOBAL hTopicBuffer, DWORD dwTopicCharOffset ); 
static BOOL AllTopicsReleased( HGLOBAL hTopicBuffer ); 
static short int FreeOldestTopicBufferRec( HGLOBAL hTopicBuffer ); 
static WORD GetTopicLockedCount( HGLOBAL hTopicBuffer ); 
 
 
/***************************** TOPIC ALLOC. / TOPIC FREE ROUTINES ******************************/ 
 
/*************************************************** 
* 
* Opens and loads data for a help file.  Saves file 
* info. into the help file buffer. 
* 
* Returns a handle to the help file's info. 
* 
****************************************************/ 
HGLOBAL OpenHelpFile( HWND hMainWnd, char * szHelpFilePath ) 
{ 
  RETVAL         RetVal;            /* Custom return value from functions. */ 
 
  HGLOBAL        hHelpFileBuffer;   /* Handle to the help file buffer. */ 
   
  HLPFILEBUFFREC HelpFileBufferRec; /* File buffer record. */ 
   
  HCURSOR        hOldCursor;        /* Handle to the old cursor. */ 
   
  int           hHelpFile;                   /* Handle to the help file. */ 
  char          szUnusedBuffer[_MAX_PATH];   /* Complete path. */ 
   
  WORD wLength; 
  char *   EndPtr; 
   
 
  /* Trim leading blanks from path. */ 
  while( *szHelpFilePath == ' ' )  
  {  
    szHelpFilePath = szHelpFilePath + 1; 
  } 
 
  /* Trim ending blanks from path. */ 
  wLength = strlen( szHelpFilePath ); 
  if( wLength > 0 ); 
  { 
    EndPtr = szHelpFilePath + ( wLength/sizeof(char) - 1 ); 
    while( *EndPtr == ' ' )  
    {  
      EndPtr = EndPtr - 1; 
    } 
    EndPtr = EndPtr + 1; 
    *EndPtr = '\0'; 
  } 
 
  /*  
  ** See if we can open the help file and in the process  
  ** get the COMPLETE PATH to the file so we can compare 
  ** its path to the path of files already in the buffer. 
  */ 
  hHelpFile = OpenFileForReading( szHelpFilePath, szUnusedBuffer ); 
  if( hHelpFile == -1 ) 
  { 
    /* Error message. */ 
    MsgBox( GetLibInst(), NULL, IDS_ERRORTITLE, IDS_BADFILEOPEN, MB_ICONHAND | MB_OK ); 
     
    /* Failure. */ 
    return( NULL ); 
  } 
 
  /* Close help file. */ 
  _close( hHelpFile ); 
 
  /* Get help file buffer handle from HWND of main window. */ 
  hHelpFileBuffer = (HGLOBAL) WndData( hMainWnd, WDM_GETFILEBUFFER, 0L, NULL ); 
 
  /* Error - No buffer. */ 
  if( hHelpFileBuffer == NULL )  
  { 
    return( NULL ); 
  }  
 
  /* Find help file in file buffer. */ 
  RetVal = GetFileBufferRec( hHelpFileBuffer, szUnusedBuffer, (FPHLPFILEBUFFREC) &HelpFileBufferRec ); 
 
  /*  
  ** If help file not in buffer, 
  ** load the help file's info. 
  */ 
  if( RetVal != NO_ERROR ) 
  { 
    /* Loading the help file info may take a while. */ 
    hOldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) ); 
   
    /* Load the help file info. */ 
    RetVal = LoadHelpFileInfo( hMainWnd, szUnusedBuffer, &(HelpFileBufferRec).hHelpFileInfo ); 
 
    /* Restore original cursor. */ 
    SetCursor( hOldCursor ); 
   
    /* File loading has failed. */ 
    if( RetVal != NO_ERROR ) 
    { 
      /* If we've had memory problems, report them now. */ 
      if( RetVal == ERR_MEMORY ) 
      { 
        MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_MEMORYALLOC, MB_ICONHAND | MB_OK ); 
      } 
 
      /* Failure. */ 
      return( NULL ); 
    } 
 
    /*  
    ** File loading has NOT failed.  
    */ 
     
    /* Create a topic buffer. */ 
    RetVal = AllocTopicBuffer( hMainWnd, &(HelpFileBufferRec).hTopicBuffer );     
 
    /* Topic buffer creation has failed. */ 
    if( RetVal != NO_ERROR ) 
    { 
      /* If we've had memory problems, report them now. */ 
      if( RetVal == ERR_MEMORY ) 
      { 
        MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_MEMORYALLOC, MB_ICONHAND | MB_OK ); 
      } 
 
      /* Failure. */ 
      return( NULL ); 
    } 
 
    /* Save the file's path. */   
    strcpy( HelpFileBufferRec.szHelpFile, szUnusedBuffer );  
       
    /* 
    ** File buffer record successfully created.  Add record to buffer.  
    */ 
    InsertFileBufferRec( hHelpFileBuffer, &HelpFileBufferRec ); 
  } 
 
  /* Success. */ 
  return( HelpFileBufferRec.hHelpFileInfo ); 
} 
 
 
 
/*************************************************** 
* 
* Create and returns a handle to a TOPICDATA  
* structure containing data for the desired 
* help file/topic. 
* 
* If the file's topic data is not in the buffer,  
* the information is loaded and placed into 
* the buffer for future use. 
* 
* The topic record in the topic buffer is locked 
* and will not be removed from the buffer until 
* the TOPICDATA is freed using the UnlockTopicData() 
* function. 
* 
****************************************************/ 
HGLOBAL LockTopicData( HWND hMainWnd, char * szHelpFilePath, DWORD dwTopicCharOffset ) 
{ 
  RETVAL         RetVal;             /* Custom return value from functions. */ 
 
  HGLOBAL        hHelpFileBuffer;    /* Handle to the help file buffer. */ 
   
  HLPFILEBUFFREC HelpFileBufferRec;  /* File buffer record. */ 
  TOPICBUFFREC   TopicDataRec;       /* Topic buffer record. */ 
   
  HGLOBAL        hTopicData;         /* Handle to the topic data. */ 
  FPTOPICDATA    fpTopicData;        /* Pointer to the topic data. */ 
   
  HCURSOR        hOldCursor;        /* Handle to the old cursor. */ 
 
  WORD wLength; 
  char *   EndPtr; 
   
  int           hHelpFile;                      /* Handle to the help file. */ 
  char          szFullHelpFilePath[_MAX_PATH];  /* Complete path. */ 
   
 
  /* Trim leading blanks from path. */ 
  while( *szHelpFilePath == ' ' )  
  {  
    szHelpFilePath = szHelpFilePath + 1; 
  } 
 
  /* Trim ending blanks from path. */ 
  wLength = strlen( szHelpFilePath ); 
  if( wLength > 0 ); 
  { 
    EndPtr = szHelpFilePath + ( wLength/sizeof(char) - 1 ); 
    while( *EndPtr == ' ' )  
    {  
      EndPtr = EndPtr - 1; 
    } 
    EndPtr = EndPtr + 1; 
    *EndPtr = '\0'; 
  } 
 
  /*  
  ** See if we can open the help file and in the process  
  ** get the COMPLETE PATH to the file so we can compare 
  ** its path to the path of files already in the buffer. 
  */ 
  hHelpFile = OpenFileForReading( szHelpFilePath, szFullHelpFilePath ); 
  if( hHelpFile == -1 ) 
  { 
    /* Error message. */ 
    MsgBox( GetLibInst(), NULL, IDS_ERRORTITLE, IDS_BADFILEOPEN, MB_ICONHAND | MB_OK ); 
     
    /* Failure. */ 
    return( NULL ); 
  } 
 
  /* Close help file. */ 
  _close( hHelpFile ); 
 
  /* Get help file buffer handle from HWND of main window. */ 
  hHelpFileBuffer = (HGLOBAL) WndData( hMainWnd, WDM_GETFILEBUFFER, 0L, NULL ); 
 
  /* Error - No buffer. */ 
  if( hHelpFileBuffer == NULL ) return( NULL ); 
 
  /* Find help file in file buffer. */ 
  RetVal = GetFileBufferRec( hHelpFileBuffer, szFullHelpFilePath, (FPHLPFILEBUFFREC) &HelpFileBufferRec ); 
 
  /* Finding file has failed. */ 
  if( RetVal != NO_ERROR ) return( NULL ); 
 
  /* Retrieve topics data. */ 
  RetVal = GrabTopicBufferRec( HelpFileBufferRec.hTopicBuffer, dwTopicCharOffset, &TopicDataRec ); 
 
  /*  
  ** If topic not in buffer, 
  ** load the topics info. 
  */ 
  if( RetVal != NO_ERROR ) 
  { 
    /* Loading the topic's info may take a while. */ 
    hOldCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) ); 
   
    /* Load the topic's info. */ 
    RetVal = FillTopicBuffRec( hMainWnd, dwTopicCharOffset, &HelpFileBufferRec, &TopicDataRec ); 
 
    /* Restore original cursor. */ 
    SetCursor( hOldCursor ); 
   
    /* Topic loading has failed. */ 
    if( RetVal != NO_ERROR ) 
    { 
      /* If we've had memory problems, report them now. */ 
      if( RetVal == ERR_MEMORY ) 
      { 
        MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_MEMORYALLOC, MB_ICONHAND | MB_OK ); 
      } 
 
      /* Show error message. */ 
      MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_CANTLOADTOPIC, MB_ICONHAND | MB_OK ); 
       
      /* Failure. */ 
      return( NULL ); 
    } 
 
    /* Save topic data to buffer. */ 
    RetVal = InsertTopicBufferRec( HelpFileBufferRec.hTopicBuffer, &TopicDataRec ); 
 
    /* Topic data could not be saved to buffer. */ 
    if( RetVal != NO_ERROR )  
    { 
      /* Display error. */ 
      MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_MAXTOPICS, MB_ICONHAND | MB_OK ); 
       
      /* Clear any memory allocated for the record. */ 
      ClearTopicBufferRec( &TopicDataRec ); 
       
      /* Failure. */ 
      return( NULL ); 
    } 
  } 
 
  /* Create a TOPICDATA structure. */ 
  hTopicData = GlobalAlloc( GHND, sizeof(TOPICDATA) ); 
 
  /* Creating structure has failed. */ 
  if( hTopicData == NULL ) 
  { 
    /* Display error message. */ 
    MsgBox( GetLibInst(), hMainWnd, IDS_ERRORTITLE, IDS_MEMORYALLOC, MB_ICONHAND | MB_OK ); 
 
    /* Clear any memory allocated for the record. */ 
    ClearTopicBufferRec( &TopicDataRec ); 
       
    /* Failure. */ 
    return( NULL ); 
  } 
 
  /* Lock topic data. */ 
  fpTopicData = (FPTOPICDATA) GlobalLock( hTopicData );   
   
  /* Load data into TOPICDATA. */ 
  fpTopicData->dwTopicCharOffset  = dwTopicCharOffset; 
  strcpy( fpTopicData->szHelpFile, HelpFileBufferRec.szHelpFile );  
  fpTopicData->hHelpFileInfo = HelpFileBufferRec.hHelpFileInfo; 
  fpTopicData->hTopicElements  = TopicDataRec.hTopicElements; 
  fpTopicData->NextTopicCharOffset = TopicDataRec.NextTopicCharOffset;  
  fpTopicData->PrevTopicCharOffset = TopicDataRec.PrevTopicCharOffset;  
  fpTopicData->hTopicMacroString = TopicDataRec.hTopicMacroString;  
  fpTopicData->bHasNonScrollData = TopicDataRec.bHasNonScrollData;  
  fpTopicData->bHasScrollData = TopicDataRec.bHasScrollData;  
 
  /* Unlock topic data. */ 
  GlobalUnlock( hTopicData );   
 
  /* Success. */ 
  return( hTopicData ); 
} 
 
 
/*************************************************** 
* 
* Free a TOPICDATA structure.  The data that the 
* structure contains is not freed because it is 
* only a copy of what is in the help buffer and topic 
* buffer. 
* 
* The topic record in the topic buffer is locked 
* and will not be removed from the buffer until 
* the TOPICDATA is unlocked using this 
* function. 
* 
****************************************************/ 
void UnlockTopicData( HWND hMainWnd, HGLOBAL hTopicData ) 
{ 
  RETVAL         RetVal;             /* Custom return value from functions. */ 
                                     
  HGLOBAL        hHelpFileBuffer;    /* Handle to the help file buffer. */ 
  HLPFILEBUFFREC HelpFileBufferRec;  /* File buffer record. */ 
   
  FPTOPICDATA    fpTopicData;        /* Pointer to the topic data. */ 
 
 
  /* Get help file buffer handle from HWND of main window. */ 
  hHelpFileBuffer = (HGLOBAL) WndData( hMainWnd, WDM_GETFILEBUFFER, 0L, NULL ); 
 
  /* Error - No buffer. */ 
  if( hHelpFileBuffer == NULL ) return; 
 
  /* Lock topic data. */ 
  fpTopicData = (FPTOPICDATA) GlobalLock( hTopicData );   
   
  /* Find help file in file buffer. */ 
  RetVal = GetFileBufferRec( hHelpFileBuffer, fpTopicData->szHelpFile, (FPHLPFILEBUFFREC) &HelpFileBufferRec ); 
 
  /*  
  ** Help file not in buffer. 
  */ 
  if( RetVal != NO_ERROR ) return; 
 
  /* Release the topic record's use. */ 
  ReleaseTopicBufferRec( HelpFileBufferRec.hTopicBuffer, fpTopicData->dwTopicCharOffset ); 
 
  /* Unlock topic data. */ 
  GlobalUnlock( hTopicData );   
 
  /* Free the structure. */ 
  MyGlobalFree( hTopicData ); 
} 
 
 
/*************************************************** 
* 
* Determines if there are any topics being used 
* for the given help file. 
* 
* This is determined by looking at the file's topic 
* buffer and getting the number of topics currently 
* locked. 
* 
****************************************************/ 
BOOL FileInUse( HWND hMainWnd, char * szHelpFilePath ) 
{ 
  RETVAL         RetVal;            /* Custom return value from functions. */ 
 
  HGLOBAL        hHelpFileBuffer;   /* Handle to the help file buffer. */ 
   
  HLPFILEBUFFREC HelpFileBufferRec; /* File buffer record. */ 
   
  WORD   wLength; 
  char * EndPtr; 
   
  int    hHelpFile;                       /* Handle to the help file. */ 
  char   szFullHelpFilePath[_MAX_PATH];   /* Complete path. */ 
   
  WORD   wCount; 
   
   
  /* Trim leading blanks from path. */ 
  while( *szHelpFilePath == ' ' )  
  {  
    szHelpFilePath = szHelpFilePath + 1; 
  } 
 
  /* Trim ending blanks from path. */ 
  wLength = strlen( szHelpFilePath ); 
  if( wLength > 0 ); 
  { 
    EndPtr = szHelpFilePath + ( wLength/sizeof(char) - 1 ); 
    while( *EndPtr == ' ' )  
    {  
      EndPtr = EndPtr - 1; 
    } 
    EndPtr = EndPtr + 1; 
    *EndPtr = '\0'; 
  } 
 
  /*  
  ** See if we can open the help file and in the process  
  ** get the COMPLETE PATH to the file so we can compare 
  ** its path to the path of files already in the buffer. 
  */ 
  hHelpFile = OpenFileForReading( szHelpFilePath, szFullHelpFilePath ); 
  if( hHelpFile == -1 ) 
  { 
    return( FALSE ); 
  } 
 
  /* Close help file. */ 
  _close( hHelpFile ); 
 
  /* Get help file buffer handle from HWND of main window. */ 
  hHelpFileBuffer = (HGLOBAL) WndData( hMainWnd, WDM_GETFILEBUFFER, 0L, NULL ); 
 
  /* Error - No buffer. */ 
  if( hHelpFileBuffer == NULL )  
  { 
    return( FALSE ); 
  }  
 
  /* Find help file in file buffer. */ 
  RetVal = GetFileBufferRec( hHelpFileBuffer, szFullHelpFilePath, (FPHLPFILEBUFFREC) &HelpFileBufferRec ); 
 
  /*  
  ** If help file not in buffer, 
  ** load the help file's info. 
  */ 
  if( RetVal != NO_ERROR ) 
  { 
    return( FALSE ); 
  } 
 
  /* Get the locked topic count. */ 
  wCount = GetTopicLockedCount( HelpFileBufferRec.hTopicBuffer ); 
 
  /* If no topics being used. */ 
  if( wCount == 0 ) 
  { 
    return( FALSE ); 
  } 
 
  /* If topic is being used. */ 
  else 
  { 
    return( TRUE ); 
  } 
} 
 
 
 
/******************************** FILE BUFFER ROUTINES **********************************/ 
 
BOOL AllocHelpFileBuffer( HWND hMainWnd ) 
{ 
  HGLOBAL hHelpFileBuffer; 
                           
                           
  /* Allocate memory for the topic buffer. */ 
  if( !GlobalAllocMem( hMainWnd, &hHelpFileBuffer, sizeof(HLPFILEBUFFREC) * MAXFILEBUFF  ) ) 
  { 
    /* Failure. */ 
    return( FALSE ); 
  } 
   
  /* Set the handle to the file buffer. */ 
  WndData( hMainWnd, WDM_SETFILEBUFFER, (DWORD) hHelpFileBuffer, NULL ); 
   
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
void FreeHelpFileBuffer( HWND hMainWnd  ) 
{ 
  HGLOBAL hHelpFileBuffer;         /* Help file buffer. */ 
  FPHLPFILEBUFFREC fpRec;          /* A pointer to a record in the file buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
 
 
  /* Get the handle to the file buffer. */ 
  hHelpFileBuffer = (HGLOBAL) WndData( hMainWnd, WDM_GETFILEBUFFER, 0L, NULL ); 
 
  if( hHelpFileBuffer == NULL ) return; 
   
  /*  
  ** Free all information in the buffer.  
  */ 
 
  /* Lock first record. */ 
  fpRec = (FPHLPFILEBUFFREC) GlobalLock( hHelpFileBuffer ); 
   
  /* Find record that are being used. */ 
  for( nCounter = 0; nCounter < MAXFILEBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid != FALSE ) 
    { 
      /* Free memory inside of record. */ 
      ClearFileBufferRec( fpRec );  
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* Unlock the buffer. */ 
  GlobalUnlock( hHelpFileBuffer ); 
 
  /* Free the buffer itself. */ 
  MyGlobalFree( hHelpFileBuffer ); 
 
  /* Clear the handle to the file buffer in the window data. */ 
  WndData( hMainWnd, WDM_SETFILEBUFFER, 0L, NULL ); 
} 
 
 
 
static void ClearFileBufferRec( FPHLPFILEBUFFREC fpRec ) 
{ 
  /* Free the file's HLPFILEINFO structure. */ 
  if( fpRec->hHelpFileInfo != NULL ) FreeHelpFileInfo( fpRec->hHelpFileInfo );   
 
  /* Free the file's topic buffer. */ 
  if( fpRec->hTopicBuffer != NULL ) FreeTopicBuffer( fpRec->hTopicBuffer );     
 
  /* Record does not contain valid info. */ 
  fpRec->bValid = FALSE; 
} 
 
 
 
static RETVAL GetFileBufferRec( HGLOBAL hHelpFileBuffer, char * szHelpFile, FPHLPFILEBUFFREC fpSaveRec ) 
{ 
  FPHLPFILEBUFFREC fpRec;          /* A pointer to a record in the file buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
  short int nResult;               /* Result of string compare. */ 
 
 
  /* Lock first record. */ 
  fpRec = (FPHLPFILEBUFFREC) GlobalLock( hHelpFileBuffer ); 
   
  /* For each record in the buffer. */ 
  for( nCounter = 0; nCounter < MAXFILEBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* See if its the right file. */ 
      nResult = stricmp( fpRec->szHelpFile, szHelpFile ); 
       
      /* Found right record? */ 
      if( nResult == 0 ) 
      { 
        /* Set record's last used time. */ 
        fpRec->dwTickCount = GetTickCount(); 
 
        /* Copy record's data. */ 
        CopyFileBufferRec( fpSaveRec, fpRec ); 
   
        /* Unlock buffer. */ 
        GlobalUnlock( hHelpFileBuffer ); 
   
        /* Success. */ 
        return( NO_ERROR ); 
      } 
    } 
     
    /* Go to next record. */ 
    fpRec++; 
  } 
 
  /* Unlock buffer. */ 
  GlobalUnlock( hHelpFileBuffer ); 
 
  /* Failure. */ 
  return( ERR_OTHER ); 
} 
 
 
   
/*************************************************** 
* 
* Copy the fields of a file buffer record from 
* one record to another. 
* 
****************************************************/ 
static void CopyFileBufferRec( FPHLPFILEBUFFREC fpDest, FPHLPFILEBUFFREC fpSrc ) 
{ 
  fpDest->bValid        = fpSrc->bValid; 
 
  fpDest->dwTickCount   = fpSrc->dwTickCount; 
                        
  fpDest->hHelpFileInfo = fpSrc->hHelpFileInfo;        
 
  fpDest->hTopicBuffer  = fpSrc->hTopicBuffer;        
 
  strcpy( fpDest->szHelpFile, fpSrc->szHelpFile ); 
}  
 
 
 
 
static short int FreeOldestFileBufferRec( HGLOBAL hHelpFileBuffer, char * szKeepFileName ) 
{ 
  FPHLPFILEBUFFREC fpRec, fpStart;          /* A pointer to a record in the file buffer. */ 
                                      
  short int nCounter;                       /* Simple counter variable. */ 
    
  DWORD dwOldestTick;                       /* What's the tick count of the oldest file. */ 
  short int nOldestRecNum;                  /* What's the record number of the oldest file. */ 
   
  short int nResult;                        /* Result of string compare. */ 
 
  BOOL BFoundOne; 
   
   
  /* Have not found a file to free. */ 
  BFoundOne = FALSE; 
   
  /* Lock first record. */ 
  fpStart = fpRec = (FPHLPFILEBUFFREC) GlobalLock( hHelpFileBuffer ); 
   
  /* Find first record that we can use as initial oldest record. */ 
  for( nCounter = 0; nCounter < MAXFILEBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* See if its a file to ignore. */ 
      nResult = stricmp( fpRec->szHelpFile, szKeepFileName ); 
       
      /* Should we consider releasing this file? */ 
      if( nResult != 0 ) 
      { 
        /*  
        ** Verify that the topics in the file's 
        ** topic buffer are not locked. 
        */ 
        if( AllTopicsReleased( fpRec->hTopicBuffer ) ) 
        { 
          /* Found initial record to free. */ 
          dwOldestTick = fpRec->dwTickCount; 
          nOldestRecNum = nCounter; 
          BFoundOne = TRUE; 
        } 
      } 
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* Have not found a file to free. */ 
  if( !BFoundOne ) 
  { 
    /* Unlock buffer. */ 
    GlobalUnlock( hHelpFileBuffer ); 
   
    /* Failure. */ 
    return( -1 ); 
  } 
   
  /* Now compare the rest of the records with our current file to be freed. */ 
  for( ; nCounter < MAXFILEBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* See if its a file to ignore. */ 
      nResult = stricmp( fpRec->szHelpFile, szKeepFileName ); 
       
      /* Should consider use file? */ 
      if( nResult != 0 ) 
      { 
        /*  
        ** Verify that the topics in the file's 
        ** topic buffer are not locked. 
        */ 
        if( AllTopicsReleased( fpRec->hTopicBuffer ) ) 
        { 
          /* Older? */ 
          if( fpRec->dwTickCount < dwOldestTick ) 
          { 
            /* Found initial record to free. */ 
            dwOldestTick = fpRec->dwTickCount; 
            nOldestRecNum = nCounter; 
          } 
        } 
      } 
    } 
       
    /* Go to next record. */ 
    fpRec++; 
  } 
 
 
  /*  
  ** We DID find a record to free - lets free it.  
  */ 
   
  /* Goto record. */ 
  fpRec = fpStart + nOldestRecNum; 
     
  /* Free information inside of the record. */ 
  ClearFileBufferRec( fpRec ); 
   
  /* Unlock buffer. */ 
  GlobalUnlock( hHelpFileBuffer ); 
 
  /* Success. */ 
  return( nOldestRecNum ); 
} 
 
 
 
static BOOL InsertFileBufferRec( HGLOBAL hHelpFileBuffer, FPHLPFILEBUFFREC fpRecData ) 
{ 
  FPHLPFILEBUFFREC fpRec, fpStart;    /* A pointer to a record in the file buffer. */ 
  short int nCounter;                 /* Simple counter variable. */ 
  short int nFreeRecord;              /* Record number of freed record. */ 
   
    
  /* Lock first record. */ 
  fpStart = fpRec = (FPHLPFILEBUFFREC) GlobalLock( hHelpFileBuffer ); 
   
  /* Find first record that is not used. */ 
  for( nCounter = 0; nCounter < MAXFILEBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == FALSE ) 
    { 
      break;  
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* No unused records. */ 
  if( nCounter == MAXFILEBUFF ) 
  {  
    /* Free the oldest file record in the buffer. */ 
    nFreeRecord = FreeOldestFileBufferRec( hHelpFileBuffer, "\0" );  
 
    /* Can't free a record. */ 
    if( nFreeRecord == -1 ) 
    { 
      /* Unlock buffer. */ 
      GlobalLock( hHelpFileBuffer ); 
       
      /* Failure. */ 
      return( FALSE ); 
    } 
     
    /* Get the first record. */ 
    fpRec = fpStart + nFreeRecord; 
   
  } 
   
  /*  
  ** Now that we have a record - lets save the data.  
  */ 
   
  /* Copy fields. */ 
  CopyFileBufferRec( fpRec, fpRecData ); 
 
  /* Record now has data. */ 
  fpRec->bValid = TRUE; 
   
  /* Set record's insertion time. */ 
  fpRec->dwTickCount = GetTickCount(); 
 
  /* Unlock buffer. */ 
  GlobalLock( hHelpFileBuffer ); 
       
  /* Success. */ 
  return( TRUE ); 
} 
 
 
 
/******************************** TOPIC BUFFER ROUTINES **********************************/ 
 
static RETVAL AllocTopicBuffer( HWND hErrorWnd, HGLOBAL * hTopicBuffer ) 
{ 
  /* Allocate memory for the topic buffer. */ 
  *hTopicBuffer = GlobalAlloc( GHND, sizeof(TOPICBUFFREC) * MAXTOPICBUFF ); 
   
  /* Error? */ 
  if( *hTopicBuffer == NULL ) 
  { 
    /* Failure. */ 
    return( ERR_MEMORY ); 
  } 
   
  /* Success. */ 
  return( NO_ERROR ); 
} 
 
 
static void FreeTopicBuffer( HGLOBAL hTopicBuffer ) 
{ 
  FPTOPICBUFFREC fpRec;            /* A pointer to a record in the topic buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
 
  /*  
  ** Free all information in the buffer.  
  */ 
 
  /* Lock first record. */ 
  fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* Find record that are being used. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid != FALSE ) 
    { 
      /* Free memory inside of record. */ 
      ClearTopicBufferRec( fpRec );  
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* Unlock the buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
 
  /* Free the buffer itself. */ 
  MyGlobalFree( hTopicBuffer ); 
} 
 
 
/*************************************************** 
* 
* Clears any memory INSIDE of a TOPICBUFFREC structure.  
* 
****************************************************/ 
static void ClearTopicBufferRec( FPTOPICBUFFREC fpRec ) 
{ 
  /* Free the element list's information. */ 
  DestroyElementList( &(fpRec)->hTopicElements ); 
 
  /* Free macro string if there is one. */ 
  if( fpRec->hTopicMacroString != NULL ) 
  { 
    MyGlobalFree( fpRec->hTopicMacroString  ); 
  } 
 
  /* Record does not contain valid info. */ 
  fpRec->bValid = FALSE; 
} 
 
 
/*************************************************** 
* 
* Copy the fields of a topic buffer record from 
* one record to another. 
* 
****************************************************/ 
static void CopyTopicBuffRec( FPTOPICBUFFREC fpDest, FPTOPICBUFFREC fpSrc ) 
{ 
  fpDest->bValid       = fpSrc->bValid; 
 
  fpDest->dwTickCount  = fpSrc->dwTickCount; 
                        
  fpDest->dwUsage      = fpSrc->dwUsage;        
 
  fpDest->dwTopicCharOffset = fpSrc->dwTopicCharOffset;        
   
  fpDest->hTopicElements = fpSrc->hTopicElements; 
   
  fpDest->NextTopicCharOffset = fpSrc->NextTopicCharOffset; 
   
  fpDest->PrevTopicCharOffset = fpSrc->PrevTopicCharOffset; 
   
  fpDest->hTopicMacroString = fpSrc->hTopicMacroString; 
 
  fpDest->bHasNonScrollData = fpSrc->bHasNonScrollData; 
 
  fpDest->bHasScrollData = fpSrc->bHasScrollData; 
}  
 
 
/*************************************************** 
* 
* Load topic data for a topic buffer record. 
* 
****************************************************/ 
static RETVAL FillTopicBuffRec( HWND hErrorWnd, DWORD dwTopicCharOffset, FPHLPFILEBUFFREC fpHelpBuffRec, FPTOPICBUFFREC fpTopicBuffRec ) 
{ 
  RETVAL        RetVal;           /* Custom return value. */ 
 
  RetVal = CreateElementLists( hErrorWnd,  
                               dwTopicCharOffset, 
                               &(fpTopicBuffRec)->hTopicElements, 
                               &(fpTopicBuffRec)->PrevTopicCharOffset, 
                               &(fpTopicBuffRec)->NextTopicCharOffset, 
                               &(fpTopicBuffRec)->hTopicMacroString, 
                               &(fpTopicBuffRec)->bHasNonScrollData, 
                               &(fpTopicBuffRec)->bHasScrollData, 
                               fpHelpBuffRec->hHelpFileInfo ); 
 
  /* Save block/offset information to record. */ 
  fpTopicBuffRec->dwTopicCharOffset = dwTopicCharOffset; 
         
  /* Return the error that we got. */ 
  return( RetVal ); 
} 
 
 
/*************************************************** 
* 
* Inserts the a topic record's data into the topic buffer. 
* 
****************************************************/ 
static RETVAL InsertTopicBufferRec( HGLOBAL hTopicBuffer, FPTOPICBUFFREC fpRecData ) 
{ 
  FPTOPICBUFFREC fpRec, fpStart;    /* A pointer to a record in the topic buffer. */ 
  short int nCounter;               /* Simple counter variable. */ 
  short int nFreeRecord;            /* Record number of freed record. */ 
 
    
  /* Lock first record. */ 
  fpStart = fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* Find first record that is not used. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == FALSE ) 
    { 
      break;  
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* No unused records. */ 
  if( nCounter == MAXTOPICBUFF ) 
  {  
    /* Free the oldest file record in the buffer. */ 
    nFreeRecord = FreeOldestTopicBufferRec( hTopicBuffer );  
     
    if( nFreeRecord == -1 ) 
    { 
      /* Unlock buffer. */ 
      GlobalLock( hTopicBuffer ); 
 
      return( ERR_OTHER ); 
    } 
 
    /* Get the first record. */ 
    fpRec = fpStart + nFreeRecord; 
  } 
   
  /*  
  ** Now that we have a record - lets save the data.  
  */ 
   
  /* Copy fields. */ 
  CopyTopicBuffRec( fpRec, fpRecData ); 
 
  /* Record now has data. */ 
  fpRec->bValid = TRUE; 
   
  /* Set record's insertion time. */ 
  fpRec->dwTickCount = GetTickCount(); 
 
  /* Assume that on insertion we are using the topic data. */ 
  fpRec->dwUsage = 1;        
   
  /* Unlock buffer. */ 
  GlobalLock( hTopicBuffer ); 
 
  return( NO_ERROR ); 
} 
 
 
 
/*************************************************** 
* 
* Grab the information for a topic from the 
* topic buffer.  
* 
* Increments the usage count for the topic. 
* 
****************************************************/ 
static RETVAL GrabTopicBufferRec 
(  
  HGLOBAL hTopicBuffer,   
  DWORD dwTopicCharOffset,  
  FPTOPICBUFFREC fpSaveRec  
) 
{ 
  FPTOPICBUFFREC fpRec;            /* A pointer to a record in the topic buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
    
   
  /* Lock first record. */ 
  fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* For each record in the buffer. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* Found right record? */ 
      if( fpRec->dwTopicCharOffset == dwTopicCharOffset ) 
      { 
        /* Increment topic usage count. */ 
        fpRec->dwUsage = fpRec->dwUsage + 1; 
         
        /* Set record's last used time. */ 
        fpRec->dwTickCount = GetTickCount(); 
 
        /* Copy record's data. */ 
        CopyTopicBuffRec( fpSaveRec, fpRec ); 
   
        /* Unlock buffer. */ 
        GlobalUnlock( hTopicBuffer ); 
   
        /* Success. */ 
        return( NO_ERROR ); 
      } 
    } 
     
    /* Go to next record. */ 
    fpRec++; 
  } 
 
  /* Unlock buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
 
  /* Failure. */ 
  return( ERR_OTHER ); 
} 
 
 
/*************************************************** 
* 
* Release a topic in the topic buffer by 
* finding it and decrementing its usage count. 
* 
****************************************************/ 
static void ReleaseTopicBufferRec 
(  
  HGLOBAL hTopicBuffer,   
  DWORD dwTopicCharOffset 
) 
{ 
  FPTOPICBUFFREC fpRec;            /* A pointer to a record in the topic buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
    
   
  /* Lock first record. */ 
  fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* For each record in the buffer. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* Found right record? */ 
      if( fpRec->dwTopicCharOffset == dwTopicCharOffset )  
      { 
        /* Decrement topic usage count. */ 
        if( fpRec->dwUsage > 0 )  
        { 
          fpRec->dwUsage = fpRec->dwUsage - 1; 
        } 
         
        /* Unlock buffer. */ 
        GlobalUnlock( hTopicBuffer ); 
   
        /* Success. */ 
        return; 
      } 
    } 
     
    /* Go to next record. */ 
    fpRec++; 
  } 
 
  /* Unlock buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
} 
 
 
/*************************************************** 
* 
* Tells if all topics in a topic buffer are currently 
* released ( not being used ). 
* 
* Returns: BOOL 
* 
*    TRUE:  All topics in the buffer are released. 
*    FALSE: One or more topics in the buffe are currently 
*           being used (grabbed). 
* 
****************************************************/ 
static BOOL AllTopicsReleased( HGLOBAL hTopicBuffer ) 
{ 
  FPTOPICBUFFREC fpRec;            /* A pointer to a record in the topic buffer. */ 
  short int nCounter;              /* Simple counter variable. */ 
    
   
  /* Lock first record. */ 
  fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* For each record in the buffer. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* Decrement topic usage count. */ 
      if( fpRec->dwUsage > 0 )  
      { 
        /* Unlock buffer. */ 
        GlobalUnlock( hTopicBuffer ); 
   
        /* Found a topic being used. */ 
        return( FALSE ); 
      } 
    } 
     
    /* Go to next record. */ 
    fpRec++; 
  } 
 
  /* Unlock buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
 
  /* Found no topics being used. */ 
  return( TRUE ); 
} 
 
 
/*************************************************** 
* 
* Free the oldest topic record from the topic buffer. 
* 
****************************************************/ 
static short int FreeOldestTopicBufferRec( HGLOBAL hTopicBuffer ) 
{ 
  FPTOPICBUFFREC fpRec, fpStart;            /* A pointer to a record in the topic buffer. */ 
                                      
  short int nCounter;                       /* Simple counter variable. */ 
    
  DWORD dwOldestTick;                       /* What's the tick count of the oldest topic. */ 
  short int nOldestRecNum;                  /* What's the record number of the oldest topic. */ 
  BOOL bFoundIt; 
     
  /* Haven't found a topic to free. */ 
  bFoundIt = FALSE; 
   
  /* Lock first record. */ 
  fpStart = fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* Find first record that we can use as initial oldest record. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /*  
      ** Should we can consider this topic?  
      ** Only if its usage count is zero. 
      */ 
      if( fpRec->dwUsage == 0 ) 
      { 
        /* Found initial record to free. */ 
        dwOldestTick = fpRec->dwTickCount; 
        nOldestRecNum = nCounter; 
        bFoundIt = TRUE; 
      } 
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
     
  /* Haven't found a topic to free. */ 
  if( !bFoundIt ) 
  { 
    /* Unlock buffer. */ 
    GlobalUnlock( hTopicBuffer ); 
   
    /* Failure. */ 
    return( -1 ); 
  } 
   
 
  /* Now compare the rest of the records with our current file to be freed. */ 
  for( ; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /*  
      ** Should we can consider this file?  
      ** Only if its usage count is zero. 
      */ 
      if( fpRec->dwUsage == 0 ) 
      { 
        /* Found an older topic. */ 
        if( fpRec->dwTickCount < dwOldestTick ) 
        { 
          /* Assign curent record as one to free. */ 
          dwOldestTick = fpRec->dwTickCount; 
          nOldestRecNum = nCounter; 
        } 
      } 
    } 
       
    /* Go to next record. */ 
    fpRec++; 
  } 
 
  /*  
  ** We DID find a record to free - lets free it.  
  */ 
   
  /* Goto record. */ 
  fpRec = fpStart + nOldestRecNum; 
     
  /* Free information inside of the record. */ 
  ClearTopicBufferRec( fpRec ); 
   
  /* Unlock buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
 
  /* Success. */ 
  return( nOldestRecNum ); 
} 
 
 
/*************************************************** 
* 
* Counts how many topic in the given topic buffer 
* are being used. 
* 
****************************************************/ 
static WORD GetTopicLockedCount( HGLOBAL hTopicBuffer ) 
{ 
  FPTOPICBUFFREC fpRec;    /* A pointer to a record in the topic buffer. */ 
  short int nCounter;      /* Simple counter variable. */ 
  WORD wInUseCount;        /* How many topic's in use? */ 
   
   
  /* None used. */ 
  wInUseCount = 0; 
   
  /* Lock first record. */ 
  fpRec = (FPTOPICBUFFREC) GlobalLock( hTopicBuffer ); 
   
  /* Find first record that we can use as initial oldest record. */ 
  for( nCounter = 0; nCounter < MAXTOPICBUFF; nCounter++ ) 
  { 
    /* Does record contain valid info? */ 
    if( fpRec->bValid == TRUE ) 
    { 
      /* If the topic is being used. */ 
      if( fpRec->dwUsage > 0 ) 
      { 
        wInUseCount = wInUseCount + 1; 
      } 
    } 
 
    /* Go to next record. */ 
    fpRec++; 
  }     
   
  /* Unlock buffer. */ 
  GlobalUnlock( hTopicBuffer ); 
 
  /* Return count. */ 
  return( wInUseCount ); 
}