/*!---------------------------------------------------------------------
  @file           RTE_IniFileHandling.c
  @author         JoergM
  @brief          Registry for UNIX: Handling of '.ini' files on UNIX
  @see            example.html ...


    Each .ini File consists of sections, which are identified by a section header.
    The section header consists of an name framed by brackets.
    Each entry in the section consists of a key an value separated by an equal sign.
    The section is seperated from the next section by an empty line.
    Example
    [Database]
    SUT72=/home/remuser/releases/V72/usr
    MUT7264=/home/remuser/releases/V7264/usr

    first created 1998-06-23 12:30

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2003-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


\endif
---------------------------------------------------------------------*/

#include "RunTime/RTE_IniFileHandling.h"
#include "SAPDBCommon/SAPDB_Names.h"
#include <malloc.h>
#include "geo007_2.h"
#include "heo46.h"

/*
 * first check if any UNIX platform is defined
 */
#if !(defined SUN || defined AIX || defined OSF1 || defined NMP || defined HPUX || defined(LINUX))

#include <windows.h>
#include <shlobj.h>


static SAPDB_Int4 RTE_GetWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk );

static SAPDB_Int4 RTE_PutWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        const SAPDB_Char *    szString,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk );

static SAPDB_Int4 RTE_RemoveWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk );
static RTE_RegistryHandle RTE_OpenWindowsConfigEnum( RTE_RegistryHandle hEnum,
                                                     HKEY               globalKey,
                                                     const SAPDB_Char *       szFile,
                                                     const SAPDB_Char *       szSection,
                                                     RTE_IniFileErrtext ErrText,
                                                     RTE_IniFileResult * pOk );
static SAPDB_Int4 RTE_CloseWindowsConfigEnum ( RTE_RegistryHandle hEnum,
                                               RTE_IniFileErrtext ErrText,
                                               RTE_IniFileResult * pOk );
static SAPDB_Int4 RTE_NextWindowsConfigEnum( RTE_RegistryHandle hEnum,
                                             SAPDB_Char *szEntry,
                                             const SAPDB_Int4 MaxEntryLen,
                                             SAPDB_Char *szString,
                                             const SAPDB_Int4 MaxStringLen,
                                             RTE_IniFileErrtext ErrText,
                                             RTE_IniFileResult * pOk );
#else

#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>

/*! found in Env{"ODBCINI"} oder in home directory */
#define UNIX_USER_ODBC_INI_FILE "/.odbc.ini"
/*! found in absolute path */
#define UNIX_GLOBAL_ODBC_INI_FILE "/etc/odbc.ini"

/* for void *alloca(size_t) */
#ifdef AIX

# include       <malloc.h>

#else

# ifdef NMP

   extern void *alloca(size_t);

# else

#  include       <alloca.h>

# endif

#endif

/*
 *  declarations for config file processing
 */
#define LINE_SIZE_MAX 800

/*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/

/*
 * This must be implemented as a Macro, since it uses alloca...
 */
#define READLINE_EN09(_Fd, _Buffer, _OldSize, _Size, _MoreData, _Result) \
  _OldSize = 0; \
  for ( _MoreData = true; _MoreData; ) \
  { \
    _Result = ReadLine ( _Fd, _Buffer+_OldSize, (_Size-_OldSize), &_MoreData ); \
    if ( _Result != 1 ) \
    { \
      break; \
    } \
\
    if ( _MoreData ) \
    { \
      SAPDB_Char *SecondBuffer; \
      _OldSize = _Size-1; \
      _Size += LINE_SIZE_MAX; \
\
      SecondBuffer = (SAPDB_Char *)alloca(_Size); \
      strcpy( SecondBuffer, _Buffer); \
      _Buffer = SecondBuffer; \
    } \
  }

/*
 * This must be implemented as a Macro, since it uses alloca...
 */
#define COPYLINE_EN09(_ppNextEntry, _Buffer, _OldSize, _Size, _MoreData, _Result) \
  _OldSize = 0; \
  for ( _MoreData = true; _MoreData; ) \
  { \
    _Result = CopyLine ( _ppNextEntry, _Buffer+_OldSize, (_Size-_OldSize), &_MoreData ); \
    if ( _Result != 1 ) \
    { \
      break; \
    } \
\
    if ( _MoreData ) \
    { \
      SAPDB_Char *SecondBuffer; \
      _OldSize = _Size-1; \
      _Size += LINE_SIZE_MAX; \
\
      SecondBuffer = (SAPDB_Char *)alloca(_Size); \
      strcpy( SecondBuffer, _Buffer); \
      _Buffer = SecondBuffer; \
    } \
  }

/*===========================================================================*
 *  LOCAL FUNCTIONS (PROTOTYPES)                                             *
 *===========================================================================*/

typedef struct _LockFile {
    SAPDB_Bool m_IsTemp;
    const SAPDB_Char *m_Path;
    SAPDB_Int4 Fd;
    SAPDB_Bool m_IsOpen;
    SAPDB_Bool m_IsLocked;
    SAPDB_Bool m_ReadOnly;
} LockFile;

static SAPDB_Char *CopySection(SAPDB_Int4 Fd);
static SAPDB_Int4 CopyLine (
                     SAPDB_Char **ppNextEntry,
                     SAPDB_Char * pBuffer,
                     SAPDB_Int4    BufSize,
                     SAPDB_Bool * pMoreData );
static SAPDB_Int4 ReadLine (SAPDB_Int4,
                            SAPDB_Char *,
                            SAPDB_Int4 /* PTS 1104679 */,
                            SAPDB_Bool * );
static SAPDB_Int4 FindSection (SAPDB_Int4,
                               const SAPDB_Char *);
static SAPDB_Int4 WriteSection (SAPDB_Int4,
                                const SAPDB_Char*,
                                SAPDB_Int4);
static SAPDB_Int4 WriteEntry (SAPDB_Int4,
                              const SAPDB_Char *,
                              const SAPDB_Char *);
static SAPDB_Int4 WriteLine (SAPDB_Int4,
                             const SAPDB_Char*);
static SAPDB_Int4 UpdateConfigString (const SAPDB_Char *szPath, 
                                      const SAPDB_Char *szSection, 
                                      const SAPDB_Char *szEntry,
                                      const SAPDB_Char *szString,
                                      const SAPDB_Bool DeleteFlag,
                                      RTE_IniFileErrtext ErrText,
                                      RTE_IniFileResult *pOk);
static SAPDB_Int4 UpdateInstallationConfigString (const SAPDB_Char *szEntry,
                                                  const SAPDB_Char *szString,
                                                  const SAPDB_Bool DeleteFlag,
                                                  RTE_IniFileErrtext ErrText,
                                                  RTE_IniFileResult *pOk);
static SAPDB_Int4 UnlockedGetConfigString (LockFile       *pRegistryFile,
                                           const SAPDB_Char *    szPath,
                                           const SAPDB_Char *    szSection,
                                           const SAPDB_Char *    szEntry, 
                                           SAPDB_Char       *    szString,
                                           const SAPDB_Int4    MaxStringLen,
                                           RTE_IniFileErrtext  ErrText,
                                           RTE_IniFileResult * pOk);
static SAPDB_Int4 RTE_NextUNIXConfigEnum( RTE_RegistryHandle hEnum,
                                          SAPDB_Char *szEntry,
                                          const SAPDB_Int4 MaxEntryLen,
                                          SAPDB_Char *szString,
                                          const SAPDB_Int4 MaxStringLen,
                                          RTE_IniFileErrtext ErrText,
                                          RTE_IniFileResult * pOk );
static SAPDB_Bool FoundMatchingEntry( SAPDB_Char const *buffer
                                    , SAPDB_Char const *szEntry );
static void RegistryFile_Init(LockFile *this,
                              SAPDB_Bool IsTemp,
                              SAPDB_Bool readOnly);
static SAPDB_Bool RegistryFile_Open(LockFile *this,
                                    const SAPDB_Char *Path);
static SAPDB_Bool RegistryFile_Create(LockFile *this,
                                      const SAPDB_Char *Path);
static SAPDB_Bool RegistryFile_Lock(LockFile *this);
static SAPDB_Bool RegistryFile_Reset(LockFile *this);
static void RegistryFile_Close(LockFile *this);
static RTE_RegistryHandle OpenConfigFileForEnumeration (RTE_RegistryHandle hEnum,
                                                        const SAPDB_Char *szPath,
                                                        SAPDB_Int4         readOnly,
                                                        const SAPDB_Char *szSection,
                                                        RTE_IniFileErrtext ErrText, 
                                                        RTE_IniFileResult *pOk);
static SAPDB_Char const *GetLastSystemErrorAsString();
static SAPDB_Bool ValidateConfigPath( SAPDB_Char const * szConfigPath,
                                     RTE_IniFileErrtext ErrText,
                                     RTE_IniFileResult *pOk);
static SAPDB_Bool myGetEnv  ( SAPDB_Char        *EnvName,
                              SAPDB_Char        *EnvValue,
                              SAPDB_Int4         MaxEnvValueLen);
static int my_save_chmod(SAPDB_Char const *path, SAPDB_Int4 newMode);
static SAPDB_Int4 RTE_RemoveUNIXConfigString (  const SAPDB_Char *szFile,
                                                const SAPDB_Char *szSection,
                                                const SAPDB_Char *szEntry, 
                                                RTE_IniFileErrtext ErrText,
                                                RTE_IniFileResult *pOk);

#endif /*--------- end of UNIX specifc part --------------*/

static SAPDB_Bool TrimDelimiter(RTE_Path path,
                                SAPDB_Int4 forceDelimiter);

/*---------------------------------------------------------------------------*/

/*===========================================================================*
 *  LOCAL VARIABLES                                                          *
 *===========================================================================*/

#if defined( WIN32 ) && !defined( _WIN64 )
static BOOL FunctionPointerInitialized = false;
static BOOL AlternativeRegistryAccess = false;
static LONG (WINAPI *fRegDeleteKeyEx)(HKEY, LPCTSTR, REGSAM, DWORD) = 0;
#endif

/*===========================================================================*
 *  GLOBAL FUNCTIONS (CODE)                                                  *
 *===========================================================================*/

/*
  Function:     RTE_GetGlobalConfigString ()
  description:  Read Config Entry
  
    Gives back a string from ini-file 'szFile' from the position descripted by
    'szSection' and 'szEntry'. It fails if either the file, the section or the
    entry does not exist.
    
  arguments:
    szFile [in] Name of registry file
    szSection [in] Given section to read from
    szEntry [in] Given key of 'key=value' entry
    szString [out] Place to store the value of 'key=value' entry
    MaxStringLen [in] Maximum value buffer size
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
SAPDB_Int4 RTE_GetGlobalConfigString (
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
#if defined(WIN32)
    return RTE_GetWindowsConfigString(HKEY_LOCAL_MACHINE,
                                     (SAPDB_Bool)( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                   && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                      szFile,
                                      szSection,
                                      szEntry,
                                      szString,
                                      MaxStringLen,
                                      ErrText,
                                      pOk );
#else
    SAPDB_Int4 result;
    LockFile RegistryFile;
    SAPDB_Int4 readOnlyFile;
    SAPDB_Char *szPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    readOnlyFile = !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
                || !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE)
                || !strcmp(szFile, SAPDB_ODBC_INI_FILE);

    RegistryFile_Init(&RegistryFile, false, readOnlyFile);

    if ( *szFile == '/' )
    {
        if ( !strncmp(szFile, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/", strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/")) 
          || !strcmp(szFile, SAPDB_GLOBAL_CONFIG_FILE) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(szFile)+1);
            strcpy (szPath, szFile);
        }
        else
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
            strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
            return 0;
        }
    }
    else
    {
        if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(UNIX_GLOBAL_ODBC_INI_FILE)+1);
            strcpy(szPath, UNIX_GLOBAL_ODBC_INI_FILE);
        }
        else
        {
            RTE_Path indepConfigPath;
            if ( !RTE_GetCommonConfigPath(indepConfigPath, true, ErrText) )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }
            szPath = (SAPDB_Char *)alloca(strlen(indepConfigPath)+1+strlen(szFile)+1);
            strcpy(szPath, indepConfigPath); strcat(szPath, szFile);
        }
    }

    result = UnlockedGetConfigString ( &RegistryFile,
                                        szPath,
                                        szSection,
                                        szEntry, 
                                        szString,
                                        MaxStringLen,
                                        ErrText,
                                        pOk);

    RegistryFile_Close(&RegistryFile);

    return result == 0 ? 0 : (SAPDB_Int4)strlen (szString);
#endif
}

/*
  Function:     RTE_GetInstallationConfigString ()
  description:  Read Config Entry from globals section
  
    Gives back a string from "Globals" section in ini-file '/etc/opt/sapdb' from the position
    descripted by 'szEntry'. It fails if either the file, the section or the entry does not exist.
    
  arguments:
    szEntry [in] Given key of 'key=value' entry
    szString [out] Place to store the value of 'key=value' entry
    MaxStringLen [in] Maximum value buffer size
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
SAPDB_Int4 RTE_GetInstallationConfigString (
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
#if defined(WIN32)
    return RTE_GetWindowsConfigString(HKEY_LOCAL_MACHINE,
                                      false,
                                      SAPDB_GLOBAL_CONFIG_FILE,
                                      SAPDB_GLOBALS_SECTION,
                                      szEntry,
                                      szString,
                                      MaxStringLen,
                                      ErrText,
                                      pOk );
#else
    LockFile RegistryFile;
    SAPDB_Int4 result;

    RegistryFile_Init(&RegistryFile, false, true);

    result = UnlockedGetConfigString( &RegistryFile,
                                      SAPDB_GLOBAL_CONFIG_FILE,
                                      SAPDB_GLOBALS_SECTION,
                                      szEntry,
                                      szString,
                                      MaxStringLen,
                                      ErrText,
                                      pOk );
    RegistryFile_Close(&RegistryFile);

    return result == 0 ? 0 : (SAPDB_Int4)strlen (szString);
#endif
}

#if defined(WIN32)
/* ------------------------------------------------------------------
 * PUBLIC FUNCTION RTE_GetCommonApplicationDataPath (Windows only)
 * ------------------------------------------------------------------
 * purpose:   Gets the common path for the application data.
 *
 *     "C:\Documents and Settings\All Users\Application Data\sdb" on Windows is used as path 
 *     for the 'Application Data'. 
 *
 *   applicationDataPath    (out)   - Application Data path 
 *                                    applicationDataPath is a C ( null terminated ) string
 *   terminateWithDelimiter (in)    - TERM_WITHOUT_DELIMITER_EO01 
 *                                  - TERM_WITH_DELIMITER_EO01 
 *   errText                (out)   - Error message if any
 * return - true if call succeeded, false else (ErrText filled)
 */
