/****************************** Module Header *******************************
*
* Module Name: hilite.c
*
* Generic routines to support extended keyword hilighting
*
* Due to the lack of source code, the highlighting still has to work via
* epmkwds.* alike files. These are automatically created for a mode, when
* they need to be rebuilt and a file of that mode is loaded.
*
* The mode determination and the configuration for the highlighting was
* improved. For mode determination, see mode.c. The configuration of the
* highlighting separates keywords, color names and color values.
*
* The temporary epmkwds files are built from these files:
*
*    o  \hilite\common.cfg            color names and default symbol names
*    o  \hilite\<mode>\hilite.cfg     charset and symbol names
*    o  \hilite\<mode>\*.hil          keywords and special chars
*
* To change these default values, copy a file from the netlabs tree to your
* user tree, while keeping the same subdir hierarchy. Change the values only
* in that file. Upon load of a file with the edited mode, the change is
* recognized and the temporary epmkwds file is rebuilt.
*
* A refresh of the temporary epmkwds file can also be forced by holding the
* Ctrl key and clicking the 'Keyword highlighting' menu item or toolbar
* button. Both toggle the highlighting state. Therefore click them twice.
*
* Copyright (c) Netlabs EPM Distribution Project 2002
*
* $Id$
*
* ===========================================================================
*
* This file is part of the Netlabs EPM Distribution package and is free
* software.  You can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* Netlabs EPM Distribution.  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
* General Public License for more details.
*
****************************************************************************/

#define INCL_DOS
#define INCL_WIN
#define INCL_ERRORS
#include <os2.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <search.h>
#include <ctype.h>

#include "macros.h"
#include "nepmd.h"

#include "libreg.h"
#include "init.h"
#include "instval.h"
#include "file.h"
#include "mmf.h"
#include "eas.h"
#include "epmenv.h"

#include "hilite.h"

// --- define to allow color value names in HIL files
#define ALLOW_COLOR_VALUES 1

// --- defines for detailed debug messages

#define DEBUG_DUMPARRAYDETAILS 0

// debug output on func entry and exit
#define DEBUG_NOMESSAGE_ENTEREXIT 1

#if DEBUG_NOMESSAGE_ENTEREXIT
#undef FUNCENTER
#undef FUNCEXIT
#undef FUNCEXITRC
#define FUNCENTER
#define FUNCEXIT
#define FUNCEXITRC
#endif

// ----- values for control files for creating hilite files -------------

// definitions for file format for *.hil
#define CHAR_HILCOMMENT      ';'
#define CHAR_SECTION_START   '['
#define CHAR_SECTION_END     ']'

// default values
#define MAX_INI_LINE        (_MAX_PATH)
#define MAX_REG_PATH        (_MAX_PATH)

// global string vars
static   PSZ            pszEnvnameEpmHilitePath = "EPMHILITEPATH";
static   PSZ            pszEnvnameEpmkwdsPath   = "EPMKWDSPATH";

static   PSZ            pszEpmkwdsFileSection = "EPMKWDSFILE";
static   PSZ            pszColorsSection      = "COLORS";
static   PSZ            pszSymbolsSection     = "SYMBOLS";

static   PSZ            pszFileInfoListEaName = "NEPMD.FileListInfo";

static   PSZ            pszKeywordNone    = "NONE:";

static   PSZ            pszRegPathHilCommentChar = "\\NEPMD\\User\\KeywordHighlighting\\HilCommentChar";
static   PSZ            pszDirKeyPath            = "\\NEPMD\\User\\KeywordHighlighting\\HiliteCacheDir";
static   PSZ            pszRegPathTemplate       = "\\NEPMD\\User\\Mode\\%s\\%s";
static   PSZ            pszRegPathPrevTemplate   = "\\NEPMD\\Var\\PrevModeValues\\%s\\%s";

// defines for strings
#define SEARCHMASK_HILITEFILES "%s\\%s\\*.hil"
#define SEARCHMASK_TARGETFILES "%s.???"
#define SEARCHMASK_HILITEDIR   "%s\\nepmd\\hilite"
#define SEARCHMASK_COMMONCFG   "common.cfg"
#define SEARCHMASK_HILITECFG   "%s\\hilite.cfg"
#define SEARCHMASK_EPMKWDS     "epmkwds.%s"

// --- keyword strings for the EPMKWDSFILE section of HILITE.CFG

#define KEY_CHARSET            "CHARSET"

// --- keyword strings of NEPMD.INI

// also used for mode determination and for building EPM highlighting files
#define REGKEY_CASESENSITIVE   "CaseSensitive"

// ----------------------------------------------------------------------

// some useful macros
#define ALLOCATEMEMORYFILE(p,s)                            \
rc = MmfAlloc( hmmf, (PVOID*)&p, MMF_FILE_INMEMORY, 0, s); \
if (rc != NO_ERROR)                                        \
   break;

#define FREEMEMORYFILE(p)   MmfFree( hmmf, p)

#define QUERYINITVALUE(h,s,k,t)                             \
if (!InitQueryProfileString( h, s, k, NULL, t, sizeof( t))) \
   {                                                        \
   rc = ERROR_INVALID_DATA;                                 \
   break;                                                   \
   }
#define QUERYINITVALUESIZE(h,s,k) _queryInitValueSize( h, s, k)

// ----------------------------------------------------------------------

#ifdef DEBUG
#if DEBUG_DUMPARRAYDETAILS
#define DUMPINITVALUEARRAY(p) _dumpInitValueArray(p)
#define DPRINTF_ARRAY(p) DPRINTF(p)
#else
#define DUMPINITVALUEARRAY(p)
#define DPRINTF_ARRAY(p)
#endif
#else
#define DUMPINITVALUEARRAY(p)
#define DPRINTF_ARRAY(p)
#endif

// structure for an array of values
// used by _maintainInitValueArray and _dumpInitValueArray
typedef struct _VALUEARRAY
     {
         ULONG          ulCount;
         ULONG          ulArraySize;
         PSZ            apszValue[ 1];
     } VALUEARRAY, *PVALUEARRAY;

// ----------------------------------------------------------------------

static PSZ _stripblanks( PSZ string)
{
PSZ p = string;
if (p != NULL)
   {
   while ((*p != 0) && (*p <= 32))
      { p++;}
   strcpy( string, p);

   p = string;
   if (*p != 0)
      {
      p += strlen(p) - 1;
      while ((*p <= 32) && (p >= string))
         {
         *p = 0;
         p--;
         }
      }
   }

return string;
}

// ----------------------------------------------------------------------

BOOL _hasletters( PSZ str)
{
if (!str)
   return FALSE;

while (*str)
   {
   if (isalpha( *str))
      return TRUE;
   str++;
   }

return FALSE;
}

// -----------------------------------------------------------------------------

