/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file COPYING
$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
#include
#include
#include
#include
mrw::ConfigFileReader::Value&
mrw::ConfigFileReader::operator()(const std::string& section,
const std::string& name,
const std::string& default_)
throw(std::bad_exception) {
_values[section].insert(std::make_pair(name, Value(default_)));
return _values[section].find(name)->second;
}
mrw::ConfigFileReader& mrw::ConfigFileReader::load(const std::string& filename)
throw(std::exception) {
_filename = filename;
return reload();
}
mrw::ConfigFileReader& mrw::ConfigFileReader::reload() throw(std::exception) {
return parse(mrw::File::read(_filename));
}
mrw::ConfigFileReader& mrw::ConfigFileReader::parse(const std::string& file)
throw(std::exception) {
_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 throw(std::exception) {
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 throw(std::exception) {
// 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 throw(std::bad_exception) {
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() throw(std::exception) {
#if __GNUC__ == 3 && __GNUC_MINOR__ < 4
return parse(_file=mrw::File::read(_filename));
#else
return dynamic_cast
(parse(_file=mrw::File::read(_filename)));
#endif
}
mrw::ConfigFileWriter& mrw::ConfigFileWriter::save() throw(std::exception) {
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;
}