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.
202 lines
8.0 KiB
202 lines
8.0 KiB
/** @file |
|
|
|
$Id$ |
|
|
|
$Date$ |
|
$Author$ |
|
|
|
@copy © Marc Wäckerlin |
|
@license LGPL, see file <a href="license.html">COPYING</a> |
|
|
|
$Log$ |
|
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 <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(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 <iostream> |
|
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; |
|
}
|
|
|