static APIRET _searchFile( PSZ pszSearchPathName, PSZ pszBuffer, ULONG ulBuffLen, PSZ pszSearchMask, ...)
{
         APIRET         rc = NO_ERROR;
         CHAR           szSourceName[ _MAX_PATH];
         va_list        arg_ptr;

va_start (arg_ptr, pszSearchMask);
vsprintf( szSourceName, pszSearchMask, arg_ptr);
rc = DosSearchPath( SEARCH_IGNORENETERRS  |
                       SEARCH_ENVIRONMENT |
                       SEARCH_CUR_DIRECTORY,
                   pszSearchPathName,
                   szSourceName,
                   pszBuffer,
                   ulBuffLen);
return rc;
}

// -----------------------------------------------------------------------------

static APIRET _searchInitFile( PSZ pszSearchPathName, PSZ pszSearchMask, PSZ pszEpmMode,
                               PSZ pszBuffer, ULONG ulBuffLen)
{
         APIRET         rc = NO_ERROR;
         CHAR           szFile[ _MAX_PATH];

do
   {
   // search init file
   rc = _searchFile( pszSearchPathName, szFile, sizeof( szFile), pszSearchMask, pszEpmMode);
   if (rc != NO_ERROR)
      break;

   // hand over filename
   if (pszBuffer)
      {
      if (strlen( szFile) + 1 > ulBuffLen)
         {
         rc = ERROR_BUFFER_OVERFLOW;
         break;
         }
      strcpy( pszBuffer, szFile);
      strlwr( pszBuffer);
      }

   } while (FALSE);

return rc;
}

// -----------------------------------------------------------------------------

static APIRET _queryInitValueSize( HINIT hinit, PSZ pszSection, PSZ pszKey)
{
         ULONG          ulDataLen = 0;

InitQueryProfileSize( hinit, pszSection, pszKey, &ulDataLen);
return ulDataLen;
}

// -----------------------------------------------------------------------------

int _compareFilename( const void *pszKeyElement, const void *pszEntry)
{
pszKeyElement = (PVOID) Filespec( (PSZ) pszKeyElement, FILESPEC_NAME);
pszEntry      = (PVOID) Filespec( (PSZ) pszEntry, FILESPEC_NAME);
return stricmp( pszKeyElement, pszEntry);
}

// -----------------------------------------------------------------------------

static APIRET _getFileList( HMMF hmmf, PSZ pszEpmMode, ULONG ulKeywordFileDate,
                            PSZ *ppszFileList, PULONG pulFileCount,
                            PULONG pulTotalSize, PBOOL pfOutdated)
{

         APIRET         rc = NO_ERROR;

         PSZ            pszFileList = NULL;
         ULONG          ulListSize;
         ULONG          ulFileCount = 0;
         ULONG          ulTotalSize = 0;
         BOOL           fOutdated   = FALSE;

         PSZ            pszKeywordPath = NULL;
         PSZ            pszKeywordDir;

         CHAR           szSearchMask[ _MAX_PATH];
         CHAR           szFile[ _MAX_PATH];
         HDIR           hdir;
         PSZ            pszEntry;

do
   {
   // check parm
   if ((!pszEpmMode)   ||
       (!*pszEpmMode)  ||
       (!ppszFileList) ||
       (!pulFileCount) ||
       (!pulTotalSize) ||
       (!pfOutdated))
      {
      rc = ERROR_INVALID_PARAMETER;
      break;
      }

   // query env var
   pszKeywordPath = getenv( pszEnvnameEpmHilitePath);
   if (!pszKeywordPath)
      {
      rc = ERROR_ENVVAR_NOT_FOUND;
      break;
      }

   // create a strdup of the path, so that we can tokenize it
   pszKeywordPath = strdup( pszKeywordPath);
   if (!pszKeywordPath)
      {
      rc = ERROR_NOT_ENOUGH_MEMORY;
      break;
      }

   // allocate dynamic memory for file list
   ALLOCATEMEMORYFILE( pszFileList, 16384);

   // go through all keyword directories
   pszKeywordDir = strtok( pszKeywordPath, ";");
   while (pszKeywordDir)
      {
      // search all hilite files in that directory
      sprintf( szSearchMask, SEARCHMASK_HILITEFILES, pszKeywordDir, pszEpmMode);

      // store filenames
      hdir = HDIR_CREATE;

      while (rc == NO_ERROR)
         {
         // search it
         rc = GetNextFile( szSearchMask, &hdir,
                           szFile, sizeof( szFile));
         if (rc != NO_ERROR)
            break;

         // check if file is already in
         pszEntry = lfind( szFile, pszFileList, (PUINT) &ulFileCount, _MAX_PATH, _compareFilename);
         if (pszEntry)
            continue;

//       DPRINTF(( "HILITE: %u [%u] bytes (%u entries) (re)allocated for file list at 0x%08x\n",
//                ulListSize, _msize( pszTmp), ulListSize / _MAX_PATH, pszTmp));
         pszEntry = pszFileList + (ulFileCount * _MAX_PATH);
         strcpy( pszEntry, szFile);
         strlwr( pszEntry );
         ulFileCount++;
         ulTotalSize += QueryFileSize( szFile);

         // check filedate
         if (QueryFileDate( szFile) > ulKeywordFileDate)
            fOutdated = TRUE;

         }
      DosFindClose( hdir);

      // handle special errors
      if (rc == ERROR_NOT_ENOUGH_MEMORY)
         break;
      else if (rc = ERROR_NO_MORE_FILES)
         rc = NO_ERROR;

      // next please
      pszKeywordDir = strtok( NULL, ";");
      }

   if (rc != NO_ERROR)
      break;

   // hand over result
   *ppszFileList = pszFileList;
   *pulFileCount = ulFileCount;
   *pulTotalSize = ulTotalSize;
   *pfOutdated   = fOutdated;

   } while (FALSE);

// cleanup
if (rc)
   {
   if (pszFileList)  FREEMEMORYFILE( pszFileList);
   if (ppszFileList) *ppszFileList = NULL;
   if (pulFileCount) *pulFileCount = 0;
   }
if (pszKeywordPath) free( pszKeywordPath);
return rc;
}

// -----------------------------------------------------------------------------

int _compareValue( const void *ppszKeyElement, const void *ppszEntry)
{

#ifdef DEBUG
// for view in debugger only:
         PSZ            pszKey   = *(PSZ*)ppszKeyElement;
         PSZ            pszEntry = *(PSZ*)ppszEntry;
#endif
return stricmp( *(PSZ*)ppszKeyElement, *(PSZ*)ppszEntry);
}

// -----------------------------------------------------------------------------