SAPDB_Bool RTE_GetCommonApplicationDataPath   ( RTE_Path             applicationDataPath,
                                                SAPDB_Bool           terminateWithDelimiter,
                                                RTE_IniFileErrtextc  ErrTextc )
{
    HRESULT     rc;
    int         folder = CSIDL_COMMON_APPDATA;

    rc = SHGetFolderPath( NULL, CSIDL_FLAG_CREATE | folder, NULL, SHGFP_TYPE_CURRENT, applicationDataPath );

    if ( S_OK != rc )
    {
        eo46BuildErrorStringRC (ErrTextc, ERRMSG_SYS_GET_COMMON_APPL_DATA_PATH, rc);
        return false;
    }

    if ( strlen (applicationDataPath) + strlen ("\\" SAPDB_HOME_SUBDIRECTORY) >= sizeof(RTE_Path) )
    {
        eo46BuildErrorStringRC (ErrTextc, ERRMSG_SYS_COMMON_APPL_DATA_PATH_LENGTH, 0);
        return false;
    }

    strcat (applicationDataPath, "\\" SAPDB_HOME_SUBDIRECTORY);

    if (!TrimDelimiter(applicationDataPath, terminateWithDelimiter)) 
    {
        eo46BuildErrorStringRC (ErrTextc, ERRMSG_SYS_COMMON_APPL_DATA_PATH_LENGTH, 0);
        return false;        
    } 
    if (!CreateDirectory(applicationDataPath, 0 )) 
    {
        rc = GetLastError();

        if ( ERROR_ALREADY_EXISTS != rc )
        {
            eo46BuildErrorStringRC (ErrTextc, ERRMSG_SYS_CREATE_COMMON_APPL_DATA_PATH, rc );
            return false;
        }
    } 
    return true;
}
#endif

/*!
   @brief          Read User Specific Config Entry

   Gives back a string from ini-file 'szFile' from the position descripted by
   'szSection' and 'szEntry'. The ini-file is searched in '$HOME/.sapdb' first.
   If no entry is found there, it DOES NOT try again in '<indepData>/.sapdb'!
   It fails if either no file (config_error_open_chn09),
   the section and entry (SAPDB_INIFILE_RESULT_NO_ENTRY)
   does not exist. If it is not accessible another error code is returned. This allows
   to program a fallback lookup in other global files if wanted.
 */
SAPDB_Int4 RTE_GetUserConfigString (
    const SAPDB_Char * szFile,
    const SAPDB_Char * szSection,
    const SAPDB_Char * szEntry,
    SAPDB_Char       * szString,
    const SAPDB_Int4   MaxStringLen,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult       * pOk)
{
#if defined(WIN32)
    return RTE_GetWindowsConfigString(HKEY_CURRENT_USER,
                                     (SAPDB_Bool) ( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                   && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                      szFile,
                                      szSection,
                                      szEntry,
                                      szString,
                                      MaxStringLen,
                                      ErrText,
                                      pOk );
#else
    SAPDB_Int4 result;
    LockFile RegistryFile;
    SAPDB_Int4 readOnlyFile;
    SAPDB_Char *szPath;
    RTE_Path configPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

    if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
    {
        if ( myGetEnv("ODBCINI", configPath, sizeof(RTE_Path) ) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(configPath)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, configPath); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
        else
        {
            struct passwd *myPasswdEntry;

            myPasswdEntry = getpwuid ( geteuid () );

            if ( !myPasswdEntry->pw_dir )
            {
                strcpy(ErrText, "Found no home directory entry");
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }

            szPath = (SAPDB_Char *)alloca(strlen(myPasswdEntry->pw_dir)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, myPasswdEntry->pw_dir); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
    }
    else 
    {
        if ( !RTE_GetUserSpecificConfigPath(configPath, true, ErrText) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            return 0;
        }

        szPath = (SAPDB_Char *)alloca(strlen(configPath)+strlen(szFile)+1);
        strcpy(szPath, configPath); strcat(szPath, szFile);
    }

    RegistryFile_Init(&RegistryFile, false, false);

    result = UnlockedGetConfigString ( &RegistryFile,
                                        szPath,
                                        szSection,
                                        szEntry, 
                                        szString,
                                        MaxStringLen,
                                        ErrText,
                                        pOk);

    RegistryFile_Close(&RegistryFile);

    return result;
#endif
}

/*
  Function:     RTE_GetConfigString ()
  description:  Read Config Entry
  
    Gives back a string from ini-file 'szFile' from the position descripted by
    'szSection' and 'szEntry'. It fails if either the file, the section or the
    entry does not exist. This routine first tries to read the 'User' config string
    and if it fails fallbacks using the global config string file.
    
  arguments:
    szFile [in] Name of registry file
    szSection [in] Given section to read from
    szEntry [in] Given key of 'key=value' entry
    szString [out] Place to store the value of 'key=value' entry
    MaxStringLen [in] Maximum value buffer size
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
SAPDB_Int4 RTE_GetConfigString (
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
    SAPDB_Int4 result;
#if !defined(WIN32)
    /* Skip INSTALLATIONS and DATABASES search in user specific directory */
    if ( strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
      && strcmp(szFile, SAPDB_DATABASES_INI_FILE) )
#endif
    {
        result = RTE_GetUserConfigString( szFile,
                                          szSection,
                                          szEntry,
                                          szString,
                                          MaxStringLen,
                                          ErrText,
                                          pOk);
        if ( SAPDB_INIFILE_RESULT_ERR_OPEN != *pOk
          && SAPDB_INIFILE_RESULT_NO_ENTRY != *pOk )
        {
            return result;
        }
    }

    result = RTE_GetGlobalConfigString( szFile,
                                        szSection,
                                        szEntry,
                                        szString,
                                        MaxStringLen,
                                        ErrText,
                                        pOk);
    if ( SAPDB_INIFILE_RESULT_ERR_OPEN != *pOk
      && SAPDB_INIFILE_RESULT_NO_ENTRY != *pOk )
    {
        return result;
    }

#if defined(WIN32)
    return result;
#else
    {
        char *oldPath;
        if ( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE)
          || !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
          || !strcmp(szFile, SAPDB_DATABASES_INI_FILE) )
        {
            oldPath = (char *)(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/" SAPDB_OLD_GLOBAL_COMMON_CONFIG_FILE);
        }
        else
        {
            oldPath = (char *)alloca(strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/") + strlen(szFile) + 1);
            strcpy(oldPath, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/"); strcat(oldPath, szFile);
        }

        return RTE_GetGlobalConfigString( oldPath,
                                          szSection,
                                          szEntry,
                                          szString,
                                          MaxStringLen,
                                          ErrText,
                                          pOk);
    }
#endif
}
/*---------------------------------------------------------------------------*/
/*                           Put Config String                               */
/*---------------------------------------------------------------------------*/

/*
  Function:     RTE_PutConfigString ()
  description:  Write or update Registry Entry
  
   Inserts or replaces a string into the ini-file name 'szString'
   to a position descripted by 'section'-name and 'entry'-name.
 
   If the file does not exists, it will be created
   files are located in <sapdb_indep>/config.
   If <sapdb_indep>/config does not exists the function fails,
  
   This function needs write permission in '<sapdb_indep>/config'
   to create a temporary file, otherwise it fails 
    
  arguments:
    szFile [in] Name of registry file
    szSection [in] Section to write into
    szEntry [in] Given key of 'key=value' entry
    szString [in] Given value of 'key=value' entry
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: failed ( inspect errno )
        <LI>1: ok
    </UL>
 */
SAPDB_Int4 RTE_PutConfigString (
    const SAPDB_Char *szFile,
    const SAPDB_Char *szSection,
    const SAPDB_Char *szEntry, 
    const SAPDB_Char *szString,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult *pOk)
{
#if defined(WIN32)
    return RTE_PutWindowsConfigString(HKEY_LOCAL_MACHINE,
                                     (SAPDB_Bool)( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                   && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                      szFile,
                                      szSection,
                                      szEntry,
                                      szString,
                                      ErrText,
                                      pOk );
#else
    SAPDB_Int4 result;
    SAPDB_Char *szPath;
    RTE_Path indepConfigPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( !strcmp(szFile, SAPDB_GLOBAL_CONFIG_FILE) )
    {
        szPath = (SAPDB_Char *)alloca(strlen(szFile)+1);
        strcpy(szPath, szFile);
    }
    else if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }
    else
    {
        if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(UNIX_GLOBAL_ODBC_INI_FILE)+1);
            strcpy(szPath, UNIX_GLOBAL_ODBC_INI_FILE);
        }
        else
        {
            if ( !RTE_GetCommonConfigPath(indepConfigPath, false, ErrText) )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }
            /* If needed create directory for registry file */
            if ( !ValidateConfigPath(indepConfigPath, ErrText, pOk) )
            {
                /* Ok & ErrText already setup... */
                return 0;
            }

            szPath = (SAPDB_Char *)alloca(strlen(indepConfigPath)+1+strlen(szFile)+1);
            strcpy(szPath, indepConfigPath); strcat(szPath, "/"); strcat(szPath, szFile);
        }
    }

	if ( !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE) && !access( szPath, F_OK|R_OK ))
	{
		my_save_chmod(szPath, 0644);
	}
	else if ( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) && !access( szPath, F_OK|R_OK ))
	{
		my_save_chmod(szPath, 0644);
	}

	result = UpdateConfigString ( szPath,
                                  szSection,
                                  szEntry,
                                  szString,
                                  false,
                                  ErrText, 
                                  pOk );

	if ( !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE) && ! access( szPath, F_OK ))
	{
		my_save_chmod(szPath, 0444);
	}
	else if (!strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) && ! access( szPath, F_OK ))
	{
		my_save_chmod(szPath, 0444);
	}

    return result;
#endif
}

/*
  Function:     RTE_PutInstallationConfigString ()
  description:  Write or update global Registry Entry
  
   Inserts or replaces a string in the global ini-file.
   The entry named 'szString' is at a position descripted by 
   fixed section "Globals" and 'entry'-name.
 
   If the file does not exists, it will be created
   files are located in "/etc/opt/". If "/etc/opt/" does not exists the function fails,
  
   This function needs write permission in "/etc/opt/"
   to create a temporary file, otherwise it fails too.
    
  arguments:
    szEntry [in] Given key of 'key=value' entry
    szString [in] Given value of 'key=value' entry
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: failed ( inspect errno )
        <LI>1: ok
    </UL>
 */
SAPDB_Int4 RTE_PutInstallationConfigString (
    const SAPDB_Char *szEntry, 
    const SAPDB_Char *szString,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult *pOk)
{
#if defined(WIN32)
    return RTE_PutWindowsConfigString( HKEY_LOCAL_MACHINE,
                                       false,
                                       SAPDB_GLOBAL_CONFIG_FILE,
                                       SAPDB_GLOBALS_SECTION,
                                       szEntry,
                                       szString,
                                       ErrText,
                                       pOk );
#else
    return ( UpdateConfigString ( SAPDB_GLOBAL_CONFIG_FILE,
                                  SAPDB_GLOBALS_SECTION,
                                  szEntry, 
                                  szString, 
                                  false,
                                  ErrText,
                                  pOk) );
#endif
}

/*!
   @brief          Write or update user specific registry Entry
   @param          szFile [in] Name of registry file in $HOME/.sapdb
   @param          szSection [in] Section to write into
   @param          szEntry [in] Given key of 'key=value' entry
   @param          szString [in] Given value of 'key=value' entry
   @param          ErrText [out] Place for descriptive error message
   @param          Ok [out] Error return value  Possible values for Ok 
   &lt;UL>    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_KEY   bad key given (contains '=') or bad entry found (no '=')    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_TEMP  failed to create temp file or directory    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_OPEN  failed to open registry    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_LOCK  failed to lock registry    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_READ  failed to read registry    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_WRITE failed to write temp file    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_SEEK  failed to seek to begin of file    
   &lt;LI>SAPDB_INIFILE_RESULT_ERR_PARAM   NULL pointer given    
   &lt;LI>SAPDB_INIFILE_RESULT_OK  No error 
   &lt;/UL>
   @return         none, but err is updated to
  &lt;UL>
      &lt;LI>0: failed ( inspect Ok )
      &lt;LI>1: ok
  &lt;/UL>


    Inserts or replaces a string into the user specific ini-file name 'szString'
    to a position descripted by 'section'-name and 'entry'-name.
    If the file does not exists, it will be created
    Files are located in '$HOME/.sapdb'.
    If this directory does not exists, the directory 'ini' will be 
    created in '$HOME'
    If '$HOME' does not exists the function failed
    This function needs write permission in '$HOME/.sapdb'
    to create a temporary file, otherwise it fails 
 */
