/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "system.hxx"
#include "readwrite_helper.hxx"
#include "file_url.hxx"
#include "file_impl.hxx"
#include "unixerrnostring.hxx"

#include <osl/diagnose.h>
#include <osl/profile.h>
#include <sal/log.hxx>

#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define LINES_INI       32
#define LINES_ADD       10
#define SECTIONS_INI    5
#define SECTIONS_ADD    3
#define ENTRIES_INI     5
#define ENTRIES_ADD     3

#define STR_INI_BOOLYES     "yes"
#define STR_INI_BOOLON      "on"
#define STR_INI_BOOLONE     "1"
#define STR_INI_BOOLNO      "no"
#define STR_INI_BOOLOFF     "off"
#define STR_INI_BOOLZERO    "0"

#define FLG_USER            0x00FF
#define FLG_AUTOOPEN        0x0100
#define FLG_MODIFIED        0x0200

#define DEFAULT_PMODE   (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

typedef time_t  osl_TStamp;

namespace {

enum osl_TLockMode
{
    un_lock, read_lock, write_lock
};

struct osl_TFile
{
    int     m_Handle;
    char*   m_pReadPtr;
    char    m_ReadBuf[512];
    char*   m_pWriteBuf;
    sal_uInt32  m_nWriteBufLen;
    sal_uInt32  m_nWriteBufFree;
};

struct osl_TProfileEntry
{
    sal_uInt32  m_Line;
    sal_uInt32  m_Offset;
    sal_uInt32  m_Len;
};

struct osl_TProfileSection
{
    sal_uInt32  m_Line;
    sal_uInt32  m_Offset;
    sal_uInt32  m_Len;
    sal_uInt32  m_NoEntries;
    sal_uInt32  m_MaxEntries;
    osl_TProfileEntry*  m_Entries;
};

/* Profile-data structure hidden behind oslProfile: */
struct osl_TProfileImpl
{
    sal_uInt32  m_Flags;
    osl_TFile*  m_pFile;
    osl_TStamp  m_Stamp;
    char    m_FileName[PATH_MAX + 1];
    sal_uInt32  m_NoLines;
    sal_uInt32  m_MaxLines;
    sal_uInt32  m_NoSections;
    sal_uInt32  m_MaxSections;
    char**  m_Lines;
    osl_TProfileSection* m_Sections;
    pthread_mutex_t m_AccessLock;
    bool        m_bIsValid;
};

}

static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags);
static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags);
static bool   OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode);
static bool   OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate);
static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile);

static char*   OslProfile_getLine(osl_TFile* pFile);
static bool   OslProfile_putLine(osl_TFile* pFile, const char *pszLine);
static char* stripBlanks(char* String, sal_uInt32* pLen);
static char* addLine(osl_TProfileImpl* pProfile, const char* Line);
static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo);
static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo);
static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
                     sal_uInt32 NoEntry, sal_uInt32 Line,
                     char* Entry, sal_uInt32 Len);
static bool addEntry(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection,
                     int Line, char* Entry, sal_uInt32 Len);
static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry);
static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len);
static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection);
static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile, const char* Section,
                                      const char* Entry, sal_uInt32 *pNoEntry);
static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile);
static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup);
static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable);
static bool releaseProfile(osl_TProfileImpl* pProfile);

static bool writeProfileImpl (osl_TFile* pFile);
static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl*);
static bool osl_ProfileSwapProfileNames(osl_TProfileImpl*);
static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen);
static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags);

oslProfile SAL_CALL osl_openProfile(rtl_uString *ustrProfileName, oslProfileOption Options)
{
    char profilePath[PATH_MAX] = "";
    return
        (ustrProfileName == nullptr
         || ustrProfileName->buffer[0] == 0
         || (FileURLToPath(profilePath, PATH_MAX, ustrProfileName)
             == osl_File_E_None))
        ? osl_psz_openProfile(profilePath, Options)
        : nullptr;
}

static oslProfile osl_psz_openProfile(const char *pszProfileName, oslProfileOption Flags)
{
    osl_TFile*        pFile;
    osl_TProfileImpl* pProfile;
    bool bRet = false;

    if ( ( pFile = openFileImpl(pszProfileName, Flags ) ) == nullptr )
    {
        return nullptr;
    }

    pProfile = static_cast<osl_TProfileImpl*>(calloc(1, sizeof(osl_TProfileImpl)));

    if ( pProfile == nullptr )
    {
        closeFileImpl(pFile, Flags);
        return nullptr;
    }

    pProfile->m_Flags = Flags & FLG_USER;

    if ( Flags & ( osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
    {
        pProfile->m_pFile = pFile;
    }

    pthread_mutex_init(&(pProfile->m_AccessLock),PTHREAD_MUTEXATTR_DEFAULT);
    pProfile->m_bIsValid = true;

    pProfile->m_Stamp = OslProfile_getFileStamp(pFile);
    bRet=loadProfile(pFile, pProfile);
    bRet &= realpath(pszProfileName, pProfile->m_FileName) != nullptr;
    SAL_WARN_IF(!bRet, "sal.osl", "realpath(pszProfileName, pProfile->m_FileName) != NULL ==> false");

    if (pProfile->m_pFile == nullptr)
        closeFileImpl(pFile,pProfile->m_Flags);

    return pProfile;
}

sal_Bool SAL_CALL osl_closeProfile(oslProfile Profile)
{
    osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
    osl_TProfileImpl* pTmpProfile;

    if ( Profile == nullptr )
    {
        return false;
    }

    pthread_mutex_lock(&(pProfile->m_AccessLock));

    if ( !pProfile->m_bIsValid )
    {
        SAL_WARN("sal.osl", "!pProfile->m_bIsValid");
        pthread_mutex_unlock(&(pProfile->m_AccessLock));

        return false;
    }

    pProfile->m_bIsValid = false;

    if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) && ( pProfile->m_Flags & FLG_MODIFIED ) )
    {
        pTmpProfile = acquireProfile(Profile, true);

        if ( pTmpProfile != nullptr )
        {
            bool bRet = storeProfile(pTmpProfile, true);
            SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pTmpProfile, true) ==> false");
        }
    }
    else
    {
        pTmpProfile = acquireProfile(Profile, false);
    }

    if ( pTmpProfile == nullptr )
    {
        pthread_mutex_unlock(&(pProfile->m_AccessLock));

        SAL_INFO("sal.osl", "Out osl_closeProfile [pProfile==0]");
        return false;
    }

    pProfile = pTmpProfile;

    if (pProfile->m_pFile != nullptr)
        closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);

    pProfile->m_pFile = nullptr;
    pProfile->m_FileName[0] = '\0';

    /* release whole profile data types memory */
    if ( pProfile->m_NoLines > 0)
    {
        unsigned int idx=0;
        if ( pProfile->m_Lines != nullptr )
        {
            for ( idx = 0 ; idx < pProfile->m_NoLines ; ++idx)
            {
                if ( pProfile->m_Lines[idx] != nullptr )
                {
                    free(pProfile->m_Lines[idx]);
                    pProfile->m_Lines[idx]=nullptr;
                }
            }
            free(pProfile->m_Lines);
            pProfile->m_Lines=nullptr;
        }
        if ( pProfile->m_Sections != nullptr )
        {
            /*osl_TProfileSection* pSections=pProfile->m_Sections;*/
            for ( idx = 0 ; idx < pProfile->m_NoSections ; ++idx )
            {
                if ( pProfile->m_Sections[idx].m_Entries != nullptr )
                {
                    free(pProfile->m_Sections[idx].m_Entries);
                    pProfile->m_Sections[idx].m_Entries=nullptr;
                }
            }
            free(pProfile->m_Sections);
            pProfile->m_Sections=nullptr;
        }
    }

    pthread_mutex_unlock(&(pProfile->m_AccessLock));

    pthread_mutex_destroy(&(pProfile->m_AccessLock));

    free(pProfile);

    return true;
}

