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.
193 lines
7.6 KiB
193 lines
7.6 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.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) { |
|
#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() 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; |
|
}
|
|
|