static PVALUEARRAY _maintainInitValueArray( HCONFIG hinit, PSZ pszSection, PVALUEARRAY pva)
{
         PVALUEARRAY    pvaResult = pva;
         APIRET         rc = NO_ERROR;
         ULONG          i;
         BOOL           fSkip;

         PSZ            pszKey;
         ULONG          ulKeyCount;
         PVALUEARRAY    pvaNew = NULL;
         ULONG          ulBuffLen;
         CHAR           szKeyList[ 1024];
         CHAR           szKeyValue[ 128];

         PSZ           *ppszAnchor;
         PSZ            pszEntry;

         PSZ           *ppszOldAnchor;
         PSZ            pszOldEntry;

do
   {
   // read the key list
   QUERYINITVALUE( hinit, pszSection, NULL, szKeyList);

// if (pva)
//    DPRINTF_ARRAY(( "\nmaintaining symbols array at 0x%08x:\n", pva));

   // count items and its size
   ulBuffLen = sizeof( VALUEARRAY);
   ulKeyCount = 0;
   pszKey = szKeyList;
   while (*pszKey)
      {
      // does entry exist in the old array? then skip
      fSkip = FALSE;
      if (pva)
         {
         ppszOldAnchor = bsearch( &pszKey, pva->apszValue, pva->ulCount, sizeof( PSZ), _compareValue);
         if (ppszOldAnchor)
            fSkip = TRUE;
         }

      if (!fSkip)
         {
         // add appropriate space and count up
         ulKeyCount++;
         ulBuffLen += sizeof( PSZ) +
                     strlen( pszKey) + 1 +
                     QUERYINITVALUESIZE( hinit, pszSection, pszKey) + 1;
         }

      // next key
      pszKey = NEXTSTR( pszKey);
      }

   // add count and space of existing array
   if (pva)
      {
      ulBuffLen  += pva->ulArraySize;
      ulKeyCount += pva->ulCount;
      }

   // allocate memory
   pvaNew = malloc( ulBuffLen);
   if (!pvaNew)
      break;
   memset( pvaNew, 0, ulBuffLen);
   pvaNew->ulArraySize = ulBuffLen;
   pvaNew->ulCount = ulKeyCount;
   pvaResult = pvaNew;

   // remove values from old array, that we want to readd
   if (pva)
      {
      pszKey = szKeyList;
      while (*pszKey)
         {
         // can entry be found in old array? if yes, mark it as deleted
         ppszOldAnchor = (PVOID)lfind( (PSZ)&pszKey, (PSZ)pva->apszValue, (PUINT)&pva->ulCount, sizeof( PSZ), _compareValue);
         if (ppszOldAnchor)
            {
//          DPRINTF_ARRAY(( "HILITE: delete entry %s\n", *ppszOldAnchor));
            **ppszOldAnchor = 0;
            }

         // next key
         pszKey = NEXTSTR( pszKey);
         }
      } // if (pva)

   // transfer existing entries
   ppszAnchor   = pvaNew->apszValue;
   pszEntry     = (PSZ) pvaNew->apszValue + (ulKeyCount * sizeof( PSZ));
   if (pva)
      {
      for ( i = 0, ppszOldAnchor = pva->apszValue; i < pva->ulCount; i++, ppszOldAnchor++, ppszAnchor++)
         {
         // store entry
         pszOldEntry = *ppszOldAnchor;
         if (!*pszOldEntry)
            {
            // skip this entry, it has beeen deleted
            ppszAnchor--;
            continue;
            }

//       DPRINTF_ARRAY(( "HILITE: transfer entry 0x%08x / 0x%08x: %s - %s\n",
//                       pszOldEntry, NEXTSTR( pszOldEntry),
//                       pszOldEntry, NEXTSTR( pszOldEntry)));

         *ppszAnchor = pszEntry;
         strcpy( pszEntry, pszOldEntry);

//       DPRINTF_ARRAY(( "HILITE:      new entry 0x%08x / 0x%08x: %s - ",
//                       pszEntry, NEXTSTR( pszEntry), pszEntry));

         pszEntry = NEXTSTR( pszEntry);
         pszOldEntry = NEXTSTR( pszOldEntry);

         strcpy( pszEntry, pszOldEntry);
//       DPRINTF_ARRAY(( "%s\n", pszEntry));

         pszEntry = NEXTSTR( pszEntry);
         }
      }

   // add new values to array
   // duplicates are now removed
   pszKey = szKeyList;
   while (*pszKey)
      {
      // store entry
      *ppszAnchor = pszEntry;

      strcpy( pszEntry, pszKey);
      QUERYINITVALUE( hinit, pszSection, pszKey, szKeyValue);
//    DPRINTF_ARRAY(( "HILITE: add entry %s - %s\n", pszEntry, szKeyValue));
      pszEntry = NEXTSTR( pszEntry);

      strcpy( pszEntry, szKeyValue);
      pszEntry = NEXTSTR( pszEntry);

//    DPRINTF_ARRAY(( "HILITE: array: store %s=%s\n", pszKey, szKeyValue));

      // next key
      ppszAnchor++;
      pszKey = NEXTSTR( pszKey);
      }

   // sort the entries
   qsort( pvaNew->apszValue, pvaNew->ulCount, sizeof( PSZ), _compareValue);

   // throw away the old array
   if (pva)
      free( pva);

   } while (FALSE);

return pvaResult;
}

// -----------------------------------------------------------------------------

static PSZ _queryInitValue( PVALUEARRAY pva, PSZ pszKey)
{
         PSZ            pszResult = NULL;
         PSZ           *ppszEntry;

do
   {
   // search the entry
   ppszEntry = bsearch( &pszKey, pva->apszValue, pva->ulCount, sizeof( PSZ), _compareValue);
   if (!ppszEntry)
   break;

   // report the value
   pszResult = NEXTSTR( *ppszEntry);

   } while (FALSE);

return pszResult;
}

// -----------------------------------------------------------------------------

static VOID _dumpInitValueArray( PVALUEARRAY pva)
{

         PSZ           *ppszEntry = pva->apszValue;
         PSZ            pszEntry;
         ULONG          i;

printf( "\nSymbols from array at 0x%08x:\n", pva);

for (i = 0; i < pva->ulCount; i++)
   {
   pszEntry =  *ppszEntry;
   printf( "-> 0x%08x / 0x%08x: %s %s\n",
           *ppszEntry, NEXTSTR( *ppszEntry),
           *ppszEntry, NEXTSTR( *ppszEntry));
   ppszEntry++;
   }
printf( "%u entries\n\n", i);
}

// -----------------------------------------------------------------------------
// Determine the filename for a tmp EPM kwds file. Compare EA of the existing
// EPM kwds file with the hilite mode's INI and HIL file data. If outdated,
// rebuild it using these files. The EPM kwds file is created in a tmp dir.
// Therefore following env vars are queried, while one must exist:
// EPMKWDSTMP, TMP, TEMP.
// A subdir "nepmd" is created in the tmp dir, holding the kwds files with the
// filename = <hilite mode>.
//
// Reads:
//    pszEpmMode  mode
//    ulOptions   1 if checking for outdated files should be suppressed; also
//                used to control the 1st word of the parm for toggle_parse
// Returns/alters via call by reference:
//    pfReload    1 if tmp EPM kwds file was rebuilt; used to determine the
//                1st word of the parm for toggle_parse
//    pszBuffer   fully qualified tmp EPM kwds filename
// Returns:
//    rc          0 if no error
//
// Called by NepmdActivateHighlight (E proc):
//    NepmdQueryHighlightArgs         (NEPMDLIB.C)
//       QueryHilightFile             (HILITE.C)
//          _assembleKeywordFile      (HILITE.C)
//
// E calls:
//    a_togl_hilit                    (SAMPACTN.E)
//       ActivateHighlighting         (STDCMDS.E)
//          NepmdActivateHighlight    (ACTIVATEHIGHLIGHT.E)
//    toggle_highlight, toggle_default_highlight  (NEWMENU.E)
//       SetHighlight                 (MODEEXECUTE.E)
//          NepmdActivateHighlight    (ACTIVATEHIGHLIGHT.E)
//    ProcessLoadSettings             (MODEEXECUTE.E)
//       NepmdActivateHighlight       (ACTIVATEHIGHLIGHT.E)
//
// NepmdActivateHighlight uses the toggle_parse command (STDCTRL.E) to execute
// the highlighting. toggle_parse accepts 0, 1 or 2 as the first word of its
// parm:
//    0   unhighlight
//    1   highlight, load the kwds file if not already loaded
//    2   highlight and force reloading of the kwds file to reparse it