sal_Bool SAL_CALL osl_flushProfile(oslProfile Profile)
{
    osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
    osl_TFile* pFile;
    bool bRet = false;

    if ( pProfile == nullptr )
    {
        return false;
    }

    pthread_mutex_lock(&(pProfile->m_AccessLock));

    if ( !pProfile->m_bIsValid )
    {
        SAL_WARN_IF(!pProfile->m_bIsValid, "sal.osl", "!pProfile->m_bIsValid");
        pthread_mutex_unlock(&(pProfile->m_AccessLock));
        return false;
    }

    pFile = pProfile->m_pFile;
    if ( pFile == nullptr || pFile->m_Handle < 0 )
    {
        pthread_mutex_unlock(&(pProfile->m_AccessLock));

        return false;
    }

    if ( pProfile->m_Flags & FLG_MODIFIED )
    {
        bRet = storeProfile(pProfile, false);
        SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
    }

    pthread_mutex_unlock(&(pProfile->m_AccessLock));
    return bRet;
}

static bool writeProfileImpl(osl_TFile* pFile)
{
    if ( pFile == nullptr || pFile->m_Handle < 0 || pFile->m_pWriteBuf == nullptr )
    {
        return false;
    }

    SAL_WARN_IF(
        (strlen(pFile->m_pWriteBuf)
         != pFile->m_nWriteBufLen - pFile->m_nWriteBufFree),
        "sal.osl",
        strlen(pFile->m_pWriteBuf) << " != "
            << (pFile->m_nWriteBufLen - pFile->m_nWriteBufFree));

    if ( !safeWrite(pFile->m_Handle, pFile->m_pWriteBuf, pFile->m_nWriteBufLen - pFile->m_nWriteBufFree) )
    {
        SAL_INFO("sal.osl", "write failed: " << UnixErrnoString(errno));
        return false;
    }

    free(pFile->m_pWriteBuf);
    pFile->m_pWriteBuf=nullptr;
    pFile->m_nWriteBufLen=0;
    pFile->m_nWriteBufFree=0;

    return true;
}

sal_Bool SAL_CALL osl_readProfileString(oslProfile Profile,
                                        const char* pszSection,
                                        const char* pszEntry,
                                        char* pszString,
                                        sal_uInt32 MaxLen,
                                        const char* pszDefault)
{
    sal_uInt32    NoEntry;
    char* pStr=nullptr;
    osl_TProfileImpl*    pProfile=nullptr;
    osl_TProfileImpl*    pTmpProfile=nullptr;
    bool bRet = false;

    pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);

    if ( pTmpProfile == nullptr )
    {
        return false;
    }

    pthread_mutex_lock(&(pTmpProfile->m_AccessLock));

    if ( !pTmpProfile->m_bIsValid )
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    pProfile = acquireProfile(Profile, false);

    if ( pProfile == nullptr )
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
    {
        osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
        if ((pSec != nullptr) &&
            (NoEntry < pSec->m_NoEntries) &&
            ((pStr = strchr(pProfile->m_Lines[pSec->m_Entries[NoEntry].m_Line],
                            '=')) != nullptr))
        {
            pStr++;
        }
        else
        {
            pStr=const_cast<char*>(pszDefault);
        }

        if ( pStr != nullptr )
        {
            pStr = stripBlanks(pStr, nullptr);
            MaxLen = (MaxLen - 1 < strlen(pStr)) ? (MaxLen - 1) : strlen(pStr);
            pStr = stripBlanks(pStr, &MaxLen);
            strncpy(pszString, pStr, MaxLen);
            pszString[MaxLen] = '\0';
        }
    }
    else
    { /* not implemented */ }

    bRet=releaseProfile(pProfile);
    SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

    if ( pStr == nullptr )
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

    return true;
}

sal_Bool SAL_CALL osl_readProfileBool(oslProfile Profile,
                                      const char* pszSection,
                                      const char* pszEntry,
                                      sal_Bool Default)
{
    char Line[32];
    Line[0] = '\0';

    if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
    {
        if ((strcasecmp(Line, STR_INI_BOOLYES) == 0) ||
            (strcasecmp(Line, STR_INI_BOOLON)  == 0) ||
            (strcasecmp(Line, STR_INI_BOOLONE) == 0))
            Default = true;
        else
            if ((strcasecmp(Line, STR_INI_BOOLNO)   == 0) ||
                (strcasecmp(Line, STR_INI_BOOLOFF)  == 0) ||
                (strcasecmp(Line, STR_INI_BOOLZERO) == 0))
                Default = false;
    }

    return Default;
}

sal_uInt32 SAL_CALL osl_readProfileIdent(oslProfile Profile,
                                         const char* pszSection,
                                         const char* pszEntry,
                                         sal_uInt32 FirstId,
                                         const char* Strings[],
                                         sal_uInt32 Default)
{
    sal_uInt32  i;
    char    Line[256];
    Line[0] = '\0';

    if (osl_readProfileString(Profile, pszSection, pszEntry, Line, sizeof(Line), ""))
    {
        i = 0;
        while (Strings[i] != nullptr)
        {
            if (strcasecmp(Line, Strings[i]) == 0)
            {
                Default = i + FirstId;
                break;
            }
            i++;
        }
    }

    return Default;
}

