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

/** @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;
}