939 lines
26 KiB
C++
939 lines
26 KiB
C++
/* -*- 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 <cstddef>
|
|
#include <cstdlib>
|
|
#include <string.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <osl/file.hxx>
|
|
#include <tools/config.hxx>
|
|
#include <sal/log.hxx>
|
|
|
|
namespace {
|
|
|
|
struct ImplKeyData
|
|
{
|
|
ImplKeyData* mpNext;
|
|
OString maKey;
|
|
OString maValue;
|
|
bool mbIsComment;
|
|
};
|
|
|
|
}
|
|
|
|
struct ImplGroupData
|
|
{
|
|
ImplGroupData* mpNext;
|
|
ImplKeyData* mpFirstKey;
|
|
OString maGroupName;
|
|
sal_uInt16 mnEmptyLines;
|
|
};
|
|
|
|
struct ImplConfigData
|
|
{
|
|
ImplGroupData* mpFirstGroup;
|
|
OUString maFileName;
|
|
sal_uInt32 mnDataUpdateId;
|
|
sal_uInt32 mnTimeStamp;
|
|
bool mbModified;
|
|
bool mbRead;
|
|
bool mbIsUTF8BOM;
|
|
};
|
|
|
|
static OUString toUncPath( const OUString& rPath )
|
|
{
|
|
OUString aFileURL;
|
|
|
|
// check if rFileName is already a URL; if not make it so
|
|
if( rPath.startsWith( "file://"))
|
|
{
|
|
aFileURL = rPath;
|
|
}
|
|
else if( ::osl::FileBase::getFileURLFromSystemPath( rPath, aFileURL ) != ::osl::FileBase::E_None )
|
|
{
|
|
aFileURL = rPath;
|
|
}
|
|
return aFileURL;
|
|
}
|
|
|
|
static sal_uInt32 ImplSysGetConfigTimeStamp( const OUString& rFileName )
|
|
{
|
|
sal_uInt32 nTimeStamp = 0;
|
|
::osl::DirectoryItem aItem;
|
|
::osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime );
|
|
|
|
if( ::osl::DirectoryItem::get( rFileName, aItem ) == ::osl::FileBase::E_None &&
|
|
aItem.getFileStatus( aStatus ) == ::osl::FileBase::E_None )
|
|
{
|
|
nTimeStamp = aStatus.getModifyTime().Seconds;
|
|
}
|
|
|
|
return nTimeStamp;
|
|
}
|
|
|
|
static std::unique_ptr<sal_uInt8[]> ImplSysReadConfig( const OUString& rFileName,
|
|
sal_uInt64& rRead, bool& rbRead, bool& rbIsUTF8BOM, sal_uInt32& rTimeStamp )
|
|
{
|
|
std::unique_ptr<sal_uInt8[]> pBuf;
|
|
::osl::File aFile( rFileName );
|
|
|
|
if( aFile.open( osl_File_OpenFlag_Read ) == ::osl::FileBase::E_None )
|
|
{
|
|
sal_uInt64 nPos = 0;
|
|
if( aFile.getSize( nPos ) == ::osl::FileBase::E_None )
|
|
{
|
|
if (nPos > SAL_MAX_SIZE) {
|
|
aFile.close();
|
|
return nullptr;
|
|
}
|
|
pBuf.reset(new sal_uInt8[static_cast< std::size_t >(nPos)]);
|
|
sal_uInt64 nRead = 0;
|
|
if( aFile.read( pBuf.get(), nPos, nRead ) == ::osl::FileBase::E_None && nRead == nPos )
|
|
{
|
|
//skip the byte-order-mark 0xEF 0xBB 0xBF, if it was UTF8 files
|
|
unsigned char const BOM[3] = {0xEF, 0xBB, 0xBF};
|
|
if (nRead > 2 && memcmp(pBuf.get(), BOM, 3) == 0)
|
|
{
|
|
nRead -= 3;
|
|
memmove(pBuf.get(), pBuf.get() + 3, sal::static_int_cast<std::size_t>(nRead * sizeof(sal_uInt8)) );
|
|
rbIsUTF8BOM = true;
|
|
}
|
|
|
|
rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
|
|
rbRead = true;
|
|
rRead = nRead;
|
|
}
|
|
else
|
|
{
|
|
pBuf.reset();
|
|
}
|
|
}
|
|
aFile.close();
|
|
}
|
|
|
|
return pBuf;
|
|
}
|
|
|
|
static bool ImplSysWriteConfig( const OUString& rFileName,
|
|
const sal_uInt8* pBuf, sal_uInt32 nBufLen, bool rbIsUTF8BOM, sal_uInt32& rTimeStamp )
|
|
{
|
|
bool bSuccess = false;
|
|
bool bUTF8BOMSuccess = false;
|
|
|
|
::osl::File aFile( rFileName );
|
|
::osl::FileBase::RC eError = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
|
|
if( eError != ::osl::FileBase::E_None )
|
|
eError = aFile.open( osl_File_OpenFlag_Write );
|
|
if( eError == ::osl::FileBase::E_None )
|
|
{
|
|
// truncate
|
|
aFile.setSize( 0 );
|
|
sal_uInt64 nWritten;
|
|
|
|
//write the byte-order-mark 0xEF 0xBB 0xBF first , if it was UTF8 files
|
|
if ( rbIsUTF8BOM )
|
|
{
|
|
unsigned char const BOM[3] = {0xEF, 0xBB, 0xBF};
|
|
sal_uInt64 nUTF8BOMWritten;
|
|
if( aFile.write( BOM, 3, nUTF8BOMWritten ) == ::osl::FileBase::E_None && 3 == nUTF8BOMWritten )
|
|
{
|
|
bUTF8BOMSuccess = true;
|
|
}
|
|
}
|
|
|
|
if( aFile.write( pBuf, nBufLen, nWritten ) == ::osl::FileBase::E_None && nWritten == nBufLen )
|
|
{
|
|
bSuccess = true;
|
|
}
|
|
if ( rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess )
|
|
{
|
|
rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
|
|
}
|
|
}
|
|
|
|
return rbIsUTF8BOM ? bSuccess && bUTF8BOMSuccess : bSuccess;
|
|
}
|
|
|
|
namespace {
|
|
OString makeOString(const sal_uInt8* p, sal_uInt64 n)
|
|
{
|
|
if (n > SAL_MAX_INT32)
|
|
{
|
|
#ifdef _WIN32
|
|
abort();
|
|
#else
|
|
::std::abort(); //TODO: handle this gracefully
|
|
#endif
|
|
}
|
|
return OString(
|
|
reinterpret_cast< char const * >(p),
|
|
sal::static_int_cast< sal_Int32 >(n));
|
|
}
|
|
}
|
|
|
|
static void ImplMakeConfigList( ImplConfigData* pData,
|
|
const sal_uInt8* pBuf, sal_uInt64 nLen )
|
|
{
|
|
if ( !nLen )
|
|
return;
|
|
|
|
// Parse buffer and build config list
|
|
sal_uInt64 nStart;
|
|
sal_uInt64 nLineLen;
|
|
sal_uInt64 nNameLen;
|
|
sal_uInt64 nKeyLen;
|
|
sal_uInt64 i;
|
|
const sal_uInt8* pLine;
|
|
ImplKeyData* pPrevKey = nullptr;
|
|
ImplGroupData* pPrevGroup = nullptr;
|
|
ImplGroupData* pGroup = nullptr;
|
|
i = 0;
|
|
while ( i < nLen )
|
|
{
|
|
// Ctrl+Z
|
|
if ( pBuf[i] == 0x1A )
|
|
break;
|
|
|
|
// Remove spaces and tabs
|
|
while ( (pBuf[i] == ' ') || (pBuf[i] == '\t') )
|
|
i++;
|
|
|
|
// remember line-starts
|
|
nStart = i;
|
|
pLine = pBuf+i;
|
|
|
|
// search line-endings
|
|
while ( (i < nLen) && pBuf[i] && (pBuf[i] != '\r') && (pBuf[i] != '\n') &&
|
|
(pBuf[i] != 0x1A) )
|
|
i++;
|
|
|
|
nLineLen = i-nStart;
|
|
|
|
// if Line-ending is found, continue once
|
|
if ( (i+1 < nLen) &&
|
|
(pBuf[i] != pBuf[i+1]) &&
|
|
((pBuf[i+1] == '\r') || (pBuf[i+1] == '\n')) )
|
|
i++;
|
|
i++;
|
|
|
|
// evaluate line
|
|
if ( *pLine == '[' )
|
|
{
|
|
pGroup = new ImplGroupData;
|
|
pGroup->mpNext = nullptr;
|
|
pGroup->mpFirstKey = nullptr;
|
|
pGroup->mnEmptyLines = 0;
|
|
if ( pPrevGroup )
|
|
pPrevGroup->mpNext = pGroup;
|
|
else
|
|
pData->mpFirstGroup = pGroup;
|
|
pPrevGroup = pGroup;
|
|
pPrevKey = nullptr;
|
|
|
|
// filter group names
|
|
pLine++;
|
|
assert(nLineLen > 0);
|
|
nLineLen--;
|
|
// remove spaces and tabs
|
|
while ( nLineLen > 0 && (*pLine == ' ' || *pLine == '\t') )
|
|
{
|
|
nLineLen--;
|
|
pLine++;
|
|
}
|
|
nNameLen = 0;
|
|
while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') )
|
|
nNameLen++;
|
|
while ( nNameLen > 0 && (pLine[nNameLen-1] == ' ' || pLine[nNameLen-1] == '\t') )
|
|
nNameLen--;
|
|
pGroup->maGroupName = makeOString(pLine, nNameLen);
|
|
}
|
|
else
|
|
{
|
|
if ( nLineLen )
|
|
{
|
|
// If no group exists yet, add to default
|
|
if ( !pGroup )
|
|
{
|
|
pGroup = new ImplGroupData;
|
|
pGroup->mpNext = nullptr;
|
|
pGroup->mpFirstKey = nullptr;
|
|
pGroup->mnEmptyLines = 0;
|
|
pData->mpFirstGroup = pGroup;
|
|
pPrevGroup = pGroup;
|
|
pPrevKey = nullptr;
|
|
}
|
|
|
|
// if empty line, append it
|
|
if ( pPrevKey )
|
|
{
|
|
while ( pGroup->mnEmptyLines )
|
|
{
|
|
ImplKeyData* pKey = new ImplKeyData;
|
|
pKey->mbIsComment = true;
|
|
pPrevKey->mpNext = pKey;
|
|
pPrevKey = pKey;
|
|
pGroup->mnEmptyLines--;
|
|
}
|
|
}
|
|
|
|
// Generate new key
|
|
ImplKeyData* pKey = new ImplKeyData;
|
|
pKey->mpNext = nullptr;
|
|
if ( pPrevKey )
|
|
pPrevKey->mpNext = pKey;
|
|
else
|
|
pGroup->mpFirstKey = pKey;
|
|
pPrevKey = pKey;
|
|
if ( pLine[0] == ';' )
|
|
{
|
|
pKey->maValue = makeOString(pLine, nLineLen);
|
|
pKey->mbIsComment = true;
|
|
}
|
|
else
|
|
{
|
|
pKey->mbIsComment = false;
|
|
nNameLen = 0;
|
|
while ( (nNameLen < nLineLen) && (pLine[nNameLen] != '=') )
|
|
nNameLen++;
|
|
nKeyLen = nNameLen;
|
|
// Remove spaces and tabs
|
|
while ( nNameLen > 0 && (pLine[nNameLen-1] == ' ' || pLine[nNameLen-1] == '\t') )
|
|
nNameLen--;
|
|
pKey->maKey = makeOString(pLine, nNameLen);
|
|
nKeyLen++;
|
|
if ( nKeyLen < nLineLen )
|
|
{
|
|
pLine += nKeyLen;
|
|
nLineLen -= nKeyLen;
|
|
// Remove spaces and tabs
|
|
while ( (*pLine == ' ') || (*pLine == '\t') )
|
|
{
|
|
nLineLen--;
|
|
pLine++;
|
|
}
|
|
if ( nLineLen )
|
|
{
|
|
while ( (pLine[nLineLen-1] == ' ') || (pLine[nLineLen-1] == '\t') )
|
|
nLineLen--;
|
|
pKey->maValue = makeOString(pLine, nLineLen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Spaces are counted and appended only after key generation,
|
|
// as we want to store spaces even after adding new keys
|
|
if ( pGroup )
|
|
pGroup->mnEmptyLines++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::unique_ptr<sal_uInt8[]> ImplGetConfigBuffer( const ImplConfigData* pData, sal_uInt32& rLen )
|
|
{
|
|
std::unique_ptr<sal_uInt8[]> pWriteBuf;
|
|
sal_uInt8* pBuf;
|
|
sal_uInt8 aLineEndBuf[2] = {0, 0};
|
|
ImplKeyData* pKey;
|
|
ImplGroupData* pGroup;
|
|
sal_uInt32 nBufLen;
|
|
sal_uInt32 nValueLen;
|
|
sal_uInt32 nKeyLen;
|
|
sal_uInt32 nLineEndLen;
|
|
|
|
aLineEndBuf[0] = '\r';
|
|
aLineEndBuf[1] = '\n';
|
|
nLineEndLen = 2;
|
|
|
|
nBufLen = 0;
|
|
pGroup = pData->mpFirstGroup;
|
|
while ( pGroup )
|
|
{
|
|
// Don't write empty groups
|
|
if ( pGroup->mpFirstKey )
|
|
{
|
|
nBufLen += pGroup->maGroupName.getLength() + nLineEndLen + 2;
|
|
pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
nValueLen = pKey->maValue.getLength();
|
|
if ( pKey->mbIsComment )
|
|
nBufLen += nValueLen + nLineEndLen;
|
|
else
|
|
nBufLen += pKey->maKey.getLength() + nValueLen + nLineEndLen + 1;
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
|
|
// Write empty lines after each group
|
|
if ( !pGroup->mnEmptyLines )
|
|
pGroup->mnEmptyLines = 1;
|
|
nBufLen += nLineEndLen * pGroup->mnEmptyLines;
|
|
}
|
|
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
// Output buffer length
|
|
rLen = nBufLen;
|
|
if ( !nBufLen )
|
|
{
|
|
pWriteBuf.reset(new sal_uInt8[nLineEndLen]);
|
|
pWriteBuf[0] = aLineEndBuf[0];
|
|
if ( nLineEndLen == 2 )
|
|
pWriteBuf[1] = aLineEndBuf[1];
|
|
return pWriteBuf;
|
|
}
|
|
|
|
// Allocate new write buffer (caller frees it)
|
|
pWriteBuf.reset(new sal_uInt8[nBufLen]);
|
|
|
|
// fill buffer
|
|
pBuf = pWriteBuf.get();
|
|
pGroup = pData->mpFirstGroup;
|
|
while ( pGroup )
|
|
{
|
|
// Don't write empty groups
|
|
if ( pGroup->mpFirstKey )
|
|
{
|
|
*pBuf = '['; pBuf++;
|
|
memcpy( pBuf, pGroup->maGroupName.getStr(), pGroup->maGroupName.getLength() );
|
|
pBuf += pGroup->maGroupName.getLength();
|
|
*pBuf = ']'; pBuf++;
|
|
*pBuf = aLineEndBuf[0]; pBuf++;
|
|
if ( nLineEndLen == 2 )
|
|
{
|
|
*pBuf = aLineEndBuf[1]; pBuf++;
|
|
}
|
|
pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
nValueLen = pKey->maValue.getLength();
|
|
if ( pKey->mbIsComment )
|
|
{
|
|
if ( nValueLen )
|
|
{
|
|
memcpy( pBuf, pKey->maValue.getStr(), nValueLen );
|
|
pBuf += nValueLen;
|
|
}
|
|
*pBuf = aLineEndBuf[0]; pBuf++;
|
|
if ( nLineEndLen == 2 )
|
|
{
|
|
*pBuf = aLineEndBuf[1]; pBuf++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nKeyLen = pKey->maKey.getLength();
|
|
memcpy( pBuf, pKey->maKey.getStr(), nKeyLen );
|
|
pBuf += nKeyLen;
|
|
*pBuf = '='; pBuf++;
|
|
memcpy( pBuf, pKey->maValue.getStr(), nValueLen );
|
|
pBuf += nValueLen;
|
|
*pBuf = aLineEndBuf[0]; pBuf++;
|
|
if ( nLineEndLen == 2 )
|
|
{
|
|
*pBuf = aLineEndBuf[1]; pBuf++;
|
|
}
|
|
}
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
|
|
// Store empty line after each group
|
|
sal_uInt16 nEmptyLines = pGroup->mnEmptyLines;
|
|
while ( nEmptyLines )
|
|
{
|
|
*pBuf = aLineEndBuf[0]; pBuf++;
|
|
if ( nLineEndLen == 2 )
|
|
{
|
|
*pBuf = aLineEndBuf[1]; pBuf++;
|
|
}
|
|
nEmptyLines--;
|
|
}
|
|
}
|
|
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
return pWriteBuf;
|
|
}
|
|
|
|
static void ImplReadConfig( ImplConfigData* pData )
|
|
{
|
|
sal_uInt32 nTimeStamp = 0;
|
|
sal_uInt64 nRead = 0;
|
|
bool bRead = false;
|
|
bool bIsUTF8BOM = false;
|
|
std::unique_ptr<sal_uInt8[]> pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp );
|
|
|
|
// Read config list from buffer
|
|
if ( pBuf )
|
|
{
|
|
ImplMakeConfigList( pData, pBuf.get(), nRead );
|
|
pBuf.reset();
|
|
}
|
|
pData->mnTimeStamp = nTimeStamp;
|
|
pData->mbModified = false;
|
|
if ( bRead )
|
|
pData->mbRead = true;
|
|
if ( bIsUTF8BOM )
|
|
pData->mbIsUTF8BOM = true;
|
|
}
|
|
|
|
static void ImplWriteConfig( ImplConfigData* pData )
|
|
{
|
|
SAL_WARN_IF( pData->mnTimeStamp != ImplSysGetConfigTimeStamp( pData->maFileName ),
|
|
"tools.generic", "Config overwrites modified configfile: " << pData->maFileName );
|
|
|
|
// Read config list from buffer
|
|
sal_uInt32 nBufLen;
|
|
std::unique_ptr<sal_uInt8[]> pBuf = ImplGetConfigBuffer( pData, nBufLen );
|
|
if ( pBuf )
|
|
{
|
|
if ( ImplSysWriteConfig( pData->maFileName, pBuf.get(), nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) )
|
|
pData->mbModified = false;
|
|
}
|
|
else
|
|
pData->mbModified = false;
|
|
}
|
|
|
|
static void ImplDeleteConfigData( ImplConfigData* pData )
|
|
{
|
|
ImplKeyData* pTempKey;
|
|
ImplKeyData* pKey;
|
|
ImplGroupData* pTempGroup;
|
|
ImplGroupData* pGroup = pData->mpFirstGroup;
|
|
while ( pGroup )
|
|
{
|
|
pTempGroup = pGroup->mpNext;
|
|
|
|
// remove all keys
|
|
pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
pTempKey = pKey->mpNext;
|
|
delete pKey;
|
|
pKey = pTempKey;
|
|
}
|
|
|
|
// remove group and continue
|
|
delete pGroup;
|
|
pGroup = pTempGroup;
|
|
}
|
|
|
|
pData->mpFirstGroup = nullptr;
|
|
}
|
|
|
|
static std::unique_ptr<ImplConfigData> ImplGetConfigData( const OUString& rFileName )
|
|
{
|
|
std::unique_ptr<ImplConfigData> pData(new ImplConfigData);
|
|
pData->maFileName = rFileName;
|
|
pData->mpFirstGroup = nullptr;
|
|
pData->mnDataUpdateId = 0;
|
|
pData->mbRead = false;
|
|
pData->mbIsUTF8BOM = false;
|
|
ImplReadConfig( pData.get() );
|
|
|
|
return pData;
|
|
}
|
|
|
|
bool Config::ImplUpdateConfig() const
|
|
{
|
|
// Re-read file if timestamp differs
|
|
if ( mpData->mnTimeStamp != ImplSysGetConfigTimeStamp( maFileName ) )
|
|
{
|
|
ImplDeleteConfigData( mpData.get() );
|
|
ImplReadConfig( mpData.get() );
|
|
mpData->mnDataUpdateId++;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
ImplGroupData* Config::ImplGetGroup() const
|
|
{
|
|
if ( !mpActGroup || (mnDataUpdateId != mpData->mnDataUpdateId) )
|
|
{
|
|
ImplGroupData* pPrevGroup = nullptr;
|
|
ImplGroupData* pGroup = mpData->mpFirstGroup;
|
|
while ( pGroup )
|
|
{
|
|
if ( pGroup->maGroupName.equalsIgnoreAsciiCase(maGroupName) )
|
|
break;
|
|
|
|
pPrevGroup = pGroup;
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
// Add group if not exists
|
|
if ( !pGroup )
|
|
{
|
|
pGroup = new ImplGroupData;
|
|
pGroup->mpNext = nullptr;
|
|
pGroup->mpFirstKey = nullptr;
|
|
pGroup->mnEmptyLines = 1;
|
|
if ( pPrevGroup )
|
|
pPrevGroup->mpNext = pGroup;
|
|
else
|
|
mpData->mpFirstGroup = pGroup;
|
|
}
|
|
|
|
// Always inherit group names and update cache members
|
|
pGroup->maGroupName = maGroupName;
|
|
const_cast<Config*>(this)->mnDataUpdateId = mpData->mnDataUpdateId;
|
|
const_cast<Config*>(this)->mpActGroup = pGroup;
|
|
}
|
|
|
|
return mpActGroup;
|
|
}
|
|
|
|
Config::Config( const OUString& rFileName )
|
|
{
|
|
// Initialize config data
|
|
maFileName = toUncPath( rFileName );
|
|
mpData = ImplGetConfigData( maFileName );
|
|
mpActGroup = nullptr;
|
|
mnDataUpdateId = 0;
|
|
|
|
SAL_INFO("tools.generic", "Config::Config( " << maFileName << " )");
|
|
}
|
|
|
|
Config::~Config()
|
|
{
|
|
SAL_INFO("tools.generic", "Config::~Config()" );
|
|
|
|
Flush();
|
|
ImplDeleteConfigData( mpData.get() );
|
|
}
|
|
|
|
void Config::SetGroup(const OString& rGroup)
|
|
{
|
|
// If group is to be reset, it needs to be updated on next call
|
|
if ( maGroupName != rGroup )
|
|
{
|
|
maGroupName = rGroup;
|
|
mnDataUpdateId = mpData->mnDataUpdateId-1;
|
|
}
|
|
}
|
|
|
|
void Config::DeleteGroup(std::string_view rGroup)
|
|
{
|
|
// Update config data if necessary
|
|
if ( !mpData->mbRead )
|
|
{
|
|
ImplUpdateConfig();
|
|
mpData->mbRead = true;
|
|
}
|
|
|
|
ImplGroupData* pPrevGroup = nullptr;
|
|
ImplGroupData* pGroup = mpData->mpFirstGroup;
|
|
while ( pGroup )
|
|
{
|
|
if ( pGroup->maGroupName.equalsIgnoreAsciiCase(rGroup) )
|
|
break;
|
|
|
|
pPrevGroup = pGroup;
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
if ( !pGroup )
|
|
return;
|
|
|
|
// Remove all keys
|
|
ImplKeyData* pTempKey;
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
pTempKey = pKey->mpNext;
|
|
delete pKey;
|
|
pKey = pTempKey;
|
|
}
|
|
|
|
// Rewire pointers and remove group
|
|
if ( pPrevGroup )
|
|
pPrevGroup->mpNext = pGroup->mpNext;
|
|
else
|
|
mpData->mpFirstGroup = pGroup->mpNext;
|
|
delete pGroup;
|
|
|
|
// Rewrite config data
|
|
mpData->mbModified = true;
|
|
|
|
mnDataUpdateId = mpData->mnDataUpdateId;
|
|
mpData->mnDataUpdateId++;
|
|
}
|
|
|
|
OString Config::GetGroupName(sal_uInt16 nGroup) const
|
|
{
|
|
ImplGroupData* pGroup = mpData->mpFirstGroup;
|
|
sal_uInt16 nGroupCount = 0;
|
|
OString aGroupName;
|
|
while ( pGroup )
|
|
{
|
|
if ( nGroup == nGroupCount )
|
|
{
|
|
aGroupName = pGroup->maGroupName;
|
|
break;
|
|
}
|
|
|
|
nGroupCount++;
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
return aGroupName;
|
|
}
|
|
|
|
sal_uInt16 Config::GetGroupCount() const
|
|
{
|
|
ImplGroupData* pGroup = mpData->mpFirstGroup;
|
|
sal_uInt16 nGroupCount = 0;
|
|
while ( pGroup )
|
|
{
|
|
nGroupCount++;
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
return nGroupCount;
|
|
}
|
|
|
|
bool Config::HasGroup(std::string_view rGroup) const
|
|
{
|
|
ImplGroupData* pGroup = mpData->mpFirstGroup;
|
|
bool bRet = false;
|
|
|
|
while( pGroup )
|
|
{
|
|
if( pGroup->maGroupName.equalsIgnoreAsciiCase(rGroup) )
|
|
{
|
|
bRet = true;
|
|
break;
|
|
}
|
|
|
|
pGroup = pGroup->mpNext;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
OString Config::ReadKey(const OString& rKey) const
|
|
{
|
|
return ReadKey(rKey, OString());
|
|
}
|
|
|
|
OString Config::ReadKey(const OString& rKey, const OString& rDefault) const
|
|
{
|
|
SAL_INFO("tools.generic", "Config::ReadKey( " << rKey << " ) from " << GetGroup()
|
|
<< " in " << maFileName);
|
|
|
|
// Search key, return value if found
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( pGroup )
|
|
{
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
|
|
return pKey->maValue;
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
}
|
|
|
|
return rDefault;
|
|
}
|
|
|
|
void Config::WriteKey(const OString& rKey, const OString& rStr)
|
|
{
|
|
SAL_INFO("tools.generic", "Config::WriteKey( " << rKey << ", " << rStr << " ) to "
|
|
<< GetGroup() << " in " << maFileName);
|
|
|
|
// Update config data if necessary
|
|
if ( !mpData->mbRead )
|
|
{
|
|
ImplUpdateConfig();
|
|
mpData->mbRead = true;
|
|
}
|
|
|
|
// Search key and update value if found
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( !pGroup )
|
|
return;
|
|
|
|
ImplKeyData* pPrevKey = nullptr;
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
|
|
break;
|
|
|
|
pPrevKey = pKey;
|
|
pKey = pKey->mpNext;
|
|
}
|
|
|
|
bool bNewValue;
|
|
if ( !pKey )
|
|
{
|
|
pKey = new ImplKeyData;
|
|
pKey->mpNext = nullptr;
|
|
pKey->maKey = rKey;
|
|
pKey->mbIsComment = false;
|
|
if ( pPrevKey )
|
|
pPrevKey->mpNext = pKey;
|
|
else
|
|
pGroup->mpFirstKey = pKey;
|
|
bNewValue = true;
|
|
}
|
|
else
|
|
bNewValue = pKey->maValue != rStr;
|
|
|
|
if ( bNewValue )
|
|
{
|
|
pKey->maValue = rStr;
|
|
|
|
mpData->mbModified = true;
|
|
}
|
|
}
|
|
|
|
void Config::DeleteKey(std::string_view rKey)
|
|
{
|
|
// Update config data if necessary
|
|
if ( !mpData->mbRead )
|
|
{
|
|
ImplUpdateConfig();
|
|
mpData->mbRead = true;
|
|
}
|
|
|
|
// Search key and update value
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( !pGroup )
|
|
return;
|
|
|
|
ImplKeyData* pPrevKey = nullptr;
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment && pKey->maKey.equalsIgnoreAsciiCase(rKey) )
|
|
break;
|
|
|
|
pPrevKey = pKey;
|
|
pKey = pKey->mpNext;
|
|
}
|
|
|
|
if ( pKey )
|
|
{
|
|
// Rewire group pointers and delete
|
|
if ( pPrevKey )
|
|
pPrevKey->mpNext = pKey->mpNext;
|
|
else
|
|
pGroup->mpFirstKey = pKey->mpNext;
|
|
delete pKey;
|
|
|
|
mpData->mbModified = true;
|
|
}
|
|
}
|
|
|
|
sal_uInt16 Config::GetKeyCount() const
|
|
{
|
|
SAL_INFO("tools.generic", "Config::GetKeyCount() from " << GetGroup() << " in " << maFileName);
|
|
|
|
// Search key and update value
|
|
sal_uInt16 nCount = 0;
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( pGroup )
|
|
{
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment )
|
|
nCount++;
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
OString Config::GetKeyName(sal_uInt16 nKey) const
|
|
{
|
|
SAL_INFO("tools.generic", "Config::GetKeyName( " << OString::number(static_cast<sal_Int32>(nKey))
|
|
<< " ) from " << GetGroup() << " in " << maFileName);
|
|
|
|
// search key and return name if found
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( pGroup )
|
|
{
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment )
|
|
{
|
|
if ( !nKey )
|
|
return pKey->maKey;
|
|
nKey--;
|
|
}
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
}
|
|
|
|
return OString();
|
|
}
|
|
|
|
OString Config::ReadKey(sal_uInt16 nKey) const
|
|
{
|
|
SAL_INFO("tools.generic", "Config::ReadKey( " << OString::number(static_cast<sal_Int32>(nKey))
|
|
<< " ) from " << GetGroup() << " in " << maFileName);
|
|
|
|
// Search key and return value if found
|
|
ImplGroupData* pGroup = ImplGetGroup();
|
|
if ( pGroup )
|
|
{
|
|
ImplKeyData* pKey = pGroup->mpFirstKey;
|
|
while ( pKey )
|
|
{
|
|
if ( !pKey->mbIsComment )
|
|
{
|
|
if ( !nKey )
|
|
return pKey->maValue;
|
|
nKey--;
|
|
}
|
|
|
|
pKey = pKey->mpNext;
|
|
}
|
|
}
|
|
|
|
return OString();
|
|
}
|
|
|
|
void Config::Flush()
|
|
{
|
|
if ( mpData->mbModified )
|
|
ImplWriteConfig( mpData.get() );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|