// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// File
// Name: Configuration.cpp
// Purpose: Reading configuration files
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
#include "Box.h"
#include <stdlib.h>
#include <limits.h>
#include "Configuration.h"
#include "CommonException.h"
#include "Guards.h"
#include "FdGetLine.h"
#include "MemLeakFindOn.h"
// utility whitespace function
inline bool iw(int c)
{
return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded
}
// boolean values
static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0};
static const bool sValueBooleanValue[] = {true, true, false, false};
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::Configuration(const std::string &)
// Purpose: Constructor
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
Configuration::Configuration(const std::string &rName)
: mName(rName)
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::Configuration(const Configuration &)
// Purpose: Copy constructor
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
Configuration::Configuration(const Configuration &rToCopy)
: mName(rToCopy.mName),
mSubConfigurations(rToCopy.mSubConfigurations),
mKeys(rToCopy.mKeys)
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::~Configuration()
// Purpose: Destructor
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
Configuration::~Configuration()
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::LoadAndVerify(const std::string &, const ConfigurationVerify *, std::string &)
// Purpose: Loads a configuration file from disc, checks it. Returns NULL if it was faulting, in which
// case they'll be an error message.
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
std::auto_ptr<Configuration> Configuration::LoadAndVerify(const char *Filename, const ConfigurationVerify *pVerify, std::string &rErrorMsg)
{
// Check arguments
if(Filename == 0)
{
THROW_EXCEPTION(CommonException, BadArguments)
}
// Just to make sure
rErrorMsg.erase();
// Open the file
FileHandleGuard<O_RDONLY> file(Filename);
// GetLine object
FdGetLine getline(file);
// Object to create
Configuration *pconfig = new Configuration(std::string("<root>"));
try
{
// Load
LoadInto(*pconfig, getline, rErrorMsg, true);
if(!rErrorMsg.empty())
{
// An error occured, return now
//TRACE1("Error message from LoadInto: %s", rErrorMsg.c_str());
TRACE0("Error at Configuration::LoadInfo\n");
delete pconfig;
pconfig = 0;
return std::auto_ptr<Configuration>(0);
}
// Verify?
if(pVerify)
{
if(!Verify(*pconfig, *pVerify, std::string(), rErrorMsg))
{
//TRACE1("Error message from Verify: %s", rErrorMsg.c_str());
TRACE0("Error at Configuration::Verify\n");
delete pconfig;
pconfig = 0;
return std::auto_ptr<Configuration>(0);
}
}
}
catch(...)
{
// Clean up
delete pconfig;
pconfig = 0;
throw;
}
// Success. Return result.
return std::auto_ptr<Configuration>(pconfig);
}
// --------------------------------------------------------------------------
//
// Function
// Name: LoadInto(Configuration &, FdGetLine &, std::string &, bool)
// Purpose: Private. Load configuration information from the file into the config object.
// Returns 'abort' flag, if error, will be appended to rErrorMsg.
// Created: 2003/07/24
//
// --------------------------------------------------------------------------
bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel)
{
bool startBlockExpected = false;
std::string blockName;
//TRACE1("BLOCK: |%s|\n", rConfig.mName.c_str());
while(!rGetLine.IsEOF())
{
std::string line(rGetLine.GetLine(true)); /* preprocess out whitespace and comments */
if(line.empty())
{
// Ignore blank lines
continue;
}
// Line an open block string?
if(line == "{")
{
if(startBlockExpected)
{
// New config object
Configuration config(blockName);
// Continue processing into this block
if(!LoadInto(config, rGetLine, rErrorMsg, false))
{
// Abort error
return false;
}
startBlockExpected = false;
// Store...
rConfig.mSubConfigurations.push_back(std::pair<std::string, Configuration>(blockName, config));
}
else
{
rErrorMsg += "Unexpected start block in " + rConfig.mName + "\n";
}
}
else
{
// Close block?
if(line == "}")
{
if(RootLevel)
{
// error -- root level doesn't have a close
rErrorMsg += "Root level has close block -- forget to terminate subblock?\n";
// but otherwise ignore
}
else
{
//TRACE0("ENDBLOCK\n");
return true; // All very good and nice
}
}
// Either a key, or a sub block beginning
else
{
// Can't be a start block
if(startBlockExpected)
{
rErrorMsg += "Block " + blockName + " wasn't started correctly (no '{' on line of it's own)\n";
startBlockExpected = false;
}
// Has the line got an = in it?
unsigned int equals = 0;
for(; equals < line.size(); ++equals)
{
if(line[equals] == '=')
{
// found!
break;
}
}
if(equals < line.size())
{
// Make key value pair
unsigned int keyend = equals;
while(keyend > 0 && iw(line[keyend-1]))
{
keyend--;
}
unsigned int valuestart = equals+1;
while(valuestart < line.size() && iw(line[valuestart]))
{
valuestart++;
}
if(keyend > 0 && valuestart <= line.size())
{
std::string key(line.substr(0, keyend));
std::string value(line.substr(valuestart));
//TRACE2("KEY: |%s|=|%s|\n", key.c_str(), value.c_str());
// Check for duplicate values
if(rConfig.mKeys.find(key) != rConfig.mKeys.end())
{
// Multi-values allowed here, but checked later on
rConfig.mKeys[key] += MultiValueSeparator;
rConfig.mKeys[key] += value;
}
else
{
// Store
rConfig.mKeys[key] = value;
}
}
else
{
rErrorMsg += "Invalid key in block "+rConfig.mName+"\n";
}
}
else
{
// Start of sub block
blockName = line;
startBlockExpected = true;
}
}
}
}
// End of file?
if(!RootLevel && rGetLine.IsEOF())
{
// Error if EOF and this isn't the root level
rErrorMsg += "File ended without terminating all subblocks\n";
}
return true;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::KeyExists(const char *)
// Purpose: Checks to see if a key exists
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
bool Configuration::KeyExists(const char *pKeyName) const
{
if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
return mKeys.find(pKeyName) != mKeys.end();
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetKeyValue(const char *)
// Purpose: Returns the value of a configuration variable
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
const std::string &Configuration::GetKeyValue(const char *pKeyName) const
{
if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
if(i == mKeys.end())
{
THROW_EXCEPTION(CommonException, ConfigNoKey)
}
else
{
return i->second;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetKeyValueInt(const char *)
// Purpose: Gets a key value as an integer
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
int Configuration::GetKeyValueInt(const char *pKeyName) const
{
if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
if(i == mKeys.end())
{
THROW_EXCEPTION(CommonException, ConfigNoKey)
}
else
{
long value = ::strtol((i->second).c_str(), NULL, 0 /* C style handling */);
if(value == LONG_MAX || value == LONG_MIN)
{
THROW_EXCEPTION(CommonException, ConfigBadIntValue)
}
return (int)value;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetKeyValueBool(const char *) const
// Purpose: Gets a key value as a boolean
// Created: 17/2/04
//
// --------------------------------------------------------------------------
bool Configuration::GetKeyValueBool(const char *pKeyName) const
{
if(pKeyName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
std::map<std::string, std::string>::const_iterator i(mKeys.find(pKeyName));
if(i == mKeys.end())
{
THROW_EXCEPTION(CommonException, ConfigNoKey)
}
else
{
bool value = false;
// Anything this is called for should have been verified as having a correct
// string in the verification section. However, this does default to false
// if it isn't in the string table.
for(int l = 0; sValueBooleanStrings[l] != 0; ++l)
{
if(::strcasecmp((i->second).c_str(), sValueBooleanStrings[l]) == 0)
{
// Found.
value = sValueBooleanValue[l];
break;
}
}
return value;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetKeyNames()
// Purpose: Returns list of key names
// Created: 2003/07/24
//
// --------------------------------------------------------------------------
std::vector<std::string> Configuration::GetKeyNames() const
{
std::map<std::string, std::string>::const_iterator i(mKeys.begin());
std::vector<std::string> r;
for(; i != mKeys.end(); ++i)
{
r.push_back(i->first);
}
return r;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::SubConfigurationExists(const char *)
// Purpose: Checks to see if a sub configuration exists
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
bool Configuration::SubConfigurationExists(const char *pSubName) const
{
if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
// Attempt to find it...
std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
for(; i != mSubConfigurations.end(); ++i)
{
// This the one?
if(i->first == pSubName)
{
// Yes.
return true;
}
}
// didn't find it.
return false;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetSubConfiguration(const char *)
// Purpose: Gets a sub configuration
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
const Configuration &Configuration::GetSubConfiguration(const char *pSubName) const
{
if(pSubName == 0) {THROW_EXCEPTION(CommonException, BadArguments)}
// Attempt to find it...
std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
for(; i != mSubConfigurations.end(); ++i)
{
// This the one?
if(i->first == pSubName)
{
// Yes.
return i->second;
}
}
THROW_EXCEPTION(CommonException, ConfigNoSubConfig)
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::GetSubConfigurationNames()
// Purpose: Return list of sub configuration names
// Created: 2003/07/24
//
// --------------------------------------------------------------------------
std::vector<std::string> Configuration::GetSubConfigurationNames() const
{
std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
std::vector<std::string> r;
for(; i != mSubConfigurations.end(); ++i)
{
r.push_back(i->first);
}
return r;
}
// --------------------------------------------------------------------------
//
// Function
// Name: Configuration::Verify(const Configuration &, const ConfigurationVerify &, const std::string &, std::string &)
// Purpose: Return list of sub configuration names
// Created: 2003/07/24
//
// --------------------------------------------------------------------------
bool Configuration::Verify(Configuration &rConfig, const ConfigurationVerify &rVerify, const std::string &rLevel, std::string &rErrorMsg)
{
bool ok = true;
// First... check the keys
if(rVerify.mpKeys != 0)
{
const ConfigurationVerifyKey *pvkey = rVerify.mpKeys;
bool todo = true;
do
{
// Can the key be found?
ASSERT(pvkey->mpName);
if(rConfig.KeyExists(pvkey->mpName))
{
// Get value
const std::string &rval = rConfig.GetKeyValue(pvkey->mpName);
const char *val = rval.c_str();
// Check it's a number?
if((pvkey->Tests & ConfigTest_IsInt) == ConfigTest_IsInt)
{
// Test it...
char *end;
long r = ::strtol(val, &end, 0);
if(r == LONG_MIN || r == LONG_MAX || end != (val + rval.size()))
{
// not a good value
ok = false;
rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid integer.\n";
}
}
// Check it's a bool?
if((pvkey->Tests & ConfigTest_IsBool) == ConfigTest_IsBool)
{
// See if it's one of the allowed strings.
bool found = false;
for(int l = 0; sValueBooleanStrings[l] != 0; ++l)
{
if(::strcasecmp(val, sValueBooleanStrings[l]) == 0)
{
// Found.
found = true;
break;
}
}
// Error if it's not one of them.
if(!found)
{
ok = false;
rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) is not a valid boolean value.\n";
}
}
// Check for multi valued statments where they're not allowed
if((pvkey->Tests & ConfigTest_MultiValueAllowed) == 0)
{
// Check to see if this key is a multi-value -- it shouldn't be
if(rval.find(MultiValueSeparator) != rval.npos)
{
ok = false;
rErrorMsg += rLevel + rConfig.mName +"." + pvkey->mpName + " (key) multi value not allowed (duplicated key?).\n";
}
}
}
else
{
// Is it required to exist?
if((pvkey->Tests & ConfigTest_Exists) == ConfigTest_Exists)
{
// Should exist, but doesn't.
ok = false;
rErrorMsg += rLevel + rConfig.mName + "." + pvkey->mpName + " (key) is missing.\n";
}
else if(pvkey->mpDefaultValue)
{
rConfig.mKeys[std::string(pvkey->mpName)] = std::string(pvkey->mpDefaultValue);
}
}
if((pvkey->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
// No more!
todo = false;
}
// next
pvkey++;
} while(todo);
// Check for additional keys
for(std::map<std::string, std::string>::const_iterator i = rConfig.mKeys.begin();
i != rConfig.mKeys.end(); ++i)
{
// Is the name in the list?
const ConfigurationVerifyKey *scan = rVerify.mpKeys;
bool found = false;
while(scan)
{
if(scan->mpName == i->first)
{
found = true;
break;
}
// Next?
if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
break;
}
scan++;
}
if(!found)
{
// Shouldn't exist, but does.
ok = false;
rErrorMsg += rLevel + rConfig.mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n";
}
}
}
// Then the sub configurations
if(rVerify.mpSubConfigurations)
{
// Find the wildcard entry, if it exists, and check that required subconfigs are there
const ConfigurationVerify *wildcardverify = 0;
const ConfigurationVerify *scan = rVerify.mpSubConfigurations;
while(scan)
{
ASSERT(scan->mpName);
if(scan->mpName[0] == '*')
{
wildcardverify = scan;
}
// Required?
if((scan->Tests & ConfigTest_Exists) == ConfigTest_Exists)
{
if(scan->mpName[0] == '*')
{
// Check something exists
if(rConfig.mSubConfigurations.size() < 1)
{
// A sub config should exist, but doesn't.
ok = false;
rErrorMsg += rLevel + rConfig.mName + ".* (block) is missing (a block must be present).\n";
}
}
else
{
// Check real thing exists
if(!rConfig.SubConfigurationExists(scan->mpName))
{
// Should exist, but doesn't.
ok = false;
rErrorMsg += rLevel + rConfig.mName + "." + scan->mpName + " (block) is missing.\n";
}
}
}
// Next?
if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
break;
}
scan++;
}
// Go through the sub configurations, one by one
for(std::list<std::pair<std::string, Configuration> >::const_iterator i(rConfig.mSubConfigurations.begin());
i != rConfig.mSubConfigurations.end(); ++i)
{
// Can this be found?
const ConfigurationVerify *subverify = 0;
const ConfigurationVerify *scan = rVerify.mpSubConfigurations;
const char *name = i->first.c_str();
ASSERT(name);
while(scan)
{
if(strcmp(scan->mpName, name) == 0)
{
// found it!
subverify = scan;
}
// Next?
if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
{
break;
}
scan++;
}
// Use wildcard?
if(subverify == 0)
{
subverify = wildcardverify;
}
// Verify
if(subverify)
{
// override const-ness here...
if(!Verify((Configuration&)i->second, *subverify, rConfig.mName + '.', rErrorMsg))
{
ok = false;
}
}
}
}
return ok;
}
syntax highlighted by Code2HTML, v. 0.9.1