static APIRET _assembleKeywordFile( PSZ pszEpmMode, ULONG ulOptions, PBOOL pfReload,
                                    HCONFIG hconfig,
                                    PSZ pszBuffer, ULONG ulBuffLen)
{
         APIRET         rc = NO_ERROR;
         ULONG          i;
         HMMF           hmmf = NULLHANDLE;

         PSZ            p;
         PSZ            pszTmpDir;

         PSZ           *ppszEntry;
         PSZ            pszEntry;
         PSZ            pszSymbol;
         PSZ           *ppszSymbolValue;
         CHAR           szValue[ MAX_INI_LINE] = "";

         // ----------------------------------

         HINIT          hinitCommon = NULLHANDLE;
         CHAR           szInitCommonFilename[ _MAX_PATH] = "";
         HINIT          hinitHilite = NULLHANDLE;
         CHAR           szInitHiliteFilename[ _MAX_PATH] = "";

         // ----------------------------------

         ULONG          ulInfoSize;
         BOOL           fCaseSensitive = FALSE;

         // ----------------------------------

         PVALUEARRAY    pvaColors = NULL;
         PVALUEARRAY    pvaSymbols = NULL;

         // ----------------------------------

         CHAR           szHilCommentChar[ 20] = "";

         // ----------------------------------

         CHAR           szRegPath[ MAX_REG_PATH] = "";
         BOOL           fImplicitOpen = FALSE;
         CHAR           szDataVal[ _MAX_PATH];

         // ----------------------------------

         CHAR           szKeywordFile[ _MAX_PATH] = "";
         ULONG          ulKeywordFileDate;
         BOOL           fOutdated = FALSE;

         // ----------------------------------

         PSZ            pszFileList = NULL;
         ULONG          ulFileCount;
         ULONG          ulTotalSize;

         PSZ            pszOldFileInfoList = NULL;

static   PSZ            pszFileInfoMask = "%s %s %u %u\r\n";
         PSZ            pszFileInfoList = NULL;
         ULONG          ulInfoListSize  = 0;

         // ----------------------------------

         PSZ            pszSectionDelimiter = NULL;
         PSZ            pszSectionDelimiteri = NULL;
         PSZ            pszSectionKeywords  = NULL;
         PSZ            pszSectionSpecial   = NULL;
         PSZ            pszSectionBreakChar = NULL;
         PSZ            pszSectionEndChar   = NULL;

         PSZ            pszCurrentDelimiter;
         PSZ            pszCurrentDelimiteri;
         PSZ            pszCurrentKeywords;
         PSZ            pszCurrentSpecial;
         PSZ            pszCurrentBreakChar;
         PSZ            pszCurrentEndChar;

static   PSZ            pszHeaderMask = "\r\n%s%s\r\n";

         // ----------------------------------
         ULONG          ulCurrentFile;
         PSZ            pszSourceFile;
         FILE          *pfile = NULL;
         ULONG          ulLineCount;
         CHAR           szLine[ 1024] = "";
         BOOL           fSectionStart;
         PSZ            pszLine;
         CHAR           szCurrentSection[ 64] = "";
         PSZ            pszCurrentSectionColors;
         CHAR           szEntryColors[32];
         BOOL           fEntryColors;

// keep these definitions in sync!!!
         ULONG          ulSectionIndex;
         PSZ            apszSpecialSections[] = {"", "COMMENT", "LITERAL", "SPECIAL", "OPERATOR", "BREAKCHAR", "ENDCHAR"};
#define  COUNT_SPECIALSECTION (sizeof( apszSpecialSections) / sizeof( PSZ))
#define  SECTION_DEFAULT   0
#define  SECTION_COMMENT   1
#define  SECTION_LITERAL   2
#define  SECTION_SPECIAL   3
#define  SECTION_OPERATOR  4
#define  SECTION_BREAKCHAR 5
#define  SECTION_ENDCHAR   6

         PSZ            pszStartStr;
         PSZ            pszStopStr;
         PSZ            pszBreakStr;
         PSZ            pszStartPos;
         PSZ            pszSymbolValue;
         PSZ            pszColorValue1;
         PSZ            pszColorValue2;
         PSZ            pszInvalid;
         ULONG          ulSelectIndex;

         ULONG          ulThisSectionIndex;

         // ----------------------------------

         PSZ            pszHiliteContents = NULL;
         ULONG          ulHiliteContentsLen;
         PSZ            pszCurrent;

         CHAR           szCharset[ MAX_INI_LINE] = "";
         CHAR           szFileBuffer[ 2* MAX_INI_LINE + 1] = "";

         BOOL           fDELIMI = FALSE;

         // ----------------------------------

         CHAR           szDirKeyValue[ _MAX_PATH] = "";
         PSZ            pszDirKeyValue = NULL;
         ULONG          ulDirKeyLen = 0;

FUNCENTER;

do
   {
   // check parms
   if ((!pszEpmMode)  ||
       (!*pszEpmMode) ||
       (!pfReload)    ||
       (!pszBuffer))
      {
      rc = ERROR_INVALID_PARAMETER;
      break;
      }

   // check env
   do
      {
      pszTmpDir = getenv( "TMP");
      if (pszTmpDir)
         break;

      pszTmpDir = getenv( "TEMP");

      } while (FALSE);

   if (!pszTmpDir)
      {
      rc = ERROR_ENVVAR_NOT_FOUND;
      break;
      }

   // init target vars
   *pfReload = TRUE;
   memset( pszBuffer, 0, ulBuffLen);

   // -----------------------------------------------

   // get the name and date of the temporary file

   // get keywordfile
   strupr( pszEpmMode);

   // no keyword file yet, create a new one

   // query optional HiliteCacheDir from NEPMD.INI
   ulDirKeyLen = QueryConfigValue( hconfig, pszDirKeyPath, szDirKeyValue);
   // Expand env vars
   pszDirKeyValue = ExpandEnvVar( szDirKeyValue);
   if ((ulDirKeyLen > 0) && (strlen( pszDirKeyValue) > 0))
      // use HiliteCacheDir for the keyword files
      strcpy( szKeywordFile, pszDirKeyValue);
   else
      // for that create subdirectory below TMP
      sprintf( szKeywordFile, SEARCHMASK_HILITEDIR, pszTmpDir);

   // create directory tree
   rc = CreatePath( szKeywordFile);
   if ((rc != NO_ERROR) && (rc != ERROR_ACCESS_DENIED))
      break;

   // append backslash and filename
   sprintf( _EOS( szKeywordFile), "\\%s", pszEpmMode);
   strlwr( szKeywordFile);

   // -----------------------------------------------

   // --- hand over filename already here

   // check result buffer
   if (strlen( szKeywordFile) + 1 > ulBuffLen)
      {
      rc = ERROR_BUFFER_OVERFLOW;
      break;
      }

   // hand over result
   strcpy( pszBuffer, szKeywordFile);

   // if no update check, return filename if file exists
   if ((ulOptions && HILITE_NOOUTDATECHECK) &&
       (FileExists( szKeywordFile)))
      {
      rc = NO_ERROR;
      break;
      }

   // -----------------------------------------------

   // initialize support for memory mapped files

   rc = MmfInitialize( &hmmf, 16);
   if (rc != NO_ERROR)
      break;

   // -----------------------------------------------

   // Query comment char for temp. epmkwds file from config
   // This char is read from config to avoid unnecessary opening and reading
   // of the text config files.

   QueryConfigValue( hconfig, pszRegPathHilCommentChar, szHilCommentChar);

   // -----------------------------------------------

   // check for the file date - if it doesn't exist, will return -1,
   // then reset to zero to force a rebuild
   ulKeywordFileDate = QueryFileDate( szKeywordFile);
   if (ulKeywordFileDate == - 1)
      ulKeywordFileDate  = 0;

   // get the list of files
   ulFileCount = 0;
   rc = _getFileList( hmmf, pszEpmMode, ulKeywordFileDate, &pszFileList,
                      &ulFileCount, &ulTotalSize, &fOutdated);
   if (rc != NO_ERROR)
      break;

   // if result file is not outdated, check the file list
   if (!fOutdated)
      {
      // check if file has an EA containing the old filelist
      rc = QueryStringEa( szKeywordFile, pszFileInfoListEaName,
                          NULL, &ulInfoListSize);
      if (rc == ERROR_BUFFER_OVERFLOW)
         {
         pszOldFileInfoList = malloc( ulInfoListSize);
         if (!pszOldFileInfoList)
            {
            rc = ERROR_NOT_ENOUGH_MEMORY;
            break;
            }
         rc = QueryStringEa( szKeywordFile, pszFileInfoListEaName,
                             pszOldFileInfoList, &ulInfoListSize);
         if (rc != NO_ERROR)
            break;
         }
      else
         // EA not found, just proceed
         rc = NO_ERROR;
      }

   // -----------------------------------------------

   // generate info list
   ulInfoListSize = ulFileCount * (MAX_INI_LINE + 32);
   ALLOCATEMEMORYFILE( pszFileInfoList, ulInfoListSize);

   // first of all add info of init files to file list
   rc = _searchInitFile( pszEnvnameEpmHilitePath, SEARCHMASK_COMMONCFG, NULL,
                         szInitCommonFilename, sizeof( szInitCommonFilename));
   if (rc != NO_ERROR)
      break;
   pszSourceFile = szInitCommonFilename;
// DPRINTF(( "HILITE: szInitCommonFilename = %s\n", szInitCommonFilename));
   sprintf( _EOS( pszFileInfoList), pszFileInfoMask, szHilCommentChar,
            pszSourceFile, QueryFileSize( pszSourceFile),
            QueryFileDate( pszSourceFile));

   rc = _searchInitFile( pszEnvnameEpmHilitePath, SEARCHMASK_HILITECFG, pszEpmMode,
                         szInitHiliteFilename, sizeof( szInitHiliteFilename));
   if (rc != NO_ERROR)
      break;
   pszSourceFile = szInitHiliteFilename;
// DPRINTF(( "HILITE: szInitHiliteFilename = %s\n", szInitHiliteFilename));
   sprintf( _EOS( pszFileInfoList), pszFileInfoMask, szHilCommentChar,
            pszSourceFile, QueryFileSize( pszSourceFile),
            QueryFileDate( pszSourceFile));

   // loop thru all files
   for (ulCurrentFile = 0, pszSourceFile = pszFileList;
           ulCurrentFile < ulFileCount;
           ulCurrentFile++, pszSourceFile += _MAX_PATH)
      {
      // add file to the info list
      sprintf( _EOS( pszFileInfoList), pszFileInfoMask, szHilCommentChar,
               pszSourceFile, QueryFileSize( pszSourceFile),
               QueryFileDate( pszSourceFile));
      }

   // now check the file list, if old one is present
   // if it is equal (including timestamps!)
   // break with no error
   if (pszOldFileInfoList)
      {
      if (!strcmp( pszOldFileInfoList, pszFileInfoList))
         {
//       DPRINTF(( "HILITE: File has not changed.\n"
//                 "Files used for last generation\n"
//                 "------------------------------\n"
//                 "%s\n", pszOldFileInfoList));

         *pfReload = FALSE;
         }
      }

// DPRINTF(( "HILITE: *pfReload 1 = %d\n", *pfReload));

   // -----------------------------------------------

   // Query value from config for further processing

   sprintf( szRegPath, pszRegPathTemplate, pszEpmMode, REGKEY_CASESENSITIVE);
   memset( szDataVal, 0, sizeof( szDataVal));
   QueryConfigValue( hconfig, szRegPath, szDataVal);

   // Key doesn't exist or is empty: query DEFAULT value
   if (*szDataVal == 0)
      {
      sprintf( szRegPath, pszRegPathTemplate, "DEFAULT", REGKEY_CASESENSITIVE);
      memset( szDataVal, 0, sizeof( szDataVal));
      QueryConfigValue( hconfig, szRegPath, szDataVal);
      }
   if (*szDataVal == 0)
      strcpy( szDataVal, "1");

   fCaseSensitive = atol( szDataVal);
// DPRINTF(( "HILITE: Case sensitive = %d\n", fCaseSensitive));

   // -----------------------------------------------

   // Omit the following check, if *pfReload is already TRUE
   do {
      if (*pfReload)
         break;

      // Compare previously saved config values with current ones

      sprintf( szRegPath, pszRegPathPrevTemplate, pszEpmMode, REGKEY_CASESENSITIVE);
      memset( szDataVal, 0, sizeof( szDataVal));
      QueryConfigValue( hconfig, szRegPath, szDataVal);

      // Key doesn't exist or is empty: query DEFAULT value
      if (*szDataVal == 0)
         {
         sprintf( szRegPath, pszRegPathTemplate, "DEFAULT", REGKEY_CASESENSITIVE);
         memset( szDataVal, 0, sizeof( szDataVal));
         QueryConfigValue( hconfig, szRegPath, szDataVal);
         }
      if (*szDataVal == 0)
         strcpy( szDataVal, "1");

//    DPRINTF(( "HILITE: prev CaseSensitive = %s, cur CaseSensitive = %d\n", szDataVal, fCaseSensitive));
      if (!*szDataVal || atol( szDataVal) != fCaseSensitive)
         {
         *pfReload = TRUE;
         break;
         }

//    DPRINTF(( "HILITE: *pfReload 2 = %d\n", *pfReload));
      } while (FALSE);

   // -----------------------------------------------

   if (!*pfReload)
      // No need to open and read the text ini files
      // No need to (re)build the temp. epmkwds file
      break;

   // -----------------------------------------------

   // Open common highlight text ini file, read values

   // - read common highlight values
   rc = InitOpenProfile( szInitCommonFilename, &hinitCommon,
                         INIT_OPEN_READONLY, 0, NULL);
   if (rc != NO_ERROR)
      break;
// DPRINTF(("Common filename: '%s'\n", szInitCommonFilename));

   pvaColors  = _maintainInitValueArray( hinitCommon, pszColorsSection, NULL);
   pvaSymbols = _maintainInitValueArray( hinitCommon, pszSymbolsSection, NULL);

   // -----------------------------------------------

   // Open mode highlight text ini file, read values

   // - read highlight values of the mode
   rc = InitOpenProfile( szInitHiliteFilename, &hinitHilite,
                         INIT_OPEN_READONLY, 0, NULL);
   if (rc != NO_ERROR)
      break;
// DPRINTF(("Hilite filename: '%s'\n", szInitHiliteFilename));

   QUERYINITVALUE( hinitHilite, pszEpmkwdsFileSection, KEY_CHARSET, szCharset);
// DPRINTF(( "HILITE: Charset = %s\n", szCharset));

   DUMPINITVALUEARRAY( pvaSymbols);

   // add/replace with symbols from <mode>\hilite.cfg
   pvaSymbols = _maintainInitValueArray( hinitHilite, pszSymbolsSection,
                                         pvaSymbols);

   DUMPINITVALUEARRAY( pvaSymbols);

   // -----------------------------------------------

   // replace color values in symbol defs
   ppszEntry = pvaSymbols->apszValue;
   for (i = 0; i < pvaSymbols->ulCount; i++)
      {

               PSZ            pszValueBuf;
               ULONG          ulValueLen;
               PSZ            pszValue;

      // loop through all symbols
      pszEntry =  *ppszEntry;
      szValue[ 0] = 0;

      // replace color names with color values
      pszValueBuf = NEXTSTR( *ppszEntry);
      ulValueLen  = strlen( pszValueBuf);

      pszSymbol = strtok( pszValueBuf, " ");
      while (pszSymbol)
         {
         ppszSymbolValue = bsearch( &pszSymbol,
                                    pvaColors->apszValue,
                                    pvaColors->ulCount,
                                    sizeof( PSZ),
                                    _compareValue);
         if (ppszSymbolValue)
            {
            pszValue = NEXTSTR( *ppszSymbolValue);
            strcat( szValue, " ");
            strcat( szValue, pszValue);
//          DPRINTF(( "HILITE: %s: %s -> %s\n", pszEntry, pszSymbol, pszValue));
            }

         // next symbol
         pszSymbol = strtok( NULL, " ");
         }

      // check buffer
      _stripblanks( szValue);
      if (strlen( szValue) <= ulValueLen)
         strcpy( pszValueBuf, szValue);

      // next entry
      ppszEntry++;
      }

   // -----------------------------------------------

   // open up in-memory files for the six sections
   ALLOCATEMEMORYFILE( pszSectionDelimiter,  ulTotalSize);
   ALLOCATEMEMORYFILE( pszSectionDelimiteri, ulTotalSize);
   ALLOCATEMEMORYFILE( pszSectionKeywords,   ulTotalSize * 2);
   ALLOCATEMEMORYFILE( pszSectionSpecial,    ulTotalSize);
   ALLOCATEMEMORYFILE( pszSectionBreakChar,  ulTotalSize);
   ALLOCATEMEMORYFILE( pszSectionEndChar,    ulTotalSize);

   pszCurrentDelimiter  = pszSectionDelimiter;
   pszCurrentDelimiteri = pszSectionDelimiteri;
   pszCurrentKeywords   = pszSectionKeywords;
   pszCurrentSpecial    = pszSectionSpecial;
   pszCurrentBreakChar  = pszSectionBreakChar;
   pszCurrentEndChar    = pszSectionEndChar;

   // -----------------------------------------------

   // loop thru all files
   for (ulCurrentFile = 0, pszSourceFile = pszFileList;
           ulCurrentFile < ulFileCount;
           ulCurrentFile++, pszSourceFile += _MAX_PATH)
      {

      do
         {
         // open the file
//       DPRINTF(( "HILITE: process file %s\n", pszSourceFile));
         pfile = fopen( pszSourceFile, "r");
         szCurrentSection[ 0] = 0;
         pszCurrentSectionColors = 0;
         pszLine = szLine;
         ulLineCount = 0;

         while (!feof( pfile))
            {
            // read line
            if (!fgets( szLine, sizeof( szLine),pfile))
               break;
            pszLine = szLine;
            ulLineCount++;

            // skip comments - the comment char must be on the first column!
            if ((*pszLine == CHAR_HILCOMMENT) ||
                (*pszLine == 0))
               continue;

            // check for section start before stipping blanks!
            fSectionStart = (*pszLine == CHAR_SECTION_START);

            // skip leading blanks anyway (also trailing newline)
            _stripblanks( pszLine);

            // skip empty lines
            if (*pszLine == 0)
               continue;

            // handle new section
            if (fSectionStart)
               {
               strcpy( pszLine, pszLine + 1);
               p = strchr( pszLine, CHAR_SECTION_END);
               if (!p)
                  continue;
               *p = 0;

               // if there exists a symbol, store values
               strupr( pszLine);
               pszCurrentSectionColors = _queryInitValue( pvaSymbols, pszLine);
               if (pszCurrentSectionColors)
                  {
                  strcpy( szCurrentSection, strupr( pszLine));
//                DPRINTF(( "HILITE: - Process section %s with colors: %s\n", szCurrentSection, pszCurrentSectionColors));
                  }
               else
                  DPRINTF(( "HILITE: - Error: skipping invalid section = %s from line %u on, filename = %s\n", pszLine, ulLineCount, pszSourceFile));

               // check if the values of the current symbol belong to a special section
               ulSectionIndex = 0;
               for (i = 1; i < COUNT_SPECIALSECTION; i++)
                  {
                  if (!strcmp( apszSpecialSections[ i], szCurrentSection))
                     {
                     ulSectionIndex = i;
//                   DPRINTF(( "HILITE: - special section recognized, index %u\n", ulSectionIndex));
                     }
                  }

               continue;
               }

            // from here, skip line if no valid section has been found
            if (!pszCurrentSectionColors)
               continue;

            // tokenize line
            pszStartStr    = NULL;
            pszStopStr     = NULL;
            pszBreakStr    = NULL;
            pszStartPos    = NULL;

            pszSymbolValue = NULL;
            pszColorValue1 = NULL;
            pszColorValue2 = NULL;
            pszInvalid     = NULL;

            pszStartStr = strtok( pszLine, " ");
            pszLine     = strtok( NULL, " ");
            while (pszLine)
               {
               do
                  {

                  // ------------------------------------
                  // check if keyword is a symbol
                  p = _queryInitValue( pvaSymbols, pszLine);
                  if (p)
                     {
                     if ((!pszSymbolValue) &&
                         (!pszColorValue1) &&
                         (!pszColorValue2))
                        pszSymbolValue = p;
                     else
                        pszInvalid = pszLine;

                     break;
                     }

                  // ------------------------------------

                  // check if keyword is a color
                  p = _queryInitValue( pvaColors, pszLine);
                  if (p)
                     {
#if ALLOW_COLOR_VALUES
                     if ((!pszColorValue1) && (!pszSymbolValue))
                        pszColorValue1 = p;
                     else if ((!pszColorValue2)  && (!pszSymbolValue))
                        pszColorValue2 = p;
                     else
                        pszInvalid = pszLine;
#else
                     pszInvalid = pszLine;
#endif
                     break;
                     }

                  // ------------------------------------

                  // use comment char if special keyword is specified
                  if (!strcmp( pszKeywordNone, pszLine))
                     pszLine = szHilCommentChar;

                  // store all other values
                  if (!pszStopStr)
                     pszStopStr = pszLine;
                  else if (!pszBreakStr)
                     pszBreakStr = pszLine;
                  else if (!pszStartPos)
                     pszStartPos = pszLine;
                  else
                     pszInvalid = pszLine;

                  } while (FALSE);

               if (pszInvalid)
                  break;

               // next one
               pszLine = strtok( NULL, " ");
               }

            // still invalid line?
            if (pszInvalid)
               {
               DPRINTF(( "HILITE: Error: skipping invalid line %u, invalid token = %s, filename = %s\n", ulLineCount, pszInvalid, pszSourceFile));
               continue;
               }

            // check for color to be used
            fEntryColors = FALSE;
            if (pszSymbolValue)
               {
               strcpy( szEntryColors, pszSymbolValue);
               fEntryColors = TRUE;
               }
            else if ((pszColorValue1) && (pszColorValue2))
               {
               sprintf( szEntryColors, "%s %s", pszColorValue1, pszColorValue2);
               fEntryColors = TRUE;
               }
            else
               strcpy( szEntryColors, pszCurrentSectionColors);

            // if a stop character is still available, make it a DELIM/DELIMI entry
            ulSelectIndex = ulSectionIndex;
            if (pszStopStr)
               ulSelectIndex = SECTION_COMMENT;

            // handle different sections
            switch (ulSelectIndex)
               {
               case SECTION_DEFAULT:
                  sprintf( pszCurrentKeywords, "%s %s\r\n", pszStartStr, szEntryColors);
                  pszCurrentKeywords = _EOS( pszCurrentKeywords);
                  break;

               case SECTION_COMMENT:
               case SECTION_LITERAL:
                  // Use DELIM or DELIMI section? Escape char doesn't work with DELIMI
                  // (EPM bug), so prefer DELIM.

                  // Case-sensitive mode: Use DELIM.
                  if (fCaseSensitive)
                     fDELIMI = FALSE;
                  // Escape char is different from comment char: Use DELIM,
                  // because escaping doesn't work with DELIMI.
                  else if (pszBreakStr && strcmp( pszBreakStr, szHilCommentChar))
                     fDELIMI = FALSE;
                  // No letters in start string and end string: Use DELIM.
                  else if (!_hasletters( pszStartStr) && !_hasletters( pszStopStr))
                     fDELIMI = FALSE;
                  // Else must use DELIMI.
                  else
                     fDELIMI = TRUE;
                  //DPRINTF(( "pszStartStr = %s, pszStopStr = %s, pszBreakStr = %s, fCaseSensitive = %d, fEntryColors = %d, pszStartPos = %s, fDELIMI = %d\n", pszStartStr, pszStopStr, pszBreakStr, fCaseSensitive, fEntryColors, pszStartPos, fDELIMI));

                  p = (fDELIMI) ?
                     pszCurrentDelimiteri : pszCurrentDelimiter;

                  sprintf( p, "%s %s",
                           pszStartStr, szEntryColors);
                  if (pszStopStr)
                     sprintf( _EOS( p), " %s", pszStopStr);
                  if (pszBreakStr)
                     sprintf( _EOS( p), " %s", pszBreakStr);
                  if (pszStartPos)
                     sprintf( _EOS( p), " %s", pszStartPos);
                  strcat( p, "\r\n");

                  if (fDELIMI)
                     pszCurrentDelimiteri = _EOS( pszCurrentDelimiteri);
                  else
                     pszCurrentDelimiter = _EOS( pszCurrentDelimiter);
                  break;

               case SECTION_SPECIAL:
               case SECTION_OPERATOR:
                  sprintf( pszCurrentSpecial, "%s %s\r\n", pszStartStr, szEntryColors);
                  pszCurrentSpecial = _EOS( pszCurrentSpecial);
                  break;

               case SECTION_BREAKCHAR:
                  sprintf( pszCurrentBreakChar, "%s", pszStartStr);
                  if (fEntryColors)
                     sprintf( _EOS( pszCurrentBreakChar), " %s", szEntryColors);
                  strcat( pszCurrentBreakChar, "\r\n");
                  pszCurrentBreakChar = _EOS( pszCurrentBreakChar);
                  break;

               case SECTION_ENDCHAR:
                  sprintf( pszCurrentEndChar, "%s", pszStartStr);
                  if (fEntryColors)
                     sprintf( _EOS( pszCurrentEndChar), " %s", szEntryColors);
                  strcat( pszCurrentEndChar, "\r\n");
                  pszCurrentEndChar = _EOS( pszCurrentEndChar);
                  break;

               } // switch (ulThisSectionIndex)

            } // while (!feof( pinit->pfile))

         } while (FALSE);

      // cleanup
      if (pfile) fclose( pfile);

      } // for [all files]

   // -----------------------------------------------

   // determine the length
   ulHiliteContentsLen = (pszCurrentDelimiter  - pszSectionDelimiter)  +
                         (pszCurrentDelimiteri - pszSectionDelimiteri) +
                         (pszCurrentKeywords   - pszSectionKeywords)   +
                         (pszCurrentSpecial    - pszSectionSpecial)    +
                         (pszCurrentBreakChar  - pszSectionBreakChar)  +
                         (pszCurrentEndChar    - pszSectionEndChar)    +
                         (strlen(szCharset) + 32);

// DPRINTF(( "HILITE: assembling %u bytes to hilite file: %s\n", ulHiliteContentsLen, szKeywordFile));

   rc = MmfAlloc( hmmf,
                  (PVOID*)&pszHiliteContents,
                  szKeywordFile,
                  MMF_ACCESS_READWRITE |
                  MMF_OPENMODE_RESETFILE,
                  ulHiliteContentsLen + 4096);
   if (rc != NO_ERROR)
      break;

// DPRINTF(( "- allocated buffer at 0x%08x, len %u, end address 0x%08x\n",
//           pszHiliteContents, ulHiliteContentsLen, pszHiliteContents + ulHiliteContentsLen));

   // write one line with the comment char only, followed by header and FileInfoList
   pszCurrent = pszHiliteContents;
   sprintf( pszCurrent, "%s\r\n"
                        "%s NEPMD keyword highlighting definition - Files used\r\n"
                        "%s ==================================================\r\n",
                        szHilCommentChar, szHilCommentChar, szHilCommentChar);
   pszCurrent = _EOS( pszCurrent);
   strcat( pszCurrent, pszFileInfoList);
   pszCurrent = _EOS( pszCurrent);

   // first of all write CHARSET
   sprintf( pszCurrent, pszHeaderMask, szHilCommentChar, "CHARSET");
// DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
   pszCurrent = _EOS( pszCurrent);
   sprintf( pszCurrent, "%s\r\n", szCharset);
   pszCurrent = _EOS( pszCurrent);

   // add all sections, if something in it
   if (pszCurrentDelimiter - pszSectionDelimiter)
      {
      sprintf( pszCurrent, pszHeaderMask, szHilCommentChar, "DELIM");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionDelimiter);
      pszCurrent = _EOS( pszCurrent);
      }

   if (pszCurrentDelimiteri - pszSectionDelimiteri)
      {
      sprintf( pszCurrent, pszHeaderMask, szHilCommentChar, "DELIMI");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionDelimiteri);
      pszCurrent = _EOS( pszCurrent);
      }

   if (pszCurrentKeywords  - pszSectionKeywords)
      {
      sprintf( pszCurrent, pszHeaderMask, szHilCommentChar, (fCaseSensitive) ? "KEYWORDS" : "INSENSITIVE");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionKeywords);
      pszCurrent = _EOS( pszCurrent);
      }

   if (pszCurrentSpecial   - pszSectionSpecial)
      {
      sprintf( pszCurrent,  pszHeaderMask, szHilCommentChar, (fCaseSensitive) ? "SPECIAL"  : "SPECIALI");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionSpecial);
      pszCurrent = _EOS( pszCurrent);
      }

   if (pszCurrentBreakChar - pszSectionBreakChar)
      {
      sprintf( pszCurrent,  pszHeaderMask, szHilCommentChar, "BREAK");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionBreakChar);
      pszCurrent = _EOS( pszCurrent);
      }

   if (pszCurrentEndChar   - pszSectionEndChar)
      {
      sprintf( pszCurrent,  pszHeaderMask, szHilCommentChar, "ENDCHAR");
//    DPRINTF(( "-  at 0x%08x adding header for %s\n", pszCurrent, pszCurrent));
      pszCurrent = _EOS( pszCurrent);
      strcpy( pszCurrent, pszSectionEndChar);
      pszCurrent = _EOS( pszCurrent);
      }

   // set filesize
   ulHiliteContentsLen = strlen( pszHiliteContents);
   rc = MmfSetSize( hmmf, pszHiliteContents, ulHiliteContentsLen);
