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.

197 lines
7.6 KiB

20 years ago
/** @file
$Id$
20 years ago
$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
20 years ago
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>
20 years ago
#include <fstream>
mrw::ConfigFileReader::Value&
mrw::ConfigFileReader::operator()(const std::string& section,
const std::string& name,
const std::string& default_)
20 years ago
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));
20 years ago
}
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]=='\''))
20 years ago
? 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<mrw::ConfigFileWriter&>
(parse(_file=mrw::File::read(_filename)));
#endif
20 years ago
}
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) {
20 years ago
value = '"'+value+'"';
} else if (value.find('\'')==std::string::npos) {
20 years ago
value = '\''+value+'\'';
} else {
20 years ago
throw mrw::invalid_argument("Configuration value is not quotable: "
+value);
}
}
20 years ago
// 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);
20 years ago
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);
20 years ago
return *this;
}