SAPDB_Int4 RTE_PutUserConfigString (
    const SAPDB_Char * szFile,
    const SAPDB_Char * szSection,
    const SAPDB_Char * szEntry,
    const SAPDB_Char * szString,
    RTE_IniFileErrtext   ErrText,
    RTE_IniFileResult    * pOk)
{
#if defined(WIN32)
    return RTE_PutWindowsConfigString( HKEY_CURRENT_USER,
                                     (SAPDB_Bool)( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                   && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                       szFile,
                                       szSection,
                                       szEntry,
                                       szString,
                                       ErrText,
                                       pOk );
#else
    SAPDB_Char *szPath;
    RTE_Path userConfigPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

    if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
    {
        if ( myGetEnv("ODBCINI", userConfigPath, sizeof(RTE_Path) ) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(userConfigPath)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, userConfigPath); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
        else
        {
            struct passwd *myPasswdEntry;

            myPasswdEntry = getpwuid ( geteuid () );

            if ( !myPasswdEntry->pw_dir )
            {
                strcpy(ErrText, "Found no home directory entry");
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }

            szPath = (SAPDB_Char *)alloca(strlen(myPasswdEntry->pw_dir)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, myPasswdEntry->pw_dir); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
    }
    else 
    {
        if ( !RTE_GetUserSpecificConfigPath(userConfigPath, false, ErrText) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            return 0;
        }

        /* If needed create directory for registry file */
        if ( !ValidateConfigPath(userConfigPath, ErrText, pOk) )
        {
            /* Ok & ErrText already setup... */
            return 0;
        }

        szPath = (SAPDB_Char *)alloca(strlen(userConfigPath)+1+strlen(szFile)+1);
        strcpy(szPath, userConfigPath); strcat(szPath, "/"); strcat(szPath, szFile);
    }

    return ( UpdateConfigString ( szPath,
                                  szSection,
                                  szEntry, 
                                  szString, 
                                  false,
                                  ErrText,
                                  pOk) );
#endif
}

/*---------------------------------------------------------------------------*/

/*
  Function:     RTE_RemoveConfigString ()
  description:  Remove entry from Registry
                
    Removes the entry specified by 'szSection' and 'szEntry' from config file 

    This function needs write permission in <indep_data>/config to create a temporary file, otherwise it fails .
    
  arguments:
    szFile [in] Name of registry file
    szSection [in] Section to write into
    szEntry [in] Given key of 'key=value' entry
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value: none, but err is updated to
    <UL>
        <LI>0: failed ( inspect errno )
        <LI>1: ok
    </UL>
 */
SAPDB_Int4 RTE_RemoveConfigString (
    const SAPDB_Char *szFile,
    const SAPDB_Char *szSection,
    const SAPDB_Char *szEntry, 
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult *pOk)
{
#if defined(WIN32)
    return RTE_RemoveWindowsConfigString( HKEY_LOCAL_MACHINE,
                                         (SAPDB_Bool)( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                       && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                          szFile,
                                          szSection,
                                          szEntry,
                                          ErrText,
                                          pOk );
#else
    SAPDB_Int4 firstResult;
    RTE_IniFileErrtext firstErrText;
    RTE_IniFileResult firstOk;
    SAPDB_Int4 fallbackResult;
    RTE_IniFileErrtext fallbackErrText;
    RTE_IniFileResult fallbackOk;
    SAPDB_Char *szPath;
    char *oldPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

	if (( !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE) ||
		  !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE )))
	{
		RTE_Path indepConfigPath;

		if ( !RTE_GetCommonConfigPath(indepConfigPath, false, ErrText) )
		{
			*pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
			return 0;
		}

		szPath = (SAPDB_Char *)alloca(strlen(indepConfigPath)+1+strlen(szFile)+1);
		strcpy(szPath, indepConfigPath); strcat(szPath, "/"); strcat(szPath, szFile);
		
	}

	if ( !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE) && !access( szPath, F_OK | R_OK ))
	{
		my_save_chmod(szPath, 0644);
	}
	else if ( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) && !access( szPath, F_OK | R_OK ))
	{
		my_save_chmod(szPath, 0644);
	}

	firstResult = RTE_RemoveUNIXConfigString ( szFile,
                                               szSection,
                                               szEntry, 
                                               firstErrText,
                                              &firstOk );

	if( !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE) && !access( szPath, F_OK ))
	{
		my_save_chmod(szPath, 0444);
    }
	else if( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) && !access( szPath, F_OK ))
	{
		my_save_chmod(szPath, 0444);
	}

    if ( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE)
        || !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
        || !strcmp(szFile, SAPDB_DATABASES_INI_FILE) )
    {
        oldPath = (char *)(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/" SAPDB_OLD_GLOBAL_COMMON_CONFIG_FILE);
    }
    else
    {
        oldPath = (char *)alloca(strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/") + strlen(szFile) + 1);
        strcpy(oldPath, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/"); strcat(oldPath, szFile);
    }

    fallbackResult = RTE_RemoveUNIXConfigString ( oldPath,
                                                  szSection,
                                                  szEntry, 
                                                  fallbackErrText,
                                                 &fallbackOk );

    if ( SAPDB_INIFILE_RESULT_OK == firstOk )
    {
        *pOk = firstOk;
        return firstResult;
    }

    if ( SAPDB_INIFILE_RESULT_OK == fallbackOk )
    {
        *pOk = fallbackOk;
        return fallbackResult;
    }

    if ( SAPDB_INIFILE_RESULT_NO_ENTRY != firstOk )
    {
        *pOk = firstOk;
        memcpy(ErrText, firstErrText, sizeof(RTE_IniFileErrtext));
        return firstResult;
    }

    *pOk = fallbackOk;
    memcpy(ErrText, fallbackErrText, sizeof(RTE_IniFileErrtext));
    return fallbackResult;
#endif
}

/*!
   @brief          Remove User specific Entry from Registry

     Removes the entry specified by 'szSection' and 'szEntry' from config file 
     This function needs write permission in '$HOME/.sapdb'
     to create a temporary file, otherwise it fails .
 */

SAPDB_Int4 RTE_RemoveUserConfigString (
    const SAPDB_Char * szFile,
    const SAPDB_Char * szSection,
    const SAPDB_Char * szEntry,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult * pOk)
{
#if defined(WIN32)
    return RTE_RemoveWindowsConfigString( HKEY_CURRENT_USER,
                                     (SAPDB_Bool)( (strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) == 0) 
                                   && (strcmp(szSection, SAPDB_RUNTIME_SECTION) == 0) ),
                                          szFile,
                                          szSection,
                                          szEntry,
                                          ErrText,
                                          pOk );
#else
    SAPDB_Char *szPath;
    RTE_Path userConfigPath;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

    if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
    {
        if ( myGetEnv("ODBCINI", userConfigPath, sizeof(RTE_Path) ) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(userConfigPath)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, userConfigPath); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
        else
        {
            struct passwd *myPasswdEntry;

            myPasswdEntry = getpwuid ( geteuid () );

            if ( !myPasswdEntry->pw_dir )
            {
                strcpy(ErrText, "Found no home directory entry");
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }

            szPath = (SAPDB_Char *)alloca(strlen(myPasswdEntry->pw_dir)+strlen(UNIX_USER_ODBC_INI_FILE)+1);
            strcpy(szPath, myPasswdEntry->pw_dir); strcat(szPath, UNIX_USER_ODBC_INI_FILE);
        }
    }
    else 
    {
        if ( !RTE_GetUserSpecificConfigPath(userConfigPath, false, ErrText) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            return 0;
        }

        /* If needed create directory for registry file */
        if ( !ValidateConfigPath(userConfigPath, ErrText, pOk) )
        {
            /* Ok & ErrText already setup... */
            return 0;
        }

        szPath = (SAPDB_Char *)alloca(strlen(userConfigPath)+1+strlen(szFile)+1);
        strcpy(szPath, userConfigPath); strcat(szPath, "/"); strcat(szPath, szFile);
    }

    return ( UpdateConfigString (
                    szPath,
                    szSection,
                    szEntry,
                    NULL, 
                    true, 
                    ErrText,
                    pOk) );
#endif
}

/*---------------------------------------------------------------------------*/

/*
  Function:     RTE_OpenGlobalConfigEnum ()
  description:  Open registry for enumeration of a section
                
    Open the registery and position to the given section.
    It is expected that a loop over 'RTE_NextConfigEnum()' is used to retrieve the
    entries.

    Note: The returned handle has no state information. The complete Registry is locked.
    Therefore you must call RTE_CloseConfigEnum() after last usage, or the Registry is locked
    until end of process.
  
  arguments:
    szFile [in] Name of registry file
    szSection [in] Section to write into
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value:
    <UL>
        handle  used in subsequent RTE_NextConfigEnum calls
    </UL>
 */
RTE_RegistryHandle RTE_OpenGlobalConfigEnum (
    const SAPDB_Char *szFile,
    const SAPDB_Char *szSection,
    RTE_IniFileErrtext ErrText, 
    RTE_IniFileResult *pOk)
{
    SAPDB_Int4      Fd;
    SAPDB_Char *   szPath;
    SAPDB_Int4      ReadResult;
    SAPDB_Int4      readOnly;
    RTE_RegistryHandle hEnum;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "OpenConfigEnum NULL pointer passed" );
        return 0;
    }

    hEnum = (RTE_RegistryHandle)malloc(sizeof(RTE_RegistryHandleStruct)
                                       +strlen(szSection)+1
                                       +strlen(szFile)+1);
    if ( !hEnum )
    {
        strcpy((char *)ErrText, "Out of memory");
        *pOk = SAPDB_INIFILE_RESULT_ERR_MEMORY;
        return 0;
    }

    hEnum->section = (SAPDB_Char *)(hEnum+1);
    strcpy(hEnum->section, szSection);
    hEnum->file = hEnum->section + strlen(szSection) + 1;
    strcpy(hEnum->file, szFile);
    hEnum->parseAll = 0;

#if defined(WIN32)
    hEnum->location = RTE_GlobalRegistry;

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

    return RTE_OpenWindowsConfigEnum( hEnum,
                                      HKEY_LOCAL_MACHINE,
                                      szFile,
                                      szSection,
                                      ErrText,
                                      pOk );
#else
    if ( !strncmp(szFile, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/", strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/")) )
    {
        hEnum->location = RTE_OldUNIXRegistry;
    }
    else
    {
        hEnum->location = RTE_GlobalRegistry;
    }

    if ( *szFile == '/' )
    {
        if ( !strncmp(szFile, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/", strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/"))
          || !strcmp(szFile, SAPDB_GLOBAL_CONFIG_FILE) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(szFile)+1);
            strcpy (szPath, szFile);
        }
        else
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
            strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
            return 0;
        }
    }
    else
    {
        RTE_Path indepConfigPath;
        if ( !RTE_GetCommonConfigPath(indepConfigPath, true, ErrText) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
            return 0;
        }
        szPath = (SAPDB_Char *)alloca(strlen(indepConfigPath)+strlen(szFile)+1);
        strcpy(szPath, indepConfigPath); strcat(szPath, szFile);
    }

    readOnly = (  !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
              ||  !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE) 
              ||  !strcmp(szFile, SAPDB_GLOBAL_CONFIG_FILE)
              || !strncmp(szFile, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/", strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/")) );

    return OpenConfigFileForEnumeration(hEnum,
                                        szPath,
                                        readOnly,
                                        szSection,
                                        ErrText,
                                        pOk);
#endif
}

/*-----------------------------------*/

RTE_RegistryHandle RTE_OpenUserConfigEnum (
    const SAPDB_Char * szFile,
    const SAPDB_Char * szSection,
    RTE_IniFileErrtext ErrText, 
    RTE_IniFileResult  * pOk)
{
    RTE_RegistryHandle hEnum;

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer passed for file or section" );
        return 0;
    }

    if ( *szFile == '/' )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "Only relativ pathes allowed" );
        return 0;
    }

    hEnum = (RTE_RegistryHandle)malloc( sizeof(RTE_RegistryHandleStruct)
                                        +strlen(szSection)+1
                                        +strlen(szFile)+1);
    if ( !hEnum )
    {
        strcpy((char *)ErrText, "Out of memory");
        *pOk = SAPDB_INIFILE_RESULT_ERR_MEMORY;
        return 0;
    }

    hEnum->section = (SAPDB_Char *)(hEnum+1);
    strcpy(hEnum->section, szSection);
    hEnum->file = hEnum->section + strlen(szSection) + 1;
    strcpy(hEnum->file, szFile);
    hEnum->parseAll = 0;

    hEnum->location = RTE_UserRegistry;

#if defined(WIN32)
    return RTE_OpenWindowsConfigEnum( hEnum,
                                      HKEY_CURRENT_USER,
                                      szFile,
                                      szSection,
                                      ErrText,
                                      pOk );
#else
    {
        SAPDB_Char * szPath;
        RTE_Path userConfigPath;
        if ( !RTE_GetUserSpecificConfigPath(userConfigPath, true, ErrText) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            strcpy( (SAPDB_Char *)ErrText, "user configuration path not accessible" );
            free(hEnum);
            return 0;
        }
        szPath = (SAPDB_Char *)alloca(strlen(userConfigPath)+strlen(szFile)+1);
        strcpy(szPath, userConfigPath); strcat(szPath, szFile);

        return OpenConfigFileForEnumeration(hEnum,
                                            szPath,
                                            true,
                                            szSection,
                                            ErrText,
                                            pOk);
    }
#endif
}

/*---------------------------------------------------------------------------*/