// DPRINTF(( "-  setting filesize to size of %u bytes, rc=%u\n", ulHiliteContentsLen, rc));

   // write temporary file
   rc = MmfUpdate( hmmf, pszHiliteContents);
// DPRINTF(( "-  update file, rc=%u\n", rc));

   // close target file first so that we can write the EA
   FREEMEMORYFILE( pszHiliteContents);
   pszHiliteContents = NULL;

   // add file infolist as extended attribute
   rc = WriteStringEa( szKeywordFile, pszFileInfoListEaName, pszFileInfoList);
// DPRINTF(( "\n"
//           "HILITE: used files for generation\n"
//           "---------------------------------\n"
//           "%s\n", pszFileInfoList));

   // -----------------------------------------------

   // At the end write current config values for next comparison
   sprintf( szRegPath, pszRegPathPrevTemplate, pszEpmMode, REGKEY_CASESENSITIVE);
   _ltoa( fCaseSensitive, szDataVal, 10);
   WriteConfigValue( hconfig, szRegPath, szDataVal, strlen( szDataVal) + 1);

   } while (FALSE);

// cleanup

if (pvaColors)            free( pvaColors);
if (pvaSymbols)           free( pvaSymbols);
if (pszOldFileInfoList)   free( pszOldFileInfoList);

