//------------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1998 by Yura Bidus
//
/// \file
/// This source file implements TConfigFile,TIniConfigFile,TRegConfigFile and
/// TConfigFileSection.
//------------------------------------------------------------------------------
#include <owl/pch.h>
//#pragma hdrstop
#include <owl/configfl.h>
#include <owl/file.h>
#include <owl/date.h>
#include <owl/time.h>
#include <owl/template.h>
#include <owl/pointer.h>
#include <owl/wsyscls.h>
#include <owl/color.h>
#include <owl/private/strmdefs.h>
#include <algorithm>
#if defined WINELIB
//#include <wine/msvcrt/stdlib.h> //for _ltoa, _itoa
#endif
using namespace std;
namespace owl {
static const tchar szNO[] = _T("no");
static const tchar szOFF[] = _T("off");
static const tchar szZERO[] = _T("0");
static const tchar szFALSE[] = _T("false");
static const tchar szYES[] = _T("yes");
static const tchar szON[] = _T("on");
static const tchar szONE[] = _T("1");
static const tchar szTRUE[] = _T("true");
//static const tchar szDef[] = _T("!)@(#*$&%^");
static const tchar szSep[] = _T("\\");
int TConfigFile::DefaultPrecision = -1;
namespace {
auto SkipWhitespace_(LPCTSTR sz) -> LPCTSTR
{
PRECONDITION(sz);
while (*sz == _T(' ') || *sz == _T('\t'))
++sz;
return sz;
}
} // anonymous namespace
//
/// This static method converts a string to a bool, accepting most common
/// boolean keywords: yes, on, true and 1.
//
bool TConfigFile::StringToBool(LPCTSTR str, bool *pVal)
{
PRECONDITION(str && pVal);
if ((_tcsicmp(str,szNO)==0) ||
(_tcsicmp(str,szOFF)==0) ||
(_tcsicmp(str,szZERO)==0) ||
(_tcsicmp(str,szFALSE)==0)
){
*pVal = false;
return true;
}
if ((_tcsicmp(str,szYES)==0) ||
(_tcsicmp(str,szON)==0) ||
(_tcsicmp(str,szONE)==0) ||
(_tcsicmp(str,szTRUE)==0)
){
*pVal = true;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////
/// This static method returns yes or no given a boolean value.
//
tstring TConfigFile::BoolToString(bool b)
{
return tstring(b ? szYES : szNO);
}
void TConfigFile::LoadValues(const TConfigFile& file)
{
//
// Checks if string (up to newline or EOS) is digits.
//
const auto isInteger = [](LPCTSTR text) -> bool
{
PRECONDITION(text);
const auto isDigit = [](tchar c) -> bool
{
return _istdigit(c) || (_T('a') <= c && c <= _T('f')) || (_T('A') <= c && c <= _T('F'));
};
auto p = SkipWhitespace_(text); // Ignore leading whitespace.
if (!isDigit(*p))
return false;
else
{
do ++p; while (isDigit(*p));
p = SkipWhitespace_(p); // Ignore trailing whitespace.
return *p == _T('\n') || *p == _T('\0');
}
};
const auto nextName = [](LPTSTR p)
{
PRECONDITION(p);
while (*p != _T('\0'))
++p;
return ++p;
};
tchar text[MAX_PATH];
TAPointer<tchar> _ClnObj1,_ClnObj2;
tchar *sections, *entries;
int sect_size = 4096;
int size;
do{
_ClnObj1 = sections = new tchar[sect_size];
size = file.ReadSections(sections, sect_size);
sect_size += 1024;
}
while(size >= (sect_size-2));
// while sections not '\0'
while(*sections){
sect_size = 4096;
do{
_ClnObj2 = entries = new tchar[sect_size];
size = file.ReadSection(sections, entries, sect_size);
sect_size += 1024;
}
while(size >= (sect_size-2));
//2. Read all values
while(*entries){
//?? where difference between int and text entries????
int size = file.ReadString(sections,entries,text,MAX_PATH,_T(""));
// if size ==0 or strlen == 0 -> error
if(!size || !::_tcslen(text) || isInteger(text)){
size = file.ReadInteger(sections,entries, INT_MAX);
if(size < INT_MAX)
WriteInteger(sections,entries, size);
}
else
WriteString(sections,entries, text);
//get next entry name
entries = nextName(entries);
}
//get next section
sections = nextName(sections);
}
}
//
/// This method reads binary data.
//
bool TConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
TAPointer<tchar> __ClnObj(new tchar[bufSize * 3 + 1]);
LPTSTR text = __ClnObj;
if (!ReadString(section, entry, text, bufSize * 3 + 1, _T("")))
return false;
if(!::_tcslen(text))
return false;
uint8* data = (uint8*)buffer;
for (uint i = 0; i < bufSize; i++) {
int n; // string in form 'XX '
if(_stscanf(text, _T("%2x"), &n) == EOF)
return true;
*data++ = (uint8)n;
text += 3;
}
return true;
}
//
/// This method writes binary data.
//
bool TConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
PRECONDITION(section && entry && buffer);
TAPointer<tchar> __ClnObj(new tchar[bufSize * 3 + 1]);
LPTSTR text = __ClnObj;
uint8* data = (uint8*)buffer;
for (uint i = 0; i < bufSize; i++) {
_stprintf(text, _T("%02X "), (int)*data++);
text += 3;
}
return WriteString(section, entry, __ClnObj);
}
//
/// This method reads an integer.
//
int TConfigFile::ReadInteger(LPCTSTR section, LPCTSTR entry, int defval) const
{
PRECONDITION(section && entry);
tchar text [20];
LPTSTR end;
if (!ReadString(section, entry, text, COUNTOF(text), _T("")))
return defval;
int retval = (int)_tcstol(text, &end, 10);
if (*end)
return retval;
return retval;
}
//
/// This method writes an integer. We format it as a string.
//
bool TConfigFile::WriteInteger(LPCTSTR section, LPCTSTR entry, int value)
{
PRECONDITION(section && entry);
tchar text [20];
#if defined __GNUC__
#if defined UNICODE
snwprintf(text, 20, _T("%d"), value);
#else
snprintf(text, 20, "%d", value);
#endif
#else
_ltot (value, text, 10);
#endif
return WriteString(section, entry, text);
}
//
//
//
int TConfigFile::ReadSection(const tstring& section, tstring& buffer) const
{
TAPointer<tchar> _ClnObj1;
tchar *sections;
int sect_size = 4096;
int size;
do{
_ClnObj1 = sections = new tchar[sect_size];
size = ReadSection(section.c_str(),sections, sect_size);
sect_size += 1024;
}
while(size >= (sect_size-2));
buffer = sections;
return size;
}
//
//
//
int TConfigFile::ReadSections(tstring& buffer) const
{
TAPointer<tchar> _ClnObj1;
tchar *sections;
int sect_size = 4096;
int size;
do{
_ClnObj1 = sections = new tchar[sect_size];
size = ReadSections(sections, sect_size);
sect_size += 1024;
}
while(size >= (sect_size-2));
buffer = sections;
return size;
}
//
/// String-aware overload.
//
uint TConfigFile::ReadString(const tstring& section, const tstring& entry, tstring& buffer, LPCTSTR defstr) const
{
auto result = tstring(MAX_PATH, _T('\0')); // String length; space for an additional null-terminator is guarantied (since C++11).
const auto resultSize = static_cast<uint>(result.size() + 1); // Size in character count, including null-terminator.
const auto p = const_cast<LPTSTR>(result.data()); // Workaround for pre-C++17 compilers.
const auto n = ReadString(section.c_str(), entry.c_str(), p, resultSize, defstr);
result.resize(n);
result.shrink_to_fit();
swap(buffer, result);
return n;
}
//
//
//
double TConfigFile::ReadDouble(LPCTSTR section, LPCTSTR entry, double defval) const
{
PRECONDITION(section && entry);
double retval = defval;
tchar* end;
tchar text[80];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if (::_tcslen(text)){
retval = _tcstod(text, &end);
if(*end)
retval = defval;
}
return retval;
}
//
/// This method writes an integer.
//
bool TConfigFile::WriteDouble(LPCTSTR section, LPCTSTR entry, double val)
{
PRECONDITION(section && entry);
tchar text [80];
//
// We request a formatted number with a LOT of significant digits:
//
if(DefaultPrecision < 0)
_stprintf(text, _T("%1.20Lg"), (long double)val);
else{
if(DefaultPrecision==0)
_stprintf(text, _T("%Lg"), (long double)val);
else{
tstring format = _T("%1.");
#if defined __GNUC__
#if defined UNICODE
snwprintf(text, 10, _T("%d"), DefaultPrecision);
#else
snprintf(text, 10, "%d", DefaultPrecision);
#endif
format += text;
#else
format += _itot(DefaultPrecision, text, 10);
#endif
format += _T("Lg");
_stprintf(text, format.c_str(), (long double)val);
}
}
return WriteString(section, entry, text);
}
//
/// This method reads a boolean value.
//
bool TConfigFile::ReadBool(LPCTSTR section, LPCTSTR entry, bool defval) const
{
PRECONDITION(section && entry);
tchar text[20];
ReadString(section, entry, text, COUNTOF(text), _T(""));
bool bVal;
if (::_tcslen(text)){
bool b = StringToBool(text, &bVal);
if (b)
return bVal;
}
return defval;
}
//
/// This method writes a bool.
//
bool TConfigFile::WriteBool (LPCTSTR section, LPCTSTR entry, bool val)
{
PRECONDITION(section && entry);
return WriteString(section, entry, BoolToString(val).c_str());
}
//
//
//
bool TConfigFile::ReadDate(LPCTSTR section, LPCTSTR entry, TDate& value) const
{
PRECONDITION(section && entry);
tstring text;
ReadString(section, entry, text, _T(""));
if(text.length() > 0)
{
tistringstream strtemp(text);
value = TDate((const tistream&)strtemp);
return true;
}
return false;
}
//
//
//
bool TConfigFile::WriteDate(LPCTSTR section, LPCTSTR entry, const TDate& val)
{
PRECONDITION(section && entry);
tostringstream strtemp;
TDate::HowToPrint oldOpt = TDate::SetPrintOption(TDate::Numbers);
strtemp << val.AsString().c_str();
TDate::SetPrintOption(oldOpt);
return WriteString(section, entry, strtemp.str());
}
//
//
//
bool TConfigFile::ReadSystemTime(LPCTSTR section, LPCTSTR entry, TSystemTime& val) const
{
PRECONDITION(section && entry);
tchar text[80];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if(::_tcslen(text)){
SYSTEMTIME dt;
_stscanf(text,_T("%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu"),&dt.wYear,&dt.wMonth,&dt.wDayOfWeek,
&dt.wDay,&dt.wHour,&dt.wMinute,&dt.wSecond,&dt.wMilliseconds);
val = TSystemTime(dt);
return true;
}
return false;
}
//
//
//
bool TConfigFile::WriteSystemTime(LPCTSTR section, LPCTSTR entry, const TSystemTime& dt)
{
PRECONDITION(section && entry);
tchar text[80];
wsprintf(text,_T("%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu"),dt.wYear,dt.wMonth,dt.wDayOfWeek,
dt.wDay,dt.wHour,dt.wMinute,dt.wSecond,dt.wMilliseconds);
return WriteString(section, entry, text);
}
//
//
//
bool TConfigFile::ReadTime(LPCTSTR section, LPCTSTR entry, TTime& val) const
{
PRECONDITION(section && entry);
tchar text[80];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if(::_tcslen(text)){
int h,m,s;
_stscanf(text,_T("h=%d m=%d s=%d"), &h, &m, &s);
val = TTime(h,m,s);
return true;
}
return false;
}
//
//
//
bool TConfigFile::WriteTime(LPCTSTR section, LPCTSTR entry, TTime& val)
{
PRECONDITION(section && entry);
tchar text[80];
wsprintf(text,_T("h=%d m=%d s=%d"),val.Hour(),val.Minute(),val.Second());
return WriteString(section, entry, text);
}
//
//
/// This method will take a profile string of the form:
/// \code
/// +---------------- lfHeight
/// | +------------- lfWidth
/// | | /lfWeight
/// | | +------- / lfItalic
/// | | | \ lfUnderline
/// | | | \lfStrikeOut
/// | | | +----- lfEscapement
/// | | | | +-- lfOrientation
/// | | | | |
/// v v v v v
/// 10, 8, BIU, 0, 0, 0, 0, 0, 0, 0, Times New Roman
/// ^ ^ ^ ^ ^ ^
/// | | | | | |
/// lfCharSet -+ | | | | |
/// lfOutPrecision ----+ | | | |
/// lfClipPrecision -------+ | | |
/// lfQuality ----------+ | |
/// lfPitchAndFamily -------------+ |
/// lfFaceName ----------------+
/// \endcode
/// and fills in a logfont structure.
//
bool TConfigFile::ReadFont(LPCTSTR section, LPCTSTR entry, LOGFONT& pFont) const
{
PRECONDITION(section && entry);
tchar text[MAX_PATH];
ReadString(section, entry, text, MAX_PATH, _T(""));
if(!::_tcslen(text))
return false;
LPCTSTR szSep = _T(", \t");
bool b;
tchar* sz;
LOGFONT lf;
int i, n;
_tcscat(text,_T("\n"));
sz = _tcstok(text, szSep);
n = 0;
while (sz){
i = _ttoi(sz); // not needed all the time, but oh wel...
switch (n++){
// int lfHeight;
case 0:
lf.lfHeight = i;
break;
// int lfWidth;
case 1:
lf.lfWidth = i;
break;
// int lfWeight;
// BYTE lfItalic;
// BYTE lfUnderline;
// BYTE lfStrikeOut;
case 2:
lf.lfWeight = FW_NORMAL;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = FALSE;
if (_tcschr(sz, _T('B')))
lf.lfWeight = FW_BOLD;
if (_tcschr(sz, _T('I')))
lf.lfItalic = TRUE;
if (_tcschr(sz, _T('U')))
lf.lfUnderline = TRUE;
if (_tcschr(sz, _T('S')))
lf.lfStrikeOut = TRUE;
break;
// int lfEscapement;
case 3:
lf.lfEscapement = i;
break;
// int lfOrientation;
case 4:
lf.lfOrientation = i;
break;
// BYTE lfCharSet;
case 5:
lf.lfCharSet = uint8(i);
break;
// BYTE lfOutPrecision;
case 6:
lf.lfOutPrecision = uint8(i);
break;
// BYTE lfClipPrecision;
case 7:
lf.lfClipPrecision = uint8(i);
break;
// BYTE lfQuality;
case 8:
lf.lfQuality = uint8(i);
break;
// BYTE lfPitchAndFamily;
case 9:
lf.lfPitchAndFamily = uint8(i);
break;
// BYTE lfFaceName[LF_FACESIZE];
case 10:
::_tcscpy(lf.lfFaceName, sz);
break;
}
if (n==10)
sz = _tcstok(0, _T("\n"));
else
sz = _tcstok(0, szSep);
}
b = (n == 11);
if(b)
pFont = lf;
return b;
}
//
//
/// This method writes a font description in the following format:
/// \code
/// +---------------- lfHeight
/// | +------------- lfWidth
/// | | /lfWeight
/// | | +------- / lfItalic
/// | | | \ lfUnderline
/// | | | \lfStrikeOut
/// | | | +----- lfEscapement
/// | | | | +-- lfOrientation
/// | | | | |
/// v v v v v
/// 10, 8, BIU, 0, 0, 0, 0, 0, 0, 0, Times New Roman
/// ^ ^ ^ ^ ^ ^
/// | | | | | |
/// lfCharSet -+ | | | | |
/// lfOutPrecision ----+ | | | |
/// lfClipPrecision -------+ | | |
/// lfQuality ----------+ | |
/// lfPitchAndFamily -------------+ |
/// lfFaceName ----------------+
/// \endcode
bool TConfigFile::WriteFont (LPCTSTR section, LPCTSTR entry, const LOGFONT& logFont)
{
PRECONDITION(section && entry);
tstring s;
tchar ach [40];
int i;
tchar comma[] = _T(",");
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfHeight);
s = ach;
#else
s = _itot(logFont.lfHeight, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfWidth);
s += ach;
#else
s += _itot(logFont.lfWidth, ach, 10);
#endif
s += comma;
i = 0;
ach[i++] = (logFont.lfWeight < FW_BOLD) ? _T('N') : _T('B');
if (logFont.lfItalic)
ach[i++] = _T('I');
if (logFont.lfUnderline)
ach[i++] = _T('U');
if (logFont.lfStrikeOut)
ach[i++] = _T('S');
ach[i] = 0;
s += ach;
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfEscapement);
s += ach;
#else
s += _itot(logFont.lfEscapement, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfOrientation);
s += ach;
#else
s += _itot(logFont.lfOrientation, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfCharSet);
s += ach;
#else
s += _itot(logFont.lfCharSet, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfOutPrecision);
s += ach;
#else
s += _itot(logFont.lfOutPrecision, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfClipPrecision);
s += ach;
#else
s += _itot(logFont.lfClipPrecision, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfQuality);
s += ach;
#else
s += _itot(logFont.lfQuality, ach, 10);
#endif
s += comma;
#if defined __GNUC__
_tprintf(ach, _T("%d"), logFont.lfPitchAndFamily);
s += ach;
#else
s += _itot(logFont.lfPitchAndFamily, ach, 10);
#endif
s += comma;
s += logFont.lfFaceName;
return WriteString(section, entry, s.c_str());
}
//
/// This method reads an RGB color.
//
bool TConfigFile::ReadColor(LPCTSTR section, LPCTSTR entry, TColor& color) const
{
PRECONDITION(section && entry);
const auto skipPrefix = [](LPCTSTR sz, tchar ch) -> LPCTSTR
{
PRECONDITION(sz);
sz = SkipWhitespace_(sz);
if (_totupper(*sz) == ch)
{
++sz;
sz = SkipWhitespace_(sz);
if (*sz == _T('='))
sz = SkipWhitespace_(sz + 1);
}
return sz;
};
tchar text[40];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if(!::_tcslen(text))
return false;
TColor clr;
tchar* sz[3];
bool b;
b = false;
sz[0] = text;
sz[1] = _tcschr(sz[0], _T(','));
if(sz[1]){
*(sz[1]++) = 0;
sz[2] = _tcschr(sz[1], _T(','));
if (sz[2]){
*(sz[2]++) = 0;
int R = _ttoi(skipPrefix(sz[0], _T('R')));
int G = _ttoi(skipPrefix(sz[1], _T('G')));
int B = _ttoi(skipPrefix(sz[2], _T('B')));
if (R >= 0 && R < 256 &&
G >= 0 && G < 256 &&
B >= 0 && B < 256){
clr = RGB(R,G,B);
b = true;
}
}
}
if(b)
color = clr;
return b;
}
//
/// This method writes an RGB color
//
bool TConfigFile::WriteColor(LPCTSTR section, LPCTSTR entry, const TColor& clr)
{
PRECONDITION(section && entry);
tchar ach[40];
wsprintf (ach, _T("R=%d,G=%d,B=%d"), clr.Red(), clr.Green(), clr.Blue());
return WriteString(section, entry, ach);
}
//
// This method reads an X,Y point.
//
bool TConfigFile::ReadPoint (LPCTSTR section, LPCTSTR entry, TPoint& point) const
{
PRECONDITION(section && entry);
tchar text[40];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if(!::_tcslen(text))
return false;
LPCTSTR sz;
bool b = false;
int array[2], i;
sz = text;
for ( i = 0; i < 2; i++){
sz = _tcschr (sz, _T('='));
if(! sz)
break;
sz++;
if(!_istdigit(*sz))
break;
array[i] = _ttoi(sz);
}
if (i == 2){
b = true;
point.x = array[0];
point.y = array[1];
}
return b;
}
//
/// This method writes a TPoint.
//
bool TConfigFile::WritePoint (LPCTSTR section, LPCTSTR entry, const TPoint& point)
{
PRECONDITION(section && entry);
tchar buf[20];
wsprintf(buf, _T("X=%d,Y=%d"), point.x, point.y);
return WriteString(section, entry, buf);
}
//
/// This method reads a rectangle.
//
bool TConfigFile::ReadRect (LPCTSTR section, LPCTSTR entry, TRect& rect) const
{
PRECONDITION(section && entry);
tchar text[40];
ReadString(section, entry, text, COUNTOF(text), _T(""));
if(!::_tcslen(text))
return false;
LPCTSTR sz;
bool b = false;
int nArray[4], i;
sz = text;
for(i = 0; i < 4; i++){
sz = _tcschr(sz, _T('='));
if (!sz)
break;
sz++;
if(!_istdigit(*sz))
break;
nArray[i] = _ttoi(sz);
}
if(i == 4){
b = true;
rect.left = nArray[0];
rect.top = nArray[1];
rect.right = nArray[0] + nArray[2];
rect.bottom = nArray[1] + nArray[3];
}
return b;
}
//
/// This method writes a rectangle.
//
bool TConfigFile::WriteRect (LPCTSTR section, LPCTSTR entry, const TRect& r)
{
PRECONDITION(section && entry);
tchar buf [40];
wsprintf (buf, _T("L=%d,T=%d,W=%d,H=%d"), r.left, r.top, r.Width(), r.Height());
return WriteString(section, entry, buf);
}
//
/// This method writes a TSize.
//
bool TConfigFile::WriteSize (LPCTSTR section, LPCTSTR entry, const TSize& size)
{
PRECONDITION(section && entry);
tchar buf [80];
wsprintf(buf, _T("W=%d,H=%d"), size.cx, size.cy);
return WriteString(section, entry, buf);
}
//
bool TConfigFile::SectionExists(LPCTSTR section) const
{
PRECONDITION(section);
tchar buff[3];
return ReadSection(section, buff, 3) != 0;
}
///////////////////////////////////////////////////////////////////////////////////////
//
// class TIniConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
//
uint TIniConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
PRECONDITION(section && buffer && bufSize > 1);
return GetPrivateProfileString(section, 0, _T(""), buffer, bufSize, FileName.c_str());
}
//
uint TIniConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
PRECONDITION(buffer && bufSize > 1);
return GetPrivateProfileString(0, 0, _T(""), buffer, bufSize,FileName.c_str());
}
//
/// This method deletes the specified section.
//
bool TIniConfigFile::EraseSection (LPCTSTR section)
{
PRECONDITION(section);
return ::WritePrivateProfileString(section, 0, 0, FileName.c_str()) != 0;
}
//
//
//
/// This method deletes the specified entry.
//
bool TIniConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
PRECONDITION(section && entry);
return ::WritePrivateProfileString (section, entry, 0, FileName.c_str()) != 0;
}
//
//
//
void TIniConfigFile::UpdateFile()
{
::WritePrivateProfileString(0, 0, 0, FileName.c_str());
}
//
/// This method is the base method for reading strings.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TIniConfigFile::ReadString (LPCTSTR section, LPCTSTR entry,
LPTSTR buffer, uint bufSize, LPCTSTR def) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
return ::GetPrivateProfileString (section, entry, def?def:_T(""), buffer, bufSize, FileName.c_str());
}
//
/// This method is the low-level method for writing strings.
//
bool TIniConfigFile::WriteString (LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
PRECONDITION(section && entry);
return ::WritePrivateProfileString (section, entry, value, FileName.c_str()) != 0;
}
//
/// This method reads binary data.
//
bool TIniConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
return ::GetPrivateProfileStruct(section, entry, buffer, bufSize, FileName.c_str()) != 0;
}
//
/// This method writes binary data.
//
bool TIniConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
PRECONDITION(section && entry && buffer);
return ::WritePrivateProfileStruct(section, entry, buffer, bufSize, FileName.c_str()) != 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
/// Container support
//
class TStringMapNode: public TMapNode<tstring,tstring> {
public:
TStringMapNode(const tstring& str, const tstring& val)
: TMapNode<tstring,tstring>() {Name = str; Value = val;}
TStringMapNode(const tstring& str)
: TMapNode<tstring,tstring>() {Name = str;}
};
//
typedef TSortedObjectArray<TStringMapNode> TStringMap;
//
class TStringNode: public TMapNode<tstring,TStringMap> {
public:
TStringNode(const tstring& str)
: TMapNode<tstring,TStringMap>(){Name = str;}
};
//
class TMemConfigFileSections: public TIPtrArray<TStringNode*> {
public:
TMemConfigFileSections(){}
~TMemConfigFileSections(){}
bool DestroyItem(TStringNode* item);
bool Destroy(int loc);
int Add(TStringNode* item);
bool HasMember(TStringNode* item) const;
int Find(TStringNode* item) const;
};
//
bool TMemConfigFileSections::HasMember(TStringNode* item) const
{
return Find(item) != NPOS;
}
//
bool TMemConfigFileSections::DestroyItem(TStringNode* item)
{
const auto index = Find(item);
if (index != NPOS)
{
TStringNode* item = Data[index];
Remove(index);
delete item;// using global delete because we didn't allocate it
return true;
}
return false;
}
//
bool TMemConfigFileSections::Destroy(int loc)
{
if(loc < (int)Size()){
TStringNode* item = Data[loc];
Remove(loc);
delete item;// using global delete because we didn't allocate it
return true;
}
return false;
}
//
int TMemConfigFileSections::Add(TStringNode* item)
{
if(ItemCnt>=Reserved)
Resize(ItemCnt+1); // on error -> throw xalloc
unsigned loc = ItemCnt++;
while( loc > 0 && *item < *Data[loc-1]) {
Data[loc] = Data[loc-1];
loc--;
}
Data[loc] = item;
return loc;
}
//
int TMemConfigFileSections::Find(TStringNode* item) const
{
auto lower = 0;
auto upper = static_cast<int>(Size()) - 1;
while (lower <= upper)
{
const auto middle = (lower + upper) / 2;
if (*Data[middle] == *item)
return middle;
if (*Data[middle] < *item)
lower = middle + 1;
else
upper = middle - 1;
}
return NPOS;
}
///////////////////////////////////////////////////////////////////////
//
// class TMemConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
TMemConfigFile::TMemConfigFile(const tstring& fileName)
:
TConfigFile(fileName)
{
Sections = new TMemConfigFileSections();
}
//
TMemConfigFile::~TMemConfigFile()
{
Flush();
delete Sections;
}
//
void TMemConfigFile::Flush()
{
Sections->Flush();
}
//
void TMemConfigFile::LoadValues(const TConfigFile& file)
{
Flush();
TConfigFile::LoadValues(file);
}
//
int TMemConfigFile::AddSection(const tstring& section)
{
return Sections->Add(new TStringNode(section));
}
//
bool TMemConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
PRECONDITION(section && entry);
TStringNode sn(section);
const auto index = Sections->Find(&sn);
if (index != Sections->NPOS)
{
return (*Sections)[index]->Value.DestroyItem(TStringMapNode(entry));
}
return false;
}
//
bool TMemConfigFile::EraseSection(LPCTSTR section)
{
PRECONDITION(section);
TStringNode sn(section);
return Sections->DestroyItem(&sn);
}
//
/// Copies all value names of the given section to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TMemConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
PRECONDITION(section && buffer && bufSize > 1);
tchar* p = buffer;
*p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
TStringNode sn(section);
const auto k = Sections->Find(&sn);
if (k == Sections->NPOS) return 0;
const TStringMap& map = (*Sections)[k]->Value;
if (map.Empty()) return 0;
for (uint i = 0; i != map.Size(); ++i)
{
const tstring& s = map[i].Name;
size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
if (m == 0) break; // We've run out of space.
size_t n = min(s.size(), m - 1);
::_tcsncpy(p, s.c_str(), n);
p += n;
*p++ = _T('\0');
}
// Add buffer terminator.
//
*p++ = _T('\0');
return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
//
/// Copies all section names to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TMemConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
PRECONDITION(buffer && bufSize > 1);
tchar* p = buffer;
*p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
if (Sections->Empty()) return 0;
for (uint i = 0; i != Sections->Size(); ++i)
{
const tstring& s = (*Sections)[i]->Name;
size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
if (m == 0) break; // We've run out of space.
size_t n = min(s.size(), m - 1);
::_tcsncpy(p, s.c_str(), n);
p += n;
*p++ = _T('\0');
}
// Add buffer terminator.
//
*p++ = _T('\0');
return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
//
/// Looks up the value of the given entry of the given section, and if found, copies it to the given buffer.
/// If not found, the given default value `defstr` is copied to the buffer, unless `defstr` is null, in which
/// case an empty string is copied to the buffer.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TMemConfigFile::ReadString(LPCTSTR section, LPCTSTR entry, LPTSTR buffer, uint bufSize, LPCTSTR defstr) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
const auto lookup = [](const TMemConfigFile& f, LPCTSTR section, LPCTSTR entry, LPCTSTR def) -> LPCTSTR
{
TStringNode sn(section);
const auto i = f.Sections->Find(&sn);
if (i == f.Sections->NPOS) return def;
const TStringMap& map = (*f.Sections)[i]->Value;
const auto j = map.Find(TStringMapNode{entry});
if (j == map.NPOS) return def;
return map[j].Value.c_str();
};
LPCTSTR s = lookup(*this, section, entry, defstr ? defstr : _T(""));
size_t n = min(::_tcslen(s), static_cast<size_t>(bufSize - 1));
::_tcsncpy(buffer, s, n);
buffer[n] = _T('\0');
return static_cast<uint>(n); // Exclude null-terminator in the count.
}
//
bool TMemConfigFile::WriteString(LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
PRECONDITION(section && entry && value);
TStringNode sn(section);
auto index = Sections->Find(&sn);
if (index == Sections->NPOS)
index = AddSection(section);
TStringMap& map = (*Sections)[index]->Value;
index = map.Find(TStringMapNode(entry));
if (index != map.NPOS)
map[index].Value = tstring(value);
else
index = map.Add(TStringMapNode(entry,value));
return index != map.NPOS;
}
//
void TMemConfigFile::UpdateFile()
{
TTextFile file(FileName.c_str(),TTextFile::WriteOnly|TTextFile::CreateAlways);
if(!file.IsOpen())
return;
TAPointer<tchar> buf(new tchar[MAX_PATH]);
for(uint section = 0; section < Sections->Size(); section++){
// Section name
file << _T("[") << (*Sections)[section]->Name.c_str() << _T("]") << _T("\r\n");
TStringMap& map = (*Sections)[section]->Value;
for(uint value = 0; value < map.Size(); value++)
file << map[value].Name.c_str() << _T("=") << map[value].Value.c_str() << _T("\r\n");
}
}
void TMemConfigFile::LoadFromFile()
{
const auto getName = [](tstring& name, LPCTSTR p)
{
PRECONDITION(p);
while (!_istalpha(*p))
++p;
const auto b = p++;
while (_istalpha(*p))
++p;
name.assign(b, p);
return p;
};
const auto getValue = [](tstring& name, LPCTSTR p)
{
PRECONDITION(p);
while (*p && (*p == _T(' ') || *p == _T('\t')) && *p != _T('='))
++p;
name = ++p;
};
TTextFile file(FileName.c_str());
if(!file.IsOpen())
return;
Sections->Flush();
TFileLineIterator fitr(file);
int sec_index = -1;
tstring tmp_name1,tmp_name2;
while(fitr){
auto p = SkipWhitespace_(fitr);
if(::_tcslen(p) && *p != _T(';')){
// read section
if(*p == _T('[')){
getName(tmp_name1,p);
sec_index = AddSection(tmp_name1.c_str());
}
// else read value
else if(sec_index >= 0){
p = getName(tmp_name1,p);
getValue(tmp_name2,p);
(*Sections)[sec_index]->Value.Add(TStringMapNode(tmp_name1,tmp_name2));
}
}
++fitr;
}
}
//
//
/// \class TRegConfigFile
// ~~~~~ ~~~~~~~~~~~~~~
/// This source file implements TRegConfigFile. This class is derived from
/// TConfigFile and overrides the low-level storage methods to vector them
/// to the registry.
//
/// TRegConfigFile is not intended to handle all of an app's registry needs,
/// but rather to be used for a convenient wrapper for app preference
/// settings. All data is stored in HKEY_CURRENT_USER, using FileName like
/// so:
/// \code
/// HKEY_CURRENT_USER\Software\"FileName"\...
/// \endcode
/// FileName should contain the company name and product name to adhere to
/// standard registry practices. Eg:
/// \code
/// FileName = "My Company\\Product"
/// HKEY_CURRENT_USER\Software\My Company\Product\...
/// \endcode
/// A version number can also be used to allow multiple versions of the
/// same product to live together:
/// \code
/// mFile = "My Company\\Product\\V1.0"
/// HKEY_CURRENT_USER\Software\My Company\Product\V1.0\...
/// \endcode
/// \note FileName must not end with a backslash!
///
/// All sections are created under the subtree as described above. Eg,
/// \code
/// Section = "Options"
/// HKEY_CURRENT_USER\Software\My Company\Product\V1.0\Options
/// \endcode
/// Entries are stored under sections as REG_SZ values (for numbers it is REG_DWORD). Eg,
/// \code
/// entry = "Tile"
/// HKEY_CURRENT_USER\Software\My Company\Product\V1.0\Options\Tile
/// \endcode
//
TRegConfigFile::TRegConfigFile(const tstring& name, HKEY root)
: TConfigFile{_T("Software\\") + name}, Root{root}
{}
//
TRegConfigFile::~TRegConfigFile()
{
}
//
bool TRegConfigFile::SectionExists(LPCTSTR section) const
{
PRECONDITION(section);
return Root.HasSubkey(FileName + szSep + section);
}
//
/// Copies all value names of the given section to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TRegConfigFile::ReadSection(LPCTSTR section, LPTSTR buffer, uint bufSize) const
{
PRECONDITION(section && buffer && bufSize > 1);
tchar* p = buffer;
*p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
TRegKey root(Root, FileName + szSep + section);
TRegValueIterator i(root);
if (!i) return 0;
for (; i; ++i)
{
TRegValue v(i);
LPCTSTR s = v.GetName();
size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
if (m == 0) break; // We've run out of space.
size_t n = min(::_tcslen(s), m - 1);
::_tcsncpy(p, s, n);
p += n;
*p++ = _T('\0');
}
// Add buffer terminator.
//
*p++ = _T('\0');
return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
//
/// Copies all section names to the specified buffer.
/// Each string is null-terminated, and the buffer is then terminated with a trailing null.
/// Returns the number of characters written, excluding the trailing null-terminators.
/// If the buffer is too small, the last string copied to the buffer may be truncated.
//
uint TRegConfigFile::ReadSections(LPTSTR buffer, uint bufSize) const
{
PRECONDITION(buffer && bufSize > 1);
tchar* p = buffer;
*p = _T('\0'); // Ensure the buffer is terminated, if we exit early.
TRegKey root(Root, FileName);
TRegKeyIterator i(root);
if (!i) return 0;
for (; i; ++i)
{
TRegKey k(i);
LPCTSTR s = k.GetName();
size_t m = static_cast<size_t>(buffer + bufSize - 1 - p); // space left
if (m == 0) break; // We've run out of space.
size_t n = min(::_tcslen(s), m - 1);
::_tcsncpy(p, s, n);
p += n;
*p++ = _T('\0');
}
// Add buffer terminator.
//
*p++ = _T('\0');
return static_cast<uint>(p - buffer) - 2; // Exclude trailing nulls in the count.
}
//
bool TRegConfigFile::EraseSection(LPCTSTR section)
{
PRECONDITION(section);
return TRegKey{Root, FileName}.NukeKey(section) == ERROR_SUCCESS;
}
//
bool TRegConfigFile::EraseEntry(LPCTSTR section, LPCTSTR entry)
{
PRECONDITION(section && entry);
return TRegKey{Root, FileName + szSep + section}.DeleteValue(entry) == ERROR_SUCCESS;
}
//
void TRegConfigFile::UpdateFile()
{
// don't need this currently
}
//
int TRegConfigFile::ReadInteger(LPCTSTR section, LPCTSTR entry, int defint) const
{
PRECONDITION(section && entry);
const auto k = Root.GetSubkey(FileName + szSep + section);
return k ? static_cast<int>(k->GetValueOrDefault<uint32>(entry, defint)) : defint;
}
//
bool TRegConfigFile::WriteInteger(LPCTSTR section, LPCTSTR entry, int value)
{
PRECONDITION(section && entry);
const auto k = TRegKey{Root, FileName + szSep + section};
return k.SetValue(entry, static_cast<uint32>(value)) == ERROR_SUCCESS;
}
//
/// Looks up the value of the given entry of the given section, and if found, copies it to the given buffer.
/// If not found, the given default value `defstr` is copied to the buffer, unless `defstr` is null, in which case an empty
/// string is copied to the buffer.
///
/// \return The number of characters written to the output buffer (excluding the null-terminator, which is also written).
/// If an error occurs, e.g. registry access is denied, a TXRegistry exception is thrown.
///
/// \note If the buffer is too small to hold the whole string, the string is truncated to (bufSize - 1) characters and
/// null-terminated.
//
uint TRegConfigFile::ReadString(LPCTSTR section, LPCTSTR entry, LPTSTR buffer, uint bufSize, LPCTSTR defstr) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
const auto k = Root.GetSubkey(FileName + szSep + section);
const auto d = defstr ? defstr : tstring{};
const auto s = k ? k->GetValueOrDefault(entry, d) : d;
const auto n = min(static_cast<uint>(s.size()), bufSize - 1);
const auto e = copy_n(s.begin(), n, buffer);
*e = _T('\0');
return n;
}
//
bool TRegConfigFile::WriteString(LPCTSTR section, LPCTSTR entry, LPCTSTR value)
{
PRECONDITION(section && entry && value);
const auto k = TRegKey{Root, FileName + szSep + section};
return k.SetValue(entry, value) == ERROR_SUCCESS;
}
//
/// This method reads binary data.
//
bool TRegConfigFile::ReadData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize) const
{
PRECONDITION(section && entry && buffer && bufSize > 0);
const auto k = Root.GetSubkey(FileName + szSep + section);
if (!k) return false;
auto size = uint32{bufSize};
auto type = uint32{};
return k->QueryValue(entry, &type, reinterpret_cast<uint8*>(buffer), &size) == ERROR_SUCCESS;
}
//
/// This method writes binary data.
//
bool TRegConfigFile::WriteData(LPCTSTR section, LPCTSTR entry, void* buffer, uint bufSize)
{
PRECONDITION(section && entry && buffer);
const auto k = TRegKey{Root, FileName + szSep + section};
return k.SetValue(entry, REG_BINARY, reinterpret_cast<const uint8*>(buffer), bufSize) == ERROR_SUCCESS;
}
//
} // OWL namespace
//============================================================================================
↑ V1004 The 'sz' pointer was used unsafely after it was verified against nullptr. Check lines: 48, 49.
↑ V1004 The 'text' pointer was used unsafely after it was verified against nullptr. Check lines: 97, 104.
↑ V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 117, 118.
↑ V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 394, 396.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 394, 396.
↑ V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 410, 415.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 410, 415.
↑ V1004 The 'sz' pointer was used unsafely after it was verified against nullptr. Check lines: 724, 725.
↑ V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1198, 1214.
↑ V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1227.
↑ V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 1256, 1257.
↑ V1004 The 'p' pointer was used unsafely after it was verified against nullptr. Check lines: 1268, 1269.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1353, 1354.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1365, 1368.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1430, 1431.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1441, 1442.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1448, 1449.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1466, 1467.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1479, 1480.
↑ V1004 The 'value' pointer was used unsafely after it was verified against nullptr. Check lines: 1479, 1481.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1489, 1490.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1501, 1502.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'sscanf' function. A pointer to the unsigned int type is expected.
↑ V1004 The 'pVal' pointer was used unsafely after it was verified against nullptr. Check lines: 62, 68.
↑ V1004 The 'entry' pointer was used unsafely after it was verified against nullptr. Check lines: 1107, 1112.
↑ V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1132, 1143.
↑ V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1165, 1172.
↑ V1004 The 'section' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1225.
↑ V1004 The 'value' pointer was used unsafely after it was verified against nullptr. Check lines: 1221, 1231.
↑ V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1365, 1375.
↑ V1004 The 'buffer' pointer was used unsafely after it was verified against nullptr. Check lines: 1397, 1407.
↑ V576 Incorrect format. Consider checking the fifth actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.
↑ V576 Incorrect format. Consider checking the fourth actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.
↑ V576 Incorrect format. Consider checking the third actual argument of the 'wsprintfA' function. The SIGNED integer type argument is expected.
↑ V811 Decreased performance. Excessive type casting: string -> char * -> string. Consider inspecting first argument of the function AddSection.
↑ V832 It's better to use '= default;' syntax instead of empty constructor body.
↑ V832 It's better to use '= default;' syntax instead of empty destructor body.