RTE_RegistryHandle RTE_OpenConfigEnum (
    const SAPDB_Char * szFile,
    const SAPDB_Char * szSection,
    RTE_IniFileErrtext ErrText, 
    RTE_IniFileResult  * pOk)
{
    RTE_RegistryHandle hEnum;

#if !defined(WIN32)
    /* Skip INSTALLATIONS and DATABASES search in user specific directory */
    if ( strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
      && strcmp(szFile, SAPDB_DATABASES_INI_FILE) )
#endif
    {
        hEnum = RTE_OpenUserConfigEnum(szFile, szSection, ErrText, pOk);
        if ( SAPDB_INIFILE_RESULT_OK == *pOk )
        {
            hEnum->parseAll = 1;
            return hEnum;
        }
    }

    hEnum = RTE_OpenGlobalConfigEnum (szFile, szSection, ErrText, pOk);
#if !defined(WIN32)
    /* For UNIX another special old registry must be parsed if [Installations], [Databases] or [Runtime] section is searched */
    if ( SAPDB_INIFILE_RESULT_OK == *pOk  || '/' == *szFile )
    {
        hEnum->parseAll = 1;
        return hEnum;
    }

    {
        char *oldPath;
        if ( !strcmp(szFile, SAPDB_RUNTIMES_INI_FILE)
            || !strcmp(szFile, SAPDB_INSTALLATIONS_INI_FILE)
            || !strcmp(szFile, SAPDB_DATABASES_INI_FILE) )
        {
            oldPath = (char *)(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/" SAPDB_OLD_GLOBAL_COMMON_CONFIG_FILE);
        }
        else
        {
            oldPath = (char *)alloca(strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/") + strlen(szFile) + 1);
            strcpy(oldPath, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/"); strcat(oldPath, szFile);
        }

        hEnum = RTE_OpenGlobalConfigEnum ( oldPath,
                                           szSection,
                                           ErrText,
                                           pOk );
    }
#endif

    return hEnum;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     RTE_CloseConfigEnum ()
  description:  Closes registry opened by RTE_OpenConfigFile ()
                
    Close the registry. This invalidates the given handle.
    Subsequent calls to RTE_NextConfigEnum() must fail.
  
  arguments:
    hEnum [in] Handle of registry enumeration
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value:
    <UL>
        <LI>0: failed ( inspect errno )
        <LI>1: ok
    </UL>
 */
SAPDB_Int4 RTE_CloseConfigEnum (
    RTE_RegistryHandle hEnum,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult * pOk)
{
    if ( hEnum )
    {
#if defined(WIN32)
        return RTE_CloseWindowsConfigEnum(hEnum, ErrText, pOk);
#else
        void *buffer = hEnum->buffer;

        hEnum->buffer = hEnum->nextEntry = 0;
        free(buffer);
        free(hEnum);

        ErrText[0] = '\0';
        *pOk = SAPDB_INIFILE_RESULT_OK;
        return 1;
#endif
    }
    else
    {
        /* nothing to do */
        *pOk = SAPDB_INIFILE_RESULT_OK;
        return 1;
    }
}

/*
  Function:     RTE_NextConfigEnum ()
  description:  Reads next entry from registry opened by RTE_OpenConfigFile ()
                
   Reads next 'szEntry' and 'szString' from section of config file opened by 
   RTE_OpenConfigEnum ().
   Gives back 'SAPDB_INIFILE_RESULT_EOF' on 'Ok' on end of section or end of file.
  
  arguments:
    hEnum [in] Handle of registry file
    szEntry [out] Place to store to key of 'key=value' entry
    MaxEntryLen [in] Maximum key buffer size
    szString [out] Place to store the value of 'key=value' entry
    MaxStringLen [in] Maximum value buffer size
    ErrText [out] Place for descriptive error message
    Ok [out] Error return value

  return value:
    <UL>
        <LI>0: failed ( inspect errno )
        <LI>1: ok
    </UL>
 */
SAPDB_Int4 RTE_NextConfigEnum (
    RTE_RegistryHandle hEnum,
    SAPDB_Char *szEntry,
    const SAPDB_Int4 MaxEntryLen,
    SAPDB_Char *szString,
    const SAPDB_Int4 MaxStringLen,
    RTE_RegistryLocation *pLocation,
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult *pOk)
{
    *pLocation = hEnum->location;

#if defined(WIN32)
    if ( RTE_NextWindowsConfigEnum( hEnum,
                                    szEntry,
                                    MaxEntryLen,
                                    szString,
                                    MaxStringLen,
                                    ErrText,
                                    pOk ) )
    {
        return 1;
    }

    if ( SAPDB_INIFILE_RESULT_EOF == *pOk
      && hEnum->parseAll 
      && hEnum->location == RTE_UserRegistry )
    {
        RTE_RegistryHandle nextEnum;
        *pOk = SAPDB_INIFILE_RESULT_OK;
        nextEnum = RTE_OpenGlobalConfigEnum ( hEnum->file,
                                              hEnum->section,
                                              ErrText, 
                                              pOk);

        if ( *pOk == SAPDB_INIFILE_RESULT_OK )
        {
            /* exchange search key */
            HKEY hkOld = hEnum->hkResult;
            hEnum->hkResult = nextEnum->hkResult;
            nextEnum->hkResult = hkOld;
            hEnum->Index = 0;

            /* copy new location */
            hEnum->location = nextEnum->location;
            /* close old key and free new space! */
            RTE_CloseConfigEnum(nextEnum, ErrText, pOk);

            return RTE_NextConfigEnum( hEnum,
                                       szEntry,
                                       MaxEntryLen,
                                       szString,
                                       MaxStringLen,
                                       pLocation,
                                       ErrText,
                                       pOk);
        }
    }
    return 0;

#else
    if ( RTE_NextUNIXConfigEnum( hEnum,
                                 szEntry,
                                 MaxEntryLen,
                                 szString,
                                 MaxStringLen,
                                 ErrText,
                                 pOk ) )
    {
        return 1;
    }

    if ( SAPDB_INIFILE_RESULT_EOF == *pOk
      && hEnum->parseAll 
      && hEnum->location != RTE_OldUNIXRegistry )
    {
        RTE_RegistryHandle nextEnum;

        if ( hEnum->location == RTE_GlobalRegistry )
        {
            char *oldPath;
            if ( !strcmp(hEnum->file, SAPDB_RUNTIMES_INI_FILE)
                || !strcmp(hEnum->file, SAPDB_INSTALLATIONS_INI_FILE)
                || !strcmp(hEnum->file, SAPDB_DATABASES_INI_FILE) )
            {
                oldPath = (char *)(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/" SAPDB_OLD_GLOBAL_COMMON_CONFIG_FILE);
            }
            else
            {
                oldPath = (char *)alloca(strlen(SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/") + strlen(hEnum->file) + 1);
                strcpy(oldPath, SAPDB_OLD_GLOBAL_CONFIG_DIRECTORY "/"); strcat(oldPath, hEnum->file);
            }

            nextEnum = RTE_OpenGlobalConfigEnum ( oldPath,
                                                  hEnum->section,
                                                  ErrText,
                                                  pOk );
        }
        else
        {
            nextEnum = RTE_OpenGlobalConfigEnum ( hEnum->file,
                                                  hEnum->section,
                                                  ErrText,
                                                  pOk);
        }

        if ( *pOk == SAPDB_INIFILE_RESULT_OK )
        {
            /* exchange buffer pointer and copy nextEntry pointer */
            SAPDB_Char *oldBuffer = hEnum->buffer;

            hEnum->buffer = nextEnum->buffer;
            hEnum->nextEntry = nextEnum->nextEntry;

            nextEnum->buffer = oldBuffer;

            /* copy new location */
            hEnum->location = nextEnum->location;

            /* close old key and free new space! */
            RTE_CloseConfigEnum(nextEnum, ErrText, pOk);

            return RTE_NextConfigEnum( hEnum,
                                       szEntry,
                                       MaxEntryLen,
                                       szString,
                                       MaxStringLen,
                                       pLocation,
                                       ErrText,
                                       pOk);
        }
        else
        {
            strcpy( ErrText, "End of Registry" );
            *pOk = SAPDB_INIFILE_RESULT_EOF;
        }
    }
    return 0;
#endif
}

/* ------------------------------------------------------------------
 * PUBLIC FUNCTION RTE_GetUserSpecificConfigPath
 * ------------------------------------------------------------------
 * purpose:   gets the work path of the actual SAPDB user working directory
 *
 * If the environment variable "SAPDB_HOMEDIRECTORY" is found and a directory 
 *     with such name exists is accessible, it is used. If it is not accessible,
 *     the environment variable is ignored.
 * Otherwise if a home directory exists the subdirectory ".sapdb" in the home 
 *     directory is used as path. If such a subdirectory exists or can be 
 *     created and is accessible, it is used.
 * NOTE If the directory does not exist this call will try to create it.
 *     On UNIX it does this with mode 0777, so use the current 'umask' setting
 *     if further restriction is wanted. Default should be umask 022.
 *     On Windows the CreateDirectory() with the default security descriptor
 *     NULL is used.
 * If still no valid directory is found and the program runs in an user environ-
 *     ment, the path is build by appending the username to the result of 
 *     RTE_GetIndependentWrkPath().
 *     If such a subdirectory exists or can be created (see above) and is
 *     accessible, it is used. If it is not, an error is returned.
 * Otherwise if not running in an user environment (Windows only), the call
 * falls back to RTE_GetIndependentServiceProtPath().
 *
 *   WrkPath                  - work path to the actual SAPDB for R/3 release 
 *                              WrkPath is a C ( null terminated ) string
 *   TerminateWithDelimiter   - TERM_WITHOUT_DELIMITER_EO01 
 *                            - TERM_WITH_DELIMITER_EO01 
 *   ErrText                  - Error message if any
 * return - true if call succeeded, false else (ErrText filled)
 */
SAPDB_Bool RTE_GetUserSpecificConfigPath ( RTE_Path           ConfigPath,
                                           SAPDB_Bool         TerminateWithDelimiter,
                                           RTE_IniFileErrtext ErrText )
{
#if defined(WIN32)
  if (!RTE_GetCommonDataPath(ConfigPath, true, ErrText))
    return false;

  if(strlen(ConfigPath) + 3 > sizeof(RTE_Path)) {
    strcpy(ErrText, "Path to <indep data>/wrk too long");
    return false;        
  }
  strcat(ConfigPath, "wrk");
  if (!TrimDelimiter(ConfigPath, TerminateWithDelimiter)) {
    strcpy(ErrText, "Path to <indep data>/wrk too long");
    return false;        
  } 
  return true;
#else
    SAPDB_Char *pPath = &ConfigPath[0];
    memset(pPath, 0, sizeof(RTE_Path));
    memset(ErrText, 0, sizeof(RTE_IniFileErrtext));

    if ( !myGetEnv(SAPDB_HOMEDIRECTORY, &ConfigPath[0], sizeof(RTE_Path) ) )
    {
        struct passwd *myPasswdEntry;

        myPasswdEntry = getpwuid ( geteuid () );

        if ( !myPasswdEntry->pw_dir )
        {
            strcpy(ErrText, "Found no home directory entry");
            return false;
        }

        if ( (strlen(myPasswdEntry->pw_dir) + strlen(SAPDB_HOME_SUBDIRECTORY) + 1) < sizeof(RTE_Path) )
        {
            strcpy( pPath, myPasswdEntry->pw_dir );
            strcat( pPath, "/" SAPDB_HOME_SUBDIRECTORY );
        }
        else
        {
            strcpy(ErrText, "Path to home too long");
            return false;
        }

        if ( 0 != access(pPath, R_OK|W_OK) )
        {
            /* Use external umask if you want to restrict this... */
            if ( 0 != mkdir(pPath, 0777)  )
            {
                strcpy(ErrText, "Cannot create sapdb user subdirectory"); /* size of string no longer exceeds size of ErrText... */
                return false;
            }
        }
    }

    if ( 0 != access(pPath, R_OK|W_OK) )
    {
        strcpy(ErrText, "Failed to access directory");
        return false;
    }

    return TrimDelimiter(pPath, TerminateWithDelimiter);
#endif
}

/*
  Get Common Independend Config Path
  @param ConfigPath [out] the path found as IndepData=path entry in /etc/opt/sapdb appened with /config
  @param TerminateWithDelimiter [in] forces to terminate path with path delimiter '/'
  @param ErrText [out] in case of problems filled with error text
  @return true if call succeeded, false if call failed
 */
SAPDB_Bool RTE_GetCommonConfigPath ( RTE_Path           ConfigPath,
                                     SAPDB_Bool         TerminateWithDelimiter,
                                     RTE_IniFileErrtext ErrText )
{
    RTE_IniFileResult Ok;
    SAPDB_Int4 pathLen = RTE_GetInstallationConfigString( SAPDB_INDEPDATA_KEY,
                                                          &ConfigPath[0],
                                                          (const SAPDB_Int4)sizeof(RTE_Path),
                                                          ErrText,
                                                          &Ok );
    if ( Ok == SAPDB_INIFILE_RESULT_OK )
    {
        if ( !TrimDelimiter(&ConfigPath[0], true) )
        {
            strcpy(ErrText, "Independend Data Path too long");
            return false;
        }

        if ( (pathLen + strlen(SAPDB_CONFIG_SUBPATH)) > sizeof(RTE_Path) )
        {
            strcpy(ErrText, "Independend Config Path too long");
            return false;
        }

        strcat(&ConfigPath[0], SAPDB_CONFIG_SUBPATH);
        if ( !TrimDelimiter(&ConfigPath[0], TerminateWithDelimiter) )
        {
            strcpy(ErrText, "Independend Config Path just too long");
            return false;
        }

        return true;
    }
    else
    {
        return false;
    }
}

/*
  Get Common Independend Data Path
  @param ConfigPath [out] the path found as IndepData=path entry in /etc/opt/sapdb appened with /config
  @param TerminateWithDelimiter [in] forces to terminate path with path delimiter '/'
  @param ErrText [out] in case of problems filled with error text
  @return true if call succeeded, false if call failed
 */
SAPDB_Bool RTE_GetCommonDataPath ( RTE_Path           ConfigPath,
                                   SAPDB_Bool         TerminateWithDelimiter,
                                   RTE_IniFileErrtext ErrText )
{
    RTE_IniFileResult Ok;
    SAPDB_Int4 pathLen = RTE_GetInstallationConfigString( SAPDB_INDEPDATA_KEY,
                                                          &ConfigPath[0],
                                                          (const SAPDB_Int4)sizeof(RTE_Path),
                                                          ErrText,
                                                          &Ok );
    if ( Ok == SAPDB_INIFILE_RESULT_OK )
    {
        if ( !TrimDelimiter(&ConfigPath[0], TerminateWithDelimiter) )
        {
            strcpy(ErrText, "Independend Data Path too long");
            return false;
        }

        return true;
    }
    return false;
}

SAPDB_Int4 RTE_GetSapdbOwner ( SAPDB_Char * szOwner,
                               const SAPDB_Int4       MaxStringLen,
                               RTE_IniFileErrtext   ErrText,
                               RTE_IniFileResult * pOk )
{
    return RTE_GetInstallationConfigString (
                        SAPDB_SAPDBOWNER_KEY,
                        szOwner,
                        MaxStringLen,
                        ErrText,
                        pOk );
}

SAPDB_Int4 RTE_GetSapdbGroup ( SAPDB_Char * szOwner,
                               const SAPDB_Int4       MaxStringLen,
                               RTE_IniFileErrtext   ErrText,
                               RTE_IniFileResult * pOk )
{
    return RTE_GetInstallationConfigString (
                        SAPDB_SAPDBGROUP_KEY,
                        szOwner,
                        MaxStringLen,
                        ErrText,
                        pOk );
}

#if !defined(WIN32)
SAPDB_Bool RTE_GetSapdbOwnerUserId(SAPDB_Int4 *pUserId)
{
    static SAPDB_Int4 ownerUserId = -1;
    SAPDB_Char OwnernameBuffer[256];
    RTE_IniFileResult  ok;
    RTE_IniFileErrtext errtext;

    if ( -1 == ownerUserId )
    {
        RTE_GetInstallationConfigString( SAPDB_SAPDBOWNER_KEY, 
                                         OwnernameBuffer, 
                                         (SAPDB_Int4)sizeof(OwnernameBuffer),
                                         errtext,
                                         &ok );
        if ( SAPDB_INIFILE_RESULT_OK != ok)
        {
            return false;
        }

        {
            struct passwd *pwEntry = getpwnam (&OwnernameBuffer[0]);

            if ( ! pwEntry )
            {
                return false;
            }

            ownerUserId = (SAPDB_Int4)(pwEntry->pw_uid);
        }
    }
    *pUserId = ownerUserId;
    return true;
}
#endif

void RTE_InitIniFileHandling( )
{
#if defined( WIN32 ) && !defined( _WIN64 )
	BOOL Wow64Process = false;
	BOOL (WINAPI *fIsWow64Process)(HANDLE, BOOL *);

	fIsWow64Process = (BOOL (WINAPI *)(HANDLE, BOOL *))GetProcAddress( GetModuleHandle( "kernel32" ), "IsWow64Process" );
 	if( !fIsWow64Process )
		return;

	if( !fIsWow64Process( GetCurrentProcess( ), &Wow64Process ))
		return;

	if( !Wow64Process )
		return;

	fRegDeleteKeyEx =
	(LONG (WINAPI *)(HKEY, LPCTSTR, REGSAM, DWORD))GetProcAddress( GetModuleHandle( "advapi32" ), "RegDeleteKeyExA" );
	if( !fRegDeleteKeyEx )
		return;

	AlternativeRegistryAccess = true;
	return;
#endif
}

/*===========================================================================*
 *  LOCAL FUNCTIONS (CODE)                                                   *
 *===========================================================================*/

#if defined SUN || defined AIX || defined OSF1 || defined NMP || defined HPUX || defined(LINUX)
/*----------------------------*/
/*  UNIX only static routines */
/*----------------------------*/

/*
  Function:     ReadLine ()
  description:  Read from given filedescriptor until EOL('\n') or EOF
  
    Puts the next line as a zero terminated string to szBuffer.
    Any nonprintable characters are ignored (this works on dos files too).
    If given buffer space is not sufficient, 'MoreData' returns true.

    An empty line read is returned as a Buffer with first character set to 0, MoreData is false.

    If MoreData flag is set, the outer function must loop to read the rest of the line. Since buffer
    is always 0 terminated, it must use 'BufSize-1' bytes of each call to ReadLine().

    Single character I/O is used since no buffering is used.

  arguments:
    Fd [in] Opened Filedescriptor
    pBuffer [out] Pointer to Buffer space
    BufSize[in] Size of Bufferspace
    MoreData [out] Flag to indicate more data follows

  return value:
    <UL>
        <LI>0: EOL or EOF already reached (Buffer not modified)
        <LI>-1: Read error
        <LI>1: Buffer content filled
    </UL>
 */
static SAPDB_Int4 ReadLine (
                     SAPDB_Int4    Fd,
                     SAPDB_Char * pBuffer,
                     SAPDB_Int4    BufSize,
                     SAPDB_Bool * pMoreData )
{
#undef MF__
#define MF__ MOD__"ReadLine"
  SAPDB_Char Ch;
  SAPDB_Int4 Val;
  SAPDB_Char *ptr;
  SAPDB_Int4 ReadLen ;
  SAPDB_Int4 ReadResult ;

  *pMoreData = true;
  ptr = pBuffer;
  for (ReadLen=0; ReadLen<(BufSize-1); ReadLen++ ) 
  {
    Val = read (Fd, &Ch, 1);
    if (Val == 0)
    {
      *pMoreData = false;
      if (ptr > pBuffer)
      {
        break;
      }
      else
      {
        return 0;
      }
    }

    if ( Val != 1 )
    {
      *pMoreData = false;
      *ptr = 0;
      return -1;
    }

    /* end of line */
    if (Ch == '\n')
    {
      *pMoreData = false;
      break;
    }
    /* ignore any other nonprintable character */
    if (Ch < ' ')
    {
      ReadLen--;
      continue;
    }
    *ptr++ = Ch;
  }
  *ptr = '\0';
  return 1;
}


/*
  Function:     CopyLine ()
  description:  Copy from given buffer until EOL('\n') or '\0'
  
    Puts the next line as a zero terminated string to szBuffer.
    Any nonprintable characters are ignored (this works on dos files too).
    If given buffer space is not sufficient, 'MoreData' returns true.

    An empty line read is returned as a Buffer with first character set to 0, MoreData is false.

    If MoreData flag is set, the outer function must loop to read the rest of the line. Since buffer
    is always 0 terminated, it must use 'BufSize-1' bytes of each call to ReadLine().

  arguments:
    ppNextEntry [inout] pointer to next entry in buffer to copy from (updated)
    pBuffer   [out] Pointer to Buffer space
    BufSize   [in]  Size of Bufferspace
    MoreData  [out] Flag to indicate more data follows

  return value:
    <UL>
        <LI>0: EOL or '\0' already reached (Buffer not modified)
        <LI>-1: Read error
        <LI>1: Buffer content filled
    </UL>
 */
static SAPDB_Int4 CopyLine (
                     SAPDB_Char **ppNextEntry,
                     SAPDB_Char * pBuffer,
                     SAPDB_Int4    BufSize,
                     SAPDB_Bool * pMoreData )
{
#undef MF__
#define MF__ MOD__"CopyLine"
  SAPDB_Char Ch;
  SAPDB_Char *ptr;
  SAPDB_Int4 ReadLen ;
  SAPDB_Int4 ReadResult ;

  *pMoreData = true;
  ptr = pBuffer;
  for (ReadLen=0; ReadLen<(BufSize-1); ReadLen++ ) 
  {
    Ch = **ppNextEntry;
    if ( '\0' == Ch)
    {
      *pMoreData = false;
      if (ptr > pBuffer)
      {
        break;
      }
      else
      {
        return 0;
      }
    }

    /* if not already at end of buffer, adjust next entry pointer */
    ++(*ppNextEntry);

    /* end of line */
    if ( '\n' == Ch )
    {
      *pMoreData = false;
      break;
    }
    /* ignore any other nonprintable character */
    if (Ch < ' ')
    {
      ReadLen--;
      continue;
    }
    *ptr++ = Ch;
  }
  *ptr = '\0';
  return 1;
}

/*---------------------------------------------------------------------------*/

static SAPDB_Char *CopySection(SAPDB_Int4 Fd)
{
    SAPDB_Char *buffer;
    SAPDB_Char *readBuffer;
    SAPDB_Int8  bufferSize;
    SAPDB_Int8  bytesCopied;
    SAPDB_Int8  currentPosition;
    SAPDB_Int8  endPosition;
    SAPDB_Int8  tmpPosition;

    do {
        currentPosition = (SAPDB_Int8)lseek(Fd, (off_t)0, SEEK_CUR );
    } while ( currentPosition == (SAPDB_Int8)-1 && errno == EINTR);
    if ( (SAPDB_Int8)-1 == currentPosition ) return 0;

    do {
        endPosition = (SAPDB_Int8)lseek(Fd, (off_t)0, SEEK_END);
    } while ( endPosition == (SAPDB_Int8)-1 && errno == EINTR);
    if ( (SAPDB_Int8)-1 == endPosition ) return 0;

    do {
        tmpPosition = (SAPDB_Int8)lseek(Fd, (off_t)currentPosition, SEEK_SET);
    } while ( tmpPosition == (SAPDB_Int8)-1 && errno == EINTR);
    if ( currentPosition != tmpPosition ) return 0;

    bufferSize = (endPosition - currentPosition) + 2; /* delta + null byte */
    if ( bufferSize < 1 || (bufferSize != ((SAPDB_Int4)bufferSize)) )
    {   /* strange buffer size... */
        return 0;
    }

    buffer = (SAPDB_Char *)calloc(1, bufferSize);
    readBuffer = buffer;
    
    do {
        bytesCopied = (SAPDB_Int8)read(Fd, readBuffer, bufferSize - 1);
        if ( bytesCopied > 0 )
        {
            bufferSize -= bytesCopied;
            readBuffer += bytesCopied;
        }
    } while ( ( (bytesCopied > 0) && (bufferSize > 1) )
           || ( bytesCopied < 0 && errno == EINTR ) );

    if ( bytesCopied < 0 )
    {
        free(buffer);
        return 0;
    }

    return buffer;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     FindSection ()
  description:  Read from given filedescriptor until given section is reached
  
   Next SAPDB_Char to read is the first SAPDB_Char of the first line of this section

  arguments:
    Fd [in] Opened Filedescriptor
    szSection [in] Given Section Identifier

  return value:
    <UL>
        <LI>0: EOF reached (Section not found)
        <LI>1: Section found
        <LI>-1: Read error
    </UL>
 */
static SAPDB_Int4 FindSection (
                        SAPDB_Int4          Fd,
                        const SAPDB_Char * szSection)
{
#undef MF__
#define MF__ MOD__"FindSection"
  SAPDB_Char *szTempSection = (SAPDB_Char *)alloca(strlen(szSection) + strlen("[]") + 1);
  
  SAPDB_Char *Buffer = (SAPDB_Char *)alloca(LINE_SIZE_MAX);
  SAPDB_Int4   BufferSize = LINE_SIZE_MAX;
  SAPDB_Int4   OldBufferSize = 0;
  SAPDB_Bool  MoreData;
  SAPDB_Int4   ReadResult;

  strcpy (szTempSection, "[");
  strcat (szTempSection, szSection);
  strcat (szTempSection, "]");

  do 
  {
    READLINE_EN09(Fd, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
    if ( ReadResult != 1 )
    {
        return ReadResult;
    }
  } while ( strcasecmp (Buffer, szTempSection) );
  return 1;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     WriteSection ()
  description:  Write a section header
  
   To seperate each section, the section header can be prepended by a newline.

   "[szSection]\n"   without NewLineFlag

   or

   "\n[szSection]\n" with NewLineFlag

  arguments:
    Fd [in] Opened Filedescriptor
    szSection [in] Given Section Identifier
    NewLineFlag [in] Flag to indicate additional Newline prepended

  return value:
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
static SAPDB_Int4 WriteSection (
                         SAPDB_Int4 Fd,
                         const SAPDB_Char *szSection,
                         SAPDB_Int4 NewLineFlag)
{
#undef MF__
#define MF__ MOD__"WriteSection"
  SAPDB_Char *szTempSection;
  SAPDB_Int4 cbLen, rc;

  szTempSection = (SAPDB_Char *)alloca(strlen(szSection)+strlen("\n[]\n") + 1);
  if (NewLineFlag == 0) 
  {
    strcpy (szTempSection, "[");
  }
  else 
  {
    strcpy (szTempSection, "\n[");
  }
  strcat (szTempSection, szSection);
  strcat (szTempSection, "]\n");
  cbLen = strlen (szTempSection); 

  rc = write (Fd, szTempSection, cbLen);
  if (cbLen != rc) 
  {
    return 0;
  }
  return 1;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     WriteEntry ()
  description:  Write a entry line
  
    Write a line formed as 

    "szEntry=szString\n"

  arguments:
    Fd [in] Opened Filedescriptor
    szEntry [in] Given entry key
    szString [in] Given entry value
    
  return value:
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
#undef MF__
#define MF__ MOD__"WriteEntry"
static SAPDB_Int4 WriteEntry (
                       SAPDB_Int4 Fd,
                       const SAPDB_Char *szEntry,
                       const SAPDB_Char *szString)
{
  SAPDB_Char *szTempEntry;
  SAPDB_Int4 cbLen, rc;

  szTempEntry = (SAPDB_Char *)alloca(strlen(szEntry) + strlen("=\n") + strlen(szString) + 1);
  strcpy (szTempEntry, szEntry);
  strcat (szTempEntry, "=");
  strcat (szTempEntry, szString);
  strcat (szTempEntry, "\n");
  
  cbLen = strlen (szTempEntry); 
  rc = write (Fd, szTempEntry, cbLen);
  if ( cbLen != rc) 
  {
    return 0;
  }
  return 1;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     WriteLine ()
  description:  Write a line formed read by ReadLine
  
    Used to copy Registry content into a temporary file. Need to add a newline on each line.
    
  arguments:
    Fd [in] Opened Filedescriptor
    szString [in] Given line
    
  return value:
    <UL>
        <LI>0: Write failed ( inspect errno )
        <LI>1: Write ok
    </UL>
 */
#undef MF__
#define MF__ MOD__"WriteLine"
static SAPDB_Int4 WriteLine (
                      SAPDB_Int4 Fd,
                      const SAPDB_Char *szString)
{
  SAPDB_Char *szLine;
  SAPDB_Int4 cbLen, rc;

  szLine = (SAPDB_Char *)alloca(strlen(szString) + 2);
  strcpy (szLine, szString);
  strcat (szLine, "\n");
  cbLen = strlen (szLine);
  rc = write (Fd, szLine, cbLen);
  if (cbLen != rc)
  {
    return 0;
  }
  return 1;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     ValidateConfigPath ()
  description:  Open the given path to the config files

    First create directory used for registry file.

  arguments:
    szConfigPath [in] Complete path to configuration directory
    ErrText [out] Errormessage if something went wrong
    Ok [out] Error enumeration

  return value:
    <UL>
        <LI>true:  Open ok,
        <LI>false: Open failed (Ok and ErrText are set). 
    </UL>
 */
static SAPDB_Bool ValidateConfigPath( SAPDB_Char const * szConfigPath,
                                      RTE_IniFileErrtext ErrText,
                                      RTE_IniFileResult *pOk)
{
    struct stat StatBuff;
    SAPDB_Int4  umask_old ;
    SAPDB_Bool result = true;

    umask_old = umask(0);

    if (stat (szConfigPath, &StatBuff) != 0)
    {
        if ( mkdir (szConfigPath, 0711) != 0 )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_TEMP;
            strcpy( (SAPDB_Char *)ErrText, "Mkdir(Registry):" );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            result = false;
        }
    }

    umask(umask_old);

    return result;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     OpenTempConfigFile ()
  description:  Open the temporary config file

    First create directory used for registry file. Then try to create the temporary file.

  arguments:
    szTempPath [in] Complete path to temporary configuration file
    ErrText [out] Errormessage if something went wrong
    Ok [out] Error enumeration

  return value:
    <UL>
        <LI>true:  Open ok,
        <LI>false: Open failed (Ok and ErrText are set). 
    </UL>
 */
#undef MF__
#define MF__ MOD__"OpenTempConfigFile"
static SAPDB_Bool OpenTempConfigFile(LockFile *pTempFile,
                              const SAPDB_Char *szTempPath,
                              RTE_IniFileErrtext ErrText,
                              RTE_IniFileResult *pOk)
{
  SAPDB_Int4  umask_old ;

  umask_old = umask(0);

  if ( !RegistryFile_Open(pTempFile, szTempPath) )
  {
    *pOk = SAPDB_INIFILE_RESULT_ERR_TEMP;
    strcpy( (SAPDB_Char *)ErrText, "Open(TempRegistry):" );
    strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
    return false;
  }

  umask(umask_old);

  return true;
}

/*---------------------------------------------------------------------------*/

/*
  Function:     CopyBackTempFile ()
  description:  Copy Temp file Back to Original

    A simple rename() would create a critical section since unlocking of registry 
    would takes place before rename() could start. So we have to copy content...
  
  arguments:
    TempFile [in] LockFile of Tempfile
    RegistryFile [in] LockFile of Registryfile
    ErrText [out] Errormessage if something went wrong
    Ok [out] Error enumeration

  return value:
    <UL>
        <LI>0: Write failed ( inspect Ok and ErrText )
        <LI>1: Write ok
    </UL>
 */
static SAPDB_Int4 CopyBackTempFile(  LockFile *pTempFile,
                              LockFile *pRegistryFile,
                              RTE_IniFileErrtext ErrText,
                              RTE_IniFileResult *pOk)
{
    SAPDB_Char Buffer[4096];
    SAPDB_Int4  BytesRead = 0;
    SAPDB_Int4  BytesWritten = 0;
    SAPDB_Int4  TotalSize = 0;

    if ( !RegistryFile_Reset(pTempFile) )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_SEEK;
        strcpy( (SAPDB_Char *)ErrText, "Seek(TempRegistry):" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        RegistryFile_Close(pRegistryFile);
        /* PTS 1107324 */
        RegistryFile_Close(pTempFile);
        return 0;
    }
    if ( !RegistryFile_Reset(pRegistryFile) )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_SEEK;
        strcpy( (SAPDB_Char *)ErrText, "Seek(Registry):" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        RegistryFile_Close(pRegistryFile);
        /* PTS 1107324 */
        RegistryFile_Close(pTempFile);
        return 0;
    }

    while ( (BytesRead = read(pTempFile->Fd, Buffer, 4096)) > 0 )
    {
        size_t BytesOffset;

        for ( BytesOffset = 0; 
              BytesOffset < BytesRead; 
              BytesOffset += BytesWritten, TotalSize += BytesWritten )
        {
            BytesWritten = write(pRegistryFile->Fd, Buffer+BytesOffset, BytesRead-BytesOffset);
            if ( (BytesWritten < 0) 
              && (errno != EINTR) )
            {
                break;
            }
        }
    }

    if ( BytesRead < 0 )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
        strcpy( (SAPDB_Char *)ErrText, "Read(TempRegistry):" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        RegistryFile_Close(pRegistryFile);
        /* PTS 1107324 */
        RegistryFile_Close(pTempFile);
        return 0;
    }

    if ( BytesWritten < 0 )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
        strcpy( (SAPDB_Char *)ErrText, "Writeback(Registry):" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        RegistryFile_Close(pRegistryFile);
        RegistryFile_Close(pTempFile);
        return 0;
    }

    ftruncate( pRegistryFile->Fd, TotalSize );
    ErrText[0] = '\0';
    *pOk = SAPDB_INIFILE_RESULT_OK;
    RegistryFile_Close(pRegistryFile);
    /* PTS 1107324 */
    RegistryFile_Close(pTempFile);
    return 1;
}

/*
   UpdateConfigString
 */
static SAPDB_Int4 UpdateConfigString (
                                      const SAPDB_Char *szPath,
                                      const SAPDB_Char *szSection,
                                      const SAPDB_Char *szEntry, 
                                      const SAPDB_Char *szString,
                                      const SAPDB_Bool DeleteFlag,
                                      RTE_IniFileErrtext ErrText,
                                      RTE_IniFileResult *pOk)
{
    SAPDB_Int4   BufferSize = LINE_SIZE_MAX;
    SAPDB_Int4   OldBufferSize = 0;
    SAPDB_Char *Buffer = (SAPDB_Char *)alloca(BufferSize);
    SAPDB_Bool  MoreData;
    LockFile RegistryFile;
    LockFile TempFile;
    SAPDB_Char *szTempPath;
    
    if ( !szEntry )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for key passed" );
        return 0;
    }
    
    if ( !szString && !DeleteFlag )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy( (SAPDB_Char *)ErrText, "NULL pointer for string passed" );
        return 0;
    }
    
    RegistryFile_Init(&RegistryFile, false, false);
    RegistryFile_Init(&TempFile, true, false);
    
    /* Validate given szEntry */
    if (strchr (szEntry, '=') != 0) 
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_KEY;
        strcpy( (SAPDB_Char *)ErrText, "Found '=' in key for Registry:" );
        strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }
    
    *pOk = SAPDB_INIFILE_RESULT_OK;
    
    szTempPath = (SAPDB_Char *)alloca(strlen(szPath) + strlen(".XXXXXX") + 1);
    strcpy (szTempPath, szPath); strcat (szTempPath, ".XXXXXX"); 
    /* PTS 1100015 */
    if ( mkstemp (szTempPath) == -1 )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
        strcpy( (SAPDB_Char *)ErrText, "mkstemp failed:" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }
    
    /* Open temporary registry file */
    if ( !OpenTempConfigFile(&TempFile, szTempPath, ErrText, pOk) )
    {
        unlink(szTempPath);
        /* Ok & ErrText already setup... */
        return 0;
    }
    
    /* Open and lock Registry (create if does not exist */
    if ( !RegistryFile_Open (&RegistryFile, szPath ) )
    {
        if ( errno != ENOENT )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            strcpy( (SAPDB_Char *)ErrText, "Open(Registry) failed:" );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            RegistryFile_Close(&TempFile);
            return 0;
        }

        if ( DeleteFlag )
        {
            /* nothing to delete */ 
            *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
            strcpy( (SAPDB_Char *)ErrText, "Registry empty" );
            RegistryFile_Close(&TempFile);
            return 0;
        }

        /* Registry.ini-file did not exist before... */
        
        if ( !RegistryFile_Create(&RegistryFile, szPath) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
            strcpy( (SAPDB_Char *)ErrText, "Create(Empty Registry):" );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            RegistryFile_Close(&TempFile);
            return 0;
        }
        
        if ( !RegistryFile_Lock(&RegistryFile) )
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_LOCK;
            strcpy( (SAPDB_Char *)ErrText, "Lock(Empty Registry):" );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            RegistryFile_Close(&TempFile);
            return 0;
        }
        
        /* Write initial section */
        
        if (WriteSection (TempFile.Fd, szSection, 0) == 0) 
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
            strcpy( (SAPDB_Char *)ErrText, "First write(Registry) section '" );
            strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            RegistryFile_Close(&RegistryFile);
            RegistryFile_Close(&TempFile);
            return 0;
        }
        
        if (WriteEntry (TempFile.Fd, szEntry, szString) == 0) 
        {
            *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
            strcpy( (SAPDB_Char *)ErrText, "First write(Registry) entry '" );
            strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            RegistryFile_Close(&RegistryFile);
            RegistryFile_Close(&TempFile);
            return 0;
        }
        
        return CopyBackTempFile(&TempFile, &RegistryFile, ErrText, pOk);
    }
    
    /*-------------------------------------
    * Registry File exists and is opened
    */
    if ( !RegistryFile_Lock(&RegistryFile) )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_LOCK;
        strcpy( (SAPDB_Char *)ErrText, "Lock(Registry) failed:" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        RegistryFile_Close(&RegistryFile);
        RegistryFile_Close(&TempFile);
        return 0;
    }
    
    /*------------------------------------------------
    Copy each line until searched section is found 
    If EOF is reached before, append new Section.
    */
    {
        SAPDB_Int4 ReadResult;
        SAPDB_Char *szTempSection = (SAPDB_Char *)alloca( strlen("[]") + strlen(szSection) + 1 );
        /*  ini-file already exists */
        strcpy (szTempSection, "[");
        strcat (szTempSection, szSection);
        strcat (szTempSection, "]");
        
        do
        {
            READLINE_EN09(RegistryFile.Fd, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
            if ( ReadResult == -1 )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
                strcpy( (SAPDB_Char *)ErrText, "Read head(Registry):" );
                strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                RegistryFile_Close(&RegistryFile);
                RegistryFile_Close(&TempFile);
                return 0;
            }
            
            if ( ReadResult == 0 )
            {
                /* EOF reached before section was found */
                if ( DeleteFlag )
                {
                    /* nothing to delete */ 
                    *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
                    strcpy( (SAPDB_Char *)ErrText, "No Registry section '" );
                    strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, "'", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    RegistryFile_Close(&RegistryFile);
                    RegistryFile_Close(&TempFile);
                    return 0;
                }
                
                /* no such section found, append it */
                if (WriteSection (TempFile.Fd, szSection, 1) == 0) 
                {
                    *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                    strcpy( (SAPDB_Char *)ErrText, "Write(Registry) section '" );
                    strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    RegistryFile_Close(&RegistryFile);
                    RegistryFile_Close(&TempFile);
                    return 0;
                }
                
                if (WriteEntry (TempFile.Fd, szEntry, szString) == 0) 
                {
                    *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                    strcpy( (SAPDB_Char *)ErrText, "Write(Registry) entry '" );
                    strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    RegistryFile_Close(&RegistryFile);
                    RegistryFile_Close(&TempFile);
                    return 0;
                }
                
                return CopyBackTempFile(&TempFile, &RegistryFile, ErrText, pOk);
                
            }
            
            if ( WriteLine (TempFile.Fd, Buffer) == 0 )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                strcpy( (SAPDB_Char *)ErrText, "Copy write(Registry) failed:" );
                strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                RegistryFile_Close(&RegistryFile);
                RegistryFile_Close(&TempFile);
                return 0;
            }
            
        } while ( strcasecmp (Buffer, szTempSection) != 0 );
        
        /*--------------------------------------------------
        searched section found,
        copy each line until searched entry key is found 
        */
        for (;;) 
        {
            READLINE_EN09(RegistryFile.Fd, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
            if ( ReadResult == -1 )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
                strcpy( (SAPDB_Char *)ErrText, "Read(Registry) entries:" );
                strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                RegistryFile_Close(&RegistryFile);
                RegistryFile_Close(&TempFile);
                return 0;
            }
            
            if ( ReadResult == 0 )
            {
                /* end of file, no such entry found */
                if ( DeleteFlag )
                {
                    /* nothing to delete */ 
                    *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
                    strcpy( (SAPDB_Char *)ErrText, "No Registry entry '" );
                    strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, "'", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    RegistryFile_Close(&RegistryFile);
                    RegistryFile_Close(&TempFile);
                    return 0;
                }
                
                /* append entry */
                if (WriteEntry (TempFile.Fd, szEntry, szString) == 0) 
                {
                    *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                    strcpy( (SAPDB_Char *)ErrText, "Append(Registry) entry '" );
                    strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                    RegistryFile_Close(&RegistryFile);
                    RegistryFile_Close(&TempFile);
                    return 0;
                }
                
                return CopyBackTempFile(&TempFile, &RegistryFile, ErrText, pOk);
            }
            
            {
                SAPDB_Char *EqualSign = strchr(Buffer, '=');
                
                if ( EqualSign != NULL
                  && FoundMatchingEntry(Buffer, szEntry) )
                {
                    /* entry found */ 
                    if ( !DeleteFlag ) 
                    {
                        /* replace it */
                        if (WriteEntry (TempFile.Fd, szEntry, szString) == 0) 
                        {
                            *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                            strcpy( (SAPDB_Char *)ErrText, "Replace(Registry) entry '" );
                            strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                            strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                            RegistryFile_Close(&RegistryFile);
                            RegistryFile_Close(&TempFile);
                            return 0;
                        }
                    }   
                    break;
               }
                
               if ( Buffer[0] == '[' 
                 || Buffer[0] == '\0' )
               {
                   if ( DeleteFlag )
                   {
                       /* nothing to delete */ 
                       *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
                       strcpy( (SAPDB_Char *)ErrText, "No Registry entry '" );
                       strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                       strncat( (SAPDB_Char *)ErrText, "'", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                       RegistryFile_Close(&RegistryFile);
                       RegistryFile_Close(&TempFile);
                       return 0;
                   }
                   else
                   {
                       /* insert it */
                       if (WriteEntry (TempFile.Fd, szEntry, szString) == 0) 
                       {
                           *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                           strcpy( (SAPDB_Char *)ErrText, "Insert(Registry) entry '" );
                           strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                           strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                           strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                           RegistryFile_Close(&RegistryFile);
                           RegistryFile_Close(&TempFile);
                           return 0;
                       }
                   }
                    
                   if ( Buffer[0] == '[' )
                   {
                       if ( WriteLine (TempFile.Fd, "") == 0 )
                       {
                           *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                           strcpy( (SAPDB_Char *)ErrText, "Insert(Registry) empty line:" );
                           strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                           RegistryFile_Close(&RegistryFile);
                           RegistryFile_Close(&TempFile);
                           return 0;
                       }
                   }
                    
                   if ( WriteLine (TempFile.Fd, Buffer) == 0 )
                   {
                       *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                       strcpy( (SAPDB_Char *)ErrText, "Insert(Registry) next section:" );
                       strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                       RegistryFile_Close(&RegistryFile);
                       RegistryFile_Close(&TempFile);
                       return 0;
                   }
                    
                   break;
               }
               else
               {
                   if ( !EqualSign )
                   {
                       *pOk = SAPDB_INIFILE_RESULT_ERR_KEY;
                       strcpy( (SAPDB_Char *)ErrText, "No '=' found in:" );
                       strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                       RegistryFile_Close(&RegistryFile);
                       RegistryFile_Close(&TempFile);
                       return 0;
                   }
               }
           }
            
           if (WriteLine (TempFile.Fd, Buffer) == 0)
           {
               *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
               strcpy( (SAPDB_Char *)ErrText, "Copy entrys(Registry):" );
               strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
               RegistryFile_Close(&RegistryFile);
               RegistryFile_Close(&TempFile);
               return 0;
           }
       }
  
       do
       {
           READLINE_EN09(RegistryFile.Fd, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
           if ( ReadResult == -1 )
           {
               *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
               strcpy( (SAPDB_Char *)ErrText, "Read rest(Registry):" );
               strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
               RegistryFile_Close(&RegistryFile);
               RegistryFile_Close(&TempFile);
               return 0;
           }
           if ( ReadResult == 1 )
           {
               if ( WriteLine (TempFile.Fd, Buffer) == 0 )
               {
                   *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                   strcpy( (SAPDB_Char *)ErrText, "Copy rest(Registry):" );
                   strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                   RegistryFile_Close(&RegistryFile);
                   RegistryFile_Close(&TempFile);
                   return 0;
               }
           }
       } while( ReadResult != 0 );
    }
  
    return CopyBackTempFile(&TempFile, &RegistryFile, ErrText, pOk);
}

/*
   RemoveConfigString
 */
static SAPDB_Int4 RTE_RemoveUNIXConfigString (
    const SAPDB_Char *szFile,
    const SAPDB_Char *szSection,
    const SAPDB_Char *szEntry, 
    RTE_IniFileErrtext ErrText,
    RTE_IniFileResult *pOk)
{
    SAPDB_Char *szPath;
    RTE_Path indepConfigPath;

    if ( !strcmp(szFile, SAPDB_GLOBAL_CONFIG_FILE) 
      || *szFile == '/' )
    {
        szPath = (SAPDB_Char *)alloca(strlen(szFile)+1);
        strcpy(szPath, szFile);
    }
    else
    {
        if ( !strcmp(SAPDB_ODBC_INI_FILE, szFile) )
        {
            szPath = (SAPDB_Char *)alloca(strlen(UNIX_GLOBAL_ODBC_INI_FILE)+1);
            strcpy(szPath, UNIX_GLOBAL_ODBC_INI_FILE);
        }
        else
        {
            if ( !RTE_GetCommonConfigPath(indepConfigPath, false, ErrText) )
            {
                *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
                return 0;
            }
            /* If needed create directory for registry file */
            if ( !ValidateConfigPath(indepConfigPath, ErrText, pOk) )
            {
                /* Ok & ErrText already setup... */
                return 0;
            }

            szPath = (SAPDB_Char *)alloca(strlen(indepConfigPath)+1+strlen(szFile)+1);
            strcpy(szPath, indepConfigPath); strcat(szPath, "/"); strcat(szPath, szFile);
        }
    }

    return ( UpdateConfigString (
                    szPath,
                    szSection,
                    szEntry,
                    NULL, 
                    true, 
                    ErrText,
                    pOk) );
}
/*---------------------------------------------------------------------------*/

/*
  Function:     UpdateConfigString ()
  description:  Write or delete a complete config string
  
    Used to write a new entry into a config file if DeleteFlag == 0 or
    to delete an entry from config file if DeleteFlag != 0.
    
  arguments:
    szFile [in] Name of the configuration file
    szSection [in] Given section
    szEntry [in] Given entry key in 'key=value' string
    szString [in] Given entry value in 'key=value' string
    DeleteFlag [in] Flag to indicate delete instead of write
    ErrText [out] Errormessage if something went wrong
    Ok [out] Error enumeration

  return value:
    <UL>
        <LI>0: Write failed ( inspect ErrText )
        <LI>1: Write ok
    </UL>
 */
static SAPDB_Int4 UpdateInstallationConfigString (
                              const SAPDB_Char *szEntry, 
                              const SAPDB_Char *szString,
                              const SAPDB_Bool DeleteFlag,
                              RTE_IniFileErrtext ErrText,
                              RTE_IniFileResult *pOk)
{
  return UpdateConfigString ( SAPDB_GLOBAL_CONFIG_FILE,
                              SAPDB_GLOBALS_SECTION,
                              szEntry, 
                              szString,
                              DeleteFlag,
                              ErrText,
                              pOk);
}


/*---------------------------------*/

/*
  buffer contains a line beginning with <whiteSpace>*szEntry<whiteSpace>*=

  This call returns if a matiching entry is found
 */
static SAPDB_Bool FoundMatchingEntry( SAPDB_Char const *buffer
                                    , SAPDB_Char const *szEntry )
{
    SAPDB_Char const *bufferPtr = buffer;
    SAPDB_Int iChar;

    while ( 0 != *bufferPtr 
         && isspace(*bufferPtr) )
        ++bufferPtr;

    for ( iChar = 0; '=' != *bufferPtr 
                    && 0 != *bufferPtr
                    && 0 != szEntry[iChar]; ++iChar, ++bufferPtr )
    {
        if ( toupper(*bufferPtr) != toupper(szEntry[iChar]) )
        {
            return false;
        }
    }

    if ( 0 == szEntry[iChar] )
    {
        while (   0 != *bufferPtr 
             && '=' != *bufferPtr
             && isspace(*bufferPtr) )
            ++bufferPtr;
    }

    return ( 0 == szEntry[iChar] && '=' == *bufferPtr );
}

/*---------------------------------*/

static SAPDB_Char *SkipWhiteSpaces(SAPDB_Char *buffer)
{
    SAPDB_Char *bufferPtr = buffer;

    while (   0 != *bufferPtr 
         && isspace(*bufferPtr) )
        ++bufferPtr;
    return bufferPtr;
}

/*---------------------------------*/

static SAPDB_Int4 UnlockedGetConfigString (
                        LockFile       *pRegistryFile,
                        const SAPDB_Char *    szPath,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk)
{  
    SAPDB_Char * pVal;
    SAPDB_Char * ptr;
    SAPDB_Int4    ReadResult;

    if ( !szEntry )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for key passed" );
        return 0;
    }
    
    if ( !szString )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for value passed" );
        return 0;
    }
    
    if ( *szEntry == 0 )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "empty key passed" );
        return 0;
    }

    *szString = 0;

    if ( !RegistryFile_Open (pRegistryFile, szPath) )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
        strcpy ( (SAPDB_Char *)ErrText, "Open Registry:" );
        strncat ( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }

    if ( !RegistryFile_Lock(pRegistryFile) )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_LOCK;
        strcpy ( (SAPDB_Char *)ErrText, "Lock(Registry) failed:" );
        strncat ( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }

    ReadResult = FindSection (pRegistryFile->Fd, szSection);
    if ( ReadResult == -1 )
    {
        szString[0] = '\0';
        *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
        strcpy( (SAPDB_Char *)ErrText, "Read section(Registry) '" );
        strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        strncat( (SAPDB_Char *)ErrText, "':", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }

    if ( ReadResult == 0) 
    {
        szString[0] = '\0';
        *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
        strcpy( (SAPDB_Char *)ErrText, "Section not in Registry:" );
        strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
    }
    {
        SAPDB_Bool FoundEntry = false;

        SAPDB_Char *Buffer = (SAPDB_Char *)alloca(LINE_SIZE_MAX);
        SAPDB_Int4   OldBufferSize = 0;
        SAPDB_Int4   BufferSize = LINE_SIZE_MAX;
        SAPDB_Bool  MoreData;

        do
        {
            READLINE_EN09(pRegistryFile->Fd, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
            if ( ReadResult == -1 )
            {
                szString[0] = '\0';
                *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
                strcpy( (SAPDB_Char *)ErrText, "Read problem(Registry):" );
                strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                return 0;
            }

            if ( ReadResult == 0 
              || Buffer[0] == '\0'
              || Buffer[0] == '[' )
            {
                szString[0] = '\0';
                *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
                strcpy( (SAPDB_Char *)ErrText, "Entry not in Registry:" );
                strncat( (SAPDB_Char *)ErrText, szEntry, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
                return 0;
            }

            /* PTS 1105650 */
            pVal = strchr (Buffer, '=');

        } while ( pVal == NULL 
             || !FoundMatchingEntry(&Buffer[0], szEntry) );
                         
        /* PTS 1105122 */
        if ( pVal != NULL )
        {
             pVal = SkipWhiteSpaces(pVal+1);
             strncpy(szString, pVal, MaxStringLen-1);
             szString[MaxStringLen-1] = 0;

             if ( strlen(pVal) >= MaxStringLen )
             {
                 *pOk = SAPDB_INIFILE_RESULT_TRUNCATED;
                 sprintf( (SAPDB_Char *)ErrText, "Value [%d/%d] truncated:", MaxStringLen, strlen(pVal)+1 );
                 strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
             }
             else
             {
                 *pOk = SAPDB_INIFILE_RESULT_OK;
                 ErrText[0] = '\0';
             }
        }
        else
        {
             *pOk = SAPDB_INIFILE_RESULT_ERR_KEY;
             strcpy( (SAPDB_Char *)ErrText, "Bad entry:");
             strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
             return 0;
        }
    }
    return (strlen (szString));
}

/*----------------------------------------------------------------------*/
static SAPDB_Int4 RTE_NextUNIXConfigEnum( RTE_RegistryHandle hEnum,
                                          SAPDB_Char *szEntry,
                                          const SAPDB_Int4 MaxEntryLen,
                                          SAPDB_Char *szString,
                                          const SAPDB_Int4 MaxStringLen,
                                          RTE_IniFileErrtext ErrText,
                                          RTE_IniFileResult * pOk )
{

    SAPDB_Int4 i, j, State;
    SAPDB_Char  *nextEntry = hEnum->nextEntry;
    SAPDB_Char  *Buffer = (SAPDB_Char *)alloca(LINE_SIZE_MAX);
    SAPDB_Int4   OldBufferSize = 0;
    SAPDB_Int4   BufferSize = LINE_SIZE_MAX;
    SAPDB_Bool   MoreData;
    SAPDB_Int4   ReadResult;

    if ( szEntry != NULL ) *szEntry = 0;
    if ( szString != NULL ) *szString = 0;

    for (;;) 
    {
        COPYLINE_EN09(&nextEntry, Buffer, OldBufferSize, BufferSize, MoreData, ReadResult);
        hEnum->nextEntry = nextEntry;

        if ( ReadResult == 0 )
        {
          strcpy( ErrText, "End of Registry" );
          *pOk = SAPDB_INIFILE_RESULT_EOF;
          return 0;
        }

        if (Buffer[0] == '[')
        {
          strcpy( ErrText, "End of Section" );
          *pOk = SAPDB_INIFILE_RESULT_EOF;
          return 0;
        }

        /* skip empty lines */
        if (Buffer[0] != '\0')
            break;
    }

    {
    SAPDB_Char *EqualSign;

    EqualSign = strchr(Buffer, '=');
    if ( !EqualSign )
    {
      *pOk = SAPDB_INIFILE_RESULT_ERR_KEY;
      strcpy( (SAPDB_Char *)ErrText, "No '=' found in:" );
      strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
      return 0;
    }

    *EqualSign = '\0';

    if ( szEntry != NULL )
    {
      strncpy(szEntry, Buffer, MaxEntryLen-1);
      szEntry[MaxEntryLen-1] = '\0';

      if ( strlen(Buffer) >= MaxEntryLen )
      {
        *pOk = SAPDB_INIFILE_RESULT_TRUNCATED;
        sprintf( (SAPDB_Char *)ErrText, "Key [%d/%d] truncated:", MaxEntryLen, strlen(Buffer)+1 );
        strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
      }
    }

    if ( szString != NULL )
    {
      strncpy(szString, EqualSign+1, MaxStringLen-1);
      szString[MaxStringLen-1] = '\0';

      if ( strlen(EqualSign+1) >= MaxStringLen )
      {
        *pOk = SAPDB_INIFILE_RESULT_TRUNCATED;
        sprintf( (SAPDB_Char *)ErrText, "Value [%d/%d] truncated:", MaxStringLen, strlen(EqualSign+1)+1 );
        strncat( (SAPDB_Char *)ErrText, Buffer, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        return 0;
      }
    }
    }

    ErrText[0] = '\0';
    *pOk = SAPDB_INIFILE_RESULT_OK;
    return 1;
}

/*
  Class:     LockFile
  description:  Helper class to make sure file lock is removed and file is closed after return

    LockFile object is created without any file handle attached.
    After call to Open() the file handle is stored internally.
    The member function 'Lock()' is called afterwards.
    Destructor will remove the lock automatically if function returns and close the File.

  arguments: none
  return value: none
 */
static void RegistryFile_Init(LockFile *this, SAPDB_Bool IsTemp, SAPDB_Bool readOnly)
{
  this->m_IsTemp = IsTemp;
  this->m_Path   = "";
  this->Fd     = -1;
  this->m_IsOpen = false;
  this->m_IsLocked = readOnly; /* readonly implicit locks... */
  this->m_ReadOnly = readOnly;
}

/*---------------------------------*/

static SAPDB_Bool RegistryFile_Open(LockFile *this, const SAPDB_Char *Path)
{
  if ( !this->m_IsOpen )
  {
    this->m_Path = Path;
    /* To obtain an exclusive lock file must be opened read/write */
    this->Fd = open(Path, this->m_ReadOnly ? O_RDONLY : O_RDWR);
    this->m_IsOpen = ( this->Fd != -1 );
  }
  return this->m_IsOpen;
}

/*---------------------------------*/

static SAPDB_Bool RegistryFile_Create(LockFile *this, const SAPDB_Char *Path)
{
  if ( !this->m_IsOpen )
  {
    this->m_Path = Path;
    this->Fd = open(Path, O_RDWR|O_CREAT, 0666); /* external umask is used to limitate creation mode */
    this->m_IsOpen = ( this->Fd != -1 );
  }
  return this->m_IsOpen;
}

/*---------------------------------*/

static SAPDB_Bool RegistryFile_Lock(LockFile *this)
{
  if ( !this->m_IsLocked 
    && RegistryFile_Reset(this) )
  {
    this->m_IsLocked = ( lockf(this->Fd, F_LOCK, (off_t)0) != -1 ); /* PTS 1108386 */
  }
  return this->m_IsLocked;
}

/*---------------------------------*/

static SAPDB_Bool RegistryFile_Reset(LockFile *this)
{
  if ( !this->m_IsOpen
    || lseek(this->Fd, (off_t)0, SEEK_SET) == -1 ) /* CTS 1108386 */
  {
      return false;
  }
  return true;
}

/*---------------------------------*/

static void RegistryFile_Close(LockFile *this)
{
  if ( this->m_IsOpen )
  {
    this->m_IsLocked = this->m_IsOpen = ( close(this->Fd) == -1 );
  }
  if ( this->m_IsTemp )
  {
    unlink(this->m_Path);
  }
}

/*------------------------------------*/

static RTE_RegistryHandle OpenConfigFileForEnumeration (
    RTE_RegistryHandle hEnum,
    const SAPDB_Char *szPath,
    SAPDB_Int4         readOnly,
    const SAPDB_Char *szSection,
    RTE_IniFileErrtext ErrText, 
    RTE_IniFileResult *pOk)
{
    SAPDB_Int4      Fd;
    SAPDB_Int4      ReadResult;

    Fd = open (szPath, readOnly ? O_RDONLY : O_RDWR );
    if (Fd < 0)
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
        strcpy( (SAPDB_Char *)ErrText, "Open Registry failed:" );
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        free(hEnum);
        return 0;
    }

    if ( !readOnly )
    {
        if ( lockf(Fd, F_LOCK, (off_t)0) == -1 ) /* PTS 1108386 */
        {
            close(Fd);
            *pOk = SAPDB_INIFILE_RESULT_ERR_LOCK;
            strcpy( (SAPDB_Char *)ErrText, "Lock Registry failed:" );
            strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
            free(hEnum);
            return 0;
        }
    }

    ReadResult = FindSection (Fd, szSection);
    if ( ReadResult == -1 )
    {
        close(Fd);
        *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
        strcpy( (SAPDB_Char *)ErrText, "Read Registry:");
        strncat( (SAPDB_Char *)ErrText, GetLastSystemErrorAsString(), sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        free(hEnum);
        return 0;
    }
    if ( ReadResult == 0) 
    {
        close(Fd);
        *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
        strcpy( (SAPDB_Char *)ErrText, "Can't find Registry section:" );
        strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        free(hEnum);
        return 0;
    }

    ErrText[0] = '\0';
    *pOk = SAPDB_INIFILE_RESULT_OK;

    hEnum->buffer = CopySection(Fd);
    close(Fd);
    if ( !hEnum->buffer )
    {
        strcpy( (SAPDB_Char *)ErrText, "Memory copy of section [" );
        strncat( (SAPDB_Char *)ErrText, szSection, sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        strncat( (SAPDB_Char *)ErrText, "]", sizeof(RTE_IniFileErrtext) - strlen((SAPDB_Char *)ErrText) - 1 );
        *pOk = SAPDB_INIFILE_RESULT_ERR_MEMORY;
        free(hEnum);
        return 0;
    }

    hEnum->nextEntry = hEnum->buffer;
    return hEnum;
}

/*---------------------------------------------------*/

static SAPDB_Char const *GetLastSystemErrorAsString()
{
	/* imported data */
	if ( errno == 0 )
	{
		return ( "NO ERROR(0)" );
	}
#if defined(SUN) || defined(HPUX) || defined(LINUX) || defined(FREEBSD)
	{
		SAPDB_Char const *ErrorMessage = (SAPDB_Char const *)strerror(errno);
		if ( ErrorMessage == NULL )
		{
			ErrorMessage = "errno unknown";
		}
		return (SAPDB_Char *)ErrorMessage;
	}
#else
    if ( (errno < 1) || (errno > sys_nerr) )
	{
		return ( "ERRNO NOT IN sys_errlist[]" );
	}
	return ( (SAPDB_Char *)sys_errlist [ errno ] );
#endif
}

/*-------------------*/

static SAPDB_Bool myGetEnv  ( SAPDB_Char        *EnvName,
                              SAPDB_Char        *EnvValue,
                              SAPDB_Int4          MaxEnvValueLen)
{
  SAPDB_Bool  Ok = MaxEnvValueLen > 0 ;
  
  if ( Ok ) 
  {
    SAPDB_Char       *PtrToEnvValue = getenv(EnvName);
    
    Ok = PtrToEnvValue != NULL ;
    
    if ( Ok )
    {
      if ( (SAPDB_Int4)strlen(PtrToEnvValue) < MaxEnvValueLen)
        strcpy(EnvValue, PtrToEnvValue);
      else
      {
        strncpy(EnvValue, PtrToEnvValue, MaxEnvValueLen-1) ;
        EnvValue[MaxEnvValueLen-1] = '\0' ;
        Ok = false ;
      }
    }
    else
      *EnvValue = '\0' ;
  }
  return (Ok) ;
}

/*--------------------*/

static int my_save_chmod(SAPDB_Char const *path, SAPDB_Int4 newMode)
{
   int result;
   do {
        result = chmod(path, newMode);
   } while ( -1 == result && errno == EINTR );
   return result;
}
/*-----------------------------------*/
/*  End of UNIX only static routines */
/*-----------------------------------*/

#endif

/*-------------------------*/
/*  Common static routines */
/*-------------------------*/

static SAPDB_Bool TrimDelimiter(RTE_Path path,
                                SAPDB_Int4 forceDelimiter)
{
    SAPDB_Int4 pathLen = strlen(path);
    SAPDB_Int4 hasDelimiter = ( path[pathLen - 1] == '/' );

    if ( forceDelimiter )
    {
        if ( hasDelimiter )
        {
            while ( pathLen >= 2 
               && path[pathLen - 2] == '/' )
            {
                --pathLen;
                path[pathLen] = 0;
            }
        }
        else
        {
            if ( pathLen >= sizeof(RTE_Path) )
            {
                return false;
            }
            path[pathLen] = '/';
            path[pathLen+1] = 0;
        }
    }
    else
    {
        if ( hasDelimiter )
        {
            while ( pathLen >= 1 
               && path[pathLen - 1] == '/' )
            {
                --pathLen;
                path[pathLen] = 0;
            }
        }
    }

    return true;
}

/*--------------------------------*/
/*  End of common static routines */
/*--------------------------------*/

#if defined(WIN32)

/* access mask flags for explicit registry access on windows x64 */
#ifndef KEY_WOW64_64KEY
#define KEY_WOW64_64KEY 0x0100 	
#endif
#ifndef KEY_WOW64_32KEY
#define KEY_WOW64_32KEY 0x0200 	
#endif

/*-----------------------------*/
/*  WIN32 only static routines */
/*-----------------------------*/

static SAPDB_Int4 RTE_GetWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        SAPDB_Char       *    szString,
                        const SAPDB_Int4       MaxStringLen,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
    SAPDB_Int4 result;
    HKEY hkResult;
    SAPDB_Char *szRegKey;
    DWORD cbType;
    SAPDB_Char const *szValue;
#ifndef _WIN64
	SAPDB_Bool ErrCode;
#endif

    if ( !szFile || !szSection )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for file or section passed" );
        return 0;
    }

    if ( MaxStringLen > 0 && !szString )
    {
        *pOk = SAPDB_INIFILE_RESULT_ERR_PARAM;
        strcpy ( (SAPDB_Char *)ErrText, "NULL pointer for string passed" );
        return 0;
    }

    if ( MaxStringLen > 0 )
    {
        *szString = 0; /* If an empty string is found, the RegQueryValueEx does not return zero termination... */
    }

    if ( wantDefaultString )
    {
        /* value is not stored as as string value, but use the default string of the key.
           This is the only way to enumerate without broken fingers */

        szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1+strlen(szEntry)+1);
        strcpy(szRegKey, szFile);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szSection);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szEntry);
        szValue = "";
    }
    else
    {
        /* value is not stored as as string value, but use the default string of the key.
           This is the only way to enumerate without broken fingers */

        szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1);
        strcpy(szRegKey, szFile);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szSection);
        szValue = szEntry;
    }

#ifdef _WIN64
    if ( RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ, &hkResult) == ERROR_SUCCESS )
#else
    if( AlternativeRegistryAccess ) 
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ | KEY_WOW64_64KEY, &hkResult);
    else
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ, &hkResult);

    if( ErrCode == ERROR_SUCCESS )