sal_Bool SAL_CALL osl_writeProfileString(oslProfile Profile,
                                         const char* pszSection,
                                         const char* pszEntry,
                                         const char* pszString)
{
    bool bRet = false;
    sal_uInt32    NoEntry;
    char* pStr;
    char*       Line = nullptr;
    osl_TProfileImpl*    pProfile = nullptr;
    osl_TProfileImpl*    pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);

    if ( pTmpProfile == nullptr )
    {
        return false;
    }

    pthread_mutex_lock(&(pTmpProfile->m_AccessLock));

    if ( !pTmpProfile->m_bIsValid )
    {
        SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    pProfile=acquireProfile(Profile, true);

    if (pProfile == nullptr)
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    Line = static_cast<char*>(malloc(strlen(pszEntry)+strlen(pszString)+48));

    if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
    {
        osl_TProfileSection* pSec;
        if ((pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry)) == nullptr)
        {
            Line[0] = '\0';
            addLine(pProfile, Line);

            Line[0] = '[';
            strcpy(&Line[1], pszSection);
            Line[1 + strlen(pszSection)] = ']';
            Line[2 + strlen(pszSection)] = '\0';

            pStr = addLine(pProfile, Line);
            if ((pStr == nullptr) ||
                (! addSection(pProfile, pProfile->m_NoLines - 1, &pStr[1], strlen(pszSection))))
            {
                bRet=releaseProfile(pProfile);
                SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

                pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

                free(Line);
                return false;
            }

            pSec = &pProfile->m_Sections[pProfile->m_NoSections - 1];
            NoEntry = pSec->m_NoEntries;
        }

        Line[0] = '\0';
        strcpy(&Line[0], pszEntry);
        Line[0 + strlen(pszEntry)] = '=';
        strcpy(&Line[1 + strlen(pszEntry)], pszString);

        if (NoEntry >= pSec->m_NoEntries)
        {
            sal_uInt32  i;
            if (pSec->m_NoEntries > 0)
                i = pSec->m_Entries[pSec->m_NoEntries - 1].m_Line + 1;
            else
                i = pSec->m_Line + 1;

            pStr = insertLine(pProfile, Line, i);
            if ((pStr == nullptr) ||
                (! addEntry(pProfile, pSec, i, pStr, strlen(pszEntry))))
            {
                bRet=releaseProfile(pProfile);
                SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

                pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
                free(Line);

                return false;
            }

            pProfile->m_Flags |= FLG_MODIFIED;
        }
        else
        {
            sal_uInt32  i = pSec->m_Entries[NoEntry].m_Line;
            free(pProfile->m_Lines[i]);
            pProfile->m_Lines[i] = strdup(Line);
            setEntry(pProfile, pSec, NoEntry, i, pProfile->m_Lines[i], strlen(pszEntry));

            pProfile->m_Flags |= FLG_MODIFIED;
        }
    }
    else {
        /* not implemented */
    }

    bRet = releaseProfile(pProfile);
    SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

    pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
    if ( Line!= nullptr )
    {
        free(Line);
    }

    return bRet;
}

sal_Bool SAL_CALL osl_writeProfileBool(oslProfile Profile,
                                       const char* pszSection,
                                       const char* pszEntry,
                                       sal_Bool Value)
{
    bool bRet = false;

    if (Value)
        bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLONE);
    else
        bRet=osl_writeProfileString(Profile, pszSection, pszEntry, STR_INI_BOOLZERO);

    return bRet;
}

sal_Bool SAL_CALL osl_writeProfileIdent(oslProfile Profile,
                                        const char* pszSection,
                                        const char* pszEntry,
                                        sal_uInt32 FirstId,
                                        const char* Strings[],
                                        sal_uInt32 Value)
{
    int i, n = 0;
    bool bRet = false;

    while (Strings[n] != nullptr)
        ++n;

    if ((i = Value - FirstId) >= n)
        bRet = false;
    else
        bRet = osl_writeProfileString(Profile, pszSection, pszEntry, Strings[i]);

    return bRet;
}

sal_Bool SAL_CALL osl_removeProfileEntry(oslProfile Profile,
                                         const char *pszSection,
                                         const char *pszEntry)
{
    sal_uInt32    NoEntry;
    osl_TProfileImpl*    pProfile = nullptr;
    osl_TProfileImpl*    pTmpProfile = nullptr;
    bool bRet = false;

    pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);

    if ( pTmpProfile == nullptr )
    {
        return false;
    }

    pthread_mutex_lock(&(pTmpProfile->m_AccessLock));

    if ( !pTmpProfile->m_bIsValid )
    {
        SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid");
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));
        return false;
    }

    pProfile = acquireProfile(Profile, true);

    if (pProfile == nullptr)
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return false;
    }

    if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
    {
        osl_TProfileSection* pSec = findEntry(pProfile, pszSection, pszEntry, &NoEntry);
        if ((pSec != nullptr) &&
            (NoEntry < pSec->m_NoEntries))
        {
            removeLine(pProfile, pSec->m_Entries[NoEntry].m_Line);
            removeEntry(pSec, NoEntry);
            if (pSec->m_NoEntries == 0)
            {
                removeLine(pProfile, pSec->m_Line);

                /* remove any empty separation line */
                if ((pSec->m_Line > 0) && (pProfile->m_Lines[pSec->m_Line - 1][0] == '\0'))
                    removeLine(pProfile, pSec->m_Line - 1);

                removeSection(pProfile, pSec);
            }

            pProfile->m_Flags |= FLG_MODIFIED;
        }
    }
    else
    { /* not implemented */ }

    bRet = releaseProfile(pProfile);
    SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

    pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

    return bRet;
}

sal_uInt32 SAL_CALL osl_getProfileSectionEntries(oslProfile Profile,
                                                 const char *pszSection,
                                                 char* pszBuffer,
                                                 sal_uInt32 MaxLen)
{
    sal_uInt32    i, n = 0;
    sal_uInt32    NoEntry;
    osl_TProfileImpl*    pProfile = nullptr;
    osl_TProfileImpl*    pTmpProfile = nullptr;
    bool bRet = false;

    pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);

    if ( pTmpProfile == nullptr )
    {
        return 0;

    }

    pthread_mutex_lock(&(pTmpProfile->m_AccessLock));

    if ( !pTmpProfile->m_bIsValid )
    {
        SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid"); 

        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return 0;
    }

    pProfile = acquireProfile(Profile, false);

    if (pProfile == nullptr)
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return 0;
    }

    if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
    {
        osl_TProfileSection* pSec;
        if ((pSec = findEntry(pProfile, pszSection, "", &NoEntry)) != nullptr)
        {
            if (MaxLen != 0)
            {
                for (i = 0; i < pSec->m_NoEntries; i++)
                {
                    if ((n + pSec->m_Entries[i].m_Len + 1) < MaxLen)
                    {
                        strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
                                [pSec->m_Entries[i].m_Offset], pSec->m_Entries[i].m_Len);
                        n += pSec->m_Entries[i].m_Len;
                        pszBuffer[n++] = '\0';
                    }
                    else
                        break;

                }

                pszBuffer[n++] = '\0';
            }
            else
            {
                for (i = 0; i < pSec->m_NoEntries; i++)
                    n += pSec->m_Entries[i].m_Len + 1;

                n += 1;
            }
        }
        else
            n = 0;
    }
    else {
        /* not implemented */
    }

    bRet=releaseProfile(pProfile);
    SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

    pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

    return n;
}

