diff --git a/mrw/configfile.cpp b/mrw/configfile.cpp
new file mode 100644
index 0000000..5548ac0
--- /dev/null
+++ b/mrw/configfile.cpp
@@ -0,0 +1,202 @@
+/** @file
+
+ $Id$
+
+ $Date$
+ $Author$
+
+ @copy © Marc Wäckerlin
+ @license LGPL, see file COPYING
+
+ $Log$
+ 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
+
+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(readFile());
+}
+
+std::string mrw::ConfigFileReader::readFile() const throw(std::exception) {
+ std::string fileContents; // declaration as first allows optimization
+ std::ifstream file(_filename.c_str());
+ if (!file)
+ throw mrw::invalid_argument("Error file not found: '"+_filename+"'");
+ file.seekg(0, std::ios::end);
+ int size(file.tellg());
+ fileContents.resize(size); // make buffer long enough to store all
+ file.seekg(0, std::ios::beg);
+ file.read(fileContents.begin().operator->(), size); // hack to get the buffer
+ if (!file.good() && file.eof())
+ throw mrw::invalid_argument("Error reading file: '"+_filename+"'");
+ return fileContents;
+}
+
+#include
+using namespace std;
+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=readFile());
+}
+
+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) return *this;
+ // save to file
+ std::ofstream file(_filename.c_str());
+ if (!(file<<_file))
+ throw mrw::invalid_argument("Cannot write file '"+_filename+"'");
+ return *this;
+}
diff --git a/mrw/configfile.hpp b/mrw/configfile.hpp
new file mode 100644
index 0000000..8ba0e5e
--- /dev/null
+++ b/mrw/configfile.hpp
@@ -0,0 +1,362 @@
+/** @file
+
+ $Id$
+
+ $Date$
+ $Author$
+
+ @copy © Marc Wäckerlin
+ @license LGPL, see file COPYING
+
+ $Log$
+ Revision 1.1 2005/01/07 00:31:38 marc
+ initial version
+
+
+*/
+#ifndef __MRW_CONFIGFILE_HPP__
+#define __MRW_CONFIGFILE_HPP__
+
+#include
+#include