if (pszFileList)          FREEMEMORYFILE( pszFileList);
if (pszFileInfoList)      FREEMEMORYFILE( pszFileInfoList);
if (pszSectionDelimiter)  FREEMEMORYFILE( pszSectionDelimiter);
if (pszSectionDelimiteri) FREEMEMORYFILE( pszSectionDelimiteri);
if (pszSectionKeywords)   FREEMEMORYFILE( pszSectionKeywords);
if (pszSectionSpecial)    FREEMEMORYFILE( pszSectionSpecial);
if (pszSectionBreakChar)  FREEMEMORYFILE( pszSectionBreakChar);
if (pszSectionEndChar)    FREEMEMORYFILE( pszSectionEndChar);
if (pszHiliteContents)    FREEMEMORYFILE( pszHiliteContents);

if (hinitHilite)          InitCloseProfile( hinitHilite, FALSE);
if (hinitCommon)          InitCloseProfile( hinitCommon, FALSE);
if (hmmf)                 MmfTerminate( hmmf);

FUNCEXITRC;
return rc;
}

// -----------------------------------------------------------------------------

APIRET QueryHilightFile( PSZ pszEpmMode, ULONG ulOptions, PBOOL pfReload,
                         HCONFIG hconfig,
                         PSZ pszBuffer, ULONG ulBuffLen)
{
         APIRET         rc = NO_ERROR;
         CHAR           szValue[ _MAX_PATH];

do
   {
   // init return value first
   if (pszBuffer)
      memset( pszBuffer, 0, ulBuffLen);

   // check parms
   if ((!pszEpmMode)   ||
       (!*pszEpmMode)  ||
       (!pfReload)     ||
       (!pszBuffer))
      {
      rc = ERROR_INVALID_PARAMETER;
      break;
      }

// DPRINTF(( "### HILITE: %s: options: %08x\n", pszEpmMode, ulOptions));

   // search hilite files; if outdated, rebuild temp. epmkwds file
   rc = _assembleKeywordFile( pszEpmMode, ulOptions, pfReload, hconfig, szValue, sizeof( szValue));
   if (rc == NO_ERROR)
      {
      // disable reload if HighlightFlag <> 0
      if (ulOptions)
         *pfReload = FALSE;
      }
   else
      {
      // if no hilite infos available, search for an EPMKWDS.* file
      rc = _searchFile( pszEnvnameEpmkwdsPath, szValue, sizeof( szValue), SEARCHMASK_EPMKWDS, pszEpmMode);
      if (rc != NO_ERROR)
         break;
      // always do not reload this one (default EPM behaviour)
      *pfReload = FALSE;
      }

   // check result buffer
   if (strlen( szValue) + 1 > ulBuffLen)
      {
      rc = ERROR_BUFFER_OVERFLOW;
      break;
      }

   // hand over result
   strcpy( pszBuffer, szValue);

   } while (FALSE);

return rc;
}

