If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle
See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// lib_config.cpp - abstract configuration
#include "library.h"
/*
==============================================================================
LConfigString IMPLEMENTATION
==============================================================================
*/
class LConfigString : public LConfig {
public:
char *m_pszValue;
char *m_pszDefault;
public:
LConfigString(const char *pszName, const char *pszDefault, const char *pszValue = 0);
virtual ~LConfigString();
virtual void Parse(LParser *p);
virtual void Store(LFileWrite *fw);
virtual bool MustStore();
virtual char *intGetString() const;
virtual void intSetString(const char *value);
};
LConfigString::LConfigString(const char *pszName, const char *pszDefault,
const char *pszValue)
: LConfig(pszName)
{
if (pszDefault)
m_pszDefault = L_Strdup(pszDefault, TAG_CONFIG);
else
m_pszDefault = 0;
if (pszValue)
m_pszValue = L_Strdup(pszDefault, TAG_CONFIG);
else
m_pszValue = 0;
}
LConfigString::~LConfigString()
{
if (m_pszDefault)
L_Free(m_pszDefault);
if (m_pszValue)
L_Free(m_pszValue);
}
void LConfigString::Parse(LParser *p)
{
intSetString(p->AnyString());
}
void LConfigString::Store(LFileWrite *fw)
{
fw->Printf("\"%s\"", m_pszValue ? m_pszValue : m_pszDefault);
}
bool LConfigString::MustStore()
{
return m_pszValue ? true : false;
}
char *LConfigString::intGetString() const
{
if (m_pszValue)
return m_pszValue;
if (m_pszDefault)
return m_pszDefault;
throw LError("no value given");
}
void LConfigString::intSetString(const char *value)
{
lassert(value);
if (m_pszValue) {
L_Free(m_pszValue);
m_pszValue = 0;
}
m_pszValue = L_Strdup(value, TAG_CONFIG);
}
/*
==============================================================================
LConfigInt IMPLEMENTATION
==============================================================================
*/
class LConfigInt : public LConfig {
public:
bool m_bValue;
int m_iValue;
bool m_bDefault;
int m_iDefault;
public:
LConfigInt(const char *pszName, bool bDefault, int iDefault,
bool bValue, int iValue);
virtual void Parse(LParser *p);
virtual void Store(LFileWrite *fw);
virtual bool MustStore();
virtual int intGetInt() const;
virtual void intSetInt(int value);
};
LConfigInt::LConfigInt(const char *pszName, bool bDefault, int iDefault,
bool bValue, int iValue)
: LConfig(pszName)
{
m_bDefault = bDefault;
m_iDefault = iDefault;
m_bValue = bValue;
m_iValue = iValue;
}
void LConfigInt::Parse(LParser *p)
{
m_iValue = p->Integer();
m_bValue = true;
}
void LConfigInt::Store(LFileWrite *fw)
{
fw->Printf("%i", m_bValue ? m_iValue : m_iDefault);
}
bool LConfigInt::MustStore()
{
return m_bValue;
}
int LConfigInt::intGetInt() const
{
if (m_bValue)
return m_iValue;
if (m_bDefault)
return m_iDefault;
throw LError("no value given");
}
void LConfigInt::intSetInt(int value)
{
m_iValue = value;
m_bValue = true;
}
/*
==============================================================================
LConfigFloat IMPLEMENTATION
==============================================================================
*/
class LConfigFloat : public LConfig {
public:
bool m_bValue;
float m_flValue;
bool m_bDefault;
float m_flDefault;
public:
LConfigFloat(const char *pszName, bool bDefault, float flDefault,
bool bValue, float flValue);
virtual void Parse(LParser *p);
virtual void Store(LFileWrite *fw);
virtual bool MustStore();
virtual float intGetFloat() const;
virtual void intSetFloat(float value);
};
LConfigFloat::LConfigFloat(const char *pszName, bool bDefault, float flDefault,
bool bValue, float flValue)
: LConfig(pszName)
{
m_bDefault = bDefault;
m_flDefault = flDefault;
m_bValue = bValue;
m_flValue = flValue;
}
void LConfigFloat::Parse(LParser *p)
{
m_flValue = p->Float();
m_bValue = true;
}
void LConfigFloat::Store(LFileWrite *fw)
{
fw->Printf("%f", m_bValue ? m_flValue : m_flDefault);
}
bool LConfigFloat::MustStore()
{
return m_bValue;
}
float LConfigFloat::intGetFloat() const
{
if (m_bValue)
return m_flValue;
if (m_bDefault)
return m_flDefault;
throw LError("no value given");
}
void LConfigFloat::intSetFloat(float value)
{
m_flValue = value;
m_bValue = true;
}
/*
==============================================================================
LConfigBool IMPLEMENTATION
==============================================================================
*/
class LConfigBool : public LConfig {
public:
bool m_bValue;
bool m_value;
bool m_bDefault;
bool m_default;
public:
LConfigBool(const char *pszName, bool bDefault, bool thedefault,
bool bValue, bool thevalue);
virtual void Parse(LParser *p);
virtual void Store(LFileWrite *fw);
virtual bool MustStore();
virtual bool intGetBool() const;
virtual void intSetBool(bool value);
};
LConfigBool::LConfigBool(const char *pszName, bool bDefault, bool thedefault,
bool bValue, bool thevalue)
: LConfig(pszName)
{
m_bDefault = bDefault;
m_default = thedefault;
m_bValue = bValue;
m_value = thevalue;
}
void LConfigBool::Parse(LParser *p)
{
m_value = p->Integer() ? true : false;
m_bValue = true;
}
void LConfigBool::Store(LFileWrite *fw)
{
fw->Printf("%i", (m_bValue ? m_value : m_default) ? 1 : 0);
}
bool LConfigBool::MustStore()
{
return m_bValue;
}
bool LConfigBool::intGetBool() const
{
if (m_bValue)
return m_value;
if (m_bDefault)
return m_default;
throw LError("no value given");
}
void LConfigBool::intSetBool(bool value)
{
m_value = value;
m_bValue = true;
}
/*
==============================================================================
LConfigRecord IMPLEMENTATION
==============================================================================
*/
class LConfigRecord : public LConfig {
public:
LConfig *m_pItems;
public:
LConfigRecord(const char *pszName, l_configrecord_t *definition);
virtual ~LConfigRecord();
virtual void Parse(LParser *p);
virtual void Store(LFileWrite *fw);
virtual bool MustStore();
virtual LConfig *intGetItem(const char *name) const;
};
/*
==============
LConfigRecord::LConfigRecord
Completely build a configuration record, including all children
==============
*/
LConfigRecord::LConfigRecord(const char *pszName, l_configrecord_t *definition)
: LConfig(pszName)
{
l_configitem_t *it;
LConfig *pItem;
m_pItems = 0;
for(it = definition->items; it->name; it++) {
bool havedflt = it->dflt ? true : false;
switch(it->type) {
case CI_STRING:
pItem = new LConfigString(it->name, it->dflt, 0);
break;
case CI_INT:
pItem = new LConfigInt(it->name, havedflt, havedflt ? atoi(it->dflt) : 0,
false, 0);
break;
case CI_FLOAT:
pItem = new LConfigFloat(it->name, havedflt, havedflt ? atof(it->dflt) : 0,
false, 0);
break;
case CI_BOOL:
pItem = new LConfigBool(it->name,
havedflt, havedflt ? (atoi(it->dflt) ? true : false) : false,
false, false);
break;
case CI_RECORD:
pItem = new LConfigRecord(it->name, it->record);
break;
default:
lassert(0);
}
pItem->m_pNext = m_pItems;
if (m_pItems)
m_pItems->m_pPrev = pItem;
pItem->m_pPrev = 0;
m_pItems = pItem;
}
}
/*
==============
LConfigRecord::~LConfigRecord
Free all children
==============
*/
LConfigRecord::~LConfigRecord()
{
LConfig *pItem;
while(m_pItems) {
pItem = m_pItems;
m_pItems = m_pItems->m_pNext;
delete pItem;
}
}
/*
==============
LConfigRecord::Parse
Read a record from a buffer, including { and }
==============
*/
void LConfigRecord::Parse(LParser *p)
{
LConfig *pItem;
p->Expect("{");
for(;;) {
p->NextToken();
if (p->tok.type == '}')
break;
if (p->tok.type != TOK_STRING)
throw p->Error("} or string expected");
pItem = GetItem(p->tok.string);
if (!pItem)
throw p->Error("unknown config item %s", p->tok.string);
pItem->Parse(p);
p->Expect(";");
}
}
/*
==============
LConfigRecord::Store
Write all non-default children to the file
==============
*/
void LConfigRecord::Store(LFileWrite *fw)
{
LConfig *pItem;
fw->Printf("{\n");
for(pItem = m_pItems; pItem; pItem = pItem->m_pNext) {
if (!pItem->MustStore())
continue;
fw->Printf("%s ", pItem->m_pszName);
pItem->Store(fw);
fw->Printf(";\n");
}
fw->Printf("}");
}
/*
==============
LConfigRecord::MustStore
We must store a record when any of our children must be stored
==============
*/
bool LConfigRecord::MustStore()
{
LConfig *pItem;
for(pItem = m_pItems; pItem; pItem = pItem->m_pNext)
if (pItem->MustStore())
return true;
return false;
}
/*
==============
LConfigRecord::intGetItem
Return one of the children.
Throws an exception when the child cannot be found.
==============
*/
LConfig *LConfigRecord::intGetItem(const char *name) const
{
LConfig *pItem;
for(pItem = m_pItems; pItem; pItem = pItem->m_pNext) {
if (!strcmp(pItem->m_pszName, name))
return pItem;
}
throw LError("%s: no such item", name);
}
/*
==============================================================================
LConfig IMPLEMENTATION
==============================================================================
*/
/*
==============
LConfig::LConfig
LConfig::~LConfig
Don't initialize m_pNext/m_pPrev, they are maintained by the parent
==============
*/
LConfig::LConfig(const char *pszName)
{
if (pszName)
m_pszName = L_Strdup(pszName, TAG_CONFIG);
else
m_pszName = 0;
}
LConfig::~LConfig()
{
if (m_pszName)
L_Free(m_pszName);
}
/*
==============
LConfig::TryLoad
Load the configuration through the parser
==============
*/
bool LConfig::TryLoad(const char *filename)
{
try
{
LParser p;
p.AddFile(filename);
Parse(&p);
return true;
}
catch(LError &)
{
return false;
}
}
/*
==============
LConfig::TrySave
Open a file for writing and save the configuration
==============
*/
bool LConfig::TrySave(const char *filename)
{
bool bSuccess;
bSuccess = false;
try
{
LFileWrite fw;
Store(&fw);
bSuccess = fw.TryWrite(filename);
}
catch(LError &err)
{
L_Printf("Error writing config to %s: %s\n", filename, err.Get());
}
return bSuccess;
}
/*
==============
LConfig::GetString
LConfig::GetInt
LConfig::GetFloat
Wrapper functions for simplified access.
An exception is raised if the given configuration item doesn't
exist or it isn't possible to get a value for it.
==============
*/
char *LConfig::GetString(const char *name) const
{
const LConfig *pItem;
pItem = GetItem(name);
return pItem->intGetString();
}
int LConfig::GetInt(const char *name) const
{
const LConfig *pItem;
pItem = GetItem(name);
return pItem->intGetInt();
}
float LConfig::GetFloat(const char *name) const
{
const LConfig *pItem;
pItem = GetItem(name);
return pItem->intGetFloat();
}
bool LConfig::GetBool(const char *name) const
{
const LConfig *pItem;
pItem = GetItem(name);
return pItem->intGetBool();
}
/*
==============
LConfig::SetString
LConfig::SetInt
LConfig::SetFloat
Simple wrapper functions for setting configuration.
An exception is raised if the given configuration item doesn't
exist, or if it isn't of the expected type.
==============
*/
void LConfig::SetString(const char *name, const char *value)
{
LConfig *pItem;
pItem = GetItem(name);
pItem->intSetString(value);
}
void LConfig::SetInt(const char *name, int value)
{
LConfig *pItem;
pItem = GetItem(name);
pItem->intSetInt(value);
}
void LConfig::SetFloat(const char *name, float value)
{
LConfig *pItem;
pItem = GetItem(name);
pItem->intSetFloat(value);
}
void LConfig::SetBool(const char *name, bool value)
{
LConfig *pItem;
pItem = GetItem(name);
pItem->intSetBool(value);
}
/*
==============
LConfig::GetItem
Walk the given item path to retrieve the requested item.
Throws an exception if the item doesn't exist
==============
*/
const LConfig *LConfig::GetItem(const char *name) const
{
LConfig *pItem;
char buf[64];
const char *p;
int len;
if (!name)
return this;
while(*name && *name == '.') name++;
if (!*name)
return this;
p = name;
while(*p && *p != '.') p++;
len = p - name + 1;
if (len > sizeof(buf))
len = sizeof(buf);
xstrcpy(buf, len, name);
pItem = intGetItem(buf);
return pItem->GetItem(p);
}
LConfig *LConfig::GetItem(const char *name)
{
LConfig *pItem;
char buf[64];
const char *p;
int len;
if (!name)
return this;
while(*name && *name == '.') name++;
if (!*name)
return this;
p = name;
while(*p && *p != '.') p++;
len = p - name + 1;
if (len > sizeof(buf))
len = sizeof(buf);
xstrcpy(buf, len, name);
pItem = intGetItem(buf);
return pItem->GetItem(p);
}
/*
==============
LConfig::Get*
LConfig::Set*
Default implementations throw errors.
==============
*/
char *LConfig::intGetString() const
{
throw LError("not a string");
}
int LConfig::intGetInt() const
{
throw LError("not an integer");
}
float LConfig::intGetFloat() const
{
throw LError("not a float");
}
bool LConfig::intGetBool() const
{
throw LError("not a bool");
}
LConfig *LConfig::intGetItem(const char *name) const
{
throw LError("not a record");
}
void LConfig::intSetString(const char *value)
{
throw LError("not a string");
}
void LConfig::intSetInt(int value)
{
throw LError("not an integer");
}
void LConfig::intSetFloat(float value)
{
throw LError("not a float");
}
void LConfig::intSetBool(bool value)
{
throw LError("not a bool");
}
/*
==============
LConfig::Create
Create a record object
==============
*/
LConfig *LConfig::Create(l_configrecord_t *definition)
{
return new LConfigRecord(0, definition);
}