sal_uInt32 SAL_CALL osl_getProfileSections(oslProfile Profile,
                                           char* pszBuffer,
                                           sal_uInt32 MaxLen)
{
    sal_uInt32    i, n = 0;
    osl_TProfileImpl*    pProfile = nullptr;
    osl_TProfileImpl*    pTmpProfile = nullptr;
    bool bRet = false;

    pTmpProfile = static_cast<osl_TProfileImpl*>(Profile);

    if ( pTmpProfile == nullptr )
    {
        return 0;
    }

    pthread_mutex_lock(&(pTmpProfile->m_AccessLock));

    if ( !pTmpProfile->m_bIsValid )
    {
        SAL_WARN_IF(!pTmpProfile->m_bIsValid, "sal.osl", "!pTmpProfile->m_bIsValid"); 
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return 0;
    }

    pProfile = acquireProfile(Profile, false);

    if (pProfile == nullptr)
    {
        pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

        return 0;
    }

    if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
    {
        if (MaxLen != 0)
        {
            for (i = 0; i < pProfile->m_NoSections; i++)
            {
                osl_TProfileSection* pSec = &pProfile->m_Sections[i];

                if ((n + pSec->m_Len + 1) < MaxLen)
                {
                    strncpy(&pszBuffer[n], &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset],
                            pSec->m_Len);
                    n += pSec->m_Len;
                    pszBuffer[n++] = '\0';
                }
                else
                    break;
            }

            pszBuffer[n++] = '\0';
        }
        else
        {
            for (i = 0; i < pProfile->m_NoSections; i++)
                n += pProfile->m_Sections[i].m_Len + 1;

            n += 1;
        }
    }
    else
    { /* not implemented */ }

    bRet=releaseProfile(pProfile);
    SAL_WARN_IF(!bRet, "sal.osl", "releaseProfile(pProfile) ==> false");

    pthread_mutex_unlock(&(pTmpProfile->m_AccessLock));

    return n;
}

static osl_TStamp OslProfile_getFileStamp(osl_TFile* pFile)
{
    struct stat status;

    if ( (pFile->m_Handle < 0) || (fstat(pFile->m_Handle, &status) < 0) )
    {
        return 0;
    }

    return status.st_mtime;
}

static bool OslProfile_lockFile(const osl_TFile* pFile, osl_TLockMode eMode)
{
    struct flock lock;
    static bool const bLockingDisabled = getenv( "STAR_PROFILE_LOCKING_DISABLED" ) != nullptr;

    if (pFile->m_Handle < 0)
    {
        return false;
    }

    if ( bLockingDisabled )
    {
        return true;
    }

    lock.l_start  = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len    = 0;

    switch (eMode)
    {
        case un_lock:
            lock.l_type = F_UNLCK;
            break;

        case read_lock:
            lock.l_type = F_RDLCK;
            break;

        case write_lock:
            lock.l_type = F_WRLCK;
            break;
    }

#ifndef MACOSX
    if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 )
#else
    /* Mac OSX will return ENOTSUP for webdav drives so we should ignore it */
    if ( fcntl(pFile->m_Handle, F_SETLKW, &lock) == -1 && errno != ENOTSUP )
#endif
    {
        SAL_INFO("sal.osl", "fcntl failed: " << UnixErrnoString(errno));
        return false;
    }

    return true;
}

static osl_TFile* openFileImpl(const char* pszFilename, oslProfileOption ProfileFlags )
{
    int        Flags;
    bool       bWriteable = false;

    if ( isForbidden( pszFilename, osl_File_OpenFlag_Write ) )
        return nullptr;

    osl_TFile* pFile = static_cast<osl_TFile*>(calloc(1, sizeof(osl_TFile)));

    if ( ProfileFlags & ( osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE ) )
    {
        bWriteable = true;
    }

    if (! bWriteable)
    {
        pFile->m_Handle = open(pszFilename, O_RDONLY);

        if (pFile->m_Handle == -1)
        {
            int e = errno;
            SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY): " << UnixErrnoString(e));
        }
        else
            SAL_INFO("sal.file", "open(" << pszFilename << ",O_RDONLY) => " << pFile->m_Handle);

        /* mfe: argghh!!! do not check if the file could be opened */
        /*      default mode expects it that way!!!                 */
    }
    else
    {
        if (((pFile->m_Handle = open(pszFilename, O_RDWR | O_CREAT | O_EXCL, DEFAULT_PMODE)) < 0) &&
            ((pFile->m_Handle = open(pszFilename, O_RDWR)) < 0))
        {
            int e = errno;
            SAL_INFO("sal.file", "open(" << pszFilename << ",...): " << UnixErrnoString(e));
            free(pFile);
            return nullptr;
        }
        else
            SAL_INFO("sal.file", "open(" << pszFilename << ",...) => " << pFile->m_Handle);
    }

    /* set close-on-exec flag */
    if ((Flags = fcntl(pFile->m_Handle, F_GETFD, 0)) != -1)
    {
        Flags |= FD_CLOEXEC;
        int e = fcntl(pFile->m_Handle, F_SETFD, Flags);
        SAL_INFO_IF(
            e != 0, "sal.osl",
            "fcntl to set FD_CLOEXEC failed for " << pszFilename);
    }

    pFile->m_pWriteBuf=nullptr;
    pFile->m_nWriteBufFree=0;
    pFile->m_nWriteBufLen=0;

    if ( ProfileFlags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
    {
        OslProfile_lockFile(pFile, bWriteable ? write_lock : read_lock);
    }

    return pFile;
}

static osl_TStamp closeFileImpl(osl_TFile* pFile, oslProfileOption Flags)
{
    osl_TStamp stamp = 0;

    if ( pFile == nullptr )
    {
        return stamp;
    }

    if ( pFile->m_Handle >= 0 )
    {
        stamp = OslProfile_getFileStamp(pFile);

        if ( Flags & (osl_Profile_WRITELOCK | osl_Profile_READLOCK ) )
        {
            OslProfile_lockFile(pFile, un_lock);
        }

        close(pFile->m_Handle);
        SAL_INFO("sal.file", "close(" << pFile->m_Handle << ")");
        pFile->m_Handle = -1;
    }

    if ( pFile->m_pWriteBuf )
    {
        free(pFile->m_pWriteBuf);
    }

    free(pFile);

    return stamp;
}

