C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

196 lines
7.4 KiB

/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.3 2005/11/29 12:39:42 marc
make it compilable with gcc 4.0.2 and newer doxygen
Revision 1.2 2005/01/28 07:49:32 marc
Save configuration using file.hxx
Revision 1.1 2005/01/07 00:31:38 marc
initial version
1 2 3 4 5 6 7 8
5678901234567890123456789012345678901234567890123456789012345678901234567890
*/
#include <mrw/configfile.hxx>
#include <mrw/exception.hxx>
#include <mrw/string.hxx>
#include <mrw/file.hxx>
#include <fstream>
mrw::ConfigFileReader::Value&
mrw::ConfigFileReader::operator()(const std::string& section,
const std::string& name,
const std::string& default_)
{
_values[section].insert(std::make_pair(name, Value(default_)));
return _values[section].find(name)->second;
}
mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename)
{
_filename = filename;
return reload();
}
mrw::ConfigFileReader& mrw::ConfigFileReader::reload() {
return parse(mrw::File::read(_filename));
}
mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file)
{
_values.erase(_values.begin(), _values.end());
std::string section;
for (std::string::size_type pos(0);
(pos=file.find_first_of("[=#", pos))!=std::string::npos; ++pos) {
switch (file[pos]) {
case '[': _sections[section]=pos; section=parseSection(file, pos); break;
case '=': _values[section].insert(parseValue(file, pos)); break;
case '#': parseComment(file, pos); break;
};
}
_sections[section]=file.size();
return *this;
}
std::string mrw::ConfigFileReader::parseSection(const std::string& file,
std::string::size_type& pos)
const {
std::string::size_type start(pos);
if ((pos=file.find(']', pos))==std::string::npos)
throw mrw::invalid_argument
("section not terminated in file '"+_filename+"'\n"
"environment is: "+file.substr(start-10>0?start-10:0));
return file.substr(start+1, pos-start-1);
}
mrw::ConfigFileReader::Values::mapped_type::value_type
mrw::ConfigFileReader::parseValue(const std::string& file,
std::string::size_type& pos)
const {
// find the begin of the variable name
std::string::size_type startName(file.rfind('\n', pos));
if (startName==std::string::npos) startName=0;
startName=file.find_first_not_of("\n\t ", startName);
if (startName==pos)
throw mrw::invalid_argument
("empty variable name in file '"+_filename+"'\n"
"environment is: "+file.substr(startName-10>0?startName-10:0, pos+10));
// find the end of the variable name
std::string::size_type endName(file.find_last_not_of("\n\t ", pos-1)+1);
// find the begin of the variable contents
std::string::size_type startVal(file.find_first_not_of("\n\t ", ++pos));
if (startVal==std::string::npos)
return std::make_pair // end of file, empty value
(file.substr(startName, endName-startName), Value("", pos, pos));
// find the end of the variable contents
std::string::size_type realStart(startVal); // doesn't cut the first quote
std::string::size_type endVal(file[startVal]=='"' ? // quoted with "
file.find('"', ++startVal) :
file[startVal]=='\'' ? // quoted with '
file.find('\'', ++startVal) :
file.find_first_of("[=#", startVal)); // normal
if (endVal!=std::string::npos && file[endVal]=='=')
endVal=file.rfind('\n', endVal); // jump before following variable name
endVal = file.find_last_not_of("\n\t ", // remove trailing white spaces
endVal!=std::string::npos?endVal-1:endVal)+1;
std::string::size_type realEnd(endVal!=std::string::npos && // quotes?
((file[realStart]=='"' && file[endVal]=='"') ||
(file[realStart]=='\'' && file[endVal]=='\''))
? endVal+1 : endVal);
if (endVal<=startVal)
return std::make_pair // empty value
(file.substr(startName, endName-startName), Value("", pos, pos));
return std::make_pair // normal case
(file.substr(startName, endName-startName),
Value(file.substr(startVal, endVal-startVal), realStart, pos=realEnd));
}
std::string mrw::ConfigFileReader::parseComment(const std::string& file,
std::string::size_type& pos)
const {
std::string::size_type start(pos);
pos = file.find('\n', pos);
return file.substr(start, pos-start);
}
// GNU g++ prior to 3.4 does not implement covariant returns
#if __GNUC__ == 3 && __GNUC_MINOR__ < 4
mrw::ConfigFileReader&
#else
mrw::ConfigFileWriter&
#endif
mrw::ConfigFileWriter::reload() {
#if __GNUC__ == 3 && __GNUC_MINOR__ < 4
return parse(_file=mrw::File::read(_filename));
#else
return dynamic_cast<mrw::ConfigFileWriter&>
(parse(_file=mrw::File::read(_filename)));
#endif
}
mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() {
if (_filename=="") return *this;
// work in changes
bool changed(false);
for (Values::iterator sec(_values.begin()); sec!=_values.end(); ++sec)
for (Values::mapped_type::iterator var(sec->second.begin());
var!=sec->second.end(); ++var)
if (var->second.changed) {
var->second.changed = false;
changed = true;
// check if we need quoting
std::string value(var->second.value);
if (value.find_first_of("[=#")!=std::string::npos) {
if (value.find('"')==std::string::npos) {
value = '"'+value+'"';
} else if (value.find('\'')==std::string::npos) {
value = '\''+value+'\'';
} else {
throw mrw::invalid_argument("Configuration value is not quotable: "
+value);
}
}
// recalculate all positions
if (var->second.start &&
value.size() != var->second.end-var->second.start) {
std::string::size_type diff
(value.size()-var->second.end+var->second.start);
for (Values::iterator sec2(_values.begin());
sec2!=_values.end(); ++sec2)
for (Values::mapped_type::iterator var2(sec2->second.begin());
var2!=sec2->second.end(); ++var2)
if (var2->second.start>var->second.end) {
var2->second.start += diff;
var2->second.end += diff;
}
for (Sections::iterator sec2(_sections.begin());
sec2!=_sections.end(); ++sec2)
if (sec2->second>=var->second.end) sec2->second += diff;
}
// set the new value
if (var->second.start) // simple change
_file.replace(var->second.start, var->second.end-var->second.start,
value);
else if (_sections.find(sec->first)!=_sections.end()) // new value
_file.insert(_sections[sec->first], "\n"+var->first+" = "+value
+(_file[_sections[sec->first]]=='\n'?"":"\n"));
else { // even a new section
_file+="\n["+sec->first+"]\n"+var->first+" = "+value;
_sections[sec->first] = _file.size();
}
}
if (changed) mrw::File::save(_filename, _file);
return *this;
}