#endif
    {
        if ( RegQueryValueEx( hkResult, szValue, 0, &cbType, (char *)szString, (LPDWORD)&MaxStringLen ) != ERROR_SUCCESS )
        {
            strcpy(ErrText, "Can't read registry key");
            *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
            result = 0;
        }
        else
        {
            *pOk = SAPDB_INIFILE_RESULT_OK;
            result = strlen(szString);
        }
        RegCloseKey(hkResult);
    }
    else
    {
        strcpy(ErrText, "Can't open registy key");
        *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
        result = 0;
    }
    return result;
}
/*---------------------------------------------------*/
static SAPDB_Int4 RTE_PutWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        const SAPDB_Char *    szString,
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
    SAPDB_Int4 result = 0;
    HKEY hkResult;
    SAPDB_Char *szRegKey;
    SAPDB_Char const *szValue;
	LONG ErrCode;

    if ( wantDefaultString )
    {
        /* value is not stored as as string value, but use the default string of the key.
           This is the only way to enumerate without broken fingers */

        szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1+strlen(szEntry)+1);
        strcpy(szRegKey, szFile);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szSection);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szEntry);
        szValue = "";
    }
    else
    {
        /* value is not stored as as string value, but use the default string of the key.
           This is the only way to enumerate without broken fingers */

        szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1);
        strcpy(szRegKey, szFile);
        strcat(szRegKey, "\\");
        strcat(szRegKey, szSection);
        szValue = szEntry;
    }