static bool OslProfile_rewindFile(osl_TFile* pFile, bool bTruncate)
{
    bool bRet = true;

    if (pFile->m_Handle >= 0)
    {
        pFile->m_pReadPtr  = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);

        bRet = (lseek(pFile->m_Handle, SEEK_SET, 0) == 0);

        if (bTruncate)
        {
            bRet &= (ftruncate(pFile->m_Handle, 0) == 0);
        }

    }

    return bRet;
}

static char* OslProfile_getLine(osl_TFile* pFile)
{
    ssize_t Max, nLineBytes = 0;
    char* pChr;
    char* pLine = nullptr;
    char* pNewLine;

    if ( pFile == nullptr )
    {
        return nullptr;
    }

    if (pFile->m_Handle < 0)
        return nullptr;

    do
    {
        ssize_t Bytes = sizeof(pFile->m_ReadBuf) - (pFile->m_pReadPtr - pFile->m_ReadBuf);

        if (Bytes <= 1)
        {
            /* refill buffer */
            memcpy(pFile->m_ReadBuf, pFile->m_pReadPtr, Bytes);
            pFile->m_pReadPtr = pFile->m_ReadBuf;

            ssize_t Free = sizeof(pFile->m_ReadBuf) - Bytes;

            Max = read(pFile->m_Handle, &pFile->m_ReadBuf[Bytes], Free);
            if (Max < 0)
            {
                SAL_INFO("sal.osl", "read failed: " << UnixErrnoString(errno));

                if( pLine )
                    free( pLine );
                pLine = nullptr;
                break;
            }

            if (Max < Free)
            {
                 if ((Max == 0) && ! pLine)
                     break;

                 pFile->m_ReadBuf[Bytes + Max] = '\0';
            }
        }

        for (pChr = pFile->m_pReadPtr;
             (*pChr != '\n') && (*pChr != '\r') && (*pChr != '\0') &&
             (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1));
             pChr++);

        Max = pChr - pFile->m_pReadPtr;
        pNewLine = static_cast<char*>(malloc( nLineBytes + Max + 1 ));
        if( pLine )
        {
            memcpy( pNewLine, pLine, nLineBytes );
            free( pLine );
        }
        memcpy(pNewLine+nLineBytes, pFile->m_pReadPtr, Max);
        nLineBytes += Max;
        pNewLine[ nLineBytes ] = 0;
        pLine = pNewLine;

        if (pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf) - 1))
        {
            if (*pChr != '\0')
            {
                if ((pChr[0] == '\r') && (pChr[1] == '\n'))
                    pChr += 2;
                else
                    pChr += 1;
            }

            if ((pChr < (pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf))) &&
                (*pChr == '\0'))
                pChr = pFile->m_ReadBuf + sizeof(pFile->m_ReadBuf);

            /* setting Max to -1 indicates terminating read loop */
            Max = -1;
        }

        pFile->m_pReadPtr = pChr;
    }
    while (Max > 0) ;

    return pLine;
}

static bool OslProfile_putLine(osl_TFile* pFile, const char *pszLine)
{
    unsigned int Len = strlen(pszLine);

    if ( pFile == nullptr || pFile->m_Handle < 0 )
    {
        return false;
    }

    if ( pFile->m_pWriteBuf == nullptr )
    {
        pFile->m_pWriteBuf = static_cast<char*>(malloc(Len+3));
        pFile->m_nWriteBufLen = Len+3;
        pFile->m_nWriteBufFree = Len+3;
    }
    else
    {
        if ( pFile->m_nWriteBufFree <= Len + 3 )
        {
            char* pTmp;

            pTmp=static_cast<char*>(realloc(pFile->m_pWriteBuf,( ( pFile->m_nWriteBufLen + Len ) * 2) ));
            if ( pTmp == nullptr )
            {
                return false;
            }
            pFile->m_pWriteBuf = pTmp;
            pFile->m_nWriteBufFree = pFile->m_nWriteBufFree + pFile->m_nWriteBufLen + ( 2 * Len );
            pFile->m_nWriteBufLen = ( pFile->m_nWriteBufLen + Len ) * 2;
            memset( (pFile->m_pWriteBuf) + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ), 0, pFile->m_nWriteBufFree);
        }
    }

    memcpy(pFile->m_pWriteBuf + ( pFile->m_nWriteBufLen - pFile->m_nWriteBufFree ),pszLine,Len+1);
    pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len]='\n';
    pFile->m_pWriteBuf[pFile->m_nWriteBufLen - pFile->m_nWriteBufFree + Len + 1]='\0';

    pFile->m_nWriteBufFree-=Len+1;

    return true;
}

static char* stripBlanks(char* String, sal_uInt32* pLen)
{
    if ( ( pLen != nullptr ) && ( *pLen != 0 ) )
    {
        while ((String[*pLen - 1] == ' ') || (String[*pLen - 1] == '\t'))
            (*pLen)--;

        while ( (*String == ' ') || (*String == '\t') )
        {
            String++;
            (*pLen)--;
        }
    }
    else
        while ( (*String == ' ') || (*String == '\t') )
            String++;

    return String;
}

static char* addLine(osl_TProfileImpl* pProfile, const char* Line)
{
    if (pProfile->m_NoLines >= pProfile->m_MaxLines)
    {
        if (pProfile->m_Lines == nullptr)
        {
            pProfile->m_MaxLines = LINES_INI;
            pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
        }
        else
        {
            unsigned int idx=0;
            unsigned int oldmax=pProfile->m_MaxLines;

            pProfile->m_MaxLines += LINES_ADD;
            pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
                                                 pProfile->m_MaxLines * sizeof(char *)));
            for ( idx = oldmax ; idx < pProfile->m_MaxLines ; ++idx )
            {
                pProfile->m_Lines[idx]=nullptr;
            }
        }
    }
    if (pProfile->m_Lines == nullptr)
    {
        pProfile->m_NoLines  = 0;
        pProfile->m_MaxLines = 0;
        return nullptr;
    }

    if ( pProfile->m_Lines[pProfile->m_NoLines] != nullptr )
    {
        free(pProfile->m_Lines[pProfile->m_NoLines]);
    }
    pProfile->m_Lines[pProfile->m_NoLines++] = strdup(Line);

    return pProfile->m_Lines[pProfile->m_NoLines - 1];
}

