186 lines
7.3 KiB
C++
186 lines
7.3 KiB
C++
/** @file
|
|
|
|
$Id$
|
|
|
|
$Date$
|
|
$Author$
|
|
|
|
@copy © Marc Wäckerlin
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
$Log$
|
|
Revision 1.2 2005/01/28 07:49:32 marc
|
|
Save configuration using file.hpp
|
|
|
|
Revision 1.1 2005/01/07 00:31:38 marc
|
|
initial version
|
|
|
|
|
|
1 2 3 4 5 6 7 8
|
|
5678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
*/
|
|
|
|
#include <mrw/configfile.hpp>
|
|
#include <mrw/exception.hpp>
|
|
#include <mrw/string.hpp>
|
|
#include <mrw/file.hpp>
|
|
#include <fstream>
|
|
|
|
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) {
|
|
return parse(_file=mrw::File::read(_filename));
|
|
}
|
|
|
|
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) {
|
|
int 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;
|
|
}
|