#ifdef _WIN64
    ErrCode = RegCreateKeyEx( globalKey, szRegKey, 0, 0, 0, KEY_READ |KEY_WRITE, 0, &hkResult, 0 );
#else
    if( AlternativeRegistryAccess ) 
	    ErrCode = RegCreateKeyEx( globalKey, szRegKey, 0, 0, 0, KEY_READ |KEY_WRITE | KEY_WOW64_64KEY, 0, &hkResult, 0 );
    else
	    ErrCode = RegCreateKeyEx( globalKey, szRegKey, 0, 0, 0, KEY_READ |KEY_WRITE, 0, &hkResult, 0 );

#endif
    if( ErrCode == ERROR_SUCCESS )
    {
        if (RegSetValueEx( hkResult, szValue, 0, REG_SZ, 
		       (const char *)szString, (int)strlen(szString)) == ERROR_SUCCESS ) 
        {
            *pOk = SAPDB_INIFILE_RESULT_OK;
            result = 1;
        }
        else
        {
            strcpy(ErrText, "Can't write registy key");
            *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
            result = 0;
        }
        RegCloseKey(hkResult);
    }
    else 
    {
        strcpy(ErrText, "Can't create registy key");
        *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
        result = 0;
    }
    return result;
}
/*---------------------------------------------------*/
static SAPDB_Int4 RTE_RemoveWindowsConfigString (
                        HKEY                  globalKey,
                        SAPDB_Bool            wantDefaultString,
                        const SAPDB_Char *    szFile,
                        const SAPDB_Char *    szSection,
                        const SAPDB_Char *    szEntry, 
                        RTE_IniFileErrtext   ErrText,
                        RTE_IniFileResult * pOk )
{
    SAPDB_Int4 result = 0;
    HKEY hkResult;
    SAPDB_Char *szRegKey;
#ifndef _WIN64
	SAPDB_Bool ErrCode;
#endif

    szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1);
    strcpy(szRegKey, szFile);
    strcat(szRegKey, "\\");
    strcat(szRegKey, szSection);

    /* PTS 1126834 */