static char* insertLine(osl_TProfileImpl* pProfile, const char* Line, sal_uInt32 LineNo)
{
    if (pProfile->m_NoLines >= pProfile->m_MaxLines)
    {
        if (pProfile->m_Lines == nullptr)
        {
            pProfile->m_MaxLines = LINES_INI;
            pProfile->m_Lines = static_cast<char **>(calloc(pProfile->m_MaxLines, sizeof(char *)));
        }
        else
        {
            pProfile->m_MaxLines += LINES_ADD;
            pProfile->m_Lines = static_cast<char **>(realloc(pProfile->m_Lines,
                                                 pProfile->m_MaxLines * sizeof(char *)));

            memset(&pProfile->m_Lines[pProfile->m_NoLines],
                0,
                (pProfile->m_MaxLines - pProfile->m_NoLines - 1) * sizeof(char*));
        }

        if (pProfile->m_Lines == nullptr)
        {
            pProfile->m_NoLines  = 0;
            pProfile->m_MaxLines = 0;
            return nullptr;
        }
    }

    LineNo = std::min(LineNo, pProfile->m_NoLines);

    if (LineNo < pProfile->m_NoLines)
    {
        sal_uInt32 i, n;

        memmove(&pProfile->m_Lines[LineNo + 1], &pProfile->m_Lines[LineNo],
                (pProfile->m_NoLines - LineNo) * sizeof(char *));

        /* adjust line references */
        for (i = 0; i < pProfile->m_NoSections; i++)
        {
            osl_TProfileSection* pSec = &pProfile->m_Sections[i];

            if (pSec->m_Line >= LineNo)
                pSec->m_Line++;

            for (n = 0; n < pSec->m_NoEntries; n++)
                if (pSec->m_Entries[n].m_Line >= LineNo)
                    pSec->m_Entries[n].m_Line++;
        }
    }

    pProfile->m_NoLines++;

    pProfile->m_Lines[LineNo] = strdup(Line);

    return pProfile->m_Lines[LineNo];
}

static void removeLine(osl_TProfileImpl* pProfile, sal_uInt32 LineNo)
{
    if (LineNo >= pProfile->m_NoLines)
        return;

    free(pProfile->m_Lines[LineNo]);
    pProfile->m_Lines[LineNo]=nullptr;
    if (pProfile->m_NoLines - LineNo > 1)
    {
        sal_uInt32 i, n;

        memmove(&pProfile->m_Lines[LineNo], &pProfile->m_Lines[LineNo + 1],
                (pProfile->m_NoLines - LineNo - 1) * sizeof(char *));

        memset(&pProfile->m_Lines[pProfile->m_NoLines - 1],
            0,
            (pProfile->m_MaxLines - pProfile->m_NoLines) * sizeof(char*));

        /* adjust line references */
        for (i = 0; i < pProfile->m_NoSections; i++)
        {
            osl_TProfileSection* pSec = &pProfile->m_Sections[i];

            if (pSec->m_Line > LineNo)
                pSec->m_Line--;

            for (n = 0; n < pSec->m_NoEntries; n++)
                if (pSec->m_Entries[n].m_Line > LineNo)
                    pSec->m_Entries[n].m_Line--;
        }
    }
    else
    {
        pProfile->m_Lines[LineNo] = nullptr;
    }

    pProfile->m_NoLines--;
}

static void setEntry(osl_TProfileImpl* pProfile, osl_TProfileSection* pSection,
                     sal_uInt32 NoEntry, sal_uInt32 Line,
                     char* Entry, sal_uInt32 Len)
{
    Entry = stripBlanks(Entry, &Len);
    pSection->m_Entries[NoEntry].m_Line   = Line;
    pSection->m_Entries[NoEntry].m_Offset = Entry - pProfile->m_Lines[Line];
    pSection->m_Entries[NoEntry].m_Len    = Len;
}

