/* -*- 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 <limits>
#include <new>
#include <string.h>
#ifdef _WIN32
#include <stdlib.h>
#endif
#include <osl/file.hxx>
#include <tools/stream.hxx>
#include <tools/config.hxx>
#include <osl/security.h>
#include <rtl/strbuf.hxx>
#include <sal/log.hxx>
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;
LineEnd meLineEnd;
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 sal_uInt8* ImplSysReadConfig( const OUString& rFileName,
sal_uInt64& rRead, bool& rbRead, bool& rbIsUTF8BOM, sal_uInt32& rTimeStamp )
{
sal_uInt8* pBuf = nullptr;
::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 = new sal_uInt8[static_cast< std::size_t >(nPos)];
sal_uInt64 nRead = 0;
if( aFile.read( pBuf, 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, BOM, 3) == 0)
{
nRead -= 3;
memmove(pBuf, pBuf + 3, sal::static_int_cast<std::size_t>(nRead * sizeof(sal_uInt8)) );
rbIsUTF8BOM = true;
}
rTimeStamp = ImplSysGetConfigTimeStamp( rFileName );
rbRead = true;
rRead = nRead;
}
else
{
delete[] pBuf;
pBuf = nullptr;
}
}
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;
ImplKeyData* pKey;
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;
pKey = nullptr;
// filter group names
pLine++;
nLineLen--;
// remove spaces and tabs
while ( (*pLine == ' ') || (*pLine == '\t') )
{
nLineLen--;
pLine++;
}
nNameLen = 0;
while ( (nNameLen < nLineLen) && (pLine[nNameLen] != ']') )
nNameLen++;
if ( nNameLen )
{
while ( (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 )
{
pKey = new ImplKeyData;
pKey->mbIsComment = true;
pPrevKey->mpNext = pKey;
pPrevKey = pKey;
pGroup->mnEmptyLines--;
}
}
// Generate new key
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
if ( nNameLen )
{
while ( (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 sal_uInt8* ImplGetConfigBuffer( const ImplConfigData* pData, sal_uInt32& rLen )
{
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;
if ( pData->meLineEnd == LINEEND_CR )
{
aLineEndBuf[0] = '\r';
nLineEndLen = 1;
}
else if ( pData->meLineEnd == LINEEND_LF )
{
aLineEndBuf[0] = '\n';
nLineEndLen = 1;
}
else
{
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 = new sal_uInt8[nLineEndLen];
if ( pWriteBuf )
{
pWriteBuf[0] = aLineEndBuf[0];
if ( nLineEndLen == 2 )
pWriteBuf[1] = aLineEndBuf[1];
return pWriteBuf;
}
else
return nullptr;
}
// Allocate new write buffer (caller frees it)
pWriteBuf = new sal_uInt8[nBufLen];
if ( !pWriteBuf )
return nullptr;
// fill buffer
pBuf = pWriteBuf;
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;
sal_uInt8* pBuf = ImplSysReadConfig( pData->maFileName, nRead, bRead, bIsUTF8BOM, nTimeStamp );
// Read config list from buffer
if ( pBuf )
{
ImplMakeConfigList( pData, pBuf, nRead );
delete[] pBuf;
}
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;
sal_uInt8* pBuf = ImplGetConfigBuffer( pData, nBufLen );
if ( pBuf )
{
if ( ImplSysWriteConfig( pData->maFileName, pBuf, nBufLen, pData->mbIsUTF8BOM, pData->mnTimeStamp ) )
pData->mbModified = false;
delete[] pBuf;
}
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->meLineEnd = LINEEND_CRLF;
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(const OString& 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 )
{
// 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(const OString& 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 )
{
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(const OString& rKey)
{
// Update config data if necessary
if ( !mpData->mbRead )
{
ImplUpdateConfig();
mpData->mbRead = true;
}
// Search key and update value
ImplGroupData* pGroup = ImplGetGroup();
if ( pGroup )
{
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: */
↑ V557 Array overrun is possible. The '1' index is pointing beyond array bound.
↑ V668 There is no sense in testing the 'pWriteBuf' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.
↑ V668 There is no sense in testing the 'pWriteBuf' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.