#ifdef _WIN64
    if (RegOpenKeyEx(globalKey, szRegKey, 0, KEY_WRITE | KEY_READ, &hkResult) == ERROR_SUCCESS ) 
#else
    if( AlternativeRegistryAccess ) 
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_WRITE | KEY_READ | KEY_WOW64_64KEY, &hkResult);
    else
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_WRITE | KEY_READ, &hkResult);

    if( ErrCode == ERROR_SUCCESS )
#endif
    {
        if ( wantDefaultString )
        {
#ifdef _WIN64
            if (RegDeleteKey(hkResult, szEntry) == ERROR_SUCCESS ) 
#else
			SAPDB_Bool ErrCode;
            if( AlternativeRegistryAccess ) 
				ErrCode = fRegDeleteKeyEx(hkResult, szEntry, KEY_WOW64_64KEY, 0);
			else
			    ErrCode = RegDeleteKey(hkResult, szEntry);
            if( ErrCode == ERROR_SUCCESS )
#endif
            {
                *pOk = SAPDB_INIFILE_RESULT_OK;
                result = 1;
            }
            else
            {
                strcpy(ErrText, "Can't delete registy key");
                *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                result = 0;
            }
        }
        else
        {
            if (RegDeleteValue(hkResult, szEntry) == ERROR_SUCCESS ) 
            {
                *pOk = SAPDB_INIFILE_RESULT_OK;
                result = 1;
            }
            else
            {
                strcpy(ErrText, "Can't delete registy value");
                *pOk = SAPDB_INIFILE_RESULT_ERR_WRITE;
                result = 0;
            }
        }
        RegCloseKey(hkResult);
    }
    else 
    {
        strcpy(ErrText, "Can't open registy key");
        *pOk = SAPDB_INIFILE_RESULT_NO_ENTRY;
        result = 0;
    }
    return result;
}
/*---------------------------------------------------*/
static RTE_RegistryHandle RTE_OpenWindowsConfigEnum( RTE_RegistryHandle hEnum,
                                                     HKEY               globalKey,
                                                     const SAPDB_Char *szFile,
                                                     const SAPDB_Char *szSection,
                                                     RTE_IniFileErrtext   ErrText,
                                                     RTE_IniFileResult * pOk )
{
    char *szRegKey;
#ifndef _WIN64
	SAPDB_Bool ErrCode;
#endif

    hEnum->Index = 0;
    szRegKey = (SAPDB_Char *)alloca(strlen(szFile)+1+strlen(szSection)+1);
    strcpy(szRegKey, szFile);
    strcat(szRegKey, "\\");
    strcat(szRegKey, szSection);

#ifdef _WIN64
    if ( RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hEnum->hkResult) == ERROR_SUCCESS ) 