static bool addEntry(osl_TProfileImpl* pProfile,
                     osl_TProfileSection *pSection,
                     int Line, char* Entry,
                     sal_uInt32 Len)
{
    if (pSection != nullptr)
    {
        if (pSection->m_NoEntries >= pSection->m_MaxEntries)
        {
            if (pSection->m_Entries == nullptr)
            {
                pSection->m_MaxEntries = ENTRIES_INI;
                pSection->m_Entries = static_cast<osl_TProfileEntry *>(malloc(
                                pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
            }
            else
            {
                pSection->m_MaxEntries += ENTRIES_ADD;
                pSection->m_Entries = static_cast<osl_TProfileEntry *>(realloc(pSection->m_Entries,
                                pSection->m_MaxEntries * sizeof(osl_TProfileEntry)));
            }

            if (pSection->m_Entries == nullptr)
            {
                pSection->m_NoEntries  = 0;
                pSection->m_MaxEntries = 0;
                return false;
            }
        }

        pSection->m_NoEntries++;

        Entry = stripBlanks(Entry, &Len);
        setEntry(pProfile, pSection, pSection->m_NoEntries - 1, Line,
                 Entry, Len);

        return true;
    }

    return false;
}

static void removeEntry(osl_TProfileSection *pSection, sal_uInt32 NoEntry)
{
    if (NoEntry >= pSection->m_NoEntries)
        return;

    if (pSection->m_NoEntries - NoEntry > 1)
    {
        memmove(&pSection->m_Entries[NoEntry],
                &pSection->m_Entries[NoEntry + 1],
                (pSection->m_NoEntries - NoEntry - 1) * sizeof(osl_TProfileEntry));
        pSection->m_Entries[pSection->m_NoEntries - 1].m_Line=0;
        pSection->m_Entries[pSection->m_NoEntries - 1].m_Offset=0;
        pSection->m_Entries[pSection->m_NoEntries - 1].m_Len=0;
    }

    pSection->m_NoEntries--;

}

static bool addSection(osl_TProfileImpl* pProfile, int Line, const char* Section, sal_uInt32 Len)
{
    if (pProfile->m_NoSections >= pProfile->m_MaxSections)
    {
        if (pProfile->m_Sections == nullptr)
        {
            pProfile->m_MaxSections = SECTIONS_INI;
            pProfile->m_Sections = static_cast<osl_TProfileSection *>(calloc(pProfile->m_MaxSections, sizeof(osl_TProfileSection)));
        }
        else
        {
            unsigned int idx=0;
            unsigned int oldmax=pProfile->m_MaxSections;

            pProfile->m_MaxSections += SECTIONS_ADD;
            pProfile->m_Sections = static_cast<osl_TProfileSection *>(realloc(pProfile->m_Sections,
                                          pProfile->m_MaxSections * sizeof(osl_TProfileSection)));
            for ( idx = oldmax ; idx < pProfile->m_MaxSections ; ++idx )
            {
                pProfile->m_Sections[idx].m_Entries=nullptr;
            }
        }

        if (pProfile->m_Sections == nullptr)
        {
            pProfile->m_NoSections = 0;
            pProfile->m_MaxSections = 0;
            return false;
        }
    }

    pProfile->m_NoSections++;

    if ( pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries != nullptr )
    {
         free(pProfile->m_Sections[(pProfile->m_NoSections) - 1].m_Entries);
    }
    pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries    = nullptr;
    pProfile->m_Sections[pProfile->m_NoSections - 1].m_NoEntries  = 0;
    pProfile->m_Sections[pProfile->m_NoSections - 1].m_MaxEntries = 0;

    pProfile->m_Sections[pProfile->m_NoSections - 1].m_Line = Line;
    pProfile->m_Sections[pProfile->m_NoSections - 1].m_Offset = Section - pProfile->m_Lines[Line];
    pProfile->m_Sections[pProfile->m_NoSections - 1].m_Len = Len;

    return true;
}

static void removeSection(osl_TProfileImpl* pProfile, osl_TProfileSection *pSection)
{
    sal_uInt32 Section;

    if ((Section = pSection - pProfile->m_Sections) >= pProfile->m_NoSections)
        return;

    free (pSection->m_Entries);
    pSection->m_Entries=nullptr;
    if (pProfile->m_NoSections - Section > 1)
    {
        memmove(&pProfile->m_Sections[Section], &pProfile->m_Sections[Section + 1],
                (pProfile->m_NoSections - Section - 1) * sizeof(osl_TProfileSection));

        memset(&pProfile->m_Sections[pProfile->m_NoSections - 1],
               0,
               (pProfile->m_MaxSections - pProfile->m_NoSections) * sizeof(osl_TProfileSection));
        pProfile->m_Sections[pProfile->m_NoSections - 1].m_Entries = nullptr;
    }
    else
    {
        pSection->m_Entries = nullptr;
    }

    pProfile->m_NoSections--;
}

static osl_TProfileSection* findEntry(osl_TProfileImpl* pProfile,
                                      const char* Section,
                                      const char* Entry,
                                      sal_uInt32 *pNoEntry)
{
    static  sal_uInt32    Sect = 0;
    sal_uInt32    i, n;
    sal_uInt32  Len;
    osl_TProfileSection* pSec=nullptr;

    Len = strlen(Section);

    n = Sect;

    for (i = 0; i < pProfile->m_NoSections; i++)
    {
        n %= pProfile->m_NoSections;
        pSec = &pProfile->m_Sections[n];
        if ((Len == pSec->m_Len) &&
            (strncasecmp(Section, &pProfile->m_Lines[pSec->m_Line][pSec->m_Offset], pSec->m_Len)
             == 0))
            break;
        n++;
    }

    Sect = n;

    if (i < pProfile->m_NoSections)
    {
        Len = strlen(Entry);

        *pNoEntry = pSec->m_NoEntries;

        for (i = 0; i < pSec->m_NoEntries; i++)
        {
            const char* pStr = &pProfile->m_Lines[pSec->m_Entries[i].m_Line]
                                     [pSec->m_Entries[i].m_Offset];
            if ((Len == pSec->m_Entries[i].m_Len) &&
                (strncasecmp(Entry, pStr, pSec->m_Entries[i].m_Len)
                 == 0))
            {
                *pNoEntry = i;
                break;
            }
        }
    }
    else
        pSec = nullptr;

    return pSec;
}

static bool loadProfile(osl_TFile* pFile, osl_TProfileImpl* pProfile)
{
    sal_uInt32  i;
    char*   pStr;
    char*   pChar;

    char* pLine;

    if ( !pFile )
    {
        return false;
    }

    if ( !pProfile )
    {
        return false;
    }

    pProfile->m_NoLines    = 0;
    pProfile->m_NoSections = 0;

    OSL_VERIFY(OslProfile_rewindFile(pFile, false));

    while ( ( pLine=OslProfile_getLine(pFile) ) != nullptr )
    {
        char* bWasAdded = addLine( pProfile, pLine );
        free( pLine );
        SAL_WARN_IF(!bWasAdded, "sal.osl", "addLine( pProfile, pLine ) ==> false");
        if ( ! bWasAdded )
            return false;
    }

    for (i = 0; i < pProfile->m_NoLines; i++)
    {
        pStr = stripBlanks(pProfile->m_Lines[i], nullptr);

        if ((*pStr == '\0') || (*pStr == ';'))
            continue;

        if ((*pStr != '[') || ((pChar = strrchr(pStr, ']')) == nullptr) ||
            ((pChar - pStr) <= 2))
        {
            /* insert entry */

            if (pProfile->m_NoSections < 1)
                continue;

            if ((pChar = strchr(pStr, '=')) == nullptr)
                pChar = pStr + strlen(pStr);

            if (! addEntry(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1],
                           i, pStr, pChar - pStr))
            {
                SAL_WARN("sal.osl", "Adding entry => false");
                continue;
            }

        }
        else
        {
            /* new section */

            if (! addSection(pProfile, i, pStr + 1, pChar - pStr - 1))
            {
                SAL_WARN("sal.osl", "Adding section => false");
                continue;
            }

        }
    }

    return true;
}

static bool storeProfile(osl_TProfileImpl* pProfile, bool bCleanup)
{
    if (pProfile->m_Lines != nullptr)
    {
        if (pProfile->m_Flags & FLG_MODIFIED)
        {
            sal_uInt32 i;

            osl_TFile* pTmpFile = osl_openTmpProfileImpl(pProfile);

            if ( pTmpFile == nullptr )
            {
                return false;
            }

            OSL_VERIFY(OslProfile_rewindFile(pTmpFile, true));

            for ( i = 0 ; i < pProfile->m_NoLines ; i++ )
            {
                OSL_VERIFY(OslProfile_putLine(pTmpFile, pProfile->m_Lines[i]));
            }

            if ( ! writeProfileImpl(pTmpFile) )
            {
                if ( pTmpFile->m_pWriteBuf != nullptr )
                {
                    free(pTmpFile->m_pWriteBuf);
                }

                pTmpFile->m_pWriteBuf=nullptr;
                pTmpFile->m_nWriteBufLen=0;
                pTmpFile->m_nWriteBufFree=0;

                closeFileImpl(pTmpFile,pProfile->m_Flags);

                return false;
            }

            pProfile->m_Flags &= ~FLG_MODIFIED;

            closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
            closeFileImpl(pTmpFile,pProfile->m_Flags);

            osl_ProfileSwapProfileNames(pProfile);

            pProfile->m_pFile = openFileImpl(pProfile->m_FileName,pProfile->m_Flags);

        }

        if (bCleanup)
        {
            while (pProfile->m_NoLines > 0)
                removeLine(pProfile, pProfile->m_NoLines - 1);

            free(pProfile->m_Lines);
            pProfile->m_Lines = nullptr;
            pProfile->m_NoLines = 0;
            pProfile->m_MaxLines = 0;

            while (pProfile->m_NoSections > 0)
                removeSection(pProfile, &pProfile->m_Sections[pProfile->m_NoSections - 1]);

            free(pProfile->m_Sections);
            pProfile->m_Sections = nullptr;
            pProfile->m_NoSections = 0;
            pProfile->m_MaxSections = 0;
        }
    }

    return true;
}

static osl_TFile* osl_openTmpProfileImpl(osl_TProfileImpl* pProfile)
{
    osl_TFile* pFile=nullptr;
    char const * const pszExtension = "tmp";
    char pszTmpName[PATH_MAX];
    oslProfileOption PFlags=0;

    pszTmpName[0] = '\0';

    /* generate tmp profilename */
    osl_ProfileGenerateExtension(pProfile->m_FileName, pszExtension, pszTmpName, PATH_MAX);

    if ( pszTmpName[0] == 0 )
    {
        return nullptr;
    }

    if ( ! ( pProfile->m_Flags & osl_Profile_READLOCK ) )
    {
        PFlags |= osl_Profile_WRITELOCK;
    }

    /* open this file */
    pFile = openFileImpl(pszTmpName,pProfile->m_Flags | PFlags);

    /* return new pFile */
    return pFile;
}

static bool osl_ProfileSwapProfileNames(osl_TProfileImpl* pProfile)
{
    char pszBakFile[PATH_MAX];
    char pszTmpFile[PATH_MAX];

    pszBakFile[0] = '\0';
    pszTmpFile[0] = '\0';

    osl_ProfileGenerateExtension(pProfile->m_FileName, "bak", pszBakFile, PATH_MAX);
    osl_ProfileGenerateExtension(pProfile->m_FileName, "tmp", pszTmpFile, PATH_MAX);

    /* unlink bak */
    unlink( pszBakFile );

    // Rename ini -> bak, then tmp -> ini:
    bool result = rename( pProfile->m_FileName, pszBakFile ) == 0;
    if (!result)
    {
        int e = errno;
        SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): " << UnixErrnoString(e));
    }
    else
    {
        SAL_INFO("sal.file", "rename(" << pProfile->m_FileName << "," << pszBakFile << "): OK");
        result = rename( pszTmpFile, pProfile->m_FileName ) == 0;
        if (!result)
        {
            int e = errno;
            SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): " << UnixErrnoString(e));
        }
        else
        {
            SAL_INFO("sal.file", "rename(" << pszTmpFile << "," << pProfile->m_FileName << "): OK");
        }
    }
    return result;
}