#else
    if( AlternativeRegistryAccess ) 
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, &hEnum->hkResult);
    else
        ErrCode = RegOpenKeyEx(globalKey, szRegKey, 0, KEY_READ | KEY_ENUMERATE_SUB_KEYS, &hEnum->hkResult);

    if( ErrCode == ERROR_SUCCESS )
#endif
    {
        *pOk = SAPDB_INIFILE_RESULT_OK;
        return hEnum;
    }
    else
    {
        strcpy(ErrText, "Failed to open registry key");
        *pOk = SAPDB_INIFILE_RESULT_ERR_OPEN;
        free(hEnum);
        return 0;
    }
}
/*---------------------------------------------------*/
static SAPDB_Int4 RTE_CloseWindowsConfigEnum ( RTE_RegistryHandle hEnum,
                                               RTE_IniFileErrtext   ErrText,
                                               RTE_IniFileResult * pOk )
{
    HKEY key = hEnum->hkResult;
    LONG ret = RegCloseKey(key);

    free(hEnum);
    if (ret == ERROR_SUCCESS) 
    {
        *pOk = SAPDB_INIFILE_RESULT_OK;
        return 1;
    }

    strcpy(ErrText, "Can't close registry");
    *pOk = SAPDB_INIFILE_RESULT_ERR_CLOSE;
    return 0;
}
/*---------------------------------------------------*/
static SAPDB_Int4 RTE_NextWindowsConfigEnum( RTE_RegistryHandle hEnum,
                                             SAPDB_Char *szEntry,
                                             const SAPDB_Int4 MaxEntryLen,
                                             SAPDB_Char *szString,
                                             const SAPDB_Int4 MaxStringLen,
                                             RTE_IniFileErrtext ErrText,
                                             RTE_IniFileResult * pOk )
{
    LONG rc;
    HKEY hkSub;
    DWORD cbType;
    rc = RegEnumKey(hEnum->hkResult, hEnum->Index, szEntry, MaxEntryLen);
    if (rc == ERROR_SUCCESS) 
    {
#ifdef _WIN64
        rc = RegOpenKeyEx(hEnum->hkResult, szEntry, 0, KEY_READ, &hkSub);
#else
        if( AlternativeRegistryAccess ) 
            rc = RegOpenKeyEx(hEnum->hkResult, szEntry, 0, KEY_READ | KEY_WOW64_64KEY, &hkSub);
        else
            rc = RegOpenKeyEx(hEnum->hkResult, szEntry, 0, KEY_READ, &hkSub);
#endif
        if ( rc == ERROR_SUCCESS ) 
        {
            rc = RegQueryValueEx( hkSub, "", 0, &cbType, (char *)szString, (LPDWORD)&MaxStringLen );  
            RegCloseKey(hkSub);
            if ( rc == ERROR_SUCCESS
	          || rc == ERROR_NO_MORE_ITEMS ) 
            {
                if (rc == ERROR_SUCCESS) 
                {
                    hEnum->Index++;
                    *pOk = SAPDB_INIFILE_RESULT_OK;
                    return 1;
                }
            }
        }
    }

    if ( rc == ERROR_NO_MORE_ITEMS )
    {
        strcpy( ErrText, "End of Registry" );
        *pOk = SAPDB_INIFILE_RESULT_EOF;      
        return 0;
    }else if (rc == ERROR_FILE_NOT_FOUND){
			  hEnum->Index++;
		}		  

    strcpy(ErrText, "Cant read from registry");
    *pOk = SAPDB_INIFILE_RESULT_ERR_READ;
    return 0;
}
/*------------------------------------*/
/*  End of WIN32 only static routines */
/*------------------------------------*/

#endif
/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