static void osl_ProfileGenerateExtension(const char* pszFileName, const char* pszExtension, char* pszTmpName, int BufferMaxLen)
{
    char* cursor = pszTmpName;
    int len;

    /* concatenate filename + "." + extension, limited to the size of the
     * output buffer; in case of overrun, data is truncated at the end...
     * and the result is always 0-terminated.
     */
    len = strlen(pszFileName);
    if(len < BufferMaxLen)
    {
        memcpy(cursor, pszFileName, len);
        cursor += len;
        BufferMaxLen -= len;
    }
    else
    {
        memcpy(cursor, pszFileName, BufferMaxLen - 1);
        cursor += BufferMaxLen - 1;
        BufferMaxLen = 1;
    }
    if(BufferMaxLen > 1)
    {
        *cursor++ = '.';
        BufferMaxLen -= 1;
    }
    len = strlen(pszExtension);
    if(len < BufferMaxLen)
    {
        memcpy(cursor, pszExtension, len);
        cursor += len;
    }
    else
    {
        memcpy(cursor, pszExtension, BufferMaxLen - 1);
        cursor += BufferMaxLen - 1;
    }
    *cursor = 0;
}

static osl_TProfileImpl* acquireProfile(oslProfile Profile, bool bWriteable)
{
    osl_TProfileImpl* pProfile = static_cast<osl_TProfileImpl*>(Profile);
    oslProfileOption PFlags=0;

    if ( bWriteable )
    {
        PFlags = osl_Profile_DEFAULT | osl_Profile_WRITELOCK;
    }
    else
    {
        PFlags = osl_Profile_DEFAULT;
    }

    if (pProfile == nullptr)
    {
        if ( ( pProfile = static_cast<osl_TProfileImpl*>(osl_openProfile(nullptr, PFlags )) ) != nullptr )
        {
            pProfile->m_Flags |= FLG_AUTOOPEN;
        }
    }
    else
    {
        if (! (pProfile->m_Flags & osl_Profile_SYSTEM))
        {
            if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
            {
                osl_TStamp Stamp;

                if (! (pProfile->m_pFile = openFileImpl(pProfile->m_FileName, pProfile->m_Flags | PFlags )))
                    return nullptr;

                Stamp = OslProfile_getFileStamp(pProfile->m_pFile);

                if (memcmp(&Stamp, &(pProfile->m_Stamp), sizeof(osl_TStamp)))
                {
                    pProfile->m_Stamp = Stamp;
                    bool bRet = loadProfile(pProfile->m_pFile, pProfile);
                    SAL_WARN_IF(!bRet, "sal.osl", "loadProfile(pProfile->m_pFile, pProfile) ==> false");
                }
            }
            else
            {
                /* A readlock file could not be written */
                if ((pProfile->m_Flags & osl_Profile_READLOCK) && bWriteable)
                {
                    return nullptr;
                }
            }
        }
    }

    return pProfile;
}

static bool releaseProfile(osl_TProfileImpl* pProfile)
{
    if ( pProfile == nullptr )
    {
        return false;
    }

    if (pProfile->m_Flags & FLG_AUTOOPEN)
    {
        return osl_closeProfile(static_cast<oslProfile>(pProfile));
    }

    if (! (pProfile->m_Flags & (osl_Profile_READLOCK | osl_Profile_WRITELOCK | osl_Profile_FLUSHWRITE )))
    {
        if (pProfile->m_Flags & FLG_MODIFIED)
        {
            bool bRet = storeProfile(pProfile, false);
            SAL_WARN_IF(!bRet, "sal.osl", "storeProfile(pProfile, false) ==> false");
        }

        closeFileImpl(pProfile->m_pFile,pProfile->m_Flags);
        pProfile->m_pFile = nullptr;
    }

    return true;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
